1 /*
2   LICENSE
3   -------
4 Copyright 2005-2013 Nullsoft, Inc.
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without modification,
8 are permitted provided that the following conditions are met:
9 
10   * Redistributions of source code must retain the above copyright notice,
11     this list of conditions and the following disclaimer.
12 
13   * Redistributions in binary form must reproduce the above copyright notice,
14     this list of conditions and the following disclaimer in the documentation
15     and/or other materials provided with the distribution.
16 
17   * Neither the name of Nullsoft nor the names of its contributors may be used to
18     endorse or promote products derived from this software without specific prior written permission.
19 
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
21 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
22 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
26 IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
27 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 
30 #include "api.h"
31 #include <windows.h>
32 #include "vis.h"
33 #include "plugin.h"
34 #include "defines.h"
35 #include "resource.h"
36 #include "utility.h"
37 
38 CPlugin  g_plugin;
39 _locale_t g_use_C_locale = 0;
40 char keyMappings[8];
41 bool g_bFullyExited = true;
42 
43 // wasabi based services for localisation support
44 api_service *WASABI_API_SVC = 0;
45 api_language *WASABI_API_LNG = 0;
46 api_application *WASABI_API_APP = 0;
47 api_syscb *WASABI_API_SYSCB = 0;
48 HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
49 
50 void config(struct winampVisModule *this_mod); // configuration dialog
51 int init(struct winampVisModule *this_mod);       // initialization for module
52 int render1(struct winampVisModule *this_mod);  // rendering for module 1
53 void quit(struct winampVisModule *this_mod);   // deinitialization for module
54 
55 // our only plugin module in this plugin:
56 winampVisModule mod1 =
57 {
58     MODULEDESC,
59     NULL,	// hwndParent
60     NULL,	// hDllInstance
61     0,		// sRate
62     0,		// nCh
63     0,		// latencyMS - tells winamp how much in advance you want the audio data,
64 			//             in ms.
65     10,		// delayMS - if winamp tells the plugin to render a frame and it takes
66 			//           less than this # of milliseconds, winamp will sleep (go idle)
67             //           for the remainder.  In effect, this limits the framerate of
68             //           the plugin.  A value of 10 would cause a fps limit of ~100.
69             //           Derivation: (1000 ms/sec) / (10 ms/frame) = 100 fps.
70     0,		// spectrumNch
71     2,		// waveformNch
72     { 0, },	// spectrumData
73     { 0, },	// waveformData
74     config,
75     init,
76     render1,
77     quit
78 };
79 
80 // getmodule routine from the main header. Returns NULL if an invalid module was requested,
81 // otherwise returns either mod1, mod2 or mod3 depending on 'which'.
getModule(int which)82 winampVisModule *getModule(int which)
83 {
84     switch (which)
85     {
86         case 0: return &mod1;
87         //case 1: return &mod2;
88         //case 2: return &mod3;
89         default: return NULL;
90     }
91 }
92 
93 // Module header, includes version, description, and address of the module retriever function
94 winampVisHeader hdr = { VIS_HDRVER, DLLDESC, getModule };
95 
96 // use this to get our own HINSTANCE since overriding DllMain(..) causes instant crashes (should see why)
GetMyInstance()97 static HINSTANCE GetMyInstance()
98 {
99 	MEMORY_BASIC_INFORMATION mbi = {0};
100 	if(VirtualQuery(GetMyInstance, &mbi, sizeof(mbi)))
101 		return (HINSTANCE)mbi.AllocationBase;
102 	return NULL;
103 }
104 
105 // this is the only exported symbol. returns our main header.
106 // if you are compiling C++, the extern "C" { is necessary, so we just #ifdef it
107 #ifdef __cplusplus
108 extern "C" {
109 #endif
winampVisGetHeader(HWND hwndParent)110 	__declspec( dllexport ) winampVisHeader *winampVisGetHeader(HWND hwndParent)
111 	{
112 		if(!WASABI_API_LNG_HINST)
113 		{
114 			// loader so that we can get the localisation service api for use
115 			WASABI_API_SVC = (api_service*)SendMessage(hwndParent, WM_WA_IPC, 0, IPC_GET_API_SERVICE);
116 			if (WASABI_API_SVC == (api_service*)1) WASABI_API_SVC = NULL;
117 
118 			waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID);
119 			if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
120 
121 			sf = WASABI_API_SVC->service_getServiceByGuid(applicationApiServiceGuid);
122 			if (sf) WASABI_API_APP = reinterpret_cast<api_application*>(sf->getInterface());
123 
124 			sf = WASABI_API_SVC->service_getServiceByGuid(syscbApiServiceGuid);
125 			if (sf) WASABI_API_SYSCB = reinterpret_cast<api_syscb*>(sf->getInterface());
126 
127 			// need to have this initialised before we try to do anything with localisation features
128 			WASABI_API_START_LANG(GetMyInstance(),VisMilkdropLangGUID);
129 
130 			/* added for v2.25 as a quick work around to allow partial
131 			/* keyboard mappings (mainly coming from de-de requirements)
132 			** [yY][Y][yY][zZ]
133 			**  1   2   3   4
134 			**
135 			** 1 - does yes for the 3 different prompt types
136 			** 2 - does Ctrl+Y for stopping display of custom message of song title
137 			** 3 - something for preset editing (not 100% sure what)
138 			** 4 - used for the previous track sent to Winamp
139 			*/
140 			WASABI_API_LNGSTRING_BUF(IDS_KEY_MAPPINGS, keyMappings, 8);
141 
142 			// as we're under a different thread we need to set the locale
143 			//WASABI_API_LNG->UseUserNumericLocale();
144 			g_use_C_locale = WASABI_API_LNG->Get_C_NumericLocale();
145 		}
146 
147 		return &hdr;
148 	}
149 #ifdef __cplusplus
150 }
151 #endif
152 
WaitUntilPluginFinished(HWND hWndWinamp)153 bool WaitUntilPluginFinished(HWND hWndWinamp)
154 {
155     int slept = 0;
156     while (!g_bFullyExited && slept < 1000)
157     {
158         Sleep(50);
159         slept += 50;
160     }
161 
162     if (!g_bFullyExited)
163     {
164 		wchar_t title[64];
165         MessageBoxW(hWndWinamp, WASABI_API_LNGSTRINGW(IDS_ERROR_THE_PLUGIN_IS_ALREADY_RUNNING),
166 				    WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
167 				    MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
168         return false;
169     }
170 
171     return true;
172 }
173 
GetDialogBoxParent(HWND winamp)174 HWND GetDialogBoxParent(HWND winamp)
175 {
176 	HWND parent = (HWND)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETDIALOGBOXPARENT);
177 	if (!parent || parent == (HWND)1)
178 		return winamp;
179 	return parent;
180 }
181 
182 // configuration. Passed this_mod, as a "this" parameter. Allows you to make one configuration
183 // function that shares code for all your modules (you don't HAVE to use it though, you can make
184 // config1(), config2(), etc...)
config(struct winampVisModule * this_mod)185 void config(struct winampVisModule *this_mod)
186 {
187     if (!g_bFullyExited)
188     {
189       g_plugin.OnAltK();
190       return;
191     }
192 
193     g_bFullyExited = false;
194     g_plugin.PluginPreInitialize(this_mod->hwndParent, this_mod->hDllInstance);
195     WASABI_API_DIALOGBOXPARAMW(IDD_CONFIG, GetDialogBoxParent(this_mod->hwndParent), g_plugin.ConfigDialogProc, (LPARAM)&g_plugin);
196     g_bFullyExited = true;
197 }
198 
199 int (*warand)(void) = 0;
200 
fallback_rand_fn(void)201 int fallback_rand_fn(void) {
202   return rand();
203 }
204 
205 // initialization. Registers our window class, creates our window, etc. Again, this one works for
206 // both modules, but you could make init1() and init2()...
207 // returns 0 on success, 1 on failure.
init(struct winampVisModule * this_mod)208 int init(struct winampVisModule *this_mod)
209 {
210     DWORD version = GetWinampVersion(mod1.hwndParent);
211 
212 	if (!warand)
213     {
214 		warand = (int (*)(void))SendMessage(this_mod->hwndParent, WM_WA_IPC, 0, IPC_GET_RANDFUNC);
215         if ((size_t)warand <= 1)
216         {
217             warand = fallback_rand_fn;
218         }
219     }
220 
221     if (!WaitUntilPluginFinished(this_mod->hwndParent))
222     {
223         return 1;
224     }
225 
226     if (GetWinampVersion(mod1.hwndParent) < 0x4000)
227     {
228         // query winamp for its playback state
229         LRESULT ret = SendMessage(this_mod->hwndParent, WM_USER, 0, 104);
230         // ret=1: playing, ret=3: paused, other: stopped
231 
232         if (ret != 1)
233         {
234 			wchar_t title[64];
235             MessageBoxW(this_mod->hwndParent, WASABI_API_LNGSTRINGW(IDS_THIS_PLUGIN_NEEDS_MUSIC_TO_RUN),
236 					   WASABI_API_LNGSTRINGW_BUF(IDS_NO_MUSIC_PLAYING, title, 64),
237 					   MB_OK|MB_SETFOREGROUND|MB_TOPMOST|MB_TASKMODAL );
238             return 1;  // failure
239         }
240     }
241 
242     g_bFullyExited = false;
243 
244     if (!g_plugin.PluginPreInitialize(this_mod->hwndParent, this_mod->hDllInstance))
245     {
246         g_plugin.PluginQuit();
247         g_bFullyExited = true;
248         return 1;
249     }
250 
251     if (!g_plugin.PluginInitialize())
252     {
253         g_plugin.PluginQuit();
254         g_bFullyExited = true;
255         return 1;
256     }
257 
258     return 0;    // success
259 }
260 
261 // render function for oscilliscope. Returns 0 if successful, 1 if visualization should end.
render1(struct winampVisModule * this_mod)262 int render1(struct winampVisModule *this_mod)
263 {
264 	if (g_plugin.PluginRender(this_mod->waveformData[0], this_mod->waveformData[1]))
265 		return 0;    // ok
266 	else
267 		return 1;    // failed
268 }
269 
270 // cleanup (opposite of init()). Should destroy the window, unregister the window class, etc.
quit(struct winampVisModule * this_mod)271 void quit(struct winampVisModule *this_mod)
272 {
273 	g_plugin.PluginQuit();
274 	g_bFullyExited = true;
275 }