-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathProgram.cs
250 lines (213 loc) · 10.3 KB
/
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
using System;
using System.Diagnostics;
using System.Drawing.Text;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
using ToNSaveManager.Localization;
using ToNSaveManager.Models;
using ToNSaveManager.Utils.API;
using ToNSaveManager.Utils.Discord;
using ToNSaveManager.Utils.LogParser;
namespace ToNSaveManager
{
internal static class Program
{
internal const string ProgramName = "ToNSaveManager";
internal static readonly string ProgramDirectory = AppContext.BaseDirectory ?? string.Empty;
internal static readonly string ProgramLocation = Path.Combine(ProgramDirectory, ProgramFile);
internal static readonly string ProgramLocationTemporary = Path.Combine(ProgramDirectory, "_" + ProgramFile.ToLowerInvariant() + ".old");
internal static readonly string ProgramLocationTemporaryLegacy = Path.Combine(ProgramDirectory, "__" + ProgramFile);
internal const string ProgramFile = ProgramName + ".exe";
internal static readonly string DataLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ProgramName);
internal static readonly string LegacyDataLocation = Path.Combine(LogWatcher<ToNLogContext>.GetVRChatDataLocation(), ProgramName);
internal static Mutex? AppMutex = new Mutex(true, ProgramName);
internal static void ReleaseMutex() {
if (AppMutex != null) {
AppMutex.ReleaseMutex();
AppMutex.Dispose();
AppMutex = null;
}
}
internal static bool CheckMutex()
{
return AppMutex != null && !AppMutex.WaitOne(TimeSpan.Zero, true);
}
static string[] Arguments = Array.Empty<string>();
internal static bool ContainsArg(string arg) {
return Array.IndexOf(Arguments, arg) != -1;
}
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
if (CheckMutex()) {
// Don't run program if it's already running, instead we focus the already existing window
NativeMethods.PostMessage((IntPtr)NativeMethods.HWND_BROADCAST, NativeMethods.WM_FOCUSINST, IntPtr.Zero, IntPtr.Zero);
return;
}
if (!Directory.Exists(DataLocation)) Directory.CreateDirectory(DataLocation);
try {
Logger.Log("Initializing logging.");
Directory.SetCurrentDirectory(ProgramDirectory);
Logger.Log("Program Directory: " + ProgramDirectory);
Arguments = args;
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
LANG.Initialize();
Updater.CheckPostUpdate(args);
Logger.Log("Initializing application rendering.");
ApplicationConfiguration.Initialize();
Application.SetCompatibleTextRenderingDefault(true);
Logger.Log("Initializing font.");
InitializeFont();
MainWindow.Started = false;
if (!StartCheckForUpdate()) {
Application.ApplicationExit += OnApplicationExit;
Logger.Log("Running 'MainWindow'");
Application.Run(new MainWindow());
}
// Check when all forms close
Logger.Log("All windows are closed...");
GitHubUpdate.Start();
} catch (Exception e) {
// What the heck happened?
MessageBox.Show(e.ToString(), "APP STARTUP FAILED!");
File.WriteAllText(Path.Combine(DataLocation, "output.error"), e.ToString());
}
}
private static void OnApplicationExit(object? sender, EventArgs e) {
Logger.Log("Disposing used resources...");
FontCollection.Dispose();
DefaultFont?.Dispose();
ReleaseMutex();
Logger.Log("Saving before app exit...");
MainWindow.SaveData.Export();
StatsWindow.WriteChanges();
Logger.Log("Done.");
}
static readonly PrivateFontCollection FontCollection = new PrivateFontCollection();
static Font? DefaultFont;
static void InitializeFont()
{
using (Stream? fontStream = GetEmbededResource("FiraCode.ttf"))
{
if (fontStream != null) try
{
IntPtr data = Marshal.AllocCoTaskMem((int)fontStream.Length);
byte[] fontdata = new byte[fontStream.Length];
fontStream.Read(fontdata, 0, (int)fontStream.Length);
Marshal.Copy(fontdata, 0, data, (int)fontStream.Length);
uint cFonts = 0;
NativeMethods.AddFontMemResourceEx(data, (uint)fontdata.Length, IntPtr.Zero, ref cFonts);
FontCollection.AddMemoryFont(data, (int)fontStream.Length);
fontStream.Close();
Marshal.FreeCoTaskMem(data);
}
catch (Exception ex)
{
Logger.Error(ex.ToString());
}
}
DefaultFont = new Font(FontCollection.Families[0], 8.999999f, GraphicsUnit.Point);
Application.SetDefaultFont(DefaultFont);
}
internal static Stream? GetEmbededResource(string name)
{
return Assembly.GetExecutingAssembly()
.GetManifestResourceStream($"ToNSaveManager.Resources.{name}");
}
internal static Version? GetVersion()
{
Assembly assembly = Assembly.GetExecutingAssembly();
return assembly.GetName().Version;
}
/// <summary>
/// Check for updates on the GitHub repo.
/// </summary>
/// <param name="showUpToDate">Shows a message if there's no updates available.</param>
internal static bool StartCheckForUpdate(bool showUpToDate = false)
{
Version? currentVersion = GetVersion();
if (currentVersion == null) return false; // No current version?
Logger.Log("Checking for updates...");
GitHubRelease? release = GitHubRelease.GetLatest();
if (release == null || release.assets.Length == 0 || (!showUpToDate && release.tag_name == Settings.Get.IgnoreRelease)) return false;
GitHubRelease.Asset? asset = release.assets.FirstOrDefault(v => v.name == "ToNSaveManager.zip" && v.content_type == "application/zip" && v.state == "uploaded");
if (asset == null) return false;
Logger.Log("Latest release: " + release.tag_name);
if (Version.TryParse(release.tag_name, out Version? releaseVersion) && releaseVersion > currentVersion)
{
const string log_start = "[changelog]: <> (START)";
const string log_end = "[changelog]: <> (END)";
int start = release.body.IndexOf(log_start, StringComparison.InvariantCulture);
int end = release.body.IndexOf(log_end, StringComparison.InvariantCulture);
string body = string.Empty;
if (start > -1 && end > (start + log_start.Length) && end > start)
{
start += log_start.Length;
body = "\n\n" + release.body.Substring(start, end - start).Trim();
}
DialogResult result = MessageBox.Show((LANG.S("MESSAGE.UPDATE_AVAILABLE") ?? "A new update have been released on GitHub.\n\nWould you like to automatically download and update to the new version?") + body, LANG.S("MESSAGE.UPDATE_AVAILABLE.TITLE") ?? "New update available", MessageBoxButtons.YesNo, MessageBoxIcon.Information);
if (result == DialogResult.Yes)
{
GitHubUpdate.Set(release, asset);
return true;
} else if (!showUpToDate)
{
Settings.Get.IgnoreRelease = release.tag_name;
Settings.Export();
}
} else if (showUpToDate)
{
MessageBox.Show(LANG.S("MESSAGE.UPDATE_UNAVAILABLE") ?? "No updates are currently available.", LANG.S("MESSAGE.UPDATE_UNAVAILABLE.TITLE") ?? "No updates available", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
return false;
}
internal static bool CreateFileBackup(string filePath)
{
Logger.Info("Creating Backup For: " + filePath);
try
{
if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
File.Copy(filePath, filePath + ".backup_" + DateTimeOffset.UtcNow.ToUnixTimeSeconds());
return true;
}
catch
{
return false;
}
}
}
public partial class MainWindow : Form
{
protected override void WndProc(ref Message m)
{
if (m.Msg == NativeMethods.WM_FOCUSINST) FocusInstance();
base.WndProc(ref m);
}
private void FocusInstance()
{
if (WindowState == FormWindowState.Minimized)
WindowState = FormWindowState.Normal;
NativeMethods.SetForegroundWindow(this.Handle);
}
}
internal class NativeMethods
{
public const int HWND_BROADCAST = 0xffff;
public static readonly int WM_FOCUSINST = RegisterWindowMessage("WM_FOCUSINST");
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("gdi32.dll")]
public static extern IntPtr AddFontMemResourceEx(IntPtr pbFont, uint cbFont, IntPtr pdv, [In] ref uint pcFonts);
}
}