1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.IO;
7 using System.Linq;
8 using System.Text;
9 using System.Threading.Tasks;
10 using System.Windows.Forms;
11 using Mesen.GUI.Config;
12 using Mesen.GUI.GoogleDriveIntegration;
13 
14 namespace Mesen.GUI.Forms.Config
15 {
16 	public partial class frmPreferences : BaseConfigForm
17 	{
18 		public bool NeedRestart { get; private set; }
19 
frmPreferences()20 		public frmPreferences()
21 		{
22 			InitializeComponent();
23 
24 			Entity = ConfigManager.Config.PreferenceInfo;
25 
26 			AddBinding("DisplayLanguage", cboDisplayLanguage);
27 
28 			AddBinding("AutomaticallyCheckForUpdates", chkAutomaticallyCheckForUpdates);
29 			AddBinding("SingleInstance", chkSingleInstance);
30 			AddBinding("AutoLoadIpsPatches", chkAutoLoadIps);
31 			AddBinding("DeveloperMode", chkDeveloperMode);
32 
33 			AddBinding("AssociateNesFiles", chkNesFormat);
34 			AddBinding("AssociateFdsFiles", chkFdsFormat);
35 			AddBinding("AssociateMmoFiles", chkMmoFormat);
36 			AddBinding("AssociateNsfFiles", chkNsfFormat);
37 			AddBinding("AssociateMstFiles", chkMstFormat);
38 			AddBinding("AssociateUnfFiles", chkUnfFormat);
39 
40 			AddBinding("DisableMouseResize", chkDisableMouseResize);
41 
42 			AddBinding("NsfAutoDetectSilence", chkNsfAutoDetectSilence);
43 			AddBinding("NsfMoveToNextTrackAfterTime", chkNsfMoveToNextTrackAfterTime);
44 			AddBinding("NsfMoveToNextTrackTime", nudNsfMoveToNextTrackTime);
45 			AddBinding("NsfAutoDetectSilenceDelay", nudNsfAutoDetectSilenceDelay);
46 			AddBinding("NsfEnableApuIrqs", chkNsfEnableApuIrqs);
47 
48 			AddBinding("VsDualVideoOutput", cboVsDualVideoOutput);
49 			AddBinding("VsDualAudioOutput", cboVsDualAudioOutput);
50 
51 			AddBinding("FdsAutoLoadDisk", chkFdsAutoLoadDisk);
52 			AddBinding("FdsFastForwardOnLoad", chkFdsFastForwardOnLoad);
53 			AddBinding("FdsAutoInsertDisk", chkFdsAutoInsertDisk);
54 
55 			AddBinding("PauseWhenInBackground", chkPauseWhenInBackground);
56 			AddBinding("PauseWhenInMenusAndConfig", chkPauseInMenuAndConfig);
57 			AddBinding("PauseWhenInDebuggingTools", chkPauseInDebugger);
58 
59 			AddBinding("AllowBackgroundInput", chkAllowBackgroundInput);
60 			AddBinding("ConfirmExitResetPower", chkConfirmExitResetPower);
61 
62 			AddBinding("PauseOnMovieEnd", chkPauseOnMovieEnd);
63 
64 			AddBinding("AlwaysOnTop", chkAlwaysOnTop);
65 
66 			AddBinding("DisableGameDatabase", chkDisableGameDatabase);
67 			AddBinding("DisableHighResolutionTimer", chkDisableHighResolutionTimer);
68 			AddBinding("DisableOsd", chkDisableOsd);
69 
70 			AddBinding("AutoSave", chkAutoSave);
71 			AddBinding("AutoSaveDelay", nudAutoSave);
72 			AddBinding("AutoSaveNotify", chkAutoSaveNotify);
73 			AddBinding("AllowMismatchingSaveStates", chkAllowMismatchingSaveStates);
74 
75 			AddBinding("DisplayMovieIcons", chkDisplayMovieIcons);
76 			AddBinding("HidePauseOverlay", chkHidePauseOverlay);
77 
78 			AddBinding("AutoHideMenu", chkAutoHideMenu);
79 			AddBinding("DisplayTitleBarInfo", chkDisplayTitleBarInfo);
80 
81 			AddBinding("ShowFullPathInRecents", chkShowFullPathInRecents);
82 
83 			AddBinding("ShowFrameCounter", chkShowFrameCounter);
84 			AddBinding("ShowGameTimer", chkShowGameTimer);
85 
86 			AddBinding("DisableGameSelectionScreen", chkDisableGameSelectionScreen);
87 			AddBinding("GameSelectionScreenResetGame", chkGameSelectionScreenResetGame);
88 
89 			AddBinding("RewindBufferSize", nudRewindBufferSize);
90 
91 			AddBinding("ShowVsConfigOnLoad", chkShowVsConfigOnLoad);
92 
93 			AddBinding("GameFolder", psGame);
94 			AddBinding("AviFolder", psAvi);
95 			AddBinding("MovieFolder", psMovies);
96 			AddBinding("SaveDataFolder", psSaveData);
97 			AddBinding("SaveStateFolder", psSaveStates);
98 			AddBinding("ScreenshotFolder", psScreenshots);
99 			AddBinding("WaveFolder", psWave);
100 
101 			AddBinding("OverrideGameFolder", chkGameOverride);
102 			AddBinding("OverrideAviFolder", chkAviOverride);
103 			AddBinding("OverrideMovieFolder", chkMoviesOverride);
104 			AddBinding("OverrideSaveDataFolder", chkSaveDataOverride);
105 			AddBinding("OverrideSaveStateFolder", chkSaveStatesOverride);
106 			AddBinding("OverrideScreenshotFolder", chkScreenshotsOverride);
107 			AddBinding("OverrideWaveFolder", chkWaveOverride);
108 
109 			radStorageDocuments.Checked = ConfigManager.HomeFolder == ConfigManager.DefaultDocumentsFolder;
110 			radStoragePortable.Checked = !radStorageDocuments.Checked;
111 
112 			UpdateLocationText();
113 			UpdateFolderOverrideUi();
114 			UpdateCloudDisplay();
115 
116 			if(Program.IsMono) {
117 				//This option does nothing on Linux, hide it.
118 				chkDisableHighResolutionTimer.Visible = false;
119 			}
120 		}
121 
UpdateConfig()122 		protected override void UpdateConfig()
123 		{
124 			base.UpdateConfig();
125 			ctrlEmulatorShortcuts.UpdateConfig();
126 		}
127 
OnFormClosed(FormClosedEventArgs e)128 		protected override void OnFormClosed(FormClosedEventArgs e)
129 		{
130 			base.OnFormClosed(e);
131 			PreferenceInfo.ApplyConfig();
132 		}
133 
chkPauseWhenInBackground_CheckedChanged(object sender, EventArgs e)134 		private void chkPauseWhenInBackground_CheckedChanged(object sender, EventArgs e)
135 		{
136 			chkAllowBackgroundInput.Enabled = !chkPauseWhenInBackground.Checked;
137 			if(!chkAllowBackgroundInput.Enabled) {
138 				chkAllowBackgroundInput.Checked = false;
139 			}
140 		}
141 
btnOpenMesenFolder_Click(object sender, EventArgs e)142 		private void btnOpenMesenFolder_Click(object sender, EventArgs e)
143 		{
144 			System.Diagnostics.Process.Start(ConfigManager.HomeFolder);
145 		}
146 
UpdateCloudDisplay()147 		private void UpdateCloudDisplay()
148 		{
149 			if(!this.IsDisposed) {
150 				if(this.InvokeRequired) {
151 					this.BeginInvoke((MethodInvoker)(() => this.UpdateCloudDisplay()));
152 				} else {
153 					this.tlpCloudSaveDesc.Visible = !ConfigManager.Config.PreferenceInfo.CloudSaveIntegration;
154 					this.tlpCloudSaveEnabled.Visible = ConfigManager.Config.PreferenceInfo.CloudSaveIntegration;
155 				}
156 			}
157 		}
158 
TryEnableSync(bool retry = true)159 		private void TryEnableSync(bool retry = true)
160 		{
161 			if(CloudSyncHelper.EnableSync()) {
162 				if(!CloudSyncHelper.Sync()) {
163 					if(retry) {
164 						TryEnableSync(false);
165 					} else {
166 						MesenMsgBox.Show("GoogleDriveIntegrationError", MessageBoxButtons.OK, MessageBoxIcon.Error);
167 					}
168 				} else {
169 					UpdateCloudDisplay();
170 				}
171 			} else {
172 				MesenMsgBox.Show("GoogleDriveIntegrationError", MessageBoxButtons.OK, MessageBoxIcon.Error);
173 			}
174 		}
175 
btnEnableIntegration_Click(object sender, EventArgs e)176 		private void btnEnableIntegration_Click(object sender, EventArgs e)
177 		{
178 			Task.Run(() => TryEnableSync());
179 		}
180 
btnDisableIntegration_Click(object sender, EventArgs e)181 		private void btnDisableIntegration_Click(object sender, EventArgs e)
182 		{
183 			Task.Run(() => {
184 				CloudSyncHelper.DisableSync();
185 				UpdateCloudDisplay();
186 			});
187 		}
188 
tmrSyncDateTime_Tick(object sender, EventArgs e)189 		private void tmrSyncDateTime_Tick(object sender, EventArgs e)
190 		{
191 			btnDisableIntegration.Enabled = !CloudSyncHelper.Syncing;
192 			btnResync.Enabled = btnDisableIntegration.Enabled;
193 
194 			if(ConfigManager.Config.PreferenceInfo.CloudLastSync != DateTime.MinValue) {
195 				lblLastSyncDateTime.Text = ConfigManager.Config.PreferenceInfo.CloudLastSync.ToLongDateString() + " " + ConfigManager.Config.PreferenceInfo.CloudLastSync.ToLongTimeString();
196 			} else {
197 				lblLastSyncDateTime.Text = "";
198 			}
199 		}
200 
btnResync_Click(object sender, EventArgs e)201 		private void btnResync_Click(object sender, EventArgs e)
202 		{
203 			Task.Run(() => CloudSyncHelper.Sync());
204 		}
205 
chkAutoSave_CheckedChanged(object sender, EventArgs e)206 		private void chkAutoSave_CheckedChanged(object sender, EventArgs e)
207 		{
208 			nudAutoSave.Enabled = chkAutoSave.Checked;
209 			chkAutoSaveNotify.Enabled = chkAutoSave.Checked;
210 		}
211 
btnResetSettings_Click(object sender, EventArgs e)212 		private void btnResetSettings_Click(object sender, EventArgs e)
213 		{
214 			if(MesenMsgBox.Show("ResetSettingsConfirmation", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK) {
215 				ConfigManager.ResetSettings();
216 				this.Close();
217 			}
218 		}
219 
OnFormClosing(FormClosingEventArgs e)220 		protected override void OnFormClosing(FormClosingEventArgs e)
221 		{
222 			if(this.DialogResult == DialogResult.OK) {
223 				if(!ValidateFolderSettings()) {
224 					e.Cancel = true;
225 					return;
226 				}
227 
228 				if(radStorageDocuments.Checked != (ConfigManager.HomeFolder == ConfigManager.DefaultDocumentsFolder)) {
229 					//Need to copy files and display confirmation
230 					string targetFolder = radStorageDocuments.Checked ? ConfigManager.DefaultDocumentsFolder : ConfigManager.DefaultPortableFolder;
231 					if(MesenMsgBox.Show("CopyMesenDataPrompt", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, ConfigManager.HomeFolder, targetFolder) == DialogResult.OK) {
232 						try {
233 							//Save any other changes before copying the data
234 							UpdateObject();
235 							ConfigManager.ApplyChanges();
236 
237 							MigrateData(ConfigManager.HomeFolder, targetFolder);
238 						} catch(Exception ex) {
239 							MesenMsgBox.Show("UnexpectedError", MessageBoxButtons.OK, MessageBoxIcon.Error, ex.ToString());
240 							e.Cancel = true;
241 						}
242 					} else {
243 						e.Cancel = true;
244 						return;
245 					}
246 				}
247 			} else {
248 				base.OnFormClosing(e);
249 			}
250 		}
251 
MigrateData(string source, string target)252 		private bool MigrateData(string source, string target)
253 		{
254 			using(frmCopyFiles frm = new frmCopyFiles(source, target)) {
255 				frm.ShowDialog(this);
256 				if(frm.Exception != null) {
257 					throw frm.Exception;
258 				}
259 			}
260 			if(File.Exists(Path.Combine(source, "settings.backup.xml"))) {
261 				File.Delete(Path.Combine(source, "settings.backup.xml"));
262 			}
263 			File.Move(Path.Combine(source, "settings.xml"), Path.Combine(source, "settings.backup.xml"));
264 
265 			ConfigManager.InitHomeFolder();
266 			ConfigManager.SaveConfig();
267 
268 			ConfigManager.RestartMesen(true);
269 			this.NeedRestart = true;
270 
271 			return true;
272 		}
273 
ValidateFolderSettings()274 		private bool ValidateFolderSettings()
275 		{
276 			bool result = true;
277 			List<string> invalidFolders = new List<string>();
278 			try {
279 				if(chkGameOverride.Checked && !CheckFolderPermissions(psGame.Text, false)) {
280 					invalidFolders.Add(chkGameOverride.Text.Replace(":", "").Trim());
281 				}
282 				if(chkAviOverride.Checked && !CheckFolderPermissions(psAvi.Text)) {
283 					invalidFolders.Add(chkAviOverride.Text.Replace(":", "").Trim());
284 				}
285 				if(chkMoviesOverride.Checked && !CheckFolderPermissions(psMovies.Text)) {
286 					invalidFolders.Add(chkMoviesOverride.Text.Replace(":", "").Trim());
287 				}
288 				if(chkSaveDataOverride.Checked && !CheckFolderPermissions(psSaveData.Text)) {
289 					invalidFolders.Add(chkSaveDataOverride.Text.Replace(":", "").Trim());
290 				}
291 				if(chkSaveStatesOverride.Checked && !CheckFolderPermissions(psSaveStates.Text)) {
292 					invalidFolders.Add(chkSaveStatesOverride.Text.Replace(":", "").Trim());
293 				}
294 				if(chkScreenshotsOverride.Checked && !CheckFolderPermissions(psScreenshots.Text)) {
295 					invalidFolders.Add(chkScreenshotsOverride.Text.Replace(":", "").Trim());
296 				}
297 				if(chkWaveOverride.Checked && !CheckFolderPermissions(psWave.Text)) {
298 					invalidFolders.Add(chkWaveOverride.Text.Replace(":", "").Trim());
299 				}
300 
301 				result = invalidFolders.Count == 0;
302 			} catch {
303 				result = false;
304 			}
305 			if(!result) {
306 				MesenMsgBox.Show("InvalidPaths", MessageBoxButtons.OK, MessageBoxIcon.Error, string.Join(Environment.NewLine, invalidFolders));
307 			}
308 			return result;
309 		}
310 
CheckFolderPermissions(string folder, bool checkWritePermission = true)311 		private bool CheckFolderPermissions(string folder, bool checkWritePermission = true)
312 		{
313 			if(!Directory.Exists(folder)) {
314 				try {
315 					if(string.IsNullOrWhiteSpace(folder)) {
316 						return false;
317 					}
318 					Directory.CreateDirectory(folder);
319 				} catch {
320 					return false;
321 				}
322 			}
323 			if(checkWritePermission) {
324 				try {
325 					File.WriteAllText(Path.Combine(folder, "test.txt"), "");
326 					File.Delete(Path.Combine(folder, "test.txt"));
327 				} catch {
328 					return false;
329 				}
330 			}
331 			return true;
332 		}
333 
UpdateFolderOverrideUi()334 		private void UpdateFolderOverrideUi()
335 		{
336 			psGame.Enabled = chkGameOverride.Checked;
337 			psAvi.Enabled = chkAviOverride.Checked;
338 			psMovies.Enabled = chkMoviesOverride.Checked;
339 			psSaveData.Enabled = chkSaveDataOverride.Checked;
340 			psSaveStates.Enabled = chkSaveStatesOverride.Checked;
341 			psScreenshots.Enabled = chkScreenshotsOverride.Checked;
342 			psWave.Enabled = chkWaveOverride.Checked;
343 
344 			psGame.DisabledText = ResourceHelper.GetMessage("LastFolderUsed");
345 			psAvi.DisabledText = ConfigManager.DefaultAviFolder;
346 			psMovies.DisabledText = ConfigManager.DefaultMovieFolder;
347 			psSaveData.DisabledText = ConfigManager.DefaultSaveDataFolder;
348 			psSaveStates.DisabledText = ConfigManager.DefaultSaveStateFolder;
349 			psScreenshots.DisabledText = ConfigManager.DefaultScreenshotFolder;
350 			psWave.DisabledText = ConfigManager.DefaultWaveFolder;
351 		}
352 
UpdateLocationText()353 		private void UpdateLocationText()
354 		{
355 			lblLocation.Text = radStorageDocuments.Checked ? ConfigManager.DefaultDocumentsFolder : ConfigManager.DefaultPortableFolder;
356 		}
357 
chkOverride_CheckedChanged(object sender, EventArgs e)358 		private void chkOverride_CheckedChanged(object sender, EventArgs e)
359 		{
360 			UpdateFolderOverrideUi();
361 		}
362 
radStorageDocuments_CheckedChanged(object sender, EventArgs e)363 		private void radStorageDocuments_CheckedChanged(object sender, EventArgs e)
364 		{
365 			UpdateLocationText();
366 		}
367 	}
368 }
369