1 ////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // Nestopia - NES/Famicom emulator written in C++
4 //
5 // Copyright (C) 2003-2008 Martin Freij
6 //
7 // This file is part of Nestopia.
8 //
9 // Nestopia is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // Nestopia is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with Nestopia; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 //
23 ////////////////////////////////////////////////////////////////////////////////////////
24 
25 #include "NstIoLog.hpp"
26 #include "NstIoStream.hpp"
27 #include "NstIoFile.hpp"
28 #include "NstApplicationInstance.hpp"
29 #include "NstWindowParam.hpp"
30 #include "NstWindowDropFiles.hpp"
31 #include "NstManagerPaths.hpp"
32 #include "NstDialogNetPlay.hpp"
33 #include "../core/NstXml.hpp"
34 #include <CommCtrl.h>
35 
36 namespace Nestopia
37 {
38 	namespace Window
39 	{
40 		struct Netplay::Handlers
41 		{
42 			static const MsgHandler::Entry<Netplay> messages[];
43 			static const MsgHandler::Entry<Netplay> commands[];
44 			static const Control::NotificationHandler::Entry<Netplay> notifications[];
45 		};
46 
47 		const MsgHandler::Entry<Netplay> Netplay::Handlers::messages[] =
48 		{
49 			{ WM_INITDIALOG, &Netplay::OnInitDialog },
50 			{ WM_DROPFILES,  &Netplay::OnDropFiles  }
51 		};
52 
53 		const MsgHandler::Entry<Netplay> Netplay::Handlers::commands[] =
54 		{
55 			{ IDC_NETPLAY_ADD,             &Netplay::OnAdd        },
56 			{ IDC_NETPLAY_REMOVE,          &Netplay::OnRemove     },
57 			{ IDC_NETPLAY_CLEAR,           &Netplay::OnClear      },
58 			{ IDC_NETPLAY_DEFAULT,         &Netplay::OnDefault    },
59 			{ IDC_NETPLAY_LAUNCH,          &Netplay::OnLaunch     },
60 			{ IDC_NETPLAY_PLAY_FULLSCREEN, &Netplay::OnFullscreen }
61 		};
62 
63 		const Control::NotificationHandler::Entry<Netplay> Netplay::Handlers::notifications[] =
64 		{
65 			{ LVN_KEYDOWN,     &Netplay::OnKeyDown        },
66 			{ LVN_ITEMCHANGED, &Netplay::OnItemChanged    },
67 			{ LVN_INSERTITEM,  &Netplay::OnInsertItem     },
68 			{ LVN_DELETEITEM,  &Netplay::OnDeleteItem     }
69 		};
70 
operator ()(const Path & a,const Path & b) const71 		bool Netplay::Games::Less::operator () (const Path& a,const Path& b) const
72 		{
73 			return a.Target().File() < b.Target().File();
74 		}
75 
Games()76 		Netplay::Games::Games()
77 		: state(UNINITIALIZED) {}
78 
Netplay(Managers::Emulator & e,const Managers::Paths & p,bool fullscreen)79 		Netplay::Netplay(Managers::Emulator& e,const Managers::Paths& p,bool fullscreen)
80 		:
81 		dialog        ( IDD_NETPLAY, this, Handlers::messages, Handlers::commands ),
82 		doFullscreen  ( fullscreen ),
83 		paths         ( p ),
84 		emulator      ( e ),
85 		notifications ( IDC_NETPLAY_GAMELIST, dialog.Messages(), this, Handlers::notifications )
86 		{
87 		}
88 
~Netplay()89 		Netplay::~Netplay()
90 		{
91 		}
92 
LoadFile()93 		void Netplay::LoadFile()
94 		{
95 			try
96 			{
97 				//const Path path( Application::Instance::GetExePath(L"netplaylist.xml") );
98 				const Path path( Application::Instance::GetConfigPath(L"netplaylist.xml") );//bg
99 
100 				if (path.FileExists())
101 				{
102 					typedef Nes::Core::Xml Xml;
103 					Xml xml;
104 
105 					{
106 						Io::Stream::In stream( path );
107 						xml.Read( stream );
108 					}
109 
110 					if (!xml.GetRoot().IsType( L"netplaylist" ))
111 						throw 1;
112 
113 					for (Xml::Node node(xml.GetRoot().GetFirstChild()); node; node=node.GetNextSibling())
114 					{
115 						if (!node.IsType( L"file" ))
116 							throw 1;
117 
118 						Add( node.GetValue() );
119 					}
120 
121 					Io::Log() << "Netplay: loaded game list from \"netplaylist.xml\"\r\n";
122 				}
123 				else
124 				{
125 					Io::Log() << "Netplay: game list file \"netplaylist.xml\" not present..\r\n";
126 				}
127 			}
128 			catch (...)
129 			{
130 				games.state = Games::DIRTY;
131 				Io::Log() << "Netplay: warning, couldn't load game list \"netplaylist.xml\"!\r\n";
132 			}
133 		}
134 
SaveFile() const135 		void Netplay::SaveFile() const
136 		{
137 			if (games.state == Games::DIRTY)
138 			{
139 				//const Path path( Application::Instance::GetExePath(L"netplaylist.xml") );
140 				const Path path( Application::Instance::GetConfigPath(L"netplaylist.xml") );//bg
141 
142 				if (!games.paths.empty())
143 				{
144 					try
145 					{
146 						typedef Nes::Core::Xml Xml;
147 
148 						Xml xml;
149 						Xml::Node root( xml.Create( L"netplaylist" ) );
150 						root.AddAttribute( L"version", L"1.0" );
151 
152 						for (Games::Paths::const_iterator it(games.paths.begin()), end(games.paths.end()); it != end; ++it)
153 							root.AddChild( L"file", it->Ptr() );
154 
155 						Io::Stream::Out stream( path );
156 						xml.Write( root, stream );
157 
158 						Io::Log() << "Netplay: saved game list to \"netplaylist.xml\"\r\n";
159 					}
160 					catch (...)
161 					{
162 						Io::Log() << "Netplay: warning, couldn't save game list to \"netplaylist.xml\"!\r\n";
163 					}
164 				}
165 				else if (path.FileExists())
166 				{
167 					if (Io::File::Delete( path.Ptr() ))
168 						Io::Log() << "Netplay: game list empty, deleted \"netplaylist.xml\"\r\n";
169 					else
170 						Io::Log() << "Netplay: warning, couldn't delete \"netplaylist.xml\"!\r\n";
171 				}
172 			}
173 		}
174 
GetPath(wcstring const path) const175 		wcstring Netplay::GetPath(wcstring const path) const
176 		{
177 			Games::Paths::const_iterator it(games.paths.find( path ));
178 			return it != games.paths.end() ? it->Ptr() : NULL;
179 		}
180 
Add(Path path)181 		void Netplay::Add(Path path)
182 		{
183 			enum
184 			{
185 				TYPES = Managers::Paths::File::GAME|Managers::Paths::File::ARCHIVE
186 			};
187 
188 			if (path.Length() && games.paths.size() < Games::LIMIT && paths.CheckFile( path, TYPES ) && games.paths.insert( path ).second)
189 			{
190 				games.state = Games::DIRTY;
191 
192 				if (dialog)
193 					dialog.ListView( IDC_NETPLAY_GAMELIST ).Add( path.Target().File() );
194 			}
195 		}
196 
OnInitDialog(Param &)197 		ibool Netplay::OnInitDialog(Param&)
198 		{
199 			dialog.CheckBox( IDC_NETPLAY_PLAY_FULLSCREEN ).Check( doFullscreen );
200 			dialog.CheckBox( IDC_NETPLAY_REMOVE ).Disable();
201 
202 			if (games.state == Games::UNINITIALIZED)
203 			{
204 				LoadFile();
205 			}
206 			else
207 			{
208 				Control::ListView list( dialog.ListView( IDC_NETPLAY_GAMELIST ) );
209 				list.Reserve( games.paths.size() );
210 
211 				for (Games::Paths::const_iterator it(games.paths.begin()), end(games.paths.end()); it != end; ++it)
212 					list.Add( it->Target().File().Ptr() );
213 			}
214 
215 			dialog.CheckBox( IDC_NETPLAY_CLEAR ).Enable( games.paths.size() );
216 			dialog.CheckBox( IDC_NETPLAY_LAUNCH ).Enable( games.paths.size() );
217 
218 			return true;
219 		}
220 
OnAdd(Param & param)221 		ibool Netplay::OnAdd(Param& param)
222 		{
223 			if (param.Button().Clicked())
224 				Add( paths.BrowseLoad(Managers::Paths::File::GAME|Managers::Paths::File::ARCHIVE) );
225 
226 			return true;
227 		}
228 
OnRemove(Param & param)229 		ibool Netplay::OnRemove(Param& param)
230 		{
231 			if (param.Button().Clicked())
232 				dialog.ListView( IDC_NETPLAY_GAMELIST ).Selection().Delete();
233 
234 			return true;
235 		}
236 
OnClear(Param & param)237 		ibool Netplay::OnClear(Param& param)
238 		{
239 			if (param.Button().Clicked())
240 				dialog.ListView( IDC_NETPLAY_GAMELIST ).Clear();
241 
242 			return true;
243 		}
244 
OnDefault(Param & param)245 		ibool Netplay::OnDefault(Param& param)
246 		{
247 			if (param.Button().Clicked())
248 			{
249 				doFullscreen = false;
250 				dialog.CheckBox( IDC_NETPLAY_PLAY_FULLSCREEN ).Check( false );
251 			}
252 
253 			return true;
254 		}
255 
OnLaunch(Param & param)256 		ibool Netplay::OnLaunch(Param& param)
257 		{
258 			if (param.Button().Clicked())
259 				dialog.Close( LAUNCH );
260 
261 			return true;
262 		}
263 
OnFullscreen(Param & param)264 		ibool Netplay::OnFullscreen(Param& param)
265 		{
266 			if (param.Button().Clicked())
267 				doFullscreen = dialog.CheckBox( IDC_NETPLAY_PLAY_FULLSCREEN ).Checked();
268 
269 			return true;
270 		}
271 
OnDropFiles(Param & param)272 		ibool Netplay::OnDropFiles(Param& param)
273 		{
274 			DropFiles dropFiles( param );
275 
276 			if (dropFiles.Inside( dialog.ListView( IDC_NETPLAY_GAMELIST ).GetWindow() ))
277 			{
278 				for (uint i=0, n=dropFiles.Size(); i < n; ++i)
279 					Add( dropFiles[i] );
280 			}
281 
282 			return true;
283 		}
284 
OnKeyDown(const NMHDR & nmhdr)285 		void Netplay::OnKeyDown(const NMHDR& nmhdr)
286 		{
287 			switch (reinterpret_cast<const NMLVKEYDOWN&>(nmhdr).wVKey)
288 			{
289 				case VK_INSERT: dialog.PostCommand( IDC_NETPLAY_ADD    ); break;
290 				case VK_DELETE: dialog.PostCommand( IDC_NETPLAY_REMOVE ); break;
291 			}
292 		}
293 
OnItemChanged(const NMHDR & nmhdr)294 		void Netplay::OnItemChanged(const NMHDR& nmhdr)
295 		{
296 			const NMLISTVIEW& nm = reinterpret_cast<const NMLISTVIEW&>(nmhdr);
297 
298 			if ((nm.uOldState ^ nm.uNewState) & LVIS_SELECTED)
299 				dialog.CheckBox( IDC_NETPLAY_REMOVE ).Enable( nm.uNewState & LVIS_SELECTED );
300 		}
301 
OnInsertItem(const NMHDR &)302 		void Netplay::OnInsertItem(const NMHDR&)
303 		{
304 			dialog.CheckBox( IDC_NETPLAY_CLEAR ).Enable();
305 			dialog.CheckBox( IDC_NETPLAY_LAUNCH ).Enable();
306 		}
307 
OnDeleteItem(const NMHDR & nmhdr)308 		void Netplay::OnDeleteItem(const NMHDR& nmhdr)
309 		{
310 			Games::Paths::iterator it(games.paths.begin());
311 
312 			for (int i=reinterpret_cast<const NMLISTVIEW&>(nmhdr).iItem; i > 0; --i)
313 				++it;
314 
315 			games.paths.erase( it );
316 			games.state = Games::DIRTY;
317 
318 			if (dialog.ListView( IDC_NETPLAY_GAMELIST ).Size() <= 1)
319 			{
320 				dialog.CheckBox( IDC_NETPLAY_CLEAR ).Disable();
321 				dialog.CheckBox( IDC_NETPLAY_LAUNCH ).Disable();
322 				dialog.CheckBox( IDC_NETPLAY_REMOVE ).Disable();
323 			}
324 		}
325 	}
326 }
327