aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_nsv
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Input/in_nsv')
-rw-r--r--Src/Plugins/Input/in_nsv/BigLib.cpp1428
-rw-r--r--Src/Plugins/Input/in_nsv/Main.cpp976
-rw-r--r--Src/Plugins/Input/in_nsv/api.h16
-rw-r--r--Src/Plugins/Input/in_nsv/config.cpp197
-rw-r--r--Src/Plugins/Input/in_nsv/in_nsv.rc294
-rw-r--r--Src/Plugins/Input/in_nsv/in_nsv.sln30
-rw-r--r--Src/Plugins/Input/in_nsv/in_nsv.vcxproj274
-rw-r--r--Src/Plugins/Input/in_nsv/in_nsv.vcxproj.filters67
-rw-r--r--Src/Plugins/Input/in_nsv/infodlg.cpp1089
-rw-r--r--Src/Plugins/Input/in_nsv/nsv_logo.bmpbin0 -> 7480 bytes
-rw-r--r--Src/Plugins/Input/in_nsv/proxydt.h16
-rw-r--r--Src/Plugins/Input/in_nsv/resource.h86
-rw-r--r--Src/Plugins/Input/in_nsv/version.rc239
13 files changed, 4512 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_nsv/BigLib.cpp b/Src/Plugins/Input/in_nsv/BigLib.cpp
new file mode 100644
index 00000000..5e8c541f
--- /dev/null
+++ b/Src/Plugins/Input/in_nsv/BigLib.cpp
@@ -0,0 +1,1428 @@
+#ifdef WINAMPX
+#define WIN32_LEAN_AND_MEAN 1
+#include <windows.h>
+#include "stdio.h"
+#include "proxydt.h"
+#include <winsock2.h>
+#include ".\ws2tcpip.h"
+#include ".\wininet.h"
+#include "../jnetlib/jnetlib.h"
+
+#include <urlmon.h>
+
+
+extern void SendMetadata( char *data, int arg );
+HRESULT JNetLibDownloadToFile(LPVOID lpUnused1, LPSTR lpWPADLocation, LPSTR lpTempFile, LPVOID lpUnused2, LPVOID lpUnused3);
+enum {
+ BK_UNKNOWN = 0,
+ BK_IE4 = 1,
+ BK_NETSCAPE4 = 2,
+ BK_NETSCAPE6 = 3,
+ BK_MOZILLA = 4,
+ BK_FIREFOX = 5
+};
+
+// browser info struct
+typedef struct{
+ LPSTR lpName;
+ BOOL bSupported;
+}BK_INFO;
+
+// browser info
+BK_INFO BrowserInfo[] = {
+ "Unknown", FALSE,
+ "IE 4.0+", TRUE,
+ "Netscape 4 or 5", FALSE,
+ "Netscape 6+", TRUE,
+ "Mozilla", TRUE,
+ "Firefox", TRUE
+};
+
+// Global variables
+int gBrowserKind = BK_UNKNOWN;
+
+int gTryAuto = 1;
+
+// Exported C functions
+extern "C" BOOL ProxyInit();
+extern "C" void ProxyDeInit();
+extern "C" int ResolvProxyFromURL(LPSTR lpURL, LPSTR lpHostname, LPSTR lpDest);
+
+// Global C functions
+BOOL IsIEProxySet();
+int GetIESettings();
+int ResolveURL_IE(LPSTR lpURL, LPSTR lpHostname, LPSTR lpIPAddress, int sizeof_address, int *pnPort);
+BOOL IsFirefoxProxySet();
+int GetFirefoxSettings();
+int ResolveURL_Firefox(LPSTR lpURL, LPSTR lpHostname, LPSTR lpIPAddress, int sizeof_address, int *pnPort);
+BOOL IsMozillaProxySet();
+int GetMozillaSettings();
+int ResolveURL_Mozilla(LPSTR lpURL, LPSTR lpHostname, LPSTR lpIPAddress, int sizeof_address, int *pnPort);
+int GetDefaultBrowser();
+int ReadWPADFile(LPSTR lpWPADLocation, LPSTR pIPAddress, int *pnPort);
+int GetFirefoxOrMozillaSettings(BOOL bFirefox);
+BOOL IsFirefoxOrMozillaProxySet(BOOL bFirefox);
+int ResolveURL_MozillaOrFirefox(BOOL bFirefox, LPSTR lpURL, LPSTR lpHostname, LPSTR lpIPAddress, int sizeof_address, int *pnPort);
+
+// exported functions
+extern "C" BOOL ProxyInit()
+{
+ BOOL bRet;
+
+ bRet = FALSE;
+ gBrowserKind = GetDefaultBrowser();
+
+ switch(gBrowserKind) {
+ case BK_IE4:
+ bRet = IsIEProxySet();
+ break;
+
+ case BK_MOZILLA:
+ bRet = IsMozillaProxySet();
+
+ break;
+
+ case BK_FIREFOX:
+ bRet = IsFirefoxProxySet();
+ break;
+ }
+
+ return bRet;
+}
+
+extern "C" void ProxyDeInit()
+{
+}
+
+extern "C" int ResolvProxyFromURL(LPSTR lpURL, LPSTR lpHostname, LPSTR lpDest)
+{
+ // lpURL = URL to resolve
+ // lpHostname = hostname
+ // lpDest = where to store the result, such as "www.proxyserver.com:8080"
+ char szIPAddress[MAX_PATH] = {0};
+ int ret, nPort=0;
+
+ lpDest[0]=0;
+
+ if(lpURL && lpHostname && lpDest) {
+ switch(gBrowserKind) {
+ case BK_IE4:
+ ret = ResolveURL_IE(lpURL, lpHostname, szIPAddress, sizeof(szIPAddress), &nPort);
+ break;
+
+ case BK_MOZILLA:
+ ret = ResolveURL_Mozilla(lpURL, lpHostname, szIPAddress, sizeof(szIPAddress), &nPort);
+ break;
+
+ case BK_FIREFOX:
+ ret = ResolveURL_Firefox(lpURL, lpHostname, szIPAddress, sizeof(szIPAddress), &nPort);
+ break;
+ }
+
+ if(ret == 0) {
+ if ( szIPAddress[0] )
+ {
+ wsprintf(lpDest, "%s:%d", szIPAddress, nPort);
+ return 1;
+ }
+ else return 0;
+ }
+ else return 0;
+ }
+ else return -1;
+
+}
+
+int GetDefaultBrowser()
+{
+ DWORD dwSize, dwType;
+ TCHAR valueBuf[MAX_PATH] = {0};
+ DWORD valueSize = sizeof(valueBuf);
+ HKEY hKey;
+ long lRet;
+
+
+ memset(valueBuf, 0, sizeof(valueBuf));
+ lRet = RegOpenKeyEx(HKEY_CLASSES_ROOT, "http\\shell\\open\\ddeexec\\Application", 0, KEY_READ, &hKey);
+ if (lRet == ERROR_SUCCESS) {
+ dwSize = valueSize;
+ lRet = RegQueryValueEx(hKey, "", NULL, &dwType, (LPBYTE)valueBuf, &dwSize);
+ if(lRet == ERROR_SUCCESS && dwType == REG_SZ) {
+ if (_tcsicmp(_T("NSShell"), valueBuf) == 0) { //NS 4.x
+ return BK_NETSCAPE4;
+ } else if (_tcsicmp(_T("IExplore"), valueBuf) == 0) { //IE 4+
+ return BK_IE4;
+ } else if (_tcsicmp(_T("Mozilla"), valueBuf) == 0) { //Mozilla
+ return BK_MOZILLA;
+ } else if (_tcsicmp(_T("Firefox"), valueBuf) == 0) { //Firefox
+ return BK_FIREFOX;
+ }
+ }
+ }
+ RegCloseKey(hKey);
+
+ lRet = RegOpenKeyEx(HKEY_CLASSES_ROOT, "http\\shell\\open\\command", 0, KEY_READ, &hKey);
+ if(lRet == ERROR_SUCCESS) {
+ dwSize = valueSize;
+ lRet = RegQueryValueEx(hKey, "", NULL, &dwType, (LPBYTE)valueBuf, &dwSize);
+ if(lRet == ERROR_SUCCESS && dwType == REG_SZ) {
+ if(strstr(valueBuf, "MOZILLA")) {
+ return BK_MOZILLA;
+ }
+ if(strstr(valueBuf, "NETSCAPE")) {
+ return BK_MOZILLA;
+ }
+ if(strstr(valueBuf, "FIREFOX")) {
+ return BK_FIREFOX;
+ }
+ }
+ }
+ RegCloseKey(hKey);
+
+ return BK_UNKNOWN;
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// helper functions for JSProxy.dll
+DWORD __stdcall ResolveHostName(LPSTR lpszHostName, LPSTR lpszIPAddress, LPDWORD lpdwIPAddressSize);
+BOOL __stdcall IsResolvable(LPSTR lpszHost);
+DWORD __stdcall GetIPAddress(LPSTR lpszIPAddress, LPDWORD lpdwIPAddressSize);
+BOOL __stdcall IsInNet(LPSTR lpszIPAddress, LPSTR lpszDest, LPSTR lpszMask);
+
+// functions to get IE checkbox state
+BOOL GetAutomaticallyDetectSettingsCheckboxState();
+BOOL GetUseAProxyServerForYourLanCheckboxState();
+BOOL GetAutomaticConfigurationScriptCheckboxState();
+BOOL GetBypassProxyServerForLocalAddressesCheckboxState();
+
+// functions to actually get an IP address and port # of the proxy server
+int GetAutomaticDetectSettings(LPSTR lpIPAddress, int *pnPort);
+int GetProxyServerForLanProxySettings(LPSTR lpIPAddress, int *pnPort);
+int GetAutoConfigScriptProxySettings(LPSTR lpIPAddress, int *pnPort);
+
+// various helper functions
+BOOL IsDirect(LPSTR proxy);
+BOOL IsAProxy(LPSTR proxy);
+void reportFuncErr(TCHAR* funcName);
+char * strstri(LPSTR lpOne, LPSTR lpTwo);
+int GetProxyIP(LPSTR proxy, LPSTR szProxyIP);
+int GetProxyPort(LPSTR proxy);
+
+// some global variables
+char gszURL[1025] = {0};
+char gszHost[256] = {0};
+
+// returns TRUE if the user has set a proxy in IE
+BOOL IsIEProxySet()
+{
+ BOOL bAutomaticallyDetectSettings = GetAutomaticallyDetectSettingsCheckboxState();
+ BOOL bUseAutomaticConfigurationScript = GetAutomaticConfigurationScriptCheckboxState();
+ BOOL bUseAProxyServerForYourLan = GetUseAProxyServerForYourLanCheckboxState();
+
+ if(bAutomaticallyDetectSettings || bUseAutomaticConfigurationScript || bUseAProxyServerForYourLan) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+int ResolveURL_IE(LPSTR lpURL, LPSTR lpHostname, LPSTR lpIPAddress, int sizeof_address, int *pnPort)
+{
+ // get the state of the four checkboxes in the proxy settings dialog for IE
+ BOOL bAutomaticallyDetectSettings = GetAutomaticallyDetectSettingsCheckboxState();
+ BOOL bUseAutomaticConfigurationScript = GetAutomaticConfigurationScriptCheckboxState();
+ BOOL bUseAProxyServerForYourLan = GetUseAProxyServerForYourLanCheckboxState();
+ //BOOL bBypassProxyServerForLocalAddresses = GetBypassProxyServerForLocalAddressesCheckboxState();
+ int ret;
+
+ lstrcpyn(gszURL, lpURL, 1025);
+ lstrcpyn(gszHost, lpHostname, 256);
+
+ // if nothing checked, return
+ if(!bAutomaticallyDetectSettings && !bUseAutomaticConfigurationScript && !bUseAProxyServerForYourLan) {
+ return 0;
+ }
+
+ // if all three checkboxes on...
+ if(bAutomaticallyDetectSettings && gTryAuto)
+ {
+ // try the automatic configuration next
+ ret = GetAutomaticDetectSettings(lpIPAddress, pnPort);
+ if(ret == 0 && *pnPort) {
+ return 0;
+ }
+ gTryAuto = 0;
+
+ }
+
+ if ( bUseAutomaticConfigurationScript)
+ {
+ // try the automatic config script method first
+ ret = GetAutoConfigScriptProxySettings(lpIPAddress, pnPort);
+ if(ret == 0 && *pnPort ) {
+ return 0;
+ }
+
+ }
+
+ if ( bUseAProxyServerForYourLan)
+ {
+ // if still no success, try the "Use a proxy server for your lan" settings
+ ret = GetProxyServerForLanProxySettings(lpIPAddress, pnPort);
+ if(ret == 0 && *pnPort) {
+ return 0;
+ }
+ }
+
+
+
+
+
+ // no success...
+ return 0;
+
+
+}
+
+// handles the "Automatically Detect" checkbox
+int GetAutomaticDetectSettings(LPSTR lpIPAddress, int *pnPort)
+{
+ // By not specifying a domain name, Windows uses the local domain name,
+ // so form an http request to go to http://wpad/wpad.dat,
+ // store results in szWPADLocation and call URLDownloadToFileA()
+ if(lpIPAddress && pnPort) {
+ // download wpad.dat from the URL in szURL
+ return ReadWPADFile("http://wpad/wpad.dat", lpIPAddress, pnPort);
+ }
+
+ return -1;
+}
+
+// handles the "Use automatic configuration script" checkbox
+int GetAutoConfigScriptProxySettings(LPSTR lpIPAddress, int *pnPort)
+{
+ DWORD dwType, dwSize;
+ HKEY hKey;
+ char szWPADLocation[MAX_PATH] = {0};
+ long lRet;
+ int retval = -1;
+
+
+ if(!lpIPAddress) {
+ return retval;
+ }
+ if(!pnPort) {
+ return retval;
+ }
+
+ // use the registry read of HKCU\\software\microsoft\windows\current version\internet settings to see if "Use Automatic Configuration Script" is checked
+ lstrcpyn(szWPADLocation, "", MAX_PATH);
+
+ lRet = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_READ, &hKey);
+ if (lRet == ERROR_SUCCESS) {
+ dwSize = sizeof(szWPADLocation);
+ lRet = RegQueryValueEx(hKey, "AutoConfigURL", NULL, &dwType, (LPBYTE)szWPADLocation, &dwSize);
+ if(lRet == ERROR_SUCCESS && dwType == REG_SZ) {
+ retval = ReadWPADFile(szWPADLocation, lpIPAddress, pnPort);
+ }
+ }
+ RegCloseKey(hKey);
+
+ return retval; //0 = success
+}
+
+// handles the "Use a proxy server for your LAN" checkbox
+int GetProxyServerForLanProxySettings(LPSTR lpIPAddress, int *pnPort)
+{
+ DWORD dwType, dwSize;
+ HKEY hKey;
+ BOOL bDirectOrProxy;
+ char szProxy[MAX_PATH] = {0};
+ long lRet;
+ int retval = -1;
+
+ if(lpIPAddress) {
+ strcpy(lpIPAddress, "");
+ }
+ if(pnPort) {
+ *pnPort = 0;
+ }
+ lRet = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_READ, &hKey);
+ if (lRet == ERROR_SUCCESS) {
+ dwSize = sizeof(szProxy);
+ lRet = RegQueryValueEx(hKey, "ProxyServer", NULL, &dwType, (LPBYTE)szProxy, &dwSize);
+ if(lRet == ERROR_SUCCESS && dwType == REG_SZ) {
+ retval = 0;
+
+ bDirectOrProxy = FALSE;
+ if(IsDirect(szProxy)) {
+ // string is something like "DIRECT"
+ // It's a 'direct' kind of proxy.
+ bDirectOrProxy = TRUE;
+
+ // set the 'out' parameters
+ if(lpIPAddress) {
+ strcpy(lpIPAddress, "");
+ }
+ if(pnPort) {
+ *pnPort = 0;
+ }
+ }
+
+ if(IsAProxy(szProxy)) {
+ char szProxyIP[MAX_PATH] = {0};
+
+ // string is something like "D
+ bDirectOrProxy = TRUE;
+ GetProxyIP(szProxy, szProxyIP);
+ // It's a 'regular' kind of proxy, with an IP of %s and a port of %d\n", szProxyIP, GetProxyPort(szProxy)
+
+ // set the 'out' parameters
+ if(lpIPAddress) {
+ strcpy(lpIPAddress, szProxyIP);
+ }
+ if(pnPort) {
+ *pnPort = GetProxyPort(szProxy);
+ }
+ }
+
+ if(!bDirectOrProxy) {
+ // string is something like "10.0.0.1:4543"
+ LPSTR lpColon = NULL;
+
+ if ( isdigit(szProxy[0]) )
+ {
+ lpColon = strchr(szProxy, ':');
+ if(lpColon) {
+ *lpColon = '\0';
+
+ // set the 'out' parameters
+ if(lpIPAddress) {
+ strcpy(lpIPAddress, szProxy);
+ }
+ *lpColon = ':';
+ if(pnPort) {
+ *pnPort = GetProxyPort(szProxy);
+ }
+ }
+ }
+ else if ( strstr(szProxy,"http=") )
+ {
+ char *p = strstr(szProxy,"http=");
+ int offset= strlen("http=");
+ char *semi = strchr(p+offset, ';');
+ if(semi) {
+ *semi= '\0';
+ }
+ lpColon = strchr(p+offset, ':');
+ if(lpColon) {
+ *lpColon = '\0';
+ }
+ // set the 'out' parameters
+ if(lpIPAddress) {
+ strcpy(lpIPAddress, p+offset);
+ }
+ if (lpColon)
+ if(pnPort) {
+ *pnPort = (short)atoi(lpColon+1);
+ }
+ if ( !*pnPort ) *pnPort = 80;
+
+ }
+ else
+ {
+ if(lpIPAddress) {
+ strcpy(lpIPAddress, "");
+ }
+ if(pnPort) {
+ *pnPort = 0;
+ }
+ }
+ }
+ }
+ }
+ RegCloseKey(hKey);
+
+ return retval;
+}
+
+int ReadWPADFile(LPSTR lpWPADLocation, LPSTR lpIPAddress, int *pnPort)
+{
+ // Declare function pointers for the three autoproxy functions
+ pfnInternetInitializeAutoProxyDll pInternetInitializeAutoProxyDll;
+ pfnInternetDeInitializeAutoProxyDll pInternetDeInitializeAutoProxyDll;
+ pfnInternetGetProxyInfo pInternetGetProxyInfo;
+
+ // Declare and populate an AutoProxyHelperVtbl structure, and then
+ // place a pointer to it in a containing AutoProxyHelperFunctions
+ // structure, which will be passed to InternetInitializeAutoProxyDll:
+ AutoProxyHelperVtbl Vtbl = {IsResolvable, GetIPAddress, ResolveHostName, IsInNet };
+ AutoProxyHelperFunctions HelperFunctions = { &Vtbl };
+ HMODULE hModJS;
+ HRESULT hr;
+ char szTempPath[MAX_PATH] = {0};
+ char szTempFile[MAX_PATH] = {0};
+ int retval = 0;
+
+
+ if(!(hModJS = LoadLibrary("jsproxy.dll"))) {
+ reportFuncErr("LoadLibrary");
+ return -1;
+ }
+
+ if(!(pInternetInitializeAutoProxyDll = (pfnInternetInitializeAutoProxyDll)
+ GetProcAddress(hModJS, "InternetInitializeAutoProxyDll")) ||
+ !(pInternetDeInitializeAutoProxyDll = (pfnInternetDeInitializeAutoProxyDll)
+ GetProcAddress(hModJS, "InternetDeInitializeAutoProxyDll")) ||
+ !(pInternetGetProxyInfo = (pfnInternetGetProxyInfo)
+ GetProcAddress(hModJS, "InternetGetProxyInfo"))) {
+ FreeLibrary(hModJS);
+ reportFuncErr("GetProcAddress");
+ return -1;
+ }
+
+ if(lpIPAddress)
+ {
+ strcpy(lpIPAddress, "");
+ }
+ if(pnPort) {
+ *pnPort = 0;
+ }
+
+ GetTempPathA(sizeof(szTempPath)/sizeof(szTempPath[0]), szTempPath);
+ GetTempFileNameA(szTempPath, "X", 2, szTempFile);
+ //printf(" Downloading %s ...\n", lpWPADLocation);
+ hr = JNetLibDownloadToFile(NULL, lpWPADLocation, szTempFile, NULL, NULL);
+ if(hr == S_OK) {
+ if(!pInternetInitializeAutoProxyDll(0, szTempFile, NULL, &HelperFunctions, NULL)) {
+ //printf(" Calling 'InternetInitializeAutoProxyDll' in JSPROXY.DLL failed\n (usually because 'Use Automatic Configuration Script' checkbox is OFF)\n");
+ pInternetDeInitializeAutoProxyDll(NULL, 0);
+ FreeLibrary(hModJS);
+ retval = -1;
+ }else{
+ // printf("\n InternetInitializeAutoProxyDll returned: %d\n", returnVal);
+
+ // Delete the temporary file
+ // (or, to examine the auto-config script, comment out the
+ // file delete and substitute the following printf call)
+ // printf("\n The auto-config script temporary file is:\n %s\n", szTempFile);
+ DeleteFileA(szTempFile);
+
+ DWORD dwSize = 0;
+ LPSTR pProxy = NULL;
+ if(!pInternetGetProxyInfo((LPSTR)gszURL, sizeof(gszURL), (LPSTR)gszHost, sizeof(gszHost), &pProxy, &dwSize)) {
+ reportFuncErr("InternetGetProxyInfo");
+ retval = -1;
+ }else{
+ // printf("\n Proxy is: %s\n", proxy);
+ if(IsDirect(pProxy)) {
+ //printf(" It's a 'direct' kind of proxy.\n");
+
+ // set the 'out' parameters
+ if(lpIPAddress) {
+ strcpy(lpIPAddress, "");
+ }
+ if(pnPort) {
+ *pnPort = 0;
+ }
+ }
+
+ if(IsAProxy(pProxy)) {
+ char szProxyIP[MAX_PATH] = {0};
+
+ GetProxyIP(pProxy, szProxyIP);
+ //printf(" It's a 'regular' kind of proxy, with an IP of %s and a port of %d\n", szProxyIP, GetProxyPort(szProxy));
+
+ // set the 'out' parameters
+ if(lpIPAddress) {
+ strcpy(lpIPAddress, szProxyIP);
+ }
+ if(pnPort) {
+ *pnPort = GetProxyPort(pProxy);
+ }
+ }
+ }
+ }
+ }else{
+ //printf(" Error downloading %s (hr=0x%X)\n", lpWPADLocation, hr);
+ // there is no proxy, go direct
+ if(lpIPAddress) {
+ strcpy(lpIPAddress, "");
+ }
+ if(pnPort) {
+ *pnPort = 0;
+ }
+ retval = 0;
+ }
+
+ if(!pInternetDeInitializeAutoProxyDll(NULL, 0)) {
+ reportFuncErr("InternetDeInitializeAutoProxyDll");
+ }
+
+ return retval; // 0 = success
+}
+
+
+// Puts "10.0.0.1" into lpDest from a string like "PROXY 10.0.0.1:8088"
+// Returns 0 if success, -1 if an error
+int GetProxyIP(LPSTR lpProxy, LPSTR lpDest)
+{
+ LPSTR lpData;
+ LPSTR lpLastColon;
+ BOOL bDone;
+ char szProxy[MAX_PATH] = {0};
+ int ret = 0;
+
+ if(lpProxy && lpDest) {
+ lstrcpyn(szProxy, lpProxy, MAX_PATH);
+
+ // find the last ":" in the string
+ lpLastColon = NULL;
+ lpData = szProxy;
+ while(*lpData) {
+ if(*lpData == ':') {
+ lpLastColon = lpData;
+ }
+ lpData++;
+ }
+
+ if(lpLastColon) {
+ // truncate the string at the last colon
+ *lpLastColon = '\0';
+
+ bDone = FALSE;
+ while(lpData > szProxy && !bDone) {
+ if(*lpData == ' ') {
+ bDone = TRUE;
+ lpData++;
+ }else{
+ lpData--;
+ }
+ }
+ strcpy(lpDest, lpData);
+ ret = 0;
+ }else {
+ strcpy(lpDest, lpProxy);
+ ret =0;
+ }
+ }else{
+ ret = -1;
+ }
+
+ return ret;
+}
+
+// Returns 8088 from a string like "PROXY 10.0.0.1:8088"
+// Returns a port # if success, -1 if an error
+int GetProxyPort(LPSTR lpProxy)
+{
+ LPSTR lpData;
+ LPSTR lpLastColon = NULL;
+ char szProxy[MAX_PATH] = {0};
+ int ret = -1;
+
+ if(lpProxy) {
+ lstrcpyn(szProxy, lpProxy, MAX_PATH);
+
+ // find the last ":" in the string
+ lpData = szProxy;
+ while(*lpData) {
+ if(*lpData == ':') {
+ lpLastColon = lpData;
+ }
+ lpData++;
+ }
+
+ // from the last colon to the end of the string is the port number
+ if ( lpLastColon )
+ {
+ lpLastColon++;
+ ret = (unsigned short)atoi(lpLastColon);
+ }
+ else ret = 80;
+ }
+
+ return ret;
+}
+
+BOOL IsDirect(LPSTR proxy)
+{
+ BOOL bRet = FALSE;
+
+ if(proxy) {
+ if(strstri("DIRECT", proxy)) {
+ bRet = TRUE;
+ }
+ }
+
+ return bRet;
+}
+
+BOOL IsAProxy(LPSTR proxy)
+{
+ BOOL bRet = FALSE;
+
+ if(proxy) {
+ if(strstri("PROXY", proxy)) {
+ bRet = TRUE;
+ }
+ }
+
+ return bRet;
+}
+
+// like strstr() but case-insensitive
+char * strstri(LPSTR lpOne, LPSTR lpTwo)
+{
+ unsigned int b;
+ char szOne[MAX_PATH] = {0}, szTwo[MAX_PATH] = {0};
+
+ if(lpOne && lpTwo) {
+ strcpy(szOne, lpOne);
+ strcpy(szTwo, lpTwo);
+
+ for(b=0; b<strlen(szOne); b++) {
+ szOne[b] = tolower(szOne[b]);
+ }
+
+ for(b=0; b<strlen(szTwo); b++) {
+ szTwo[b] = tolower(szTwo[b]);
+ }
+ }
+
+ return strstr(szTwo, szOne);
+}
+
+BOOL GetAutomaticallyDetectSettingsCheckboxState()
+{
+ DWORD dwSize, dwType;
+ HKEY hKey;
+ BOOL bAutomaticallyDetectSettings = FALSE;
+ long lRet;
+
+ // see if the "Automatically Detect Settings" checkbox is on (I know, it's ugly)
+ // I noticed that the 9th byte in a binary struct at HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Connections\DefaultConnectionSettings"
+ // changes a bit to 1 or 0 based on the state of the checkbox. I'm using Windows XP. Not sure what byte to check on other Windows versions.
+ BYTE Buffer[200] = {0};
+
+ lRet = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Connections", 0, KEY_READ, &hKey);
+ if (lRet == ERROR_SUCCESS) {
+ dwSize = sizeof(Buffer);
+ lRet = RegQueryValueEx(hKey, "DefaultConnectionSettings", NULL, &dwType, (LPBYTE)&Buffer, &dwSize);
+ if(lRet == ERROR_SUCCESS && dwType == REG_BINARY) {
+ if(Buffer[8] & 8) {
+ bAutomaticallyDetectSettings = TRUE;
+ }
+ }
+ }
+ RegCloseKey(hKey);
+
+ return bAutomaticallyDetectSettings;
+}
+
+BOOL GetUseAProxyServerForYourLanCheckboxState()
+{
+ DWORD dwSize, dwValue, dwType;
+ HKEY hKey;
+ BOOL bUseAProxyServerForYourLan = FALSE;
+ long lRet;
+
+
+ // see if the "Use a proxy server for your LAN" checkbox is on
+ lRet = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_READ, &hKey);
+ if (lRet == ERROR_SUCCESS) {
+ dwSize = sizeof(DWORD);
+ lRet = RegQueryValueEx(hKey, "ProxyEnable", NULL, &dwType, (LPBYTE)&dwValue, &dwSize);
+ if(lRet == ERROR_SUCCESS && dwType == REG_DWORD) {
+ bUseAProxyServerForYourLan = dwValue;
+ }
+ }
+ RegCloseKey(hKey);
+
+ return bUseAProxyServerForYourLan;
+}
+
+BOOL GetAutomaticConfigurationScriptCheckboxState()
+{
+ DWORD dwType, dwSize;
+ HKEY hKey;
+ BOOL bUseAutomaticConfigurationScript = FALSE;
+ char szWPAD[MAX_PATH] = {0};
+ long lRet;
+
+
+#if 1
+ // use the registry read of HKCU\\software\microsoft\windows\current version\internet settings to see if "Use Automatic Configuration Script" is checked
+ szWPAD[0] = '\0';
+
+ lRet = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_READ, &hKey);
+ if (lRet == ERROR_SUCCESS) {
+ dwSize = sizeof(szWPAD);
+ lRet = RegQueryValueEx(hKey, "AutoConfigURL", NULL, &dwType, (LPBYTE)szWPAD, &dwSize);
+ if(lRet == ERROR_SUCCESS && dwType == REG_SZ) {
+
+ }
+ }
+ RegCloseKey(hKey);
+#else
+ // use DetectAutoProxyURL
+ if(!DetectAutoProxyUrl(szWPADLocation, sizeof(szWPADLocation), PROXY_AUTO_DETECT_TYPE_DHCP | PROXY_AUTO_DETECT_TYPE_DNS_A)) {
+ reportFuncErr("DetectAutoProxyUrl");
+ }
+#endif
+
+ if(strlen(szWPAD)) {
+ bUseAutomaticConfigurationScript = TRUE;
+ }
+
+ return bUseAutomaticConfigurationScript;
+}
+
+BOOL GetBypassProxyServerForLocalAddressesCheckboxState()
+{
+ DWORD dwSize, dwType;
+ HKEY hKey;
+ BOOL bBypassProxyServerForLocalAddresses = FALSE;
+ char szBuffer[MAX_PATH] = {0};
+ long lRet;
+
+ dwSize = sizeof(szBuffer);
+ lRet = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_READ, &hKey);
+ if (lRet == ERROR_SUCCESS) {
+ lRet = RegQueryValueEx(hKey, "ProxyOverride", NULL, &dwType, (LPBYTE)&szBuffer, &dwSize);
+ if(lRet == ERROR_SUCCESS && dwType == REG_SZ) {
+
+ }
+ }
+ RegCloseKey(hKey);
+
+ if(strcmp(szBuffer, "<local>") == 0) {
+ bBypassProxyServerForLocalAddresses = TRUE;
+ }
+
+ return bBypassProxyServerForLocalAddresses;
+}
+
+
+/* ==================================================================
+ HELPER FUNCTIONS
+ ================================================================== */
+// ResolveHostName (a helper function)
+DWORD __stdcall ResolveHostName(LPSTR lpszHostName, LPSTR lpszIPAddress, LPDWORD lpdwIPAddressSize)
+{
+ DWORD dwIPAddressSize;
+ addrinfo Hints;
+ LPADDRINFO lpAddrInfo;
+ LPADDRINFO IPv4Only;
+ DWORD error;
+
+ // Figure out first whether to resolve a name or an address literal.
+ // If getaddrinfo() with the AI_NUMERICHOST flag succeeds, then
+ // lpszHostName points to a string representation of an IPv4 or IPv6
+ // address. Otherwise, getaddrinfo() should return EAI_NONAME.
+ ZeroMemory(&Hints, sizeof(addrinfo));
+ Hints.ai_flags = AI_NUMERICHOST; // Only check for address literals.
+ Hints.ai_family = PF_UNSPEC; // Accept any protocol family.
+ Hints.ai_socktype = SOCK_STREAM; // Constrain results to stream socket.
+ Hints.ai_protocol = IPPROTO_TCP; // Constrain results to TCP.
+
+ error = getaddrinfo(lpszHostName, NULL, &Hints, &lpAddrInfo);
+ if(error != EAI_NONAME) {
+ if(error != 0) {
+ error = (error == EAI_MEMORY) ?
+ ERROR_NOT_ENOUGH_MEMORY : ERROR_INTERNET_NAME_NOT_RESOLVED;
+ goto quit;
+ }
+ freeaddrinfo(lpAddrInfo);
+
+ // An IP address (either v4 or v6) was passed in, so if there is
+ // room in the lpszIPAddress buffer, copy it back out and return.
+ dwIPAddressSize = lstrlen(lpszHostName);
+
+ if((*lpdwIPAddressSize < dwIPAddressSize) || (lpszIPAddress == NULL)) {
+ *lpdwIPAddressSize = dwIPAddressSize + 1;
+ error = ERROR_INSUFFICIENT_BUFFER;
+ goto quit;
+ }
+ lstrcpy(lpszIPAddress, lpszHostName);
+ goto quit;
+ }
+
+ // Call getaddrinfo() again, this time with no flag set.
+ Hints.ai_flags = 0;
+ error = getaddrinfo(lpszHostName, NULL, &Hints, &lpAddrInfo);
+ if(error != 0) {
+ error = (error == EAI_MEMORY) ?
+ ERROR_NOT_ENOUGH_MEMORY : ERROR_INTERNET_NAME_NOT_RESOLVED;
+ goto quit;
+ }
+
+ // Convert the IP address in addrinfo into a string.
+ // (the following code only handles IPv4 addresses)
+ IPv4Only = lpAddrInfo;
+ while(IPv4Only->ai_family != AF_INET) {
+ IPv4Only = IPv4Only->ai_next;
+ if(IPv4Only == NULL)
+ {
+ error = ERROR_INTERNET_NAME_NOT_RESOLVED;
+ goto quit;
+ }
+ }
+ error = getnameinfo(IPv4Only->ai_addr, (socklen_t)IPv4Only->ai_addrlen, lpszIPAddress, *lpdwIPAddressSize, NULL, 0, NI_NUMERICHOST);
+ if(error != 0)
+ error = ERROR_INTERNET_NAME_NOT_RESOLVED;
+
+quit:
+ return(error);
+}
+
+
+// IsResolvable (a helper function)
+BOOL __stdcall IsResolvable(LPSTR lpszHost)
+{
+ char szDummy[255] = {0};
+ DWORD dwDummySize = sizeof(szDummy) - 1;
+
+ if(ResolveHostName(lpszHost, szDummy, &dwDummySize))
+ return(FALSE);
+
+ return TRUE;
+}
+
+
+// GetIPAddress (a helper function)
+DWORD __stdcall GetIPAddress(LPSTR lpszIPAddress, LPDWORD lpdwIPAddressSize)
+{
+ char szHostBuffer[255] = {0};
+
+ if(gethostname(szHostBuffer, sizeof(szHostBuffer) - 1) != ERROR_SUCCESS)
+ return(ERROR_INTERNET_INTERNAL_ERROR);
+
+ return(ResolveHostName(szHostBuffer, lpszIPAddress, lpdwIPAddressSize));
+}
+
+
+// IsInNet (a helper function)
+BOOL __stdcall IsInNet(LPSTR lpszIPAddress, LPSTR lpszDest, LPSTR lpszMask)
+{
+ DWORD dwDest;
+ DWORD dwIpAddr;
+ DWORD dwMask;
+
+ dwIpAddr = inet_addr(lpszIPAddress);
+ dwDest = inet_addr(lpszDest);
+ dwMask = inet_addr(lpszMask);
+
+ if((dwDest == INADDR_NONE) || (dwIpAddr == INADDR_NONE) || ((dwIpAddr & dwMask) != dwDest))
+ return(FALSE);
+
+ return(TRUE);
+}
+
+
+// reportFuncErr (simple error reporting)
+void reportFuncErr(TCHAR* funcName)
+{
+ //printf(" ERROR: %s failed with error number %d.\n", funcName, GetLastError());
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+class CProfileFolder
+{
+public:
+ int GetProfileFolder(LPSTR lpProfileFolder, BOOL bFirefox);
+
+private:
+ int GetProfileFolder_9598ME(LPSTR lpProfileFolder, BOOL bFirefox);
+ int GetProfileFolder_2000XP(LPSTR lpProfileFolder, BOOL bFirefox);
+};
+
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+class CMozSettings
+{
+public:
+ CMozSettings(BOOL bFirefox);
+ virtual ~CMozSettings();
+ int GetPreference(LPSTR lpPreferenceWanted, int *pnDest);
+ int GetPreference(LPSTR lpPreferenceWanted, LPSTR lpDest, int sizeof_dest);
+
+private:
+ CProfileFolder m_pf;
+ HGLOBAL m_hData;
+ LPSTR m_lpData;
+ int m_sizeof_data;
+};
+
+int CProfileFolder::GetProfileFolder(LPSTR lpProfileFolder, BOOL bFirefox)
+{
+ // See http://www.mozilla.org/support/firefox/edit for where I got this info:
+ // On Windows XP/2000, the path is usually %AppData%\Mozilla\Firefox\Profiles\xxxxxxxx.default\, where xxxxxxxx is a random string of characters. Just browse to C:\Documents and Settings\[User Name]\Application Data\Mozilla\Firefox\Profiles\ and the rest should be obvious.
+ // On Windows 95/98/Me, the path is usually C:\WINDOWS\Application Data\Mozilla\Firefox\Profiles\xxxxxxxx.default\
+ // On Linux, the path is usually ~/.mozilla/firefox/xxxxxxxx.default/
+ // On Mac OS X, the path is usually ~/Library/Application Support/Firefox/Profiles/xxxxxxxx.default/
+ OSVERSIONINFO version;
+
+ ZeroMemory(&version, sizeof(version));
+ version.dwOSVersionInfoSize = sizeof(version);
+ GetVersionEx(&version);
+ if(version.dwMajorVersion == 3) {
+ return -1; // NT 3.51 not supported
+ }
+ if(version.dwMajorVersion == 4) {
+ return GetProfileFolder_9598ME(lpProfileFolder, bFirefox);
+ }
+ if(version.dwMajorVersion >= 5) {
+ return GetProfileFolder_2000XP(lpProfileFolder, bFirefox);
+ }
+
+ return -1;
+}
+
+// private function for GetProfileFolder()
+// on my test system, the folder to get is c:\windows\application data\mozilla\profiles\default\y3h9azmi.slt
+int CProfileFolder::GetProfileFolder_9598ME(LPSTR lpProfileFolder, BOOL bFirefox)
+{
+ WIN32_FIND_DATA fd;
+ HANDLE hFind;
+ BOOL bDone, bFound;
+ char szHomePath[MAX_PATH] = {0};
+ char szTemp[MAX_PATH] = {0};
+
+
+ if(lpProfileFolder) {
+ GetEnvironmentVariable("WINDIR", szHomePath, sizeof(szHomePath));
+ strcpy(lpProfileFolder, szHomePath);
+ if(bFirefox) {
+ strcat(lpProfileFolder, "\\Application Data\\Mozilla\\Firefox\\Profiles\\");
+ }else{
+ strcat(lpProfileFolder, "\\Application Data\\Mozilla\\Profiles\\default\\");
+ }
+
+ // find the first folder in the the path specified in szProfileFolder
+ lstrcpyn(szTemp, lpProfileFolder, MAX_PATH-4);
+ strcat(szTemp, "*.*");
+
+ bDone = FALSE;
+ bFound = FALSE;
+ hFind = FindFirstFile(szTemp, &fd);
+ while(hFind != INVALID_HANDLE_VALUE && !bDone) {
+ if(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ // we're at a directory.
+ // make sure it's not "." or ".."
+ if(fd.cFileName[0] != '.') {
+ bFound = TRUE;
+ }
+ }
+
+ bDone = !FindNextFile(hFind, &fd);
+ }
+ FindClose(hFind);
+
+ if(bFound) {
+ strcat(lpProfileFolder, fd.cFileName);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+// private function for GetProfileFolder()
+int CProfileFolder::GetProfileFolder_2000XP(LPSTR lpProfileFolder, BOOL bFirefox)
+{
+ WIN32_FIND_DATA fd;
+ HANDLE hFind;
+ BOOL bDone, bFound;
+ char szHomePath[MAX_PATH] = {0};
+ char szTemp[MAX_PATH] = {0};
+
+
+ if(lpProfileFolder) {
+ GetEnvironmentVariable("APPDATA", szHomePath, sizeof(szHomePath));
+ strcpy(lpProfileFolder, szHomePath);
+ if(bFirefox) {
+ strcat(lpProfileFolder, "\\Mozilla\\Firefox\\Profiles\\");
+ }else{
+ strcat(lpProfileFolder, "\\Mozilla\\Profiles\\default\\");
+ }
+
+ // find the first folder in the the path specified in szProfileFolder
+ strcpy(szTemp, lpProfileFolder);
+ strcat(szTemp, "*.*");
+
+ bDone = FALSE;
+ bFound = FALSE;
+ hFind = FindFirstFile(szTemp, &fd);
+ while(hFind != INVALID_HANDLE_VALUE && !bDone) {
+ if(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ // we're at a directory.
+ // make sure it's not "." or ".."
+ if(fd.cFileName[0] != '.') {
+ bFound = TRUE;
+ }
+ }
+
+ bDone = !FindNextFile(hFind, &fd);
+ }
+ FindClose(hFind);
+
+ if(bFound) {
+ strcat(lpProfileFolder, fd.cFileName);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+CMozSettings::CMozSettings(BOOL bFirefox)
+{
+ WIN32_FIND_DATA fd;
+ OFSTRUCT of;
+ HANDLE hFind;
+ HFILE hPrefsFile;
+ char szProfileFolder[MAX_PATH] = {0};
+ char szPrefsFile[MAX_PATH] = {0};
+ int ret;
+
+
+ m_hData = NULL;
+ m_lpData = NULL;
+
+ ret = m_pf.GetProfileFolder(szProfileFolder, bFirefox);
+
+
+ if(ret == 0) {
+ // We found the folder where prefs.js lives. Read it in.
+ strcpy(szPrefsFile, szProfileFolder);
+ strcat(szPrefsFile, "\\prefs.js");
+
+ // get the size of the file and alloc memory for it
+ hFind = FindFirstFile(szPrefsFile, &fd);
+ if(hFind != INVALID_HANDLE_VALUE) {
+ m_hData = GlobalAlloc(GHND, fd.nFileSizeLow + 256);
+ if(m_hData) {
+ m_lpData = (LPSTR)GlobalLock(m_hData);
+ if(m_lpData) {
+ hPrefsFile = OpenFile(szPrefsFile, &of, OF_READ);
+ if(hPrefsFile) {
+ m_sizeof_data = fd.nFileSizeLow;
+ _lread(hPrefsFile, m_lpData, m_sizeof_data);
+ _lclose(hPrefsFile);
+ hPrefsFile = NULL;
+ }
+ }
+ }
+
+ FindClose(hFind);
+ }
+ }
+}
+
+CMozSettings::~CMozSettings()
+{
+ if(m_lpData) {
+ GlobalUnlock(m_hData);
+ m_lpData = NULL;
+ }
+
+ if(m_hData) {
+ GlobalFree(m_hData);
+ m_hData = NULL;
+ }
+}
+
+int CMozSettings::GetPreference(LPSTR lpPreferenceWanted, LPSTR lpDest, int sizeof_dest)
+{
+ LPSTR lpPointer, lpPointerEnd, lpData;
+ LPSTR lpLineStart, lpSearchStart, lpFoundString, lpResult;
+ BOOL bDone;
+ int nDoubleQuoteCount, retval;
+
+
+ retval = -1;
+ if(m_lpData) {
+ if(lpPreferenceWanted) {
+ if(lpDest) {
+ *lpDest = '\0';
+ bDone = FALSE;
+ lpPointer = m_lpData;
+ lpPointerEnd = lpPointer + m_sizeof_data;
+
+ while(lpPointer < lpPointerEnd && !bDone) {
+ if(strncmp(lpPointer, "user_pref(", 10) == 0) {
+ lpLineStart = lpPointer;
+ lpSearchStart = lpLineStart + 11;
+ if(strncmp(lpSearchStart, lpPreferenceWanted, strlen(lpPreferenceWanted)) == 0) {
+ lpFoundString = lpSearchStart + strlen(lpPreferenceWanted);
+
+ // lpFoundString almost points to what we want. Skip over the " character it's at now, skip over the " character
+ // starting the value we want and null-terminate what we want when we find the 3rd " character
+ lpData = lpFoundString;
+ nDoubleQuoteCount = 0;
+ while(nDoubleQuoteCount <= 3 && !bDone && lpData < lpPointerEnd) {
+ if(*lpData == '"') {
+ nDoubleQuoteCount++;
+ if(nDoubleQuoteCount == 2) {
+ // we're at the starting quote
+ lpResult = lpData;
+ lpResult++;
+ }
+ if(nDoubleQuoteCount == 3) {
+ // we're at the ending quote
+ // null-terminate what we want, and copy it to the dest buffer
+ *lpData = '\0';
+ lstrcpyn(lpDest, lpResult, sizeof_dest);
+
+ bDone = TRUE;
+ retval = 0;
+ }
+ }
+ lpData++;
+ }
+ }
+ }
+
+ lpPointer++;
+ }
+ }
+ }
+ }
+
+ return retval;
+}
+
+int CMozSettings::GetPreference(LPSTR lpPreferenceWanted, int *pnDest)
+{
+ LPSTR lpPointer, lpPointerEnd, lpData;
+ LPSTR lpLineStart, lpSearchStart, lpFoundString;
+ BOOL bDone;
+ int retval;
+
+
+ retval = -1;
+ if(m_lpData) {
+ if(lpPreferenceWanted) {
+ if(pnDest) {
+ bDone = FALSE;
+ lpPointer = m_lpData;
+ lpPointerEnd = lpPointer + m_sizeof_data;
+
+ while(lpPointer < lpPointerEnd && !bDone) {
+ if(strncmp(lpPointer, "user_pref(", 10) == 0) {
+ lpLineStart = lpPointer;
+ lpSearchStart = lpLineStart + 11;
+ if(strncmp(lpSearchStart, lpPreferenceWanted, strlen(lpPreferenceWanted)) == 0) {
+ lpFoundString = lpSearchStart + strlen(lpPreferenceWanted);
+
+ // lpFoundString almost points to what we want. Skip over the " character it's at now, skip over the ","
+ // starting the value we want and null-terminate what we want when we find the 3rd " character
+ lpData = lpFoundString;
+ while(*lpData != ',' && lpData < lpPointerEnd) {
+ lpData++;
+ }
+ if(*lpData == ',') {
+ lpData++;
+
+ lpFoundString = lpData;
+ while(*lpData != ')' && lpData < lpPointerEnd) {
+ lpData++;
+ }
+ if(*lpData == ')') {
+ // null-terminate what we want, and copy it to the dest buffer
+ *lpData = '\0';
+ *pnDest = atoi(lpFoundString);
+ bDone = TRUE;
+
+ retval = 0;
+ }
+ }
+ }
+ }
+
+ lpPointer++;
+ }
+ }
+ }
+ }
+
+ return retval;
+}
+
+////////////////////////////////////////////////////////////////////////
+BOOL IsFirefoxProxySet()
+{
+ return IsFirefoxOrMozillaProxySet(TRUE);
+}
+
+BOOL IsMozillaProxySet()
+{
+ return IsFirefoxOrMozillaProxySet(FALSE);
+}
+
+BOOL IsFirefoxOrMozillaProxySet(BOOL bFirefox)
+{
+ CMozSettings settings(bFirefox);
+ int ret, nValue;
+
+ ret = settings.GetPreference("network.proxy.type", &nValue);
+ if(ret == 0) {
+ switch(nValue) {
+ case 0: // shouldn't get here
+ break;
+ case 1: // manual configuration
+ return TRUE;
+ case 2: // automatic configuration
+ return TRUE;
+ case 4: // auto-detect
+ return TRUE;
+ default: // don't know
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+int ResolveURL_Mozilla(LPSTR lpURL, LPSTR lpHostname, LPSTR lpIPAddress, int sizeof_address, int *pnPort)
+{
+ return ResolveURL_MozillaOrFirefox(FALSE, lpURL, lpHostname, lpIPAddress, sizeof_address, pnPort);
+}
+
+int ResolveURL_Firefox(LPSTR lpURL, LPSTR lpHostname, LPSTR lpIPAddress, int sizeof_address, int *pnPort)
+{
+ return ResolveURL_MozillaOrFirefox(TRUE, lpURL, lpHostname, lpIPAddress, sizeof_address, pnPort);
+}
+
+int ResolveURL_MozillaOrFirefox(BOOL bFirefox, LPSTR lpURL, LPSTR lpHostname, LPSTR lpIPAddress, int sizeof_address, int *pnPort)
+{
+ CMozSettings setting(bFirefox);
+ int ret, nValue;
+
+
+ // search for the "network.proxy.http" preference
+ ret = setting.GetPreference("network.proxy.type", &nValue);
+ if(ret == 0) {
+ switch(nValue) {
+ case 0:
+ // shouldn't get here
+ break;
+
+ case 1:
+ // manual configuration
+ setting.GetPreference("network.proxy.http", lpIPAddress, sizeof_address);
+ setting.GetPreference("network.proxy.http_port", pnPort);
+ break;
+
+ case 2:
+ // automatic configuration
+ {
+ char szWPADLocation[MAX_PATH] = {0};
+
+ setting.GetPreference("network.proxy.autoconfig_url", szWPADLocation, sizeof(szWPADLocation));
+ ret = ReadWPADFile(szWPADLocation, lpIPAddress, pnPort);
+ }
+ break;
+
+ case 4:
+ // Auto-detect proxy settings for this network
+ ret = ReadWPADFile("http://wpad/wpad.dat", lpIPAddress, pnPort);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+// My function that downloads from a URL to a file using the Nullsoft JNetLib library instead of using
+// URLDownloadToFile(). Only parameters 2 and 3 are used, to mimick the parameters of URLDownloadToFile().
+HRESULT JNetLibDownloadToFile(LPVOID lpUnused1, LPSTR lpWPADLocation, LPSTR lpTempFile, LPVOID lpUnused2, LPVOID lpUnused3)
+{
+ api_httpreceiver *http=0;
+ waServiceFactory *sf=0;
+
+ OFSTRUCT of;
+ HGLOBAL hData;
+ HRESULT hRet = S_FALSE; // default return value
+ LPSTR lpData;
+ DWORD dwSize;
+ HFILE hFile;
+ BOOL bDone;
+ //JNL jNetLib;
+ int ret;
+
+
+ if(lpWPADLocation && lpTempFile)
+ {
+ if (WASABI_API_SVC)
+ {
+ sf = WASABI_API_SVC->service_getServiceByGuid(httpreceiverGUID);
+ if (sf) http = (api_httpreceiver *)sf->getInterface();
+ }
+ if (!http)
+ return S_FALSE;
+
+ // init the library and open a connection to the URL
+ http->Open();
+ http->Connect(lpWPADLocation);
+
+ // loop until JNetLib gets the data.
+ // run() returns 0 if OK, -1 if error (call geterrorstr()), or 1 if connection closed.
+ bDone = FALSE;
+ while(!bDone) {
+ ret = http->Run();
+ if(ret == -1 || ret == 1) {
+ bDone = TRUE;
+ }
+ Sleep(50);
+ }
+
+
+ dwSize = (DWORD)http->GetContentLength();
+ if(dwSize && ret == 1) {
+ // Got something!
+ // Allocate memory for it and write it to lpTempFile
+ hData = GlobalAlloc(GHND, dwSize + 100);
+ if(hData) {
+ lpData = (LPSTR)GlobalLock(hData);
+ if(lpData) {
+ http->GetBytes(lpData, (int)dwSize);
+
+ // create the output file and write to it
+ hFile = OpenFile(lpTempFile, &of, OF_CREATE);
+ if(hFile != HFILE_ERROR) {
+ _lwrite(hFile, lpData, (UINT)dwSize);
+ _lclose(hFile);
+
+ hRet = S_OK; // success
+ }
+
+ GlobalUnlock(hData);
+ lpData = NULL;
+ }
+
+ GlobalFree(hData);
+ hData = NULL;
+ }
+ }
+ }
+if (http && sf)
+sf->releaseInterface(http);
+ return hRet;
+}
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_nsv/Main.cpp b/Src/Plugins/Input/in_nsv/Main.cpp
new file mode 100644
index 00000000..159ee873
--- /dev/null
+++ b/Src/Plugins/Input/in_nsv/Main.cpp
@@ -0,0 +1,976 @@
+#define PLUGIN_NAME "Nullsoft NSV Decoder"
+#define PLUGIN_VERSION L"1.76"
+
+#include <windows.h>
+#include "../Winamp/in2.h"
+#include "../nsv/nsvplay/main.h"
+#include "resource.h"
+#include "../nu/AutoWide.h"
+#include "../nu/AutoCharFn.h"
+#define NO_IVIDEO_DECLARE
+#include "../winamp/wa_ipc.h"
+#include "../Winamp/strutil.h"
+#include "api.h"
+extern In_Module mod; // the output module (filled in near the bottom of this file)
+
+#define g_hInstance mod.hDllInstance
+#define WNDMENU_CAPTION L"Winamp in_nsv"
+#define MODAL_ABOUT
+#define LOC_MODAL_ABOUT
+#include "../nsv/nsvplay/about.h"
+#undef g_hInstance
+
+#include <shlwapi.h>
+#include <strsafe.h>
+extern int config_precseek;
+extern int config_vidoffs;
+extern int config_bufms;
+extern int config_prebufms;
+extern int config_underunbuf;
+extern int config_bufms_f;
+extern int config_prebufms_f;
+extern int config_underunbuf_f;
+
+// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
+static const GUID playbackConfigGroupGUID =
+ {
+ 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf }
+ };
+
+
+char lastfn[1024] = {0}; // currently playing file (used for getting info on the current file)
+static char statusbuf[1024];
+
+char stream_url[1024] = {0};
+
+ULONGLONG g_bufferstat;
+int m_last_bitrate;
+void config_read();
+void config_write();
+void config(HWND hwndParent);
+
+int file_length = 0; // file length, in bytes
+// Used for correcting DSP plug-in pitch changes
+int paused = 0; // are we paused?
+volatile int seek_needed; // if != -1, it is the point that the decode
+// thread should seek to, in ms.
+
+CRITICAL_SECTION g_decoder_cs;
+char g_streaminfobuf[512] = {0};
+int g_streaminfobuf_used = 0;
+
+char error_string[128] = {0};
+
+volatile int killDecodeThread = 0; // the kill switch for the decode thread
+HANDLE thread_handle = INVALID_HANDLE_VALUE; // the handle to the decode thread
+
+int has_opened_outmod = 0;
+int m_srate = 0; // seek needs this
+
+int decoders_initted = 0;
+
+api_config *AGAVE_API_CONFIG = 0;
+api_memmgr *WASABI_API_MEMMGR = 0;
+// wasabi based services for localisation support
+api_language *WASABI_API_LNG = 0;
+HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
+
+void process_url(char *url)
+{
+ lstrcpynA(stream_url, url, sizeof(stream_url));
+
+ // if (!strncmp(stream_url,"hTtP",4))
+ // {
+ // DWORD dw;
+ // SendMessageTimeout(mod.hMainWindow,WM_USER,(WPARAM)0,241,SMTO_NORMAL,500,&dw);
+ // } // I (Tag) removed this support, its annoying.
+ DWORD_PTR dw = 0;
+ if (stream_url[0]) SendMessageTimeout(mod.hMainWindow, WM_USER, (WPARAM)stream_url, 241, SMTO_NORMAL, 500, &dw);
+}
+
+char last_title_sent[256] = {0};
+
+void process_metadata(char *data, int len)
+{
+ if (len && *data)
+ {
+ char *ld;
+ int x;
+ if (len > 4096) return ;
+ for (x = 0; x < len; x ++)
+ if (!data[x]) break;
+ if (x == len) return ;
+ while ((ld = strstr(data, "='")))
+ {
+ char * n = data;
+ ld[0] = 0;
+ ld += 2;
+ data = strstr(ld, "';");
+ if (data)
+ {
+ data[0] = 0;
+ data += 2;
+ if (!lstrcmpiA(n, "StreamTitle"))
+ {
+ lstrcpynA(last_title_sent, ld, sizeof(last_title_sent));
+ last_title_sent[sizeof(last_title_sent) - 1] = 0;
+ PostMessage(mod.hMainWindow, WM_USER, 0, 243);
+ }
+ else if (!lstrcmpiA(n, "StreamUrl"))
+ {
+ process_url(ld);
+ }
+ }
+ else break;
+ }
+ }
+}
+
+class WA2AudioOutput : public IAudioOutput
+{
+public:
+ WA2AudioOutput(int srate, int nch, int bps)
+ {
+ memset(m_stuffbuf, 0, sizeof(m_stuffbuf));
+ decode_pos_samples = 0;
+ m_srate = srate; m_bps = bps; m_nch = nch;
+ m_open_success = 0;
+ m_stuffbuf_u = 0;
+ int maxlat = mod.outMod->Open(srate, nch, bps, -1, -1);
+ if (maxlat == 0 && strstr(lastfn, "://"))
+ {
+ maxlat = -1;
+ mod.outMod->Close(); // boom
+ }
+ if (maxlat >= 0)
+ {
+ mod.SetInfo( -1, srate / 1000, nch, 1);
+ mod.SAVSAInit(maxlat, srate);
+ mod.VSASetInfo(srate, nch);
+ mod.outMod->SetVolume( -666);
+ m_open_success = 1;
+ has_opened_outmod = 1;
+ }
+ }
+ ~WA2AudioOutput(){}
+
+ int canwrite()
+ {
+ int a = mod.outMod->CanWrite();
+ if (mod.dsp_isactive() == 1) a /= 2;
+ return a & ~((m_nch * (m_bps / 8)) - 1);
+ } // returns bytes writeable
+
+ void write(void *buf, int len)
+ {
+ char *b = (char *)buf;
+ int s = 576 * m_nch * (m_bps / 8);
+ if (s > sizeof(m_stuffbuf)) s = sizeof(m_stuffbuf);
+
+ while (len > 0)
+ {
+ int l = s;
+ if (!m_stuffbuf_u && len >= s) // straight copy of data
+ {
+ int dms = (int) ((decode_pos_samples * (__int64)1000) / (__int64)m_srate);
+ mod.SAAddPCMData(b, m_nch, m_bps, dms);
+ mod.VSAAddPCMData(b, m_nch, m_bps, dms);
+ }
+ else if (m_stuffbuf_u + len >= s)
+ {
+ int dms = (int) (((decode_pos_samples - (m_stuffbuf_u / m_nch / (m_bps / 8))) * (__int64)1000) / (__int64)m_srate);
+ l = (s - m_stuffbuf_u);
+ memcpy(m_stuffbuf + m_stuffbuf_u, b, l);
+ m_stuffbuf_u = 0;
+
+ mod.SAAddPCMData(m_stuffbuf, m_nch, m_bps, dms);
+ mod.VSAAddPCMData(m_stuffbuf, m_nch, m_bps, dms);
+ }
+ else // put all of len into m_stuffbuf
+ {
+ memcpy(m_stuffbuf + m_stuffbuf_u, b, len);
+ m_stuffbuf_u += len;
+ l = len;
+ }
+
+ if (l > len)l = len; // this shouldn't happen but we'll leave it here just in case
+
+ decode_pos_samples += (l / m_nch / (m_bps / 8));
+
+ if (mod.dsp_isactive())
+ {
+ static char sample_buffer[576*2*(16 / 8)*2];
+ int spll = l / m_nch / (m_bps / 8);
+ memcpy(sample_buffer, b, l);
+ int l2 = l;
+ if (spll > 0) l2 = mod.dsp_dosamples((short *)sample_buffer, spll, m_bps, m_nch, m_srate) * (m_nch * (m_bps / 8));
+ mod.outMod->Write(sample_buffer, l2);
+ }
+ else mod.outMod->Write(b, l);
+ len -= l;
+ b += l;
+ }
+ }
+ ULONGLONG getwritepos()
+ {
+ return (unsigned int) ((decode_pos_samples * 1000) / m_srate);
+ }
+ ULONGLONG getpos()
+ {
+ if (seek_needed != -1) return seek_needed;
+ return (unsigned int) ((decode_pos_samples * 1000) / m_srate) +
+ (mod.outMod->GetOutputTime() - mod.outMod->GetWrittenTime()) - config_vidoffs;
+ }
+ void flush(unsigned int newtime)
+ {
+ m_stuffbuf_u = 0;
+ mod.outMod->Flush(newtime);
+ decode_pos_samples = (((__int64)newtime) * m_srate) / 1000;
+ }
+ void pause(int pause)
+ {
+ mod.outMod->Pause(pause);
+ }
+ int get_open_success() { return m_open_success; }
+ int isplaying(void) { return mod.outMod->IsPlaying(); }
+private:
+ __int64 decode_pos_samples; // current decoding position, in milliseconds.
+ int m_nch, m_bps;
+ int m_open_success;
+ int m_stuffbuf_u;
+ char m_stuffbuf[576*2*2];
+};
+
+IAudioOutput *PCMOUT_CREATE(unsigned int outfmt[8])
+{
+ if (outfmt[1] && outfmt[2] && outfmt[3] && outfmt[0] == NSV_MAKETYPE('P', 'C', 'M', ' '))
+ {
+ WA2AudioOutput *r = new WA2AudioOutput(outfmt[1], outfmt[2], outfmt[3]);
+ if (r->get_open_success()) return r;
+ delete r;
+ }
+ return NULL;
+}
+
+DWORD WINAPI DecodeThread(LPVOID b); // the decode thread procedure
+
+void about(HWND hwndParent)
+{
+ do_about(hwndParent,WASABI_API_LNG_HINST);
+}
+
+void SetFileExtensions(void)
+{
+ static char fileExtensionsString[1200] = {0}; // "NSV;NSA\0Nullsoft Audio/Video File (*.NSV;*.NSA)\0"
+ char* end = 0;
+ StringCchCopyExA(fileExtensionsString, 1200, "NSV;NSA", &end, 0, 0);
+ StringCchCopyExA(end+1, 1200, WASABI_API_LNGSTRING(IDS_NSA_NSV_FILE), 0, 0, 0);
+ mod.FileExtensions = fileExtensionsString;
+}
+
+int init()
+{
+ if (!IsWindow(mod.hMainWindow))
+ return IN_INIT_FAILURE;
+
+ waServiceFactory *sf = mod.service->service_getServiceByGuid(AgaveConfigGUID);
+ if (sf) AGAVE_API_CONFIG = (api_config *)sf->getInterface();
+
+ sf = mod.service->service_getServiceByGuid(memMgrApiServiceGuid);
+ if (sf) WASABI_API_MEMMGR = reinterpret_cast<api_memmgr*>(sf->getInterface());
+
+ // loader so that we can get the localisation service api for use
+ sf = mod.service->service_getServiceByGuid(languageApiGUID);
+ if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
+
+ // need to have this initialised before we try to do anything with localisation features
+ WASABI_API_START_LANG(mod.hDllInstance,InNSVLangGUID);
+
+ static wchar_t szDescription[256];
+ StringCchPrintfW(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_NSV_DECODER),PLUGIN_VERSION);
+ mod.description = (char*)szDescription;
+
+ SetFileExtensions();
+
+ config_read();
+ InitializeCriticalSection(&g_decoder_cs);
+ return IN_INIT_SUCCESS;
+}
+
+void quit()
+{
+ Decoders_Quit();
+ DeleteCriticalSection(&g_decoder_cs);
+ waServiceFactory *sf = mod.service->service_getServiceByGuid(AgaveConfigGUID);
+ if (sf) sf->releaseInterface(AGAVE_API_CONFIG);
+
+ sf = mod.service->service_getServiceByGuid(memMgrApiServiceGuid);
+ if (sf) sf->releaseInterface(WASABI_API_MEMMGR);
+}
+
+int isourfile(const char *fn)
+{
+ // used for detecting URL streams.. unused here.
+ // return !strncmp(fn,"http://",7); to detect HTTP streams, etc
+ return !_strnicmp( fn, "unsv://", 7 );
+}
+
+NSVDecoder *m_decoder = 0;
+IVideoOutput *m_video_output = 0;
+
+int g_play_needseek = -1;
+
+int play(const char *fn)
+{
+ m_last_bitrate = -1;
+ g_play_needseek = -1;
+ last_title_sent[0] = 0;
+ g_bufferstat = 0;
+ mod.is_seekable = 0;
+ has_opened_outmod = 0;
+ error_string[0] = 0;
+ if (!decoders_initted)
+ {
+ decoders_initted = 1;
+ char buf[MAX_PATH] = {0}, *p = buf;
+ GetModuleFileNameA(mod.hDllInstance, buf, sizeof(buf));
+ while (p && *p) p++;
+ while (p && p > buf && *p != '\\') p--;
+ if (p) *p = 0;
+ Decoders_Init(buf);
+ }
+
+ unsigned long thread_id = 0;
+
+ paused = 0;
+ seek_needed = -1;
+ EnterCriticalSection(&g_decoder_cs);
+ if (strstr(fn, "://"))
+ WASABI_API_LNGSTRING_BUF(IDS_CONNECTING,error_string,128);
+ else
+ WASABI_API_LNGSTRING_BUF(IDS_OPENING,error_string,128);
+
+ LeaveCriticalSection(&g_decoder_cs);
+
+ m_video_output = (IVideoOutput *)SendMessage(mod.hMainWindow, WM_USER, 0, IPC_GET_IVIDEOOUTPUT);
+ if (!m_video_output) return 1;
+
+ m_video_output->open(0, 0, 0, 0, 0);
+
+ m_decoder = new NSVDecoder(fn, m_video_output, NULL);
+ lstrcpynA(lastfn, fn, sizeof(lastfn));
+
+ if (strstr(fn, "://") || !strncmp(fn, "\\\\", 2))
+ {
+ m_decoder->SetPreciseSeeking(config_precseek&2);
+ m_decoder->SetBuffering(config_bufms, config_prebufms, config_underunbuf);
+ }
+ else
+ {
+ m_decoder->SetPreciseSeeking(config_precseek&1);
+ m_decoder->SetBuffering(config_bufms_f, config_prebufms_f, config_underunbuf_f);
+ }
+
+ // launch decode thread
+ killDecodeThread = 0;
+ thread_handle = (HANDLE)CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) DecodeThread, NULL, 0, &thread_id);
+ SetThreadPriority(thread_handle, (int)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
+
+ return 0;
+}
+
+// standard pause implementation
+void pause()
+{
+ paused = 1;
+ // if (has_opened_outmod) mod.outMod->Pause(1);
+ // else
+ if (m_decoder) m_decoder->pause(1);
+}
+void unpause()
+{
+ paused = 0;
+ // if (has_opened_outmod) mod.outMod->Pause(0);
+ //else
+ if (m_decoder) m_decoder->pause(0);
+}
+int ispaused() { return paused; }
+
+// stop playing.
+void stop()
+{
+ g_play_needseek = -1;
+
+ if (thread_handle != INVALID_HANDLE_VALUE)
+ {
+ killDecodeThread = 1;
+ int nTimes = 0;
+ const int maxTimes = 1000;
+ while (WaitForSingleObject(thread_handle, 0) == WAIT_TIMEOUT)
+ {
+ MSG msg = {0};
+ if (PeekMessage(&msg, NULL, 0, 0, 1))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ else
+ Sleep(10);
+
+ nTimes++;
+ if (nTimes == maxTimes)
+ {
+#ifdef WINAMPX
+ SendStatus( WINAMPX_STATUS_ERROR_KILLING_THREAD, 0 );
+#else
+ /*MessageBox(mod.hMainWindow, "error asking thread to die!\n",
+ "error killing decode thread", 0);*/
+#endif
+ TerminateThread(thread_handle, 0);
+ break;
+ }
+ }
+ CloseHandle(thread_handle);
+ thread_handle = INVALID_HANDLE_VALUE;
+ }
+ if (has_opened_outmod) mod.outMod->Close();
+ g_bufferstat = 0;
+ has_opened_outmod = 0;
+ mod.SAVSADeInit();
+
+ EnterCriticalSection(&g_decoder_cs);
+ delete(m_decoder);
+ m_decoder = NULL;
+LeaveCriticalSection(&g_decoder_cs);
+ g_streaminfobuf[0] = 0;
+}
+
+int getlength()
+{
+ if (m_decoder)
+ {
+ int x = m_decoder->getlen();
+ if (x != -1) return x;
+ }
+ return -1000;
+}
+
+int getoutputtime()
+{
+ if (g_bufferstat) return (int)g_bufferstat;
+ EnterCriticalSection(&g_decoder_cs);
+ if (m_decoder)
+ {
+ LeaveCriticalSection(&g_decoder_cs);
+ return (int)(m_decoder ? m_decoder->getpos() + config_vidoffs : 0);
+ }
+ LeaveCriticalSection(&g_decoder_cs);
+ return 0;
+}
+
+void setoutputtime(int time_in_ms)
+{
+ seek_needed = time_in_ms;
+}
+
+void setvolume(int volume) { mod.outMod->SetVolume(volume); }
+
+void setpan(int pan) { mod.outMod->SetPan(pan); }
+
+int infoDlg(const char *fn, HWND hwnd);
+
+// this is an odd function. it is used to get the title and/or
+// length of a track.
+// if filename is either NULL or of length 0, it means you should
+// return the info of lastfn. Otherwise, return the information
+// for the file in filename.
+// if title is NULL, no title is copied into it.
+// if length_in_ms is NULL, no length is copied into it.
+void getfileinfo(const char *filename, char *title, int *length_in_ms)
+{
+ if (!filename || !*filename) // currently playing file
+ {
+ EnterCriticalSection(&g_decoder_cs);
+ if (length_in_ms) *length_in_ms = getlength();
+ if (title) // get non-path portion.of filename
+ {
+ char *p = NULL;
+ if (m_decoder)
+ {
+ p = m_decoder->getTitle();
+ }
+ if (!p)
+ {
+ p = lastfn + strlen(lastfn);
+ while (p && *p != '\\' && p >= lastfn) p--;
+ p++;
+ }
+ while (p && *p == ';') p++;
+ title[0] = 0;
+
+ if (error_string[0])
+ {
+ StringCchPrintfA(title, GETFILEINFO_TITLE_LENGTH, "[%s] ", error_string);
+ }
+ if (!error_string[0] && last_title_sent[0])
+ {
+ StringCchPrintfA(title, GETFILEINFO_TITLE_LENGTH-strlen(title), "%s (%s)", last_title_sent, p);
+ }
+ else
+ lstrcpynA(title + strlen(title), p, FILETITLE_SIZE);
+ }
+ LeaveCriticalSection(&g_decoder_cs);
+ }
+ else if (1) // some other file
+ {
+ if ((length_in_ms || title) && !strstr(filename, "://"))
+ {
+ nsv_InBS bs;
+ nsv_fileHeader hdr = {0, };
+ if (length_in_ms) // calculate length
+ {
+ *length_in_ms = -1000; // the default is unknown file length (-1000).
+ }
+ if (title) // get non path portion of filename
+ {
+ const char *p = filename + strlen(filename);
+ while (p && *p != '\\' && p >= filename) p--;
+ lstrcpynA(title, ++p, GETFILEINFO_TITLE_LENGTH);
+ }
+
+ IDataReader *rdr = CreateReader(filename);
+
+ if (rdr)
+ {
+ while (!rdr->iseof())
+ {
+ char buf[1024] = {0};
+ int l = (int)rdr->read(buf, sizeof(buf));
+ if (!l) break;
+ bs.add(buf, l);
+ l = nsv_readheader(bs, &hdr);
+ if (l <= 0)
+ {
+ if (!l)
+ {
+ if (length_in_ms) *length_in_ms = hdr.file_lenms;
+ if (title && hdr.metadata)
+ {
+ char *t = nsv_getmetadata(hdr.metadata, "TITLE");
+ if (t) lstrcpynA(title, t, 1024);
+ }
+ }
+ free(hdr.metadata);
+ free(hdr.toc);
+ break;
+ }
+ }
+ delete rdr;
+ }
+ // try to parse out lengths
+ }
+ }
+}
+
+void eq_set(int on, char data[10], int preamp)
+{}
+
+DWORD WINAPI DecodeThread(LPVOID b)
+{
+ int last_bpos = -1;
+ int firstsynch = 0;
+ ULONGLONG next_status_time = 0;
+ while (!killDecodeThread)
+ {
+ EnterCriticalSection(&g_decoder_cs);
+ int r = m_decoder->run((int*)&killDecodeThread);
+ LeaveCriticalSection(&g_decoder_cs);
+ if (r < 0)
+ {
+ if (m_decoder->get_error())
+ {
+ EnterCriticalSection(&g_decoder_cs);
+ lstrcpynA(error_string, m_decoder->get_error(), sizeof(error_string));
+ LeaveCriticalSection(&g_decoder_cs);
+ PostMessage(mod.hMainWindow, WM_USER, 0, 243);
+ Sleep(200);
+ }
+ break;
+ }
+ else if (!r)
+ {
+ Sleep(1);
+ int br = m_decoder->getBitrate() / 1000;
+ if (br != m_last_bitrate)
+ {
+ m_last_bitrate = br;
+ mod.SetInfo(br, -1, -1, -1);
+ }
+
+ int bpos = m_decoder->getBufferPos();
+ if (bpos > 255)
+ {
+ ULONGLONG obuf = g_bufferstat;
+ g_bufferstat = 0;
+ if (last_bpos >= 0)
+ {
+ EnterCriticalSection(&g_decoder_cs);
+ error_string[0] = 0;
+ LeaveCriticalSection(&g_decoder_cs);
+ PostMessage(mod.hMainWindow, WM_USER, 0, 243);
+ last_bpos = -1;
+ int csa = mod.SAGetMode();
+ if (csa && obuf)
+ {
+ char tempdata[75*2] = {0, };
+ mod.SAAdd(tempdata, (int)++obuf, (csa == 3) ? 0x80000003 : csa);
+ }
+ }
+ }
+ else
+ {
+ if (!g_bufferstat)
+ {
+ if (!has_opened_outmod) mod.SAVSAInit(10, 44100);
+
+ g_bufferstat = m_decoder->getpos() + 1;
+ }
+
+ if (bpos != last_bpos)
+ {
+ last_bpos = bpos;
+ EnterCriticalSection(&g_decoder_cs);
+ StringCchPrintfA(error_string, 128, WASABI_API_LNGSTRING(IDS_BUFFER_X), (bpos*100) / 256);
+ LeaveCriticalSection(&g_decoder_cs);
+ int csa = mod.SAGetMode();
+ char tempdata[2*75] = {0, };
+ int x;
+ if (csa&1)
+ {
+ for (x = 0; x < bpos*75 / 256; x ++)
+ {
+ tempdata[x] = x * 16 / 75;
+ }
+ }
+ if (csa&2)
+ {
+ int offs = (csa & 1) ? 75 : 0;
+ x = 0;
+ while (x < bpos*75 / 256)
+ {
+ tempdata[offs + x++] = -6 + x * 14 / 75;
+ }
+ while (x < 75)
+ {
+ tempdata[offs + x++] = 0;
+ }
+ }
+ if (csa == 4)
+ {
+ tempdata[0] = tempdata[1] = (bpos * 127 / 256);
+ }
+
+ if (csa) mod.SAAdd(tempdata, (int)++g_bufferstat, (csa == 3) ? 0x80000003 : csa);
+ PostMessage(mod.hMainWindow, WM_USER, 0, 243);
+ }
+ }
+
+ if (GetTickCount64() > next_status_time || GetTickCount64() < next_status_time - 5000)
+ {
+ char statusbuf[1024] = {0};
+ EnterCriticalSection(&g_decoder_cs);
+ g_streaminfobuf[0] = 0;
+
+ if (g_streaminfobuf_used)
+ {
+ char *outp = g_streaminfobuf;
+ size_t size = 512;
+ const char *p = m_decoder->getServerHeader("server");
+ if (!p) p = m_decoder->getServerHeader("icy-notice2");
+
+ if (p && strlen(p) < sizeof(g_streaminfobuf) - (outp - g_streaminfobuf) - 32)
+ StringCchPrintfExA(outp, size, &outp, &size, 0, "%s: %s\r\n", WASABI_API_LNGSTRING(IDS_SERVER), p);
+
+ p = m_decoder->getServerHeader("content-type");
+ if (p && strlen(p) < sizeof(g_streaminfobuf) - (outp - g_streaminfobuf) - 32)
+ StringCchPrintfExA(outp, size, &outp, &size, 0, "%s: %s\r\n", WASABI_API_LNGSTRING(IDS_CONTENT_TYPE), p);
+
+ p = m_decoder->getServerHeader("content-length");
+ if (p && strlen(p) < sizeof(g_streaminfobuf) - (outp - g_streaminfobuf) - 32)
+ StringCchPrintfExA(outp, size, &outp, &size, 0, "%s: %s\r\n", WASABI_API_LNGSTRING(IDS_CONTENT_LENGTH), p);
+
+ p = m_decoder->getServerHeader("icy-name");
+ if (p && strlen(p) < sizeof(g_streaminfobuf) - (outp - g_streaminfobuf) - 32)
+ StringCchPrintfExA(outp, size, &outp, &size, 0, "%s: %s\r\n", WASABI_API_LNGSTRING(IDS_STREAM_NAME), p);
+ }
+
+ lstrcpynA(statusbuf, "NSV: ", 1024);
+ { // codecs
+ char *sb = statusbuf;
+ size_t size = 1024;
+
+ int l = m_decoder->getlen();
+ if (l > 0)
+ {
+ l /= 1000;
+ if (l >= 3600)
+ {
+ StringCchPrintfExA(sb, size, &sb, &size, 0, "%d:", l / 3600);
+ l %= 3600;
+ StringCchPrintfExA(sb, size, &sb, &size, 0, "%02d:%02d", l / 60, l % 60);
+ }
+ else
+ StringCchPrintfExA(sb, size, &sb, &size, 0, "%d:%02d", l / 60, l % 60);
+ }
+
+ int a = (m_decoder->getBitrate() + 500) / 1000;
+ if (a)
+ {
+ if (strlen(statusbuf) > 5)
+ StringCchCatExA(sb, size, " @ ", &sb, &size, 0);
+ StringCchPrintfExA(sb, size, &sb, &size, 0, "%d%s", a, WASABI_API_LNGSTRING(IDS_KBPS));
+ }
+
+ if (strlen(statusbuf) > 5)
+ StringCchCatExA(sb, size, ", ", &sb, &size, 0);
+
+ char *p = statusbuf + strlen(statusbuf);
+ m_decoder->getVideoDesc(p);
+ if (p && !*p) StringCchCopyExA(p, size, "?, ", &p, &size, 0);
+ else if (!strncmp(p, "NONE", 4)) *p = 0;
+ else StringCchCatExA(p, size, ", ", &p, &size, 0);
+
+ p = statusbuf + strlen(statusbuf);
+ m_decoder->getAudioDesc(p);
+ if (p && !*p) StringCchCopyExA(p, size, "?", &p, &size, 0);
+ else if (!strncmp(p, "NONE", 4)) *p = 0;
+ }
+ LeaveCriticalSection(&g_decoder_cs);
+ m_video_output->extended(VIDUSER_SET_INFOSTRING, (intptr_t)statusbuf, 0);
+
+ next_status_time = GetTickCount64() + 2500;
+ }
+ }
+ else
+ {
+ if (!firstsynch)
+ {
+ if (m_decoder->canseek())
+ {
+ mod.is_seekable = 1;
+ if (g_play_needseek >= 0) seek_needed = g_play_needseek;
+ g_play_needseek = -1;
+ }
+ firstsynch++;
+ PostMessage(mod.hMainWindow, WM_USER, 0, 243);
+ }
+ }
+ if (seek_needed >= 0)
+ {
+ EnterCriticalSection(&g_decoder_cs);
+ m_decoder->seek(seek_needed);
+ seek_needed = -1;
+ LeaveCriticalSection(&g_decoder_cs);
+ }
+ }
+ if (!killDecodeThread)
+ {
+ PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
+ }
+ m_decoder->CloseVideo();
+ return 0;
+}
+
+// module definition.
+In_Module mod =
+ {
+ IN_VER_RET, // defined in IN2.H
+ "nullsoft(in_nsv.dll)",
+ 0, // hMainWindow (filled in by winamp)
+ 0, // hDllInstance (filled in by winamp)
+ 0, // this is a double-null limited list. "EXT\0Description\0EXT\0Description\0" etc.
+ 1, // is_seekable
+ 1, // uses output plug-in system
+ config,
+ about,
+ init,
+ quit,
+ getfileinfo,
+ infoDlg,
+ isourfile,
+ play,
+ pause,
+ unpause,
+ ispaused,
+ stop,
+
+ getlength,
+ getoutputtime,
+ setoutputtime,
+
+ setvolume,
+ setpan,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, // visualization calls filled in by winamp
+
+ 0, 0, // dsp calls filled in by winamp
+
+ eq_set,
+
+ NULL, // setinfo call filled in by winamp
+
+ 0 // out_mod filled in by winamp
+ };
+
+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;
+}
+
+extern "C"
+{
+ __declspec( dllexport ) In_Module * winampGetInModule2()
+ {
+ return &mod;
+ }
+
+ wchar_t lastextfn[1024] = {0};
+ // Keep track of file timestamp for file system change notification handling
+ FILETIME last_write_time = {0, 0};
+ static int valid;
+ static nsv_fileHeader hdr;
+
+ bool isFileChanged(const wchar_t* file)
+ {
+ WIN32_FIND_DATAW FindFileData = {0};
+ HANDLE hFind = FindFirstFileW(file, &FindFileData);
+ if (hFind == INVALID_HANDLE_VALUE)
+ return true;
+ FindClose(hFind);
+
+ if ((last_write_time.dwHighDateTime ==
+ FindFileData.ftLastWriteTime.dwHighDateTime) &&
+ (last_write_time.dwLowDateTime ==
+ FindFileData.ftLastWriteTime.dwLowDateTime))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ __declspec( dllexport ) int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, int destlen)
+ {
+ if (!_stricmp(data, "type"))
+ {
+ if (!fn || !fn[0] || _wcsicmp(PathFindExtensionW(fn), L".nsa")) // if extension is NOT nsa
+ lstrcpyn(dest, L"1", destlen); //video
+ else
+ lstrcpyn(dest, L"0", destlen); // audio
+ return 1;
+ }
+
+ if (!fn || (fn && !fn[0]))
+ return 0;
+
+ if (!_stricmp(data, "family"))
+ {
+ int pID = -1;
+ LPCWSTR e = PathFindExtensionW(fn);
+ if (L'.' != *e) return 0;
+ e++;
+ DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+ if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"NSV", -1)) pID = IDS_FAMILY_STRING_NSV;
+ if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"NSA", -1)) pID = IDS_FAMILY_STRING_NSA;
+ if (pID != -1 && S_OK == StringCchCopyW(dest, destlen, WASABI_API_LNGSTRINGW(pID))) return 1;
+ return 0;
+ }
+
+ //the file name differs from the last file
+ //name but we need to check the time stamp too
+ if (_wcsicmp(fn, lastextfn) || isFileChanged(fn) || HasFileTimeChanged(fn))
+ {
+ free(hdr.metadata);
+ memset(&hdr, 0, sizeof(hdr));
+ valid = 0;
+
+ lstrcpyn(lastextfn, fn, ARRAYSIZE(lastextfn));
+
+ nsv_InBS bs;
+
+ IDataReader *rdr = CreateReader(AutoCharFn(lastextfn));
+
+ if (rdr)
+ {
+ while (!rdr->iseof())
+ {
+ char buf[1024] = {0};
+ int l = (int)rdr->read(buf, sizeof(buf));
+ if (!l) break;
+ bs.add(buf, l);
+ l = nsv_readheader(bs, &hdr);
+ if (l <= 0)
+ {
+ free(hdr.toc);
+ if (!l)
+ {
+ valid = 1;
+
+ //Save time stamp
+ WIN32_FIND_DATAW FindFileData = {0};
+ HANDLE hFind = FindFirstFileW(fn, &FindFileData);
+ if (hFind == INVALID_HANDLE_VALUE)
+ {
+ last_write_time.dwHighDateTime = NULL;
+ last_write_time.dwLowDateTime = NULL;
+ }
+ else
+ {
+ last_write_time.dwHighDateTime = FindFileData.ftLastWriteTime.dwHighDateTime;
+ last_write_time.dwLowDateTime = FindFileData.ftLastWriteTime.dwLowDateTime;
+ FindClose(hFind);
+ }
+ break;
+ }
+ break;
+ }
+ }
+ delete rdr;
+ }
+ }
+
+ dest[0] = 0;
+
+ if (!valid)
+ {
+ return 0;
+ }
+
+ if (!_stricmp(data, "length"))
+ {
+ if (hdr.file_lenms > 0)
+ {
+ StringCchPrintfW(dest, destlen, L"%d", hdr.file_lenms);
+ }
+ }
+ else if (hdr.metadata)
+ {
+ const char *t = nsv_getmetadata(hdr.metadata, (char*)data);
+ if (t) lstrcpyn(dest, AutoWide(t), destlen);
+ }
+
+ return 1;
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_nsv/api.h b/Src/Plugins/Input/in_nsv/api.h
new file mode 100644
index 00000000..e94bdd9c
--- /dev/null
+++ b/Src/Plugins/Input/in_nsv/api.h
@@ -0,0 +1,16 @@
+#ifndef NULLSOFT_API_H
+#define NULLSOFT_API_H
+
+#include "../Agave/Config/api_config.h"
+extern api_config *configApi;
+#define AGAVE_API_CONFIG configApi
+
+#include <api/memmgr/api_memmgr.h>
+extern api_memmgr *memmgrApi;
+#define WASABI_API_MEMMGR memmgrApi
+
+#include <api/service/waServiceFactory.h>
+
+#include "../Agave/Language/api_language.h"
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_nsv/config.cpp b/Src/Plugins/Input/in_nsv/config.cpp
new file mode 100644
index 00000000..6c4ada01
--- /dev/null
+++ b/Src/Plugins/Input/in_nsv/config.cpp
@@ -0,0 +1,197 @@
+#include <windows.h>
+#include "api.h"
+#include "resource.h"
+#include "../Winamp/wa_ipc.h"
+#include "../Winamp/in2.h"
+
+extern In_Module mod; // the output module (filled in near the bottom of this file)
+
+static char app_name[] = "Nullsoft NSV Decoder2";
+
+//char config_http_proxynonport80=1;
+
+int config_padtag=1024;
+int config_bufms=10000;
+int config_prebufms=2000;
+int config_underunbuf=3000;
+int config_bufms_f=1000;
+int config_prebufms_f=1000;
+int config_underunbuf_f=1000;
+int config_vidoffs=0;
+int config_precseek=3;
+int config_subtitles=1;
+
+void config_write();
+
+char INI_FILE[512] = {0};
+
+static int _r_i(char *name, int def)
+{
+ if (!_strnicmp(name,"config_",7)) name += 7;
+ return GetPrivateProfileIntA(app_name,name,def,INI_FILE);
+}
+#define RI(x) (( x ) = _r_i(#x,( x )))
+static void _w_i(char *name, int d)
+{
+ char str[120] = {0};
+ wsprintfA(str,"%d",d);
+ if (!_strnicmp(name,"config_",7)) name += 7;
+ WritePrivateProfileStringA(app_name,name,str,INI_FILE);
+}
+#define WI(x) _w_i(#x,( x ))
+
+static void _r_s(char *name,char *data, int mlen)
+{
+ char buf[2048] = {0};
+ strncpy(buf, data, 2048);
+ if (!_strnicmp(name,"config_",7)) name += 7;
+ GetPrivateProfileStringA(app_name,name,buf,data,mlen,INI_FILE);
+}
+#define RS(x) (_r_s(#x,x,sizeof(x)))
+
+static void _w_s(char *name, char *data)
+{
+ if (!_strnicmp(name,"config_",7)) name += 7;
+ WritePrivateProfileStringA(app_name,name,data,INI_FILE);
+}
+#define WS(x) (_w_s(#x,x))
+
+
+
+static void config_init()
+{
+char *p;
+ if (mod.hMainWindow &&
+ (p = (char *)SendMessageA(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILE))
+ && p!= (char *)1)
+ {
+ strncpy(INI_FILE, p, MAX_PATH);
+ }
+ else
+ {
+ GetModuleFileNameA(NULL,INI_FILE,sizeof(INI_FILE));
+ p=INI_FILE+strlen(INI_FILE);
+ while (p >= INI_FILE && *p != '.') p--;
+ strcpy(++p,"ini");
+ }
+}
+
+static INT_PTR CALLBACK configProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ // show config
+ SetDlgItemInt(hwndDlg,IDC_BUF1,config_bufms,FALSE);
+ SetDlgItemInt(hwndDlg,IDC_BUF2,config_prebufms,FALSE);
+ SetDlgItemInt(hwndDlg,IDC_BUF3,config_underunbuf,FALSE);
+ SetDlgItemInt(hwndDlg,IDC_BUF4,config_bufms_f,FALSE);
+ SetDlgItemInt(hwndDlg,IDC_BUF5,config_prebufms_f,FALSE);
+ SetDlgItemInt(hwndDlg,IDC_BUF6,config_underunbuf_f,FALSE);
+ SetDlgItemInt(hwndDlg,IDC_OFFS,config_vidoffs,TRUE);
+ SetDlgItemInt(hwndDlg,IDC_TAGPAD,config_padtag,FALSE);
+ if (config_precseek&1)CheckDlgButton(hwndDlg,IDC_CHECK1,BST_CHECKED);
+ if (config_precseek&2)CheckDlgButton(hwndDlg,IDC_CHECK2,BST_CHECKED);
+ if (config_subtitles)CheckDlgButton(hwndDlg,IDC_CHECK3,BST_CHECKED);
+// if (!config_http_proxynonport80) CheckDlgButton(hwndDlg,IDC_CHECK5,BST_CHECKED);
+ return 0;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_CHECK1:
+ case IDC_CHECK2:
+ config_precseek=(IsDlgButtonChecked(hwndDlg,IDC_CHECK1)?1:0)|
+ (IsDlgButtonChecked(hwndDlg,IDC_CHECK2)?2:0);
+
+ break;
+ case IDC_CHECK3:
+ config_subtitles=(IsDlgButtonChecked(hwndDlg,IDC_CHECK3)?1:0);
+
+ break;
+ case IDC_OFFS:
+ if (HIWORD(wParam) == EN_CHANGE)
+ {
+ BOOL t;
+ config_vidoffs=GetDlgItemInt(hwndDlg,IDC_OFFS,&t,TRUE);
+ }
+ break;
+ case IDCANCEL: EndDialog(hwndDlg,0); break;
+ case IDOK:
+ // save config
+// config_http_proxynonport80=!IsDlgButtonChecked(hwndDlg,IDC_CHECK5);
+ {
+ BOOL t;
+ config_bufms=GetDlgItemInt(hwndDlg,IDC_BUF1,&t,FALSE);
+ if (config_bufms < 100) config_bufms=100;
+ if (config_bufms > 100000) config_bufms=100000;
+
+ config_prebufms=GetDlgItemInt(hwndDlg,IDC_BUF2,&t,FALSE);
+ if (config_prebufms < 100) config_prebufms=100;
+ if (config_prebufms > config_bufms) config_prebufms=config_bufms;
+
+ config_underunbuf=GetDlgItemInt(hwndDlg,IDC_BUF3,&t,FALSE);
+ if (config_underunbuf < 100) config_underunbuf=100;
+ if (config_underunbuf > config_bufms) config_underunbuf=config_bufms;
+
+ config_bufms_f=GetDlgItemInt(hwndDlg,IDC_BUF4,&t,FALSE);
+ if (config_bufms_f < 100) config_bufms_f=100;
+ if (config_bufms_f > 100000) config_bufms_f=100000;
+
+ config_prebufms_f=GetDlgItemInt(hwndDlg,IDC_BUF5,&t,FALSE);
+ if (config_prebufms_f < 100) config_prebufms_f=100;
+ if (config_prebufms_f > config_bufms_f) config_prebufms_f=config_bufms_f;
+
+ config_underunbuf_f=GetDlgItemInt(hwndDlg,IDC_BUF6,&t,FALSE);
+ if (config_underunbuf_f < 100) config_underunbuf_f=100;
+ if (config_underunbuf_f > config_bufms_f) config_underunbuf_f=config_bufms_f;
+
+ config_vidoffs=GetDlgItemInt(hwndDlg,IDC_OFFS,&t,TRUE);
+
+ config_padtag=GetDlgItemInt(hwndDlg,IDC_TAGPAD,&t,FALSE);
+ }
+ config_write();
+ EndDialog(hwndDlg,1);
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+void config(HWND hwndParent)
+{
+ WASABI_API_DIALOGBOXW(IDD_DIALOG1,hwndParent,configProc);
+}
+
+void config_read()
+{
+ config_init();
+ RI(config_bufms);
+ RI(config_prebufms);
+ RI(config_underunbuf);
+ RI(config_bufms_f);
+ RI(config_prebufms_f);
+ RI(config_underunbuf_f);
+ RI(config_vidoffs);
+ RI(config_padtag);
+// RI(allow_uvox);
+// RI(config_http_proxynonport80);
+ RI(config_precseek);
+ RI(config_subtitles);
+}
+
+void config_write()
+{
+ WI(config_bufms);
+ WI(config_prebufms);
+ WI(config_underunbuf);
+ WI(config_bufms_f);
+ WI(config_prebufms_f);
+ WI(config_underunbuf_f);
+ WI(config_vidoffs);
+ WI(config_padtag);
+ WI(config_precseek);
+ WI(config_subtitles);
+// WI(allow_uvox);
+// WI(config_http_proxynonport80);
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_nsv/in_nsv.rc b/Src/Plugins/Input/in_nsv/in_nsv.rc
new file mode 100644
index 00000000..55fac0cc
--- /dev/null
+++ b/Src/Plugins/Input/in_nsv/in_nsv.rc
@@ -0,0 +1,294 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_DIALOG1 DIALOGEX 0, 0, 185, 158
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Nullsoft NSV Decoder Configuration"
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ GROUPBOX "Buffering",IDC_STATIC,4,4,177,86
+ LTEXT "Stream",IDC_STATIC,83,15,23,8
+ LTEXT "Local File",IDC_STATIC,132,15,31,8
+ RTEXT "Buffer size:",IDC_STATIC,14,28,66,8
+ EDITTEXT IDC_BUF1,83,26,27,12,ES_NUMBER
+ LTEXT "ms",IDC_STATIC,113,28,10,8
+ EDITTEXT IDC_BUF4,132,26,27,12,ES_NUMBER
+ LTEXT "ms",IDC_STATIC,162,28,10,8
+ RTEXT "Prebuffer at start:",IDC_STATIC,14,44,66,8
+ EDITTEXT IDC_BUF2,83,42,27,12,ES_NUMBER
+ LTEXT "ms",IDC_STATIC,113,44,10,8
+ EDITTEXT IDC_BUF5,132,42,27,12,ES_NUMBER
+ LTEXT "ms",IDC_STATIC,162,44,10,8
+ LTEXT "Rebuffer on underrun:",IDC_STATIC,8,60,77,8
+ EDITTEXT IDC_BUF3,83,58,27,12,ES_NUMBER
+ LTEXT "ms",IDC_STATIC,113,60,10,8
+ EDITTEXT IDC_BUF6,132,58,27,12,ES_NUMBER
+ LTEXT "ms",IDC_STATIC,162,60,10,8
+ LTEXT "Precise seeking:",IDC_STATIC,29,75,55,8
+ CONTROL "enabled",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,83,74,41,10
+ CONTROL "enabled",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,132,74,41,10
+ RTEXT "Offset video from audio by:",IDC_STATIC,4,97,91,8
+ EDITTEXT IDC_OFFS,101,95,27,12
+ LTEXT "ms",IDC_STATIC,133,97,10,8
+ LTEXT "Pad NSV tags with: ",IDC_STATIC,34,113,63,8
+ EDITTEXT IDC_TAGPAD,101,111,27,12,ES_NUMBER
+ LTEXT "bytes",IDC_STATIC,133,113,18,8
+ LTEXT "Display Subtitles:",IDC_STATIC,40,128,60,8
+ CONTROL "enabled",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,101,127,41,10
+ DEFPUSHBUTTON "OK",IDOK,131,141,50,13
+END
+
+IDD_DIALOG2 DIALOGEX 0, 0, 276, 84
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "NSV Tag Metadata Editor"
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ LTEXT "Metadata should be in the format of NAME=value. Examples:",IDC_STATIC,7,6,197,8
+ LTEXT "TITLE=My Movie (sets the title of the video)",IDC_STATIC,21,17,160,8
+ LTEXT "ASPECT=1.0 (sets the aspect ratio of the video)",IDC_STATIC,22,27,186,8
+ LTEXT "FRAMERATE=18.79 (overrides the default NSV framerate)",IDC_STATIC,22,37,194,8
+ EDITTEXT IDC_EDIT1,7,50,262,12,ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,67,50,13
+ PUSHBUTTON "Cancel",IDCANCEL,61,67,50,13
+END
+
+IDD_DIALOG3 DIALOGEX 0, 0, 355, 204
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "NSV File Info / Tag Editor"
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_FN,5,5,345,12,ES_AUTOHSCROLL | ES_READONLY
+ GROUPBOX "NSV Bitstream Information",IDC_STATIC,5,22,345,30
+ EDITTEXT IDC_BSINFO,11,34,333,12,ES_AUTOHSCROLL | ES_READONLY
+ GROUPBOX "Length/TOC Information",IDC_STATIC,5,56,151,106
+ LTEXT "Stream size:",IDC_STATIC,12,70,50,8
+ EDITTEXT IDC_LENBYTES,67,68,78,12,ES_AUTOHSCROLL | ES_READONLY | ES_NUMBER | NOT WS_TABSTOP
+ LTEXT "Stream length:",IDC_STATIC,12,82,50,8
+ EDITTEXT IDC_LENMS,67,81,78,12,ES_AUTOHSCROLL | ES_READONLY | ES_NUMBER | NOT WS_TABSTOP
+ LTEXT "Avg bitrate:",IDC_STATIC,12,96,50,8
+ EDITTEXT IDC_AVGBITRATE,67,94,78,12,ES_AUTOHSCROLL | ES_READONLY | ES_NUMBER | NOT WS_TABSTOP
+ CONTROL "Table of contents (for seeking)",IDC_TOC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,111,112,10
+ RTEXT "TOC size:",IDC_STATIC,20,127,40,8
+ EDITTEXT IDC_TOCSIZE,67,123,78,12,ES_AUTOHSCROLL | ES_READONLY | ES_NUMBER | NOT WS_TABSTOP
+ LTEXT "Max TOC size:",IDC_STATIC,15,142,48,8
+ EDITTEXT IDC_SETTOCSIZE,67,139,30,12,ES_AUTOHSCROLL | ES_NUMBER
+ PUSHBUTTON "Analyze",IDC_ANALYZE,105,139,40,12
+ CONTROL "Fast tag update (don't shrink tag)",IDC_FASTUPD,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,166,119,10
+ LTEXT "ttl",IDC_TAG_LEN,5,175,127,8
+ GROUPBOX "Metadata",IDC_STATIC,161,56,189,143
+ LISTBOX IDC_METADATA,167,67,177,109,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "Add item",IDC_ADD,167,180,41,13
+ PUSHBUTTON "Delete item",IDC_REM,211,180,49,13
+ PUSHBUTTON "Edit item",IDC_EDIT,263,180,40,13
+ LTEXT "tml",IDC_METADATA_LEN,307,183,37,8
+ DEFPUSHBUTTON "Update tag",IDOK,5,186,46,13
+ PUSHBUTTON "Remove tag",IDC_REMTAG,55,186,45,13
+ PUSHBUTTON "Cancel",IDCANCEL,102,186,45,13
+END
+
+IDD_DIALOG4 DIALOGEX 0, 0, 266, 132
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "NSV Stream Info"
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ EDITTEXT IDC_FN,4,5,258,13,ES_AUTOHSCROLL | ES_READONLY
+ GROUPBOX "Stream Info",IDC_INFOBORDER,4,22,258,89
+ EDITTEXT IDC_INFO,9,32,246,75,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
+ DEFPUSHBUTTON "Close",IDCANCEL,4,115,45,13
+END
+
+IDD_ABOUT DIALOGEX 0, 0, 184, 54
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Nullsoft NSV Decoder"
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ CONTROL 102,IDC_STATIC,"Static",SS_BITMAP,7,7,67,39
+ CTEXT "",IDC_VERSION,74,7,103,8
+ CTEXT "© 2003-2023 Winamp SA",IDC_STATIC,74,16,103,8
+ DEFPUSHBUTTON "OK",IDOK,105,34,50,13
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_DIALOG1, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 181
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 154
+ END
+
+ IDD_DIALOG2, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 269
+ TOPMARGIN, 6
+ BOTTOMMARGIN, 80
+ END
+
+ IDD_DIALOG3, DIALOG
+ BEGIN
+ LEFTMARGIN, 5
+ RIGHTMARGIN, 350
+ TOPMARGIN, 5
+ BOTTOMMARGIN, 199
+ END
+
+ IDD_DIALOG4, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 262
+ TOPMARGIN, 5
+ BOTTOMMARGIN, 128
+ END
+
+ IDD_ABOUT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 177
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 47
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+#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
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+IDB_BITMAP1 BITMAP "nsv_logo.bmp"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_NSV_DECODER "Nullsoft NSV Decoder v%s"
+ 65535 "{11B847DB-29A7-47ac-B386-43B40385B817}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_TOTAL_TAG_SIZE "Total tag size: 0 bytes"
+ IDS_NO_METADATA "No metadata"
+ IDS_TOTAL_TAG_SIZE_X_BYTES "Total tag size: %d bytes"
+ IDS_NO_TAG "No tag"
+ IDS_ERROR_WRITING_STRING_TO_TAG
+ "Error writing string to tag (string too complex!) - string may be corrupted with a !\n"
+ IDS_NSV_TAG_EDITOR_ERROR "NSV Tag Editor Error"
+ IDS_LARGE_DATA "[LARGE DATA] - "
+ IDS_NO_VALID_NSV_BITSTREAM_FOUND "No valid NSV bitstream found\n"
+ IDS_VIDEO_X_AUDIO_X "Video: %s %dx%d@%d.%02dfps, Audio: %s"
+ IDS_METADATA_STRING_MUST_CONTAIN_ONE_EQUAL_SIGN
+ "Metadata string must contain at least one equal sign (=)."
+ IDS_NSV_TAG_EDITOR "NSV Tag Editor"
+ IDS_CANNOT_CREATE_TEMPFILE_CANNOT_UPDATE_TAG
+ "Cannot create tempfile - cannot update tag!"
+ IDS_ERROR_COPYING_SOURCE "Error copying source - cannot update tag!"
+ IDS_COPYING_X_BYTES "Copying %d bytes..."
+ IDS_ERROR_SIZE_MISMATCH "Error size mismatch - cannot update tag!"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_ERROR_RENAMING_SOURCE "Error renaming source - cannot update tag!"
+ IDS_ERROR_RENAMING_NEW_FILE "Error renaming new file - cannot update tag!"
+ IDS_READING_X_FRAMES "Reading %d frames..."
+ IDS_NSV_ANALYSIS_FAILED "NSV analysis failed"
+ IDS_METADATA_ITEM_CONTAINS_LARGE_AMOUNT_DATA
+ "This metadata item appear to contain a large amount of data.\nA binary metadata editor is coming soon, until then, you can\ndelete it if you wish to create something new."
+ IDS_CONNECTING "Connecting"
+ IDS_OPENING "Opening"
+ IDS_BUFFER_X "Buffer: %d%%"
+ IDS_SERVER "Server"
+ IDS_CONTENT_TYPE "Content Type"
+ IDS_CONTENT_LENGTH "Content Length"
+ IDS_STREAM_NAME "Stream name"
+ IDS_NSA_NSV_FILE "Nullsoft Audio/Video File (*.NSV;*.NSA)"
+ IDS_KBPS "kbps"
+ IDS_FPS "fps"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_FAMILY_STRING_NSV "Nullsoft Streaming Video File"
+ IDS_FAMILY_STRING_NSA "Nullsoft Streaming Audio 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_nsv/in_nsv.sln b/Src/Plugins/Input/in_nsv/in_nsv.sln
new file mode 100644
index 00000000..f06edb48
--- /dev/null
+++ b/Src/Plugins/Input/in_nsv/in_nsv.sln
@@ -0,0 +1,30 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29609.76
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "in_nsv", "in_nsv.vcxproj", "{9E4B1D43-137A-4AE1-A05A-92EEE0028BF0}"
+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
+ {9E4B1D43-137A-4AE1-A05A-92EEE0028BF0}.Debug|Win32.ActiveCfg = Debug|Win32
+ {9E4B1D43-137A-4AE1-A05A-92EEE0028BF0}.Debug|Win32.Build.0 = Debug|Win32
+ {9E4B1D43-137A-4AE1-A05A-92EEE0028BF0}.Debug|x64.ActiveCfg = Debug|x64
+ {9E4B1D43-137A-4AE1-A05A-92EEE0028BF0}.Debug|x64.Build.0 = Debug|x64
+ {9E4B1D43-137A-4AE1-A05A-92EEE0028BF0}.Release|Win32.ActiveCfg = Release|Win32
+ {9E4B1D43-137A-4AE1-A05A-92EEE0028BF0}.Release|Win32.Build.0 = Release|Win32
+ {9E4B1D43-137A-4AE1-A05A-92EEE0028BF0}.Release|x64.ActiveCfg = Release|x64
+ {9E4B1D43-137A-4AE1-A05A-92EEE0028BF0}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {382549BF-5E52-402C-8150-3E8BFCEF1439}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/Input/in_nsv/in_nsv.vcxproj b/Src/Plugins/Input/in_nsv/in_nsv.vcxproj
new file mode 100644
index 00000000..1f3c68a4
--- /dev/null
+++ b/Src/Plugins/Input/in_nsv/in_nsv.vcxproj
@@ -0,0 +1,274 @@
+<?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>{9E4B1D43-137A-4AE1-A05A-92EEE0028BF0}</ProjectGuid>
+ <RootNamespace>in_nsv</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </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">
+ <VcpkgEnabled>false</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_DEBUG;WIN32;_WINDOWS;_USRDLL;IN_NSV_EXPORTS;WINAMP_PLUGIN;_CRT_SECURE_NO_WARNINGS;NO_ABOUT_EGG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;winmm.lib;wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </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>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_DEBUG;WIN64;_WINDOWS;_USRDLL;IN_NSV_EXPORTS;WINAMP_PLUGIN;_CRT_SECURE_NO_WARNINGS;NO_ABOUT_EGG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;winmm.lib;wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </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>NDEBUG;WIN32;_WINDOWS;_USRDLL;IN_NSV_EXPORTS;WINAMP_PLUGIN;_CRT_SECURE_NO_WARNINGS;NO_ABOUT_EGG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;winmm.lib;wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </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>NDEBUG;WIN64;_WINDOWS;_USRDLL;IN_NSV_EXPORTS;WINAMP_PLUGIN;_CRT_SECURE_NO_WARNINGS;NO_ABOUT_EGG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;winmm.lib;wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </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>
+ <ClInclude Include="..\..\..\nsv\nsvbs.h" />
+ <ClInclude Include="..\..\..\nsv\nsvlib.h" />
+ <ClInclude Include="api.h" />
+ <ClInclude Include="resource.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\..\nsv\nsvlib.cpp" />
+ <ClCompile Include="..\..\..\nsv\nsvplay\decoders.cpp" />
+ <ClCompile Include="..\..\..\nsv\nsvplay\nsvdecode.cpp" />
+ <ClCompile Include="..\..\..\nsv\nsvplay\readers.cpp" />
+ <ClCompile Include="..\..\..\nsv\nsvplay\subtitles.cpp" />
+ <ClCompile Include="config.cpp" />
+ <ClCompile Include="infodlg.cpp" />
+ <ClCompile Include="Main.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="in_nsv.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="nsv_logo.bmp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Input/in_nsv/in_nsv.vcxproj.filters b/Src/Plugins/Input/in_nsv/in_nsv.vcxproj.filters
new file mode 100644
index 00000000..12bf6b29
--- /dev/null
+++ b/Src/Plugins/Input/in_nsv/in_nsv.vcxproj.filters
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="Main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="infodlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="config.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nsv\nsvplay\decoders.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nsv\nsvplay\nsvdecode.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nsv\nsvlib.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nsv\nsvplay\readers.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nsv\nsvplay\subtitles.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nsv\nsvbs.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nsv\nsvlib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{a867382b-03bf-4021-989d-458eb1ef9db6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{6ccfd425-b17c-4d1c-985a-d58134034d50}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{17af3ae5-8365-4ae7-aab5-737661ec6b5a}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Image Files">
+ <UniqueIdentifier>{78b43273-fca3-401e-9ca5-bbf4cfe27a2c}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="nsv_logo.bmp">
+ <Filter>Image Files</Filter>
+ </Image>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="in_nsv.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Input/in_nsv/infodlg.cpp b/Src/Plugins/Input/in_nsv/infodlg.cpp
new file mode 100644
index 00000000..99a99342
--- /dev/null
+++ b/Src/Plugins/Input/in_nsv/infodlg.cpp
@@ -0,0 +1,1089 @@
+#include <windows.h>
+#include "api.h"
+#include "resource.h"
+
+#include "../Winamp/in2.h"
+
+extern In_Module mod; // the output module (filled in near the bottom of this file)
+
+extern "C" { extern wchar_t lastextfn[1024]; };
+
+#define MAX_EDITABLE_METASTRING 8192
+
+#include "../nsv/nsvlib.h"
+ static int isplaying;
+ static int timems;
+extern char lastfn[1024];
+extern int g_play_needseek;
+
+ static void restartPlayback()
+ {
+ if (isplaying)
+ {
+ SendMessage(mod.hMainWindow,WM_USER,0,3007); // disable stats updating
+ SendMessage(mod.hMainWindow,WM_COMMAND,40045,0);
+ if (timems)
+ {
+ g_play_needseek=timems;
+// SendMessage(mod.hMainWindow,WM_USER,timems,106);
+ }
+ if (isplaying & 2)
+ {
+ SendMessage(mod.hMainWindow,WM_COMMAND,40046,0);
+ }
+ SendMessage(mod.hMainWindow,WM_USER,1,3007); // enable stats updating
+ }
+ }
+
+ static void stopPlayback(const char *fn)
+ {
+ isplaying=0;
+ timems=0;
+ if (!_stricmp(lastfn,fn))
+ {
+ isplaying= (int)SendMessage(mod.hMainWindow,WM_USER,0,104);
+ if (isplaying)
+ {
+ timems= (int)SendMessage(mod.hMainWindow,WM_USER,0,105);
+ SendMessage(mod.hMainWindow,WM_COMMAND,40047,0);
+ }
+ }
+ }
+
+
+extern int config_padtag;
+int fillBs(HANDLE hFile, nsv_InBS &bs, int lenbytes);
+ static nsv_fileHeader m_set_lhdr;
+ static char m_set_lfile[1024];
+
+ static void closeset()
+ {
+ free(m_set_lhdr.toc);
+ free(m_set_lhdr.metadata);
+ memset(&m_set_lhdr,0,sizeof(m_set_lhdr));
+ m_set_lfile[0]=0;
+ }
+
+extern int config_bufms, config_prebufms, config_underunbuf;
+
+extern "C"
+{
+
+ __declspec( dllexport ) int winampSetExtendedFileInfo(const char *fn, const char *data, char *val)
+ {
+ if(!fn || !fn[0]) return 0;
+
+ //muahaha, <3 hacks
+ if(!_stricmp(data,"setHttpConfigValues"))
+ {
+ config_bufms=atoi(val);
+ char *p=strstr(val,",");
+ if(!p) return 0;
+ config_prebufms=atoi(p+1);
+ p=strstr(p+1,",");
+ if(!p) return 0;
+ config_underunbuf=atoi(p+1);
+ return 1;
+ }
+
+ if(strcmpi(fn,m_set_lfile))
+ {
+ closeset();
+
+ lstrcpynA(m_set_lfile,fn,sizeof(m_set_lfile));
+
+ HANDLE hFile = CreateFileA(fn,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ nsv_InBS bs;
+ for (;;)
+ {
+ int ret=nsv_readheader(bs,&m_set_lhdr);
+ if (ret <= 0 || fillBs(hFile,bs,ret)) break;
+ }
+ CloseHandle(hFile);
+ }
+ }
+
+ char *p=(char*)m_set_lhdr.metadata;
+ unsigned int pos=0;
+
+ int omdl=m_set_lhdr.metadata_len;
+ if (p) while (pos < m_set_lhdr.metadata_len)
+ {
+ // scan for =
+ while (pos < m_set_lhdr.metadata_len && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) { pos++; p++; }
+ if (pos >= m_set_lhdr.metadata_len) break;
+
+ char *lp=p;
+ while (pos < m_set_lhdr.metadata_len && *p != '=') { pos++; p++; }
+ if (pos >= m_set_lhdr.metadata_len) break;
+
+ // skip =
+ p++; if (++pos >= m_set_lhdr.metadata_len) break;
+
+ // get delim char
+ char divc=*p++; if (++pos >= m_set_lhdr.metadata_len) break;
+
+ // scan for new delim char
+ while (pos < m_set_lhdr.metadata_len && *p != divc) { pos++; p++; }
+
+ p++; // advance over our delim char
+ if (++pos > m_set_lhdr.metadata_len) break;
+
+
+ if (!strncmp(lp,data,strlen(data)) && lp[strlen(data)]=='=')
+ {
+ if (pos >= m_set_lhdr.metadata_len)
+ {
+ m_set_lhdr.metadata_len = (unsigned int)(lp - (char*)m_set_lhdr.metadata);
+ }
+ else
+ {
+ memcpy(lp,p,m_set_lhdr.metadata_len - (p-(char*)m_set_lhdr.metadata));
+ m_set_lhdr.metadata_len -= (unsigned int)(p-lp);
+ }
+ break;
+ }
+ }
+
+ if (val && *val)
+ {
+ unsigned char divc; //fucko
+ int x;
+ for (x = 1; x < 256 && strchr(val,x); x ++);
+ if (x == 256) return 1;
+ divc=(unsigned char)x;
+
+ int nmdl= (int)(m_set_lhdr.metadata_len + 5 + strlen(data) + strlen(val));
+
+ if (!m_set_lhdr.metadata || omdl<nmdl)
+ m_set_lhdr.metadata=realloc(m_set_lhdr.metadata,nmdl);
+ memcpy((char*)m_set_lhdr.metadata + m_set_lhdr.metadata_len,data,strlen(data));
+ m_set_lhdr.metadata_len+= (int)strlen(data);
+ memcpy((char*)m_set_lhdr.metadata + m_set_lhdr.metadata_len,"=",1);
+ m_set_lhdr.metadata_len++;
+ memcpy((char*)m_set_lhdr.metadata + m_set_lhdr.metadata_len,&divc,1);
+ m_set_lhdr.metadata_len++;
+
+ memcpy((char*)m_set_lhdr.metadata + m_set_lhdr.metadata_len,val,strlen(val));
+ m_set_lhdr.metadata_len+= (int)strlen(val);
+
+ memcpy((char*)m_set_lhdr.metadata + m_set_lhdr.metadata_len,&divc,1);
+ m_set_lhdr.metadata_len++;
+
+ memcpy((char*)m_set_lhdr.metadata + m_set_lhdr.metadata_len," ",1); //space to be ghey
+ m_set_lhdr.metadata_len++;
+ }
+
+ return 1;
+ }
+
+
+ __declspec( dllexport ) int winampWriteExtendedFileInfo()
+ {
+ lastextfn[0]=0; // flush cache
+
+ if (m_set_lfile[0])
+ {
+
+
+
+ HANDLE hFile = CreateFileA(m_set_lfile,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+// MessageBox(mod.hMainWindow,"Error opening NSV file for update","NSV Tag Error",MB_OK);
+ return 0;
+ }
+
+ nsv_OutBS bs;
+ int osize=m_set_lhdr.header_size;
+ nsv_writeheader(bs,&m_set_lhdr,osize);
+
+ int hdrlen;
+ char *hdr=(char*)bs.get(&hdrlen);
+
+ if (hdr && hdrlen == (int)osize) // fast update of header
+ {
+ DWORD dw = 0;
+ SetFilePointer(hFile,0,NULL,SEEK_SET);
+ WriteFile(hFile,hdr,hdrlen,&dw,NULL);
+ CloseHandle(hFile);
+ }
+ else
+ {
+ if (hdr && config_padtag>0) // enlarge header by config_padtag bytes =)
+ {
+ bs.clear();
+ nsv_writeheader(bs,&m_set_lhdr,config_padtag+m_set_lhdr.header_size);
+ hdr=(char*)bs.get(&hdrlen); // update
+ }
+
+ char tmpfn[1024+8];
+ char tmpfn2[1024+8];
+ wsprintfA(tmpfn,"%s.new",m_set_lfile);
+ wsprintfA(tmpfn2,"%s.old",m_set_lfile);
+
+ HANDLE hTempFile=CreateFileA(tmpfn,GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL);
+ if (hTempFile == INVALID_HANDLE_VALUE)
+ {
+ // MessageBox(mod.hMainWindow,"Can't create tempfile - can't update tag!","NSV Tag Editor Error",MB_ICONSTOP|MB_OK);
+ CloseHandle(hFile);
+ return 0;
+ }
+ SetFilePointer(hFile,osize,NULL,SEEK_SET);
+ if (hdrlen)
+ {
+ DWORD dw = 0;
+ if (!WriteFile(hTempFile,hdr,hdrlen,&dw,NULL) || (int)dw != hdrlen)
+ {
+ CloseHandle(hTempFile);
+ CloseHandle(hFile);
+ DeleteFileA(tmpfn);
+ // MessageBox(mod.hMainWindow,"Error copying source - can't update tag!","NSV Tag Editor Error",MB_ICONSTOP|MB_OK);
+ return 0;
+ }
+ }
+
+ for (;;)
+ {
+ char buf[8192] = {0};
+ DWORD dw = 0;
+ BOOL r1=ReadFile(hFile,buf,sizeof(buf),&dw,NULL);
+ if (r1 && !dw) break;
+ DWORD dwout = 0;
+ if (!r1 || !WriteFile(hTempFile,buf,dw,&dwout,NULL) || dwout < dw)
+ {
+ CloseHandle(hTempFile);
+ DeleteFileA(tmpfn);
+ // MessageBox(mod.hMainWindow,"Error copying source - can't update tag!","NSV Tag Editor Error",MB_ICONSTOP|MB_OK);
+ return 0;
+ }
+ }
+ if (GetFileSize(hFile,NULL)-osize != GetFileSize(hTempFile,NULL)-hdrlen)
+ {
+ CloseHandle(hTempFile);
+ CloseHandle(hFile);
+ DeleteFileA(tmpfn);
+ // MessageBox(mod.hMainWindow,"Error size mismatch - can't update tag!","NSV Tag Editor Error",MB_ICONSTOP|MB_OK);
+ return 0;
+ }
+ CloseHandle(hFile);
+ CloseHandle(hTempFile);
+
+ stopPlayback(m_set_lfile);
+
+ if (!MoveFileA(m_set_lfile,tmpfn2))
+ {
+ DeleteFileA(tmpfn);
+ restartPlayback();
+ // MessageBox(mod.hMainWindow,"Error renaming source - can't update tag!","NSV Tag Editor Error",MB_ICONSTOP|MB_OK);
+ return 0;
+ }
+ if (!MoveFileA(tmpfn,m_set_lfile))
+ {
+ MoveFileA(tmpfn2,m_set_lfile);
+ DeleteFileA(tmpfn);
+ restartPlayback();
+ // MessageBox(mod.hMainWindow,"Error renaming new file - can't update tag!","NSV Tag Editor Error",MB_ICONSTOP|MB_OK);
+ return 0;
+ }
+ DeleteFileA(tmpfn2);
+ restartPlayback();
+ }
+ }
+ closeset();
+ lastextfn[0]=0;
+ return 1;
+ }
+}
+
+
+
+const char *g_lastfile;
+HANDLE g_hFile=INVALID_HANDLE_VALUE;
+nsv_fileHeader g_filehdr={0,~0,~0,};
+unsigned int g_oldtag_size;
+unsigned int *g_toc_save, *g_toc_save_ex, g_toc_savesize;
+
+void enableControls(HWND hwndDlg, int en)
+{
+ EnableWindow(GetDlgItem(hwndDlg,IDC_SETTOCSIZE),en);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_TOC),en);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_ANALYZE),en);
+ EnableWindow(GetDlgItem(hwndDlg,IDOK),en);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_REMTAG),en);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_ADD),en);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_REM),en);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_EDIT),en);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_FASTUPD),en);
+}
+
+void closeNsv(HWND hwndDlg)
+{
+ if (g_hFile != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(g_hFile);
+ g_hFile=INVALID_HANDLE_VALUE;
+ }
+ free(g_filehdr.metadata);
+ g_filehdr.metadata=0;
+ g_filehdr.metadata_len=0;
+ free(g_filehdr.toc);
+ g_filehdr.toc=0;
+ g_filehdr.toc_ex=0;
+ free(g_toc_save);
+ g_toc_save=0;
+ g_toc_save_ex=0;
+
+ g_toc_savesize=0;
+ g_filehdr.toc_size=0;
+ g_filehdr.toc_alloc=0;
+ g_filehdr.file_lenbytes=~0;
+ g_filehdr.file_lenms=~0;
+ g_filehdr.header_size=0;
+ SetDlgItemTextA(hwndDlg,IDC_LENBYTES,"");
+ SetDlgItemTextA(hwndDlg,IDC_LENMS,"");
+ SetDlgItemTextA(hwndDlg,IDC_AVGBITRATE,"");
+ CheckDlgButton(hwndDlg,IDC_TOC,0);
+ SetDlgItemTextA(hwndDlg,IDC_TOCSIZE,"");
+ SetDlgItemTextA(hwndDlg,IDC_SETTOCSIZE,"");
+ SetDlgItemTextA(hwndDlg,IDC_FN,"");
+ int x;
+ int cnt= (int)SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_GETCOUNT,0,0);
+ for ( x= 0; x < cnt; x ++)
+ {
+ void *v=(void *)SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_GETITEMDATA,x,0);
+ if (v) free(v);
+ SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_SETITEMDATA,x,0);
+ }
+ SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_RESETCONTENT,0,0);
+ SetDlgItemTextA(hwndDlg,IDC_TAG_LEN,WASABI_API_LNGSTRING(IDS_TOTAL_TAG_SIZE));
+ SetDlgItemTextA(hwndDlg,IDC_METADATA_LEN,WASABI_API_LNGSTRING(IDS_NO_METADATA));
+
+ enableControls(hwndDlg,0);
+}
+
+int fillBs(HANDLE hFile, nsv_InBS &bs, int lenbytes)
+{
+ while (lenbytes > 0)
+ {
+ DWORD r=0;
+ char buf[8192] = {0};
+ BOOL ret=ReadFile(hFile,buf,sizeof(buf),&r,NULL);
+ lenbytes-=r;
+ bs.add(buf,r);
+ if (!ret || !r) return 1;
+ }
+ return lenbytes > 0;
+}
+
+void makeNewHeaderInfo(HWND hwndDlg)
+{
+ nsv_OutBS bs;
+ g_filehdr.toc_alloc=0;
+ nsv_writeheader(bs,&g_filehdr,0);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_FASTUPD),g_filehdr.header_size <= g_oldtag_size);
+ char buf[128] = {0};
+ if (g_filehdr.header_size) wsprintfA(buf,WASABI_API_LNGSTRING(IDS_TOTAL_TAG_SIZE_X_BYTES),g_filehdr.header_size);
+ else WASABI_API_LNGSTRING_BUF(IDS_NO_TAG,buf,128);
+ SetDlgItemTextA(hwndDlg,IDC_TAG_LEN,buf);
+}
+
+void populateInfo(HWND hwndDlg)
+{
+ if (g_filehdr.file_lenbytes != ~0)
+ {
+ wchar_t buf[128] = {0};
+ unsigned int low=g_filehdr.file_lenbytes;
+ SetDlgItemTextW(hwndDlg,IDC_LENBYTES,WASABI_API_LNG->FormattedSizeString(buf, 128, low));
+ }
+ else SetDlgItemTextA(hwndDlg,IDC_LENBYTES,"?");
+
+ if (g_filehdr.file_lenms != ~0)
+ {
+ char buf[128] = {0};
+ unsigned int timems=g_filehdr.file_lenms;
+ if (timems < 1000) wsprintfA(buf,"%ums",timems);
+ else if (timems < 1000*60) wsprintfA(buf,"%02u.%03us",timems/1000,timems%1000);
+ else if (timems < 1000*60*60) wsprintfA(buf,"%02u:%02u.%03us",timems/60000,(timems/1000)%60,timems%1000);
+ else wsprintfA(buf,"%u:%02u:%02u.%03us",timems/3600000,(timems/60000)%60,(timems/1000)%60,timems%1000);
+
+ SetDlgItemTextA(hwndDlg,IDC_LENMS,buf);
+ }
+ else SetDlgItemTextA(hwndDlg,IDC_LENMS,"?");
+ CheckDlgButton(hwndDlg,IDC_TOC,g_filehdr.toc_size?BST_CHECKED:BST_UNCHECKED);
+ if (g_filehdr.toc_size)
+ {
+ char buf[128] = {0};
+ wsprintfA(buf,"%d%s",g_filehdr.toc_size,g_filehdr.toc_ex ? " (TOC 2.0)":"");
+ SetDlgItemTextA(hwndDlg,IDC_TOCSIZE,buf);
+ SetDlgItemInt(hwndDlg,IDC_SETTOCSIZE,g_filehdr.toc_size,FALSE);
+ }
+ else
+ {
+ SetDlgItemTextA(hwndDlg,IDC_TOCSIZE,"");
+ SetDlgItemTextA(hwndDlg,IDC_SETTOCSIZE,"4096");
+ }
+
+ if (g_filehdr.file_lenms != ~0 && g_filehdr.file_lenbytes != ~0)
+ {
+ unsigned int bitrate = g_filehdr.file_lenms ? MulDiv(g_filehdr.file_lenbytes,8000,g_filehdr.file_lenms) : 0;
+ char buf[1024] = {0};
+ wsprintfA(buf,"%u %s",bitrate/1000,WASABI_API_LNGSTRING(IDS_KBPS));
+ SetDlgItemTextA(hwndDlg,IDC_AVGBITRATE,buf);
+ }
+ else SetDlgItemTextA(hwndDlg,IDC_AVGBITRATE,"?");
+
+ char buf[128] = {0};
+ if (g_filehdr.header_size) wsprintfA(buf,WASABI_API_LNGSTRING(IDS_TOTAL_TAG_SIZE_X_BYTES),g_filehdr.header_size);
+ else WASABI_API_LNGSTRING_BUF(IDS_NO_TAG,buf,128);
+ SetDlgItemTextA(hwndDlg,IDC_TAG_LEN,buf);
+}
+
+void updateMetaData(HWND hwndDlg)
+{
+ free(g_filehdr.metadata);
+ g_filehdr.metadata=0;
+ g_filehdr.metadata_len=0;
+ int n= (int)SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_GETCOUNT,0,0);
+ int x;
+ int total_size=0;
+
+ for (x = 0; x < n; x ++)
+ {
+ int l;
+ char *bigstr = (char *)SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_GETITEMDATA,x,0);
+ if (bigstr) l = (int)strlen((char *)bigstr)+2;
+ else l= (int)SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_GETTEXTLEN,x,0);
+ total_size+=l+2; // text+ two chars
+ if (x) total_size++; // space
+ }
+
+ g_filehdr.metadata=malloc(total_size+1);
+ char *metaout=(char*)g_filehdr.metadata;
+ for (x = 0; x < n; x ++)
+ {
+ if (x) *metaout++=' ';
+
+ const char *bigstr = (const char *)SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_GETITEMDATA,x,0);
+
+ char *this_text;
+ if (bigstr) this_text=_strdup((const char *)bigstr);
+ else
+ {
+ int l= (int)SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_GETTEXTLEN,x,0);
+ this_text=(char*)malloc(l+1);
+ SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_GETTEXT,x,(LPARAM)this_text);
+ }
+ char *p=this_text;
+ while (p && *p != '=' && *p) *metaout++=*p++;
+ if (p && *p)
+ {
+ *metaout++=*p++;
+ int x;
+ for (x = 1; x < 256 && strchr(p,x); x ++);
+
+ if (x == 256)
+ {
+ char title[64] = {0};
+ MessageBoxA(hwndDlg,WASABI_API_LNGSTRING(IDS_ERROR_WRITING_STRING_TO_TAG),
+ WASABI_API_LNGSTRING_BUF(IDS_NSV_TAG_EDITOR_ERROR,title,64),
+ MB_OK|MB_ICONSTOP);
+ x=1;
+ }
+
+ *metaout++=x;
+ while (p && *p)
+ {
+ int a=*p++;
+ if (a == x) a = '!';
+ *metaout++=a;
+ }
+ *metaout++=x;
+ }
+ free(this_text);
+ }
+ g_filehdr.metadata_len= unsigned int(metaout - (char*)g_filehdr.metadata);
+ *metaout=0;
+}
+
+void populateMetaData(HWND hwndDlg)
+{
+ wchar_t buf[128] = {0};
+ if (g_filehdr.metadata_len) WASABI_API_LNG->FormattedSizeString(buf,128,g_filehdr.metadata_len);
+ else WASABI_API_LNGSTRINGW_BUF(IDS_NO_METADATA, buf, 128);
+ SetDlgItemTextW(hwndDlg,IDC_METADATA_LEN,buf);
+
+ int x;
+ int cnt= (int)SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_GETCOUNT,0,0);
+ for ( x= 0; x < cnt; x ++)
+ {
+ void *v=(void *)SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_GETITEMDATA,x,0);
+ if (v) free(v);
+ SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_SETITEMDATA,x,0);
+ }
+ SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_RESETCONTENT,0,0);
+
+ if (g_filehdr.metadata)
+ {
+ char *p=(char*)g_filehdr.metadata;
+ for (;;)
+ {
+ while (p && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++;
+ if (!p || !*p) break;
+
+ char *this_name=p;
+
+ // advance to next item
+ while (p && *p && *p != '=') p++;
+ char *end_name=p;
+
+ if (!*p++) break;
+ if (!*p) break;
+
+ char c=*p++;
+ char *begin_value=p;
+ while (p && *p && *p != c) p++;
+ char *end_value=p;
+ if (*p) p++;
+
+ char *name=(char *)malloc(end_name-this_name + 1 + end_value-begin_value + 1);
+ char *tmp=name;
+ memcpy(tmp,this_name,end_name-this_name); tmp+=end_name-this_name;
+ *tmp++='=';
+ memcpy(tmp,begin_value,end_value-begin_value); tmp+=end_value-begin_value;
+ *tmp=0;
+
+ if (strlen(name) < MAX_EDITABLE_METASTRING)
+ {
+ LRESULT a=SendDlgItemMessageA(hwndDlg,IDC_METADATA,LB_ADDSTRING,0,(LPARAM)name);
+ SendDlgItemMessageA(hwndDlg,IDC_METADATA,LB_SETITEMDATA,(WPARAM)a,(LPARAM)0);
+ free(name);
+ }
+ else
+ {
+ char buf[512] = {0};
+ WASABI_API_LNGSTRING_BUF(IDS_LARGE_DATA,buf,512);
+ lstrcpynA(buf+strlen(buf),name,128);
+ strcpy(buf+strlen(buf),"...");
+ LRESULT a=SendDlgItemMessageA(hwndDlg,IDC_METADATA,LB_ADDSTRING,0,(LPARAM)buf);
+ SendDlgItemMessageA(hwndDlg,IDC_METADATA,LB_SETITEMDATA,(WPARAM)a,(LPARAM)name);
+ }
+ }
+ }
+}
+
+void openNsv(HWND hwndDlg)
+{
+ closeNsv(hwndDlg);
+ g_hFile = CreateFileA(g_lastfile,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
+ SetDlgItemTextA(hwndDlg,IDC_FN,g_lastfile);
+
+ if (g_hFile == INVALID_HANDLE_VALUE) return;
+ enableControls(hwndDlg,1);
+ CheckDlgButton(hwndDlg,IDC_FASTUPD,BST_CHECKED);
+ // try to read existing tag.
+ nsv_InBS bs;
+ for (;;)
+ {
+ int ret=nsv_readheader(bs,&g_filehdr);
+ if (ret <= 0 || fillBs(g_hFile,bs,ret)) break;
+ }
+ nsv_Unpacketer unpacket;
+ char infobuf[256] = {0};
+ WASABI_API_LNGSTRING_BUF(IDS_NO_VALID_NSV_BITSTREAM_FOUND,infobuf,256);
+ for (;;)
+ {
+ int ret=unpacket.unpacket(bs);
+ if (ret < 0) break;
+ if (ret > 0 && fillBs(g_hFile,bs,ret)) break;
+ if (!ret)
+ {
+ char vfmt[32] = {0};
+ char afmt[5] = {0};
+
+ int fr=(int)(unpacket.getFrameRate()*100.0);
+
+ if (unpacket.getVidFmt()!=NSV_MAKETYPE('V','L','B',' ')) nsv_type_to_string(unpacket.getVidFmt(),vfmt);
+ else strcpy(vfmt,"Dolby AAC");
+
+ nsv_type_to_string(unpacket.getAudFmt(),afmt);
+
+ wsprintfA(infobuf,WASABI_API_LNGSTRING(IDS_VIDEO_X_AUDIO_X),
+ vfmt,unpacket.getWidth(),
+ unpacket.getHeight(),
+ fr/100,fr%100,afmt);
+ break;
+ }
+ }
+ SetDlgItemTextA(hwndDlg,IDC_BSINFO,infobuf);
+
+ g_oldtag_size=g_filehdr.header_size;
+ populateInfo(hwndDlg);
+ populateMetaData(hwndDlg);
+}
+
+static int g_metaitem_edit;
+
+INT_PTR CALLBACK EditProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
+{
+ if (uMsg == WM_INITDIALOG)
+ {
+ if (g_metaitem_edit>=0)
+ {
+ HWND ctl=GetDlgItem(GetParent(hwndDlg),IDC_METADATA);
+ int x= (int)SendMessage(ctl,LB_GETTEXTLEN,g_metaitem_edit,0);
+ if (x != LB_ERR)
+ {
+ char *t=(char*)malloc(x+1);
+ if (SendMessage(ctl,LB_GETTEXT,g_metaitem_edit,(LPARAM)t) != LB_ERR)
+ {
+ SetDlgItemTextA(hwndDlg,IDC_EDIT1,t);
+ }
+ free(t);
+ }
+ }
+ }
+ if (uMsg == WM_CLOSE) EndDialog(hwndDlg,0);
+ if (uMsg == WM_COMMAND)
+ {
+ if (LOWORD(wParam) == IDCANCEL) EndDialog(hwndDlg,0);
+ if (LOWORD(wParam) == IDOK)
+ {
+ int x= (int)SendDlgItemMessage(hwndDlg,IDC_EDIT1,WM_GETTEXTLENGTH,0,0);
+ char *t=(char*)malloc(x+3);
+ GetDlgItemTextA(hwndDlg,IDC_EDIT1,t,x+2);
+ if (!strstr(t,"="))
+ {
+ char title[32] = {0};
+ free(t);
+ MessageBoxA(hwndDlg,WASABI_API_LNGSTRING(IDS_METADATA_STRING_MUST_CONTAIN_ONE_EQUAL_SIGN),
+ WASABI_API_LNGSTRING_BUF(IDS_NSV_TAG_EDITOR,title,32),MB_OK|MB_ICONINFORMATION);
+ }
+ else
+ {
+ HWND ctl=GetDlgItem(GetParent(hwndDlg),IDC_METADATA);
+ LRESULT added;
+ if (g_metaitem_edit>=0)
+ {
+ SendMessage(ctl,LB_DELETESTRING,g_metaitem_edit,0);
+ added=SendMessage(ctl,LB_INSERTSTRING,g_metaitem_edit,(LPARAM)t);
+ }
+ else added=SendMessage(ctl,LB_ADDSTRING,0,(LPARAM)t);
+ SendMessage(ctl,LB_SETITEMDATA,(WPARAM)added,0);
+ free(t);
+ EndDialog(hwndDlg,1);
+ }
+ }
+ }
+ return 0;
+
+}
+
+int doTagWrite(HWND hwndDlg, int writeheader)
+{
+ nsv_OutBS bs;
+ if (writeheader)
+ {
+ g_filehdr.toc_alloc=0;
+ nsv_writeheader(bs,&g_filehdr,IsDlgButtonChecked(hwndDlg,IDC_FASTUPD)?g_oldtag_size:0);
+ }
+
+ int hdrlen;
+ char *hdr=(char*)bs.get(&hdrlen);
+
+ if (hdr && writeheader && hdrlen == (int)g_oldtag_size) // fast update of header
+ {
+ DWORD dw = 0;
+ SetFilePointer(g_hFile,0,NULL,SEEK_SET);
+ WriteFile(g_hFile,hdr,hdrlen,&dw,NULL);
+ }
+ else if (writeheader || g_oldtag_size)
+ {
+ if (hdr && writeheader && config_padtag>0) // enlarge header by config_padtag bytes =)
+ {
+ bs.clear();
+ g_filehdr.toc_alloc=0;
+ nsv_writeheader(bs,&g_filehdr,config_padtag+g_filehdr.header_size);
+ hdr=(char*)bs.get(&hdrlen); // update
+ }
+
+ char tmpfn[1024+8] = {0};
+ char tmpfn2[1024+8] = {0};
+ wsprintfA(tmpfn,"%s.new",g_lastfile);
+ wsprintfA(tmpfn2,"%s.old",g_lastfile);
+
+ HANDLE hTempFile=CreateFileA(tmpfn,GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL);
+ if (hTempFile == INVALID_HANDLE_VALUE)
+ {
+ char title[64] = {0};
+ MessageBoxA(hwndDlg,WASABI_API_LNGSTRING(IDS_CANNOT_CREATE_TEMPFILE_CANNOT_UPDATE_TAG),
+ WASABI_API_LNGSTRING_BUF(IDS_NSV_TAG_EDITOR_ERROR,title,64),
+ MB_ICONSTOP|MB_OK);
+ return 1;
+ }
+ SetFilePointer(g_hFile,g_oldtag_size,NULL,SEEK_SET);
+ if (hdrlen)
+ {
+ DWORD dw = 0;
+ if (!WriteFile(hTempFile,hdr,hdrlen,&dw,NULL) || (int)dw != hdrlen)
+ {
+ char title[64] = {0};
+ CloseHandle(hTempFile);
+ DeleteFileA(tmpfn);
+ MessageBoxA(hwndDlg,WASABI_API_LNGSTRING(IDS_ERROR_COPYING_SOURCE),
+ WASABI_API_LNGSTRING_BUF(IDS_NSV_TAG_EDITOR_ERROR,title,64),
+ MB_ICONSTOP|MB_OK);
+ return 1;
+ }
+ }
+
+ unsigned int bytes=0;
+ for (;;)
+ {
+ DWORD dw = 0;
+ char buf[8192] = {0};
+ BOOL r1=ReadFile(g_hFile,buf,sizeof(buf),&dw,NULL);
+ if (r1 && !dw) break;
+ DWORD dwout = 0;
+ if (!r1 || !WriteFile(hTempFile,buf,dw,&dwout,NULL) || dwout < dw)
+ {
+ char title[64] = {0};
+ CloseHandle(hTempFile);
+ DeleteFileA(tmpfn);
+ MessageBoxA(hwndDlg,WASABI_API_LNGSTRING(IDS_ERROR_COPYING_SOURCE),
+ WASABI_API_LNGSTRING_BUF(IDS_NSV_TAG_EDITOR_ERROR,title,64),
+ MB_ICONSTOP|MB_OK);
+ return 1;
+ }
+ bytes+=dwout;
+ wsprintfA(buf,WASABI_API_LNGSTRING(IDS_COPYING_X_BYTES),bytes);
+ SetDlgItemTextA(hwndDlg,IDC_TAG_LEN,buf);
+ }
+ if (GetFileSize(g_hFile,NULL)-g_oldtag_size != GetFileSize(hTempFile,NULL)-hdrlen)
+ {
+ char title[64] = {0};
+ CloseHandle(hTempFile);
+ DeleteFileA(tmpfn);
+ MessageBoxA(hwndDlg,WASABI_API_LNGSTRING(IDS_ERROR_SIZE_MISMATCH),
+ WASABI_API_LNGSTRING_BUF(IDS_NSV_TAG_EDITOR_ERROR,title,64),
+ MB_ICONSTOP|MB_OK);
+ return 1;
+ }
+ CloseHandle(g_hFile);
+ CloseHandle(hTempFile);
+ stopPlayback(g_lastfile);
+ if (!MoveFileA(g_lastfile,tmpfn2))
+ {
+ char title[64] = {0};
+ DeleteFileA(tmpfn);
+ restartPlayback();
+ MessageBoxA(hwndDlg,WASABI_API_LNGSTRING(IDS_ERROR_RENAMING_SOURCE),
+ WASABI_API_LNGSTRING_BUF(IDS_NSV_TAG_EDITOR_ERROR,title,64),
+ MB_ICONSTOP|MB_OK);
+ return 1;
+ }
+ if (!MoveFileA(tmpfn,g_lastfile))
+ {
+ char title[64] = {0};
+ MoveFileA(tmpfn2,g_lastfile);
+ DeleteFileA(tmpfn);
+ restartPlayback();
+ MessageBoxA(hwndDlg,WASABI_API_LNGSTRING(IDS_ERROR_RENAMING_NEW_FILE),
+ WASABI_API_LNGSTRING_BUF(IDS_NSV_TAG_EDITOR_ERROR,title,64),
+ MB_ICONSTOP|MB_OK);
+ return 1;
+ }
+ g_hFile = CreateFileA(g_lastfile,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
+
+ DeleteFileA(tmpfn2);
+ restartPlayback();
+ }
+ return 0;
+}
+
+void analyzeFile(HWND hwndDlg)
+{
+ unsigned int nframes=0;
+ nsv_Unpacketer unpack;
+ nsv_InBS bs;
+ GrowBuf framePos;
+ unsigned int lastPos=0;
+ SetFilePointer(g_hFile,g_oldtag_size,NULL,SEEK_SET);
+ for (;;)
+ {
+ int ret=unpack.unpacket(bs);
+ if (ret)
+ {
+ if (ret<0) break;
+ if (fillBs(g_hFile,bs,ret>0?ret:8)) unpack.setEof();
+ }
+ else
+ {
+ if (unpack.isSynchFrame())
+ {
+ framePos.add(&lastPos,4);
+ framePos.add(&nframes,4);
+ }
+ lastPos=SetFilePointer(g_hFile,0,NULL,SEEK_CUR) - (unsigned int)(bs.avail()+7)/8 - g_oldtag_size;
+ bs.compact();
+ nframes++;
+ char buf[128] = {0};
+ wsprintfA(buf,WASABI_API_LNGSTRING(IDS_READING_X_FRAMES),nframes);
+ SetDlgItemTextA(hwndDlg,IDC_TAG_LEN,buf);
+ }
+ }
+ if (unpack.isValid() && nframes)
+ {
+ g_filehdr.file_lenbytes=lastPos;
+ g_filehdr.file_lenms = (int) (nframes * 1000.0 / unpack.getFrameRate());
+ if (IsDlgButtonChecked(hwndDlg,IDC_TOC))
+ {
+ BOOL t;
+ DWORD d=GetDlgItemInt(hwndDlg,IDC_SETTOCSIZE,&t,FALSE);
+ if (d && t)
+ {
+ g_filehdr.toc_size=d;
+ g_filehdr.toc_alloc=0;
+ free(g_filehdr.toc);
+
+ unsigned int x;
+ unsigned int *in=(unsigned int *)framePos.get();
+ unsigned int tf=(unsigned int)framePos.getlen()/8;
+ g_filehdr.toc=(unsigned int *)malloc(g_filehdr.toc_size * sizeof(unsigned int) * 2);
+ g_filehdr.toc_ex=g_filehdr.toc + g_filehdr.toc_size;
+
+ if (tf < g_filehdr.toc_size) // we can store all keyframes without dropping any
+ {
+ g_filehdr.toc_size=tf;
+ for (x = 0; x < tf; x ++)
+ {
+ g_filehdr.toc[x]=in[x*2];
+ g_filehdr.toc_ex[x]=in[x*2+1];
+ }
+ }
+ else // drop keyframes to fit
+ {
+ double pos=0.0;
+ double dpos = (double) tf / (double) g_filehdr.toc_size;
+ for (x = 0; x < g_filehdr.toc_size; x ++)
+ {
+ unsigned int ipos=(unsigned int)pos;
+ if (ipos >= tf)
+ {
+ g_filehdr.toc_size=x;
+ break;
+ }
+ g_filehdr.toc[x]=in[ipos*2];
+ g_filehdr.toc_ex[x]=in[ipos*2+1];
+ pos+=dpos;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ char title[64] = {0};
+ MessageBoxA(hwndDlg,WASABI_API_LNGSTRING(IDS_NSV_ANALYSIS_FAILED),
+ WASABI_API_LNGSTRING_BUF(IDS_NSV_TAG_EDITOR_ERROR,title,64), MB_OK);
+ }
+}
+
+INT_PTR CALLBACK StreamProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
+{
+ extern int g_streaminfobuf_used;
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ SetDlgItemTextA(hwndDlg,IDC_FN,g_lastfile);
+ g_streaminfobuf_used=1;
+ SetTimer(hwndDlg,1,1000,NULL);
+ SendMessage(hwndDlg,WM_TIMER,1,0);
+ return TRUE;
+
+ case WM_CLOSE:
+ g_streaminfobuf_used=0;
+ KillTimer(hwndDlg,1);
+ EndDialog(hwndDlg,1);
+ return 0;
+
+ case WM_TIMER:
+ if (wParam == 1)
+ {
+ extern char lastfn[];
+ extern char g_streaminfobuf[];
+ extern CRITICAL_SECTION g_decoder_cs;
+ if (!lstrcmpiA(g_lastfile,lastfn) && g_streaminfobuf[0])
+ {
+ int start = -1, end = 0;
+ EnterCriticalSection(&g_decoder_cs);
+ SendDlgItemMessage(hwndDlg, IDC_INFO, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
+ SetDlgItemTextA(hwndDlg,IDC_INFO,g_streaminfobuf);
+ SendDlgItemMessage(hwndDlg, IDC_INFO, EM_SETSEL, start, end);
+ LeaveCriticalSection(&g_decoder_cs);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_INFO),1);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_INFOBORDER),1);
+ }
+ else
+ {
+ SetDlgItemTextA(hwndDlg,IDC_INFO,"");
+ EnableWindow(GetDlgItem(hwndDlg,IDC_INFO),0);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_INFOBORDER),0);
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ case IDCANCEL:
+ g_streaminfobuf_used=0;
+ KillTimer(hwndDlg,1);
+ EndDialog(hwndDlg,1);
+ return 0;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+INT_PTR CALLBACK MainProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ openNsv(hwndDlg);
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDCANCEL:
+ closeNsv(hwndDlg);
+ EndDialog(hwndDlg,1);
+ return 0;
+
+ case IDOK:
+ case IDC_REMTAG:
+ if (!doTagWrite(hwndDlg,LOWORD(wParam) == IDOK))
+ {
+ closeNsv(hwndDlg);
+ lastextfn[0]=0;
+ EndDialog(hwndDlg,0);
+ }
+ else lastextfn[0]=0;
+ return 0;
+
+ case IDC_ANALYZE:
+ analyzeFile(hwndDlg);
+ populateInfo(hwndDlg);
+ makeNewHeaderInfo(hwndDlg);
+ return 0;
+
+ case IDC_ADD:
+ g_metaitem_edit=-1;
+ if (WASABI_API_DIALOGBOXW(IDD_DIALOG2,hwndDlg,EditProc))
+ {
+ updateMetaData(hwndDlg);
+ populateMetaData(hwndDlg);
+ makeNewHeaderInfo(hwndDlg);
+ }
+ return 0;
+
+ case IDC_METADATA:
+ if (HIWORD(wParam) != LBN_DBLCLK) return 0;
+
+ case IDC_EDIT:
+ {
+ DWORD res=(DWORD)SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_GETCURSEL,0,0);
+ if (res != LB_ERR)
+ {
+ int ptr= (int)SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_GETITEMDATA,res,0);
+ if (ptr)
+ {
+ char title[32] = {0};
+ MessageBoxA(hwndDlg,WASABI_API_LNGSTRING(IDS_METADATA_ITEM_CONTAINS_LARGE_AMOUNT_DATA),
+ WASABI_API_LNGSTRING_BUF(IDS_NSV_TAG_EDITOR,title,32), MB_OK);
+ }
+ else
+ {
+ g_metaitem_edit=res;
+ if (WASABI_API_DIALOGBOXW(IDD_DIALOG2,hwndDlg,EditProc))
+ {
+ updateMetaData(hwndDlg);
+ populateMetaData(hwndDlg);
+ makeNewHeaderInfo(hwndDlg);
+ SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_SETCURSEL,(WPARAM)res,0);
+ }
+ }
+ }
+ }
+ return 0;
+
+ case IDC_REM:
+ {
+ DWORD res=(DWORD)SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_GETCURSEL,0,0);
+ if (res != LB_ERR)
+ {
+ void *ptr=(void *)SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_GETITEMDATA,res,0);
+ if (ptr) free(ptr);
+ SendDlgItemMessage(hwndDlg,IDC_METADATA,LB_DELETESTRING,res,0);
+ updateMetaData(hwndDlg);
+ populateMetaData(hwndDlg);
+ makeNewHeaderInfo(hwndDlg);
+ }
+ }
+ return 0;
+
+ case IDC_TOC:
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ int ch=!!IsDlgButtonChecked(hwndDlg,IDC_TOC);
+ if (ch)
+ {
+ if (g_toc_save)
+ {
+ g_filehdr.toc=g_toc_save;
+ g_filehdr.toc_ex=g_toc_save_ex;
+ g_filehdr.toc_size=g_toc_savesize;
+ g_toc_save=0;
+ g_toc_save_ex=0;
+ g_toc_savesize=0;
+ makeNewHeaderInfo(hwndDlg);
+ }
+ }
+ else
+ {
+ if (!g_toc_save)
+ {
+ g_toc_save=g_filehdr.toc;
+ g_toc_save_ex=g_filehdr.toc_ex;
+ g_toc_savesize=g_filehdr.toc_size;
+ g_filehdr.toc=0;
+ g_filehdr.toc_size=0;
+ g_filehdr.toc_ex=0;
+ makeNewHeaderInfo(hwndDlg);
+ }
+ }
+ }
+ return 0;
+ }
+ return 0;
+
+ case WM_CLOSE:
+ closeNsv(hwndDlg);
+ EndDialog(hwndDlg,1);
+ return 0;
+ }
+ return 0;
+}
+
+int infoDlg(const char *fn, HWND hwnd)
+{
+ g_lastfile=fn;
+ if (strstr(fn,"://")) return (int)WASABI_API_DIALOGBOXW(IDD_DIALOG4,hwnd,StreamProc);
+ else return (int)WASABI_API_DIALOGBOXW(IDD_DIALOG3,hwnd,MainProc);
+} \ No newline at end of file
diff --git a/Src/Plugins/Input/in_nsv/nsv_logo.bmp b/Src/Plugins/Input/in_nsv/nsv_logo.bmp
new file mode 100644
index 00000000..bcdeaf9d
--- /dev/null
+++ b/Src/Plugins/Input/in_nsv/nsv_logo.bmp
Binary files differ
diff --git a/Src/Plugins/Input/in_nsv/proxydt.h b/Src/Plugins/Input/in_nsv/proxydt.h
new file mode 100644
index 00000000..8802075d
--- /dev/null
+++ b/Src/Plugins/Input/in_nsv/proxydt.h
@@ -0,0 +1,16 @@
+// proxydt.h
+#ifndef PROXYDT_H
+#define PROXYDT_H
+
+#include <string>
+#include <stdio.h>
+#include <atlbase.h>
+
+char* detectBrowserProxy();
+
+char* DetectIEProxy();
+
+char* DetectNS4Proxy();
+char* DetectNS6Proxy();
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Input/in_nsv/resource.h b/Src/Plugins/Input/in_nsv/resource.h
new file mode 100644
index 00000000..108d7bbc
--- /dev/null
+++ b/Src/Plugins/Input/in_nsv/resource.h
@@ -0,0 +1,86 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by res.rc
+//
+#define IDS_TOTAL_TAG_SIZE 1
+#define IDS_NO_METADATA 2
+#define IDS_TOTAL_TAG_SIZE_X_BYTES 3
+#define IDS_NO_TAG 4
+#define IDS_ERROR_WRITING_STRING_TO_TAG 5
+#define IDS_NSV_TAG_EDITOR_ERROR 6
+#define IDS_LARGE_DATA 7
+#define IDS_NO_VALID_NSV_BITSTREAM_FOUND 8
+#define IDS_VIDEO_X_AUDIO_X 9
+#define IDS_METADATA_STRING_MUST_CONTAIN_ONE_EQUAL_SIGN 10
+#define IDS_NSV_TAG_EDITOR 11
+#define IDS_CANNOT_CREATE_TEMPFILE_CANNOT_UPDATE_TAG 12
+#define IDS_ERROR_COPYING_SOURCE 13
+#define IDS_COPYING_X_BYTES 14
+#define IDS_ERROR_SIZE_MISMATCH 15
+#define IDS_ERROR_RENAMING_SOURCE 16
+#define IDS_ERROR_RENAMING_NEW_FILE 17
+#define IDS_READING_X_FRAMES 18
+#define IDS_NSV_ANALYSIS_FAILED 19
+#define IDS_METADATA_ITEM_CONTAINS_LARGE_AMOUNT_DATA 20
+#define IDS_CONNECTING 21
+#define IDS_OPENING 22
+#define IDS_BUFFER_X 23
+#define IDS_SERVER 24
+#define IDS_CONTENT_TYPE 25
+#define IDS_CONTENT_LENGTH 26
+#define IDS_STREAM_NAME 27
+#define IDS_NSA_NSV_FILE 28
+#define IDS_KBPS 29
+#define IDS_FPS 31
+#define IDS_FAMILY_STRING_NSV 32
+#define IDS_FAMILY_STRING_NSA 33
+#define IDD_DIALOG1 101
+#define IDB_BITMAP1 102
+#define IDD_DIALOG2 103
+#define IDD_DIALOG3 104
+#define IDD_DIALOG4 105
+#define IDD_ABOUT 106
+#define IDC_ANALYZE 1000
+#define IDC_VERSION 1000
+#define IDC_BUF1 1001
+#define IDC_LENBYTES 1001
+#define IDC_BUF2 1002
+#define IDC_LENMS 1002
+#define IDC_BUF3 1003
+#define IDC_TOC 1003
+#define IDC_OFFS 1004
+#define IDC_TOCSIZE 1004
+#define IDC_SETTOCSIZE 1005
+#define IDC_TAGPAD 1005
+#define IDC_METADATA_LEN 1006
+#define IDC_BUF4 1006
+#define IDC_TAG_LEN 1007
+#define IDC_BUF5 1007
+#define IDC_METADATA 1008
+#define IDC_BUF6 1008
+#define IDC_ADD 1009
+#define IDC_REM 1010
+#define IDC_EDIT 1011
+#define IDC_REMTAG 1012
+#define IDC_FASTUPD 1013
+#define IDC_EDIT1 1014
+#define IDC_AVGBITRATE 1014
+#define IDC_FN 1015
+#define IDC_INFO 1016
+#define IDC_BSINFO 1016
+#define IDC_INFOBORDER 1017
+#define IDC_CHECK1 1018
+#define IDC_CHECK3 1019
+#define IDC_CHECK2 1020
+#define IDS_NULLSOFT_NSV_DECODER 65534
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 107
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1019
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/Plugins/Input/in_nsv/version.rc2 b/Src/Plugins/Input/in_nsv/version.rc2
new file mode 100644
index 00000000..51e28c40
--- /dev/null
+++ b/Src/Plugins/Input/in_nsv/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,76,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,76,0,0"
+ VALUE "InternalName", "Nullsoft NSV Decoder"
+ VALUE "LegalCopyright", "Copyright © 2003-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "in_nsv.dll"
+ VALUE "ProductName", "Winamp"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END