From 636746490e7ff2e5b7c6a71b656c81c21b441bf3 Mon Sep 17 00:00:00 2001 From: John Peterson Date: Sat, 20 Jun 2009 10:58:15 +0000 Subject: [PATCH] MusicMod: Prepare to fix the MusicMod build git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@3505 8ced0084-cf51-0410-be5f-012b33b47a6e --- Externals/MusicMod/Common/Common.vcproj | 342 ++++ Externals/MusicMod/Data/win32/Plainamp.ini | 40 + .../Data/win32/PluginsMusic/in_vgmstream.dll | Bin 0 -> 331776 bytes .../Data/win32/PluginsMusic/out_ds.dll | Bin 0 -> 40960 bytes .../Data/win32/PluginsMusic/out_wave_gpl.dll | Bin 0 -> 14336 bytes Externals/MusicMod/Data/x64/Plainamp.ini | 29 + .../Data/x64/PluginsMusic/in_vgmstream.dll | Bin 0 -> 177152 bytes .../Data/x64/PluginsMusic/out_wave_gpl.dll | Bin 0 -> 55296 bytes Externals/MusicMod/Main/Main.vcproj | 417 +++++ Externals/MusicMod/Main/Src/Frame.cpp | 466 ++++++ Externals/MusicMod/Main/Src/Main.cpp | 342 ++++ Externals/MusicMod/Main/Src/Main.h | 31 + Externals/MusicMod/Player/Lib/fftw3.lib | Bin 0 -> 31296 bytes Externals/MusicMod/Player/Lib/libzlib1.lib | Bin 0 -> 37266 bytes Externals/MusicMod/Player/Player.vcproj | 925 +++++++++++ .../MusicMod/Player/Src/AddDirectory.cpp | 364 ++++ Externals/MusicMod/Player/Src/AddDirectory.h | 26 + Externals/MusicMod/Player/Src/AddFiles.cpp | 181 ++ Externals/MusicMod/Player/Src/AddFiles.h | 26 + Externals/MusicMod/Player/Src/Config.cpp | 687 ++++++++ Externals/MusicMod/Player/Src/Config.h | 239 +++ Externals/MusicMod/Player/Src/Console.cpp | 228 +++ Externals/MusicMod/Player/Src/Console.h | 40 + Externals/MusicMod/Player/Src/DspModule.cpp | 136 ++ Externals/MusicMod/Player/Src/DspModule.h | 67 + Externals/MusicMod/Player/Src/DspPlugin.cpp | 186 +++ Externals/MusicMod/Player/Src/DspPlugin.h | 69 + .../MusicMod/Player/Src/Emabox/Emabox.cpp | 455 +++++ Externals/MusicMod/Player/Src/Emabox/Emabox.h | 141 ++ .../MusicMod/Player/Src/Emabox/EmaboxConfig.h | 32 + Externals/MusicMod/Player/Src/Embed.cpp | 233 +++ Externals/MusicMod/Player/Src/Embed.h | 36 + Externals/MusicMod/Player/Src/Font.cpp | 83 + Externals/MusicMod/Player/Src/Font.h | 34 + Externals/MusicMod/Player/Src/GenPlugin.cpp | 199 +++ Externals/MusicMod/Player/Src/GenPlugin.h | 64 + Externals/MusicMod/Player/Src/Global.h | 151 ++ Externals/MusicMod/Player/Src/GlobalVersion.h | 28 + Externals/MusicMod/Player/Src/Input.cpp | 483 ++++++ Externals/MusicMod/Player/Src/Input.h | 28 + Externals/MusicMod/Player/Src/InputPlugin.cpp | 389 +++++ Externals/MusicMod/Player/Src/InputPlugin.h | 96 ++ Externals/MusicMod/Player/Src/Lock.cpp | 16 + Externals/MusicMod/Player/Src/Lock.h | 111 ++ Externals/MusicMod/Player/Src/Main.cpp | 816 +++++++++ Externals/MusicMod/Player/Src/Main.h | 51 + Externals/MusicMod/Player/Src/Output.cpp | 382 +++++ Externals/MusicMod/Player/Src/Output.h | 27 + .../MusicMod/Player/Src/OutputPlugin.cpp | 316 ++++ Externals/MusicMod/Player/Src/OutputPlugin.h | 87 + Externals/MusicMod/Player/Src/Path.cpp | 243 +++ Externals/MusicMod/Player/Src/Path.h | 31 + Externals/MusicMod/Player/Src/Playback.cpp | 804 +++++++++ Externals/MusicMod/Player/Src/Playback.h | 106 ++ Externals/MusicMod/Player/Src/PlaybackEq.cpp | 223 +++ .../MusicMod/Player/Src/PlaybackOrder.cpp | 205 +++ Externals/MusicMod/Player/Src/Player.cpp | 380 +++++ .../MusicMod/Player/Src/PlayerExport.cpp | 181 ++ Externals/MusicMod/Player/Src/PlayerExport.h | 49 + Externals/MusicMod/Player/Src/Playlist.cpp | 1383 ++++++++++++++++ Externals/MusicMod/Player/Src/Playlist.h | 65 + .../MusicMod/Player/Src/PlaylistControler.cpp | 467 ++++++ .../MusicMod/Player/Src/PlaylistControler.h | 65 + Externals/MusicMod/Player/Src/PlaylistModel.h | 122 ++ .../MusicMod/Player/Src/PlaylistView.cpp | 782 +++++++++ Externals/MusicMod/Player/Src/PlaylistView.h | 27 + Externals/MusicMod/Player/Src/Plugin.cpp | 63 + Externals/MusicMod/Player/Src/Plugin.h | 142 ++ .../MusicMod/Player/Src/PluginManager.cpp | 1143 +++++++++++++ Externals/MusicMod/Player/Src/PluginManager.h | 42 + Externals/MusicMod/Player/Src/Prefs.cpp | 565 +++++++ Externals/MusicMod/Player/Src/Prefs.h | 36 + Externals/MusicMod/Player/Src/Rebar.cpp | 1457 +++++++++++++++++ Externals/MusicMod/Player/Src/Rebar.h | 40 + .../MusicMod/Player/Src/Resources/Buttons.bmp | Bin 0 -> 734 bytes .../Player/Src/Resources/Plainamp.ico | Bin 0 -> 38638 bytes .../MusicMod/Player/Src/Resources/resrc1.aps | Bin 0 -> 73968 bytes .../MusicMod/Player/Src/Resources/resrc1.h | 17 + .../MusicMod/Player/Src/Resources/resrc1.rc | 131 ++ Externals/MusicMod/Player/Src/Status.cpp | 80 + Externals/MusicMod/Player/Src/Status.h | 33 + Externals/MusicMod/Player/Src/Timer.cpp | 126 ++ Externals/MusicMod/Player/Src/Unicode.cpp | 77 + Externals/MusicMod/Player/Src/Unicode.h | 29 + Externals/MusicMod/Player/Src/Util.cpp | 48 + Externals/MusicMod/Player/Src/Util.h | 26 + Externals/MusicMod/Player/Src/VisCache.cpp | 303 ++++ Externals/MusicMod/Player/Src/VisCache.h | 50 + Externals/MusicMod/Player/Src/VisModule.cpp | 358 ++++ Externals/MusicMod/Player/Src/VisModule.h | 71 + Externals/MusicMod/Player/Src/VisPlugin.cpp | 189 +++ Externals/MusicMod/Player/Src/VisPlugin.h | 68 + Externals/MusicMod/Player/Src/Winamp.cpp | 996 +++++++++++ Externals/MusicMod/Player/Src/Winamp.h | 26 + Externals/MusicMod/Player/Src/Winamp/Dsp.h | 47 + Externals/MusicMod/Player/Src/Winamp/Gen.h | 28 + Externals/MusicMod/Player/Src/Winamp/In2.h | 119 ++ Externals/MusicMod/Player/Src/Winamp/Out.h | 69 + Externals/MusicMod/Player/Src/Winamp/Vis.h | 57 + Externals/MusicMod/Player/Src/Winamp/wa_ipc.h | 1103 +++++++++++++ .../MusicMod/Player/Src/Winamp/wa_msgids.h | 297 ++++ Externals/MusicMod/Player/Src/Winmain.cpp | 236 +++ Externals/MusicMod/Player/Src/afxres.h | 823 ++++++++++ Externals/MusicMod/Player/Src/fftw3/fftw3.h | 276 ++++ .../Player/Src/vis_plainbar/vis_plainbar.cpp | 259 +++ Externals/MusicMod/Player/Src/zlib/zconf.h | 332 ++++ Externals/MusicMod/Player/Src/zlib/zlib.h | 1357 +++++++++++++++ Externals/MusicMod/TestPlayer/Src/Player.cpp | 37 + .../MusicMod/TestPlayer/TestPlayer.vcproj | 403 +++++ Source/MusicMod.sln | 498 ++++++ 110 files changed, 26289 insertions(+) create mode 100644 Externals/MusicMod/Common/Common.vcproj create mode 100644 Externals/MusicMod/Data/win32/Plainamp.ini create mode 100644 Externals/MusicMod/Data/win32/PluginsMusic/in_vgmstream.dll create mode 100644 Externals/MusicMod/Data/win32/PluginsMusic/out_ds.dll create mode 100644 Externals/MusicMod/Data/win32/PluginsMusic/out_wave_gpl.dll create mode 100644 Externals/MusicMod/Data/x64/Plainamp.ini create mode 100644 Externals/MusicMod/Data/x64/PluginsMusic/in_vgmstream.dll create mode 100644 Externals/MusicMod/Data/x64/PluginsMusic/out_wave_gpl.dll create mode 100644 Externals/MusicMod/Main/Main.vcproj create mode 100644 Externals/MusicMod/Main/Src/Frame.cpp create mode 100644 Externals/MusicMod/Main/Src/Main.cpp create mode 100644 Externals/MusicMod/Main/Src/Main.h create mode 100644 Externals/MusicMod/Player/Lib/fftw3.lib create mode 100644 Externals/MusicMod/Player/Lib/libzlib1.lib create mode 100644 Externals/MusicMod/Player/Player.vcproj create mode 100644 Externals/MusicMod/Player/Src/AddDirectory.cpp create mode 100644 Externals/MusicMod/Player/Src/AddDirectory.h create mode 100644 Externals/MusicMod/Player/Src/AddFiles.cpp create mode 100644 Externals/MusicMod/Player/Src/AddFiles.h create mode 100644 Externals/MusicMod/Player/Src/Config.cpp create mode 100644 Externals/MusicMod/Player/Src/Config.h create mode 100644 Externals/MusicMod/Player/Src/Console.cpp create mode 100644 Externals/MusicMod/Player/Src/Console.h create mode 100644 Externals/MusicMod/Player/Src/DspModule.cpp create mode 100644 Externals/MusicMod/Player/Src/DspModule.h create mode 100644 Externals/MusicMod/Player/Src/DspPlugin.cpp create mode 100644 Externals/MusicMod/Player/Src/DspPlugin.h create mode 100644 Externals/MusicMod/Player/Src/Emabox/Emabox.cpp create mode 100644 Externals/MusicMod/Player/Src/Emabox/Emabox.h create mode 100644 Externals/MusicMod/Player/Src/Emabox/EmaboxConfig.h create mode 100644 Externals/MusicMod/Player/Src/Embed.cpp create mode 100644 Externals/MusicMod/Player/Src/Embed.h create mode 100644 Externals/MusicMod/Player/Src/Font.cpp create mode 100644 Externals/MusicMod/Player/Src/Font.h create mode 100644 Externals/MusicMod/Player/Src/GenPlugin.cpp create mode 100644 Externals/MusicMod/Player/Src/GenPlugin.h create mode 100644 Externals/MusicMod/Player/Src/Global.h create mode 100644 Externals/MusicMod/Player/Src/GlobalVersion.h create mode 100644 Externals/MusicMod/Player/Src/Input.cpp create mode 100644 Externals/MusicMod/Player/Src/Input.h create mode 100644 Externals/MusicMod/Player/Src/InputPlugin.cpp create mode 100644 Externals/MusicMod/Player/Src/InputPlugin.h create mode 100644 Externals/MusicMod/Player/Src/Lock.cpp create mode 100644 Externals/MusicMod/Player/Src/Lock.h create mode 100644 Externals/MusicMod/Player/Src/Main.cpp create mode 100644 Externals/MusicMod/Player/Src/Main.h create mode 100644 Externals/MusicMod/Player/Src/Output.cpp create mode 100644 Externals/MusicMod/Player/Src/Output.h create mode 100644 Externals/MusicMod/Player/Src/OutputPlugin.cpp create mode 100644 Externals/MusicMod/Player/Src/OutputPlugin.h create mode 100644 Externals/MusicMod/Player/Src/Path.cpp create mode 100644 Externals/MusicMod/Player/Src/Path.h create mode 100644 Externals/MusicMod/Player/Src/Playback.cpp create mode 100644 Externals/MusicMod/Player/Src/Playback.h create mode 100644 Externals/MusicMod/Player/Src/PlaybackEq.cpp create mode 100644 Externals/MusicMod/Player/Src/PlaybackOrder.cpp create mode 100644 Externals/MusicMod/Player/Src/Player.cpp create mode 100644 Externals/MusicMod/Player/Src/PlayerExport.cpp create mode 100644 Externals/MusicMod/Player/Src/PlayerExport.h create mode 100644 Externals/MusicMod/Player/Src/Playlist.cpp create mode 100644 Externals/MusicMod/Player/Src/Playlist.h create mode 100644 Externals/MusicMod/Player/Src/PlaylistControler.cpp create mode 100644 Externals/MusicMod/Player/Src/PlaylistControler.h create mode 100644 Externals/MusicMod/Player/Src/PlaylistModel.h create mode 100644 Externals/MusicMod/Player/Src/PlaylistView.cpp create mode 100644 Externals/MusicMod/Player/Src/PlaylistView.h create mode 100644 Externals/MusicMod/Player/Src/Plugin.cpp create mode 100644 Externals/MusicMod/Player/Src/Plugin.h create mode 100644 Externals/MusicMod/Player/Src/PluginManager.cpp create mode 100644 Externals/MusicMod/Player/Src/PluginManager.h create mode 100644 Externals/MusicMod/Player/Src/Prefs.cpp create mode 100644 Externals/MusicMod/Player/Src/Prefs.h create mode 100644 Externals/MusicMod/Player/Src/Rebar.cpp create mode 100644 Externals/MusicMod/Player/Src/Rebar.h create mode 100644 Externals/MusicMod/Player/Src/Resources/Buttons.bmp create mode 100644 Externals/MusicMod/Player/Src/Resources/Plainamp.ico create mode 100644 Externals/MusicMod/Player/Src/Resources/resrc1.aps create mode 100644 Externals/MusicMod/Player/Src/Resources/resrc1.h create mode 100644 Externals/MusicMod/Player/Src/Resources/resrc1.rc create mode 100644 Externals/MusicMod/Player/Src/Status.cpp create mode 100644 Externals/MusicMod/Player/Src/Status.h create mode 100644 Externals/MusicMod/Player/Src/Timer.cpp create mode 100644 Externals/MusicMod/Player/Src/Unicode.cpp create mode 100644 Externals/MusicMod/Player/Src/Unicode.h create mode 100644 Externals/MusicMod/Player/Src/Util.cpp create mode 100644 Externals/MusicMod/Player/Src/Util.h create mode 100644 Externals/MusicMod/Player/Src/VisCache.cpp create mode 100644 Externals/MusicMod/Player/Src/VisCache.h create mode 100644 Externals/MusicMod/Player/Src/VisModule.cpp create mode 100644 Externals/MusicMod/Player/Src/VisModule.h create mode 100644 Externals/MusicMod/Player/Src/VisPlugin.cpp create mode 100644 Externals/MusicMod/Player/Src/VisPlugin.h create mode 100644 Externals/MusicMod/Player/Src/Winamp.cpp create mode 100644 Externals/MusicMod/Player/Src/Winamp.h create mode 100644 Externals/MusicMod/Player/Src/Winamp/Dsp.h create mode 100644 Externals/MusicMod/Player/Src/Winamp/Gen.h create mode 100644 Externals/MusicMod/Player/Src/Winamp/In2.h create mode 100644 Externals/MusicMod/Player/Src/Winamp/Out.h create mode 100644 Externals/MusicMod/Player/Src/Winamp/Vis.h create mode 100644 Externals/MusicMod/Player/Src/Winamp/wa_ipc.h create mode 100644 Externals/MusicMod/Player/Src/Winamp/wa_msgids.h create mode 100644 Externals/MusicMod/Player/Src/Winmain.cpp create mode 100644 Externals/MusicMod/Player/Src/afxres.h create mode 100644 Externals/MusicMod/Player/Src/fftw3/fftw3.h create mode 100644 Externals/MusicMod/Player/Src/vis_plainbar/vis_plainbar.cpp create mode 100644 Externals/MusicMod/Player/Src/zlib/zconf.h create mode 100644 Externals/MusicMod/Player/Src/zlib/zlib.h create mode 100644 Externals/MusicMod/TestPlayer/Src/Player.cpp create mode 100644 Externals/MusicMod/TestPlayer/TestPlayer.vcproj create mode 100644 Source/MusicMod.sln diff --git a/Externals/MusicMod/Common/Common.vcproj b/Externals/MusicMod/Common/Common.vcproj new file mode 100644 index 0000000000..bfd7927ca2 --- /dev/null +++ b/Externals/MusicMod/Common/Common.vcproj @@ -0,0 +1,342 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Externals/MusicMod/Data/win32/Plainamp.ini b/Externals/MusicMod/Data/win32/Plainamp.ini new file mode 100644 index 0000000000..d82f44952a --- /dev/null +++ b/Externals/MusicMod/Data/win32/Plainamp.ini @@ -0,0 +1,40 @@ +[Plainamp] +OutputPluginActive___out_ds.dll=1 +Volume=255 +Loop=1 +WinPlaceConsole=(1,(62,441,1179,845)) +WinPlaceMain=(1,(274,203,1005,765)) +MinimizeToTray=1 +Panning=0 +CurPresetFixed=-1 +PreventDistortion=1 +Order=3 +PlaylistFollow=1 +PlaylistEntryNumberZeroPadding=1 +CurPlaylistPosition=0 +InfinitePlaylist=0 +ManagerGrid=1 +WinPlaceManager=(1,(500,400,1000,700)) +OrderBand=(0,-2,0,1) +EqBand=(1,-2,0,1) +SeekBand=(2,-2,0,1) +VolBand=(3,-2,0,1) +PanBand=(4,-2,0,1) +ButtonsBand=(5,134,0,1) +VisBand=(6,134,0,1) +InvertPanSlider=0 +CurDir=C:\ +WarnPluginsMissing=1 +[out_wave_gpl] +config=14000000002400000100000000000000FFFFFFFF0100000036 +[out_ds] +cfg_hw_mix=1 +cfg_buf_ms=2000 +cfg_trackhack=0 +cfg_prebuf2=500 +cfg_fade_seek.on=0 +cfg_fade_pause.on=0 +cfg_fadevol=0 +cfg_wait=0 +[Interface] +ShowConsole = False diff --git a/Externals/MusicMod/Data/win32/PluginsMusic/in_vgmstream.dll b/Externals/MusicMod/Data/win32/PluginsMusic/in_vgmstream.dll new file mode 100644 index 0000000000000000000000000000000000000000..f54a9f9e625e99abd495ffe4d883242be3ede0b8 GIT binary patch literal 331776 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 z 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 M$Zvpx!_A)gJ^;`G6XCV3)`BVM@ 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

    lfg7``g=We3QK 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 z zrdCWeG`5&VVXaO0B_BO&V~eTlTI6~lKdUXy%{uaWdMY2W`Ayx|C^TF4l2h8_by;yc zDZFgb_O@!%7TRKU3F|I0YYa2VuemC32b;Zbh!ge#X-XN{iLXFRO> zLwEOxx=f@Y6v&qFYVI8n{!ltOneureTgfF44;B!H;mM*lKTUVn@Myd-KW*cORJ+%I zxh2j$vH7>LYzinTFlc`Yx-rSUm150*I0dXW597gX_lLUO?|k`lsD;>C-$GmKGnS13 zJMp;2^OeF<-a)plDt;PEUh zH1iIRc4rMWoxIX{$Z-;TJ!~|#U{drH;rXOqy0Bi#sjrvu7&Miviaa?{pqO3*%ZazP z66bi+Wa`M_(PSE4S=6jfO(&YeGeTqrDT$a&I=T^?iYE|~@)D?`k5{Foq?0ynGi;Pj ziJ;kNe-2*CVV2Rh_V|hM5K3a4CrWHSFFLWh9NxyCUG_^gsAJ8G;@Q0VH4E`e4~vSaP2Qcw#!Dg{+I!>pej|HKv`2kydd^n15r6-yk&=Q91RfcpT;7BT&DK+kzh zzvz6Xp9FkyG1IRE^Z`;YV0ts)X+Rw|W$YA~JOPjac<0G31>^(H1Dp-OzJCD8Sd7ah zfG+|r0DK;h1JD670oXvZX^C+2+3`POfg7)YdjJ`Lqku8MF2D}J7JwBH1at!G0MA_x zI>09YIu79d_G17#^3gsB*a8>;NB|FDHJ}zy3|Iu151^w2JemQWfCyj{;M;&70v-cg z3|I)5Y+&p)03GeFUQuXrg~MWm6?lYoD}gKU0A%eNt*``LJ<+AqC$A`=P~57$1c-qlNBJk17THA_;*|XkAnY_}7U7*-*7q!t#PQ6akig zg-STa=ZeA>;6Rj-h#Zy*h1S!CX(pS?JJG_-OlLrJMZv)*!h)yI8uHGZ)D`py(A)3r z2i_$Ke*B9HQ3OZSi?|9!`njM8kcbfNk4j<>IbkjvJ0g)#1bt4;eTiSy@FSaBgm0mm zONL~S34!JU1VcivD*($=*dW4s{cbUu879pTfuW!(X<}BP!OO$cN3<>>8j>No+ZFK$ zqyhynSZ4Ad=8BN&o(M7|66KSTNkz95=*RDS2&X_m^eiWjMExESj)?RlsxxU&t)J*x zIDbT17{R7_h(?MogyFRfK~?fvF7)-}bI8ZdEn^;t&#s0@Jv`Vbz&iubJS2GieX3Tl z3Q#>lXTKl@(Tmyn@H}Tt6QwB0jfl$2423CA=|@Sl4VzNHB7E>K$=e!ZPY4-Lzq3Z=a5xk}Y4M4f-cTSAT2I=O;>Zb1X3flO zarJ~@vDGf*+X7U<`WrP=i~mLyg;E%WK+&a{R84|nAWD>(nFUdQFxhF8R;m%B++c31 zJ({@_4p!&QL=*@vPq!SU5=Y`uxQ1W|wOEq-1YbrpNE~qBtOX)M6lbBcUEnpSMcQL{;@XjKY{tNejf(%*vWcW@Xhup{c3m z>XueVQ^IZ`H%|;&cKiso5TaO%i=>K#nwpqg70N0UL^FA@gPtBN2L*B_&Cn$FP_;xB zgjAO%X3-GPkaO*nNlENo68i*_?dRA#;FSm~Ny1HH55s^PNW%RX`;kWAq$J!kz*PY^ z*YVG`XL^DqBL6Z`YL>6P>3**Dw1`q$C?NM>Cv{N6t)`t1V~3LNX#;|f3N#GlbB3@RKn!AMsbQV8z%lle zcHbVseu1&w+I>wRME&gz%}q^(o>Pew7ZD-vHg!fU4fs!a>lO_Vv#xxzme6V6iBBHxWciz+0ke$<_2T;Lw;8v)U`6y z=XBxQooaZ^5%j1?b{0um8|Z4lJ_Sz$bl2_=dP3_PL%}X+|4K>|bZ~Wv7+%c==E3p|w@>a7)Q?9^nwbkE)Fe~MBiHWAjZzO&y0lTWjv09UPMS73e z1M}mhIbHGCYHDe#S#36`N=>D1W8#~ivzzcL5LgW|aGwRx1GZlX`=gz?r1N2^?3N3xwvj#01eyDUQ3<1m#vI8ns`=ls^+xGT3 z@bf_Uz#RAm8PngK5igBD;l~J(IHvHpcmcR^Cu4VM=(Y;4FC^SMSpZjEc!}X>LEP7{5Lz$p zx`2;>TEaEq@C3~{wF2h@rUz~E0a2alc%$(UK7zt)HXH+qS;C3AYB6swxVgD)lgFmA zi=5f)EOsv|Wc){1Wpi>jBi_5g+nL0>4Kk4IJ>aIvEkR?h!oP8W zTn^~tVx6EQFHavT?H4#L|Lz5}tGU!7I7j(RD={(v2+pC0YJ*E)yVfZitb~kS{1FGT zao6;8{3?abb1q~ie4e);G3G1y7%Tui+-w~*Yr&`+XUTacbUKTbqqmgNcSk*t%?(cl z&~~6DztQMUv=L_H@1k1J>&F**BuCL4GMowg639Sb&M3-PW>Aof7RY$z`sRPBEYZ2b8t`#TZSO;Zsc!frywS?X`%Ho+b_Htd8R!I?btb>kPgtSwHMb(g~~` za`|yb_rW=NU&Y7r4}s?7-SMfssd~CF7wItDhN`dBvHY{5IeB-_;H}0`1hNXKbs@-4 zlB=;gH-B|J|43<0{?ZKojXXlh4sJ+2n1X$$1}@}lx4JITLsIgQ;{0xchSqVS zsev|Xji0%mV0Y#GO@bDw!D(q_^MYPRJV@{w9S^0J;M|7#-GQ?M>qrZ0z>3(6`v#or za81V(X>Wf*J2rKhUoh2H@?P36q+#Wod1PYZ6!4=;udXR|XlXTTfXrTagR$2!CNl1jZU#Z@i%(0+>Q=PJIdqspP~)fg%!`D0arzv`>N zl+LQ>$TOcNb0PQ!60)!5eo-sD2RhPTBiXlquRpBZA2a#S_R6REq!v1)AeLySqj_{X z3+Sgg%B)uOQa(qaQSzP^Ki6gKN-K9oqhe1dUh?+!1cFg}9^PbHqj(qIW9uo`**=V{x8<=Q7+hX%ZB+)!^B$W_4>Gy?mz^z4knFp`p-Z zC^ewXqyL#gPurnLb;Rw%O9VIG$$~+jxfFdeUKhKs5c^w0>qHDJ^198P#!ic?NGvjo z#?HKo@)fhC(G*yr=`>S=@+mymb$Wb-v)Wl*Tieil*_t(pi~p;8`UT&KT4*=a7`7Ph zG(2M1Yk1Z0CxgCdQIV;rsVG|XjiMhG?JYV`^z)+UijEfjvFMYc`Ni4A6~&hq%f&Yp z$BVyNytnw5#ixodFjg5mjbAnXz_`o!N8_Y%eo0ozIVEK!4JEOXdrH1v^6w?TDS5Hv z4<&Dwq?xizrKZ&;(X`&Q!E~4D+otcDwwoR`jhc>_{$M(0ddqZ1sZiQbx~jCJbaUyJ z(g#YvU;5+HpOn5>da^XVY)M&3nYFC0tg*~p_NB7kvVpQ#+1JYMDSNnVPuae*v9jmO zepmKt*?8HhvJ`WMImdjyIp4g}e3`k`e2uxoESlGud(AhQH=1uVKWyG@K45;@{F?bs z=Cu}&C1{Z>H(748JZRZr`47up%daiJwH&ql(ejRE(z3{Uq4g4Lk+s5FZ@t>;wg#+G z>wxuE>yY(M>pj*7tvjrbS)a5XwmxHh&ibPDsP%R0aqCIz$JT|mFW3ZIfz4>Eu+`g~ zwly}7En>ULw$XN*ZOC?~?LOOswjH*IZI9ay*`BsNZ+pe|U$zO`JGKvOAKTK(&n!Q? zTqs{!URGXR-cjx^?<*fDzpZ?z{I2r*%eR$3RQ_oB-tq(GZnmF;J1WJ>b(Mpa zvC2CtzfpN#<-b+_r1FW%pH=>%@`cLZSH4k+V#|NWKgV!^VJY&ioR$W_5;SRs9K#~R k`3Av|Z&+ejZZH^13}#SN8EOsnhDLP}0o`ZE|DFZ@8yfJxfdBvi literal 0 HcmV?d00001 diff --git a/Externals/MusicMod/Data/x64/Plainamp.ini b/Externals/MusicMod/Data/x64/Plainamp.ini new file mode 100644 index 0000000000..b1a908bd6d --- /dev/null +++ b/Externals/MusicMod/Data/x64/Plainamp.ini @@ -0,0 +1,29 @@ +[Plainamp] +OutputPluginActive___out_wave_gpl.dll=1 +Volume=100 +Loop=1 +WinPlaceConsole=(1,(62,441,1179,845)) +WinPlaceMain=(1,(274,203,1005,765)) +MinimizeToTray=1 +Panning=0 +CurPresetFixed=-1 +PreventDistortion=1 +Order=3 +PlaylistFollow=1 +PlaylistEntryNumberZeroPadding=1 +CurPlaylistPosition=0 +InfinitePlaylist=1 +ManagerGrid=1 +WinPlaceManager=(1,(500,400,1000,700)) +OrderBand=(0,-2,0,1) +EqBand=(1,-2,0,1) +SeekBand=(2,-2,0,1) +VolBand=(3,-2,0,1) +PanBand=(4,-2,0,1) +ButtonsBand=(5,134,0,1) +VisBand=(6,134,0,1) +InvertPanSlider=0 +CurDir=C:\ +WarnPluginsMissing=1 +[out_wave_gpl] +config=14000000002400000100000000000000FFFFFFFF0100000036 diff --git a/Externals/MusicMod/Data/x64/PluginsMusic/in_vgmstream.dll b/Externals/MusicMod/Data/x64/PluginsMusic/in_vgmstream.dll new file mode 100644 index 0000000000000000000000000000000000000000..bf2af34f79df624d3f71a082582cb2624bc6f187 GIT binary patch literal 177152 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

    ak4-ObO2c_um0!oJ#`&v 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≫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 literal 0 HcmV?d00001 diff --git a/Externals/MusicMod/Data/x64/PluginsMusic/out_wave_gpl.dll b/Externals/MusicMod/Data/x64/PluginsMusic/out_wave_gpl.dll new file mode 100644 index 0000000000000000000000000000000000000000..da0ccab700ab8be3018e49fe122c2a8890f59cb8 GIT binary patch literal 55296 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 IlO^!~09~ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Externals/MusicMod/Main/Src/Frame.cpp b/Externals/MusicMod/Main/Src/Frame.cpp new file mode 100644 index 0000000000..a29d02c1ed --- /dev/null +++ b/Externals/MusicMod/Main/Src/Frame.cpp @@ -0,0 +1,466 @@ +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + + +////////////////////////////////////////////////////////////////////////////////////////// +// Include +// ŻŻŻŻŻŻŻŻŻŻ +#include // System + +#include "Core.h" // Core + +#include "IniFile.h" // Common +#include "Log.h" + +#include "../../../../Source/Core/DolphinWX/Src/Globals.h" // DolphinWX +#include "../../../../Source/Core/DolphinWX/Src/Frame.h" + +#include "../../../../Source/Core/DolphinWX/resources/toolbar_plugin_dsp.c" // Icons +#include "../../../../Source/Core/DolphinWX/resources/Boomy.h" +#include "../../../../Source/Core/DolphinWX/resources/Vista.h" +#include "../../../../Source/Core/DolphinWX/resources/KDE.h" +#include "../../../../Source/Core/DolphinWX/resources/X-Plastik.h" + +#include "../../Player/Src/PlayerExport.h" // Player +////////////////////////////////// + + + +////////////////////////////////////////////////////////////////////////////////////////// +// Declarations and definitions +// ŻŻŻŻŻŻŻŻŻŻ +namespace MusicMod +{ + bool GlobalMute = false; + bool GlobalPause = false; + bool bShowConsole = false; + int GlobalVolume = 125; + extern bool dllloaded; + + void ShowConsole(); + void Init(); +} +////////////////////////////////// + + + +////////////////////////////////////////////////////////////////////////////////////////// +// Change the brightness of a wxBitmap +// ŻŻŻŻŻŻŻŻŻŻ +wxBitmap SetBrightness(wxBitmap _Bitmap, int _Brightness, bool Gray) +{ + wxImage _Image = _Bitmap.ConvertToImage(); + wxImage _Image2 = _Bitmap.ConvertToImage(); + + wxString Tmp; + + if(_Brightness < 0) _Brightness = 0; // Limits + if(_Brightness > 255) _Brightness = 255; + + _Brightness = 255 - _Brightness; // Make big values brighter + + // Remove the alpha layer first + for(int y = 0; y < _Bitmap.GetWidth(); y++) + { + for(int x = 0; x < _Bitmap.GetHeight(); x++) + _Image.SetAlpha(x, y, 255); + } + + for(int y = 0; y < _Bitmap.GetWidth(); y++) + { + //Tmp += wxString::Format("\n %i: ", y); + + for(int x = 0; x < _Bitmap.GetHeight(); x++) + { + u8 R = _Image.GetRed(x, y); // Get colors + u8 G = _Image.GetGreen(x, y); + u8 B = _Image.GetBlue(x, y); + + //if((x == 5 | x == 6) && y == 15) Tmp += wxString::Format("%03i %03i %03i", R, G, B); + + if(_Brightness > 128) + { + int Bright = _Brightness - 128; + R = R - Bright * (R - 0) / 128; + G = G - Bright * (G - 0) / 128; + B = B - Bright * (B - 0) / 128; + + // 118 - 72 * 118 / 128 = 118 - 66.3 = 52 + // 119 - = 119 - 66.9 = 52 + } + else + { + int Bright = 128 - _Brightness; + R = R - Bright * (R - 255) / 128; + G = G - Bright * (G - 255) / 128; + B = B - Bright * (B - 255) / 128; + } + + //if((x == 5 | x == 6) && y == 15) Tmp += wxString::Format(" %03i %03i %03i | ", R, G, B); + + _Image.SetRGB(x, y, R, G, B); + } + } + // Return the alpha + _Image.SetAlpha(_Image2.GetAlpha(), true); + + // Update the bitmap + if(Gray) + return wxBitmap(_Image.ConvertToGreyscale()); + else + return wxBitmap(_Image); + + //wxMessageBox(Tmp); +} +////////////////////////////////// + + +#ifdef MUSICMOD +void +CFrame::MM_InitBitmaps(int Theme) +{ + // Define the log bitmaps + switch (Theme) + { + case BOOMY: + m_Bitmaps[Toolbar_Log] = wxGetBitmapFromMemory(Toolbar_Log_png); + break; + case VISTA: + m_Bitmaps[Toolbar_Log] = wxGetBitmapFromMemory(Toolbar_Log1_png); + break; + case XPLASTIK: + m_Bitmaps[Toolbar_Log] = wxGetBitmapFromMemory(Toolbar_Log2_png); + break; + case KDE: + m_Bitmaps[Toolbar_Log] = wxGetBitmapFromMemory(Toolbar_Log3_png); + break; + default: PanicAlert("Theme selection went wrong"); + } + + // Create a gray version + m_Bitmaps[Toolbar_PluginDSP_Dis] = wxBitmap(SetBrightness(m_Bitmaps[Toolbar_PluginDSP], 165, true)); + m_Bitmaps[Toolbar_Log_Dis] = wxBitmap(SetBrightness(m_Bitmaps[Toolbar_Log], 165, true)); + + // Update in case the bitmap has been updated + //if (GetToolBar() != NULL) TheToolBar->FindById(Toolbar_Log)->SetNormalBitmap(m_Bitmaps[Toolbar_Log]); +} + + +void +CFrame::MM_PopulateGUI() +{ + wxToolBar* toolBar = TheToolBar; // Shortcut + + toolBar->AddSeparator(); + + MusicMod::Init(); + + // --------------------------------------- + // Draw a rotated music label + // --------------------- + wxBitmap m_RotatedText(30, 15); + wxMemoryDC dc; + dc.SelectObject(m_RotatedText); + wxBrush BackgroundGrayBrush(_T("#ece9d8")); // The right color in windows + + // Set outline and fill colors + dc.SetBackground(BackgroundGrayBrush); + dc.Clear(); + + wxFont m_font(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD); + dc.SetFont(m_font); + dc.SetTextForeground(wxColour(*wxLIGHT_GREY)); + + dc.DrawText(wxT("Music"), 0, 0); + m_RotatedText = wxBitmap(m_RotatedText.ConvertToImage().Rotate90(false)); + + wxStaticBitmap * m_StaticBitmap = new wxStaticBitmap(toolBar, wxID_ANY, m_RotatedText); + + toolBar->AddControl(m_StaticBitmap); + // --------------------------- + + + + mm_ToolMute = toolBar->AddTool(IDM_MUTE, _T("Mute"), m_Bitmaps[Toolbar_PluginDSP], _T("Mute music")); + mm_ToolPlay = toolBar->AddTool(IDM_MUSIC_PLAY, _T("Play"), m_Bitmaps[Toolbar_Play], _T("Play or pause music without pausing the game")); + + // This cause the disabled tool bitmap to become some kind of monochrome version + /* + mm_ToolMute = new wxToolBarToolBase(toolBar, IDM_MUTE, _T("Mute"), m_Bitmaps[Toolbar_PluginDSP], + m_Bitmaps[Toolbar_PluginDSP], wxITEM_CHECK, 0, _T("Mute music")); + toolBar->AddTool(mm_ToolMute); + + mm_ToolPlay = new wxToolBarToolBase(toolBar, IDM_MUSIC_PLAY, _T("Play"), m_Bitmaps[Toolbar_Play], + m_Bitmaps[Toolbar_Play], wxITEM_NORMAL, 0, _T("Play or pause music without pausing the game")); + toolBar->AddTool(mm_ToolPlay); + */ + + + // --------------------- + /* Lots of code to get a label for the slider, in 2.9.0 AddControl accepts a label so then + this code can be simplified a lot */ + // --------- + wxPanel * mm_SliderPanel = new wxPanel(toolBar, IDS_VOLUME_PANEL, wxDefaultPosition, wxDefaultSize); + mm_Slider = new wxSlider(mm_SliderPanel, IDS_VOLUME, 125, 0, 255, wxDefaultPosition, wxDefaultSize); + //mm_Slider->SetToolTip("Change the music volume"); + mm_Slider->SetValue(MusicMod::GlobalVolume); + + wxStaticText * mm_SliderText = new wxStaticText(mm_SliderPanel, IDS_VOLUME_LABEL, _T("Volume"), wxDefaultPosition, wxDefaultSize); + wxBoxSizer * mm_VolSizer = new wxBoxSizer(wxVERTICAL); + mm_VolSizer->Add(mm_Slider, 0, wxEXPAND | wxALL, 0); + mm_VolSizer->Add(mm_SliderText, 0, wxCENTER | wxALL, 0); + + mm_SliderPanel->SetSizer(mm_VolSizer); + mm_SliderPanel->SetSize(mm_VolSizer->GetMinSize().GetWidth(), mm_VolSizer->GetMinSize().GetHeight()); + + toolBar->AddControl((wxControl*)mm_SliderPanel); + // --------- + + mm_ToolLog = toolBar->AddTool(IDT_LOG, _T("Log"), m_Bitmaps[Toolbar_Log], + wxT("Show or hide log. Enable the log window and restart Dolphin to show the DLL status.")); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// Update GUI +// ŻŻŻŻŻŻŻŻŻŻ +void +CFrame::MM_UpdateGUI() +{ + if(MusicMod::GlobalMute) + { + mm_ToolMute->SetLabel("Unmute"); + mm_ToolMute->SetNormalBitmap(m_Bitmaps[Toolbar_PluginDSP_Dis]); + } + else + { + mm_ToolMute->SetLabel("Mute"); + mm_ToolMute->SetNormalBitmap(m_Bitmaps[Toolbar_PluginDSP]); + } + + if(MusicMod::GlobalPause) + { + mm_ToolPlay->SetLabel("Play"); + mm_ToolPlay->SetNormalBitmap(m_Bitmaps[Toolbar_Play]); + } + else + { + mm_ToolPlay->SetLabel("Pause"); + mm_ToolPlay->SetNormalBitmap(m_Bitmaps[Toolbar_Pause]); + } + + if(MusicMod::bShowConsole) + { + mm_ToolLog->SetNormalBitmap(m_Bitmaps[Toolbar_Log]); + } + else + { + mm_ToolLog->SetNormalBitmap(m_Bitmaps[Toolbar_Log_Dis]); + } +} +////////////////////////////////// + + + +// ======================================================================================= +// Play and stop music +// --------------------------------------------------------------------------------------- +void +CFrame::MM_OnPlay() +{ + //INFO_LOG(AUDIO,"\nCFrame::OnPlayMusicMod > Begin\n"); + + // Save the volume + MusicMod::GlobalVolume = mm_Slider->GetValue(); + + IniFile file; + file.Load("Plainamp.ini"); + file.Set("Plainamp", "Volume", MusicMod::GlobalVolume); + file.Save("Plainamp.ini"); + + if (Core::GetState() != Core::CORE_UNINITIALIZED) + { + if (Core::GetState() == Core::CORE_RUN) + { + //INFO_LOG(AUDIO,"CFrame::OnPlayMusicMod > Pause\n"); + if(!MusicMod::GlobalPause) // we may has set this elsewhere + { + MusicMod::GlobalPause = true; + if (MusicMod::dllloaded) + { + Player_Pause(); + } + } + } + else + { + //INFO_LOG(AUDIO,"CFrame::OnPlayMusicMod > Play\n"); + if(MusicMod::GlobalPause) // we may has set this elsewhere + { + MusicMod::GlobalPause = false; + if (MusicMod::dllloaded) + { + Player_Unpause(); + } + } + } + } +} + +void +CFrame::MM_OnStop() +{ + Player_Stop(); + MusicMod::GlobalPause = false; +} +// ======================================================================================= + + +// ======================================================================================= +// Mute music +// --------------------------------------------------------------------------------------- +void +CFrame::MM_OnMute(wxCommandEvent& WXUNUSED (event)) +{ + //INFO_LOG(AUDIO,"CFrame::OnMute > Begin\n"); + //MessageBox(0, "", "", 0); + + if(!MusicMod::GlobalMute) + { + if(MusicMod::dllloaded) // avoid crash + { + Player_Mute(MusicMod::GlobalVolume); + } + + MusicMod::GlobalMute = true; + UpdateGUI(); + } + else + { + if(MusicMod::dllloaded) // avoid crash + { + Player_Mute(MusicMod::GlobalVolume); + } + MusicMod::GlobalMute = false; + UpdateGUI(); + } +} +// ======================================================================================= + + +// ======================================================================================= +// Pause music +// --------------------------------------------------------------------------------------- +void +CFrame::MM_OnPause(wxCommandEvent& WXUNUSED (event)) +{ + INFO_LOG(AUDIO,"CFrame::OnPause > Begin\n"); + //MessageBox(0, "", "", 0); + + if(!MusicMod::GlobalPause) + { + if(MusicMod::dllloaded) // avoid crash + { + Player_Pause(); + } + MusicMod::GlobalPause = true; + UpdateGUI(); + } + else + { + if(MusicMod::dllloaded) // avoid crash + { + Player_Pause(); + } + MusicMod::GlobalPause = false; + UpdateGUI(); + } +} + + + + +// ======================================================================================= +// Change volume +// --------------------------------------------------------------------------------------- +void CFrame::MM_OnVolume(wxScrollEvent& event) +{ + //INFO_LOG(AUDIO,"CFrame::OnVolume > Begin <%i>\n", event.GetPosition()); + //MessageBox(0, "", "", 0); + + //if(event.GetEventType() == wxEVT_SCROLL_PAGEUP || event.GetEventType() == wxEVT_SCROLL_PAGEDOWN) + // return; + + if(MusicMod::dllloaded) // avoid crash + { + + Player_Volume(event.GetPosition()); + MusicMod::GlobalVolume = event.GetPosition(); + + MusicMod::GlobalMute = false; // Unmute to + mm_ToolMute->Toggle(false); + + + if(event.GetEventType() == wxEVT_SCROLL_CHANGED) + { + // Only update this on release, to avoid major flickering when changing volume + UpdateGUI(); + + /* Use this to avoid that the focus get stuck on the slider when the main + window has been replaced */ + this->SetFocus();} + } +} +//======================================================================================= + + + +// ======================================================================================= +// Show log +// --------------------------------------------------------------------------------------- +void CFrame::MM_OnLog(wxCommandEvent& event) +{ + //INFO_LOG(AUDIO,"CFrame::OnLog > Begin\n"); + //MessageBox(0, "", "", 0); + + if(!MusicMod::dllloaded) return; // Avoid crash + + MusicMod::bShowConsole = !MusicMod::bShowConsole; + + if(MusicMod::bShowConsole) + /* What we do here is run StartConsoleWin() in Common directly after each + other first in the exe then in the DLL, sometimes this would give me a rampant memory + usage increase until the exe crashed at 700 MB memory usage or something like that. + For that reason I'm trying to sleep for a moment between them here. */ + { MusicMod::ShowConsole(); Sleep(100); Player_Console(true); } + else + { + #if defined (_WIN32) + Console::Close(); Player_Console(false); + #endif + } + + IniFile file; + file.Load("Plainamp.ini"); + file.Set("Interface", "ShowConsole", MusicMod::bShowConsole); + file.Save("Plainamp.ini"); + + UpdateGUI(); +} +//======================================================================================= + +#endif // MUSICMOD diff --git a/Externals/MusicMod/Main/Src/Main.cpp b/Externals/MusicMod/Main/Src/Main.cpp new file mode 100644 index 0000000000..89d95367a2 --- /dev/null +++ b/Externals/MusicMod/Main/Src/Main.cpp @@ -0,0 +1,342 @@ +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + + +////////////////////////////////////////////////////////////////////////////////////////// +// Include +// ŻŻŻŻŻŻŻŻŻŻ +#include +#include +#include +#include + +#include "Common.h" // Common +#include "IniFile.h" +#include "Log.h" + +#include "PowerPC/PowerPc.h" // Core + +#include "../../../../Source/Core/DiscIO/Src/FileSystemGCWii.h" // This file has #include "Filesystem.h" +#include "../../../../Source/Core/DiscIO/Src/VolumeCreator.h" + +#include "../../Player/Src/PlayerExport.h" // Local player +////////////////////////////////// + + + +////////////////////////////////////////////////////////////////////////////////////////// +// Declarations and definitions +// ŻŻŻŻŻŻŻŻŻŻ +namespace MusicMod +{ + +struct MyFilesStructure +{ + std::string path; + int offset; // Is int enough, how high does offset go? +}; + +std::vector MyFiles; +void StructSort (std::vector &MyFiles); + + +// Playback +std::string CurrentFile; +std::string CurrentPlayFile; +std::string CurrentPlayFilePath; +std::string unique_gameid; + +std::string MusicPath; + +DiscIO::CFileSystemGCWii* my_pFileSystem; + +int WritingFile = false; +bool dllloaded = false; + +extern bool bShowConsole; // Externally define +extern int GlobalVolume; +////////////////////////////////// + + +// ======================================================================================= +// Supported music files +// --------------------------------------------------------------------------------------- +bool CheckFileEnding(std::string FileName) +{ + if ( + (FileName.find(".adp") != std::string::npos) // 1080 Avalanche, Crash Bandicoot etc + || (FileName.find(".afc") != std::string::npos) // Zelda WW + || (FileName.find(".ast") != std::string::npos) // Zelda TP, Mario Kart + || (FileName.find(".dsp") != std::string::npos) // Metroid Prime + || (FileName.find(".hps") != std::string::npos) // SSB Melee + ) + return true; + + return false; +} +// ======================================================================================= + + +// ======================================================================================= +// A function to sort the filelist table after offset +// ------------------------ +void StructSort (std::vector &MyFiles) +{ + MyFilesStructure temp; + + //INFO_LOG(AUDIO,"StructSort > Begin\n"); + + for(int i = 0; i < MyFiles.size() - 1; i++) + { + for (int j = i + 1; j < MyFiles.size(); j++) + { + if (MyFiles[ i ].offset > MyFiles[ j ].offset) //comparing cost + { + temp = MyFiles[ i ]; // Swapping entire struct + MyFiles[ i ] = MyFiles[ j ]; + MyFiles[ j ] = temp; + } + } + } + + + for (long i=1; i<(long)MyFiles.size(); ++i) + { + std::cout << i << " " << MyFiles[i].path.c_str() << "#" << MyFiles[i].offset << "\n"; + } + + //INFO_LOG(AUDIO,"StructSort > Done\n"); +} +// ============================ + + +// ======================================================================================= +/* Run these things once */ +// ------------------------ +void ShowConsole() +{ +// Console::Open(100, 2000, "MusicMod", true); // Give room for 2000 rows +} + +void Init() +{ + // These things below will not need to be updated after a new game is started + if (dllloaded) return; + + // --------------------------------------- + // Load config + // --------------------- + IniFile file; + file.Load("Plainamp.ini"); + file.Get("Interface", "ShowConsole", &MusicMod::bShowConsole, false); + file.Get("Plainamp", "Volume", &MusicMod::GlobalVolume, 125); + // ------- + + // --------------------------------------- + // Make a debugging window + // --------------------- + if(MusicMod::bShowConsole) ShowConsole(); + + // Write version + #ifdef _M_X64 + INFO_LOG(AUDIO,"64 bit version\n"); + #else + INFO_LOG(AUDIO,"32 bit version\n"); + #endif + // ----------- + + // Set volume + Player_Volume(MusicMod::GlobalVolume); + + // Show DLL status + Player_Main(MusicMod::bShowConsole); + //play_file("c:\\demo36_02.ast"); + //INFO_LOG(AUDIO,"DLL loaded\n"); + + dllloaded = true; // Do this once +} +// ============================ + + +// ======================================================================================= +/* This will read the GC file system. */ +// ------------------------ +void Main(std::string FileName) +{ + // + DiscIO::IVolume* pVolume = DiscIO::CreateVolumeFromFilename(FileName.c_str()); + + // + my_pFileSystem = new DiscIO::CFileSystemGCWii(pVolume); + + /* We have to sort the files according to offset so that out scan in Blob.cpp works. + Because StructSort() only works for MyFilesStructure I copy the offset and filenames + to a new vetor first. */ + MyFiles.resize(my_pFileSystem->m_FileInfoVector.size()); // Set size + for (size_t i = 0; i < my_pFileSystem->m_FileInfoVector.size(); i++) + { + MyFiles.at(i).offset = my_pFileSystem->m_FileInfoVector.at(i).m_Offset; + MyFiles.at(i).path = my_pFileSystem->m_FileInfoVector.at(i).m_FullPath; + } + + // Sort the files by offset + StructSort(MyFiles); + + // --------------------------------------------------------------------------------------- + // Make Music directory + // ------------------------- + LPSECURITY_ATTRIBUTES attr; + attr = NULL; + MusicPath = "Music\\"; + INFO_LOG(AUDIO,"Created a Music directory\n"); + CreateDirectory(MusicPath.c_str(), attr); + // ---------------------------------------------------------------------------------------- +} + + +// ======================================================================================= +// Check if we should play this file +// --------------------------------------------------------------------------------------- +void CheckFile(std::string File, int FileNumber) +{ + // Do nothing if we found the same file again + if (CurrentFile == File) return; + + //INFO_LOG(AUDIO,">>>> (%i)Current read %s <%u = %ux%i> \n", i, CurrentFiles[i].path.c_str(), offset, CurrentFiles[i].offset, size); + + // Check if it's a music file + if (CheckFileEnding(File.c_str())) + { + /* Don't restart the playback if we find the same music file again. If the game is playing + a streaming music file it may read from it once in a while, after it has read other + files in between, if did not do this check we would restart the playback in those cases */ + if (CurrentPlayFile == File) return; + + // Notify the user + INFO_LOG(AUDIO,"\n >>> (%i/%i) Match %s\n\n", FileNumber, MyFiles.size(), File.c_str()); + + // Save the matched file + CurrentPlayFile = File; + + // --------------------------------------------------------------------------------------- + // We will now save the file to the PC hard drive + // ------------------ + // Get the filename + std::size_t pointer = File.find_last_of("\\"); + std::string fragment = File.substr (0, (pointer-0)); + int compare = File.length() - fragment.length(); // Get the length of the filename + fragment = File.substr ((pointer+1), compare); // Now we have the filename + + // Create the target file path + std::string FilePath = (MusicPath + fragment); + + WritingFile = true; // Avoid detecting the file we are writing + INFO_LOG(AUDIO,"Writing <%s> to <%s>\n", File.c_str(), FilePath.c_str()); + my_pFileSystem->ExportFile(File.c_str(), FilePath.c_str()); + WritingFile = false; + + // --------------------------------------------------------------------------------------- + // Play the file we found + // ------------------ + if(dllloaded) + { + Player_Play((char*)FilePath.c_str()); // retype it from const char* to char* + } else { + INFO_LOG(AUDIO,"Warning > Music DLL is not loaded"); + } + + // --------------------------------------------------------------------------------------- + // Remove the last file, if any + // ------------------ + if(CurrentPlayFilePath.length() > 0) + { + if(!remove(CurrentPlayFilePath.c_str())) + { + INFO_LOG(AUDIO,"The program failed to remove <%s>\n", CurrentPlayFilePath.c_str()); + } else { + INFO_LOG(AUDIO,"The program removed <%s>\n", CurrentPlayFilePath.c_str()); + } + } + + // --------------------------------------------------------------------------------------- + // Save the current playing file + // ------------------ + CurrentPlayFilePath = FilePath; // Save the filename so we can remove it later + } + + // Tell the user about the files we ignored + INFO_LOG(AUDIO,"(%i/%i) Ignored %s\n", FileNumber, MyFiles.size(), File.c_str()); + + // Update the current file + CurrentFile = File; +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// Find the current filename for a certain offset on the GC fileystem +// ŻŻŻŻŻŻŻŻŻŻŻŻŻ +void FindFilename(u64 offset, u64 size) +{ + // ======================================================================================= + /* Only do this test: + 1. After boot, not on ISO scan + 2. Not if offset = 0. + 3. Not when WritingFile. We will lead to calls back to here (Read) and it will mess + upp the scanning */ + if(PowerPC::GetState() == PowerPC::CPUState::CPU_RUNNING && offset != 0 && !WritingFile) + { + + + ////////////////////////////////////////////////////////////////////////////////////////// + /* Get the filename. Here we go through all files until we come to the file that has + the matching offset. Before MyFiles has data this loop will go nowhere. We have to + specify (MyFiles.size() - 1) because we will be reading MyFiles.at(i + 1). + MyFiles.size() is the amount of files on the disc, and the last file is + MyFiles.at(MyFiles.size() - 1) */ + // --------------------------------------------------------------------------------------- + for (int i = 0; i < (int)(MyFiles.size() - 1); ++i) + { + // --------------------------------------------------------------------------------------- + /* If we assume that myoffset is the begginning of every file this works. + Suppose there are three files + 1 is 0 to 149 + 2 is 150 to 170 + 3 is 171 to 200 + If the offset is 160 the game is reading file number two, for example + myoffset = 150: (myoffset >= offset) == false + myoffset = 171: (myoffset >= offset) == true, break + + However if the offset is 195 the game is reading the last file and we will get + myoffset = 171: (myoffset >= offset) == false + we therefore need to add the condition (offset > MyFiles[MyFiles.size() - 1].offset) + to. */ + if (MyFiles[i + 1].offset >= offset || offset > MyFiles[MyFiles.size() - 1].offset) + { + // Now we know that the game is reading from MyFiles[i].path + CheckFile(MyFiles[i].path, i); + + // Stop checking + break; + } + } + } // This ends the entire filescan + // ======================================================================================= +} +///////////////////////////////// + + +} // end of namespace diff --git a/Externals/MusicMod/Main/Src/Main.h b/Externals/MusicMod/Main/Src/Main.h new file mode 100644 index 0000000000..92ea473507 --- /dev/null +++ b/Externals/MusicMod/Main/Src/Main.h @@ -0,0 +1,31 @@ +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + + +#include // System: For std + +#include "Common.h" // Common: For u64 + + +namespace MusicMod +{ + +void Main(std::string FileName); +void FindFilename(u64 offset, u64 size); +void CheckFile(std::string File, int FileNumber = 0); + +} \ No newline at end of file diff --git a/Externals/MusicMod/Player/Lib/fftw3.lib b/Externals/MusicMod/Player/Lib/fftw3.lib new file mode 100644 index 0000000000000000000000000000000000000000..0fe0789d9a1bc375cffca840cdb5e769af047f92 GIT binary patch literal 31296 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 literal 0 HcmV?d00001 diff --git a/Externals/MusicMod/Player/Player.vcproj b/Externals/MusicMod/Player/Player.vcproj new file mode 100644 index 0000000000..4b7df6c15f --- /dev/null +++ b/Externals/MusicMod/Player/Player.vcproj @@ -0,0 +1,925 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Externals/MusicMod/Player/Src/AddDirectory.cpp b/Externals/MusicMod/Player/Src/AddDirectory.cpp new file mode 100644 index 0000000000..17dd6de1d8 --- /dev/null +++ b/Externals/MusicMod/Player/Src/AddDirectory.cpp @@ -0,0 +1,364 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "AddDirectory.h" +#include "Playlist.h" +#include "Main.h" +#include "InputPlugin.h" +#include +#include +#include +#include + +using namespace std; + + + +HWND WindowBrowse = NULL; + + + +void SearchFolder( TCHAR * szPath ); + + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +LPITEMIDLIST GetCurrentFolder() +{ +/* + How To Convert a File Path to an ITEMIDLIST + http://support.microsoft.com/default.aspx?scid=kb;en-us;132750 +*/ + LPITEMIDLIST pidl; + LPSHELLFOLDER pDesktopFolder; + TCHAR szPath[ MAX_PATH ]; +#ifndef PA_UNICODE + OLECHAR olePath[ MAX_PATH ]; +#endif + ULONG chEaten; + ULONG dwAttributes; + HRESULT hr; + + // + // Get the path we need to convert. + // + GetCurrentDirectory( MAX_PATH, szPath ); + + // + // Get a pointer to the Desktop's IShellFolder interface. + // + if( SUCCEEDED( SHGetDesktopFolder( &pDesktopFolder ) ) ) + { + + // + // IShellFolder::ParseDisplayName requires the file name be in + // Unicode. + // +#ifndef PA_UNICODE + MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, szPath, -1, olePath, MAX_PATH ); +#endif + // + // Convert the path to an ITEMIDLIST. + // + // hr = pDesktopFolder->lpVtbl->ParseDisplayName( + hr = pDesktopFolder->ParseDisplayName( + ( HWND__ * )pDesktopFolder, + NULL, +#ifndef PA_UNICODE + olePath, +#else + szPath, +#endif + &chEaten, + &pidl, + &dwAttributes + ); + + if( FAILED( hr ) ) + { + // Handle error. + return NULL; + } + + // + // pidl now contains a pointer to an ITEMIDLIST for .\readme.txt. + // This ITEMIDLIST needs to be freed using the IMalloc allocator + // returned from SHGetMalloc(). + // + + // release the desktop folder object + // pDesktopFolder->lpVtbl->Release(); + pDesktopFolder->Release(); + + return pidl; + } + else + { + return NULL; + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +BOOL CALLBACK EnumChildProc( HWND hwnd, LPARAM lp ) +{ + TCHAR szClassName[ 8 ] = TEXT( "\0" ); + HWND * hFirstFoundStatic = ( ( HWND * )lp ); + + if( GetClassName( hwnd, szClassName, 7 ) ) + { + if( !_tcscmp( szClassName, TEXT( "Static" ) ) ) + { + if( *hFirstFoundStatic ) + { + // Both found + RECT r1; + GetWindowRect( *hFirstFoundStatic, &r1 ); + + RECT r2; + GetWindowRect( hwnd, &r2 ); + + // First must be taller one + if( r1.bottom - r1.top < r2.bottom - r2.top ) + { + // Swap + RECT r = r1; + HWND h = *hFirstFoundStatic; + + r1 = r2; + *hFirstFoundStatic = hwnd; + + r2 = r; + hwnd = h; + } + + POINT xy2 = { r2.left, r2.top }; + ScreenToClient( WindowBrowse, &xy2 ); + + SetWindowPos( + *hFirstFoundStatic, + NULL, + 0, + 0, + r2.right - r2.left, + r2.bottom - r2.top, + SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER + ); + + SetWindowPos( + hwnd, + NULL, + xy2.x, + xy2.y + ( r2.bottom - r2.top ) - ( r1.bottom - r1.top ), + r1.right - r1.left, + r1.bottom - r1.top, + SWP_NOOWNERZORDER | SWP_NOZORDER + ); + + + return FALSE; // Stop + } + else + { + // First found + *hFirstFoundStatic = hwnd; + } + } + } + + return TRUE; // Continue +} + + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +int CALLBACK BrowseCallbackProc( HWND hwnd, UINT message, LPARAM lp, LPARAM wp ) +{ + switch( message ) + { + case BFFM_INITIALIZED: + { + WindowBrowse = hwnd; + + // Init with curdir + SendMessage( hwnd, BFFM_SETSELECTION, FALSE, ( LPARAM )GetCurrentFolder() ); + + // Swap static dimensions + HWND hFirstFoundStatic = NULL; + EnumChildWindows( hwnd, EnumChildProc, ( LPARAM )&hFirstFoundStatic ); + + break; + } + + case BFFM_SELCHANGED: + { + TCHAR szPath[ MAX_PATH ] = TEXT( "\0" ); + SHGetPathFromIDList( ( LPITEMIDLIST )lp, szPath ); + SendMessage( hwnd, BFFM_SETSTATUSTEXT, 0, ( LPARAM )szPath ); + + break; + } + + case BFFM_VALIDATEFAILED: + return TRUE; + + } + + return 0; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// Shows a Browse-For-Folder dialog and recursively adds supported files +/// to the playlist. Files are sorted by full filaname before being added. +//////////////////////////////////////////////////////////////////////////////// +void AddDirectory() +{ + TCHAR szPath[ MAX_PATH ]; + BROWSEINFO bi = { 0 }; + bi.hwndOwner = WindowMain; + bi.pidlRoot = NULL; // Desktop folder + bi.lpszTitle = TEXT( "Please select a directory:" ); + bi.ulFlags = BIF_VALIDATE | BIF_STATUSTEXT; + bi.lpfn = BrowseCallbackProc; + + LPITEMIDLIST pidl = SHBrowseForFolder( &bi ); + if( !pidl ) return; + + // Get path + SHGetPathFromIDList( pidl, szPath ); + + + // Search + SearchFolder( szPath ); + + + // Stay here + SetCurrentDirectory( szPath ); + + + // Free memory used + IMalloc * imalloc = 0; + if( SUCCEEDED( SHGetMalloc( &imalloc ) ) ) + { + imalloc->Free( pidl ); + imalloc->Release(); + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/* Warning: There is SetCurrentDirectory() here, be aware of it. We don't really + want to use that. */ +//////////////////////////////////////////////////////////////////////////////// +void SearchFolder( TCHAR * szPath ) +{ + // Remove trailing backslash + int iPathLen = ( int )_tcslen( szPath ); + if( iPathLen < 1 ) return; + if( szPath[ iPathLen - 1 ] == TEXT( '\\' ) ) + { + iPathLen--; + } + + // Init working buffer + TCHAR szFullpath[ MAX_PATH ]; + memcpy( szFullpath, szPath, iPathLen * sizeof( TCHAR ) ); + szFullpath[ iPathLen ] = TEXT( '\\' ); + szFullpath[ iPathLen + 1 ] = TEXT( '\0' ); + + // Make pattern + _tcscpy( szFullpath + iPathLen + 1, TEXT( "*" ) ); + + // Find + vector Files; + vector Dirs; + WIN32_FIND_DATA FindFileData; + HANDLE hFind; + hFind = FindFirstFile( szFullpath, &FindFileData ); + if( hFind == INVALID_HANDLE_VALUE ) return; + + do + { + // Skip "." and ".." + if( !_tcscmp( FindFileData.cFileName, TEXT( "." ) ) || + !_tcscmp( FindFileData.cFileName, TEXT( ".." ) ) ) continue; + + // Make full path + _tcscpy( szFullpath + iPathLen + 1, FindFileData.cFileName ); + + // Is directory? + TCHAR * szPartname = new TCHAR[ MAX_PATH ]; + _tcscpy( szPartname, FindFileData.cFileName ); + if( SetCurrentDirectory( szFullpath ) ) + { + // New dir + Dirs.push_back( szPartname ); + continue; + } + + // Search "." + const int iFilenameLen = ( int )_tcslen( FindFileData.cFileName ); + TCHAR * szExt = FindFileData.cFileName + iFilenameLen - 1; + while( ( szExt > FindFileData.cFileName ) && ( *szExt != TEXT( '.' ) ) ) szExt--; + if( *szExt != TEXT( '.' ) ) continue; + szExt++; + + // Check extension + map ::iterator iter = ext_map.find( szExt ); + if( iter == ext_map.end() ) continue; + + // New file + Files.push_back( szPartname ); + } + while( FindNextFile( hFind, &FindFileData ) ); + + FindClose( hFind ); + vector ::iterator iter; + + // Sort and recurse directories + sort( Dirs.begin(), Dirs.end(), TextCompare() ); + iter = Dirs.begin(); + while( iter != Dirs.end() ) + { + TCHAR * szWalk = *iter; + + _tcscpy( szFullpath + iPathLen + 1, szWalk ); + SearchFolder( szFullpath ); + + iter++; + } + + // Sort and add files + sort( Files.begin(), Files.end(), TextCompare() ); + iter = Files.begin(); + while( iter != Files.end() ) + { + TCHAR * szWalk = *iter; + + TCHAR * szKeep = new TCHAR[ MAX_PATH ]; + memcpy( szKeep, szFullpath, ( iPathLen + 1 ) * sizeof( TCHAR ) ); + _tcscpy( szKeep + iPathLen + 1, szWalk ); + playlist->PushBack( szKeep ); + + iter++; + } +} diff --git a/Externals/MusicMod/Player/Src/AddDirectory.h b/Externals/MusicMod/Player/Src/AddDirectory.h new file mode 100644 index 0000000000..8c819cbd63 --- /dev/null +++ b/Externals/MusicMod/Player/Src/AddDirectory.h @@ -0,0 +1,26 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_ADD_DIRECTORY_H +#define PA_ADD_DIRECTORY_H + + + +#include "Global.h" + + + +void AddDirectory(); + + + +#endif // PA_ADD_DIRECTORY_H diff --git a/Externals/MusicMod/Player/Src/AddFiles.cpp b/Externals/MusicMod/Player/Src/AddFiles.cpp new file mode 100644 index 0000000000..d8367894b2 --- /dev/null +++ b/Externals/MusicMod/Player/Src/AddFiles.cpp @@ -0,0 +1,181 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "AddFiles.h" +#include "InputPlugin.h" +#include "Main.h" +#include "Playlist.h" +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +void AddFiles() +{ + int total = 0; + + int iFilterLen = 0; + + InputPlugin * input; + vector ::iterator iter; + + // Get len +// if( input_plugins.empty() ) return; + iter = input_plugins.begin(); + while( iter != input_plugins.end() ) + { + input = *iter; + if( !input ) iter++; + iFilterLen += input->iFiltersLen; + iter++; + } + +// if( !iFilterLen ) return; + + iFilterLen += 40 + 29 + ( int )ext_map.size() * ( 2 + 4 + 1 ) + 7; + + TCHAR * szFilters = new TCHAR[ iFilterLen ]; + TCHAR * walk = szFilters; + + // ..................1.........1....\....\.1.........1........\.1... + memcpy( walk, TEXT( "All files (*.*)\0*.*\0All supported types\0" ), 40 * sizeof( TCHAR ) ); + walk += 40; + + // Add all extensions as ";*.ext" +// if( ext_map.empty() ) return; + map ::iterator iter_ext = ext_map.begin(); + bool bFirst = true; + while( iter_ext != ext_map.end() ) + { + if( !bFirst ) + { + memcpy( walk, TEXT( ";*." ), 3 * sizeof( TCHAR ) ); + walk += 3; + } + else + { + memcpy( walk, TEXT( "*." ), 2 * sizeof( TCHAR ) ); + walk += 2; + bFirst = false; + } + + TCHAR * szExt = iter_ext->first; + int uLen = ( int )_tcslen( szExt ); + memcpy( walk, szExt, uLen * sizeof( TCHAR ) ); + walk += uLen; + + iter_ext++; + } + + // *walk = TEXT( '\0' ); + // walk++; + + // ..................1..........1... + memcpy( walk, TEXT( ";*.m3u\0" ), 7 * sizeof( TCHAR ) ); + walk += 7; + + // ..................1.........1.........1...........1... + memcpy( walk, TEXT( "Playlist files (*.M3U)\0*.m3u\0" ), 29 * sizeof( TCHAR ) ); + walk += 29; + + // Copy filters + iter = input_plugins.begin(); + while( iter != input_plugins.end() ) + { + input = *iter; + if( !input ) iter++; + memcpy( walk, input->szFilters, input->iFiltersLen * sizeof( TCHAR ) ); + walk += input->iFiltersLen; + iter++; + } + + *walk = TEXT( '\0' ); + walk++; + + +//////////////////////////////////////////////////////////////////////////////// + + static TCHAR szFilenames[ 20001 ]; + *szFilenames = TEXT( '\0' ); // Each time! + + OPENFILENAME ofn; + memset( &ofn, 0, sizeof( OPENFILENAME ) ); + ofn.lStructSize = sizeof( OPENFILENAME ); + ofn.hwndOwner = WindowMain; + ofn.hInstance = g_hInstance; + ofn.lpstrFilter = szFilters; // "MPEG Layer 3\0*.mp3\0"; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 2; + ofn.lpstrFile = szFilenames; + ofn.nMaxFile = 20000; + ofn.Flags = OFN_EXPLORER | OFN_ALLOWMULTISELECT | OFN_ENABLESIZING | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + ofn.nMaxFileTitle = 0, // NULL; + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = TEXT( "Add files" ); + + if( !GetOpenFileName( &ofn ) ) return; + + int uDirLen = ( int )_tcslen( szFilenames ); + TCHAR * szDir = szFilenames; + + TCHAR * szFileWalk = szDir + uDirLen + 1; + if( *szFileWalk == TEXT( '\0' ) ) // "\0\0" or just "\0"? + { + // \0\0 -> Single file + if( !_tcsncmp( szDir + uDirLen - 3, TEXT( "m3u" ), 3 ) ) + { + // Playlist file + Playlist::AppendPlaylistFile( szDir ); + } + else + { + // Music file + TCHAR * szKeep = new TCHAR[ uDirLen + 1 ]; + memcpy( szKeep, szDir, uDirLen * sizeof( TCHAR ) ); + szKeep[ uDirLen ] = TEXT( '\0' ); + playlist->PushBack( szKeep ); + } + } + else + { + // \0 -> Several files + int iFileLen; + while( *szFileWalk != TEXT( '\0' ) ) + { + iFileLen = ( int )_tcslen( szFileWalk ); + if( !iFileLen ) return; + + TCHAR * szKeep = new TCHAR[ uDirLen + 1 + iFileLen + 1 ]; + memcpy( szKeep, szDir, uDirLen * sizeof( TCHAR ) ); + szKeep[ uDirLen ] = TEXT( '\\' ); + memcpy( szKeep + uDirLen + 1, szFileWalk, iFileLen * sizeof( TCHAR ) ); + szKeep[ uDirLen + 1 + iFileLen ] = TEXT( '\0' ); + + if( !_tcsncmp( szKeep + uDirLen + 1 + iFileLen - 3, TEXT( "m3u" ), 3 ) ) + { + // Playlist file + Playlist::AppendPlaylistFile( szKeep ); + delete [] szKeep; + } + else + { + // Music file + playlist->PushBack( szKeep ); + } + + szFileWalk += iFileLen + 1; + } + } +} diff --git a/Externals/MusicMod/Player/Src/AddFiles.h b/Externals/MusicMod/Player/Src/AddFiles.h new file mode 100644 index 0000000000..387242ce1a --- /dev/null +++ b/Externals/MusicMod/Player/Src/AddFiles.h @@ -0,0 +1,26 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_ADD_FILES_H +#define PA_ADD_FILES_H + + + +#include "Global.h" + + + +void AddFiles(); + + + +#endif // PA_ADD_FILES_H diff --git a/Externals/MusicMod/Player/Src/Config.cpp b/Externals/MusicMod/Player/Src/Config.cpp new file mode 100644 index 0000000000..d6b3615bf9 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Config.cpp @@ -0,0 +1,687 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////////////////// +/// Include and declarations, definitions +//////////////////////////////////////////////////////////////////////////////// +#include "Config.h" +#include "Console.h" +#include + +using namespace std; + +map * conf_map = NULL; + +TCHAR * szIniPath = NULL; +const TCHAR * SECTION = TEXT( "Plainamp" ); +// ============================================================================ + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfVar::ConfVar( TCHAR * szKey, ConfMode mode ) +{ + // MessageBox( 0, TEXT( "no const @ ConfVar" ), TEXT( "" ), 0 ); + //INFO_LOG(AUDIO,"ConfVar::ConfVar(TCHAR) > Got <%s>\n", szKey); + + // Init + const int iLen = ( int )_tcslen( szKey ); + m_szKey = new TCHAR[ iLen + 1 ]; + memcpy( m_szKey, szKey, iLen * sizeof( TCHAR ) ); + m_szKey[ iLen ] = TEXT( '\0' ); + + m_bCopyKey = true; + m_Mode = mode; + + m_bRead = false; + + // Register + if( !conf_map ) conf_map = new map; + conf_map->insert( pair( m_szKey, this ) ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfVar::ConfVar( const TCHAR * szKey, ConfMode mode ) +{ + //INFO_LOG(AUDIO,"ConfVar::ConfVar(const TCHAR) > Got <%s>\n", szKey); + + // Init + m_szKey = ( TCHAR * )szKey; + + m_bCopyKey = false; + m_Mode = mode; + + m_bRead = false; + + // Register + if( !conf_map ) conf_map = new map; + conf_map->insert( pair( m_szKey, this ) ); + + //INFO_LOG(AUDIO,"ConfVar::ConfVar(const TCHAR) > Insert <%s>\n", ConfVar::m_szKey); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfVar::~ConfVar() +{ + if( m_bCopyKey && m_szKey ) delete [] m_szKey; +} + + + +// ======================================================================================= +// The line name is collected in ConfVar, then ConfBool gets the boolean +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfBool::ConfBool( bool * pbData, TCHAR * szKey, ConfMode mode, bool bDefault ) : ConfVar( szKey, mode ) +{ + // MessageBox( 0, TEXT( "no const @ ConfBool" ), TEXT( "" ), 0 ); + //INFO_LOG(AUDIO,"ConfBool(TCHAR) > Get <%s>\n", szKey); + + m_pbData = pbData; + m_bDefault = bDefault; + + // *pbData = bDefault; +} +// ======================================================================================= + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfBool::ConfBool( bool * pbData, const TCHAR * szKey, ConfMode mode, bool bDefault ) : ConfVar( szKey, mode ) +{ + //INFO_LOG(AUDIO,"ConfBool(TCHAR) > Get <%s>\n", szKey); + + m_pbData = pbData; + m_bDefault = bDefault; + + // *pbData = bDefault; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ConfBool::Read() +{ + //INFO_LOG(AUDIO,"ConfBool::Read() > Begin and \n", m_bRead, szIniPath); + + if( m_bRead || !szIniPath ) return; + + *m_pbData = ( GetPrivateProfileInt( SECTION, m_szKey, ( int )m_bDefault, szIniPath ) != 0 ); + m_bRead = true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ConfBool::Write() +{ + WritePrivateProfileString( SECTION, m_szKey, *m_pbData ? TEXT( "1" ) : TEXT( "0" ), szIniPath ); + m_bRead = true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfInt::ConfInt( int * piData, TCHAR * szKey, ConfMode mode, int iDefault ) : ConfVar( szKey, mode ) +{ + MessageBox( 0, TEXT( "no const" ), TEXT( "" ), 0 ); + + m_piData = piData; + m_iDefault = iDefault; + + // *piData = iDefault; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfInt::ConfInt( int * piData, const TCHAR * szKey, ConfMode mode, int iDefault ) : ConfVar( szKey, mode ) +{ + m_piData = piData; + m_iDefault = iDefault; + + // *piData = iDefault; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ConfInt::Read() +{ + if( m_bRead || !szIniPath ) return; + + *m_piData = GetPrivateProfileInt( SECTION, m_szKey, m_iDefault, szIniPath ); + m_bRead = true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ConfInt::Write() +{ + TCHAR szNumber[ 12 ] = TEXT( "" ); + _stprintf( szNumber, TEXT( "%i" ), *m_piData ); + WritePrivateProfileString( SECTION, m_szKey, szNumber, szIniPath ); + m_bRead = true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfIntMinMax::ConfIntMinMax( int * piData, TCHAR * szKey, ConfMode mode, int iDefault, int iMin, int iMax ) : ConfInt( piData, szKey, mode, iDefault ) +{ + MessageBox( 0, TEXT( "no const" ), TEXT( "" ), 0 ); + + m_iMin = iMin; + m_iMax = iMax; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfIntMinMax::ConfIntMinMax( int * piData, const TCHAR * szKey, ConfMode mode, int iDefault, int iMin, int iMax ) : ConfInt( piData, szKey, mode, iDefault ) +{ + m_iMin = iMin; + m_iMax = iMax; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfWinPlaceCallback::ConfWinPlaceCallback( WINDOWPLACEMENT * pwpData, TCHAR * szKey, RECT * prDefault, ConfCallback fpCallback ) : ConfVar( szKey, CONF_MODE_INTERNAL ) +{ + MessageBox( 0, TEXT( "no const" ), TEXT( "" ), 0 ); + + m_pwpData = pwpData; + m_prDefault = prDefault; + m_fpCallback = fpCallback; + + pwpData->length = sizeof( WINDOWPLACEMENT ); + pwpData->flags = 0; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfWinPlaceCallback::ConfWinPlaceCallback( WINDOWPLACEMENT * pwpData, const TCHAR * szKey, RECT * prDefault, ConfCallback fpCallback ) : ConfVar( szKey, CONF_MODE_INTERNAL ) +{ + m_pwpData = pwpData; + m_prDefault = prDefault; + m_fpCallback = fpCallback; + + pwpData->length = sizeof( WINDOWPLACEMENT ); + pwpData->flags = 0; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ConfWinPlaceCallback::Read() +{ + if( m_bRead || !szIniPath ) return; + + POINT ptMinPosition = { -1, -1 }; + POINT ptMaxPosition = { -GetSystemMetrics( SM_CXBORDER ), -GetSystemMetrics( SM_CYBORDER ) }; + + m_pwpData->length = sizeof( WINDOWPLACEMENT ); + m_pwpData->flags = 0; + m_pwpData->ptMinPosition = ptMinPosition; + m_pwpData->ptMaxPosition = ptMaxPosition; + + + TCHAR szOut[ 111 ] = TEXT( "" ); + int iChars = GetPrivateProfileString( SECTION, m_szKey, TEXT( "" ), szOut, 110, szIniPath ); + bool bApplyDefault = true; + if( iChars ) + { + int iFields = _stscanf( + szOut, + TEXT( "(%u,(%i,%i,%i,%i))" ), + &m_pwpData->showCmd, + &m_pwpData->rcNormalPosition.left, + &m_pwpData->rcNormalPosition.top, + &m_pwpData->rcNormalPosition.right, + &m_pwpData->rcNormalPosition.bottom + ); + if( iFields == 5 ) + { + bApplyDefault = false; + } + } + + if( bApplyDefault ) + { + m_pwpData->showCmd = SW_SHOWNORMAL; + m_pwpData->rcNormalPosition = *m_prDefault; + } + + m_bRead = true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ConfWinPlaceCallback::Write() +{ + // Refresh data + if( m_fpCallback ) m_fpCallback( this ); + + TCHAR szData[ 111 ] = TEXT( "" ); + _stprintf( + szData, + TEXT( "(%u,(%i,%i,%i,%i))" ), + m_pwpData->showCmd, + m_pwpData->rcNormalPosition.left, + m_pwpData->rcNormalPosition.top, + m_pwpData->rcNormalPosition.right, + m_pwpData->rcNormalPosition.bottom + ); + WritePrivateProfileString( SECTION, m_szKey, szData, szIniPath ); + m_bRead = true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfBandInfoCallback::ConfBandInfoCallback( BandInfo * pbiData, TCHAR * szKey, BandInfo * pbiDefault, ConfCallback fpCallback ) : ConfVar( szKey, CONF_MODE_INTERNAL ) +{ + MessageBox( 0, TEXT( "no const" ), TEXT( "" ), 0 ); + + m_pbiData = pbiData; + m_pbiDefault = pbiDefault; + m_fpCallback = fpCallback; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfBandInfoCallback::ConfBandInfoCallback( BandInfo * pbiData, const TCHAR * szKey, BandInfo * pbiDefault, ConfCallback fpCallback ) : ConfVar( szKey, CONF_MODE_INTERNAL ) +{ + m_pbiData = pbiData; + m_pbiDefault = pbiDefault; + m_fpCallback = fpCallback; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ConfBandInfoCallback::Read() +{ + if( m_bRead || !szIniPath ) return; + + int iBreak; + int iVisible; + + TCHAR szOut[ 91 ] = TEXT( "" ); + int iChars = GetPrivateProfileString( SECTION, m_szKey, TEXT( "" ), szOut, 90, szIniPath ); + bool bApplyDefault = true; + if( iChars ) + { + int iFields = _stscanf( + szOut, + TEXT( "(%i,%i,%i,%i)" ), + &m_pbiData->m_iIndex, + &m_pbiData->m_iWidth, + &iBreak, + &iVisible + ); + if( iFields == 4 ) + { + m_pbiData->m_bBreak = ( iBreak != 0 ); + m_pbiData->m_bVisible = ( iVisible != 0 ); + + bApplyDefault = false; + } + } + + if( bApplyDefault ) + { + *m_pbiData = *m_pbiDefault; + } + + m_bRead = true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ConfBandInfoCallback::Write() +{ + // Refresh data + if( m_fpCallback ) m_fpCallback( this ); + + TCHAR szData[ 91 ] = TEXT( "" ); + _stprintf( + szData, + TEXT( "(%i,%i,%i,%i)" ), + m_pbiData->m_iIndex, + m_pbiData->m_iWidth, + ( int )m_pbiData->m_bBreak, + ( int )m_pbiData->m_bVisible + ); + WritePrivateProfileString( SECTION, m_szKey, szData, szIniPath ); + m_bRead = true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool ConfBandInfoCallback::Apply( HWND hRebar, int iBandId ) +{ + const int iOldIndex = ( int )SendMessage( hRebar, RB_IDTOINDEX, iBandId, 0 ); + if( iOldIndex == -1 ) return false; + + int & iNewIndex = m_pbiData->m_iIndex; + if( iOldIndex != iNewIndex ) + { + // Move band + if( !SendMessage( hRebar, RB_MOVEBAND, iOldIndex, iNewIndex ) ) + { + return false; + } + } + + REBARBANDINFO rbbi; + rbbi.cbSize = sizeof( REBARBANDINFO ); + rbbi.fMask = RBBIM_STYLE; + + // Get current info + if( !SendMessage( hRebar, RB_GETBANDINFO, iNewIndex, ( LPARAM )&rbbi ) ) + { + return false; + } + + rbbi.fMask = RBBIM_SIZE | RBBIM_STYLE; + rbbi.cx = m_pbiData->m_iWidth; + if( ( rbbi.fStyle & RBBS_BREAK ) == RBBS_BREAK ) + { + if( !m_pbiData->m_bBreak ) rbbi.fStyle -= RBBS_BREAK; + } + else + { + if( m_pbiData->m_bBreak ) rbbi.fStyle |= RBBS_BREAK; + } + + // Update info + if( !SendMessage( hRebar, RB_SETBANDINFO, iNewIndex, ( LPARAM )&rbbi ) ) + { + return false; + } + + // Show/hide band + if( !SendMessage( hRebar, RB_SHOWBAND, iNewIndex, m_pbiData->m_bVisible ? TRUE : FALSE ) ) + { + return false; + } + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfString::ConfString( TCHAR * szData, TCHAR * szKey, ConfMode mode, TCHAR * szDefault, int iMaxLen ) : ConfVar( szKey, mode ) +{ + MessageBox( 0, TEXT( "no const" ), TEXT( "" ), 0 ); + + m_szData = szData; + m_szDefault = szDefault; + m_iMaxLen = iMaxLen; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfString::ConfString( TCHAR * szData, const TCHAR * szKey, ConfMode mode, TCHAR * szDefault, int iMaxLen ) : ConfVar( szKey, mode ) +{ + m_szData = szData; + m_szDefault = szDefault; + m_iMaxLen = iMaxLen; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ConfString::Read() +{ + //INFO_LOG(AUDIO, "ConfString::Read() > Begin\n"); + + if( m_bRead || !szIniPath ) return; + + GetPrivateProfileString( SECTION, m_szKey, m_szDefault, m_szData, m_iMaxLen, szIniPath ); + + //INFO_LOG(AUDIO, "ConfString::Read() > GetPrivateProfileString <%s> <%s> <%s>\n", m_szKey, m_szData, szIniPath); + + m_bRead = true; +} + + +// ======================================================================================= +// I don't use this +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ConfString::Write() +{ + /* + WritePrivateProfileString( SECTION, m_szKey, m_szData, szIniPath ); + m_bRead = true; + */ +} +// ======================================================================================= + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfCurDir::ConfCurDir( TCHAR * szData, TCHAR * szKey ) : ConfString( szData, szKey, + CONF_MODE_INTERNAL, TEXT( "C:\\" ), MAX_PATH ) +{ + +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +ConfCurDir::ConfCurDir( TCHAR * szData, const TCHAR * szKey ) : ConfString( szData, szKey, + CONF_MODE_INTERNAL, TEXT( "C:\\" ), MAX_PATH ) +{ + +} + + +//////////////////////////////////////////////////////////////////////////////// +/* IMPORTANT: This SetCurrentDirectory() has to be disabled or we change the relative paths + for the entire application */ +//////////////////////////////////////////////////////////////////////////////// +void ConfCurDir::Read() +{ + ConfString::Read(); + + // Apply + //SetCurrentDirectory( m_szData ); + + //INFO_LOG(AUDIO,"ConfCurDir::Read > End <%s>\n", m_szData); +} +// ============================================================================== + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ConfCurDir::Write() +{ + // Refresh + GetCurrentDirectory( MAX_PATH, m_szData ); // Note: without trailing slash + + // MessageBox( 0, m_szData, TEXT( "CurDir" ), 0 ); + //INFO_LOG(AUDIO,"ConfCurDir::Read <%s>\n", m_szData); + + ConfString::Write(); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void Conf::Init() +{ + if( szIniPath ) return; + + // ======================================================================================= + // Find the right ini file + // Long filename + + szIniPath = new TCHAR[ _MAX_PATH ]; + + TCHAR szFull[ _MAX_PATH ] = TEXT( "" ); + TCHAR szDrive[ _MAX_DRIVE ] = TEXT( "" ); + TCHAR szDir[ _MAX_DIR ] = TEXT( "" ); + + + // --------------------------------------------------------------------------------------- + // We place the full path in szFull + //GetModuleFileName( hInstance, szFull, _MAX_PATH ); + GetModuleFileName( NULL, szFull, _MAX_PATH ); // No don't use that + // --------------------------------------------------------------------------------------- + + _tsplitpath( szFull, szDrive, szDir, NULL, NULL ); + + + // Convert short to long path + GetLongPathName( szDir, szDir, _MAX_DIR ); + + + // --------------------------------------------------------------------------------------- + // We place information about the file in fd + // --------------------------------------------------------------------------------------- + // Convert short to long file + WIN32_FIND_DATA fd; + HANDLE h = FindFirstFile( szFull, &fd ); + // --------------------------------------------------------------------------------------- + + // --------------------------------------------------------------------------------------- + // Convert File.exe to File.ini + // --------------------------------------------------------------------------------------- + // Search last dot + TCHAR * szSearch = fd.cFileName + _tcslen( fd.cFileName ) - 1; + while( ( *szSearch != TEXT( '.' ) ) && ( szSearch > fd.cFileName ) ) + { + szSearch--; + } + // --------------------------------------------------------------------------------------- + // Replace extension + _tcscpy( szSearch, TEXT( ".ini" ) ); + // --------------------------------------------------------------------------------------- + + // Copy full filename + //_sntprintf( szIniPath, _MAX_PATH, TEXT( "%s%s%s" ), szDrive, szDir, fd.cFileName ); + _sntprintf( szIniPath, _MAX_PATH, TEXT( "%s%s%s" ), szDrive, szDir, TEXT( "Plainamp.ini" ) ); + + INFO_LOG(AUDIO,"DLL > Ini path <%s>\n", szIniPath); + // ======================================================================================= + + + // --------------------------------------------------------------------------------------- + // Read all settings in it + // --------------------------------------------------------------------------------------- + // Read all (original comment) + map::iterator iter = conf_map->begin(); + + // ======================================================================================= + // This will lead us to the Read() function earlier in this file. For example ConfBool::Read() + while( iter != conf_map->end() ) + { + /* By default there was a SetCurrentDirectory() here that would affect the entire process, + exe and everything. It is disabled now. */ + iter->second->Read(); + iter++; + } + // ======================================================================================= + // --------------------------------------------------------------------------------------- + +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void Conf::Write() +{ + if( !szIniPath ) return; + + map::iterator iter = conf_map->begin(); + while( iter != conf_map->end() ) + { + iter->second->Write(); + iter++; + } +} diff --git a/Externals/MusicMod/Player/Src/Config.h b/Externals/MusicMod/Player/Src/Config.h new file mode 100644 index 0000000000..c139c120af --- /dev/null +++ b/Externals/MusicMod/Player/Src/Config.h @@ -0,0 +1,239 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_CONFIG_H +#define PA_CONFIG_H + + + +#include "Global.h" +#include "OutputPlugin.h" + + + +namespace Conf +{ + //void Init( HINSTANCE hInstance ); + void Init( ); + void Write(); +}; + + + +enum ConfMode +{ + CONF_MODE_INTERNAL, // Will not be shown to the user + CONF_MODE_PUBLIC +}; + +class ConfVar; +typedef void ( * ConfCallback )( ConfVar * var ); + +struct BandInfo +{ + int m_iIndex; + int m_iWidth; + bool m_bBreak; + bool m_bVisible; +}; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Config container +//////////////////////////////////////////////////////////////////////////////// +class ConfVar +{ +public: + ConfVar( TCHAR * szKey, ConfMode mode ); + ConfVar( const TCHAR * szKey, ConfMode mode ); + ~ConfVar(); + +protected: + TCHAR * m_szKey; ///< Unique identifier + ConfMode m_Mode; ///< Mode/visibility + bool m_bRead; ///< Initilization flag + + virtual void Read() = 0; + virtual void Write() = 0; + + // virtual void Backup() = 0; ///< Creates a backup and deletes old backup if it exists + // virtual void Restore() = 0; ///< Restores settings from backup and destroys the backup + +private: + bool m_bCopyKey; ///< Keyname is copy (has to be freed on destruction) + + //friend void Conf::Init( HINSTANCE hInstance ); + friend void Conf::Init( ); + + friend void Conf::Write(); +}; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Boolean config container +//////////////////////////////////////////////////////////////////////////////// +class ConfBool : public ConfVar +{ +public: + ConfBool( bool * pbData, TCHAR * szKey, ConfMode mode, bool bDefault ); + ConfBool( bool * pbData, const TCHAR * szKey, ConfMode mode, bool bDefault ); + +private: + bool * m_pbData; ///< Target + bool m_bDefault; ///< Default value + + void Read(); + void Write(); + + + friend OutputPlugin::OutputPlugin( TCHAR * szDllpath, bool bKeepLoaded ); +}; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Integer config container +//////////////////////////////////////////////////////////////////////////////// +class ConfInt : public ConfVar +{ +public: + ConfInt( int * piData, TCHAR * szKey, ConfMode mode, int iDefault ); + ConfInt( int * piData, const TCHAR * szKey, ConfMode mode, int iDefault ); + +protected: + int * m_piData; + int m_iDefault; + + void Read(); + void Write(); +}; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Integer config container with restricted range +//////////////////////////////////////////////////////////////////////////////// +class ConfIntMinMax : public ConfInt +{ +public: + ConfIntMinMax( int * piData, TCHAR * szKey, ConfMode mode, int iDefault, int iMin, int iMax ); + ConfIntMinMax( int * piData, const TCHAR * szKey, ConfMode mode, int iDefault, int iMin, int iMax ); + +// bool IsValid() { return ( ( *m_piData >= m_iMin ) && ( *m_piData <= m_iMax ) ); } + inline bool IsMin() { return ( *m_piData == m_iMin ); } + inline bool IsMax() { return ( *m_piData == m_iMax ); } + + inline void MakeValidDefault() { if( ( *m_piData < m_iMin ) || ( *m_piData > m_iMax ) ) *m_piData = m_iDefault; } + inline void MakeValidPull() { if( *m_piData < m_iMin ) *m_piData = m_iMin; else if( *m_piData > m_iMax ) *m_piData = m_iMax; } + +private: + int m_iMin; + int m_iMax; + + void Read() { ConfInt::Read(); MakeValidPull(); } +}; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Window placement config container +/// +/// The callback funtion is called on write() +/// so the data written is up to date. +//////////////////////////////////////////////////////////////////////////////// +class ConfWinPlaceCallback : public ConfVar +{ +public: + ConfWinPlaceCallback( WINDOWPLACEMENT * pwpData, TCHAR * szKey, RECT * prDefault, ConfCallback fpCallback ); + ConfWinPlaceCallback( WINDOWPLACEMENT * pwpData, const TCHAR * szKey, RECT * prDefault, ConfCallback fpCallback ); + + inline void TriggerCallback() { if( m_fpCallback ) m_fpCallback( this ); } + inline void RemoveCallback() { m_fpCallback = NULL; } + +private: + WINDOWPLACEMENT * m_pwpData; + RECT * m_prDefault; + ConfCallback m_fpCallback; + + void Read(); + void Write(); +}; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Rebar band info config container +/// +/// The callback funtion is called on write() +/// so the data written is up to date. +//////////////////////////////////////////////////////////////////////////////// +class ConfBandInfoCallback : public ConfVar +{ +public: + ConfBandInfoCallback( BandInfo * pbiData, TCHAR * szKey, BandInfo * pbiDefault, ConfCallback fpCallback ); + ConfBandInfoCallback( BandInfo * pbiData, const TCHAR * szKey, BandInfo * pbiDefault, ConfCallback fpCallback ); + + inline void TriggerCallback() { if( m_fpCallback ) m_fpCallback( this ); } + inline void RemoveCallback() { m_fpCallback = NULL; } + + bool Apply( HWND hRebar, int iBandId ); + +private: + BandInfo * m_pbiData; + BandInfo * m_pbiDefault; + ConfCallback m_fpCallback; + + void Read(); + void Write(); +}; + + + +//////////////////////////////////////////////////////////////////////////////// +/// String config container +//////////////////////////////////////////////////////////////////////////////// +class ConfString : public ConfVar +{ +public: + ConfString( TCHAR * szData, TCHAR * szKey, ConfMode mode, TCHAR * szDefault, int iMaxLen ); + ConfString( TCHAR * szData, const TCHAR * szKey, ConfMode mode, TCHAR * szDefault, int iMaxLen ); + +protected: + TCHAR * m_szData; + int m_iMaxLen; + TCHAR * m_szDefault; + + void Read(); + void Write(); +}; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Current directory config container +//////////////////////////////////////////////////////////////////////////////// +class ConfCurDir : public ConfString +{ +public: + ConfCurDir( TCHAR * szData, TCHAR * szKey ); + ConfCurDir( TCHAR * szData, const TCHAR * szKey ); + +private: + void Read(); + void Write(); +}; + + + +#endif // PA_CONFIG_H diff --git a/Externals/MusicMod/Player/Src/Console.cpp b/Externals/MusicMod/Player/Src/Console.cpp new file mode 100644 index 0000000000..08868156d4 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Console.cpp @@ -0,0 +1,228 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Console.h" +#include "Font.h" +#include "Main.h" +#include "Config.h" +#include + + + +HWND WindowConsole = NULL; // extern +int iNext = 0; + +const int iMaxEntries = 10000; + + + +WNDPROC WndprocConsoleBackup = NULL; +LRESULT CALLBACK WndprocConsole( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); + + + +bool bConsoleVisible; +WINDOWPLACEMENT WinPlaceConsole; + +void WinPlaceConsoleCallback( ConfVar * var ) +{ + if( !IsWindow( WindowConsole ) ) return; + + GetWindowPlacement( WindowConsole, &WinPlaceConsole ); + + // MSDN: If the window identified by the hWnd parameter + // is maximized, the showCmd member is SW_SHOWMAXIMIZED. + // If the window is minimized, showCmd is SW_SHOWMINIMIZED. + // Otherwise, it is SW_SHOWNORMAL. + if( !bConsoleVisible ) + { + WinPlaceConsole.showCmd = SW_HIDE; + } +} + +RECT rConsoleDefault = { 50, 400, 450, 700 }; + +ConfWinPlaceCallback cwpcWinPlaceConsole( + &WinPlaceConsole, + TEXT( "WinPlaceConsole" ), + &rConsoleDefault, + WinPlaceConsoleCallback +); + + + +//////////////////////////////////////////////////////////////////////////////// +/// Creates the console window. +/// Size and visibility is used from config. +/// +/// @return Success flag +//////////////////////////////////////////////////////////////////////////////// +bool Console::Create() +{ + WindowConsole = CreateWindowEx( + WS_EX_TOOLWINDOW | // DWORD dwExStyle + WS_EX_CLIENTEDGE, // + TEXT( "LISTBOX" ), // LPCTSTR lpClassName + TEXT( "Console" ), // LPCTSTR lpWindowName + WS_VSCROLL | // DWORD dwStyle + LBS_DISABLENOSCROLL | // + LBS_EXTENDEDSEL | // + LBS_HASSTRINGS | // + LBS_NOTIFY | // + LBS_NOINTEGRALHEIGHT | // + WS_POPUP | // + WS_OVERLAPPEDWINDOW, // + rConsoleDefault.left, // int x + rConsoleDefault.top, // int y + rConsoleDefault.right - rConsoleDefault.left, // int nWidth + rConsoleDefault.bottom - rConsoleDefault.top, // int nHeight + WindowMain, // HWND hWndParent + NULL, // HMENU hMenu + g_hInstance, // HINSTANCE hInstance + NULL // LPVOID lpParam + ); + + if( !WindowConsole ) return false; + + // A blank line at the bottom will give us more space + SendMessage( WindowConsole, LB_INSERTSTRING, 0, ( LPARAM )TEXT( "" ) ); + + Font::Apply( WindowConsole ); + + bConsoleVisible = ( WinPlaceConsole.showCmd != SW_HIDE ); + SetWindowPlacement( WindowConsole, &WinPlaceConsole ); + + // Exchange window procedure + WndprocConsoleBackup = ( WNDPROC )GetWindowLong( WindowConsole, GWL_WNDPROC ); + if( WndprocConsoleBackup != NULL ) + { + SetWindowLong( WindowConsole, GWL_WNDPROC, ( LONG )WndprocConsole ); + } + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// Destroys the console window. +/// +/// @return Success flag +//////////////////////////////////////////////////////////////////////////////// +bool Console::Destroy() +{ + if( !WindowConsole ) return false; + DestroyWindow( WindowConsole ); + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// Pops up the console window. +/// +/// @return Success flag +//////////////////////////////////////////////////////////////////////////////// +bool Console::Popup() +{ + if( !WindowConsole ) return false; + if( !IsWindowVisible( WindowConsole ) ) + { + ShowWindow( WindowConsole, SW_SHOW ); + } + + SetActiveWindow( WindowConsole ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// Adds a new entry at the end/bottom +/// +/// @param szText Log entry +/// @return Success flag +//////////////////////////////////////////////////////////////////////////////// +bool Console::Append( TCHAR * szText ) +{ + if( !WindowConsole ) return false; + if( iNext > iMaxEntries - 1 ) + { + SendMessage( + WindowConsole, + LB_DELETESTRING, + 0, + 0 + ); + iNext--; + } + + const int uTextLen = ( int )_tcslen( szText ); + TCHAR * szBuffer = new TCHAR[ 11 + uTextLen + 1 ]; + time_t now_time_t = time( NULL ); + struct tm * now_tm = localtime( &now_time_t ); + _tcsftime( szBuffer, 12, TEXT( "%H:%M:%S " ), now_tm ); + memcpy( szBuffer + 11, szText, uTextLen * sizeof( TCHAR ) ); + szBuffer[ 11 + uTextLen ] = TEXT( '\0' ); + + SendMessage( WindowConsole, LB_INSERTSTRING, iNext, ( LPARAM )szBuffer ); + SendMessage( WindowConsole, LB_SETSEL, FALSE, -1 ); + SendMessage( WindowConsole, LB_SETSEL, TRUE, iNext ); + SendMessage( WindowConsole, LB_SETTOPINDEX, iNext, 0 ); + iNext++; + + delete [] szBuffer; + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +LRESULT CALLBACK WndprocConsole( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + switch( message ) + { +/* + case WM_CTLCOLORLISTBOX: + if( ( HWND )lp == WindowConsole ) + { + SetBkColor (( HDC )wp, GetSysColor(COLOR_3DFACE)); + return ( LRESULT )GetSysColorBrush(COLOR_3DFACE); + } + break; +*/ + + case WM_SYSCOMMAND: + // Hide instead of closing + if( ( wp & 0xFFF0 ) == SC_CLOSE ) + { + ShowWindow( hwnd, SW_HIDE ); + return 0; + } + break; + + case WM_DESTROY: + cwpcWinPlaceConsole.TriggerCallback(); + cwpcWinPlaceConsole.RemoveCallback(); + break; + + case WM_SHOWWINDOW: + bConsoleVisible = ( wp == TRUE ); + break; + + } + return CallWindowProc( WndprocConsoleBackup, hwnd, message, wp, lp ); +} diff --git a/Externals/MusicMod/Player/Src/Console.h b/Externals/MusicMod/Player/Src/Console.h new file mode 100644 index 0000000000..28386af265 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Console.h @@ -0,0 +1,40 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_CONSOLE_H +#define PA_CONSOLE_H + + + +#include "Global.h" + + + +extern HWND WindowConsole; + + +//////////////////////////////////////////////////////////////////////////////// +/// Logging console window +//////////////////////////////////////////////////////////////////////////////// +namespace Console +{ + bool Create(); + bool Destroy(); + + bool Popup(); + + bool Append( TCHAR * szText ); +} + + + +#endif // PA_CONSOLE_H diff --git a/Externals/MusicMod/Player/Src/DspModule.cpp b/Externals/MusicMod/Player/Src/DspModule.cpp new file mode 100644 index 0000000000..5a49715d1e --- /dev/null +++ b/Externals/MusicMod/Player/Src/DspModule.cpp @@ -0,0 +1,136 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "DspModule.h" +#include "Unicode.h" + + + +DspModule ** active_dsp_mods = NULL; // extern +int active_dsp_count = 0; // extern + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +DspModule::DspModule( char * szName, int iIndex, winampDSPModule * mod, DspPlugin * plugin ) +{ + iArrayIndex = -1; + + iNameLen = ( int )strlen( szName ); + this->szName = new TCHAR[ iNameLen + 1 ]; + ToTchar( this->szName, szName, iNameLen ); + this->szName[ iNameLen ] = TEXT( '\0' ); + + this->iIndex = iIndex; + this->mod = mod; + this->plugin = plugin; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool DspModule::Start( int iIndex ) +{ + if( !mod ) return false; + if( iArrayIndex != -1 ) return false; + if( !mod->Init ) return false; + if( mod->Init( mod ) != 0 ) return false; + +//////////////////////////////////////////////////////////////////////////////// + DspLock.Enter(); +//////////////////////////////////////////////////////////////////////////////// + + if( !active_dsp_count ) + { + active_dsp_mods = new DspModule * [ 1 ]; + active_dsp_mods[ 0 ] = this; + iArrayIndex = 0; + } + else + { + if( iIndex < 0 ) + iIndex = 0; + else if( iIndex > active_dsp_count ) + iIndex = active_dsp_count; + + DspModule ** new_active_dsp_mods = new DspModule * [ active_dsp_count + 1 ]; + memcpy( new_active_dsp_mods, active_dsp_mods, iIndex * sizeof( DspModule * ) ); + memcpy( new_active_dsp_mods + iIndex + 1, active_dsp_mods + iIndex, ( active_dsp_count - iIndex ) * sizeof( DspModule * ) ); + for( int i = iIndex + 1; i < active_dsp_count + 1; i++ ) + { + new_active_dsp_mods[ i ]->iArrayIndex = i; + } + new_active_dsp_mods[ iIndex ] = this; + iArrayIndex = iIndex; + delete [] active_dsp_mods; + active_dsp_mods = new_active_dsp_mods; + } + active_dsp_count++; + +//////////////////////////////////////////////////////////////////////////////// + DspLock.Leave(); +//////////////////////////////////////////////////////////////////////////////// + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool DspModule::Stop() +{ + if( !mod ) return false; + if( iArrayIndex == -1 ) return false; + if( !mod->Quit ) return true; + +//////////////////////////////////////////////////////////////////////////////// + DspLock.Enter(); +//////////////////////////////////////////////////////////////////////////////// + + for( int i = iArrayIndex; i < active_dsp_count - 1; i++ ) + { + active_dsp_mods[ i ] = active_dsp_mods[ i + 1 ]; + active_dsp_mods[ i ]->iArrayIndex = i; + } + active_dsp_count--; + +//////////////////////////////////////////////////////////////////////////////// + DspLock.Leave(); +//////////////////////////////////////////////////////////////////////////////// + + mod->Quit( mod ); + + iArrayIndex = -1; + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool DspModule::Config() +{ + if( !mod ) return false; + if( iArrayIndex == -1 ) return false; + if( !mod->Config ) return false; + + mod->Config( mod ); + + return true; +} diff --git a/Externals/MusicMod/Player/Src/DspModule.h b/Externals/MusicMod/Player/Src/DspModule.h new file mode 100644 index 0000000000..5b69b177b1 --- /dev/null +++ b/Externals/MusicMod/Player/Src/DspModule.h @@ -0,0 +1,67 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_DSP_MODULE_H +#define PA_DSP_MODULE_H + + + +#include "Global.h" +#include "DspPlugin.h" +#include "Winamp/Dsp.h" + + + +class DspModule; +class DspPlugin; + +extern DspModule ** active_dsp_mods; +extern int active_dsp_count; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Winamp DSP module wrapper +//////////////////////////////////////////////////////////////////////////////// +class DspModule +{ +public: + inline bool IsActive() { return ( iArrayIndex != -1 ); } + + inline TCHAR * GetName() { return szName; } + inline int GetNameLen() { return iNameLen; } + + DspModule( char * szName, int iIndex, winampDSPModule * mod, DspPlugin * plugin ); +// DspModule( wchar_t * szName, int iIndex, winampVisModule * mod, VisPlugin * plugin ); + + bool Start( int iIndex ); + bool Stop(); + + bool Config(); + +private: + int iArrayIndex; + + TCHAR * szName; + int iNameLen; + + int iIndex; + winampDSPModule * mod; + DspPlugin * plugin; + + + friend int dsp_dosamples( short int * samples, int numsamples, int bps, int nch, int srate ); +}; + + + +#endif // PA_DSP_MODULE_H diff --git a/Externals/MusicMod/Player/Src/DspPlugin.cpp b/Externals/MusicMod/Player/Src/DspPlugin.cpp new file mode 100644 index 0000000000..40988eb8c8 --- /dev/null +++ b/Externals/MusicMod/Player/Src/DspPlugin.cpp @@ -0,0 +1,186 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "DspPlugin.h" +#include "Main.h" +#include "Unicode.h" +#include "Console.h" + + + +vector dsp_plugins; // extern +Lock DspLock = Lock( TEXT( "PLAINAMP_DSP_LOCK" ) ); // extern + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +DspPlugin::DspPlugin( TCHAR * szDllpath, bool bKeepLoaded ) : Plugin( szDllpath ) +{ + header = NULL; + + if( !Load() ) + { + return; + } + + if( !bKeepLoaded ) + { + Unload(); + } + + dsp_plugins.push_back( this ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool DspPlugin::Load() +{ + if( IsLoaded() ) return true; + + // (1) Load DLL + hDLL = LoadLibrary( GetFullpath() ); + if( !hDLL ) return false; + + // (2) Find export + WINAMP_DSP_GETTER winampGetDSPHeader2 = + ( WINAMP_DSP_GETTER )GetProcAddress( hDLL, "winampDSPGetHeader2" ); + if( winampGetDSPHeader2 == NULL ) + { + FreeLibrary( hDLL ); + hDLL = NULL; + return false; + } + + // (3) Get header + header = winampGetDSPHeader2(); + if( header == NULL ) + { + FreeLibrary( hDLL ); + hDLL = NULL; + return false; + } + +//////////////////////////////////////////////////////////////////////////////// + // Forget old modules or we get them twice + if( !modules.empty() ) + { + modules.clear(); + } +//////////////////////////////////////////////////////////////////////////////// + + if( !szName ) + { + // Note: The prefix is not removed to hide their + // origin at Nullsoft! It just reads easier. + if( !strnicmp( header->description, "nullsoft ", 9 ) ) + { + header->description += 9; + } + iNameLen = ( int )strlen( header->description ); + szName = new TCHAR[ iNameLen + 1 ]; + ToTchar( szName, header->description, iNameLen ); + szName[ iNameLen ] = TEXT( '\0' ); + } + + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "Loading <%s>, %s" ), GetFilename(), szName ); + Console::Append( szBuffer ); + + // (4) Get modules + winampDSPModule * mod; + int iFound = 0; + while( true ) + { + mod = header->getModule( iFound ); + if( !mod ) break; + + // (4a) Modify module + mod->hDllInstance = hDLL; + mod->hwndParent = WindowMain; + + // (4b) Add module to list + DspModule * dspmod = new DspModule( + mod->description, // char * szName + iFound, // UINT uIndex + mod, // winampDspModule * mod + this // DspPlugin * plugin + ); + modules.push_back( dspmod ); + iFound++; + + _stprintf( szBuffer, TEXT( " %s" ), dspmod->GetName() ); + Console::Append( szBuffer ); + } + + Console::Append( TEXT( " " ) ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool DspPlugin::Unload() +{ + if( !IsLoaded() ) return true; + if( IsActive() ) return false; + + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "Unloading <%s>" ), GetFilename() ); + Console::Append( szBuffer ); + Console::Append( TEXT( " " ) ); + printf( ">>>Unloading <%s>\n" , GetFilename() ); + + header = NULL; + + /* + TODO + DspModule * walk; + vector ::iterator iter = modules.begin(); + while( iter != modules.end() ) + { + walk = *iter; + delete [] walk->szName; + delete walk; + + iter++; + } + */ + + FreeLibrary( hDLL ); + hDLL = NULL; + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool DspPlugin::IsActive() +{ + vector ::iterator iter = modules.begin(); + while( iter != modules.end() ) + { + if( ( *iter )->IsActive() ) return true; + iter++; + } + return false; +} diff --git a/Externals/MusicMod/Player/Src/DspPlugin.h b/Externals/MusicMod/Player/Src/DspPlugin.h new file mode 100644 index 0000000000..afd299a60c --- /dev/null +++ b/Externals/MusicMod/Player/Src/DspPlugin.h @@ -0,0 +1,69 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_DSP_PLUGIN_H +#define PA_DSP_PLUGIN_H + + + +#include "Global.h" +#include "Plugin.h" +#include "Winamp/Dsp.h" +#include "Lock.h" +#include "DspModule.h" +#include + +using namespace std; + + + +typedef winampDSPHeader * ( * WINAMP_DSP_GETTER )( void ); + + + +class DspModule; +class DspPlugin; + +extern vector dsp_plugins; +extern Lock DspLock; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Winamp DSP plugin wrapper +//////////////////////////////////////////////////////////////////////////////// +class DspPlugin : public Plugin +{ +public: + DspPlugin( TCHAR * szDllpath, bool bKeepLoaded ); + + bool Load(); + bool Unload(); + + TCHAR * GetTypeString() { return TEXT( "DSP" ); } + int GetTypeStringLen() { return 3; } + PluginType GetType() { return PLUGIN_TYPE_DSP; } + + bool IsActive(); + +private: + winampDSPHeader * header; + vector modules; + + + friend class DspModule; + friend void ContextMenuDsp( DspPlugin * dsp, POINT * p ); +}; + + + +#endif // PA_DSP_PLUGIN_H diff --git a/Externals/MusicMod/Player/Src/Emabox/Emabox.cpp b/Externals/MusicMod/Player/Src/Emabox/Emabox.cpp new file mode 100644 index 0000000000..1f1d2bf032 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Emabox/Emabox.cpp @@ -0,0 +1,455 @@ +/*////////////////////////////////////////////////////////////////////////////// +// ExtraMessageBox +// +// Copyright İ 2006 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////*/ + + +/* +TODO +* realign/recenter after height change +* tab stop order when adding buttons +* offer extra callback? +* auto click timer (one button after XXX seconds) +* allow several checkboxes? radio buttons? + +MB_YESNO +MB_YESNOCANCEL +--> MB_YESNOALL +--> MB_YESNOCANCELALL +--> MB_DEFBUTTON5 +--> IDNOALL +--> IDYESALL +*/ + + +#include "Emabox.h" + + + +#define FUNCTION_NORMAL 0 +#define FUNCTION_EXTENDED 1 +#define FUNCTION_INDIRECT 2 + + + +const int SPACE_UNDER_CHECKBOX = 10; +const int SPACE_EXTRA_BOTTOM = 4; + +TCHAR * const szNeverAgain = TEXT( "Do not show again" ); +TCHAR * const szRememberChoice = TEXT( "Remember my choice" ); + +DWORD dwTlsSlot = TLS_OUT_OF_INDEXES; + +#ifdef EMA_AUTOINIT +int bEmaInitDone = 0; +#endif + + + +struct StructEmaBoxData +{ + int * bCheckState; + HHOOK hCBT; /* CBT hook handle */ + WNDPROC WndprocMsgBoxBackup; /* Old wndproc */ + UINT uType; /* Message box type */ + HWND hCheck; /* Checkbox handle */ +}; + +typedef struct StructEmaBoxData EmaBoxData; + + + +void RectScreenToClient( const HWND h, RECT * const r ) +{ + POINT p; + RECT after; + + p.x = r->left; + p.y = r->top; + ScreenToClient( h, &p ); + + after.left = p.x; + after.right = p.x + r->right - r->left; + after.top = p.y; + after.bottom = p.y + r->bottom - r->top; + + memcpy( r, &after, sizeof( RECT ) ); +} + + + +LRESULT CALLBACK WndprocMsgBox( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + /* Find data */ + EmaBoxData * const data = ( EmaBoxData * )TlsGetValue( dwTlsSlot ); + + switch( message ) + { + case WM_COMMAND: + if( HIWORD( wp ) == BN_CLICKED ) + { + if( !data->hCheck || ( ( HWND )lp != data->hCheck ) ) break; + + { + const LRESULT res = SendMessage( ( HWND )lp, BM_GETSTATE, 0, 0 ); + const int bCheckedAfter = ( ( res & BST_CHECKED ) == 0 ); + + /* Update external variable */ + *( data->bCheckState ) = bCheckedAfter ? 1 : 0; + + SendMessage( ( HWND )lp, BM_SETCHECK, ( bCheckedAfter ) ? BST_CHECKED : 0, 0 ); + } + } + break; + + case WM_INITDIALOG: + { + /* Add checkbox */ + if( ( data->uType & MB_CHECKMASC ) != 0 ) + { + int SPACE_OVER_CHECKBOX; + HDC hdc; + RECT rw; /* Window rect */ + RECT rc; /* Client rect */ + HWND hText; /* Message handle */ + RECT rt; /* Message rect */ + int iLabelHeight; + TCHAR * szCheckboxLabel; /* Checkbox label */ + + int iWindowWidthBefore; + int iWindowHeightBefore; + int iClientWidthBefore; + int iClientHeightBefore; + int iNeverAgainWidth; + int iNeverAgainHeight; + + + + /* Get original window dimensions */ + GetWindowRect( hwnd, &rw ); + iWindowWidthBefore = rw.right - rw.left; + iWindowHeightBefore = rw.bottom - rw.top; + + GetClientRect( hwnd, &rc ); + iClientWidthBefore = rc.right - rc.left; + iClientHeightBefore = rc.bottom - rc.top; + + + + { + /* Find handle of the text label */ + HWND hFirstStatic; + HWND hSecondStatic; + + hFirstStatic = FindWindowEx( hwnd, NULL, TEXT( "STATIC" ), NULL ); + if( !hFirstStatic ) break; + + hSecondStatic = FindWindowEx( hwnd, hFirstStatic, TEXT( "STATIC" ), NULL ); + if( !hSecondStatic ) + { + /* Only one static means no icon. */ + /* So hFirstStatic must be the text window. */ + hText = hFirstStatic; + } + else + { + TCHAR szBuf[ 2 ] = TEXT( "" ); + if( !GetWindowText( hSecondStatic, szBuf, 2 ) ) break; + + if( *szBuf != TEXT( '\0' ) ) + { + /* Has text so it must be the label */ + hText = hSecondStatic; + } + else + { + hText = hFirstStatic; + } + } + } + + GetWindowRect( hText, &rt ); + RectScreenToClient( hwnd, &rt ); + + iLabelHeight = rt.bottom - rt.top; + + { + /* Get distance between label and the buttons */ + HWND hAnyButton; + RECT rab; + + hAnyButton = FindWindowEx( hwnd, NULL, TEXT( "BUTTON" ), NULL ); + if( !hAnyButton ) break; + + GetWindowRect( hAnyButton, &rab ); + RectScreenToClient( hwnd, &rab ); + + SPACE_OVER_CHECKBOX = rab.top - rt.bottom; + } + + szCheckboxLabel = ( data->uType & MB_CHECKNEVERAGAIN ) + ? EMA_TEXT_NEVER_AGAIN + : EMA_TEXT_REMEMBER_CHOICE; + + /* Add checkbox */ + data->hCheck = CreateWindow( + TEXT( "BUTTON" ), + szCheckboxLabel, + WS_CHILD | + WS_VISIBLE | + WS_TABSTOP | + BS_VCENTER | + BS_CHECKBOX, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + hwnd, + NULL, + GetModuleHandle( NULL ), + NULL + ); + + + /* Set initial check state */ + SendMessage( data->hCheck, BM_SETCHECK, *( data->bCheckState ) ? BST_CHECKED : 0, 0 ); + + { + /* Apply default font */ + const int cyMenuSize = GetSystemMetrics( SM_CYMENUSIZE ); + const int cxMenuSize = GetSystemMetrics( SM_CXMENUSIZE ); + const HFONT hNewFont = ( HFONT )GetStockObject( DEFAULT_GUI_FONT ); + HFONT hOldFont; + SIZE size; + + SendMessage( data->hCheck, WM_SETFONT, ( WPARAM )hNewFont, ( LPARAM )TRUE ); + + hdc = GetDC( data->hCheck ); + hOldFont = ( HFONT )SelectObject( hdc, GetStockObject( DEFAULT_GUI_FONT ) ); + GetTextExtentPoint32( hdc, szCheckboxLabel, _tcslen( szCheckboxLabel ), &size ); + SelectObject( hdc, hOldFont ); + ReleaseDC( data->hCheck, hdc ); + + iNeverAgainWidth = cxMenuSize + size.cx + 1; + iNeverAgainHeight = ( cyMenuSize > size.cy ) ? cyMenuSize : size.cy; + } + + MoveWindow( + data->hCheck, + ( iClientWidthBefore - ( iNeverAgainWidth ) ) / 2, + rt.top + iLabelHeight + SPACE_OVER_CHECKBOX, + iNeverAgainWidth, + iNeverAgainHeight, + FALSE + ); + + { + /* Move all buttons down (except the checkbox) */ + const int iDistance = iNeverAgainHeight + SPACE_UNDER_CHECKBOX; + HWND hLastButton = NULL; + RECT rb; + for( ; ; ) + { + hLastButton = FindWindowEx( hwnd, hLastButton, TEXT( "BUTTON" ), NULL ); + if( !hLastButton ) break; + if( hLastButton == data->hCheck ) continue; + + GetWindowRect( hLastButton, &rb ); + RectScreenToClient( hwnd, &rb ); + + MoveWindow( hLastButton, rb.left, rb.top + iDistance, rb.right - rb.left, rb.bottom - rb.top, FALSE ); + } + + + /* Enlarge dialog */ + MoveWindow( hwnd, rw.left, rw.top, iWindowWidthBefore, iWindowHeightBefore + iDistance + SPACE_EXTRA_BOTTOM, FALSE ); + } + } + else + { + data->hCheck = NULL; + } + + /* Modify close button */ + switch( data->uType & MB_CLOSEMASK ) + { + case MB_DISABLECLOSE: + { + const HMENU hSysMenu = GetSystemMenu( hwnd, FALSE ); + EnableMenuItem( hSysMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED ); + } + break; + + case MB_NOCLOSE: + { + const LONG style = GetWindowLong( hwnd, GWL_STYLE ); + if( ( style & WS_SYSMENU ) == 0 ) break; + SetWindowLong( hwnd, GWL_STYLE, ( LONG )( style - WS_SYSMENU ) ); + } + break; + + } + } + break; + + } + return CallWindowProc( data->WndprocMsgBoxBackup, hwnd, message, wp, lp ); +} + + + +/* int bFound = 0; */ + +LRESULT CALLBACK HookprocMsgBox( int code, WPARAM wp, LPARAM lp ) +{ + /* Get hook handle */ + EmaBoxData * const data = ( EmaBoxData * )TlsGetValue( dwTlsSlot ); + + if( code == HCBT_CREATEWND ) + { + /* MSDN says WE CANNOT TRUST "CBT_CREATEWND" */ + /* so we use only the window handle */ + /* and get the class name using "GetClassName". (-> Q106079) */ + HWND hwnd = ( HWND )wp; + + /* Check windowclass */ + TCHAR szClass[ 7 ] = TEXT( "" ); + GetClassName( hwnd, szClass, 7 ); + if( !_tcscmp( szClass, TEXT( "#32770" ) ) ) + { +/* + if( bFound ) + { + return CallNextHookEx( hCBT, code, wp, lp ); + } + + bFound = 1; +*/ + /* Exchange window procedure */ + data->WndprocMsgBoxBackup = ( WNDPROC )GetWindowLong( hwnd, GWL_WNDPROC ); + if( data->WndprocMsgBoxBackup != NULL ) + { + SetWindowLong( hwnd, GWL_WNDPROC, ( LONG )WndprocMsgBox ); + } + } + } + return CallNextHookEx( data->hCBT, code, wp, lp ); +} + + + +int ExtraAllTheSame( const HWND hWnd, const LPCTSTR lpText, const LPCTSTR lpCaption, const UINT uType, const WORD wLanguageId, const LPMSGBOXPARAMS lpMsgBoxParams, int * const pbCheckRes, const int iFunction ) +{ + EmaBoxData * data; + HHOOK hCBT; + int res; + +#ifdef EMA_AUTOINIT + if( !bEmaInitDone ) + { + EmaBoxLive(); + bEmaInitDone = 1; + } +#endif + + /* Create thread data */ + data = ( EmaBoxData * )LocalAlloc( NONZEROLPTR, sizeof( EmaBoxData ) ); + TlsSetValue( dwTlsSlot, data ); + data->bCheckState = pbCheckRes; + data->uType = ( iFunction != FUNCTION_INDIRECT ) ? uType : lpMsgBoxParams->dwStyle; + + /* Setup this-thread-only hook */ + hCBT = SetWindowsHookEx( WH_CBT, &HookprocMsgBox, GetModuleHandle( NULL ), GetCurrentThreadId() ); + + switch( iFunction ) + { + case FUNCTION_NORMAL: + res = MessageBox( hWnd, lpText, lpCaption, uType ); + break; + + case FUNCTION_EXTENDED: + res = MessageBoxEx( hWnd, lpText, lpCaption, uType, wLanguageId ); + break; + + case FUNCTION_INDIRECT: + res = MessageBoxIndirect( lpMsgBoxParams ); + break; + + } + + /* Remove hook */ + if( hCBT != NULL ) UnhookWindowsHookEx( hCBT ); + + /* Destroy thread data */ + LocalFree( ( HLOCAL )data ); + + return res; +} + + + +int EmaBox( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType, int * pbCheckRes ) +{ + /* Check extra flags */ + if( ( uType & MB_EXTRAMASC ) == 0 ) + { + /* No extra */ + return MessageBox( hWnd, lpText, lpCaption, uType ); + } + + return ExtraAllTheSame( hWnd, lpText, lpCaption, uType, 0, NULL, pbCheckRes, FUNCTION_NORMAL ); +} + + + +int EmaBoxEx( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType, WORD wLanguageId, int * pbCheckRes ) +{ + /* Check extra flags */ + if( ( uType & MB_EXTRAMASC ) == 0 ) + { + /* No extra */ + return MessageBoxEx( hWnd, lpText, lpCaption, uType, wLanguageId ); + } + + return ExtraAllTheSame( hWnd, lpText, lpCaption, uType, wLanguageId, NULL, pbCheckRes, FUNCTION_EXTENDED ); +} + + + +int EmaBoxIndirect( const LPMSGBOXPARAMS lpMsgBoxParams, int * pbCheckRes ) +{ + /* Check extra flags */ + if( ( lpMsgBoxParams->dwStyle & MB_EXTRAMASC ) == 0 ) + { + /* No extra */ + return MessageBoxIndirect( lpMsgBoxParams ); + } + + return ExtraAllTheSame( NULL, NULL, NULL, 0, 0, lpMsgBoxParams, pbCheckRes, FUNCTION_INDIRECT ); +} + + + +int EmaBoxLive() +{ + dwTlsSlot = TlsAlloc(); + if( dwTlsSlot == TLS_OUT_OF_INDEXES ) return 0; + + return 1; +} + + + +int EmaBoxDie() +{ + if( dwTlsSlot == TLS_OUT_OF_INDEXES ) return 0; + + TlsFree( dwTlsSlot ); + return 1; +} diff --git a/Externals/MusicMod/Player/Src/Emabox/Emabox.h b/Externals/MusicMod/Player/Src/Emabox/Emabox.h new file mode 100644 index 0000000000..873ec62f0c --- /dev/null +++ b/Externals/MusicMod/Player/Src/Emabox/Emabox.h @@ -0,0 +1,141 @@ +/*////////////////////////////////////////////////////////////////////////////// +// ExtraMessageBox +// +// Copyright İ 2006 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////*/ + + +#ifndef EXTRA_MESSAGE_BOX_H +#define EXTRA_MESSAGE_BOX_H 1 + +#include "EmaboxConfig.h" +#include +#include + +// ======================================================================================= +// Global declarations +#include "../Global.h" +// ======================================================================================= + +/* +== TYPE ============================================================================= +#define MB_TYPEMASK 15 1111 +#define MB_OK 0 0000 +#define MB_OKCANCEL 1 0001 +#define MB_ABORTRETRYIGNORE 2 0010 +#define MB_YESNOCANCEL 3 0011 +#define MB_YESNO 4 0100 +#define MB_RETRYCANCEL 5 0101 +#define MB_CANCELTRYCONTINUE 6 0110 +*/ + +#define MB_YESNOCANCELALL 7 /* 0111 */ +#define MB_YESNOALL 8 /* 1000 */ + +/* +== ICON ============================================================================= +#define MB_ICONMASK 240 11110000 +#define MB_ICONERROR 16 00010000 +#define MB_ICONHAND 16 00010000 +#define MB_ICONSTOP 16 00010000 +#define MB_ICONQUESTION 32 00100000 +#define MB_ICONEXCLAMATION 0x30 00110000 +#define MB_ICONWARNING 0x30 00110000 +#define MB_ICONINFORMATION 64 01000000 +#define MB_ICONASTERISK 64 01000000 +#define MB_USERICON 128 10000000 + +== DEFAULT BUTTON =================================================================== +#define MB_DEFMASK 3840 111100000000 +#define MB_DEFBUTTON1 0 000000000000 +#define MB_DEFBUTTON2 256 000100000000 +#define MB_DEFBUTTON3 512 001000000000 +#define MB_DEFBUTTON4 0x300 001100000000 +*/ + +#define MB_DEFBUTTON5 1024 /* 010000000000 */ +#define MB_DEFBUTTON6 1280 /* 010100000000 */ + +/* +== MODE ============================================================================= +#define MB_MODEMASK 0x00003000 11000000000000 +#define MB_APPLMODAL 0 00000000000000 +#define MB_SYSTEMMODAL 4096 01000000000000 +#define MB_TASKMODAL 0x2000 10000000000000 + +== MISC ============================================================================= +#define MB_MISCMASK 0x0000C000 1100000000000000 +#define MB_HELP 0x4000 0100000000000000 +#define MB_NOFOCUS 0x00008000 1000000000000000 + +== FLAGS ============================================================================ +#define MB_SETFOREGROUND 0x10000 10000000000000000 +#define MB_DEFAULT_DESKTOP_ONLY 0x20000 100000000000000000 +#define MB_TOPMOST 0x40000 1000000000000000000 +#define MB_SERVICE_NOTIFICATION_NT3X 0x00040000 1000000000000000000 +#define MB_SERVICE_NOTIFICATION 0x00040000 1000000000000000000 +#define MB_TOPMOST 0x40000 1000000000000000000 +#define MB_RIGHT 0x80000 10000000000000000000 +#define MB_RTLREADING 0x100000 100000000000000000000 +#define MB_SERVICE_NOTIFICATION 0x00200000 1000000000000000000000 + +== EXTRA FLAGS ====================================================================== +*/ + +#define MB_EXTRAMASC 0xF0000000 /* 11110000000000000000000000000000 */ + +#define MB_CHECKMASC 0xC0000000 /* 11000000000000000000000000000000 */ +#define MB_CHECKNONE 0 /* 00000000000000000000000000000000 */ +#define MB_CHECKNEVERAGAIN 0x40000000 /* 01000000000000000000000000000000 */ +#define MB_CHECKREMEMBERCHOICE 0x80000000 /* 10000000000000000000000000000000 */ + +#define MB_CLOSEMASK 0x30000000 /* 00110000000000000000000000000000 */ +#define MB_NORMALCLOSE 0 /* 00000000000000000000000000000000 */ +#define MB_DISABLECLOSE 0x10000000 /* 00010000000000000000000000000000 */ +#define MB_NOCLOSE 0x20000000 /* 00100000000000000000000000000000 */ + + + +/* Function aliases */ +#define ExtraMessageBoxLive EmaBoxLive +#define ExtraMessageBoxDie EmaBoxDie +#define ExtraMessageBox EmaBox +#define ExtraMessageBoxEx EmaBoxEx +#define ExtraMessageBoxIndirect EmaBoxIndirect + + + +int EmaBoxLive(); + +int EmaBoxDie(); + +int EmaBox( + HWND hWnd, + LPCTSTR lpText, + LPCTSTR lpCaption, + UINT uType, + int * pbCheckRes +); + +int EmaBoxEx( + HWND hWnd, + LPCTSTR lpText, + LPCTSTR lpCaption, + UINT uType, + WORD wLanguageId, + int * pbCheckRes +); + +int EmaBoxIndirect( + const LPMSGBOXPARAMS lpMsgBoxParams, + int * pbCheckRes +); + + + +#endif /* EXTRA_MESSAGE_BOX_H */ diff --git a/Externals/MusicMod/Player/Src/Emabox/EmaboxConfig.h b/Externals/MusicMod/Player/Src/Emabox/EmaboxConfig.h new file mode 100644 index 0000000000..c7730335cd --- /dev/null +++ b/Externals/MusicMod/Player/Src/Emabox/EmaboxConfig.h @@ -0,0 +1,32 @@ +/*////////////////////////////////////////////////////////////////////////////// +// ExtraMessageBox +// +// Copyright İ 2006 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////*/ + + +#ifndef EXTRA_MESSAGE_BOX_CONFIG_H +#define EXTRA_MESSAGE_BOX_CONFIG_H 1 + + + +/* Allow laziness */ +#define EMA_AUTOINIT + +/* Allow overwriting message text */ +#ifndef EMA_TEXT_NEVER_AGAIN +# define EMA_TEXT_NEVER_AGAIN szNeverAgain +#endif + +#ifndef EMA_TEXT_REMEMBER_CHOICE +# define EMA_TEXT_REMEMBER_CHOICE szRememberChoice +#endif + + + +#endif /* EXTRA_MESSAGE_BOX_CONFIG_H */ diff --git a/Externals/MusicMod/Player/Src/Embed.cpp b/Externals/MusicMod/Player/Src/Embed.cpp new file mode 100644 index 0000000000..fdfb456e2e --- /dev/null +++ b/Externals/MusicMod/Player/Src/Embed.cpp @@ -0,0 +1,233 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Embed.h" +#include "Console.h" + + + +#define CLASSNAME_EMBED TEXT( "Winamp Gen" ) +#define TITLE_EMBED TEXT( "Embed target" ) + +#define EMBED_WIDTH 320 +#define EMBED_HEIGHT 240 + + + +const TCHAR * const szEmbedTitle = TITLE_EMBED; +bool bEmbedClassRegistered = false; + + +LRESULT CALLBACK WndprocEmbed( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); + + + +//////////////////////////////////////////////////////////////////////////////// +/// Creates a new embed window. +/// +/// @param ews Embed window state +/// @return New embed window handle +//////////////////////////////////////////////////////////////////////////////// +HWND Embed::Embed( embedWindowState * ews ) +{ + // Register class + if ( !bEmbedClassRegistered ) + { + WNDCLASS wc = { + 0, // UINT style + WndprocEmbed, // WNDPROC lpfnWndProc + 0, // int cbClsExtra + 0, // int cbWndExtra + g_hInstance, // HINSTANCE hInstance + NULL, // HICON hIcon + LoadCursor( NULL, IDC_ARROW ), // HCURSOR hCursor + ( HBRUSH )COLOR_WINDOW, // HBRUSH hbrBackground + NULL, // LPCTSTR lpszMenuName + CLASSNAME_EMBED // LPCTSTR lpszClassName + }; + + if( !RegisterClass( &wc ) ) return NULL; + + bEmbedClassRegistered = true; + } + + // Create window + HWND WindowEmbed = CreateWindowEx( + WS_EX_WINDOWEDGE | // DWORD dwExStyle + WS_EX_TOOLWINDOW, // + CLASSNAME_EMBED, // LPCTSTR lpClassName + szEmbedTitle, // LPCTSTR lpWindowName + WS_OVERLAPPED | // DWORD dwStyle + WS_CLIPCHILDREN | // + WS_BORDER | // + WS_CAPTION | // + WS_SYSMENU | // + WS_THICKFRAME | // + WS_MINIMIZEBOX | // + WS_MAXIMIZEBOX, // + 10, // int x + 10, // int y + EMBED_WIDTH, // int nWidth + EMBED_HEIGHT, // int nHeight + NULL, // HWND hWndParent + NULL, // HMENU hMenu + g_hInstance, // HINSTANCE hInstance + NULL // LPVOID lpParam + ); + + Console::Append( TEXT( "Embed window born" ) ); + Console::Append( TEXT( " " ) ); + + if( !ews || !ews->me ) return WindowEmbed; + + SetParent( ews->me, WindowEmbed ); + + return WindowEmbed; +} + + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +inline bool SameThread( HWND hOther ) +{ + const DWORD dwOtherThreadId = GetWindowThreadProcessId( hOther, NULL ); + const DWORD dwThisThreadId = GetCurrentThreadId(); + return ( dwOtherThreadId == dwThisThreadId ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +LRESULT CALLBACK WndprocEmbed( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ +// static bool bAllowSizeMove = false; + + switch( message ) + { + + case WM_PARENTNOTIFY: + switch( LOWORD( wp ) ) + { + case WM_DESTROY: + { + const HWND hChild = GetWindow( hwnd, GW_CHILD ); + if( !SameThread( hChild ) ) + { + // Vis plugin + DestroyWindow( hwnd ); + } + break; + } + + } + break; + + case WM_SIZE: + { + const HWND hChild = GetWindow( hwnd, GW_CHILD ); + if( !hChild ) break; + MoveWindow( hChild, 0, 0, LOWORD( lp ), HIWORD( lp ), TRUE ); + break; + } + +/* + case WM_ENTERSIZEMOVE: + bAllowSizeMove = true; + break; + + case WM_EXITSIZEMOVE: + bAllowSizeMove = false; + break; + + case WM_WINDOWPOSCHANGING: + { + WINDOWPOS * pos = ( WINDOWPOS * )lp; + + // Update child + if( IsWindow( WindowEmbedChild = GetWindow( WindowEmbed, GW_CHILD ) ) ) + { + RECT r; + GetClientRect( WindowEmbed, &r ); + MoveWindow( WindowEmbedChild, 0, 0, r.right, r.bottom, TRUE ); + } + + if( !bAllowSizeMove ) + { + // Force SWP_NOMOVE + if( ( pos->flags & SWP_NOMOVE ) == 0 ) + { + pos->flags |= SWP_NOMOVE; + } + + // Force SWP_NOSIZE + if( ( pos->flags & SWP_NOSIZE ) == 0 ) + { + pos->flags |= SWP_NOSIZE; + } + + return 0; + } + + break; + } +*/ + case WM_SHOWWINDOW: + { + const HWND hChild = GetWindow( hwnd, GW_CHILD ); + if( wp ) // Shown + { + // Update child size + RECT r; + GetClientRect( hwnd, &r ); + MoveWindow( hChild, 0, 0, r.right, r.bottom, TRUE ); + } + else // Hidden + { + ShowWindow( hChild, SW_HIDE ); + DestroyWindow( hChild ); + } + break; + } + + case WM_SYSCOMMAND: + if( ( wp & 0xFFF0 ) == SC_CLOSE ) + { + const HWND hChild = GetWindow( hwnd, GW_CHILD ); + if( SameThread( hChild ) ) + { + // Not a vis plugin + ShowWindow( hwnd, SW_HIDE ); + return 0; + } + } + break; + + case WM_DESTROY: + { + const HWND hChild = GetWindow( hwnd, GW_CHILD ); + if( hChild && SameThread( hChild ) ) + { + DestroyWindow( hChild ); + } + + Console::Append( TEXT( "Embed window dead" ) ); + Console::Append( TEXT( " " ) ); + break; + } + + } + return DefWindowProc( hwnd, message, wp, lp ); +} diff --git a/Externals/MusicMod/Player/Src/Embed.h b/Externals/MusicMod/Player/Src/Embed.h new file mode 100644 index 0000000000..fc1a7d8611 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Embed.h @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_EMBED_H +#define PA_EMBED_H + + + +#include "Global.h" +#include "Winamp/wa_ipc.h" + + + +//////////////////////////////////////////////////////////////////////////////// +/// Embed window service. +/// Winamp provides embed windows so plugins don't have to take care +/// of window skinning. A plugin let's Winamp create an embed window +/// and uses this new window as parent for its own window. +//////////////////////////////////////////////////////////////////////////////// +namespace Embed +{ + HWND Embed( embedWindowState * ews ); +}; + + + +#endif // PA_EMBED_H diff --git a/Externals/MusicMod/Player/Src/Font.cpp b/Externals/MusicMod/Player/Src/Font.cpp new file mode 100644 index 0000000000..a47b217bb9 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Font.cpp @@ -0,0 +1,83 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Font.h" + + + +HFONT hFont = NULL; + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Font::Create() +{ + hFont = CreateFont( + -11, // int nHeight + 0, // int nWidth + 0, // int nEscapement + 0, // int nOrientation + FW_REGULAR, // int fnWeight + FALSE, // DWORD fdwItalic + FALSE, // DWORD fdwUnderline + FALSE, // DWORD fdwStrikeOut + ANSI_CHARSET, // DWORD fdwCharSet + OUT_TT_PRECIS, // DWORD fdwOutputPrecision + CLIP_DEFAULT_PRECIS, // DWORD fdwClipPrecision + ANTIALIASED_QUALITY, // DWORD fdwQuality + FF_DONTCARE | DEFAULT_PITCH, // DWORD fdwPitchAndFamily + TEXT( "Verdana" ) // LPCTSTR lpszFace + ); + + return ( hFont != NULL ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Font::Destroy() +{ + if( !hFont ) return false; + DeleteObject( hFont ); + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Font::Apply( HWND hwnd ) +{ + if( !hFont ) return false; + SendMessage( + hwnd, + WM_SETFONT, + ( WPARAM )hFont, + FALSE + ); + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +HFONT Font::Get() +{ + return hFont; +} diff --git a/Externals/MusicMod/Player/Src/Font.h b/Externals/MusicMod/Player/Src/Font.h new file mode 100644 index 0000000000..d32bec237e --- /dev/null +++ b/Externals/MusicMod/Player/Src/Font.h @@ -0,0 +1,34 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_FONT_H +#define PA_FONT_H + + + +#include "Global.h" + + + +namespace Font +{ + bool Create(); + bool Destroy(); + + bool Apply( HWND hwnd ); + HFONT Get(); +}; + + + +#endif // PA_FONT_H + diff --git a/Externals/MusicMod/Player/Src/GenPlugin.cpp b/Externals/MusicMod/Player/Src/GenPlugin.cpp new file mode 100644 index 0000000000..8856229bbe --- /dev/null +++ b/Externals/MusicMod/Player/Src/GenPlugin.cpp @@ -0,0 +1,199 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "GenPlugin.h" +#include "Main.h" +#include "Unicode.h" +#include "Console.h" +#include + + + +vector gen_plugins; // extern + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +GenPlugin::GenPlugin( TCHAR * szDllpath, bool bKeepLoaded ) : Plugin( szDllpath ) +{ + iHookerIndex = -1; + plugin = NULL; + + if( !Load() ) + { + return; + } + + gen_plugins.push_back( this ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool GenPlugin::Load() +{ + if( IsLoaded() ) return true; + + // (1) Load DLL + hDLL = LoadLibrary( GetFullpath() ); + if( !hDLL ) return false; + + // (2) Find export + WINAMP_GEN_GETTER winampGetGeneralPurposePlugin = + ( WINAMP_GEN_GETTER )GetProcAddress( hDLL, "winampGetGeneralPurposePlugin" ); + if( winampGetGeneralPurposePlugin == NULL ) + { + FreeLibrary( hDLL ); + hDLL = NULL; + return false; + } + + // (3) Get module + plugin = winampGetGeneralPurposePlugin(); + if( !plugin ) + { + FreeLibrary( hDLL ); + hDLL = NULL; + return false; + } + + // (4) Process module + plugin->hDllInstance = hDLL; + plugin->hwndParent = WindowMain; + + // Note: Some plugins (mainly old ones) set description in init. + // Therefore we init first and copy the name after. + + + // (5) Init + if( plugin->init ) + { + const WNDPROC WndprocBefore = ( WNDPROC )GetWindowLong( WindowMain, GWL_WNDPROC ); + plugin->init(); + const WNDPROC WndprocAfter = ( WNDPROC )GetWindowLong( WindowMain, GWL_WNDPROC ); + + if( WndprocBefore != WndprocAfter ) + { + WndprocBackup = WndprocBefore; + iHookerIndex = iWndprocHookCounter++; + } + } + + + if( !szName ) + { + // Note: The prefix is not removed to hide their + // origin at Nullsoft! It just reads easier. + if( !strnicmp( plugin->description, "nullsoft ", 9 ) ) + { + plugin->description += 9; + } + + // Get rid of " (xxx.dll)" postfix + char * walk = plugin->description + strlen( plugin->description ) - 5; + while( true ) + { + if( ( walk <= plugin->description ) || strnicmp( walk, ".dll)", 5 ) ) break; + while( ( walk > plugin->description ) && ( *walk != '(' ) ) walk--; + if( walk <= plugin->description ) break; + walk--; + if( ( walk <= plugin->description ) || ( *walk != ' ' ) ) break; + *walk = '\0'; + } + + iNameLen = ( int )strlen( plugin->description ); + szName = new TCHAR[ iNameLen + 1 ]; + ToTchar( szName, plugin->description, iNameLen ); + szName[ iNameLen ] = TEXT( '\0' ); + } + + + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "Loading <%s>, %s" ), GetFilename(), szName ); + Console::Append( szBuffer ); + Console::Append( TEXT( " " ) ); + + + // Note: Plugins that use a wndproc hook need + // to be unloaded in the inverse loading order. + // This is due to the nature of wndproc hooking. + if( iHookerIndex != -1 ) + { + Console::Append( TEXT( "Wndproc hook added (by plugin)" ) ); + } + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool GenPlugin::Unload() +{ + if( !IsLoaded() ) return true; + + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "Unloading <%s>" ), GetFilename() ); + Console::Append( szBuffer ); + Console::Append( TEXT( " " ) ); + printf( ">>>Unloading <%s>\n" , GetFilename() ); + + + // Quit + if( plugin ) + { + if( plugin->quit ) plugin->quit(); + plugin = NULL; + } + + + // Remove wndproc hook + if( ( iHookerIndex != -1 ) && ( iHookerIndex == iWndprocHookCounter - 1 ) ) + { + // If we don't restore it the plugins wndproc will + // still be called which is not there anymore -> crash + SetWindowLong( WindowMain, GWL_WNDPROC, ( LONG )WndprocBackup ); + Console::Append( TEXT( "Wndproc hook removed (by host)" ) ); + Console::Append( TEXT( " " ) ); + + iHookerIndex = -1; + iWndprocHookCounter--; + } + + + FreeLibrary( hDLL ); + hDLL = NULL; + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool GenPlugin::Config() +{ + if( !IsLoaded() ) return false; + if( !plugin ) return false; + if( !plugin->config ) return false; + + plugin->config(); + + return true; +} diff --git a/Externals/MusicMod/Player/Src/GenPlugin.h b/Externals/MusicMod/Player/Src/GenPlugin.h new file mode 100644 index 0000000000..b32f49536a --- /dev/null +++ b/Externals/MusicMod/Player/Src/GenPlugin.h @@ -0,0 +1,64 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_GEN_PLUGIN_H +#define PA_GEN_PLUGIN_H + + + +#include "Global.h" +#include "Plugin.h" +#include "Winamp/Gen.h" +#include + +using namespace std; + + + +typedef winampGeneralPurposePlugin * ( * WINAMP_GEN_GETTER )( void ); + + + +class GenPlugin; + +extern vector gen_plugins; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Winamp general purpose plugin wrapper +//////////////////////////////////////////////////////////////////////////////// +class GenPlugin : public Plugin +{ +public: + GenPlugin( TCHAR * szDllpath, bool bKeepLoaded ); + + bool Load(); + bool Unload(); + + TCHAR * GetTypeString() { return TEXT( "General" ); } + int GetTypeStringLen() { return 7; } + PluginType GetType() { return PLUGIN_TYPE_GEN; } + + inline bool IsActive() { return IsLoaded(); } + + bool Config(); + + inline bool AllowRuntimeUnload() { return ( iHookerIndex == -1 ) || ( iHookerIndex == iWndprocHookCounter - 1 ); } + +private: + winampGeneralPurposePlugin * plugin; +}; + + + +#endif // PA_GEN_PLUGIN_H diff --git a/Externals/MusicMod/Player/Src/Global.h b/Externals/MusicMod/Player/Src/Global.h new file mode 100644 index 0000000000..684774048d --- /dev/null +++ b/Externals/MusicMod/Player/Src/Global.h @@ -0,0 +1,151 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////////// +// Settings +// ŻŻŻŻŻŻŻŻŻŻ +/* This will build Plainamp without the GUI. Todo: Fix this, I disabled a little to much + to make the regular GUI version build */ +#define NOGUI +//////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////////// +// Include +// ŻŻŻŻŻŻŻŻŻŻ +#include "../../../../Source/Core/Common/Src/Log.h" // Global common +///////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////////// +// Declarations and definitions +// ŻŻŻŻŻŻŻŻŻŻ + +// ======================================================================================= +// Because there are undefined in 64 bit it's easy to redefine them in case we use 64 bit +// --------------------- +#ifdef _M_X64 + #define GWL_WNDPROC (-4) + #define GetWindowLong GetWindowLongPtrA // or GetWindowLongPtr +#endif +// ======================================================================================= + +//////////////////////////////////// + + +// ======================================================================================= +// Back to Plainamp code +// --------------------- +#ifndef PA_GLOBAL_H +#define PA_GLOBAL_H + + +// #include "ide_devcpp/Plainamp_Private.h" + + +#ifdef UNICODE +# define PA_UNICODE +#else +# ifdef _UNICODE +# define PA_UNICODE +# endif +#endif + + +// For GetLongPathName +#if _WIN32_WINDOWS < 0x0410 +# undef _WIN32_WINDOWS +# define _WIN32_WINDOWS 0x0410 +#endif + + +#define WIN32_LEAN_AND_MEAN + +/* +#ifndef WINVER +# define WINVER 0x0500 +#else +# if (WINVER < 0x0500) +# undef WINVER +# define WINVER 0x0500 +# endif +#endif +*/ + +#include +#include +#include + + + +#ifndef _WIN32_IE +# define _WIN32_IE 0x0400 +#else +# if (_WIN32_IE < 0x0400) +# undef _WIN32_IE +# define _WIN32_IE 0x0400 +# endif +#endif + +#include + + + + + +extern HINSTANCE g_hInstance; + +extern TCHAR * szHomeDir; +extern int iHomeDirLen; + +extern TCHAR * szPluginDir; +extern int iPluginDirLen; + + + +/* +inline int abs( int x ) +{ + return ( x < 0 ) ? -x : x; +} +*/ + +inline int MIN( int a, int b ) +{ + return ( a < b ) ? a : b; +} + +inline int MAX( int a, int b ) +{ + return ( a > b ) ? a : b; +} + + + + +// Typo help +#define UNIT UINT +#define UINT_PRT UINT_PTR + + + +struct TextCompare +{ + bool operator()( const TCHAR * a, const TCHAR * b ) const + { + return _tcscmp( a, b ) < 0; + } +}; + + +#endif // PA_GLOBAL_H + diff --git a/Externals/MusicMod/Player/Src/GlobalVersion.h b/Externals/MusicMod/Player/Src/GlobalVersion.h new file mode 100644 index 0000000000..9b38065f34 --- /dev/null +++ b/Externals/MusicMod/Player/Src/GlobalVersion.h @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_GLOBAL_VERSION_H +#define PA_GLOBAL_VERSION_H + + + +#define FILE_DESCRIPTION "Plainamp" +#define VER_STRING "0.2.3.1" + + +#define PLAINAMP_TITLE TEXT( FILE_DESCRIPTION ) +#define PLAINAMP_VERSION TEXT( VER_STRING ) +#define PLAINAMP_LONG_TITLE PLAINAMP_TITLE TEXT( " " ) PLAINAMP_VERSION + + + +#endif // PA_GLOBAL_VERSION_H diff --git a/Externals/MusicMod/Player/Src/Input.cpp b/Externals/MusicMod/Player/Src/Input.cpp new file mode 100644 index 0000000000..9054b3d86f --- /dev/null +++ b/Externals/MusicMod/Player/Src/Input.cpp @@ -0,0 +1,483 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Input.h" +#include "Console.h" +#include "Status.h" +#include "Playback.h" +#include "InputPlugin.h" +#include "VisPlugin.h" +#include "DspPlugin.h" +#include "VisCache.h" +#include "Output.h" + +/* +#define FIXED_POINT 16 +#include "kiss_fft/kiss_fftr.h" +*/ + +// ======================================================================================= +//#include "fftw3/fftw3.h" +// ======================================================================================= +#include + + +// #define SKIPPY +#ifndef SKIPPY +# define MAX_DATA_FPS ( 1000 / 12 ) // in_mp3 gives new vis data every 13 ms, so 12 leaves a little space +#else +# define MAX_DATA_FPS ( 1000 / 24 ) // will skip every second frame +#endif + + +#ifdef SKIPPY +bool bLastSkipped = true; +#endif + +int iSpecChannels = 2; +int iWaveChannels = 2; + + +/* +kiss_fft_cfg kiss = { 0 }; +bool bKissInitDone = false; +*/ + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int dsp_isactive() +{ +//////////////////////////////////////////////////////////////////////////////// + DspLock.Enter(); +//////////////////////////////////////////////////////////////////////////////// + + int res = ( active_dsp_count > 0 ); + +//////////////////////////////////////////////////////////////////////////////// + DspLock.Leave(); +//////////////////////////////////////////////////////////////////////////////// + + return res; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int dsp_dosamples( short int * samples, int numsamples, int bps, int nch, int srate ) +{ + int num = numsamples; + +//////////////////////////////////////////////////////////////////////////////// + DspLock.Enter(); +//////////////////////////////////////////////////////////////////////////////// + + for( int i = 0; i < active_dsp_count; i++ ) + { + // Process + num = active_dsp_mods[ i ]->mod->ModifySamples( + active_dsp_mods[ i ]->mod, + samples, + numsamples, + bps, + nch, + srate + ); + } + +//////////////////////////////////////////////////////////////////////////////// + DspLock.Leave(); +//////////////////////////////////////////////////////////////////////////////// + + return num; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void SAVSAInit( int maxlatency_in_ms, int srate ) +{ +//////////////////////////////////////////////////////////////////////////////// + VisCache::Create(); + VisCache::EnsureLatency( maxlatency_in_ms ); + VisCache::EnsureDataFps( MAX_DATA_FPS ); + VisCache::Clean(); +//////////////////////////////////////////////////////////////////////////////// +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void SAVSADeInit() +{ + // TODO + // Console::Append( TEXT( "SAVSADeInit" ) ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void SAAddPCMData( void * PCMData, int nch, int bps, int timestamp ) +{ + // TODO + // Console::Append( TEXT( "SAAddPCMData" ) ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int SAGetMode() +{ + // TODO + // Console::Append( TEXT( "SAGetMode" ) ); + return 0; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void SAAdd( void * data, int timestamp, int csa ) +{ + // TODO + // Console::Append( TEXT( "SAAdd" ) ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VSAAddPCMData( void * PCMData, int nch, int bps, int timestamp ) +{ + // TODO + // Console::Append( TEXT( "VSAAddPCMData" ) ); + + + #ifdef SKIPPY + if( bLastSkipped ) + bLastSkipped = false; + else + { + // Skip + bLastSkipped = true; + return; + } + #endif + + +//////////////////////////////////////////////////////////////////////////////// + bool bVisLockLeft = false; + VisLock.Enter(); +//////////////////////////////////////////////////////////////////////////////// + + if( active_vis_count ) + { +//////////////////////////////////////////////////////////////////////////////// + VisLock.Leave(); + bVisLockLeft = true; +//////////////////////////////////////////////////////////////////////////////// + + VisCache::NextFrame(); + VisCache::SetWriteTime( timestamp ); + VisCache::SetReadTime( active_input_plugin->plugin->GetOutputTime() ); + + short * source = ( short * )PCMData; + + // Waveform + static unsigned char wave_left[ 576 ]; + static unsigned char wave_right[ 576 ]; + if( nch < 2 ) + { + // Mono + for( int i = 0; i < 576; i++ ) + { + wave_left[ i ] = ( source[ i ] >> 8 ); + } + + VisCache::PutWaveLeft( wave_left ); + } + else + { + int i; + + // Stereo or more + for( i = 0; i < 576; i++ ) + { + wave_left [ i ] = ( source[ i * nch ] >> 8 ); + wave_right[ i ] = ( source[ i * nch + 1 ] >> 8 ); + } + + VisCache::PutWaveLeft( wave_left ); + VisCache::PutWaveRight( wave_right ); + } + + + // TODO: Much to optimize! + + // ======================================================================================= + /* + // Spectrum + static unsigned char spec_left[ 576 ]; + static unsigned char spec_right[ 576 ]; + static fftw_complex * in = NULL; + static fftw_complex * out = NULL; + + if( !in ) + { + in = ( fftw_complex * )fftw_malloc( 576 * sizeof( fftw_complex ) ); + out = ( fftw_complex * )fftw_malloc( 576 * sizeof( fftw_complex ) ); + } + + static const double factor = 1.0 / 65536.0 / sqrt( 2.0 ); + + + // Put left + int index = 0; + for( int i = 0; i < 576; i++ ) + { + in[ i ][ 0 ] = source[ index += nch ]; + in[ i ][ 1 ] = 0.0; + } + + + // Init FFT + fftw_plan p = fftw_plan_dft_1d( 576, in, out, FFTW_FORWARD, FFTW_ESTIMATE ); + + + // Run left + fftw_execute( p ); + + // Get left + for( int i = 0; i < 576; i++ ) + { + if( i & 1 ) + { + spec_left [ i ] = spec_left [ i - 1 ]; + continue; + } + const double re = out[ i >> 1 ][ 0 ]; + const double im = out[ i >> 1 ][ 1 ]; + const double root = sqrt( re*re + im*im ); + const double final = 160.0 * log10( 1.0 + root * factor ); + spec_left[ i ] = ( final < 255.0 ) ? ( unsigned char )final : 255; + } + VisCache::PutSpecLeft( spec_left ); + + + if( nch > 1 ) + { + // Put right + index = 1; + for( int i = 0; i < 576; i++ ) + { + in[ i ][ 0 ] = source[ index += nch ]; + } + + // Run right + fftw_execute( p ); + + // Get right + for( int i = 0; i < 576; i++ ) + { + if( i & 1 ) + { + spec_right[ i ] = spec_right[ i - 1 ]; + continue; + } + const double re = out[ i >> 1 ][ 0 ]; + const double im = out[ i >> 1 ][ 1 ]; + const double root = sqrt( re*re + im*im ); + const double final = 160.0 * log10( 1.0 + root * factor ); + spec_right[ i ] = ( final < 255.0 ) ? ( unsigned char )final : 255; + } + VisCache::PutSpecRight( spec_right ); + } + // ======================================================================================= + */ + + // Cleanup FFT + // ======================================================================================= + //fftw_destroy_plan( p ); + // ======================================================================================= + + // fftw_free(in); + // fftw_free(out); + } + +//////////////////////////////////////////////////////////////////////////////// + if( bVisLockLeft ) VisLock.Enter(); +//////////////////////////////////////////////////////////////////////////////// + + for( int i = 0; i < active_vis_count; i++ ) + { + active_vis_mods[ i ]->bAllowRender = true; + } + +//////////////////////////////////////////////////////////////////////////////// + VisLock.Leave(); +//////////////////////////////////////////////////////////////////////////////// +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int VSAGetMode( int * specNch, int * waveNch ) +{ + iSpecChannels = 0; + iWaveChannels = 0; + +//////////////////////////////////////////////////////////////////////////////// + VisLock.Enter(); +//////////////////////////////////////////////////////////////////////////////// + + for( int i = 0; i < active_vis_count; i++ ) + { + const int iSpec = active_vis_mods[ i ]->mod->spectrumNch; + const int iWave = active_vis_mods[ i ]->mod->waveformNch; + + if( iSpec > iSpecChannels ) iSpecChannels = iSpec; + if( iWave > iWaveChannels ) iWaveChannels = iWave; + } + +//////////////////////////////////////////////////////////////////////////////// + VisLock.Leave(); +//////////////////////////////////////////////////////////////////////////////// + + *specNch = iSpecChannels; + *waveNch = iWaveChannels; + + return 1; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VSAAdd( void * data, int timestamp ) +{ + #ifdef SKIPPY + if( bLastSkipped ) + bLastSkipped = false; + else + { + // Skip + bLastSkipped = true; + return; + } + #endif + + +//////////////////////////////////////////////////////////////////////////////// + bool bVisLockLeft = false; + VisLock.Enter(); +//////////////////////////////////////////////////////////////////////////////// + + if( active_vis_count ) + { +//////////////////////////////////////////////////////////////////////////////// + VisLock.Leave(); + bVisLockLeft = true; +//////////////////////////////////////////////////////////////////////////////// + + VisCache::NextFrame(); + VisCache::SetWriteTime( timestamp ); + VisCache::SetReadTime( active_input_plugin->plugin->GetOutputTime() ); + + unsigned char * source = ( unsigned char * )data; + if( iSpecChannels > 0 ) + { + VisCache::PutSpecLeft( source ); + source += 576; + } + if( iSpecChannels > 1 ) + { + VisCache::PutSpecRight( source ); + source += 576; + } + if( iWaveChannels > 0 ) + { + VisCache::PutWaveLeft( source ); + source += 576; + } + if( iWaveChannels > 1 ) + { + VisCache::PutWaveRight( source ); + } + } + +//////////////////////////////////////////////////////////////////////////////// + if( bVisLockLeft ) VisLock.Enter(); +//////////////////////////////////////////////////////////////////////////////// + + for( int i = 0; i < active_vis_count; i++ ) + { + active_vis_mods[ i ]->bAllowRender = true; + } + +//////////////////////////////////////////////////////////////////////////////// + VisLock.Leave(); +//////////////////////////////////////////////////////////////////////////////// +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VSASetInfo( int nch, int srate ) +{ + // TODO + +//////////////////////////////////////////////////////////////////////////////// + VisCache::Create(); + VisCache::EnsureDataFps( MAX_DATA_FPS ); + VisCache::Clean(); +//////////////////////////////////////////////////////////////////////////////// +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void SetInfo( int bitrate, int srate, int stereo, int synched ) +{ + // TODO + static int last_valid_srate = 0; + + if( bitrate < 0 ) return; + + if( srate < 0 ) + srate = last_valid_srate; + else + last_valid_srate = srate; + + TCHAR szBuffer[ 5000 ] = TEXT( "" ); + _stprintf( szBuffer, TEXT( " %i kbps, %i kHz" ), bitrate, srate ); + StatusUpdate( szBuffer ); +} diff --git a/Externals/MusicMod/Player/Src/Input.h b/Externals/MusicMod/Player/Src/Input.h new file mode 100644 index 0000000000..f139df901f --- /dev/null +++ b/Externals/MusicMod/Player/Src/Input.h @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Global.h" + + + +int dsp_isactive(); +int dsp_dosamples( short int * samples, int numsamples, int bps, int nch, int srate ); +void SAVSAInit( int maxlatency_in_ms, int srate ); +void SAVSADeInit(); +void SAAddPCMData( void * PCMData, int nch, int bps, int timestamp ); +int SAGetMode(); +void SAAdd(void * data, int timestamp, int csa ); +void VSAAddPCMData( void * PCMData, int nch, int bps, int timestamp ); +int VSAGetMode( int * specNch, int * waveNch ); +void VSAAdd( void * data, int timestamp ); +void VSASetInfo( int nch, int srate ); +void SetInfo( int bitrate, int srate, int stereo, int synched ); diff --git a/Externals/MusicMod/Player/Src/InputPlugin.cpp b/Externals/MusicMod/Player/Src/InputPlugin.cpp new file mode 100644 index 0000000000..4fe945773c --- /dev/null +++ b/Externals/MusicMod/Player/Src/InputPlugin.cpp @@ -0,0 +1,389 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + +// ======================================================================================= +#include "Global.h" +// ======================================================================================= + +#include "InputPlugin.h" +#include "Console.h" +#include "Main.h" +#include "Input.h" +#include "Unicode.h" + + + +map ext_map; // extern +vector input_plugins; // extern +InputPlugin * active_input_plugin = NULL; // extern + + + +// ======================================================================================= +// The InputPlugin class is inherited from the Plugin class +InputPlugin::InputPlugin( TCHAR * szDllpath, bool bKeepLoaded ) : Plugin( szDllpath ) +{ + iHookerIndex = -1; + + szFilters = NULL; + iFiltersLen = 0; + plugin = NULL; + + //INFO_LOG(AUDIO,"\InputPlugin::InputPlugin > Begin\n"); + + if( !Load() ) + { + return; + } + + if( !bKeepLoaded ) + { + Unload(); + } + + input_plugins.push_back( this ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +InputPlugin::~InputPlugin() +{ + if( szFilters ) delete [] szFilters; +} + + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +bool InputPlugin::Load() +{ + printf("InputPlugin::Load() was called. IsLoaded: %i\n", IsLoaded()); + + if( IsLoaded() ) return true; + + + // (1) Load DLL + hDLL = LoadLibrary( GetFullpath() ); + if( !hDLL ) return false; + + // (2) Find export + WINAMP_INPUT_GETTER winampGetInModule2 = + ( WINAMP_INPUT_GETTER )GetProcAddress( hDLL, "winampGetInModule2" ); + if( winampGetInModule2 == NULL ) + { + FreeLibrary( hDLL ); + hDLL = NULL; + return false; + } + + // (3) Get module + plugin = winampGetInModule2(); + if( plugin == NULL ) + { + FreeLibrary( hDLL ); + hDLL = NULL; + return false; + } + + // (4) Process module + plugin->hDllInstance = hDLL; + plugin->hMainWindow = WindowMain; + + plugin->SAVSAInit = SAVSAInit; + plugin->SAVSADeInit = SAVSADeInit; + plugin->SAAddPCMData = SAAddPCMData; + plugin->SAGetMode = SAGetMode; + plugin->SAAdd = SAAdd; + plugin->VSAAddPCMData = VSAAddPCMData; + plugin->VSAGetMode = VSAGetMode; + plugin->VSAAdd = VSAAdd; + plugin->VSASetInfo = VSASetInfo; + + plugin->dsp_dosamples = dsp_dosamples; + plugin->dsp_isactive = dsp_isactive; + + plugin->SetInfo = SetInfo; + + + if( !szName ) + { + // Note: The prefix is not removed to hide their + // origin at Nullsoft! It just reads easier. + if( !strnicmp( plugin->description, "nullsoft ", 9 ) ) + { + plugin->description += 9; + if( !strnicmp( plugin->description, "mpeg(layer1-3/ct aac+/dolby aac) ", 33 ) ) + { + plugin->description += ( 33 - 5 ); + memcpy( plugin->description, "MPEG", 4 * sizeof( char ) ); + } + } + iNameLen = ( int )strlen( plugin->description ); + szName = new TCHAR[ iNameLen + 1 ]; + ToTchar( szName, plugin->description, iNameLen ); + szName[ iNameLen ] = TEXT( '\0' ); + } + + + + // (5) Init + const WNDPROC WndprocBefore = ( WNDPROC )GetWindowLong( WindowMain, GWL_WNDPROC ); + plugin->Init(); + const WNDPROC WndprocAfter = ( WNDPROC )GetWindowLong( WindowMain, GWL_WNDPROC ); + + if( WndprocBefore != WndprocAfter ) + { + WndprocBackup = WndprocBefore; + iHookerIndex = iWndprocHookCounter++; + } + + +#ifdef NOGUI + printf( "Loading <%s>, %s\n" , GetFilename(), szName ); +#else + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "Loading <%s>, %s" ), GetFilename(), szName ); + Console::Append( szBuffer ); +#endif + + Integrate(); // This function is just below + + + // Note: Plugins that use a wndproc hook need + // to be unloaded in the inverse loading order. + // This is due to the nature of wndproc hooking. + if( iHookerIndex != -1 ) + { + Console::Append( TEXT( "Wndproc hook added (by plugin)" ) ); + } + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +bool InputPlugin::Integrate() +{ + if( !IsLoaded() ) return false; + + // (6) Build filter + + // (6a) First pass: get needed buffer length + int needed = 0; + int len = 0; + bool even = false; + char * walk = plugin->FileExtensions; + while( ( len = ( int )strlen( walk ) ) > 0 ) + { + len++; // For '\0' + if( even ) + { + // Extensions e.g. "mp3;mp2;mp1" + // Worst case "a;b;c" (5) --> "*.a;*.b;*.c" (11) + needed += ( 3 * len ); + } + else + { + // Filter name e.g. "MPEG audio files" + needed += len; + } + even = !even; + walk += len; + } + szFilters = new TCHAR[ needed + 1 ]; + TCHAR * walk_out = szFilters; + + // (6b) Once again with copy + walk = plugin->FileExtensions; + even = false; + while( true ) + { + // Check extensions + char * start_filter = walk; + const int len_filter = ( int )strlen( walk ); + if( len_filter == 0 ) + { + // End reached + break; + } + walk += len_filter + 1; + + // Check filter name + char * start_display = walk; + int len_display = ( int )strlen( walk ); + if( len_display == 0 ) + { + break; + } + walk += ++len_display; + + // Append filter name + ToTchar( walk_out, start_display, len_display ); + + // ======================================================================================= + // Print used filetypes + TCHAR szBuffer[ 5000 ]; + *(walk_out + len_display) = TEXT( '\0' ); + _stprintf( szBuffer, TEXT( " %s" ), walk_out ); + Console::Append( szBuffer ); + //printf( szBuffer, TEXT( " %s\n" ), walk_out ); + // ======================================================================================= + walk_out += len_display; + + // Convert and append extensions + char * walk_filter = start_filter; + char * last_filter = start_filter; + int len; + while( true ) + { + if( ( *walk_filter == ';' ) || ( *walk_filter == '\0' ) ) + { + len = ( walk_filter - last_filter ); + if( len < 1 ) + { + break; + } + + // Add extension to map + TCHAR * szExt = new TCHAR[ len + 1 ]; + ToTchar( szExt, last_filter, len ); + szExt[ len ] = TEXT( '\0' ); + _tcslwr( szExt ); + + ext_map.insert( pair( szExt, this ) ); + + // Append extension as "*.ext[;\0]" + len++; // Also copy ';' and '\0' + memcpy( walk_out, TEXT( "*." ), 2 * sizeof( TCHAR ) ); + walk_out += 2; + ToTchar( walk_out, last_filter, len ); + walk_out += len; + + // Any more extensions? + if( *walk_filter == '\0' ) + { + break; + } + last_filter = walk_filter + 1; + } + walk_filter++; + } + + if( *walk == '\0' ) + { + *walk_out = TEXT( '\0' ); + iFiltersLen = walk_out - szFilters; + + break; + } + } + + Console::Append( TEXT( " " ) ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +bool InputPlugin::DisIntegrate() +{ + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +bool InputPlugin::Unload() +{ + if( !IsLoaded() ) return true; + + DisIntegrate(); + + #ifndef NOGUI + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "Unloading '%s'" ), GetFilename() ); + Console::Append( szBuffer ); + Console::Append( TEXT( " " ) ); + #else + printf( ">>> Unloading '%s'\n" , GetFilename() ); + #endif + + // Quit + if( plugin ) + { + if( plugin->Quit ) plugin->Quit(); + plugin->outMod = NULL; + plugin = NULL; + } + + // Remove wndproc hook + if( ( iHookerIndex != -1 ) && ( iHookerIndex == iWndprocHookCounter - 1 ) ) + { + // If we don't restore it the plugins wndproc will + // still be called which is not there anymore -> crash + SetWindowLong( WindowMain, GWL_WNDPROC, ( LONG )WndprocBackup ); + Console::Append( TEXT( "Wndproc hook removed (by host)" ) ); + Console::Append( TEXT( " " ) ); + + iHookerIndex = -1; + iWndprocHookCounter--; + } + + FreeLibrary( hDLL ); + hDLL = NULL; + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +bool InputPlugin::About( HWND hParent ) +{ + if( !plugin ) return false; + if( !plugin->About ) return false; + + plugin->About( hParent ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +bool InputPlugin::Config( HWND hParent ) +{ + if( !plugin ) return false; + if( !plugin->Config ) return false; + + plugin->Config( hParent ); + + // TODO: File extension could have changed (e.g. in_mp3) + // So we have to process ext_map here... + + return true; +} diff --git a/Externals/MusicMod/Player/Src/InputPlugin.h b/Externals/MusicMod/Player/Src/InputPlugin.h new file mode 100644 index 0000000000..2702fbe767 --- /dev/null +++ b/Externals/MusicMod/Player/Src/InputPlugin.h @@ -0,0 +1,96 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_INPUT_PLUGIN_H +#define PA_INPUT_PLUGIN_H + + + +#include "Global.h" +#include "Plugin.h" +#include "Playback.h" +#include "Playlist.h" +#include "Winamp/In2.h" +#include +#include + +using namespace std; + + + +typedef In_Module * ( * WINAMP_INPUT_GETTER )( void ); + + + +class InputPlugin; + +extern map ext_map; +extern vector input_plugins; +extern InputPlugin * active_input_plugin; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Winamp input plugin wrapper +//////////////////////////////////////////////////////////////////////////////// +class InputPlugin : public Plugin +{ +public: + InputPlugin( TCHAR * szDllpath, bool bKeepLoaded ); + ~InputPlugin(); + + bool Load(); + bool Unload(); + + TCHAR * GetTypeString() { return TEXT( "Input" ); } + int GetTypeStringLen() { return 5; } + PluginType GetType() { return PLUGIN_TYPE_INPUT; } + + inline bool IsActive() { return false; } + + bool About( HWND hParent ); + bool Config( HWND hParent ); + + In_Module * plugin; // I moved this from private to public + +private: + TCHAR * szFilters; + int iFiltersLen; + + + bool Integrate(); + bool DisIntegrate(); + + + // TODO + friend bool OpenPlay( TCHAR * szFilename, int iNumber ); + friend bool Playback_PrevOrNext( bool bPrevOrNext ); + friend bool Playback::Play(); + friend bool Playback::Pause(); + friend bool Playback::Stop(); + friend bool Playback::UpdateSeek(); // this one calls some plugin-> members + friend int Playback::PercentToMs( float fPercent ); + friend bool Playback::SeekPercent( float fPercent ); + friend bool SeekRelative( int ms ); + friend void Playback_Volume_Set( int iVol ); + friend bool Playback::Pan::Set( int iPan ); + friend void Playback_Eq_Set( int iPresetIndex ); + + friend void AddFiles(); + friend void VSAAdd( void * data, int timestamp ); + friend void VSAAddPCMData( void * PCMData, int nch, int bps, int timestamp ); + friend int Playlist::GetTitle( int iIndex, char * szAnsiTitle, int iChars ); +}; + + + +#endif // PA_INPUT_PLUGIN_H diff --git a/Externals/MusicMod/Player/Src/Lock.cpp b/Externals/MusicMod/Player/Src/Lock.cpp new file mode 100644 index 0000000000..8590b944d0 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Lock.cpp @@ -0,0 +1,16 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +// #include "Lock.h" + + +// Code moved to for inlining! diff --git a/Externals/MusicMod/Player/Src/Lock.h b/Externals/MusicMod/Player/Src/Lock.h new file mode 100644 index 0000000000..113c93f50e --- /dev/null +++ b/Externals/MusicMod/Player/Src/Lock.h @@ -0,0 +1,111 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_LOCK_H +#define PA_LOCK_H + + + +#include "Global.h" + + + +// INFO: http://www.devarticles.com/c/a/Cplusplus/Multithreading-in-C/3/ + + +// #define LOCK_USES_MUTEX + + + +//////////////////////////////////////////////////////////////////////////////// +/// Lock for thread synchronization +//////////////////////////////////////////////////////////////////////////////// +class Lock +{ +public: + Lock( TCHAR * szName ); + ~Lock(); + + void Enter(); + void Leave(); + +private: +#ifndef LOCK_USES_MUTEX + CRITICAL_SECTION * m_pCrit; +#else + HANDLE hLock; +#endif +}; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Creates a new named lock. +/// Note: Don't use the same name for several locks +//////////////////////////////////////////////////////////////////////////////// +inline Lock::Lock( TCHAR * szName ) +{ +#ifndef LOCK_USES_MUTEX + m_pCrit = new CRITICAL_SECTION; + InitializeCriticalSection( m_pCrit ); +#else + hLock = CreateMutex( NULL, TRUE, szName ); + ReleaseMutex( hLock ); +#endif +} + + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +inline Lock::~Lock() +{ +#ifndef LOCK_USES_MUTEX + DeleteCriticalSection( m_pCrit ); + delete [] m_pCrit; +#else + CloseHandle( hLock ); +#endif +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// Closes lock. +//////////////////////////////////////////////////////////////////////////////// +inline void Lock::Enter() +{ +#ifndef LOCK_USES_MUTEX + EnterCriticalSection( m_pCrit ); +#else + WaitForSingleObject( hLock, INFINITE ); +#endif +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// Opens lock. +//////////////////////////////////////////////////////////////////////////////// +inline void Lock::Leave() +{ +#ifndef LOCK_USES_MUTEX + LeaveCriticalSection( m_pCrit ); +#else + ReleaseMutex( hLock ); +#endif +} + + + +#endif // PA_LOCK_H diff --git a/Externals/MusicMod/Player/Src/Main.cpp b/Externals/MusicMod/Player/Src/Main.cpp new file mode 100644 index 0000000000..61fd7428f4 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Main.cpp @@ -0,0 +1,816 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////////// +// Documentation +// ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ + +/* ---- The Seekbar ---- + Rebar.cpp handle the progress (playback position) bar. WndprocMain() is called every + second during playback. And several times a second when the cursor is moving over the + main window. If WM_TIMER called WndprocMain it calls Playback::UpdateSeek(); */ + + +/////////////////////////////////// + + +#include "Main.h" +#include "GlobalVersion.h" +#include "Playlist.h" +#include "Console.h" +#include "Status.h" +#include "Rebar.h" +#include "Playback.h" +#include "PluginManager.h" +#include "DspModule.h" +#include "VisModule.h" +#include "InputPlugin.h" +#include "OutputPlugin.h" +#include "InputPlugin.h" +#include "AddDirectory.h" +#include "AddFiles.h" +#include "Winamp.h" +#include "Winamp/wa_ipc.h" +#include "Config.h" +#include + +#define CLASSNAME_MAIN TEXT( "Winamp v1.x" ) +#define MAIN_TITLE PLAINAMP_LONG_TITLE + +#define MAIN_WIDTH 731 +#define MAIN_HEIGHT 562 + + + +HWND WindowMain = NULL; // extern +HMENU main_context_menu = NULL; // extern +HMENU play_context_menu = NULL; +HMENU opts_context_menu = NULL; +HMENU playback_context_menu = NULL; + + + +LRESULT CALLBACK WndprocMain( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); + + + +WINDOWPLACEMENT WinPlaceMain; + +void WinPlaceMainCallback( ConfVar * var ) +{ + if( !IsWindow( WindowMain ) ) return; + GetWindowPlacement( WindowMain, &WinPlaceMain ); +} + +const int cxScreen = GetSystemMetrics( SM_CXFULLSCREEN ); +const int cyScreen = GetSystemMetrics( SM_CYFULLSCREEN ); + +RECT rMainDefault = { + ( cxScreen - MAIN_WIDTH ) / 2, + ( cyScreen - MAIN_HEIGHT ) / 2, + ( cxScreen - MAIN_WIDTH ) / 2 + MAIN_WIDTH, + ( cyScreen - MAIN_HEIGHT ) / 2 + MAIN_HEIGHT +}; + +ConfWinPlaceCallback cwpcWinPlaceMain( + &WinPlaceMain, + TEXT( "WinPlaceMain" ), + &rMainDefault, + WinPlaceMainCallback +); + + +bool bMinimizeToTray; +ConfBool cbMinimizeToTray( &bMinimizeToTray, TEXT( "MinimizeToTray" ), CONF_MODE_PUBLIC, true ); + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool BuildMainWindow() +{ + + //#ifndef NOGUI + + // ======================================================================================= + /* Disabling this window creation cause continuous "Error setting DirectSound cooperative level" + messages for some reason. So I leave it here for now. */ + + // Register class + WNDCLASS wc = { + 0, // UINT style + WndprocMain, // WNDPROC lpfnWndProc + 0, // int cbClsExtra + 0, // int cbWndExtra + g_hInstance, // HINSTANCE hInstance + LoadIcon( g_hInstance, TEXT( "IDI_ICON1" ) ), // HICON hIcon + LoadCursor( NULL, IDC_ARROW ), // HCURSOR hCursor + ( HBRUSH )COLOR_WINDOW, // HBRUSH hbrBackground + NULL, // LPCTSTR lpszMenuName + CLASSNAME_MAIN // LPCTSTR lpszClassName + }; + + if( !RegisterClass( &wc ) ) return false; + + + // Create WindowMain + WindowMain = CreateWindowEx( + WS_EX_WINDOWEDGE, // DWORD dwExStyle + CLASSNAME_MAIN, // LPCTSTR lpClassName + MAIN_TITLE, // LPCTSTR lpWindowName + WS_OVERLAPPED | // DWORD dwStyle + // WS_VISIBLE | // + WS_CLIPCHILDREN | // + WS_BORDER | // + WS_SYSMENU | // + WS_THICKFRAME | // + WS_MINIMIZEBOX | // + WS_MAXIMIZEBOX, // + rMainDefault.left, // int x + rMainDefault.top, // int y + rMainDefault.right - rMainDefault.left, // int nWidth + rMainDefault.bottom - rMainDefault.top, // int nHeight + NULL, // HWND hWndParent + NULL, // HMENU hMenu + g_hInstance, // HINSTANCE hInstance + NULL // LPVOID lpParam + ); + // ======================================================================================= + + //#endif + + // ======================================================================================= + #ifdef NOGUI + + // If this is not called a crash occurs + PlaylistView::Create(); + + // We have now done what we wanted so we skip the rest of the file + return true; + + #else + // ======================================================================================= + + + if( !WindowMain ) + { + UnregisterClass( CLASSNAME_MAIN, g_hInstance ); + return false; + } + + + + + // Build context menu + HMENU main_menu = CreateMenu(); + HMENU plainamp_menu = CreatePopupMenu(); + HMENU playback_menu = CreatePopupMenu(); + HMENU playlist_menu = CreatePopupMenu(); + HMENU windows_menu = CreatePopupMenu(); + + + // Plainamp + AppendMenu( plainamp_menu, MF_STRING, WINAMP_OPTIONS_PREFS, TEXT( "Preferences \tCtrl+P" ) ); + AppendMenu( plainamp_menu, MF_SEPARATOR | MF_DISABLED | MF_GRAYED, ( UINT_PTR )-1, NULL ); + AppendMenu( plainamp_menu, MF_STRING, WINAMP_HELP_ABOUT, TEXT( "&About\tCtrl+F1" ) ); + AppendMenu( plainamp_menu, MF_STRING, WINAMP_FILE_QUIT, TEXT( "&Exit \tAlt+F4" ) ); + + // Playback + AppendMenu( playback_menu, MF_STRING, WINAMP_BUTTON1, TEXT( "Pre&vious \tZ" ) ); + AppendMenu( playback_menu, MF_STRING, WINAMP_BUTTON2, TEXT( "&Play\tX" ) ); + AppendMenu( playback_menu, MF_STRING, WINAMP_BUTTON3, TEXT( "P&ause\tC" ) ); + AppendMenu( playback_menu, MF_STRING, WINAMP_BUTTON4, TEXT( "&Stop\tV" ) ); + AppendMenu( playback_menu, MF_STRING, WINAMP_BUTTON5, TEXT( "&Next\tB" ) ); + + // Playlist + AppendMenu( playlist_menu, MF_STRING, ID_PE_OPEN, TEXT( "&Open\tCtrl+O" ) ); + AppendMenu( playlist_menu, MF_STRING, ID_PE_SAVEAS, TEXT( "&Save as\tCtrl+S" ) ); + AppendMenu( playlist_menu, MF_SEPARATOR | MF_DISABLED | MF_GRAYED, ( UINT_PTR )-1, NULL ); + AppendMenu( playlist_menu, MF_STRING, WINAMP_FILE_PLAY, TEXT( "Add &files\tL" ) ); + AppendMenu( playlist_menu, MF_STRING, WINAMP_FILE_DIR, TEXT( "Add &directory\tShift+L" ) ); + AppendMenu( playlist_menu, MF_SEPARATOR | MF_DISABLED | MF_GRAYED, ( UINT_PTR )-1, NULL ); + AppendMenu( playlist_menu, MF_STRING, PLAINAMP_PL_REM_SEL, TEXT( "Remove selected\tDel" ) ); + AppendMenu( playlist_menu, MF_STRING, PLAINAMP_PL_REM_CROP, TEXT( "Remove unselected \tCtrl+Del" ) ); + AppendMenu( playlist_menu, MF_STRING, ID_PE_CLEAR, TEXT( "Remove all\tCtrl+Shift+Del" ) ); + AppendMenu( playlist_menu, MF_SEPARATOR | MF_DISABLED | MF_GRAYED, ( UINT_PTR )-1, NULL ); + AppendMenu( playlist_menu, MF_STRING, ID_PE_SELECTALL, TEXT( "Select &all\tCtrl+A" ) ); + AppendMenu( playlist_menu, MF_STRING, ID_PE_NONE, TEXT( "Select &zero\tCtrl+Shift+A" ) ); + AppendMenu( playlist_menu, MF_STRING, ID_PE_INVERT, TEXT( "Select &invert\tCtrl+I" ) ); + + // Windows + AppendMenu( windows_menu, MF_STRING, MENU_MAIN_WINDOWS_CONSOLE, TEXT( "&Console" ) ); + AppendMenu( windows_menu, MF_STRING, MENU_MAIN_WINDOWS_MANAGER, TEXT( "Plugin &Manager" ) ); + + // Main + AppendMenu( main_menu, MF_STRING | MF_POPUP, ( UINT_PTR )plainamp_menu, TEXT( "&Plainamp" ) ); + AppendMenu( main_menu, MF_STRING | MF_POPUP, ( UINT_PTR )playback_menu, TEXT( "Play&back" ) ); + AppendMenu( main_menu, MF_STRING | MF_POPUP, ( UINT_PTR )playlist_menu, TEXT( "Play&list" ) ); + AppendMenu( main_menu, MF_STRING | MF_POPUP, ( UINT_PTR )windows_menu, TEXT( "&Windows" ) ); + + SetMenu( WindowMain, main_menu ); + + //////////////////////////////////////////////////////////////////////////////// + + main_context_menu = CreatePopupMenu(); + AppendMenu( main_context_menu, MF_STRING, WINAMP_HELP_ABOUT, TEXT( "Plainamp" ) ); + + AppendMenu( main_context_menu, MF_SEPARATOR | MF_DISABLED | MF_GRAYED, ( UINT_PTR )-1, NULL ); + + play_context_menu = CreatePopupMenu(); + AppendMenu( play_context_menu, MF_STRING, WINAMP_FILE_PLAY, TEXT( "Files \tL" ) ); + AppendMenu( play_context_menu, MF_STRING, WINAMP_FILE_DIR, TEXT( "Folder \tShift+L" ) ); + AppendMenu( main_context_menu, MF_STRING | MF_POPUP, ( UINT_PTR )play_context_menu, TEXT( "Play" ) ); + + AppendMenu( main_context_menu, MF_SEPARATOR | MF_DISABLED | MF_GRAYED, ( UINT_PTR )-1, NULL ); + + AppendMenu( main_context_menu, MF_STRING | MF_DISABLED | MF_GRAYED | MF_CHECKED, WINAMP_MAIN_WINDOW, TEXT( "Main Window\tAlt+W" ) ); + AppendMenu( main_context_menu, MF_STRING | MF_DISABLED | MF_GRAYED | MF_CHECKED, WINAMP_OPTIONS_PLEDIT, TEXT( "Playlist Editor\tAlt+E" ) ); + AppendMenu( main_context_menu, MF_STRING | MF_DISABLED | MF_GRAYED, WINAMP_OPTIONS_EQ, TEXT( "Equalizer\tAlt+G" ) ); + AppendMenu( main_context_menu, MF_STRING | MF_DISABLED | MF_GRAYED, WINAMP_OPTIONS_VIDEO, TEXT( "Video\tAlt+V" ) ); + AppendMenu( main_context_menu, MF_STRING, PLAINAMP_TOGGLE_CONSOLE, TEXT( "Console" ) ); + AppendMenu( main_context_menu, MF_STRING, PLAINAMP_TOGGLE_MANAGER, TEXT( "Plugin Manager" ) ); + + + /* + AppendMenu( main_context_menu, MF_STRING | MF_DISABLED | MF_GRAYED, MENU_MAIN_CONTEXT_MANAGER, TEXT( "Plugin Manager" ) ); + AppendMenu( main_context_menu, MF_STRING | MF_DISABLED | MF_GRAYED, MENU_MAIN_CONTEXT_CONSOLE, TEXT( "Console" ) ); + */ + + AppendMenu( main_context_menu, MF_SEPARATOR | MF_DISABLED | MF_GRAYED, ( UINT_PTR )-1, NULL ); + + opts_context_menu = CreatePopupMenu(); + AppendMenu( opts_context_menu, MF_STRING, WINAMP_OPTIONS_PREFS, TEXT( "Preferences \tCtrl+P" ) ); + AppendMenu( main_context_menu, MF_STRING | MF_POPUP, ( UINT_PTR )opts_context_menu, TEXT( "Options" ) ); + + playback_context_menu = CreatePopupMenu(); + AppendMenu( playback_context_menu, MF_STRING, WINAMP_BUTTON1, TEXT( "Previous \tZ" ) ); + AppendMenu( playback_context_menu, MF_STRING, WINAMP_BUTTON2, TEXT( "Play\tX" ) ); + AppendMenu( playback_context_menu, MF_STRING, WINAMP_BUTTON3, TEXT( "Pause\tC" ) ); + AppendMenu( playback_context_menu, MF_STRING, WINAMP_BUTTON4, TEXT( "Stop\tV" ) ); + AppendMenu( playback_context_menu, MF_STRING, WINAMP_BUTTON5, TEXT( "Next\tB" ) ); + AppendMenu( main_context_menu, MF_STRING | MF_POPUP, ( UINT_PTR )playback_context_menu, TEXT( "Playback" ) ); + + AppendMenu( main_context_menu, MF_SEPARATOR | MF_DISABLED | MF_GRAYED, ( UINT_PTR )-1, NULL ); + + AppendMenu( main_context_menu, MF_STRING, WINAMP_FILE_QUIT, TEXT( "Exit" ) ); + + + Toolbar::Create(); // This removes all buttons and status bars + //BuildMainStatus(); + + + // ======================================================================================= + // If this is not created a crash occurs + PlaylistView::Create(); + + Playlist::Create(); + + + SetWindowPlacement( WindowMain, &WinPlaceMain ); + + + return true; +#endif +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void About( HWND hParent ) +{ + // For info goto + // http://predef.sourceforge.net/precomp.html + + TCHAR szBuildDetails[ 1000 ] = ""; + +#ifdef __GNUC__ + _stprintf( szBuildDetails, + TEXT( "\n\n\nGNU GCC " __VERSION__ "\n" __DATE__ ) + ); +#else +# ifdef _MSC_VER + _stprintf( + szBuildDetails, + TEXT( "\n\n\nMicrosoft Visual C++ %i.%i\n" __DATE__ ), + _MSC_VER / 100 - 6, + ( _MSC_VER % 100 ) / 10 + ); +# endif +#endif + + TCHAR szBuffer[ 1000 ]; + _stprintf( + szBuffer, + PLAINAMP_LONG_TITLE TEXT( "\n" + "\n" + "Copyright İ 2005 Sebastian Pipping \n" + "\n" + "\n" + "--> http://www.hartwork.org" + "%s" + ), + szBuildDetails + ); + + MessageBox( + hParent, + szBuffer, + TEXT( "About" ), + MB_ICONINFORMATION + ); +} + +#define TRAY_MAIN_ID 13 +#define TRAY_MSG ( WM_USER + 1 ) + + + +NOTIFYICONDATA nid; + +bool AddTrayIcon( HWND hwnd ) +{ + ZeroMemory( &nid, sizeof( NOTIFYICONDATA ) ); + nid.cbSize = sizeof( NOTIFYICONDATA ); + nid.hWnd = hwnd; + nid.uID = TRAY_MAIN_ID; + nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + nid.uCallbackMessage = TRAY_MSG; + nid.hIcon = LoadIcon( g_hInstance, TEXT( "IDI_ICON1" ) ); + _tcscpy( nid.szTip, TEXT( "Plainamp" ) ); + + return ( Shell_NotifyIcon( NIM_ADD, &nid ) != 0 ); +} + +void RemoveTrayIcon() +{ + Shell_NotifyIcon( NIM_DELETE, &nid ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +LRESULT CALLBACK WndprocMain( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + // Tool windows are hidden on minimize/re-shown on restore + static bool bConsoleTodo = false; + static bool bManagerTodo = false; + static bool bRemoveIcon = false; + + #ifdef NOGUI + //INFO_LOG(AUDIO,"DLL > Main.cpp:WndprocMain() was called. But nothing will be done. \n"); + #else + Console::Append( TEXT( "Main.cpp:WndprocMain was called" ) ); + #endif + + + switch( message ) + { + + #ifdef NOGUI + //printf(" > WndprocMain message: %i\n", message); + #else + + case WM_SETFOCUS: + // To re-"blue" + SetFocus( WindowPlaylist ); + break; + + case WM_CREATE: + // Note: [WindowMain] is not valid yet but [hwnd] is! + Console::Create(); // make the console window + PluginManager::Build(); // make the plugin window + break; + + case WM_NOTIFY: + { + NMHDR * hdr = ( NMHDR * )lp; + + switch( hdr->code ) + { + case LVN_GETDISPINFO: + { + LV_DISPINFO * lpdi = ( LV_DISPINFO * )lp; + playlist->Fill( lpdi->item ); + } + return 0; +/* + case LVN_ODCACHEHINT: + { + LPNMLVCACHEHINT lpCacheHint = (LPNMLVCACHEHINT)lParam; + / + This sample doesn't use this notification, but this is sent when the + ListView is about to ask for a range of items. On this notification, + you should load the specified items into your local cache. It is still + possible to get an LVN_GETDISPINFO for an item that has not been cached, + therefore, your application must take into account the chance of this + occurring. + / + } + return 0; + + case LVN_ODFINDITEM: + { + LPNMLVFINDITEM lpFindItem = (LPNMLVFINDITEM)lParam; + / + This sample doesn't use this notification, but this is sent when the + ListView needs a particular item. Return -1 if the item is not found. + / + } + return 0; +*/ + case NM_CUSTOMDRAW: + { + NMLVCUSTOMDRAW * custom = ( NMLVCUSTOMDRAW * )lp; + + switch( custom->nmcd.dwDrawStage ) + { + case CDDS_PREPAINT: + return CDRF_NOTIFYITEMDRAW; + + case CDDS_ITEMPREPAINT: + return CDRF_NOTIFYSUBITEMDRAW; + + case ( CDDS_SUBITEM | CDDS_ITEMPREPAINT ): + { + // This is the prepaint stage for an item. Here's where we set the + // item's text color. Our return value will tell Windows to draw the + // item itself, but it will use the new color we set here. + // We'll cycle the colors through red, green, and light blue. + + if( custom->nmcd.dwItemSpec == playlist->GetCurIndex() ) + { + custom->clrTextBk = RGB( 225, 225, 225 ); + } + else + { + if( custom->nmcd.dwItemSpec & 1 ) + custom->clrTextBk = RGB( 245, 248, 250 ); + else + custom->clrTextBk = RGB( 255, 255, 255 ); + } + + if( custom->iSubItem == 0 ) + custom->clrText = RGB( 255, 0, 0 ); + else + custom->clrText = RGB( 0, 0, 0 ); + + +/* + if ( (custom->nmcd.dwItemSpec % 3) == 0 ) + crText = RGB(255,0,0); + else if ( (custom->nmcd.dwItemSpec % 3) == 1 ) + crText = RGB(0,255,0); + else + crText = RGB(128,128,255); + + // Store the color back in the NMLVCUSTOMDRAW struct. + custom->clrText = crText; +*/ + // Tell Windows to paint the control itself. + } + /* + custom->clrText = RGB( 190, 190, 190 ); + custom->clrTextBk = RGB( 255, 0, 0 );*/ + return CDRF_DODEFAULT; + + + } + break; + + } + + /* + case RBN_CHILDSIZE: + { + NMREBARCHILDSIZE * chs = ( NMREBARCHILDSIZE * )lp; + const int width_client = chs->rcChild.right - chs->rcChild.left; + int diff = width_client - 120; + if( diff > 0 ) + { + const int width_band = chs->rcBand.right - chs->rcBand.left; + // chs->rcChild.right = chs->rcChild.left + 120; + + DEBUGF( 1000, "CHILDSIZE [%i] [%i]", chs->uBand, width_band ); + + const int client_band_diff = width_band - width_client; + chs->rcBand.right = chs->rcBand.left + 120 + client_band_diff; + // chs->uBand + + + REBARBANDINFO rbbi; + rbbi.cbSize = sizeof( REBARBANDINFO ); + rbbi.fMask = RBBIM_SIZE; + rbbi.cx = 154; //width_band + diff; + LRESULT lResult = SendMessage( + rebar, + RB_SETBANDINFO, + chs->uBand, + ( LPARAM )( REBARBANDINFO * )&rbbi + ); + + } + break; + } + */ + case RBN_HEIGHTCHANGE: + { + const int iRebarHeightBefore = iRebarHeight; + RECT r; + GetWindowRect( WindowRebar, &r ); + iRebarHeight = r.bottom - r.top; + + InvalidateRect( WindowRebar, NULL, TRUE ); + InvalidateRect( WindowPlaylist, NULL, TRUE ); + + RECT client; + GetClientRect( WindowMain, &client ); + PostMessage( + hwnd, + WM_SIZE, + SIZE_RESTORED, + ( client.right - client.left ) << 16 | + ( client.bottom - client.top ) + ); + + break; + } + } + break; + } + + case WM_SYSKEYDOWN: + switch( wp ) // [Alt]+[...] + { + case VK_UP: + case VK_DOWN: + SetFocus( WindowPlaylist ); + SendMessage( WindowPlaylist, message, wp, lp ); + break; + } + break; + + case WM_KEYDOWN: + case WM_KEYUP: + SetFocus( WindowPlaylist ); + SendMessage( WindowPlaylist, message, wp, lp ); + break; + + case WM_COMMAND: + { + const int code = HIWORD( wp ); + switch( code ) + { + case 1: // also == CBN_SELCHANGE + { + if( ( HWND )lp == WindowOrder ) + { + LRESULT res = SendMessage( WindowOrder, CB_GETCURSEL, 0, 0 ); + if( res == CB_ERR ) break; + Playback::Order::SetMode( ( int )res ); + } + else if( ( HWND )lp == WindowEq ) + { + LRESULT res = SendMessage( WindowEq, CB_GETCURSEL, 0, 0 ); + if( res == CB_ERR ) break; + Playback::Eq::SetIndex( ( int )( res - 1 ) ); + } + + return WndprocWinamp( hwnd, message, wp, lp ); + } + + case 0: + { + // Menu + const int id = LOWORD( wp ); + switch( id ) + { + case MENU_MAIN_WINDOWS_CONSOLE: + Console::Popup(); + break; + + case MENU_MAIN_WINDOWS_MANAGER: + PluginManager::Popup(); + break; + + } +/* + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "default 1 id = <%i>" ), id ); + MessageBox( 0, szBuffer, "", 0 ); +*/ + return WndprocWinamp( hwnd, message, wp, lp ); + } + + default: + return WndprocWinamp( hwnd, message, wp, lp ); + } + break; + } + + case WM_GETMINMAXINFO: + { + MINMAXINFO * mmi = ( MINMAXINFO * )lp; + mmi->ptMinTrackSize.x = 400; + mmi->ptMinTrackSize.y = 300; + return 0; + } + + case WM_SIZE: + { + // Resize children + RECT client; + GetClientRect( WindowMain, &client ); + + const int iClientWidth = client.right - client.left; + const int iClientHeight = client.bottom - client.top; + const int iPlaylistHeight = iClientHeight - iRebarHeight - iStatusHeight; + + if( WindowRebar ) + MoveWindow( WindowRebar, 0, 0, iClientWidth, iRebarHeight, TRUE ); + if( WindowPlaylist ) + { + MoveWindow( WindowPlaylist, 0, iRebarHeight, iClientWidth, iPlaylistHeight, TRUE ); + playlist->Resize( WindowMain ); + } + if( WindowStatus ) + MoveWindow( WindowStatus, 0, iRebarHeight + iPlaylistHeight, iClientWidth, iStatusHeight, TRUE ); + break; + } + + case WM_TIMER: + Playback::UpdateSeek(); + break; + + case WM_CONTEXTMENU: + PostMessage( hwnd, WM_COMMAND, WINAMP_MAINMENU, 0 ); + break; + + case WM_CLOSE: + { + // Clean shutdown + + // Stop + Playback::Stop(); + + // Dsp + DspLock.Enter(); + for( int d = active_dsp_count - 1; d >= 0; d-- ) + { + DspLock.Leave(); + active_dsp_mods[ d ]->Stop(); + DspLock.Enter(); + } + DspLock.Leave(); + + + // Vis + VisLock.Enter(); + for( int v = active_vis_count - 1; v >= 0; v-- ) + { + VisLock.Leave(); + active_vis_mods[ v ]->Stop(); + VisLock.Enter(); + } + VisLock.Leave(); + } + break; + + case WM_DESTROY: + { + // ======================================================================================= + // Save playlist + /* + TCHAR * szPlaylistMind = new TCHAR[ iHomeDirLen + 12 + 1 ]; + memcpy( szPlaylistMind, szHomeDir, iHomeDirLen * sizeof( TCHAR ) ); + memcpy( szPlaylistMind + iHomeDirLen, TEXT( "Plainamp.m3u" ), 12 * sizeof( TCHAR ) ); + szPlaylistMind[ iHomeDirLen + 12 ] = TEXT( '\0' ); + + Playlist::ExportPlaylistFile( szPlaylistMind ); + + delete [] szPlaylistMind; + */ + // ======================================================================================= + + cwpcWinPlaceMain.TriggerCallback(); + cwpcWinPlaceMain.RemoveCallback(); + + Console::Destroy(); + PluginManager::Destroy(); + + if( bRemoveIcon ) + { + RemoveTrayIcon(); + bRemoveIcon = false; + } + + PostQuitMessage( 0 ); + return 0; + } + + case WM_ACTIVATEAPP: + { + if( wp != TRUE ) break; + + // Also bring console/manager to front + const bool bConsoleVisible = ( IsWindowVisible( WindowConsole ) == TRUE ); + const bool bManagerVisible = ( IsWindowVisible( WindowManager ) == TRUE ); + const bool bMainTodo = ( bConsoleVisible || bManagerVisible ); + + if( bConsoleVisible ) BringWindowToTop( WindowConsole ); + if( bManagerVisible ) BringWindowToTop( WindowManager ); + if( bMainTodo ) BringWindowToTop( WindowMain ); + + break; + } + + case WM_SYSCOMMAND: + switch( ( wp & 0xFFF0 ) ) + { + case SC_CLOSE: + if( !SendMessage( WindowMain, WM_WA_IPC, 0, IPC_HOOK_OKTOQUIT ) ) + { + return 0; + } + break; + + case SC_MINIMIZE: + { + // Hide console/manager on minimize + bConsoleTodo = ( IsWindowVisible( WindowConsole ) == TRUE ); + if( bConsoleTodo ) ShowWindow( WindowConsole, SW_HIDE ); + + bManagerTodo = ( IsWindowVisible( WindowManager ) == TRUE ); + if( bManagerTodo ) ShowWindow( WindowManager, SW_HIDE ); + + if( bMinimizeToTray ) + { + if( !bRemoveIcon ) + { + bRemoveIcon = AddTrayIcon( hwnd ); + } + + ShowWindow( hwnd, FALSE ); + return 0; + } + + break; + } + + case SC_RESTORE: + { + const LRESULT res = DefWindowProc( hwnd, message, wp, lp ); + + // Unhide console/manager + const bool bMainTodo = ( bConsoleTodo || bManagerTodo ); + if( bConsoleTodo ) ShowWindow( WindowConsole, SW_SHOW ); + if( bManagerTodo ) ShowWindow( WindowManager, SW_SHOW ); + if( bMainTodo ) BringWindowToTop( WindowMain ); + + return res; + } + } + break; + + case TRAY_MSG: + switch( lp ) + { + case WM_RBUTTONDOWN: // TODO: context menu instead + case WM_LBUTTONDOWN: + if( IsWindowVisible( hwnd ) == FALSE ) + { + ShowWindow( hwnd, TRUE ); + } + break; + + case WM_RBUTTONUP: // TODO: context menu instead + case WM_LBUTTONUP: + if( bRemoveIcon ) + { + RemoveTrayIcon(); + bRemoveIcon = false; + } + break; + + } + return 0; + #endif + default: + return WndprocWinamp( hwnd, message, wp, lp ); + } + return DefWindowProc( hwnd, message, wp, lp ); + +} diff --git a/Externals/MusicMod/Player/Src/Main.h b/Externals/MusicMod/Player/Src/Main.h new file mode 100644 index 0000000000..c8a2b4309c --- /dev/null +++ b/Externals/MusicMod/Player/Src/Main.h @@ -0,0 +1,51 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_MAIN_H +#define PA_MAIN_H + + + +#include "Global.h" +#include "Winamp/wa_msgids.h" + + + +// TODO + + + +#define MENU_MAIN_WINDOWS_CONSOLE 41 +#define MENU_MAIN_WINDOWS_MANAGER 42 + +#define MENU_MAIN_CONTEXT_MANAGER WINAMP_MAIN_WINDOW // first window, for gen_dl +#define MENU_MAIN_CONTEXT_CONSOLE WINAMP_OPTIONS_VIDEO // last window, for gen_template + + +#define PLAINAMP_TOGGLE_CONSOLE 50001 +#define PLAINAMP_TOGGLE_MANAGER 50002 + + + + +extern HWND WindowMain; +extern HMENU main_context_menu; + + + +bool BuildMainWindow(); +void About( HWND hParent ); +LRESULT CALLBACK WndprocMain( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); + + + +#endif // PA_MAIN_H diff --git a/Externals/MusicMod/Player/Src/Output.cpp b/Externals/MusicMod/Player/Src/Output.cpp new file mode 100644 index 0000000000..86bc6f4a08 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Output.cpp @@ -0,0 +1,382 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Output.h" +#include "OutputPlugin.h" +#include "Console.h" +#include + + + +int iNullSampleRate; +int iNullSumBytesPerSample; +int iNullWrittenMillis; +bool bNullPlaying; +int bNullPaused; +DWORD dwNullOpenTimestamp; +DWORD dwNullPauseTimestamp; + +const int NULL_DEFAULT_LATENCY = 1000; + + +int Output_Open( int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms ); +void Output_Close(); +int Output_Write( char * buf, int len ); +int Output_CanWrite(); +int Output_IsPlaying(); +int Output_Pause( int pause ); +void Output_SetVolume( int volume ); +void Output_SetPan( int pan ); +void Output_Flush( int t ); +int Output_GetOutputTime(); +int Output_GetWrittenTime(); + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +Out_Module output_server = { + OUT_VER, // int version + "Plainamp Output Server", // char * description; + 0x7fffffff, // int id + NULL, // HWND hMainWindow + NULL, // HINSTANCE hDllInstance + NULL, // void (*Config)(HWND hwndParent); + NULL, // void (*About)(HWND hwndParent); + NULL, // void (*Init)(); + NULL, // void (*Quit)(); + Output_Open, // int (*Open)(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms); + Output_Close, // void (*Close)(); + Output_Write, // int (*Write)(char *buf, int len); + Output_CanWrite, // int (*CanWrite)(); + Output_IsPlaying, // int (*IsPlaying)(); + Output_Pause, // int (*Pause)(int pause); + Output_SetVolume, // void (*SetVolume)(int volume); + Output_SetPan, // void (*SetPan)(int pan); + Output_Flush, // void (*Flush)(int t); + Output_GetOutputTime, // int (*GetOutputTime)(); + Output_GetWrittenTime, // int (*GetWrittenTime)(); +}; + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int Output_Open( int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms ) +{ + if( active_output_count > 0 ) + { + // Maximum + int res = 0; + int res_temp; + for( int i = 0; i < active_output_count; i++ ) + { + res_temp = active_output_plugins[ i ]->plugin->Open( samplerate, numchannels, bitspersamp, bufferlenms, prebufferms ); + if( res_temp > res ) res = res_temp; + } + return res; + } + else + { + iNullSampleRate = samplerate; + iNullSumBytesPerSample = numchannels * ( bitspersamp / 8 ); + iNullWrittenMillis = 0; + bNullPlaying = false; + bNullPaused = 0; + dwNullOpenTimestamp = GetTickCount(); + + return NULL_DEFAULT_LATENCY; + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void Output_Close() +{ + if( active_output_count > 0 ) + { + for( int i = 0; i < active_output_count; i++ ) + { + active_output_plugins[ i ]->plugin->Close(); + } + } + else + { + bNullPlaying = false; + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int Output_Write( char * buf, int len ) +{ + if( active_output_count > 0 ) + { + // Maximum + int res = 0; + int res_temp; + for( int i = 0; i < active_output_count; i++ ) + { + res_temp = active_output_plugins[ i ]->plugin->Write( buf, len ); + if( res_temp > res ) res = res_temp; + } + return res; + } + else + { + if( iNullWrittenMillis == 0 ) + { + dwNullOpenTimestamp = GetTickCount(); + bNullPlaying = true; + } + + iNullWrittenMillis += ( len / iNullSumBytesPerSample ) * 1000 / iNullSampleRate; + return 0; // 0 == Success + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int Output_CanWrite() +{ + if( active_output_count > 0 ) + { + // Minimum + int res = 0x7fffffff; + int res_temp; + for( int i = 0; i < active_output_count; i++ ) + { + res_temp = active_output_plugins[ i ]->plugin->CanWrite(); + if( res_temp < res ) res = res_temp; + } + return res; + } + else + { + const int diff = Output_GetWrittenTime() - Output_GetOutputTime(); + if( diff >= NULL_DEFAULT_LATENCY ) + { + return 0; + } + else + { + return ( NULL_DEFAULT_LATENCY - diff ) * iNullSumBytesPerSample * iNullSampleRate / 1000; + } + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int Output_IsPlaying() +{ + if( active_output_count > 0 ) + { + // Maximum + int res = 0; + int res_temp; + for( int i = 0; i < active_output_count; i++ ) + { + res_temp = active_output_plugins[ i ]->plugin->IsPlaying(); + if( res_temp > res ) res = res_temp; + } + return res; + } + else + { + return ( Output_GetOutputTime() < Output_GetWrittenTime() ) ? 1 : 0; + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int Output_Pause( int pause ) +{ + if( active_output_count > 0 ) + { + // Maximum + int res = 0; + int res_temp; + for( int i = 0; i < active_output_count; i++ ) + { + res_temp = active_output_plugins[ i ]->plugin->Pause( pause ); + if( res_temp > res ) res = res_temp; + } + return res; + } + else + { + int res = bNullPaused; + if( pause && !bNullPaused ) + { + // Playback should be paused + dwNullPauseTimestamp = GetTickCount(); + bNullPaused = 1; + } + else if( !pause && bNullPaused ) + { + // Playback should be continued + // Add the gap length to the open timestamp like no gap exists + dwNullOpenTimestamp += ( GetTickCount() - dwNullPauseTimestamp ); + bNullPaused = 0; + } + return res; // Previous state + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void Output_SetVolume( int volume ) +{ + /* There was a call here with the volume value -666 that I could not see the source of, + so I added this check to see that we got a positive volume. */ + if(volume >= 0) + { + // ======================================================================================= + // The volume goes from 0 to 255 + //TCHAR szBuffer[ 5000 ]; + //_stprintf( szBuffer, TEXT( "DLL > Output_SetVolume <%i>" ), volume ); + //Console::Append( szBuffer ); + //Console::Append( TEXT( " " ) ); + //INFO_LOG(AUDIO, "DLL > Output_SetVolume <%i>\n", volume ); + // ======================================================================================= + + for( int i = 0; i < active_output_count; i++ ) + { + active_output_plugins[ i ]->plugin->SetVolume( volume ); + } + } + +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void Output_SetPan( int pan ) +{ + for( int i = 0; i < active_output_count; i++ ) + { + active_output_plugins[ i ]->plugin->SetPan( pan ); + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void Output_Flush( int t ) +{ + if( active_output_count > 0 ) + { + for( int i = 0; i < active_output_count; i++ ) + { + active_output_plugins[ i ]->plugin->Flush( t ); + } + } + else + { + dwNullOpenTimestamp = GetTickCount() - t; + iNullWrittenMillis = t; + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// Returns the number of milliseconds the song would be at +/// if we had zero latency. This value is never bigger than +/// the one returned by Output_GetWrittenTime(). +//////////////////////////////////////////////////////////////////////////////// +int Output_GetOutputTime() // <= GetWrittenTime() +{ + if( active_output_count > 0 ) + { + // Minimum + int res = 0x7fffffff; + int res_temp; + for( int i = 0; i < active_output_count; i++ ) + { + res_temp = active_output_plugins[ i ]->plugin->GetOutputTime(); + if( res_temp < res ) res = res_temp; + } + return res; + } + else + { + if( bNullPlaying ) + { + int res; + if( bNullPaused ) + { + res = dwNullPauseTimestamp - dwNullOpenTimestamp; + } + else + { + res = GetTickCount() - dwNullOpenTimestamp; + } + return MIN( res, iNullWrittenMillis ); + } + else + { + return 0; + } + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// Returns the number of milliseconds already written. +/// Due to latency this value is always bigger than +/// the value returned by Output_GetOutputTime(). +//////////////////////////////////////////////////////////////////////////////// +int Output_GetWrittenTime() +{ + if( active_output_count > 0 ) + { + // Maximum + int res = 0; + int res_temp; + for( int i = 0; i < active_output_count; i++ ) + { + res_temp = active_output_plugins[ i ]->plugin->GetWrittenTime(); + if( res_temp > res ) res = res_temp; + } + return res; + } + else + { + return iNullWrittenMillis; + } +} diff --git a/Externals/MusicMod/Player/Src/Output.h b/Externals/MusicMod/Player/Src/Output.h new file mode 100644 index 0000000000..21723f3e73 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Output.h @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_OUTPUT_H +#define PA_OUTPUT_H + + + +#include "Global.h" +#include "Winamp/Out.h" + + + +extern Out_Module output_server; + + + +#endif // PA_OUTPUT_H diff --git a/Externals/MusicMod/Player/Src/OutputPlugin.cpp b/Externals/MusicMod/Player/Src/OutputPlugin.cpp new file mode 100644 index 0000000000..73b01f7be5 --- /dev/null +++ b/Externals/MusicMod/Player/Src/OutputPlugin.cpp @@ -0,0 +1,316 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "OutputPlugin.h" +#include "Main.h" +#include "Unicode.h" +#include "Console.h" +#include "Config.h" +#include "Playback.h" + + + +vector output_plugins; // extern +OutputPlugin ** active_output_plugins = NULL; // extern +int active_output_count = 0; // extern + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +OutputPlugin::OutputPlugin( TCHAR * szDllpath, bool bKeepLoaded ) : Plugin( szDllpath ) +{ + iHookerIndex = -1; + + bActive = false; + iArrayIndex = -1; + plugin = NULL; + + if( !Load() ) + { + return; + } + + //////////////////////////////////////////////////////////////////////////////// + + // Quick hack!!! + TCHAR * szBuffer = new TCHAR[ 500 ]; // NOT LOCAL!!! + _stprintf( szBuffer, TEXT( "OutputPluginActive___%s" ), GetFilename() ); + ConfBool * cbActive = new ConfBool( &bActive, szBuffer, CONF_MODE_INTERNAL, false ); + cbActive->Read(); + printf("OutputPlugin > GetFilename() returned <%s>\n", szBuffer ); + + printf("OutputPlugin > We now have and \n", bActive, bKeepLoaded ); + + if( bActive ) + { + bActive = false; + Start(); + } + else + { + if( !bKeepLoaded ) + { + // Note: out_ds seems to do weird things + // when unloaded here!? + // So out_ds keeps loaded for now. + if( _tcscmp( GetFilename(), TEXT( "out_ds.dll" ) ) ) + { + printf("OutputPlugin > Unload called from OutputPlugin::OutputPlugin\n"); + Unload(); + } + } + } + // Quick hack!!! + //////////////////////////////////////////////////////////////////////////////// + + + output_plugins.push_back( this ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool OutputPlugin::Load() +{ + if( IsLoaded() ) return true; + + // (1) Load DLL + hDLL = LoadLibrary( GetFullpath() ); + if( !hDLL ) return false; + + // (2) Find export + WINAMP_OUTPUT_GETTER winampGetOutModule = + ( WINAMP_OUTPUT_GETTER )GetProcAddress( hDLL, "winampGetOutModule" ); + if( winampGetOutModule == NULL ) + { + FreeLibrary( hDLL ); + hDLL = NULL; + return false; + } + + // (3) Get module + plugin = winampGetOutModule(); + if( !plugin ) + { + FreeLibrary( hDLL ); + hDLL = NULL; + return false; + } + + // (4) Process module + plugin->hDllInstance = hDLL; + plugin->hMainWindow = WindowMain; + + if( !szName ) + { + // Note: The prefix is not removed to hide their + // origin at Nullsoft! It just reads easier. + if( !strnicmp( plugin->description, "nullsoft ", 9 ) ) + { + plugin->description += 9; + } + iNameLen = ( int )strlen( plugin->description ); + szName = new TCHAR[ iNameLen + 1 ]; + ToTchar( szName, plugin->description, iNameLen ); + szName[ iNameLen ] = TEXT( '\0' ); + } + + + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "Loading <%s>, %s" ), GetFilename(), szName ); + Console::Append( szBuffer ); + Console::Append( TEXT( " " ) ); + printf( ">>>Loading <%s>, %s\n" , GetFilename(), szName ); + + if( plugin->Init ) + { + // We remove the WNDPROC things +#ifdef NOGUI + plugin->Init(); +#else + + // Init + const WNDPROC WndprocBefore = ( WNDPROC )GetWindowLong( WindowMain, GWL_WNDPROC ); + plugin->Init(); + const WNDPROC WndprocAfter = ( WNDPROC )GetWindowLong( WindowMain, GWL_WNDPROC ); + + if( WndprocBefore != WndprocAfter ) + { + WndprocBackup = WndprocBefore; + iHookerIndex = iWndprocHookCounter++; + } + + + // Note: Plugins that use a wndproc hook need + // to be unloaded in the inverse loading order. + // This is due to the nature of wndproc hooking. + if( iHookerIndex != -1 ) + { + Console::Append( TEXT( "Wndproc hook added (by plugin)" ) ); + } +#endif + } + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool OutputPlugin::Unload() +{ + if( !IsLoaded() ) return true; + if( bActive && Playback::IsPlaying() ) return false; + + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "Unloading <%s>" ), GetFilename() ); + Console::Append( szBuffer ); + Console::Append( TEXT( " " ) ); + printf( ">>>Unloading <%s>\n" , GetFilename() ); + + // Quit + if( plugin ) + { + if( plugin->Quit ) plugin->Quit(); + plugin = NULL; + } + + // Remove wndproc hook + if( ( iHookerIndex != -1 ) && ( iHookerIndex == iWndprocHookCounter - 1 ) ) + { + // If we don't restore it the plugins wndproc will + // still be called which is not there anymore -> crash + SetWindowLong( WindowMain, GWL_WNDPROC, ( LONG )WndprocBackup ); + Console::Append( TEXT( "Wndproc hook removed (by host)" ) ); + Console::Append( TEXT( " " ) ); + + iHookerIndex = -1; + iWndprocHookCounter--; + } + + FreeLibrary( hDLL ); + hDLL = NULL; + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool OutputPlugin::About( HWND hParent ) +{ + if( !IsLoaded() ) return false; + if( !plugin ) return false; + if( !plugin->About ) return false; + + plugin->About( hParent ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool OutputPlugin::Config( HWND hParent ) +{ + if( !IsLoaded() ) return false; + if( !plugin ) return false; + if( !plugin->Config ) return false; + + plugin->Config( hParent ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool OutputPlugin::Start() +{ + //INFO_LOG(AUDIO, "OutputPlugin::Start() > Begin \n", + // IsLoaded(), bActive, active_output_count ); + + if( !IsLoaded() ) return false; + if( bActive ) return true; + + if( active_output_count ) + { + active_output_plugins = ( OutputPlugin ** )realloc( active_output_plugins, ( active_output_count + 1 ) * sizeof( OutputPlugin * ) ); + active_output_plugins[ active_output_count ] = this; + iArrayIndex = active_output_count; + active_output_count++; + } + else + { + active_output_plugins = ( OutputPlugin ** )malloc( sizeof( OutputPlugin * ) ); + active_output_plugins[ 0 ] = this; + iArrayIndex = 0; + active_output_count = 1; + } + + #ifndef NOGUI + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "Output plugin <%s> activated" ), GetFilename() ); + Console::Append( szBuffer ); + Console::Append( TEXT( " " ) ); + #else + INFO_LOG(AUDIO, "\n >>> Output plugin '%s' activated\n\n" , GetFilename() ); + #endif + + bActive = true; + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool OutputPlugin::Stop() +{ + if( !IsLoaded() ) return true; + if( !bActive ) return true; + + const int iMaxIndex = active_output_count - 1; + if( iArrayIndex < iMaxIndex ) + { + active_output_plugins[ iArrayIndex ] = active_output_plugins[ iMaxIndex ]; + active_output_plugins[ iArrayIndex ]->iArrayIndex = iArrayIndex; + } + iArrayIndex = -1; + active_output_count--; + + // TODO Flush? + // TODO Close? + + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "Output plugin <%s> deactivated" ), GetFilename() ); + Console::Append( szBuffer ); + Console::Append( TEXT( " " ) ); + + bActive = false; + + return true; +} diff --git a/Externals/MusicMod/Player/Src/OutputPlugin.h b/Externals/MusicMod/Player/Src/OutputPlugin.h new file mode 100644 index 0000000000..6dc4ca0cb9 --- /dev/null +++ b/Externals/MusicMod/Player/Src/OutputPlugin.h @@ -0,0 +1,87 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_OUTPUT_PLUGIN_H +#define PA_OUTPUT_PLUGIN_H + + + +#include "Global.h" +#include "Plugin.h" +#include "Winamp/Out.h" +#include + +using namespace std; + + + +typedef Out_Module * ( * WINAMP_OUTPUT_GETTER )( void ); + + +class OutputPlugin; + +extern vector output_plugins; +extern OutputPlugin ** active_output_plugins; +extern int active_output_count; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Winamp output plugin wrapper +//////////////////////////////////////////////////////////////////////////////// +class OutputPlugin : public Plugin +{ +public: + OutputPlugin( TCHAR * szDllpath, bool bKeepLoaded ); + + bool Load(); + bool Unload(); + + TCHAR * GetTypeString() { return TEXT( "Output" ); } + int GetTypeStringLen() { return 6; } + PluginType GetType() { return PLUGIN_TYPE_OUTPUT; } + + inline bool IsActive() { return bActive; } + + bool About( HWND hParent ); + bool Config( HWND hParent ); + + bool Start(); + bool Stop(); + + Out_Module * plugin; // Moved to public + +private: + bool bActive; + int iArrayIndex; + + + + // TODO + friend bool OpenPlay( TCHAR * szFilename, int iNumber ); + + friend int Output_Open( int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms ); + friend void Output_Close(); + friend int Output_Write( char * buf, int len ); + friend int Output_CanWrite(); + friend int Output_IsPlaying(); + friend int Output_Pause( int pause ); + friend void Output_SetVolume( int volume ); + friend void Output_SetPan( int pan ); + friend void Output_Flush( int t ); + friend int Output_GetOutputTime(); + friend int Output_GetWrittenTime(); +}; + + + +#endif // PA_OUTPUT_PLUGIN_H diff --git a/Externals/MusicMod/Player/Src/Path.cpp b/Externals/MusicMod/Player/Src/Path.cpp new file mode 100644 index 0000000000..956489bccc --- /dev/null +++ b/Externals/MusicMod/Player/Src/Path.cpp @@ -0,0 +1,243 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Path.h" +#include +#include + +using namespace std; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Applies a virtual root to a filename +/// +/// Example: +/// Rootpath: "C:\111\222\333\" +/// Filename before: "C:\111\444\test.mp3" +/// Filename after: "..\..\444\test.mp3" +//////////////////////////////////////////////////////////////////////////////// +bool ApplyRootToFilename( TCHAR * szRootpath, TCHAR * szFilename ) +{ + // returns modified flag + + int iFilenameLen = ( int )_tcslen( szFilename ); + int iRootLen = ( int )_tcslen( szRootpath ); + + // Too short? + if( ( iRootLen < 2 ) || ( iFilenameLen < 4 ) ) + { + return false; + } + + // Ensure trailing backslash + bool bDelOnRet = false; + TCHAR * szFinalRoot; + TCHAR * szFinalRootBackup; + if( szRootpath[ iRootLen - 1 ] != TEXT( '\\' ) ) + { + szFinalRoot = new TCHAR[ iRootLen + 2 ]; + memcpy( szFinalRoot, szRootpath, iRootLen * sizeof( TCHAR ) ); + memcpy( szFinalRoot + iRootLen, TEXT( "\\\0" ), 2 * sizeof( TCHAR ) ); + iRootLen++; + szFinalRootBackup = szFinalRoot; + bDelOnRet = true; + } + else + { + szFinalRoot = szRootpath; + szFinalRootBackup = NULL; + bDelOnRet = false; + } + + + // Different drives? + if( _totlower( *szFilename ) != _totlower( *szFinalRoot ) ) + { + if( bDelOnRet ) delete [] szFinalRootBackup; + return false; + } + + + // Skip drive + if( _tcsnicmp( szFilename, szFinalRoot, 3 ) ) + { + szFinalRoot += 3; + iRootLen -= 3; + + memmove( szFilename, szFilename + 3, ( iFilenameLen - 2 ) * sizeof( TCHAR ) ); // Plus \0 + iFilenameLen -=3; + } + + + int iBackslashLast = -1; + + int iCompLen; // Maximum chars to compare + if( iRootLen > iFilenameLen ) + iCompLen = iFilenameLen; + else + iCompLen = iRootLen; + + + // Walk while equal + int i = 0; + while( i < iCompLen ) + { + if( ( szFilename[ i ] == TEXT( '\\' ) ) && ( szFinalRoot[ i ] == TEXT( '\\' ) ) ) + { + iBackslashLast = i; + i++; + } + else if( _totlower( szFilename[ i ] ) == _totlower( szFinalRoot[ i ] ) ) + { + i++; + } + else + { + break; + } + } + + + // Does the filename contain the full root? + int iLevelDiff = 0; + if( i != iCompLen ) + { + // Calculate level difference + for( i = iBackslashLast + 1; i < iRootLen; i++ ) + { + if( szFinalRoot[ i ] == TEXT( '\\' ) ) + { + iLevelDiff++; + } + } + } + + + if( iBackslashLast == -1 ) + { + if( bDelOnRet ) delete [] szFinalRootBackup; + return false; + } + + + TCHAR * szSource = szFilename + iBackslashLast + 1; + if( iLevelDiff > 0 ) + { + const int iExtraCharsForPrefix = ( 3 * iLevelDiff ) - iBackslashLast - 1; + const int iCharsToMove = iFilenameLen - iBackslashLast; // One more for '\0' + memmove( szSource + iExtraCharsForPrefix, szSource, sizeof( TCHAR ) * iCharsToMove ); + + TCHAR * szWalk = szFilename; + while( iLevelDiff-- ) + { + memcpy( szWalk, TEXT( "..\\" ), 3 * sizeof( TCHAR ) ); + szWalk += 3; + } + } + else + { + const int iCharsToMove = iFilenameLen - iBackslashLast; // One more for '\0' + memmove( szFilename, szSource, sizeof( TCHAR ) * iCharsToMove ); + } + + + if( bDelOnRet ) delete [] szFinalRootBackup; + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// Compresses filenames (inplace) +/// +/// Example: +/// Before "C:\111\222\..\333\.\444\..\..\test.mp3" +/// After "C:\111\test.mp3" +//////////////////////////////////////////////////////////////////////////////// +bool UnbloatFilename( TCHAR * szFullpath, bool bFixTooDeep ) +{ + int iLen = ( int )_tcslen( szFullpath ); + bool bModified = false; + + // Exclude drive letter from conversion "C:\" + if( ( iLen > 3 ) && !_tcsnicmp( szFullpath + 1, TEXT( ":\\" ), 2 ) ) + { + szFullpath += 3; + iLen -= 3; + } + + vector< TCHAR * > after_backslash; + TCHAR * end = szFullpath + iLen; + TCHAR * szWalk = szFullpath; + + while( true ) + { + if( !_tcsnicmp( szWalk, TEXT( "..\\" ), 3 ) ) + { + TCHAR * szAfterBackslashLast; + + if( after_backslash.empty() ) + { + // Getting here means we go deeper than root level e.g. "../test" + if( bFixTooDeep ) + { + szAfterBackslashLast = szWalk; + } + else + { + break; + } + } + else + { + szAfterBackslashLast = after_backslash.back(); + after_backslash.pop_back(); + } + + const int iBytesToCopy = end - szWalk - ( 3 * sizeof( TCHAR ) ); + const int iBytesLess = szWalk + ( 3 * sizeof( TCHAR ) ) - szAfterBackslashLast; + + memmove( szAfterBackslashLast, szWalk + 3, iBytesToCopy ); + + char * byte_end = ( char * )end; + byte_end -= iBytesLess; + end = byte_end; + *end = TEXT( '\0' ); + + szWalk = szAfterBackslashLast; + + bModified = true; + } + else if( !_tcsnicmp( szWalk, TEXT( ".\\" ), 2 ) ) + { + const int iBytesToCopy = end - szWalk - ( 2 * sizeof( TCHAR ) ); + memmove( szWalk, szWalk + 2, iBytesToCopy ); + end -= 2; + *end = TEXT( '\0' ); + + bModified = true; + } + else + { + + if( szWalk >= end ) break; + after_backslash.push_back( szWalk ); + + // Jump after next backslash + while( ( szWalk < end ) && ( *szWalk != TEXT( '\\' ) ) ) szWalk++; + szWalk++; + } + } + + return bModified; +} diff --git a/Externals/MusicMod/Player/Src/Path.h b/Externals/MusicMod/Player/Src/Path.h new file mode 100644 index 0000000000..63b510dd48 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Path.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_PATH_H +#define PA_PATH_H + + + +#ifndef PLAINAMP_TESTING +# include "Global.h" +#else +# include "../Testing/GlobalTest.h" +#endif + + + +bool ApplyRootToFilename( TCHAR * szRootpath, TCHAR * szFilename ); +bool UnbloatFilename( TCHAR * szFullpath, bool bFixTooDeep ); + + + +#endif // PA_PATH_H diff --git a/Externals/MusicMod/Player/Src/Playback.cpp b/Externals/MusicMod/Player/Src/Playback.cpp new file mode 100644 index 0000000000..090d2e07a5 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Playback.cpp @@ -0,0 +1,804 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Playback.h" +#include "InputPlugin.h" +#include "Output.h" +#include "Playlist.h" +#include "Console.h" +#include "Unicode.h" +#include "Rebar.h" +#include "Main.h" +#include "Config.h" +#include "Status.h" + + + +int iCurVol = 255; +ConfIntMinMax ciCurVol( &iCurVol, TEXT( "Volume" ), CONF_MODE_INTERNAL, 255, 0, 255 ); + +int iCurPan = 0; +ConfIntMinMax ciCurPan( &iCurPan, TEXT( "Panning" ), CONF_MODE_INTERNAL, 0, -127, 127 ); + + + +#define VOLUME_STEP ( 255 / 10 ) + + + +bool bPlaying = false; +bool bPaused = false; + +bool bTimerRunning = false; + + + +// Only for reference comparison!!! +TCHAR * szCurrentFilename = NULL; + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +// ======================================================================================= +// The timer that calls WndprocMain every second +void EnableTimer( bool bEnabled ) +{ + // Return if the timer is already activated + if( bEnabled == bTimerRunning ) return; + + if( bEnabled ) + { + SetTimer( WindowMain, TIMER_SEEK_UPDATE, 1000, NULL ); + INFO_LOG(AUDIO, "EnableTimer > Activated\n" ); + } + else + { + KillTimer( WindowMain, TIMER_SEEK_UPDATE ); + StatusReset(); + + INFO_LOG(AUDIO, "EnableTimer > Killed\n" ); + } + + bTimerRunning = bEnabled; +} +// ======================================================================================= + + +//////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////// +bool OpenPlay( TCHAR * szFilename, int iNumber ) +{ + // ======================================================================================= + #ifdef NOGUI + //INFO_LOG(AUDIO, "Playback.cpp: OpenPlay > Begin <%i> <%s>\n" , iNumber, szFilename ); + #else + TCHAR sszBuffer[ 5000 ]; + _stprintf( sszBuffer, TEXT( "Playback.cpp: OpenPlay was called <%i> <%s>" ), iNumber, szFilename ); + Console::Append( sszBuffer ); + #endif + // ======================================================================================= + + + // ======================================================================================= + // Get the right input plugin + // --------------------------------------------------------------------------------------- + if( !szFilename ) return false; + szCurrentFilename = szFilename; + + // --------------------------------------------------------------------------------------- + // TODO: non-file support + // --------------------------------------------------------------------------------------- + // Get file extension + const int iLen = ( int )_tcslen( szFilename ); + TCHAR * walk = szFilename + iLen - 1; + while( ( walk >= szFilename ) && ( *walk != TEXT( '.' ) ) ) walk--; + walk++; + + // --------------------------------------------------------------------------------------- + const int iExtLen = ( int )_tcslen( walk ); + TCHAR * szExt = new TCHAR[ iExtLen + 1 ]; + memcpy( szExt, walk, iExtLen * sizeof( TCHAR ) ); + szExt[ iExtLen ] = TEXT( '\0' ); + + // --------------------------------------------------------------------------------------- + // Compare the extension to the supported extension by the current input plugins + map ::iterator iter = + ext_map.find( szExt ); + delete [] szExt; + if( iter == ext_map.end() ) + { + Console::Append( TEXT( "ERROR: Extension not supported" ) ); + Console::Append( " " ); + INFO_LOG(AUDIO,"OpenPlay > ERROR: Extension not supported\n"); + return false; + } + // --------------------------------------------------------------------------------------- + // Now that we know which input pugin to use we set that one as active + InputPlugin * old_input = active_input_plugin; // Save the last one, if any + active_input_plugin = iter->second; + INFO_LOG(AUDIO,"OpenPlay > Input plugin '%s' activated\n", active_input_plugin->GetFilename()); + // ======================================================================================= + + if( old_input ) + { + // if( active_input_plugin != old_input ) ----> TODO unload old plugin + + // Some output plugins require a call to Close() before each + // call to Open(). Calling Input::Stop() will make the input plugin + // call Output::Close() and thus solve this problem. + old_input->plugin->Stop(); + } + + if( !active_input_plugin->plugin ) + { + Console::Append( TEXT( "ERROR: Input plugin is NULL" ) ); + Console::Append( " " ); + INFO_LOG(AUDIO,"OpenPlay > ERROR: Input plugin is NULL\n"); + return false; + } + + // Connect + //INFO_LOG(AUDIO, "OpenPlay > OutMod\n" ); + active_input_plugin->plugin->outMod = &output_server; // output->plugin; + + // ======================================================================================= + // Re-apply volume and panning + //active_input_plugin->plugin->SetVolume( iCurVol ); + //active_input_plugin->plugin->SetPan( iCurPan ); + //Playback::Eq::Reapply(); + // ======================================================================================= + + // Play + +#ifdef PA_UNICODE + // Filename + const int iFilenameLen = _tcslen( szFilename ); + char * szTemp = new char[ iFilenameLen + 1 ]; + ToAnsi( szTemp, szFilename, iFilenameLen ); + szTemp[ iFilenameLen ] = '\0'; + + // Ansi Title + char szAnsiTitle[ 2000 ] = "\0"; + int length_in_ms; + active_input_plugin->plugin->GetFileInfo( szTemp, szAnsiTitle, &length_in_ms ); + const int iAnsiTitleLen = strlen( szAnsiTitle ); + + // Unicode title + TCHAR szTitle[ 2000 ]; + ToTchar( szTitle, szAnsiTitle, iFilenameLen, iAnsiTitleLen ); + szTitle[ iAnsiTitleLen ] = TEXT( "\0" ); + + active_input_plugin->plugin->Play( szTemp ); + delete [] szTemp; +#else + // ======================================================================================= + // Play the file + // --------------------------------------------------------------------------------------- + // Title + TCHAR szTitle[ 2000 ] = TEXT( "\0" ); + int length_in_ms; + + //INFO_LOG(AUDIO, "OpenPlay > GetFileInfo\n" ); + active_input_plugin->plugin->GetFileInfo( szFilename, szTitle, &length_in_ms ); + + //INFO_LOG(AUDIO, "OpenPlay > Play\n" ); + active_input_plugin->plugin->Play( szFilename ); + // ======================================================================================= +#endif + + bPlaying = true; + bPaused = false; + + // ======================================================================================= + // Title + + //TCHAR szBuffer[ 5000 ]; + //_stprintf( szBuffer, TEXT( "%i. %s - Plainamp" ), iNumber, szTitle ); + //SetWindowText( WindowMain, szBuffer ); + // ======================================================================================= + + /* + TCHAR * szBasename = szFilename + uLen - 1; + while( ( szBasename > szFilename ) && ( *szBasename != TEXT( '\\' ) ) ) szBasename--; + szBasename++; + */ + + // Timer ON + //EnableTimer( true ); + + //INFO_LOG(AUDIO, "OpenPlay > End\n" ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback_PrevOrNext( bool bPrevOrNext ) +{ + // todo: prev/next in pause mode? + + if( !active_input_plugin ) return false; + if( !active_input_plugin->plugin ) return false; + + int iNextIndex = playlist->GetCurIndex(); + int iMaxIndex = playlist->GetMaxIndex(); + if( iMaxIndex < 0 || iNextIndex < 0 ) return false; + + bool res; + if( bPrevOrNext ) + res = Playback::Order::Prev( iNextIndex, iMaxIndex ); + else + res = Playback::Order::Next( iNextIndex, iMaxIndex ); + + if( res ) + { + if( bPlaying ) + { + // NOT TWICE active_input_plugin->plugin->Stop(); + bPlaying = false; + bPaused = false; + + // Timer OFF + EnableTimer( false ); + } + + TCHAR * szFilename = Playlist::GetFilename( iNextIndex ); + if( !szFilename ) return false; + + playlist->SetCurIndex( iNextIndex ); + + return OpenPlay( szFilename, iNextIndex + 1 ); + } + else + { + return false; + } + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Prev() +{ + return Playback_PrevOrNext( true ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +// Play the file +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Play() +{ + static int iLastIndex = -1; + + // --------------------------------------------------------------------------------------- + + #ifndef NOGUI + TCHAR sszBuffer[ 5000 ]; + _stprintf( sszBuffer, TEXT( "Playback::Play() with bPlaying <%i>\n" ), bPlaying ); + Console::Append( sszBuffer ); + Console::Append( TEXT( " " ) ); + #else + //INFO_LOG(AUDIO, "Playback::Play() > Begin <%i>\n" , bPlaying ); + #endif + // --------------------------------------------------------------------------------------- + + // --------------------------------------------------------------------------------------- + if( bPlaying ) // If we are currently playing a file + { + if( !active_input_plugin ) return false; + if( !active_input_plugin->plugin ) return false; + + const int iIndex = playlist->GetCurIndex(); + if( iIndex < 0 ) return false; + + /* + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "OLD [%i] NEW [%i]" ), iLastIndex, iIndex ); + SetWindowText( WindowMain, szBuffer ); + */ + + // If we are not playing the same track/file as before + TCHAR * szFilename = Playlist::GetFilename( iIndex ); + if( szFilename != szCurrentFilename ) + { + // New track! + + // Stop + // NOT TWICE active_input_plugin->plugin->Stop(); + + // Timer OFF + //EnableTimer( false ); + + // Get filename + if( !szFilename ) + { + Console::Append( TEXT( "ERROR: Could not resolve filename" ) ); + Console::Append( " " ); + return false; + } + + // Play + iLastIndex = iIndex; + bPlaying = OpenPlay( szFilename, iIndex + 1 ); + bPaused = false; + } + else + { + // Same track! + if( bPaused ) + { + // Unpause + active_input_plugin->plugin->UnPause(); + bPaused = false; + + // Timer ON + //EnableTimer( true ); + } + else + { + // Restart at beginning + active_input_plugin->plugin->SetOutputTime( 0 ); + } + } + } + // ======================================================================================= + else // we are not currently playing + { + const int iIndex = playlist->GetCurIndex(); + if( iIndex < 0 ) return false; + + // Get filename + TCHAR * szFilename = Playlist::GetFilename( iIndex ); + + // ======================================================================================= + + + //bool bPlaying = false; + //TCHAR * szFilename = TEXT("C:\Files\Spel och spelfusk\Console\Gamecube\Code\vgmstream (isolate ast)\Music\demo36_02.ast"); + //bPlaying = OpenPlay( szFilename, 1 ); + //bPlaying = OpenPlay( szFilename, iIndex + 1 ); + //Console::Append( TEXT( "Playback.cpp:Playback::Play() called OpenPlay" ) ); + + // ======================================================================================= + + if( !szFilename ) + { + Console::Append( TEXT( "ERROR: Could not resolve filename" ) ); + Console::Append( " " ); + return false; + } + #ifndef NOGUI + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "Playback.cpp: Play() got the filename <%s>" ), szFilename); + Console::Append( szBuffer ); + //Console::Append( TEXT( " " ) ); + #else + //INFO_LOG(AUDIO, "Playback::Play() > Filename <%s>\n", szFilename); + #endif + + // Play + iLastIndex = iIndex; + bPlaying = OpenPlay( szFilename, iIndex + 1 ); + bPaused = false; + } + return true; +} +// ======================================================================================= + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Pause() +{ + if( !bPlaying ) return false; + if( !active_input_plugin ) return false; + if( !active_input_plugin->plugin ) return false; + + if( bPaused ) + { + // Unpause + active_input_plugin->plugin->UnPause(); + bPaused = false; + + // Timer ON + //EnableTimer( true ); + } + else + { + // Pause + active_input_plugin->plugin->Pause(); + bPaused = true; + + // Timer OFF + //EnableTimer( false ); + } + +// Console::Append( TEXT( "Playback::Pause" ) ); + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Stop() +{ + if( !bPlaying ) return false; + + // --------------------------------------------------------------------------------------- + // Stop + if( active_input_plugin && active_input_plugin->plugin ) + { + active_input_plugin->plugin->Stop(); + } + active_input_plugin = NULL; // QUICK FIX + // --------------------------------------------------------------------------------------- + + bPlaying = false; + bPaused = false; + + // Timer OFF > It was never turned on + //EnableTimer( false ); + + // Reset seekbar > We don't need this + //Playback::UpdateSeek(); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Next() +{ + return Playback_PrevOrNext( false ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::IsPlaying() +{ + return bPlaying; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::IsPaused() +{ + return bPaused; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +//int getlength(); +//int getoutputtime(); + +bool Playback::UpdateSeek() +{ +#ifdef NOGUI + printf( "Playback::UpdateSeek > Begin\n" ); + if( active_input_plugin ) + { + printf( "GetLength() > Begin\n" ); + const int ms_len = active_input_plugin->plugin->GetLength(); + //const int ms_cur = active_input_plugin->plugin->GetOutputTime(); + + //const int ms_len = getlength(); + //const int ms_cur = getoutputtime(); + + //printf( "Current position is <%i of %i>\n", ms_cur, ms_len ); + } + return true; +#else + static bool bSliderEnabledBefore = false; + bool bSliderEnabledAfter; + + + //if( !WindowSeek ) return false; + + int iVal = 0; + + // If it has not been set + if( !active_input_plugin || !active_input_plugin->plugin ) + { + if( bSliderEnabledBefore ) + { + // Update slider + PostMessage( WindowSeek, TBM_SETPOS, ( WPARAM )( TRUE ), iVal ); + + // Disable slider + EnableWindow( WindowSeek, FALSE ); + bSliderEnabledBefore = false; + } + } + else + { + const int ms_len = active_input_plugin->plugin->GetLength(); + if( ms_len ) + { + const int ms_cur = active_input_plugin->plugin->GetOutputTime(); + iVal = ( ms_cur * 1000 ) / ms_len; + + if( iVal > 1000 ) iVal = 0; + + // ======================================================================================= + //TCHAR szBuffer[ 5000 ]; + //_stprintf( szBuffer, TEXT( "Current position is <%i of %i>" ), ms_cur, ms_len ); + //Console::Append( szBuffer ); + //Console::Append( TEXT( " " ) ); + printf( "Current position is <%i of %i>\n", ms_cur, ms_len ); + // ======================================================================================= + } + + + + if( !bSliderEnabledBefore ) + { + EnableWindow( WindowSeek, TRUE ); + bSliderEnabledBefore = true; + } + + // Update slider + PostMessage( WindowSeek, TBM_SETPOS, ( WPARAM )( TRUE ), iVal ); + } + + return true; +#endif +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int Playback::PercentToMs( float fPercent ) +{ + if( !active_input_plugin ) return -1; + if( !active_input_plugin->plugin ) return -1; + + const int ms_len = active_input_plugin->plugin->GetLength(); + const int ms_res = ( int )( ms_len * fPercent / 100.0f ); + return ms_res; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::SeekPercent( float fPercent ) +{ + // TODO update slider, NOT HERE!!! + + if( !bPlaying ) return false; + if( bPaused ) return false; // TODO: apply seek when unpausing + if( !active_input_plugin ) return false; + if( !active_input_plugin->plugin ) return false; + + if( fPercent < 0.0f ) + fPercent = 0.0f; + else if( fPercent > 100.0f ) + fPercent = 100.0f; + + const int ms_len = active_input_plugin->plugin->GetLength(); + const int ms_cur = ( int )( ms_len * fPercent / 100.0f ); + active_input_plugin->plugin->SetOutputTime( ms_cur ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool SeekRelative( int ms ) +{ + if( !bPlaying ) return false; + if( bPaused ) return false; // TODO: apply seek when unpausing + if( !active_input_plugin ) return false; + if( !active_input_plugin->plugin ) return false; + + const int ms_len = active_input_plugin->plugin->GetLength(); + const int ms_old = active_input_plugin->plugin->GetOutputTime(); + int ms_new = ms_old + ms; + + if( ms_new < 0 ) + ms_new = 0; + else if( ms_new > ms_len ) + ms_new = ms_len; + + if( ms_new == ms_old ) return true; + active_input_plugin->plugin->SetOutputTime( ms_new ); + + /* + // PROGRESS + // PostMessage( hwnd, PBM_SETPOS , ( WPARAM )( iVal ), 0 ); + // TARCKBAR + PostMessage( wnd_pos, TBM_SETPOS, ( WPARAM )( TRUE ), ms_cur * 1000 / ms_len ); + */ + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Forward() +{ + return SeekRelative( 5000 ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Rewind() +{ + return SeekRelative( -5000 ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void Playback::NotifyTrackEnd() +{ + bPlaying = false; + bPaused = false; + + // Timer + EnableTimer( false ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int Playback::Volume::Get() +{ + return iCurVol; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +inline void Playback_Volume_Set( int iVol ) +{ + if( active_input_plugin && active_input_plugin->plugin ) + { + active_input_plugin->plugin->SetVolume( iVol ); + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Volume::Set( int iVol ) +{ + const int iCurVolBackup = iCurVol; + iCurVol = iVol; + ciCurVol.MakeValidPull(); + + if( iCurVol != iCurVolBackup ) + { + Playback_Volume_Set( iCurVol ); + } + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Volume::Up() +{ + if( ciCurVol.IsMax() ) return true; + + const int iCurVolBackup = iCurVol; + iCurVol += VOLUME_STEP; + ciCurVol.MakeValidPull(); + + if( iCurVol != iCurVolBackup ) + { + Console::Append( TEXT( "Volume UP" ) ); + Playback_Volume_Set( iCurVol ); + } + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Volume::Down() +{ + if( ciCurVol.IsMin() ) return true; + + const int iCurVolBackup = iCurVol; + iCurVol -= VOLUME_STEP; + ciCurVol.MakeValidPull(); + + if( iCurVol != iCurVolBackup ) + { + Console::Append( TEXT( "Volume DOWN" ) ); + Playback_Volume_Set( iCurVol ); + } + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int Playback::Pan::Get() +{ + return iCurPan; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Pan::Set( int iPan ) +{ + const int iCurPanBackup = iCurPan; + iCurPan = iPan; + ciCurPan.MakeValidPull(); + + if( ( iCurPan != iCurPanBackup ) && active_input_plugin && active_input_plugin->plugin ) + { + active_input_plugin->plugin->SetPan( iCurPan ); + } + + return true; +} diff --git a/Externals/MusicMod/Player/Src/Playback.h b/Externals/MusicMod/Player/Src/Playback.h new file mode 100644 index 0000000000..558b191d1d --- /dev/null +++ b/Externals/MusicMod/Player/Src/Playback.h @@ -0,0 +1,106 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_PLAYBACK_H +#define PA_PLAYBACK_H + + + +#include "Global.h" + + + +#define ORDER_SINGLE 0 +#define ORDER_SINGLE_REPEAT 1 +#define ORDER_NORMAL 2 +#define ORDER_NORMAL_REPEAT 3 +#define ORDER_INVERSE 4 +#define ORDER_INVERSE_REPEAT 5 +#define ORDER_RANDOM 6 + +#define ORDER_FIRST ORDER_SINGLE +#define ORDER_LAST ORDER_RANDOM + +#define ORDER_DEFAULT ORDER_NORMAL_REPEAT + + +#define TIMER_SEEK_UPDATE 1 + + + +typedef bool ( * PresetCallback )( TCHAR * ); + + +namespace Playback +{ + bool Prev(); + bool Play(); + bool Pause(); + bool Stop(); + bool Next(); + + bool IsPlaying(); + bool IsPaused(); + + bool UpdateSeek(); + int PercentToMs( float fPercent ); + bool SeekPercent( float fPercent ); + bool Forward(); + bool Rewind(); + + void NotifyTrackEnd(); + + namespace Volume + { + int Get(); + bool Set( int iVol ); + bool Up(); + bool Down(); + }; + + namespace Pan + { + int Get(); + bool Set( int iPan ); + }; + + namespace Order + { + int GetCurMode(); + bool SetMode( int iMode ); + + TCHAR * GetModeName( int iMode ); +// int GetModeNameLen( int iMode ); + + bool Next( int & iCur, int iMax ); + bool Prev( int & iCur, int iMax ); + }; + + namespace Eq + { + // 63 -> -12db + // 31 -> 0 + // 0 -> +12db + // bool Get( char * eq_data ); + // bool Set( bool bOn, char * pData, int iPreamp ); + int GetCurIndex(); + bool SetIndex( int iPresetIndex ); + + bool Reapply(); + + bool ReadPresets( PresetCallback AddPreset ); + }; +}; + + + +#endif // PA_PLAYBACK_H diff --git a/Externals/MusicMod/Player/Src/PlaybackEq.cpp b/Externals/MusicMod/Player/Src/PlaybackEq.cpp new file mode 100644 index 0000000000..ee779e5545 --- /dev/null +++ b/Externals/MusicMod/Player/Src/PlaybackEq.cpp @@ -0,0 +1,223 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Playback.h" +#include "InputPlugin.h" +#include "Console.h" +#include "Config.h" +#include + +using namespace std; + + + +vector eq_vec; + + + +// Note: CurPresetFixed will be renamed to CurPreset when +// custom presets are implemented. This hopefully avoids +// migration trouble. + +int iCurPreset; // -1 means EQ off +ConfInt ciCurPreset( &iCurPreset, TEXT( "CurPresetFixed" ), CONF_MODE_INTERNAL, -1 ); + +bool bPreventDistortion; // Automatic preamp adjustment +ConfBool cbPreventDistortion( &bPreventDistortion, TEXT( "PreventDistortion" ), CONF_MODE_PUBLIC, true ); + + + +//////////////////////////////////////////////////////////////////////////////// +// PRE valid index +//////////////////////////////////////////////////////////////////////////////// +void Playback_Eq_Set( int iPresetIndex ) +{ + if( active_input_plugin && active_input_plugin->plugin ) + { + if( iPresetIndex == -1 ) // == EQ disabled + { + char data[ 10 ] = { 31, 31, 31, 31, 31, 31, 31, 31, 31, 31 }; + active_input_plugin->plugin->EQSet( 0, data, 31 ); + } + else + { + char data[ 10 ]; + memcpy( data, eq_vec[ iPresetIndex ], sizeof( char ) * 10 ); + + if( bPreventDistortion ) + { + // Search minimum (most amplifying band) + int iMin = 63; + int i; + for( i = 0; i < 10; i++ ) + { + if( data[ i ] < iMin ) iMin = data[ i ]; + } + + if( iMin < 31 ) // Possible distortion + { + // Adjust preamp to prevent distortion + active_input_plugin->plugin->EQSet( 1, data, 31 + ( 31 - iMin ) ); + } + else + { + if( iMin > 31 ) // Lower than necessary + { + // Push to zero level so we get + // more volume without distortion + const int iSub = iMin - 31; + for( i = 0; i < 10; i++ ) + { + data[ i ] -= iSub; + } + } + active_input_plugin->plugin->EQSet( 1, data, 31 ); + } + } + else + { + active_input_plugin->plugin->EQSet( 1, data, 31 ); + } + } + } +} + + + +/* +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Eq::Set( bool bOn, char * pData, int iPreamp ) +{ + if( active_input_plugin && active_input_plugin->plugin ) + { + char data[ 10 ]; + memcpy( data, pData, sizeof( char ) * 10 ); + active_input_plugin->plugin->EQSet( bOn ? 1 : 0, data, iPreamp ); + } + return true; +} +*/ + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int Playback::Eq::GetCurIndex() +{ + return iCurPreset; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Eq::SetIndex( int iPresetIndex ) +{ + if( iPresetIndex >= ( int )eq_vec.size() ) return false; + Playback_Eq_Set( iPresetIndex ); + iCurPreset = iPresetIndex; + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Eq::Reapply() +{ + Playback_Eq_Set( iCurPreset ); + return true; +} + + + +// Note: Most of these are the exact presets used in Winamp. +// I do not expect any legal trouble with this but +// in case I am wrong please let me know and I will +// remove them. + +char eq_classical [ 10 ] = { 31, 31, 31, 31, 31, 31, 44, 44, 44, 48 }; +char eq_club [ 10 ] = { 31, 31, 26, 22, 22, 22, 26, 31, 31, 31 }; +char eq_dance [ 10 ] = { 16, 20, 28, 32, 32, 42, 44, 44, 32, 32 }; +char eq_full_bass [ 10 ] = { 16, 16, 16, 22, 29, 39, 46, 49, 50, 50 }; +char eq_full_bass_treble[ 10 ] = { 20, 22, 31, 44, 40, 29, 18, 14, 12, 12 }; +char eq_full_treble [ 10 ] = { 48, 48, 48, 39, 27, 14, 6, 6, 6, 4 }; +char eq_headphones [ 10 ] = { 24, 14, 23, 38, 36, 29, 24, 16, 11, 8 }; +char eq_laptop [ 10 ] = { 24, 14, 23, 38, 36, 29, 24, 16, 11, 8 }; +char eq_large_hall [ 10 ] = { 15, 15, 22, 22, 31, 40, 40, 40, 31, 31 }; +char eq_live [ 10 ] = { 40, 31, 25, 23, 22, 22, 25, 27, 27, 28 }; +char eq_more_bass [ 10 ] = { 22, 22, 22, 22, 22, 22, 26, 31, 31, 31 }; +char eq_party [ 10 ] = { 20, 20, 31, 31, 31, 31, 31, 31, 20, 20 }; +char eq_pop [ 10 ] = { 35, 24, 20, 19, 23, 34, 36, 36, 35, 35 }; +char eq_reggae [ 10 ] = { 31, 31, 33, 42, 31, 21, 21, 31, 31, 31 }; +char eq_rock [ 10 ] = { 19, 24, 41, 45, 38, 25, 17, 14, 14, 14 }; +char eq_ska [ 10 ] = { 36, 40, 39, 33, 25, 22, 17, 16, 14, 16 }; +char eq_soft [ 10 ] = { 24, 29, 34, 36, 34, 25, 18, 16, 14, 12 }; +char eq_soft_rock [ 10 ] = { 25, 25, 28, 33, 39, 41, 38, 33, 27, 17 }; +char eq_techno [ 10 ] = { 19, 22, 31, 41, 40, 31, 19, 16, 16, 17 }; + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Eq::ReadPresets( PresetCallback AddPreset ) +{ + if( AddPreset == NULL ) return false; + + eq_vec.push_back( eq_classical ); AddPreset( TEXT( "Classical" ) ); + eq_vec.push_back( eq_club ); AddPreset( TEXT( "Club" ) ); + eq_vec.push_back( eq_dance ); AddPreset( TEXT( "Dance" ) ); + eq_vec.push_back( eq_full_bass ); AddPreset( TEXT( "Full Bass" ) ); + eq_vec.push_back( eq_full_bass_treble ); AddPreset( TEXT( "Full Bass & Treble" ) ); + eq_vec.push_back( eq_full_treble ); AddPreset( TEXT( "Full Treble" ) ); + eq_vec.push_back( eq_headphones ); AddPreset( TEXT( "Headphones" ) ); + eq_vec.push_back( eq_laptop ); AddPreset( TEXT( "Laptop Speakers" ) ); + eq_vec.push_back( eq_large_hall ); AddPreset( TEXT( "Large Hall" ) ); + eq_vec.push_back( eq_live ); AddPreset( TEXT( "Live" ) ); + eq_vec.push_back( eq_more_bass ); AddPreset( TEXT( "More Bass" ) ); + eq_vec.push_back( eq_party ); AddPreset( TEXT( "Party" ) ); + eq_vec.push_back( eq_pop ); AddPreset( TEXT( "Pop" ) ); + eq_vec.push_back( eq_reggae ); AddPreset( TEXT( "Reggae" ) ); + eq_vec.push_back( eq_rock ); AddPreset( TEXT( "Rock" ) ); + eq_vec.push_back( eq_ska ); AddPreset( TEXT( "Ska" ) ); + eq_vec.push_back( eq_soft ); AddPreset( TEXT( "Soft" ) ); + eq_vec.push_back( eq_soft_rock ); AddPreset( TEXT( "Soft Rock" ) ); + eq_vec.push_back( eq_techno ); AddPreset( TEXT( "Techno" ) ); + + // Fix invalid indices + if( iCurPreset < -1 ) + { + iCurPreset = -1; + } + else + { + const int iLen = ( int )eq_vec.size(); + if( iCurPreset >= iLen ) + { + iCurPreset = iLen - 1; + } + } + + // TODO load/save eqf files + // GPL eqf loading from koders.com + // equalizer.c / xmms2 A Gtk2 port of xmms.(xmms2) + // equalizer.c / Digital Disco System(dds) + + return true; +} diff --git a/Externals/MusicMod/Player/Src/PlaybackOrder.cpp b/Externals/MusicMod/Player/Src/PlaybackOrder.cpp new file mode 100644 index 0000000000..50e09a5da2 --- /dev/null +++ b/Externals/MusicMod/Player/Src/PlaybackOrder.cpp @@ -0,0 +1,205 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + + +#include "Playback.h" +#include "Config.h" +#include +#include + + + +int iCurOrder = ORDER_DEFAULT; +ConfIntMinMax ciCurOrder( &iCurOrder, TEXT( "Order" ), CONF_MODE_INTERNAL, ORDER_DEFAULT, ORDER_FIRST, ORDER_LAST ); + +bool bRandomReady = false; + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int Playback::Order::GetCurMode() +{ + return iCurOrder; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Order::SetMode( int iMode ) +{ + iCurOrder = iMode; + ciCurOrder.MakeValidDefault(); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +TCHAR * Playback::Order::GetModeName( int iMode ) +{ + switch( iMode ) + { + case ORDER_SINGLE: return TEXT( " Single" ); + case ORDER_SINGLE_REPEAT: return TEXT( " Single + Repeat" ); + case ORDER_NORMAL: return TEXT( " Normal" ); + case ORDER_NORMAL_REPEAT: return TEXT( " Normal + Repeat" ); + case ORDER_INVERSE: return TEXT( " Inverse" ); + case ORDER_INVERSE_REPEAT: return TEXT( " Inverse + Repeat" ); + case ORDER_RANDOM: return TEXT( " Random" ); + default: return NULL; + } +} + +/* +int Playback::Order::GetModeNameLen( int iMode ) +{ + switch( uMode ) + { + case ORDER_SINGLE: return 7; + case ORDER_SINGLE_REPEAT: return 16; + case ORDER_NORMAL: return 7; + case ORDER_NORMAL_REPEAT: return 16; + case ORDER_INVERSE: return 8; + case ORDER_INVERSE_REPEAT: return 17; + case ORDER_RANDOM: return 7; + default: return 0; + } +} +*/ + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool NextNormal( int & iCur, int iMax ) +{ + if( iCur >= iMax ) return false; + iCur++; + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool NextNormalRepeat( int & iCur, int iMax ) +{ + if( iCur >= iMax ) + iCur = 0; + else + iCur++; + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool NextInverse( int & iCur, int iMax ) +{ + if( iCur <= 0 ) return false; + iCur--; + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool NextInverseRepeat( int & iCur, int iMax ) +{ + if( iCur <= 0 ) + iCur = iMax; + else + iCur--; + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool NextRandom( int & iCur, int iMax ) +{ + if( iMax < 2 ) return false; + + if( !bRandomReady ) + { + srand( ( unsigned )time( NULL ) ); + bRandomReady = true; + } + + const int iNew = ( int )( rand() / ( float )RAND_MAX * iMax ); + if( iNew != iCur ) + iCur = iNew; + else + { + if( iCur >= iMax ) + iCur = 0; + else + iCur++; + } + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Order::Next( int & iCur, int iMax ) +{ + switch( iCurOrder ) + { + case ORDER_SINGLE: return false; + case ORDER_SINGLE_REPEAT: return true; + case ORDER_NORMAL: return NextNormal( iCur, iMax ); + case ORDER_NORMAL_REPEAT: return NextNormalRepeat( iCur, iMax ); + case ORDER_INVERSE: return NextInverse( iCur, iMax ); + case ORDER_INVERSE_REPEAT: return NextInverseRepeat( iCur, iMax ); + case ORDER_RANDOM: return NextRandom( iCur, iMax ); + default: return false; + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playback::Order::Prev( int & iCur, int iMax ) +{ + switch( iCurOrder ) + { + case ORDER_SINGLE: return false; + case ORDER_SINGLE_REPEAT: return true; + case ORDER_NORMAL: return NextInverse( iCur, iMax ); + case ORDER_NORMAL_REPEAT: return NextInverseRepeat( iCur, iMax ); + case ORDER_INVERSE: return NextNormal( iCur, iMax ); + case ORDER_INVERSE_REPEAT: return NextNormalRepeat( iCur, iMax ); + case ORDER_RANDOM: return NextRandom( iCur, iMax ); + default: return false; + } +} + diff --git a/Externals/MusicMod/Player/Src/Player.cpp b/Externals/MusicMod/Player/Src/Player.cpp new file mode 100644 index 0000000000..8c1075ba56 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Player.cpp @@ -0,0 +1,380 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + + +////////////////////////////////////////////////////////////////////////////////////////// +// Usage instructions +// ŻŻŻŻŻŻŻŻŻŻŻŻŻŻ + +// ======================================================================================= +// Plugins +// --------------------------------------------------------------------------------------- +// Plainamp setup > The output plugin must be manually loaded and activated the first time it's used. +// After that the ini saves OutputPluginActive___out_wave_gpl.dll=1. Input plugins are automatically +// found, loaded and activated. +// ======================================================================================= + + +// ======================================================================================= +// The ini path szIniPath +// --------------------------------------------------------------------------------------- +/* We will get problems if the program can't find the ini settings. Plugins will not be loaded, + or loadedand then unloaded before activated, or not working. */ +// ======================================================================================= + +/////////////////////////////////// + + + +////////////////////////////////////////////////////////////////////////////////////////// +// Include +// ŻŻŻŻŻŻŻŻŻŻ +#include +//#include "Unicode.h" +//#include "Output.h" +#include +#include + +#include "Global.h" // Local +#include "Font.h" +#include "InputPlugin.h" +#include "OutputPlugin.h" +#include "VisPlugin.h" +#include "DspPlugin.h" +#include "GenPlugin.h" +#include "Main.h" +#include "Rebar.h" +#include "Playlist.h" +#include "Status.h" +#include "PluginManager.h" +#include "Prefs.h" +#include "Config.h" +#include "Emabox/Emabox.h" +#include "Console.h" + +#include "PlayerExport.h" // DLL Player +///////////////////////// + + + +////////////////////////////////////////////////////////////////////////////////////////// +// Declarations and definitions +// ŻŻŻŻŻŻŻŻŻŻ + + +// ------------------------- +// Keys +// --------- +#define PLUS_ALT ( FVIRTKEY | FALT ) +#define PLUS_CONTROL ( FVIRTKEY | FCONTROL ) +#define PLUS_CONTROL_ALT ( FVIRTKEY | FCONTROL | FALT ) +#define PLUS_CONTROL_SHIFT ( FVIRTKEY | FCONTROL | FSHIFT ) +#define PLUS_SHIFT ( FVIRTKEY | FSHIFT ) +// ------------- + + +HINSTANCE g_hInstance = NULL; // extern +HINSTANCE hInstance = NULL; // extern + +TCHAR * szHomeDir = NULL; // extern +int iHomeDirLen = 0; // extern + +TCHAR * szPluginDir = NULL; // extern +int iPluginDirLen = 0; // extern + + +// ------------------------- +/* Read global settings from the ini file. They are read from the ini file through Config.h. + Usage: ( &where to place it, the option name, public or private, default value) */ +// --------- +TCHAR szCurDir[ MAX_PATH + 1 ] = TEXT( "" ); +ConfCurDir ccdCurDir( szCurDir, TEXT( "CurDir" ) ); +// ------------------------- + +bool bWarnPluginsMissing; +ConfBool cbWarnPluginsMissing( &bWarnPluginsMissing, TEXT( "WarnPluginsMissing" ), CONF_MODE_PUBLIC, false ); + +bool bLoop; +ConfBool cbLoop( &bLoop, TEXT( "Loop" ), CONF_MODE_PUBLIC, false ); +///////////////////////// + + + +//////////////////////////////////////////////////////////////////////////////// +// The first function that is called by the exe +//////////////////////////////////////////////////////////////////////////////// +void Player_Main(bool Console) +{ + // ======================================================================================= + // Set variables + //g_hInstance = hInstance; + // ======================================================================================= + + //printf( "DLL > main_dll() opened\n" ); + + if (Console) Player_Console(true); + + //MessageBox(0, "main() opened", "", 0); + //printf( "main() opened\n" ); + INFO_LOG(AUDIO,"\n=========================================================\n\n\n" ); + //INFO_LOG(AUDIO, "DLL > Player_Main() > Begin\n" ); + INFO_LOG(AUDIO, "DLL > Settings:\n", bLoop); + + + // ======================================================================================= + // Load full config from ini file + //Conf::Init( hInstance ); + Conf::Init( ); + + // --------------------------------------------------------------------------------------- + + INFO_LOG(AUDIO, "DLL > Loop: %i\n", bLoop); + INFO_LOG(AUDIO, "DLL > WarnPluginsMissing: %i\n", bWarnPluginsMissing); + // --------------------------------------------------------------------------------------- + + // ======================================================================================= + + + // ======================================================================================= + /* Get home dir. We use the TCHAR type for the dirname to insure us against foreign letters + in the dirnames */ + szHomeDir = new TCHAR[ MAX_PATH + 1 ]; + iHomeDirLen = GetModuleFileName( NULL, szHomeDir, MAX_PATH ); + //if( !iHomeDirLen ) return 1; + + // --------------------------------------------------------------------------------------- + // Walk through the pathname and look for backslashes + TCHAR * walk = szHomeDir + iHomeDirLen - 1; + while( ( walk > szHomeDir ) && ( *walk != TEXT( '\\' ) ) ) walk--; + // --------------------------------------------------------------------------------------- + + walk++; + *walk = TEXT( '\0' ); + iHomeDirLen = walk - szHomeDir; + // ======================================================================================= + + + // ======================================================================================= + /* Get plugins dir. Notice to change the number 8 in two places below if the dir name + is changed */ + szPluginDir = new TCHAR[ MAX_PATH + 1 ]; + memcpy( szPluginDir, szHomeDir, iHomeDirLen * sizeof( TCHAR ) ); + memcpy( szPluginDir + iHomeDirLen, TEXT( "PluginsMusic" ), 12 * sizeof( TCHAR ) ); + szPluginDir[ iHomeDirLen + 12 ] = TEXT( '\0' ); + INFO_LOG(AUDIO,"DLL > Plugindir: %s\n", szPluginDir); + // ======================================================================================= + #ifndef NOGUI + Font::Create(); + //Console::Append( TEXT( "Winmain.cpp called Font::Create()" ) ); + #endif + + + // --------------------------------------------------------------------------------------- + // Set volume. This must probably be done after the dll is loaded. + //GlobalVolume = Playback::Volume::Get(); // Don't bother with this for now + //GlobalCurrentVolume = GlobalVolume; + //Output_SetVolume( GlobalVolume ); + INFO_LOG(AUDIO,"DLL > Volume: %i\n\n", GlobalVolume); + // --------------------------------------------------------------------------------------- + + + // ======================================================================================= + // The only thing this function currently does is creating the Playlist. + // ======================================================================================= + BuildMainWindow(); + + //addfiletoplaylist("c:\\zelda\\demo37_01.ast"); + //addfiletoplaylist("c:\\zelda\\demo36_02.ast"); + //Console::Append( TEXT( "Winmain.cpp called BuildMainWindow()" ) ); + + //Prefs::Create(); // This creates windows preferences + //Console::Append( TEXT( "Winmain.cpp called Prefs::Create()" ) ); + + // Find plugins + Plugin::FindAll ( szPluginDir, TEXT( "in_*.dll" ), true ); + Plugin::FindAll( szPluginDir, TEXT( "out_*.dll" ), false ); + Plugin::FindAll ( szPluginDir, TEXT( "vis_*.dll" ), false ); + Plugin::FindAll ( szPluginDir, TEXT( "dsp_*.dll" ), false ); + Plugin::FindAll ( szPluginDir, TEXT( "gen_*.dll" ), true ); + + //INFO_LOG(AUDIO, "Winmain.cpp > PluginManager::Fill()\n" ); + PluginManager::Fill(); + + //INFO_LOG(AUDIO, "Winmain.cpp > PluginManager::Fill()\n" ); + + + + // ======================================================================================= + + // Check plugin presence + // ======================================================================================= + + + // ======================================================================================= + + // Todo: all the rest... + // ACCEL accels[] = { + + // ======================================================================================= + + + + // ======================================================================================= + + //Playback::Play(); + //play_file("C:\\opening.hps"); + //play_file("C:\\Files\\Spel och spelfusk\\Console\\Gamecube\\Code\\Dolphin\\Binary\\win32\\evt_x_event_00.dsp"); + //printf("Winmain.cpp called Playback.cpp:Playback::Play()\n"); + + // ======================================================================================= + // ---- Set volume and get current location ---- + // Somehow we don't have access to active_input_plugin->plugin->GetLength() from here so + // we have to call it in Playback::UpdateSeek() instead + //Sleep(1000); + + //Playback::UpdateSeek(); + //Output_SetVolume( 100 ); // volume is better set from the ini file + // --------------------------------------------------------------------------------------- + // ======================================================================================= + + + // ======================================================================================= + // Check the plugins + if( input_plugins.empty() ) + { + INFO_LOG(AUDIO,"\n *** Warning: No valid input plugins found\n\n"); + } + else + { + INFO_LOG(AUDIO," >>> These valid input plugins were found:\n"); + for(int i = 0; i < input_plugins.size(); i++) + INFO_LOG(AUDIO," %i: %s\n", (i + 1), input_plugins.at(i)->GetFilename()); + INFO_LOG(AUDIO,"\n"); + } + + // The input plugins are never activated here, they are activate for each file + if( !active_input_plugin || !active_input_plugin->plugin ) + { + // INFO_LOG(AUDIO,"The input plugin is not activated yet\n"); + } + else + { + //const int ms_len = active_input_plugin->plugin->GetLength(); + //const int ms_cur = active_input_plugin->plugin->GetOutputTime(); + //INFO_LOG(AUDIO,"We are at <%i of %i>\n", ms_cur, ms_len); + } + // --------------------------------------------------------------------------------------- + if( active_output_count > 0 ) + { + // Show current playback progress + /*int res_temp; + for( int i = 0; i < active_output_count; i++ ) + { + res_temp = active_output_plugins[ i ]->plugin->GetOutputTime(); + } + INFO_LOG(AUDIO,"Playback progress <%i>\n", res_temp);*/ + } + else + { + INFO_LOG(AUDIO,"\n *** Warning: The output plugin is not working\n\n"); + } + // ======================================================================================= + + // ======================================================================================= + // Start the timer + if(!TimerCreated && bLoop) // Only create this the first time + { + //INFO_LOG(AUDIO,"Created the timer\n"); + MakeTime(); + TimerCreated = true; + } + // ======================================================================================= + + INFO_LOG(AUDIO, "\n=========================================================\n\n" ); + //INFO_LOG(AUDIO, "DLL > main_dll() > End\n\n\n" ); + + //std::cin.get(); +} + + + + + + +// ======================================================================================= +// Should I use this? +void close() +{ + + printf( "The Winmain.cpp message loop was reached\n" ); + + // Message loop + //std::cin.get(); + + Playback::Stop(); // If we don't call this before we unload the dll we get a crash + + printf("We are now past the message loop\n" ); + + //DestroyAcceleratorTable( hAccel ); + + + // ======================================================================================= + // Input + vector ::iterator iter_input = input_plugins.begin(); + while( iter_input != input_plugins.end() ) + { + ( *iter_input )->Unload(); + iter_input++; + } + + // Output + vector ::iterator iter_output = output_plugins.begin(); + while( iter_output != output_plugins.end() ) + { + ( *iter_output )->Unload(); + iter_output++; + } + + // General + vector ::iterator iter_gen = gen_plugins.begin(); + while( iter_gen != gen_plugins.end() ) + { + ( *iter_gen )->Unload(); + iter_gen++; + } + // ======================================================================================= + + + // TODO: create main::destroy + // UnregisterClass( PA_CLASSNAME, g_hInstance ); + + //Prefs::Destroy(); + + //Font::Destroy(); + +/* + delete [] szPluginDir; + delete [] szHomeDir; +*/ + + // --------------------------------------------------------------------------------------- + // We don't save any changes + //Conf::Write(); + + //printf("Winmain.cpp called Conf::Write(), the last function\n"); + // --------------------------------------------------------------------------------------- + + //std::cin.get(); // Let use see all messages + + //return 0; +} +// ======================================================================================= diff --git a/Externals/MusicMod/Player/Src/PlayerExport.cpp b/Externals/MusicMod/Player/Src/PlayerExport.cpp new file mode 100644 index 0000000000..db35042358 --- /dev/null +++ b/Externals/MusicMod/Player/Src/PlayerExport.cpp @@ -0,0 +1,181 @@ + +////////////////////////////////////////////////////////////////////////////////////////// +// Include +// ŻŻŻŻŻŻŻŻŻŻ +#include // System + +#include "Common.h" // Global common +#include "Log.h" + +//#include "../../Common/Src/Console.h" // Local common + +#include "OutputPlugin.h" // Local +#include "Playback.h" +#include "Playlist.h" + +#define _DLL_PLAYER_H_ +#include "PlayerExport.h" // DLL Player +////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////////// +// Declarations and definitions +// ŻŻŻŻŻŻŻŻŻŻ +std::string CurrentlyPlayingFile; +int GlobalVolume = -1; +bool GlobalMute = false; +bool GlobalPause; +bool TimerCreated = false; +bool Initialized = false; +///////////////////////////////// + + +// ------------------------- +/* We keep the file in the playlist, even though we currently only ever have one file here + at a time */ +// --------- +void AddFileToPlaylist(char * a) +{ + //playlist->RemoveAll(); + + #include "unicode.h" + const int iLen = strlen(a); // I can't do this because I don't + + printf( "iLen <%i>\n", iLen ); + + // --------------------------------------------------------------------------------------- + // Do some string conversion + TCHAR * szKeep; + szKeep = new TCHAR[ iLen + 1 ]; + ToTchar( szKeep, a, iLen ); + szKeep[ iLen ] = TEXT( '\0' ); + playlist->PushBack( szKeep ); + // --------------------------------------------------------------------------------------- + + // If we added a second file the current index = -1 so we have to change that back + playlist->SetCurIndex( 0 ); +} + + + +void Player_Play(char * FileName) +{ + INFO_LOG(AUDIO,"Play file <%s>\n", FileName); + + // Check if the file exists + if(GetFileAttributes(FileName) == INVALID_FILE_ATTRIBUTES) + { + INFO_LOG(AUDIO,"Warning: The file <%s> does not exist. Something is wrong.\n", FileName); + return; + } + + Playback::Stop(); + //INFO_LOG(AUDIO,"Stop\n"); + playlist->RemoveAll(); + //INFO_LOG(AUDIO,"RemoveAll\n"); + AddFileToPlaylist(FileName); + //INFO_LOG(AUDIO,"addfiletoplaylist\n"); + + // Play the file + Playback::Play(); + + CurrentlyPlayingFile = FileName; + + // --------------------------------------------------------------------------------------- + // Set volume. This must probably be done after the dll is loaded. + //Output_SetVolume( Playback::Volume::Get() ); + //INFO_LOG(AUDIO,"Volume(%i)\n", Playback::Volume::Get()); + // --------------------------------------------------------------------------------------- + + GlobalPause = false; +} + +void Player_Stop() +{ + Playback::Stop(); + //INFO_LOG(AUDIO,"Stop\n"); + playlist->RemoveAll(); + + CurrentlyPlayingFile = ""; + + GlobalPause = false; +} + + +void Player_Pause() +{ + if (!GlobalPause) + { + INFO_LOG(AUDIO,"DLL > Pause\n"); + Playback::Pause(); + GlobalPause = true; + } + else + { + INFO_LOG(AUDIO,"DLL > UnPause from Pause\n"); + Player_Unpause(); + GlobalPause = false; + } +} + +void Player_Unpause() +{ + INFO_LOG(AUDIO,"DLL > UnPause\n"); + Playback::Play(); + GlobalPause = false; +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// Declarations and definitions +// ŻŻŻŻŻŻŻŻŻŻŻŻŻ +/* About the volume: The colume is normally update for the output plugin in Output.cpp and for the + inout plugin (why?) in Playback.cpp with Playback::Volume::Set(). But I don't know how that works + so I only use GlobalVolume to keep track of the volume */ +// ------------------------------ +void Player_Mute(int Vol) +{ + if(GlobalVolume == -1) GlobalVolume = Vol; + INFO_LOG(AUDIO,"DLL > Mute <%i> <%i>\n", GlobalVolume, GlobalMute); + + GlobalMute = !GlobalMute; + + // Set volume + if(GlobalMute) + { + Output_SetVolume( 0 ); + INFO_LOG(AUDIO,"DLL > Volume <%i>\n", GlobalMute); + } + else + { + Output_SetVolume( GlobalVolume ); + INFO_LOG(AUDIO,"DLL > Volume <%i>\n", GlobalMute); + } + + //INFO_LOG(AUDIO,"Volume(%i)\n", Playback::Volume::Get()); +} +/////////////////////////////////////// + + +void Player_Volume(int Vol) +{ + GlobalVolume = Vol; + Output_SetVolume( GlobalVolume ); + //INFO_LOG(AUDIO,"DLL > Volume <%i> <%i>\n", GlobalVolume, GlobalCurrentVolume); +} + +void ShowConsole() +{ +// Console::Open(100, 2000, "MusicMod", true); // give room for 2000 rows +} + + +void Player_Console(bool Console) +{ + if(Console) + ShowConsole(); + else + #if defined (_WIN32) + FreeConsole(); + #endif +} diff --git a/Externals/MusicMod/Player/Src/PlayerExport.h b/Externals/MusicMod/Player/Src/PlayerExport.h new file mode 100644 index 0000000000..54ed9fba31 --- /dev/null +++ b/Externals/MusicMod/Player/Src/PlayerExport.h @@ -0,0 +1,49 @@ +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + + + +////////////////////////////////////////////////////////////////////////////////////////// +// Include +// ŻŻŻŻŻŻŻŻŻŻ + +////////////////////////////////// + + +// ------------------------- +// DLL Player +// --------- +void MakeTime(); +#ifndef _DLL_PLAYER_H_ + extern int GlobalVolume; + extern int GlobalCurrentVolume; + extern bool GlobalPause; + extern bool TimerCreated; +#endif + + +// Dll export +#define EXPORT __declspec(dllexport) +EXPORT void Player_Main(bool Console); +EXPORT void Player_Console(bool Console); +EXPORT void Player_Play(char *); +EXPORT void Player_Stop(); +EXPORT void Player_Pause(); +EXPORT void Player_Unpause(); +EXPORT void Player_Mute(int Vol); +EXPORT void Player_Volume(int Vol); + diff --git a/Externals/MusicMod/Player/Src/Playlist.cpp b/Externals/MusicMod/Player/Src/Playlist.cpp new file mode 100644 index 0000000000..68060b72b4 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Playlist.cpp @@ -0,0 +1,1383 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Playlist.h" +#include "AddDirectory.h" +#include "Rebar.h" +#include "Main.h" +#include "Status.h" +#include "Console.h" +#include "Font.h" +#include "Playback.h" +#include "InputPlugin.h" +#include "Prefs.h" +#include "Config.h" +#include "Unicode.h" +#include "Path.h" +#include "commdlg.h" + + + + + + + + +HWND WindowPlaylist = NULL; // extern +// WNDPROC WndprocPlaylistBackup = NULL; + +// int iCurIndex = -1; +// int iMaxIndex = -1; + + + +// LRESULT CALLBACK WndprocPlaylist( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); +void Playlist_SelectSingle( int iIndex ); + + +struct PlaylistEntry +{ + TCHAR * szFilename; + // More to come +}; + +/* +/////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int Playlist::GetCurIndex() +{ + return iCurIndex; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int Playlist::GetMaxIndex() +{ + return iMaxIndex; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playlist::SetCurIndex( int iIndex ) +{ + if( iIndex < 0 || iIndex > iMaxIndex ) return false; + + iCurIndex = iIndex; + if( bPlaylistFollow ) + { + Playlist_SelectSingle( iCurIndex ); + } + + return true; +} +*/ + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playlist::Create() +{ + if( WindowPlaylist ) return false; + + // If this is not called a crash occurs + PlaylistView::Create(); + + + #ifndef NOGUI + + RECT ClientMain; + GetClientRect( WindowMain, &ClientMain ); + + const int iClientHeight = ClientMain.bottom - ClientMain.top; + const int iClientWidth = ClientMain.right - ClientMain.left; + const int iPlaylistHeight = iClientHeight - iRebarHeight - iStatusHeight; + + WindowPlaylist = CreateWindowEx( + WS_EX_CLIENTEDGE, + TEXT( "LISTBOX" ), + NULL, + WS_VSCROLL | + LBS_DISABLENOSCROLL | + LBS_EXTENDEDSEL | + LBS_HASSTRINGS | + LBS_NOTIFY | + LBS_NOINTEGRALHEIGHT | + WS_CHILD | + WS_VISIBLE, // + 0, + iRebarHeight, // + -2, + iClientWidth, + iPlaylistHeight, + WindowMain, + NULL, + g_hInstance, + NULL + ); + + // Exchange window procedure + WndprocPlaylistBackup = ( WNDPROC )GetWindowLong( WindowPlaylist, GWL_WNDPROC ); + if( WndprocPlaylistBackup != NULL ) + { + SetWindowLong( WindowPlaylist, GWL_WNDPROC, ( LONG )WndprocPlaylist ); + } + + Font::Apply( WindowPlaylist ); + + #endif NOGUI + + + + TCHAR * szPlaylistMind = new TCHAR[ iHomeDirLen + 12 + 1 ]; + memcpy( szPlaylistMind, szHomeDir, iHomeDirLen * sizeof( TCHAR ) ); + memcpy( szPlaylistMind + iHomeDirLen, TEXT( "Plainamp.m3u" ), 12 * sizeof( TCHAR ) ); + + + + szPlaylistMind[ iHomeDirLen + 12 ] = TEXT( '\0' ); + + + Playlist::AppendPlaylistFile( szPlaylistMind ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +inline void Playlist_SelectSingle( int iIndex ) +{ + SendMessage( + WindowPlaylist, + LB_SETSEL, + FALSE, + -1 + ); + + SendMessage( + WindowPlaylist, + LB_SETSEL, + TRUE, + iIndex + ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +inline bool Playlist_IsSelected( int iIndex ) +{ + return ( 0 != SendMessage( + WindowPlaylist, + LB_GETSEL, + ( WPARAM )iIndex, + 0 + ) ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +inline bool Playlist_SelectModify( int iIndex, bool bSelected ) +{ + return ( LB_ERR != SendMessage( + WindowPlaylist, + LB_SETSEL, + ( WPARAM )( bSelected ? TRUE : FALSE ), + iIndex + ) ); +} + + +/* +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playlist_Remove( int iIndex ) +{ + if( iIndex < 0 || iIndex > iMaxIndex ) return false; + + // Get entry data + PlaylistEntry * entry = ( PlaylistEntry * )SendMessage( + WindowPlaylist, + LB_GETITEMDATA, + iIndex, + 0 + ); + + // Free data + if( entry ) + { + if( entry->szFilename ) delete [] entry->szFilename; + delete entry; + } + + SendMessage( + WindowPlaylist, + LB_DELETESTRING, + iIndex, + 0 + ); + + if( iIndex < iCurIndex ) + iCurIndex--; + if( ( iIndex == iCurIndex ) && ( iCurIndex == iMaxIndex ) ) + iCurIndex = iMaxIndex - 1; + iMaxIndex--; + + return true; +} +*/ + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +inline int Playlist_GetCount() +{ + return ( int )SendMessage( + WindowPlaylist, + LB_GETCOUNT, + 0, + 0 + ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +inline int Playlist_GetCaretIndex() +{ + return ( int )SendMessage( + WindowPlaylist, + LB_GETCARETINDEX, + 0, + 0 + ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +inline bool Playlist_SetCaretIndex( int iIndex ) +{ + return ( LB_OKAY == SendMessage( + WindowPlaylist, + LB_SETCARETINDEX, + ( WPARAM )iIndex, + FALSE + ) ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +inline int Playlist_GetSelCount() +{ + return ( int )SendMessage( + WindowPlaylist, + LB_GETSELCOUNT, + 0, + 0 + ); +} + + +/* +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int Playlist_MouseToIndex() +{ + POINT p; + GetCursorPos( &p ); + ScreenToClient( WindowPlaylist, &p ); + + int iIndex = ( int )SendMessage( + WindowPlaylist, + LB_ITEMFROMPOINT, + 0, + p.x | ( p.y << 16 ) + ); + + if( ( iIndex < 0 ) || ( iIndex > iMaxIndex ) ) + return -1; + else + return iIndex; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playlist::Clear() +{ + if( iMaxIndex < 0 ) return false; + + int iCount = iMaxIndex + 1; + while( iCount-- ) + { + Playlist_Remove( iCount ); + } + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playlist::RemoveSelected() +{ + int iSelCount = Playlist_GetSelCount(); + if( iSelCount < 0 ) return false; + + // Which items are selected? + int * sel = new int[ iSelCount ]; + LRESULT lResult = SendMessage( + WindowPlaylist, + LB_GETSELITEMS, + ( WPARAM )iSelCount, + ( LPARAM )sel + ); + + // Remove + if( lResult > 0 ) + { + while( lResult-- ) + { + int iIndex = sel[ lResult ]; + Playlist_Remove( iIndex ); + } + } + + delete [] sel; + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playlist::Crop() +{ + int iAllCount = Playlist_GetCount(); + if( iAllCount < 1 ) return false; + + int iSelCount = Playlist_GetSelCount(); + if( iSelCount < 0 ) + { + return false; + } + else if( iSelCount == 0 ) + { + // None selected + return Clear(); + } + + // Which items are selected? + int * sel = new int[ iSelCount ]; + LRESULT lResult = SendMessage( + WindowPlaylist, + LB_GETSELITEMS, + ( WPARAM )iSelCount, + ( LPARAM )sel + ); + + int iLowerEqualIndex = iSelCount - 1; + for( int i = iAllCount - 1; i >= 0; i-- ) + { + while( ( sel[ iLowerEqualIndex ] > i ) && ( iLowerEqualIndex > 0 ) ) + { + iLowerEqualIndex--; + } + + if( i != sel[ iLowerEqualIndex ] ) + { + // Not selected -> remove + Playlist_Remove( i ); + } + } + + delete [] sel; + return true; +} +*/ + +/* +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playlist_MoveSel( bool bUpOrDown ) +{ + static bool bMoving = false; + if( bMoving ) return false; + + bMoving = true; + + const int iSelCount = Playlist_GetSelCount(); + if( iSelCount < 0 ) + { + // No items selected + bMoving = false; + return false; + } + + // Which items are selected? + int * sel = new int[ iSelCount ]; + LRESULT lResult = SendMessage( + WindowPlaylist, + LB_GETSELITEMS, + ( WPARAM )iSelCount, + ( LPARAM )sel + ); + + if( lResult <= 0 ) + { + // Nothing to move + delete [] sel; + bMoving = false; + return false; + } + + if( ( bUpOrDown && ( sel[ 0 ] == 0 ) ) || + ( !bUpOrDown && ( sel[ iSelCount - 1 ] == iMaxIndex ) ) ) + { + // Cannot move + delete [] sel; + bMoving = false; + return false; + } + + const int iOldTop = ( int )SendMessage( + WindowPlaylist, + LB_GETTOPINDEX, + 0, + 0 + ); + + // 1 _2_[3][4][5] 6 7 [8] 9 + // --> 1 [3][4][5]_2_ 6 [8]_7_ 9 + + // Redrawing OFF + // SendMessage( WindowPlaylist, WM_SETREDRAW, ( WPARAM )FALSE, 0 ); + + int i = ( bUpOrDown ? 0 : iSelCount - 1 ); + do + { + // Backup the jumper + PlaylistEntry * entry_old = ( PlaylistEntry * )SendMessage( + WindowPlaylist, + LB_GETITEMDATA, + sel[ i ] + ( bUpOrDown ? -1 : 1 ), + 0 + ); + + do + { + // Copy on + PlaylistEntry * entry_new = ( PlaylistEntry * )SendMessage( + WindowPlaylist, + LB_GETITEMDATA, + sel[ i ], + 0 + ); + + int iDest = sel[ i ] + ( bUpOrDown ? -1 : 1 ); + + // Update entry display == delete, insert, set data + SendMessage( + WindowPlaylist, + LB_DELETESTRING, + iDest, + 0 + ); + iDest = ( int )SendMessage( + WindowPlaylist, + LB_INSERTSTRING, + iDest, + ( LPARAM )entry_new->szFilename + ); + SendMessage( + WindowPlaylist, + LB_SETITEMDATA, + iDest, + ( LPARAM )entry_new + ); + + if( sel[ i ] == iCurIndex ) + { + iCurIndex += ( bUpOrDown ? -1 : 1 ); + } + + i += ( bUpOrDown ? 1 : -1 ); + } while( bUpOrDown + ? + ( i < iSelCount ) && ( sel[ i - 1 ] + 1 == sel[ i ] ) + : + ( i >= 0 ) && ( sel[ i + 1 ] - 1 == sel[ i ] ) + ); + + // Place the jumper + int iLast = ( bUpOrDown ? sel[ i - 1 ] : sel[ i + 1 ] ); + + // Update entry display == delete, insert, set data + SendMessage( + WindowPlaylist, + LB_DELETESTRING, + iLast, + 0 + ); + iLast = ( int )SendMessage( + WindowPlaylist, + LB_INSERTSTRING, + iLast, + ( LPARAM )entry_old->szFilename + ); + SendMessage( + WindowPlaylist, + LB_SETITEMDATA, + iLast, + ( LPARAM )entry_old + ); + } while( bUpOrDown + ? + ( i < iSelCount ) + : + ( i >= 0 ) + ); + + // Select new indices (old selection went away on insert/delete + if( bUpOrDown ) + { + for( i = 0; i < iSelCount; i++ ) + SendMessage( WindowPlaylist, LB_SETSEL, TRUE, sel[ i ] - 1 ); + } + else + { + for( i = 0; i < iSelCount; i++ ) + SendMessage( WindowPlaylist, LB_SETSEL, TRUE, sel[ i ] + 1 ); + } + + // Prevent scrolling + SendMessage( + WindowPlaylist, + LB_SETTOPINDEX, + ( WPARAM )iOldTop, + 0 + ); + + // Redrawing ON + // SendMessage( WindowPlaylist, WM_SETREDRAW, ( WPARAM )TRUE, 0 ); + + + delete [] sel; + bMoving = false; + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +LRESULT CALLBACK WndprocPlaylist( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + + static bool bDragging = false; + static bool bMoveLock = false; + static int iDragStartY; + static int iItemHeight = 0x7fffffff; + + switch( message ) + { + case WM_MOUSEMOVE: + { + if( !bDragging || bMoveLock ) break; + bMoveLock = true; + + const int y = HIWORD( lp ); + const int diff = y - iDragStartY; + if( abs( diff ) > iItemHeight / 2 ) + { + iDragStartY += ( ( diff > 0 ) ? iItemHeight : -iItemHeight ); + Playlist_MoveSel( diff < 0 ); + } + + bMoveLock = false; + break; + } + + case WM_LBUTTONDOWN: + { + if( GetKeyState( VK_MENU ) >= 0 ) break; + + // Dragging ON + iDragStartY = HIWORD( lp ); + iItemHeight = ( int )SendMessage( + WindowPlaylist, + LB_GETITEMHEIGHT, + 0, + 0 + ); + bDragging = true; + + return 0; + } + + case WM_LBUTTONUP: + // Dragging OFF + bDragging = false; + break; + + case WM_SYSKEYDOWN: + switch( wp ) // [Alt]+[...] + { + case VK_UP: + Playlist_MoveSel( true ); + break; + + case VK_DOWN: + Playlist_MoveSel( false ); + break; + } + break; + + case WM_CHAR: + case WM_KEYUP: + // SMALL LETTERS!!!!!! + switch( wp ) + { + case 'z': + case 'y': + case 'x': + case 'c': + case 'v': + case 'b': + case 'l': + return 0; + } + break; + + case WM_KEYDOWN: + { + const bool bShift = ( ( GetKeyState( VK_SHIFT ) & 0x8000 ) != 0 ); + const bool bControl = ( ( GetKeyState( VK_CONTROL ) & 0x8000 ) != 0 ); + // const bool bAlt = ( ( GetKeyState( VK_MENU ) & 0x8000 ) != 0 ); + + + switch( wp ) + { + case VK_LEFT: + if( bShift || bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, WINAMP_REW5S, 0 ); + return 0; + + case VK_RIGHT: + if( bShift || bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, WINAMP_FFWD5S, 0 ); + return 0; + + case VK_UP: + if( bControl && !bShift ) + { + // Move caret not selection + const int iCaretBefore = Playlist_GetCaretIndex(); + if( iCaretBefore > 0 ) + { + Playlist_SetCaretIndex( iCaretBefore - 1 ); + } + else if( ( iCaretBefore == 0 ) && bInfinitePlaylist ) + { + Playlist_SetCaretIndex( iMaxIndex ); + } + return 0; + } + else + { + if( bInfinitePlaylist ) + { + if( Playlist_GetCaretIndex() != 0 ) break; + Playlist_SelectSingle( iMaxIndex ); + return 0; // Or it will increase one more + } + } + break; + + case VK_DOWN: + if( bControl && !bShift ) + { + // Move caret not selection + const int iCaretBefore = Playlist_GetCaretIndex(); + if( ( iCaretBefore < iMaxIndex ) && ( iCaretBefore >= 0 ) ) + { + Playlist_SetCaretIndex( iCaretBefore + 1 ); + } + else if( ( iCaretBefore == iMaxIndex ) && bInfinitePlaylist ) + { + Playlist_SetCaretIndex( 0 ); + } + return 0; + } + else + { + if( bInfinitePlaylist ) + { + if( Playlist_GetCaretIndex() != iMaxIndex ) break; + Playlist_SelectSingle( 0 ); + return 0; // Or it will increase one more + } + } + break; + + case VK_SPACE: + if( bControl && !bShift ) + { + const int iCaret = Playlist_GetCaretIndex(); + if( iCaret == -1 ) return 0; + bool bSelected = Playlist_IsSelected( iCaret ); + Playlist_SelectModify( iCaret, !bSelected ); + } + return 0; + + case VK_DELETE: + { + if( bShift ) break; + + if( bControl ) + playlist->RemoveSelected( false ); + else + playlist->RemoveSelected( true ); + + break; + } + + case VK_RETURN: + iCurIndex = Playlist_GetCaretIndex(); + SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON2, 0 ); + return 0; + + case 'Y': + case 'Z': + if( bShift || bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON1, 0 ); + return 0; + + case 'X': + if( bShift || bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON2, 0 ); + return 0; + + case 'C': + if( bShift || bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON3, 0 ); + return 0; + + case 'V': + // Todo modifiers pressed? -> fadeout/... + if( bShift || bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON4, 0 ); + return 0; + + case 'B': + if( bShift || bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON5, 0 ); + return 0; + / + case 'J': + if( bShift || bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, WINAMP_JUMPFILE, 0 ); + return 0; + / + case 'L': + if( bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, bShift ? WINAMP_FILE_DIR : WINAMP_FILE_PLAY, 0 ); + return 0; + } + break; + } + + case WM_LBUTTONDBLCLK: + iCurIndex = Playlist_MouseToIndex(); + if( iCurIndex < 0 ) break; + Playback::Play(); + Playback::UpdateSeek(); + break; + + } + return CallWindowProc( WndprocPlaylistBackup, hwnd, message, wp, lp ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playlist::Add( int iIndex, TCHAR * szDisplay, TCHAR * szFilename ) +{ + iMaxIndex++; + if( iIndex < 0 || iIndex > iMaxIndex ) iIndex = iMaxIndex; + if( iIndex <= iCurIndex ) + { + iCurIndex++; + } + + // Create entry data + PlaylistEntry * new_entry = new PlaylistEntry; + new_entry->szFilename = szFilename; + + iIndex = ( int )SendMessage( + WindowPlaylist, + LB_INSERTSTRING, // LB_ADDSTRING, + iIndex, + ( LPARAM )szDisplay + ); + + // Associate data + SendMessage( + WindowPlaylist, + LB_SETITEMDATA, + iIndex, + ( LPARAM )new_entry + ); + + return true; +} +*/ + + +//////////////////////////////////////////////////////////////////////////////// +/// Opens a dialog box and loads the playlist if is [true], +/// or saves the playlist if it is [false]. +//////////////////////////////////////////////////////////////////////////////// +bool Playlist_DialogBoth( bool bOpenOrSave ) +{ + TCHAR szFilters[] = TEXT( + "All files (*.*)\0*.*\0" + "Playlist files (*.M3U)\0*.m3u\0" + "\0" + ); + TCHAR szFilename[ MAX_PATH ] = TEXT( "\0" ); + + OPENFILENAME ofn; + memset( &ofn, 0, sizeof( OPENFILENAME ) ); + ofn.lStructSize = sizeof( OPENFILENAME ); + ofn.hwndOwner = WindowMain; + ofn.hInstance = g_hInstance; + ofn.lpstrFilter = szFilters; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 2; + ofn.lpstrFile = szFilename; + ofn.nMaxFile = MAX_PATH; + ofn.Flags = OFN_EXPLORER | + OFN_ENABLESIZING | + ( bOpenOrSave ? OFN_FILEMUSTEXIST : OFN_OVERWRITEPROMPT ) | + OFN_PATHMUSTEXIST | + OFN_HIDEREADONLY; + ofn.nMaxFileTitle = 0, + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = ( bOpenOrSave ? TEXT( "Open playlist" ) : TEXT( "Save playlist" ) ); + + if( bOpenOrSave ) + { + if( !GetOpenFileName( &ofn ) ) return false; + } + else + { + if( !GetSaveFileName( &ofn ) ) return false; + } + + if( bOpenOrSave ) + { + // Open + const int iFilenameLen = ( int )_tcslen( szFilename ); + if( !_tcsncmp( szFilename + iFilenameLen - 3, TEXT( "m3u" ), 3 ) ) + { + // Playlist file + playlist->RemoveAll(); + Playlist::AppendPlaylistFile( szFilename ); + Playback::Play(); + + + Console::Append( TEXT( "Playlist.cpp:Playlist_DialogBoth() called Playback::Play()" ) ); + Console::Append( TEXT( " " ) ); + } + } + else + { + // TODO: Check extension, ask for appending if missing + + // Save + Playlist::ExportPlaylistFile( szFilename ); + } + + return true; + +} + + +//////////////////////////////////////////////////////////////////////////////// +/// Opens a dialog box and loads the selected playlist +//////////////////////////////////////////////////////////////////////////////// +bool Playlist::DialogOpen() +{ + return Playlist_DialogBoth( true ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// Opens a dialog box and saves the playlist to the filename selected +//////////////////////////////////////////////////////////////////////////////// +bool Playlist::DialogSaveAs() +{ + return Playlist_DialogBoth( false ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playlist::AppendPlaylistFile( TCHAR * szFilename ) +{ + + + + // Open playlist file + HANDLE hFile = CreateFile( + szFilename, // LPCTSTR lpFileName + FILE_READ_DATA, // DWORD dwDesiredAccess + FILE_SHARE_READ, // DWORD dwShareMode + NULL, // LPSECURITY_ATTRIBUTES lpSecurityAttributes + OPEN_EXISTING, // DWORD dwCreationDisposition + FILE_ATTRIBUTE_NORMAL, // DWORD dwFlagsAndAttributes + NULL // HANDLE hTemplateFile + ); + + // This will happen if there is no Playlist.m3u + /* + if( hFile == INVALID_HANDLE_VALUE ) + { + // MessageBox( 0, TEXT( "Could not read playlist file" ), TEXT( "Error" ), MB_ICONERROR ); + //Console::Append( "We got INVALID_HANDLE_VALUE" ); + return false; + } + */ + // Disable this + //const bool bEmptyBefore = ( playlist->GetSize() == 0 ); + + +// ======================================================================================= + // Remove filename from so we can + // use it as relative directory root + TCHAR * szWalk = szFilename + _tcslen( szFilename ) - 1; + while( ( szWalk > szFilename ) && ( *szWalk != TEXT( '\\' ) ) ) szWalk--; + szWalk++; + *szWalk = TEXT( '\0' ); + + TCHAR * szBaseDir = szFilename; + const int iBaseDirLen = ( int )_tcslen( szBaseDir ); + + + DWORD iSizeBytes = GetFileSize( hFile, NULL ); + if( iSizeBytes <= 0 ) + { + CloseHandle( hFile ); + return false; + } + + // Allocate + char * rawdata = new char[ iSizeBytes + 1 ]; // One more so we can write '\0' on EOF + DWORD iBytesRead; + + // ======================================================================================= + // Read whole file + ReadFile( + hFile, // HANDLE hFile + rawdata, // LPVOID lpBuffer + iSizeBytes, // DWORD nNumberOfBytesToRead + &iBytesRead, // LPDWORD lpNumberOfBytesRead + NULL // LPOVERLAPPED lpOverlapped + ); + + if( iBytesRead < iSizeBytes ) + { + delete [] rawdata; + CloseHandle( hFile ); + + MessageBox( 0, TEXT( "Could not read whole file" ), TEXT( "Error" ), MB_ICONERROR ); + return false; + } + + // Parse file content + + // File must be + // * M3U + // * ANSI + + char * walk = rawdata; + const char * eof = rawdata + iSizeBytes; + + char * beg = rawdata; + char * end; + + while( true ) + { + // Find newline or eof + while( ( walk < eof ) && ( *walk != '\015' ) && ( *walk != '\012' ) ) walk++; + end = walk; + + if( ( end - beg > 2 ) && ( *beg != '#' ) ) + { + + TCHAR * szKeep; + if( beg[ 1 ] == ':' ) + { + // TODO: Better detection, network path? + + // Absolute path, skip this + /* + const int iLen = end - beg; + + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "iLen <%i>" ), iLen ); + Console::Append( szBuffer ); + + szKeep = new TCHAR[ iLen + 1 ]; + ToTchar( szKeep, beg, iLen ); + szKeep[ iLen ] = TEXT( '\0' ); + */ + + } + else + { + // Skip initial so we don't get a double backslash in between + + + + while( ( beg[ 0 ] == '\\' ) && ( beg < end ) ) beg++; + + // Relative path + const int iSecondLen = end - beg; + szKeep = new TCHAR[ iBaseDirLen + iSecondLen + 1 ]; + memcpy( szKeep, szBaseDir, iBaseDirLen * sizeof( TCHAR ) ); + ToTchar( szKeep + iBaseDirLen, beg, iSecondLen ); + + szKeep[ iBaseDirLen + iSecondLen ] = TEXT( '\0' ); + + UnbloatFilename( szKeep, false ); + } + + // if( !Add( iMaxIndex + 1, szKeep, szKeep ) ) break; + playlist->PushBack( szKeep ); + } + + // Skip newlines + while( ( walk < eof ) && ( ( *walk == '\015' ) || ( *walk == '\012' ) ) ) walk++; + if( walk == eof ) + { + break; + } + + beg = walk; + } + + delete [] rawdata; + CloseHandle( hFile ); +/* + if( bEmptyBefore ) + { + iCurIndex = 0; + } +*/ + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playlist::ExportPlaylistFile( TCHAR * szFilename ) +{ + // Open playlist file + HANDLE hFile = CreateFile( + szFilename, // LPCTSTR lpFileName + FILE_WRITE_DATA, // DWORD dwDesiredAccess + 0, // DWORD dwShareMode + NULL, // LPSECURITY_ATTRIBUTES lpSecurityAttributes + CREATE_ALWAYS, // DWORD dwCreationDisposition + FILE_ATTRIBUTE_NORMAL, // DWORD dwFlagsAndAttributes + NULL // HANDLE hTemplateFile + ); + + if( hFile == INVALID_HANDLE_VALUE ) + { + MessageBox( 0, TEXT( "Could not write playlist file" ), TEXT( "Error" ), MB_ICONERROR ); + return false; + } + + + // Remove filename from so we can + // use it as relative directory root + TCHAR * szWalk = szFilename + _tcslen( szFilename ) - 1; + while( ( szWalk > szFilename ) && ( *szWalk != TEXT( '\\' ) ) ) szWalk--; + szWalk++; + *szWalk = TEXT( '\0' ); + + TCHAR * szBaseDir = szFilename; + const int iBaseDirLen = ( int )_tcslen( szBaseDir ); + + + char * rawdata = new char[ ( playlist->GetMaxIndex() + 1 ) * ( MAX_PATH + 2 ) ]; + char * walk = rawdata; + + // Write playlist to buffer + const int iMaxMax = playlist->GetMaxIndex(); + for( int i = 0; i <= iMaxMax; i++ ) + { + // Get + TCHAR * szEntry = GetFilename( i ); + if( !szEntry ) break; + int iEntryLen = ( int )_tcslen( szEntry ); + + // Copy + TCHAR * szTemp = new TCHAR[ iEntryLen + 1 ]; + memcpy( szTemp, szEntry, iEntryLen * sizeof( TCHAR ) ); + szTemp[ iEntryLen ] = TEXT( '\0' ); + + // Convert + if( ApplyRootToFilename( szBaseDir, szTemp ) ) + { + // Update length or we are writing too much + iEntryLen = ( int )_tcslen( szTemp ); + } + + // Copy +#ifdef PA_UNICODE + ToAnsi( walk, szTemp, iEntryLen ); +#else + memcpy( walk, szTemp, iEntryLen ); +#endif + + delete [] szTemp; + + walk += iEntryLen; + memcpy( walk, "\015\012", 2 ); + walk += 2; + } + + const DWORD iSizeBytes = walk - rawdata; + DWORD iBytesRead; + WriteFile( + hFile, // HANDLE hFile, + rawdata, // LPCVOID lpBuffer, + iSizeBytes, // DWORD nNumberOfBytesToWrite, + &iBytesRead, // LPDWORD lpNumberOfBytesWritten, + NULL // LPOVERLAPPED lpOverlapped + ); + + if( iBytesRead < iSizeBytes ) + { + delete [] rawdata; + CloseHandle( hFile ); + + MessageBox( 0, TEXT( "Could not write whole file" ), TEXT( "Error" ), MB_ICONERROR ); + return false; + } + + delete [] rawdata; + CloseHandle( hFile ); + + return true; +} + + +/* +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playlist::Append( TCHAR * szDisplay, TCHAR * szFilename ) +{ + return Add( iMaxIndex + 1, szDisplay, szFilename ); +} +*/ + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +TCHAR * Playlist::GetFilename( int iIndex ) +{ + // if( iIndex < 0 || iIndex > iMaxIndex ) return NULL; + +/* + PlaylistEntry * entry = ( PlaylistEntry * )SendMessage( + WindowPlaylist, + LB_GETITEMDATA, + iIndex, + 0 + ); + + return ( entry ? entry->szFilename : NULL ); + */ + //TCHAR * szFilename = "C:\Files\Spel och spelfusk\Console\Gamecube\Code\vgmstream (isolate ast)\Music\demo36_02.ast"; + //return szFilename; + return ( TCHAR * )playlist->Get( iIndex ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int Playlist::GetFilename( int iIndex, char * szAnsiFilename, int iChars ) +{ + // if( iIndex < 0 || iIndex > iMaxIndex ) return 0; + /* + PlaylistEntry * entry = ( PlaylistEntry * )SendMessage( + WindowPlaylist, + LB_GETITEMDATA, + iIndex, + 0 + ); + if( !entry || !entry->szFilename ) return 0; + + TCHAR * & szFilename = entry->szFilename; + */ + TCHAR * szFilename = ( TCHAR * )playlist->Get( iIndex ); + + const int iFilenameLen = ( int )_tcslen( szFilename ); + const int iCopyLen = ( iFilenameLen < iChars ) ? iFilenameLen : iChars; + +#ifdef PA_UNICODE + ToAnsi( szAnsiFilename, szFilename, iCopyLen ); +#else + memcpy( szAnsiFilename, szFilename, iCopyLen ); +#endif + + szAnsiFilename[ iCopyLen ] = '\0'; + return iCopyLen; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int Playlist::GetTitle( int iIndex, char * szAnsiTitle, int iChars ) +{ + // if( iIndex < 0 || iIndex > iMaxIndex ) return 0; + /* + TCHAR * szFilename = ( TCHAR * )SendMessage( + WindowPlaylist, + LB_GETITEMDATA, + iIndex, + 0 + ); + if( !szFilename ) return 0; + */ + TCHAR * szFilename = ( TCHAR * )playlist->Get( iIndex ); + + // Get extension + const int iFilenameLen = ( int )_tcslen( szFilename ); + TCHAR * szExt = szFilename + iFilenameLen - 1; + while( ( szExt > szFilename ) && ( *szExt != TEXT( '.' ) ) ) szExt--; + szExt++; + + // Get plugin for extension + map ::iterator iter = ext_map.find( szExt ); + if( iter == ext_map.end() ) return 0; + InputPlugin * input_plugin = iter->second; + +#ifdef PA_UNICODE + // Filename + char * szTemp = new char[ iFilenameLen + 1 ]; + ToAnsi( szTemp, szFilename, iFilenameLen ); + szTemp[ iFilenameLen ] = '\0'; + + // Ansi Title + char szTitle[ 2000 ] = "\0"; + int length_in_ms; + input_plugin->plugin->GetFileInfo( szTemp, szTitle, &length_in_ms ); + const int iTitleLen = strlen( szTitle ); + memcpy( szAnsiTitle, szTitle, iChars * sizeof( char ) ); + szTitle[ iChars ] = '\0'; +#else + char szTitle[ 2000 ] = "\0"; + int length_in_ms; + input_plugin->plugin->GetFileInfo( szFilename, szTitle, &length_in_ms ); + const int iTitleLen = ( int )strlen( szAnsiTitle ); + memcpy( szAnsiTitle, szTitle, iChars * sizeof( char ) ); + szTitle[ iChars ] = '\0'; +#endif + + return ( iTitleLen < iChars ) ? iTitleLen : iChars; +} + + +/* +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playlist::SelectZero() +{ + SendMessage( + WindowPlaylist, + LB_SETSEL, + FALSE, + -1 + ); + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Playlist::SelectAll() +{ + SendMessage( + WindowPlaylist, + LB_SETSEL, + TRUE, + -1 + ); + return true; +} +*/ diff --git a/Externals/MusicMod/Player/Src/Playlist.h b/Externals/MusicMod/Player/Src/Playlist.h new file mode 100644 index 0000000000..82aaab7952 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Playlist.h @@ -0,0 +1,65 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_PLAYLIST_H +#define PA_PLAYLIST_H + + + +#include "Global.h" +#include "PlaylistControler.h" +#include "PlaylistView.h" + + +#define PLAINAMP_PL_REM_SEL 50004 +#define PLAINAMP_PL_REM_CROP 50005 + + + +extern HWND WindowPlaylist; + +extern PlaylistControler * playlist; + +namespace Playlist +{ + bool Create(); +/* + int GetCurIndex(); + int GetMaxIndex(); + bool SetCurIndex( int iIndex ); +*/ + TCHAR * GetFilename( int iIndex ); + + int GetFilename( int iIndex, char * szAnsiFilename, int iChars ); + int GetTitle( int iIndex, char * szAnsiTitle, int iChars ); + + bool DialogOpen(); + bool DialogSaveAs(); + + bool AppendPlaylistFile( TCHAR * szFilename ); + bool ExportPlaylistFile( TCHAR * szFilename ); +/* + bool Append( TCHAR * szDisplay, TCHAR * szFilename ); + bool Add( int iIndex, TCHAR * szDisplay, TCHAR * szFilename ); + + bool Clear(); // aka RemoveAll() + bool RemoveSelected(); + bool Crop(); // aka RemoveUnselected + + bool SelectZero(); + bool SelectAll(); +*/ +}; + + + +#endif // PA_PLAYLIST_H diff --git a/Externals/MusicMod/Player/Src/PlaylistControler.cpp b/Externals/MusicMod/Player/Src/PlaylistControler.cpp new file mode 100644 index 0000000000..a3b0f17a68 --- /dev/null +++ b/Externals/MusicMod/Player/Src/PlaylistControler.cpp @@ -0,0 +1,467 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "PlaylistControler.h" +#include "Config.h" +#include "Font.h" +#include "Console.h" + +bool bPlaylistFollow; +ConfBool cbPlaylistFollow( &bPlaylistFollow, TEXT( "PlaylistFollow" ), CONF_MODE_PUBLIC, true ); + + +void PlaylistControler::MoveSelected( int iDistance ) +{ + if( iDistance == -1 ) + { + if( ListView_GetItemState( _hView, 0, LVIS_SELECTED ) ) + { + // Cannot move upwards + return; + } + } + else if( iDistance == 1 ) + { + if( ListView_GetItemState( _hView, _database.GetMaxIndex(), LVIS_SELECTED ) ) + { + // Cannot move downwards + return; + } + } + else + { + // More distance maybe later + return; + } + + const int iFocus = ListView_GetNextItem( _hView, ( UINT )-1, LVIS_FOCUSED ); + + // Negative is to the top + LRESULT iBefore = 0; + LRESULT iAfter = -2; // Extra value to check after big for-loop + + for( ; ; ) + { + // Count + LRESULT iAfter = ListView_GetNextItem( _hView, iBefore - 1, LVNI_SELECTED ); + if( iAfter == -1 ) break; // No more selections selected + + LRESULT iFirst = iAfter; + + // Search end of selection block + iBefore = iAfter + 1; + for( ; ; ) + { + iAfter = ListView_GetNextItem( _hView, iBefore - 1, LVNI_SELECTED ); + if( iAfter == iBefore ) + { + // Keep searching + iBefore++; + } + else + { + // End found (iBefore is the first not selected) + const int iSelSize = iBefore - iFirst; + if( iDistance == -1 ) + { + // Updwards + const int iOldIndex = iFirst - 1; + const int iNewIndex = iOldIndex + iSelSize; + + TCHAR * szData = ( TCHAR * )_database.Get( iOldIndex ); + _database.Erase( iOldIndex ); + _database.Insert( iNewIndex, szData ); + + ListView_SetItemState( _hView, iOldIndex, LVIS_SELECTED, LVIS_SELECTED ); + ListView_SetItemState( _hView, iNewIndex, 0, LVIS_SELECTED ); + ListView_RedrawItems( _hView, iOldIndex, iNewIndex ); + } + else + { + // Downwards + const int iOldIndex = iFirst + iSelSize; + const int iNewIndex = iFirst; + + TCHAR * szData = ( TCHAR * )_database.Get( iOldIndex ); + _database.Erase( iOldIndex ); + _database.Insert( iNewIndex, szData ); + + ListView_SetItemState( _hView, iOldIndex, LVIS_SELECTED, LVIS_SELECTED ); + ListView_SetItemState( _hView, iNewIndex, 0, LVIS_SELECTED ); + ListView_RedrawItems( _hView, iNewIndex, iOldIndex ); + } + + iBefore++; + break; + } + } + } + + if( iAfter != -2 ) return; // Nothing was selected so nothing was moved + ListView_SetItemState( _hView, iFocus + iDistance, LVIS_FOCUSED, LVIS_FOCUSED ); + + Refresh(); +} + +int PlaylistControler::GetCurIndex() +{ + return _database.GetCurIndex(); +} + +int PlaylistControler::GetMaxIndex() +{ + return _database.GetMaxIndex(); +} + +int PlaylistControler::GetSize() +{ + return _database.GetSize(); +} + +void PlaylistControler::SetCurIndex( int iIndex ) +{ + const int iCurIndexBefore = _database.GetCurIndex(); + _database.SetCurIndex( iIndex ); + + // --------------------------------------------------------------------------------------- + // We disable the windows management + /* + if( bPlaylistFollow ) + { + ListView_SetItemState( _hView, ( UINT )-1, 0, LVIS_SELECTED | LVIS_FOCUSED ); + ListView_SetItemState( _hView, iIndex, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED ); + } + + if( iCurIndexBefore != -1 ) + ListView_RedrawItems( _hView, iCurIndexBefore, iCurIndexBefore ); + ListView_RedrawItems( _hView, iIndex, iIndex ); + */ + // --------------------------------------------------------------------------------------- +} + +// Returns on digit count change +bool PlaylistControler::FixDigitsMore() +{ + const int iCountAfter = _database.GetSize(); + const int iDigitsBefore = _iDigits; + while( iCountAfter > _iDigitMax ) + { + _iDigitMin *= 10; // 10 -> 100 + _iDigitMax = _iDigitMax * 10 + 9; // 99 -> 999 + _iDigits++; // 2 -> 3 + } + + + return ( ( _iDigits != iDigitsBefore ) + || ( iCountAfter == 1 ) ); // Force update when first item is inserted +} + +// Returns on digit count change +bool PlaylistControler::FixDigitsLess() +{ + const int iCountAfter = _database.GetSize(); + const int iDigitsBefore = _iDigits; + while( ( iCountAfter < _iDigitMin ) && ( _iDigits > 1 ) ) + { + _iDigitMin /= 10; // 999 -> 99 + _iDigitMax /= 10; // 100 -> 10 + _iDigits--; // 3 -> 2 + } + + return ( _iDigits != iDigitsBefore ); +} + + +void PlaylistControler::Refresh() +{ + AutosizeColumns(); + + const int iFirst = ListView_GetTopIndex( _hView ); + const int iLast = iFirst + ListView_GetCountPerPage( _hView ); + + ListView_RedrawItems( _hView, iFirst, iLast ); +} + +PlaylistControler::PlaylistControler( HWND hView, bool bEnableZeroPadding, int * piIndexSlave ) +{ + _hView = hView; + + _bZeroPadding = bEnableZeroPadding; + _iDigits = 1; + _iDigitMin = 1; + _iDigitMax = 9; + + _database.SetCurIndexSlave( piIndexSlave ); + + printf("PlaylistControler::PlaylistControler was called\n"); + + Refresh(); + + // TODO clear list view here??? +} + + +void PlaylistControler::PushBack( TCHAR * szText ) +{ + const int iSize = _database.GetMaxIndex(); + + _database.PushBack( szText ); + + // --------------------------------------------------------------------------------------- + // Disabled windows function + //ListView_SetItemCount( _hView, _database.GetSize() ); + + //if( FixDigitsMore() ) Refresh(); + // --------------------------------------------------------------------------------------- +} + +void PlaylistControler::Insert( int i, TCHAR * szText ) +{ + const int iSize = _database.GetMaxIndex(); + + _database.Insert( i, szText ); + ListView_SetItemCount( _hView, _database.GetSize() ); + + if( FixDigitsMore() ) Refresh(); +} + +void PlaylistControler::RemoveAll() +{ + _database.Clear(); + + // This is for the windows playlist, we don't use that + //ListView_DeleteAllItems( _hView ); + + if( FixDigitsLess() ) Refresh(); +} + +void PlaylistControler::RemoveSelected( bool bPositive ) +{ + SendMessage( _hView, WM_SETREDRAW, FALSE, 0 ); + + if( bPositive ) + { + LRESULT iWalk = 0; + for( ; ; ) + { + // MSDN: The specified item itself is excluded from the search. + iWalk = ListView_GetNextItem( _hView, iWalk - 1, LVNI_SELECTED ); + if( iWalk != -1 ) + { + _database.Erase( iWalk ); + ListView_DeleteItem( _hView, iWalk ); + } + else + { + break; + } + } + } + else + { + LRESULT iBefore = 0; + LRESULT iAfter = 0; + for( ; ; ) + { + // MSDN: The specified item itself is excluded from the search. + iAfter = ListView_GetNextItem( _hView, iBefore - 1, LVNI_SELECTED ); + if( iAfter != -1 ) + { + // is the first selected + // so we delete all before and restart + // at the beginning + const int iDelIndex = iBefore; + for( int i = iBefore; i < iAfter; i++ ) + { + _database.Erase( iDelIndex ); + ListView_DeleteItem( _hView, iDelIndex ); + } + + iBefore++; // Exclude the one we found selected right before + } + else + { + if( iBefore == 0 ) + { + // All selected + _database.Clear(); + ListView_DeleteAllItems( _hView ); + } + else + { + const int iSize = _database.GetSize(); + const int iDelIndex = iBefore; + for( int i = iBefore; i < iSize; i++ ) + { + _database.Erase( iDelIndex ); + ListView_DeleteItem( _hView, iDelIndex ); + } + } + + break; + } + } + } + + bool bRefresh = FixDigitsLess(); + + SendMessage( _hView, WM_SETREDRAW, TRUE, 0 ); + + if( bRefresh ) Refresh(); +} + +void PlaylistControler::SelectAll( bool bPositive ) +{ + ListView_SetItemState( _hView, ( UINT )-1, bPositive ? LVIS_SELECTED : 0, LVIS_SELECTED ); +} + +void PlaylistControler::SelectInvert() +{ + SendMessage( _hView, WM_SETREDRAW, FALSE, 0 ); + + const int iOneTooMuch = _database.GetSize(); + for( int i = 0; i < iOneTooMuch; i++ ) + { + ListView_SetItemState( + _hView, + i, + ( ListView_GetItemState( _hView, i, LVIS_SELECTED ) == LVIS_SELECTED ) + ? 0 + : LVIS_SELECTED, + LVIS_SELECTED + ); + + } + + SendMessage( _hView, WM_SETREDRAW, TRUE, 0 ); +} + +const TCHAR * PlaylistControler::Get( int i ) +{ + Console::Append( TEXT("We are in PlaylistControler::Get()") ); + + return _database.Get( i ); +} + +void PlaylistControler::Fill( LVITEM & request ) +{ + if( ( request.mask & LVIF_TEXT ) == 0 ) return; + if( request.iSubItem ) + { + // Text + _sntprintf( request.pszText, request.cchTextMax, TEXT( "%s" ), _database.Get( request.iItem ) ); + } + else + { + // Number + if( _bZeroPadding ) + { + TCHAR szFormat[ 6 ]; + _stprintf( szFormat, TEXT( "%%0%dd" ), _iDigits ); + _sntprintf( request.pszText, request.cchTextMax, szFormat, request.iItem + 1 ); + } + else + { + _sntprintf( request.pszText, request.cchTextMax, TEXT( "%d" ), request.iItem + 1 ); + } + } +} + +void PlaylistControler::EnableZeroPadding( bool bActive ) +{ + if( bActive == _bZeroPadding ) return; + + LVCOLUMN col; + memset( &col, 0, sizeof( LVCOLUMN ) ); + col.mask = LVCF_FMT; + + if( bActive ) + { + // TODO recalculation yes/no? + /* + int iSize = _database.GetSize(); + if( iSize != 0 ) + { + int iDigits = 0; + while( iSize > 0 ) + { + iSize /= 10; + iDigits++; + } + + _iDigits = iSize; + _iDigitMin = 1; + _iDigitMax = 9; + while( iSize-- > 0 ) + { + _iDigitMin *= 10; + _iDigitMax = _iDigitMax * 10 + 9; + } + } + else + { + _iDigits = 1; + _iDigitMin = 1; + _iDigitMax = 9; + } + */ + + col.fmt = LVCFMT_LEFT; + ListView_SetColumn( _hView, 0, &col ); + + Refresh(); + } + else + { + col.fmt = LVCFMT_RIGHT; + ListView_SetColumn( _hView, 0, &col ); + + Refresh(); + } + _bZeroPadding = bActive; +} + +void PlaylistControler::AutosizeColumns() +{ + RECT r; + if( !GetClientRect( _hView, &r ) ) return; + + if( _bZeroPadding ) + { + ListView_SetColumnWidth( _hView, 0, LVSCW_AUTOSIZE ); + const int iWidth = ListView_GetColumnWidth( _hView, 0 ); + ListView_SetColumnWidth( _hView, 1, r.right - r.left - iWidth ); + } + else + { + HDC hdc = GetDC( _hView ); + const HFONT hOldFont = ( HFONT )SelectObject( hdc, Font::Get() ); + SIZE size; + BOOL res = GetTextExtentPoint32( hdc, TEXT( "0" ), 1, &size ); + SelectObject( hdc, hOldFont ); + ReleaseDC( _hView, hdc ); + const int iWidth = res ? ( int )( size.cx * ( _iDigits + 0.25f ) ) : 120; + ListView_SetColumnWidth( _hView, 0, iWidth ); + ListView_SetColumnWidth( _hView, 1, r.right - r.left - iWidth ); + } +} + +void PlaylistControler::Resize( HWND hParent ) +{ + /* + RECT rc; + GetClientRect( hParent, &rc ); + MoveWindow( _hView, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE ); + */ + + AutosizeColumns(); +} diff --git a/Externals/MusicMod/Player/Src/PlaylistControler.h b/Externals/MusicMod/Player/Src/PlaylistControler.h new file mode 100644 index 0000000000..cc85f5e317 --- /dev/null +++ b/Externals/MusicMod/Player/Src/PlaylistControler.h @@ -0,0 +1,65 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PLAYLIST_CONTROLER_H +#define PLAYLIST_CONTROLER_H 1 + +#include +#include +#include +#include "PlaylistModel.h" + + + +// TODO: column width update on scrollbar show/hide + +class PlaylistControler +{ + PlaylistModel _database; + HWND _hView; + + bool _bZeroPadding; + int _iDigits; // 3 + int _iDigitMin; // 100 + int _iDigitMax; // 999 + +private: + bool FixDigitsMore(); + bool FixDigitsLess(); + void Refresh(); + void AutosizeColumns(); + +public: + PlaylistControler( HWND hView, bool bEnableZeroPadding, int * piIndexSlave ); + + void MoveSelected( int iDistance ); + + int GetCurIndex(); + int GetMaxIndex(); + int GetSize(); + void SetCurIndex( int iIndex ); + + void PushBack( TCHAR * szText ); + void Insert( int i, TCHAR * szText ); + void RemoveAll(); + void RemoveSelected( bool bPositive ); + void SelectAll( bool bPositive ); + void SelectInvert(); + + const TCHAR * Get( int i ); + + void Fill( LVITEM & request ); + void EnableZeroPadding( bool bActive ); + void Resize( HWND hParent ); +}; + +#endif // PLAYLIST_CONTROLER_H diff --git a/Externals/MusicMod/Player/Src/PlaylistModel.h b/Externals/MusicMod/Player/Src/PlaylistModel.h new file mode 100644 index 0000000000..1a914c8f3a --- /dev/null +++ b/Externals/MusicMod/Player/Src/PlaylistModel.h @@ -0,0 +1,122 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + +// ======================================================================================= +#include "Console.h" +#include "Global.h" +// ======================================================================================= + + +#ifndef PLAYLIST_MODEL_H +#define PLAYLIST_MODEL_H 1 + +#include +#include +#include +using namespace std; + + +class PlaylistModel +{ + vector _database; + int _iCurIndex; + int * _piCurIndex; + +public: + PlaylistModel() + { + _piCurIndex = &_iCurIndex; + } + + PlaylistModel( int * piIndexSlave ) + { + _piCurIndex = piIndexSlave; + } + + void SetCurIndexSlave( int * piIndexSlave ) + { + // *piIndexSlave = *_piCurIndex; + _piCurIndex = piIndexSlave; + } + + // ======================================================================================= + // This is where the _database is populated + // ======================================================================================= + void PushBack( TCHAR * szText ) + { + Console::Append( TEXT( "PlaylistModel.h:_database.PushBack() was called " ) ); + + _database.push_back( szText ); // this is a API call + } + + void Insert( int i, TCHAR * szText ) + { + Console::Append( TEXT( "PlaylistModel.h:_database.Insert() was called " ) ); + + if( i <= *_piCurIndex ) ( *_piCurIndex )++; + _database.insert( _database.begin() + i, szText ); + } + + void Erase( int i ) + { + if( i < *_piCurIndex ) ( *_piCurIndex )--; + _database.erase( _database.begin() + i ); + } + + const TCHAR * Get( int i ) + { + //Console::Append( TEXT( "PlaylistModel.h:_database.Get() was called " ) ); + + if( 0 > i || i >= ( int )_database.size() ) + { + static const TCHAR * szError = TEXT( "INDEX OUT OF RANGE" ); + return szError; + } + + return _database[ i ]; + } + + void Clear() + { + _database.clear(); + *_piCurIndex = -1; + } + + int GetMaxIndex() + { + return _database.size() - 1; + } + + int GetCurIndex() + { + return *_piCurIndex; + } + + void SetCurIndex( int iIndex ) + { + + if( 0 > iIndex || iIndex >= ( int )_database.size() ) + { + INFO_LOG(AUDIO,"SetCurIndex > Return"); + return; + } + + *_piCurIndex = iIndex; + } + + int GetSize() + { + return _database.size(); + } +}; + + +#endif // PLAYLIST_MODEL_H diff --git a/Externals/MusicMod/Player/Src/PlaylistView.cpp b/Externals/MusicMod/Player/Src/PlaylistView.cpp new file mode 100644 index 0000000000..f2ba322d8d --- /dev/null +++ b/Externals/MusicMod/Player/Src/PlaylistView.cpp @@ -0,0 +1,782 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + +// ======================================================================================= +#include "Playlist.h" +#include "Main.h" +#include "Status.h" +#include "Rebar.h" +#include "Playback.h" +#include "Config.h" +#include "Util.h" + +WNDPROC WndprocPlaylistBackup = NULL; +LRESULT CALLBACK WndprocPlaylist( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); +// ======================================================================================= + + +// ======================================================================================= +// This gives the values from the ini file. +// ConfBool = class +PlaylistControler * playlist = NULL; // extern + +bool bPlaylistEntryNumberZeroPadding; +ConfBool cbPlaylistEntryNumberZeroPadding( &bPlaylistEntryNumberZeroPadding, TEXT( "PlaylistEntryNumberZeroPadding" ), CONF_MODE_PUBLIC, true ); + +int iCurPlaylistPosition; +ConfInt ciCurPlaylistPosition( &iCurPlaylistPosition, TEXT( "CurPlaylistPosition" ), CONF_MODE_INTERNAL, -1 ); + +bool bInfinitePlaylist; +ConfBool cbInfinitePlaylist( &bInfinitePlaylist, TEXT( "InfinitePlaylist" ), CONF_MODE_PUBLIC, false ); +// ======================================================================================= + + +// ======================================================================================= + +void PlaylistView::Create() +{ + #ifndef NOGUI + + RECT ClientMain; + GetClientRect( WindowMain, &ClientMain ); + + const int iClientHeight = ClientMain.bottom - ClientMain.top; + const int iClientWidth = ClientMain.right - ClientMain.left; + const int iPlaylistHeight = iClientHeight - iRebarHeight - iStatusHeight; + + + //LoadCommonControls(); + + + DWORD dwStyle; + // HWND WindowPlaylist; + BOOL bSuccess = TRUE; + + dwStyle = WS_TABSTOP | + WS_CHILD | + WS_BORDER | + WS_VISIBLE | + LVS_AUTOARRANGE | // TODO + LVS_REPORT | + LVS_OWNERDATA | + LVS_NOCOLUMNHEADER ; + + WindowPlaylist = CreateWindowEx( WS_EX_CLIENTEDGE, // ex style + WC_LISTVIEW, // class name - defined in commctrl.h + TEXT( "" ), // dummy text + dwStyle, // style + 0, + iRebarHeight, // + -2, + iClientWidth, + iPlaylistHeight, + WindowMain, // parent + NULL, // ID + g_hInstance, // instance + NULL); // no extra data + + if(!WindowPlaylist) return; // TODO + + // This calls PlaylistControler::PlaylistControler() + playlist = new PlaylistControler( WindowPlaylist, bPlaylistEntryNumberZeroPadding, &iCurPlaylistPosition ); + + #else + + HWND WindowPlaylist = NULL; + playlist = new PlaylistControler( WindowPlaylist, bPlaylistEntryNumberZeroPadding, &iCurPlaylistPosition ); + + #endif + + // Exchange window procedure + //WndprocPlaylistBackup = ( WNDPROC )GetWindowLong( WindowPlaylist, GWL_WNDPROC ); + //if( WndprocPlaylistBackup != NULL ) + //{ + // SetWindowLong( WindowPlaylist, GWL_WNDPROC, ( LONG )WndprocPlaylist ); + //} + + + //ListView_SetExtendedListViewStyle( WindowPlaylist, LVS_EX_FULLROWSELECT ); // | LVS_EX_GRIDLINES ); + //playlist->Resize( WindowMain ); + + /* + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/listview/structures/lvcolumn.asp + * + * Remarks: + * If a column is added to a list-view control with index 0 (the leftmost column) + * and with LVCFMT_RIGHT or LVCFMT_CENTER specified, the text is not right-aligned + * or centered. The text in the index 0 column is left-aligned. Therefore if you + * keep inserting columns with index 0, the text in all columns are left-aligned. + * If you want the first column to be right-aligned or centered you can make a dummy + * column, then insert one or more columns with index 1 or higher and specify the + * alignment you require. Finally delete the dummy column. + */ + + //LV_COLUMN lvColumn; + //memset( &lvColumn, 0, sizeof( LV_COLUMN ) ); + //lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT; + // + //// Number column (with dummy hack) + //lvColumn.fmt = LVCFMT_LEFT; + //lvColumn.cx = 0; + //lvColumn.pszText = TEXT( "" ); + //ListView_InsertColumn( WindowPlaylist, 0, &lvColumn ); + //lvColumn.fmt = LVCFMT_RIGHT; + //lvColumn.cx = 120; + //lvColumn.pszText = TEXT( "" ); + //ListView_InsertColumn( WindowPlaylist, 1, &lvColumn ); + //ListView_DeleteColumn( WindowPlaylist, 0 ); + + // Entry + //lvColumn.fmt = LVCFMT_LEFT; + //lvColumn.cx = 120; + //lvColumn.pszText = TEXT( "Filename" ); + //ListView_InsertColumn(WindowPlaylist, 1, &lvColumn); + + + + /* + stupid test code + + SCROLLINFO scrollinfo; + ZeroMemory( &scrollinfo, sizeof( SCROLLINFO ) ); + scrollinfo.cbSize = sizeof( SCROLLINFO ); + scrollinfo.fMask = 0; // SIF_DISABLENOSCROLL; + + if( !GetScrollInfo( WindowPlaylist, SB_VERT, &scrollinfo ) ) + { + MessageBox( 0, "ERROR", "", 0 ); + } + else + { + MessageBox( 0, "OKAY", "", 0 ); + scrollinfo.fMask = SIF_DISABLENOSCROLL; + SetScrollInfo( WindowPlaylist, SB_VERT, &scrollinfo, TRUE ); + } + + if( !ShowScrollBar( WindowPlaylist, SB_VERT, TRUE ) ) + { + MessageBox( 0, "ERROR ShowScrollBar", "", 0 ); + } + + + SCROLLBARINFO scrollbarinfo; + scrollbarinfo.cbSize = sizeof( SCROLLBARINFO ); + if( !GetScrollBarInfo( WindowPlaylist, OBJID_VSCROLL, &scrollbarinfo ) ) + { + MessageBox( 0, "ERROR GetScrollBarInfo", "", 0 ); + } + */ + +} + + + + + +// Dragging +static int iItemHeight = 15; +static int iDragStartY = 0; +static bool bDragging = false; + +// Liquid selection +static bool bLiquidSelecting = false; +static int iLastTouched = -1; + +// Liquid or range selection +static int iSelAnchor = -1; + +LRESULT CALLBACK WndprocPlaylist( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + /* + * Click, click and click + * + * [Alt] [Ctrl] [Shift] Action + * -------+---------+---------+----------------------- + * X | X | X | + * X | X | | + * X | | X | + * X | | | + * | X | X | Range selection + * | X | | Toggle selection + * | | X | Range selection + * | | | Single selection + * + * + * Click, hold and move + * + * [Alt] [Ctrl] [Shift] Action + * -------+---------+---------+----------------------- + * X | X | X | Selection move + * X | X | | Selection move + * X | | X | Selection move + * X | | | Selection move + * | X | X | + * | X | | + * | | X | + * | | | Liquid selection + */ + + static bool bCapturing = true; + + switch( message ) + { +/* + case WM_CAPTURECHANGED: + if( bCapturing && ( GetCapture() != WindowPlaylist ) ) + { + MessageBox( 0, TEXT( "Capture stolen" ), TEXT( "" ), 0 ); + } + break; +*/ + case WM_MOUSEMOVE: + if( bLiquidSelecting ) + { + LVHITTESTINFO hittest; + memset( &hittest, 0, sizeof( LVHITTESTINFO ) ); + hittest.pt.x = LOWORD( lp ); + hittest.pt.y = HIWORD( lp ); + const int iIndex = ( int )ListView_HitTest( WindowPlaylist, &hittest ); + if( iIndex == -1 ) return 0; + if( iIndex == iLastTouched ) return 0; + + // Note: Update this as early as possible! + // We cannot be sure this code is + // not called two or three times at the + // same time without losing much speed + // but this at least lowers the chance + const int iLastTouchedBackup = iLastTouched; + iLastTouched = iIndex; + + const bool bControl = ( ( GetKeyState( VK_CONTROL ) & 0x8000 ) != 0 ); + + + + ListView_SetItemState( WindowPlaylist, ( UINT )-1, 0, LVIS_FOCUSED ); + ListView_SetItemState( WindowPlaylist, iIndex, LVIS_FOCUSED, LVIS_FOCUSED ); + + // Below anchor? + if( iIndex > iSelAnchor ) + { + if( iIndex > iLastTouchedBackup ) + { + // iSelAnchor + // .. + // iLastTouchedBackup + // .. + // >> iIndex << + if( iLastTouchedBackup > iSelAnchor ) + { + // Select downwards + for( int i = iLastTouchedBackup + 1; i <= iIndex; i++ ) + ListView_SetItemState( WindowPlaylist, i, LVIS_SELECTED, LVIS_SELECTED ); + } + + // iLastTouchedBackup + // .. + // iSelAnchor + // .. + // >> iIndex << + else + { + // Unselect downwards + for( int i = iLastTouchedBackup; i < iSelAnchor; i++ ) + ListView_SetItemState( WindowPlaylist, i, 0, LVIS_SELECTED ); + + // Select downwards + for( int i = iSelAnchor + 1; i <= iIndex; i++ ) + ListView_SetItemState( WindowPlaylist, i, LVIS_SELECTED, LVIS_SELECTED ); + } + } + else // iIndex < iLastTouchedBackup + { + // iSelAnchor + // .. + // >> iIndex << + // .. + // iLastTouchedBackup + + // Unselect upwards + for( int i = iLastTouchedBackup; i > iIndex; i-- ) + ListView_SetItemState( WindowPlaylist, i, 0, LVIS_SELECTED ); + } + } + + // Above anchor? + else if( iIndex < iSelAnchor ) + { + if( iIndex < iLastTouchedBackup ) + { + // >> iIndex << + // .. + // iSelAnchor + // .. + // iLastTouchedBackup + if( iIndex < iLastTouchedBackup ) + { + // Unselect upwards + for( int i = iLastTouchedBackup; i > iSelAnchor; i-- ) + ListView_SetItemState( WindowPlaylist, i, 0, LVIS_SELECTED ); + + // Select upwards + for( int i = iSelAnchor - 1; i >= iIndex; i-- ) + ListView_SetItemState( WindowPlaylist, i, LVIS_SELECTED, LVIS_SELECTED ); + } + + // >> iIndex << + // .. + // iLastTouchedBackup + // .. + // iSelAnchor + else // iIndex < iLastTouchedBackup + { + // Select upwards + for( int i = iLastTouchedBackup - 1; i >= iIndex; i-- ) + ListView_SetItemState( WindowPlaylist, i, LVIS_SELECTED, LVIS_SELECTED ); + } + } + else // iIndex > iLastTouchedBackup + { + // iLastTouchedBackup + // .. + // >> iIndex << + // .. + // iSelAnchor + + // Unselect downwards + for( int i = iLastTouchedBackup; i < iIndex; i++ ) + ListView_SetItemState( WindowPlaylist, i, 0, LVIS_SELECTED ); + } + } + + // At anchor + else // iIndex == iSelAnchor + { + if( iIndex < iLastTouchedBackup ) + { + // iSelAnchor / >> iIndex << + // .. + // iLastTouchedBackup + + // Unselect upwards + for( int i = iLastTouchedBackup; i > iSelAnchor; i-- ) + { + ListView_SetItemState( WindowPlaylist, i, 0, LVIS_SELECTED ); + } + } + else // iIndex > iLastTouchedBackup + { + // iLastTouchedBackup + // .. + // iSelAnchor / >> iIndex << + + // Unselect downwards + for( int i = iLastTouchedBackup; i < iSelAnchor; i++ ) + ListView_SetItemState( WindowPlaylist, i, 0, LVIS_SELECTED ); + } + } + } + else if( bDragging ) + { + static bool bMoveLock = false; + + if( bMoveLock ) return 0; + bMoveLock = true; + + const int y = HIWORD( lp ); + const int diff = y - iDragStartY; + if( abs( diff ) > iItemHeight / 2 ) + { + iDragStartY += ( ( diff > 0 ) ? iItemHeight : -iItemHeight ); + playlist->MoveSelected( ( diff > 0 ) ? +1 : -1 ); + } + + bMoveLock = false; + } + return 0; + + case WM_LBUTTONDOWN: + { + static int iLastClicked = -1; + static bool bLastClickNoneOrShift = true; + + + SetFocus( hwnd ); // TODO monitor focus loss + + LVHITTESTINFO hittest; + memset( &hittest, 0, sizeof( LVHITTESTINFO ) ); + GetCursorPos( &hittest.pt ); + ScreenToClient( hwnd, &hittest.pt ); + const int iIndex = ( int )ListView_HitTest( WindowPlaylist, &hittest ); + if( iIndex == -1 ) return 0; + + const bool bShift = ( ( GetKeyState( VK_SHIFT ) & 0x8000 ) != 0 ); + const bool bControl = ( ( GetKeyState( VK_CONTROL ) & 0x8000 ) != 0 ); + const bool bAlt = ( ( GetKeyState( VK_MENU ) & 0x8000 ) != 0 ); + + // [Shift] or [Shift]+[Ctrl]? + if( bShift ) + { + if( bAlt ) return 0; + + // Last click usable as selection anchor? + if( !bLastClickNoneOrShift ) + { + // Treat as normal click + iSelAnchor = iIndex; + iLastClicked = iIndex; + + ListView_SetItemState( WindowPlaylist, ( UINT )-1, 0, LVIS_SELECTED | LVIS_FOCUSED ); + ListView_SetItemState( WindowPlaylist, iIndex, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED ); + + bLiquidSelecting = true; + bLastClickNoneOrShift = true; + + return 0; + } + + if( iIndex != iLastClicked ) + { + // Below anchor? + if( iIndex > iSelAnchor ) + { + // Below last click? + if( iIndex > iLastClicked ) + { + // Other side of anchor? + if( iLastClicked < iSelAnchor ) + { + // Unselect downwards + for( int i = iLastClicked; i < iSelAnchor; i++ ) + ListView_SetItemState( WindowPlaylist, i, 0, LVIS_SELECTED ); + + // Select downwards + for( int i = iSelAnchor; i <= iIndex; i++ ) + ListView_SetItemState( WindowPlaylist, i, LVIS_SELECTED, LVIS_SELECTED ); + } + + // Same side of anchor? + else + { + // Select downwards + for( int i = iLastClicked + 1; i <= iIndex; i++ ) + ListView_SetItemState( WindowPlaylist, i, LVIS_SELECTED, LVIS_SELECTED ); + } + } + + // Above last click? + else // iIndex < iLastClicked + { + // Unselect upwards + for( int i = iLastClicked; i > iIndex; i-- ) + ListView_SetItemState( WindowPlaylist, i, 0, LVIS_SELECTED ); + } + } + + // Above anchor? + else if( iIndex < iSelAnchor ) + { + // Above last clicked? + if( iIndex < iLastClicked ) + { + // Other side of anchor? + if( iLastClicked > iSelAnchor ) + { + // Unselect upwards + for( int i = iLastClicked; i > iSelAnchor; i-- ) + ListView_SetItemState( WindowPlaylist, i, 0, LVIS_SELECTED ); + + // Select upwards + for( int i = iSelAnchor; i >= iIndex; i-- ) + ListView_SetItemState( WindowPlaylist, i, LVIS_SELECTED, LVIS_SELECTED ); + } + + // Same side of anchor? + else + { + // Select upwards + for( int i = iLastClicked - 1; i >= iIndex; i-- ) + ListView_SetItemState( WindowPlaylist, i, LVIS_SELECTED, LVIS_SELECTED ); + } + } + + // Below last clicked? + else // iIndex > iLastClicked + { + // Unselect downwards + for( int i = iLastClicked; i < iIndex; i++ ) + ListView_SetItemState( WindowPlaylist, i, 0, LVIS_SELECTED ); + } + } + + // At anchor + else // iIndex == iSelAnchor + { + if( iLastClicked < iSelAnchor ) + { + // Unselect downwards + for( int i = iLastClicked; i < iIndex; i++ ) + ListView_SetItemState( WindowPlaylist, i, 0, LVIS_SELECTED ); + } + else if( iLastClicked > iSelAnchor ) + { + // Unselect upwards + for( int i = iLastClicked; i > iIndex; i-- ) + ListView_SetItemState( WindowPlaylist, i, 0, LVIS_SELECTED ); + } + } + + iLastClicked = iIndex; + + ListView_SetItemState( WindowPlaylist, ( UINT )-1, 0, LVIS_FOCUSED ); + ListView_SetItemState( WindowPlaylist, iIndex, LVIS_FOCUSED, LVIS_FOCUSED ); + } + + bLastClickNoneOrShift = true; + } + + // [Ctrl]? + else if( bControl ) + { + if( bAlt ) return 0; + + iLastTouched = iIndex; + + // Toggle selection + const bool bSelected = ( ListView_GetItemState( WindowPlaylist, iIndex, LVIS_SELECTED ) == LVIS_SELECTED ); + ListView_SetItemState( WindowPlaylist, iIndex, bSelected ? 0 : LVIS_SELECTED, LVIS_SELECTED ); + + ListView_SetItemState( WindowPlaylist, ( UINT )-1, 0, LVIS_FOCUSED ); + ListView_SetItemState( WindowPlaylist, iIndex, LVIS_FOCUSED, LVIS_FOCUSED ); + + bLastClickNoneOrShift = false; + } + + // [Alt]? + else if( bAlt ) + { + // Update item height + RECT rc; + GetClientRect( WindowPlaylist, &rc ); + const int iItemsPerPage = ListView_GetCountPerPage( WindowPlaylist ); + iItemHeight = ( rc.bottom - rc.top ) / iItemsPerPage; + + iDragStartY = hittest.pt.y; + bDragging = true; + + bCapturing = true; + SetCapture( WindowPlaylist ); + } + + // No modifiers? + else + { + iSelAnchor = iIndex; + iLastClicked = iIndex; + + ListView_SetItemState( WindowPlaylist, ( UINT )-1, 0, LVIS_SELECTED | LVIS_FOCUSED ); + ListView_SetItemState( WindowPlaylist, iIndex, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED ); + + bLiquidSelecting = true; + bLastClickNoneOrShift = true; + + bCapturing = true; + SetCapture( WindowPlaylist ); + } + } + return 0; + + case WM_LBUTTONUP: + bLiquidSelecting = false; + bDragging = false; + + bCapturing = false; + ReleaseCapture(); + + return 0; + + case WM_SYSKEYDOWN: + switch( wp ) // [Alt]+[...] + { + case VK_UP: + playlist->MoveSelected( -1 ); + break; + + case VK_DOWN: + playlist->MoveSelected( +1 ); + break; + + } + break; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + case WM_CHAR: + case WM_KEYUP: + // SMALL LETTERS!!!!!! + switch( wp ) + { + case 'z': + case 'y': + case 'x': + case 'c': + case 'v': + case 'b': + case 'l': + return 0; + } + break; + + case WM_KEYDOWN: + { + const bool bShift = ( ( GetKeyState( VK_SHIFT ) & 0x8000 ) != 0 ); + const bool bControl = ( ( GetKeyState( VK_CONTROL ) & 0x8000 ) != 0 ); + + // Note: [Alt] goes to WM_SYSKEYDOWN instead + // const bool bAlt = ( ( GetKeyState( VK_MENU ) & 0x8000 ) != 0 ); + + + switch( wp ) + { + case VK_LEFT: + if( bShift || bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, WINAMP_REW5S, 0 ); + return 0; + + case VK_RIGHT: + if( bShift || bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, WINAMP_FFWD5S, 0 ); + return 0; + + case VK_UP: + if( bInfinitePlaylist ) + { + // First item has focus? + if( ListView_GetNextItem( WindowPlaylist, ( UINT )-1, LVNI_FOCUSED ) != 0 ) break; + + if( bControl && !bShift ) + { + // Move caret only + ListView_SetItemState( WindowPlaylist, ( UINT )-1, 0, LVNI_FOCUSED ); + ListView_SetItemState( WindowPlaylist, playlist->GetMaxIndex(), LVNI_FOCUSED, LVNI_FOCUSED ); + return 0; + } + else + { + // Move Caret and selection + ListView_SetItemState( WindowPlaylist, ( UINT )-1, 0, LVNI_FOCUSED ); + ListView_SetItemState( WindowPlaylist, playlist->GetMaxIndex() - 1, LVNI_FOCUSED, LVNI_FOCUSED ); + wp = VK_DOWN; + } + } + break; + + case VK_DOWN: + if( bInfinitePlaylist ) + { + // Last item has focus? + if( ListView_GetNextItem( WindowPlaylist, playlist->GetMaxIndex() - 1, LVNI_FOCUSED ) == -1 ) break; + + if( bControl && !bShift ) + { + // Move caret only + ListView_SetItemState( WindowPlaylist, ( UINT )-1, 0, LVNI_FOCUSED ); + ListView_SetItemState( WindowPlaylist, 0, LVNI_FOCUSED, LVNI_FOCUSED ); + return 0; + } + else + { + // Workaround + ListView_SetItemState( WindowPlaylist, ( UINT )-1, 0, LVNI_FOCUSED ); + ListView_SetItemState( WindowPlaylist, 1, LVNI_FOCUSED, LVNI_FOCUSED ); + wp = VK_UP; + } + } + break; + + case VK_DELETE: + { + if( bShift ) break; + + if( bControl ) + playlist->RemoveSelected( false ); + else + playlist->RemoveSelected( true ); + + break; + } + + case VK_RETURN: + playlist->SetCurIndex( ListView_GetNextItem( WindowPlaylist, ( UINT )-1, LVIS_FOCUSED ) ); + SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON2, 0 ); + return 0; + + case 'Y': + case 'Z': + if( bShift || bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON1, 0 ); + return 0; + + case 'X': + if( bShift || bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON2, 0 ); + return 0; + + case 'C': + if( bShift || bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON3, 0 ); + return 0; + + case 'V': + // Todo modifiers pressed? -> fadeout/... + if( bShift || bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON4, 0 ); + return 0; + + case 'B': + if( bShift || bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, WINAMP_BUTTON5, 0 ); + return 0; + /* + case 'J': + if( bShift || bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, WINAMP_JUMPFILE, 0 ); + return 0; + */ + case 'L': + if( bControl ) return 0; + SendMessage( WindowMain, WM_COMMAND, bShift ? WINAMP_FILE_DIR : WINAMP_FILE_PLAY, 0 ); + return 0; + } + break; + } + + case WM_LBUTTONDBLCLK: + // iCurIndex = Playlist_MouseToIndex(); + + LVHITTESTINFO hittest; + memset( &hittest, 0, sizeof( LVHITTESTINFO ) ); + GetCursorPos( &hittest.pt ); + ScreenToClient( hwnd, &hittest.pt ); + const int iIndex = ( int )ListView_HitTest( WindowPlaylist, &hittest ); + if( iIndex == -1 ) break; + + playlist->SetCurIndex( iIndex ); + + Playback::Play(); + Playback::UpdateSeek(); + + break; + + } + + return CallWindowProc( WndprocPlaylistBackup, hwnd, message, wp, lp ); +} diff --git a/Externals/MusicMod/Player/Src/PlaylistView.h b/Externals/MusicMod/Player/Src/PlaylistView.h new file mode 100644 index 0000000000..5783ef9075 --- /dev/null +++ b/Externals/MusicMod/Player/Src/PlaylistView.h @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PLAYLIST_VIEW_H +#define PLAYLIST_VIEW_H 1 + + +#include "Global.h" + +namespace PlaylistView +{ + void Create(); + + +}; + + +#endif // PLAYLIST_VIEW_H diff --git a/Externals/MusicMod/Player/Src/Plugin.cpp b/Externals/MusicMod/Player/Src/Plugin.cpp new file mode 100644 index 0000000000..fcad94bb74 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Plugin.cpp @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Plugin.h" +#include "InputPlugin.h" +#include "OutputPlugin.h" +#include "VisPlugin.h" +#include "DspPlugin.h" +#include "GenPlugin.h" + + + +vector plugins; // extern + +int Plugin::iWndprocHookCounter = 0; + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +Plugin::Plugin( TCHAR * szDllpath ) +{ + hDLL = NULL; + szName = NULL; + iNameLen = 0; + + + iFullpathLen = ( int )_tcslen( szDllpath ); + szFullpath = new TCHAR[ iFullpathLen + 1 ]; + memcpy( szFullpath, szDllpath, iFullpathLen * sizeof( TCHAR ) ); + szFullpath[ iFullpathLen ] = TEXT( '\0' ); + + TCHAR * walk = szFullpath + iFullpathLen - 1; + while( ( *walk != TEXT( '\\') ) && ( walk >= szFullpath ) ) walk--; + if( *walk == TEXT( '\\') ) walk++; + + szFilename = walk; + iFilenameLen = iFullpathLen - ( walk - szFullpath ); + _tcslwr( szFilename ); + + plugins.push_back( this ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +Plugin::~Plugin() +{ + if( szFullpath ) delete [] szFullpath; + if( szName ) delete [] szName; +} diff --git a/Externals/MusicMod/Player/Src/Plugin.h b/Externals/MusicMod/Player/Src/Plugin.h new file mode 100644 index 0000000000..eab9cff84c --- /dev/null +++ b/Externals/MusicMod/Player/Src/Plugin.h @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_PLUGIN_H +#define PA_PLUGIN_H + + + +#include "Global.h" +#include + +using namespace std; + + +class Plugin; +extern vector plugins; + + + +enum PluginType +{ + PLUGIN_TYPE_INPUT, + PLUGIN_TYPE_OUTPUT, + PLUGIN_TYPE_VIS, + PLUGIN_TYPE_DSP, + PLUGIN_TYPE_GEN +}; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Winamp plugin wrapper +//////////////////////////////////////////////////////////////////////////////// +class Plugin +{ +public: + Plugin( TCHAR * szDllpath ); + ~Plugin(); + + virtual bool Load() = 0; + virtual bool Unload() = 0; + + virtual TCHAR * GetTypeString() = 0; + virtual int GetTypeStringLen() = 0; + virtual PluginType GetType() = 0; + +// void AllowUnload( bool bAllow ) { bAllowUnload = bAllow; } + inline bool IsLoaded() { return ( hDLL != NULL ); } + virtual bool IsActive() = 0; + + inline TCHAR * GetFullpath() { return szFullpath; } + // inline int GetFullpathLen() { return iFilenameLen; } + + inline TCHAR * GetFilename() { return szFilename; } + inline int GetFilenameLen() { return iFilenameLen; } + inline TCHAR * GetName() { return szName; } + inline int GetNameLen() { return iNameLen; } + + template< class PluginKind > + static bool FindAll( TCHAR * szPath, TCHAR * szPattern, bool bKeepLoaded ); + +protected: + HINSTANCE hDLL; ///< Library handle + TCHAR * szName; ///< Name + int iNameLen; ///< Length of name (in characters) + + BOOL iHookerIndex; ///< Window hook index (0..HC-1). Only last can be unloaded + WNDPROC WndprocBackup; ///< Window procedure backup. Is restored when unloading. Only valid for + static int iWndprocHookCounter; ///< Number of window hooks (=HC) + +private: + TCHAR * szFullpath; ///< Full path e.g. "C:\test.dll" + TCHAR * szFilename; ///< Filename e.g. "test.dll" + int iFullpathLen; ///< Length of full path (in characters) + int iFilenameLen; ///< Length of filename (in characters) +}; + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +template< class PluginKind > +bool Plugin::FindAll( TCHAR * szPath, TCHAR * szPattern, bool bKeepLoaded ) +{ + const int uPathLen = ( int )_tcslen( szPath ); + const int uPatternLen = ( int )_tcslen( szPattern ); + + TCHAR * szFullPattern = new TCHAR[ uPathLen + 1 + uPatternLen + 1 ]; + memcpy( szFullPattern, szPath, uPathLen * sizeof( TCHAR ) ); + szFullPattern[ uPathLen ] = TEXT( '\\' ); + memcpy( szFullPattern + uPathLen + 1, szPattern, uPatternLen * sizeof( TCHAR ) ); + szFullPattern[ uPathLen + 1 + uPatternLen ] = TEXT( '\0' ); + + + WIN32_FIND_DATA fd; + HANDLE hFind = FindFirstFile( szFullPattern, &fd ); + if( hFind == INVALID_HANDLE_VALUE ) + { + delete [] szFullPattern; + return false; + } + + do + { + const int iFilenameLen = ( int )_tcslen( fd.cFileName ); + + TCHAR * szFullpath = new TCHAR[ uPathLen + 1 + iFilenameLen + 1 ]; + memcpy( szFullpath, szPath, uPathLen * sizeof( TCHAR ) ); + szFullpath[ uPathLen ] = TEXT( '\\' ); + memcpy( szFullpath + uPathLen + 1, fd.cFileName, iFilenameLen * sizeof( TCHAR ) ); + szFullpath[ uPathLen + 1 + iFilenameLen ] = TEXT( '\0' ); + + +//////////////////////////////////////////////////////////////////////////////// + new PluginKind( szFullpath, bKeepLoaded ); +//////////////////////////////////////////////////////////////////////////////// + + + delete [] szFullpath; + } + while( FindNextFile( hFind, &fd ) ); + + FindClose( hFind ); + + + delete [] szFullPattern; + return true; +} + + + +#endif // PA_PLUGIN_H diff --git a/Externals/MusicMod/Player/Src/PluginManager.cpp b/Externals/MusicMod/Player/Src/PluginManager.cpp new file mode 100644 index 0000000000..ab5ac603fe --- /dev/null +++ b/Externals/MusicMod/Player/Src/PluginManager.cpp @@ -0,0 +1,1143 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "PluginManager.h" +#include "Plugin.h" +#include "InputPlugin.h" +#include "OutputPlugin.h" +#include "VisPlugin.h" +#include "DspPlugin.h" +#include "GenPlugin.h" +#include "Console.h" +#include "Main.h" +#include "Util.h" +#include "Config.h" + + + +#define MENU_INPUT_CONFIG 1 +#define MENU_INPUT_ABOUT 2 + +#define MENU_OUTPUT_CONFIG 1 +#define MENU_OUTPUT_ABOUT 2 +#define MENU_OUTPUT_SEP 3 +#define MENU_OUTPUT_ACTIVE 4 +#define MENU_OUTPUT_LOAD 5 + +#define MENU_GEN_CONFIG 1 +#define MENU_GEN_SEP 2 +#define MENU_GEN_LOAD 3 + + + +HWND WindowManager = NULL; // extern +HWND WindowListView = NULL; + + +WNDPROC WndprocListViewBackup = NULL; +LRESULT CALLBACK WndprocListView( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); + +LRESULT CALLBACK WndprocManager( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); + + +HMENU input_menu = NULL; +HMENU output_menu = NULL; +HMENU gen_menu = NULL; + + + +bool bManagerGrid; +ConfBool cbManagerGrid( &bManagerGrid, "ManagerGrid", CONF_MODE_PUBLIC, true ); + +bool bManagerVisible; +WINDOWPLACEMENT WinPlaceManager; + +void WinPlaceManagerCallback( ConfVar * var ) +{ + if( !IsWindow( WindowManager ) ) return; + + GetWindowPlacement( WindowManager, &WinPlaceManager ); + + // MSDN: If the window identified by the hWnd parameter + // is maximized, the showCmd member is SW_SHOWMAXIMIZED. + // If the window is minimized, showCmd is SW_SHOWMINIMIZED. + // Otherwise, it is SW_SHOWNORMAL. + if( !bManagerVisible ) + { + WinPlaceManager.showCmd = SW_HIDE; + } +} + +RECT rManagerDefault = { 500, 400, 1000, 700 }; + +ConfWinPlaceCallback cwpcWinPlaceManager( + &WinPlaceManager, + TEXT( "WinPlaceManager" ), + &rManagerDefault, + WinPlaceManagerCallback +); + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +#define CLASSNAME_PAPMC TEXT( "PLAINAMP_PMC" ) +#define PAPMC_TITLE TEXT( "Plugin Manager" ) + +bool PluginManager::Build() +{ + LoadCommonControls(); + + // Register class + WNDCLASS wc = { + 0, // UINT style + WndprocManager, // WNDPROC lpfnWndProc + 0, // int cbClsExtra + 0, // int cbWndExtra + g_hInstance, // HINSTANCE hInstance + NULL, // HICON hIcon + LoadCursor( NULL, IDC_ARROW ), // HCURSOR hCursor + ( HBRUSH )COLOR_WINDOW, // HBRUSH hbrBackground + NULL, // LPCTSTR lpszMenuName + CLASSNAME_PAPMC // LPCTSTR lpszClassName + }; + + if( !RegisterClass( &wc ) ) return false; + + WindowManager = CreateWindowEx( + WS_EX_TOOLWINDOW, // DWORD dwExStyle + CLASSNAME_PAPMC, // LPCTSTR lpClassName + PAPMC_TITLE, // LPCTSTR lpWindowName + WS_OVERLAPPEDWINDOW | // DWORD dwStyle + WS_CLIPCHILDREN, // + rManagerDefault.left, // int x + rManagerDefault.top, // int y + rManagerDefault.right - rManagerDefault.left, // int nWidth + rManagerDefault.bottom - rManagerDefault.top, // int nHeight + NULL, // HWND hWndParent + NULL, // HMENU hMenu + g_hInstance, // HINSTANCE hInstance + NULL // LPVOID lpParam + ); + + RECT r; + GetClientRect( WindowManager, &r ); + + WindowListView = CreateWindowEx( + WS_EX_CLIENTEDGE, // DWORD dwExStyle + WC_LISTVIEW, // LPCTSTR lpClassName + NULL, // LPCTSTR lpWindowName + WS_VSCROLL | // DWORD dwStyle + LVS_REPORT | // + LVS_SORTASCENDING | // + LVS_SINGLESEL | // + WS_CHILD | // + WS_VISIBLE, // + 0, // int x + 0, // int y + r.right - r.left, // int nWidth + r.bottom - r.top, // int nHeight + WindowManager, // HWND hWndParent + NULL, // HMENU hMenu + g_hInstance, // HINSTANCE hInstance + NULL // LPVOID lpParam + ); + + if( !WindowListView ) return false; + + ListView_SetExtendedListViewStyle( + WindowListView, + LVS_EX_FULLROWSELECT | + ( bManagerGrid ? LVS_EX_GRIDLINES : 0 ) | + LVS_EX_HEADERDRAGDROP + ); + + // (0) File + LVCOLUMN lvc = { + LVCF_TEXT | LVCF_WIDTH | LVCF_FMT, // UINT mask; + LVCFMT_LEFT, // int fmt; + 100, // int cx; + TEXT( "File" ), // LPTSTR pszText; + 5, // int cchTextMax; + 0, // int iSubItem; + 0, // int iOrder; + 0 // int iImage; + }; + ListView_InsertColumn( WindowListView, 0, &lvc ); + + // (1) Status + lvc.pszText = TEXT( "Status" ); + lvc.cchTextMax = 7; + ListView_InsertColumn( WindowListView, 1, &lvc ); + + // (2) Name + lvc.cx = 220; + lvc.pszText = TEXT( "Name" ); + lvc.cchTextMax = 5; + ListView_InsertColumn( WindowListView, 2, &lvc ); + + + // (3) Type + lvc.pszText = TEXT( "Type" ); + lvc.cchTextMax = 5; + ListView_InsertColumn( WindowListView, 3, &lvc ); + + + bManagerVisible = ( WinPlaceManager.showCmd != SW_HIDE ); + SetWindowPlacement( WindowManager, &WinPlaceManager ); + + + // Exchange window procedure + WndprocListViewBackup = ( WNDPROC )GetWindowLong( WindowListView, GWL_WNDPROC ); + if( WndprocListViewBackup != NULL ) + { + SetWindowLong( WindowListView, GWL_WNDPROC, ( LONG )WndprocListView ); + } + + + // Build context menu + input_menu = CreatePopupMenu(); + AppendMenu( input_menu, MF_STRING, MENU_INPUT_CONFIG, TEXT( "Config" ) ); + AppendMenu( input_menu, MF_STRING, MENU_INPUT_ABOUT, TEXT( "About" ) ); + + output_menu = CreatePopupMenu(); + AppendMenu( output_menu, MF_STRING, MENU_OUTPUT_CONFIG, TEXT( "Config" ) ); + AppendMenu( output_menu, MF_STRING, MENU_OUTPUT_ABOUT, TEXT( "About" ) ); + AppendMenu( output_menu, MF_SEPARATOR | MF_GRAYED | MF_DISABLED, MENU_OUTPUT_SEP, NULL ); + AppendMenu( output_menu, MF_STRING, MENU_OUTPUT_ACTIVE, TEXT( "Active" ) ); + AppendMenu( output_menu, MF_STRING, MENU_OUTPUT_LOAD, TEXT( " " ) ); + + gen_menu = CreatePopupMenu(); + AppendMenu( gen_menu, MF_STRING, MENU_GEN_CONFIG, TEXT( "Config" ) ); + AppendMenu( gen_menu, MF_SEPARATOR | MF_GRAYED | MF_DISABLED, MENU_GEN_SEP, NULL ); + AppendMenu( gen_menu, MF_STRING, MENU_GEN_LOAD, TEXT( " " ) ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool PluginManager::Fill() +{ + if( !WindowListView ) return false; + + LVITEM lvi; + memset( &lvi, 0, sizeof( LVITEM ) ); + lvi.mask = LVIF_TEXT | LVIF_PARAM; + + Plugin * plugin; + int iIndex; + vector::iterator iter = plugins.begin(); + while( iter != plugins.end() ) + { +lvi.mask = LVIF_TEXT | LVIF_PARAM; + + plugin = *iter; + lvi.iItem = 0; + lvi.lParam = ( LPARAM )plugin; + + // (0) File + + lvi.iSubItem = 0; + lvi.pszText = plugin->GetFilename(); + lvi.cchTextMax = plugin->GetFilenameLen() + 1; + iIndex = ListView_InsertItem( WindowListView, &lvi ); + lvi.iItem = iIndex; +lvi.mask = LVIF_TEXT; + + // (1) Status + lvi.iSubItem = 1; + if( plugin->IsLoaded() ) + { + if( plugin->IsActive() ) + { + lvi.pszText = TEXT( "Active" ); + } + else + { + lvi.pszText = TEXT( "Loaded" ); + } + lvi.cchTextMax = 7; + } + else + { + lvi.pszText = TEXT( "Not loaded" ); + lvi.cchTextMax = 11; + } + ListView_SetItem( WindowListView, &lvi ); + + // (2) Name + lvi.iSubItem = 2; + lvi.pszText = plugin->GetName(); + lvi.cchTextMax = plugin->GetNameLen() + 1; + ListView_SetItem( WindowListView, &lvi ); + + // (3) Type + lvi.iSubItem = 3; + lvi.pszText = plugin->GetTypeString(); + lvi.cchTextMax = plugin->GetTypeStringLen() + 1; + ListView_SetItem( WindowListView, &lvi ); + + iter++; + } + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool PluginManager::Destroy() +{ + if( !WindowListView ) return false; + + DestroyWindow( WindowManager ); + DestroyMenu( input_menu ); + DestroyMenu( output_menu ); + DestroyMenu( gen_menu ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool PluginManager::Popup() +{ + if( !WindowListView ) return false; + if( !IsWindowVisible( WindowManager ) ) + ShowWindow( WindowManager, SW_SHOW ); + + SetActiveWindow( WindowManager ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void UpdatePluginStatus( Plugin * plugin, bool bLoaded, bool bActive ) +{ + LVFINDINFO lvfi = { + LVFI_PARAM, // UINT flags + NULL, // LPCTSTR psz + ( LPARAM )plugin, // LPARAM lParam + POINT(), // POINT pt + 0 // UINT vkDirection + }; + + int iIndex = ListView_FindItem( WindowListView, -1, &lvfi ); + if( iIndex != -1 ) + { + LVITEM lvi; + memset( &lvi, 0, sizeof( LVITEM ) ); + lvi.mask = LVIF_TEXT; + lvi.iItem = iIndex; + lvi.iSubItem = 1; + if( bLoaded ) + { + if( bActive ) + { + lvi.pszText = TEXT( "Active" ); + } + else + { + lvi.pszText = TEXT( "Loaded" ); + } + lvi.cchTextMax = 7; + } + else + { + lvi.pszText = TEXT( "Not loaded" ); + lvi.cchTextMax = 11; + } + ListView_SetItem( WindowListView, &lvi ); + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ContextMenuInput( InputPlugin * input, POINT * p ) +{ + BOOL iIndex = TrackPopupMenu( + input_menu, + TPM_LEFTALIGN | + TPM_TOPALIGN | + TPM_NONOTIFY | + TPM_RETURNCMD | + TPM_RIGHTBUTTON, + p->x, + p->y, + 0, + WindowManager, + NULL + ); + + switch( iIndex ) + { + case MENU_INPUT_ABOUT: + input->About( WindowManager ); + break; + + case MENU_INPUT_CONFIG: + input->Config( WindowManager ); + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ContextMenuOutput( OutputPlugin * output, POINT * p ) +{ + const bool bLoaded = output->IsLoaded(); + const bool bActive = output->IsActive(); + + EnableMenuItem( + output_menu, + MENU_OUTPUT_CONFIG, + bLoaded ? MF_ENABLED : MF_GRAYED | MF_DISABLED + ); + EnableMenuItem( + output_menu, + MENU_OUTPUT_ABOUT, + bLoaded ? MF_ENABLED : MF_GRAYED | MF_DISABLED + ); + EnableMenuItem( + output_menu, + MENU_OUTPUT_ACTIVE, + bLoaded ? MF_ENABLED : MF_GRAYED | MF_DISABLED + ); + CheckMenuItem( + output_menu, + MENU_OUTPUT_ACTIVE, + bActive ? MF_CHECKED : MF_UNCHECKED + ); + + + MENUITEMINFO mii = { 0 }; + mii.cbSize = sizeof( MENUITEMINFO ); + mii.fMask = MIIM_STATE | MIIM_STRING; + mii.fState = bActive ? MFS_GRAYED | MFS_DISABLED : MFS_ENABLED; + mii.dwTypeData = ( LPTSTR )( bLoaded ? TEXT( "Unload" ) : TEXT( "Load" ) ); + mii.cch = bLoaded ? 7 : 5; + + SetMenuItemInfo( + output_menu, // HMENU hMenu + MENU_OUTPUT_LOAD, // UINT uItem + FALSE, // BOOL fByPosition, + &mii // LPMENUITEMINFO lpmii + ); + + + BOOL iIndex = TrackPopupMenu( + output_menu, + TPM_LEFTALIGN | + TPM_TOPALIGN | + TPM_NONOTIFY | + TPM_RETURNCMD | + TPM_RIGHTBUTTON, + p->x, + p->y, + 0, + WindowManager, + NULL + ); + + switch( iIndex ) + { + case MENU_OUTPUT_CONFIG: + output->Config( WindowManager ); + break; + + case MENU_OUTPUT_ABOUT: + output->About( WindowManager ); + break; + + case MENU_OUTPUT_ACTIVE: + { + if( Playback::IsPlaying() ) + { + MessageBox( WindowManager, TEXT( "Cannot do this while playing!" ), TEXT( "Error" ), MB_ICONERROR ); + break; + } + + if( bActive ) + output->Stop(); + else + output->Start(); + + const bool bActiveNew = output->IsActive(); + if( bActiveNew != bActive ) + { + UpdatePluginStatus( output, bLoaded, bActiveNew ); + } + + break; + } + + case MENU_OUTPUT_LOAD: + { + if( bLoaded ) + output->Unload(); + else + output->Load(); + + const bool bLoadedNew = output->IsLoaded(); + if( bLoadedNew != bLoaded ) + { + UpdatePluginStatus( output, bLoadedNew, bActive ); + } + + break; + } + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ContextMenuVis( VisPlugin * vis, POINT * p ) +{ + HMENU vis_menu = CreatePopupMenu(); + + const bool bLoaded = vis->IsLoaded(); + const bool bPluginActive = vis->IsActive(); + + VisModule * mod; + HMENU * menus = NULL; + + const int iModCount = ( int )vis->modules.size(); + if( iModCount == 1 ) + { + // Single module + mod = vis->modules[ 0 ]; + const UINT uFlagsActive = MF_STRING | + ( mod->IsActive() ? MF_CHECKED : MF_UNCHECKED ) | + ( bLoaded ? MF_ENABLED : MF_DISABLED | MF_GRAYED ); + const UINT uFlagsConfig = MF_STRING | + ( bLoaded ? MF_ENABLED : MF_DISABLED | MF_GRAYED ); + AppendMenu( vis_menu, uFlagsActive, 1, TEXT( "Active" ) ); + AppendMenu( vis_menu, uFlagsConfig, 2, TEXT( "Config" ) ); + } + else + { + // Two or more + menus = new HMENU[ iModCount ]; + int iIndex = 0; + vector ::iterator iter = vis->modules.begin(); + while( iter != vis->modules.end() ) + { + mod = *iter; + + menus[ iIndex ] = CreatePopupMenu(); + UINT uFlags = MF_STRING | ( mod->IsActive() ? MF_CHECKED : MF_UNCHECKED ); + AppendMenu( menus[ iIndex ], uFlags, iIndex * 2 + 1, TEXT( "Active" ) ); + AppendMenu( menus[ iIndex ], MF_STRING, iIndex * 2 + 2, TEXT( "Config" ) ); + + uFlags = MF_STRING | MF_POPUP | ( ( !bLoaded || ( bPluginActive && !mod->IsActive() ) ) ? MF_GRAYED | MF_DISABLED : 0 ); + AppendMenu( vis_menu, uFlags, ( UINT_PTR )menus[ iIndex ], mod->GetName() ); + + iIndex++; + iter++; + } + } + + + + const int iPos = ( int )vis->modules.size() * 2 + 1; + AppendMenu( vis_menu, MF_SEPARATOR | MF_GRAYED | MF_DISABLED, iPos, NULL ); + + AppendMenu( + vis_menu, + MF_STRING | ( bPluginActive ? MF_DISABLED | MF_GRAYED : MF_ENABLED ), + iPos + 1, + bLoaded ? TEXT( "Unload" ) : TEXT( "Load" ) + ); + + + BOOL iIndex = TrackPopupMenu( + vis_menu, // HMENU hMenu + TPM_LEFTALIGN | // UINT uFlags + TPM_TOPALIGN | // . + TPM_NONOTIFY | // . + TPM_RETURNCMD | // . + TPM_RIGHTBUTTON, // . + p->x, // int x + p->y, // int y + 0, // int nReserved + WindowManager, // HWND hWnd + NULL // HWND prcRect + ); + + if( iIndex ) + { + if( iIndex >= iPos ) + { + // Load/unload + if( bLoaded ) + vis->Unload(); + else + vis->Load(); + + const bool bLoadedNew = vis->IsLoaded(); + + if( bLoadedNew != bLoaded ) + UpdatePluginStatus( vis, bLoadedNew, FALSE ); + } + else + { + int iWhich = ( iIndex - 1 ) / 2; + if( iIndex & 1 ) + { + // Active + const bool bPluginActive = vis->IsActive(); + + if( !vis->modules[ iWhich ]->IsActive() ) + vis->modules[ iWhich ]->Start(); + else + vis->modules[ iWhich ]->Stop(); + + const bool bPluginActiveNew = vis->IsActive(); + + if( bPluginActiveNew && ( bPluginActiveNew != bPluginActive ) ) + UpdatePluginStatus( vis, TRUE, bPluginActiveNew ); + } + else + { + // Config + vis->modules[ iWhich ]->Config(); + } + } + } + + DestroyMenu( vis_menu ); + if( iModCount > 1 ) + { + for( int i = 0; i < iModCount; i++ ) + DestroyMenu( menus[ i ] ); + delete [] menus; + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ContextMenuDsp( DspPlugin * dsp, POINT * p ) +{ + HMENU context_menu = CreatePopupMenu(); + + const bool bLoaded = dsp->IsLoaded(); + const bool bPluginActive = dsp->IsActive(); + + DspModule * mod; + + HMENU * menus = NULL; + + HMENU * add_rem_menus = NULL; + + const int iEach = ( 3 * active_dsp_count + 2 ); + + const int iModCount = ( int )dsp->modules.size(); + if( iModCount == 1 ) + { + // Single module + mod = dsp->modules[ 0 ]; + + add_rem_menus = new HMENU[ 2 ]; + add_rem_menus[ 0 ] = CreatePopupMenu(); + add_rem_menus[ 1 ] = CreatePopupMenu(); + + int i; + TCHAR szHere[ 12 ]; + for( i = 0; i < active_dsp_count; i++ ) + { + TCHAR * szName = active_dsp_mods[ i ]->GetName(); + + // Entry for "Add" + _stprintf( szHere, TEXT( "Here (%i)" ), i + 1 ); + AppendMenu( + add_rem_menus[ 0 ], + MF_STRING, + 2 * i + 1, + szHere + ); + AppendMenu( + add_rem_menus[ 0 ], + MF_STRING | MF_DISABLED | MF_GRAYED, + 2 * i + 2, + szName + ); + + // Entry for "Remove" + AppendMenu( + add_rem_menus[ 1 ], + MF_STRING, + 2 * active_dsp_count + 2 + i, + szName + ); + } + _stprintf( szHere, TEXT( "Here (%i)" ), i + 1 ); + AppendMenu( + add_rem_menus[ 0 ], + MF_STRING, + 2 * i + 1, + szHere + ); + + + // Main entries + const UINT uFlagsAdd = MF_STRING | MF_POPUP | + ( ( bLoaded && !mod->IsActive() ) ? 0 : MF_DISABLED | MF_GRAYED ); + const UINT uFlagsRemove = MF_STRING | MF_POPUP | + ( ( bLoaded && active_dsp_count ) ? 0 : MF_GRAYED | MF_DISABLED ); + const UINT uFlagsConfig = MF_STRING | + ( ( bLoaded && mod->IsActive() ) ? 0 : MF_DISABLED | MF_GRAYED ); + + AppendMenu( + context_menu, + uFlagsAdd, + ( UINT_PTR )add_rem_menus[ 0 ], + TEXT( "Add" ) + ); + AppendMenu( + context_menu, + uFlagsRemove, + ( UINT_PTR )add_rem_menus[ 1 ], + TEXT( "Remove" ) + ); + AppendMenu( + context_menu, + uFlagsConfig, + 3 * active_dsp_count + 1 + 1, + TEXT( "Config" ) + ); + } + else + { + // Two or more + menus = new HMENU[ iModCount ]; + add_rem_menus = new HMENU[ 2 * iModCount ]; + + int iIndex = 0; + vector ::iterator iter = dsp->modules.begin(); + while( iter != dsp->modules.end() ) + { + mod = *iter; + + menus[ iIndex ] = CreatePopupMenu(); + + add_rem_menus[ 2 * iIndex ] = CreatePopupMenu(); + add_rem_menus[ 2 * iIndex + 1 ] = CreatePopupMenu(); + + int i; + TCHAR szHere[ 12 ]; + for( i = 0; i < active_dsp_count; i++ ) + { + TCHAR * szName = active_dsp_mods[ i ]->GetName(); + + // Entry for "Add" + _stprintf( szHere, TEXT( "Here (%i)" ), i + 1 ); + AppendMenu( + add_rem_menus[ 2 * iIndex ], + MF_STRING, + ( iIndex * iEach ) + ( 2 * i + 1 ), + szHere + ); + AppendMenu( + add_rem_menus[ 2 * iIndex ], + MF_STRING | MF_DISABLED | MF_GRAYED, + ( iIndex * iEach ) + ( 2 * i + 2 ), + szName + ); + + // Entry for "Remove" + AppendMenu( + add_rem_menus[ 2 * iIndex + 1 ], + MF_STRING, + ( iIndex * iEach ) + ( 2 * active_dsp_count + 2 + i ), + szName + ); + } + _stprintf( szHere, TEXT( "Here (%i)" ), i + 1 ); + AppendMenu( + add_rem_menus[ 2 * iIndex ], + MF_STRING, + ( iIndex * iEach ) + ( 2 * i + 1 ), + szHere + ); + + AppendMenu( + menus[ iIndex ], + MF_STRING | MF_POPUP | ( mod->IsActive() ? MF_DISABLED | MF_GRAYED : 0 ), + ( UINT_PTR )add_rem_menus[ 2 * iIndex ], + TEXT( "Add" ) + ); + AppendMenu( + menus[ iIndex ], + MF_STRING | MF_POPUP | ( active_dsp_count ? 0 : MF_GRAYED | MF_DISABLED ), + ( UINT_PTR )add_rem_menus[ 2 * iIndex + 1 ], + TEXT( "Remove" ) + ); + AppendMenu( + menus[ iIndex ], + MF_STRING | ( mod->IsActive() ? 0 : MF_DISABLED | MF_GRAYED ), + ( iIndex * iEach ) + ( 3 * active_dsp_count + 1 + 1 ), + TEXT( "Config" ) + ); + AppendMenu( context_menu, MF_STRING | MF_POPUP | ( bLoaded ? 0 : ( MF_DISABLED | MF_GRAYED ) ), ( UINT_PTR )menus[ iIndex ], mod->GetName() ); + + iIndex++; + iter++; + } + } + + + const int iPos = ( int )dsp->modules.size() * ( 3 * active_dsp_count + 1 + 1 ) + 1; + AppendMenu( context_menu, MF_SEPARATOR | MF_GRAYED | MF_DISABLED, iPos, NULL ); + + UINT uFlags = MF_STRING; + + AppendMenu( + context_menu, + uFlags | ( bPluginActive ? MF_DISABLED | MF_GRAYED : MF_ENABLED ), + iPos + 1, + bLoaded ? TEXT( "Unload" ) : TEXT( "Load" ) + ); + + + BOOL iIndex = TrackPopupMenu( + context_menu, // HMENU hMenu + TPM_LEFTALIGN | // UINT uFlags + TPM_TOPALIGN | // . + TPM_NONOTIFY | // . + TPM_RETURNCMD | // . + TPM_RIGHTBUTTON, // . + p->x, // int x + p->y, // int y + 0, // int nReserved + WindowManager, // HWND hWnd + NULL // HWND prcRect + ); + + /* + + Example menu + + Mod1 + Add + Here [ 1] + Active1 [ 2] + Here [ 3] + Active2 [ 4] + Here [ 5] + Remove + Active1 [ 6] + Active2 [ 7] + Config [ 8] + Mod2 + Add + Here [ 9] + Active1 [10] + ... + --- + Load/Unload [11] + + */ + + + if( iIndex ) + { + if( iIndex >= iPos ) + { + // Load/unload + if( bLoaded ) + dsp->Unload(); + else + dsp->Load(); + + const bool bLoadedNew = dsp->IsLoaded(); + + if( bLoadedNew != bLoaded ) + UpdatePluginStatus( dsp, bLoadedNew, FALSE ); + } + else + { + int iWhichMod = ( iIndex - 1 ) / iEach; + int iRem = iIndex % iEach; + if( iRem == 0 ) + { + // Config + dsp->modules[ iWhichMod ]->Config(); + + } + else if( iRem < 2 * active_dsp_count + 2 ) + { + // Add module + const bool bPluginActive = dsp->IsActive(); + + dsp->modules[ iWhichMod ]->Start( iRem / 2 ); + + const bool bPluginActiveNew = dsp->IsActive(); + if( bPluginActiveNew != bPluginActive ) + { + UpdatePluginStatus( dsp, TRUE, bPluginActiveNew ); + } + } + else + { + // Remove module + const bool bPluginActive = dsp->IsActive(); + + iWhichMod = iRem - ( 2 * active_dsp_count + 2 ); +/* + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "REM <%i> <%i>" ), iIndex, iWhichMod ); + Console::Append( szBuffer ); +*/ + active_dsp_mods[ iWhichMod ]->Stop(); + + const bool bPluginActiveNew = dsp->IsActive(); + if( bPluginActiveNew != bPluginActive ) + { + UpdatePluginStatus( dsp, TRUE, bPluginActiveNew ); + } + } + } + } + + DestroyMenu( context_menu ); + if( iModCount > 1 ) + { + for( int i = 0; i < iModCount; i++ ) + DestroyMenu( menus[ i ] ); + delete [] menus; + } + + for( int j = 0; j < iModCount; j++ ) + DestroyMenu( add_rem_menus[ j ] ); + delete [] add_rem_menus; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ContextMenuGen( GenPlugin * gen, POINT * p ) +{ + const bool bLoaded = gen->IsLoaded(); + + EnableMenuItem( + gen_menu, + MENU_GEN_CONFIG, + bLoaded ? MF_ENABLED : MF_GRAYED | MF_DISABLED + ); + + + MENUITEMINFO mii = { 0 }; + mii.cbSize = sizeof( MENUITEMINFO ); + mii.fMask = MIIM_STATE | MIIM_STRING; + mii.fState = gen->AllowRuntimeUnload() ? MFS_ENABLED : MFS_GRAYED | MFS_DISABLED; + mii.dwTypeData = ( LPTSTR )( bLoaded ? TEXT( "Unload" ) : TEXT( "Load and activate" ) ); + mii.cch = bLoaded ? 7 : 18; + + SetMenuItemInfo( + gen_menu, // HMENU hMenu + MENU_GEN_LOAD, // UINT uItem + FALSE, // BOOL fByPosition, + &mii // LPMENUITEMINFO lpmii + ); + + + BOOL iIndex = TrackPopupMenu( + gen_menu, + TPM_LEFTALIGN | + TPM_TOPALIGN | + TPM_NONOTIFY | + TPM_RETURNCMD | + TPM_RIGHTBUTTON, + p->x, + p->y, + 0, + WindowManager, + NULL + ); + + switch( iIndex ) + { + case MENU_GEN_CONFIG: + gen->Config(); + break; + + case MENU_GEN_LOAD: + { + if( bLoaded ) + gen->Unload(); + else + gen->Load(); + + const bool bLoadedNew = gen->IsLoaded(); + if( bLoadedNew != bLoaded ) + { + UpdatePluginStatus( gen, bLoadedNew, bLoadedNew ); + } + + break; + } + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ContextMenu( Plugin * plugin, POINT * p ) +{ + if( !plugin ) return; + + switch( plugin->GetType() ) + { + case PLUGIN_TYPE_INPUT: + ContextMenuInput( ( InputPlugin * )plugin, p ); + break; + + case PLUGIN_TYPE_OUTPUT: + ContextMenuOutput( ( OutputPlugin * )plugin, p ); + break; + + case PLUGIN_TYPE_VIS: + ContextMenuVis( ( VisPlugin * )plugin, p ); + break; + + case PLUGIN_TYPE_DSP: + ContextMenuDsp( ( DspPlugin * )plugin, p ); + break; + + case PLUGIN_TYPE_GEN: + ContextMenuGen( ( GenPlugin * )plugin, p ); + break; + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +LRESULT CALLBACK WndprocListView( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + static bool bContextOpen = false; + + switch( message ) + { + case WM_CONTEXTMENU: + { + if( ( HWND )wp != WindowListView ) break; + if( bContextOpen ) break; + bContextOpen = true; +//////////////////////////////////////////////////////////////////////////////// + + POINT p = { LOWORD( lp ), HIWORD( lp ) }; + ScreenToClient( WindowListView, &p ); + + // Which item? + LVHITTESTINFO hti; + memset( &hti, 0, sizeof( LVHITTESTINFO ) ); + hti.pt = p; + ListView_HitTest( WindowListView, &hti ); + + // Which plugin? + LVITEM lvi; + memset( &lvi, 0, sizeof( LVITEM ) ); + lvi.mask = LVIF_PARAM; + lvi.iItem = hti.iItem; + lvi.iSubItem = 0; + + if( !ListView_GetItem( WindowListView, &lvi ) ) + { + bContextOpen = false; + break; + } + Plugin * plugin = ( Plugin * )lvi.lParam; + + // Context menu + p.x = LOWORD( lp ); + p.y = HIWORD( lp ); + ContextMenu( plugin, &p ); + +//////////////////////////////////////////////////////////////////////////////// + bContextOpen = false; + break; + } + } + return CallWindowProc( WndprocListViewBackup, hwnd, message, wp, lp ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +LRESULT CALLBACK WndprocManager( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + switch( message ) + { + case WM_SIZE: + { + // Resize children + RECT client; + GetClientRect( WindowManager, &client ); + + if( WindowListView ) + MoveWindow( WindowListView, 0, 0, client.right - client.left, client.bottom - client.top, TRUE ); + break; + } + + case WM_SYSCOMMAND: + if( ( wp & 0xFFF0 ) == SC_CLOSE ) + { + ShowWindow( hwnd, SW_HIDE ); + return 0; + } + break; + + case WM_DESTROY: + cwpcWinPlaceManager.TriggerCallback(); + cwpcWinPlaceManager.RemoveCallback(); + break; + + case WM_SHOWWINDOW: + bManagerVisible = ( wp == TRUE ); + break; + } + return DefWindowProc( hwnd, message, wp, lp ); +} diff --git a/Externals/MusicMod/Player/Src/PluginManager.h b/Externals/MusicMod/Player/Src/PluginManager.h new file mode 100644 index 0000000000..e68fb6d311 --- /dev/null +++ b/Externals/MusicMod/Player/Src/PluginManager.h @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_PLUGIN_MANAGER_H +#define PA_PLUGIN_MANAGER_H + + + +#include "Global.h" +#include "Plugin.h" + + + +extern HWND WindowManager; + + + +void UpdatePluginStatus( Plugin * plugin, bool bLoaded, bool bActive ); + + + +namespace PluginManager +{ + bool Build(); + bool Fill(); + bool Destroy(); + + bool Popup(); +}; + + + +#endif // PA_PLUGIN_MANAGER_H diff --git a/Externals/MusicMod/Player/Src/Prefs.cpp b/Externals/MusicMod/Player/Src/Prefs.cpp new file mode 100644 index 0000000000..359fa40b5f --- /dev/null +++ b/Externals/MusicMod/Player/Src/Prefs.cpp @@ -0,0 +1,565 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Prefs.h" +#include "Util.h" +#include "Font.h" +#include "Console.h" +#include + +using namespace std; + + +#define CLASSNAME_PREFS TEXT( "CLASSNAME_PREFS" ) +#define TITLE_PREFS TEXT( "Preferences" ) + + +#define PAGE_WIDTH 409 +#define PAGE_HEIGHT 400 + + +#define GAP_LEFT 4 +#define GAP_RIGHT 5 +#define GAP_TOP 4 +#define GAP_BOTTOM 5 +#define SPLITTER_WIDTH 6 +#define SPLITTER_HEIGHT 6 + +#define BOTTOM_SPACE 36 + +#define BUTTON_WIDTH 80 + + +#define PREFS_WIDTH 606 +#define PREFS_HEIGHT ( GAP_TOP + PAGE_HEIGHT + BOTTOM_SPACE ) + + + +struct PrefRecCompare +{ + bool operator()( const prefsDlgRec * a, const prefsDlgRec * b ) const + { + if( a->hInst < b->hInst ) return true; + if( a->dlgID < b->dlgID ) return true; + return strcmp( a->name, b->name ) < 0; + } +}; + + +map rec_to_item; + + +struct AllWeNeed +{ + prefsDlgRec * PageData; + HWND hwnd; +}; + + + +HWND WindowPrefs = NULL; +HWND WindowPage = NULL; +HWND WindowTree = NULL; +HWND ButtonClose = NULL; + +HTREEITEM root = NULL; + +HWND hCurrentPage = NULL; +// bool bKeepFocus = false; + + +LRESULT CALLBACK WndprocPrefs( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); +LRESULT CALLBACK WndprocTree( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); +WNDPROC WndprocTreeBackup = NULL; + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Prefs::Create() +{ + if( WindowPrefs ) return false; + + // Register class + WNDCLASS wc = { + 0, // UINT style + WndprocPrefs, // WNDPROC lpfnWndProc + 0, // int cbClsExtra + 0, // int cbWndExtra + g_hInstance, // HINSTANCE hInstance + NULL, // HICON hIcon + LoadCursor( NULL, IDC_ARROW ), // HCURSOR hCursor + ( HBRUSH )COLOR_WINDOW, // HBRUSH hbrBackground + NULL, // LPCTSTR lpszMenuName + CLASSNAME_PREFS // LPCTSTR lpszClassName + }; + + if( !RegisterClass( &wc ) ) return false; + + const int cxScreen = GetSystemMetrics( SM_CXFULLSCREEN ); + const int cyScreen = GetSystemMetrics( SM_CYFULLSCREEN ); + + // Create window + WindowPrefs = CreateWindowEx( + WS_EX_WINDOWEDGE | // DWORD dwExStyle + WS_EX_TOOLWINDOW, // + CLASSNAME_PREFS, // LPCTSTR lpClassName + TITLE_PREFS, // LPCTSTR lpWindowName + WS_OVERLAPPED | // DWORD dwStyle +// WS_VISIBLE | // + WS_CLIPCHILDREN | // + WS_SYSMENU, // + ( cxScreen - PREFS_WIDTH ) / 2, // int x + ( cyScreen - PREFS_HEIGHT ) / 2, // int y + PREFS_WIDTH, // int nWidth + PREFS_HEIGHT, // int nHeight + NULL, // HWND hWndParent + NULL, // HMENU hMenu + g_hInstance, // HINSTANCE hInstance + NULL // LPVOID lpParam + ); + + + if( !WindowPrefs ) + { + UnregisterClass( CLASSNAME_PREFS, g_hInstance ); + return false; + } + + + RECT r; + GetClientRect( WindowPrefs, &r ); + + +//////////////////////////////////////////////////////////////////////////////// + const int iWidth = PREFS_WIDTH * 2 - r.right; + const int iHeight = PREFS_HEIGHT * 2 - r.bottom; + SetWindowPos( + WindowPrefs, + NULL, + ( cxScreen - iWidth ) / 2, + ( cyScreen - iHeight ) / 2, + iWidth, + iHeight, + SWP_NOZORDER + ); + GetClientRect( WindowPrefs, &r ); +//////////////////////////////////////////////////////////////////////////////// + + + LoadCommonControls(); + + const int iTreeWidth = ( r.right - r.left ) - PAGE_WIDTH - GAP_LEFT - GAP_RIGHT - SPLITTER_WIDTH; + + WindowTree = CreateWindowEx( + WS_EX_CLIENTEDGE, // DWORD dwExStyle + WC_TREEVIEW, // LPCTSTR lpClassName + NULL, // LPCTSTR lpWindowName + WS_VSCROLL | // DWORD dwStyle + WS_VISIBLE | // + WS_CHILD | // + WS_TABSTOP | // + TVS_HASLINES | // + TVS_LINESATROOT | // + TVS_HASBUTTONS | // + TVS_SHOWSELALWAYS | // + TVS_DISABLEDRAGDROP | // + TVS_NONEVENHEIGHT, // + GAP_LEFT, // int x + GAP_TOP, // int y + iTreeWidth, // int nWidth + PAGE_HEIGHT, // int nHeight + WindowPrefs, // HWND hWndParent + NULL, // HMENU hMenu + g_hInstance, // HINSTANCE hInstance + NULL // LPVOID lpParam + ); + + if( !WindowTree ) + { + ; //... + } + + Font::Apply( WindowTree ); + + // Exchange window procedure + WndprocTreeBackup = ( WNDPROC )GetWindowLong( WindowTree, GWL_WNDPROC ); + if( WndprocTreeBackup != NULL ) + { + SetWindowLong( WindowTree, GWL_WNDPROC, ( LONG )WndprocTree ); + } + + + const int iPageLeft = GAP_LEFT + iTreeWidth + SPLITTER_WIDTH; + + WindowPage = CreateWindowEx( + 0, // DWORD dwExStyle + TEXT( "Static" ), // LPCTSTR lpClassName + TEXT( "Nothing to see here" ), // LPCTSTR lpWindowName + WS_TABSTOP | // DWORD dwStyle + WS_VISIBLE | // + WS_CHILD | // + SS_CENTER | // + SS_CENTERIMAGE, // + iPageLeft, // int x + GAP_TOP, // int y + PAGE_WIDTH, // int nWidth + PAGE_HEIGHT, // int nHeight + WindowPrefs, // HWND hWndParent + NULL, // HMENU hMenu + g_hInstance, // HINSTANCE hInstance + NULL // LPVOID lpParam + ); + + if( !WindowPage ) + { + ; //... + } + + Font::Apply( WindowPage ); + + + const int iButtonLeft = ( r.right - r.left ) - GAP_RIGHT - BUTTON_WIDTH - 1; + const int iButtonTop = ( r.bottom - r.top ) - BOTTOM_SPACE + SPLITTER_HEIGHT; + + + ButtonClose = CreateWindowEx( + 0, // DWORD dwExStyle + TEXT( "Button" ), // LPCTSTR lpClassName + TEXT( "Close" ), // LPCTSTR lpWindowName + WS_TABSTOP | // DWORD dwStyle + WS_VISIBLE | // + WS_CHILD | // + BS_PUSHBUTTON | // + BS_TEXT | // + BS_VCENTER, // + iButtonLeft, // int x + iButtonTop, // int y + BUTTON_WIDTH, // int nWidth + BOTTOM_SPACE - SPLITTER_HEIGHT - GAP_BOTTOM, // int nHeight + WindowPrefs, // HWND hWndParent + NULL, // HMENU hMenu + g_hInstance, // HINSTANCE hInstance + NULL // LPVOID lpParam + ); + + Font::Apply( ButtonClose ); + + AllWeNeed * awn_root = new AllWeNeed; +// memset( &awn_root->PageData, 0, sizeof( prefsDlgRec ) ); + awn_root->PageData = NULL; + awn_root->hwnd = NULL; + + TV_INSERTSTRUCT tvi = { + root, // HTREEITEM hParent + TVI_SORT, // HTREEITEM hInsertAfter + { + TVIF_PARAM | TVIF_STATE | TVIF_TEXT, // UINT mask + NULL, // HTREEITEM hItem + TVIS_EXPANDED | TVIS_SELECTED, // UINT state + 0, // UINT stateMask + TEXT( "General" ), // LPSTR pszText + 8, // int cchTextMax + 0, // int iImage + 0, // int iSelectedImage + 0, // int cChildren + ( LPARAM )awn_root // LPARAM lParam + } + }; + + root = TreeView_InsertItem( WindowTree, &tvi ); + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Prefs::Destroy() +{ + if( !WindowPrefs ) return false; + + DestroyWindow( WindowPrefs ); + UnregisterClass( CLASSNAME_PREFS, g_hInstance ); + WindowPrefs = NULL; + + DestroyWindow( WindowTree ); + WindowTree = NULL; + + DestroyWindow( WindowPage ); + WindowPage = NULL; + + DestroyWindow( ButtonClose ); + ButtonClose = NULL; + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Prefs_Show( HTREEITEM item ) +{ + if( !WindowPrefs ) return false; + + // Select and load associated page + TreeView_SelectItem( WindowTree, item ); + + if( !IsWindowVisible( WindowPrefs ) ) + { + ShowWindow( WindowPrefs, SW_SHOW ); + } + + SetActiveWindow( WindowPrefs ); + SetFocus( WindowTree ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Prefs::Show() +{ + return Prefs_Show( root ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Prefs::Show( prefsDlgRec * PageData ) +{ + map::iterator iter = rec_to_item.find( PageData ); + if( iter != rec_to_item.end() ) + { + return Prefs_Show( iter->second ); + } + else + { + return Prefs_Show( root ); + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Prefs::AddPage( prefsDlgRec * PageData ) +{ + // TODO unicode! + if( !WindowPrefs ) return false; + + // Backup + char * NameBackup = new char[ strlen( PageData->name ) + 1 ]; + strcpy( NameBackup, PageData->name ); + prefsDlgRec * PageDataBackup = new prefsDlgRec; + memcpy( PageDataBackup, PageData, sizeof( prefsDlgRec ) ); + PageDataBackup->name = NameBackup; + + AllWeNeed * awn = new AllWeNeed; + awn->PageData = PageDataBackup; + awn->hwnd = NULL; + + TV_INSERTSTRUCT tvi = { + root, // HTREEITEM hParent + TVI_SORT, // HTREEITEM hInsertAfter + { // + TVIF_PARAM | TVIF_STATE | TVIF_TEXT, // UINT mask + NULL, // HTREEITEM hItem + TVIS_EXPANDED | TVIS_SELECTED, // UINT state + 0, // UINT stateMask + PageDataBackup->name, // LPSTR pszText + ( int )strlen( PageDataBackup->name ) + 1, // int cchTextMax + 0, // int iImage + 0, // int iSelectedImage + 0, // int cChildren + ( LPARAM )awn // LPARAM lParam + } + }; + + HTREEITEM new_item = TreeView_InsertItem( WindowTree, &tvi ); + + TreeView_Expand( WindowTree, root, TVE_EXPAND ); + + rec_to_item.insert( pair( PageDataBackup, new_item ) ); + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +LRESULT CALLBACK WndprocPrefs( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + switch( message ) + { + case WM_NOTIFY: + { + NMHDR * nmhdr = ( NMHDR * )lp; + if( ( nmhdr->hwndFrom == WindowTree ) && ( nmhdr->code == TVN_SELCHANGING ) ) + { + NMTREEVIEW * nmtv = ( NMTREEVIEW * )lp; + TVITEM * OldPage = &nmtv->itemOld; + TVITEM * NewPage = &nmtv->itemNew; + + // Destroy old window + AllWeNeed * old_awn = ( AllWeNeed * )OldPage->lParam; + if( old_awn && old_awn->hwnd && IsWindow( old_awn->hwnd ) ) + { + DestroyWindow( old_awn->hwnd ); + old_awn->hwnd = NULL; + } + + // Create new window + AllWeNeed * new_awn = ( AllWeNeed * )NewPage->lParam; + if( new_awn ) + { + prefsDlgRec * PageData = new_awn->PageData; + if( PageData && PageData->hInst ) // root has NULL here + { + if( !PageData->proc ) + { + MessageBox( 0, TEXT( "proc NULL" ), TEXT( "" ), 0 ); + PageData->proc = ( void * )WndprocPrefs; + } + +/* + RECT r; + GetWindowRect( WindowPage, &r ); + const int iWidth = r.right - r.left; + const int iHeight = r.bottom - r.top; + POINT p = { r.left, r.top }; + ScreenToClient( WindowPrefs, &p ); + MoveWindow( WindowPage, p.x, p.y, iWidth - 10, iHeight - 10, FALSE ); +*/ +// bKeepFocus = true; + + HWND hPage = CreateDialog( + PageData->hInst, // HINSTANCE hInstance, + ( LPCTSTR )PageData->dlgID, // LPCTSTR lpTemplate, + WindowPage, // HWND hWndParent, + ( DLGPROC )PageData->proc // DLGPROC lpDialogFunc + ); + new_awn->hwnd = hPage; + +// MoveWindow( WindowPage, p.x, p.y, iWidth, iHeight, FALSE ); + + ShowWindow( hPage, SW_SHOW ); + UpdateWindow( hPage ); + SetFocus( WindowTree ); +/* + SetActiveWindow( hPage ); + SetActiveWindow( hwnd ); +*/ + hCurrentPage = hPage; +// bKeepFocus = false; + } +/* + else + { + MessageBox( 0, TEXT( "hInst NULL" ), TEXT( "" ), 0 ); + } +*/ + } + else + { + MessageBox( 0, TEXT( "awn NULL" ), TEXT( "" ), 0 ); + } + } + break; + } + + case WM_COMMAND: + switch( HIWORD( wp ) ) + { + case BN_CLICKED: + if( ( HWND )lp == ButtonClose ) + { + PostMessage( hwnd, WM_SYSCOMMAND, SC_CLOSE, 0 ); + } + break; + } + break; + + case WM_SYSCOMMAND: + if( ( wp & 0xFFF0 ) == SC_CLOSE ) + { + ShowWindow( hwnd, SW_HIDE ); + + // Destroy current page so the settings are saved + // (currently be selecting the empty page + TreeView_SelectItem( WindowTree, root ); + +/* + if( hCurrentPage && IsWindow( hCurrentPage ) ) + { + DestroyWindow( hCurrentPage ); + hCurrentPage = NULL; + } +*/ + return 0; + } + break; + + case WM_KEYDOWN: + switch( wp ) + { + case VK_ESCAPE: + PostMessage( WindowPrefs, WM_SYSCOMMAND, SC_CLOSE, 0 ); + break; + + } + break; +/* + case WM_KILLFOCUS: + if( bKeepFocus ) + { + return 1; + } + break; +*/ + } + + return DefWindowProc( hwnd, message, wp, lp ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +LRESULT CALLBACK WndprocTree( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + switch( message ) + { + case WM_KEYDOWN: + switch( wp ) + { + case VK_ESCAPE: + PostMessage( WindowPrefs, WM_SYSCOMMAND, SC_CLOSE, 0 ); + break; + + } + break; + + } + return CallWindowProc( WndprocTreeBackup, hwnd, message, wp, lp ); +} diff --git a/Externals/MusicMod/Player/Src/Prefs.h b/Externals/MusicMod/Player/Src/Prefs.h new file mode 100644 index 0000000000..4100d48760 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Prefs.h @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_PREFS_H +#define PA_PREFS_H + + + +#include "Global.h" +#include "Winamp/wa_ipc.h" + + + +namespace Prefs +{ + bool Create(); + bool Destroy(); + + bool Show(); + bool Show( prefsDlgRec * PageData ); + + bool AddPage( prefsDlgRec * PageData ); +}; + + + +#endif // PA_PREFS_H diff --git a/Externals/MusicMod/Player/Src/Rebar.cpp b/Externals/MusicMod/Player/Src/Rebar.cpp new file mode 100644 index 0000000000..b4c62ebeaf --- /dev/null +++ b/Externals/MusicMod/Player/Src/Rebar.cpp @@ -0,0 +1,1457 @@ + + +// ======================================================================================= +// WndprocRebar is called once every second during playback +// ======================================================================================= +// In Toolbar::Create() the progress bar is called the Seekbar + + + + +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Rebar.h" +#include "Font.h" +#include "Main.h" +#include "Playlist.h" +#include "Playback.h" +#include "Console.h" +#include "Util.h" +#include "Config.h" + +#include "Winamp/wa_ipc.h" +#include "Resources/resrc1.h" + + + +#define BAND_ORDER 1 +#define BAND_EQ 2 +#define BAND_SEEK 3 +#define BAND_VOL 4 +#define BAND_PAN 5 +#define BAND_BUTTONS 6 +#define BAND_VIS 7 + +#define BAND_FIRST BAND_ORDER +#define BAND_LAST BAND_VIS + + +#define IMAGE_PREV 0 +#define IMAGE_PLAY 1 +#define IMAGE_PAUSE 2 +#define IMAGE_STOP 3 +#define IMAGE_NEXT 4 +#define IMAGE_OPEN 5 + + +HWND WindowRebar = NULL; // extern +int iRebarHeight = 40; // extern + + +HMENU rebar_menu = NULL; + + +WNDPROC WndprocRebarBackup = NULL; +LRESULT CALLBACK WndprocRebar( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); + + +HWND WindowOrder = NULL; // extern +HWND WindowEq = NULL; // extern + + +HWND WindowSeek = NULL; // extern +WNDPROC WndprocSeekBackup = NULL; +LRESULT CALLBACK WndprocSeek( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); + +HWND WindowVol = NULL; +WNDPROC WndprocVolBackup = NULL; +LRESULT CALLBACK WndprocVol( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); + +HWND WindowPan = NULL; +WNDPROC WndprocPanBackup = NULL; +LRESULT CALLBACK WndprocPan( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); + +HWND WindowButtons = NULL; + +HWND WindowVis = NULL; // extern +WNDPROC WndprocVisBackup = NULL; +LRESULT CALLBACK WndprocVis( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); + + + + +bool Rebar_BuildOrderBand(); +bool Rebar_BuildEqBand(); +bool Rebar_BuildSeekBand(); +bool Rebar_BuildVolBand(); +bool Rebar_BuildPanBand(); +bool Rebar_BuildButtonsBand(); +bool Rebar_BuildVisBand(); + +BandInfo biOrderBand = { 0 }; +BandInfo biEqBand = { 0 }; +BandInfo biSeekBand = { 0 }; +BandInfo biVolBand = { 0 }; +BandInfo biPanBand = { 0 }; +BandInfo biButtonsBand = { 0 }; +BandInfo biVisBand = { 0 }; + +const BandInfo biOrderBandDefault = { 0, 184, true, true }; +const BandInfo biVolBandDefault = { 1, 450, false, true }; +const BandInfo biPanBandDefault = { 2, 138, false, true }; +const BandInfo biEqBandDefault = { 3, 170, true, true }; +const BandInfo biVisBandDefault = { 4, 350, false, true }; +const BandInfo biButtonsBandDefault = { 5, 147, true, true }; +const BandInfo biSeekBandDefault = { 6, 373, false, true }; + + +void BandCallback( ConfVar * var ); + +ConfBandInfoCallback cbiOrderBand ( &biOrderBand, TEXT( "OrderBand" ), ( BandInfo * )&biOrderBandDefault, BandCallback ); +ConfBandInfoCallback cbiEqBand ( &biEqBand, TEXT( "EqBand" ), ( BandInfo * )&biEqBandDefault, BandCallback ); +ConfBandInfoCallback cbiSeekBand ( &biSeekBand, TEXT( "SeekBand" ), ( BandInfo * )&biSeekBandDefault, BandCallback ); +ConfBandInfoCallback cbiVolBand ( &biVolBand, TEXT( "VolBand" ), ( BandInfo * )&biVolBandDefault, BandCallback ); +ConfBandInfoCallback cbiPanBand ( &biPanBand, TEXT( "PanBand" ), ( BandInfo * )&biPanBandDefault, BandCallback ); +ConfBandInfoCallback cbiButtonsBand( &biButtonsBand, TEXT( "ButtonsBand" ), ( BandInfo * )&biButtonsBandDefault, BandCallback ); +ConfBandInfoCallback cbiVisBand ( &biVisBand, TEXT( "VisBand" ), ( BandInfo * )&biVisBandDefault, BandCallback ); + +bool bInvertPanSlider; +ConfBool cbInvertPanSlider( &bInvertPanSlider, TEXT( "InvertPanSlider" ), CONF_MODE_PUBLIC, false ); + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void BandCallback( ConfVar * var ) +{ + BandInfo * band; + int id; + + if( var == &cbiOrderBand ) + { + band = &biOrderBand; + id = BAND_ORDER; + } + else if( var == &cbiEqBand ) + { + band = &biEqBand; + id = BAND_EQ; + } + else if( var == &cbiSeekBand ) + { + band = &biSeekBand; + id = BAND_SEEK; + } + else if( var == &cbiVolBand ) + { + band = &biVolBand; + id = BAND_VOL; + } + else if( var == &cbiPanBand ) + { + band = &biPanBand; + id = BAND_PAN; + } + else if( var == &cbiButtonsBand ) + { + band = &biButtonsBand; + id = BAND_BUTTONS; + } + else if( var == &cbiVisBand ) + { + band = &biVisBand; + id = BAND_VIS; + } + else + { + return; + } + + band->m_iIndex = ( int )SendMessage( WindowRebar, RB_IDTOINDEX, id, 0 ); + if( band->m_iIndex == -1 ) return; + + REBARBANDINFO rbbi; + rbbi.cbSize = sizeof( REBARBANDINFO ); + rbbi.fMask = RBBIM_SIZE | RBBIM_STYLE; + SendMessage( WindowRebar, RB_GETBANDINFO, band->m_iIndex, ( LPARAM )&rbbi ); + + band->m_iWidth = rbbi.cx; + band->m_bBreak = ( ( rbbi.fStyle & RBBS_BREAK ) == RBBS_BREAK ); + band->m_bVisible = ( ( rbbi.fStyle & RBBS_HIDDEN ) != RBBS_HIDDEN ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Toolbar::Create() +{ + LoadCommonControls(); + + WindowRebar = CreateWindowEx( + WS_EX_TOOLWINDOW | + WS_EX_LEFT | + WS_EX_LTRREADING | + WS_EX_RIGHTSCROLLBAR, + REBARCLASSNAME, + NULL, + WS_CHILD | + WS_VISIBLE | + WS_CLIPSIBLINGS | + WS_CLIPCHILDREN | + WS_BORDER | + RBS_VARHEIGHT | + RBS_BANDBORDERS | + RBS_DBLCLKTOGGLE | // RBS_AUTOSIZE + CCS_TOP | + CCS_NOPARENTALIGN | + CCS_NODIVIDER, // | CCS_ADJUSTABLE, + 0, + 0, + 0, + 0, + WindowMain, + NULL, + g_hInstance, + NULL + ); + +/* + #define STYLE 1 + #if STYLE == 1 + // Normal + MoveWindow( rebar, 0, 0, width, 30, TRUE ); + #elif STYLE == 2 + // Left ONLY + MoveWindow( rebar, 1, 0, width - 1, 30, TRUE ); + #endif +*/ + + REBARINFO rbi = { + sizeof( REBARINFO ), // UINT cbSize; + 0, // UINT fMask + NULL // HIMAGELIST himl + }; + + if( !SendMessage( WindowRebar, RB_SETBARINFO, 0, ( LPARAM )&rbi ) ) + { + return false; + } + + // Exchange window procedure + WndprocRebarBackup = ( WNDPROC )GetWindowLong( WindowRebar, GWL_WNDPROC ); + //WndprocRebarBackup = ( WNDPROC )GetWindowLongPtr( WindowRebar, GWLP_WNDPROC ); // 64 bit + if( WndprocRebarBackup != NULL ) + { + SetWindowLong( WindowRebar, GWL_WNDPROC, ( LONG )WndprocRebar ); + //SetWindowLongPtr( WindowRebar, GWLP_WNDPROC, ( LONG )WndprocRebar ); + } + + rebar_menu = CreatePopupMenu(); + AppendMenu( rebar_menu, MF_STRING, BAND_BUTTONS, TEXT( "Buttons" ) ); + AppendMenu( rebar_menu, MF_STRING, BAND_EQ, TEXT( "Equalizer" ) ); + AppendMenu( rebar_menu, MF_STRING, BAND_ORDER, TEXT( "Order" ) ); + AppendMenu( rebar_menu, MF_STRING, BAND_PAN, TEXT( "Panning" ) ); + AppendMenu( rebar_menu, MF_STRING, BAND_SEEK, TEXT( "Seekbar" ) ); + AppendMenu( rebar_menu, MF_STRING, BAND_VIS, TEXT( "Visualization" ) ); + AppendMenu( rebar_menu, MF_STRING, BAND_VOL, TEXT( "Volume" ) ); + + Rebar_BuildOrderBand(); + Rebar_BuildEqBand(); + Rebar_BuildSeekBand(); + Rebar_BuildVolBand(); + Rebar_BuildPanBand(); + Rebar_BuildButtonsBand(); + Rebar_BuildVisBand(); + + cbiOrderBand.Apply ( WindowRebar, BAND_ORDER ); + cbiEqBand.Apply ( WindowRebar, BAND_EQ ); + cbiSeekBand.Apply ( WindowRebar, BAND_SEEK ); + cbiVolBand.Apply ( WindowRebar, BAND_VOL ); + cbiPanBand.Apply ( WindowRebar, BAND_PAN ); + cbiButtonsBand.Apply( WindowRebar, BAND_BUTTONS ); + cbiVisBand.Apply ( WindowRebar, BAND_VIS ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Rebar_BuildOrderBand() +{ + if( !WindowRebar ) return false; + + WindowOrder = CreateWindowEx( + 0, + TEXT( "COMBOBOX" ), + TEXT( "" ), + CBS_HASSTRINGS | + CBS_DROPDOWNLIST | + WS_CHILD, + 0, + 0, + 133, + 200, + WindowRebar, + NULL, + g_hInstance, + NULL + ); + + if( !WindowOrder ) return false; + + // Add mode names + TCHAR * szName; + for( int i = ORDER_FIRST; i <= ORDER_LAST; i++ ) + { + szName = Playback::Order::GetModeName( i ); + SendMessage( WindowOrder, CB_ADDSTRING, 0, ( LPARAM )szName ); + } + + // Initial value + SendMessage( WindowOrder, CB_SETCURSEL, Playback::Order::GetCurMode(), 0 ); + + RECT rc; + GetWindowRect( WindowOrder, &rc ); + + REBARBANDINFO rbBand; + rbBand.cbSize = sizeof( REBARBANDINFO ); + rbBand.fMask = 0 | + // RBBIM_BACKGROUND | // The hbmBack member is valid or must be filled. + RBBIM_CHILD | // The hwndChild member is valid or must be filled. + RBBIM_CHILDSIZE | // The cxMinChild, cyMinChild, cyChild, cyMaxChild, and cyIntegral members are valid or must be filled. + // RBBIM_COLORS | // The clrFore and clrBack members are valid or must be filled. + // RBBIM_HEADERSIZE | // Version 4.71. The cxHeader member is valid or must be filled. + RBBIM_IDEALSIZE | // Version 4.71. The cxIdeal member is valid or must be filled. + RBBIM_ID | // The wID member is valid or must be filled. + // RBBIM_IMAGE | // The iImage member is valid or must be filled. + // RBBIM_LPARAM | // Version 4.71. The lParam member is valid or must be filled. + RBBIM_SIZE | // The cx member is valid or must be filled. + RBBIM_STYLE | // The fStyle member is valid or must be filled. + RBBIM_TEXT | // The lpText member is valid or must be filled. + 0; + + rbBand.fStyle = RBBS_GRIPPERALWAYS | + RBBS_CHILDEDGE | + ( biOrderBand.m_bBreak ? RBBS_BREAK : 0 ); + + rbBand.lpText = TEXT( " Order " ); + rbBand.hwndChild = WindowOrder; + rbBand.cxMinChild = rc.right - rc.left; + rbBand.cyMinChild = 21; // IMP + rbBand.cx = biOrderBand.m_iWidth; + rbBand.wID = BAND_ORDER; + rbBand.cyChild = 21; + rbBand.cyMaxChild = 21; + rbBand.cyIntegral = 1; + rbBand.cxIdeal = 200; + + // Add band + SendMessage( WindowRebar, RB_INSERTBAND, ( WPARAM )-1, ( LPARAM )&rbBand ); + + Font::Apply( WindowOrder ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool AddPreset( TCHAR * szPresetName ) +{ + const int iLen = _tcslen( szPresetName ); + TCHAR * szFinal = new TCHAR[ iLen + 2 + 1 ]; + szFinal[ 0 ] = TEXT( ' ' ); + szFinal[ iLen + 1 ] = TEXT( ' ' ); + szFinal[ iLen + 2 ] = TEXT( '\0' ); + memcpy( szFinal + 1, szPresetName, iLen * sizeof( TCHAR ) ); + + SendMessage( WindowEq, CB_ADDSTRING, 0, ( LPARAM )szFinal ); + + delete [] szFinal; + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Rebar_BuildEqBand() +{ + if( !WindowRebar ) return false; + + WindowEq = CreateWindowEx( + 0, + TEXT( "COMBOBOX" ), + TEXT( "" ), + CBS_HASSTRINGS | + CBS_DROPDOWNLIST | + WS_CHILD, + 0, + 0, + 133, + 300, + WindowRebar, + NULL, + g_hInstance, + NULL + ); + + if( !WindowEq ) return false; + + // Add preset names + SendMessage( WindowEq, CB_ADDSTRING, 0, ( LPARAM )TEXT( " Disabled " ) ); + Playback::Eq::ReadPresets( AddPreset ); + + // Initial value + SendMessage( WindowEq, CB_SETCURSEL, Playback::Eq::GetCurIndex() + 1, 0 ); + + // TODO Disabled + // EnableWindow( WindowEq, FALSE ); + + + RECT rc; + GetWindowRect( WindowEq, &rc ); + + REBARBANDINFO rbBand; + rbBand.cbSize = sizeof( REBARBANDINFO ); + rbBand.fMask = 0 | + // RBBIM_BACKGROUND | // The hbmBack member is valid or must be filled. + RBBIM_CHILD | // The hwndChild member is valid or must be filled. + RBBIM_CHILDSIZE | // The cxMinChild, cyMinChild, cyChild, cyMaxChild, and cyIntegral members are valid or must be filled. + // RBBIM_COLORS | // The clrFore and clrBack members are valid or must be filled. + // RBBIM_HEADERSIZE | // Version 4.71. The cxHeader member is valid or must be filled. + RBBIM_IDEALSIZE | // Version 4.71. The cxIdeal member is valid or must be filled. + RBBIM_ID | // The wID member is valid or must be filled. + // RBBIM_IMAGE | // The iImage member is valid or must be filled. + // RBBIM_LPARAM | // Version 4.71. The lParam member is valid or must be filled. + RBBIM_SIZE | // The cx member is valid or must be filled. + RBBIM_STYLE | // The fStyle member is valid or must be filled. + RBBIM_TEXT | // The lpText member is valid or must be filled. + 0; + + rbBand.fStyle = RBBS_GRIPPERALWAYS | + RBBS_CHILDEDGE | + ( biEqBand.m_bBreak ? RBBS_BREAK : 0 ); + + rbBand.lpText = TEXT( " EQ " ); + rbBand.hwndChild = WindowEq; + rbBand.cxMinChild = rc.right - rc.left; + rbBand.cyMinChild = 21; // IMP + rbBand.cx = biEqBand.m_iWidth; + rbBand.wID = BAND_EQ; + rbBand.cyChild = 21; + rbBand.cyMaxChild = 21; + rbBand.cyIntegral = 1; + rbBand.cxIdeal = 200; + + // Add band + SendMessage( WindowRebar, RB_INSERTBAND, ( WPARAM )-1, ( LPARAM )&rbBand ); + + Font::Apply( WindowEq ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Rebar_BuildSeekBand() +{ + if( !WindowRebar ) return false; + + WindowSeek = CreateWindowEx( + 0, + TRACKBAR_CLASS, + TEXT( "" ), + WS_CHILD | + WS_DISABLED | + TBS_HORZ | + TBS_NOTICKS | + TBS_FIXEDLENGTH | + TBS_BOTH, + 0, + 0, + 300, + 21, + WindowRebar, + NULL, + g_hInstance, + NULL + ); + + // Range + SendMessage( + WindowSeek, + TBM_SETRANGE, + TRUE, + ( LPARAM )MAKELONG( 0, 999 ) + ); + + // Thumb size + SendMessage( + WindowSeek, + TBM_SETTHUMBLENGTH, + 14, + 0 + ); + + // Exchange window procedure + WndprocSeekBackup = ( WNDPROC )GetWindowLong( WindowSeek, GWL_WNDPROC ); + if( WndprocSeekBackup != NULL ) + { + SetWindowLong( WindowSeek, GWL_WNDPROC, ( LONG )WndprocSeek ); + } + + + REBARBANDINFO rbbi_seek; + rbbi_seek.cbSize = sizeof( REBARBANDINFO ); + rbbi_seek.fMask = 0 | + // RBBIM_BACKGROUND | // The hbmBack member is valid or must be filled. + RBBIM_CHILD | // The hwndChild member is valid or must be filled. + RBBIM_CHILDSIZE | // The cxMinChild, cyMinChild, cyChild, cyMaxChild, and cyIntegral members are valid or must be filled. + // RBBIM_COLORS | // The clrFore and clrBack members are valid or must be filled. + // RBBIM_HEADERSIZE | // Version 4.71. The cxHeader member is valid or must be filled. + RBBIM_IDEALSIZE | // Version 4.71. The cxIdeal member is valid or must be filled. + RBBIM_ID | // The wID member is valid or must be filled. + // RBBIM_IMAGE | // The iImage member is valid or must be filled. + // RBBIM_LPARAM | // Version 4.71. The lParam member is valid or must be filled. + RBBIM_SIZE | // The cx member is valid or must be filled. + RBBIM_STYLE | // The fStyle member is valid or must be filled. + RBBIM_TEXT | // The lpText member is valid or must be filled. + 0; + + rbbi_seek.fStyle = RBBS_GRIPPERALWAYS | + RBBS_CHILDEDGE | + ( biSeekBand.m_bBreak ? RBBS_BREAK : 0 ); + + rbbi_seek.lpText = " Pos"; + rbbi_seek.hwndChild = WindowSeek; + rbbi_seek.cxMinChild = 100; + rbbi_seek.cyMinChild = 21; // IMP + rbbi_seek.cx = biSeekBand.m_iWidth; + rbbi_seek.wID = BAND_SEEK; + rbbi_seek.cyChild = 21; + rbbi_seek.cyMaxChild = 21; + rbbi_seek.cyIntegral = 1; + rbbi_seek.cxIdeal = 300; + + + // Add band + SendMessage( + WindowRebar, + RB_INSERTBAND, + ( WPARAM )-1, + ( LPARAM )&rbbi_seek + ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Rebar_BuildVolBand() +{ + if( !WindowRebar ) return false; + + WindowVol = CreateWindowEx( + 0, + TRACKBAR_CLASS, + TEXT( "" ), + WS_CHILD | + TBS_HORZ | + TBS_NOTICKS | + TBS_FIXEDLENGTH | + TBS_BOTH, + 0, + 0, + 200, + 21, + WindowRebar, + NULL, + g_hInstance, + NULL + ); + + if( !WindowVol ) return false; + + // Range + SendMessage( WindowVol, TBM_SETRANGE, TRUE, ( LPARAM )MAKELONG( 0, 255 ) ); + + // Initial value + SendMessage( WindowVol, TBM_SETPOS, TRUE, Playback::Volume::Get() ); + + // Thumbs size + SendMessage( WindowVol, TBM_SETTHUMBLENGTH, 14, 0 ); + + // Exchange window procedure + WndprocVolBackup = ( WNDPROC )GetWindowLong( WindowVol, GWL_WNDPROC ); + if( WndprocVolBackup != NULL ) + { + SetWindowLong( WindowVol, GWL_WNDPROC, ( LONG )WndprocVol ); + } + + + REBARBANDINFO rbbi_vol; + rbbi_vol.cbSize = sizeof( REBARBANDINFO ); + rbbi_vol.fMask = 0 | + // RBBIM_BACKGROUND | // The hbmBack member is valid or must be filled. + RBBIM_CHILD | // The hwndChild member is valid or must be filled. + RBBIM_CHILDSIZE | // The cxMinChild, cyMinChild, cyChild, cyMaxChild, and cyIntegral members are valid or must be filled. + // RBBIM_COLORS | // The clrFore and clrBack members are valid or must be filled. + // RBBIM_HEADERSIZE | // Version 4.71. The cxHeader member is valid or must be filled. + RBBIM_IDEALSIZE | // Version 4.71. The cxIdeal member is valid or must be filled. + RBBIM_ID | // The wID member is valid or must be filled. + // RBBIM_IMAGE | // The iImage member is valid or must be filled. + // RBBIM_LPARAM | // Version 4.71. The lParam member is valid or must be filled. + RBBIM_SIZE | // The cx member is valid or must be filled. + RBBIM_STYLE | // The fStyle member is valid or must be filled. + RBBIM_TEXT | // The lpText member is valid or must be filled. + 0; + + rbbi_vol.fStyle = RBBS_GRIPPERALWAYS | + RBBS_CHILDEDGE | + ( biVolBand.m_bBreak ? RBBS_BREAK : 0 ); + + rbbi_vol.lpText = TEXT( " Vol" ); + rbbi_vol.hwndChild = WindowVol; + rbbi_vol.cxMinChild = 100; + rbbi_vol.cyMinChild = 21; // IMP + rbbi_vol.cx = biVolBand.m_iWidth; + rbbi_vol.wID = BAND_VOL; + rbbi_vol.cyChild = 21; + rbbi_vol.cyMaxChild = 21; + rbbi_vol.cyIntegral = 1; + rbbi_vol.cxIdeal = 100; + + // Add band + SendMessage( WindowRebar, RB_INSERTBAND, ( WPARAM )-1, ( LPARAM )&rbbi_vol ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Rebar_BuildPanBand() +{ + if( !WindowRebar ) return false; + + WindowPan = CreateWindowEx( + 0, + TRACKBAR_CLASS, + TEXT( "" ), + WS_CHILD | + TBS_HORZ | + TBS_NOTICKS | + TBS_FIXEDLENGTH | + TBS_BOTH, + 0, + 0, + 200, + 21, + WindowRebar, + NULL, + g_hInstance, + NULL + ); + + if( !WindowPan ) return false; + + // Range + SendMessage( WindowPan, TBM_SETRANGE, TRUE, ( LPARAM )MAKELONG( -127, 127 ) ); + + // Initial value + const int iCurPan = Playback::Pan::Get(); + SendMessage( WindowPan, TBM_SETPOS, TRUE, bInvertPanSlider ? -iCurPan : iCurPan ); + + // Thumb size + SendMessage( WindowPan, TBM_SETTHUMBLENGTH, 14, 0 ); + + + // Exchange window procedure + WndprocPanBackup = ( WNDPROC )GetWindowLong( WindowPan, GWL_WNDPROC ); + if( WndprocPanBackup != NULL ) + { + SetWindowLong( WindowPan, GWL_WNDPROC, ( LONG )WndprocPan ); + } + + + REBARBANDINFO rbbi_pan; + rbbi_pan.cbSize = sizeof( REBARBANDINFO ); + rbbi_pan.fMask = 0 | + // RBBIM_BACKGROUND | // The hbmBack member is valid or must be filled. + RBBIM_CHILD | // The hwndChild member is valid or must be filled. + RBBIM_CHILDSIZE | // The cxMinChild, cyMinChild, cyChild, cyMaxChild, and cyIntegral members are valid or must be filled. + // RBBIM_COLORS | // The clrFore and clrBack members are valid or must be filled. + // RBBIM_HEADERSIZE | // Version 4.71. The cxHeader member is valid or must be filled. + RBBIM_IDEALSIZE | // Version 4.71. The cxIdeal member is valid or must be filled. + RBBIM_ID | // The wID member is valid or must be filled. + // RBBIM_IMAGE | // The iImage member is valid or must be filled. + // RBBIM_LPARAM | // Version 4.71. The lParam member is valid or must be filled. + RBBIM_SIZE | // The cx member is valid or must be filled. + RBBIM_STYLE | // The fStyle member is valid or must be filled. + RBBIM_TEXT | // The lpText member is valid or must be filled. + 0; + + rbbi_pan.fStyle = RBBS_GRIPPERALWAYS | + RBBS_CHILDEDGE | + ( biPanBand.m_bBreak ? RBBS_BREAK : 0 ); + + rbbi_pan.lpText = TEXT( " Pan" ); + rbbi_pan.hwndChild = WindowPan; + rbbi_pan.cxMinChild = 100; + rbbi_pan.cyMinChild = 21; // IMP + rbbi_pan.cx = biPanBand.m_iWidth; + rbbi_pan.wID = BAND_PAN; + rbbi_pan.cyChild = 21; + rbbi_pan.cyMaxChild = 21; + rbbi_pan.cyIntegral = 1; + rbbi_pan.cxIdeal = 100; + + // Add band + SendMessage( WindowRebar, RB_INSERTBAND, ( WPARAM )-1, ( LPARAM )&rbbi_pan ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Rebar_BuildButtonsBand() +{ + if( !WindowRebar ) return false; + + WindowButtons = CreateWindowEx( + 0, + TOOLBARCLASSNAME, + TEXT( "" ), + WS_CHILD | + TBSTYLE_FLAT | + CCS_NORESIZE | // Means we care about the size ourselves + CCS_NOPARENTALIGN | // Make it work with the rebar control + CCS_NODIVIDER, // No divider on top + 0, + 0, + 100, + 21, + WindowRebar, + NULL, + g_hInstance, + NULL + ); + + if( !WindowButtons ) return false; + + SendMessage( WindowButtons, TB_BUTTONSTRUCTSIZE, ( WPARAM )sizeof( TBBUTTON ), 0 ); + + TBBUTTON tb_button[ 7 ]; + + // Make image list, TODO delete later + HIMAGELIST hImages = ImageList_LoadBitmap( + GetModuleHandle( NULL ), // HINSTANCE hi + MAKEINTRESOURCE( IDB_BITMAP1 ), // LPCTSTR lpbmp + 14, // int cx + 6, // int cGrow + RGB( 255, 000, 255 ) // COLORREF crMask + ); + + SendMessage( WindowButtons, TB_SETIMAGELIST, 0, ( LPARAM )hImages ); + // SendMessage( WindowButtons, TB_SETHOTIMAGELIST, 0, ( LPARAM )hImages ); + // SendMessage( WindowButtons, TB_SETDISABLEDIMAGELIST, 0, ( LPARAM )hImages ); + + // Build buttons + tb_button[ 0 ].iBitmap = IMAGE_PREV; + tb_button[ 0 ].idCommand = WINAMP_BUTTON1; + tb_button[ 0 ].fsState = TBSTATE_ENABLED; + tb_button[ 0 ].fsStyle = TBSTYLE_BUTTON; + tb_button[ 0 ].dwData = 0; + tb_button[ 0 ].iString = SendMessage( WindowButtons, TB_ADDSTRING, 0, ( LPARAM )TEXT( "Previous" )); + + tb_button[ 1 ].iBitmap = IMAGE_PLAY; + tb_button[ 1 ].idCommand = WINAMP_BUTTON2; + tb_button[ 1 ].fsState = TBSTATE_ENABLED; + tb_button[ 1 ].fsStyle = TBSTYLE_BUTTON; + tb_button[ 1 ].dwData = 0; + tb_button[ 1 ].iString = SendMessage( WindowButtons, TB_ADDSTRING, 0, ( LPARAM )TEXT( "Play" )); + + tb_button[ 2 ].iBitmap = IMAGE_PAUSE; + tb_button[ 2 ].idCommand = WINAMP_BUTTON3; + tb_button[ 2 ].fsState = TBSTATE_ENABLED; + tb_button[ 2 ].fsStyle = TBSTYLE_BUTTON; + tb_button[ 2 ].dwData = 0; + tb_button[ 2 ].iString = SendMessage( WindowButtons, TB_ADDSTRING, 0, ( LPARAM )TEXT( "Pause" )); + + tb_button[ 3 ].iBitmap = IMAGE_STOP; + tb_button[ 3 ].idCommand = WINAMP_BUTTON4; + tb_button[ 3 ].fsState = TBSTATE_ENABLED; + tb_button[ 3 ].fsStyle = TBSTYLE_BUTTON; + tb_button[ 3 ].dwData = 0; + tb_button[ 3 ].iString = SendMessage( WindowButtons, TB_ADDSTRING, 0, ( LPARAM )TEXT( "Stop" )); + + tb_button[ 4 ].iBitmap = IMAGE_NEXT; + tb_button[ 4 ].idCommand = WINAMP_BUTTON5; + tb_button[ 4 ].fsState = TBSTATE_ENABLED; + tb_button[ 4 ].fsStyle = TBSTYLE_BUTTON; + tb_button[ 4 ].dwData = 0; + tb_button[ 4 ].iString = SendMessage( WindowButtons, TB_ADDSTRING, 0, ( LPARAM )TEXT( "Next" )); + + tb_button[ 5 ].iBitmap = 0; + tb_button[ 5 ].idCommand = -1; + tb_button[ 5 ].fsState = TBSTATE_ENABLED; + tb_button[ 5 ].fsStyle = TBSTYLE_SEP; + tb_button[ 5 ].dwData = 0; + tb_button[ 5 ].iString = 0; + + tb_button[ 6 ].iBitmap = IMAGE_OPEN; + tb_button[ 6 ].idCommand = WINAMP_FILE_PLAY; + tb_button[ 6 ].fsState = TBSTATE_ENABLED; + tb_button[ 6 ].fsStyle = TBSTYLE_BUTTON; + tb_button[ 6 ].dwData = 0; + tb_button[ 6 ].iString = SendMessage( WindowButtons, TB_ADDSTRING, 0, ( LPARAM )TEXT( "Open" )); + + // Add buttons + SendMessage( WindowButtons, TB_SETBUTTONSIZE, 0, MAKELONG( 14, 14 ) ); + SendMessage( WindowButtons, TB_ADDBUTTONS, 7, ( LPARAM )( LPTBBUTTON )&tb_button ); + + // Disable labels + SendMessage( WindowButtons, TB_SETMAXTEXTROWS, 0, 0 ); + + // Resize + RECT r; + GetWindowRect( WindowButtons, &r ); + SetWindowPos( WindowButtons, NULL, 0, 0, 134, r.bottom - r.top, SWP_NOMOVE ); + GetWindowRect( WindowButtons, &r ); + + REBARBANDINFO rbbi_buttons; + rbbi_buttons.cbSize = sizeof( REBARBANDINFO ); + rbbi_buttons.fMask = 0 | + // RBBIM_BACKGROUND | // The hbmBack member is valid or must be filled. + RBBIM_CHILD | // The hwndChild member is valid or must be filled. + RBBIM_CHILDSIZE | // The cxMinChild, cyMinChild, cyChild, cyMaxChild, and cyIntegral members are valid or must be filled. + // RBBIM_COLORS | // The clrFore and clrBack members are valid or must be filled. + // RBBIM_HEADERSIZE | // Version 4.71. The cxHeader member is valid or must be filled. + RBBIM_IDEALSIZE | // Version 4.71. The cxIdeal member is valid or must be filled. + RBBIM_ID | // The wID member is valid or must be filled. + // RBBIM_IMAGE | // The iImage member is valid or must be filled. + // RBBIM_LPARAM | // Version 4.71. The lParam member is valid or must be filled. + RBBIM_SIZE | // The cx member is valid or must be filled. + RBBIM_STYLE | // The fStyle member is valid or must be filled. + // RBBIM_TEXT | // The lpText member is valid or must be filled. + 0; + + rbbi_buttons.fStyle = RBBS_GRIPPERALWAYS | + RBBS_CHILDEDGE | + ( biButtonsBand.m_bBreak ? RBBS_BREAK : 0 ); + + // rbbi_buttons.lpText = TEXT( " Playback" ); + rbbi_buttons.hwndChild = WindowButtons; + rbbi_buttons.cxMinChild = r.right - r.left; + rbbi_buttons.cyMinChild = 21; // IMP + rbbi_buttons.cx = r.right - r.left; + rbbi_buttons.wID = BAND_BUTTONS; + rbbi_buttons.cyChild = 21; + rbbi_buttons.cyMaxChild = 21; + rbbi_buttons.cyIntegral = 1; + rbbi_buttons.cxIdeal = r.right - r.left; + + // Add band + SendMessage( WindowRebar, RB_INSERTBAND, ( WPARAM )-1, ( LPARAM )&rbbi_buttons ); + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool Rebar_BuildVisBand() +{ + if( !WindowRebar ) return false; + + WindowVis = CreateWindowEx( + WS_EX_STATICEDGE, + TEXT( "STATIC" ), + TEXT( "" ), + WS_CHILD | SS_CENTER, + 0, + 0, + 100, + 21, + WindowRebar, + NULL, + g_hInstance, + NULL + ); + + if( !WindowVis ) return false; + Font::Apply( WindowVis ); + + // Resize + RECT r; + GetWindowRect( WindowButtons, &r ); +// SetWindowPos( WindowButtons, NULL, 0, 0, 146, r.bottom - r.top, SWP_NOMOVE ); +// GetWindowRect( WindowButtons, &r ); + + REBARBANDINFO rbbi_buttons; + rbbi_buttons.cbSize = sizeof( REBARBANDINFO ); + rbbi_buttons.fMask = 0 | + // RBBIM_BACKGROUND | // The hbmBack member is valid or must be filled. + RBBIM_CHILD | // The hwndChild member is valid or must be filled. + RBBIM_CHILDSIZE | // The cxMinChild, cyMinChild, cyChild, cyMaxChild, and cyIntegral members are valid or must be filled. + // RBBIM_COLORS | // The clrFore and clrBack members are valid or must be filled. + // RBBIM_HEADERSIZE | // Version 4.71. The cxHeader member is valid or must be filled. + RBBIM_IDEALSIZE | // Version 4.71. The cxIdeal member is valid or must be filled. + RBBIM_ID | // The wID member is valid or must be filled. + // RBBIM_IMAGE | // The iImage member is valid or must be filled. + // RBBIM_LPARAM | // Version 4.71. The lParam member is valid or must be filled. + RBBIM_SIZE | // The cx member is valid or must be filled. + RBBIM_STYLE | // The fStyle member is valid or must be filled. + RBBIM_TEXT | // The lpText member is valid or must be filled. + 0; + + rbbi_buttons.fStyle = RBBS_GRIPPERALWAYS | + RBBS_CHILDEDGE | + ( biVisBand.m_bBreak ? RBBS_BREAK : 0 ); + + rbbi_buttons.lpText = TEXT( " Vis " ); + rbbi_buttons.hwndChild = WindowVis; + rbbi_buttons.cxMinChild = r.right - r.left; + rbbi_buttons.cyMinChild = 21; // IMP + rbbi_buttons.cx = r.right - r.left; + rbbi_buttons.wID = BAND_VIS; + rbbi_buttons.cyChild = 21; + rbbi_buttons.cyMaxChild = 21; + rbbi_buttons.cyIntegral = 1; + rbbi_buttons.cxIdeal = r.right - r.left; + + // Add band + SendMessage( WindowRebar, RB_INSERTBAND, ( WPARAM )-1, ( LPARAM )&rbbi_buttons ); + + // Exchange window procedure + WndprocVisBackup = ( WNDPROC )GetWindowLong( WindowVis, GWL_WNDPROC ); + if( WndprocVisBackup != NULL ) + { + SetWindowLong( WindowVis, GWL_WNDPROC, ( LONG )WndprocVis ); + } + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ContextMenuRebar( POINT * p ) +{ + REBARBANDINFO rbbi; + rbbi.cbSize = sizeof( REBARBANDINFO ); + rbbi.fMask = RBBIM_STYLE; + + bool bBandVisible[ BAND_LAST - BAND_FIRST + 1 ] = { 0 }; // ID to visibility + int iBandIndex [ BAND_LAST - BAND_FIRST + 1 ] = { 0 }; // ID to Index + + for( int i = BAND_FIRST; i <= BAND_LAST; i++ ) + { + const int iArrayIndex = i - BAND_FIRST; + + // Get index + iBandIndex[ iArrayIndex ] = ( int )SendMessage( WindowRebar, RB_IDTOINDEX, i, 0 ); + if( iBandIndex[ iArrayIndex ] == -1 ) break; + + // Get info + if( !SendMessage( WindowRebar, RB_GETBANDINFO, iBandIndex[ iArrayIndex ], ( LPARAM )&rbbi ) ) + { + break; + } + + bBandVisible[ iArrayIndex ] = ( ( rbbi.fStyle & RBBS_HIDDEN ) != RBBS_HIDDEN ); + + // Update menu item + CheckMenuItem( + rebar_menu, + i, + bBandVisible[ iArrayIndex ] ? MF_CHECKED : MF_UNCHECKED + ); + } + + BOOL iIndex = TrackPopupMenu( + rebar_menu, + TPM_LEFTALIGN | + TPM_TOPALIGN | + TPM_NONOTIFY | + TPM_RETURNCMD | + TPM_RIGHTBUTTON, + p->x, + p->y, + 0, + WindowRebar, + NULL + ); + + // Toggle visibility + if( ( iIndex >= BAND_FIRST ) && ( iIndex <= BAND_LAST ) ) + { + const int iArrayIndex = iIndex - BAND_FIRST; + SendMessage( WindowRebar, RB_SHOWBAND, iBandIndex[ iArrayIndex ], bBandVisible[ iArrayIndex ] ? FALSE : TRUE ); + + // Turn off vis child + if( iIndex == BAND_VIS ) + { + HWND hChild = GetWindow( WindowVis, GW_CHILD ); + if( IsWindow( hChild ) ) + { + SendMessage( hChild, WM_DESTROY, 0, 0 ); + } + } + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +LRESULT CALLBACK WndprocRebar( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + //Console::Append( TEXT("Rebar.cpp: WndprocRebar called") ); + + switch( message ) + { + case WM_CONTEXTMENU: // This affects the placement of the progress bar + { + //POINT p; + //GetCursorPos( &p ); + //ContextMenuRebar( &p ); + return 0; + } + + case WM_DESTROY: + cbiOrderBand.TriggerCallback(); + cbiOrderBand.RemoveCallback(); + cbiEqBand.TriggerCallback(); + cbiEqBand.RemoveCallback(); + cbiSeekBand.TriggerCallback(); + cbiSeekBand.RemoveCallback(); + cbiVolBand.TriggerCallback(); + cbiVolBand.RemoveCallback(); + cbiPanBand.TriggerCallback(); + cbiPanBand.RemoveCallback(); + cbiButtonsBand.TriggerCallback(); + cbiButtonsBand.RemoveCallback(); + cbiVisBand.TriggerCallback(); + cbiVisBand.RemoveCallback(); + break; + + } + return CallWindowProc( WndprocRebarBackup, hwnd, message, wp, lp ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +LRESULT CALLBACK WndprocSeek( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + static bool bDown = false; + static int iLastVal = 0; + + //Console::Append( TEXT("Rebar.cpp: WndprocSeek called") ); + + switch( message ) + { + case WM_LBUTTONDOWN: + { + SetFocus( hwnd ); + + // Capture mouse + SetCapture( hwnd ); + bDown = true; + + // NO BREAK! + } + case WM_MOUSEMOVE: + { + if( !bDown ) break; + if( !Playback::IsPlaying() ) return 0; // Deny slider move + + RECT r; + GetWindowRect( hwnd, &r ); + + const int iWidth = r.right - r.left; + if( !iWidth ) return 0; + int iVal = 1000 * MAX( 0, MIN( ( short )LOWORD( lp ), iWidth ) ) / iWidth; + + // Snap + if( ( short )LOWORD( lp ) < 7 ) + { + iVal = 0; + } + else if( abs( iWidth - ( short )LOWORD( lp ) ) < 7 ) + { + iVal = 999; + } + + // Update slider + PostMessage( hwnd, TBM_SETPOS, ( WPARAM )( TRUE ), iVal ); + + // Seek + iLastVal = iVal; + Playback::SeekPercent( iLastVal / 10.0f ); + + // TODO: Seek on slide ON/OFF + + + return 0; + } + case WM_LBUTTONUP: + case WM_NCLBUTTONUP: + { + // Release capture + bDown = false; + ReleaseCapture(); + + if( !Playback::IsPlaying() ) return 0; + + int ms = Playback::PercentToMs( iLastVal / 10.0f ); + if( ms < 0 ) break; + + int h = ms / 3600000; + int m = ( ms / 60000 ) % 60; + int s = ( ms / 1000 ) % 60; + ms = ms % 1000; + + + TCHAR szBuffer[ 5000 ]; + + /* + bool bShowMs = false; + + if( h ) + { + if( bShowMs ) + { + */ + + // 00:00:00.000 + _stprintf( szBuffer, TEXT( "Jumped to %02i:%02i:%02i.%03i" ), h, m, s, ms ); + Console::Append( szBuffer ); + Console::Append( " " ); + + /* + + } + else + { + // 00:00:00 + } + } + else if( m ) + { + if( bShowMs ) + { + // 00:00.000 + } + else + { + // 00:00 + } + } + else + { + if( bShowMs ) + { + // 00.000 + } + else + { + // 00s + } + } + */ + + return 0; + } + + case WM_KEYDOWN: + case WM_KEYUP: + SendMessage( WindowPlaylist, message, wp, lp ); + return 0; + + } + return CallWindowProc( WndprocSeekBackup, hwnd, message, wp, lp ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +LRESULT CALLBACK WndprocVol( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + static bool bDown = false; + static int iLastVal = 0; + + switch( message ) + { + case WM_LBUTTONDOWN: + SetFocus( hwnd ); + + // Capture mouse + SetCapture( hwnd ); + bDown = true; + + // NO BREAK! + + case WM_MOUSEMOVE: + { + if( !bDown ) break; + + RECT r; + GetWindowRect( hwnd, &r ); + + const int iWidth = r.right - r.left; + if( !iWidth ) return 0; + const int x = ( int )( short )LOWORD( lp ); + int iVal = 255 * x / ( int )iWidth; + + if( iVal < 0 ) + iVal = 0; + else if( ( iVal > 255 ) || ( abs( iWidth - x ) < 7 ) ) // Snap + iVal = 255; + + // Update slider + PostMessage( hwnd, TBM_SETPOS , ( WPARAM )( TRUE ), iVal ); + + // Apply volume + iLastVal = iVal; + Playback::Volume::Set( iLastVal ); + + return 0; + } + case WM_LBUTTONUP: + case WM_NCLBUTTONUP: + { + // Release capture + bDown = false; + ReleaseCapture(); + + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "Volume changed to %i%%" ), iLastVal * 100 / 255 ); + Console::Append( szBuffer ); + Console::Append( " " ); + + return 0; + } + } + return CallWindowProc( WndprocVolBackup, hwnd, message, wp, lp ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +LRESULT CALLBACK WndprocPan( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + static bool bDown = false; + static int iLastVal = 0; + + switch( message ) + { + case WM_LBUTTONDOWN: + SetFocus( hwnd ); + + // Capture mouse + SetCapture( hwnd ); + bDown = true; + + // NO BREAK! + + case WM_MOUSEMOVE: + { + if( !bDown ) break; + + RECT r; + GetWindowRect( hwnd, &r ); + + const int iWidth = r.right - r.left; + if( !iWidth ) return 0; + const int x = ( int )( short )LOWORD( lp ); + int iVal = 254 * x / ( int )iWidth - 127; + + if( iVal < -127 ) + iVal = -127; + else if( iVal > 127 ) + iVal = 127; + else if( abs( ( int )iWidth / 2 - x ) < 7 ) // Snap + iVal = 0; + + // Update slider + PostMessage( hwnd, TBM_SETPOS , ( WPARAM )( TRUE ), iVal ); + + // Invert if needed (slider needs original value!) + if( bInvertPanSlider ) + { + iVal = -iVal; + } + + // Apply panning + iLastVal = iVal; + Playback::Pan::Set( iVal ); + + return 0; + } + case WM_LBUTTONUP: + case WM_NCLBUTTONUP: + { + // Release capture + bDown = false; + ReleaseCapture(); + + TCHAR szBuffer[ 5000 ]; + if( iLastVal < 0 ) + _stprintf( szBuffer, TEXT( "Panning changed to %i%% left" ), -iLastVal * 100 / 127 ); + else if( iLastVal == 0 ) + _stprintf( szBuffer, TEXT( "Panning changed to center" ) ); + else // if( iLastVal > 0 ) + _stprintf( szBuffer, TEXT( "Panning changed to %i%% right" ), iLastVal * 100 / 127 ); + Console::Append( szBuffer ); + Console::Append( " " ); + + return 0; + } + } + return CallWindowProc( WndprocPanBackup, hwnd, message, wp, lp ); +} + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +LRESULT CALLBACK WndprocVis( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + switch( message ) + { + case WM_SHOWWINDOW: + { + if( wp == FALSE ) + { + const HWND hChild = GetWindow( hwnd, GW_CHILD ); + if( !IsWindow( hChild ) ) break; + + // Strange workaround + ShowWindow( hChild, FALSE ); + SetParent( hChild, NULL ); + PostMessage( hChild, WM_SYSCOMMAND, SC_CLOSE, 0 ); + } + } + break; + + case WM_SIZE: + { + // Resize vis child + HWND hChild = GetWindow( hwnd, GW_CHILD ); + if( !IsWindow( hChild ) ) break; + MoveWindow( hChild, 0, 0, LOWORD( lp ), HIWORD( lp ), TRUE ); + } + break; + } + return CallWindowProc( WndprocVisBackup, hwnd, message, wp, lp ); +} + + +///////////////////////// + + + + /* + // Create the toolbar control to be added. + // hwndTB = CreateToolbar(hwndOwner, dwStyle); + + // Get the height of the toolbar. + dwBtnSize = SendMessage(hwndTB, TB_GETBUTTONSIZE, 0,0); + + // Set values unique to the band with the toolbar. + rbBand.lpText = "Tool Bar"; + rbBand.hwndChild = hwndTB; + rbBand.cxMinChild = 0; + rbBand.cyMinChild = HIWORD(dwBtnSize); + rbBand.cx = 250; + + // Add the band that has the toolbar. + SendMessage(rebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand); + */ + + +/////////////////////////////////////// + +/* +void band_dummy() +{ + REBARBANDINFO rbbi3; + rbbi3.cbSize = sizeof( REBARBANDINFO ); + rbbi3.fMask = 0 | + // RBBIM_BACKGROUND | // The hbmBack member is valid or must be filled. + // RBBIM_CHILD | // The hwndChild member is valid or must be filled. + // RBBIM_CHILDSIZE | // The cxMinChild, cyMinChild, cyChild, cyMaxChild, and cyIntegral members are valid or must be filled. + // RBBIM_COLORS | // The clrFore and clrBack members are valid or must be filled. + // RBBIM_HEADERSIZE | // Version 4.71. The cxHeader member is valid or must be filled. + RBBIM_IDEALSIZE | // Version 4.71. The cxIdeal member is valid or must be filled. + RBBIM_ID | // The wID member is valid or must be filled. + // RBBIM_IMAGE | // The iImage member is valid or must be filled. + // RBBIM_LPARAM | // Version 4.71. The lParam member is valid or must be filled. + RBBIM_SIZE | // The cx member is valid or must be filled. + RBBIM_STYLE | // The fStyle member is valid or must be filled. + RBBIM_TEXT | // The lpText member is valid or must be filled. + 0; + + rbbi3.fStyle = RBBS_GRIPPERALWAYS; // | RBBS_BREAK; + rbbi3.lpText = " "; //NULL; // "Dummy"; + rbbi3.hwndChild = NULL; + rbbi3.cxMinChild = 10; //rc.right - rc.left; + rbbi3.cyMinChild = 21; // IMP + rbbi3.cx = 300; + rbbi3.wID = BAND_ID_DUMMY; + rbbi3.cyChild = 21; //rc.bottom - rc.top; + rbbi3.cyMaxChild = 21; // rc.bottom - rc.top; + rbbi3.cyIntegral = 1; + rbbi3.cxIdeal = 700; //rc.right - rc.left; + + // Add the band that has the combo box. + SendMessage( + rebar, + RB_INSERTBAND, + ( WPARAM )-1, + ( LPARAM )&rbbi3 + ); +} +*/ diff --git a/Externals/MusicMod/Player/Src/Rebar.h b/Externals/MusicMod/Player/Src/Rebar.h new file mode 100644 index 0000000000..e2f1feb685 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Rebar.h @@ -0,0 +1,40 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_TOOLBAR_H +#define PA_TOOLBAR_H + + + +#include "Global.h" + + + +extern HWND WindowRebar; +extern HWND WindowOrder; +extern HWND WindowEq; +extern HWND WindowSeek; + +extern HWND WindowVis; + +extern int iRebarHeight; + + + +namespace Toolbar +{ + bool Create(); +}; + + + +#endif // PA_TOOLBAR_H diff --git a/Externals/MusicMod/Player/Src/Resources/Buttons.bmp b/Externals/MusicMod/Player/Src/Resources/Buttons.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3fb32f69cd10e66d25c5c4fd53e3403728275f08 GIT binary patch literal 734 zcmb7>%Mrpb3xTGK2Sg!2(6fXPkbgGW|An9tXLaKcFvdQJ}h_q z3At;Z!1OQ z8HJ)!1A-)Pb|eXCo3SJW8SYqMwtsb1!>r(*jlm_5374~1qHs}rGJqhWMfxcblcb$Uk`YE literal 0 HcmV?d00001 diff --git a/Externals/MusicMod/Player/Src/Resources/Plainamp.ico b/Externals/MusicMod/Player/Src/Resources/Plainamp.ico new file mode 100644 index 0000000000000000000000000000000000000000..dbe09766afe091d1dde4871fb34d0b8724067750 GIT binary patch literal 38638 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 K0`1IogZv+)oi|Vb literal 0 HcmV?d00001 diff --git a/Externals/MusicMod/Player/Src/Resources/resrc1.aps b/Externals/MusicMod/Player/Src/Resources/resrc1.aps new file mode 100644 index 0000000000000000000000000000000000000000..9270c80c07c5789b4c8d4cf18b103219956ed014 GIT binary patch literal 73968 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≷!*| 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 +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Status.h" +#include "Main.h" +#include "Util.h" +#include "GlobalVersion.h" + + + +int iStatusHeight = 40; // extern +HWND WindowStatus = NULL; // extern + +const TCHAR * const szStatusDefault = TEXT( " " ) PLAINAMP_LONG_TITLE; + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool BuildMainStatus() +{ + LoadCommonControls(); + + WindowStatus = CreateWindowEx( + 0, + STATUSCLASSNAME, + szStatusDefault, + WS_CHILD | + WS_VISIBLE, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + WindowMain, + NULL, + g_hInstance, + NULL + ); + + if( !WindowStatus ) return false; + + RECT r = { 0, 0, 0, 0 }; + GetWindowRect( WindowStatus, &r ); + iStatusHeight = r.bottom - r.top; + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool StatusUpdate( TCHAR * szText ) +{ + if( !WindowStatus ) return false; + SetWindowText( WindowStatus, szText ); + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void StatusReset() +{ + if( !WindowStatus ) return; + SetWindowText( WindowStatus, szStatusDefault ); +} diff --git a/Externals/MusicMod/Player/Src/Status.h b/Externals/MusicMod/Player/Src/Status.h new file mode 100644 index 0000000000..08e12d3527 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Status.h @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_STATUS_H +#define PA_STATUS_H + + + +#include "Global.h" + + + +extern int iStatusHeight; +extern HWND WindowStatus; + + + +bool BuildMainStatus(); +bool StatusUpdate( TCHAR * szText ); +void StatusReset(); + + + +#endif // PA_STATUS_H diff --git a/Externals/MusicMod/Player/Src/Timer.cpp b/Externals/MusicMod/Player/Src/Timer.cpp new file mode 100644 index 0000000000..ca6aa21526 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Timer.cpp @@ -0,0 +1,126 @@ +// ================================================================================================ +// File description +// ---------------- +/* In the GUI build there is a timer that is initiated with SetTimer() that will setup a timer that + calls the lpTimerFunc function pointer as long as your program is running a message loop. If it + doesn't run one, like in a console application (the NOGUI build), you'll have to use a different + kind of timer API, like timeSetEvent() or CreateThreadPoolTimer(). These timers use a different + thread to make the callback so be careful to properly lock and synchronize. */ +// ================================================================================================ + +// ================================================================================================ +// Library requirements +// ---------------- +// This program needs winmm.lib. There's no simpler or better way to make a timer withouth it. +// ================================================================================================ + +// ================================================================================================ +// Includes +// ---------------- +#include +//using namespace std; +#include +#include +#include "Global.h" +#include "PlayerExport.h" + +#include "InputPlugin.h" +// ================================================================================================ + + +//////////////////////////////////////////////////////////////////////////////////////////// +// Declarations and definitions +// ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ + +void MakeTime(); + +int g_Stop = 0; +extern std::string CurrentlyPlayingFile; +extern bool GlobalPause; +/////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////// +// Manage restart when playback for a file has reached the end of the file +// ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ +#ifdef _M_X64 + void CALLBACK Update() +#else + void CALLBACK Update(unsigned int,unsigned int,unsigned long,unsigned long,unsigned long) +#endif +{ + //INFO_LOG(AUDIO,"DLL > Update() > Begin (%i)\n", active_input_plugin); + + // -------------------------------- + // Manage restart when playback for a file has reached the end of the file + // -------------- + // Check if the input plugin is activated + if(!active_input_plugin || !active_input_plugin->plugin) + { + //INFO_LOG(AUDIO,"The input plugin is not activated yet\n"); + } + else + { + const int ms_len = active_input_plugin->plugin->GetLength(); + const int ms_cur = active_input_plugin->plugin->GetOutputTime(); + + // Get the current playback progress + float progress; + if(ms_len > 0) + progress = (float)ms_cur / ms_len; + else + progress = 0; + + if ( progress > 0.7 ) // Only show this if we are getting close to the end, for bugtesting + // basically + { + //INFO_LOG(AUDIO,"Playback progress <%i of %i>\n", ms_cur, ms_len); + } + + // Because cur never go all the way to len we can't use a == comparison. Insted of this + // we could also check if the location is the same as right before. + if(ms_cur > ms_len - 1000 && !GlobalPause) // avoid restarting in cases where we just pressed pause + { + INFO_LOG(AUDIO,"Restart <%s>\n", CurrentlyPlayingFile.c_str()); + Player_Play((char *)CurrentlyPlayingFile.c_str()); + } + } + // -------------- + + //INFO_LOG(AUDIO,"Make new time\n"); + MakeTime(); // Make a new one +} + +void MakeTime() +{ + timeSetEvent( + 2000, // Interval in ms + 0, + #ifdef _M_X64 + (LPTIMECALLBACK) Update, // The function + #else + Update, + #endif + 0, + 0); +} +/////////////////////////// + + + +//////////////////////////////////////////////////////////////////////////////////////////// +// Start the timer +// ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ +int MainTimer() +{ + MakeTime(); + + //while( g_Stop == 0 ) + //{ + // cout << "."; + //} + + //INFO_LOG(AUDIO,"MakeTime\n"); + + return 0; +} +/////////////////////////// diff --git a/Externals/MusicMod/Player/Src/Unicode.cpp b/Externals/MusicMod/Player/Src/Unicode.cpp new file mode 100644 index 0000000000..820895d93e --- /dev/null +++ b/Externals/MusicMod/Player/Src/Unicode.cpp @@ -0,0 +1,77 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Unicode.h" + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ToAnsi( char * szDest, wchar_t * szSource, int iLen ) +{ + char * const szBytesource = ( char * )szSource; + for( int i = 0; i < iLen; i++ ) + { + szDest[ i ] = szBytesource[ 2 * i + 1 ]; + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +/* +void ToUnicode( wchar_t * szDest, char * szSource, int iLen ) +{ + for( int i = 0; i < iLen; i++ ) + { + szDest[ i ] = ( wchar_t )szSource[ i ]; + } +} +*/ + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ToTchar( TCHAR * szDest, wchar_t * szSource, int iLen ) +{ +#ifdef PA_UNICODE + memcpy( szDest, szSource, 2 * iLen ); +#else + char * const stByteSource = ( TCHAR * )szSource; + for( int i = 0; i < iLen; i++ ) + { + szDest[ i ] = stByteSource[ 2 * i + 1 ]; + } +#endif +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void ToTchar( TCHAR * szDest, char * szSource, int iLen ) +{ +#ifdef PA_UNICODE + for( int i = 0; i < iLen; i++ ) + { + szDest[ i ] = szSource[ 2 * i + 1 ]; + } +#else + memcpy( szDest, szSource, iLen ); +#endif +} diff --git a/Externals/MusicMod/Player/Src/Unicode.h b/Externals/MusicMod/Player/Src/Unicode.h new file mode 100644 index 0000000000..10a53e873c --- /dev/null +++ b/Externals/MusicMod/Player/Src/Unicode.h @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_UNICODE_H +#define PA_UNICODE_H + + + +#include "Global.h" + + + +void ToAnsi( char * szDest, wchar_t * szSource, int iLen ); +// void ToUnicode( wchar_t * szDest, char * szSource, int iLen ) +void ToTchar( TCHAR * szDest, wchar_t * szSource, int iLen ); +void ToTchar( TCHAR * szDest, char * szSource, int iLen ); + + + +#endif // PA_UNICODE_H diff --git a/Externals/MusicMod/Player/Src/Util.cpp b/Externals/MusicMod/Player/Src/Util.cpp new file mode 100644 index 0000000000..26c5deb3dc --- /dev/null +++ b/Externals/MusicMod/Player/Src/Util.cpp @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Util.h" + + + +#ifndef ICC_STANDARD_CLASSES +# define ICC_STANDARD_CLASSES 0x00004000 +#endif + + + +bool bLoaded = false; +bool bAvailable = false; + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool LoadCommonControls() +{ + if( bLoaded ) return bAvailable; + + INITCOMMONCONTROLSEX icce = { + sizeof( INITCOMMONCONTROLSEX ), + ICC_BAR_CLASSES | // Statusbar, trackbar, toolbar + ICC_COOL_CLASSES | // Rebar + ICC_LISTVIEW_CLASSES | // Listview + ICC_STANDARD_CLASSES | // + ICC_TREEVIEW_CLASSES // Treeview + }; + + bLoaded = true; + bAvailable = ( InitCommonControlsEx( &icce ) == TRUE ); + + return bAvailable; +} diff --git a/Externals/MusicMod/Player/Src/Util.h b/Externals/MusicMod/Player/Src/Util.h new file mode 100644 index 0000000000..5bee2a9d2c --- /dev/null +++ b/Externals/MusicMod/Player/Src/Util.h @@ -0,0 +1,26 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_UTIL_H +#define PA_UTIL_H + + + +#include "Global.h" + + + +bool LoadCommonControls(); + + + +#endif // PA_UTIL_H diff --git a/Externals/MusicMod/Player/Src/VisCache.cpp b/Externals/MusicMod/Player/Src/VisCache.cpp new file mode 100644 index 0000000000..37d5d1497b --- /dev/null +++ b/Externals/MusicMod/Player/Src/VisCache.cpp @@ -0,0 +1,303 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "VisCache.h" +#include "Console.h" +#include "malloc.h" + + + +unsigned char * SpecCacheLeft; +unsigned char * SpecCacheRight; +unsigned char * WaveCacheLeft; +unsigned char * WaveCacheRight; + +int iWritePos; +int iWriteOffset; // == iWritePos * 576 + +int iVisLatency; +int iDataFps; +int iCacheLen; + +bool bReady = false; + +int iReadTimeMs = 0; +int iWriteTimeMs = 0; + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache_Resize( int iLatency, int iFps ) +{ + // if( !bReady ) return; + + const int iNewCacheLen = ( iFps * iLatency ) / 1000 + 1; +/* +TCHAR szBuffer[ 5000 ]; +_stprintf( szBuffer, TEXT( "RESIZE ( %i * %i ) / 1000 + 1 === %i" ), iFps, iLatency, iNewCacheLen ); +Console::Append( szBuffer ); +*/ + const int iByteNewCacheLen = iNewCacheLen * 576; + if( !iCacheLen ) + { + // First time + SpecCacheLeft = ( unsigned char * )malloc( iByteNewCacheLen ); +// memset( SpecCacheLeft, 0, iByteNewCacheLen ); + SpecCacheRight = ( unsigned char * )malloc( iByteNewCacheLen ); +// memset( SpecCacheRight, 0, iByteNewCacheLen ); + WaveCacheLeft = ( unsigned char * )malloc( iByteNewCacheLen ); +// memset( WaveCacheLeft, 0, iByteNewCacheLen ); + WaveCacheRight = ( unsigned char * )malloc( iByteNewCacheLen ); +// memset( WaveCacheRight, 0, iByteNewCacheLen ); + } + else if( iNewCacheLen > iCacheLen ) + { + // Grow + const int iByteCacheLen = iCacheLen * 576; + const int iByteClearLen = ( iNewCacheLen - iCacheLen ) * 576; + + SpecCacheLeft = ( unsigned char * )realloc( SpecCacheLeft, iByteNewCacheLen ); +// memset( SpecCacheLeft + iByteCacheLen, 0, iByteClearLen ); + SpecCacheRight = ( unsigned char * )realloc( SpecCacheRight, iByteNewCacheLen ); +// memset( SpecCacheRight + iByteCacheLen, 0, iByteClearLen ); + WaveCacheLeft = ( unsigned char * )realloc( WaveCacheLeft, iByteNewCacheLen ); +// memset( WaveCacheLeft + iByteCacheLen, 0, iByteClearLen ); + WaveCacheRight = ( unsigned char * )realloc( WaveCacheRight, iByteNewCacheLen ); +// memset( WaveCacheRight + iByteCacheLen, 0, iByteClearLen ); + } + + iCacheLen = iNewCacheLen; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache::Create() +{ + if( bReady ) return; + + iWritePos = 0; + iWriteOffset = 0; + + iVisLatency = 50; + iDataFps = 40; + iCacheLen = 0; + + bReady = true; + + + VisCache_Resize( iVisLatency, iDataFps ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache::Destroy() +{ + if( !bReady ) return; + + if( SpecCacheLeft ) free( SpecCacheLeft ); + if( SpecCacheRight ) free( SpecCacheRight ); + if( WaveCacheLeft ) free( WaveCacheLeft ); + if( WaveCacheRight ) free( WaveCacheRight ); + + bReady = false; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache::EnsureLatency( int iLatency ) +{ + if( !bReady ) return; + + if( iLatency <= iVisLatency ) return; + + VisCache_Resize( + iLatency, + iDataFps + ); + iVisLatency = iLatency; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache::EnsureDataFps( int iFps ) +{ + if( !bReady ) return; + if( iFps <= iDataFps ) return; + + VisCache_Resize( + iVisLatency, + iFps + ); + iDataFps = iFps; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache::Clean() +{ + if( !bReady ) return; + + const int iByteCacheLen = iCacheLen * 576; + memset( SpecCacheLeft, 0, iByteCacheLen ); + memset( SpecCacheRight, 0, iByteCacheLen ); + memset( WaveCacheLeft, 0, iByteCacheLen ); + memset( WaveCacheRight, 0, iByteCacheLen ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache::SetReadTime( int ms ) +{ + iReadTimeMs = ms; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache::SetWriteTime( int ms ) +{ + iWriteTimeMs = ms; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int VisCache::LatencyToOffset( int iLatency ) +{ + int iFrame = iWritePos - 1 - ( ( iWriteTimeMs - iReadTimeMs - iLatency ) * iDataFps ) / 1000; + if( iFrame < 0 ) iFrame += iCacheLen; + return iFrame * 576; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache::NextFrame() +{ + iWritePos++; + if( iWritePos >= iCacheLen ) iWritePos = 0; + iWriteOffset = iWritePos * 576; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache::PutSpecLeft( unsigned char * data ) +{ + if( !bReady ) return; + memcpy( SpecCacheLeft + iWriteOffset, data, 576 ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache::PutSpecRight( unsigned char * data ) +{ + if( !bReady ) return; + memcpy( SpecCacheRight + iWriteOffset, data, 576 ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache::PutWaveLeft( unsigned char * data ) +{ + if( !bReady ) return; + memcpy( WaveCacheLeft + iWriteOffset, data, 576 ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache::PutWaveRight( unsigned char * data ) +{ + if( !bReady ) return; + memcpy( WaveCacheRight + iWriteOffset, data, 576 ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache::GetSpecLeft( unsigned char * dest, int iOffset ) +{ + if( !bReady ) return; + memcpy( dest, SpecCacheLeft + iOffset, 576 ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache::GetSpecRight( unsigned char * dest, int iOffset ) +{ + if( !bReady ) return; + memcpy( dest, SpecCacheRight + iOffset, 576 ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache::GetWaveLeft( unsigned char * dest, int iOffset ) +{ + if( !bReady ) return; + memcpy( dest, WaveCacheLeft + iOffset, 576 ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void VisCache::GetWaveRight( unsigned char * dest, int iOffset ) +{ + if( !bReady ) return; + memcpy( dest, WaveCacheRight + iOffset, 576 ); +} diff --git a/Externals/MusicMod/Player/Src/VisCache.h b/Externals/MusicMod/Player/Src/VisCache.h new file mode 100644 index 0000000000..f093d5d089 --- /dev/null +++ b/Externals/MusicMod/Player/Src/VisCache.h @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_VIS_CACHE_H +#define PA_VIS_CACHE_H + + +#include "Global.h" + + + +namespace VisCache +{ + void Create(); + void Destroy(); + + void EnsureLatency( int iLatency ); // TODO + void EnsureDataFps( int iFps ); // TODO + + void Clean(); + + void SetReadTime( int ms ); + void SetWriteTime( int ms ); + + int LatencyToOffset( int iLatency ); + void NextFrame(); + + void PutSpecLeft( unsigned char * data ); + void PutSpecRight( unsigned char * data ); + void PutWaveLeft( unsigned char * data ); + void PutWaveRight( unsigned char * data ); + + void GetSpecLeft( unsigned char * dest, int iOffset ); + void GetSpecRight( unsigned char * dest, int iOffset ); + void GetWaveLeft( unsigned char * dest, int iOffset ); + void GetWaveRight( unsigned char * dest, int iOffset ); +}; + + + +#endif // PA_VIS_CACHE_H diff --git a/Externals/MusicMod/Player/Src/VisModule.cpp b/Externals/MusicMod/Player/Src/VisModule.cpp new file mode 100644 index 0000000000..b171ed7b3f --- /dev/null +++ b/Externals/MusicMod/Player/Src/VisModule.cpp @@ -0,0 +1,358 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "VisModule.h" +#include "Console.h" +#include "Unicode.h" +#include "Playback.h" +#include "VisCache.h" +#include "PluginManager.h" +#include + + +VisModule ** active_vis_mods = NULL; // extern +int active_vis_count = 0; // extern + + +/* +BOOL CALLBACK EnumThreadWndProc( HWND hwnd, LPARAM lp ) +{ +// MessageBox( 0, "EnumThreadWndProc", "", 0 ); + DestroyWindow( hwnd ); +} +*/ + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +void PlugThread( PVOID pvoid ) +{ + // TODO: cleanup!!! + + Console::Append( TEXT( "Visualization thread born" ) ); + Console::Append( " " ); + + VisModule * mod = ( VisModule * )pvoid; + if( !mod ) return; + if( mod->mod->Init( mod->mod ) != 0 ) return; + + + VisCache::Create(); + + +//////////////////////////////////////////////////////////////////////////////// + VisLock.Enter(); +//////////////////////////////////////////////////////////////////////////////// + + active_vis_count++; + +//////////////////////////////////////////////////////////////////////////////// + VisLock.Leave(); +//////////////////////////////////////////////////////////////////////////////// + + + bool bKeepItGoing = true; + + + bool bQuitCalled = false; + + int iLast = GetTickCount(); + + // Message loop + MSG msg; + msg.message = WM_QUIT + 1; // Must be != WM_QUIT + do + { + if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) + { + if( msg.message == WM_QUIT ) + { +//////////////////////////////////////////////////////////////////////////////// + // Stop + if( !bQuitCalled ) + { + mod->mod->Quit( mod->mod ); + bQuitCalled = true; + } +//////////////////////////////////////////////////////////////////////////////// + + break; + } + + TranslateMessage( &msg ); + DispatchMessage( &msg ); + + if( msg.message == WM_CLOSE || ( ( msg.message == WM_SYSCOMMAND ) && ( msg.wParam == SC_CLOSE ) ) ) + { +//////////////////////////////////////////////////////////////////////////////// + // Stop + if( !bQuitCalled ) + { + mod->mod->Quit( mod->mod ); + bQuitCalled = true; + } +//////////////////////////////////////////////////////////////////////////////// + } + } + + if( bKeepItGoing ) + { + // Variant A + const int iNow = GetTickCount(); + if( iNow - iLast > mod->mod->delayMs ) + { + if( Playback::IsPlaying() ) + { + if( mod->bAllowRender ) + { + mod->bAllowRender = false; + + + const int iOffset = VisCache::LatencyToOffset( mod->mod->latencyMs ); + + switch( mod->mod->spectrumNch ) + { + case 2: + VisCache::GetSpecRight( mod->mod->spectrumData[ 1 ], iOffset ); + case 1: + VisCache::GetSpecLeft( mod->mod->spectrumData[ 0 ], iOffset ); + } + + switch( mod->mod->waveformNch ) + { + case 2: + VisCache::GetWaveRight( mod->mod->waveformData[ 1 ], iOffset ); + case 1: + VisCache::GetWaveLeft( mod->mod->waveformData[ 0 ], iOffset ); + } + + if( mod->mod->Render( mod->mod ) != 0 ) + { +//////////////////////////////////////////////////////////////////////////////// + // Stop + if( !bQuitCalled ) + { + // TODO: milkdrop doesn#t save window position + // when quit using manual plugin stop + + mod->mod->Quit( mod->mod ); + bQuitCalled = true; + } +//////////////////////////////////////////////////////////////////////////////// +/* + // Destroy all windows belonging to this thread + // This will lead to WM_QUIT automatically + EnumThreadWindows( GetCurrentThreadId(), EnumThreadWndProc, 0 ); + bKeepItGoing = false; +*/ + } + + + iLast = iNow; + } + } + else + { + if( mod->mod->Render( mod->mod ) != 0 ) + { +//////////////////////////////////////////////////////////////////////////////// + // Stop + if( !bQuitCalled ) + { + mod->mod->Quit( mod->mod ); + bQuitCalled = true; + } +//////////////////////////////////////////////////////////////////////////////// +/* + // Destroy all windows belonging to this thread + // This will lead to WM_QUIT automatically + EnumThreadWindows( GetCurrentThreadId(), EnumThreadWndProc, 0 ); + bKeepItGoing = false; +*/ + } + + iLast = iNow; + } + } + +//////////////////////////////////////////////////////////////////////////////// + bool bVisLockLeft = false; + VisLock.Enter(); +//////////////////////////////////////////////////////////////////////////////// + + if( mod->bShouldQuit ) + { + +//////////////////////////////////////////////////////////////////////////////// + VisLock.Leave(); + bVisLockLeft = true; +//////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////// + // Stop + if( !bQuitCalled ) + { + mod->mod->Quit( mod->mod ); + bQuitCalled = true; + } +//////////////////////////////////////////////////////////////////////////////// +/* + // Destroy all windows belonging to this thread + // This will lead to WM_QUIT automatically + EnumThreadWindows( GetCurrentThreadId(), EnumThreadWndProc, 0 ); + bKeepItGoing = false; +*/ + } + +//////////////////////////////////////////////////////////////////////////////// + if( !bVisLockLeft ) + { + VisLock.Leave(); + } +//////////////////////////////////////////////////////////////////////////////// + } + + Sleep( 1 ); + } + while( msg.message != WM_QUIT ); + + mod->bShouldQuit = false; + +//////////////////////////////////////////////////////////////////////////////// + VisLock.Enter(); + if( ( active_vis_count > 1 ) && ( mod->iArrayIndex < active_vis_count - 1 ) ) + { + active_vis_mods[ mod->iArrayIndex ] = active_vis_mods[ active_vis_count - 1 ]; + active_vis_mods[ mod->iArrayIndex ]->iArrayIndex = mod->iArrayIndex; + } + active_vis_count--; + VisLock.Leave(); +//////////////////////////////////////////////////////////////////////////////// + + mod->iArrayIndex = -1; + +/* + // Stop + mod->mod->Quit( mod->mod ); +*/ + +//////////////////////////////////////////////////////////////////////////////// + VisLock.Enter(); + mod->bActive = false; + VisLock.Leave(); +//////////////////////////////////////////////////////////////////////////////// + + UpdatePluginStatus( mod->plugin, true, mod->plugin->IsActive() ); + + Console::Append( TEXT( "Visualization thread dead" ) ); + Console::Append( " " ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +VisModule::VisModule( char * szName, int iIndex, winampVisModule * mod, VisPlugin * plugin ) +{ + iArrayIndex = -1; + bActive = false; + bShouldQuit = false; + bAllowRender = false; + + iNameLen = ( int )strlen( szName ); + this->szName = new TCHAR[ iNameLen + 1 ]; + ToTchar( this->szName, szName, iNameLen ); + this->szName[ iNameLen ] = TEXT( '\0' ); + + this->iIndex = iIndex; + this->mod = mod; + this->plugin = plugin; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool VisModule::Start() +{ + if( !mod ) return false; + if( bActive ) return false; + if( plugin->IsActive() ) return false; + +//////////////////////////////////////////////////////////////////////////////// + VisLock.Enter(); + if( !active_vis_count ) + { + active_vis_mods = new VisModule * [ 1 ]; + active_vis_mods[ 0 ] = this; + } + else + { + VisModule ** new_active_vis_mods = new VisModule * [ active_vis_count + 1 ]; + memcpy( new_active_vis_mods, active_vis_mods, active_vis_count * sizeof( VisModule * ) ); + new_active_vis_mods[ active_vis_count ] = this; + delete [] active_vis_mods; + active_vis_mods = new_active_vis_mods; + } + VisLock.Leave(); +//////////////////////////////////////////////////////////////////////////////// + + iArrayIndex = active_vis_count; + + + // Start + _beginthread( PlugThread, 1024 * 1024, ( PVOID )this ); + + bActive = true; + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool VisModule::Stop() +{ + if( !mod ) return false; + if( !bActive ) return false; + if( !plugin->IsActive() ) return false; + +//////////////////////////////////////////////////////////////////////////////// + VisLock.Enter(); + bShouldQuit = true; + VisLock.Leave(); +//////////////////////////////////////////////////////////////////////////////// + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool VisModule::Config() +{ + if( !mod ) return false; + if( !mod->Config ) return false; + + mod->Config( mod ); + + return true; +} diff --git a/Externals/MusicMod/Player/Src/VisModule.h b/Externals/MusicMod/Player/Src/VisModule.h new file mode 100644 index 0000000000..23e13e7450 --- /dev/null +++ b/Externals/MusicMod/Player/Src/VisModule.h @@ -0,0 +1,71 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_VIS_MODULE_H +#define PA_VIS_MODULE_H + + + +#include "Global.h" +#include "VisPlugin.h" +#include + + + +class VisModule; +class VisPlugin; + +extern VisModule ** active_vis_mods; +extern int active_vis_count; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Winamp visualization module wrapper +//////////////////////////////////////////////////////////////////////////////// +class VisModule +{ +public: + VisModule( char * szName, int iIndex, winampVisModule * mod, VisPlugin * plugin ); + + bool Start(); + bool Config(); + bool Stop(); + + inline bool IsActive() { return bActive; } + + inline TCHAR * GetName() { return szName; } + inline int GetNameLen() { return iNameLen; } + +private: + int iArrayIndex; + bool bActive; + bool bShouldQuit; + bool bAllowRender; + + TCHAR * szName; + int iNameLen; + + int iIndex; + winampVisModule * mod; + VisPlugin * plugin; + + + friend void PlugThread( PVOID pvoid ); + friend void VSAAdd( void * data, int timestamp ); + friend void VSAAddPCMData( void * PCMData, int nch, int bps, int timestamp ); + friend int VSAGetMode( int * specNch, int * waveNch ); +}; + + + +#endif // PA_VIS_MODULE_H diff --git a/Externals/MusicMod/Player/Src/VisPlugin.cpp b/Externals/MusicMod/Player/Src/VisPlugin.cpp new file mode 100644 index 0000000000..c25262e715 --- /dev/null +++ b/Externals/MusicMod/Player/Src/VisPlugin.cpp @@ -0,0 +1,189 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "VisPlugin.h" +#include "Unicode.h" +#include "Console.h" +#include "Main.h" + + + +vector vis_plugins; // extern + +Lock VisLock = Lock( TEXT( "PLAINAMP_VIS_LOCK" ) ); // extern + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +VisPlugin::VisPlugin( TCHAR * szDllpath, bool bKeepLoaded ) : Plugin( szDllpath ) +{ + header = NULL; + + if( !Load() ) + { + return; + } + + if( !bKeepLoaded ) + { + Unload(); + } + + vis_plugins.push_back( this ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool VisPlugin::Load() +{ + // (1) Load DLL + hDLL = LoadLibrary( GetFullpath() ); + if( !hDLL ) return false; + + // (2) Find export + WINAMP_VIS_GETTER winampGetVisHeader = + ( WINAMP_VIS_GETTER )GetProcAddress( hDLL, "winampVisGetHeader" ); + if( winampGetVisHeader == NULL ) + { + FreeLibrary( hDLL ); + hDLL = NULL; + return false; + } + + // (3) Get header + header = winampGetVisHeader(); + if( header == NULL ) + { + FreeLibrary( hDLL ); + hDLL = NULL; + return false; + } + +//////////////////////////////////////////////////////////////////////////////// + // Forget old modules or we get them twice + if( !modules.empty() ) + { + modules.clear(); + } +//////////////////////////////////////////////////////////////////////////////// + + if( !szName ) + { + // Note: The prefix is not removed to hide their + // origin at Nullsoft! It just reads easier. + if( !strnicmp( header->description, "nullsoft ", 9 ) ) + { + header->description += 9; + } + iNameLen = ( int )strlen( header->description ); + szName = new TCHAR[ iNameLen + 1 ]; + ToTchar( szName, header->description, iNameLen ); + szName[ iNameLen ] = TEXT( '\0' ); + } + + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "Loading <%s>, %s" ), GetFilename(), szName ); + Console::Append( szBuffer ); + + // (4) Get modules + winampVisModule * mod; + int iFound = 0; + while( true ) + { + mod = header->getModule( iFound ); + if( !mod ) break; + + // (4a) Modify module + mod->hDllInstance = hDLL; + mod->hwndParent = WindowMain; + mod->sRate = 44100; // TODO + mod->nCh = 2; // TODO + + // (4b) Add module to list + VisModule * vismod = new VisModule( + mod->description, // char * szName + iFound, // UINT uIndex + mod, // winampVisModule * mod + this // VisPlugin * plugin + ); + modules.push_back( vismod ); + + iFound++; + + _stprintf( szBuffer, TEXT( " %s" ), vismod->GetName() ); + Console::Append( szBuffer ); + } + + Console::Append( TEXT( " " ) ); + + if( !iFound ) return false; + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool VisPlugin::Unload() +{ + if( !IsLoaded() ) return true; + + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "Unloading <%s>" ), GetFilename() ); + Console::Append( szBuffer ); + Console::Append( TEXT( " " ) ); + printf( ">>>Unloading <%s>\n" , GetFilename() ); + + header = NULL; + + /* + TODO + VisModule * walk; + vector ::iterator iter = modules.begin(); + while( iter != modules.end() ) + { + walk = *iter; + delete [] walk->szName; + delete walk; + + iter++; + } + */ + + FreeLibrary( hDLL ); + hDLL = NULL; + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +bool VisPlugin::IsActive() +{ + vector ::iterator iter = modules.begin(); + while( iter != modules.end() ) + { + if( ( *iter )->IsActive() ) return true; + iter++; + } + return false; +} diff --git a/Externals/MusicMod/Player/Src/VisPlugin.h b/Externals/MusicMod/Player/Src/VisPlugin.h new file mode 100644 index 0000000000..d28b1a5f8e --- /dev/null +++ b/Externals/MusicMod/Player/Src/VisPlugin.h @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_VIS_PLUGIN_H +#define PA_VIS_PLUGIN_H + + + +#include "Global.h" +#include "Plugin.h" +#include "Winamp/Vis.h" +#include "VisModule.h" +#include "Lock.h" +#include + +using namespace std; + + + +typedef winampVisHeader * ( * WINAMP_VIS_GETTER )( void ); + + + +class VisModule; +class VisPlugin; + +extern vector vis_plugins; +extern Lock VisLock; + + + +//////////////////////////////////////////////////////////////////////////////// +/// Winamp visualization plugin wrapper +//////////////////////////////////////////////////////////////////////////////// +class VisPlugin : public Plugin +{ +public: + VisPlugin( TCHAR * szDllpath, bool bKeepLoaded ); + + bool Load(); + bool Unload(); + + TCHAR * GetTypeString() { return TEXT( "Visual" ); } + int GetTypeStringLen() { return 6; } + PluginType GetType() { return PLUGIN_TYPE_VIS; } + + bool IsActive(); + +private: + winampVisHeader * header; + vector modules; + + + friend void ContextMenuVis( VisPlugin * vis, POINT * p ); +}; + + + +#endif // PA_VIS_PLUGIN_H diff --git a/Externals/MusicMod/Player/Src/Winamp.cpp b/Externals/MusicMod/Player/Src/Winamp.cpp new file mode 100644 index 0000000000..6e123b6355 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Winamp.cpp @@ -0,0 +1,996 @@ +// ======================================================================================= +// WndprocWinamp is called repeatedly when the cursor is moved over the main window +// ======================================================================================= + +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Winamp.h" +#include "Playback.h" +#include "Playlist.h" +#include "Console.h" +#include "Main.h" +#include "Winamp/wa_ipc.h" +#include "Winamp/wa_msgids.h" +#include "AddDirectory.h" +#include "AddFiles.h" +#include "Prefs.h" +#include "PluginManager.h" +#include "Embed.h" +#include "Unicode.h" +#include "zlib/zlib.h" +#include "Rebar.h" + + + +int IPC_GENHOTKEYS_ADD = 0; +int ID_DYNAMICLIBRARY = 0; +int IPC_GETPLAINBARTARGET = 0; + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +LRESULT CALLBACK WndprocWinamp( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + //Console::Append( TEXT( "Winamp.cc:WndprocWinamp was called" ) ); + + switch( message ) + { + case WM_COMMAND: +/* + { + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "WM_COMMAND <%i> <%i>" ), wp, lp ); + Console::Append( szBuffer ); + } +*/ + switch( LOWORD( wp ) ) + { + case WINAMP_FILE_QUIT: + PostMessage( WindowMain, WM_SYSCOMMAND, SC_CLOSE, 0 ); + break; + + case WINAMP_OPTIONS_PREFS: + Prefs::Show(); + break; +/* + case WINAMP_OPTIONS_AOT: break; + case WINAMP_FILE_REPEAT: break; + case WINAMP_FILE_SHUFFLE: break; + case WINAMP_HIGH_PRIORITY: break; +*/ + case WINAMP_FILE_PLAY: + AddFiles(); + break; +/* + case WINAMP_OPTIONS_EQ: break; + case WINAMP_OPTIONS_ELAPSED: break; + case WINAMP_OPTIONS_REMAINING: break; + case WINAMP_OPTIONS_PLEDIT: break; +*/ + case WINAMP_HELP_ABOUT: + About( hwnd ); + break; + + case WINAMP_MAINMENU: + { + POINT p; + GetCursorPos( &p ); + + CheckMenuItem( + main_context_menu, + PLAINAMP_TOGGLE_CONSOLE, + IsWindowVisible( WindowConsole ) ? MF_CHECKED : MF_UNCHECKED + ); + CheckMenuItem( + main_context_menu, + PLAINAMP_TOGGLE_MANAGER, + IsWindowVisible( WindowManager ) ? MF_CHECKED : MF_UNCHECKED + ); + + TrackPopupMenu( + main_context_menu, + TPM_LEFTALIGN | + TPM_TOPALIGN | + TPM_RIGHTBUTTON, + p.x, + p.y, + 0, + hwnd, + NULL + ); + + break; + } + + case WINAMP_BUTTON1: + Playback::Prev(); + Playback::UpdateSeek(); + break; + + case WINAMP_BUTTON2: + Playback::Play(); + Playback::UpdateSeek(); + break; + + case WINAMP_BUTTON3: + Playback::Pause(); + Playback::UpdateSeek(); + break; + + case WINAMP_BUTTON4: + Playback::Stop(); + Playback::UpdateSeek(); + break; + + case WINAMP_BUTTON5: + Playback::Next(); + Playback::UpdateSeek(); + break; + +/* + case WINAMP_BUTTON1_SHIFT: break; + case WINAMP_BUTTON2_SHIFT: break; + case WINAMP_BUTTON3_SHIFT: break; + case WINAMP_BUTTON4_SHIFT: break; + case WINAMP_BUTTON5_SHIFT: break; + case WINAMP_BUTTON1_CTRL: break; + case WINAMP_BUTTON2_CTRL: break; + case WINAMP_BUTTON3_CTRL: break; + case WINAMP_BUTTON4_CTRL: break; + case WINAMP_BUTTON5_CTRL: break; +*/ + + case WINAMP_VOLUMEUP: + Playback::Volume::Up(); + // TODO Update slider + break; + + case WINAMP_VOLUMEDOWN: + Playback::Volume::Down(); + // TODO Update slider + break; + + case WINAMP_FFWD5S: + Playback::Forward(); + Playback::UpdateSeek(); + break; + + case WINAMP_REW5S: + Playback::Rewind(); + Playback::UpdateSeek(); + break; +/* + case WINAMP_NEXT_WINDOW: break; + case WINAMP_OPTIONS_WINDOWSHADE: break; + case WINAMP_OPTIONS_DSIZE: break; + case IDC_SORT_FILENAME: break; + case IDC_SORT_FILETITLE: break; + case IDC_SORT_ENTIREFILENAME: break; + case IDC_SELECTALL: break; + case IDC_SELECTNONE: break; + case IDC_SELECTINV: break; + case IDM_EQ_LOADPRE: break; + case IDM_EQ_LOADMP3: break; + case IDM_EQ_LOADDEFAULT: break; + case IDM_EQ_SAVEPRE: break; + case IDM_EQ_SAVEMP3: break; + case IDM_EQ_SAVEDEFAULT: break; + case IDM_EQ_DELPRE: break; + case IDM_EQ_DELMP3: break; + case IDC_PLAYLIST_PLAY: break; + case WINAMP_FILE_LOC: break; + case WINAMP_OPTIONS_EASYMOVE: break; +*/ + case WINAMP_FILE_DIR: + AddDirectory(); + break; +/* + case WINAMP_EDIT_ID3: break; + case WINAMP_TOGGLE_AUTOSCROLL: break; + case WINAMP_VISSETUP: break; + case WINAMP_PLGSETUP: break; + case WINAMP_VISPLUGIN: break; + case WINAMP_JUMP: break; + case WINAMP_JUMPFILE: break; + case WINAMP_JUMP10FWD: break; + case WINAMP_JUMP10BACK: break; + case WINAMP_PREVSONG: break; + case WINAMP_OPTIONS_EXTRAHQ: break; +*/ + case ID_PE_NEW: + playlist->RemoveAll(); + break; + + case ID_PE_OPEN: + Playlist::DialogOpen(); + break; +/* + case ID_PE_SAVE: break; +*/ + case ID_PE_SAVEAS: + Playlist::DialogSaveAs(); + break; + + case ID_PE_SELECTALL: + playlist->SelectAll( true ); + break; + + case ID_PE_INVERT: + playlist->SelectInvert(); + break; + + case ID_PE_NONE: + playlist->SelectAll( false ); + break; +/* + case ID_PE_ID3: break; + case ID_PE_S_TITLE: break; + case ID_PE_S_FILENAME: break; + case ID_PE_S_PATH: break; + case ID_PE_S_RANDOM: break; + case ID_PE_S_REV: break; +*/ + case ID_PE_CLEAR: + playlist->RemoveAll(); + break; +/* + case ID_PE_MOVEUP: break; + case ID_PE_MOVEDOWN: break; + case WINAMP_SELSKIN: break; + case WINAMP_VISCONF: break; + case ID_PE_NONEXIST: break; + case ID_PE_DELETEFROMDISK: break; + case ID_PE_CLOSE: break; + case WINAMP_VIS_SETOSC: break; + case WINAMP_VIS_SETANA: break; + case WINAMP_VIS_SETOFF: break; + case WINAMP_VIS_DOTSCOPE: break; + case WINAMP_VIS_LINESCOPE: break; + case WINAMP_VIS_SOLIDSCOPE: break; + case WINAMP_VIS_NORMANA: break; + case WINAMP_VIS_FIREANA: break; + case WINAMP_VIS_LINEANA: break; + case WINAMP_VIS_NORMVU: break; + case WINAMP_VIS_SMOOTHVU: break; + case WINAMP_VIS_FULLREF: break; + case WINAMP_VIS_FULLREF2: break; + case WINAMP_VIS_FULLREF3: break; + case WINAMP_VIS_FULLREF4: break; + case WINAMP_OPTIONS_TOGTIME: break; + case EQ_ENABLE: break; + case EQ_AUTO: break; + case EQ_PRESETS: break; + case WINAMP_VIS_FALLOFF0: break; + case WINAMP_VIS_FALLOFF1: break; + case WINAMP_VIS_FALLOFF2: break; + case WINAMP_VIS_FALLOFF3: break; + case WINAMP_VIS_FALLOFF4: break; + case WINAMP_VIS_PEAKS: break; + case ID_LOAD_EQF: break; + case ID_SAVE_EQF: break; + case ID_PE_ENTRY: break; + case ID_PE_SCROLLUP: break; + case ID_PE_SCROLLDOWN: break; + case WINAMP_MAIN_WINDOW: break; + case WINAMP_VIS_PFALLOFF0: break; + case WINAMP_VIS_PFALLOFF1: break; + case WINAMP_VIS_PFALLOFF2: break; + case WINAMP_VIS_PFALLOFF3: break; + case WINAMP_VIS_PFALLOFF4: break; + case ID_PE_TOP: break; + case ID_PE_BOTTOM: break; + case WINAMP_OPTIONS_WINDOWSHADE_PL: break; + case EQ_INC1: break; + case EQ_INC2: break; + case EQ_INC3: break; + case EQ_INC4: break; + case EQ_INC5: break; + case EQ_INC6: break; + case EQ_INC7: break; + case EQ_INC8: break; + case EQ_INC9: break; + case EQ_INC10: break; + case EQ_INCPRE: break; + case EQ_DECPRE: break; + case EQ_DEC1: break; + case EQ_DEC2: break; + case EQ_DEC3: break; + case EQ_DEC4: break; + case EQ_DEC5: break; + case EQ_DEC6: break; + case EQ_DEC7: break; + case EQ_DEC8: break; + case EQ_DEC9: break; + case EQ_DEC10: break; + case ID_PE_SCUP: break; + case ID_PE_SCDOWN: break; + case WINAMP_REFRESHSKIN: break; + case ID_PE_PRINT: break; + case ID_PE_EXTINFO: break; + case WINAMP_PLAYLIST_ADVANCE: break; + case WINAMP_VIS_LIN: break; + case WINAMP_VIS_BAR: break; + case WINAMP_OPTIONS_MINIBROWSER: break; + case MB_FWD: break; + case MB_BACK: break; + case MB_RELOAD: break; + case MB_OPENMENU: break; + case MB_OPENLOC: break; + case WINAMP_NEW_INSTANCE: break; + case MB_UPDATE: break; + case WINAMP_OPTIONS_WINDOWSHADE_EQ: break; + case EQ_PANLEFT: break; + case EQ_PANRIGHT: break; + case WINAMP_GETMORESKINS: break; + case WINAMP_VIS_OPTIONS: break; + case WINAMP_PE_SEARCH: break; + case ID_PE_BOOKMARK: break; + case WINAMP_EDIT_BOOKMARKS: break; + case WINAMP_MAKECURBOOKMARK: break; + case ID_MAIN_PLAY_BOOKMARK_NONE: break; + case ID_MAIN_PLAY_AUDIOCD: break; + case ID_MAIN_PLAY_AUDIOCD2: break; + case ID_MAIN_PLAY_AUDIOCD3: break; + case ID_MAIN_PLAY_AUDIOCD4: break; + case WINAMP_OPTIONS_VIDEO: break; + case ID_VIDEOWND_ZOOMFULLSCREEN: break; + case ID_VIDEOWND_ZOOM100: break; + case ID_VIDEOWND_ZOOM200: break; + case ID_VIDEOWND_ZOOM50: break; + case ID_VIDEOWND_VIDEOOPTIONS: break; + case WINAMP_MINIMIZE: break; + case ID_PE_FONTBIGGER: break; + case ID_PE_FONTSMALLER: break; + case WINAMP_VIDEO_TOGGLE_FS: break; + case WINAMP_VIDEO_TVBUTTON: break; + case WINAMP_LIGHTNING_CLICK: break; + case ID_FILE_ADDTOLIBRARY: break; + case ID_HELP_HELPTOPICS: break; + case ID_HELP_GETTINGSTARTED: break; + case ID_HELP_WINAMPFORUMS: break; +*/ + case ID_PLAY_VOLUMEUP: + Playback::Volume::Up(); + // TODO Update slider + break; + + case ID_PLAY_VOLUMEDOWN: + Playback::Volume::Down(); + // TODO Update slider + break; +/* + case ID_PEFILE_OPENPLAYLISTFROMLIBRARY_NOPLAYLISTSINLIBRARY: break; + case ID_PEFILE_ADDFROMLIBRARY: break; + case ID_PEFILE_CLOSEPLAYLISTEDITOR: break; + case ID_PEPLAYLIST_PLAYLISTPREFERENCES: break; + case ID_MLFILE_NEWPLAYLIST: break; + case ID_MLFILE_LOADPLAYLIST: break; + case ID_MLFILE_SAVEPLAYLIST: break; + case ID_MLFILE_ADDMEDIATOLIBRARY: break; + case ID_MLFILE_CLOSEMEDIALIBRARY: break; + case ID_MLVIEW_NOWPLAYING: break; + case ID_MLVIEW_LOCALMEDIA_ALLMEDIA: break; + case ID_MLVIEW_LOCALMEDIA_AUDIO: break; + case ID_MLVIEW_LOCALMEDIA_VIDEO: break; + case ID_MLVIEW_PLAYLISTS_NOPLAYLISTINLIBRARY: break; + case ID_MLVIEW_INTERNETRADIO: break; + case ID_MLVIEW_INTERNETTV: break; + case ID_MLVIEW_LIBRARYPREFERENCES: break; + case ID_MLVIEW_DEVICES_NOAVAILABLEDEVICE: break; + case ID_MLFILE_IMPORTCURRENTPLAYLIST: break; + case ID_MLVIEW_MEDIA: break; + case ID_MLVIEW_PLAYLISTS: break; + case ID_MLVIEW_MEDIA_ALLMEDIA: break; + case ID_MLVIEW_DEVICES: break; + case ID_FILE_SHOWLIBRARY: break; + case ID_FILE_CLOSELIBRARY: break; + case ID_POST_PLAY_PLAYLIST: break; + case ID_VIS_NEXT: break; + case ID_VIS_PREV: break; + case ID_VIS_RANDOM: break; + case ID_MANAGEPLAYLISTS: break; + case ID_PREFS_SKIN_SWITCHTOSKIN: break; + case ID_PREFS_SKIN_DELETESKIN: break; + case ID_PREFS_SKIN_RENAMESKIN: break; + case ID_VIS_FS: break; + case ID_VIS_CFG: break; + case ID_VIS_MENU: break; + case ID_VIS_SET_FS_FLAG: break; + case ID_PE_SHOWPLAYING: break; + case ID_HELP_REGISTERWINAMPPRO: break; + case ID_PE_MANUAL_ADVANCE: break; + case WA_SONG_5_STAR_RATING: break; + case WA_SONG_4_STAR_RATING: break; + case WA_SONG_3_STAR_RATING: break; + case WA_SONG_2_STAR_RATING: break; + case WA_SONG_1_STAR_RATING: break; + case WA_SONG_NO_RATING: break; + case PL_SONG_5_STAR_RATING: break; + case PL_SONG_4_STAR_RATING: break; + case PL_SONG_3_STAR_RATING: break; + case PL_SONG_2_STAR_RATING: break; + case PL_SONG_1_STAR_RATING: break; + case PL_SONG_NO_RATING: break; + case AUDIO_TRACK_ONE: break; + case VIDEO_TRACK_ONE: break; + case ID_SWITCH_COLOURTHEME: break; + case ID_GENFF_LIMIT: break; + +*/ + case PLAINAMP_TOGGLE_CONSOLE: + ShowWindow( WindowConsole, IsWindowVisible( WindowConsole ) ? SW_HIDE : SW_SHOW ); + break; + + case PLAINAMP_TOGGLE_MANAGER: + ShowWindow( WindowManager, IsWindowVisible( WindowManager ) ? SW_HIDE : SW_SHOW ); + break; + + case PLAINAMP_PL_REM_SEL: + playlist->RemoveSelected( true ); + break; + + case PLAINAMP_PL_REM_CROP: + playlist->RemoveSelected( false ); + break; + + default: + { + /* + if( wp == ID_DYNAMICLIBRARY ) + { + // Stupid dnylib workaround + PostMessage( hwnd, WM_COMMAND, ID_DYNAMICLIBRARY | ( 1 << 16 ), 0 ); + } + */ + + if( LOWORD( wp ) < 40001 ) break; + + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "WM_COMMAND <%i> <%i>" ), wp, lp ); + Console::Append( szBuffer ); + Console::Append( TEXT( "NOT handled" ) ); + Console::Append( " " ); + } + + } + break; + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + case WM_WA_IPC: +/* + { + TCHAR szBuffer[ 5000 ]; + if( lp != 2006 ) + { + _stprintf( szBuffer, TEXT( "WM_WA_IPC <%i> <%i>" ), wp, lp ); + } + else + { + // Hotkey sent as strings! + // Idea: make stl map + _stprintf( szBuffer, TEXT( "WM_WA_IPC <%s> <%i>" ), ( char * )wp, lp ); + } + Console::Append( szBuffer ); + } +*/ + switch( lp ) + { + case IPC_GETVERSION: + return 0x5010; // 5.10 +/* + case IPC_GETREGISTEREDVERSION: break; + case IPC_PLAYFILE: break; + case IPC_ENQUEUEFILE: break; +*/ + case IPC_DELETE: + playlist->RemoveAll(); + break; +/* + case IPC_DELETE_INT: break; +*/ + case IPC_STARTPLAY: + Playback::Play(); + Playback::UpdateSeek(); + break; +/* + case IPC_STARTPLAY_INT: break; + case IPC_CHDIR: break; +*/ + case IPC_ISPLAYING: // untested + return ( Playback::IsPlaying() ? ( Playback::IsPaused() ? 3 : 1 ) : 0 ); +/* + case IPC_GETOUTPUTTIME: break; + case IPC_JUMPTOTIME: break; + case IPC_GETMODULENAME: break; + case IPC_EX_ISRIGHTEXE: break; + case IPC_WRITEPLAYLIST: break; +*/ + case IPC_SETPLAYLISTPOS: + playlist->SetCurIndex( ( int )wp ); + break; + + case IPC_SETVOLUME: break; + Playback::Volume::Set( ( int )wp ); + // TODO Update slider + break; + + case IPC_SETPANNING: break; + Playback::Pan::Set( ( int )wp ); + // TODO Update slider + break; + + case IPC_GETLISTLENGTH: + return playlist->GetSize(); + + case IPC_GETLISTPOS: + return playlist->GetCurIndex(); +/* + case IPC_GETINFO: break; + case IPC_GETEQDATA: break; + // TODO + case IPC_SETEQDATA: break; + // TODO + case IPC_ADDBOOKMARK: break; + case IPC_INSTALLPLUGIN: break; + case IPC_RESTARTWINAMP: break; + case IPC_ISFULLSTOP: break; + case IPC_INETAVAILABLE: break; + case IPC_UPDTITLE: break; + case IPC_REFRESHPLCACHE: break; + case IPC_GET_SHUFFLE: break; + case IPC_GET_REPEAT: break; + case IPC_SET_SHUFFLE: break; + case IPC_SET_REPEAT: break; + case IPC_ENABLEDISABLE_ALL_WINDOWS: break; +*/ + case IPC_GETWND: + { + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "IPC_GETWND <%i>" ), wp ); + Console::Append( szBuffer ); + Console::Append( " " ); + } + + switch( wp ) + { + case IPC_GETWND_EQ: break; + case IPC_GETWND_PE: return ( LRESULT )WindowMain; + case IPC_GETWND_MB: break; + case IPC_GETWND_VIDEO: break; + } + + return ( LRESULT )NULL; + + case IPC_ISWNDVISIBLE: + { + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "IPC_ISWNDVISIBLE <%i>" ), wp ); + Console::Append( szBuffer ); + Console::Append( " " ); + } + + switch( wp ) + { + case IPC_GETWND_EQ: break; + case IPC_GETWND_PE: return 1; + case IPC_GETWND_MB: break; + case IPC_GETWND_VIDEO: break; + } + + return 0; +/* + case IPC_SETSKIN: break; + case IPC_GETSKIN: break; + case IPC_EXECPLUG: break; +*/ + case IPC_GETPLAYLISTFILE: + { + static char szAnsiFilename[ 2000 ] = "\0"; + Playlist::GetFilename( ( int )wp, szAnsiFilename, 1999 ); + return ( LRESULT )szAnsiFilename; + } + + case IPC_GETPLAYLISTTITLE: + { + static char szAnsiTitle[ 2000 ] = "\0"; + Playlist::GetTitle( ( int )wp, szAnsiTitle, 1999 ); + return ( LRESULT )szAnsiTitle; + } +/* + case IPC_GETHTTPGETTER: break; + case IPC_MBOPEN: break; + case IPC_CHANGECURRENTFILE: break; + case IPC_GETMBURL: break; + case IPC_MBBLOCK: break; + case IPC_MBOPENREAL: break; + case IPC_ADJUST_OPTIONSMENUPOS: break; +*/ + case IPC_GET_HMENU: + { + switch( wp ) + { + case 0: + return ( LRESULT )main_context_menu; +/* + case 1: break; + case 2: break; + case 3: break; + case 4: break; +*/ + } + return ( LRESULT )NULL; + } + + case IPC_GET_EXTENDED_FILE_INFO: + Console::Append( "IPC_GET_EXTENDED_FILE_INFO" ); + Console::Append( TEXT( "NOT handled" ) ); + Console::Append( " " ); + break; +/* + case IPC_GET_EXTENDED_FILE_INFO_HOOKABLE: break; + case IPC_GET_BASIC_FILE_INFO: break; +*/ + case IPC_GET_EXTLIST: + // TODO + return ( LRESULT )GlobalAlloc( GMEM_ZEROINIT, 2 ); // "\0\0" +/* + case IPC_INFOBOX: break; + case IPC_SET_EXTENDED_FILE_INFO: break; + case IPC_WRITE_EXTENDED_FILE_INFO: break; + case IPC_FORMAT_TITLE: break; +*/ +// ======================================================================================= +// Let's remove this +/* + case IPC_GETUNCOMPRESSINTERFACE: + if( wp == 0x10100000 ) + { + Console::Append( "IPC_GETUNCOMPRESSINTERFACE @ wa_inflate_struct" ); + Console::Append( TEXT( "NOT handled" ) ); + Console::Append( " " ); + } + else + { + Console::Append( "IPC_GETUNCOMPRESSINTERFACE @ zlib" ); + Console::Append( " " ); + return ( LRESULT )uncompress; + } + break; +*/ +// ======================================================================================= + + case IPC_ADD_PREFS_DLG: + Prefs::AddPage( ( prefsDlgRec * )wp ); + break; +/* + case IPC_REMOVE_PREFS_DLG: break; +*/ + case IPC_OPENPREFSTOPAGE: + Prefs::Show( ( prefsDlgRec * )wp ); + break; + + case IPC_GETINIFILE: + { + static char szWinampInipath[ MAX_PATH ] = ""; + if( *szWinampInipath == '\0' ) + { + GetModuleFileNameA( NULL, szWinampInipath, MAX_PATH - 1 ); + char * szWalk = szWinampInipath + strlen( szWinampInipath ) - 1; + while( ( szWalk > szWinampInipath ) && ( *szWalk != '.' ) ) szWalk--; + szWalk++; + strcpy( szWalk, "ini" ); + } + return ( LRESULT )szWinampInipath; + } + + case IPC_GETINIDIRECTORY: + { + // TODO: trailing slash or not??? + static char szPluginInipath[ MAX_PATH ] = ""; + if( *szPluginInipath == '\0' ) + { + GetModuleFileNameA( NULL, szPluginInipath, MAX_PATH - 1 ); + char * szWalk = szPluginInipath + strlen( szPluginInipath ) - 1; + while( ( szWalk > szPluginInipath ) && ( *szWalk != '\\' ) ) szWalk--; + szWalk++; + strcpy( szWalk, TEXT( "Plugins" ) ); + } + return ( LRESULT )szPluginInipath; + } +/* + case IPC_SPAWNBUTTONPOPUP: break; + case IPC_OPENURLBOX: break; +*/ + case IPC_OPENFILEBOX: + AddFiles(); + break; + + case IPC_OPENDIRBOX: + AddDirectory(); + break; + + case IPC_GET_GENSKINBITMAP: + { + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "IPC_GET_GENSKINBITMAP <%i>" ), wp ); + Console::Append( szBuffer ); + + switch( wp ) + { + case 0: + { + // TODO: make it work + + // Gen.bmp? + static HBITMAP hBitmap = NULL; + static BYTE * image = NULL; + if( !hBitmap ) + { + const int iWidth = 200; // 194; + const int iHeight = 300; // 109; + const int bpp = 24; + int bytes_per_row = iWidth * ( bpp / 8 ); + const int diff = bytes_per_row % 4; + if( diff ) bytes_per_row += ( 4 - diff ); + + const int size_in_bytes = bytes_per_row * iHeight; + image = new BYTE[ size_in_bytes ]; + + hBitmap = CreateBitmap( + iWidth, // int nWidth + iHeight, // int nHeight + 1, // UINT cPlanes + bpp, // UINT cBitsPerPel + image // CONST VOID * lpvBits + ); + + memset( image, 255, size_in_bytes ); + } + + return ( LRESULT )hBitmap; + } + + default: + { + Console::Append( TEXT( "NOT handled" ) ); + } + + } + Console::Append( TEXT( " " ) ); + + break; + } + + case IPC_GET_EMBEDIF: + // TODO + if( !wp ) + { + return ( LRESULT )Embed::Embed; + } + else + { + return ( LRESULT )Embed::Embed( ( embedWindowState * )wp ); + } + break; +/* + case IPC_EMBED_ENUM: break; + case IPC_EMBED_ISVALID: break; + case IPC_CONVERTFILE: break; + case IPC_CONVERTFILE_END: break; + case IPC_CONVERT_CONFIG: break; + case IPC_CONVERT_CONFIG_END: break; + case IPC_GETSADATAFUNC: break; + case IPC_ISMAINWNDVISIBLE: break; + case IPC_SETPLEDITCOLORS: break; + case IPC_SPAWNEQPRESETMENU: break; + case IPC_SPAWNFILEMENU: break; + case IPC_SPAWNOPTIONSMENU: break; + case IPC_SPAWNWINDOWSMENU: break; + case IPC_SPAWNHELPMENU: break; + case IPC_SPAWNPLAYMENU: break; + case IPC_SPAWNPEFILEMENU: break; + case IPC_SPAWNPEPLAYLISTMENU: break; + case IPC_SPAWNPESORTMENU: break; + case IPC_SPAWNPEHELPMENU: break; + case IPC_SPAWNMLFILEMENU: break; + case IPC_SPAWNMLVIEWMENU: break; + case IPC_SPAWNMLHELPMENU: break; + case IPC_SPAWNPELISTOFPLAYLISTS: break; + case WM_WA_SYSTRAY: break; + case IPC_IS_PLAYING_VIDEO: break; + case IPC_GET_IVIDEOOUTPUT: break; +*/ + case IPC_CB_ONSHOWWND: + case IPC_CB_ONHIDEWND: + case IPC_CB_GETTOOLTIP: + case IPC_CB_MISC: + case IPC_CB_CONVERT_STATUS: + case IPC_CB_CONVERT_DONE: + break; +/* + case IPC_ADJUST_FFWINDOWSMENUPOS: break; + case IPC_ISDOUBLESIZE: break; + case IPC_ADJUST_FFOPTIONSMENUPOS: break; +*/ + case IPC_GETTIMEDISPLAYMODE: + return 0; // == elapsed time +/* + case IPC_SETVISWND: break; + case IPC_GETVISWND: break; + case IPC_ISVISRUNNING: break; +*/ + case IPC_CB_VISRANDOM: break; +/* + case IPC_SETIDEALVIDEOSIZE: break; + case IPC_GETSTOPONVIDEOCLOSE: break; + case IPC_SETSTOPONVIDEOCLOSE: break; +*/ + case IPC_TRANSLATEACCELERATOR: + Console::Append( TEXT( "IPC_TRANSLATEACCELERATOR" ) ); + Console::Append( TEXT( "NOT handled" ) ); + Console::Append( TEXT( " " ) ); + break; + + case IPC_CB_ONTOGGLEAOT: break; +/* + case IPC_GETPREFSWND: break; + case IPC_SET_PE_WIDTHHEIGHT: break; + case IPC_GETLANGUAGEPACKINSTANCE: break; +*/ + case IPC_CB_PEINFOTEXT: + case IPC_CB_OUTPUTCHANGED: + break; +/* + case IPC_GETOUTPUTPLUGIN: break; + case IPC_SETDRAWBORDERS: break; + case IPC_DISABLESKINCURSORS: break; +*/ + case IPC_CB_RESETFONT: break; +/* + case IPC_IS_FULLSCREEN: break; + case IPC_SET_VIS_FS_FLAG: break; + case IPC_SHOW_NOTIFICATION: break; + case IPC_GETSKININFO: break; + case IPC_GET_MANUALPLADVANCE: break; + case IPC_SET_MANUALPLADVANCE: break; + case IPC_GET_NEXT_PLITEM: break; + case IPC_GET_PREVIOUS_PLITEM: break; + case IPC_IS_WNDSHADE: break; + case IPC_SETRATING: break; + case IPC_GETRATING: break; + case IPC_GETNUMAUDIOTRACKS: break; + case IPC_GETNUMVIDEOTRACKS: break; + case IPC_GETAUDIOTRACK: break; + case IPC_GETVIDEOTRACK: break; + case IPC_SETAUDIOTRACK: break; + case IPC_SETVIDEOTRACK: break; + case IPC_PUSH_DISABLE_EXIT: break; + case IPC_POP_DISABLE_EXIT: break; + case IPC_IS_EXIT_ENABLED: break; + case IPC_IS_AOT: break; + case IPC_PLCMD: break; + case IPC_MBCMD: break; + case IPC_VIDCMD: break; + case IPC_MBURL: break; + case IPC_MBGETCURURL: break; + case IPC_MBGETDESC: break; + case IPC_MBCHECKLOCFILE: break; + case IPC_MBREFRESH: break; + case IPC_MBGETDEFURL: break; + case IPC_STATS_LIBRARY_ITEMCNT: break; + case IPC_FF_FIRST: break; + case IPC_FF_LAST: break; + case IPC_GETDROPTARGET: break; + case IPC_PLAYLIST_MODIFIED: break; + case IPC_PLAYING_FILE: break; + case IPC_FILE_TAG_MAY_HAVE_UPDATED: break; + case IPC_ALLOW_PLAYTRACKING: break; +*/ + case IPC_HOOK_OKTOQUIT: + return 1; // Okay +/* + case IPC_WRITECONFIG: break; +*/ + case IPC_REGISTER_WINAMP_IPCMESSAGE: + { + // TODO convert to TCHAR???? + UINT res = RegisterWindowMessage( ( LPCTSTR )wp ); + + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "Message \"%s\" registered as #%i" ), wp, res ); + Console::Append( szBuffer ); + Console::Append( " " ); + + if( !stricmp( ( char * )wp, "GenHotkeysAdd" ) ) + { + IPC_GENHOTKEYS_ADD = res; + } + else if( !stricmp( ( char * )wp, "Dynamic Library" ) ) + { + ID_DYNAMICLIBRARY = res; + } + else if( !stricmp( ( char * )wp, "IPC_GETPLAINBARTARGET" ) ) + { + IPC_GETPLAINBARTARGET = res; + } + + return res; + } + + case 2006: // undocumented, name IPC_CB_HOTKEY or so later and ask to add this to sdk + { + // Hotkey sent as strings! + // Idea: make stl map + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "Hotkey \"%s\" detected" ), wp ); + Console::Append( szBuffer ); + Console::Append( " " ); + + return 1; // Accept??? + } + + default: + { + if( lp == IPC_GENHOTKEYS_ADD ) + { + break; + } + else if( lp == IPC_GETPLAINBARTARGET ) + { + return ( LRESULT )( IsWindowVisible( WindowVis ) ? WindowVis : NULL ); + } + + TCHAR szBuffer[ 5000 ]; + _stprintf( szBuffer, TEXT( "WM_WA_IPC <%i> <%i>" ), wp, lp ); + Console::Append( szBuffer ); + Console::Append( TEXT( "NOT handled" ) ); + Console::Append( " " ); + } + } + break; + + case WM_WA_MPEG_EOF: + Playback::NotifyTrackEnd(); + Playback::Next(); + break; + + case WM_COPYDATA: + { + if( !lp ) return FALSE; + + COPYDATASTRUCT * cds = ( COPYDATASTRUCT * )lp; + switch( cds->dwData ) + { + case IPC_PLAYFILE: + { + const int iLen = cds->cbData; + if( !iLen ) return FALSE; + TCHAR * szKeep = new TCHAR[ iLen + 1 ]; + ToTchar( szKeep, ( char * )cds->lpData, iLen ); + szKeep[ iLen ] = TEXT( '\0' ); + + playlist->PushBack( szKeep ); + return TRUE; + } + } + return FALSE; + } + + } + return DefWindowProc( hwnd, message, wp, lp ); +} diff --git a/Externals/MusicMod/Player/Src/Winamp.h b/Externals/MusicMod/Player/Src/Winamp.h new file mode 100644 index 0000000000..7ef4344c69 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Winamp.h @@ -0,0 +1,26 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef PA_WINAMP_H +#define PA_WINAMP_H + + + +#include "Global.h" + + + +LRESULT CALLBACK WndprocWinamp( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); + + + +#endif // PA_WINAMP_H diff --git a/Externals/MusicMod/Player/Src/Winamp/Dsp.h b/Externals/MusicMod/Player/Src/Winamp/Dsp.h new file mode 100644 index 0000000000..0c62886b8a --- /dev/null +++ b/Externals/MusicMod/Player/Src/Winamp/Dsp.h @@ -0,0 +1,47 @@ +#ifndef WINAMP_DSP_H +#define WINAMP_DSP_H + + +#include + +// DSP plugin interface + +// notes: +// any window that remains in foreground should optimally pass unused +// keystrokes to the parent (winamp's) window, so that the user +// can still control it. As for storing configuration, +// Configuration data should be stored in \plugin.ini +// (look at the vis plugin for configuration code) + +typedef struct winampDSPModule { + char *description; // description + HWND hwndParent; // parent window (filled in by calling app) + HINSTANCE hDllInstance; // instance handle to this DLL (filled in by calling app) + + void (*Config)(struct winampDSPModule *this_mod); // configuration dialog (if needed) + int (*Init)(struct winampDSPModule *this_mod); // 0 on success, creates window, etc (if needed) + + // modify waveform samples: returns number of samples to actually write + // (typically numsamples, but no more than twice numsamples, and no less than half numsamples) + // numsamples should always be at least 128. should, but I'm not sure + int (*ModifySamples)(struct winampDSPModule *this_mod, short int *samples, int numsamples, int bps, int nch, int srate); + + void (*Quit)(struct winampDSPModule *this_mod); // called when unloading + + void *userData; // user data, optional +} winampDSPModule; + +typedef struct { + int version; // DSP_HDRVER + char *description; // description of library + winampDSPModule* (*getModule)(int); // module retrieval function +} winampDSPHeader; + +// exported symbols +typedef winampDSPHeader* (*winampDSPGetHeaderType)(); + +// header version: 0x20 == 0.20 == winamp 2.0 +#define DSP_HDRVER 0x20 + + +#endif // WINAMP_DSP_H diff --git a/Externals/MusicMod/Player/Src/Winamp/Gen.h b/Externals/MusicMod/Player/Src/Winamp/Gen.h new file mode 100644 index 0000000000..7e3c5a2368 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Winamp/Gen.h @@ -0,0 +1,28 @@ +#ifndef WINAMP_GEN_H +#define WINAMP_GEN_H + + + +#define GPPHDR_VER 0x10 + + + +typedef struct { + int version; + char *description; + int (*init)(); + void (*config)(); + void (*quit)(); + HWND hwndParent; + HINSTANCE hDllInstance; +} winampGeneralPurposePlugin; + +typedef winampGeneralPurposePlugin * ( * winampGeneralPurposePluginGetter )(); + + + +// extern winampGeneralPurposePlugin * gen_plugins[ 256 ]; + + + +#endif // WINAMP_GEN_H diff --git a/Externals/MusicMod/Player/Src/Winamp/In2.h b/Externals/MusicMod/Player/Src/Winamp/In2.h new file mode 100644 index 0000000000..5f990eb2f3 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Winamp/In2.h @@ -0,0 +1,119 @@ +#ifndef WINAMP_IN_H +#define WINAMP_IN_H + + +#include +#include "out.h" + +// note: exported symbol is now winampGetInModule2. + +#define IN_VER 0x100 + + +// Changed +// void (*VSASetInfo)(int nch, int srate); +// to +// void (*VSASetInfo)(int srate, int nch); +// since the old one is wrong! +// +// Otherwise in_mp3 is the problem!?... + + +typedef struct +{ + int version; // module type (IN_VER) + char *description; // description of module, with version string + + HWND hMainWindow; // winamp's main window (filled in by winamp) + HINSTANCE hDllInstance; // DLL instance handle (Also filled in by winamp) + + char *FileExtensions; // "mp3\0Layer 3 MPEG\0mp2\0Layer 2 MPEG\0mpg\0Layer 1 MPEG\0" + // May be altered from Config, so the user can select what they want + + int is_seekable; // is this stream seekable? + int UsesOutputPlug; // does this plug-in use the output plug-ins? (musn't ever change, ever :) + + void (*Config)(HWND hwndParent); // configuration dialog + void (*About)(HWND hwndParent); // about dialog + + void (*Init)(); // called at program init + void (*Quit)(); // called at program quit + + void (*GetFileInfo)(char *file, char *title, int *length_in_ms); // if file == NULL, current playing is used + int (*InfoBox)(char *file, HWND hwndParent); + + int (*IsOurFile)(char *fn); // called before extension checks, to allow detection of mms://, etc + // playback stuff + int (*Play)(char *fn); // return zero on success, -1 on file-not-found, some other value on other (stopping winamp) error + void (*Pause)(); // pause stream + void (*UnPause)(); // unpause stream + int (*IsPaused)(); // ispaused? return 1 if paused, 0 if not + void (*Stop)(); // stop (unload) stream + + // time stuff + int (*GetLength)(); // get length in ms + int (*GetOutputTime)(); // returns current output time in ms. (usually returns outMod->GetOutputTime() + void (*SetOutputTime)(int time_in_ms); // seeks to point in stream (in ms). Usually you signal yoru thread to seek, which seeks and calls outMod->Flush().. + + // volume stuff + void (*SetVolume)(int volume); // from 0 to 255.. usually just call outMod->SetVolume + void (*SetPan)(int pan); // from -127 to 127.. usually just call outMod->SetPan + + // in-window builtin vis stuff + + void (*SAVSAInit)(int maxlatency_in_ms, int srate); // call once in Play(). maxlatency_in_ms should be the value returned from outMod->Open() + // call after opening audio device with max latency in ms and samplerate + void (*SAVSADeInit)(); // call in Stop() + + + // simple vis supplying mode + void (*SAAddPCMData)(void *PCMData, int nch, int bps, int timestamp); + // sets the spec data directly from PCM data + // quick and easy way to get vis working :) + // needs at least 576 samples :) + + // advanced vis supplying mode, only use if you're cool. Use SAAddPCMData for most stuff. + int (*SAGetMode)(); // gets csa (the current type (4=ws,2=osc,1=spec)) + // use when calling SAAdd() + void (*SAAdd)(void *data, int timestamp, int csa); // sets the spec data, filled in by winamp + + + // vis stuff (plug-in) + // simple vis supplying mode + void (*VSAAddPCMData)(void *PCMData, int nch, int bps, int timestamp); // sets the vis data directly from PCM data + // quick and easy way to get vis working :) + // needs at least 576 samples :) + + // advanced vis supplying mode, only use if you're cool. Use VSAAddPCMData for most stuff. + int (*VSAGetMode)(int *specNch, int *waveNch); // use to figure out what to give to VSAAdd + void (*VSAAdd)(void *data, int timestamp); // filled in by winamp, called by plug-in + + + // call this in Play() to tell the vis plug-ins the current output params. + void (*VSASetInfo)(int srate, int nch); + + + // dsp plug-in processing: + // (filled in by winamp, called by input plug) + + // returns 1 if active (which means that the number of samples returned by dsp_dosamples + // could be greater than went in.. Use it to estimate if you'll have enough room in the + // output buffer + int (*dsp_isactive)(); + + // returns number of samples to output. This can be as much as twice numsamples. + // be sure to allocate enough buffer for samples, then. + int (*dsp_dosamples)(short int *samples, int numsamples, int bps, int nch, int srate); + + + // eq stuff + void (*EQSet)(int on, char data[10], int preamp); // 0-64 each, 31 is +0, 0 is +12, 63 is -12. Do nothing to ignore. + + // info setting (filled in by winamp) + void (*SetInfo)(int bitrate, int srate, int stereo, int synched); // if -1, changes ignored? :) + + Out_Module *outMod; // filled in by winamp, optionally used :) +} In_Module; + + +#endif // WINAMP_IN_H diff --git a/Externals/MusicMod/Player/Src/Winamp/Out.h b/Externals/MusicMod/Player/Src/Winamp/Out.h new file mode 100644 index 0000000000..7653bcbd96 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Winamp/Out.h @@ -0,0 +1,69 @@ +#ifndef WINAMP_OUT_H +#define WINAMP_OUT_H + + +#include + + +// [ids] +// waveout 32 +// disk 33 +// dsound 38 +// xfade 63 +// gapless 64 +// null 65 +// mm2 69 + +#define OUT_VER 0x10 + +typedef struct +{ + int version; // module version (OUT_VER) + char *description; // description of module, with version string + int id; // module id. each input module gets its own. non-nullsoft modules should + // be >= 65536. + + HWND hMainWindow; // winamp's main window (filled in by winamp) + HINSTANCE hDllInstance; // DLL instance handle (filled in by winamp) + + void (*Config)(HWND hwndParent); // configuration dialog + void (*About)(HWND hwndParent); // about dialog + + void (*Init)(); // called when loaded + void (*Quit)(); // called when unloaded + + int (*Open)(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms); + // returns >=0 on success, <0 on failure + // NOTENOTENOTE: bufferlenms and prebufferms are ignored in most if not all output plug-ins. + // ... so don't expect the max latency returned to be what you asked for. + // returns max latency in ms (0 for diskwriters, etc) + // bufferlenms and prebufferms must be in ms. 0 to use defaults. + // prebufferms must be <= bufferlenms + + void (*Close)(); // close the ol' output device. + + int (*Write)(char *buf, int len); + // 0 on success. Len == bytes to write (<= 8192 always). buf is straight audio data. + // 1 returns not able to write (yet). Non-blocking, always. + + int (*CanWrite)(); // returns number of bytes possible to write at a given time. + // Never will decrease unless you call Write (or Close, heh) + + int (*IsPlaying)(); // non0 if output is still going or if data in buffers waiting to be + // written (i.e. closing while IsPlaying() returns 1 would truncate the song + + int (*Pause)(int pause); // returns previous pause state + + void (*SetVolume)(int volume); // volume is 0-255 + void (*SetPan)(int pan); // pan is -128 to 128 + + void (*Flush)(int t); // flushes buffers and restarts output at time t (in ms) + // (used for seeking) + + int (*GetOutputTime)(); // returns played time in MS + int (*GetWrittenTime)(); // returns time written in MS (used for synching up vis stuff) + +} Out_Module; + + +#endif // WINAMP_OUT_H diff --git a/Externals/MusicMod/Player/Src/Winamp/Vis.h b/Externals/MusicMod/Player/Src/Winamp/Vis.h new file mode 100644 index 0000000000..280830d7cd --- /dev/null +++ b/Externals/MusicMod/Player/Src/Winamp/Vis.h @@ -0,0 +1,57 @@ +#ifndef WINAMP_VIS_H +#define WINAMP_VIS_H + + +// notes: +// any window that remains in foreground should optimally pass +// keystrokes to the parent (winamp's) window, so that the user +// can still control it. unless escape is hit, or some option +// key specific to the vis is hit. As for storing configuration, +// Configuration data should be stored in \plugin.ini +// Look at the example plugin for a framework. + +// ints are 32 bits, and structure members are aligned on the default 8 byte boundaries +// tested with VC++ 4.2 and 5.0 + + +#include + + +typedef struct winampVisModule { + char *description; // description of module + HWND hwndParent; // parent window (filled in by calling app) + HINSTANCE hDllInstance; // instance handle to this DLL (filled in by calling app) + int sRate; // sample rate (filled in by calling app) + int nCh; // number of channels (filled in...) + int latencyMs; // latency from call of RenderFrame to actual drawing + // (calling app looks at this value when getting data) + int delayMs; // delay between calls in ms + + // the data is filled in according to the respective Nch entry + int spectrumNch; + int waveformNch; + unsigned char spectrumData[2][576]; + unsigned char waveformData[2][576]; + + void (*Config)(struct winampVisModule *this_mod); // configuration dialog + int (*Init)(struct winampVisModule *this_mod); // 0 on success, creates window, etc + int (*Render)(struct winampVisModule *this_mod); // returns 0 if successful, 1 if vis should end + void (*Quit)(struct winampVisModule *this_mod); // call when done + + void *userData; // user data, optional +} winampVisModule; + +typedef struct { + int version; // VID_HDRVER + char *description; // description of library + winampVisModule* (*getModule)(int); +} winampVisHeader; + +// exported symbols +typedef winampVisHeader* (*winampVisGetHeaderType)(); + +// version of current module (0x101 == 1.01) +#define VIS_HDRVER 0x101 + + +#endif // WINAMP_VIS_H diff --git a/Externals/MusicMod/Player/Src/Winamp/wa_ipc.h b/Externals/MusicMod/Player/Src/Winamp/wa_ipc.h new file mode 100644 index 0000000000..75689b9eea --- /dev/null +++ b/Externals/MusicMod/Player/Src/Winamp/wa_ipc.h @@ -0,0 +1,1103 @@ +/* +** Copyright (C) 2003 Nullsoft, Inc. +** +** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held +** liable for any damages arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to +** alter it and redistribute it freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. +** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +** +** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +** +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#ifndef _WA_IPC_H_ +#define _WA_IPC_H_ + +/* +** This is the modern replacement for the classic 'frontend.h'. Most of these +** updates are designed for in-process use, i.e. from a plugin. +** +*/ + +/* message used to sent many messages to winamp's main window. +** most all of the IPC_* messages involve sending the message in the form of: +** result = SendMessage(hwnd_winamp,WM_WA_IPC,(parameter),IPC_*); +*/ +#define WM_WA_IPC WM_USER +/* but some of them use WM_COPYDATA. be afraid. +*/ + +#define IPC_GETVERSION 0 +/* int version = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETVERSION); +** +** Version will be 0x20yx for winamp 2.yx. versions previous to Winamp 2.0 +** typically (but not always) use 0x1zyx for 1.zx versions. Weird, I know. +*/ + +#define IPC_GETREGISTEREDVERSION 770 + + +typedef struct { + char *filename; + char *title; + int length; +} enqueueFileWithMetaStruct; // send this to a IPC_PLAYFILE in a non WM_COPYDATA, +// and you get the nice desired result. if title is NULL, it is treated as a "thing", +// otherwise it's assumed to be a file (for speed) + +#define IPC_PLAYFILE 100 // dont be fooled, this is really the same as enqueufile +#define IPC_ENQUEUEFILE 100 +/* sent as a WM_COPYDATA, with IPC_PLAYFILE as the dwData, and the string to play +** as the lpData. Just enqueues, does not clear the playlist or change the playback +** state. +*/ + + +#define IPC_DELETE 101 +#define IPC_DELETE_INT 1101 // don't use this, it's used internally by winamp when + // dealing with some lame explorer issues. +/* SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_DELETE); +** Use IPC_DELETE to clear Winamp's internal playlist. +*/ + + +#define IPC_STARTPLAY 102 // starts playback. almost like hitting play in Winamp. +#define IPC_STARTPLAY_INT 1102 // used internally, don't bother using it (won't be any fun) + + +#define IPC_CHDIR 103 +/* sent as a WM_COPYDATA, with IPC_CHDIR as the dwData, and the directory to change to +** as the lpData. +*/ + + +#define IPC_ISPLAYING 104 +/* int res = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_ISPLAYING); +** If it returns 1, it is playing. if it returns 3, it is paused, +** if it returns 0, it is not playing. +*/ + + +#define IPC_GETOUTPUTTIME 105 +/* int res = SendMessage(hwnd_winamp,WM_WA_IPC,mode,IPC_GETOUTPUTTIME); +** returns the position in milliseconds of the current track (mode = 0), +** or the track length, in seconds (mode = 1). Returns -1 if not playing or error. +*/ + + +#define IPC_JUMPTOTIME 106 +/* (requires Winamp 1.60+) +** SendMessage(hwnd_winamp,WM_WA_IPC,ms,IPC_JUMPTOTIME); +** IPC_JUMPTOTIME sets the position in milliseconds of the +** current song (approximately). +** Returns -1 if not playing, 1 on eof, or 0 if successful +*/ + +#define IPC_GETMODULENAME 109 +#define IPC_EX_ISRIGHTEXE 666 +/* usually shouldnt bother using these, but here goes: +** send a WM_COPYDATA with IPC_GETMODULENAME, and an internal +** flag gets set, which if you send a normal WM_WA_IPC message with +** IPC_EX_ISRIGHTEXE, it returns whether or not that filename +** matches. lame, I know. +*/ + +#define IPC_WRITEPLAYLIST 120 +/* (requires Winamp 1.666+) +** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_WRITEPLAYLIST); +** +** IPC_WRITEPLAYLIST writes the current playlist to \\Winamp.m3u, +** and returns the current playlist position. +** Kinda obsoleted by some of the 2.x new stuff, but still good for when +** using a front-end (instead of a plug-in) +*/ + + +#define IPC_SETPLAYLISTPOS 121 +/* (requires Winamp 2.0+) +** SendMessage(hwnd_winamp,WM_WA_IPC,position,IPC_SETPLAYLISTPOS) +** IPC_SETPLAYLISTPOS sets the playlist position to 'position'. It +** does not change playback or anything, it just sets position, and +** updates the view if necessary +*/ + + +#define IPC_SETVOLUME 122 +/* (requires Winamp 2.0+) +** SendMessage(hwnd_winamp,WM_WA_IPC,volume,IPC_SETVOLUME); +** IPC_SETVOLUME sets the volume of Winamp (from 0-255). +*/ + + +#define IPC_SETPANNING 123 +/* (requires Winamp 2.0+) +** SendMessage(hwnd_winamp,WM_WA_IPC,panning,IPC_SETPANNING); +** IPC_SETPANNING sets the panning of Winamp (from 0 (left) to 255 (right)). +*/ + + +#define IPC_GETLISTLENGTH 124 +/* (requires Winamp 2.0+) +** int length = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTLENGTH); +** IPC_GETLISTLENGTH returns the length of the current playlist, in +** tracks. +*/ + + +#define IPC_GETLISTPOS 125 +/* (requires Winamp 2.05+) +** int pos=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTPOS); +** IPC_GETLISTPOS returns the playlist position. A lot like IPC_WRITEPLAYLIST +** only faster since it doesn't have to write out the list. Heh, silly me. +*/ + + +#define IPC_GETINFO 126 +/* (requires Winamp 2.05+) +** int inf=SendMessage(hwnd_winamp,WM_WA_IPC,mode,IPC_GETINFO); +** IPC_GETINFO returns info about the current playing song. The value +** it returns depends on the value of 'mode'. +** Mode Meaning +** ------------------ +** 0 Samplerate (i.e. 44100) +** 1 Bitrate (i.e. 128) +** 2 Channels (i.e. 2) +** 3 (5+) Video LOWORD=w HIWORD=h +** 4 (5+) > 65536, string (video description) +*/ + + +#define IPC_GETEQDATA 127 +/* (requires Winamp 2.05+) +** int data=SendMessage(hwnd_winamp,WM_WA_IPC,pos,IPC_GETEQDATA); +** IPC_GETEQDATA queries the status of the EQ. +** The value returned depends on what 'pos' is set to: +** Value Meaning +** ------------------ +** 0-9 The 10 bands of EQ data. 0-63 (+20db - -20db) +** 10 The preamp value. 0-63 (+20db - -20db) +** 11 Enabled. zero if disabled, nonzero if enabled. +** 12 Autoload. zero if disabled, nonzero if enabled. +*/ + + +#define IPC_SETEQDATA 128 +/* (requires Winamp 2.05+) +** SendMessage(hwnd_winamp,WM_WA_IPC,pos,IPC_GETEQDATA); +** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SETEQDATA); +** IPC_SETEQDATA sets the value of the last position retrieved +** by IPC_GETEQDATA. This is pretty lame, and we should provide +** an extended version that lets you do a MAKELPARAM(pos,value). +** someday... + + new (2.92+): + if the high byte is set to 0xDB, then the third byte specifies + which band, and the bottom word specifies the value. +*/ + +#define IPC_ADDBOOKMARK 129 +/* (requires Winamp 2.4+) +** Sent as a WM_COPYDATA, using IPC_ADDBOOKMARK, adds the specified +** file/url to the Winamp bookmark list. +*/ +/* +In winamp 5+, we use this as a normal WM_WA_IPC and the string: + + "filename\0title\0" + + to notify the library/bookmark editor that a bookmark +was added. Note that using this message in this context does not +actually add the bookmark. +do not use :) +*/ + + +#define IPC_INSTALLPLUGIN 130 +/* not implemented, but if it was you could do a WM_COPYDATA with +** a path to a .wpz, and it would install it. +*/ + + +#define IPC_RESTARTWINAMP 135 +/* (requires Winamp 2.2+) +** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_RESTARTWINAMP); +** IPC_RESTARTWINAMP will restart Winamp (isn't that obvious ? :) +*/ + + +#define IPC_ISFULLSTOP 400 +/* (requires winamp 2.7+ I think) +** ret=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_ISFULLSTOP); +** useful for when you're an output plugin, and you want to see +** if the stop/close is a full stop, or just between tracks. +** returns nonzero if it's full, zero if it's just a new track. +*/ + + +#define IPC_INETAVAILABLE 242 +/* (requires Winamp 2.05+) +** val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_INETAVAILABLE); +** IPC_INETAVAILABLE will return 1 if the Internet connection is available for Winamp. +*/ + + +#define IPC_UPDTITLE 243 +/* (requires Winamp 2.2+) +** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_UPDTITLE); +** IPC_UPDTITLE will ask Winamp to update the informations about the current title. +*/ + + +#define IPC_REFRESHPLCACHE 247 +/* (requires Winamp 2.2+) +** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_REFRESHPLCACHE); +** IPC_REFRESHPLCACHE will flush the playlist cache buffer. +** (send this if you want it to go refetch titles for tracks) +*/ + + +#define IPC_GET_SHUFFLE 250 +/* (requires Winamp 2.4+) +** val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_SHUFFLE); +** +** IPC_GET_SHUFFLE returns the status of the Shuffle option (1 if set) +*/ + + +#define IPC_GET_REPEAT 251 +/* (requires Winamp 2.4+) +** val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_REPEAT); +** +** IPC_GET_REPEAT returns the status of the Repeat option (1 if set) +*/ + + +#define IPC_SET_SHUFFLE 252 +/* (requires Winamp 2.4+) +** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SET_SHUFFLE); +** +** IPC_SET_SHUFFLE sets the status of the Shuffle option (1 to turn it on) +*/ + + +#define IPC_SET_REPEAT 253 +/* (requires Winamp 2.4+) +** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SET_REPEAT); +** +** IPC_SET_REPEAT sets the status of the Repeat option (1 to turn it on) +*/ + + +#define IPC_ENABLEDISABLE_ALL_WINDOWS 259 // 0xdeadbeef to disable +/* (requires Winamp 2.9+) +** SendMessage(hwnd_winamp,WM_WA_IPC,enable?0:0xdeadbeef,IPC_MBOPENREAL); +** sending with 0xdeadbeef as the param disables all winamp windows, +** any other values will enable all winamp windows. +*/ + + +#define IPC_GETWND 260 +/* (requires Winamp 2.9+) +** HWND h=SendMessage(hwnd_winamp,WM_WA_IPC,IPC_GETWND_xxx,IPC_GETWND); +** returns the HWND of the window specified. +*/ + #define IPC_GETWND_EQ 0 // use one of these for the param + #define IPC_GETWND_PE 1 + #define IPC_GETWND_MB 2 + #define IPC_GETWND_VIDEO 3 +#define IPC_ISWNDVISIBLE 261 // same param as IPC_GETWND + + + + +/************************************************************************ +***************** in-process only (WE LOVE PLUGINS) +************************************************************************/ + + +#define IPC_SETSKIN 200 +/* (requires Winamp 2.04+, only usable from plug-ins (not external apps)) +** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)"skinname",IPC_SETSKIN); +** IPC_SETSKIN sets the current skin to "skinname". Note that skinname +** can be the name of a skin, a skin .zip file, with or without path. +** If path isn't specified, the default search path is the winamp skins +** directory. +*/ + + +#define IPC_GETSKIN 201 +/* (requires Winamp 2.04+, only usable from plug-ins (not external apps)) +** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)skinname_buffer,IPC_GETSKIN); +** IPC_GETSKIN puts the directory where skin bitmaps can be found +** into skinname_buffer. +** skinname_buffer must be MAX_PATH characters in length. +** When using a .zip'd skin file, it'll return a temporary directory +** where the ZIP was decompressed. +*/ + + +#define IPC_EXECPLUG 202 +/* (requires Winamp 2.04+, only usable from plug-ins (not external apps)) +** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)"vis_file.dll",IPC_EXECPLUG); +** IPC_EXECPLUG executes a visualization plug-in pointed to by WPARAM. +** the format of this string can be: +** "vis_whatever.dll" +** "vis_whatever.dll,0" // (first mod, file in winamp plug-in dir) +** "C:\\dir\\vis_whatever.dll,1" +*/ + + +#define IPC_GETPLAYLISTFILE 211 +/* (requires Winamp 2.04+, only usable from plug-ins (not external apps)) +** char *name=SendMessage(hwnd_winamp,WM_WA_IPC,index,IPC_GETPLAYLISTFILE); +** IPC_GETPLAYLISTFILE gets the filename of the playlist entry [index]. +** returns a pointer to it. returns NULL on error. +*/ + + +#define IPC_GETPLAYLISTTITLE 212 +/* (requires Winamp 2.04+, only usable from plug-ins (not external apps)) +** char *name=SendMessage(hwnd_winamp,WM_WA_IPC,index,IPC_GETPLAYLISTTITLE); +** +** IPC_GETPLAYLISTTITLE gets the title of the playlist entry [index]. +** returns a pointer to it. returns NULL on error. +*/ + + +#define IPC_GETHTTPGETTER 240 +/* retrieves a function pointer to a HTTP retrieval function. +** if this is unsupported, returns 1 or 0. +** the function should be: +** int (*httpRetrieveFile)(HWND hwnd, char *url, char *file, char *dlgtitle); +** if you call this function, with a parent window, a URL, an output file, and a dialog title, +** it will return 0 on successful download, 1 on error. +*/ + + +#define IPC_MBOPEN 241 +/* (requires Winamp 2.05+) +** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_MBOPEN); +** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)url,IPC_MBOPEN); +** IPC_MBOPEN will open a new URL in the minibrowser. if url is NULL, it will open the Minibrowser window. +*/ + + + +#define IPC_CHANGECURRENTFILE 245 +/* (requires Winamp 2.05+) +** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)file,IPC_CHANGECURRENTFILE); +** IPC_CHANGECURRENTFILE will set the current playlist item. +*/ + + +#define IPC_GETMBURL 246 +/* (requires Winamp 2.2+) +** char buffer[4096]; // Urls can be VERY long +** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)buffer,IPC_GETMBURL); +** IPC_GETMBURL will retrieve the current Minibrowser URL into buffer. +** buffer must be at least 4096 bytes long. +*/ + + +#define IPC_MBBLOCK 248 +/* (requires Winamp 2.4+) +** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_MBBLOCK); +** +** IPC_MBBLOCK will block the Minibrowser from updates if value is set to 1 +*/ + +#define IPC_MBOPENREAL 249 +/* (requires Winamp 2.4+) +** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)url,IPC_MBOPENREAL); +** +** IPC_MBOPENREAL works the same as IPC_MBOPEN except that it will works even if +** IPC_MBBLOCK has been set to 1 +*/ + +#define IPC_ADJUST_OPTIONSMENUPOS 280 +/* (requires Winamp 2.9+) +** int newpos=SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)adjust_offset,IPC_ADJUST_OPTIONSMENUPOS); +** moves where winamp expects the Options menu in the main menu. Useful if you wish to insert a +** menu item above the options/skins/vis menus. +*/ + +#define IPC_GET_HMENU 281 +/* (requires Winamp 2.9+) +** HMENU hMenu=SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)0,IPC_GET_HMENU); +** values for data: +** 0 : main popup menu +** 1 : main menubar file menu +** 2 : main menubar options menu +** 3 : main menubar windows menu +** 4 : main menubar help menu +** other values will return NULL. +*/ + +#define IPC_GET_EXTENDED_FILE_INFO 290 //pass a pointer to the following struct in wParam +#define IPC_GET_EXTENDED_FILE_INFO_HOOKABLE 296 +/* (requires Winamp 2.9+) +** to use, create an extendedFileInfoStruct, point the values filename and metadata to the +** filename and metadata field you wish to query, and ret to a buffer, with retlen to the +** length of that buffer, and then SendMessage(hwnd_winamp,WM_WA_IPC,&struct,IPC_GET_EXTENDED_FILE_INFO); +** the results should be in the buffer pointed to by ret. +** returns 1 if the decoder supports a getExtendedFileInfo method +*/ +typedef struct { + char *filename; + char *metadata; + char *ret; + int retlen; +} extendedFileInfoStruct; + +#define IPC_GET_BASIC_FILE_INFO 291 //pass a pointer to the following struct in wParam +typedef struct { + char *filename; + + int quickCheck; // set to 0 to always get, 1 for quick, 2 for default (if 2, quickCheck will be set to 0 if quick wasnot used) + + // filled in by winamp + int length; + char *title; + int titlelen; +} basicFileInfoStruct; + +#define IPC_GET_EXTLIST 292 //returns doublenull delimited. GlobalFree() it when done. if data is 0, returns raw extlist, if 1, returns something suitable for getopenfilename + +#define IPC_INFOBOX 293 +typedef struct { + HWND parent; + char *filename; +} infoBoxParam; + +#define IPC_SET_EXTENDED_FILE_INFO 294 //pass a pointer to the a extendedFileInfoStruct in wParam +/* (requires Winamp 2.9+) +** to use, create an extendedFileInfoStruct, point the values filename and metadata to the +** filename and metadata field you wish to write in ret. (retlen is not used). and then +** SendMessage(hwnd_winamp,WM_WA_IPC,&struct,IPC_SET_EXTENDED_FILE_INFO); +** returns 1 if the metadata is supported +** Call IPC_WRITE_EXTENDED_FILE_INFO once you're done setting all the metadata you want to update +*/ + +#define IPC_WRITE_EXTENDED_FILE_INFO 295 +/* (requires Winamp 2.9+) +** writes all the metadata set thru IPC_SET_EXTENDED_FILE_INFO to the file +** returns 1 if the file has been successfully updated, 0 if error +*/ + +#define IPC_FORMAT_TITLE 297 +typedef struct +{ + char *spec; // NULL=default winamp spec + void *p; + + char *out; + int out_len; + + char * (*TAGFUNC)(char * tag, void * p); //return 0 if not found + void (*TAGFREEFUNC)(char * tag,void * p); +} waFormatTitle; + +#define IPC_GETUNCOMPRESSINTERFACE 331 +/* returns a function pointer to uncompress(). +** int (*uncompress)(unsigned char *dest, unsigned long *destLen, const unsigned char *source, unsigned long sourceLen); +** right out of zlib, useful for decompressing zlibbed data. +** if you pass the parm of 0x10100000, it will return a wa_inflate_struct * to an inflate API. +*/ + +typedef struct { + int (*inflateReset)(void *strm); + int (*inflateInit_)(void *strm,const char *version, int stream_size); + int (*inflate)(void *strm, int flush); + int (*inflateEnd)(void *strm); + unsigned long (*crc32)(unsigned long crc, const unsigned char *buf, unsigned int len); +} wa_inflate_struct; + + +#define IPC_ADD_PREFS_DLG 332 +#define IPC_REMOVE_PREFS_DLG 333 +/* (requires Winamp 2.9+) +** to use, allocate a prefsDlgRec structure (either on the heap or some global +** data, but NOT on the stack), initialze the members: +** hInst to the DLL instance where the resource is located +** dlgID to the ID of the dialog, +** proc to the window procedure for the dialog +** name to the name of the prefs page in the prefs. +** where to 0 (eventually we may add more options) +** then, SendMessage(hwnd_winamp,WM_WA_IPC,&prefsRec,IPC_ADD_PREFS_DLG); +** +** you can also IPC_REMOVE_PREFS_DLG with the address of the same prefsRec, +** but you shouldn't really ever have to. +** +*/ +#define IPC_OPENPREFSTOPAGE 380 // pass an id of a builtin page, or a &prefsDlgRec of prefs page to open + +typedef struct _prefsDlgRec { + HINSTANCE hInst; + int dlgID; + void *proc; + + char *name; + int where; // 0 for options, 1 for plugins, 2 for skins, 3 for bookmarks, 4 for prefs + + + int _id; + struct _prefsDlgRec *next; +} prefsDlgRec; + + +#define IPC_GETINIFILE 334 // returns a pointer to winamp.ini +#define IPC_GETINIDIRECTORY 335 // returns a pointer to the directory to put config files in (if you dont want to use winamp.ini) + +#define IPC_SPAWNBUTTONPOPUP 361 // param = +// 0 = eject +// 1 = previous +// 2 = next +// 3 = pause +// 4 = play +// 5 = stop + +#define IPC_OPENURLBOX 360 // pass a HWND to a parent, returns a HGLOBAL that needs to be freed with GlobalFree(), if successful +#define IPC_OPENFILEBOX 362 // pass a HWND to a parent +#define IPC_OPENDIRBOX 363 // pass a HWND to a parent + +// pass an HWND to a parent. call this if you take over the whole UI so that the dialogs are not appearing on the +// bottom right of the screen since the main winamp window is at 3000x3000, call again with NULL to reset +#define IPC_SETDIALOGBOXPARENT 364 + + + +// pass 0 for a copy of the skin HBITMAP +// pass 1 for name of font to use for playlist editor likeness +// pass 2 for font charset +// pass 3 for font size +#define IPC_GET_GENSKINBITMAP 503 + + +#define IPC_GET_EMBEDIF 505 // pass an embedWindowState +// returns an HWND embedWindow(embedWindowState *); if the data is NULL, otherwise returns the HWND directly +typedef struct +{ + HWND me; //hwnd of the window + + int flags; + + RECT r; + + void *user_ptr; // for application use + + int extra_data[64]; // for internal winamp use +} embedWindowState; + +#define EMBED_FLAGS_NORESIZE 1 // set this bit in embedWindowState.flags to keep window from being resizable +#define EMBED_FLAGS_NOTRANSPARENCY 2 // set this bit in embedWindowState.flags to make gen_ff turn transparency off for this wnd + + +#define IPC_EMBED_ENUM 532 +typedef struct embedEnumStruct +{ + int (*enumProc)(embedWindowState *ws, struct embedEnumStruct *param); // return 1 to abort + int user_data; // or more :) +} embedEnumStruct; + // pass + +#define IPC_EMBED_ISVALID 533 + +#define IPC_CONVERTFILE 506 +/* (requires Winamp 2.92+) +** Converts a given file to a different format (PCM, MP3, etc...) +** To use, pass a pointer to a waFileConvertStruct struct +** This struct can be either on the heap or some global +** data, but NOT on the stack. At least, until the conversion is done. +** +** eg: SendMessage(hwnd_winamp,WM_WA_IPC,&myConvertStruct,IPC_CONVERTFILE); +** +** Return value: +** 0: Can't start the conversion. Look at myConvertStruct->error for details. +** 1: Conversion started. Status messages will be sent to the specified callbackhwnd. +** Be sure to call IPC_CONVERTFILE_END when your callback window receives the +** IPC_CB_CONVERT_DONE message. +*/ +typedef struct +{ + char *sourcefile; // "c:\\source.mp3" + char *destfile; // "c:\\dest.pcm" + int destformat[8]; // like 'PCM ',srate,nch,bps + HWND callbackhwnd; // window that will receive the IPC_CB_CONVERT notification messages + + //filled in by winamp.exe + char *error; //if IPC_CONVERTFILE returns 0, the reason will be here + + int bytes_done; //you can look at both of these values for speed statistics + int bytes_total; + int bytes_out; + + int killswitch; // don't set it manually, use IPC_CONVERTFILE_END + int extra_data[64]; // for internal winamp use +} convertFileStruct; + +#define IPC_CONVERTFILE_END 507 +/* (requires Winamp 2.92+) +** Stop/ends a convert process started from IPC_CONVERTFILE +** You need to call this when you receive the IPC_CB_CONVERTDONE message or when you +** want to abort a conversion process +** +** eg: SendMessage(hwnd_winamp,WM_WA_IPC,&myConvertStruct,IPC_CONVERTFILE_END); +** +** No return value +*/ + +typedef struct { + HWND hwndParent; + int format; + + //filled in by winamp.exe + HWND hwndConfig; + int extra_data[8]; +} convertConfigStruct; +#define IPC_CONVERT_CONFIG 508 +#define IPC_CONVERT_CONFIG_END 509 + +typedef struct +{ + void (*enumProc)(int user_data, const char *desc, int fourcc); + int user_data; +} converterEnumFmtStruct; +#define IPC_CONVERT_CONFIG_ENUMFMTS 510 +/* (requires Winamp 2.92+) +*/ + + +typedef struct +{ + char cdletter; + char *playlist_file; + HWND callback_hwnd; + + //filled in by winamp.exe + char *error; +} burnCDStruct; +#define IPC_BURN_CD 511 +/* (requires Winamp 5.0+) +*/ + +typedef struct +{ + convertFileStruct *cfs; + int priority; +} convertSetPriority; +#define IPC_CONVERT_SET_PRIORITY 512 + +typedef struct +{ + char *filename; + char *title; // 2048 bytes + int length; + int force_useformatting; // can set this to 1 if you want to force a url to use title formatting shit +} waHookTitleStruct; +// return TRUE if you hook this +#define IPC_HOOK_TITLES 850 + +#define IPC_GETSADATAFUNC 800 +// 0: returns a char *export_sa_get() that returns 150 bytes of data +// 1: returns a export_sa_setreq(int want); + +#define IPC_ISMAINWNDVISIBLE 900 + + +#define IPC_SETPLEDITCOLORS 920 +typedef struct +{ + int numElems; + int *elems; + HBITMAP bm; // set if you want to override +} waSetPlColorsStruct; + + +// the following IPC use waSpawnMenuParms as parameter +#define IPC_SPAWNEQPRESETMENU 933 +#define IPC_SPAWNFILEMENU 934 //menubar +#define IPC_SPAWNOPTIONSMENU 935 //menubar +#define IPC_SPAWNWINDOWSMENU 936 //menubar +#define IPC_SPAWNHELPMENU 937 //menubar +#define IPC_SPAWNPLAYMENU 938 //menubar +#define IPC_SPAWNPEFILEMENU 939 //menubar +#define IPC_SPAWNPEPLAYLISTMENU 940 //menubar +#define IPC_SPAWNPESORTMENU 941 //menubar +#define IPC_SPAWNPEHELPMENU 942 //menubar +#define IPC_SPAWNMLFILEMENU 943 //menubar +#define IPC_SPAWNMLVIEWMENU 944 //menubar +#define IPC_SPAWNMLHELPMENU 945 //menubar +#define IPC_SPAWNPELISTOFPLAYLISTS 946 + +typedef struct +{ + HWND wnd; + int xpos; // in screen coordinates + int ypos; +} waSpawnMenuParms; + +// waSpawnMenuParms2 is used by the menubar submenus +typedef struct +{ + HWND wnd; + int xpos; // in screen coordinates + int ypos; + int width; + int height; +} waSpawnMenuParms2; + + +// system tray sends this (you might want to simulate it) +#define WM_WA_SYSTRAY WM_USER+1 + +// input plugins send this when they are done playing back +#define WM_WA_MPEG_EOF WM_USER+2 + + + +//// video stuff + +#define IPC_IS_PLAYING_VIDEO 501 // returns >1 if playing, 0 if not, 1 if old version (so who knows):) +#define IPC_GET_IVIDEOOUTPUT 500 // see below for IVideoOutput interface +#define VIDEO_MAKETYPE(A,B,C,D) ((A) | ((B)<<8) | ((C)<<16) | ((D)<<24)) +#define VIDUSER_SET_INFOSTRING 0x1000 +#define VIDUSER_GET_VIDEOHWND 0x1001 +#define VIDUSER_SET_VFLIP 0x1002 +#define VIDUSER_SET_TRACKSELINTERFACE 0x1003 // give your ITrackSelector interface as param2 + +#ifndef NO_IVIDEO_DECLARE +#ifdef __cplusplus + +class VideoOutput; +class SubsItem; + +typedef struct { + unsigned char* baseAddr; + long rowBytes; +} YV12_PLANE; + +typedef struct { + YV12_PLANE y; + YV12_PLANE u; + YV12_PLANE v; +} YV12_PLANES; + +class IVideoOutput +{ + public: + virtual ~IVideoOutput() { } + virtual int open(int w, int h, int vflip, double aspectratio, unsigned int fmt)=0; + virtual void setcallback(LRESULT (*msgcallback)(void *token, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam), void *token) { } + virtual void close()=0; + virtual void draw(void *frame)=0; + virtual void drawSubtitle(SubsItem *item) { } + virtual void showStatusMsg(const char *text) { } + virtual int get_latency() { return 0; } + virtual void notifyBufferState(int bufferstate) { } /* 0-255*/ + + virtual int extended(int param1, int param2, int param3) { return 0; } // Dispatchable, eat this! +}; + +class ITrackSelector +{ + public: + virtual int getNumAudioTracks()=0; + virtual void enumAudioTrackName(int n, const char *buf, int size)=0; + virtual int getCurAudioTrack()=0; + virtual int getNumVideoTracks()=0; + virtual void enumVideoTrackName(int n, const char *buf, int size)=0; + virtual int getCurVideoTrack()=0; + + virtual void setAudioTrack(int n)=0; + virtual void setVideoTrack(int n)=0; +}; + +#endif //cplusplus +#endif//NO_IVIDEO_DECLARE + +// these messages are callbacks that you can grab by subclassing the winamp window + +// wParam = +#define IPC_CB_WND_EQ 0 // use one of these for the param +#define IPC_CB_WND_PE 1 +#define IPC_CB_WND_MB 2 +#define IPC_CB_WND_VIDEO 3 +#define IPC_CB_WND_MAIN 4 + +#define IPC_CB_ONSHOWWND 600 +#define IPC_CB_ONHIDEWND 601 + +#define IPC_CB_GETTOOLTIP 602 + +#define IPC_CB_MISC 603 + #define IPC_CB_MISC_TITLE 0 + #define IPC_CB_MISC_VOLUME 1 // volume/pan + #define IPC_CB_MISC_STATUS 2 + #define IPC_CB_MISC_EQ 3 + #define IPC_CB_MISC_INFO 4 + #define IPC_CB_MISC_VIDEOINFO 5 + +#define IPC_CB_CONVERT_STATUS 604 // param value goes from 0 to 100 (percent) +#define IPC_CB_CONVERT_DONE 605 + +#define IPC_ADJUST_FFWINDOWSMENUPOS 606 +/* (requires Winamp 2.9+) +** int newpos=SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)adjust_offset,IPC_ADJUST_FFWINDOWSMENUPOS); +** moves where winamp expects the freeform windows in the menubar windows main menu. Useful if you wish to insert a +** menu item above extra freeform windows. +*/ + +#define IPC_ISDOUBLESIZE 608 + +#define IPC_ADJUST_FFOPTIONSMENUPOS 609 +/* (requires Winamp 2.9+) +** int newpos=SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)adjust_offset,IPC_ADJUST_FFOPTIONSMENUPOS); +** moves where winamp expects the freeform preferences item in the menubar windows main menu. Useful if you wish to insert a +** menu item above preferences item. +*/ + +#define IPC_GETTIMEDISPLAYMODE 610 // returns 0 if displaying elapsed time or 1 if displaying remaining time + +#define IPC_SETVISWND 611 // param is hwnd, setting this allows you to receive ID_VIS_NEXT/PREVOUS/RANDOM/FS wm_commands +#define ID_VIS_NEXT 40382 +#define ID_VIS_PREV 40383 +#define ID_VIS_RANDOM 40384 +#define ID_VIS_FS 40389 +#define ID_VIS_CFG 40390 +#define ID_VIS_MENU 40391 + +#define IPC_GETVISWND 612 // returns the vis cmd handler hwnd +#define IPC_ISVISRUNNING 613 +#define IPC_CB_VISRANDOM 628 // param is status of random + +#define IPC_SETIDEALVIDEOSIZE 614 // sent by winamp to winamp, trap it if you need it. width=HIWORD(param), height=LOWORD(param) + +#define IPC_GETSTOPONVIDEOCLOSE 615 +#define IPC_SETSTOPONVIDEOCLOSE 616 + +typedef struct { + HWND hwnd; + int uMsg; + int wParam; + int lParam; +} transAccelStruct; + +#define IPC_TRANSLATEACCELERATOR 617 + +typedef struct { + int cmd; + int x; + int y; + int align; +} windowCommand; // send this as param to an IPC_PLCMD, IPC_MBCMD, IPC_VIDCMD + +#define IPC_CB_ONTOGGLEAOT 618 + +#define IPC_GETPREFSWND 619 + +#define IPC_SET_PE_WIDTHHEIGHT 620 // data is a pointer to a POINT structure that holds width & height + +#define IPC_GETLANGUAGEPACKINSTANCE 621 + +#define IPC_CB_PEINFOTEXT 622 // data is a string, ie: "04:21/45:02" + +#define IPC_CB_OUTPUTCHANGED 623 // output plugin was changed in config + +#define IPC_GETOUTPUTPLUGIN 625 + +#define IPC_SETDRAWBORDERS 626 +#define IPC_DISABLESKINCURSORS 627 +#define IPC_CB_RESETFONT 629 + +#define IPC_IS_FULLSCREEN 630 // returns 1 if video or vis is in fullscreen mode +#define IPC_SET_VIS_FS_FLAG 631 // a vis should send this message with 1/as param to notify winamp that it has gone to or has come back from fullscreen mode + +#define IPC_SHOW_NOTIFICATION 632 + +#define IPC_GETSKININFO 633 + +#define IPC_GET_MANUALPLADVANCE 634 +/* (requires Winamp 5.03+) +** val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_MANUALPLADVANCE); +** +** IPC_GET_MANUALPLADVANCE returns the status of the Manual Playlist Advance (1 if set) +*/ + +#define IPC_SET_MANUALPLADVANCE 635 +/* (requires Winamp 5.03+) +** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SET_MANUALPLADVANCE); +** +** IPC_SET_MANUALPLADVANCE sets the status of the Manual Playlist Advance option (1 to turn it on) +*/ + +#define IPC_GET_NEXT_PLITEM 636 +/* (requires Winamp 5.04+) +** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_EOF_GET_NEXT_PLITEM); +** +** Sent to Winamp's main window when an item has just finished playback or the next button has been pressed and +** requesting the new playlist item number to go to. +** Mainly used by gen_jumpex. Subclass this message in your application to return the new item number. +** -1 for normal winamp operation (default) or the new item number in the playlist to play. +*/ + +#define IPC_GET_PREVIOUS_PLITEM 637 +/* (requires Winamp 5.04+) +** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_EOF_GET_PREVIOUS_PLITEM); +** +** Sent to Winamp's main window when the previous button has been pressed and Winamp is requesting the new playlist item number to go to. +** Mainly used by gen_jumpex. Subclass this message in your application to return the new item number. +** -1 for normal winamp operation (default) or the new item number in the playlist to play. +*/ + +#define IPC_IS_WNDSHADE 638 +/* (requires Winamp 5.04+) +** SendMessage(hwnd_winamp,WM_WA_IPC,wnd,IPC_IS_WNDSHADE); +** +** 'wnd' is window id as defined for IPC_GETWND, or -1 for main window +** Returns 1 if wnd is set to winshade mode, or 0 if it is not +*/ + +#define IPC_SETRATING 639 +/* (requires Winamp 5.04+ with ML) +** SendMessage(hwnd_winamp,WM_WA_IPC,rating,IPC_SETRATING); +** 'rating' is an int value from 0 (no rating) to 5 +*/ + +#define IPC_GETRATING 640 +/* (requires Winamp 5.04+ with ML) +** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETRATING); +** returns the current item's rating +*/ + +#define IPC_GETNUMAUDIOTRACKS 641 +/* (requires Winamp 5.04+) +** int n = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETNUMAUDIOTRACKS); +** returns the number of audio tracks for the currently playing item +*/ + +#define IPC_GETNUMVIDEOTRACKS 642 +/* (requires Winamp 5.04+) +** int n = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETNUMVIDEOTRACKS); +** returns the number of video tracks for the currently playing item +*/ + +#define IPC_GETAUDIOTRACK 643 +/* (requires Winamp 5.04+) +** int cur = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETAUDIOTRACK); +** returns the id of the current audio track for the currently playing item +*/ + +#define IPC_GETVIDEOTRACK 644 +/* (requires Winamp 5.04+) +** int cur = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETVIDEOTRACK); +** returns the id of the current video track for the currently playing item +*/ + +#define IPC_SETAUDIOTRACK 645 +/* (requires Winamp 5.04+) +** SendMessage(hwnd_winamp,WM_WA_IPC,track,IPC_SETAUDIOTRACK); +** switch the currently playing item to a new audio track +*/ + +#define IPC_SETVIDEOTRACK 646 +/* (requires Winamp 5.04+) +** SendMessage(hwnd_winamp,WM_WA_IPC,track,IPC_SETVIDEOTRACK); +** switch the currently playing item to a new video track +*/ + +#define IPC_PUSH_DISABLE_EXIT 647 +/* (requires Winamp 5.04+) +** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_PUSH_DISABLE_EXIT ); +** lets you disable or re-enable the UI exit functions (close button, +** context menu, alt-f4). +** call IPC_POP_DISABLE_EXIT when you are done doing whatever required +** preventing exit +*/ + +#define IPC_POP_DISABLE_EXIT 648 +/* (requires Winamp 5.04+) +** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_POP_DISABLE_EXIT ); +** see IPC_PUSH_DISABLE_EXIT +*/ + +#define IPC_IS_EXIT_ENABLED 649 +/* (requires Winamp 5.04+) +** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_IS_EXIT_ENABLED); +** returns 0 if exit is disabled, 1 otherwise +*/ + +#define IPC_IS_AOT 650 +/* (requires Winamp 5.04+) +** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_IS_AOT); +** returns status of always on top flag. note: this may not match the actual +** TOPMOST window flag while another fullscreen application is focused +*/ + + +// >>>>>>>>>>> Next is 651 + +#define IPC_PLCMD 1000 + +#define PLCMD_ADD 0 +#define PLCMD_REM 1 +#define PLCMD_SEL 2 +#define PLCMD_MISC 3 +#define PLCMD_LIST 4 + +#define IPC_MBCMD 1001 + +#define MBCMD_BACK 0 +#define MBCMD_FORWARD 1 +#define MBCMD_STOP 2 +#define MBCMD_RELOAD 3 +#define MBCMD_MISC 4 + +#define IPC_VIDCMD 1002 + +#define VIDCMD_FULLSCREEN 0 +#define VIDCMD_1X 1 +#define VIDCMD_2X 2 +#define VIDCMD_LIB 3 +#define VIDPOPUP_MISC 4 + +#define IPC_MBURL 1003 //sets the URL +#define IPC_MBGETCURURL 1004 //copies the current URL into wParam (have a 4096 buffer ready) +#define IPC_MBGETDESC 1005 //copies the current URL description into wParam (have a 4096 buffer ready) +#define IPC_MBCHECKLOCFILE 1006 //checks that the link file is up to date (otherwise updates it). wParam=parent HWND +#define IPC_MBREFRESH 1007 //refreshes the "now playing" view in the library +#define IPC_MBGETDEFURL 1008 //copies the default URL into wParam (have a 4096 buffer ready) + +#define IPC_STATS_LIBRARY_ITEMCNT 1300 // updates library count status + +// IPC 2000-3000 reserved for freeform messages, see gen_ff/ff_ipc.h +#define IPC_FF_FIRST 2000 +#define IPC_FF_LAST 3000 + +#define IPC_GETDROPTARGET 3001 + +#define IPC_PLAYLIST_MODIFIED 3002 // sent to main wnd whenever the playlist is modified + +#define IPC_PLAYING_FILE 3003 // sent to main wnd with the file as parm whenever a file is played +#define IPC_FILE_TAG_MAY_HAVE_UPDATED 3004 // sent to main wnd with the file as parm whenever a file tag might be updated + + +#define IPC_ALLOW_PLAYTRACKING 3007 +// send nonzero to allow, zero to disallow + +#define IPC_HOOK_OKTOQUIT 3010 // return 0 to abort a quit, nonzero if quit is OK + +#define IPC_WRITECONFIG 3011 // pass 2 to write all, 1 to write playlist + common, 0 to write common+less common + +// pass a string to be the name to register, and returns a value > 65536, which is a unique value you can use +// for custom WM_WA_IPC messages. +#define IPC_REGISTER_WINAMP_IPCMESSAGE 65536 + + + +#endif //_WA_IPC_H_ diff --git a/Externals/MusicMod/Player/Src/Winamp/wa_msgids.h b/Externals/MusicMod/Player/Src/Winamp/wa_msgids.h new file mode 100644 index 0000000000..ae5ce4fe7b --- /dev/null +++ b/Externals/MusicMod/Player/Src/Winamp/wa_msgids.h @@ -0,0 +1,297 @@ +/* +** wa_msgids.h (created 14/04/2004 12:23:19 PM) +** Created from wa_ipc.h and resource.h from the language sdk +** by Darren Owen aka DrO +** +** This a simple header file which defines the message ids to allow you to control +** Winamp in keeping with the old frontend.h (R.I.P.) +** +** +** Version History: +** +** v1.0 :: intial version with ids for Winamp 5.02+ +** v1.0a :: fixed the file to work on compile +** v1.1 :: added the msg id for 'Manual Playlist Advance' +** v1.2 :: added in song rating menu items +** +** +** How to use: +** +** To send these, use the format: +** +** SendMessage(hwnd_winamp, WM_COMMAND,command_name,0); +** +** For other languages such as Visual Basic, Pascal, etc you will need to use +** the equivalent calling SendMessage(..) calling convention +** +** +** Notes: +** +** IDs 42000 to 45000 are reserved for gen_ff +** IDs from 45000 to 57000 are reserved for library +** +*/ + +#ifndef _WA_MSGIDS_H_ +#define _WA_MSGIDS_H_ + +#define WINAMP_FILE_QUIT 40001 +#define WINAMP_OPTIONS_PREFS 40012 // pops up the preferences +#define WINAMP_OPTIONS_AOT 40019 // toggles always on top +#define WINAMP_FILE_REPEAT 40022 +#define WINAMP_FILE_SHUFFLE 40023 +#define WINAMP_HIGH_PRIORITY 40025 +#define WINAMP_FILE_PLAY 40029 // pops up the load file(s) box +#define WINAMP_OPTIONS_EQ 40036 // toggles the EQ window +#define WINAMP_OPTIONS_ELAPSED 40037 +#define WINAMP_OPTIONS_REMAINING 40038 +#define WINAMP_OPTIONS_PLEDIT 40040 // toggles the playlist window +#define WINAMP_HELP_ABOUT 40041 // pops up the about box :) +#define WINAMP_MAINMENU 40043 + + +/* the following are the five main control buttons, with optionally shift +** or control pressed +** (for the exact functions of each, just try it out) +*/ +#define WINAMP_BUTTON1 40044 +#define WINAMP_BUTTON2 40045 +#define WINAMP_BUTTON3 40046 +#define WINAMP_BUTTON4 40047 +#define WINAMP_BUTTON5 40048 +#define WINAMP_BUTTON1_SHIFT 40144 +#define WINAMP_BUTTON2_SHIFT 40145 +#define WINAMP_BUTTON3_SHIFT 40146 +#define WINAMP_BUTTON4_SHIFT 40147 +#define WINAMP_BUTTON5_SHIFT 40148 +#define WINAMP_BUTTON1_CTRL 40154 +#define WINAMP_BUTTON2_CTRL 40155 +#define WINAMP_BUTTON3_CTRL 40156 +#define WINAMP_BUTTON4_CTRL 40157 +#define WINAMP_BUTTON5_CTRL 40158 + +#define WINAMP_VOLUMEUP 40058 // turns the volume up a little +#define WINAMP_VOLUMEDOWN 40059 // turns the volume down a little +#define WINAMP_FFWD5S 40060 // fast forwards 5 seconds +#define WINAMP_REW5S 40061 // rewinds 5 seconds +#define WINAMP_NEXT_WINDOW 40063 +#define WINAMP_OPTIONS_WINDOWSHADE 40064 +#define WINAMP_OPTIONS_DSIZE 40165 +#define IDC_SORT_FILENAME 40166 +#define IDC_SORT_FILETITLE 40167 +#define IDC_SORT_ENTIREFILENAME 40168 +#define IDC_SELECTALL 40169 +#define IDC_SELECTNONE 40170 +#define IDC_SELECTINV 40171 +#define IDM_EQ_LOADPRE 40172 +#define IDM_EQ_LOADMP3 40173 +#define IDM_EQ_LOADDEFAULT 40174 +#define IDM_EQ_SAVEPRE 40175 +#define IDM_EQ_SAVEMP3 40176 +#define IDM_EQ_SAVEDEFAULT 40177 +#define IDM_EQ_DELPRE 40178 +#define IDM_EQ_DELMP3 40180 +#define IDC_PLAYLIST_PLAY 40184 +#define WINAMP_FILE_LOC 40185 +#define WINAMP_OPTIONS_EASYMOVE 40186 +#define WINAMP_FILE_DIR 40187 // pops up the load directory box +#define WINAMP_EDIT_ID3 40188 +#define WINAMP_TOGGLE_AUTOSCROLL 40189 +#define WINAMP_VISSETUP 40190 +#define WINAMP_PLGSETUP 40191 +#define WINAMP_VISPLUGIN 40192 +#define WINAMP_JUMP 40193 +#define WINAMP_JUMPFILE 40194 +#define WINAMP_JUMP10FWD 40195 +#define WINAMP_JUMP10BACK 40197 +#define WINAMP_PREVSONG 40198 +#define WINAMP_OPTIONS_EXTRAHQ 40200 +#define ID_PE_NEW 40201 +#define ID_PE_OPEN 40202 +#define ID_PE_SAVE 40203 +#define ID_PE_SAVEAS 40204 +#define ID_PE_SELECTALL 40205 +#define ID_PE_INVERT 40206 +#define ID_PE_NONE 40207 +#define ID_PE_ID3 40208 +#define ID_PE_S_TITLE 40209 +#define ID_PE_S_FILENAME 40210 +#define ID_PE_S_PATH 40211 +#define ID_PE_S_RANDOM 40212 +#define ID_PE_S_REV 40213 +#define ID_PE_CLEAR 40214 +#define ID_PE_MOVEUP 40215 +#define ID_PE_MOVEDOWN 40216 +#define WINAMP_SELSKIN 40219 +#define WINAMP_VISCONF 40221 +#define ID_PE_NONEXIST 40222 +#define ID_PE_DELETEFROMDISK 40223 +#define ID_PE_CLOSE 40224 +#define WINAMP_VIS_SETOSC 40226 +#define WINAMP_VIS_SETANA 40227 +#define WINAMP_VIS_SETOFF 40228 +#define WINAMP_VIS_DOTSCOPE 40229 +#define WINAMP_VIS_LINESCOPE 40230 +#define WINAMP_VIS_SOLIDSCOPE 40231 +#define WINAMP_VIS_NORMANA 40233 +#define WINAMP_VIS_FIREANA 40234 +#define WINAMP_VIS_LINEANA 40235 +#define WINAMP_VIS_NORMVU 40236 +#define WINAMP_VIS_SMOOTHVU 40237 +#define WINAMP_VIS_FULLREF 40238 +#define WINAMP_VIS_FULLREF2 40239 +#define WINAMP_VIS_FULLREF3 40240 +#define WINAMP_VIS_FULLREF4 40241 +#define WINAMP_OPTIONS_TOGTIME 40242 +#define EQ_ENABLE 40244 +#define EQ_AUTO 40245 +#define EQ_PRESETS 40246 +#define WINAMP_VIS_FALLOFF0 40247 +#define WINAMP_VIS_FALLOFF1 40248 +#define WINAMP_VIS_FALLOFF2 40249 +#define WINAMP_VIS_FALLOFF3 40250 +#define WINAMP_VIS_FALLOFF4 40251 +#define WINAMP_VIS_PEAKS 40252 +#define ID_LOAD_EQF 40253 +#define ID_SAVE_EQF 40254 +#define ID_PE_ENTRY 40255 +#define ID_PE_SCROLLUP 40256 +#define ID_PE_SCROLLDOWN 40257 +#define WINAMP_MAIN_WINDOW 40258 +#define WINAMP_VIS_PFALLOFF0 40259 +#define WINAMP_VIS_PFALLOFF1 40260 +#define WINAMP_VIS_PFALLOFF2 40261 +#define WINAMP_VIS_PFALLOFF3 40262 +#define WINAMP_VIS_PFALLOFF4 40263 +#define ID_PE_TOP 40264 +#define ID_PE_BOTTOM 40265 +#define WINAMP_OPTIONS_WINDOWSHADE_PL 40266 +#define EQ_INC1 40267 +#define EQ_INC2 40268 +#define EQ_INC3 40269 +#define EQ_INC4 40270 +#define EQ_INC5 40271 +#define EQ_INC6 40272 +#define EQ_INC7 40273 +#define EQ_INC8 40274 +#define EQ_INC9 40275 +#define EQ_INC10 40276 +#define EQ_INCPRE 40277 +#define EQ_DECPRE 40278 +#define EQ_DEC1 40279 +#define EQ_DEC2 40280 +#define EQ_DEC3 40281 +#define EQ_DEC4 40282 +#define EQ_DEC5 40283 +#define EQ_DEC6 40284 +#define EQ_DEC7 40285 +#define EQ_DEC8 40286 +#define EQ_DEC9 40287 +#define EQ_DEC10 40288 +#define ID_PE_SCUP 40289 +#define ID_PE_SCDOWN 40290 +#define WINAMP_REFRESHSKIN 40291 +#define ID_PE_PRINT 40292 +#define ID_PE_EXTINFO 40293 +#define WINAMP_PLAYLIST_ADVANCE 40294 +#define WINAMP_VIS_LIN 40295 +#define WINAMP_VIS_BAR 40296 +#define WINAMP_OPTIONS_MINIBROWSER 40298 +#define MB_FWD 40299 +#define MB_BACK 40300 +#define MB_RELOAD 40301 +#define MB_OPENMENU 40302 +#define MB_OPENLOC 40303 +#define WINAMP_NEW_INSTANCE 40305 +#define MB_UPDATE 40309 +#define WINAMP_OPTIONS_WINDOWSHADE_EQ 40310 +#define EQ_PANLEFT 40313 +#define EQ_PANRIGHT 40314 +#define WINAMP_GETMORESKINS 40316 +#define WINAMP_VIS_OPTIONS 40317 +#define WINAMP_PE_SEARCH 40318 +#define ID_PE_BOOKMARK 40319 +#define WINAMP_EDIT_BOOKMARKS 40320 +#define WINAMP_MAKECURBOOKMARK 40321 +#define ID_MAIN_PLAY_BOOKMARK_NONE 40322 +#define ID_MAIN_PLAY_AUDIOCD 40323 // starts playing the audio CD in the first CD reader +#define ID_MAIN_PLAY_AUDIOCD2 40324 // plays the 2nd +#define ID_MAIN_PLAY_AUDIOCD3 40325 // plays the 3rd +#define ID_MAIN_PLAY_AUDIOCD4 40326 // plays the 4th +#define WINAMP_OPTIONS_VIDEO 40328 +#define ID_VIDEOWND_ZOOMFULLSCREEN 40329 +#define ID_VIDEOWND_ZOOM100 40330 +#define ID_VIDEOWND_ZOOM200 40331 +#define ID_VIDEOWND_ZOOM50 40332 +#define ID_VIDEOWND_VIDEOOPTIONS 40333 +#define WINAMP_MINIMIZE 40334 +#define ID_PE_FONTBIGGER 40335 +#define ID_PE_FONTSMALLER 40336 +#define WINAMP_VIDEO_TOGGLE_FS 40337 +#define WINAMP_VIDEO_TVBUTTON 40338 +#define WINAMP_LIGHTNING_CLICK 40339 +#define ID_FILE_ADDTOLIBRARY 40344 +#define ID_HELP_HELPTOPICS 40347 +#define ID_HELP_GETTINGSTARTED 40348 +#define ID_HELP_WINAMPFORUMS 40349 +#define ID_PLAY_VOLUMEUP 40351 +#define ID_PLAY_VOLUMEDOWN 40352 +#define ID_PEFILE_OPENPLAYLISTFROMLIBRARY_NOPLAYLISTSINLIBRARY 40355 +#define ID_PEFILE_ADDFROMLIBRARY 40356 +#define ID_PEFILE_CLOSEPLAYLISTEDITOR 40357 +#define ID_PEPLAYLIST_PLAYLISTPREFERENCES 40358 +#define ID_MLFILE_NEWPLAYLIST 40359 +#define ID_MLFILE_LOADPLAYLIST 40360 +#define ID_MLFILE_SAVEPLAYLIST 40361 +#define ID_MLFILE_ADDMEDIATOLIBRARY 40362 +#define ID_MLFILE_CLOSEMEDIALIBRARY 40363 +#define ID_MLVIEW_NOWPLAYING 40364 +#define ID_MLVIEW_LOCALMEDIA_ALLMEDIA 40366 +#define ID_MLVIEW_LOCALMEDIA_AUDIO 40367 +#define ID_MLVIEW_LOCALMEDIA_VIDEO 40368 +#define ID_MLVIEW_PLAYLISTS_NOPLAYLISTINLIBRARY 40369 +#define ID_MLVIEW_INTERNETRADIO 40370 +#define ID_MLVIEW_INTERNETTV 40371 +#define ID_MLVIEW_LIBRARYPREFERENCES 40372 +#define ID_MLVIEW_DEVICES_NOAVAILABLEDEVICE 40373 +#define ID_MLFILE_IMPORTCURRENTPLAYLIST 40374 +#define ID_MLVIEW_MEDIA 40376 +#define ID_MLVIEW_PLAYLISTS 40377 +#define ID_MLVIEW_MEDIA_ALLMEDIA 40377 +#define ID_MLVIEW_DEVICES 40378 +#define ID_FILE_SHOWLIBRARY 40379 +#define ID_FILE_CLOSELIBRARY 40380 +#define ID_POST_PLAY_PLAYLIST 40381 +#define ID_VIS_NEXT 40382 +#define ID_VIS_PREV 40383 +#define ID_VIS_RANDOM 40384 +#define ID_MANAGEPLAYLISTS 40385 +#define ID_PREFS_SKIN_SWITCHTOSKIN 40386 +#define ID_PREFS_SKIN_DELETESKIN 40387 +#define ID_PREFS_SKIN_RENAMESKIN 40388 +#define ID_VIS_FS 40389 +#define ID_VIS_CFG 40390 +#define ID_VIS_MENU 40391 +#define ID_VIS_SET_FS_FLAG 40392 +#define ID_PE_SHOWPLAYING 40393 +#define ID_HELP_REGISTERWINAMPPRO 40394 +#define ID_PE_MANUAL_ADVANCE 40395 +#define WA_SONG_5_STAR_RATING 40396 +#define WA_SONG_4_STAR_RATING 40397 +#define WA_SONG_3_STAR_RATING 40398 +#define WA_SONG_2_STAR_RATING 40399 +#define WA_SONG_1_STAR_RATING 40400 +#define WA_SONG_NO_RATING 40401 +#define PL_SONG_5_STAR_RATING 40402 +#define PL_SONG_4_STAR_RATING 40403 +#define PL_SONG_3_STAR_RATING 40404 +#define PL_SONG_2_STAR_RATING 40405 +#define PL_SONG_1_STAR_RATING 40406 +#define PL_SONG_NO_RATING 40407 +#define AUDIO_TRACK_ONE 40408 +#define VIDEO_TRACK_ONE 40424 + +#define ID_SWITCH_COLOURTHEME 44500 +#define ID_GENFF_LIMIT 45000 + +#endif diff --git a/Externals/MusicMod/Player/Src/Winmain.cpp b/Externals/MusicMod/Player/Src/Winmain.cpp new file mode 100644 index 0000000000..bf9362a991 --- /dev/null +++ b/Externals/MusicMod/Player/Src/Winmain.cpp @@ -0,0 +1,236 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp, Open source Winamp core +// +// Copyright İ 2005 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#include "Global.h" +#include "Font.h" +#include "InputPlugin.h" +#include "OutputPlugin.h" +#include "VisPlugin.h" +#include "DspPlugin.h" +#include "GenPlugin.h" +#include "Main.h" +#include "Rebar.h" +#include "Playlist.h" +#include "Status.h" +#include "PluginManager.h" +#include "Prefs.h" +#include "Config.h" + +#include "Emabox/Emabox.h" + + + +#define PLUS_ALT ( FVIRTKEY | FALT ) +#define PLUS_CONTROL ( FVIRTKEY | FCONTROL ) +#define PLUS_CONTROL_ALT ( FVIRTKEY | FCONTROL | FALT ) +#define PLUS_CONTROL_SHIFT ( FVIRTKEY | FCONTROL | FSHIFT ) +#define PLUS_SHIFT ( FVIRTKEY | FSHIFT ) + + + +HINSTANCE g_hInstance = NULL; // extern + +TCHAR * szHomeDir = NULL; // extern +int iHomeDirLen = 0; // extern + +TCHAR * szPluginDir = NULL; // extern +int iPluginDirLen = 0; // extern + + + +TCHAR szCurDir[ MAX_PATH + 1 ] = TEXT( "" ); +ConfCurDir ccdCurDir( szCurDir, TEXT( "CurDir" ) ); + + +bool bWarnPluginsMissing; +ConfBool cbWarnPluginsMissing( &bWarnPluginsMissing, TEXT( "WarnPluginsMissing" ), CONF_MODE_PUBLIC, true ); + + + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR lpCmdLine, int nCmdShow ) +{ + g_hInstance = hInstance; + + + // Load full config + Conf::Init( hInstance ); + + + // Get home dir + szHomeDir = new TCHAR[ MAX_PATH + 1 ]; + iHomeDirLen = GetModuleFileName( NULL, szHomeDir, MAX_PATH ); + if( !iHomeDirLen ) return 1; + + TCHAR * walk = szHomeDir + iHomeDirLen - 1; + while( ( walk > szHomeDir ) && ( *walk != TEXT( '\\' ) ) ) walk--; + walk++; + *walk = TEXT( '\0' ); + iHomeDirLen = walk - szHomeDir; + + + // Get plugins dir + szPluginDir = new TCHAR[ MAX_PATH + 1 ]; + memcpy( szPluginDir, szHomeDir, iHomeDirLen * sizeof( TCHAR ) ); + memcpy( szPluginDir + iHomeDirLen, TEXT( "Plugins" ), 7 * sizeof( TCHAR ) ); + szPluginDir[ iHomeDirLen + 7 ] = TEXT( '\0' ); + + + Font::Create(); + BuildMainWindow(); + Prefs::Create(); + + + // Show window + ShowWindow( WindowMain, SW_SHOW ); + SetForegroundWindow( WindowMain ); + SetFocus( WindowMain ); + + Plugin::FindAll ( szPluginDir, TEXT( "in_*.dll" ), true ); + Plugin::FindAll( szPluginDir, TEXT( "out_*.dll" ), false ); + Plugin::FindAll ( szPluginDir, TEXT( "vis_*.dll" ), false ); + Plugin::FindAll ( szPluginDir, TEXT( "dsp_*.dll" ), false ); + Plugin::FindAll ( szPluginDir, TEXT( "gen_*.dll" ), true ); + + PluginManager::Fill(); + + + // Check plugin presence + // One msgbox maximum + if( bWarnPluginsMissing ) + { + + + if( input_plugins.empty() ) + { + // No input plugins + TCHAR szBuffer[ 5000 ]; + _stprintf( + szBuffer, + TEXT( + "No input plugins were found.\n" + "\n" + "Please install at least one Winamp input plugin to \n" + "%s " + ), + szPluginDir + ); + + int iNeverAgain = bWarnPluginsMissing ? 0 : 1; + EmaBox( 0, szBuffer, TEXT( "Input plugins missing" ), MB_ICONEXCLAMATION | MB_CHECKNEVERAGAIN, &iNeverAgain ); + bWarnPluginsMissing = ( iNeverAgain == 0 ); + } + else if( output_plugins.empty() ) + { + // No output plugins + TCHAR szBuffer[ 5000 ]; + _stprintf( + szBuffer, + TEXT( + "No output plugins were found.\n" + "\n" + "Please install at least one Winamp output plugin to \n" + "%s " + ), + szPluginDir + ); + + int iNeverAgain = bWarnPluginsMissing ? 0 : 1; + EmaBox( 0, szBuffer, TEXT( "Output plugins missing" ), MB_ICONEXCLAMATION | MB_CHECKNEVERAGAIN, &iNeverAgain ); + bWarnPluginsMissing = ( iNeverAgain == 0 ); + } + } + + + // Todo: all the rest... + ACCEL accels[] = { + { PLUS_CONTROL, 'A', ID_PE_SELECTALL }, // [Ctrl] + [A] + { PLUS_CONTROL, 'I', ID_PE_INVERT }, // [Ctrl] + [I] + { PLUS_CONTROL, 'N', ID_PE_NEW }, // [Ctrl] + [N] + { PLUS_CONTROL, 'O', ID_PE_OPEN }, // [Ctrl] + [O] + { PLUS_CONTROL, 'P', WINAMP_OPTIONS_PREFS }, // [Ctrl] + [P] + { PLUS_CONTROL, 'S', ID_PE_SAVEAS }, // [Ctrl] + [S] + { PLUS_CONTROL, VK_F1, WINAMP_HELP_ABOUT }, // [Ctrl] + [F1] + { PLUS_CONTROL_SHIFT, 'A', ID_PE_NONE }, // [Ctrl] + [Shift] + [A] + { PLUS_CONTROL_SHIFT, VK_DELETE, ID_PE_CLEAR }, // [Ctrl] + [Shift] + [Del] + { PLUS_ALT, 'F', WINAMP_MAINMENU }, // [Alt] + [F] + { PLUS_ALT, VK_F4, WINAMP_FILE_QUIT } // [Alt] + [F4] + }; + + + HACCEL hAccel = CreateAcceleratorTable( accels, sizeof( accels ) / sizeof( ACCEL ) ); + if( !hAccel ) + { + MessageBox( 0, TEXT( "Accelerator table error" ), TEXT( "" ), 0 ); + } + + // Message loop + MSG msg; + while( GetMessage( &msg, NULL, 0, 0 ) > 0 ) + { + // Note: Keys without [Alt] or [Ctrl] not everywhere! + if( ( ( msg.hwnd == WindowMain ) || IsChild( WindowMain, msg.hwnd ) ) && + TranslateAccelerator( WindowMain, hAccel, &msg ) ) + { + // MessageBox( 0, TEXT( "Trans" ), TEXT( "" ), 0 ); + } + + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } + + DestroyAcceleratorTable( hAccel ); + + + // Input + vector ::iterator iter_input = input_plugins.begin(); + while( iter_input != input_plugins.end() ) + { + ( *iter_input )->Unload(); + iter_input++; + } + + // Output + vector ::iterator iter_output = output_plugins.begin(); + while( iter_output != output_plugins.end() ) + { + ( *iter_output )->Unload(); + iter_output++; + } + + // General + vector ::iterator iter_gen = gen_plugins.begin(); + while( iter_gen != gen_plugins.end() ) + { + ( *iter_gen )->Unload(); + iter_gen++; + } + + + // TODO: create main::destroy + // UnregisterClass( PA_CLASSNAME, g_hInstance ); + + Prefs::Destroy(); + + Font::Destroy(); + +/* + delete [] szPluginDir; + delete [] szHomeDir; +*/ + + Conf::Write(); + + return 0; +} diff --git a/Externals/MusicMod/Player/Src/afxres.h b/Externals/MusicMod/Player/Src/afxres.h new file mode 100644 index 0000000000..7eb4900177 --- /dev/null +++ b/Externals/MusicMod/Player/Src/afxres.h @@ -0,0 +1,823 @@ +// This is a part of the Microsoft Foundation Classes C++ library. +// Copyright (C) Microsoft Corporation +// All rights reserved. +// +// This source code is only intended as a supplement to the +// Microsoft Foundation Classes Reference and related +// electronic documentation provided with the library. +// See these sources for detailed information regarding the +// Microsoft Foundation Classes product. + +#ifndef __AFXRES_H__ +#define __AFXRES_H__ + +#ifdef RC_INVOKED +#ifndef _INC_WINDOWS +#define _INC_WINDOWS + #include "winres.h" // extract from windows header +#endif +#endif + +#ifdef _AFX_MINREBUILD +#pragma component(minrebuild, off) +#endif + +#ifdef RC_INVOKED + #if defined(_VC80_UPGRADE) && (_VC80_UPGRADE >= 0x0700) && (_VC80_UPGRADE < 0x0800) && defined(IDR_MANIFEST) + // Handle project upgrade from VC7/VC7.1 for projects with manifest + #define IDR_MANIFEST 1024 + #endif +#endif // RC_INVOKED + +#ifdef APSTUDIO_INVOKED +#define APSTUDIO_HIDDEN_SYMBOLS +#endif + +///////////////////////////////////////////////////////////////////////////// +// MFC resource types (see Technical note TN024 for implementation details) + +#ifndef RC_INVOKED +#define RT_DLGINIT MAKEINTRESOURCE(240) +#define RT_TOOLBAR MAKEINTRESOURCE(241) +#endif + +///////////////////////////////////////////////////////////////////////////// + +#ifdef APSTUDIO_INVOKED +#undef APSTUDIO_HIDDEN_SYMBOLS +#endif + +///////////////////////////////////////////////////////////////////////////// +// General style bits etc + +// ControlBar styles +#define CBRS_ALIGN_LEFT 0x1000L +#define CBRS_ALIGN_TOP 0x2000L +#define CBRS_ALIGN_RIGHT 0x4000L +#define CBRS_ALIGN_BOTTOM 0x8000L +#define CBRS_ALIGN_ANY 0xF000L + +#define CBRS_BORDER_LEFT 0x0100L +#define CBRS_BORDER_TOP 0x0200L +#define CBRS_BORDER_RIGHT 0x0400L +#define CBRS_BORDER_BOTTOM 0x0800L +#define CBRS_BORDER_ANY 0x0F00L + +#define CBRS_TOOLTIPS 0x0010L +#define CBRS_FLYBY 0x0020L +#define CBRS_FLOAT_MULTI 0x0040L +#define CBRS_BORDER_3D 0x0080L +#define CBRS_HIDE_INPLACE 0x0008L +#define CBRS_SIZE_DYNAMIC 0x0004L +#define CBRS_SIZE_FIXED 0x0002L +#define CBRS_FLOATING 0x0001L + +#define CBRS_GRIPPER 0x00400000L + +#define CBRS_ORIENT_HORZ (CBRS_ALIGN_TOP|CBRS_ALIGN_BOTTOM) +#define CBRS_ORIENT_VERT (CBRS_ALIGN_LEFT|CBRS_ALIGN_RIGHT) +#define CBRS_ORIENT_ANY (CBRS_ORIENT_HORZ|CBRS_ORIENT_VERT) + +#define CBRS_ALL 0x0040FFFFL + +// the CBRS_ style is made up of an alignment style and a draw border style +// the alignment styles are mutually exclusive +// the draw border styles may be combined +#define CBRS_NOALIGN 0x00000000L +#define CBRS_LEFT (CBRS_ALIGN_LEFT|CBRS_BORDER_RIGHT) +#define CBRS_TOP (CBRS_ALIGN_TOP|CBRS_BORDER_BOTTOM) +#define CBRS_RIGHT (CBRS_ALIGN_RIGHT|CBRS_BORDER_LEFT) +#define CBRS_BOTTOM (CBRS_ALIGN_BOTTOM|CBRS_BORDER_TOP) + +///////////////////////////////////////////////////////////////////////////// +// Manifest Resource ID of manifest containing Localized DLL information +#define ID_MFCLOC_MANIFEST 1000 + +///////////////////////////////////////////////////////////////////////////// +// Standard window components + +// Mode indicators in status bar - these are routed like commands +#define ID_INDICATOR_EXT 0xE700 // extended selection indicator +#define ID_INDICATOR_CAPS 0xE701 // cap lock indicator +#define ID_INDICATOR_NUM 0xE702 // num lock indicator +#define ID_INDICATOR_SCRL 0xE703 // scroll lock indicator +#define ID_INDICATOR_OVR 0xE704 // overtype mode indicator +#define ID_INDICATOR_REC 0xE705 // record mode indicator +#define ID_INDICATOR_KANA 0xE706 // kana lock indicator + +#define ID_SEPARATOR 0 // special separator value + +#ifndef RC_INVOKED // code only +// Standard control bars (IDW = window ID) +#define AFX_IDW_CONTROLBAR_FIRST 0xE800 +#define AFX_IDW_CONTROLBAR_LAST 0xE8FF + +#define AFX_IDW_TOOLBAR 0xE800 // main Toolbar for window +#define AFX_IDW_STATUS_BAR 0xE801 // Status bar window +#define AFX_IDW_PREVIEW_BAR 0xE802 // PrintPreview Dialog Bar +#define AFX_IDW_RESIZE_BAR 0xE803 // OLE in-place resize bar +#define AFX_IDW_REBAR 0xE804 // COMCTL32 "rebar" Bar +#define AFX_IDW_DIALOGBAR 0xE805 // CDialogBar + +// Note: If your application supports docking toolbars, you should +// not use the following IDs for your own toolbars. The IDs chosen +// are at the top of the first 32 such that the bars will be hidden +// while in print preview mode, and are not likely to conflict with +// IDs your application may have used succesfully in the past. + +#define AFX_IDW_DOCKBAR_TOP 0xE81B +#define AFX_IDW_DOCKBAR_LEFT 0xE81C +#define AFX_IDW_DOCKBAR_RIGHT 0xE81D +#define AFX_IDW_DOCKBAR_BOTTOM 0xE81E +#define AFX_IDW_DOCKBAR_FLOAT 0xE81F + +// Macro for mapping standard control bars to bitmask (limit of 32) +#define AFX_CONTROLBAR_MASK(nIDC) (1L << (nIDC - AFX_IDW_CONTROLBAR_FIRST)) + +// parts of Main Frame +#define AFX_IDW_PANE_FIRST 0xE900 // first pane (256 max) +#define AFX_IDW_PANE_LAST 0xE9ff +#define AFX_IDW_HSCROLL_FIRST 0xEA00 // first Horz scrollbar (16 max) +#define AFX_IDW_VSCROLL_FIRST 0xEA10 // first Vert scrollbar (16 max) + +#define AFX_IDW_SIZE_BOX 0xEA20 // size box for splitters +#define AFX_IDW_PANE_SAVE 0xEA21 // to shift AFX_IDW_PANE_FIRST +#endif //!RC_INVOKED + +#ifndef APSTUDIO_INVOKED + +// common style for form views +#define AFX_WS_DEFAULT_VIEW (WS_CHILD | WS_VISIBLE | WS_BORDER) + +#endif //!APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// Standard app configurable strings + +// for application title (defaults to EXE name or name in constructor) +#define AFX_IDS_APP_TITLE 0xE000 +// idle message bar line +#define AFX_IDS_IDLEMESSAGE 0xE001 +// message bar line when in shift-F1 help mode +#define AFX_IDS_HELPMODEMESSAGE 0xE002 +// document title when editing OLE embedding +#define AFX_IDS_APP_TITLE_EMBEDDING 0xE003 +// company name +#define AFX_IDS_COMPANY_NAME 0xE004 +// object name when server is inplace +#define AFX_IDS_OBJ_TITLE_INPLACE 0xE005 + +///////////////////////////////////////////////////////////////////////////// +// Standard Commands + +// File commands +#define ID_FILE_NEW 0xE100 +#define ID_FILE_OPEN 0xE101 +#define ID_FILE_CLOSE 0xE102 +#define ID_FILE_SAVE 0xE103 +#define ID_FILE_SAVE_AS 0xE104 +#define ID_FILE_PAGE_SETUP 0xE105 +#define ID_FILE_PRINT_SETUP 0xE106 +#define ID_FILE_PRINT 0xE107 +#define ID_FILE_PRINT_DIRECT 0xE108 +#define ID_FILE_PRINT_PREVIEW 0xE109 +#define ID_FILE_UPDATE 0xE10A +#define ID_FILE_SAVE_COPY_AS 0xE10B +#define ID_FILE_SEND_MAIL 0xE10C +#define ID_FILE_NEW_FRAME 0xE10D + +#define ID_FILE_MRU_FIRST 0xE110 +#define ID_FILE_MRU_FILE1 0xE110 // range - 16 max +#define ID_FILE_MRU_FILE2 0xE111 +#define ID_FILE_MRU_FILE3 0xE112 +#define ID_FILE_MRU_FILE4 0xE113 +#define ID_FILE_MRU_FILE5 0xE114 +#define ID_FILE_MRU_FILE6 0xE115 +#define ID_FILE_MRU_FILE7 0xE116 +#define ID_FILE_MRU_FILE8 0xE117 +#define ID_FILE_MRU_FILE9 0xE118 +#define ID_FILE_MRU_FILE10 0xE119 +#define ID_FILE_MRU_FILE11 0xE11A +#define ID_FILE_MRU_FILE12 0xE11B +#define ID_FILE_MRU_FILE13 0xE11C +#define ID_FILE_MRU_FILE14 0xE11D +#define ID_FILE_MRU_FILE15 0xE11E +#define ID_FILE_MRU_FILE16 0xE11F +#define ID_FILE_MRU_LAST 0xE11F + +// Edit commands +#define ID_EDIT_CLEAR 0xE120 +#define ID_EDIT_CLEAR_ALL 0xE121 +#define ID_EDIT_COPY 0xE122 +#define ID_EDIT_CUT 0xE123 +#define ID_EDIT_FIND 0xE124 +#define ID_EDIT_PASTE 0xE125 +#define ID_EDIT_PASTE_LINK 0xE126 +#define ID_EDIT_PASTE_SPECIAL 0xE127 +#define ID_EDIT_REPEAT 0xE128 +#define ID_EDIT_REPLACE 0xE129 +#define ID_EDIT_SELECT_ALL 0xE12A +#define ID_EDIT_UNDO 0xE12B +#define ID_EDIT_REDO 0xE12C + +// Window commands +#define ID_WINDOW_NEW 0xE130 +#define ID_WINDOW_ARRANGE 0xE131 +#define ID_WINDOW_CASCADE 0xE132 +#define ID_WINDOW_TILE_HORZ 0xE133 +#define ID_WINDOW_TILE_VERT 0xE134 +#define ID_WINDOW_SPLIT 0xE135 +#ifndef RC_INVOKED // code only +#define AFX_IDM_WINDOW_FIRST 0xE130 +#define AFX_IDM_WINDOW_LAST 0xE13F +#define AFX_IDM_FIRST_MDICHILD 0xFF00 // window list starts here +#endif //!RC_INVOKED + +// Help and App commands +#define ID_APP_ABOUT 0xE140 +#define ID_APP_EXIT 0xE141 +#define ID_HELP_INDEX 0xE142 +#define ID_HELP_FINDER 0xE143 +#define ID_HELP_USING 0xE144 +#define ID_CONTEXT_HELP 0xE145 // shift-F1 +// special commands for processing help +#define ID_HELP 0xE146 // first attempt for F1 +#define ID_DEFAULT_HELP 0xE147 // last attempt + +// Misc +#define ID_NEXT_PANE 0xE150 +#define ID_PREV_PANE 0xE151 + +// Format +#define ID_FORMAT_FONT 0xE160 + +// OLE commands +#define ID_OLE_INSERT_NEW 0xE200 +#define ID_OLE_EDIT_LINKS 0xE201 +#define ID_OLE_EDIT_CONVERT 0xE202 +#define ID_OLE_EDIT_CHANGE_ICON 0xE203 +#define ID_OLE_EDIT_PROPERTIES 0xE204 +#define ID_OLE_VERB_FIRST 0xE210 // range - 16 max +#ifndef RC_INVOKED // code only +#define ID_OLE_VERB_LAST 0xE21F +#endif //!RC_INVOKED + +// for print preview dialog bar +#define AFX_ID_PREVIEW_CLOSE 0xE300 +#define AFX_ID_PREVIEW_NUMPAGE 0xE301 // One/Two Page button +#define AFX_ID_PREVIEW_NEXT 0xE302 +#define AFX_ID_PREVIEW_PREV 0xE303 +#define AFX_ID_PREVIEW_PRINT 0xE304 +#define AFX_ID_PREVIEW_ZOOMIN 0xE305 +#define AFX_ID_PREVIEW_ZOOMOUT 0xE306 + +// View commands (same number used as IDW used for control bar) +#define ID_VIEW_TOOLBAR 0xE800 +#define ID_VIEW_STATUS_BAR 0xE801 +#define ID_VIEW_REBAR 0xE804 +#define ID_VIEW_AUTOARRANGE 0xE805 + // E810 -> E81F must be kept in order for RANGE macros +#define ID_VIEW_SMALLICON 0xE810 +#define ID_VIEW_LARGEICON 0xE811 +#define ID_VIEW_LIST 0xE812 +#define ID_VIEW_DETAILS 0xE813 +#define ID_VIEW_LINEUP 0xE814 +#define ID_VIEW_BYNAME 0xE815 +#define AFX_ID_VIEW_MINIMUM ID_VIEW_SMALLICON +#define AFX_ID_VIEW_MAXIMUM ID_VIEW_BYNAME + // E800 -> E8FF reserved for other control bar commands + +// RecordForm commands +#define ID_RECORD_FIRST 0xE900 +#define ID_RECORD_LAST 0xE901 +#define ID_RECORD_NEXT 0xE902 +#define ID_RECORD_PREV 0xE903 + +///////////////////////////////////////////////////////////////////////////// +// Standard control IDs + +#ifdef IDC_STATIC +#undef IDC_STATIC +#endif +#define IDC_STATIC (-1) // all static controls + +///////////////////////////////////////////////////////////////////////////// +// Standard string error/warnings + +#ifndef RC_INVOKED // code only +#define AFX_IDS_SCFIRST 0xEF00 +#endif //!RC_INVOKED + +#define AFX_IDS_SCSIZE 0xEF00 +#define AFX_IDS_SCMOVE 0xEF01 +#define AFX_IDS_SCMINIMIZE 0xEF02 +#define AFX_IDS_SCMAXIMIZE 0xEF03 +#define AFX_IDS_SCNEXTWINDOW 0xEF04 +#define AFX_IDS_SCPREVWINDOW 0xEF05 +#define AFX_IDS_SCCLOSE 0xEF06 +#define AFX_IDS_SCRESTORE 0xEF12 +#define AFX_IDS_SCTASKLIST 0xEF13 + +#define AFX_IDS_MDICHILD 0xEF1F + +#define AFX_IDS_DESKACCESSORY 0xEFDA + +// General strings +#define AFX_IDS_OPENFILE 0xF000 +#define AFX_IDS_SAVEFILE 0xF001 +#define AFX_IDS_ALLFILTER 0xF002 +#define AFX_IDS_UNTITLED 0xF003 +#define AFX_IDS_SAVEFILECOPY 0xF004 +#define AFX_IDS_PREVIEW_CLOSE 0xF005 +#define AFX_IDS_UNNAMED_FILE 0xF006 +#define AFX_IDS_HIDE 0xF011 + +// MFC Standard Exception Error messages +#define AFX_IDP_NO_ERROR_AVAILABLE 0xF020 +#define AFX_IDS_NOT_SUPPORTED_EXCEPTION 0xF021 +#define AFX_IDS_RESOURCE_EXCEPTION 0xF022 +#define AFX_IDS_MEMORY_EXCEPTION 0xF023 +#define AFX_IDS_USER_EXCEPTION 0xF024 +#define AFX_IDS_INVALID_ARG_EXCEPTION 0xF025 + +// Printing and print preview strings +#define AFX_IDS_PRINTONPORT 0xF040 +#define AFX_IDS_ONEPAGE 0xF041 +#define AFX_IDS_TWOPAGE 0xF042 +#define AFX_IDS_PRINTPAGENUM 0xF043 +#define AFX_IDS_PREVIEWPAGEDESC 0xF044 +#define AFX_IDS_PRINTDEFAULTEXT 0xF045 +#define AFX_IDS_PRINTDEFAULT 0xF046 +#define AFX_IDS_PRINTFILTER 0xF047 +#define AFX_IDS_PRINTCAPTION 0xF048 +#define AFX_IDS_PRINTTOFILE 0xF049 + + +// OLE strings +#define AFX_IDS_OBJECT_MENUITEM 0xF080 +#define AFX_IDS_EDIT_VERB 0xF081 +#define AFX_IDS_ACTIVATE_VERB 0xF082 +#define AFX_IDS_CHANGE_LINK 0xF083 +#define AFX_IDS_AUTO 0xF084 +#define AFX_IDS_MANUAL 0xF085 +#define AFX_IDS_FROZEN 0xF086 +#define AFX_IDS_ALL_FILES 0xF087 +// dynamically changing menu items +#define AFX_IDS_SAVE_MENU 0xF088 +#define AFX_IDS_UPDATE_MENU 0xF089 +#define AFX_IDS_SAVE_AS_MENU 0xF08A +#define AFX_IDS_SAVE_COPY_AS_MENU 0xF08B +#define AFX_IDS_EXIT_MENU 0xF08C +#define AFX_IDS_UPDATING_ITEMS 0xF08D +// COlePasteSpecialDialog defines +#define AFX_IDS_METAFILE_FORMAT 0xF08E +#define AFX_IDS_DIB_FORMAT 0xF08F +#define AFX_IDS_BITMAP_FORMAT 0xF090 +#define AFX_IDS_LINKSOURCE_FORMAT 0xF091 +#define AFX_IDS_EMBED_FORMAT 0xF092 +// other OLE utility strings +#define AFX_IDS_PASTELINKEDTYPE 0xF094 +#define AFX_IDS_UNKNOWNTYPE 0xF095 +#define AFX_IDS_RTF_FORMAT 0xF096 +#define AFX_IDS_TEXT_FORMAT 0xF097 +// OLE datatype format error strings +#define AFX_IDS_INVALID_CURRENCY 0xF098 +#define AFX_IDS_INVALID_DATETIME 0xF099 +#define AFX_IDS_INVALID_DATETIMESPAN 0xF09A + +// General error / prompt strings +#define AFX_IDP_INVALID_FILENAME 0xF100 +#define AFX_IDP_FAILED_TO_OPEN_DOC 0xF101 +#define AFX_IDP_FAILED_TO_SAVE_DOC 0xF102 +#define AFX_IDP_ASK_TO_SAVE 0xF103 +#define AFX_IDP_FAILED_TO_CREATE_DOC 0xF104 +#define AFX_IDP_FILE_TOO_LARGE 0xF105 +#define AFX_IDP_FAILED_TO_START_PRINT 0xF106 +#define AFX_IDP_FAILED_TO_LAUNCH_HELP 0xF107 +#define AFX_IDP_INTERNAL_FAILURE 0xF108 // general failure +#define AFX_IDP_COMMAND_FAILURE 0xF109 // command failure +#define AFX_IDP_FAILED_MEMORY_ALLOC 0xF10A +#define AFX_IDP_UNREG_DONE 0xF10B +#define AFX_IDP_UNREG_FAILURE 0xF10C +#define AFX_IDP_DLL_LOAD_FAILED 0xF10D +#define AFX_IDP_DLL_BAD_VERSION 0xF10E + +// DDV parse errors +#define AFX_IDP_PARSE_INT 0xF110 +#define AFX_IDP_PARSE_REAL 0xF111 +#define AFX_IDP_PARSE_INT_RANGE 0xF112 +#define AFX_IDP_PARSE_REAL_RANGE 0xF113 +#define AFX_IDP_PARSE_STRING_SIZE 0xF114 +#define AFX_IDP_PARSE_RADIO_BUTTON 0xF115 +#define AFX_IDP_PARSE_BYTE 0xF116 +#define AFX_IDP_PARSE_UINT 0xF117 +#define AFX_IDP_PARSE_DATETIME 0xF118 +#define AFX_IDP_PARSE_CURRENCY 0xF119 +#define AFX_IDP_PARSE_GUID 0xF11A +#define AFX_IDP_PARSE_TIME 0xF11B +#define AFX_IDP_PARSE_DATE 0xF11C + +// CFile/CArchive error strings for user failure +#define AFX_IDP_FAILED_INVALID_FORMAT 0xF120 +#define AFX_IDP_FAILED_INVALID_PATH 0xF121 +#define AFX_IDP_FAILED_DISK_FULL 0xF122 +#define AFX_IDP_FAILED_ACCESS_READ 0xF123 +#define AFX_IDP_FAILED_ACCESS_WRITE 0xF124 +#define AFX_IDP_FAILED_IO_ERROR_READ 0xF125 +#define AFX_IDP_FAILED_IO_ERROR_WRITE 0xF126 + +// Script errors / prompt strings +#define AFX_IDP_SCRIPT_ERROR 0xF130 +#define AFX_IDP_SCRIPT_DISPATCH_EXCEPTION 0xF131 + +// OLE errors / prompt strings +#define AFX_IDP_STATIC_OBJECT 0xF180 +#define AFX_IDP_FAILED_TO_CONNECT 0xF181 +#define AFX_IDP_SERVER_BUSY 0xF182 +#define AFX_IDP_BAD_VERB 0xF183 +#define AFX_IDS_NOT_DOCOBJECT 0xF184 +#define AFX_IDP_FAILED_TO_NOTIFY 0xF185 +#define AFX_IDP_FAILED_TO_LAUNCH 0xF186 +#define AFX_IDP_ASK_TO_UPDATE 0xF187 +#define AFX_IDP_FAILED_TO_UPDATE 0xF188 +#define AFX_IDP_FAILED_TO_REGISTER 0xF189 +#define AFX_IDP_FAILED_TO_AUTO_REGISTER 0xF18A +#define AFX_IDP_FAILED_TO_CONVERT 0xF18B +#define AFX_IDP_GET_NOT_SUPPORTED 0xF18C +#define AFX_IDP_SET_NOT_SUPPORTED 0xF18D +#define AFX_IDP_ASK_TO_DISCARD 0xF18E +#define AFX_IDP_FAILED_TO_CREATE 0xF18F + +// MAPI errors / prompt strings +#define AFX_IDP_FAILED_MAPI_LOAD 0xF190 +#define AFX_IDP_INVALID_MAPI_DLL 0xF191 +#define AFX_IDP_FAILED_MAPI_SEND 0xF192 + +#define AFX_IDP_FILE_NONE 0xF1A0 +#define AFX_IDP_FILE_GENERIC 0xF1A1 +#define AFX_IDP_FILE_NOT_FOUND 0xF1A2 +#define AFX_IDP_FILE_BAD_PATH 0xF1A3 +#define AFX_IDP_FILE_TOO_MANY_OPEN 0xF1A4 +#define AFX_IDP_FILE_ACCESS_DENIED 0xF1A5 +#define AFX_IDP_FILE_INVALID_FILE 0xF1A6 +#define AFX_IDP_FILE_REMOVE_CURRENT 0xF1A7 +#define AFX_IDP_FILE_DIR_FULL 0xF1A8 +#define AFX_IDP_FILE_BAD_SEEK 0xF1A9 +#define AFX_IDP_FILE_HARD_IO 0xF1AA +#define AFX_IDP_FILE_SHARING 0xF1AB +#define AFX_IDP_FILE_LOCKING 0xF1AC +#define AFX_IDP_FILE_DISKFULL 0xF1AD +#define AFX_IDP_FILE_EOF 0xF1AE + +#define AFX_IDP_ARCH_NONE 0xF1B0 +#define AFX_IDP_ARCH_GENERIC 0xF1B1 +#define AFX_IDP_ARCH_READONLY 0xF1B2 +#define AFX_IDP_ARCH_ENDOFFILE 0xF1B3 +#define AFX_IDP_ARCH_WRITEONLY 0xF1B4 +#define AFX_IDP_ARCH_BADINDEX 0xF1B5 +#define AFX_IDP_ARCH_BADCLASS 0xF1B6 +#define AFX_IDP_ARCH_BADSCHEMA 0xF1B7 + +#define AFX_IDS_OCC_SCALEUNITS_PIXELS 0xF1C0 + +// 0xf200-0xf20f reserved + +// font names and point sizes +#define AFX_IDS_STATUS_FONT 0xF230 +#define AFX_IDS_TOOLTIP_FONT 0xF231 +#define AFX_IDS_UNICODE_FONT 0xF232 +#define AFX_IDS_MINI_FONT 0xF233 + +// ODBC Database errors / prompt strings +#ifndef RC_INVOKED // code only +#define AFX_IDP_SQL_FIRST 0xF280 +#endif //!RC_INVOKED +#define AFX_IDP_SQL_CONNECT_FAIL 0xF281 +#define AFX_IDP_SQL_RECORDSET_FORWARD_ONLY 0xF282 +#define AFX_IDP_SQL_EMPTY_COLUMN_LIST 0xF283 +#define AFX_IDP_SQL_FIELD_SCHEMA_MISMATCH 0xF284 +#define AFX_IDP_SQL_ILLEGAL_MODE 0xF285 +#define AFX_IDP_SQL_MULTIPLE_ROWS_AFFECTED 0xF286 +#define AFX_IDP_SQL_NO_CURRENT_RECORD 0xF287 +#define AFX_IDP_SQL_NO_ROWS_AFFECTED 0xF288 +#define AFX_IDP_SQL_RECORDSET_READONLY 0xF289 +#define AFX_IDP_SQL_SQL_NO_TOTAL 0xF28A +#define AFX_IDP_SQL_ODBC_LOAD_FAILED 0xF28B +#define AFX_IDP_SQL_DYNASET_NOT_SUPPORTED 0xF28C +#define AFX_IDP_SQL_SNAPSHOT_NOT_SUPPORTED 0xF28D +#define AFX_IDP_SQL_API_CONFORMANCE 0xF28E +#define AFX_IDP_SQL_SQL_CONFORMANCE 0xF28F +#define AFX_IDP_SQL_NO_DATA_FOUND 0xF290 +#define AFX_IDP_SQL_ROW_UPDATE_NOT_SUPPORTED 0xF291 +#define AFX_IDP_SQL_ODBC_V2_REQUIRED 0xF292 +#define AFX_IDP_SQL_NO_POSITIONED_UPDATES 0xF293 +#define AFX_IDP_SQL_LOCK_MODE_NOT_SUPPORTED 0xF294 +#define AFX_IDP_SQL_DATA_TRUNCATED 0xF295 +#define AFX_IDP_SQL_ROW_FETCH 0xF296 +#define AFX_IDP_SQL_INCORRECT_ODBC 0xF297 +#define AFX_IDP_SQL_UPDATE_DELETE_FAILED 0xF298 +#define AFX_IDP_SQL_DYNAMIC_CURSOR_NOT_SUPPORTED 0xF299 +#define AFX_IDP_SQL_FIELD_NOT_FOUND 0xF29A +#define AFX_IDP_SQL_BOOKMARKS_NOT_SUPPORTED 0xF29B +#define AFX_IDP_SQL_BOOKMARKS_NOT_ENABLED 0xF29C + +// ODBC Database strings +#define AFX_IDS_DELETED 0xF29D + +// DAO Database errors / prompt strings +#ifndef RC_INVOKED // code only +#define AFX_IDP_DAO_FIRST 0xF2B0 +#endif //!RC_INVOKED +#define AFX_IDP_DAO_ENGINE_INITIALIZATION 0xF2B0 +#define AFX_IDP_DAO_DFX_BIND 0xF2B1 +#define AFX_IDP_DAO_OBJECT_NOT_OPEN 0xF2B2 + +// ICDAORecordset::GetRows Errors +// These are not placed in DAO Errors collection +// and must be handled directly by MFC. +#define AFX_IDP_DAO_ROWTOOSHORT 0xF2B3 +#define AFX_IDP_DAO_BADBINDINFO 0xF2B4 +#define AFX_IDP_DAO_COLUMNUNAVAILABLE 0xF2B5 + +///////////////////////////////////////////////////////////////////////////// +// Strings for ISAPI support + +#define AFX_IDS_HTTP_TITLE 0xF2D1 +#define AFX_IDS_HTTP_NO_TEXT 0xF2D2 +#define AFX_IDS_HTTP_BAD_REQUEST 0xF2D3 +#define AFX_IDS_HTTP_AUTH_REQUIRED 0xF2D4 +#define AFX_IDS_HTTP_FORBIDDEN 0xF2D5 +#define AFX_IDS_HTTP_NOT_FOUND 0xF2D6 +#define AFX_IDS_HTTP_SERVER_ERROR 0xF2D7 +#define AFX_IDS_HTTP_NOT_IMPLEMENTED 0xF2D8 + +///////////////////////////////////////////////////////////////////////////// +// Strings for Accessibility support for CCheckListBox +#define AFX_IDS_CHECKLISTBOX_UNCHECK 0xF2E1 +#define AFX_IDS_CHECKLISTBOX_CHECK 0xF2E2 +#define AFX_IDS_CHECKLISTBOX_MIXED 0xF2E3 + +///////////////////////////////////////////////////////////////////////////// +// AFX implementation - control IDs (AFX_IDC) + +// Parts of dialogs +#define AFX_IDC_LISTBOX 100 +#define AFX_IDC_CHANGE 101 +#define AFX_IDC_BROWSER 102 + +// for print dialog +#define AFX_IDC_PRINT_DOCNAME 201 +#define AFX_IDC_PRINT_PRINTERNAME 202 +#define AFX_IDC_PRINT_PORTNAME 203 +#define AFX_IDC_PRINT_PAGENUM 204 + +// Property Sheet control id's (determined with Spy++) +#define ID_APPLY_NOW 0x3021 +#define ID_WIZBACK 0x3023 +#define ID_WIZNEXT 0x3024 +#define ID_WIZFINISH 0x3025 +#define AFX_IDC_TAB_CONTROL 0x3020 + +///////////////////////////////////////////////////////////////////////////// +// IDRs for standard components + +#ifndef RC_INVOKED // code only +// These are really COMMDLG dialogs, so there usually isn't a resource +// for them, but these IDs are used as help IDs. +#define AFX_IDD_FILEOPEN 28676 +#define AFX_IDD_FILESAVE 28677 +#define AFX_IDD_FONT 28678 +#define AFX_IDD_COLOR 28679 +#define AFX_IDD_PRINT 28680 +#define AFX_IDD_PRINTSETUP 28681 +#define AFX_IDD_FIND 28682 +#define AFX_IDD_REPLACE 28683 +#endif //!RC_INVOKED + +// Standard dialogs app should leave alone (0x7801->) +#define AFX_IDD_NEWTYPEDLG 30721 +#define AFX_IDD_PRINTDLG 30722 +#define AFX_IDD_PREVIEW_TOOLBAR 30723 + +// Dialogs defined for OLE2UI library +#define AFX_IDD_INSERTOBJECT 30724 +#define AFX_IDD_CHANGEICON 30725 +#define AFX_IDD_CONVERT 30726 +#define AFX_IDD_PASTESPECIAL 30727 +#define AFX_IDD_EDITLINKS 30728 +#define AFX_IDD_FILEBROWSE 30729 +#define AFX_IDD_BUSY 30730 + +#define AFX_IDD_OBJECTPROPERTIES 30732 +#define AFX_IDD_CHANGESOURCE 30733 + +//WinForms +#define AFX_IDD_EMPTYDIALOG 30734 + +// Standard cursors (0x7901->) + // AFX_IDC = Cursor resources +#define AFX_IDC_CONTEXTHELP 30977 // context sensitive help +#define AFX_IDC_MAGNIFY 30978 // print preview zoom +#define AFX_IDC_SMALLARROWS 30979 // splitter +#define AFX_IDC_HSPLITBAR 30980 // splitter +#define AFX_IDC_VSPLITBAR 30981 // splitter +#define AFX_IDC_NODROPCRSR 30982 // No Drop Cursor +#define AFX_IDC_TRACKNWSE 30983 // tracker +#define AFX_IDC_TRACKNESW 30984 // tracker +#define AFX_IDC_TRACKNS 30985 // tracker +#define AFX_IDC_TRACKWE 30986 // tracker +#define AFX_IDC_TRACK4WAY 30987 // tracker +#define AFX_IDC_MOVE4WAY 30988 // resize bar (server only) + +// Wheel mouse cursors +// NOTE: values must be in this order! See CScrollView::OnTimer() +#define AFX_IDC_MOUSE_PAN_NW 30998 // pan east +#define AFX_IDC_MOUSE_PAN_N 30999 // pan northeast +#define AFX_IDC_MOUSE_PAN_NE 31000 // pan north +#define AFX_IDC_MOUSE_PAN_W 31001 // pan northwest +#define AFX_IDC_MOUSE_PAN_HV 31002 // pan both axis +#define AFX_IDC_MOUSE_PAN_E 31003 // pan west +#define AFX_IDC_MOUSE_PAN_SW 31004 // pan south-west +#define AFX_IDC_MOUSE_PAN_S 31005 // pan south +#define AFX_IDC_MOUSE_PAN_SE 31006 // pan south-east +#define AFX_IDC_MOUSE_PAN_HORZ 31007 // pan X-axis +#define AFX_IDC_MOUSE_PAN_VERT 31008 // pan Y-axis + +// Wheel mouse bitmaps +#define AFX_IDC_MOUSE_ORG_HORZ 31009 // anchor for horz only +#define AFX_IDC_MOUSE_ORG_VERT 31010 // anchor for vert only +#define AFX_IDC_MOUSE_ORG_HV 31011 // anchor for horz/vert +#define AFX_IDC_MOUSE_MASK 31012 + +// Mini frame window bitmap ID +#define AFX_IDB_MINIFRAME_MENU 30994 + +// CheckListBox checks bitmap ID +#define AFX_IDB_CHECKLISTBOX_95 30996 + +// AFX standard accelerator resources +#define AFX_IDR_PREVIEW_ACCEL 30997 + +// AFX standard ICON IDs (for MFC V1 apps) (0x7A01->) +#define AFX_IDI_STD_MDIFRAME 31233 +#define AFX_IDI_STD_FRAME 31234 + +///////////////////////////////////////////////////////////////////////////// +// AFX OLE control implementation - control IDs (AFX_IDC) + +// Font property page +#define AFX_IDC_FONTPROP 1000 +#define AFX_IDC_FONTNAMES 1001 +#define AFX_IDC_FONTSTYLES 1002 +#define AFX_IDC_FONTSIZES 1003 +#define AFX_IDC_STRIKEOUT 1004 +#define AFX_IDC_UNDERLINE 1005 +#define AFX_IDC_SAMPLEBOX 1006 + +// Color property page +#define AFX_IDC_COLOR_BLACK 1100 +#define AFX_IDC_COLOR_WHITE 1101 +#define AFX_IDC_COLOR_RED 1102 +#define AFX_IDC_COLOR_GREEN 1103 +#define AFX_IDC_COLOR_BLUE 1104 +#define AFX_IDC_COLOR_YELLOW 1105 +#define AFX_IDC_COLOR_MAGENTA 1106 +#define AFX_IDC_COLOR_CYAN 1107 +#define AFX_IDC_COLOR_GRAY 1108 +#define AFX_IDC_COLOR_LIGHTGRAY 1109 +#define AFX_IDC_COLOR_DARKRED 1110 +#define AFX_IDC_COLOR_DARKGREEN 1111 +#define AFX_IDC_COLOR_DARKBLUE 1112 +#define AFX_IDC_COLOR_LIGHTBROWN 1113 +#define AFX_IDC_COLOR_DARKMAGENTA 1114 +#define AFX_IDC_COLOR_DARKCYAN 1115 +#define AFX_IDC_COLORPROP 1116 +#define AFX_IDC_SYSTEMCOLORS 1117 + +// Picture porperty page +#define AFX_IDC_PROPNAME 1201 +#define AFX_IDC_PICTURE 1202 +#define AFX_IDC_BROWSE 1203 +#define AFX_IDC_CLEAR 1204 + +///////////////////////////////////////////////////////////////////////////// +// IDRs for OLE control standard components + +// Standard propery page dialogs app should leave alone (0x7E01->) +#define AFX_IDD_PROPPAGE_COLOR 32257 +#define AFX_IDD_PROPPAGE_FONT 32258 +#define AFX_IDD_PROPPAGE_PICTURE 32259 + +#define AFX_IDB_TRUETYPE 32384 + +///////////////////////////////////////////////////////////////////////////// +// Standard OLE control strings + +// OLE Control page strings +#define AFX_IDS_PROPPAGE_UNKNOWN 0xFE01 +#define AFX_IDS_COLOR_DESKTOP 0xFE04 +#define AFX_IDS_COLOR_APPWORKSPACE 0xFE05 +#define AFX_IDS_COLOR_WNDBACKGND 0xFE06 +#define AFX_IDS_COLOR_WNDTEXT 0xFE07 +#define AFX_IDS_COLOR_MENUBAR 0xFE08 +#define AFX_IDS_COLOR_MENUTEXT 0xFE09 +#define AFX_IDS_COLOR_ACTIVEBAR 0xFE0A +#define AFX_IDS_COLOR_INACTIVEBAR 0xFE0B +#define AFX_IDS_COLOR_ACTIVETEXT 0xFE0C +#define AFX_IDS_COLOR_INACTIVETEXT 0xFE0D +#define AFX_IDS_COLOR_ACTIVEBORDER 0xFE0E +#define AFX_IDS_COLOR_INACTIVEBORDER 0xFE0F +#define AFX_IDS_COLOR_WNDFRAME 0xFE10 +#define AFX_IDS_COLOR_SCROLLBARS 0xFE11 +#define AFX_IDS_COLOR_BTNFACE 0xFE12 +#define AFX_IDS_COLOR_BTNSHADOW 0xFE13 +#define AFX_IDS_COLOR_BTNTEXT 0xFE14 +#define AFX_IDS_COLOR_BTNHIGHLIGHT 0xFE15 +#define AFX_IDS_COLOR_DISABLEDTEXT 0xFE16 +#define AFX_IDS_COLOR_HIGHLIGHT 0xFE17 +#define AFX_IDS_COLOR_HIGHLIGHTTEXT 0xFE18 +#define AFX_IDS_REGULAR 0xFE19 +#define AFX_IDS_BOLD 0xFE1A +#define AFX_IDS_ITALIC 0xFE1B +#define AFX_IDS_BOLDITALIC 0xFE1C +#define AFX_IDS_SAMPLETEXT 0xFE1D +#define AFX_IDS_DISPLAYSTRING_FONT 0xFE1E +#define AFX_IDS_DISPLAYSTRING_COLOR 0xFE1F +#define AFX_IDS_DISPLAYSTRING_PICTURE 0xFE20 +#define AFX_IDS_PICTUREFILTER 0xFE21 +#define AFX_IDS_PICTYPE_UNKNOWN 0xFE22 +#define AFX_IDS_PICTYPE_NONE 0xFE23 +#define AFX_IDS_PICTYPE_BITMAP 0xFE24 +#define AFX_IDS_PICTYPE_METAFILE 0xFE25 +#define AFX_IDS_PICTYPE_ICON 0xFE26 +#define AFX_IDS_COLOR_PPG 0xFE28 +#define AFX_IDS_COLOR_PPG_CAPTION 0xFE29 +#define AFX_IDS_FONT_PPG 0xFE2A +#define AFX_IDS_FONT_PPG_CAPTION 0xFE2B +#define AFX_IDS_PICTURE_PPG 0xFE2C +#define AFX_IDS_PICTURE_PPG_CAPTION 0xFE2D +#define AFX_IDS_PICTUREBROWSETITLE 0xFE30 +#define AFX_IDS_BORDERSTYLE_0 0xFE31 +#define AFX_IDS_BORDERSTYLE_1 0xFE32 + +// OLE Control verb names +#define AFX_IDS_VERB_EDIT 0xFE40 +#define AFX_IDS_VERB_PROPERTIES 0xFE41 + +// OLE Control internal error messages +#define AFX_IDP_PICTURECANTOPEN 0xFE83 +#define AFX_IDP_PICTURECANTLOAD 0xFE84 +#define AFX_IDP_PICTURETOOLARGE 0xFE85 +#define AFX_IDP_PICTUREREADFAILED 0xFE86 + +// Standard OLE Control error strings +#define AFX_IDP_E_ILLEGALFUNCTIONCALL 0xFEA0 +#define AFX_IDP_E_OVERFLOW 0xFEA1 +#define AFX_IDP_E_OUTOFMEMORY 0xFEA2 +#define AFX_IDP_E_DIVISIONBYZERO 0xFEA3 +#define AFX_IDP_E_OUTOFSTRINGSPACE 0xFEA4 +#define AFX_IDP_E_OUTOFSTACKSPACE 0xFEA5 +#define AFX_IDP_E_BADFILENAMEORNUMBER 0xFEA6 +#define AFX_IDP_E_FILENOTFOUND 0xFEA7 +#define AFX_IDP_E_BADFILEMODE 0xFEA8 +#define AFX_IDP_E_FILEALREADYOPEN 0xFEA9 +#define AFX_IDP_E_DEVICEIOERROR 0xFEAA +#define AFX_IDP_E_FILEALREADYEXISTS 0xFEAB +#define AFX_IDP_E_BADRECORDLENGTH 0xFEAC +#define AFX_IDP_E_DISKFULL 0xFEAD +#define AFX_IDP_E_BADRECORDNUMBER 0xFEAE +#define AFX_IDP_E_BADFILENAME 0xFEAF +#define AFX_IDP_E_TOOMANYFILES 0xFEB0 +#define AFX_IDP_E_DEVICEUNAVAILABLE 0xFEB1 +#define AFX_IDP_E_PERMISSIONDENIED 0xFEB2 +#define AFX_IDP_E_DISKNOTREADY 0xFEB3 +#define AFX_IDP_E_PATHFILEACCESSERROR 0xFEB4 +#define AFX_IDP_E_PATHNOTFOUND 0xFEB5 +#define AFX_IDP_E_INVALIDPATTERNSTRING 0xFEB6 +#define AFX_IDP_E_INVALIDUSEOFNULL 0xFEB7 +#define AFX_IDP_E_INVALIDFILEFORMAT 0xFEB8 +#define AFX_IDP_E_INVALIDPROPERTYVALUE 0xFEB9 +#define AFX_IDP_E_INVALIDPROPERTYARRAYINDEX 0xFEBA +#define AFX_IDP_E_SETNOTSUPPORTEDATRUNTIME 0xFEBB +#define AFX_IDP_E_SETNOTSUPPORTED 0xFEBC +#define AFX_IDP_E_NEEDPROPERTYARRAYINDEX 0xFEBD +#define AFX_IDP_E_SETNOTPERMITTED 0xFEBE +#define AFX_IDP_E_GETNOTSUPPORTEDATRUNTIME 0xFEBF +#define AFX_IDP_E_GETNOTSUPPORTED 0xFEC0 +#define AFX_IDP_E_PROPERTYNOTFOUND 0xFEC1 +#define AFX_IDP_E_INVALIDCLIPBOARDFORMAT 0xFEC2 +#define AFX_IDP_E_INVALIDPICTURE 0xFEC3 +#define AFX_IDP_E_PRINTERERROR 0xFEC4 +#define AFX_IDP_E_CANTSAVEFILETOTEMP 0xFEC5 +#define AFX_IDP_E_SEARCHTEXTNOTFOUND 0xFEC6 +#define AFX_IDP_E_REPLACEMENTSTOOLONG 0xFEC7 + +///////////////////////////////////////////////////////////////////////////// + +#ifdef _AFX_MINREBUILD +#pragma component(minrebuild, on) +#endif + +#endif //__AFXRES_H__ + +///////////////////////////////////////////////////////////////////////////// diff --git a/Externals/MusicMod/Player/Src/fftw3/fftw3.h b/Externals/MusicMod/Player/Src/fftw3/fftw3.h new file mode 100644 index 0000000000..64e445d30d --- /dev/null +++ b/Externals/MusicMod/Player/Src/fftw3/fftw3.h @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2003 Matteo Frigo + * Copyright (c) 2003 Massachusetts Institute of Technology + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* header file for fftw3 */ +/* $Id: fftw3.h,v 1.1 2005/10/28 19:01:21 hartwork Exp $ */ + +#ifndef FFTW3_H +#define FFTW3_H + +#if defined(__ICC) || defined(_MSC_VER) +#pragma comment ( lib, "fftw3" ) +#endif + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* If is included, use the C99 complex type. Otherwise + define a type bit-compatible with C99 complex */ +#ifdef _Complex_I +# define FFTW_DEFINE_COMPLEX(R, C) typedef R _Complex C +#else +# define FFTW_DEFINE_COMPLEX(R, C) typedef R C[2] +#endif + +#define FFTW_CONCAT(prefix, name) prefix ## name +#define FFTW_MANGLE_DOUBLE(name) FFTW_CONCAT(fftw_, name) +#define FFTW_MANGLE_FLOAT(name) FFTW_CONCAT(fftwf_, name) +#define FFTW_MANGLE_LONG_DOUBLE(name) FFTW_CONCAT(fftwl_, name) + + +enum fftw_r2r_kind_do_not_use_me { + FFTW_R2HC=0, FFTW_HC2R=1, FFTW_DHT=2, + FFTW_REDFT00=3, FFTW_REDFT01=4, FFTW_REDFT10=5, FFTW_REDFT11=6, + FFTW_RODFT00=7, FFTW_RODFT01=8, FFTW_RODFT10=9, FFTW_RODFT11=10 +}; + +struct fftw_iodim_do_not_use_me { + int n; /* dimension size */ + int is; /* input stride */ + int os; /* output stride */ +}; + +/* + huge second-order macro that defines prototypes for all API + functions. We expand this macro for each supported precision + + X: name-mangling macro + R: real data type + C: complex data type +*/ + +#define FFTW_DEFINE_API(X, R, C) \ + \ +FFTW_DEFINE_COMPLEX(R, C); \ + \ +typedef struct X(plan_s) *X(plan); \ + \ +typedef struct fftw_iodim_do_not_use_me X(iodim); \ + \ +typedef enum fftw_r2r_kind_do_not_use_me X(r2r_kind); \ + \ +void X(execute)(const X(plan) p); \ + \ +X(plan) X(plan_dft)(int rank, const int *n, \ + C *in, C *out, int sign, unsigned flags); \ + \ +X(plan) X(plan_dft_1d)(int n, C *in, C *out, int sign, \ + unsigned flags); \ +X(plan) X(plan_dft_2d)(int nx, int ny, \ + C *in, C *out, int sign, unsigned flags); \ +X(plan) X(plan_dft_3d)(int nx, int ny, int nz, \ + C *in, C *out, int sign, unsigned flags); \ + \ +X(plan) X(plan_many_dft)(int rank, const int *n, \ + int howmany, \ + C *in, const int *inembed, \ + int istride, int idist, \ + C *out, const int *onembed, \ + int ostride, int odist, \ + int sign, unsigned flags); \ + \ +X(plan) X(plan_guru_dft)(int rank, const X(iodim) *dims, \ + int howmany_rank, \ + const X(iodim) *howmany_dims, \ + C *in, C *out, \ + int sign, unsigned flags); \ +X(plan) X(plan_guru_split_dft)(int rank, const X(iodim) *dims, \ + int howmany_rank, \ + const X(iodim) *howmany_dims, \ + R *ri, R *ii, R *ro, R *io, \ + unsigned flags); \ + \ +void X(execute_dft)(const X(plan) p, C *in, C *out); \ +void X(execute_split_dft)(const X(plan) p, R *ri, R *ii, R *ro, R *io); \ + \ +X(plan) X(plan_many_dft_r2c)(int rank, const int *n, \ + int howmany, \ + R *in, const int *inembed, \ + int istride, int idist, \ + C *out, const int *onembed, \ + int ostride, int odist, \ + unsigned flags); \ + \ +X(plan) X(plan_dft_r2c)(int rank, const int *n, \ + R *in, C *out, unsigned flags); \ + \ +X(plan) X(plan_dft_r2c_1d)(int n,R *in,C *out,unsigned flags); \ +X(plan) X(plan_dft_r2c_2d)(int nx, int ny, \ + R *in, C *out, unsigned flags); \ +X(plan) X(plan_dft_r2c_3d)(int nx, int ny, \ + int nz, \ + R *in, C *out, unsigned flags); \ + \ + \ +X(plan) X(plan_many_dft_c2r)(int rank, const int *n, \ + int howmany, \ + C *in, const int *inembed, \ + int istride, int idist, \ + R *out, const int *onembed, \ + int ostride, int odist, \ + unsigned flags); \ + \ +X(plan) X(plan_dft_c2r)(int rank, const int *n, \ + C *in, R *out, unsigned flags); \ + \ +X(plan) X(plan_dft_c2r_1d)(int n,C *in,R *out,unsigned flags); \ +X(plan) X(plan_dft_c2r_2d)(int nx, int ny, \ + C *in, R *out, unsigned flags); \ +X(plan) X(plan_dft_c2r_3d)(int nx, int ny, \ + int nz, \ + C *in, R *out, unsigned flags); \ + \ +X(plan) X(plan_guru_dft_r2c)(int rank, const X(iodim) *dims, \ + int howmany_rank, \ + const X(iodim) *howmany_dims, \ + R *in, C *out, \ + unsigned flags); \ +X(plan) X(plan_guru_dft_c2r)(int rank, const X(iodim) *dims, \ + int howmany_rank, \ + const X(iodim) *howmany_dims, \ + C *in, R *out, \ + unsigned flags); \ + \ +X(plan) X(plan_guru_split_dft_r2c)(int rank, const X(iodim) *dims, \ + int howmany_rank, \ + const X(iodim) *howmany_dims, \ + R *in, R *ro, R *io, \ + unsigned flags); \ +X(plan) X(plan_guru_split_dft_c2r)(int rank, const X(iodim) *dims, \ + int howmany_rank, \ + const X(iodim) *howmany_dims, \ + R *ri, R *ii, R *out, \ + unsigned flags); \ + \ +void X(execute_dft_r2c)(const X(plan) p, R *in, C *out); \ +void X(execute_dft_c2r)(const X(plan) p, C *in, R *out); \ + \ +void X(execute_split_dft_r2c)(const X(plan) p, R *in, R *ro, R *io); \ +void X(execute_split_dft_c2r)(const X(plan) p, R *ri, R *ii, R *out); \ + \ +X(plan) X(plan_many_r2r)(int rank, const int *n, \ + int howmany, \ + R *in, const int *inembed, \ + int istride, int idist, \ + R *out, const int *onembed, \ + int ostride, int odist, \ + const X(r2r_kind) *kind, unsigned flags); \ + \ +X(plan) X(plan_r2r)(int rank, const int *n, R *in, R *out, \ + const X(r2r_kind) *kind, unsigned flags); \ + \ +X(plan) X(plan_r2r_1d)(int n, R *in, R *out, \ + X(r2r_kind) kind, unsigned flags); \ +X(plan) X(plan_r2r_2d)(int nx, int ny, R *in, R *out, \ + X(r2r_kind) kindx, X(r2r_kind) kindy, \ + unsigned flags); \ +X(plan) X(plan_r2r_3d)(int nx, int ny, int nz, \ + R *in, R *out, X(r2r_kind) kindx, \ + X(r2r_kind) kindy, X(r2r_kind) kindz, \ + unsigned flags); \ + \ +X(plan) X(plan_guru_r2r)(int rank, const X(iodim) *dims, \ + int howmany_rank, \ + const X(iodim) *howmany_dims, \ + R *in, R *out, \ + const X(r2r_kind) *kind, unsigned flags); \ +void X(execute_r2r)(const X(plan) p, R *in, R *out); \ + \ +void X(destroy_plan)(X(plan) p); \ +void X(forget_wisdom)(void); \ +void X(cleanup)(void); \ + \ +void X(plan_with_nthreads)(int nthreads); \ +int X(init_threads)(void); \ +void X(cleanup_threads)(void); \ + \ +void X(export_wisdom_to_file)(FILE *output_file); \ +char *X(export_wisdom_to_string)(void); \ +void X(export_wisdom)(void (*write_char)(char c, void *), void *data); \ +int X(import_system_wisdom)(void); \ +int X(import_wisdom_from_file)(FILE *input_file); \ +int X(import_wisdom_from_string)(const char *input_string); \ +int X(import_wisdom)(int (*read_char)(void *), void *data); \ + \ +void X(fprint_plan)(const X(plan) p, FILE *output_file); \ +void X(print_plan)(const X(plan) p); \ + \ +void *X(malloc)(size_t n); \ +void X(free)(void *p); \ + \ +void X(flops)(const X(plan) p, double *add, double *mul, double *fma); \ + \ +extern const char X(version)[]; \ +extern const char X(cc)[]; \ +extern const char X(codelet_optim)[]; + + +/* end of FFTW_DEFINE_API macro */ + +FFTW_DEFINE_API(FFTW_MANGLE_DOUBLE, double, fftw_complex) +FFTW_DEFINE_API(FFTW_MANGLE_FLOAT, float, fftwf_complex) +FFTW_DEFINE_API(FFTW_MANGLE_LONG_DOUBLE, long double, fftwl_complex) + +#define FFTW_FORWARD (-1) +#define FFTW_BACKWARD (+1) + +/* documented flags */ +#define FFTW_MEASURE (0U) +#define FFTW_DESTROY_INPUT (1U << 0) +#define FFTW_UNALIGNED (1U << 1) +#define FFTW_CONSERVE_MEMORY (1U << 2) +#define FFTW_EXHAUSTIVE (1U << 3) /* NO_EXHAUSTIVE is default */ +#define FFTW_PRESERVE_INPUT (1U << 4) /* cancels FFTW_DESTROY_INPUT */ +#define FFTW_PATIENT (1U << 5) /* IMPATIENT is default */ +#define FFTW_ESTIMATE (1U << 6) + +/* undocumented beyond-guru flags */ +#define FFTW_ESTIMATE_PATIENT (1U << 7) +#define FFTW_BELIEVE_PCOST (1U << 8) +#define FFTW_DFT_R2HC_ICKY (1U << 9) +#define FFTW_NONTHREADED_ICKY (1U << 10) +#define FFTW_NO_BUFFERING (1U << 11) +#define FFTW_NO_INDIRECT_OP (1U << 12) +#define FFTW_ALLOW_LARGE_GENERIC (1U << 13) /* NO_LARGE_GENERIC is default */ +#define FFTW_NO_RANK_SPLITS (1U << 14) +#define FFTW_NO_VRANK_SPLITS (1U << 15) +#define FFTW_NO_VRECURSE (1U << 16) + +#define FFTW_NO_SIMD (1U << 17) + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* FFTW3_H */ diff --git a/Externals/MusicMod/Player/Src/vis_plainbar/vis_plainbar.cpp b/Externals/MusicMod/Player/Src/vis_plainbar/vis_plainbar.cpp new file mode 100644 index 0000000000..276065e516 --- /dev/null +++ b/Externals/MusicMod/Player/Src/vis_plainbar/vis_plainbar.cpp @@ -0,0 +1,259 @@ +//////////////////////////////////////////////////////////////////////////////// +// Plainamp Toolbar Vis Plugin +// +// Copyright İ 2006 Sebastian Pipping +// +// --> http://www.hartwork.org +// +// This source code is released under the GNU General Public License (GPL). +// See GPL.txt for details. Any non-GPL usage is strictly forbidden. +//////////////////////////////////////////////////////////////////////////////// + + +#define WIN32_LEAN_AND_MEAN +#include + + +#include "../Winamp/vis.h" +#include "../Winamp/wa_ipc.h" + + + +#define PLUGIN_NAME "Plainamp Toolbar Vis Plugin" +#define PLUGIN_VERSION "v0.5" + +#define PLUGIN_DESC PLUGIN_NAME " " PLUGIN_VERSION + + + +static char * szClassName = "PlainbarClass"; + + + +winampVisModule * getModule( int which ); + + +void config( struct winampVisModule * this_mod ); +int init( struct winampVisModule * this_mod ); +int render_spec( struct winampVisModule * this_mod ); +void quit( struct winampVisModule * this_mod ); + + +// Double buffering data +HDC memDC = NULL; // Memory device context +HBITMAP memBM = NULL; // Memory bitmap (for memDC) +HBITMAP oldBM = NULL; // Old bitmap (from memDC) + + +HWND hRender = NULL; +int width = 0; +int height = 0; +bool bRunning = false; +HPEN pen = NULL; + + +WNDPROC WndprocTargetBackup = NULL; +LRESULT CALLBACK WndprocTarget( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ); + + + +winampVisHeader hdr = { + VIS_HDRVER, + PLUGIN_DESC, + getModule +}; + + + +winampVisModule mod_spec = +{ + "Default", + NULL, // hwndParent + NULL, // hDllInstance + 0, // sRate + 0, // nCh + 25, // latencyMS + 25, // delayMS + 2, // spectrumNch + 0, // waveformNch + { 0, }, // spectrumData + { 0, }, // waveformData + config, + init, + render_spec, + quit +}; + + + +#ifdef __cplusplus +extern "C" { +#endif +__declspec( dllexport ) winampVisHeader * winampVisGetHeader() +{ + return &hdr; +} +#ifdef __cplusplus +} +#endif + + + +winampVisModule * getModule( int which ) +{ + return which ? NULL : &mod_spec; +} + + + +void config( struct winampVisModule * this_mod ) +{ + MessageBox( + this_mod->hwndParent, + PLUGIN_DESC "\n" + "\n" + "Copyright İ 2006 Sebastian Pipping \n" + "\n" + "\n" + "--> http://www.hartwork.org", + "About", + MB_ICONINFORMATION + ); +} + + + +LRESULT CALLBACK WndprocTarget( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + switch( message ) + { + case WM_SIZE: + width = LOWORD( lp ); + height = HIWORD( lp ); + break; + + case WM_DESTROY: + bRunning = false; + PostQuitMessage( 0 ); + return 0; + + } + + return DefWindowProc( hwnd, message, wp, lp ); +} + + + +int init( struct winampVisModule * this_mod ) +{ + if( !this_mod ) return 1; + + // Register message + const int IPC_GETPLAINBARTARGET = ( int )SendMessage( this_mod->hwndParent, WM_WA_IPC, ( WPARAM )"IPC_GETPLAINBARTARGET", IPC_REGISTER_WINAMP_IPCMESSAGE ); + if( IPC_GETPLAINBARTARGET <= 0 ) return 1; + + // Get render parent + HWND hRenderParent = ( HWND )SendMessage( this_mod->hwndParent, WM_WA_IPC, 0, IPC_GETPLAINBARTARGET ); + if( !IsWindow( hRenderParent ) ) return 1; + + // Plug our child in + WNDCLASS wc; + ZeroMemory( &wc, sizeof( WNDCLASS ) ); + wc.lpfnWndProc = WndprocTarget; + wc.hInstance = this_mod->hDllInstance; + wc.hCursor = LoadCursor( NULL, IDC_ARROW ); + wc.lpszClassName = szClassName; + + if( !RegisterClass( &wc ) ) return 1; + + RECT r; + GetClientRect( hRenderParent, &r ); + width = r.right - r.left; + height = r.bottom - r.top; + + hRender = CreateWindowEx( + 0, + szClassName, + "", + WS_CHILD | WS_VISIBLE, + 0, + 0, + width, + height, + hRenderParent, + NULL, + this_mod->hDllInstance, + 0 + ); + + if( !hRender ) + { + UnregisterClass( szClassName, this_mod->hDllInstance ); + return 1; + } + + // Create doublebuffer + const HDC hdc = GetDC( hRender ); + memDC = CreateCompatibleDC( hdc ); + memBM = CreateCompatibleBitmap( hdc, 576, 256 ); + oldBM = ( HBITMAP )SelectObject( memDC, memBM ); + ReleaseDC( hRender, hdc ); + + pen = CreatePen( PS_SOLID, 0, GetSysColor( COLOR_APPWORKSPACE ) ); + + bRunning = true; + return 0; +} + + + +int render_spec( struct winampVisModule * this_mod ) +{ + // Clear background + RECT rect = { 0, 0, 576, 256 }; + FillRect(memDC, &rect, ( HBRUSH )( COLOR_3DFACE + 1 ) ); + + // Draw analyser + SelectObject( memDC, pen ); + for( int x = 0; x < 576; x++ ) + { + int val = 0; + + for( int y = 0; y < this_mod->nCh; y++ ) + { + if( this_mod->spectrumData[ y ][ x ] > val ) + { + val = this_mod->spectrumData[ y ][ x ]; + } + } + + MoveToEx( memDC, x, 256, NULL ); + LineTo( memDC, x, 256 - val ); + } + + // Copy doublebuffer to window + HDC hdc = GetDC( hRender ); + StretchBlt( hdc, 0, 0, width, height, memDC, 0, 0, 576, 256, SRCCOPY ); + ReleaseDC( hRender, hdc ); + + return bRunning ? 0 : 1; +} + + + +void quit( struct winampVisModule * this_mod ) +{ + if( bRunning ) + { + DestroyWindow( hRender ); + } + + UnregisterClass( szClassName, this_mod->hDllInstance ); + + // Delete doublebuffer + SelectObject( memDC, oldBM ); + DeleteObject( memDC ); + DeleteObject( memBM ); + + DeleteObject( pen ); +} diff --git a/Externals/MusicMod/Player/Src/zlib/zconf.h b/Externals/MusicMod/Player/Src/zlib/zconf.h new file mode 100644 index 0000000000..db9ff5e7e3 --- /dev/null +++ b/Externals/MusicMod/Player/Src/zlib/zconf.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zconf.h,v 1.1 2005/10/10 21:08:30 hartwork Exp $ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/Externals/MusicMod/Player/Src/zlib/zlib.h b/Externals/MusicMod/Player/Src/zlib/zlib.h new file mode 100644 index 0000000000..62d0e4675b --- /dev/null +++ b/Externals/MusicMod/Player/Src/zlib/zlib.h @@ -0,0 +1,1357 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/Externals/MusicMod/TestPlayer/Src/Player.cpp b/Externals/MusicMod/TestPlayer/Src/Player.cpp new file mode 100644 index 0000000000..d22de77d8a --- /dev/null +++ b/Externals/MusicMod/TestPlayer/Src/Player.cpp @@ -0,0 +1,37 @@ +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + + + +////////////////////////////////////////////////////////////////////////////////////////// +// File description +/* ŻŻŻŻŻŻŻŻŻŻ +// This project loads the dll */ +/////////////////////////////////// + + +#include // System + +#include "../../Player/Src/PlayerExport.h" // Local player + + +void main() +{ + Player_Main(true); + + std::cin.get(); // Keep the window open +} \ No newline at end of file diff --git a/Externals/MusicMod/TestPlayer/TestPlayer.vcproj b/Externals/MusicMod/TestPlayer/TestPlayer.vcproj new file mode 100644 index 0000000000..d19f9f460b --- /dev/null +++ b/Externals/MusicMod/TestPlayer/TestPlayer.vcproj @@ -0,0 +1,403 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/MusicMod.sln b/Source/MusicMod.sln new file mode 100644 index 0000000000..b14f3cf139 --- /dev/null +++ b/Source/MusicMod.sln @@ -0,0 +1,498 @@ +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Core", "Core\Core\Core.vcproj", "{F0B874CB-4476-4199-9315-8343D05AE684}" + ProjectSection(ProjectDependencies) = postProject + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} = {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA} = {C7E5D50A-2916-464B-86A7-E10B3CC88ADA} + {11F55366-12EC-4C44-A8CB-1D4E315D61ED} = {11F55366-12EC-4C44-A8CB-1D4E315D61ED} + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} = {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF} = {95CCAABC-7062-47C4-B8C1-A064DD5F16FF} + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510} = {29C2ABC1-ADA5-42CD-A5FC-96022D52A510} + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C} = {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_VideoDX9", "Plugins\Plugin_VideoDX9\Plugin_VideoDX9.vcproj", "{636FAD5F-02D1-4E9A-BE67-FB8EA99B9A18}" + ProjectSection(ProjectDependencies) = postProject + {3E03C179-8251-46E4-81F4-466F114BAC63} = {3E03C179-8251-46E4-81F4-466F114BAC63} + {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA} = {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA} + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_PadSimple", "Plugins\Plugin_PadSimple\Plugin_PadSimple.vcproj", "{9A183B48-ECC2-4121-876A-9B3793686073}" + ProjectSection(ProjectDependencies) = postProject + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} = {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA} = {C7E5D50A-2916-464B-86A7-E10B3CC88ADA} + {11F55366-12EC-4C44-A8CB-1D4E315D61ED} = {11F55366-12EC-4C44-A8CB-1D4E315D61ED} + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} = {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Bochs_disasm", "..\Externals\Bochs_disasm\Bochs_disasm.vcproj", "{29C2ABC1-ADA5-42CD-A5FC-96022D52A510}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\Externals\zlib\zlib.vcproj", "{3E03C179-8251-46E4-81F4-466F114BAC63}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_DSP_LLE", "Plugins\Plugin_DSP_LLE\Plugin_DSP_LLE.vcproj", "{3D8156A9-64D1-4C8E-ADBE-1B319030E4A4}" + ProjectSection(ProjectDependencies) = postProject + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DiscIO", "Core\DiscIO\DiscIO.vcproj", "{B7F1A9FB-BEA8-416E-9460-AE35A6A5165C}" + ProjectSection(ProjectDependencies) = postProject + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF} = {95CCAABC-7062-47C4-B8C1-A064DD5F16FF} + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_VideoOGL", "Plugins\Plugin_VideoOGL\Plugin_VideoOGL.vcproj", "{CFDCEE0E-FA45-4F72-9FCC-0B88F5A75160}" + ProjectSection(ProjectDependencies) = postProject + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} = {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} + {11F55366-12EC-4C44-A8CB-1D4E315D61ED} = {11F55366-12EC-4C44-A8CB-1D4E315D61ED} + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} = {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} + {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA} = {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA} + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "Core\Common\Common.vcproj", "{C573CAF7-EE6A-458E-8049-16C0BF34C2E9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dolphin", "Core\DolphinWX\DolphinWX.vcproj", "{A72606EF-C5C1-4954-90AD-F0F93A8D97D9}" + ProjectSection(ProjectDependencies) = postProject + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} = {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} + {CFDCEE0E-FA45-4F72-9FCC-0B88F5A75160} = {CFDCEE0E-FA45-4F72-9FCC-0B88F5A75160} + {11F55366-12EC-4C44-A8CB-1D4E315D61ED} = {11F55366-12EC-4C44-A8CB-1D4E315D61ED} + {8D612734-FAA5-4B8A-804F-4DEA2367D495} = {8D612734-FAA5-4B8A-804F-4DEA2367D495} + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} = {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} + {33546D62-7F34-4EA6-A88E-D538B36E16BF} = {33546D62-7F34-4EA6-A88E-D538B36E16BF} + {3E03C179-8251-46E4-81F4-466F114BAC63} = {3E03C179-8251-46E4-81F4-466F114BAC63} + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF} = {95CCAABC-7062-47C4-B8C1-A064DD5F16FF} + {521498BE-6089-4780-8223-E67C22F4E068} = {521498BE-6089-4780-8223-E67C22F4E068} + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510} = {29C2ABC1-ADA5-42CD-A5FC-96022D52A510} + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77} = {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77} + {F0B874CB-4476-4199-9315-8343D05AE684} = {F0B874CB-4476-4199-9315-8343D05AE684} + {0B72B5D6-5D72-4391-84A7-9CCA5392668A} = {0B72B5D6-5D72-4391-84A7-9CCA5392668A} + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C} = {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxBase28", "..\Externals\wxWidgets\build\msw\wx_base.vcproj", "{1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxCore28", "..\Externals\wxWidgets\build\msw\wx_core.vcproj", "{11F55366-12EC-4C44-A8CB-1D4E315D61ED}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Debugger", "Core\DebuggerWX\DebuggerWX.vcproj", "{4D3CD4C5-412B-4B49-9B1B-A68A2A129C77}" + ProjectSection(ProjectDependencies) = postProject + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} = {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} + {11F55366-12EC-4C44-A8CB-1D4E315D61ED} = {11F55366-12EC-4C44-A8CB-1D4E315D61ED} + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} = {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510} = {29C2ABC1-ADA5-42CD-A5FC-96022D52A510} + {F0B874CB-4476-4199-9315-8343D05AE684} = {F0B874CB-4476-4199-9315-8343D05AE684} + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoCommon", "Core\VideoCommon\VideoCommon.vcproj", "{E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA}" + ProjectSection(ProjectDependencies) = postProject + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_DSP_HLE", "Plugins\Plugin_DSP_HLE\Plugin_DSP_HLE.vcproj", "{D6E56527-BBB9-4EAD-A6EC-49D4BF6AFCD8}" + ProjectSection(ProjectDependencies) = postProject + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} = {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} + {11F55366-12EC-4C44-A8CB-1D4E315D61ED} = {11F55366-12EC-4C44-A8CB-1D4E315D61ED} + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} = {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LZO", "..\Externals\LZO\LZO.vcproj", "{33546D62-7F34-4EA6-A88E-D538B36E16BF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_Wiimote", "Plugins\Plugin_Wiimote\Plugin_Wiimote.vcproj", "{8D612734-FAA5-4B8A-804F-4DEA2367D495}" + ProjectSection(ProjectDependencies) = postProject + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} = {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA} = {C7E5D50A-2916-464B-86A7-E10B3CC88ADA} + {11F55366-12EC-4C44-A8CB-1D4E315D61ED} = {11F55366-12EC-4C44-A8CB-1D4E315D61ED} + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} = {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_nJoy_SDL_Test", "Plugins\Plugin_nJoy_Testing\Plugin_nJoy_SDL_Test.vcproj", "{ADF64291-57ED-4B7A-AB76-37B4A991504B}" + ProjectSection(ProjectDependencies) = postProject + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} = {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} + {11F55366-12EC-4C44-A8CB-1D4E315D61ED} = {11F55366-12EC-4C44-A8CB-1D4E315D61ED} + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} = {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxAdv28", "..\Externals\wxWidgets\build\msw\wx_adv.vcproj", "{0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Plugin_nJoy_SDL", "Plugins\Plugin_nJoy_SDL\Plugin_nJoy_SDL.vcproj", "{521498BE-6089-4780-8223-E67C22F4E068}" + ProjectSection(ProjectDependencies) = postProject + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} = {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA} = {C7E5D50A-2916-464B-86A7-E10B3CC88ADA} + {11F55366-12EC-4C44-A8CB-1D4E315D61ED} = {11F55366-12EC-4C44-A8CB-1D4E315D61ED} + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} = {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InputCommon", "Core\InputCommon\InputCommon.vcproj", "{C7E5D50A-2916-464B-86A7-E10B3CC88ADA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Main", "..\Externals\MusicMod\Main\Main.vcproj", "{95CCAABC-7062-47C4-B8C1-A064DD5F16FF}" + ProjectSection(ProjectDependencies) = postProject + {0B72B5D6-5D72-4391-84A7-9CCA5392668A} = {0B72B5D6-5D72-4391-84A7-9CCA5392668A} + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Player", "..\Externals\MusicMod\Player\Player.vcproj", "{0B72B5D6-5D72-4391-84A7-9CCA5392668A}" + ProjectSection(ProjectDependencies) = postProject + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestPlayer", "..\Externals\MusicMod\TestPlayer\TestPlayer.vcproj", "{0D14F1E9-490B-4A2D-A4EF-0535E8B3C718}" + ProjectSection(ProjectDependencies) = postProject + {0B72B5D6-5D72-4391-84A7-9CCA5392668A} = {0B72B5D6-5D72-4391-84A7-9CCA5392668A} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + DebugFast|Win32 = DebugFast|Win32 + DebugFast|x64 = DebugFast|x64 + Release JITIL|Win32 = Release JITIL|Win32 + Release JITIL|x64 = Release JITIL|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F0B874CB-4476-4199-9315-8343D05AE684}.Debug|Win32.ActiveCfg = Debug|Win32 + {F0B874CB-4476-4199-9315-8343D05AE684}.Debug|Win32.Build.0 = Debug|Win32 + {F0B874CB-4476-4199-9315-8343D05AE684}.Debug|x64.ActiveCfg = Debug|x64 + {F0B874CB-4476-4199-9315-8343D05AE684}.Debug|x64.Build.0 = Debug|x64 + {F0B874CB-4476-4199-9315-8343D05AE684}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {F0B874CB-4476-4199-9315-8343D05AE684}.DebugFast|Win32.Build.0 = DebugFast|Win32 + {F0B874CB-4476-4199-9315-8343D05AE684}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {F0B874CB-4476-4199-9315-8343D05AE684}.DebugFast|x64.Build.0 = DebugFast|x64 + {F0B874CB-4476-4199-9315-8343D05AE684}.Release JITIL|Win32.ActiveCfg = Release_JITIL|Win32 + {F0B874CB-4476-4199-9315-8343D05AE684}.Release JITIL|Win32.Build.0 = Release_JITIL|Win32 + {F0B874CB-4476-4199-9315-8343D05AE684}.Release JITIL|x64.ActiveCfg = Release_JITIL|x64 + {F0B874CB-4476-4199-9315-8343D05AE684}.Release JITIL|x64.Build.0 = Release_JITIL|x64 + {F0B874CB-4476-4199-9315-8343D05AE684}.Release|Win32.ActiveCfg = Release|Win32 + {F0B874CB-4476-4199-9315-8343D05AE684}.Release|Win32.Build.0 = Release|Win32 + {F0B874CB-4476-4199-9315-8343D05AE684}.Release|x64.ActiveCfg = Release|x64 + {F0B874CB-4476-4199-9315-8343D05AE684}.Release|x64.Build.0 = Release|x64 + {636FAD5F-02D1-4E9A-BE67-FB8EA99B9A18}.Debug|Win32.ActiveCfg = Debug|Win32 + {636FAD5F-02D1-4E9A-BE67-FB8EA99B9A18}.Debug|x64.ActiveCfg = Debug|x64 + {636FAD5F-02D1-4E9A-BE67-FB8EA99B9A18}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {636FAD5F-02D1-4E9A-BE67-FB8EA99B9A18}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {636FAD5F-02D1-4E9A-BE67-FB8EA99B9A18}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {636FAD5F-02D1-4E9A-BE67-FB8EA99B9A18}.Release JITIL|x64.ActiveCfg = Release|x64 + {636FAD5F-02D1-4E9A-BE67-FB8EA99B9A18}.Release JITIL|x64.Build.0 = Release|x64 + {636FAD5F-02D1-4E9A-BE67-FB8EA99B9A18}.Release|Win32.ActiveCfg = Release|Win32 + {636FAD5F-02D1-4E9A-BE67-FB8EA99B9A18}.Release|x64.ActiveCfg = Release|x64 + {636FAD5F-02D1-4E9A-BE67-FB8EA99B9A18}.Release|x64.Build.0 = Release|x64 + {9A183B48-ECC2-4121-876A-9B3793686073}.Debug|Win32.ActiveCfg = Debug|Win32 + {9A183B48-ECC2-4121-876A-9B3793686073}.Debug|x64.ActiveCfg = Debug|x64 + {9A183B48-ECC2-4121-876A-9B3793686073}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {9A183B48-ECC2-4121-876A-9B3793686073}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {9A183B48-ECC2-4121-876A-9B3793686073}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {9A183B48-ECC2-4121-876A-9B3793686073}.Release JITIL|x64.ActiveCfg = Release|x64 + {9A183B48-ECC2-4121-876A-9B3793686073}.Release JITIL|x64.Build.0 = Release|x64 + {9A183B48-ECC2-4121-876A-9B3793686073}.Release|Win32.ActiveCfg = Release|Win32 + {9A183B48-ECC2-4121-876A-9B3793686073}.Release|x64.ActiveCfg = Release|x64 + {9A183B48-ECC2-4121-876A-9B3793686073}.Release|x64.Build.0 = Release|x64 + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510}.Debug|Win32.ActiveCfg = Debug|Win32 + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510}.Debug|Win32.Build.0 = Debug|Win32 + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510}.Debug|x64.ActiveCfg = Debug|x64 + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510}.Debug|x64.Build.0 = Debug|x64 + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510}.DebugFast|Win32.Build.0 = DebugFast|Win32 + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510}.DebugFast|x64.Build.0 = DebugFast|x64 + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510}.Release JITIL|x64.ActiveCfg = Release|x64 + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510}.Release JITIL|x64.Build.0 = Release|x64 + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510}.Release|Win32.ActiveCfg = Release|Win32 + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510}.Release|x64.ActiveCfg = Release|x64 + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510}.Release|x64.Build.0 = Release|x64 + {3E03C179-8251-46E4-81F4-466F114BAC63}.Debug|Win32.ActiveCfg = Debug|Win32 + {3E03C179-8251-46E4-81F4-466F114BAC63}.Debug|Win32.Build.0 = Debug|Win32 + {3E03C179-8251-46E4-81F4-466F114BAC63}.Debug|x64.ActiveCfg = Debug|x64 + {3E03C179-8251-46E4-81F4-466F114BAC63}.Debug|x64.Build.0 = Debug|x64 + {3E03C179-8251-46E4-81F4-466F114BAC63}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {3E03C179-8251-46E4-81F4-466F114BAC63}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {3E03C179-8251-46E4-81F4-466F114BAC63}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {3E03C179-8251-46E4-81F4-466F114BAC63}.Release JITIL|x64.ActiveCfg = Release|x64 + {3E03C179-8251-46E4-81F4-466F114BAC63}.Release JITIL|x64.Build.0 = Release|x64 + {3E03C179-8251-46E4-81F4-466F114BAC63}.Release|Win32.ActiveCfg = Release|Win32 + {3E03C179-8251-46E4-81F4-466F114BAC63}.Release|x64.ActiveCfg = Release|x64 + {3E03C179-8251-46E4-81F4-466F114BAC63}.Release|x64.Build.0 = Release|x64 + {3D8156A9-64D1-4C8E-ADBE-1B319030E4A4}.Debug|Win32.ActiveCfg = Debug|Win32 + {3D8156A9-64D1-4C8E-ADBE-1B319030E4A4}.Debug|x64.ActiveCfg = Debug|x64 + {3D8156A9-64D1-4C8E-ADBE-1B319030E4A4}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {3D8156A9-64D1-4C8E-ADBE-1B319030E4A4}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {3D8156A9-64D1-4C8E-ADBE-1B319030E4A4}.DebugFast|x64.Build.0 = DebugFast|x64 + {3D8156A9-64D1-4C8E-ADBE-1B319030E4A4}.Release JITIL|Win32.ActiveCfg = Release|x64 + {3D8156A9-64D1-4C8E-ADBE-1B319030E4A4}.Release JITIL|x64.ActiveCfg = Release|x64 + {3D8156A9-64D1-4C8E-ADBE-1B319030E4A4}.Release|Win32.ActiveCfg = Release|Win32 + {3D8156A9-64D1-4C8E-ADBE-1B319030E4A4}.Release|x64.ActiveCfg = Release|x64 + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C}.Debug|Win32.ActiveCfg = Debug|Win32 + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C}.Debug|Win32.Build.0 = Debug|Win32 + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C}.Debug|x64.ActiveCfg = Debug|x64 + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C}.Debug|x64.Build.0 = Debug|x64 + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C}.DebugFast|Win32.Build.0 = DebugFast|Win32 + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C}.DebugFast|x64.Build.0 = DebugFast|x64 + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C}.Release JITIL|x64.ActiveCfg = Release|x64 + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C}.Release JITIL|x64.Build.0 = Release|x64 + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C}.Release|Win32.ActiveCfg = Release|Win32 + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C}.Release|Win32.Build.0 = Release|Win32 + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C}.Release|x64.ActiveCfg = Release|x64 + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C}.Release|x64.Build.0 = Release|x64 + {CFDCEE0E-FA45-4F72-9FCC-0B88F5A75160}.Debug|Win32.ActiveCfg = Debug|Win32 + {CFDCEE0E-FA45-4F72-9FCC-0B88F5A75160}.Debug|x64.ActiveCfg = Debug|x64 + {CFDCEE0E-FA45-4F72-9FCC-0B88F5A75160}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {CFDCEE0E-FA45-4F72-9FCC-0B88F5A75160}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {CFDCEE0E-FA45-4F72-9FCC-0B88F5A75160}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {CFDCEE0E-FA45-4F72-9FCC-0B88F5A75160}.Release JITIL|x64.ActiveCfg = Release|x64 + {CFDCEE0E-FA45-4F72-9FCC-0B88F5A75160}.Release JITIL|x64.Build.0 = Release|x64 + {CFDCEE0E-FA45-4F72-9FCC-0B88F5A75160}.Release|Win32.ActiveCfg = Release|Win32 + {CFDCEE0E-FA45-4F72-9FCC-0B88F5A75160}.Release|Win32.Build.0 = Release|Win32 + {CFDCEE0E-FA45-4F72-9FCC-0B88F5A75160}.Release|x64.ActiveCfg = Release|x64 + {CFDCEE0E-FA45-4F72-9FCC-0B88F5A75160}.Release|x64.Build.0 = Release|x64 + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9}.Debug|Win32.ActiveCfg = Debug|Win32 + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9}.Debug|Win32.Build.0 = Debug|Win32 + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9}.Debug|x64.ActiveCfg = Debug|x64 + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9}.Debug|x64.Build.0 = Debug|x64 + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9}.DebugFast|Win32.Build.0 = DebugFast|Win32 + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9}.DebugFast|x64.Build.0 = DebugFast|x64 + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9}.Release JITIL|x64.ActiveCfg = Release|x64 + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9}.Release JITIL|x64.Build.0 = Release|x64 + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9}.Release|Win32.ActiveCfg = Release|Win32 + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9}.Release|Win32.Build.0 = Release|Win32 + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9}.Release|x64.ActiveCfg = Release|x64 + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9}.Release|x64.Build.0 = Release|x64 + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9}.Debug|Win32.ActiveCfg = Debug|Win32 + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9}.Debug|Win32.Build.0 = Debug|Win32 + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9}.Debug|x64.ActiveCfg = Debug|x64 + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9}.Debug|x64.Build.0 = Debug|x64 + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9}.DebugFast|Win32.Build.0 = DebugFast|Win32 + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9}.DebugFast|x64.Build.0 = DebugFast|x64 + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9}.Release JITIL|Win32.ActiveCfg = Release_JITIL|Win32 + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9}.Release JITIL|Win32.Build.0 = Release_JITIL|Win32 + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9}.Release JITIL|x64.ActiveCfg = Release_JITIL|x64 + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9}.Release JITIL|x64.Build.0 = Release_JITIL|x64 + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9}.Release|Win32.ActiveCfg = Release|Win32 + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9}.Release|Win32.Build.0 = Release|Win32 + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9}.Release|x64.ActiveCfg = Release|x64 + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9}.Release|x64.Build.0 = Release|x64 + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE}.Debug|Win32.ActiveCfg = Debug|Win32 + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE}.Debug|Win32.Build.0 = Debug|Win32 + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE}.Debug|x64.ActiveCfg = Debug|x64 + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE}.Debug|x64.Build.0 = Debug|x64 + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE}.Release JITIL|x64.ActiveCfg = Release|x64 + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE}.Release JITIL|x64.Build.0 = Release|x64 + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE}.Release|Win32.ActiveCfg = Release|Win32 + {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE}.Release|x64.ActiveCfg = Release|x64 + {11F55366-12EC-4C44-A8CB-1D4E315D61ED}.Debug|Win32.ActiveCfg = Debug|Win32 + {11F55366-12EC-4C44-A8CB-1D4E315D61ED}.Debug|Win32.Build.0 = Debug|Win32 + {11F55366-12EC-4C44-A8CB-1D4E315D61ED}.Debug|x64.ActiveCfg = Debug|x64 + {11F55366-12EC-4C44-A8CB-1D4E315D61ED}.Debug|x64.Build.0 = Debug|x64 + {11F55366-12EC-4C44-A8CB-1D4E315D61ED}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {11F55366-12EC-4C44-A8CB-1D4E315D61ED}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {11F55366-12EC-4C44-A8CB-1D4E315D61ED}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {11F55366-12EC-4C44-A8CB-1D4E315D61ED}.Release JITIL|x64.ActiveCfg = Release|x64 + {11F55366-12EC-4C44-A8CB-1D4E315D61ED}.Release JITIL|x64.Build.0 = Release|x64 + {11F55366-12EC-4C44-A8CB-1D4E315D61ED}.Release|Win32.ActiveCfg = Release|Win32 + {11F55366-12EC-4C44-A8CB-1D4E315D61ED}.Release|x64.ActiveCfg = Release|x64 + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77}.Debug|Win32.ActiveCfg = Debug|Win32 + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77}.Debug|Win32.Build.0 = Debug|Win32 + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77}.Debug|x64.ActiveCfg = Debug|x64 + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77}.Debug|x64.Build.0 = Debug|x64 + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77}.DebugFast|Win32.Build.0 = DebugFast|Win32 + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77}.DebugFast|x64.Build.0 = DebugFast|x64 + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77}.Release JITIL|x64.ActiveCfg = Release|x64 + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77}.Release JITIL|x64.Build.0 = Release|x64 + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77}.Release|Win32.ActiveCfg = Release|Win32 + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77}.Release|Win32.Build.0 = Release|Win32 + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77}.Release|x64.ActiveCfg = Release|x64 + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77}.Release|x64.Build.0 = Release|x64 + {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA}.Debug|Win32.ActiveCfg = Debug|Win32 + {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA}.Debug|x64.ActiveCfg = Debug|x64 + {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA}.DebugFast|Win32.Build.0 = DebugFast|Win32 + {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA}.Release JITIL|x64.ActiveCfg = Release|x64 + {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA}.Release JITIL|x64.Build.0 = Release|x64 + {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA}.Release|Win32.ActiveCfg = Release|Win32 + {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA}.Release|Win32.Build.0 = Release|Win32 + {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA}.Release|x64.ActiveCfg = Release|x64 + {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA}.Release|x64.Build.0 = Release|x64 + {D6E56527-BBB9-4EAD-A6EC-49D4BF6AFCD8}.Debug|Win32.ActiveCfg = Debug|Win32 + {D6E56527-BBB9-4EAD-A6EC-49D4BF6AFCD8}.Debug|x64.ActiveCfg = Debug|x64 + {D6E56527-BBB9-4EAD-A6EC-49D4BF6AFCD8}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {D6E56527-BBB9-4EAD-A6EC-49D4BF6AFCD8}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {D6E56527-BBB9-4EAD-A6EC-49D4BF6AFCD8}.DebugFast|x64.Build.0 = DebugFast|x64 + {D6E56527-BBB9-4EAD-A6EC-49D4BF6AFCD8}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {D6E56527-BBB9-4EAD-A6EC-49D4BF6AFCD8}.Release JITIL|x64.ActiveCfg = Release|x64 + {D6E56527-BBB9-4EAD-A6EC-49D4BF6AFCD8}.Release JITIL|x64.Build.0 = Release|x64 + {D6E56527-BBB9-4EAD-A6EC-49D4BF6AFCD8}.Release|Win32.ActiveCfg = Release|Win32 + {D6E56527-BBB9-4EAD-A6EC-49D4BF6AFCD8}.Release|x64.ActiveCfg = Release|x64 + {33546D62-7F34-4EA6-A88E-D538B36E16BF}.Debug|Win32.ActiveCfg = Debug|Win32 + {33546D62-7F34-4EA6-A88E-D538B36E16BF}.Debug|x64.ActiveCfg = Debug|x64 + {33546D62-7F34-4EA6-A88E-D538B36E16BF}.Debug|x64.Build.0 = Debug|x64 + {33546D62-7F34-4EA6-A88E-D538B36E16BF}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {33546D62-7F34-4EA6-A88E-D538B36E16BF}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {33546D62-7F34-4EA6-A88E-D538B36E16BF}.DebugFast|x64.Build.0 = DebugFast|x64 + {33546D62-7F34-4EA6-A88E-D538B36E16BF}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {33546D62-7F34-4EA6-A88E-D538B36E16BF}.Release JITIL|x64.ActiveCfg = Release|x64 + {33546D62-7F34-4EA6-A88E-D538B36E16BF}.Release JITIL|x64.Build.0 = Release|x64 + {33546D62-7F34-4EA6-A88E-D538B36E16BF}.Release|Win32.ActiveCfg = Release|Win32 + {33546D62-7F34-4EA6-A88E-D538B36E16BF}.Release|x64.ActiveCfg = Release|x64 + {33546D62-7F34-4EA6-A88E-D538B36E16BF}.Release|x64.Build.0 = Release|x64 + {8D612734-FAA5-4B8A-804F-4DEA2367D495}.Debug|Win32.ActiveCfg = Debug|Win32 + {8D612734-FAA5-4B8A-804F-4DEA2367D495}.Debug|x64.ActiveCfg = Debug|x64 + {8D612734-FAA5-4B8A-804F-4DEA2367D495}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {8D612734-FAA5-4B8A-804F-4DEA2367D495}.DebugFast|Win32.Build.0 = DebugFast|Win32 + {8D612734-FAA5-4B8A-804F-4DEA2367D495}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {8D612734-FAA5-4B8A-804F-4DEA2367D495}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {8D612734-FAA5-4B8A-804F-4DEA2367D495}.Release JITIL|x64.ActiveCfg = Release|x64 + {8D612734-FAA5-4B8A-804F-4DEA2367D495}.Release JITIL|x64.Build.0 = Release|x64 + {8D612734-FAA5-4B8A-804F-4DEA2367D495}.Release|Win32.ActiveCfg = Release|Win32 + {8D612734-FAA5-4B8A-804F-4DEA2367D495}.Release|Win32.Build.0 = Release|Win32 + {8D612734-FAA5-4B8A-804F-4DEA2367D495}.Release|x64.ActiveCfg = Release|x64 + {8D612734-FAA5-4B8A-804F-4DEA2367D495}.Release|x64.Build.0 = Release|x64 + {ADF64291-57ED-4B7A-AB76-37B4A991504B}.Debug|Win32.ActiveCfg = Debug|Win32 + {ADF64291-57ED-4B7A-AB76-37B4A991504B}.Debug|x64.ActiveCfg = Debug|x64 + {ADF64291-57ED-4B7A-AB76-37B4A991504B}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {ADF64291-57ED-4B7A-AB76-37B4A991504B}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {ADF64291-57ED-4B7A-AB76-37B4A991504B}.Release JITIL|Win32.ActiveCfg = Release|x64 + {ADF64291-57ED-4B7A-AB76-37B4A991504B}.Release JITIL|x64.ActiveCfg = Release|x64 + {ADF64291-57ED-4B7A-AB76-37B4A991504B}.Release|Win32.ActiveCfg = Release|Win32 + {ADF64291-57ED-4B7A-AB76-37B4A991504B}.Release|x64.ActiveCfg = Release|x64 + {ADF64291-57ED-4B7A-AB76-37B4A991504B}.Release|x64.Build.0 = Release|x64 + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E}.Debug|Win32.ActiveCfg = Debug|Win32 + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E}.Debug|Win32.Build.0 = Debug|Win32 + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E}.Debug|x64.ActiveCfg = Debug|x64 + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E}.Debug|x64.Build.0 = Debug|x64 + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E}.Release JITIL|x64.ActiveCfg = Release|x64 + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E}.Release JITIL|x64.Build.0 = Release|x64 + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E}.Release|Win32.ActiveCfg = Release|Win32 + {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E}.Release|x64.ActiveCfg = Release|x64 + {521498BE-6089-4780-8223-E67C22F4E068}.Debug|Win32.ActiveCfg = Debug|Win32 + {521498BE-6089-4780-8223-E67C22F4E068}.Debug|Win32.Build.0 = Debug|Win32 + {521498BE-6089-4780-8223-E67C22F4E068}.Debug|x64.ActiveCfg = Debug|x64 + {521498BE-6089-4780-8223-E67C22F4E068}.Debug|x64.Build.0 = Debug|x64 + {521498BE-6089-4780-8223-E67C22F4E068}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {521498BE-6089-4780-8223-E67C22F4E068}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {521498BE-6089-4780-8223-E67C22F4E068}.DebugFast|x64.Build.0 = DebugFast|x64 + {521498BE-6089-4780-8223-E67C22F4E068}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {521498BE-6089-4780-8223-E67C22F4E068}.Release JITIL|x64.ActiveCfg = Release|x64 + {521498BE-6089-4780-8223-E67C22F4E068}.Release JITIL|x64.Build.0 = Release|x64 + {521498BE-6089-4780-8223-E67C22F4E068}.Release|Win32.ActiveCfg = Release|Win32 + {521498BE-6089-4780-8223-E67C22F4E068}.Release|Win32.Build.0 = Release|Win32 + {521498BE-6089-4780-8223-E67C22F4E068}.Release|x64.ActiveCfg = Release|x64 + {521498BE-6089-4780-8223-E67C22F4E068}.Release|x64.Build.0 = Release|x64 + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA}.Debug|Win32.ActiveCfg = Debug|Win32 + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA}.Debug|Win32.Build.0 = Debug|Win32 + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA}.Debug|x64.ActiveCfg = Debug|x64 + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA}.Debug|x64.Build.0 = Debug|x64 + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA}.DebugFast|Win32.Build.0 = DebugFast|Win32 + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA}.DebugFast|x64.Build.0 = DebugFast|x64 + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA}.Release JITIL|x64.ActiveCfg = Release|x64 + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA}.Release JITIL|x64.Build.0 = Release|x64 + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA}.Release|Win32.ActiveCfg = Release|Win32 + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA}.Release|Win32.Build.0 = Release|Win32 + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA}.Release|x64.ActiveCfg = Release|x64 + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA}.Release|x64.Build.0 = Release|x64 + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF}.Debug|Win32.ActiveCfg = Debug|Win32 + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF}.Debug|Win32.Build.0 = Debug|Win32 + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF}.Debug|x64.ActiveCfg = Debug|x64 + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF}.Debug|x64.Build.0 = Debug|x64 + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF}.DebugFast|x64.Build.0 = DebugFast|x64 + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF}.Release JITIL|x64.ActiveCfg = Release|x64 + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF}.Release JITIL|x64.Build.0 = Release|x64 + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF}.Release|Win32.ActiveCfg = Release|Win32 + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF}.Release|x64.ActiveCfg = Release|x64 + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF}.Release|x64.Build.0 = Release|x64 + {0B72B5D6-5D72-4391-84A7-9CCA5392668A}.Debug|Win32.ActiveCfg = Debug|Win32 + {0B72B5D6-5D72-4391-84A7-9CCA5392668A}.Debug|Win32.Build.0 = Debug|Win32 + {0B72B5D6-5D72-4391-84A7-9CCA5392668A}.Debug|x64.ActiveCfg = Debug|x64 + {0B72B5D6-5D72-4391-84A7-9CCA5392668A}.Debug|x64.Build.0 = Debug|x64 + {0B72B5D6-5D72-4391-84A7-9CCA5392668A}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {0B72B5D6-5D72-4391-84A7-9CCA5392668A}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {0B72B5D6-5D72-4391-84A7-9CCA5392668A}.DebugFast|x64.Build.0 = DebugFast|x64 + {0B72B5D6-5D72-4391-84A7-9CCA5392668A}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {0B72B5D6-5D72-4391-84A7-9CCA5392668A}.Release JITIL|x64.ActiveCfg = Release|x64 + {0B72B5D6-5D72-4391-84A7-9CCA5392668A}.Release JITIL|x64.Build.0 = Release|x64 + {0B72B5D6-5D72-4391-84A7-9CCA5392668A}.Release|Win32.ActiveCfg = Release|Win32 + {0B72B5D6-5D72-4391-84A7-9CCA5392668A}.Release|x64.ActiveCfg = Release|x64 + {0B72B5D6-5D72-4391-84A7-9CCA5392668A}.Release|x64.Build.0 = Release|x64 + {0D14F1E9-490B-4A2D-A4EF-0535E8B3C718}.Debug|Win32.ActiveCfg = Debug|Win32 + {0D14F1E9-490B-4A2D-A4EF-0535E8B3C718}.Debug|Win32.Build.0 = Debug|Win32 + {0D14F1E9-490B-4A2D-A4EF-0535E8B3C718}.Debug|x64.ActiveCfg = Debug|x64 + {0D14F1E9-490B-4A2D-A4EF-0535E8B3C718}.Debug|x64.Build.0 = Debug|x64 + {0D14F1E9-490B-4A2D-A4EF-0535E8B3C718}.DebugFast|Win32.ActiveCfg = DebugFast|Win32 + {0D14F1E9-490B-4A2D-A4EF-0535E8B3C718}.DebugFast|x64.ActiveCfg = DebugFast|x64 + {0D14F1E9-490B-4A2D-A4EF-0535E8B3C718}.DebugFast|x64.Build.0 = DebugFast|x64 + {0D14F1E9-490B-4A2D-A4EF-0535E8B3C718}.Release JITIL|Win32.ActiveCfg = Release|Win32 + {0D14F1E9-490B-4A2D-A4EF-0535E8B3C718}.Release JITIL|x64.ActiveCfg = Release|x64 + {0D14F1E9-490B-4A2D-A4EF-0535E8B3C718}.Release JITIL|x64.Build.0 = Release|x64 + {0D14F1E9-490B-4A2D-A4EF-0535E8B3C718}.Release|Win32.ActiveCfg = Release|Win32 + {0D14F1E9-490B-4A2D-A4EF-0535E8B3C718}.Release|x64.ActiveCfg = Release|x64 + {0D14F1E9-490B-4A2D-A4EF-0535E8B3C718}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + AMDCaProjectFile = D:\Dev\Dolphin\trunk\Source\CodeAnalyst\Dolphin.caw + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {B7F1A9FB-BEA8-416E-9460-AE35A6A5165C} = {61C7F431-0623-4A8D-9C4B-EDE35696554A} + {C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {61C7F431-0623-4A8D-9C4B-EDE35696554A} + {A72606EF-C5C1-4954-90AD-F0F93A8D97D9} = {61C7F431-0623-4A8D-9C4B-EDE35696554A} + {4D3CD4C5-412B-4B49-9B1B-A68A2A129C77} = {61C7F431-0623-4A8D-9C4B-EDE35696554A} + {F0B874CB-4476-4199-9315-8343D05AE684} = {61C7F431-0623-4A8D-9C4B-EDE35696554A} + {C7E5D50A-2916-464B-86A7-E10B3CC88ADA} = {61C7F431-0623-4A8D-9C4B-EDE35696554A} + {9A183B48-ECC2-4121-876A-9B3793686073} = {5C17B1EB-6C76-438A-A503-8F3C7831023B} + {C60D0E7A-ED05-4C67-9EE7-3A6C0D7801C8} = {5C17B1EB-6C76-438A-A503-8F3C7831023B} + {CFDCEE0E-FA45-4F72-9FCC-0B88F5A75160} = {5C17B1EB-6C76-438A-A503-8F3C7831023B} + {E5D1F0C0-AA07-4841-A4EB-4CF4DAA6B0FA} = {5C17B1EB-6C76-438A-A503-8F3C7831023B} + {D6E56527-BBB9-4EAD-A6EC-49D4BF6AFCD8} = {5C17B1EB-6C76-438A-A503-8F3C7831023B} + {8D612734-FAA5-4B8A-804F-4DEA2367D495} = {5C17B1EB-6C76-438A-A503-8F3C7831023B} + {ADF64291-57ED-4B7A-AB76-37B4A991504B} = {5C17B1EB-6C76-438A-A503-8F3C7831023B} + {521498BE-6089-4780-8223-E67C22F4E068} = {5C17B1EB-6C76-438A-A503-8F3C7831023B} + {636FAD5F-02D1-4E9A-BE67-FB8EA99B9A18} = {5C17B1EB-6C76-438A-A503-8F3C7831023B} + {3E03C179-8251-46E4-81F4-466F114BAC63} = {4F427D1B-8C90-4D9C-B23D-A51493A1C471} + {48AD7E0A-25B1-4974-A1E3-03F8C438D34F} = {4F427D1B-8C90-4D9C-B23D-A51493A1C471} + {0318BA30-EF48-441A-9E10-DC85EFAE39F0} = {4F427D1B-8C90-4D9C-B23D-A51493A1C471} + {33546D62-7F34-4EA6-A88E-D538B36E16BF} = {4F427D1B-8C90-4D9C-B23D-A51493A1C471} + {71B16F46-0B00-4EDA-B253-D6D9D03A215C} = {4F427D1B-8C90-4D9C-B23D-A51493A1C471} + {29C2ABC1-ADA5-42CD-A5FC-96022D52A510} = {4F427D1B-8C90-4D9C-B23D-A51493A1C471} + {DE7C596C-CBC4-4278-8909-146D63990803} = {77CF6E34-3038-4B23-A2E7-90AD17801609} + {95CCAABC-7062-47C4-B8C1-A064DD5F16FF} = {77CF6E34-3038-4B23-A2E7-90AD17801609} + {0B72B5D6-5D72-4391-84A7-9CCA5392668A} = {77CF6E34-3038-4B23-A2E7-90AD17801609} + {0D14F1E9-490B-4A2D-A4EF-0535E8B3C718} = {77CF6E34-3038-4B23-A2E7-90AD17801609} + EndGlobalSection + GlobalSection(DPCodeReviewSolutionGUID) = preSolution + DPCodeReviewSolutionGUID = {00000000-0000-0000-0000-000000000000} + EndGlobalSection +EndGlobal