aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_vorbis
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Input/in_vorbis
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/Plugins/Input/in_vorbis')
-rw-r--r--Src/Plugins/Input/in_vorbis/DlgBase.cpp67
-rw-r--r--Src/Plugins/Input/in_vorbis/DlgBase.h118
-rw-r--r--Src/Plugins/Input/in_vorbis/ExtendedRead.cpp65
-rw-r--r--Src/Plugins/Input/in_vorbis/about.cpp264
-rw-r--r--Src/Plugins/Input/in_vorbis/api__in_vorbis.h21
-rw-r--r--Src/Plugins/Input/in_vorbis/c_string.cpp96
-rw-r--r--Src/Plugins/Input/in_vorbis/c_string.h156
-rw-r--r--Src/Plugins/Input/in_vorbis/chainedstream_parse.cpp82
-rw-r--r--Src/Plugins/Input/in_vorbis/config.cpp335
-rw-r--r--Src/Plugins/Input/in_vorbis/core_api.h567
-rw-r--r--Src/Plugins/Input/in_vorbis/decoder.cpp459
-rw-r--r--Src/Plugins/Input/in_vorbis/decoder.h41
-rw-r--r--Src/Plugins/Input/in_vorbis/genres.c97
-rw-r--r--Src/Plugins/Input/in_vorbis/genres.h16
-rw-r--r--Src/Plugins/Input/in_vorbis/http.cpp596
-rw-r--r--Src/Plugins/Input/in_vorbis/in_vorbis.rc415
-rw-r--r--Src/Plugins/Input/in_vorbis/in_vorbis.sln65
-rw-r--r--Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj339
-rw-r--r--Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj.filters145
-rw-r--r--Src/Plugins/Input/in_vorbis/info_.cpp658
-rw-r--r--Src/Plugins/Input/in_vorbis/infobox.cpp1457
-rw-r--r--Src/Plugins/Input/in_vorbis/localfile.cpp260
-rw-r--r--Src/Plugins/Input/in_vorbis/main.h220
-rw-r--r--Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.cpp194
-rw-r--r--Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.h41
-rw-r--r--Src/Plugins/Input/in_vorbis/oggdrop/112.pngbin0 -> 782 bytes
-rw-r--r--Src/Plugins/Input/in_vorbis/oggdrop/113.pngbin0 -> 817 bytes
-rw-r--r--Src/Plugins/Input/in_vorbis/oggdrop/114.pngbin0 -> 753 bytes
-rw-r--r--Src/Plugins/Input/in_vorbis/oggdrop/115.pngbin0 -> 705 bytes
-rw-r--r--Src/Plugins/Input/in_vorbis/oggdrop/116.pngbin0 -> 725 bytes
-rw-r--r--Src/Plugins/Input/in_vorbis/oggdrop/117.pngbin0 -> 809 bytes
-rw-r--r--Src/Plugins/Input/in_vorbis/oggdrop/118.pngbin0 -> 824 bytes
-rw-r--r--Src/Plugins/Input/in_vorbis/oggdrop/119.pngbin0 -> 829 bytes
-rw-r--r--Src/Plugins/Input/in_vorbis/oggdrop/120.pngbin0 -> 720 bytes
-rw-r--r--Src/Plugins/Input/in_vorbis/oggdrop/121.pngbin0 -> 719 bytes
-rw-r--r--Src/Plugins/Input/in_vorbis/oggdrop/122.pngbin0 -> 727 bytes
-rw-r--r--Src/Plugins/Input/in_vorbis/oggdrop/123.pngbin0 -> 755 bytes
-rw-r--r--Src/Plugins/Input/in_vorbis/resource.h177
-rw-r--r--Src/Plugins/Input/in_vorbis/rf.h108
-rw-r--r--Src/Plugins/Input/in_vorbis/shaper.cpp245
-rw-r--r--Src/Plugins/Input/in_vorbis/shaper.h30
-rw-r--r--Src/Plugins/Input/in_vorbis/unihack.cpp2
-rw-r--r--Src/Plugins/Input/in_vorbis/vcedit.c491
-rw-r--r--Src/Plugins/Input/in_vorbis/vcedit.h62
-rw-r--r--Src/Plugins/Input/in_vorbis/version.rc239
-rw-r--r--Src/Plugins/Input/in_vorbis/wa2.cpp1097
-rw-r--r--Src/Plugins/Input/in_vorbis/winnt_helper.cpp2
47 files changed, 9027 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_vorbis/DlgBase.cpp b/Src/Plugins/Input/in_vorbis/DlgBase.cpp
new file mode 100644
index 00000000..43b0f771
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/DlgBase.cpp
@@ -0,0 +1,67 @@
+#include "DlgBase.h"
+
+void DlgBase::MakeComboEdit(UINT id, DWORD s)
+{
+ HWND w = GetDlgItem(wnd, id);
+ RECT r;
+ GetChildRect(id, r);
+ DestroyWindow(w);
+ CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", 0, WS_CHILD | s, r.left - 1, r.top - 1, r.right - r.left, r.bottom - r.top, wnd, (HMENU)id, 0, 0);
+}
+
+void DlgBase::GetChildRect(UINT id, RECT& child)
+{
+ RECT r_parent, r_child;
+ GetWindowRect(wnd, &r_parent);
+ GetWindowRect(GetDlgItem(wnd, id), &r_child);
+ int dx = r_parent.left;
+ int dy = r_parent.top;
+ if (!(GetWindowLong(wnd, GWL_STYLE)&WS_CHILD))
+ {
+ dy += GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYDLGFRAME);
+ dx += GetSystemMetrics(SM_CXDLGFRAME);
+ }
+ child.left = r_child.left - dx;
+ child.right = r_child.right - dx;
+ child.top = r_child.top - dy;
+ child.bottom = r_child.bottom - dy;
+}
+
+void DlgBase::do_sizing(UINT wp, RECT * r)
+ {
+ UINT dx, dy;
+ dx = r->right - r->left;
+ dy = r->bottom - r->top;
+ if (dx < min_size_x_w)
+ {
+ switch (wp)
+ {
+ case WMSZ_BOTTOMLEFT:
+ case WMSZ_LEFT:
+ case WMSZ_TOPLEFT:
+ r->left = r->right - min_size_x_w;
+ break;
+ case WMSZ_BOTTOMRIGHT:
+ case WMSZ_RIGHT:
+ case WMSZ_TOPRIGHT:
+ r->right = r->left + min_size_x_w;
+ break;
+ }
+ }
+ if (dy < min_size_y_w)
+ {
+ switch (wp)
+ {
+ case WMSZ_BOTTOMLEFT:
+ case WMSZ_BOTTOM:
+ case WMSZ_BOTTOMRIGHT:
+ r->bottom = r->top + min_size_y_w;
+ break;
+ case WMSZ_TOPLEFT:
+ case WMSZ_TOP:
+ case WMSZ_TOPRIGHT:
+ r->top = r->bottom - min_size_y_w;
+ break;
+ }
+ }
+ } \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/DlgBase.h b/Src/Plugins/Input/in_vorbis/DlgBase.h
new file mode 100644
index 00000000..a194e836
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/DlgBase.h
@@ -0,0 +1,118 @@
+#include <windows.h>
+#include "main.h"
+
+static void SetWindowRect(HWND w, RECT * r)
+{
+ SetWindowPos(w, 0, r->left, r->top, r->right - r->left, r->bottom - r->top, SWP_NOZORDER | SWP_NOCOPYBITS);
+}
+
+class DlgBase
+{
+ public:
+ BOOL isDialogMessage(MSG * m)
+ {
+ return wnd ? IsDialogMessage(wnd, m) : 0;
+ }
+protected:
+ void endDialog(int x)
+ {
+ EndDialog(wnd, x);
+ }
+
+ void _do_size_x(RECT * r, UINT id, UINT wx, UINT min_x)
+ {
+ RECT r1 = {r->left, r->top, wx - min_x + r->right, r->bottom};
+ SetWindowRect(GetDlgItem(wnd, id), &r1);
+ }
+
+ void _do_size_xy(RECT * r, UINT id, UINT wx, UINT wy, UINT min_x, UINT min_y)
+ {
+ RECT r1 = {r->left, r->top, wx - min_x + r->right, wy - min_y + r->bottom};
+ SetWindowRect(GetDlgItem(wnd, id), &r1);
+ }
+
+ void _do_align_x_size_y(RECT * r, UINT id, UINT wx, UINT wy, UINT min_x, UINT min_y)
+ {
+ RECT r1 = {wx - min_x + r->left, r->top, wx - min_x + r->right, wy - min_y + r->bottom};
+ SetWindowRect(GetDlgItem(wnd, id), &r1);
+ }
+
+ void _do_align_x(RECT * r, UINT id, UINT wx, UINT min_x)
+ {
+ RECT r1 = {wx - min_x + r->left, r->top, wx - min_x + r->right, r->bottom};
+ SetWindowRect(GetDlgItem(wnd, id), &r1);
+ }
+
+ void _do_align_xy(RECT * r, UINT id, UINT wx, UINT wy, UINT min_x, UINT min_y)
+ {
+ RECT r1 = {wx - min_x + r->left, wy - min_y + r->top, wx - min_x + r->right, wy - min_y + r->bottom};
+ SetWindowRect(GetDlgItem(wnd, id), &r1);
+ }
+
+#define do_size_x(id,r) _do_size_x(r,id,sx,min_size_x)
+#define do_size_xy(id,r) _do_size_xy(r,id,sx,sy,min_size_x,min_size_y)
+#define do_align_x_size_y(id,r) _do_align_x_size_y(r,id,sx,sy,min_size_x,min_size_y)
+#define do_align_xy(id,r) _do_align_xy(r,id,sx,sy,min_size_x,min_size_y)
+#define do_align_x(id,r) _do_align_x(r,id,sx,min_size_x)
+
+ HWND wnd;
+ UINT min_size_x, min_size_y;
+ UINT min_size_x_w, min_size_y_w;
+
+ void do_sizing(UINT wp, RECT * r);
+ void MakeComboEdit(UINT id, DWORD s);
+ void GetChildRect(UINT id, RECT& child);
+
+ virtual BOOL DlgProc(UINT msg, WPARAM wp, LPARAM lp) { return 0;};
+ static BOOL CALLBACK TheDialogProc(HWND wnd, UINT msg, WPARAM wp, LPARAM lp)
+ {
+ DlgBase * p;
+ if (msg == WM_INITDIALOG)
+ {
+ p = (DlgBase*)lp;
+ SetWindowLong(wnd, DWL_USER, lp);
+ p->wnd = wnd;
+ RECT r;
+ GetClientRect(wnd, &r);
+ p->min_size_x = r.right;
+ p->min_size_y = r.bottom;
+ GetWindowRect(wnd, &r);
+ p->min_size_x_w = r.right - r.left;
+ p->min_size_y_w = r.bottom - r.top;
+ }
+ else p = (DlgBase*)GetWindowLong(wnd, DWL_USER);
+ BOOL rv = 0;
+ if (p)
+ {
+ rv = p->DlgProc(msg, wp, lp);
+ if (msg == WM_DESTROY)
+ {
+ p->wnd = 0;
+ SetWindowLong(wnd, DWL_USER, 0);
+ }
+ }
+ return rv;
+ }
+ HWND myCreateDialog(UINT id, HWND parent)
+ {
+ return CreateDialogParamT(hIns, (char*)id, parent, TheDialogProc, (long)this);
+ }
+ virtual void myProcessMessage(MSG * msg)
+ {
+ if (!IsDialogMessage(wnd, msg))
+ {
+ TranslateMessage(msg);
+ DispatchMessage(msg);
+ }
+ }
+
+ int myDialogBox(UINT id, HWND parent)
+ {
+ return DialogBoxParamT(hIns, (char*)id, parent, TheDialogProc, (long)this);
+ }
+ DlgBase() {
+ wnd = 0;
+ min_size_x = min_size_y = min_size_x_w = min_size_y_w = 0;
+ }
+ virtual ~DlgBase() {}
+};
diff --git a/Src/Plugins/Input/in_vorbis/ExtendedRead.cpp b/Src/Plugins/Input/in_vorbis/ExtendedRead.cpp
new file mode 100644
index 00000000..c89f829d
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/ExtendedRead.cpp
@@ -0,0 +1,65 @@
+#include "main.h"
+#include "decoder.h"
+
+extern "C"
+{
+ //returns handle!=0 if successful, 0 if error
+ //size will return the final nb of bytes written to the output, -1 if unknown
+ __declspec( dllexport ) intptr_t winampGetExtendedRead_openW(const wchar_t *fn, int *size, int *bps, int *nch, int *srate) {
+ VorbisFile * f = VorbisFile::Create(fn,false);
+ if(f) {
+ if(!*bps) *bps=16; // FUCKO HAX
+ Decoder * d = new Decoder();
+ d->Init(f, *bps, *nch, false, false);
+ *nch = (int)d->nch;
+ *srate = (int)d->sr;
+ *bps = (int)d->bps;
+ *size = (int)(f->Length() * (double)((*nch) * (*srate) * (*bps/8)));
+ return (intptr_t)d;
+ }
+ return 0;
+ }
+
+ __declspec( dllexport ) intptr_t winampGetExtendedRead_openW_float(const wchar_t *fn, int *size, int *bps, int *nch, int *srate) {
+ VorbisFile * f = VorbisFile::Create(fn,false);
+ if(f) {
+ Decoder * d = new Decoder();
+ d->Init(f, *bps, *nch, true, false);
+ *nch = (int)d->nch;
+ *srate = (int)d->sr;
+ *bps = (int)d->bps;
+ *size = (int)(f->Length() * (double)((*nch) * (*srate) * (*bps/8)));
+ return (intptr_t)d;
+ }
+ return 0;
+ }
+
+ //returns nb of bytes read. -1 if read error (like CD ejected). if (ret<len), EOF is assumed
+ __declspec( dllexport ) intptr_t winampGetExtendedRead_getData(intptr_t handle, char *dest, size_t len, int *killswitch) {
+ Decoder * d = (Decoder *)handle;
+ size_t used = 0;
+ for(;;) {
+ used += (UINT)d->Read((UINT)(len - used),dest + used);
+ if(used >= len) break;
+ if(!d->DoFrame()) break;
+ if(*killswitch) break;
+ if (used)
+ return used;
+ }
+ return used;
+ }
+
+ // return nonzero on success, zero on failure.
+ __declspec( dllexport ) int winampGetExtendedRead_setTime(intptr_t handle, int millisecs) {
+ Decoder * d = (Decoder *)handle;
+ d->Flush();
+ return !d->Seek(((double)millisecs) / 1000.0);
+ }
+
+ __declspec( dllexport ) void winampGetExtendedRead_close(intptr_t handle) {
+ Decoder * d = (Decoder *)handle;
+ d->Flush();
+ delete d->file;
+ delete d;
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/about.cpp b/Src/Plugins/Input/in_vorbis/about.cpp
new file mode 100644
index 00000000..32409519
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/about.cpp
@@ -0,0 +1,264 @@
+#include "main.h"
+#include "api__in_vorbis.h"
+#include "resource.h"
+#include <strsafe.h>
+
+/*static UINT xiphframes_ids[12]={IDB_BITMAP1,IDB_BITMAP2,IDB_BITMAP3,IDB_BITMAP4,IDB_BITMAP5,IDB_BITMAP6,IDB_BITMAP7,IDB_BITMAP8,IDB_BITMAP9,IDB_BITMAP10,IDB_BITMAP11,IDB_BITMAP12};
+static HBITMAP xiphframes[12];*/
+
+static UINT xiphframes_ids[12]={IDB_PNG1,IDB_PNG2,IDB_PNG3,IDB_PNG4,IDB_PNG5,IDB_PNG6,IDB_PNG7,IDB_PNG8,IDB_PNG9,IDB_PNG10,IDB_PNG11,IDB_PNG12};
+static ARGB32 *xiphframes[12] = {0};
+static HBITMAP xiphframesBmp[12] = {0};
+
+static void slap(HWND wnd,int v)
+{
+ long hi=GetWindowLong(wnd,4);
+ if (v) hi+=v*1000;
+ else hi=0;
+ SetWindowLong(wnd,4,hi);
+}
+
+static CfgInt cfg_rpm("rpm",0);
+
+static int visible_rpm,visible_max_rpm;
+static char show_rpm=0;
+static DWORD last_visible_rpm;
+
+ARGB32 * loadImg(const void * data, int len, int *w, int *h, bool ldata=false)
+{
+ FOURCC imgload = svc_imageLoader::getServiceType();
+ int n = (int)mod.service->service_getNumServices(imgload);
+ for(int i=0; i<n; i++)
+ {
+ waServiceFactory *sf = mod.service->service_enumService(imgload,i);
+ if(sf)
+ {
+ svc_imageLoader * l = (svc_imageLoader*)sf->getInterface();
+ if(l)
+ {
+ if(l->testData(data,len))
+ {
+ ARGB32* ret;
+ if(ldata) ret = l->loadImageData(data,len,w,h);
+ else ret = l->loadImage(data,len,w,h);
+ sf->releaseInterface(l);
+ return ret;
+ }
+ sf->releaseInterface(l);
+ }
+ }
+ }
+ return NULL;
+}
+
+ARGB32 * loadRrc(int id, wchar_t * sec, int *w, int *h, bool data=false)
+{
+ DWORD size=0;
+ HGLOBAL resourceHandle = WASABI_API_LOADRESFROMFILEW(sec, MAKEINTRESOURCEW(id), &size);
+ if(resourceHandle)
+ {
+ ARGB32* ret = loadImg(resourceHandle,size,w,h,data);
+ UnlockResource(resourceHandle);
+ return ret;
+ }
+ return NULL;
+}
+
+static LRESULT WINAPI XiphProc(HWND wnd,UINT msg,WPARAM wp,LPARAM lp)
+{
+ switch(msg)
+ {
+ case WM_CREATE:
+ SetWindowLong(wnd,8,last_visible_rpm=GetTickCount());
+ SetTimer(wnd,666,10,0);
+ visible_rpm=-1;
+ visible_max_rpm=-1;
+ show_rpm=0;
+ break;
+ case WM_TIMER:
+ if (wp==666)
+ {
+ long low=GetWindowLong(wnd,0);
+ long hi=GetWindowLong(wnd,4);
+
+ long org=low&~0xFFFF;
+
+ int rpm=MulDiv(abs(hi),1000*60,12*0x10000);
+
+ DWORD t=GetTickCount();
+ DWORD ot=(DWORD)SetWindowLong(wnd,8,t);
+ bool redraw=0;
+
+ if (rpm>25) show_rpm=1;
+ if (cfg_rpm<rpm) cfg_rpm=rpm;
+
+ if (show_rpm && (t&~0x3F)!=(ot&~0x3F))
+ {
+ wchar_t foo[128] = {0};
+ if (visible_rpm<rpm || (visible_rpm>rpm && (t-last_visible_rpm)>333))
+ {
+ last_visible_rpm=t;
+ visible_rpm=rpm;
+ StringCchPrintfW(foo,128,WASABI_API_LNGSTRINGW(IDS_GAME_SPEED),rpm);
+ SetDlgItemTextW(GetParent(wnd),IDC_RPM,foo);
+ }
+ if (visible_max_rpm!=cfg_rpm)
+ {
+ visible_max_rpm=cfg_rpm;
+ StringCchPrintfW(foo,128,WASABI_API_LNGSTRINGW(IDS_BEST_RPM),(int)cfg_rpm);
+ SetDlgItemTextW(GetParent(wnd),IDC_RPM2,foo);
+ }
+ }
+
+ low+=hi*(t-ot);
+ while(low<0) low+=12*0x10000;
+ while(low>=12*0x10000) low-=12*0x10000;
+
+ {
+ int z=hi>>6;
+ if (z) hi-=z;
+ else if (hi>0) hi--;
+ else if (hi<0) hi++;
+ }
+
+ SetWindowLong(wnd,0,low);
+ SetWindowLong(wnd,4,hi);
+ if (redraw || (low&~0xFFFF)!=org)
+ {
+ RedrawWindow(wnd,0,0,RDW_INVALIDATE);
+ }
+ KillTimer(wnd,666);
+ SetTimer(wnd,666,10,0);
+ }
+ break;
+ case WM_LBUTTONDOWN:
+ slap(wnd,-1);
+ break;
+ case WM_RBUTTONDOWN:
+ slap(wnd,1);
+ break;
+ case WM_MBUTTONDOWN:
+ slap(wnd,0);
+ break;
+ case WM_PAINT:
+ {
+ int i=(GetWindowLong(wnd,0))>>16;
+ HDC dc = CreateCompatibleDC(0);
+
+ if (!xiphframesBmp[i])
+ {
+ int cur_w = 0, cur_h = 0;
+ xiphframes[i] = loadRrc(xiphframes_ids[i], L"PNG", &cur_w, &cur_h, true);
+
+ BITMAPINFO bmi = {0};
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = cur_w;
+ bmi.bmiHeader.biHeight = -cur_h;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ void *bits = 0;
+ if(xiphframesBmp[i]) DeleteObject(xiphframesBmp[i]);
+ xiphframesBmp[i] = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
+ memcpy(bits, xiphframes[i], cur_w * cur_h * 4);
+ }
+
+ if (xiphframesBmp[i])
+ {
+ HGDIOBJ foo = SelectObject(dc, xiphframesBmp[i]);
+ HDC wdc = GetDC(wnd);
+ RECT r = {0};
+ GetClientRect(wnd, &r);
+ FillRect(wdc, &r, GetSysColorBrush(COLOR_3DFACE));
+
+ BLENDFUNCTION blendFn = {0};
+ blendFn.BlendOp = AC_SRC_OVER;
+ blendFn.SourceConstantAlpha = 255;
+ blendFn.AlphaFormat = AC_SRC_ALPHA;
+ AlphaBlend(wdc, 2, 2, r.right - 2, r.bottom - 2, dc, 0, 0, 63, 63, blendFn);
+
+ ReleaseDC(wnd, wdc);
+ SelectObject(dc, foo);
+ }
+ DeleteDC(dc);
+ }
+ break;
+ case WM_DESTROY:
+ {
+ for (int i = 0; i < ARRAYSIZE(xiphframes_ids); i++)
+ {
+ if(xiphframesBmp[i]) DeleteObject(xiphframesBmp[i]); xiphframesBmp[i] = 0;
+ if(xiphframes[i] && WASABI_API_MEMMGR) WASABI_API_MEMMGR->sysFree((void *)xiphframes[i]); xiphframes[i] = 0;
+ }
+ KillTimer(wnd,666);
+ break;
+ }
+ };
+ return DefWindowProc(wnd,msg,wp,lp);
+}
+
+static BOOL CALLBACK AboutProc(HWND wnd,UINT msg,WPARAM wp,LPARAM lp)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ wchar_t tmp[1024] = {0}, tmp2[1024] = {0}, *t1 = tmp, *t2 = tmp2, text[1024] = {0};
+ SetWindowTextW(wnd,WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_VORBIS_DECODER_OLD,text,1024));
+ StringCchPrintfW(tmp,1024,WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),mod.description,__DATE__);
+ // 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)
+ while(t1 && *t1 && (t2 - tmp2 < 1024))
+ {
+ if(*t1 == L'\n')
+ {
+ *t2 = L'\r';
+ t2 = CharNextW(t2);
+ }
+ *t2 = *t1;
+ t1 = CharNextW(t1);
+ t2 = CharNextW(t2);
+ }
+
+ SetDlgItemTextW(wnd,IDC_ABOUT_TEXT,tmp2);
+ // fixes the incorrect selection of the text on dialog opening
+ PostMessage(GetDlgItem(wnd,IDC_ABOUT_TEXT),EM_SETSEL,-1,0);
+ return 1;
+ }
+ case WM_COMMAND:
+ if (wp==IDOK || wp==IDCANCEL)
+ {
+ do_cfg(1);
+ EndDialog(wnd,0);
+ }
+ break;
+ }
+ return 0;
+}
+
+void About(HWND hwndParent)
+{
+ static char got_xiph;
+ if (!got_xiph)
+ {
+ WNDCLASS wc=
+ {
+ 0,
+ XiphProc,
+ 0,
+ 12,
+ WASABI_API_LNG_HINST,
+ 0,
+ LoadCursor(0,IDC_ARROW),
+ 0,
+ 0,
+ L"XIPH_CLASS",
+ };
+
+ RegisterClassW(&wc);
+ got_xiph=1;
+ }
+
+ WASABI_API_DIALOGBOXW(IDD_ABOUT,hwndParent,AboutProc);
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/api__in_vorbis.h b/Src/Plugins/Input/in_vorbis/api__in_vorbis.h
new file mode 100644
index 00000000..5f89683e
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/api__in_vorbis.h
@@ -0,0 +1,21 @@
+#ifndef NULLSOFT_API_H
+#define NULLSOFT_API_H
+
+#include "../Agave/Config/api_config.h"
+#include "../Agave/Language/api_language.h"
+
+#include <api/application/api_application.h>
+extern api_application *applicationApi;
+#define WASABI_API_APP applicationApi
+
+#include <api/service/svcs/svc_imgload.h>
+
+#include <api/service/api_service.h>
+
+#include <api/service/waServiceFactory.h>
+
+#include <api/memmgr/api_memmgr.h>
+extern api_memmgr *memmgrApi;
+#define WASABI_API_MEMMGR memmgrApi
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/c_string.cpp b/Src/Plugins/Input/in_vorbis/c_string.cpp
new file mode 100644
index 00000000..4310a0dd
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/c_string.cpp
@@ -0,0 +1,96 @@
+#define STRICT
+#include <windows.h>
+#include <malloc.h>
+#include <stdio.h>
+#include "c_string.h"
+#include "../nu/ns_wc.h"
+
+extern BOOL is_nt;
+
+template<class myChar>
+void string_base<myChar>::makespace(UINT s)
+{
+ if (size<s)
+ {
+ int oldSize = size;
+ do size<<=1; while(size<s);
+ myChar *newPtr = (myChar*)realloc(ptr,size*sizeof(myChar));
+ if (!newPtr)
+ {
+ newPtr = (myChar*)malloc(size*sizeof(myChar));
+ if (newPtr)
+ {
+ memcpy(newPtr, ptr, oldSize*sizeof(myChar));
+ free(ptr);
+ ptr = newPtr;
+ }
+ else return ;
+ }
+ else ptr = newPtr;
+ }
+}
+
+void String::s_GetWindowText(HWND w)
+{
+ Reset();
+ int len=GetWindowTextLengthA(w)+1;
+ GetWindowTextA(w,StringTempA(*this,len),len);
+}
+
+void StringW::s_GetWindowText(HWND w)
+{
+ Reset();
+ int len=GetWindowTextLengthW(w)+1;
+ GetWindowTextW(w,StringTempW(*this,len),len);
+}
+
+void String::SetStringW(const WCHAR * c)
+{
+ UINT len=(lstrlenW(c)+1)*2;
+ WideCharToMultiByteSZ(CP_ACP,0,c,-1,StringTempA(*this,len),len,0,0);
+}
+
+void StringW::SetStringA(const char * c)
+{
+ UINT len=(UINT)strlen(c)+1;
+ MultiByteToWideCharSZ(CP_ACP,0,c,-1,StringTempW(*this,len),len);
+}
+
+void String::AddStringW(const WCHAR * c)
+{
+ AddString(String(c));
+}
+
+void StringW::AddStringA(const char * c)
+{
+ AddString(StringW(c));
+}
+
+void String::s_SetWindowText(HWND w)
+{
+ SetWindowTextA(w,*this);
+}
+
+void StringW::s_SetWindowText(HWND w)
+{
+ SetWindowTextW(w,*this);
+}
+
+
+StringPrintf::StringPrintf(const char * fmt,...)
+{
+ va_list list;
+ va_start(list,fmt);
+ vsprintf(StringTempA(*this,1024),fmt,list);
+ va_end(list);
+}
+
+StringPrintfW::StringPrintfW(const WCHAR * fmt,...)
+{
+ va_list list;
+ va_start(list,fmt);
+ vswprintf(StringTempW(*this,1024),1024,fmt,list);
+ va_end(list);
+}
+
+String::String(const StringW & z) {AddStringW(z);} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/c_string.h b/Src/Plugins/Input/in_vorbis/c_string.h
new file mode 100644
index 00000000..9773c078
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/c_string.h
@@ -0,0 +1,156 @@
+#pragma once
+template <class myChar>
+class string_base
+{
+private:
+ myChar * ptr;
+ UINT size,used;
+ void makespace(UINT s);
+ static UINT mylen(const myChar * p) {UINT r=0;while(p[r]) r++;return r;}
+public:
+ void AddChar(myChar c)
+ {
+ makespace(used+2);
+ ptr[used++]=c;
+ ptr[used]=0;
+ }
+ string_base()
+ {
+ used=0;
+ size=128;
+ ptr=(myChar*)malloc(size*sizeof(myChar));
+ ptr[0]=0;
+ }
+
+ ~string_base() { if (ptr) free(ptr);}
+
+ operator const myChar*() const {return ptr;}
+
+ const myChar & operator*() const {return *ptr;}
+
+ UINT Length() const {return used;}
+
+ void AddString(const myChar * c)
+ {
+ UINT d=mylen(c);
+ makespace(used+d+1);
+ memcpy(ptr+used,c,sizeof(myChar)*d);
+ used+=d;
+ ptr[used]=0;
+ }
+
+ void Reset() {Truncate(0);}
+ void Truncate(UINT x) {if (used>x) {used=x;ptr[x]=0;}}
+
+ void SetString(const myChar * s) {Reset();AddString(s);}
+
+ myChar * BufferStart(UINT n)
+ {
+ makespace(n+1);
+ memset(ptr,0,size);
+ return ptr;
+ }
+
+ inline void BufferDone() {used=mylen(ptr);}
+
+ void SetChar(UINT offset,myChar c)//hack for some ghey routines
+ {
+ if (!c) Truncate(offset);
+ else if (offset<used) ptr[offset]=c;
+ }
+};
+
+template<class myChar>
+class StringTemp
+{
+private:
+ string_base<myChar> * parent;
+ myChar * data;
+public:
+ StringTemp(string_base<myChar> & s,UINT siz) {parent=&s;data=s.BufferStart(siz);}
+ ~StringTemp() {parent->BufferDone();}
+ operator myChar* () {return data;}
+};
+
+#define StringTempW StringTemp<WCHAR>
+#define StringTempA StringTemp<char>
+
+class StringW;
+
+class String : public string_base<char>
+{
+public:
+ String() {}
+ String(HWND w) {s_GetWindowText(w);}
+ String(const char* z) {SetString(z);}
+ String(const WCHAR* z) {SetStringW(z);}
+ String(const String& z) {SetString(z);}
+ String(const StringW& z);
+ void AddStringW(const WCHAR * c);
+ void SetStringW(const WCHAR * c);
+ void s_GetWindowText(HWND w);
+ void s_SetWindowText(HWND w);
+ void operator=(const char * s) {SetString(s);}
+ void operator+=(const char * s) {AddString(s);}
+ void operator=(String & s) {SetString(s);}
+ void operator+=(String & s) {AddString(s);}
+ inline void s_GetDlgItemText(HWND w,UINT id) {s_GetWindowText(GetDlgItem(w,id));}
+ inline void s_SetDlgItemText(HWND w,UINT id) {s_SetWindowText(GetDlgItem(w,id));}
+};
+
+class StringW : public string_base<WCHAR>
+{
+public:
+ StringW() {}
+ StringW(HWND w) {s_GetWindowText(w);}
+ StringW(const WCHAR * z) {SetString(z);}
+ void AddStringA(const char * c);
+ void SetStringA(const char * c);
+ StringW(const char * z) {SetStringA(z);}
+ StringW(const StringW & z) {SetString(z);}
+ StringW(const String & z) {SetStringA(z);}
+ void s_GetWindowText(HWND w);
+ void s_SetWindowText(HWND w);
+ void operator=(const WCHAR * s) {SetString(s);}
+ void operator+=(const WCHAR * s) { if (s) AddString(s);}
+ void operator=(StringW & s) {SetString(s);}
+ void operator+=(StringW & s) {AddString(s);}
+ inline void s_GetDlgItemText(HWND w,UINT id) {s_GetWindowText(GetDlgItem(w,id));}
+ inline void s_SetDlgItemText(HWND w,UINT id) {s_SetWindowText(GetDlgItem(w,id));}
+ bool reg_read(const char *name);
+ void reg_write(const char *name);
+};
+
+
+class StringPrintf : public String
+{
+public:
+ StringPrintf(const char * fmt,...);
+};
+
+class StringPrintfW : public StringW
+{
+public:
+ StringPrintfW(const WCHAR * fmt,...);
+};
+
+template<class myChar>
+class StringF2T : public string_base<myChar>
+{
+public:
+ StringF2T(const myChar * fn)
+ {
+ const myChar * ptr=fn,*dot=0,*src=fn;
+ while(ptr && *ptr)
+ {
+ if (*ptr=='\\' || *ptr=='/' || *ptr==':') src=ptr+1;
+ else if (*ptr=='.') dot=ptr;
+ ptr++;
+ }
+
+ while(src && *src && (!dot || src<dot)) AddChar(*(src++));
+ }
+};
+
+#define StringF2T_A StringF2T<char>
+#define StringF2T_W StringF2T<WCHAR> \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/chainedstream_parse.cpp b/Src/Plugins/Input/in_vorbis/chainedstream_parse.cpp
new file mode 100644
index 00000000..35ed0198
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/chainedstream_parse.cpp
@@ -0,0 +1,82 @@
+#include <windows.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <ogg/ogg.h>
+#include <vorbis/vorbisfile.h>
+
+static size_t callback_fread(void *ptr, size_t size, size_t nmemb, HANDLE hFile)
+{
+ DWORD bw = 0;
+ ReadFile(hFile,ptr,(DWORD)(size*nmemb),&bw,0);
+ return bw/size;
+}
+
+static size_t callback_write(void * ptr, size_t size, size_t nmemb, HANDLE hFile)
+{
+ DWORD bw = 0;
+ WriteFile(hFile,ptr,(DWORD)(size*nmemb),&bw,0);
+ return bw/size;
+}
+
+static int callback_fseek(HANDLE hFile, __int64 offset, int whence)
+{
+ __int64 temp = offset;
+ SetFilePointer(hFile,*(DWORD*)&temp,((long*)&temp+1),whence);
+ return 0;
+}
+
+static int callback_fclose(HANDLE f)
+{
+ return 0;
+}
+
+static __int64 callback_ftell(HANDLE hFile)
+{
+ __int64 ret=0;
+ *(DWORD*)&ret = SetFilePointer(hFile,0,((long*)&ret+1),FILE_CURRENT);
+ return ret;
+}
+
+static void* callbacks[4]=
+{
+ callback_fread,callback_fseek,callback_fclose,callback_ftell
+};
+
+namespace ogg_helper
+{
+ int num_get_tracks(HANDLE hFile/*track_indexer::callback * out,reader * r*/)
+ {
+ SetFilePointer(hFile,0,0,FILE_BEGIN);
+ OggVorbis_File l_vf;
+ memset(&l_vf,0,sizeof(l_vf));
+ if (ov_open_callbacks(hFile,&l_vf,0,0,*(ov_callbacks*)callbacks))
+ {
+ return 0;
+ }
+ int rv = l_vf.links;
+ ov_clear(&l_vf);
+ return rv;
+ }
+
+ int query_chained_stream_offset(HANDLE hFile,int idx,__int64 * out_beginning,__int64 * out_end)
+ {
+ SetFilePointer(hFile,0,0,FILE_BEGIN);
+ OggVorbis_File l_vf;
+ memset(&l_vf,0,sizeof(l_vf));
+ if (ov_open_callbacks(hFile,&l_vf,0,0,*(ov_callbacks*)callbacks))
+ {
+ return 0;
+ }
+ int retval = 0;
+ if (idx>=0 && idx<l_vf.links)
+ {
+ retval = 1;
+ *out_beginning = l_vf.offsets[idx];
+ *out_end = l_vf.offsets[idx+1];
+ }
+
+ ov_clear(&l_vf);
+ return retval;
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/config.cpp b/Src/Plugins/Input/in_vorbis/config.cpp
new file mode 100644
index 00000000..fc3d6afd
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/config.cpp
@@ -0,0 +1,335 @@
+#include "main.h"
+#include "api__in_vorbis.h"
+#include "../nu/ns_wc.h"
+#include <commctrl.h>
+#include <shlobj.h>
+#include "../winamp/wa_ipc.h"
+#include "../nu/AutoChar.h"
+#include <strsafe.h>
+
+int mc6_dm_names_ids[]={IDS_LEAVE_AS_IS,IDS_REMAP_6_CHANNELS,IDS_DOWNMIX_TO_4_CHANNELS,IDS_DOWNMIX_TO_2_CHANNELS_DS,IDS_DOWNMIX_TO_2_CHANNELS_DS2,IDS_DOWNMIX_TO_MONO};
+int mc6_map_names_id[]={IDS_CORRECT_FL_FC_FR_BL_BR_LFE,IDS_BROKEN_FL_FR_FC_BL_BR_LFE};
+int32_t priority_tab[7]={THREAD_PRIORITY_IDLE,THREAD_PRIORITY_LOWEST,THREAD_PRIORITY_BELOW_NORMAL,THREAD_PRIORITY_NORMAL,THREAD_PRIORITY_ABOVE_NORMAL,THREAD_PRIORITY_HIGHEST,THREAD_PRIORITY_TIME_CRITICAL};
+
+char* defaultDumpDir()
+{
+ static char dumpdir[MAX_PATH] = {0};
+ if(FAILED(SHGetFolderPathA(NULL, CSIDL_MYMUSIC, NULL, SHGFP_TYPE_CURRENT, dumpdir)))
+ {
+ if(FAILED(SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, dumpdir)))
+ {
+ lstrcpynA(dumpdir, "C:\\", MAX_PATH);
+ }
+ }
+ return dumpdir;
+}
+
+CfgString
+ cfg_ssave_format("ssave_format","%filename%"),
+ cfg_dumpdir("dumpdir",defaultDumpDir());
+
+CfgInt
+ cfg_http_bsize("http_bsize",0x10000),
+ cfg_fsave("fsave",0),
+ cfg_abr("abr",0),
+ cfg_proxy_mode("proxy_mode",2),
+ cfg_prebuf1("prebuf1",50),
+ cfg_prebuf2("prebuf2",75),
+ cfg_httpseek2("httpseek2",0),
+ cfg_fix0r("fix0r",1),
+ cfg_mc6_dm("mc6_dm",0),
+ cfg_mc6_map("_mc6_map",0),
+ cfg_remember_infosize("remember_infosize",1),
+ cfg_fullbuf("fullbuf",0),
+ cfg_cur_tab("cur_tab",0);
+
+static int old_preamp;
+CfgFont cfg_font("font");
+static LOGFONT cfg_font_edit;
+
+BOOL CALLBACK browseEnumProc(HWND hwnd, LPARAM lParam)
+{
+ char cl[32] = {0};
+ GetClassNameA(hwnd, cl, ARRAYSIZE(cl));
+ if (!lstrcmpiA(cl, WC_TREEVIEWA))
+ {
+ PostMessage(hwnd, TVM_ENSUREVISIBLE, 0, (LPARAM)TreeView_GetSelection(hwnd));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int CALLBACK browzaproc(HWND hwnd, UINT msg, LPARAM lp, LPARAM dat)
+{
+ if (msg == BFFM_INITIALIZED)
+ {
+ SendMessageW(hwnd, BFFM_SETSELECTIONW, 1, (LPARAM)dat);
+
+ // this is not nice but it fixes the selection not working correctly on all OSes
+ EnumChildWindows(hwnd, browseEnumProc, 0);
+ }
+ return 0;
+}
+
+static void d_browza(HWND wnd,HWND bt,wchar_t* tx)
+{
+ IMalloc* pMalloc=0;
+
+ SHGetMalloc(&pMalloc);
+ if (!pMalloc) return;
+
+ wchar_t dir[MAX_PATH] = {0};
+ GetWindowTextW(bt,dir,MAX_PATH);
+ BROWSEINFOW bi=
+ {
+ wnd,
+ 0,
+ 0,
+ tx,
+ BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE,
+ browzaproc,
+ (LPARAM)dir,
+ 0
+ };
+ ITEMIDLIST* li=SHBrowseForFolderW(&bi);
+ if (li)
+ {
+ SHGetPathFromIDListW(li,dir);
+ SetWindowTextW(bt,dir);
+ pMalloc->Free(li);
+ }
+
+ pMalloc->Release();
+}
+
+static BOOL CALLBACK CfgProc1(HWND wnd,UINT msg,WPARAM wp,LPARAM lp)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ wchar_t temp[128] = {0}, cfg_dialog_name[128] = {0};
+ StringCchPrintfW(cfg_dialog_name,128,WASABI_API_LNGSTRINGW(IDS_TITLE_PREFERENCES),
+ WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_VORBIS_DECODER_OLD, temp, 128));
+ SetWindowTextW(wnd,cfg_dialog_name);
+
+ SendDlgItemMessage(wnd,IDC_FULLBUF,BM_SETCHECK,cfg_fullbuf,0);
+
+ UINT n;
+ HWND w=GetDlgItem(wnd,IDC_MC6_DM);
+ for(n=0;n<sizeof(mc6_dm_names_ids)/sizeof(mc6_dm_names_ids[0]);n++)
+ {
+ SendMessageW(w,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(mc6_dm_names_ids[n]));
+ }
+ SendMessage(w,CB_SETCURSEL,cfg_mc6_dm,0);
+
+ w=GetDlgItem(wnd,IDC_MC6_MAP);
+ for(n=0;n<sizeof(mc6_map_names_id)/sizeof(mc6_map_names_id[0]);n++)
+ {
+ SendMessageW(w,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(mc6_map_names_id[n]));
+ }
+ SendMessage(w,CB_SETCURSEL,cfg_mc6_map,0);
+
+ SendDlgItemMessage(wnd,IDC_AVG_BR,BM_SETCHECK,cfg_abr,0);
+
+ SetDlgItemInt(wnd,IDC_HTTP_BSIZE,cfg_http_bsize>>10,0);
+ if (cfg_fsave) SendDlgItemMessage(wnd,IDC_FSAVE,BM_SETCHECK,1,0);
+ if (cfg_fix0r) SendDlgItemMessage(wnd,IDC_FIX0R,BM_SETCHECK,1,0);
+ cfg_dumpdir.s_SetDlgItemText(wnd,IDC_STREAM_SAVE);
+ w=GetDlgItem(wnd,IDC_PROXY);
+ SendMessageW(w,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_NEVER));
+ SendMessageW(w,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_PORT_80_ONLY));
+ SendMessageW(w,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_ALWAYS));
+ SendMessage(w,CB_SETCURSEL,cfg_proxy_mode,0);
+
+ w=GetDlgItem(wnd,IDC_SLIDER1);
+ SendMessage(w,TBM_SETRANGE,0,MAKELONG(1,100));
+ SendMessage(w,TBM_SETPOS,1,cfg_prebuf1);
+ w=GetDlgItem(wnd,IDC_SLIDER2);
+ SendMessage(w,TBM_SETRANGE,0,MAKELONG(1,100));
+ SendMessage(w,TBM_SETPOS,1,cfg_prebuf2);
+
+ cfg_ssave_format.s_SetDlgItemText(wnd,IDC_SSAVE_FMT);
+ SendMessage(wnd,WM_COMMAND,MAKEWPARAM(IDC_FSAVE,BN_CLICKED),(LPARAM)GetDlgItem(wnd,IDC_FSAVE));
+ }
+ return 1;
+
+ case WM_COMMAND:
+ switch(LOWORD(wp))
+ {
+ case IDC_STREAM_SAVE:
+ d_browza(wnd,(HWND)lp,WASABI_API_LNGSTRINGW(IDS_SELECT_OUTPUT_DIRECTORY));
+ break;
+
+ case IDC_SSAVE_FMT_DEF:
+ SetDlgItemText(wnd,IDC_SSAVE_FMT,L"%filename%");
+ break;
+
+ case IDC_FSAVE:
+ {
+ int checked = IsDlgButtonChecked(wnd,IDC_FSAVE);
+ EnableWindow(GetDlgItem(wnd,IDC_STREAM_SAVE),checked);
+ EnableWindow(GetDlgItem(wnd,IDC_SSAVE_FMT),checked);
+ EnableWindow(GetDlgItem(wnd,IDC_SSAVE_FMT_DEF),checked);
+ }
+ break;
+
+ case IDOK:
+ case IDCANCEL:
+ {
+ if (LOWORD(wp) == IDOK)
+ {
+ cfg_fullbuf=(int)SendDlgItemMessage(wnd,IDC_FULLBUF,BM_GETCHECK,0,0);
+
+ cfg_mc6_dm=(int)SendDlgItemMessage(wnd,IDC_MC6_DM,CB_GETCURSEL,0,0);
+ cfg_mc6_map=(int)SendDlgItemMessage(wnd,IDC_MC6_MAP,CB_GETCURSEL,0,0);
+
+ cfg_abr=(int)SendDlgItemMessage(wnd,IDC_AVG_BR,BM_GETCHECK,0,0);
+
+ cfg_dumpdir.s_GetDlgItemText(wnd,IDC_STREAM_SAVE);
+ cfg_http_bsize=GetDlgItemInt(wnd,IDC_HTTP_BSIZE,0,0)<<10;
+ cfg_fsave=(int)SendDlgItemMessage(wnd,IDC_FSAVE,BM_GETCHECK,0,0);
+ cfg_fix0r=(int)SendDlgItemMessage(wnd,IDC_FIX0R,BM_GETCHECK,0,0);
+ cfg_proxy_mode=(int)SendDlgItemMessage(wnd,IDC_PROXY,CB_GETCURSEL,0,0);
+ cfg_prebuf1=(int)SendDlgItemMessage(wnd,IDC_SLIDER1,TBM_GETPOS,0,0);
+ cfg_prebuf2=(int)SendDlgItemMessage(wnd,IDC_SLIDER2,TBM_GETPOS,0,0);
+ cfg_ssave_format.s_GetDlgItemText(wnd,IDC_SSAVE_FMT);
+ }
+ do_cfg(1);
+ EndDialog(wnd,(LOWORD(wp) == IDOK));
+ }
+ break;
+ }
+ break;
+ }
+
+ const int controls[] =
+ {
+ IDC_SLIDER1,
+ IDC_SLIDER2,
+ };
+ if (FALSE != WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(wnd, msg, wp, lp, controls, ARRAYSIZE(controls)))
+ {
+ return TRUE;
+ }
+
+ return 0;
+}
+
+extern HANDLE hThread;//hack
+
+void Config(HWND p)
+{
+ if (WASABI_API_DIALOGBOXPARAMW(IDD_CONFIG,p,CfgProc1,0))
+ {
+ if (hThread) PostMessage(mod.hMainWindow,WM_USER,0,243);
+ }
+}
+
+int CfgVar::read_int(const char *inifile, const char *section,const char * name,int def)
+{
+ return GetPrivateProfileIntA(section, name, def, inifile);
+}
+
+void CfgVar::write_int(const char *inifile, const char *section, const char * name,int val)
+{
+ char temp[32] = {0};
+ StringCchPrintfA(temp, 32, "%d", val);
+ WritePrivateProfileStringA(section, name, temp, inifile);
+}
+
+void CfgVar::write_struct(const char *inifile, const char *section, const char * name, void * ptr,UINT size)
+{
+ WritePrivateProfileStructA("in_vorbis", name, ptr, size, INI_FILE);
+}
+
+bool CfgVar::read_struct(const char *inifile, const char *section, const char * name,void * ptr,UINT size)
+{
+ return !!GetPrivateProfileStructA("in_vorbis", name, ptr, size, INI_FILE);
+}
+
+void do_cfg(int s)
+{
+ #define CFG_VERSION 0x10204
+
+ if (!s)
+ {
+ if (CfgVar::read_int(INI_FILE, "in_vorbis", "version",0)==CFG_VERSION)
+ CfgVar::ReadConfig();
+ }
+ else
+ {
+ CfgVar::WriteConfig();
+ CfgVar::write_int(INI_FILE, "in_vorbis", "version",CFG_VERSION);
+ }
+}
+
+CfgVar * CfgVar::list=0;
+
+void CfgVar::ReadConfig()
+{
+ CfgVar * p=list;
+ while(p)
+ {
+ p->Read(p->name);
+ p=p->next;
+ }
+}
+
+void CfgVar::WriteConfig()
+{
+ CfgVar * p=list;
+ while(p)
+ {
+ p->Write(p->name);
+ p=p->next;
+ }
+}
+
+bool StringW::reg_read(const char * name)
+{
+ char utf8_data[2048] = {0};
+ wchar_t utf16_data[2048] = {0};
+ GetPrivateProfileStringA("in_vorbis", name, "@default@", utf8_data, 2048, INI_FILE);
+ if (!strcmp("@default@", utf8_data))
+ return false;
+
+ MultiByteToWideCharSZ(CP_UTF8, 0, utf8_data, -1, utf16_data, 2048);
+ SetString(utf16_data);
+
+ return true;
+}
+
+void StringW::reg_write(const char * name)
+{
+ WritePrivateProfileStringA("in_vorbis", name, AutoChar((const WCHAR *)*this, CP_UTF8), INI_FILE);
+}
+
+void CfgString::Read(const char * name)
+{
+ reg_read(name);
+}
+
+void CfgString::Write(const char * name)
+{
+ StringW temp;
+ if (temp.reg_read(name))
+ {
+ if (wcscmp(temp,*this)) reg_write(name);
+ }
+ else
+ {
+ if (wcscmp(def,*this)) reg_write(name);
+ }
+}
+
+void CfgInt::Write(const char * name)
+{
+ if (read_int(INI_FILE, "in_vorbis", name,def)!=value) write_int(INI_FILE, "in_vorbis",name,value);
+}
+
+void CfgInt::Read(const char * name)
+{
+ value=read_int(INI_FILE, "in_vorbis", name,def);
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/core_api.h b/Src/Plugins/Input/in_vorbis/core_api.h
new file mode 100644
index 00000000..07d48b5b
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/core_api.h
@@ -0,0 +1,567 @@
+
+/* Winamp 3 Player core api v0.1
+** (c)2000 nullsoft jcf/ct/dk
+** Notes:
+** Keep in mind that this header file is subject to change prior to the
+** release of Winamp 3. The ability to configure plug-ins has yet to be
+** added and is the first and foremost concern of the engineering team.
+*/
+
+#ifndef __CORE_API_H
+#define __CORE_API_H
+/*
+// Visual C 6 makes big unaligned dlls. the following will correct it
+#ifndef _DEBUG
+// release optimizations
+// /Og (global optimizations), /Os (favor small code), /Oy (no frame pointers)
+#pragma optimize("gsy",on)
+#pragma comment(linker,"/RELEASE")
+// set the 512-byte alignment
+#pragma comment(linker,"/opt:nowin98")
+#endif
+*/
+// Use Assert in your code to catch errors that shouldn't happen, when compiled in release mode, they are #defined out
+#ifndef ASSERT
+#ifdef _DEBUG
+#define ASSERT(x) if (!(x)) MessageBox(NULL,"ASSERT FAILED: " #x,"ASSERT FAILED in " __FILE__ ,MB_OK|MB_ICONSTOP);
+#else
+#define ASSERT(x)
+#endif
+#endif
+
+
+
+/* CLASS DESCRIPTIONS */
+
+/* WReader
+** File reader module class, ie. opens and reads files, streams
+*/
+class WReader;
+
+/* WInputInfo
+** Class that returns information (length, title, metadata) about a specified file
+*/
+class WInputInfo;
+
+/* WInfo_callback
+** Player's interface that provides Winamp 3 core functions to your WInputInfo classes
+*/
+class WInfo_callback;
+
+/* WInputSource
+** Input Source manager base class, ie. decodes mp3's, wavs
+*/
+class WInputSource;
+
+/* WOutputFilter
+** Abstract base class for any Output Filter plug-in, ie. changes pitch, converts format, outputs to speakers
+*/
+class WOutputFilter;
+
+/* WPlayer_callback
+** Player's interface that provides Winamp 3 core functions to your Input Sources and Output Filter plug-ins
+** (Getting a reader for opening a file, sending stuff about what's going on to the Winamp 3 core)
+*/
+class WPlayer_callback;
+
+
+
+
+class WPlayer_callback
+{
+ public:
+ /* GetReader
+ ** Allows your Input Source and Output Filter plugins to request a reader from Winamp,
+ ** so you don't have to worry about opening files or streams
+ */
+ virtual WReader *GetReader(char *url)=0;
+
+
+ /* The 3 following functions allows your Input Source and Output Filter plugins to send error/warning/status
+ ** messages back to the Winamp 3 core
+ */
+
+ /* Error
+ ** playback should stop (soundcard driver error, etc)
+ */
+ virtual void Error(char *reason)=0;
+
+ /* Warning
+ ** warning (something recoverable, like file not found, etc)
+ */
+ virtual void Warning(char *warning)=0;
+
+ /* Status
+ ** status update (nothing really wrong)
+ */
+ virtual void Status(char *status)=0;
+
+
+
+
+ /* TitleChange
+ ** should be called if the current file titlename changes during the decoding
+ */
+ virtual void TitleChange(char *new_title)=0;
+
+ /* InfoChange
+ ** should be called if the infos about the current file changes during the decoding
+ */
+ virtual void InfoChange(char *new_info_str, int new_length)=0;
+
+ /* UrlChange
+ ** should be called if the current file associated URL changes during the decoding
+ */
+ virtual void UrlChange(char *new_url)=0;
+};
+
+
+
+
+class WInfo_callback
+{
+ public:
+ /* GetReader
+ ** Allows your WInfo classes to request a reader from Winamp
+ ** so you don't have to worry about opening files or streams
+ */
+ virtual WReader *GetReader(char *url)=0;
+};
+
+
+
+
+class WInputInfo
+{
+ public:
+ /* WInputInfo
+ ** WInputInfo constructor
+ */
+ WInputInfo(){ };
+
+ /* m_info
+ ** Filled by Winamp. Pointer to WInputInfo callback function
+ */
+ WInfo_callback *m_info;
+
+ /* Open
+ ** Called by Winamp to request informations about a specified media (file, url, etc...)
+ ** returns 0 if succesful, 1 if not
+ **
+ ** You must open, get all information and close the specified file here and store
+ ** the useful information into member elements for quick access by other functions
+ */
+ virtual int Open(char *url) { return 1; }
+
+ /* GetTitle
+ ** Called by Winamp to get the decoded title about the file opened
+ ** i.e. id3 title name, etc...
+ */
+ virtual void GetTitle(char *buf, int maxlen) { if (maxlen>0) buf[0]=0; };
+
+ /* GetInfoString
+ ** Called by Winamp to get extra informations about the file opened
+ ** i.e. "160kbps stereo 44Khz" for MP3 files,"4 channels" for MOD files,etc...
+ */
+ virtual void GetInfoString(char *buf, int maxlen) { if (maxlen>0) buf[0]=0; };
+
+ /* GetLength
+ ** Called by Winamp to retrieves media type length in milliseconds
+ ** returns -1 if length is undefined/infinite
+ */
+ virtual int GetLength(void) { return -1; };
+
+ /* GetMetaData
+ ** Fetches metadata by attribute name (Artist, Album, Bitrate, etc...)
+ ** attribute names are non case-sensitive.
+ ** returns size of data
+ */
+ virtual int GetMetaData(char *name, char *data, int data_len) { if (data&&data_len>0) *data=0; return 0; }
+
+ /* ~WInputInfo
+ ** WInputInfo virtual destructor
+ */
+ //virtual ~WInputInfo() { };
+ virtual void Release(int)=0;
+};
+
+
+
+
+
+
+
+
+/* WINAMP Output Filter NOTIFY MESSAGES
+** Messages returned to notify Output Filter plug-ins of events
+*/
+
+typedef enum {
+
+ /* WOFNM_FILETITLECHANGE
+ ** Sent when the song changes
+ ** param1=new filename song
+ ** param2=new title song
+ */
+ WOFNM_FILETITLECHANGE=1024,
+
+ /* WOFNM_ENDOFDECODE
+ ** Sent when decoding ends
+ */
+ WOFNM_ENDOFDECODE,
+
+} WOutputFilterNotifyMsg;
+
+
+
+
+class WOutputFilter
+{
+ protected:
+ /* WOutputFilter
+ ** WOutputFilter constructor
+ */
+ WOutputFilter() { m_next=NULL; };
+
+ public:
+ /* m_player
+ ** Filled by Winamp. Pointer to Winamp 3 core player interface
+ */
+ WPlayer_callback *m_player;
+
+ /* m_next
+ ** Internally used by Winamp. Pointer to next activated Output Filter
+ */
+ WOutputFilter *m_next;
+
+ /* ~WOutputFilter
+ ** WOutputFilter destructor
+ */
+ //virtual ~WOutputFilter() { };
+ virtual void Release(int)=0;
+
+ /* GetDescription
+ ** Retrieves your plug-in's text description
+ */
+ virtual char *GetDescription() { return "Unknown"; };
+
+ /* ProcessSamples
+ ** Render data as it receives it
+ ** sampledata: Data to process
+ ** bytes: number of bytes to process
+ ** bps: Bits per sample (8 or 16)
+ ** nch: Number of channels (1 or 2)
+ ** srate: Sample rate in Hz
+ ** killswitch: Will be set to 1 by winamp if stop if requested. Poll the pointed value very often to
+ ** make sure Winamp doesn't hang
+ **
+ ** Returns the number of processed bytes or -1 if unable to open the device or an error occured.
+ **
+ ** You have to open your device (ie. Directsound) the first time this function is called.
+ */
+ virtual int ProcessSamples(char *sampledata, int bytes, int *bps, int *nch, int *srate, bool *killswitch) { return bytes; }
+
+ /* FlushSamples
+ ** Flushes output buffers so that all is written
+ */
+ virtual void FlushSamples(bool *killswitch) { };
+
+ /* Restart
+ ** Called by Winamp after a seek
+ */
+ virtual void Restart(void) { }
+
+ /* GetLatency
+ ** Returns < 0 for a final output latency, > 0 for an additive
+ */
+ virtual int GetLatency(void) { return 0; }
+
+ /* Pause
+ ** Suspends output
+ */
+ virtual void Pause(int pause) { }
+
+ /* ShutDown
+ ** Completely stops output
+ **
+ ** Close your device here (not in destructor)
+ */
+ virtual void ShutDown(void) { }
+
+ /* SetVolume
+ ** Sets the volume (0 to 255)
+ ** return 1 if volume successfully modified
+ */
+ virtual int SetVolume(int volume) { return 0; }
+
+ /* SetPan
+ ** Sets Left-Right sound balance (-127 to 127)
+ ** return 1 if pan successfully modified
+ */
+ virtual int SetPan(int pan) { return 0; }
+
+ /* Notify
+ ** Called by Winamp to notify what's going on
+ */
+ virtual void Notify(WOutputFilterNotifyMsg msg, int data1, int data2) { }
+
+};
+
+
+
+
+class WInputSource
+{
+
+ protected:
+ /* WInputSource
+ ** WInputSource constructor
+ */
+ WInputSource(){ };
+
+
+ public:
+ /* m_player
+ ** Filled by Winamp. Pointer to Winamp 3 core interface
+ */
+ WPlayer_callback *m_player;
+
+ /* GetDescription
+ ** Retrieves your plug-in's text description
+ */
+ virtual char *GetDescription() { return "Unknown"; };
+
+ /* UsesOutputFilters
+ ** Returns whether or not the Output Filter pipeline can be used
+ */
+ virtual int UsesOutputFilters(void) { return 1; }
+
+ /* Open
+ ** Used to open and prepare input media type
+ */
+ virtual int Open(char *url, bool *killswitch)=0;
+
+ /* GetSamples
+ ** This function must fill bps, nch and srate.
+ ** Here, you have to fill the sample_buffer with decoded data. Be sure to fill it with the specified
+ ** size (bytes). Use an internal buffer, etc ...
+ **
+ ** sample_buffer: buffer to put decoded data into
+ ** bytes: size of the sample_buffer
+ ** bps: Bits par sample (8 or 16)
+ ** nch: Number of channels (1 or 2)
+ ** srate: Sample rate in Hz
+ ** killswitch: Will be set to 1 by winamp if stop if requested. Poll the pointed value very often to
+ ** make sure Winamp doesn't hang
+ */
+ virtual int GetSamples(char *sample_buffer, int bytes, int *bps, int *nch, int *srate, bool *killswitch)=0;
+
+ /* SetVolume
+ ** Sets the volume (0 to 255)
+ ** Return 1 if volume has been set
+ */
+ virtual int SetVolume(int volume) { return 0; };
+
+ /* SetPan
+ ** Sets Left-Right sound balance (-127 to 127)
+ ** return 1 if pan successfully modified
+ */
+ virtual int SetPan(int pan) { return 0; };
+
+ /* SetPosition
+ ** Sets position in ms. returns 0 on success, 1 if seek impossible
+ */
+ virtual int SetPosition(int)=0;
+
+ /* Pause
+ ** Suspends input
+ */
+ virtual void Pause(int pause) { };
+
+ /* GetPosition
+ ** Retrieve position in milliseconds
+ */
+ virtual int GetPosition(void) { return 0; }
+
+ /* GetTitle
+ ** Called by Winamp to get the decoded title about the file opened
+ ** i.e. stream name, id3 title name, etc...
+ */
+ virtual void GetTitle(char *buf, int maxlen) { if(maxlen>0) buf[0]=0; };
+
+ /* GetInfoString
+ ** Called by Winamp to get extra informations about the file openend
+ ** i.e. "32kbps 44khz", etc...
+ */
+ virtual void GetInfoString(char *buf, int maxlen) { if(maxlen>0) buf[0]=0; };
+
+ /* GetLength
+ ** Called by Winamp to retrieves media type length in milliseconds
+ ** returns -1 if length is undefined/infinite
+ */
+ virtual int GetLength(void) { return -1; }
+
+ /* ~WInputSource
+ ** ~WInputSource virtual destructor
+ */
+ //virtual ~WInputSource() { };
+ virtual void Release(int)=0;
+};
+
+
+
+
+class WReader
+{
+ protected:
+
+ /* WReader
+ ** WReader constructor
+ */
+ WReader() { }
+
+ public:
+
+ /* m_player
+ ** Filled by Winamp. Pointer to Winamp 3 core interface
+ */
+ WPlayer_callback *m_player;
+
+ /* GetDescription
+ ** Retrieves your plug-in's text description
+ */
+ virtual char *GetDescription() { return "Unknown"; };
+
+ /* Open
+ ** Used to open a file, return 0 on success
+ */
+ virtual int Open(char *url, bool *killswitch)=0;
+
+ /* Read
+ ** Returns number of BYTES read (if < length then eof or killswitch)
+ */
+ virtual int Read(char *buffer, int length, bool *killswitch)=0;
+
+ /* GetLength
+ ** Returns length of the entire file in BYTES, return -1 on unknown/infinite (as for a stream)
+ */
+ virtual int GetLength(void)=0;
+
+ /* CanSeek
+ ** Returns 1 if you can skip ahead in the file, 0 if not
+ */
+ virtual int CanSeek(void)=0;
+
+ /* Seek
+ ** Jump to a certain absolute position
+ */
+ virtual int Seek(int position, bool *killswitch)=0;
+
+ /* GetHeader
+ ** Retrieve header. Used in read_http to retrieve the HTTP header
+ */
+ virtual char *GetHeader(char *name) { return 0; }
+
+ /* ~WReader
+ ** WReader virtual destructor
+ */
+ //virtual ~WReader() { }
+ virtual void Release(int)=0;
+};
+
+
+
+
+/* DLL PLUGINS EXPORT STRUCTURES */
+
+#define READ_VER 0x100
+#define IN_VER 0x100
+#define OF_VER 0x100
+
+
+typedef struct
+{
+ /* version
+ ** Version revision number
+ */
+ int version;
+
+ /* description
+ ** Text description of the reader plug-in
+ */
+ char *description;
+
+ /* create
+ ** Function pointer to create a reader module
+ */
+ WReader *(*create)();
+
+ /* ismine
+ ** Determines whether or not a file should be read by this plug-in
+ */
+ int (*ismine)(char *url);
+
+} reader_source;
+
+
+
+
+typedef struct
+{
+ /* version
+ ** Version revision number
+ */
+ int version;
+
+ /* description
+ ** Text description of the input plug-in
+ */
+ char *description;
+
+ /* extension_list
+ ** Defines all the supported filetypes by this input plug-in
+ ** In semicolon delimited format ("ext;desc;ext;desc" etc).
+ */
+ char *extension_list;
+
+ /* ismine
+ ** called before extension checks, to allow detection of tone://,http://, etc
+ ** Determines whether or not a file type should be decoded by this plug-in
+ */
+ int (*ismine)(char *filename);
+
+ /* create
+ ** Function pointer to create a decoder module
+ */
+ WInputSource *(*create)(void);
+
+ /* createinfo
+ ** Function pointer to create a decoder module information
+ */
+ WInputInfo *(*createinfo)(void);
+
+} input_source;
+
+
+
+typedef struct
+{
+ /* version
+ ** Version revision number
+ */
+ int version;
+
+ /* description
+ ** Text description of the output plug-in
+ */
+ char *description;
+
+ /* create
+ ** Function pointer to create an Output Filter
+ */
+ WOutputFilter *(*create)();
+
+} output_filter;
+
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/decoder.cpp b/Src/Plugins/Input/in_vorbis/decoder.cpp
new file mode 100644
index 00000000..54cd364e
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/decoder.cpp
@@ -0,0 +1,459 @@
+#include "main.h"
+#include "decoder.h"
+#include <math.h>
+#include <locale.h>
+#pragma warning(disable:4244)
+#include "shaper.h"
+#include "api__in_vorbis.h"
+
+Decoder::~Decoder() {if (shaper) delete shaper;}
+
+extern CfgInt
+ cfg_mc6_dm, cfg_mc6_map;
+/*
+if (vorbis_cfg.use_hq_preamp)
+{
+ sample *= pow(10., preamp_db/20);
+
+ //hard 6dB limiting
+ if (sample < -0.5)
+ sample = tanh((sample + 0.5) / (1-0.5)) * (1-0.5) - 0.5;
+ else if (sample > 0.5)
+ sample = tanh((sample - 0.5) / (1-0.5)) * (1-0.5) + 0.5;
+} */
+
+#if 0
+static float q_tanh(float x)
+{
+ double foo1, foo2;
+ foo1 = pow(2.71828182845904523536028747135266, x);
+ foo2 = 1.0 / foo1;
+ return (foo1 -foo2) / (foo1 + foo2);
+}
+#else
+#define q_tanh tanh
+#endif
+
+float VorbisFile::GetGain()
+{
+ float peak;
+
+ vorbis_comment * c;
+ float scale = 1.0f;
+ c = ov_comment(&vf, -1);
+ peak = 0.99f;
+ if (c)
+ {
+ if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))
+ {
+ char * _peak = 0, *_gain = 0;
+ float gain = 0;
+ bool have_rg = 0;
+ float lwing_gain = 0;
+ char *gain1 = 0, *gain2 = 0, *peak1 = 0, *peak2 = 0;
+ gain1 = vorbis_comment_query(c, "replaygain_album_gain", 0);
+ if (!gain1) gain1 = vorbis_comment_query(c, "rg_audiophile", 0);
+ gain2 = vorbis_comment_query(c, "replaygain_track_gain", 0);
+ if (!gain2) gain2 = vorbis_comment_query(c, "rg_radio", 0);
+
+ peak1 = vorbis_comment_query(c, "replaygain_album_peak", 0);
+ peak2 = vorbis_comment_query(c, "replaygain_track_peak", 0);
+ switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0))
+ {
+ case 0: // track
+ _gain = gain2;
+ if (!_gain && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
+ _gain = gain1;
+ _peak = peak2;
+ if (!_peak && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
+ _peak = peak1;
+ break;
+ case 1: // album
+ _gain = gain1;
+ if (!_gain && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
+ _gain = gain2;
+ _peak = peak1;
+ if (!_peak && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
+ _peak = peak2;
+ break;
+ }
+
+ if (!_peak)
+ {
+ _peak = vorbis_comment_query(c, "rg_peak", 0);
+ }
+
+ _locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
+
+ if (_peak) peak = _atof_l(_peak, C_locale);
+ if (_gain) gain = _atof_l(_gain, C_locale);
+
+ if (!_peak && !_gain)
+ {
+ char * l = vorbis_comment_query(c, "lwing_gain", 0);
+ if (l)
+ {
+ lwing_gain = _atof_l(l, C_locale);
+ have_rg = 1;
+ }
+ }
+ else have_rg = 1;
+
+ if (!have_rg)
+ {
+ gain = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0f);
+ }
+
+ scale = powf(10, (gain) / 20.0f);
+ if (lwing_gain)
+ scale *= lwing_gain;
+ else if (have_rg)
+ switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_mode", 1))
+ {
+ case 1: // apply gain, but don't clip
+ if (scale*peak > 1.0) scale = 1.0 / peak;
+ break;
+ case 2: // normalize
+ scale = 1.0 / peak;
+ break;
+ case 3: // no clipping
+ if (peak > 1.0f)
+ scale = 1.0 / peak;
+ break;
+ }
+ }
+ }
+
+
+ return scale;
+}
+
+void Decoder::process_rg()
+{
+ scale = file->GetGain();
+}
+
+void Decoder::setup_mc()
+{
+ if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"mono", false))
+ nch = 1;
+ else if (src_nch == 6)
+ {
+ switch (cfg_mc6_dm)
+ {
+ case 2:
+ nch = 4;
+ break;
+ case 3:
+ case 4:
+ nch = 2;
+ break;
+ case 5:
+ nch = 1;
+ break;
+ }
+
+ if (nch > 2 && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"surround", true))
+ nch = 2;
+ }
+}
+
+void Decoder::Flush()
+{
+ bptr = 0;
+ pcmbuf = 0;
+ data = 0;
+ pos = 0;
+ if (shaper) {delete shaper;shaper = 0;}
+}
+
+void Decoder::Init(VorbisFile * f, UINT _bits, UINT _nch, bool _useFloat, bool allowRG)
+{
+ useFloat = _useFloat;
+
+ file = f;
+ vorbis_info * i = ov_info(&file->vf, -1);
+
+ if (allowRG)
+ process_rg();
+ else
+ scale = 1.0f;
+
+ if (useFloat)
+ {
+ dither = false;
+ bps = 32;
+ }
+ else
+ {
+ dither = AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"dither", true);
+
+ if (_bits)
+ bps = _bits;
+ else
+ bps = AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"bits", 16);
+ }
+
+ if (useFloat)
+ {
+ clipmin = -10000; // some arbitrarily large number
+ clipmax = 10000; // some arbitrarily large number
+ }
+ else
+ {
+ clipmin = - (1 << (bps - 1));
+ clipmax = (1 << (bps - 1)) - 1;
+ }
+ sr = i->rate;
+ nch = src_nch = i->channels;
+ Flush();
+ cur_link = file->vf.current_link;
+
+ if (_nch)
+ nch = _nch;
+ else
+ setup_mc();
+}
+
+UINT Decoder::DataAvailable()
+{
+ return data * (bps >> 3);
+}
+
+int Decoder::DoFrame()
+{
+ need_reopen = 0;
+ while (1)
+ {
+ data = ov_read_float(&file->vf, &pcmbuf, 576, 0);
+ if ((int)data <= 0)
+ {
+ if (data == OV_HOLE) {continue;}
+ data = 0;
+ return 0;
+ }
+ break;
+ }
+
+ pos = 0;
+ if (cur_link != file->vf.current_link)
+ {
+ vorbis_info* i = ov_info(&file->vf, -1);
+ if (sr != (UINT)i->rate || src_nch != (UINT)i->channels)
+ {
+ UINT old_nch = nch, old_sr = sr;
+ if (shaper) {delete shaper;shaper = 0;}
+ sr = i->rate;
+ src_nch = nch = i->channels;
+ setup_mc();
+ if (nch != old_nch || sr != old_sr)
+ {
+ need_reopen = 1;
+ }
+ }
+ process_rg();
+ cur_link = file->vf.current_link;
+ }
+ data *= nch;
+ return 1;
+}
+
+int Decoder::Read(UINT bytes, void * buf)
+{
+ UINT wr = 0;
+ if (buf && bytes && data > 0)
+ {
+ char* out = (char*)buf;
+ UINT d;
+ double mul;
+ int ofs;
+ float * img;
+
+ d = bytes / (bps >> 3);
+ if (d > data) d = data;
+ if (!d) return 0;
+ data -= d;
+ if (useFloat)
+ {
+ mul = 1.0;
+ ofs = 0;
+ }
+ else
+ {
+ mul = (double)( (1 << ((bps) - 1)) - 1 );
+ ofs = (bps == 8) ? 0x80 : 0;
+ }
+ wr += d * (bps >> 3);
+
+ img = (float*)alloca(sizeof(float) * nch);
+ do
+ {
+ UINT cur_ch;
+ if (nch == 1 && src_nch > 0)
+ {
+ UINT c;
+ img[0] = 0;
+ for (c = 0;c < src_nch;c++)
+ {
+ img[0] += pcmbuf[c][pos];
+ }
+ img[0] /= (float)src_nch;
+ }
+ else if (nch == src_nch && !(nch == 6 && cfg_mc6_dm == 1))
+ {
+ UINT c;
+ for (c = 0;c < nch;c++)
+ {
+ img[c] = pcmbuf[c][pos];
+ }
+ }
+ else if (src_nch == 6)
+ {
+ UINT FL, FR, C;
+ if (cfg_mc6_map == 1)
+ {
+ FL = 0;
+ FR = 1;
+ C = 2;
+ }
+ else
+ {
+ FL = 0;
+ C = 1;
+ FR = 2;
+ }
+
+ if (nch == 6)
+ { //remap order for correct 5.1 output
+ img[0] = pcmbuf[FL][pos];
+ img[1] = pcmbuf[FR][pos];
+ img[2] = pcmbuf[C][pos];
+ img[3] = pcmbuf[5][pos];
+ img[4] = pcmbuf[3][pos];
+ img[5] = pcmbuf[4][pos];
+ }
+ else if (nch == 2)
+ {
+ /*
+ FL FR C BL BR LFE
+ 0 1 2 3 4 5
+
+ L,C,R,SL,SR,LFE
+ 0 1 2 3 4 5
+
+
+ output:
+ FL FR C LFE BL BR
+
+
+ stereo:
+ Lt=L+0.707*(V-SL-SR+LFE)
+ Rt=R+0.707*(C+SL+SR+LFE)
+
+
+ Lt=L+0.707*(C+LFE)
+ Rt=R+0.707*(C+LFE)
+ SLt=SL
+ SRt=SR
+
+ */
+ if (cfg_mc6_dm == 4) //ds2
+ {
+ const double a = pow(10., 1.5 / 20.), b = 1 / a;
+ img[0] = pcmbuf[FL][pos] + 0.707 * (pcmbuf[C][pos] - a * pcmbuf[3][pos] - b * pcmbuf[4][pos] + pcmbuf[5][pos]);
+ img[1] = pcmbuf[FR][pos] + 0.707 * (pcmbuf[C][pos] + b * pcmbuf[3][pos] + a * pcmbuf[4][pos] + pcmbuf[5][pos]);
+ }
+ else
+ {
+ img[0] = pcmbuf[FL][pos] + 0.707 * (pcmbuf[C][pos] - pcmbuf[3][pos] - pcmbuf[4][pos] + pcmbuf[5][pos]);
+ img[1] = pcmbuf[FR][pos] + 0.707 * (pcmbuf[C][pos] + pcmbuf[3][pos] + pcmbuf[4][pos] + pcmbuf[5][pos]);
+ }
+ }
+ else if (nch == 4)
+ {
+ img[0] = pcmbuf[FL][pos] + 0.707 * (pcmbuf[C][pos] + pcmbuf[5][pos]);
+ img[1] = pcmbuf[FR][pos] + 0.707 * (pcmbuf[C][pos] + pcmbuf[5][pos]);
+ img[2] = pcmbuf[3][pos];
+ img[3] = pcmbuf[4][pos];
+ }
+ }
+
+ for (cur_ch = 0;cur_ch < nch;cur_ch++)
+ {
+ float v = img[cur_ch];
+ int val;
+ v *= scale;
+ v *= mul;
+ if (dither)
+ {
+ if (!shaper)
+ {
+ //Shaper(int freq,int _nch,int min,int max,int _dtype,int pdf,double noiseamp);
+ shaper = new Shaper(sr, nch, clipmin, clipmax, 2, DITHER_TRIANGLE, 0);
+ }
+ // double peak=0;
+ val = shaper->do_shaping(v /*,&peak*/, cur_ch);
+ //shaper clips for us
+ }
+ else
+ {
+ val = (int)v;
+ if (val < clipmin) val = clipmin;
+ else if (val > clipmax) val = clipmax;
+ //1<<16 = 0x10000
+
+ }
+ val += ofs;
+
+ switch (bps)
+ {
+ case 8:
+ *(BYTE*)out = (UINT)val;
+ break;
+ case 16:
+ *(short*)out = val;
+ break;
+ case 24:
+ {
+ ((BYTE*)out)[0] = (UINT)val;
+ ((BYTE*)out)[1] = (UINT)val >> 8;
+ ((BYTE*)out)[2] = (UINT)val >> 16;
+ }
+ break;
+ case 32:
+ if (useFloat)
+ {
+ *(float *)out = v;
+ }
+ else
+ {
+ //*(long*)out=val;
+ //break;
+ *(long*)out = 0;
+ }
+ break;
+ };
+ out += (bps >> 3);
+ d--;
+ }
+ pos++;
+ }
+ while (d);
+
+ }
+ return wr;
+}
+
+int VorbisFile::Seek(double p) { return ov_time_seek(&vf, p);}
+
+int Decoder::Seek(double p)
+{
+ Flush();
+ return file->Seek(p);
+}
+
+//char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count)
+const char* VorbisFile::get_meta(const char* tag, UINT c)
+{
+ return vorbis_comment_query(vf.seekable ? vf.vc + vf.current_link : vf.vc, (char*)tag, c);
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/decoder.h b/Src/Plugins/Input/in_vorbis/decoder.h
new file mode 100644
index 00000000..aa2b8c01
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/decoder.h
@@ -0,0 +1,41 @@
+class Shaper;
+
+class Decoder
+{
+private:
+ void process_rg();
+ void setup_mc();
+ float* bptr;
+ float** pcmbuf;
+ Shaper * shaper;
+ UINT data,pos;
+ float scale;
+ int cur_link;
+ int cur_preamp;
+ int clipmin,clipmax;
+public:
+ VorbisFile * file;
+
+ UINT nch,sr,kbps,bps,src_nch;
+
+ Decoder()
+ {
+ memset(this,0,sizeof(*this));
+ }
+
+ ~Decoder();
+
+ int Seek(double p);
+ int Read(UINT bytes,void * buf);
+ void Flush();
+ void Init(VorbisFile * f, UINT _bits=0, UINT _nch=0, bool _useFloat=false, bool allowRG=true);
+ void wa2_setinfo(UINT cur_bitrate);
+
+ UINT DataAvailable();
+ int DoFrame();
+ bool need_reopen;
+ int play_init();
+ bool play_inited;
+ bool dither;
+ bool useFloat;
+}; \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/genres.c b/Src/Plugins/Input/in_vorbis/genres.c
new file mode 100644
index 00000000..3f71c504
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/genres.c
@@ -0,0 +1,97 @@
+#include <windows.h>
+#include "genres.h"
+#include <shlwapi.h>
+
+extern const wchar_t *INI_DIRECTORY;
+
+static void file_init(wchar_t *file_path, wchar_t *fn)
+{
+ PathCombineW(file_path, INI_DIRECTORY, fn);
+}
+
+static char eol[2]={13,10};
+
+static char get_char(HANDLE f,BOOL * eof)
+{
+ DWORD br=0;
+ char r=0;
+ ReadFile(f,&r,1,&br,0);
+ if (!br) *eof=1;
+ return r;
+}
+
+void genres_read(HWND wnd, wchar_t* fn)
+{
+ char temp[MAX_GENRE] = {0};
+ char add[MAX_GENRE] = {0};
+ BOOL eof=0;
+ char c = 0;
+ wchar_t file_path[MAX_PATH] = {0};
+ HANDLE f;
+
+ file_init(file_path, fn);
+
+ f = CreateFileW(file_path, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
+ if (f==INVALID_HANDLE_VALUE) return;
+ GetWindowTextA(wnd,add,MAX_GENRE);
+ while(!eof)
+ {
+ UINT ptr=0;
+ BOOL start=1;
+ while(ptr<MAX_GENRE-1)
+ {
+ c=get_char(f,&eof);
+ if (eof) break;
+ if (c==10 || c==13)
+ {
+ if (start) continue;
+ else break;
+ }
+ start=0;
+ temp[ptr++]=c;
+ }
+ if (ptr)
+ {
+ temp[ptr]=0;
+ SendMessage(wnd,CB_ADDSTRING,0, (LPARAM)temp);
+ if (add[0])
+ {
+ if (!_stricmp(add,temp)) add[0]=0;
+ }
+ }
+ }
+ CloseHandle(f);
+ if (add[0]) SendMessage(wnd,CB_ADDSTRING,0,(LPARAM)add);
+}
+
+void genres_write(HWND wnd, wchar_t* fn)
+{
+ wchar_t file_path[MAX_PATH] = {0};
+ char temp[MAX_GENRE] = {0};
+ UINT max = 0,n = 0;
+ DWORD bw = 0;
+ HANDLE f;
+ {
+ char add[MAX_GENRE] = {0};
+ GetWindowTextA(wnd,add,MAX_GENRE);
+ if (!add[0]) return;
+ max=(UINT)SendMessage(wnd,CB_GETCOUNT,0,0);
+ for(n=0;n<max;n++)
+ {
+ SendMessage(wnd,CB_GETLBTEXT,n,(LPARAM)temp);
+ if (!_stricmp(temp,add)) return;
+ }
+ SendMessage(wnd,CB_ADDSTRING,0,(LPARAM)add);
+ }
+ file_init(file_path, fn);
+ f = CreateFileW(file_path, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
+ if (f==INVALID_HANDLE_VALUE) return;
+ max=(UINT)SendMessage(wnd,CB_GETCOUNT,0,0);
+ for(n=0;n<max;n++)
+ {
+ SendMessage(wnd,CB_GETLBTEXT,n,(LPARAM)temp);
+ bw = 0; WriteFile(f,temp,(DWORD)strlen(temp),&bw,0);
+ bw = 0; WriteFile(f,eol,2,&bw,0);
+ }
+ CloseHandle(f);
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/genres.h b/Src/Plugins/Input/in_vorbis/genres.h
new file mode 100644
index 00000000..ad840701
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/genres.h
@@ -0,0 +1,16 @@
+#ifndef NULLSOFT_IN_VORBIS_GENRES_H
+#define NULLSOFT_IN_VORBIS_GENRES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void genres_read(HWND wnd, wchar_t* fn);
+void genres_write(HWND wnd, wchar_t* fn);
+#define MAX_GENRE 256
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/http.cpp b/Src/Plugins/Input/in_vorbis/http.cpp
new file mode 100644
index 00000000..87998084
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/http.cpp
@@ -0,0 +1,596 @@
+#include "api__in_vorbis.h"
+#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
+#include "rf.h"
+#include "main.h"
+#include "../Winamp/wa_ipc.h"
+#include <api/service/waservicefactory.h>
+#include "../nu/AutoWide.h"
+#include "../nu/AutoChar.h"
+extern CfgInt cfg_fix0r,cfg_httpseek2,cfg_proxy_mode,cfg_prebuf1,cfg_prebuf2,cfg_fsave,cfg_http_bsize;
+
+#define CANSEEK
+
+WORD *wdup(const char * src);//info.c
+
+#define zeromem(x) memset(&x,0,sizeof(x))
+
+class StreamSave
+{
+ private:
+ ogg_sync_state oy_src;
+ ogg_stream_state os_src;
+ ogg_stream_state os_dst;
+ ogg_page og_src;
+ ogg_page og_dst;
+ ogg_packet op;
+ StringW tmp_fn;
+ BOOL is_temp;
+ BOOL got_streams,got_delta,use_fix0r;
+ ogg_int64_t pcm_delta;
+ int packets,serial;
+ HANDLE hFile;
+ public:
+
+ StreamSave()
+ {
+ zeromem(oy_src);
+ zeromem(os_src);
+ zeromem(os_dst);
+ zeromem(og_src);
+ zeromem(og_dst);
+ zeromem(op);
+ got_streams=0;
+ got_delta=0;
+ pcm_delta=0;
+ hFile=0;
+ packets=0;
+ serial=0;
+ is_temp=1;
+
+ tmp_fn=cfg_dumpdir;
+ if (tmp_fn[tmp_fn.Length()-1]!='\\') tmp_fn.AddChar('\\');
+
+ tmp_fn+=StringPrintfW(L"oggtemp%u.foo",GetTickCount64()&0xFFFF);
+
+ hFile=CreateFileW(tmp_fn,GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_HIDDEN,0);
+ if (hFile==INVALID_HANDLE_VALUE) hFile=0;
+ else
+ {
+ ogg_sync_init(&oy_src);
+ use_fix0r=cfg_fix0r;
+ }
+ };
+
+ void Write(void * ptr,UINT siz)
+ {
+ if (!hFile) return;
+
+ void * b=ogg_sync_buffer(&oy_src,siz);
+ memcpy(b,ptr,siz);
+ ogg_sync_wrote(&oy_src,siz);
+
+ while(ogg_sync_pageout(&oy_src,&og_src)>0)
+ {
+ if (!got_streams)
+ {
+ serial=ogg_page_serialno(&og_src);
+ ogg_stream_init(&os_src,serial);
+ ogg_stream_init(&os_dst,serial);
+ got_streams=1;
+ packets=0;
+ got_delta=0;
+ }
+ else if (serial!=ogg_page_serialno(&og_src))
+ {
+ if (got_streams)
+ {
+ /*while(ogg_stream_flush(&os_dst,&og_dst))
+ {
+ write_page(dst,&og_dst,&wb);
+ }*/
+
+ ogg_stream_clear(&os_src);
+ ogg_stream_clear(&os_dst);
+ }
+ serial=ogg_page_serialno(&og_src);
+ ogg_stream_init(&os_src,serial);
+ ogg_stream_init(&os_dst,serial);
+
+ packets=0;
+ got_delta=0;
+ }
+
+ ogg_stream_pagein(&os_src,&og_src);
+ while(ogg_stream_packetout(&os_src,&op)>0)
+ {
+ if (use_fix0r && !got_delta && packets>2 && op.granulepos>=0) //hack to fix saved streams
+ {
+ got_delta=1;
+ if (op.granulepos>4096*(packets-2)) pcm_delta=op.granulepos;
+ }
+ if (got_delta)
+ {
+ if (op.granulepos>=pcm_delta) op.granulepos-=pcm_delta;
+ else if (op.granulepos>0) op.granulepos=0;
+ }
+ ogg_stream_packetin(&os_dst,&op);
+ packets++;
+ }
+
+ while((packets==3 ? ogg_stream_flush(&os_dst,&og_dst) : ogg_stream_pageout(&os_dst,&og_dst))>0)
+ {
+ DWORD bw = 0;
+ WriteFile(hFile,og_dst.header,og_dst.header_len,&bw,0);
+ bw = 0; WriteFile(hFile,og_dst.body,og_dst.body_len,&bw,0);
+ }
+ }
+ }
+
+ void FixName(VorbisFile * vf,const char * streamname)
+ {
+ if (!hFile) return;
+ CloseHandle(hFile);
+ StringW fn(cfg_dumpdir);
+
+ if (fn[fn.Length()-1]!='\\') fn.AddChar('\\');
+
+ UINT n=fn.Length();
+ fn+=(wchar_t *)AutoWide(vf->get_meta("TITLE", 0), CP_UTF8);
+ UINT m=fn.Length();
+
+ while(n<m)
+ {
+ char * b="/\\:*?\"<>|";
+ while(b && *b)
+ {
+ if (fn[n]==*b) {fn.SetChar(n,'_');break;}
+ b++;
+ }
+ n++;
+ };
+ fn.AddStringA(".ogg");
+ if (!MoveFileW(tmp_fn,fn))
+ {
+ DeleteFileW(fn);
+ MoveFileW(tmp_fn,fn);
+ }
+ SetFileAttributesW(fn,FILE_ATTRIBUTE_NORMAL);
+ hFile=CreateFileW(fn,GENERIC_WRITE,0,0,OPEN_EXISTING,0,0);
+
+ if (hFile==INVALID_HANDLE_VALUE) {hFile=0;}
+ else SetFilePointer(hFile,0,0,FILE_END);
+ is_temp=0;
+ }
+
+ ~StreamSave()
+ {
+ if (hFile)
+ {
+ /*if (got_streams)
+ {
+ while(ogg_stream_flush(&os_dst,&og_dst))
+ {
+ write_page(dst,&og_dst,&wb);
+ }
+ }*/
+
+ ogg_stream_clear(&os_src);
+ ogg_stream_clear(&os_dst);
+
+ SetFilePointer(hFile,0,0,FILE_CURRENT);
+ CloseHandle(hFile);
+ if (is_temp) DeleteFileW(tmp_fn);
+ }
+ ogg_sync_clear(&oy_src);
+ }
+};
+
+static const char * do_proxy(const char * url)
+{
+ switch(cfg_proxy_mode)
+ {
+ default:
+ return 0;
+ case 1:
+ {
+ const char * p=strstr(url,"://");
+ if (!p) p=url;
+ while(p && *p && *p!=':' && *p!='/') p++;
+ if (p && *p==':')
+ {
+ if (atoi(p+1)!=80) return 0;
+ }
+ }
+ case 2:
+ {
+ char *x = (char *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_PROXY_STRING);
+ if (x == (char *)1 || !x || !*x)
+ return 0;
+ return x;
+ }
+ }
+}
+
+class VorbisFile_HTTP : public VorbisFile
+{
+ protected:
+ api_httpreceiver *get;
+ UINT bsize;
+ uint64_t len;
+ UINT pos;
+ UINT seekpos;
+ BOOL can_seek;
+ StreamSave * saver;
+ virtual void Idle();
+ virtual int f_seek(__int64 offset,int whence);
+ virtual size_t f_read(UINT siz,void * ptr);
+ virtual UINT f_tell();
+ virtual UINT FileSize() {return len;}
+ bool is_live;
+
+ public:
+ virtual int GetType() {return TYPE_HTTP;}
+ virtual bool IsLive() {return is_live;}
+ bool http_init();
+
+ void do_prebuf() {VorbisFile::do_prebuf();fillbuf(bsize * cfg_prebuf1 / 100,0);}
+
+ VorbisFile_HTTP(UINT s, const wchar_t *u,bool is_info, bool hasauth) : VorbisFile(u,is_info), usedauth(hasauth)
+ {
+ get=0;
+ can_seek=0;
+ len=pos=seekpos=0;
+ bsize=s;
+ saver=0;
+ m_needs_auth=0;
+ lpinfo[0]=0;
+ force_lpinfo[0]=0;
+ is_live = false;
+ memset(dlg_realm, 0, sizeof(dlg_realm));
+ }
+
+ ~VorbisFile_HTTP()
+ {
+ if (get)
+ {
+ waServiceFactory *sf = mod.service->service_getServiceByGuid(httpreceiverGUID);
+ if (sf)
+ sf->releaseInterface(get);
+ get=0;
+ }
+ if (saver) delete saver;
+ }
+
+ void fillbuf(UINT max,bool shutup);
+
+ size_t _http_read(char* ptr,size_t total);
+ int reconnect(UINT ofs);
+ virtual void post_init()
+ {
+ if (saver) saver->FixName(this,get->getheader("ice-name"));
+ }
+
+ static BOOL CALLBACK httpDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam);
+ int m_needs_auth;
+ char dlg_realm[256];
+ char lpinfo[256];
+ char force_lpinfo[256];
+ bool usedauth;
+};
+
+int VorbisFile_HTTP::reconnect(UINT ofs)
+{
+ // get.reset_headers();
+ get->addheader("User-Agent: WinampOGG/5.24(MPEG stream compatible)");
+ get->addheader("Accept:*/*");
+ if (ofs>0) get->addheader(StringPrintf("Range: bytes=%u-",ofs));
+ get->connect(AutoChar(url));
+
+ Status(WASABI_API_LNGSTRINGW(IDS_CONNECTING));
+
+ int st=get->run();
+ if (st<0)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+void VorbisFile_HTTP::fillbuf(UINT max,bool shutup)
+{
+ if (len>0 && pos+max>len) max=len-pos;
+ while(!Aborting() && !abort_prebuf) //stop prebuffering if we wanna seek
+ {
+ if (!shutup)
+ {
+ Status(StringPrintfW(WASABI_API_LNGSTRINGW(IDS_PREBUFFERING), get->bytes_available()*100/bsize));
+ }
+ if (get->run()) break;
+ if (Aborting() || abort_prebuf || get->bytes_available()>=(int)max) break;
+ Sleep(2);
+ }
+ if (!shutup)
+ {
+ Status(0);
+ }
+}
+
+size_t VorbisFile_HTTP::_http_read(char* ptr,size_t total)
+{
+#ifdef CANSEEK
+ if (seekpos!=-1)
+ {
+ UINT sp=seekpos;
+ seekpos=-1;
+ if (sp!=pos)
+ {
+ if (sp>pos && sp<=pos+get->bytes_available())
+ {
+ get->get_bytes(0,sp-pos);
+ }
+ else
+ {
+ if (reconnect(sp))
+ {
+ return 0;//oh well...
+ }
+ }
+ pos=sp;
+ }
+ }
+#endif
+ UINT wr=0;
+ while(!Aborting() && wr<total)
+ {
+ int st=get->run();
+ int d=get->get_bytes(ptr,(int)total-wr);
+ if (st && !d) break;
+ wr+=d;
+ ptr+=d;
+ pos+=d;
+ if ((len>0 && pos>=len) || wr>=total || Aborting()) break;
+ if (use_prebuf) fillbuf(bsize * cfg_prebuf2 / 100,0);
+ else Sleep(1);
+ }
+ return wr;
+}
+
+void VorbisFile_HTTP::Idle()
+{
+ get->run();
+ Sleep(1);
+ get->run();
+ Sleep(1);
+}
+
+size_t VorbisFile_HTTP::f_read(UINT siz,void* ptr)
+{
+ if (Aborting()) return 0;//fixme
+ int i=(int)_http_read((char*)ptr,siz);
+ if (i>0 && saver) saver->Write(ptr,i);
+ return i;
+}
+
+int VorbisFile_HTTP::f_seek(ogg_int64_t offset,int whence)
+{
+#ifdef CANSEEK
+ if (can_seek)
+ {
+ switch(whence)
+ {
+ case FILE_BEGIN:
+ seekpos=(int)offset;
+ break;
+ case FILE_END:
+ seekpos=len+(int)offset;
+ break;
+ case FILE_CURRENT:
+ seekpos=pos+(int)offset;
+ break;
+ }
+ if (seekpos>len) seekpos=len;
+ return 0;
+ }
+ else
+#endif
+ return -1;
+}
+
+UINT VorbisFile_HTTP::f_tell()
+{
+#ifdef CANSEEK
+ if (can_seek)
+ {
+ if (seekpos!=-1) return seekpos;
+ else return pos;
+ }
+ else
+#endif
+ return -1;
+}
+
+HWND GetDialogBoxParent()
+{
+ HWND parent = (HWND)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETDIALOGBOXPARENT);
+ if (!parent || parent == (HWND)1)
+ return mod.hMainWindow;
+ return parent;
+}
+
+bool VorbisFile_HTTP::http_init()
+{
+ if (mod.service)
+ {
+ waServiceFactory *sf = mod.service->service_getServiceByGuid(httpreceiverGUID);
+ if (sf) get = (api_httpreceiver *)sf->getInterface();
+ }
+ if (!get) return false;
+ get->open(API_DNS_AUTODNS, bsize, do_proxy(AutoChar(url)));
+
+ if (reconnect(0))
+ {
+ return 0;
+ }
+
+#ifdef CANSEEK
+ // if (cfg_httpseek)
+ {
+ //need to get http headers first
+ while(!memcmp(get->getallheaders(),"\0\0",2))
+ {
+ if (get->run()<0 || Aborting())
+ {
+ int reply = get->getreplycode();
+ if ( reply == 401 )
+ {
+ api_connection *mcon=get->GetConnection();
+ if ( mcon && mcon->GetReceiveBytesAvailable())
+ {
+ char p[1024]="";
+ while ( mcon->GetReceiveBytesAvailable() )
+ {
+ char b[2048]="";
+ mcon->ReceiveLine(b,2048);
+ if ( *b )
+ {
+ char *t= strstr(b,"WWW-Authenticate:");
+ if ( t && *t )
+ {
+ char *y = strstr(t,"\"");
+ if ( y && *y )
+ {
+ y++;
+ if ( *y )
+ {
+ char *u = strstr(y,"\"");
+ if ( u && *u )
+ {
+ *u = 0;
+ wsprintfA(p,"%s",y);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( *p ) // found our realm
+ {
+ if (!force_lpinfo[0]) GetPrivateProfileStringA("HTTP-AUTH",p,"",force_lpinfo,sizeof(force_lpinfo),INI_FILE);
+ if (!force_lpinfo[0] || lpinfo[0] || usedauth )
+ {
+ lstrcpynA(dlg_realm,p,sizeof(dlg_realm));
+ if (!WASABI_API_DIALOGBOXPARAM(IDD_HTTPAUTH, GetDialogBoxParent(), httpDlgProc, (LPARAM)this))
+ {
+ force_lpinfo[0]=0;
+ }
+ else
+ {
+ WritePrivateProfileStringA("HTTP-AUTH",p,force_lpinfo,INI_FILE);
+ }
+ }
+ Status(WASABI_API_LNGSTRINGW(IDS_AUTH_REQUIRED));
+ m_needs_auth=1;
+ }
+ }
+ }
+ return 0;
+ }
+ //hg->get.wait(10);
+ Sleep(1);
+ }
+ len=get->content_length();
+ const char* poo=get->getheader("icy-name");
+ if (poo) stream_title=poo;
+ if (cfg_httpseek2 && len) can_seek=1;
+ is_live=(len<=0);
+ }
+#endif
+
+ //if (hg->len==0 || hg->len==-1) hg->can_seek=0;
+ seekpos=-1;
+
+ if (cfg_fsave && !can_seek)
+ {
+ saver=new StreamSave;
+ }
+ return 1;
+}
+
+VorbisFile * VorbisFile::Create_HTTP(const char * url,bool is_info)
+{
+ VorbisFile_HTTP * r=new VorbisFile_HTTP(cfg_http_bsize,AutoWide(url),is_info, false);
+ if (r)
+ {
+ if (!r->http_init())
+ {
+ int trys=0;
+ while ( r && r->m_needs_auth && trys++ < 2)
+ {
+ const char *p=strstr(url,"://");
+ if (p && *p)
+ {
+ p += 3;
+ if (p && *p)
+ {
+ char lurl[4096] = {0};
+ wsprintfA(lurl, "http://%s@%s", r->force_lpinfo, p);
+ delete r;
+ r = new VorbisFile_HTTP(cfg_http_bsize,AutoWide(lurl),is_info, true);
+ if (r && r->http_init())
+ {
+ return r;
+ }
+ }
+ }
+ }
+ delete r;
+ r=0;
+ }
+ }
+ return r;
+}
+
+BOOL CALLBACK VorbisFile_HTTP::httpDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
+{
+ VorbisFile_HTTP *_this;
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+#ifdef WIN64
+ SetWindowLong(hwndDlg, GWLP_USERDATA, (LONG)lParam);
+ _this = (VorbisFile_HTTP*)(GetWindowLong(hwndDlg, GWLP_USERDATA));
+#else
+ SetWindowLong(hwndDlg, GWL_USERDATA, (LONG)lParam);
+ _this = (VorbisFile_HTTP*)GetWindowLong(hwndDlg, GWL_USERDATA);
+#endif
+ if (_this->force_lpinfo[0])
+ SetDlgItemTextA(hwndDlg,IDC_EDITAUTH,_this->force_lpinfo);
+ else SetDlgItemTextA(hwndDlg,IDC_EDITAUTH,_this->lpinfo);
+ SetDlgItemTextA(hwndDlg,IDC_REALM,_this->dlg_realm);
+ return 1;
+
+ case WM_COMMAND:
+#ifdef WIN64
+ _this = (VorbisFile_HTTP*)GetWindowLong(hwndDlg, GWLP_USERDATA);
+#else
+ _this = (VorbisFile_HTTP*)GetWindowLong(hwndDlg, GWL_USERDATA);
+#endif
+ if (LOWORD(wParam) == IDOKAUTH)
+ {
+ char *p;
+ GetDlgItemTextA(hwndDlg,IDC_EDITAUTH,_this->force_lpinfo,sizeof(_this->force_lpinfo));
+ p = strstr(_this->force_lpinfo,"\r");
+ if ( p && *p ) *p=0;
+ p = strstr(_this->force_lpinfo,"\n");
+ if ( p && *p ) *p=0;
+ EndDialog(hwndDlg,1);
+ }
+ else if (LOWORD(wParam) == IDCANCELAUTH)
+ {
+ EndDialog(hwndDlg,0);
+ }
+ break;
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/in_vorbis.rc b/Src/Plugins/Input/in_vorbis/in_vorbis.rc
new file mode 100644
index 00000000..a8d4b5e3
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/in_vorbis.rc
@@ -0,0 +1,415 @@
+// 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
+
+/////////////////////////////////////////////////////////////////////////////
+// Polish resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PLK)
+#ifdef _WIN32
+LANGUAGE LANG_POLISH, SUBLANG_DEFAULT
+#pragma code_page(1250)
+#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
+
+#endif // Polish resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// 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
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_ABOUT DIALOGEX 0, 0, 173, 113
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "",IDC_CUSTOM1,"XIPH_CLASS",0x0,6,8,44,40
+ CTEXT "",IDC_RPM,7,56,45,8
+ CTEXT "",IDC_RPM2,4,64,51,8
+ LTEXT "",IDC_ABOUT_TEXT,58,8,110,85
+ DEFPUSHBUTTON "OK",IDCANCEL,117,93,50,14
+END
+
+IDD_HTTPAUTH DIALOGEX 0, 0, 154, 70
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Ogg Authentication"
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "Realm: ",IDC_STATIC,4,4,25,8
+ LTEXT "",IDC_REALM,37,4,113,8
+ LTEXT "Enter your login and password in the form of:\n\tlogin:password",IDC_STATIC,4,15,146,17
+ EDITTEXT IDC_EDITAUTH,4,35,146,12,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOKAUTH,4,52,50,13
+ PUSHBUTTON "Cancel",IDCANCELAUTH,100,53,50,13
+END
+
+IDD_INFO_DLG_NEW DIALOGEX 0, 0, 329, 238
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
+CAPTION "Ogg Vorbis Info"
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "File/URL :",-1,6,6,34,8
+ EDITTEXT IDC_URL,43,5,282,12,ES_AUTOHSCROLL | ES_READONLY
+ GROUPBOX "File / Stream Info",IDC_STATIC_MISC,224,20,101,120
+ EDITTEXT IDC_MISC,231,30,88,107,ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | NOT WS_BORDER
+ GROUPBOX "Chained Streams",IDC_STATIC_CS,224,155,101,30
+ PUSHBUTTON "<= previous",IDC_PREV_STREAM,229,167,44,13
+ PUSHBUTTON "next =>",IDC_NEXT_STREAM,276,167,43,13
+ CONTROL "Hide special fields",IDC_HIDE_SPEC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,190,73,10
+ PUSHBUTTON "",IDC_MODE_TOGGLE,228,203,93,13
+ PUSHBUTTON "Close",IDCANCEL,273,220,48,13
+END
+
+IDD_INFO_PANEL_SIMPLE DIALOGEX 0, 0, 227, 218
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ GROUPBOX "Standard Tags",IDC_STATIC_STD,3,2,219,119
+ LTEXT "Title",IDC_STATIC,29,16,14,8
+ EDITTEXT IDC_TITLE,48,14,168,12,ES_AUTOHSCROLL
+ LTEXT "Artist",IDC_STATIC,27,32,16,8
+ EDITTEXT IDC_ARTIST,48,30,168,12,ES_AUTOHSCROLL
+ LTEXT "Album",IDC_STATIC,24,48,19,8
+ EDITTEXT IDC_ALBUM,48,46,124,12,ES_AUTOHSCROLL
+ LTEXT "Track:",IDC_STATIC_TRACK,175,48,21,8
+ EDITTEXT IDC_TRACK,198,46,18,12,ES_AUTOHSCROLL
+ LTEXT "Year",IDC_STATIC,28,64,16,8
+ EDITTEXT IDC_DATE,48,62,56,12,ES_AUTOHSCROLL
+ LTEXT "Genre",IDC_STATIC,117,64,22,8
+ COMBOBOX IDC_GENRE,140,62,77,68,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Comment",IDC_STATIC,13,80,29,8
+ EDITTEXT IDC_COMMENT,48,78,169,37,ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL
+ GROUPBOX "Other",IDC_STATIC_TAGS,3,123,219,91
+ LISTBOX IDC_LIST,10,134,207,75,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+END
+
+IDD_INFO_PANEL_ADVANCED DIALOGEX 0, 0, 227, 218
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LISTBOX IDC_LIST,3,3,219,212,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+END
+
+IDD_INFO DIALOGEX 0, 0, 341, 164
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ GROUPBOX "Advanced",IDC_STATIC,0,0,341,164
+ CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,6,13,164,143
+ LTEXT "Name:",IDC_STATIC,175,13,22,8
+ EDITTEXT IDC_NAME,175,23,159,14,ES_AUTOHSCROLL
+ LTEXT "Value:",IDC_STATIC,175,39,21,8
+ EDITTEXT IDC_VALUE,175,49,159,90,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL
+ PUSHBUTTON "Add New",IDC_BUTTON_ADD,176,142,50,13
+ PUSHBUTTON "Delete",IDC_BUTTON_DEL,230,142,50,13
+ PUSHBUTTON "Delete All",IDC_BUTTON_DELALL,284,142,50,13
+END
+
+IDD_CONFIG DIALOGEX 0, 0, 222, 261
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ GROUPBOX "Decoding",IDC_STATIC,4,4,214,93
+ CONTROL "Buffer full files from disk",IDC_FULLBUF,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,17,89,10
+ CONTROL "Show average bitrate while playing",IDC_AVG_BR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,31,128,10
+ GROUPBOX "5.1-Channel Input Processing",IDC_STATIC,10,44,201,47
+ LTEXT "Apply conversion:",IDC_STATIC,16,58,72,8,0,WS_EX_RIGHT
+ COMBOBOX IDC_MC6_DM,95,56,110,96,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Source channel order:",IDC_STATIC,16,75,72,8,0,WS_EX_RIGHT
+ COMBOBOX IDC_MC6_MAP,95,73,110,96,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "Streaming",IDC_STATIC,4,101,214,139
+ LTEXT "Buffer size",IDC_STATIC,10,115,34,8
+ EDITTEXT IDC_HTTP_BSIZE,48,113,28,12,ES_AUTOHSCROLL | ES_NUMBER
+ LTEXT "KB",IDC_STATIC,80,115,9,8
+ GROUPBOX "Prebuffering:",IDC_STATIC,10,129,201,44
+ LTEXT "At start:",IDC_STATIC,16,142,31,8
+ CONTROL "",IDC_SLIDER1,"msctls_trackbar32",TBS_NOTICKS | WS_TABSTOP,70,141,130,10
+ LTEXT "After underrun:",IDC_STATIC,16,153,54,8
+ CONTROL "",IDC_SLIDER2,"msctls_trackbar32",TBS_NOTICKS | WS_TABSTOP,70,152,130,10
+ LTEXT "0%",IDC_STATIC,71,161,13,8
+ LTEXT "50%",IDC_STATIC,122,161,18,8
+ LTEXT "100%",IDC_STATIC,185,161,20,8
+ LTEXT "Use Winamp's proxy server settings:",IDC_STATIC,10,179,120,8
+ COMBOBOX IDC_PROXY,134,177,77,58,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "",IDC_STATIC,10,191,201,43
+ CONTROL "Save streamed files to:",IDC_FSAVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,201,89,10
+ PUSHBUTTON "default",IDC_SSAVE_FMT_DEF,165,200,40,11
+ PUSHBUTTON "",IDC_STREAM_SAVE,15,215,105,13
+ EDITTEXT IDC_SSAVE_FMT,120,215,85,13,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,124,244,45,13
+ PUSHBUTTON "Cancel",IDCANCEL,173,244,45,13
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_ABOUT, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 168
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 107
+ END
+
+ IDD_HTTPAUTH, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 150
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 66
+ END
+
+ IDD_CONFIG, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 218
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 257
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+#if defined(APSTUDIO_INVOKED) || defined(DISABLED)
+#if defined(APSTUDIO_INVOKED)
+IDB_BITMAP9$(DISABLED) BITMAP "oggdrop\\120.bmp"
+#else
+IDB_BITMAP9 BITMAP "oggdrop\\120.bmp"
+#endif
+#endif
+#if defined(APSTUDIO_INVOKED) || defined(DISABLED)
+#if defined(APSTUDIO_INVOKED)
+IDB_BITMAP8$(DISABLED) BITMAP "oggdrop\\119.bmp"
+#else
+IDB_BITMAP8 BITMAP "oggdrop\\119.bmp"
+#endif
+#endif
+#if defined(APSTUDIO_INVOKED) || defined(DISABLED)
+#if defined(APSTUDIO_INVOKED)
+IDB_BITMAP7$(DISABLED) BITMAP "oggdrop\\118.bmp"
+#else
+IDB_BITMAP7 BITMAP "oggdrop\\118.bmp"
+#endif
+#endif
+#if defined(APSTUDIO_INVOKED) || defined(DISABLED)
+#if defined(APSTUDIO_INVOKED)
+IDB_BITMAP6$(DISABLED) BITMAP "oggdrop\\117.bmp"
+#else
+IDB_BITMAP6 BITMAP "oggdrop\\117.bmp"
+#endif
+#endif
+#if defined(APSTUDIO_INVOKED) || defined(DISABLED)
+#if defined(APSTUDIO_INVOKED)
+IDB_BITMAP5$(DISABLED) BITMAP "oggdrop\\116.bmp"
+#else
+IDB_BITMAP5 BITMAP "oggdrop\\116.bmp"
+#endif
+#endif
+#if defined(APSTUDIO_INVOKED) || defined(DISABLED)
+#if defined(APSTUDIO_INVOKED)
+IDB_BITMAP4$(DISABLED) BITMAP "oggdrop\\115.bmp"
+#else
+IDB_BITMAP4 BITMAP "oggdrop\\115.bmp"
+#endif
+#endif
+#if defined(APSTUDIO_INVOKED) || defined(DISABLED)
+#if defined(APSTUDIO_INVOKED)
+IDB_BITMAP3$(DISABLED) BITMAP "oggdrop\\114.bmp"
+#else
+IDB_BITMAP3 BITMAP "oggdrop\\114.bmp"
+#endif
+#endif
+#if defined(APSTUDIO_INVOKED) || defined(DISABLED)
+#if defined(APSTUDIO_INVOKED)
+IDB_BITMAP2$(DISABLED) BITMAP "oggdrop\\113.bmp"
+#else
+IDB_BITMAP2 BITMAP "oggdrop\\113.bmp"
+#endif
+#endif
+#if defined(APSTUDIO_INVOKED) || defined(DISABLED)
+#if defined(APSTUDIO_INVOKED)
+IDB_BITMAP12$(DISABLED) BITMAP "oggdrop\\123.bmp"
+#else
+IDB_BITMAP12 BITMAP "oggdrop\\123.bmp"
+#endif
+#endif
+#if defined(APSTUDIO_INVOKED) || defined(DISABLED)
+#if defined(APSTUDIO_INVOKED)
+IDB_BITMAP11$(DISABLED) BITMAP "oggdrop\\122.bmp"
+#else
+IDB_BITMAP11 BITMAP "oggdrop\\122.bmp"
+#endif
+#endif
+#if defined(APSTUDIO_INVOKED) || defined(DISABLED)
+#if defined(APSTUDIO_INVOKED)
+IDB_BITMAP10$(DISABLED) BITMAP "oggdrop\\121.bmp"
+#else
+IDB_BITMAP10 BITMAP "oggdrop\\121.bmp"
+#endif
+#endif
+#if defined(APSTUDIO_INVOKED) || defined(DISABLED)
+#if defined(APSTUDIO_INVOKED)
+IDB_BITMAP1$(DISABLED) BITMAP "oggdrop\\112.bmp"
+#else
+IDB_BITMAP1 BITMAP "oggdrop\\112.bmp"
+#endif
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// PNG
+//
+
+IDB_PNG1 PNG "oggdrop\\112.png"
+IDB_PNG2 PNG "oggdrop\\113.png"
+IDB_PNG3 PNG "oggdrop\\114.png"
+IDB_PNG4 PNG "oggdrop\\115.png"
+IDB_PNG5 PNG "oggdrop\\116.png"
+IDB_PNG6 PNG "oggdrop\\117.png"
+IDB_PNG7 PNG "oggdrop\\118.png"
+IDB_PNG8 PNG "oggdrop\\119.png"
+IDB_PNG9 PNG "oggdrop\\120.png"
+IDB_PNG10 PNG "oggdrop\\121.png"
+IDB_PNG11 PNG "oggdrop\\122.png"
+IDB_PNG12 PNG "oggdrop\\123.png"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_VORBIS_DECODER "Nullsoft Vorbis Decoder v%s"
+ 65535 "{5C5BCA4E-279E-4867-8E24-58C8B186959A}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_VORBIS_DECODER_OLD "Nullsoft Vorbis Decoder"
+ IDS_FILE_ERROR "file error"
+ IDS_LENGTH "Length: "
+ IDS_AVERAGE_BITRATE "Average bitrate: "
+ IDS_FILE_SIZE "File size: "
+ IDS_NOMINAL_BITRATE "Nominal bitrate: "
+ IDS_MIN_BITRATE "Min bitrate: "
+ IDS_MAX_BITRATE "Max bitrate: "
+ IDS_CHANNELS "Channels"
+ IDS_SAMPLING_RATE "Sampling rate"
+ IDS_SERIAL_NUMBER "Serial number"
+ IDS_VERSION "Version"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_VENDOR "Vendor"
+ IDS_TO_SIMPLE_MODE "<< to simple mode"
+ IDS_TO_ADVANCED_MODE "to advanced mode >>"
+ IDS_OGG_VORBIS_INFO "Ogg Vorbis info - %s"
+ IDS_BEST_RPM "Best: %u RPM"
+ IDS_LEAVE_AS_IS "leave as is"
+ IDS_REMAP_6_CHANNELS "remap 6 channels"
+ IDS_DOWNMIX_TO_4_CHANNELS "downmix to 4 channels"
+ IDS_DOWNMIX_TO_2_CHANNELS_DS "downmix to 2 channels (DS)"
+ IDS_DOWNMIX_TO_2_CHANNELS_DS2 "downmix to 2 channels (DS2)"
+ IDS_DOWNMIX_TO_MONO "downmix to mono"
+ IDS_CORRECT_FL_FC_FR_BL_BR_LFE "correct (FL FC FR BL BR LFE)"
+ IDS_BROKEN_FL_FR_FC_BL_BR_LFE "broken (FL FR FC BL BR LFE)"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_ABOUT_TEXT "%s\n© 2001-2023 Winamp SA\nWritten by: Peter Pawlowski\nBuild date: %hs\n\nThanks to:\n Craig Freer\n Tomi 'Nebularia' Jylhä-Ollila\n Jack Moffitt\n Szabolcs Péter"
+ IDS_TITLE_PREFERENCES "%s Preferences"
+ IDS_NEVER "never"
+ IDS_PORT_80_ONLY "port 80 only"
+ IDS_ALWAYS "always"
+ IDS_SELECT_OUTPUT_DIRECTORY "Select output directory:"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_CONNECTING "Connecting..."
+ IDS_PREBUFFERING "Prebuffering : %u%%"
+ IDS_AUTH_REQUIRED "Authentication Required"
+ IDS_OGG_FILES "Ogg Vorbis Files (*.OGG;*.OGA)"
+ IDS_NAME "Name"
+ IDS_VALUE "Value"
+ IDS_KBPS "kbps"
+ IDS_HZ "Hz"
+ IDS_GAME_SPEED "%u RPM"
+ IDS_BYTES "bytes"
+ IDS_FAMILY_STRING "Ogg Vorbis File"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/Plugins/Input/in_vorbis/in_vorbis.sln b/Src/Plugins/Input/in_vorbis/in_vorbis.sln
new file mode 100644
index 00000000..fe0c2e6d
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/in_vorbis.sln
@@ -0,0 +1,65 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29424.173
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "in_vorbis", "in_vorbis.vcxproj", "{7C58AC02-7941-42BE-8F97-9BF2064D6631}"
+ ProjectSection(ProjectDependencies) = postProject
+ {4FC28B55-2A14-43D5-86F7-201054F338A9} = {4FC28B55-2A14-43D5-86F7-201054F338A9}
+ {49238ED1-3146-49AB-9523-E9826EE4A0C8} = {49238ED1-3146-49AB-9523-E9826EE4A0C8}
+ {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4} = {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libogg", "..\libogg\libogg.vcxproj", "{4FC28B55-2A14-43D5-86F7-201054F338A9}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vorbis_static", "..\libvorbis\win32\vorbis_static.vcxproj", "{49238ED1-3146-49AB-9523-E9826EE4A0C8}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vorbisfile_static", "..\libvorbis\win32\vorbisfile_static.vcxproj", "{EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}"
+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
+ {7C58AC02-7941-42BE-8F97-9BF2064D6631}.Debug|Win32.ActiveCfg = Debug|Win32
+ {7C58AC02-7941-42BE-8F97-9BF2064D6631}.Debug|Win32.Build.0 = Debug|Win32
+ {7C58AC02-7941-42BE-8F97-9BF2064D6631}.Release|Win32.ActiveCfg = Release|Win32
+ {7C58AC02-7941-42BE-8F97-9BF2064D6631}.Release|Win32.Build.0 = Release|Win32
+ {7C58AC02-7941-42BE-8F97-9BF2064D6631}.Debug|x64.ActiveCfg = Debug|x64
+ {7C58AC02-7941-42BE-8F97-9BF2064D6631}.Debug|x64.Build.0 = Debug|x64
+ {7C58AC02-7941-42BE-8F97-9BF2064D6631}.Release|x64.ActiveCfg = Release|x64
+ {7C58AC02-7941-42BE-8F97-9BF2064D6631}.Release|x64.Build.0 = Release|x64
+ {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Debug|Win32.ActiveCfg = Debug|Win32
+ {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Debug|Win32.Build.0 = Debug|Win32
+ {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Release|Win32.ActiveCfg = Release|Win32
+ {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Release|Win32.Build.0 = Release|Win32
+ {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Debug|x64.ActiveCfg = Debug|x64
+ {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Debug|x64.Build.0 = Debug|x64
+ {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Release|x64.ActiveCfg = Release|x64
+ {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Release|x64.Build.0 = Release|x64
+ {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Debug|Win32.ActiveCfg = Debug|Win32
+ {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Debug|Win32.Build.0 = Debug|Win32
+ {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Release|Win32.ActiveCfg = Release|Win32
+ {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Release|Win32.Build.0 = Release|Win32
+ {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Debug|x64.ActiveCfg = Debug|x64
+ {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Debug|x64.Build.0 = Debug|x64
+ {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Release|x64.ActiveCfg = Release|x64
+ {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Release|x64.Build.0 = Release|x64
+ {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|Win32.ActiveCfg = Debug|Win32
+ {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|Win32.Build.0 = Debug|Win32
+ {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|Win32.ActiveCfg = Release|Win32
+ {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|Win32.Build.0 = Release|Win32
+ {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|x64.ActiveCfg = Debug|x64
+ {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|x64.Build.0 = Debug|x64
+ {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|x64.ActiveCfg = Release|x64
+ {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {3248659F-0FFC-4CF4-A9BC-F7D10C713D5D}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj b/Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj
new file mode 100644
index 00000000..3bfe43ab
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj
@@ -0,0 +1,339 @@
+<?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>{7C58AC02-7941-42BE-8F97-9BF2064D6631}</ProjectGuid>
+ <RootNamespace>in_vorbis</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|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>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </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>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;WA2;TAGZ_UNICODE;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4090;4312;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0415</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <MapFileName>$(IntDir)$(TargetName).map</MapFileName>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;WA2;TAGZ_UNICODE;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4090;4312;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0415</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <MapFileName>$(IntDir)$(TargetName).map</MapFileName>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;TAGZ_UNICODE;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <DisableSpecificWarnings>4090;4312;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0415</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <MapFileName>$(IntDir)$(TargetName).map</MapFileName>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;TAGZ_UNICODE;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <DisableSpecificWarnings>4090;4312;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0415</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <MapFileName>$(IntDir)$(TargetName).map</MapFileName>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\external_dependencies\openmpt-trunk\include\vorbis\win32\vorbisfile_static.vcxproj">
+ <Project>{ec9475d2-fee2-4f8c-9bb9-a11d5eb597c4}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\..\nsmkv\Lacing.cpp" />
+ <ClCompile Include="..\..\..\nsmkv\vint.cpp" />
+ <ClCompile Include="about.cpp" />
+ <ClCompile Include="chainedstream_parse.cpp" />
+ <ClCompile Include="config.cpp" />
+ <ClCompile Include="c_string.cpp" />
+ <ClCompile Include="decoder.cpp" />
+ <ClCompile Include="ExtendedRead.cpp" />
+ <ClCompile Include="genres.c" />
+ <ClCompile Include="http.cpp" />
+ <ClCompile Include="infobox.cpp" />
+ <ClCompile Include="info_.cpp" />
+ <ClCompile Include="localfile.cpp" />
+ <ClCompile Include="mkv_vorbis_decoder.cpp" />
+ <ClCompile Include="shaper.cpp" />
+ <ClCompile Include="vcedit.c" />
+ <ClCompile Include="wa2.cpp" />
+ <ClCompile Include="winnt_helper.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api__in_vorbis.h" />
+ <ClInclude Include="c_string.h" />
+ <ClInclude Include="decoder.h" />
+ <ClInclude Include="genres.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="mkv_vorbis_decoder.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="shaper.h" />
+ <ClInclude Include="vcedit.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="in_vorbis.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="oggdrop\112.png" />
+ <Image Include="oggdrop\113.png" />
+ <Image Include="oggdrop\114.png" />
+ <Image Include="oggdrop\115.png" />
+ <Image Include="oggdrop\116.png" />
+ <Image Include="oggdrop\117.png" />
+ <Image Include="oggdrop\118.png" />
+ <Image Include="oggdrop\119.png" />
+ <Image Include="oggdrop\120.png" />
+ <Image Include="oggdrop\121.png" />
+ <Image Include="oggdrop\122.png" />
+ <Image Include="oggdrop\123.png" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj.filters b/Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj.filters
new file mode 100644
index 00000000..6ce4ad5b
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj.filters
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="winnt_helper.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wa2.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="vcedit.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="shaper.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="mkv_vorbis_decoder.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="localfile.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="infobox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="info_.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="http.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="genres.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ExtendedRead.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="decoder.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="config.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="chainedstream_parse.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="c_string.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="about.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nsmkv\Lacing.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nsmkv\vint.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api__in_vorbis.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="c_string.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="decoder.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="genres.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="mkv_vorbis_decoder.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="shaper.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="vcedit.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="oggdrop\112.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="oggdrop\113.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="oggdrop\114.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="oggdrop\115.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="oggdrop\116.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="oggdrop\117.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="oggdrop\118.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="oggdrop\119.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="oggdrop\120.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="oggdrop\121.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="oggdrop\122.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="oggdrop\123.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{7c471b36-309f-402b-a247-e640a094f9bc}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{735fc214-c8e5-4619-8c70-a184a8373c92}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{00aa1653-baa3-4e88-b6d1-fa408b0c4a85}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Image Files">
+ <UniqueIdentifier>{32126a2c-dd90-4901-a5e1-aee5b7363e37}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="in_vorbis.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/info_.cpp b/Src/Plugins/Input/in_vorbis/info_.cpp
new file mode 100644
index 00000000..687dc737
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/info_.cpp
@@ -0,0 +1,658 @@
+//tag editor file i/o code, title formatting interface
+#include "main.h"
+#include "genres.h"
+#include "../nu/AutoWide.h"
+#include "../nu/AutoChar.h"
+#include "../nu/ns_wc.h"
+#include "api__in_vorbis.h"
+#include <wchar.h>
+#include <math.h>
+#include <shlwapi.h>
+#include "vcedit.h"
+#include <strsafe.h>
+#include "resource.h"
+
+namespace ogg_helper //chainedstream_parse
+{
+ int num_get_tracks(HANDLE hFile);
+ int query_chained_stream_offset(HANDLE hFile, int idx, __int64 * out_beginning, __int64 * out_end);
+}
+
+/*static void xfer(HANDLE src, HANDLE dst, __int64 size)
+{
+ enum { BUFFER = 1024 * 1024 };
+ void * buffer = malloc((int)(BUFFER > size ? size : BUFFER));
+ while (size > 0)
+ {
+ int d = BUFFER;
+ if ((__int64)d > size) d = (int)size;
+ DWORD br = 0;
+ ReadFile(src, buffer, d, &br, 0);
+ WriteFile(dst, buffer, d, &br, 0);
+ size -= d;
+ }
+}*/
+
+static void seek64(HANDLE src, __int64 offset)
+{
+ __int64 temp = offset;
+ SetFilePointer(src, *(DWORD*)&temp, ((long*)&temp + 1), FILE_BEGIN);
+}
+
+extern OSVERSIONINFO os_ver;
+extern HANDLE hThread;
+
+static DWORDLONG get_space(const wchar_t * url)
+{
+ ULARGE_INTEGER free_space;
+ char zzz[4] = {(char)url[0], (char)url[1], (char)url[2], 0}; //"c:\";
+
+ free_space.QuadPart = 0;
+
+ if (os_ver.dwPlatformId == VER_PLATFORM_WIN32_NT || (os_ver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS && LOWORD(os_ver.dwBuildNumber) > 1000))
+ {
+ static BOOL (WINAPI* pGetDiskFreeSpaceEx)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);
+ if (!pGetDiskFreeSpaceEx)
+ {
+ pGetDiskFreeSpaceEx = (BOOL (WINAPI*)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER))GetProcAddress(GetModuleHandle(L"kernel32.dll"), "GetDiskFreeSpaceExA");
+ }
+ if (pGetDiskFreeSpaceEx)
+ {
+ ULARGE_INTEGER blah1, blah2;
+ pGetDiskFreeSpaceEx((LPCTSTR)zzz, &free_space, &blah1, &blah2);
+ }
+ }
+ if (!free_space.QuadPart)
+ {
+ DWORD spc, bps, nfc, tnc;
+ GetDiskFreeSpaceA(zzz, &spc, &bps, &nfc, &tnc);
+ free_space.QuadPart = UInt32x32To64(spc * bps, nfc);
+ }
+ return free_space.QuadPart;
+}
+
+bool sync_movefile(const wchar_t * src, const wchar_t * dst);
+
+struct vcedit_param
+{
+ HANDLE hFile;
+ __int64 remaining;
+};
+
+static size_t callback_fread(void *ptr, size_t size, size_t nmemb, vcedit_param * param)
+{
+ int to_read = (int)(nmemb *size);
+ if (to_read > param->remaining) to_read = (int)param->remaining;
+ DWORD br = 0;
+ ReadFile(param->hFile, ptr, to_read, &br, 0);
+ param->remaining -= br;
+ return br / size;
+}
+
+static size_t callback_write(const void *ptr, size_t size, size_t nmemb, HANDLE hFile)
+{
+ DWORD bw = 0;
+ WriteFile(hFile, ptr, (DWORD)(size*nmemb), &bw, 0);
+ return bw / size;
+}
+
+BOOL modify_file(const wchar_t* url, const vorbis_comment * comments, int links)
+{ //also used for stream save fix
+ HANDLE dst = INVALID_HANDLE_VALUE;
+ int scream = 0;
+ StringW tmp;
+
+ winampGetExtendedFileInfoW_Cleanup();
+
+ tmp = url;
+ tmp += L".tmp";
+
+ HANDLE src = CreateFileW(url, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
+
+ if (src != INVALID_HANDLE_VALUE)
+ {
+ ULARGE_INTEGER src_size;
+ src_size.LowPart = GetFileSize(src, &src_size.HighPart);
+ if (src_size.QuadPart > get_space(url))
+ { //shit happens... try default temp location
+ StringW tempdir;
+ GetTempPathW(MAX_PATH, StringTempW(tempdir, MAX_PATH));
+ if (get_space(tempdir) < src_size.QuadPart)
+ { //oh well
+ CloseHandle(src);
+ src = INVALID_HANDLE_VALUE;
+ }
+ {
+ tmp = tempdir;
+ if (tmp[tmp.Length() - 1] != '\\') tmp.AddChar('\\');
+
+ StringCchPrintfW(StringTempW(tempdir, MAX_PATH), MAX_PATH, L"ogg%u_%u.tmp", GetTickCount(), GetCurrentProcessId());
+ tmp.AddString(tempdir);
+ }
+ }
+ dst = CreateFileW(tmp, GENERIC_WRITE | GENERIC_READ, 0, 0, CREATE_ALWAYS, 0, 0);
+ }
+
+ if (dst != INVALID_HANDLE_VALUE && src != INVALID_HANDLE_VALUE)
+ {
+ {
+ FILETIME ct;
+ GetFileTime(src, &ct, 0, 0);
+ SetFileTime(dst, &ct, 0, 0);
+ }
+
+ int num_links = ogg_helper::num_get_tracks(src);
+ if (num_links < links) scream = 1;
+ else
+ {
+ int cur_link;
+ for (cur_link = 0; cur_link < links && !scream; cur_link++)
+ {
+ __int64 stream_beginning, stream_end;
+ if (ogg_helper::query_chained_stream_offset(src, cur_link, &stream_beginning, &stream_end))
+ {
+ seek64(src, stream_beginning);
+ vcedit_state *vs;
+ vcedit_param param;
+ param.hFile = src;
+ param.remaining = stream_end - stream_beginning;
+ vs = vcedit_new_state();
+ if (vcedit_open_callbacks(vs, &param, (vcedit_read_func)callback_fread, (vcedit_write_func)callback_write) < 0)
+ {
+ scream = 1;
+ }
+ else
+ {
+ vorbis_comment * vc = vcedit_comments(vs);
+ vorbis_comment_clear(vc);
+ vorbis_comment_init(vc);
+ const vorbis_comment * vc_src = comments + cur_link;
+
+ int n;
+ for (n = 0;n < vc_src->comments;n++)
+ {
+ if (vc_src->user_comments[n])
+ vorbis_comment_add(vc, vc_src->user_comments[n]);
+ }
+
+ vcedit_write(vs, dst);
+ vcedit_clear(vs);
+ }
+ }
+ }
+ }
+ }
+ else scream = 1;
+ if (src != INVALID_HANDLE_VALUE) CloseHandle(src);
+ if (dst != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(dst);
+ if (scream)
+ {
+ DeleteFileW(tmp);
+ }
+ }
+
+ if (!scream)
+ {
+ BOOL f_sync;
+ EnterCriticalSection(&sync);
+
+ f_sync = !_wcsicmp(url, cur_file) && hThread; //check for i/o conflict with currently played file
+ LeaveCriticalSection(&sync);
+ if (f_sync)
+ { //drat, it's now playing
+ scream = !sync_movefile(tmp, url);
+ }
+ else
+ {
+ if (!DeleteFileW(url)) scream = 1;
+ else
+ {
+ if (!MoveFileW(tmp, url))
+ {
+ if (!CopyFileW(tmp, url, 0)) scream = 1;
+ DeleteFileW(tmp);
+ }
+ }
+ }
+ }
+ if (scream) return 0;
+ else return 1;
+}
+
+wchar_t *wdup(const char * src)
+{
+ return _wcsdup(StringW(src));
+}
+
+extern StringW stat_disp;
+
+void GetFileInfo(const wchar_t *file, wchar_t *title, int *len)
+{
+ VorbisFile* vf = 0;
+ BOOL is_cur_file = 0;
+ BOOL is_vf_local = 1;
+ if (title) *title = 0;
+ if (len) *len = -1;
+
+ if (!file || !*file)
+ {
+ file = cur_file;
+ is_cur_file = 1;
+ }
+ else if (!lstrcmpiW(file, cur_file))
+ {
+ is_cur_file = 1;
+ }
+
+ if (title && stat_disp.Length() > 0 && is_cur_file)
+ {
+ lstrcpynW(title, stat_disp, 256);
+ title = 0;
+ }
+
+ if (!len && !title) return ;
+
+ if (is_cur_file)
+ {
+ EnterCriticalSection(&sync);
+ if (theFile)
+ {
+ vf = theFile;
+ is_vf_local = 0;
+ }
+ else
+ LeaveCriticalSection(&sync);
+ }
+
+ if (!vf)
+ {
+ vf = VorbisFile::Create(file, 1);
+ if (!vf)
+ {
+ if (title)
+ {
+ lstrcpynW(title, PathFindFileNameW(file), 256);
+ wchar_t *blah = PathFindExtensionW(title);
+ *blah=0;
+ }
+ return ;
+ }
+ }
+
+ if (len)
+ {
+ *len = (int)(vf->Length() * 1000);
+ }
+
+ if (title)
+ {
+ const char *t = vf->get_meta("ARTIST", 0);
+ if (t)
+ {
+ MultiByteToWideCharSZ(CP_UTF8, 0, t, -1, title, 256);
+ t = vf->get_meta("TITLE", 0);
+ if (t)
+ {
+ StringCchCatW(title, 256, L" - ");
+ StringCchCatW(title, 256, AutoWide(t, CP_UTF8));
+ }
+ }
+ else
+ {
+ const char *t = vf->get_meta("TITLE", 0);
+ if (t)
+ MultiByteToWideCharSZ(CP_UTF8, 0, t, -1, title, 256);
+ else
+ {
+ lstrcpynW(title, PathFindFileNameW(file), 256);
+ wchar_t *blah = PathFindExtensionW(title);
+ *blah=0;
+ }
+ }
+ }
+ //q:
+ if (is_vf_local)
+ delete vf;
+ else
+ LeaveCriticalSection(&sync);
+}
+
+void w9x_itow(wchar_t *dest, int value, int destlen)
+{
+ StringCchPrintfW(dest, destlen, L"%d", value);
+
+}
+void w9x_utow(wchar_t *dest, int value, int destlen)
+{
+ StringCchPrintfW(dest, destlen, L"%u", value);
+}
+void w9x_htow(wchar_t *dest, int value, int destlen)
+{
+ StringCchPrintfW(dest, destlen, L"%08x", value);
+}
+
+static void print_misc(VorbisFile * _vf,int link,wchar_t * out, int outlen)
+{
+ OggVorbis_File * vf=&_vf->vf;
+ double t=ov_time_total(vf,link);
+ vorbis_info * vi=ov_info(vf,link);
+ vorbis_comment * vc=ov_comment(vf,link);
+ if (!vi || !vc) {WASABI_API_LNGSTRINGW_BUF(IDS_FILE_ERROR,out,outlen);return;}
+
+ wchar_t kbps_str[16] = {0};
+ WASABI_API_LNGSTRINGW_BUF(IDS_KBPS, kbps_str, 16);
+
+ wchar_t length[48]=L"", avgbitrate[48]=L"", filesize[48]=L"", nombitrate[48]=L"", maxbitrate[48]=L"", minbitrate[48]=L"";
+ if (t>0)
+ {
+ int h = (int)(t/3600.0);
+ int m = (int)(t/60.0)%60;
+ int s = (int)t%60;
+
+ if(h>0) StringCchPrintfW(length,48,L"%s%u:%02u:%02u\r\n",WASABI_API_LNGSTRINGW(IDS_LENGTH),h,m,s);
+ else if(m>0) StringCchPrintfW(length,48,L"%s%u:%02u\r\n",WASABI_API_LNGSTRINGW(IDS_LENGTH),m,s);
+ else if(s>0) StringCchPrintfW(length,48,L"%s%u\r\n",WASABI_API_LNGSTRINGW(IDS_LENGTH),s);
+
+ UINT fs=_vf->FileSize();
+ if (fs>0)
+ {
+ int kbps = (int)(((double)fs)/(t*125.0));
+ wchar_t tmp[32] = {0};
+ StringCchPrintfW(avgbitrate,48,L"%s%u %s\r\n",WASABI_API_LNGSTRINGW(IDS_AVERAGE_BITRATE),kbps,kbps_str);
+
+ int fs1=fs/1000000;
+ int fs2=(fs/1000)%1000;
+ int fs3=fs%1000;
+ if(fs1)
+ StringCchPrintfW(filesize,48,L"%s%u%03u%03u %s\r\n",WASABI_API_LNGSTRINGW(IDS_FILE_SIZE),fs1,fs2,fs3,WASABI_API_LNGSTRINGW_BUF(IDS_BYTES,tmp,32));
+ else if(fs2)
+ StringCchPrintfW(filesize,48,L"%s%u%03u %s\r\n",WASABI_API_LNGSTRINGW(IDS_FILE_SIZE),fs2,fs3,WASABI_API_LNGSTRINGW_BUF(IDS_BYTES,tmp,32));
+ else
+ StringCchPrintfW(filesize,48,L"%s%u %s\r\n",WASABI_API_LNGSTRINGW(IDS_FILE_SIZE),fs3,WASABI_API_LNGSTRINGW_BUF(IDS_BYTES,tmp,32));
+ }
+ }
+
+ if (vi->bitrate_nominal>0)
+ StringCchPrintfW(nombitrate,48,L"%s%u %s\r\n",WASABI_API_LNGSTRINGW(IDS_NOMINAL_BITRATE),vi->bitrate_nominal/1000,kbps_str);
+
+ if (vi->bitrate_lower>0)
+ StringCchPrintfW(minbitrate,48,L"%s%u %s\r\n",WASABI_API_LNGSTRINGW(IDS_MIN_BITRATE),vi->bitrate_lower/1000,kbps_str);
+
+ if (vi->bitrate_nominal>0)
+ StringCchPrintfW(maxbitrate,48,L"%s%u %s\r\n",WASABI_API_LNGSTRINGW(IDS_MAX_BITRATE),vi->bitrate_nominal/1000,kbps_str);
+
+ wchar_t tmp[32] = {0}, tmp2[32] = {0}, tmp3[32] = {0}, tmp4[32] = {0}, tmp5[32] = {0}, hzStr[8] = {0};
+ StringCchPrintfW(out,outlen,L"%s%s%s%s%s%s%s: %u\r\n%s: %u %s\r\n%s: %u\r\n%s: %u\r\n%s: \r\n%s",
+ length, avgbitrate, filesize, nombitrate, maxbitrate, minbitrate,
+ WASABI_API_LNGSTRINGW_BUF(IDS_CHANNELS,tmp,32),vi->channels,
+ WASABI_API_LNGSTRINGW_BUF(IDS_SAMPLING_RATE,tmp2,32),vi->rate, WASABI_API_LNGSTRINGW_BUF(IDS_HZ,hzStr,8),
+ WASABI_API_LNGSTRINGW_BUF(IDS_SERIAL_NUMBER,tmp3,32),ov_serialnumber(vf,link),
+ WASABI_API_LNGSTRINGW_BUF(IDS_VERSION,tmp4,32),vi->version,
+ WASABI_API_LNGSTRINGW_BUF(IDS_VENDOR,tmp5,32),(wchar_t*)AutoWide(vc->vendor,CP_UTF8));
+}
+
+static VorbisFile* last_vf = 0;
+static wchar_t last_file[MAX_PATH] = {0};
+static FILETIME ftLastWriteTime;
+
+// is used to determine if the last write time of the file has changed when
+// asked to get the metadata for the same cached file so we can update things
+BOOL HasFileTimeChanged(const wchar_t *fn)
+{
+ WIN32_FILE_ATTRIBUTE_DATA fileData = {0};
+ if (GetFileAttributesExW(fn, GetFileExInfoStandard, &fileData) == TRUE)
+ {
+ if(CompareFileTime(&ftLastWriteTime, &fileData.ftLastWriteTime))
+ {
+ ftLastWriteTime = fileData.ftLastWriteTime;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void UpdateFileTimeChanged(const wchar_t *fn)
+{
+ WIN32_FILE_ATTRIBUTE_DATA fileData;
+ if (GetFileAttributesExW(fn, GetFileExInfoStandard, &fileData) == TRUE)
+ {
+ ftLastWriteTime = fileData.ftLastWriteTime;
+ }
+}
+
+// need to call this when we shut down just to make sure things are correctly cleaned up
+//(the joys of caching for speed)
+void winampGetExtendedFileInfoW_Cleanup(void)
+{
+ if (last_vf)
+ {
+ delete last_vf;
+ last_vf = 0;
+ }
+ last_file[0] = 0;
+}
+
+static void CALLBACK winampGetExtendedFileInfoW_Timer(HWND hwnd, UINT uMsg, UINT_PTR eventId, DWORD elapsed)
+{
+ // TODO need to do a better way of getting and caching the metadata
+ // this is _just_ a temp fix for the file being locked when it
+ // it really needs 'class Info' to be able to cache and read.
+ KillTimer(hwnd, eventId);
+ winampGetExtendedFileInfoW_Cleanup();
+}
+
+bool KeywordMatch(const char *mainString, const char *keyword)
+{
+ return !_stricmp(mainString, keyword);
+}
+
+#define START_TAG_ALIAS(name, alias) if (KeywordMatch(data, name)) lookup=alias
+#define TAG_ALIAS(name, alias) else if (KeywordMatch(data, name)) lookup=alias
+extern "C" __declspec( dllexport ) int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, int destlen)
+{
+ if (!_stricmp(data, "type"))
+ {
+ dest[0] = '0';
+ dest[1] = 0;
+ return 1;
+ }
+ else if (!_stricmp(data, "rateable"))
+ {
+ dest[0] = '1';
+ dest[1] = 0;
+ return 1;
+ }
+ else if (!_stricmp(data, "streammetadata"))
+ {
+ return 0;
+ }
+
+ if (!fn || (fn && !fn[0])) return 0;
+
+ if (!_stricmp(data, "family"))
+ {
+ LPCWSTR e;
+ int pID = -1;
+ DWORD lcid;
+ e = PathFindExtensionW(fn);
+ if (L'.' != *e) return 0;
+ e++;
+ lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+ if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"OGG", -1) ||
+ CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"OGA", -1)) pID = IDS_FAMILY_STRING;
+ if (pID != -1 && S_OK == StringCchCopyW(dest, destlen, WASABI_API_LNGSTRINGW(pID))) return 1;
+ return 0;
+ }
+
+ if (!_stricmp(data, "mime"))
+ {
+ StringCchCopyW(dest, destlen, L"audio/ogg");
+ return 1;
+ }
+
+ // attempt to cache/use a cached instance of VorbisFile to speed up metadata queries
+ // which is especially needed with large ogg files (like with a 4Mb embedded image!)
+ VorbisFile* vf = 0;
+ if(last_file[0] && !_wcsicmp(last_file, fn) && !HasFileTimeChanged(fn))
+ {
+ vf = last_vf;
+ }
+ else
+ {
+ // different file so clean up if there's a cached instance
+ if(last_vf)
+ {
+ delete last_vf;
+ last_vf = 0;
+ }
+ // open the new file and cache the current filename for subsequent query checks
+ vf = VorbisFile::Create(fn, 1);
+ lstrcpynW(last_file, fn, MAX_PATH);
+ }
+
+ if (!vf) return 0;
+ else last_vf = vf;
+
+ // TODO need to do a better way of getting and caching the metadata
+ // this is _just_ a temp fix for the file being locked when it
+ // it really needs 'class Info' to be able to cache and read.
+ SetTimer(mod.hMainWindow, 256, 2000, winampGetExtendedFileInfoW_Timer);
+
+ const char *lookup = 0;
+ if (!_stricmp(data, "length"))
+ {
+ int len = (int)(vf->Length() * 1000);
+ w9x_itow(dest, len, destlen);
+ return 1;
+ }
+ else if (!_stricmp(data, "bitrate"))
+ {
+ int br = vf->get_avg_bitrate();
+ w9x_itow(dest, br, destlen);
+ return 1;
+ }
+ else if (!_stricmp(data, "SERIALNUMBER"))
+ {
+ w9x_utow(dest, ov_serialnumber(&vf->vf, -1), destlen);
+ return 1;
+ }
+ else if (!_stricmp(data, "SERIALNUMBER_HEX"))
+ {
+ w9x_htow(dest, ov_serialnumber(&vf->vf, -1), destlen);
+ return 1;
+ }
+ else if (!_stricmp(data, "gain"))
+ {
+ float gain = 20.0f*(float)log10(vf->GetGain());
+ StringCchPrintfW(dest, destlen, L"%-+.2f dB", gain);
+ return 1;
+ }
+ else if(!_stricmp(data,"formatinformation"))
+ {
+ print_misc(vf,0,dest,destlen);
+ return 1;
+ }
+ TAG_ALIAS("title", "TITLE");
+ TAG_ALIAS("artist", "ARTIST");
+ TAG_ALIAS("album", "ALBUM");
+ TAG_ALIAS("genre", "GENRE");
+ TAG_ALIAS("comment", "COMMENT");
+ TAG_ALIAS("year", "DATE");
+ TAG_ALIAS("track", "TRACKNUMBER");
+ TAG_ALIAS("albumartist", "ALBUMARTIST");
+ TAG_ALIAS("composer", "COMPOSER");
+ TAG_ALIAS("disc", "DISCNUMBER");
+ TAG_ALIAS("publisher", "PUBLISHER");
+ TAG_ALIAS("conductor", "CONDUCTOR");
+ TAG_ALIAS("tool", "ENCODED-BY");
+ TAG_ALIAS("replaygain_track_gain", "REPLAYGAIN_TRACK_GAIN");
+ TAG_ALIAS("replaygain_track_peak", "REPLAYGAIN_TRACK_PEAK");
+ TAG_ALIAS("replaygain_album_gain", "REPLAYGAIN_ALBUM_GAIN");
+ TAG_ALIAS("replaygain_album_peak", "REPLAYGAIN_ALBUM_PEAK");
+ TAG_ALIAS("GracenoteFileID", "GRACENOTEFILEID");
+ TAG_ALIAS("GracenoteExtData", "GRACENOTEEXTDATA");
+ TAG_ALIAS("bpm", "BPM");
+ TAG_ALIAS("remixing", "REMIXING");
+ TAG_ALIAS("subtitle", "VERSION");
+ TAG_ALIAS("isrc", "ISRC");
+ TAG_ALIAS("category", "CATEGORY");
+ TAG_ALIAS("rating", "RATING");
+ TAG_ALIAS("producer", "PRODUCER");
+
+ if (!lookup)
+ return 0;
+
+ const char *value = vf->get_meta(lookup, 0);
+
+ if(KeywordMatch("comment",data)) {
+ if(!value || !*value) value = vf->get_meta("DESCRIPTION", 0);
+ }
+
+ if(KeywordMatch("year",data)) {
+ if(!value || !*value) value = vf->get_meta("YEAR", 0);
+ }
+
+ if(KeywordMatch("track",data)) {
+ if(!value || !*value) value = vf->get_meta("TRACK", 0);
+ }
+
+ if(KeywordMatch("albumartist",data)) {
+ if(!value || !*value) value = vf->get_meta("ALBUM ARTIST", 0);
+ if(!value || !*value) value = vf->get_meta("ENSEMBLE", 0);
+ }
+
+ if(KeywordMatch("publisher",data)) {
+ if(!value || !*value) value = vf->get_meta("ORGANIZATION", 0);
+ }
+
+ if(KeywordMatch("category",data)) {
+ if(!value || !*value) value = vf->get_meta("CONTENTGROUP", 0);
+ if(!value || !*value) value = vf->get_meta("GROUPING", 0);
+ }
+
+ if(KeywordMatch(data, "rating")) {
+ if(!value || !*value) value = vf->get_meta("RATING", 0);
+ if(value && *value) {
+ int rating = atoi(value);
+ // keeps things limited to our range of 0-100
+ if (rating >= 100) {
+ rating = 5;
+ }
+ // 1-100 case
+ else if (rating > 5 && rating < 100) {
+ rating /= 20;
+ // shift up by one rating when in next band
+ // 1-20 = 1, 21-40 = 2, 41-60 = 3, 61-80 = 4, 81-100 = 5
+ rating += ((atoi(value) - (rating * 20)) > 0);
+ }
+ // Remove support for old 1-10 range
+ /* or maybe we're dealing with a 1-10 range
+ else if (rating > 5) {
+ rating /= 2;
+ } */
+
+ // otherwise it is hopefully in the 0-5 range
+ else if (rating > 0 && rating <= 5) {
+ }
+ // otherwise just make sure and set zero
+ else {
+ rating = 0;
+ }
+
+ StringCchPrintfW(dest, destlen, L"%u", rating);
+ return 1;
+ }
+ }
+
+ if(value)
+ MultiByteToWideCharSZ(CP_UTF8, 0, value, -1, dest, destlen);
+ else
+ {
+ dest[0]=0;
+ return 1;
+ }
+
+ return 1;
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/infobox.cpp b/Src/Plugins/Input/in_vorbis/infobox.cpp
new file mode 100644
index 00000000..30d90056
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/infobox.cpp
@@ -0,0 +1,1457 @@
+#include "main.h"
+#include "api__in_vorbis.h"
+#include "genres.h"
+#include <commctrl.h>
+#include "../Agave/Language/api_language.h"
+#include "../nu/AutoWide.h"
+#include "../nu/AutoChar.h"
+#include "resource.h"
+#include <strsafe.h>
+
+extern In_Module mod;
+/* old PP info box. still used for streams */
+
+
+extern CfgFont cfg_font;
+
+extern CfgInt cfg_modeless,cfg_remember_infosize;
+
+static CfgInt
+ cfg_hide_special_fields("hide_special_fields",1),
+ cfg_adv_info("adv_info",0),
+ cfg_infobox_sx("infobox_sx",0),
+ cfg_infobox_sy("infobox_sy",0),
+ cfg_infobox_x("infobox_x",0),
+ cfg_infobox_y("infobox_y",0);
+
+void SetDlgItemTextWrap(HWND w,UINT id,wchar_t * tx)
+{
+ SetDlgItemTextW(w,id,tx);
+}
+
+typedef struct
+{
+ UINT id;
+ wchar_t * name;
+} STD_TAG_ITEM;
+
+#define N_STD_TAGZ 7
+
+extern BOOL cfg_adv_warn;
+
+static const STD_TAG_ITEM std_tagz[N_STD_TAGZ]=
+{
+ {IDC_TITLE,L"TITLE"},
+ {IDC_ARTIST,L"ARTIST"},
+ {IDC_ALBUM,L"ALBUM"},
+ {IDC_GENRE,L"GENRE"},
+ {IDC_DATE,L"DATE"},
+ {IDC_COMMENT,L"COMMENT"},
+ {IDC_TRACK,L"TRACKNUMBER"},
+};
+
+const wchar_t * special_fields[]={L"RG_PEAK",L"RG_RADIO",L"RG_AUDIOPHILE",L"LWING_GAIN",L"REPLAYGAIN_ALBUM_GAIN",L"REPLAYGAIN_ALBUM_PEAK",L"REPLAYGAIN_TRACK_GAIN",L"REPLAYGAIN_TRACK_PEAK"};
+#define N_SPECIAL_FIELDS (sizeof(special_fields)/sizeof(special_fields[0]))
+
+
+typedef struct tagTAG
+{
+ tagTAG * next;
+ wchar_t * name;
+ wchar_t * value;
+} TAG;
+
+typedef struct
+{
+ wchar_t *name,*value;
+} TAGDESC;
+
+class OggTagData
+{
+public:
+ TAG * tags;
+ TAG ** last;
+ TAG * newtag()
+ {
+ TAG * t=new TAG;
+ *last=t;
+ last=&t->next;
+ t->next=0;
+ return t;
+ }
+
+ OggTagData()
+ {
+ tags=0;
+ last=&tags;
+ }
+
+ String vendor;
+
+ OggTagData(vorbis_comment * vc) : vendor(vc->vendor)
+ {
+ tags=0;
+ last=&tags;
+ int n;
+ for(n=0;n<vc->comments;n++)
+ {
+ TAG * t=newtag();
+ char * c=vc->user_comments[n];
+ char * p=strchr(c,'=');
+ if (p)
+ {
+ int size = MultiByteToWideChar(CP_UTF8, 0, c, (int)(p-c), 0,0);
+ t->name=(wchar_t*)malloc((size+1)*sizeof(wchar_t));
+ MultiByteToWideChar(CP_UTF8, 0, c, (int)(p-c), t->name, size);
+ t->name[size]=0;
+ p++;
+ }
+ else
+ {
+ t->name=_wcsdup(L"COMMENT");
+ p=c;
+ }
+
+ int size = MultiByteToWideChar(CP_UTF8, 0, p, -1, 0,0);
+ t->value=(wchar_t*)malloc((size)*sizeof(wchar_t));
+ MultiByteToWideChar(CP_UTF8, 0, p, -1, t->value, size);
+ }
+ }
+ void Clear()
+ {
+ TAG * t=tags;
+ while(t)
+ {
+ TAG * t1=t->next;
+ free(t->name);
+ free(t->value);
+ delete t;
+ t=t1;
+ }
+ tags=0;
+ last=&tags;
+ }
+ void AddTag(const wchar_t * name,const wchar_t * value)
+ {
+ TAG * t=newtag();
+ t->name=_wcsdup(name);
+ t->value=_wcsdup(value);
+ }
+ ~OggTagData()
+ {
+ Clear();
+ }
+};
+
+static void SetWindowRect(HWND w,RECT * r)
+{
+ SetWindowPos(w,0,r->left,r->top,r->right-r->left,r->bottom-r->top,SWP_NOZORDER|SWP_NOCOPYBITS|SWP_NOACTIVATE);
+}
+
+class DlgBase
+{
+protected:
+ bool DieOnDestroyWindow,is_modeless,is_modal_ex,modal_ex_quit;
+ int modal_ex_quit_val;
+
+ void endDialog(int x)
+ {
+ if (is_modeless) DestroyWindow(wnd);
+ else if (is_modal_ex)
+ {
+ modal_ex_quit=1;
+ modal_ex_quit_val=x;
+ DestroyWindow(wnd);
+ }
+ else EndDialog(wnd,x);
+ }
+
+ void _do_size_x(RECT * r,UINT id,UINT wx,UINT min_x)
+ {
+ RECT r1={r->left,r->top,(LONG)(wx-min_x)+r->right,r->bottom};
+ SetWindowRect(GetDlgItem(wnd,id),&r1);
+ }
+
+ void _do_size_xy(RECT * r,UINT id,UINT wx,UINT wy,UINT min_x,UINT min_y)
+ {
+ RECT r1={r->left,r->top,(LONG)(wx-min_x)+r->right,(LONG)(wy-min_y)+r->bottom};
+ SetWindowRect(GetDlgItem(wnd,id),&r1);
+ }
+
+ void _do_align_x_size_y(RECT * r,UINT id,UINT wx,UINT wy,UINT min_x,UINT min_y)
+ {
+ RECT r1={ (LONG)(wx-min_x)+r->left,r->top,(LONG)(wx-min_x)+r->right,(LONG)(wy-min_y)+r->bottom};
+ SetWindowRect(GetDlgItem(wnd,id),&r1);
+ }
+
+ void _do_align_x(RECT * r,UINT id,UINT wx,UINT min_x)
+ {
+ RECT r1={ (LONG)(wx-min_x+r)->left,(LONG)r->top,(LONG)(wx-min_x+r)->right,(LONG)r->bottom};
+ SetWindowRect(GetDlgItem(wnd,id),&r1);
+ }
+
+ void _do_align_xy(RECT * r,UINT id,UINT wx,UINT wy,UINT min_x,UINT min_y)
+ {
+ RECT r1={(LONG)(wx-min_x+r)->left,(LONG)(wy-min_y+r)->top,(LONG)(wx- min_x+r)->right,(LONG)(wy-min_y+r)->bottom};
+ SetWindowRect(GetDlgItem(wnd,id),&r1);
+ }
+
+#define do_size_x(id,r) _do_size_x(r,id,sx,min_size_x)
+#define do_size_xy(id,r) _do_size_xy(r,id,sx,sy,min_size_x,min_size_y)
+#define do_align_x_size_y(id,r) _do_align_x_size_y(r,id,sx,sy,min_size_x,min_size_y)
+#define do_align_xy(id,r) _do_align_xy(r,id,sx,sy,min_size_x,min_size_y)
+#define do_align_x(id,r) _do_align_x(r,id,sx,min_size_x)
+
+ HWND wnd;
+ UINT min_size_x,min_size_y;
+ UINT min_size_x_w,min_size_y_w;
+
+ void do_sizing(UINT wp,RECT * r)
+ {
+ UINT dx,dy;
+ dx=r->right-r->left;
+ dy=r->bottom-r->top;
+ if (dx<min_size_x_w)
+ {
+ switch(wp)
+ {
+ case WMSZ_BOTTOMLEFT:
+ case WMSZ_LEFT:
+ case WMSZ_TOPLEFT:
+ r->left=r->right-min_size_x_w;
+ break;
+ case WMSZ_BOTTOMRIGHT:
+ case WMSZ_RIGHT:
+ case WMSZ_TOPRIGHT:
+ r->right=r->left+min_size_x_w;
+ break;
+ }
+ }
+ if (dy<min_size_y_w)
+ {
+ switch(wp)
+ {
+ case WMSZ_BOTTOMLEFT:
+ case WMSZ_BOTTOM:
+ case WMSZ_BOTTOMRIGHT:
+ r->bottom=r->top+min_size_y_w;
+ break;
+ case WMSZ_TOPLEFT:
+ case WMSZ_TOP:
+ case WMSZ_TOPRIGHT:
+ r->top=r->bottom-min_size_y_w;
+ break;
+ }
+ }
+ }
+ void MakeComboEdit(UINT id,DWORD s)
+ {
+ HWND w=GetDlgItem(wnd,id);
+ RECT r;
+ GetChildRect(id,r);
+ DestroyWindow(w);
+ CreateWindowEx( WS_EX_CLIENTEDGE, L"EDIT", 0, WS_CHILD | s, r.left - 1, r.top - 1, r.right - r.left, r.bottom - r.top, wnd, (HMENU)id, 0, 0 );
+ }
+ void GetChildRect(UINT id,RECT& child)
+ {
+ RECT r_parent,r_child;
+ GetWindowRect(wnd,&r_parent);
+ GetWindowRect(GetDlgItem(wnd,id),&r_child);
+ int dx=r_parent.left;
+ int dy=r_parent.top;
+ if (!(GetWindowLong(wnd,GWL_STYLE)&WS_CHILD))
+ {
+ dy+=GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYDLGFRAME);
+ dx+=GetSystemMetrics(SM_CXDLGFRAME);
+ }
+ child.left=r_child.left-dx;
+ child.right=r_child.right-dx;
+ child.top=r_child.top-dy;
+ child.bottom=r_child.bottom-dy;
+ }
+
+ virtual BOOL DlgProc(UINT msg,WPARAM wp,LPARAM lp) {return 0;};
+ static BOOL CALLBACK TheDialogProc(HWND wnd,UINT msg,WPARAM wp,LPARAM lp)
+ {
+ DlgBase * p;
+ if (msg==WM_INITDIALOG)
+ {
+ p=(DlgBase*)lp;
+#ifdef WIN64
+ SetWindowLong(wnd, DWLP_USER, (LONG)lp);
+#else
+ SetWindowLong(wnd, DWL_USER, lp);
+#endif
+ p->wnd=wnd;
+ RECT r;
+ GetClientRect(wnd,&r);
+ p->min_size_x=r.right;
+ p->min_size_y=r.bottom;
+ GetWindowRect(wnd,&r);
+ p->min_size_x_w=r.right-r.left;
+ p->min_size_y_w=r.bottom-r.top;
+ }
+#ifdef WIN64
+ else p = (DlgBase*)GetWindowLong(wnd, DWLP_USER);
+#else
+ else p = (DlgBase*)GetWindowLong(wnd, DWL_USER);
+#endif
+ BOOL rv=0;
+ if (p)
+ {
+ rv=p->DlgProc(msg,wp,lp);
+ if (msg==WM_DESTROY)
+ {
+ p->wnd=0;
+ if (p->DieOnDestroyWindow)
+ {
+ delete p;
+#ifdef WIN64
+ SetWindowLong(wnd, DWLP_USER, 0);
+#else
+ SetWindowLong(wnd, DWL_USER, 0);
+#endif
+ }
+ }
+ }
+ return rv;
+ }
+ HWND myCreateDialog(UINT id,HWND parent)
+ {
+ DieOnDestroyWindow=1;
+ is_modeless=1;
+ is_modal_ex=0;
+ return WASABI_API_CREATEDIALOGPARAMW(id,parent,TheDialogProc,(LPARAM)this);
+ }
+ virtual void myProcessMessage(MSG * msg)
+ {
+ if (!IsDialogMessage(wnd,msg))
+ {
+ TranslateMessage(msg);
+ DispatchMessage(msg);
+ }
+ }
+
+ int myDialogBoxEx(UINT id,HWND parent)
+ {
+ DieOnDestroyWindow=0;
+ is_modeless=0;
+ is_modal_ex=1;
+ modal_ex_quit=0;
+ modal_ex_quit_val=-1;
+ WASABI_API_CREATEDIALOGPARAMW(id,parent,TheDialogProc,(LPARAM)this);
+ if (wnd)
+ {
+ BOOL b=IsWindowEnabled(parent);
+
+ if (b) EnableWindow(parent,0);
+
+ MSG msg;
+ while(!modal_ex_quit && GetMessage(&msg,0,0,0))
+ {
+ myProcessMessage(&msg);
+ }
+
+ if (wnd)
+ {
+ DestroyWindow(wnd);
+ wnd=0;
+ }
+
+ if (b) EnableWindow(parent,1);
+ SetActiveWindow(parent);
+ }
+ return modal_ex_quit_val;
+ }
+
+ int myDialogBox(UINT id,HWND parent)
+ {
+ DieOnDestroyWindow=0;
+ is_modeless=0;
+ is_modal_ex=0;
+ return (int)WASABI_API_DIALOGBOXPARAMW(id,parent,TheDialogProc,(LPARAM)this);
+ }
+ DlgBase()
+ {
+ wnd=0;
+ DieOnDestroyWindow=0;
+ is_modeless=0;
+ is_modal_ex=0;
+ modal_ex_quit=0;
+ modal_ex_quit_val=0;
+ min_size_x=min_size_y=min_size_x_w=min_size_y_w=0;
+ }
+ virtual ~DlgBase() {DieOnDestroyWindow=0;if (wnd) DestroyWindow(wnd);}
+public:
+ BOOL isDialogMessage(MSG * m) {return wnd ? IsDialogMessage(wnd,m) : 0;}
+};
+
+static char tags_file[]="tags.txt";
+static wchar_t genres_file[] = L"genres.txt";
+
+class /*_declspec(novtable) */InfoDlgPanel : public DlgBase
+{
+protected:
+ HFONT font;
+ HWND list;
+ RECT info_list;
+ OggTagData hidden;
+
+ InfoDlgPanel(UINT id,HWND parent,HFONT foo)
+ {
+ font=foo;
+ myCreateDialog(id,parent);
+ //SetWindowLong(wnd,GWL_STYLE,GetWindowLong(wnd,GWL_STYLE)|WS_TABSTOP);
+ list=GetDlgItem(wnd,IDC_LIST);
+ SendMessage(list,WM_SETFONT,(WPARAM)font,0);
+
+ GetChildRect(IDC_LIST,info_list);
+ }
+
+ int lb_addstring(TAGDESC * tag,int idx=-1)
+ {
+ StringW tmp;
+ tmp+=tag->name;
+ tmp+=L"=";
+ tmp+=tag->value;
+
+ const WCHAR * p=(const WCHAR*)tmp;
+ const WCHAR * foo=wcsstr(p,L"\x0d\x0a");
+ if (foo)
+ {
+ tmp.Truncate((UINT)(foo-p));
+ tmp.AddString(L" (...)");
+ }
+ int rv=
+ (int)SendMessageW(list,
+ idx<0 ? LB_ADDSTRING : LB_INSERTSTRING,
+ idx<0 ? 0 : idx,
+ ((LPARAM)(const WCHAR*)tmp)
+ );
+ if (rv>=0) SendMessage(list,LB_SETITEMDATA,rv,(LPARAM)tag);
+ else
+ {
+ free(tag->name);
+ free(tag->value);
+ delete tag;
+ }
+ return rv;
+
+ }
+
+ virtual void OnUpdateRect(UINT sx,UINT sy)
+ {//WM_SIZE-ish
+ do_size_xy(IDC_LIST,&info_list);
+ }
+
+ virtual BOOL DlgProc(UINT msg,WPARAM wp,LPARAM lp)
+ {
+ switch(msg)
+ {
+ case WM_DESTROY:
+ lb_clear();
+ list=0;
+ break;
+ case WM_COMMAND:
+ switch(wp)
+ {
+ case IDOK:
+ case IDCANCEL:
+ SendMessage(GetParent(wnd),msg,wp,lp);
+ break;
+ }
+ break;
+// case WM_INITDIALOG:
+// return 1;
+ }
+ return 0;
+ }
+
+ void lb_clear()
+ {
+ if (!list) return;
+ int num=(int)SendMessage(list,LB_GETCOUNT,0,0);
+ while(num>0)
+ {
+ TAGDESC * l=(TAGDESC*)SendMessage(list,LB_GETITEMDATA,0,0);
+ if (l)
+ {
+ free(l->name);
+ free(l->value);
+ delete l;
+ }
+ SendMessage(list,LB_DELETESTRING,0,0);
+ num--;
+ }
+ }
+
+ virtual void SetTag(wchar_t * name,wchar_t * value)
+ {
+ TAGDESC * l=new TAGDESC;
+ l->name=_wcsdup(name);
+ l->value=_wcsdup(value);
+ lb_addstring(l);
+ }
+
+public:
+ virtual void Clear()
+ {
+ hidden.Clear();
+ lb_clear();
+ }
+ void UpdateRect(RECT &r)
+ {
+ SetWindowPos(wnd,0,r.left,r.top,r.right-r.left,r.bottom-r.top,SWP_NOZORDER|SWP_NOACTIVATE|SWP_SHOWWINDOW);
+ RECT z;
+ GetClientRect(wnd,&z);
+ OnUpdateRect(z.right-z.left,z.bottom-z.top);
+ }
+
+ virtual void GetTags(OggTagData & tags)
+ {
+ if (!list) return;
+ int num=(int)SendMessage(list,LB_GETCOUNT,0,0);
+ int n;
+ for(n=0;n<num;n++)
+ {
+ TAGDESC * l=(TAGDESC*)SendMessage(list,LB_GETITEMDATA,n,0);
+ tags.AddTag(l->name,l->value);
+ }
+ TAG * t=hidden.tags;
+ while(t)
+ {
+ tags.AddTag(t->name,t->value);
+ t=t->next;
+ }
+ }
+
+ void SetTags(OggTagData & tags,BOOL hidespec)
+ {
+ TAG * t=tags.tags;
+ while(t)
+ {
+ bool hide=0;
+ if (hidespec)
+ {
+ int n;
+ for(n=0;n<N_SPECIAL_FIELDS;n++)
+ {
+ if (!_wcsicmp(t->name,special_fields[n]))
+ {
+ hidden.AddTag(t->name,t->value);
+ hide=1;break;
+ }
+ }
+ }
+ if (!hide) SetTag(t->name,t->value);
+ t=t->next;
+ }
+ }
+};
+
+class InfoDlgPanel_adv : public InfoDlgPanel
+{
+private:
+ virtual void OnUpdateRect(UINT sx,UINT sy)
+ {
+ InfoDlgPanel::OnUpdateRect(sx,sy);
+ }
+public:
+ InfoDlgPanel_adv(HWND parent,HFONT foo) : InfoDlgPanel(IDD_INFO_PANEL_ADVANCED,parent,foo)
+ {
+ }
+};
+
+class InfoDlgPanel_simple : public InfoDlgPanel
+{
+private:
+ RECT info_static_std,info_title,info_artist,info_album,info_track,info_genre,info_comment,info_static_track,info_static_tags;
+ wchar_t *tag_bk[N_STD_TAGZ];
+ BOOL STFU;
+protected:
+ virtual void OnUpdateRect(UINT sx,UINT sy)
+ {
+ do_size_x(IDC_STATIC_STD,&info_static_std);
+ do_size_x(IDC_TITLE,&info_title);
+ do_size_x(IDC_ARTIST,&info_artist);
+ do_size_x(IDC_ALBUM,&info_album);
+ do_align_x(IDC_TRACK,&info_track);
+ do_size_x(IDC_GENRE,&info_genre);
+ do_size_x(IDC_COMMENT,&info_comment);
+ do_align_x(IDC_STATIC_TRACK,&info_static_track);
+ do_size_xy(IDC_STATIC_TAGS,&info_static_tags);
+ InfoDlgPanel::OnUpdateRect(sx,sy);
+ }
+ virtual BOOL DlgProc(UINT msg,WPARAM wp,LPARAM lp)
+ {
+ switch(msg)
+ {
+ case WM_COMMAND:
+ if (!STFU && (HIWORD(wp)==EN_CHANGE || HIWORD(wp)==CBN_EDITCHANGE || HIWORD(wp)==CBN_SELCHANGE))
+ {
+ UINT n;
+ for(n=0;n<N_STD_TAGZ;n++)
+ {
+ if (LOWORD(wp)==std_tagz[n].id)
+ {
+ if (tag_bk[n]) {free(tag_bk[n]);tag_bk[n]=0;}
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return InfoDlgPanel::DlgProc(msg,wp,lp);
+ }
+public:
+ virtual void Clear()
+ {
+ int n;
+ for(n=0;n<N_STD_TAGZ;n++)
+ {
+ if (tag_bk[n])
+ {
+ free(tag_bk[n]);
+ tag_bk[n]=0;
+ }
+ SetDlgItemText(wnd,std_tagz[n].id,L"");
+ }
+ InfoDlgPanel::Clear();
+
+ }
+ ~InfoDlgPanel_simple()
+ {
+ UINT n;
+ for(n=0;n<N_STD_TAGZ;n++)
+ {
+ if (tag_bk[n]) free(tag_bk[n]);
+ }
+ }
+ InfoDlgPanel_simple(HWND parent,HFONT foo) : InfoDlgPanel(IDD_INFO_PANEL_SIMPLE,parent,foo)
+ {
+ STFU=0;
+ memset(tag_bk,0,sizeof(tag_bk));
+ UINT n;
+ MakeComboEdit(IDC_GENRE,ES_READONLY|ES_AUTOHSCROLL|WS_VISIBLE);
+ for(n=0;n<N_STD_TAGZ;n++)
+ {
+ HWND w=GetDlgItem(wnd,std_tagz[n].id);
+ SendMessage(w,WM_SETFONT,(WPARAM)font,0);
+ SendMessage(w,EM_SETREADONLY,1,0);
+ }
+
+ GetChildRect(IDC_STATIC_STD,info_static_std);
+ GetChildRect(IDC_TITLE,info_title);
+ GetChildRect(IDC_ARTIST,info_artist);
+ GetChildRect(IDC_ALBUM,info_album);
+ GetChildRect(IDC_TRACK,info_track);
+ GetChildRect(IDC_GENRE,info_genre);
+ GetChildRect(IDC_COMMENT,info_comment);
+ GetChildRect(IDC_STATIC_TRACK,info_static_track);
+ GetChildRect(IDC_STATIC_TAGS,info_static_tags);
+
+ genres_read(GetDlgItem(wnd,IDC_GENRE), genres_file);
+ }
+
+ virtual void GetTags(OggTagData & tags)
+ {
+ genres_write(GetDlgItem(wnd,IDC_GENRE),genres_file);
+ UINT n;
+ for(n=0;n<N_STD_TAGZ;n++)
+ {
+ if (tag_bk[n])
+ {
+ tags.AddTag(std_tagz[n].name,tag_bk[n]);
+ }
+ else
+ {
+ StringW t;
+ t.s_GetWindowText(GetDlgItem(wnd,std_tagz[n].id));
+ if (t.Length()>0)
+ {
+ tags.AddTag(std_tagz[n].name,t);
+ }
+ }
+ }
+ InfoDlgPanel::GetTags(tags);
+ }
+ virtual void SetTag(wchar_t * name,wchar_t * value)
+ {
+ STFU=1;
+ UINT n;
+ for(n=0;n<N_STD_TAGZ;n++)
+ {
+ if (tag_bk[n]) continue;
+ if (!_wcsicmp(name,std_tagz[n].name))
+ {
+ tag_bk[n]=_wcsdup(value);
+ SetDlgItemTextWrap(wnd,std_tagz[n].id,value);
+ STFU=0;
+ return;
+ }
+ }
+ STFU=0;
+ InfoDlgPanel::SetTag(name,value);
+ }
+};
+
+char * rstrcpy(char* s1,char* s2)
+{
+ while(s2 && *s2) *(s1++)=*(s2++);
+ return s1;
+}
+
+static void _inline print_misc(VorbisFile * _vf,int link,char * out,int len)
+{
+ OggVorbis_File * vf=&_vf->vf;
+ char* p=out, kbps_str[16] = {0};
+ double t=ov_time_total(vf,link);
+ vorbis_info * vi=ov_info(vf,link);
+ vorbis_comment * vc=ov_comment(vf,link);
+ if (!vi || !vc) {WASABI_API_LNGSTRING_BUF(IDS_FILE_ERROR,out,512);return;}
+
+ StringCchPrintfA(kbps_str, 16, " %s\r\n", WASABI_API_LNGSTRING(IDS_KBPS));
+
+ if (t>0)
+ {
+ p=rstrcpy(p,WASABI_API_LNGSTRING(IDS_LENGTH));
+ int h=(int)(t/3600.0);
+ if (h>0)
+ {
+ uitoa(h,p);
+ while(p && *p) p++;
+ *(p++)=':';
+ }
+ int m=(int)(t/60.0)%60;
+// if (m>0 || h>0)
+ {
+ sprintf(p,h>0 ? "%02u" : "%u",m);
+ while(p && *p) p++;
+ *(p++)=':';
+ }
+ int s=(int)t%60;
+ //sprintf(p,(m>0 || h>0) ? "%02u" : "%u seconds",s);
+ sprintf(p,"%02d",s);
+ while(p && *p) p++;
+ p=rstrcpy(p,"\r\n");
+
+// uitoa((int)(t*1000.0),p);
+// while(p && *p) p++;
+// p=rstrcpy(p," ms");
+ UINT fs=_vf->FileSize();
+ if (fs>0)
+ {
+ if (vf->links==1)
+ {
+ p=rstrcpy(p,WASABI_API_LNGSTRING(IDS_AVERAGE_BITRATE));
+ uitoa((int)(((double)fs)/(t*125.0)),p);
+ while(p && *p) p++;
+ p=rstrcpy(p,kbps_str);
+ }
+ if (fs>0)
+ {
+ p=rstrcpy(p,WASABI_API_LNGSTRING(IDS_FILE_SIZE));
+
+ UINT fs1=fs/1000000;
+ UINT fs2=(fs/1000)%1000;
+ UINT fs3=fs%1000;
+ if (fs1) sprintf(p,"%u%03u%03u %s\r\n",fs1,fs2,fs3,WASABI_API_LNGSTRING(IDS_BYTES));
+ else if (fs2) sprintf(p,"%u%03u %s\r\n",fs2,fs3,WASABI_API_LNGSTRING(IDS_BYTES));
+ else sprintf(p,"%u %s\r\n",fs3,WASABI_API_LNGSTRING(IDS_BYTES));
+ while(p && *p) p++;
+ }
+ }
+ }
+ if (vi->bitrate_nominal>0)
+ {
+ p=rstrcpy(p,WASABI_API_LNGSTRING(IDS_NOMINAL_BITRATE));
+ uitoa(vi->bitrate_nominal/1000,p);
+ while(p && *p) p++;
+ p=rstrcpy(p,kbps_str);
+ }
+ if (vi->bitrate_lower>0)
+ {
+ p=rstrcpy(p,WASABI_API_LNGSTRING(IDS_MIN_BITRATE));
+ uitoa(vi->bitrate_lower/1000,p);
+ while(p && *p) p++;
+ p=rstrcpy(p,kbps_str);
+ }
+ if (vi->bitrate_upper>0)
+ {
+ p=rstrcpy(p,WASABI_API_LNGSTRING(IDS_MAX_BITRATE));
+ uitoa(vi->bitrate_upper/1000,p);
+ while(p && *p) p++;
+ p=rstrcpy(p,kbps_str);
+ }
+
+ char tmp[32] = {0}, tmp2[32] = {0}, tmp3[32] = {0}, tmp4[32] = {0}, tmp5[32] = {0}, tmp6[8] = {0};
+ StringCchPrintfA(p,len, "%s : %u\r\n"
+ "%s : %u %s\r\n"
+ "%s : %u\r\n"
+ "%s : %u\r\n"
+ "%s : \r\n%s",
+ WASABI_API_LNGSTRING_BUF(IDS_CHANNELS,tmp,32),vi->channels,
+ WASABI_API_LNGSTRING_BUF(IDS_SAMPLING_RATE,tmp2,32),vi->rate,WASABI_API_LNGSTRING_BUF(IDS_HZ,tmp6,8),
+ WASABI_API_LNGSTRING_BUF(IDS_SERIAL_NUMBER,tmp3,32),ov_serialnumber(vf,link),
+ WASABI_API_LNGSTRING_BUF(IDS_VERSION,tmp4,32),vi->version,
+ WASABI_API_LNGSTRING_BUF(IDS_VENDOR,tmp5,32),vc->vendor);
+}
+
+class InfoDlg : public DlgBase
+{
+private:
+ InfoDlg * next;
+ static InfoDlg* Instances;
+ OggTagData **tags;
+ char ** infos;
+ int n_streams,cur_stream;
+ StringW url;
+ HFONT ui_font;
+ BOOL is_adv;
+ InfoDlgPanel* panel;
+ RECT info_static_misc,info_misc,info_url,info_cancel,info_mode,info_static_cs,info_cs_next,info_cs_prev,info_hidespec;
+
+ void calc_panel_rect(RECT &r)
+ {
+ RECT cr,cr1,cr2;
+ GetClientRect(wnd,&cr);
+ GetChildRect(IDC_STATIC_MISC,cr1);
+ GetChildRect(IDC_URL,cr2);
+ r.left=0;
+ r.top=cr2.bottom+1;
+ r.right=cr1.left-1;
+ r.bottom=cr.bottom;
+ }
+ void do_size()
+ {
+ if (panel)
+ {
+ RECT r;
+ calc_panel_rect(r);
+ panel->UpdateRect(r);
+ }
+ }
+ void do_panel(BOOL mode,int d_stream)
+ {
+ //if (panel && is_adv==mode && !d_stream) return;
+ if (panel)
+ {
+ if (mode!=is_adv)
+ {
+ delete panel;
+ panel=0;
+ }
+ else
+ {
+ panel->Clear();
+ }
+ }
+ cur_stream+=d_stream;
+ if (cur_stream<0) cur_stream=0;
+ else if (cur_stream>=n_streams) cur_stream=n_streams-1;
+ is_adv=mode;
+ if (!panel)
+ {
+ panel = mode ? (InfoDlgPanel*) new InfoDlgPanel_adv(wnd,ui_font) : (InfoDlgPanel*) new InfoDlgPanel_simple(wnd,ui_font);
+ do_size();
+ }
+ if (panel)
+ {
+ panel->SetTags(*tags[cur_stream],(BOOL)SendDlgItemMessage(wnd,IDC_HIDE_SPEC,BM_GETCHECK,0,0));
+ }
+ SetDlgItemText(wnd,IDC_MODE_TOGGLE,WASABI_API_LNGSTRINGW((is_adv ? IDS_TO_SIMPLE_MODE : IDS_TO_ADVANCED_MODE)));
+ EnableWindow(GetDlgItem(wnd,IDC_PREV_STREAM),cur_stream>0);
+ EnableWindow(GetDlgItem(wnd,IDC_NEXT_STREAM),cur_stream<n_streams-1);
+ // TODO
+ SetDlgItemTextA(wnd,IDC_MISC,infos[cur_stream]);
+ }
+protected:
+ virtual BOOL DlgProc(UINT msg,WPARAM wp,LPARAM lp)
+ {
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ if (n_streams<=1)
+ {
+ ShowWindow(GetDlgItem(wnd,IDC_STATIC_CS),SW_HIDE);
+ ShowWindow(GetDlgItem(wnd,IDC_PREV_STREAM),SW_HIDE);
+ ShowWindow(GetDlgItem(wnd,IDC_NEXT_STREAM),SW_HIDE);
+ }
+
+ SendDlgItemMessage(wnd,IDC_HIDE_SPEC,BM_SETCHECK,cfg_hide_special_fields,0);
+
+ do_panel(cfg_adv_info,0);
+ url.s_SetDlgItemText(wnd,IDC_URL);
+ GetChildRect(IDC_URL,info_url);
+ GetChildRect(IDC_STATIC_MISC,info_static_misc);
+ GetChildRect(IDC_MISC,info_misc);
+ GetChildRect(IDCANCEL,info_cancel);
+ GetChildRect(IDC_MODE_TOGGLE,info_mode);
+ GetChildRect(IDC_STATIC_CS,info_static_cs);
+ GetChildRect(IDC_NEXT_STREAM,info_cs_next);
+ GetChildRect(IDC_PREV_STREAM,info_cs_prev);
+ GetChildRect(IDC_HIDE_SPEC,info_hidespec);
+
+ if (cfg_remember_infosize && cfg_infobox_sx>0 && cfg_infobox_sy>0)
+ {
+ int max_x=GetSystemMetrics(SM_CXSCREEN),max_y=GetSystemMetrics(SM_CYSCREEN);
+ if (cfg_infobox_x<0) cfg_infobox_x=0;
+ else if (cfg_infobox_x+cfg_infobox_sx>max_x) cfg_infobox_x=max_x-cfg_infobox_sx;
+ if (cfg_infobox_y<0) cfg_infobox_y=0;
+ else if (cfg_infobox_y+cfg_infobox_sy>max_y) cfg_infobox_y=max_y-cfg_infobox_sy;
+
+ SetWindowPos(wnd,0,cfg_infobox_x,cfg_infobox_y,cfg_infobox_sx,cfg_infobox_sy,SWP_NOZORDER|SWP_NOACTIVATE);
+ }
+
+ StringPrintfW(WASABI_API_LNGSTRINGW(IDS_OGG_VORBIS_INFO),(const WCHAR*)StringF2T_W((const WCHAR*)url)).s_SetWindowText(wnd);
+ return 1;
+ case WM_SIZE:
+ {
+ UINT sx=LOWORD(lp),sy=HIWORD(lp);
+ do_size_x(IDC_URL,&info_url);
+ do_align_x(IDC_STATIC_MISC,&info_static_misc);
+ do_align_x(IDC_MISC,&info_misc);
+ do_align_xy(IDCANCEL,&info_cancel);
+ do_align_xy(IDC_MODE_TOGGLE,&info_mode);
+ do_align_xy(IDC_STATIC_CS,&info_static_cs);
+ do_align_xy(IDC_PREV_STREAM,&info_cs_prev);
+ do_align_xy(IDC_NEXT_STREAM,&info_cs_next);
+ do_align_xy(IDC_HIDE_SPEC,&info_hidespec);
+ }
+ //RedrawWindow(wnd,0,0,RDW_INVALIDATE);
+ do_size();
+ break;
+ case WM_SIZING:
+ do_sizing((UINT)wp,(RECT*)lp);
+ break;
+ case WM_COMMAND:
+ switch(wp)
+ {
+ case IDCANCEL:
+ endDialog(0);
+ break;
+ case IDC_MODE_TOGGLE:
+ do_panel(!is_adv,0);
+ break;
+ case IDC_PREV_STREAM:
+ do_panel(is_adv,-1);
+ break;
+ case IDC_NEXT_STREAM:
+ do_panel(is_adv,1);
+ break;
+ case IDC_HIDE_SPEC:
+ cfg_hide_special_fields=(int)SendMessage((HWND)lp,BM_GETCHECK,0,0);
+ do_panel(is_adv,0);
+ break;
+ }
+ break;
+ case WM_CLOSE:
+ endDialog(0);
+ break;
+ case WM_DESTROY:
+ if (!is_modeless)//fucko close
+ {
+ modal_ex_quit=1;
+ }
+
+ {
+ RECT r;
+ GetWindowRect(wnd,&r);
+ cfg_infobox_sx=r.right-r.left;
+ cfg_infobox_sy=r.bottom-r.top;
+ cfg_infobox_x=r.left;
+ cfg_infobox_y=r.top;
+ }
+ break;
+ }
+ return 0;
+ }
+ virtual void myProcessMessage(MSG * msg)
+ {
+ if (!panel || !panel->isDialogMessage(msg))
+ {
+ DlgBase::myProcessMessage(msg);
+ }
+ }
+
+public:
+ InfoDlg(VorbisFile * _vf,const wchar_t * _url)
+ : url(_url), is_adv(FALSE)
+ {
+ OggVorbis_File * vf=&_vf->vf;
+ n_streams=vf->links;
+ cur_stream=vf->current_link;
+ tags=(OggTagData**)malloc(n_streams*sizeof(void*));
+ int n;
+ for(n=0;n<n_streams;n++) tags[n]=new OggTagData(ov_comment(vf,n));
+ infos=(char**)malloc(sizeof(void*)*n_streams);
+ for(n=0;n<n_streams;n++)
+ {
+ int l = 512+(int)strlen(vf->vc->vendor);
+ infos[n]=(char*)malloc(l);
+ print_misc(_vf,n,infos[n],l);
+ }
+
+ ui_font=CreateFontIndirect(&cfg_font.data);
+ panel=0;
+ next=0;
+ }
+ ~InfoDlg()
+ {
+ if (ui_font) DeleteObject((HGDIOBJ)ui_font);
+ cfg_adv_info=is_adv;
+ if (tags)
+ {
+ int n;
+ for(n=0;n<n_streams;n++)
+ delete tags[n];
+ free(tags);
+ }
+ if (infos)
+ {
+ int n;
+ for(n=0;n<n_streams;n++) free(infos[n]);
+ free(infos);
+ }
+
+ InfoDlg ** pp=&Instances,*p=Instances;
+ while(p)
+ {
+ if (p==this)
+ {
+ *pp=next;
+ break;
+ }
+ else {pp=&p->next;p=*pp;}
+ }
+ }
+ void Run(HWND parent,bool modeless)
+ {
+ next=Instances;
+ Instances=this;//HACK - prevent crash on shutdown (used to be only for modeless)
+
+ if (modeless)
+ {
+ myCreateDialog(IDD_INFO_DLG_NEW,parent);
+ }
+ else myDialogBoxEx(IDD_INFO_DLG_NEW,parent);
+ }
+
+friend int RunInfoDlg(const in_char * url,HWND parent);
+};
+
+int RunInfoDlg(const in_char * url,HWND parent)
+{
+ static bool in_modal;
+ if (in_modal) return 1;
+ else in_modal=1;
+ VorbisFile * vf;
+ bool vf_global=0;
+ int ret=0;
+ StringW _url;
+ _url.AddString(url);
+
+ {
+ InfoDlg * p=InfoDlg::Instances;
+ while(p)
+ {
+ if (!_wcsicmp(p->url,_url))
+ {
+ ShowWindow(p->wnd,SW_SHOW);
+ SetActiveWindow(p->wnd);
+ return 0;
+ }
+ p=p->next;
+ }
+ }
+
+ EnterCriticalSection(&sync);
+ if ((url==cur_file || !_wcsicmp(url,cur_file)) && theFile) {vf=theFile;vf_global=1;}
+ else
+ {
+ LeaveCriticalSection(&sync);
+ vf=VorbisFile::Create(url,1);
+ if (!vf)
+ {
+ in_modal=0;
+ return 0;
+ }
+ }
+ {
+ InfoDlg d(vf,_url);
+ if (vf_global) LeaveCriticalSection(&sync);
+ else delete vf;
+ d.Run(parent,0);
+ ret = !d.modal_ex_quit_val;
+ }
+ in_modal=0;
+ return ret;
+}
+
+InfoDlg* InfoDlg::Instances=0;
+/* end crappy PP dialog */
+
+bool VorbisTagToWinampTag(wchar_t * tag, int len)
+{
+#define TAG_ALIAS(b,a) if(!_wcsicmp(L ## a, tag)) { lstrcpynW(tag, L ## b, len); return true; }
+ TAG_ALIAS("title", "TITLE");
+ TAG_ALIAS("artist", "ARTIST");
+ TAG_ALIAS("album", "ALBUM");
+ TAG_ALIAS("genre", "GENRE");
+ TAG_ALIAS("comment", "COMMENT");
+ TAG_ALIAS("year", "DATE");
+ TAG_ALIAS("track", "TRACKNUMBER");
+ TAG_ALIAS("albumartist", "ALBUMARTIST");
+ TAG_ALIAS("composer", "COMPOSER");
+ TAG_ALIAS("disc", "DISCNUMBER");
+ TAG_ALIAS("publisher", "PUBLISHER");
+ TAG_ALIAS("conductor", "CONDUCTOR");
+ TAG_ALIAS("bpm", "BPM");
+ return false;
+#undef TAG_ALIAS
+}
+
+bool WinampTagToVorbisTag(wchar_t * tag, int len)
+{
+#define TAG_ALIAS(a,b) if(!_wcsicmp(L ## a, tag)) { lstrcpynW(tag, L ## b, len); return true; }
+ TAG_ALIAS("title", "TITLE");
+ TAG_ALIAS("artist", "ARTIST");
+ TAG_ALIAS("album", "ALBUM");
+ TAG_ALIAS("genre", "GENRE");
+ TAG_ALIAS("comment", "COMMENT");
+ TAG_ALIAS("year", "DATE");
+ TAG_ALIAS("track", "TRACKNUMBER");
+ TAG_ALIAS("albumartist", "ALBUMARTIST");
+ TAG_ALIAS("composer", "COMPOSER");
+ TAG_ALIAS("disc", "DISCNUMBER");
+ TAG_ALIAS("publisher", "PUBLISHER");
+ TAG_ALIAS("conductor", "CONDUCTOR");
+ TAG_ALIAS("bpm", "BPM");
+ return false;
+#undef TAG_ALIAS
+}
+
+static INT_PTR CALLBACK ChildProc_Advanced(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
+ static int sel=-1;
+ static int ismychange=0;
+ switch(msg)
+ {
+ case WM_NOTIFYFORMAT:
+ return NFR_UNICODE;
+ case WM_INITDIALOG:
+ {
+ #define ListView_InsertColumnW(hwnd, iCol, pcol) \
+ (int)SNDMSG((hwnd), LVM_INSERTCOLUMNW, (WPARAM)(int)(iCol), (LPARAM)(const LV_COLUMNW *)(pcol))
+ SetWindowLongPtr(hwndDlg,GWLP_USERDATA,lParam);
+ sel=-1;
+ HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST);
+ ListView_SetExtendedListViewStyle(hwndlist, LVS_EX_FULLROWSELECT);
+ LVCOLUMNW lvc = {0, };
+ lvc.mask = LVCF_TEXT|LVCF_WIDTH;
+ lvc.pszText = WASABI_API_LNGSTRINGW(IDS_NAME);
+ lvc.cx = 82;
+ ListView_InsertColumnW(hwndlist, 0, &lvc);
+ lvc.pszText = WASABI_API_LNGSTRINGW(IDS_VALUE);
+ lvc.cx = 160;
+ ListView_InsertColumnW(hwndlist, 1, &lvc);
+
+ Info *info = (Info *)lParam;
+ int n = info->GetNumMetadataItems();
+ for(int i=0; i<n; i++) {
+ wchar_t key[512] = {0};
+ wchar_t value[2048] = {0};
+ info->EnumMetadata(i,key,512,value,2048);
+ if(value[0] && key[0]) {
+ LVITEMW lvi={LVIF_TEXT,i,0};
+ lvi.pszText = key;
+ SendMessage(hwndlist,LVM_INSERTITEMW,0,(LPARAM)&lvi);
+ lvi.iSubItem=1;
+ lvi.pszText = (wchar_t*)value;
+ SendMessage(hwndlist,LVM_SETITEMW,0,(LPARAM)&lvi);
+ }
+ }
+ ListView_SetColumnWidth(hwndlist,0,(n?LVSCW_AUTOSIZE:LVSCW_AUTOSIZE_USEHEADER));
+ ListView_SetColumnWidth(hwndlist,1,(n?LVSCW_AUTOSIZE:LVSCW_AUTOSIZE_USEHEADER));
+
+ SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
+ SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
+ EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
+ }
+ break;
+ case WM_DESTROY:
+ {
+ HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST);
+ ListView_DeleteAllItems(hwndlist);
+ while(ListView_DeleteColumn(hwndlist,0));
+ Info * info = (Info*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ delete info;
+ }
+ break;
+ case WM_USER:
+ if(wParam && lParam && !ismychange)
+ {
+ wchar_t * value = (wchar_t*)lParam;
+ wchar_t tag[100] = {0};
+ lstrcpynW(tag,(wchar_t*)wParam,100);
+ WinampTagToVorbisTag(tag,100);
+ Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if(!*value)
+ {
+ info->RemoveMetadata(tag);
+ if(!_wcsicmp(L"ALBUMARTIST",tag))
+ {
+ // need to remove these two, also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using these alternate fields
+ info->RemoveMetadata(L"ALBUM ARTIST");
+ info->RemoveMetadata(L"ENSEMBLE");
+ }
+ if(!_wcsicmp(L"PUBLISHER",tag))
+ {
+ // need to remove this also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using this alternate field
+ info->RemoveMetadata(L"ORGANIZATION");
+ }
+ if(!_wcsicmp(L"DATE",tag))
+ {
+ // need to remove this also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using this alternate field
+ info->RemoveMetadata(L"YEAR");
+ }
+ if(!_wcsicmp(L"TRACKNUMBER",tag))
+ {
+ // need to remove this also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using this alternate field
+ info->RemoveMetadata(L"TRACK");
+ }
+ }
+ else
+ {
+ info->SetMetadata(tag,value);
+ }
+
+ HWND hlist = GetDlgItem(hwndDlg,IDC_LIST);
+ int n = ListView_GetItemCount(hlist);
+ for(int i=0; i<n; i++)
+ {
+ wchar_t key[100]=L"";
+ LVITEMW lvi={LVIF_TEXT,i,0};
+ lvi.pszText=key;
+ lvi.cchTextMax=100;
+ SendMessage(hlist,LVM_GETITEMW,0,(LPARAM)&lvi);
+ if(!_wcsicmp(key,tag))
+ {
+ lvi.iSubItem = 1;
+ lvi.pszText = value;
+ SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi);
+ if(!*value)
+ ListView_DeleteItem(hlist,i);
+ else if(ListView_GetItemState(hlist,i,LVIS_SELECTED))
+ SetDlgItemTextW(hwndDlg,IDC_VALUE,value);
+ return 0;
+ }
+ }
+ // bew hew, not found
+ LVITEMW lvi={0,0x7FFFFFF0,0};
+ n = (int)SendMessage(hlist,LVM_INSERTITEMW,0,(LPARAM)&lvi);
+ lvi.mask = LVIF_TEXT;
+ lvi.iItem = n;
+ lvi.iSubItem = 0;
+ lvi.pszText = tag;
+ SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi);
+ lvi.iSubItem = 1;
+ lvi.pszText = value;
+ SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi);
+ }
+ break;
+ case WM_NOTIFY:
+ {
+ LPNMHDR l=(LPNMHDR)lParam;
+ if(l->idFrom==IDC_LIST && l->code == LVN_KEYDOWN) {
+ if((((LPNMLVKEYDOWN)l)->wVKey) == VK_DELETE){
+ int selitem = ListView_GetNextItem(l->hwndFrom,-1,LVNI_SELECTED|LVNI_FOCUSED);
+ if(selitem != -1)
+ SendMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_BUTTON_DEL,BN_CLICKED),(LPARAM)GetDlgItem(hwndDlg,IDC_BUTTON_DEL));
+ }
+ }
+ else if(l->idFrom==IDC_LIST && l->code == LVN_ITEMCHANGED) {
+ LPNMLISTVIEW lv=(LPNMLISTVIEW)lParam;
+ if(lv->uNewState & LVIS_SELECTED) {
+ int n = lv->iItem;
+ LVITEMW lvi={LVIF_TEXT,lv->iItem,0};
+ wchar_t key[100] = {0};
+ wchar_t value[1024] = {0};
+ lvi.pszText=key;
+ lvi.cchTextMax=100;
+ SendMessage(l->hwndFrom,LVM_GETITEMW,0,(LPARAM)&lvi);
+ lvi.pszText=value;
+ lvi.cchTextMax=1024;
+ lvi.iSubItem=1;
+ SendMessage(l->hwndFrom,LVM_GETITEMW,0,(LPARAM)&lvi);
+ SetDlgItemTextW(hwndDlg,IDC_NAME,key);
+ SetDlgItemTextW(hwndDlg,IDC_VALUE,value);
+ sel = n;
+ EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),TRUE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),TRUE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),TRUE);
+ }
+ if(lv->uOldState & LVIS_SELECTED) {
+ sel = -1;
+ SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
+ SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
+ EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
+ }
+ }
+ }
+ break;
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDOK:
+ {
+ Info * info = (Info*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
+ if (!info->Save())
+ {
+ MessageBox(hwndDlg,
+ L"Cannot save metadata: Error writing file or file is read-only.",
+ L"Error saving metadata.",
+ MB_OK);
+ }
+ }
+ break;
+ case IDC_NAME:
+ case IDC_VALUE:
+ if(HIWORD(wParam) == EN_CHANGE && sel>=0) {
+ wchar_t key[100] = {0};
+ wchar_t value[1024] = {0};
+ LVITEMW lvi={LVIF_TEXT,sel,0};
+ GetDlgItemTextW(hwndDlg,IDC_NAME,key,100);
+ GetDlgItemTextW(hwndDlg,IDC_VALUE,value,1024);
+ lvi.pszText=key;
+ lvi.cchTextMax=100;
+ SendMessage(GetDlgItem(hwndDlg,IDC_LIST),LVM_SETITEMW,0,(LPARAM)&lvi);
+ lvi.pszText=value;
+ lvi.cchTextMax=1024;
+ lvi.iSubItem=1;
+ SendMessage(GetDlgItem(hwndDlg,IDC_LIST),LVM_SETITEMW,0,(LPARAM)&lvi);
+ VorbisTagToWinampTag(key,100);
+ ismychange=1;
+ SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)value);
+ ismychange=0;
+ }
+ else if(HIWORD(wParam) == EN_KILLFOCUS && sel>=0) {
+ wchar_t key[100] = {0};
+ wchar_t value[1024] = {0};
+ GetDlgItemTextW(hwndDlg,IDC_NAME,key,100);
+ GetDlgItemTextW(hwndDlg,IDC_VALUE,value,1024);
+ Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ wchar_t oldkey[100]=L"";
+ bool newitem=true;
+ if(sel < info->GetNumMetadataItems()) {
+ info->EnumMetadata(sel,oldkey,100,0,0);
+ newitem=false;
+ }
+
+ if(!newitem && wcscmp(oldkey,key)) { // key changed
+ info->SetTag(sel,key);
+ } else {
+ info->SetMetadata(key,value);
+ }
+ VorbisTagToWinampTag(key,100);
+ ismychange=1;
+ SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)value);
+ ismychange=0;
+ }
+ break;
+ case IDC_BUTTON_DEL:
+ if(sel >= 0) {
+ wchar_t tag[100] = {0};
+ GetDlgItemTextW(hwndDlg,IDC_NAME,tag,100);
+ SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
+ SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
+ EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
+ Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if(sel < info->GetNumMetadataItems())
+ info->RemoveMetadata(sel);
+ ListView_DeleteItem(GetDlgItem(hwndDlg,IDC_LIST),sel);
+ sel=-1;
+ VorbisTagToWinampTag(tag,100);
+ ismychange=1;
+ SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)tag,(WPARAM)L"");
+ ismychange=0;
+ }
+ break;
+ case IDC_BUTTON_DELALL:
+ ListView_DeleteAllItems(GetDlgItem(hwndDlg,IDC_LIST));
+ SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
+ SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
+ EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
+ sel=-1;
+ {
+ Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ int n = info->GetNumMetadataItems();
+ while(n>0) {
+ --n;
+ wchar_t tag[100] = {0};
+ info->EnumMetadata(n,tag,100,0,0);
+ VorbisTagToWinampTag(tag,100);
+ ismychange=1;
+ SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)tag,(WPARAM)L"");
+ ismychange=0;
+ info->RemoveMetadata(n);
+ }
+ }
+ break;
+ case IDC_BUTTON_ADD:
+ {
+ HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST);
+ LVITEMW lvi={0,0x7FFFFFF0,0};
+ int n = (int)SendMessage(hwndlist,LVM_INSERTITEMW,0,(LPARAM)&lvi);
+ ListView_SetItemState(hwndlist,n,LVIS_SELECTED,LVIS_SELECTED);
+ }
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+extern "C"
+{
+ // return 1 if you want winamp to show it's own file info dialogue, 0 if you want to show your own (via In_Module.InfoBox)
+ // if returning 1, remember to implement winampGetExtendedFileInfo("formatinformation")!
+ __declspec(dllexport) int winampUseUnifiedFileInfoDlg(const wchar_t * fn)
+ {
+ if (PathIsURLW(fn))
+ return 0;
+ return 1;
+ }
+
+ // should return a child window of 513x271 pixels (341x164 in msvc dlg units), or return NULL for no tab.
+ // Fill in name (a buffer of namelen characters), this is the title of the tab (defaults to "Advanced").
+ // filename will be valid for the life of your window. n is the tab number. This function will first be
+ // called with n == 0, then n == 1 and so on until you return NULL (so you can add as many tabs as you like).
+ // The window you return will recieve WM_COMMAND, IDOK/IDCANCEL messages when the user clicks OK or Cancel.
+ // when the user edits a field which is duplicated in another pane, do a SendMessage(GetParent(hwnd),WM_USER,(WPARAM)L"fieldname",(LPARAM)L"newvalue");
+ // this will be broadcast to all panes (including yours) as a WM_USER.
+ __declspec(dllexport) HWND winampAddUnifiedFileInfoPane(int n, const wchar_t * filename, HWND parent, wchar_t *name, size_t namelen)
+ {
+ if(n == 0) { // add first pane
+ SetPropW(parent,L"INBUILT_NOWRITEINFO", (HANDLE)1);
+ Info *info = new Info(filename);
+ if(info->Error())
+ {
+ delete info;
+ return NULL;
+ }
+ return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO,parent,ChildProc_Advanced,(LPARAM)info);
+ }
+ return NULL;
+ }
+}; \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/localfile.cpp b/Src/Plugins/Input/in_vorbis/localfile.cpp
new file mode 100644
index 00000000..7f163f7d
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/localfile.cpp
@@ -0,0 +1,260 @@
+#include "main.h"
+#include "../nu/AutoChar.h"
+
+extern CfgInt cfg_fullbuf;
+
+int VorbisFile::_f_close(void *) {return 0;}
+
+int VorbisFile::_f_seek(void* rs,__int64 offset,int whence)
+{
+ return ((VorbisFile*)rs)->f_seek(offset,whence);
+}
+
+size_t VorbisFile::_f_read(void* ptr,size_t size,size_t nmemb,void * rs)
+{
+ return ((VorbisFile*)rs)->f_read((UINT)(size*nmemb),ptr);
+}
+
+long VorbisFile::_f_tell(void* rs)
+{
+ return ((VorbisFile*)rs)->f_tell();
+}
+
+ov_callbacks VorbisFile::oc={_f_read,_f_seek,_f_close,_f_tell};
+
+static __int64 Seek64(HANDLE hf, __int64 distance, DWORD MoveMethod)
+{
+ LARGE_INTEGER li;
+
+ li.QuadPart = distance;
+
+ li.LowPart = SetFilePointer (hf, li.LowPart, &li.HighPart, MoveMethod);
+
+ if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
+ {
+ li.QuadPart = -1;
+ }
+
+ return li.QuadPart;
+}
+
+static __int64 FileSize64(HANDLE file)
+{
+ LARGE_INTEGER position;
+ position.QuadPart=0;
+ position.LowPart = GetFileSize(file, (LPDWORD)&position.HighPart);
+
+ if (position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
+ return INVALID_FILE_SIZE;
+ else
+ return position.QuadPart;
+}
+
+class VorbisFile_Local : public VorbisFile
+{
+private:
+ HANDLE hFile;
+protected:
+ int f_seek(__int64 offset,int whence)
+ {
+ if(whence==SEEK_SET) offset+=baseoffs;
+ if (Seek64(hFile,offset,whence) != INVALID_SET_FILE_POINTER) return 0;
+ else return -1;
+ }
+
+ size_t f_read(UINT siz,void * ptr)
+ {
+ DWORD bw=0;
+ ReadFile(hFile,ptr,siz,&bw,0);
+ return bw;
+ }
+
+ UINT f_tell()
+ {
+ return (UINT)(SetFilePointer(hFile,0,0,FILE_CURRENT)-baseoffs);
+ }
+
+ UINT FileSize()
+ {
+ return (UINT)(FileSize64(hFile)-baseoffs);
+ }
+
+public:
+ virtual int GetType() {return TYPE_LOCAL;}
+ VorbisFile_Local(HANDLE f,const wchar_t * u,bool is_info) : VorbisFile(u,is_info) {hFile=f;}
+ ~VorbisFile_Local() {CloseHandle(hFile);}
+};
+
+class VorbisFile_Mem : public VorbisFile
+{
+ BYTE * block;
+ UINT size,ptr;
+protected:
+ int f_seek(__int64 offset,int whence)
+ {
+ switch(whence)
+ {
+ case SEEK_SET:
+ ptr=(UINT)(offset+baseoffs);
+ break;
+ case SEEK_CUR:
+ ptr+=(UINT)offset;
+ break;
+ case SEEK_END:
+ ptr=size+whence;
+ break;
+ }
+ if (ptr<=size) return 0;
+ else {ptr=size;return -1;}
+ }
+
+ size_t f_read(UINT siz,void * out)
+ {
+ UINT d=size-ptr;
+ if (d>siz) d=siz;
+ memcpy(out,block+ptr,d);
+ ptr+=d;
+ return d;
+ }
+
+ UINT f_tell()
+ {
+ return (UINT)(ptr-baseoffs);
+ }
+
+ UINT FileSize()
+ {
+ return (UINT)(size-baseoffs);
+ }
+
+public:
+ virtual int GetType() {return TYPE_LOCAL;}
+
+ VorbisFile_Mem(HANDLE f,const wchar_t * u,bool is_info) : VorbisFile(u,is_info)
+ {
+ size=GetFileSize(f,0);
+ ptr=0;
+ block=(BYTE*)malloc(size);
+ DWORD br = 0;
+ ReadFile(f,block,size,&br,0);
+ CloseHandle(f);
+ }
+
+ ~VorbisFile_Mem() {free(block);}
+};
+
+VorbisFile * VorbisFile::Create(const wchar_t *url, bool is_info)
+{
+ VorbisFile * r;
+ if (PathIsURLW(url))
+ {
+ if (is_info) return 0;
+ r=Create_HTTP(AutoChar(url),is_info);
+ }
+ else
+ {
+ __int64 baseoffs=0;
+ HANDLE f=CreateFileW(url,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0);
+ if (f==INVALID_HANDLE_VALUE) return 0;
+ {
+ DWORD dw = 0, br = 0;
+ ReadFile(f,&dw,4,&br,0);
+ if(br==4 && dw=='SggO')
+ {
+ SetFilePointer(f,0,0,FILE_BEGIN);
+ }
+ else if(br==4 && dw=='FFIR')
+ {
+ //RIFF file
+ DWORD wavhdr = 0, nb = 0;
+ SetFilePointer(f,4,0,FILE_CURRENT);
+ ReadFile(f,&wavhdr,4,&nb,0);
+ if(nb!=4 || wavhdr!='EVAW')
+ {
+ goto abort;
+ }
+
+ //find data starting point
+ char tmp[1024] = {0};
+ ReadFile(f,&tmp,1024,&nb,0);
+ for(int i=0;i<1020;i++)
+ if(tmp[i]=='d'&&tmp[i+1]=='a'&&tmp[i+2]=='t'&&tmp[i+3]=='a')
+ {
+ baseoffs=i+12+8;
+ Seek64(f, baseoffs, FILE_BEGIN);
+ }
+
+ if(!baseoffs) goto abort;
+ }
+ else
+ {
+ abort:
+ CloseHandle(f);
+ return 0;
+ }
+ }
+
+ r=cfg_fullbuf ? (VorbisFile*)new VorbisFile_Mem(f,url,is_info) : (VorbisFile*)new VorbisFile_Local(f,url,is_info);
+ r->setBaseOffset(baseoffs);
+ }
+ if (r && !r->init())
+ {
+ delete r;
+ r=0;
+ }
+ return r;
+}
+
+bool VorbisFile::init()
+{
+ if (ov_open_callbacks(this,&vf,0,0,oc)) return 0;
+ //TODO bitrate
+ UINT siz=FileSize();
+ double len=Length();
+ if (siz>0 && len>0)
+ {
+ UINT divisor = (UINT)(len*125.0);
+ if (divisor)
+ avg_kbps=siz/divisor;
+ }
+
+ post_init();
+ return 1;
+}
+
+int is_http(const char* url)
+{
+ return (!_strnicmp(url,"http://",7) || !_strnicmp(url,"https://",8));
+}
+
+void VorbisFile::set_meta(const vorbis_comment * vc,int links)
+{
+ if (links == vf.links)
+ {
+ int n;
+ for(n=0;n<links;n++)
+ {
+ vorbis_comment_clear(vf.vc+n);
+ /*
+ extern void vorbis_comment_init(vorbis_comment *vc);
+ extern void vorbis_comment_add(vorbis_comment *vc, char *comment);
+ extern void vorbis_comment_add_tag(vorbis_comment *vc,char *tag, char *contents);
+ extern char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count);
+ extern int vorbis_comment_query_count(vorbis_comment *vc, char *tag);
+ extern void vorbis_comment_clear(vorbis_comment *vc);
+ */
+ }
+ _ogg_free(vf.vc);
+ vf.vc = (vorbis_comment*) _ogg_calloc(links,sizeof(vorbis_comment));
+ for(n=0;n<links;n++)
+ {
+ vorbis_comment_init(vf.vc+n);
+ int c;
+ for(c=0;c<vc[n].comments;c++)
+ {
+ vorbis_comment_add(vf.vc+n,vc[n].user_comments[c]);
+ }
+ vf.vc[n].vendor = _strdup(vc[n].vendor);
+ }
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/main.h b/Src/Plugins/Input/in_vorbis/main.h
new file mode 100644
index 00000000..79c09dae
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/main.h
@@ -0,0 +1,220 @@
+#ifndef IN_VORBIS_MAIN_H
+#define IN_VORBIS_MAIN_H
+
+#define WINSOCK_API_LINKAGE
+
+#ifndef STRICT
+#define STRICT
+#endif
+
+#include <windows.h>
+
+extern int (*warand)();
+extern float (*warandf)();
+
+inline void * z_malloc(int x)
+{
+ void* foo=malloc(x);
+ if (foo) memset(foo,0,x);
+ return foo;
+}
+#include <shlwapi.h>
+#include <malloc.h>
+#define uitoa(x,y) _itoa(x,y,10)
+#define atoui atoi
+
+#include <vorbis\vorbisfile.h>
+#include "c_string.h"
+#include "../Winamp/in2.h"
+
+extern In_Module mod;
+
+#include "resource.h"
+
+#define VER L"1.79"
+#define _NAME "Nullsoft Vorbis Decoder"
+
+extern "C"
+{
+ extern const char *INI_FILE;
+ extern const wchar_t *INI_DIRECTORY;
+}
+class CfgVar
+{
+private:
+ String name;
+ CfgVar * next;
+ static CfgVar * list;
+public:
+
+ static void ReadConfig();
+ static void WriteConfig();
+
+ //helpers
+ static bool read_struct(const char *inifile, const char *section, const char * name,void * ptr,UINT size);
+ static void write_struct(const char *inifile, const char *section, const char * name,void * ptr,UINT size);
+ static void write_int(const char *inifile, const char *section, const char * name,int val);
+ static int read_int(const char *inifile, const char *section,const char * name,int def);
+
+protected:
+ CfgVar(const char * n) : name(n) {next=list;list=this;}
+ virtual void Read(const char * name)=0;
+ virtual void Write(const char * name)=0;
+};
+
+class CfgInt : private CfgVar
+{
+private:
+ int def,value;
+public:
+ CfgInt(const char * name,int _def) : CfgVar(name) {value=def=_def;}
+ inline int operator=(int x) {value=x;return value;}
+ inline operator int() {return value;}
+private:
+ virtual void Read(const char * name);
+ virtual void Write(const char * name);
+};
+
+class CfgString : private CfgVar, public StringW
+{
+private:
+ StringW def;
+public:
+ CfgString(const char * name,const char * _def) : CfgVar(name), StringW(_def), def(_def) {}
+private:
+ virtual void Read(const char * name);
+ virtual void Write(const char * name);
+};
+
+template<class T>
+class CfgStructT : private CfgVar
+{
+public:
+ T data;
+ CfgStructT(const char * name) : CfgVar(name) {}
+private:
+ void Read(const char * name) { read_struct(INI_FILE, "in_vorbis",name,&data,sizeof(data));}
+ void Write(const char * name) {if (IsValueDefault()) WritePrivateProfileStringA("in_vorbis", name, 0, INI_FILE); else write_struct(INI_FILE, "in_vorbis", name, &data, sizeof(data));}
+protected:
+ virtual bool IsValueDefault() {return 0;}
+};
+
+
+class CfgFont : public CfgStructT<LOGFONT>
+{
+private:
+ void get_def(LOGFONT * f) {memset(f,0,sizeof(LOGFONT));GetObject(GetStockObject(DEFAULT_GUI_FONT),sizeof(LOGFONT),f);}
+ virtual bool IsValueDefault()
+ {
+ LOGFONT t;
+ get_def(&t);
+ return !memcmp(&data,&t,sizeof(LOGFONT));
+ }
+public:
+ CfgFont(const char * name) : CfgStructT<LOGFONT>(name)
+ {
+ get_def(&data);
+ }
+};
+
+extern int32_t priority_tab[7];
+extern HINSTANCE hIns;
+
+extern CfgString cfg_ssave_format,cfg_dumpdir;
+
+int is_http(const char* url);
+
+class VorbisFile
+{
+protected:
+ virtual int f_seek(__int64 offset,int whence)=0;
+ virtual size_t f_read(UINT siz,void * ptr)=0;
+ virtual UINT f_tell()=0;
+ static int _f_close(void *);
+ static int _f_seek(void* rs,__int64 offset,int whence);
+ static size_t _f_read(void* ptr,size_t size,size_t nmemb,void * rs);
+ static long _f_tell(void* rs);
+ static ov_callbacks oc;
+ static VorbisFile * Create_HTTP(const char * url,bool is_info);
+ VorbisFile(const wchar_t * u, bool is_info) : url(u) {memset(&vf,0,sizeof(vf));stopping=0;abort_prebuf=0;avg_kbps=0;use_prebuf=0;primary=!is_info; baseoffs=0;}
+ bool init();
+ virtual void post_init() {};
+ UINT avg_kbps;
+ bool Aborting();
+
+ __int64 baseoffs;
+public:
+ enum {TYPE_LOCAL,TYPE_HTTP};
+ virtual int GetType()=0;
+ virtual bool IsLive() {return 0;}
+ virtual void do_prebuf() {use_prebuf=1;abort_prebuf=0;};
+ StringW url;
+ String withlp;
+ String stream_title;
+ bool stopping,abort_prebuf,use_prebuf;
+ bool primary;//display status messages or not
+ OggVorbis_File vf;
+ UINT get_avg_bitrate()
+ {
+ if (avg_kbps>0) return avg_kbps;
+ vorbis_info * vi=ov_info(&vf,-1);
+ if (!vi) return 0;
+ return vi->bitrate_nominal/1000;
+ }
+
+ const char* get_meta(const char* tag,UINT c);
+ void set_meta(const vorbis_comment * vc,int links);
+
+ static VorbisFile * Create(const wchar_t * url,bool is_info);
+
+ double Length() {return ov_time_total(&vf,-1);}
+ double GetPos() {return ov_time_tell(&vf);}
+ int Seek(double p);
+ void Status(const wchar_t * zzz);
+ virtual UINT FileSize()=0;
+
+ virtual ~VorbisFile() {ov_clear(&vf);}
+ virtual void Idle() {Sleep(10);}
+
+ virtual void setBaseOffset(__int64 offs) { baseoffs=offs; }
+
+ float GetGain();
+};
+
+extern VorbisFile * theFile;
+
+extern StringW cur_file;
+
+extern CRITICAL_SECTION sync;
+
+BOOL modify_file(const wchar_t* url,const vorbis_comment * comments,int links);
+void winampGetExtendedFileInfoW_Cleanup(void);
+void UpdateFileTimeChanged(const wchar_t *fn);
+void do_cfg(int s);
+bool KeywordMatch(const char *mainString, const char *keyword);
+
+class Info
+{
+public:
+ Info(const wchar_t *filename);
+ ~Info();
+ bool Save();
+ int Error() { return vc==0?1:0; }
+ int GetNumMetadataItems();
+ void EnumMetadata(int n,wchar_t *key,int keylen, wchar_t *val, int vallen);
+ void RemoveMetadata(wchar_t * key);
+ void RemoveMetadata(int n);
+ void SetMetadata(wchar_t *key, wchar_t *val);
+ void SetMetadata(int n, wchar_t *key, wchar_t *val);
+ void SetTag(int n,wchar_t *key); // changes the key name
+private:
+ const wchar_t *filename;
+ vorbis_comment * vc;
+ int numstreams, stream;
+};
+
+// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
+static const GUID playbackConfigGroupGUID =
+ { 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf } };
+
+#endif //IN_VORBIS_MAIN_H \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.cpp b/Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.cpp
new file mode 100644
index 00000000..39f1c1c3
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.cpp
@@ -0,0 +1,194 @@
+#include "mkv_vorbis_decoder.h"
+#include "../nsmkv/Lacing.h"
+#include "../nsmkv/Cluster.h"
+#include <math.h>
+
+
+int MKVDecoderCreator::CreateAudioDecoder(const char *codec_id,
+ const nsmkv::TrackEntryData *track_entry_data, const nsmkv::AudioData *audio_data,
+ unsigned int preferred_bits, unsigned int max_channels, bool floating_point,
+ ifc_mkvaudiodecoder **decoder)
+{
+ if (!strcmp(codec_id, "A_VORBIS"))
+ {
+ MKVVorbis *vorbis = new MKVVorbis;
+ vorbis_info_init(&vorbis->info);
+ vorbis_comment_init(&vorbis->comment);
+ nsmkv::LacingState lacing_state;
+ if (nsmkv::Lacing::GetState(nsmkv::BlockBinary::XIPH_LACING, (const uint8_t *)track_entry_data->codec_private, track_entry_data->codec_private_len, &lacing_state))
+ {
+ const uint8_t *frame;
+ size_t frame_len;
+ uint16_t frame_number=0;
+ while (nsmkv::Lacing::GetFrame(frame_number, (const uint8_t *)track_entry_data->codec_private, track_entry_data->codec_private_len, &frame, &frame_len, &lacing_state))
+ {
+ ogg_packet packet = {const_cast<uint8_t *>(frame), (long)frame_len, (frame_number==0), 0, 0 /*-1?*/, vorbis->packet_number++};
+ int ret = vorbis_synthesis_headerin(&vorbis->info, &vorbis->comment, &packet);
+ if (ret != 0)
+ goto bail;
+ frame_number++;
+ }
+ if (vorbis_synthesis_init(&vorbis->dsp, &vorbis->info) == 0
+ && vorbis_block_init(&vorbis->dsp, &vorbis->block) == 0)
+ {
+ vorbis->bps = preferred_bits?preferred_bits:16;
+ *decoder = vorbis;
+ return CREATEDECODER_SUCCESS;
+ }
+ }
+
+bail:
+ delete vorbis;
+ return CREATEDECODER_FAILURE;
+ }
+
+ return CREATEDECODER_NOT_MINE;
+}
+
+
+#define CBCLASS MKVDecoderCreator
+START_DISPATCH;
+CB(CREATE_AUDIO_DECODER, CreateAudioDecoder)
+END_DISPATCH;
+#undef CBCLASS
+
+MKVVorbis::MKVVorbis()
+{
+ bps=16;
+ packet_number=0;
+}
+
+#define PA_CLIP_( val, min, max )\
+ { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
+
+#if defined(_M_IX86)
+static __inline long float_to_long(double t)
+{
+ long r;
+ __asm fld t
+ __asm fistp r
+ return r;
+}
+#else
+#define float_to_long(x) ((long)( x ))
+#endif
+
+inline static void clip(double &x, double a, double b)
+{
+ double x1 = fabs (x - a);
+ double x2 = fabs (x - b);
+ x = x1 + (a + b);
+ x -= x2;
+ x *= 0.5;
+}
+
+static void Float32_To_Int24_Clip(void *destinationBuffer, void *sourceBuffer, size_t count, size_t channels, double gain)
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ gain*=65536.*32768.;
+ while ( count-- )
+ {
+ /* convert to 32 bit and drop the low 8 bits */
+ double scaled = *src * gain;
+ clip( scaled, -2147483648., 2147483647.);
+ signed long temp = (signed long) scaled;
+
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+
+ src++;
+ dest += 3*channels;
+ }
+}
+
+static void Float32_To_Int16_Clip(void *destinationBuffer, void *sourceBuffer, size_t count, size_t channels, double gain)
+{
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+
+ gain*=32768.0;
+ while ( count-- )
+ {
+ long samp = float_to_long((*src) * gain/* - 0.5*/);
+
+ PA_CLIP_( samp, -0x8000, 0x7FFF );
+ *dest = (signed short) samp;
+
+ src ++;
+ dest += channels;
+ }
+}
+
+int MKVVorbis::DecodeBlock(void *inputBuffer, size_t inputBufferBytes, void *outputBuffer, size_t *outputBufferBytes)
+{
+ uint8_t *out = (uint8_t *)outputBuffer;
+ ogg_packet packet = {(uint8_t *)inputBuffer, (long)inputBufferBytes, 0, 0, 0 -1, packet_number++};
+ int ret = vorbis_synthesis(&block, &packet);
+ if (ret == 0)
+ {
+ vorbis_synthesis_blockin(&dsp,&block);
+ long channels = info.channels;
+ float **pcm;
+ int samples = vorbis_synthesis_pcmout(&dsp, &pcm);
+ if (samples)
+ {
+ switch(bps)
+ {
+ case 16:
+ for(int i=0;i<channels;i++)
+ {
+ Float32_To_Int16_Clip(out, pcm[i], samples, channels, 1.0 /*gain*/);
+ out+=2;
+ }
+ break;
+ case 24:
+ for(int i=0;i<channels;i++)
+ {
+ Float32_To_Int24_Clip(out, pcm[i], samples, channels, 1.0 /*gain*/);
+ out+=3;
+ }
+ break;
+ }
+ }
+ *outputBufferBytes = samples*channels*bps/8;
+ // let the decoder know we're processed them
+ vorbis_synthesis_read(&dsp,samples);
+ return MKV_SUCCESS;
+ }
+ return MKV_FAILURE;
+}
+
+int MKVVorbis::GetOutputProperties(unsigned int *sampleRate, unsigned int *channels, unsigned int *bitsPerSample, bool *isFloat)
+{
+ *sampleRate = info.rate;
+ *channels = info.channels;
+ *bitsPerSample = bps;
+ *isFloat = false; // TODO
+ return MKV_SUCCESS;
+}
+
+void MKVVorbis::Flush()
+{
+ vorbis_synthesis_restart(&dsp);
+}
+
+void MKVVorbis::Close()
+{
+ // TODO: benski> verify
+ vorbis_info_clear(&info);
+ vorbis_comment_clear(&comment);
+ vorbis_dsp_clear(&dsp);
+ vorbis_block_clear(&block);
+ delete this;
+}
+#define CBCLASS MKVVorbis
+START_DISPATCH;
+//CB(OUTPUT_FRAME_SIZE, OutputFrameSize)
+CB(GET_OUTPUT_PROPERTIES, GetOutputProperties)
+CB(DECODE_BLOCK, DecodeBlock)
+VCB(FLUSH, Flush)
+VCB(CLOSE, Close)
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.h b/Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.h
new file mode 100644
index 00000000..0893ab25
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.h
@@ -0,0 +1,41 @@
+#pragma once
+#include "../in_mkv/ifc_mkvaudiodecoder.h"
+#include "../in_mkv/svc_mkvdecoder.h"
+#include <vorbis/codec.h>
+
+// {6058D315-2F08-4b2f-903E-4C2E6B5EFFA9}
+static const GUID mkv_vorbis_guid =
+{ 0x6058d315, 0x2f08, 0x4b2f, { 0x90, 0x3e, 0x4c, 0x2e, 0x6b, 0x5e, 0xff, 0xa9 } };
+
+
+class MKVDecoderCreator : public svc_mkvdecoder
+{
+public:
+ static const char *getServiceName() { return "Vorbis MKV Decoder"; }
+ static GUID getServiceGuid() { return mkv_vorbis_guid; }
+ int CreateAudioDecoder(const char *codec_id,
+ const nsmkv::TrackEntryData *track_entry_data, const nsmkv::AudioData *audio_data,
+ unsigned int preferred_bits, unsigned int max_channels, bool floating_point,
+ ifc_mkvaudiodecoder **decoder);
+protected:
+ RECVS_DISPATCH;
+};
+
+class MKVVorbis : public ifc_mkvaudiodecoder
+{
+public:
+ MKVVorbis();
+ int DecodeBlock(void *inputBuffer, size_t inputBufferBytes, void *outputBuffer, size_t *outputBufferBytes);
+ int GetOutputProperties(unsigned int *sampleRate, unsigned int *channels, unsigned int *bitsPerSample, bool *isFloat);
+ void Flush();
+ void Close();
+//private:
+ unsigned int bps;
+ vorbis_info info;
+ vorbis_dsp_state dsp;
+ vorbis_block block;
+ vorbis_comment comment;
+ ogg_int64_t packet_number;
+protected:
+ RECVS_DISPATCH;
+}; \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/112.png b/Src/Plugins/Input/in_vorbis/oggdrop/112.png
new file mode 100644
index 00000000..4799bd0f
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/oggdrop/112.png
Binary files differ
diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/113.png b/Src/Plugins/Input/in_vorbis/oggdrop/113.png
new file mode 100644
index 00000000..4cd9b4ac
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/oggdrop/113.png
Binary files differ
diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/114.png b/Src/Plugins/Input/in_vorbis/oggdrop/114.png
new file mode 100644
index 00000000..88192596
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/oggdrop/114.png
Binary files differ
diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/115.png b/Src/Plugins/Input/in_vorbis/oggdrop/115.png
new file mode 100644
index 00000000..60c94b0b
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/oggdrop/115.png
Binary files differ
diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/116.png b/Src/Plugins/Input/in_vorbis/oggdrop/116.png
new file mode 100644
index 00000000..1d6fb8a2
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/oggdrop/116.png
Binary files differ
diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/117.png b/Src/Plugins/Input/in_vorbis/oggdrop/117.png
new file mode 100644
index 00000000..135963e7
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/oggdrop/117.png
Binary files differ
diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/118.png b/Src/Plugins/Input/in_vorbis/oggdrop/118.png
new file mode 100644
index 00000000..57a3a1ac
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/oggdrop/118.png
Binary files differ
diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/119.png b/Src/Plugins/Input/in_vorbis/oggdrop/119.png
new file mode 100644
index 00000000..a3baf233
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/oggdrop/119.png
Binary files differ
diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/120.png b/Src/Plugins/Input/in_vorbis/oggdrop/120.png
new file mode 100644
index 00000000..fb6a3ce9
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/oggdrop/120.png
Binary files differ
diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/121.png b/Src/Plugins/Input/in_vorbis/oggdrop/121.png
new file mode 100644
index 00000000..23ff9813
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/oggdrop/121.png
Binary files differ
diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/122.png b/Src/Plugins/Input/in_vorbis/oggdrop/122.png
new file mode 100644
index 00000000..6b52d9e2
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/oggdrop/122.png
Binary files differ
diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/123.png b/Src/Plugins/Input/in_vorbis/oggdrop/123.png
new file mode 100644
index 00000000..60284d13
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/oggdrop/123.png
Binary files differ
diff --git a/Src/Plugins/Input/in_vorbis/resource.h b/Src/Plugins/Input/in_vorbis/resource.h
new file mode 100644
index 00000000..425c40ea
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/resource.h
@@ -0,0 +1,177 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by ogg.rc
+//
+#define IDS_NULLSOFT_VORBIS_DECODER_OLD 0
+#define IDS_PLEASE_ENTER_TAG_NAME 1
+#define IDS_TAG_NAME_CONTAINS_INVALID_CHARS 2
+#define IDOKAUTH 3
+#define IDS_ERASE_ALL_FIELDS_ON_LIST 3
+#define IDCANCELAUTH 4
+#define IDS_WARNING 4
+#define IDS_FILE_ERROR 5
+#define IDS_LENGTH 6
+#define IDS_AVERAGE_BITRATE 7
+#define IDS_FILE_SIZE 8
+#define IDS_NOMINAL_BITRATE 9
+#define IDS_MIN_BITRATE 10
+#define IDS_MAX_BITRATE 11
+#define IDS_CHANNELS 12
+#define IDS_SAMPLING_RATE 13
+#define IDS_SERIAL_NUMBER 14
+#define IDS_VERSION 15
+#define IDS_Vendor 16
+#define IDS_VENDOR 16
+#define IDS_TO_SIMPLE_MODE 17
+#define IDS_TO_ADVANCED_MODE 18
+#define IDS_OGG_VORBIS_INFO 19
+#define IDS_WRITE_ERROR 20
+#define IDS_BEST_RPM 21
+#define IDS_ABOUT 22
+#define IDS_LEAVE_AS_IS 24
+#define IDS_REMAP_6_CHANNELS 25
+#define IDS_DOWNMIX_TO_4_CHANNELS 26
+#define IDS_DOWNMIX_TO_2_CHANNELS_DS 27
+#define IDS_DOWNMIX_TO_2_CHANNELS_DS2 28
+#define IDS_DOWNMIX_TO_MONO 29
+#define IDS_CORRECT_FL_FC_FR_BL_BR_LFE 30
+#define IDS_BROKEN_FL_FR_FC_BL_BR_LFE 31
+#define IDS_IDLE 32
+#define IDS_ABOUT_TEXT 32
+#define IDS_LOWEST 33
+#define IDS_BELOW_NORMAL 34
+#define IDS_NORMAL 35
+#define IDS_ABOVE_NORMAL 36
+#define IDS_HIGHEST 37
+#define IDS_TIME_CRITICAL 38
+#define IDS_TITLE_PREFERENCES 39
+#define IDS_DO_NOT_ENABLE_24_BIT_OUTPUT 40
+#define IDS_ONE_TIME_FAQ_REMINDER 41
+#define IDS_RUNNING_ON_NT_OS 42
+#define IDS_RUNNING_ON_WIN9X 43
+#define IDS_NEVER 44
+#define IDS_PORT_80_ONLY 45
+#define IDS_ALWAYS 46
+#define IDS_SELECT_OUTPUT_DIRECTORY 47
+#define IDS_DECODING 48
+#define IDS_DISPLAY 49
+#define IDS_STREAMING 50
+#define IDS_CONNECTING 51
+#define IDS_PREBUFFERING 52
+#define IDS_AUTH_REQUIRED 53
+#define IDS_OGG_FILES 54
+#define IDS_NAME 55
+#define IDS_VALUE 56
+#define IDS_KBPS 57
+#define IDS_STRING2 58
+#define IDS_HZ 58
+#define IDS_GAME_SPEED 59
+#define IDS_STRING1 60
+#define IDS_BYTES 60
+#define IDS_FAMILY_STRING 61
+#define IDC_CONFIG_TAB1 101
+#define IDD_INFO_DLG 102
+#define IDC_CONFIG_TAB2 102
+#define IDC_CONFIG_TAB3 103
+#define IDD_INFO_DLG1 104
+#define IDC_CONFIG_TAB4 104
+#define IDD_ABOUT 112
+#define IDB_BITMAP1 115
+#define IDB_BITMAP2 116
+#define IDB_BITMAP3 117
+#define IDB_BITMAP4 118
+#define IDB_BITMAP5 119
+#define IDB_BITMAP6 120
+#define IDB_BITMAP7 121
+#define IDB_BITMAP8 122
+#define IDB_BITMAP9 123
+#define IDB_BITMAP10 124
+#define IDB_BITMAP11 125
+#define IDB_BITMAP12 126
+#define IDD_HTTPAUTH 128
+#define IDD_INFO 131
+#define IDD_DIALOG1 132
+#define IDD_CONFIG 132
+#define IDD_INFO_DLG_NEW 133
+#define IDD_INFO_PANEL_ADVANCED 134
+#define IDD_INFO_PANEL_SIMPLE 135
+#define IDB_PNG1 136
+#define IDB_PNG2 137
+#define IDB_PNG3 138
+#define IDB_PNG4 139
+#define IDB_PNG5 140
+#define IDB_PNG6 141
+#define IDB_PNG7 142
+#define IDB_PNG8 143
+#define IDB_PNG9 144
+#define IDB_PNG10 145
+#define IDB_PNG11 146
+#define IDB_PNG12 147
+#define IDC_LIST 1001
+#define IDC_NAME 1002
+#define IDC_TITLE 1002
+#define IDC_VALUE 1003
+#define IDC_ARTIST 1003
+#define IDC_HTTP_BSIZE 1004
+#define IDC_ALBUM 1004
+#define IDC_STREAM_SAVE 1005
+#define IDC_GENRE 1005
+#define IDC_FSAVE 1006
+#define IDC_YEAR 1006
+#define IDC_DATE 1006
+#define IDC_CUSTOM1 1007
+#define IDC_MISC 1007
+#define IDC_AVG_BR 1008
+#define IDC_FIX0R 1009
+#define IDC_PROXY 1012
+#define IDC_URL 1012
+#define IDC_SLIDER1 1013
+#define IDC_SLIDER2 1014
+#define IDC_BUTTON_ADD 1015
+#define IDC_BUTTON_DEL 1016
+#define IDC_BUTTON_DELALL 1017
+#define IDC_RPM 1018
+#define IDC_RPM2 1019
+#define IDC_MC6_DM 1020
+#define IDC_MC6_MAP 1021
+#define IDC_SSAVE_FMT 1022
+#define IDC_SSAVE_FMT_DEF 1023
+#define IDC_FULLBUF 1025
+#define IDC_EDITAUTH 1026
+#define IDC_REALM 1027
+#define IDC_TRACK 1028
+#define IDC_STATIC_MISC 1034
+#define IDC_STATIC_TAGS 1035
+#define IDC_STATIC_STD 1036
+#define IDC_STATIC_TRACK 1037
+#define IDC_SEPARATE 1038
+#define IDC_DELETE_ALL 1039
+#define IDC_RG 1040
+#define IDC_RG_MODE 1041
+#define IDC_NOCLIP 1042
+#define IDC_HARDLIMIT 1043
+#define IDC_PREAMP_STAT 1044
+#define IDC_TAB 1045
+#define IDC_MODE_TOGGLE 1053
+#define IDC_NEXT_STREAM 1054
+#define IDC_PREV_STREAM 1055
+#define IDC_STATIC_CS 1056
+#define IDC_HIDE_SPEC 1058
+#define IDC_COMMENT 1060
+#define IDC_ABOUT_TEXT 1061
+#define IDC_OS_BLAH 1062
+#define IDC_REMEMBER_INFOSIZE 1063
+#define IDC_FONTNAME 1064
+#define IDC_PREAMP_RG 1065
+#define IDS_NULLSOFT_VORBIS_DECODER 65534
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 148
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1067
+#define _APS_NEXT_SYMED_VALUE 105
+#endif
+#endif
diff --git a/Src/Plugins/Input/in_vorbis/rf.h b/Src/Plugins/Input/in_vorbis/rf.h
new file mode 100644
index 00000000..70bd85bb
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/rf.h
@@ -0,0 +1,108 @@
+#ifndef _RF_H_
+#define _RF_H_
+
+//based on Tempura specs.
+//NOT compatible with WA3 alphas
+
+class WReader
+{
+ protected:
+
+ /* WReader
+ ** WReader constructor
+ */
+ WReader() : m_player(0) { }
+
+ public:
+
+ /* m_player
+ ** Filled by Winamp. Pointer to Winamp 3 core interface
+ */
+ /*WPlayer_callback*/ void *m_player; //PP: hack. read_file.dll doesn't call it at all. simply don't touch it
+
+ /* GetDescription
+ ** Retrieves your plug-in's text description
+ */
+ virtual char *GetDescription() { return "Unknown"; };
+
+ /* Open
+ ** Used to open a file, return 0 on success
+ */
+ virtual int Open(char *url, bool *killswitch)=0;
+
+ /* Read
+ ** Returns number of BYTES read (if < length then eof or killswitch)
+ */
+ virtual int Read(char *buffer, int length, bool *killswitch)=0;
+
+ /* GetLength
+ ** Returns length of the entire file in BYTES, return -1 on unknown/infinite (as for a stream)
+ */
+ virtual int GetLength(void)=0;
+
+ /* CanSeek
+ ** Returns 1 if you can skip ahead in the file, 0 if not
+ */
+ virtual int CanSeek(void)=0; //PP: currently available read_file.dll vesions can always seek in any direction
+
+ /* Seek
+ ** Jump to a certain absolute position
+ */
+ virtual int Seek(int position, bool *killswitch)=0;
+
+ /* GetHeader
+ ** Retrieve header. Used in read_http to retrieve the HTTP header
+ */
+ virtual char *GetHeader(char *name) { return 0; }
+
+ /* ~WReader
+ ** WReader virtual destructor
+ */
+ //virtual ~WReader() { }
+ virtual void Release(int) {};
+ //PP: hack - shut up linker when getting rid of evil CRT library; seems to work OK under Tempura
+};
+
+
+
+
+#define READ_VER 0x100
+
+typedef struct
+{
+ /* version
+ ** Version revision number
+ */
+ int version;
+
+ /* description
+ ** Text description of the reader plug-in
+ */
+ char *description;
+
+ /* create
+ ** Function pointer to create a reader module
+ */
+ WReader *(*create)();
+
+ /* ismine
+ ** Determines whether or not a file should be read by this plug-in
+ */
+ int (*ismine)(char *url);
+
+} reader_source;
+
+//exported symbol is:
+//int readerSource(HINSTANCE,reader_source**);
+
+/*
+(not a part of Tempura specs)
+int _stdcall gzip_writefile(char* path,void* buf,DWORD size) - writes a memory block to a GZIP file - in_midi calls it from file info box
+
+other hacks:
+recent versions understand file://... urls, can do partial file access (eg. "partial://00006666-66660000:c:\foo\bar.dat\zzz.wav" (zzz.wav is the "display name" + extension to make winamp select correct plug-in) and auto-detect CD drive letter (eg. #:\x.mp3 will scan all drives for that file; also works with partial:// )
+you can (for an example) build a playlist which will play Unreal soundtrack directly from the game CD on any system
+latest read_file.dll is bundled with the midi plug-in: http://www.blorp.com/~peter/zips/in_midi.zip
+*/
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/shaper.cpp b/Src/Plugins/Input/in_vorbis/shaper.cpp
new file mode 100644
index 00000000..85779a87
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/shaper.cpp
@@ -0,0 +1,245 @@
+#include "Shaper.h"
+
+#ifndef M_PI
+#define M_PI 3.1415926535897932384626433832795028842
+#endif
+
+#define RANDBUFLEN 65536
+
+#define RINT(x) ((x) >= 0 ? ((int)((x) + 0.5)) : ((int)((x) - 0.5)))
+
+const int scoeffreq[] =
+ {
+ 0, 48000, 44100, 37800, 32000, 22050, 48000, 44100
+ };
+
+
+const int scoeflen[] =
+ {
+ 1, 16, 20, 16, 16, 15, 16, 15
+ };
+
+const int samp[] =
+ {
+ 8, 18, 27, 8, 8, 8, 10, 9
+ };
+
+const double shapercoefs[8][21] =
+ {
+ {
+ -1
+ }
+ , /* triangular dither */
+
+ { -2.8720729351043701172, 5.0413231849670410156, -6.2442994117736816406, 5.8483986854553222656,
+ -3.7067542076110839844, 1.0495119094848632812, 1.1830236911773681641, -2.1126792430877685547,
+ 1.9094531536102294922, -0.99913084506988525391, 0.17090806365013122559, 0.32615602016448974609,
+ -0.39127644896507263184, 0.26876461505889892578, -0.097676105797290802002, 0.023473845794796943665,
+ }, /* 48k, N=16, amp=18 */
+
+ { -2.6773197650909423828, 4.8308925628662109375, -6.570110321044921875, 7.4572014808654785156,
+ -6.7263274192810058594, 4.8481650352478027344, -2.0412089824676513672, -0.7006359100341796875,
+ 2.9537565708160400391, -4.0800385475158691406, 4.1845216751098632812, -3.3311812877655029297,
+ 2.1179926395416259766, -0.879302978515625, 0.031759146600961685181, 0.42382788658142089844,
+ -0.47882103919982910156, 0.35490813851356506348, -0.17496839165687561035, 0.060908168554306030273,
+ }, /* 44.1k, N=20, amp=27 */
+
+ { -1.6335992813110351562, 2.2615492343902587891, -2.4077029228210449219, 2.6341717243194580078,
+ -2.1440362930297851562, 1.8153258562088012695, -1.0816224813461303711, 0.70302653312683105469,
+ -0.15991993248462677002, -0.041549518704414367676, 0.29416576027870178223, -0.2518316805362701416,
+ 0.27766478061676025391, -0.15785403549671173096, 0.10165894031524658203, -0.016833892092108726501,
+ }, /* 37.8k, N=16 */
+
+ { -0.82901298999786376953, 0.98922657966613769531, -0.59825712442398071289, 1.0028809309005737305,
+ -0.59938216209411621094, 0.79502451419830322266, -0.42723315954208374023, 0.54492527246475219727,
+ -0.30792605876922607422, 0.36871799826622009277, -0.18792048096656799316, 0.2261127084493637085,
+ -0.10573341697454452515, 0.11435490846633911133, -0.038800679147243499756, 0.040842197835445404053,
+ }, /* 32k, N=16 */
+
+ { -0.065229974687099456787, 0.54981261491775512695, 0.40278548002243041992, 0.31783768534660339355,
+ 0.28201797604560852051, 0.16985194385051727295, 0.15433363616466522217, 0.12507140636444091797,
+ 0.08903945237398147583, 0.064410120248794555664, 0.047146003693342208862, 0.032805237919092178345,
+ 0.028495194390416145325, 0.011695005930960178375, 0.011831838637590408325,
+ }, /* 22.05k, N=15 */
+
+ { -2.3925774097442626953, 3.4350297451019287109, -3.1853709220886230469, 1.8117271661758422852,
+ 0.20124770700931549072, -1.4759907722473144531, 1.7210904359817504883, -0.97746700048446655273,
+ 0.13790138065814971924, 0.38185903429985046387, -0.27421241998672485352, -0.066584214568138122559,
+ 0.35223302245140075684, -0.37672343850135803223, 0.23964276909828186035, -0.068674825131893157959,
+ }, /* 48k, N=16, amp=10 */
+
+ { -2.0833916664123535156, 3.0418450832366943359, -3.2047898769378662109, 2.7571926116943359375,
+ -1.4978630542755126953, 0.3427594602108001709, 0.71733748912811279297, -1.0737057924270629883,
+ 1.0225815773010253906, -0.56649994850158691406, 0.20968692004680633545, 0.065378531813621520996,
+ -0.10322438180446624756, 0.067442022264003753662, 0.00495197344571352005,
+ }, /* 44.1k, N=15, amp=9 */
+
+#if 0
+ { -3.0259189605712890625, 6.0268716812133789062, -9.195003509521484375, 11.824929237365722656,
+ -12.767142295837402344, 11.917946815490722656, -9.1739168167114257812, 5.3712320327758789062,
+ -1.1393624544143676758, -2.4484779834747314453, 4.9719839096069335938, -6.0392003059387207031,
+ 5.9359521865844726562, -4.903278350830078125, 3.5527443885803222656, -2.1909697055816650391,
+ 1.1672389507293701172, -0.4903914332389831543, 0.16519790887832641602, -0.023217858746647834778,
+ }, /* 44.1k, N=20 */
+#endif
+ };
+
+#define POOLSIZE 97
+
+Shaper::Shaper(int freq, int _nch, int min, int max, int _dtype, int pdf, double noiseamp)
+{
+ int i;
+ float pool[POOLSIZE] = {0};
+
+ nch = _nch;
+ dtype = _dtype;
+
+ for (i = 1;i < 6;i++) if (freq == scoeffreq[i]) break;
+ /* if ((dtype == 3 || dtype == 4) && i == 6) {
+ fprintf(stderr,"Warning: ATH based noise shaping for destination frequency %dHz is not available, using triangular dither\n",freq);
+ }*/
+ if (dtype == 2 || i == 6) i = 0;
+ if (dtype == 4 && (i == 1 || i == 2)) i += 5;
+
+ shaper_type = i;
+
+ shapebuf = (double**)malloc(sizeof(double *) * nch);
+ shaper_len = scoeflen[shaper_type];
+
+ for (i = 0;i < nch;i++)
+ shapebuf[i] = (double*)calloc(shaper_len, sizeof(double));
+
+ shaper_clipmin = min;
+ shaper_clipmax = max;
+
+ randbuf = (REAL*)malloc(sizeof(REAL) * RANDBUFLEN);
+
+ for (i = 0;i < POOLSIZE;i++) pool[i] = warandf();
+
+ switch (pdf)
+ {
+ case DITHER_RECTANGLE: // rectangular
+ for (i = 0;i < RANDBUFLEN;i++)
+ {
+ float r;
+ int p;
+
+ p = warand() % POOLSIZE;
+ r = pool[p]; pool[p] = warandf();
+ randbuf[i] = (REAL)(noiseamp * (((double)r) - 0.5));
+ }
+ break;
+
+ case DITHER_TRIANGLE:
+ for (i = 0;i < RANDBUFLEN;i++)
+ {
+ float r1, r2;
+ int p;
+
+ p = warand() % POOLSIZE;
+ r1 = pool[p]; pool[p] = warandf();
+ p = warand() % POOLSIZE;
+ r2 = pool[p]; pool[p] = warandf();
+ randbuf[i] = (REAL)(noiseamp * ((((double)r1)) - (((double)r2))));
+ }
+ break;
+#if 0
+ case DITHER_GAUSSIAN: // gaussian
+ for (i = 0;i < RANDBUFLEN;i++)
+ {
+ int sw = 0;
+ double t, u;
+ double r;
+ int p;
+
+ if (sw == 0)
+ {
+ sw = 1;
+
+ p = warand() % POOLSIZE;
+ r = ((double)pool[p]); pool[p] = warandf();
+
+ t = sqrt(-2 * log(1 - r));
+
+ p = warand() % POOLSIZE;
+ r = ((double)pool[p]); pool[p] = warandf();
+
+ u = 2 * M_PI * r;
+
+ randbuf[i] = noiseamp * t * cos(u);
+ }
+ else
+ {
+ sw = 0;
+
+ randbuf[i] = noiseamp * t * sin(u);
+ }
+ }
+ break;
+#endif
+ }
+
+ randptr = 0;
+
+// if (dtype == 0 || dtype == 1) return 1;
+ //return samp[shaper_type];
+}
+
+Shaper::~Shaper()
+{
+ int i;
+
+ for (i = 0;i < nch;i++) free(shapebuf[i]);
+ free(shapebuf);
+ free(randbuf);
+}
+
+int Shaper::do_shaping(double s,/*double *peak,*/int ch)
+{
+ double u, h;
+ int i;
+
+ if (dtype == 1)
+ {
+ s += randbuf[randptr++ & (RANDBUFLEN-1)];
+
+ if (s < shaper_clipmin)
+ {
+ //double d = (double)s / shaper_clipmin;
+ //*peak = *peak < d ? d : *peak;
+ s = shaper_clipmin;
+ }
+ if (s > shaper_clipmax)
+ {
+ //double d = (double)s / shaper_clipmax;
+ //*peak = *peak < d ? d : *peak;
+ s = shaper_clipmax;
+ }
+
+ return RINT(s);
+ }
+
+ h = 0;
+ for (i = 0;i < shaper_len;i++)
+ h += shapercoefs[shaper_type][i] * shapebuf[ch][i];
+ s += h;
+ u = s;
+ s += randbuf[randptr++ & (RANDBUFLEN-1)];
+ if (s < shaper_clipmin)
+ {
+ //double d = (double)s / shaper_clipmin;
+ //*peak = *peak < d ? d : *peak;
+ s = shaper_clipmin;
+ }
+ if (s > shaper_clipmax)
+ {
+ //double d = (double)s / shaper_clipmax;
+ //*peak = *peak < d ? d : *peak;
+ s = shaper_clipmax;
+ }
+ s = RINT(s);
+ for (i = shaper_len - 2;i >= 0;i--) shapebuf[ch][i+1] = shapebuf[ch][i];
+ shapebuf[ch][0] = s - u;
+
+ return (int)s;
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/shaper.h b/Src/Plugins/Input/in_vorbis/shaper.h
new file mode 100644
index 00000000..13da7fd2
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/shaper.h
@@ -0,0 +1,30 @@
+//from SSRC
+#ifndef NULLSOFT_VORBIS_SHAPER_H
+#define NULLSOFT_VORBIS_SHAPER_H
+#include "main.h"
+
+typedef float REAL;
+enum
+{
+ DITHER_RECTANGLE=0,
+ DITHER_TRIANGLE=1,
+ DITHER_GAUSSIAN=2,
+};
+class Shaper
+{
+ double **shapebuf;
+ int shaper_type,shaper_len,shaper_clipmin,shaper_clipmax;
+ REAL *randbuf;
+ int randptr;
+ int dtype;
+ int nch;
+
+ public:
+ Shaper(int freq,int _nch,int min,int max,int _dtype,int pdf,double noiseamp);
+
+ int do_shaping(double s,/*double *peak,*/int ch);
+
+ ~Shaper();
+};
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/unihack.cpp b/Src/Plugins/Input/in_vorbis/unihack.cpp
new file mode 100644
index 00000000..ada2e3d8
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/unihack.cpp
@@ -0,0 +1,2 @@
+#include "main.h"
+
diff --git a/Src/Plugins/Input/in_vorbis/vcedit.c b/Src/Plugins/Input/in_vorbis/vcedit.c
new file mode 100644
index 00000000..25f40cd8
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/vcedit.c
@@ -0,0 +1,491 @@
+/* This program is licensed under the GNU Library General Public License, version 2,
+ * a copy of which is included with this program (LICENCE.LGPL).
+ *
+ * (c) 2000-2001 Michael Smith <msmith@labyrinth.net.au>
+ *
+ *
+ * Comment editing backend, suitable for use by nice frontend interfaces.
+ *
+ * last modified: $Id: vcedit.c,v 1.3 2013/10/22 14:17:11 dromagod Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+
+#include "vcedit.h"
+//#include "i18n.h"
+
+
+#define CHUNKSIZE 4096
+
+vcedit_state *vcedit_new_state(void)
+{
+ vcedit_state *state = malloc(sizeof(vcedit_state));
+ memset(state, 0, sizeof(vcedit_state));
+
+ return state;
+}
+
+char *vcedit_error(vcedit_state *state)
+{
+ return state->lasterror;
+}
+
+vorbis_comment *vcedit_comments(vcedit_state *state)
+{
+ return state->vc;
+}
+
+static void vcedit_clear_internals(vcedit_state *state)
+{
+ if(state->vc)
+ {
+ vorbis_comment_clear(state->vc);
+ free(state->vc);
+ }
+ if(state->os)
+ {
+ ogg_stream_clear(state->os);
+ free(state->os);
+ }
+ if(state->oy)
+ {
+ ogg_sync_clear(state->oy);
+ free(state->oy);
+ }
+ if(state->vendor)
+ free(state->vendor);
+ if(state->mainbuf)
+ free(state->mainbuf);
+ if(state->bookbuf)
+ free(state->bookbuf);
+ if(state->vi) {
+ vorbis_info_clear(state->vi);
+ free(state->vi);
+ }
+
+ memset(state, 0, sizeof(*state));
+}
+
+void vcedit_clear(vcedit_state *state)
+{
+ if(state)
+ {
+ vcedit_clear_internals(state);
+ free(state);
+ }
+}
+
+/* Next two functions pulled straight from libvorbis, apart from one change
+ * - we don't want to overwrite the vendor string.
+ */
+static void _v_writestring(oggpack_buffer *o,char *s, int len)
+{
+ while(len--)
+ {
+ oggpack_write(o,*s++,8);
+ }
+}
+
+static int _commentheader_out(vorbis_comment *vc, char *vendor, ogg_packet *op)
+{
+ oggpack_buffer opb;
+
+ oggpack_writeinit(&opb);
+
+ /* preamble */
+ oggpack_write(&opb,0x03,8);
+ _v_writestring(&opb,"vorbis", 6);
+
+ /* vendor */
+ oggpack_write(&opb,(unsigned long)strlen(vendor),32);
+ _v_writestring(&opb,vendor, (int)strlen(vendor));
+
+ /* comments */
+ oggpack_write(&opb,vc->comments,32);
+ if(vc->comments){
+ int i;
+ for(i=0;i<vc->comments;i++){
+ if(vc->user_comments[i]){
+ oggpack_write(&opb,vc->comment_lengths[i],32);
+ _v_writestring(&opb,vc->user_comments[i],
+ vc->comment_lengths[i]);
+ }else{
+ oggpack_write(&opb,0,32);
+ }
+ }
+ }
+ oggpack_write(&opb,1,1);
+
+ op->packet = _ogg_malloc(oggpack_bytes(&opb));
+ memcpy(op->packet, opb.buffer, oggpack_bytes(&opb));
+
+ op->bytes=oggpack_bytes(&opb);
+ op->b_o_s=0;
+ op->e_o_s=0;
+ op->granulepos=0;
+
+ oggpack_writeclear(&opb);
+ return 0;
+}
+
+static int _blocksize(vcedit_state *s, ogg_packet *p)
+{
+ int this = vorbis_packet_blocksize(s->vi, p);
+ int ret = (this + s->prevW)/4;
+
+ if(!s->prevW)
+ {
+ s->prevW = this;
+ return 0;
+ }
+
+ s->prevW = this;
+ return ret;
+}
+
+static int _fetch_next_packet(vcedit_state *s, ogg_packet *p, ogg_page *page)
+{
+ int result = ogg_stream_packetout(s->os, p);
+
+ if(result > 0)
+ return 1;
+ else
+ {
+ if(s->eosin)
+ return 0;
+ while(ogg_sync_pageout(s->oy, page) <= 0)
+ {
+ char *buffer = ogg_sync_buffer(s->oy, CHUNKSIZE);
+ int bytes = (int)s->read(buffer,1, CHUNKSIZE, s->in);
+ ogg_sync_wrote(s->oy, bytes);
+ if(bytes == 0)
+ return 0;
+ }
+ if(ogg_page_eos(page))
+ s->eosin = 1;
+ else if(ogg_page_serialno(page) != s->serial)
+ {
+ s->eosin = 1;
+ s->extrapage = 1;
+ return 0;
+ }
+
+ ogg_stream_pagein(s->os, page);
+ return _fetch_next_packet(s, p, page);
+ }
+}
+
+int vcedit_open(vcedit_state *state, FILE *in)
+{
+ return vcedit_open_callbacks(state, (void *)in,
+ (vcedit_read_func)fread, (vcedit_write_func)fwrite);
+}
+
+int vcedit_open_callbacks(vcedit_state *state, void *in,
+ vcedit_read_func read_func, vcedit_write_func write_func)
+{
+
+ char *buffer;
+ int bytes,i;
+ ogg_packet *header;
+ ogg_packet header_main;
+ ogg_packet header_comments;
+ ogg_packet header_codebooks;
+ ogg_page og;
+
+ state->in = in;
+ state->read = read_func;
+ state->write = write_func;
+
+ state->oy = malloc(sizeof(ogg_sync_state));
+ ogg_sync_init(state->oy);
+
+ buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
+ bytes = (int)state->read(buffer, 1, CHUNKSIZE, state->in);
+
+ ogg_sync_wrote(state->oy, bytes);
+
+ if(ogg_sync_pageout(state->oy, &og) != 1)
+ {
+ if(bytes<CHUNKSIZE)
+ state->lasterror = "Input truncated or empty.";
+ else
+ state->lasterror = "Input is not an Ogg bitstream.";
+ goto err;
+ }
+
+ state->serial = ogg_page_serialno(&og);
+
+ state->os = malloc(sizeof(ogg_stream_state));
+ ogg_stream_init(state->os, state->serial);
+
+ state->vi = malloc(sizeof(vorbis_info));
+ vorbis_info_init(state->vi);
+
+ state->vc = malloc(sizeof(vorbis_comment));
+ vorbis_comment_init(state->vc);
+
+ if(ogg_stream_pagein(state->os, &og) < 0)
+ {
+ state->lasterror = "Error reading first page of Ogg bitstream.";
+ goto err;
+ }
+
+ if(ogg_stream_packetout(state->os, &header_main) != 1)
+ {
+ state->lasterror = "Error reading initial header packet.";
+ goto err;
+ }
+
+ if(vorbis_synthesis_headerin(state->vi, state->vc, &header_main) < 0)
+ {
+ state->lasterror = "Ogg bitstream does not contain vorbis data.";
+ goto err;
+ }
+
+ state->mainlen = header_main.bytes;
+ state->mainbuf = malloc(state->mainlen);
+ memcpy(state->mainbuf, header_main.packet, header_main.bytes);
+
+ i = 0;
+ header = &header_comments;
+ while(i<2) {
+ while(i<2) {
+ int result = ogg_sync_pageout(state->oy, &og);
+ if(result == 0) break; /* Too little data so far */
+ else if(result == 1)
+ {
+ ogg_stream_pagein(state->os, &og);
+ while(i<2)
+ {
+ result = ogg_stream_packetout(state->os, header);
+ if(result == 0) break;
+ if(result == -1)
+ {
+ state->lasterror = "Corrupt secondary header.";
+ goto err;
+ }
+ vorbis_synthesis_headerin(state->vi, state->vc, header);
+ if(i==1)
+ {
+ state->booklen = header->bytes;
+ state->bookbuf = malloc(state->booklen);
+ memcpy(state->bookbuf, header->packet,
+ header->bytes);
+ }
+ i++;
+ header = &header_codebooks;
+ }
+ }
+ }
+
+ buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
+ bytes = (int)state->read(buffer, 1, CHUNKSIZE, state->in);
+ if(bytes == 0 && i < 2)
+ {
+ state->lasterror = "EOF before end of vorbis headers.";
+ goto err;
+ }
+ ogg_sync_wrote(state->oy, bytes);
+ }
+
+ /* Copy the vendor tag */
+ bytes = (int)strlen(state->vc->vendor);
+ state->vendor = malloc(bytes +1);
+ strncpy(state->vendor, state->vc->vendor, bytes);
+
+ /* Headers are done! */
+ return 0;
+
+err:
+ vcedit_clear_internals(state);
+ return -1;
+}
+
+int vcedit_write(vcedit_state *state, void *out)
+{
+ ogg_stream_state streamout;
+ ogg_packet header_main;
+ ogg_packet header_comments;
+ ogg_packet header_codebooks;
+
+ ogg_page ogout, ogin;
+ ogg_packet op;
+ ogg_int64_t granpos = 0;
+ int result;
+ char *buffer;
+ int bytes;
+ int needflush=0, needout=0;
+
+ state->eosin = 0;
+ state->extrapage = 0;
+
+ header_main.bytes = state->mainlen;
+ header_main.packet = state->mainbuf;
+ header_main.b_o_s = 1;
+ header_main.e_o_s = 0;
+ header_main.granulepos = 0;
+
+ header_codebooks.bytes = state->booklen;
+ header_codebooks.packet = state->bookbuf;
+ header_codebooks.b_o_s = 0;
+ header_codebooks.e_o_s = 0;
+ header_codebooks.granulepos = 0;
+
+ ogg_stream_init(&streamout, state->serial);
+
+ _commentheader_out(state->vc, state->vendor, &header_comments);
+
+ ogg_stream_packetin(&streamout, &header_main);
+ ogg_stream_packetin(&streamout, &header_comments);
+ ogg_stream_packetin(&streamout, &header_codebooks);
+
+ while((result = ogg_stream_flush(&streamout, &ogout)))
+ {
+ if(state->write(ogout.header,1,ogout.header_len, out) !=
+ (size_t) ogout.header_len)
+ goto cleanup;
+ if(state->write(ogout.body,1,ogout.body_len, out) !=
+ (size_t) ogout.body_len)
+ goto cleanup;
+ }
+
+ while(_fetch_next_packet(state, &op, &ogin))
+ {
+ int size;
+ size = _blocksize(state, &op);
+ granpos += size;
+
+ if(needflush)
+ {
+ if(ogg_stream_flush(&streamout, &ogout))
+ {
+ if(state->write(ogout.header,1,ogout.header_len,
+ out) != (size_t) ogout.header_len)
+ goto cleanup;
+ if(state->write(ogout.body,1,ogout.body_len,
+ out) != (size_t) ogout.body_len)
+ goto cleanup;
+ }
+ }
+ else if(needout)
+ {
+ if(ogg_stream_pageout(&streamout, &ogout))
+ {
+ if(state->write(ogout.header,1,ogout.header_len,
+ out) != (size_t) ogout.header_len)
+ goto cleanup;
+ if(state->write(ogout.body,1,ogout.body_len,
+ out) != (size_t) ogout.body_len)
+ goto cleanup;
+ }
+ }
+
+ needflush=needout=0;
+
+ if(op.granulepos == -1)
+ {
+ op.granulepos = granpos;
+ ogg_stream_packetin(&streamout, &op);
+ }
+ else /* granulepos is set, validly. Use it, and force a flush to
+ account for shortened blocks (vcut) when appropriate */
+ {
+ if(granpos > op.granulepos)
+ {
+ granpos = op.granulepos;
+ ogg_stream_packetin(&streamout, &op);
+ needflush=1;
+ }
+ else
+ {
+ ogg_stream_packetin(&streamout, &op);
+ needout=1;
+ }
+ }
+ }
+
+ streamout.e_o_s = 1;
+ while(ogg_stream_flush(&streamout, &ogout))
+ {
+ if(state->write(ogout.header,1,ogout.header_len,
+ out) != (size_t) ogout.header_len)
+ goto cleanup;
+ if(state->write(ogout.body,1,ogout.body_len,
+ out) != (size_t) ogout.body_len)
+ goto cleanup;
+ }
+
+ if (state->extrapage)
+ {
+ if(state->write(ogin.header,1,ogin.header_len,
+ out) != (size_t) ogin.header_len)
+ goto cleanup;
+ if (state->write(ogin.body,1,ogin.body_len, out) !=
+ (size_t) ogin.body_len)
+ goto cleanup;
+ }
+
+ state->eosin=0; /* clear it, because not all paths to here do */
+ while(!state->eosin) /* We reached eos, not eof */
+ {
+ /* We copy the rest of the stream (other logical streams)
+ * through, a page at a time. */
+ while(1)
+ {
+ result = ogg_sync_pageout(state->oy, &ogout);
+ if(result==0)
+ break;
+ if(result<0)
+ state->lasterror = "Corrupt or missing data, continuing...";
+ else
+ {
+ /* Don't bother going through the rest, we can just
+ * write the page out now */
+ if(state->write(ogout.header,1,ogout.header_len,
+ out) != (size_t) ogout.header_len) {
+// fprintf(stderr, "Bumming out\n");
+ goto cleanup;
+ }
+ if(state->write(ogout.body,1,ogout.body_len, out) !=
+ (size_t) ogout.body_len) {
+ // fprintf(stderr, "Bumming out 2\n");
+ goto cleanup;
+ }
+ }
+ }
+ buffer = ogg_sync_buffer(state->oy, CHUNKSIZE);
+ bytes = (int)state->read(buffer,1, CHUNKSIZE, state->in);
+ ogg_sync_wrote(state->oy, bytes);
+ if(bytes == 0)
+ {
+ state->eosin = 1;
+ break;
+ }
+ }
+
+
+cleanup:
+ ogg_stream_clear(&streamout);
+ ogg_packet_clear(&header_comments);
+
+ free(state->mainbuf);
+ free(state->bookbuf);
+ state->mainbuf = state->bookbuf = NULL;
+
+ if(!state->eosin)
+ {
+ state->lasterror =
+ "Error writing stream to output. "
+ "Output stream may be corrupted or truncated.";
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/Src/Plugins/Input/in_vorbis/vcedit.h b/Src/Plugins/Input/in_vorbis/vcedit.h
new file mode 100644
index 00000000..80cc5a6b
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/vcedit.h
@@ -0,0 +1,62 @@
+/* This program is licensed under the GNU Library General Public License, version 2,
+ * a copy of which is included with this program (with filename LICENSE.LGPL).
+ *
+ * (c) 2000-2001 Michael Smith <msmith@labyrinth.net.au>
+ *
+ * VCEdit header.
+ *
+ * last modified: $ID:$
+ */
+
+#ifndef __VCEDIT_H
+#define __VCEDIT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+
+typedef size_t (*vcedit_read_func)(void *, size_t, size_t, void *);
+typedef size_t (*vcedit_write_func)(const void *, size_t, size_t, void *);
+
+typedef struct {
+ ogg_sync_state *oy;
+ ogg_stream_state *os;
+
+ vorbis_comment *vc;
+ vorbis_info *vi;
+
+ vcedit_read_func read;
+ vcedit_write_func write;
+
+ void *in;
+ long serial;
+ unsigned char *mainbuf;
+ unsigned char *bookbuf;
+ int mainlen;
+ int booklen;
+ char *lasterror;
+ char *vendor;
+ int prevW;
+ int extrapage;
+ int eosin;
+} vcedit_state;
+
+extern vcedit_state * vcedit_new_state(void);
+extern void vcedit_clear(vcedit_state *state);
+extern vorbis_comment * vcedit_comments(vcedit_state *state);
+extern int vcedit_open(vcedit_state *state, FILE *in);
+extern int vcedit_open_callbacks(vcedit_state *state, void *in,
+ vcedit_read_func read_func, vcedit_write_func write_func);
+extern int vcedit_write(vcedit_state *state, void *out);
+extern char * vcedit_error(vcedit_state *state);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VCEDIT_H */
+
diff --git a/Src/Plugins/Input/in_vorbis/version.rc2 b/Src/Plugins/Input/in_vorbis/version.rc2
new file mode 100644
index 00000000..b6a16388
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,79,0,0
+ PRODUCTVERSION WINAMP_PRODUCTVER
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Winamp SA"
+ VALUE "FileDescription", "Winamp Input Plug-in"
+ VALUE "FileVersion", "1,79,0,0"
+ VALUE "InternalName", "Nullsoft Vorbis Decoder"
+ VALUE "LegalCopyright", "Copyright © 2001-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "in_vorbis.dll"
+ VALUE "ProductName", "Winamp"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/Src/Plugins/Input/in_vorbis/wa2.cpp b/Src/Plugins/Input/in_vorbis/wa2.cpp
new file mode 100644
index 00000000..3920a0ee
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/wa2.cpp
@@ -0,0 +1,1097 @@
+#include "main.h"
+#include "genres.h"
+#include "decoder.h"
+#include "api__in_vorbis.h"
+#include "../Winamp/wa_ipc.h"
+#include "../nu/Singleton.h"
+#include "mkv_vorbis_decoder.h"
+#include <shlwapi.h>
+#include "../nu/AutoWide.h"
+#include "../nu/AutoChar.h"
+#include <strsafe.h>
+#include <api/service/waservicefactory.h>
+
+template <class api_T>
+void ServiceBuild(api_T *&api_t, GUID factoryGUID_t)
+{
+ if (mod.service)
+ {
+ waServiceFactory *factory = mod.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 (mod.service && api_t)
+ {
+ waServiceFactory *factory = mod.service->service_getServiceByGuid(factoryGUID_t);
+ if (factory)
+ factory->releaseInterface(api_t);
+ }
+ api_t = NULL;
+}
+
+VorbisFile * theFile = 0;
+
+extern CfgInt cfg_abr,cfg_httpseek2;
+
+OSVERSIONINFO os_ver = {0};
+
+static int pos_ms;
+static int seek_to=-1;
+static int length;
+static bool kill;
+
+StringW stat_disp;
+
+void show_stat(const wchar_t* txt)
+{
+ if (txt)
+ {
+ stat_disp=txt;
+ PostMessage(mod.hMainWindow,WM_USER,0,243);
+ }
+ else
+ stat_disp=L"";
+}
+
+static int is_out_open;
+static int paused;
+static int volume=255;
+static int pan=0;
+StringW cur_file;
+
+CRITICAL_SECTION sync;
+
+HANDLE hThread=0;
+
+void Config(HWND);
+void About(HWND p);
+void do_cfg(int s);
+void GetFileInfo(const in_char *file, wchar_t *title, int *len);
+
+const char *INI_FILE=0;
+const wchar_t *INI_DIRECTORY=0;
+int (*warand)()=0;
+float (*warandf)()=0;
+
+api_application *WASABI_API_APP = 0;
+// wasabi based services for localisation support
+api_language *WASABI_API_LNG = 0;
+HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
+api_memmgr* WASABI_API_MEMMGR = 0;
+api_config *AGAVE_API_CONFIG=0;
+
+static MKVDecoderCreator mkvCreator;
+static SingletonServiceFactory<svc_mkvdecoder, MKVDecoderCreator> mkvFactory;
+
+void SetFileExtensions(void)
+{
+ static char fileExtensionsString[1200] = {0}; // "OGG\0Ogg files (*.OGG)\0"
+ char* end = 0;
+ StringCchCopyExA(fileExtensionsString, 1200, "OGG;OGA", &end, 0, 0);
+ StringCchCopyExA(end+1, 1200, WASABI_API_LNGSTRING(IDS_OGG_FILES), 0, 0, 0);
+ mod.FileExtensions = fileExtensionsString;
+}
+
+int Init()
+{
+ if (!IsWindow(mod.hMainWindow))
+ return IN_INIT_FAILURE;
+
+ mod.UsesOutputPlug|=8;
+
+ warand = (int (*)())SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_RANDFUNC);
+ warandf = (float (*)())SendMessage(mod.hMainWindow, WM_WA_IPC, 1, IPC_GET_RANDFUNC);
+
+ ServiceBuild(WASABI_API_MEMMGR, memMgrApiServiceGuid);
+ ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID);
+ // loader so that we can get the localisation service api for use
+ ServiceBuild(WASABI_API_LNG, languageApiGUID);
+ ServiceBuild(WASABI_API_APP, applicationApiServiceGuid);
+
+ mkvFactory.Register(mod.service, &mkvCreator);
+
+ // need to have this initialised before we try to do anything with localisation features
+ WASABI_API_START_LANG(mod.hDllInstance,InVorbisLangGUID);
+
+ static wchar_t szDescription[256];
+ StringCchPrintfW(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_VORBIS_DECODER),VER);
+ mod.description = (char*)szDescription;
+
+ SetFileExtensions();
+
+ INI_FILE = (const char *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILE);
+ INI_DIRECTORY = (const wchar_t *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW);
+
+ os_ver.dwOSVersionInfoSize=sizeof(os_ver);
+ GetVersionEx(&os_ver);
+
+ InitializeCriticalSection(&sync);
+ do_cfg(0);
+ return IN_INIT_SUCCESS;
+}
+
+void Quit()
+{
+ winampGetExtendedFileInfoW_Cleanup();
+ DeleteCriticalSection(&sync);
+ mkvFactory.Deregister(mod.service);
+ ServiceRelease(WASABI_API_MEMMGR, memMgrApiServiceGuid);
+ ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID);
+ ServiceRelease(WASABI_API_LNG, languageApiGUID);
+ ServiceRelease(WASABI_API_APP, applicationApiServiceGuid);
+}
+
+int GetLength()
+{
+ return length;
+}
+
+int IsOurFile(const in_char *fn)
+{
+ if (PathIsURLW(fn))
+ {
+ const wchar_t *foo=wcsrchr(fn,L'.');
+ return foo ? !_wcsicmp(foo,L".ogg") : 0;
+ }
+ else return 0;
+}
+
+static UINT kbps_disp;
+
+static void out_close()
+{
+ if (is_out_open)
+ {
+ mod.outMod->Close();
+ mod.SAVSADeInit();
+ is_out_open=0;
+ }
+}
+
+static bool need_full_setinfo;
+
+static int out_open(const Decoder &dec)
+{
+ int max_l=mod.outMod->Open(dec.sr,dec.nch,dec.bps,-1,-1);
+ if (max_l<0) return 0;
+ mod.outMod->SetVolume(-666);
+ mod.outMod->SetPan(pan);
+ mod.SAVSAInit(max_l,dec.sr);
+ mod.VSASetInfo(dec.sr,dec.nch);
+
+ is_out_open=1;
+ need_full_setinfo=1;
+ return 1;
+}
+
+void Decoder::wa2_setinfo(UINT cur)
+{
+ UINT disp=file->get_avg_bitrate();
+ if (!cfg_abr)
+ {
+ disp=cur;
+ }
+ if ((disp && disp!=kbps_disp) || need_full_setinfo)
+ {
+ kbps_disp=disp;
+ if (need_full_setinfo)
+ {
+ mod.SetInfo(disp,sr/1000,nch,1);
+ need_full_setinfo=0;
+ }
+ else mod.SetInfo(disp,-1,-1,1);
+ }
+}
+
+static bool need_movefile;
+static void process_movefile();
+
+void alloc_buffers(Decoder & dec,short ** visbuf,char ** sample_buf,int * s_size)
+{
+ *s_size=576 * (dec.bps>>3) * dec.nch;
+
+ if (*sample_buf) *sample_buf=(char*)realloc(*sample_buf,*s_size*2);
+ else *sample_buf=(char*)malloc(*s_size*2);
+
+ if (dec.bps>16)
+ {
+ int vs=576*2*dec.nch;
+ if (*visbuf) *visbuf=(short*)realloc(*visbuf,vs);
+ else *visbuf=(short*)malloc(vs);
+ }
+ else if (*visbuf) {free(*visbuf);*visbuf=0;}
+}
+
+static DWORD WINAPI PlayThread(Decoder &dec)
+{
+ int pos_base=0;
+ int samp_wr=0;
+ int done=0;
+ int upd=0;
+ __int64 brate;
+ int br_div,br_t;
+ short* visbuf=0;
+ char *sample_buf=0;
+ int retries=0;
+ int s_size=0;
+
+ pos_ms=0;
+
+ {
+ int r;
+ r=dec.play_init();
+ if (r)
+ {
+ if (!kill) Sleep(50);
+ if (!kill) Sleep(50);
+ if (!kill) Sleep(50);
+ if (!kill) Sleep(50);
+ if (!kill)
+ {
+ if (r==2) PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0);
+ else PostMessage(mod.hMainWindow,WM_COMMAND,40047,0);
+ }
+ delete &dec;
+ return 0;
+ }
+ theFile->do_prebuf();
+ }
+
+ brate=0;
+ br_div=0;
+ upd=0;
+
+ alloc_buffers(dec,&visbuf,&sample_buf,&s_size);
+
+ //int f_type=theFile->GetType();
+ bool is_live=theFile->IsLive();
+
+ while(!kill)
+ {
+ if (!theFile) break;//ugh
+ if (seek_to!= -1)
+ {
+ UINT _st=seek_to;
+ int r=1;
+ seek_to=-1;
+ if (theFile)
+ {
+ theFile->use_prebuf=0;
+ int link=theFile->vf.current_link;
+ r=dec.Seek((double)_st*0.001);
+ if (link!=theFile->vf.current_link) PostMessage(mod.hMainWindow,WM_USER,0,243);
+ }
+ else r=1;
+ if (!r)
+ {
+ pos_base=pos_ms=_st;
+ mod.outMod->Flush(pos_ms);
+ samp_wr=0;
+ done=0;
+ theFile->do_prebuf();
+ }
+ }
+
+ if (need_movefile && paused)//HACK, prevent stupid lockup
+ {
+ process_movefile();
+ if (!theFile) break;//#@!
+ dec.file=theFile;
+ dec.Flush();
+ }
+
+ if (done)
+ {
+ // mod.outMod->CanWrite();
+ if (!mod.outMod->IsPlaying())
+ {
+ PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0);
+ break;
+ }
+ Sleep(10);
+ }
+ else if (mod.outMod->CanWrite() >= (s_size<<(mod.dsp_isactive()?1:0)))
+ {
+ int l=0;
+ while(1)
+ {
+ if (!dec.need_reopen)
+ {
+ l+=dec.Read(s_size-l,sample_buf+l);
+ if (l>=s_size) break;
+
+ int link=theFile->vf.current_link;
+ if (need_movefile)//safe not to flush here
+ {
+ process_movefile();
+ if (!theFile) break;//#@!
+ dec.file=theFile;
+ }
+ if (!dec.DoFrame()) break;
+ if (kill) break;
+
+ if (link!=theFile->vf.current_link)
+ {
+ PostMessage(mod.hMainWindow,WM_USER,0,243);
+ }
+ br_t=ov_bitrate_instant(&theFile->vf);
+ if (br_t>0)
+ {
+ int i = dec.DataAvailable()/((dec.bps/8)*dec.nch);
+ br_div+=i;
+ brate+=(__int64)(br_t*i);
+ }
+ if (need_full_setinfo || (!((++upd)%200) && br_div))
+ {
+ if (!br_div) {br_div=1;brate=theFile->get_avg_bitrate();}
+ dec.wa2_setinfo((int)((__int64)brate/(__int64)br_div/(__int64)1000));
+ brate=0;
+ br_div=0;
+ }
+ }
+ if (dec.need_reopen)
+ {//blargh, new PCM format
+ if (l>0) break;//got samples to play, we'll deal with this later
+ //l=0;
+ while(!kill && mod.outMod->IsPlaying()) Sleep(1);
+ if (kill) break;
+ out_close();
+ if (!out_open(dec))//boo
+ {
+ PostMessage(mod.hMainWindow,WM_COMMAND,40047,0);
+ kill=1;
+ break;
+ }
+ alloc_buffers(dec,&visbuf,&sample_buf,&s_size);
+ dec.need_reopen=0;
+ }
+ }
+
+ if (kill || !theFile) break;
+
+ if (l<=0 && (!is_live || (--retries)<0))
+ {
+ mod.outMod->Write(sample_buf,0);
+ done=1;
+ }
+ else if (l<=0)
+ {
+ int r;
+ out_close();
+ EnterCriticalSection(&sync);
+ delete theFile;
+ theFile=0;
+ LeaveCriticalSection(&sync);
+ if (sample_buf)
+ {
+ free(sample_buf);
+ sample_buf=0;
+ }
+
+ r=dec.play_init();
+
+ if (r)
+ {
+ mod.outMod->Write(sample_buf,0);
+ done=1;
+ }
+ else
+ {
+ theFile->do_prebuf();
+ }
+ }
+ else
+ {
+ if (l<s_size) memset(sample_buf+l,dec.bps==8 ? 0x80 : 0,s_size-l);
+ char * vis=sample_buf;
+ UINT vis_bps=dec.bps;
+ if (dec.bps>16)
+ {
+ UINT n;
+ UINT d=dec.bps>>3;
+ char * foo=sample_buf+d-2;
+ for(n=0;n<576*dec.nch;n++)
+ {
+ visbuf[n]=*(short*)foo;
+ foo+=d;
+ }
+ vis=(char*)visbuf;
+ vis_bps=16;
+ }
+ mod.SAAddPCMData(vis,dec.nch,vis_bps,pos_ms);
+ mod.VSAAddPCMData(vis,dec.nch,vis_bps,pos_ms);
+
+ if (mod.dsp_isactive())
+ {
+ l=(l<<3)/(dec.bps*dec.nch);
+ l=mod.dsp_dosamples((short*)sample_buf,l,dec.bps,dec.nch,dec.sr);
+ l*=(dec.nch*dec.bps)>>3;
+ }
+ if (kill) break;
+ mod.outMod->Write((char*)sample_buf,l);
+
+ samp_wr+=(8*l)/(dec.bps*dec.nch);
+ pos_ms=pos_base+MulDiv(1000,samp_wr,dec.sr);
+ }
+ }
+ else
+ {
+ theFile->Idle();
+ }
+ }
+
+ // out_close();
+ // gay gapless plugins puke, need to call this from stop
+ // ok, hetero (out_wave v2.x / out_ds v1.4+) gapless plugins wouldn't puke anymore
+
+ if (theFile)
+ {
+ VorbisFile * t=theFile;
+ EnterCriticalSection(&sync);
+ theFile=0;
+ LeaveCriticalSection(&sync);
+ delete t;
+ }
+
+ if (sample_buf)
+ {
+ free(sample_buf);
+ sample_buf=0;
+ }
+
+ if (need_movefile) process_movefile();
+
+ /* if (!kill)
+ {
+ CloseHandle(hThread);
+ hThread=0;
+ }*/
+
+ if (visbuf) free(visbuf);
+
+ delete &dec;
+ return 0;
+}
+
+static StringW move_src,move_dst;
+static bool mf_ret;
+
+static void do_movefile()
+{
+ mf_ret=1;
+ winampGetExtendedFileInfoW_Cleanup();
+ if (!DeleteFileW(move_dst)) mf_ret=0;
+ else
+ {
+ if (!MoveFileW(move_src,move_dst))
+ {
+ if (!CopyFileW(move_src,move_dst,0)) mf_ret=0;
+ DeleteFileW(move_src);
+ }
+ }
+}
+
+static void process_movefile()
+{
+ if (theFile)
+ {
+ StringW f_path;
+ f_path.AddString(theFile->url);
+ double pos=theFile->GetPos();
+ EnterCriticalSection(&sync);
+ delete theFile;
+ theFile=0;
+
+ do_movefile();
+
+ theFile=VorbisFile::Create(f_path,0);
+ LeaveCriticalSection(&sync);
+ if (theFile)
+ {
+ theFile->Seek(pos);
+ }
+ }
+ else do_movefile();
+ need_movefile=0;
+}
+
+bool sync_movefile(const wchar_t * src,const wchar_t * dst)//called from info_.cpp
+{
+ move_src=src;
+ move_dst=dst;
+ need_movefile=1;
+ if (!theFile) process_movefile();
+ else
+ {
+ while(need_movefile && hThread) Sleep(1);
+ if (need_movefile) process_movefile();//shouldnt really happen
+ move_src=L"";
+ move_dst=L"";
+ PostMessage(mod.hMainWindow,WM_USER,0,243);
+ }
+
+ return mf_ret;
+}
+
+
+int Decoder::play_init()//still messy
+{
+ if (play_inited) return 0;
+
+ kbps_disp=0;
+
+ VorbisFile * t=VorbisFile::Create(cur_file,0);
+ if (!t)
+ {
+#ifdef _DEBUG
+ OutputDebugString(L"can't open file\n");
+#endif
+ // if (scream) MessageBox(mod.hMainWindow,"error opening file",0,MB_ICONERROR);
+ return 2;
+ }
+ Init(t);
+ if (!out_open(*this))
+ {
+#ifdef _DEBUG
+ OutputDebugString(L"can't open output\n");
+#endif
+ delete t;
+ return 1;
+ }
+
+ EnterCriticalSection(&sync);
+ theFile=t;
+ LeaveCriticalSection(&sync);
+
+ wa2_setinfo(theFile->get_avg_bitrate());
+
+ {
+ double v=theFile->Length();
+ if (v==OV_EINVAL || v<=0) length=-1;
+ else length=(int)(v*1000.0);
+ }
+
+ play_inited=1;
+
+ return 0;
+}
+
+int Play(const in_char *fn)
+{
+ seek_to=-1;
+ kill=0;
+ length=0;
+ paused=0;
+
+ show_stat(0);
+
+ EnterCriticalSection(&sync);
+ cur_file=fn;
+ LeaveCriticalSection(&sync);
+
+ Decoder * dec=new Decoder;
+
+ if (!PathIsURLW(fn))
+ {
+ mod.is_seekable=1;
+#if 1
+ int rv=dec->play_init();
+ if (rv)
+ {
+ delete dec;
+ if (rv==2) return -1;
+ return 1;
+ }
+#endif
+ }
+ else mod.is_seekable=cfg_httpseek2;
+
+ {
+ DWORD id;
+ hThread=CreateThread(0,0,(LPTHREAD_START_ROUTINE)PlayThread,dec,CREATE_SUSPENDED,&id);
+ }
+
+ if (hThread)
+ {
+ SetThreadPriority(hThread, (int)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
+ ResumeThread(hThread);
+ return 0;
+ }
+ else
+ {
+ out_close();
+ delete dec;
+ return 1;
+ }
+}
+
+void Pause()
+{
+ if (!paused)
+ {
+ mod.outMod->Pause(1);
+ paused=1;
+ }
+}
+
+void UnPause()
+{
+ if (paused)
+ {
+ mod.outMod->Pause(0);
+ paused=0;
+ }
+}
+
+int IsPaused()
+{
+ return paused;
+}
+
+void Stop()
+{
+ if (hThread)
+ {
+ kill=1;
+ EnterCriticalSection(&sync);
+ if (theFile) theFile->stopping=1;
+ LeaveCriticalSection(&sync);
+ if (WaitForSingleObject(hThread,10000)!=WAIT_OBJECT_0)
+ {
+ TerminateThread(hThread,0);
+ //MessageBox(mod.hMainWindow,"error asking thread to die",0,MB_ICONERROR);
+ }
+ CloseHandle(hThread);
+ hThread=0;
+ out_close();
+ }
+ show_stat(0);
+ winampGetExtendedFileInfoW_Cleanup();
+}
+
+void EQSet(int on, char data[10], int preamp)
+{
+}
+
+int GetOutputTime()
+{
+ return pos_ms+(mod.outMod->GetOutputTime()-mod.outMod->GetWrittenTime());
+}
+
+void SetOutputTime(int t)
+{
+ seek_to=t;
+ EnterCriticalSection(&sync);
+ if (theFile) theFile->abort_prebuf=1;
+ LeaveCriticalSection(&sync);
+}
+
+void SetVolume(int v)
+{
+ mod.outMod->SetVolume(volume=v);
+}
+
+void SetPan(int p)
+{
+ mod.outMod->SetPan(pan=p);
+}
+
+//int InfoBox(char *file, HWND parent); //old
+int RunInfoDlg(const in_char * url,HWND parent);
+
+In_Module mod=
+{
+ IN_VER_RET,
+ "nullsoft(in_vorbis.dll)",
+ 0,0,
+ 0,
+ 1,
+ 1,
+
+ Config,
+ About,
+
+ Init,
+ Quit,
+
+ GetFileInfo,
+ RunInfoDlg,
+
+ IsOurFile,
+ Play,
+ Pause,
+ UnPause,
+ IsPaused,
+ Stop,
+
+ GetLength,
+ GetOutputTime,
+ SetOutputTime,
+
+ SetVolume,
+ SetPan,
+
+ 0,0,0,0,0,0,0,0,0,0,0,
+ EQSet,
+
+ 0,
+ 0,
+};
+
+extern "C" {
+ __declspec( dllexport ) In_Module * winampGetInModule2()
+ {
+ return &mod;
+ }
+}
+
+void VorbisFile::Status(const wchar_t * zzz)
+{
+ if (primary)
+ show_stat(zzz);
+}
+
+bool VorbisFile::Aborting()
+{
+ return stopping || (primary && kill);
+}
+
+
+Info::Info(const wchar_t *filename) : filename(filename), vc(0)
+{
+ VorbisFile * vf = VorbisFile::Create(filename,true);
+ if(!vf)
+ return;
+
+ numstreams = vf->vf.links;
+ if(numstreams)
+ {
+ // now copy the comment section to our own memory...
+ stream = vf->vf.current_link; // this is the stream we're editing...
+ vc = (vorbis_comment*)calloc(sizeof(vorbis_comment),numstreams);
+
+ for(int i=0; i<numstreams; i++)
+ { // one comment section per stream
+ vorbis_comment *c = ov_comment(&vf->vf,i);
+
+ vc[i].comments = c->comments;
+ vc[i].user_comments = (char **)malloc(sizeof(char*)*c->comments);
+ vc[i].comment_lengths = (int *)malloc(sizeof(int)*c->comments);
+
+ for(int j=0;j<vc[i].comments;j++)
+ { // copy the comments over
+ vc[i].user_comments[j] = _strdup(c->user_comments[j]);
+ vc[i].comment_lengths[j] = c->comment_lengths[j];
+ }
+ vc[i].vendor=_strdup(c->vendor);
+ }
+ }
+ delete vf;
+}
+
+Info::~Info()
+{
+ if(vc) {
+ for(int i=0; i < numstreams; i++)
+ vorbis_comment_clear(&vc[i]);
+ free(vc);
+ }
+}
+
+bool Info::Save()
+{
+ return !!modify_file(filename,vc,numstreams);
+}
+
+int Info::GetNumMetadataItems()
+{
+ return vc[stream].comments;
+}
+
+void Info::EnumMetadata(int n, wchar_t *key, int keylen, wchar_t *val, int vallen)
+{
+ if(keylen) key[0]=0;
+ if(vallen) val[0]=0;
+ if(!vc) return;
+ if(!vc[stream].user_comments[n]) return;
+ AutoWide comment(vc[stream].user_comments[n],CP_UTF8);
+ const wchar_t* eq = wcschr((const wchar_t*)comment,L'=');
+ if(eq)
+ {
+ if(keylen) lstrcpynW(key,comment,(int)min(eq - comment + 1,keylen));
+ if(vallen) lstrcpynW(val,eq+1,vallen);
+ }
+ else
+ {
+ if(keylen) lstrcpynW(key,L"COMMENT",keylen);
+ if(vallen) lstrcpynW(val,comment,vallen);
+ }
+}
+
+void Info::RemoveMetadata(wchar_t * key)
+{
+ wchar_t k[256] = {0};
+ for(int i=0; i<GetNumMetadataItems(); i++)
+ {
+ EnumMetadata(i,k,256,0,0);
+ if(_wcsicmp(k,key)==0)
+ RemoveMetadata(i);
+ }
+}
+
+void Info::RemoveMetadata(int n)
+{
+ if(!vc) return;
+ free(vc[stream].user_comments[n]);
+
+ for(int i=n+1; i<vc[stream].comments; i++)
+ {
+ vc[stream].user_comments[i-1] = vc[stream].user_comments[i];
+ if(vc[stream].comment_lengths)
+ vc[stream].comment_lengths[i-1] = vc[stream].comment_lengths[i];
+ }
+ vc[stream].comments--;
+ vc[stream].user_comments = (char**)realloc(vc[stream].user_comments,sizeof(vc[stream].user_comments[0]) * vc[stream].comments);
+ if(vc[stream].comment_lengths)
+ vc[stream].comment_lengths = (int*)realloc(vc[stream].comment_lengths,sizeof(vc[stream].comment_lengths[0]) * vc[stream].comments);
+}
+
+void Info::SetMetadata(wchar_t *key, wchar_t *val)
+{
+ bool set=false;
+ wchar_t k[256] = {0};
+ for(int i=0; i<GetNumMetadataItems(); i++)
+ {
+ EnumMetadata(i,k,256,0,0);
+ if(_wcsicmp(k,key)==0)
+ {
+ SetMetadata(i,key,val);
+ set=true;
+ }
+ }
+ if(!set)
+ {
+ int n = vc[stream].comments++;
+ vc[stream].user_comments = (char**)realloc(vc[stream].user_comments,sizeof(vc[stream].user_comments[0]) * vc[stream].comments);
+ if(vc[stream].comment_lengths)
+ vc[stream].comment_lengths = (int*)realloc(vc[stream].comment_lengths,sizeof(vc[stream].comment_lengths[0]) * vc[stream].comments);
+ vc[stream].user_comments[n] = NULL;
+ SetMetadata(n,key,val);
+ }
+}
+
+void Info::SetMetadata(int n, wchar_t *key, wchar_t *val)
+{
+ AutoChar k(key,CP_UTF8);
+ AutoChar v(val,CP_UTF8);
+
+ int l = (int)(strlen(k)+strlen(v)+2);
+ char * c = (char*)malloc(l);
+ StringCchPrintfA(c,l,"%s=%s",(char*)k,(char*)v);
+
+ if(vc[stream].user_comments[n])
+ free(vc[stream].user_comments[n]);
+
+ vc[stream].user_comments[n] = c;
+ if(vc[stream].comment_lengths)
+ vc[stream].comment_lengths[n] = l-1;
+}
+
+void Info::SetTag(int n,wchar_t *key) // changes the key name
+{
+ wchar_t val[2048] = {0};
+ EnumMetadata(n,NULL,0,val,2048);
+ SetMetadata(n,key,val);
+}
+
+Info *setMetadata = 0;
+
+extern "C"
+{
+ static wchar_t m_lastfn[2048];
+
+ #define START_TAG_ALIAS(name, alias) if (KeywordMatch(data, name)) lookup=alias
+ #define TAG_ALIAS(name, alias) else if (KeywordMatch(data, name)) lookup=alias
+
+ __declspec( dllexport ) int winampSetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *val)
+ {
+ if (!setMetadata || setMetadata && wcscmp(fn,m_lastfn))
+ {
+ if (setMetadata)
+ {
+ delete setMetadata;
+ setMetadata = 0;
+ }
+
+ setMetadata = new Info(fn);
+ if(setMetadata->Error())
+ {
+ delete setMetadata;
+ m_lastfn[0] = 0;
+ return 0;
+ }
+ lstrcpynW(m_lastfn,fn, 2048);
+ }
+
+ wchar_t *lookup=0;
+ START_TAG_ALIAS("artist", L"ARTIST");
+ TAG_ALIAS("title", L"TITLE");
+ TAG_ALIAS("album", L"ALBUM");
+ TAG_ALIAS("genre", L"GENRE");
+ TAG_ALIAS("comment", L"COMMENT");
+ TAG_ALIAS("year", L"DATE");
+ TAG_ALIAS("track", L"TRACKNUMBER");
+ TAG_ALIAS("albumartist", L"ALBUM ARTIST");
+ TAG_ALIAS("composer", L"COMPOSER");
+ TAG_ALIAS("disc", L"DISCNUMBER");
+ TAG_ALIAS("publisher", L"PUBLISHER");
+ TAG_ALIAS("conductor", L"CONDUCTOR");
+ TAG_ALIAS("tool", L"ENCODED-BY");
+ TAG_ALIAS("replaygain_track_gain", L"REPLAYGAIN_TRACK_GAIN");
+ TAG_ALIAS("replaygain_track_peak", L"REPLAYGAIN_TRACK_PEAK");
+ TAG_ALIAS("replaygain_album_gain", L"REPLAYGAIN_ALBUM_GAIN");
+ TAG_ALIAS("replaygain_album_peak", L"REPLAYGAIN_ALBUM_PEAK");
+ TAG_ALIAS("GracenoteFileID", L"GRACENOTEFILEID");
+ TAG_ALIAS("GracenoteExtData", L"GRACENOTEEXTDATA");
+ TAG_ALIAS("bpm", L"BPM");
+ TAG_ALIAS("remixing", L"REMIXING");
+ TAG_ALIAS("subtitle", L"VERSION");
+ TAG_ALIAS("isrc", L"ISRC");
+ TAG_ALIAS("category", L"CATEGORY");
+ TAG_ALIAS("rating", L"RATING");
+ TAG_ALIAS("producer", L"PRODUCER");
+
+ if (!lookup)
+ return 0;
+
+#if 0
+ if (val && *val)
+ {
+ if(KeywordMatch("rating",data))
+ {
+ wchar_t temp[128] = {0};
+ StringCchPrintfW(temp, 128, L"%u", _wtoi(val)*20);
+ val=temp;
+ }
+ }
+ AutoChar utf8(val, CP_UTF8);
+
+ for(int i=0;i<m_vc->comments;i++)
+ {
+ char *c=m_vc[m_curstream].user_comments[i];
+ if(!c) continue;
+ char *p=strchr(c,'=');
+ if (p && *p)
+ {
+ if(strlen(data) == (p-c) && !_strnicmp(c,data,p-c))
+ {
+ //found!
+ if (val && val[0])
+ {
+ int added_buf_len = strlen(utf8)+strlen(lookup)+2;
+ m_vc[m_curstream].user_comments[i]=(char *)realloc(m_vc[m_curstream].user_comments[i],added_buf_len);
+ StringCchPrintfA(m_vc[m_curstream].user_comments[i],added_buf_len,"%s=%s",lookup,(char *)utf8);
+ m_vc[m_curstream].comment_lengths[i]=strlen(m_vc[m_curstream].user_comments[i]);
+ }
+ else
+ {
+ free(m_vc[m_curstream].user_comments[i]);
+ m_vc[m_curstream].user_comments[i]=0;
+ m_vc[m_curstream].comment_lengths[i]=0;
+ }
+ return 1;
+ }
+ }
+ }
+
+ //not found, so create new field
+ if (val && val[0])
+ {
+ int k=m_vc[m_curstream].comments++;
+ m_vc[m_curstream].user_comments=(char **)realloc(m_vc[m_curstream].user_comments,sizeof(char*)*m_vc[m_curstream].comments);
+ m_vc[m_curstream].comment_lengths=(int *)realloc(m_vc[m_curstream].comment_lengths,sizeof(int)*m_vc[m_curstream].comments);
+ int added_buf_len = strlen(utf8)+strlen(lookup)+2;
+ m_vc[m_curstream].user_comments[k]=(char *)malloc(added_buf_len);
+ StringCchPrintfA(m_vc[m_curstream].user_comments[k],added_buf_len,"%s=%s",lookup,(char *)utf8);
+ m_vc[m_curstream].comment_lengths[k]=strlen(m_vc[m_curstream].user_comments[k]);
+ }
+#endif
+
+ if (val && *val)
+ {
+ if(KeywordMatch("rating",data))
+ {
+ wchar_t temp[128] = {0};
+ StringCchPrintfW(temp, 128, L"%u", _wtoi(val)*20);
+ setMetadata->SetMetadata(lookup, temp);
+ }
+ else
+ {
+ setMetadata->SetMetadata(lookup, val);
+ }
+ }
+ else
+ {
+ setMetadata->RemoveMetadata(lookup);
+ if(KeywordMatch("comment",data))
+ {
+ // need to remove this one also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using this alternate field
+ setMetadata->RemoveMetadata(L"DESCRIPTION");
+ }
+ else if(KeywordMatch("year",data))
+ {
+ // need to remove this one also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using this alternate field
+ setMetadata->RemoveMetadata(L"YEAR");
+ }
+ else if(KeywordMatch("track",data))
+ {
+ // need to remove this one also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using this alternate field
+ setMetadata->RemoveMetadata(L"TRACK");
+ }
+ else if(KeywordMatch("albumartist",data))
+ {
+ // need to remove these two, also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using these alternate fields
+ setMetadata->RemoveMetadata(L"ALBUMARTIST");
+ setMetadata->RemoveMetadata(L"ENSEMBLE");
+ }
+ else if(KeywordMatch("publisher",data))
+ {
+ // need to remove this one also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using this alternate field
+ setMetadata->RemoveMetadata(L"ORGANIZATION");
+ }
+ else if(KeywordMatch("category",data))
+ {
+ // need to remove these two also, or else it's gonna look like delete doesn't work
+ // if the file was tagged using these alternate fields
+ setMetadata->RemoveMetadata(L"CONTENTGROUP");
+ setMetadata->RemoveMetadata(L"GROUPING");
+ }
+ }
+
+ return 1;
+ }
+
+ __declspec( dllexport ) int winampWriteExtendedFileInfo()
+ {
+ if(!setMetadata) return 0;
+
+ bool ret = setMetadata->Save();
+ delete setMetadata;
+ setMetadata = 0;
+
+ // update last modified so we're not re-queried on our own updates
+ UpdateFileTimeChanged(m_lastfn);
+
+ return ret;
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_vorbis/winnt_helper.cpp b/Src/Plugins/Input/in_vorbis/winnt_helper.cpp
new file mode 100644
index 00000000..da44ca42
--- /dev/null
+++ b/Src/Plugins/Input/in_vorbis/winnt_helper.cpp
@@ -0,0 +1,2 @@
+#include "main.h"
+#include "api__in_vorbis.h"