├── .gitignore ├── Classes ├── FriendsBackup.cs └── UserCounts.cs ├── FodyWeavers.xml ├── FodyWeavers.xsd ├── IPC ├── CommandHandler.cs ├── Game.cs └── Launcher.cs ├── LICENSE ├── Launcher.ico ├── Launcher.png ├── News.rtf ├── News ├── NewsItem.cs ├── NewsListControl.cs ├── NewsListControl.designer.cs ├── NewsListControl.resx └── NewsManager.cs ├── Program.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs └── Resources.resx ├── README.md ├── Resources └── vrctools.png ├── Setup ├── Game.cs ├── Mods.cs ├── URI.cs └── VRCAPI │ ├── LoginModal.Designer.cs │ ├── LoginModal.cs │ └── LoginModal.resx ├── UI ├── Chat.Designer.cs ├── Chat.cs ├── Chat.resx ├── CommandLineArguments.Designer.cs ├── CommandLineArguments.cs ├── CommandLineArguments.resx ├── InputBox.cs ├── InviteDialog.Designer.cs ├── InviteDialog.cs ├── InviteDialog.resx ├── Main │ ├── Avatars.cs │ ├── Log.cs │ ├── Main.Designer.cs │ ├── Main.cs │ ├── Main.resx │ ├── Mods.cs │ ├── News.cs │ ├── Settings.cs │ ├── Users.cs │ └── Worlds.cs ├── MultilineInput.Designer.cs ├── MultilineInput.cs ├── MultilineInput.resx ├── OperationsPanel.Designer.cs ├── OperationsPanel.cs └── OperationsPanel.resx ├── Updater ├── AvatarFav.cs ├── Launcher.cs ├── VRCModLoader.cs └── VRCTools.cs ├── Utils ├── Arguments.cs ├── Arguments │ ├── game.json │ ├── launcher.json │ └── vrctools.json ├── AssemblyReader.cs ├── Config.cs ├── Extensions.cs ├── ExternalConsole.cs ├── Game.cs ├── LogReader.cs ├── Logger.cs ├── Mods.cs ├── URI.cs ├── Utils.cs └── VRCAPI.cs ├── VRCLauncher.sln ├── VRChatLauncher.csproj ├── app.config ├── packages.config ├── res ├── Launcher.ico └── Launcher.pdn └── stats /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | 33 | # Visual Studio 2015/2017 cache/options directory 34 | .vs/ 35 | # Uncomment if you have tasks that create the project's static files in wwwroot 36 | #wwwroot/ 37 | 38 | # Visual Studio 2017 auto generated files 39 | Generated\ Files/ 40 | 41 | # MSTest test Results 42 | [Tt]est[Rr]esult*/ 43 | [Bb]uild[Ll]og.* 44 | 45 | # NUNIT 46 | *.VisualState.xml 47 | TestResult.xml 48 | 49 | # Build Results of an ATL Project 50 | [Dd]ebugPS/ 51 | [Rr]eleasePS/ 52 | dlldata.c 53 | 54 | # Benchmark Results 55 | BenchmarkDotNet.Artifacts/ 56 | 57 | # .NET Core 58 | project.lock.json 59 | project.fragment.lock.json 60 | artifacts/ 61 | 62 | # StyleCop 63 | StyleCopReport.xml 64 | 65 | # Files built by Visual Studio 66 | *_i.c 67 | *_p.c 68 | *_h.h 69 | *.ilk 70 | *.meta 71 | *.obj 72 | *.iobj 73 | *.pch 74 | *.pdb 75 | *.ipdb 76 | *.pgc 77 | *.pgd 78 | *.rsp 79 | *.sbr 80 | *.tlb 81 | *.tli 82 | *.tlh 83 | *.tmp 84 | *.tmp_proj 85 | *_wpftmp.csproj 86 | *.log 87 | *.vspscc 88 | *.vssscc 89 | .builds 90 | *.pidb 91 | *.svclog 92 | *.scc 93 | 94 | # Chutzpah Test files 95 | _Chutzpah* 96 | 97 | # Visual C++ cache files 98 | ipch/ 99 | *.aps 100 | *.ncb 101 | *.opendb 102 | *.opensdf 103 | *.sdf 104 | *.cachefile 105 | *.VC.db 106 | *.VC.VC.opendb 107 | 108 | # Visual Studio profiler 109 | *.psess 110 | *.vsp 111 | *.vspx 112 | *.sap 113 | 114 | # Visual Studio Trace Files 115 | *.e2e 116 | 117 | # TFS 2012 Local Workspace 118 | $tf/ 119 | 120 | # Guidance Automation Toolkit 121 | *.gpState 122 | 123 | # ReSharper is a .NET coding add-in 124 | _ReSharper*/ 125 | *.[Rr]e[Ss]harper 126 | *.DotSettings.user 127 | 128 | # JustCode is a .NET coding add-in 129 | .JustCode 130 | 131 | # TeamCity is a build add-in 132 | _TeamCity* 133 | 134 | # DotCover is a Code Coverage Tool 135 | *.dotCover 136 | 137 | # AxoCover is a Code Coverage Tool 138 | .axoCover/* 139 | !.axoCover/settings.json 140 | 141 | # Visual Studio code coverage results 142 | *.coverage 143 | *.coveragexml 144 | 145 | # NCrunch 146 | _NCrunch_* 147 | .*crunch*.local.xml 148 | nCrunchTemp_* 149 | 150 | # MightyMoose 151 | *.mm.* 152 | AutoTest.Net/ 153 | 154 | # Web workbench (sass) 155 | .sass-cache/ 156 | 157 | # Installshield output folder 158 | [Ee]xpress/ 159 | 160 | # DocProject is a documentation generator add-in 161 | DocProject/buildhelp/ 162 | DocProject/Help/*.HxT 163 | DocProject/Help/*.HxC 164 | DocProject/Help/*.hhc 165 | DocProject/Help/*.hhk 166 | DocProject/Help/*.hhp 167 | DocProject/Help/Html2 168 | DocProject/Help/html 169 | 170 | # Click-Once directory 171 | publish/ 172 | 173 | # Publish Web Output 174 | *.[Pp]ublish.xml 175 | *.azurePubxml 176 | # Note: Comment the next line if you want to checkin your web deploy settings, 177 | # but database connection strings (with potential passwords) will be unencrypted 178 | *.pubxml 179 | *.publishproj 180 | 181 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 182 | # checkin your Azure Web App publish settings, but sensitive information contained 183 | # in these scripts will be unencrypted 184 | PublishScripts/ 185 | 186 | # NuGet Packages 187 | *.nupkg 188 | # The packages folder can be ignored because of Package Restore 189 | **/[Pp]ackages/* 190 | # except build/, which is used as an MSBuild target. 191 | !**/[Pp]ackages/build/ 192 | # Uncomment if necessary however generally it will be regenerated when needed 193 | #!**/[Pp]ackages/repositories.config 194 | # NuGet v3's project.json files produces more ignorable files 195 | *.nuget.props 196 | *.nuget.targets 197 | 198 | # Microsoft Azure Build Output 199 | csx/ 200 | *.build.csdef 201 | 202 | # Microsoft Azure Emulator 203 | ecf/ 204 | rcf/ 205 | 206 | # Windows Store app package directories and files 207 | AppPackages/ 208 | BundleArtifacts/ 209 | Package.StoreAssociation.xml 210 | _pkginfo.txt 211 | *.appx 212 | *.appxbundle 213 | *.appxupload 214 | 215 | # Visual Studio cache files 216 | # files ending in .cache can be ignored 217 | *.[Cc]ache 218 | # but keep track of directories ending in .cache 219 | !?*.[Cc]ache/ 220 | 221 | # Others 222 | ClientBin/ 223 | ~$* 224 | *~ 225 | *.dbmdl 226 | *.dbproj.schemaview 227 | *.jfm 228 | *.pfx 229 | *.publishsettings 230 | orleans.codegen.cs 231 | 232 | # Including strong name files can present a security risk 233 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 234 | #*.snk 235 | 236 | # Since there are multiple workflows, uncomment next line to ignore bower_components 237 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 238 | #bower_components/ 239 | 240 | # RIA/Silverlight projects 241 | Generated_Code/ 242 | 243 | # Backup & report files from converting an old project file 244 | # to a newer Visual Studio version. Backup files are not needed, 245 | # because we have git ;-) 246 | _UpgradeReport_Files/ 247 | Backup*/ 248 | UpgradeLog*.XML 249 | UpgradeLog*.htm 250 | ServiceFabricBackup/ 251 | *.rptproj.bak 252 | 253 | # SQL Server files 254 | *.mdf 255 | *.ldf 256 | *.ndf 257 | 258 | # Business Intelligence projects 259 | *.rdl.data 260 | *.bim.layout 261 | *.bim_*.settings 262 | *.rptproj.rsuser 263 | *- Backup*.rdl 264 | 265 | # Microsoft Fakes 266 | FakesAssemblies/ 267 | 268 | # GhostDoc plugin setting file 269 | *.GhostDoc.xml 270 | 271 | # Node.js Tools for Visual Studio 272 | .ntvs_analysis.dat 273 | node_modules/ 274 | 275 | # Visual Studio 6 build log 276 | *.plg 277 | 278 | # Visual Studio 6 workspace options file 279 | *.opt 280 | 281 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 282 | *.vbw 283 | 284 | # Visual Studio LightSwitch build output 285 | **/*.HTMLClient/GeneratedArtifacts 286 | **/*.DesktopClient/GeneratedArtifacts 287 | **/*.DesktopClient/ModelManifest.xml 288 | **/*.Server/GeneratedArtifacts 289 | **/*.Server/ModelManifest.xml 290 | _Pvt_Extensions 291 | 292 | # Paket dependency manager 293 | .paket/paket.exe 294 | paket-files/ 295 | 296 | # FAKE - F# Make 297 | .fake/ 298 | 299 | # CodeRush personal settings 300 | .cr/personal 301 | 302 | # Python Tools for Visual Studio (PTVS) 303 | __pycache__/ 304 | *.pyc 305 | 306 | # Cake - Uncomment if you are using it 307 | # tools/** 308 | # !tools/packages.config 309 | 310 | # Tabs Studio 311 | *.tss 312 | 313 | # Telerik's JustMock configuration file 314 | *.jmconfig 315 | 316 | # BizTalk build output 317 | *.btp.cs 318 | *.btm.cs 319 | *.odx.cs 320 | *.xsd.cs 321 | 322 | # OpenCover UI analysis results 323 | OpenCover/ 324 | 325 | # Azure Stream Analytics local run output 326 | ASALocalRun/ 327 | 328 | # MSBuild Binary and Structured Log 329 | *.binlog 330 | 331 | # NVidia Nsight GPU debugger configuration file 332 | *.nvuser 333 | 334 | # MFractors (Xamarin productivity tool) working folder 335 | .mfractor/ 336 | 337 | # Local History for Visual Studio 338 | .localhistory/ 339 | 340 | # BeatPulse healthcheck temp database 341 | healthchecksdb 342 | 343 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 344 | MigrationBackup/ 345 | /UI/Main - Copy.resx 346 | /UI/Main - Copy.Designer.cs 347 | /UI/Main - Copy.cs 348 | /Launcher.exe 349 | -------------------------------------------------------------------------------- /Classes/FriendsBackup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using VRChatApi.Classes; 7 | 8 | namespace VRChatLauncher 9 | { 10 | public class FriendsBackup 11 | { 12 | public Friend Account { get; set; } 13 | public DateTime TimeStamp { get; set; } 14 | public List Friends = new List(); 15 | public class Friend 16 | { 17 | // public enum Changed { Nothing, Name, Everything } 18 | public string id { get; set; } 19 | public string displayName { get; set; } 20 | public string username { get; set; } 21 | public Friend(UserBriefResponse friend) 22 | { 23 | id = friend.id; displayName = friend.displayName; username = friend.username; 24 | } 25 | /*public Changed Difference(Friend other) 26 | { 27 | if (id != other.id) return Changed.Everything; 28 | if (displayName != other.displayName) return Changed.Name; 29 | return Changed.Nothing; 30 | }*/ 31 | /*public override bool Equals(object obj) { 32 | if (!(obj is Friend)) return false; 33 | var other = obj as Friend; 34 | if (id != other.id) return false; 35 | return true; 36 | } 37 | public static bool operator ==(Friend x, Friend y) { return x.Equals(y); } 38 | public static bool operator !=(Friend x, Friend y) { return !(x == y); }*/ 39 | } 40 | public FriendsBackup(UserResponse account, List friends) 41 | { 42 | TimeStamp = DateTime.Now; 43 | Account = new Friend(account); 44 | foreach (var friend in friends) 45 | { 46 | Friends.Add(new Friend(friend)); 47 | } 48 | } 49 | } 50 | /*public class ChangedFriend 51 | { 52 | [JsonIgnore] 53 | public FriendsBackup.Friend.Changed Change { get; set; } 54 | [JsonIgnore] 55 | public FriendsBackup.Friend Old; 56 | [JsonIgnore] 57 | public FriendsBackup.Friend New; 58 | public ChangedFriend(FriendsBackup.Friend oldFriend, FriendsBackup.Friend newFriend) 59 | { 60 | Old = oldFriend; New = newFriend; Change = oldFriend.Difference(newFriend); 61 | } 62 | } 63 | public class FriendsBackupDifference 64 | { 65 | [JsonIgnore] 66 | public List AddedFriends { get; set; } 67 | [JsonIgnore] 68 | public List RemovedFriends { get; set; } 69 | [JsonIgnore] 70 | public List ChangedNames { get; set; } 71 | public FriendsBackupDifference(FriendsBackup one, FriendsBackup other) 72 | { 73 | var firstListOlder = one.TimeStamp < other.TimeStamp; 74 | var oldList = (firstListOlder) ? one : other; 75 | var newList = (firstListOlder) ? other : one; 76 | foreach (var oldFriend in oldList.Friends) 77 | { 78 | foreach (var newFriend in newList.Friends) 79 | { 80 | var changed = new ChangedFriend(oldFriend, newFriend); 81 | } 82 | } 83 | } 84 | }*/ 85 | } 86 | -------------------------------------------------------------------------------- /Classes/UserCounts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace VRChatLauncher.Classes { 8 | class UserCountsEndPoint { 9 | public string Name { get; set; } 10 | public string Mod { get; set; } 11 | public long onlineClients { get; set; } 12 | public long onlineServers { get; set; } 13 | public string lastUpdated { get; set; } 14 | } 15 | class UserCounts { 16 | public string lastStartTime { get; set; } 17 | public UserCountsEndPoint[] endpoints { get; set; } 18 | public string lastStopTime { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Assembly-CSharp 5 | 6 | 7 | -------------------------------------------------------------------------------- /FodyWeavers.xsd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks 13 | 14 | 15 | 16 | 17 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. 18 | 19 | 20 | 21 | 22 | A list of unmanaged 32 bit assembly names to include, delimited with line breaks. 23 | 24 | 25 | 26 | 27 | A list of unmanaged 64 bit assembly names to include, delimited with line breaks. 28 | 29 | 30 | 31 | 32 | The order of preloaded assemblies, delimited with line breaks. 33 | 34 | 35 | 36 | 37 | 38 | This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. 39 | 40 | 41 | 42 | 43 | Controls if .pdbs for reference assemblies are also embedded. 44 | 45 | 46 | 47 | 48 | Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. 49 | 50 | 51 | 52 | 53 | As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. 54 | 55 | 56 | 57 | 58 | Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. 59 | 60 | 61 | 62 | 63 | Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. 64 | 65 | 66 | 67 | 68 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | 69 | 70 | 71 | 72 | 73 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. 74 | 75 | 76 | 77 | 78 | A list of unmanaged 32 bit assembly names to include, delimited with |. 79 | 80 | 81 | 82 | 83 | A list of unmanaged 64 bit assembly names to include, delimited with |. 84 | 85 | 86 | 87 | 88 | The order of preloaded assemblies, delimited with |. 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. 97 | 98 | 99 | 100 | 101 | A comma-separated list of error codes that can be safely ignored in assembly verification. 102 | 103 | 104 | 105 | 106 | 'false' to turn off automatic generation of the XML Schema file. 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /IPC/CommandHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Web; 7 | using VRChatLauncher.Utils; 8 | 9 | namespace VRChatLauncher.IPC 10 | { 11 | public class CommandHandler 12 | { 13 | public static void IsLauncherRunning(string argument) // TODO: make properly 14 | { 15 | foreach (var arg in argument.Split(' ')) 16 | { 17 | if (arg.ToLower().StartsWith("vrchat://")) 18 | { 19 | var uri = URI.Parse(arg); 20 | switch (uri.Host.ToLower()) 21 | { 22 | case "launch": 23 | if (Utils.Game.IsGameAlreadyRunning()) 24 | { 25 | Logger.Log(uri.ParseQueryString()); 26 | } 27 | else 28 | { 29 | Utils.Game.StartGame(false, arg); // Todo properly implement 30 | } 31 | break; 32 | default: 33 | break; 34 | } 35 | // var queryDictionary = HttpUtility.ParseQueryString(uri.Query); 36 | /*Logger.LogLines(lines: true, 37 | uri.ToJson(), // "vrchat://launch/?ref=vrchat.com&id=wrld_9c40ca9d-216d-46d1-baa7-e1117df7d026:89595" 38 | uri.AbsoluteUri, // vrchat://launch/?ref=vrchat.com&id=wrld_9c40ca9d-216d-46d1-baa7-e1117df7d026:89595 39 | uri.AbsolutePath, // / 40 | uri.Authority, // launch 41 | uri.DnsSafeHost, // launch 42 | uri.Fragment,// 43 | uri.Host, // launch 44 | uri.HostNameType, // Dns 45 | uri.IdnHost, // launch 46 | uri.IsAbsoluteUri, // True 47 | uri.IsDefaultPort, // True 48 | uri.IsFile, // False 49 | uri.IsLoopback, // False 50 | uri.IsUnc, // False 51 | uri.LocalPath, // / 52 | uri.OriginalString, // vrchat://launch/?ref=vrchat.com&id=wrld_9c40ca9d-216d-46d1-baa7-e1117df7d026:89595 53 | uri.PathAndQuery, // /?ref=vrchat.com&id=wrld_9c40ca9d-216d-46d1-baa7-e1117df7d026:89595 54 | uri.Port, // -1 55 | uri.Query, // ?ref=vrchat.com&id=wrld_9c40ca9d-216d-46d1-baa7-e1117df7d026:89595 56 | uri.Scheme, // vrchat 57 | uri.Segments.ToJson(), // System.String[] 58 | uri.UserEscaped, // False 59 | uri.UserInfo, // 60 | uri.IsWellFormedOriginalString(), // True 61 | uri.ParseQueryString().ToJson(), // { "ref": "vrchat.com", "id": "wrld_9c40ca9d-216d-46d1-baa7-e1117df7d026:89595" } 62 | // queryDictionary, // ref=vrchat.com&id=wrld_9c40ca9d-216d-46d1-baa7-e1117df7d026%3a89595 63 | // queryDictionary.ToJson() // [ "ref", "id" ] 64 | );*/ 65 | // Game.SendCommand(arg); // Handle with launcher 66 | } 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /IPC/Game.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Specialized; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Text.RegularExpressions; 8 | using System.Threading.Tasks; 9 | using System.Web; 10 | using System.Windows.Forms; 11 | using VRChatLauncher.Utils; 12 | 13 | namespace VRChatLauncher.IPC 14 | { 15 | public class Game 16 | { 17 | /* private static List CleanArgs(List args) 18 | { 19 | args.RemoveAll(item => item.StartsWith("--vrclauncher.")); 20 | // foreach (var arg in args) { 21 | /*var cmd = Command.FromString(arg); 22 | if (cmd is null) _args.Add(arg);* 23 | //} 24 | return args; 25 | }*/ 26 | public static void Send(Command command) { 27 | var cmd = command.ToString(); 28 | Logger.Debug("Sending command to game:", Environment.NewLine, command.ToJson()); 29 | if (Utils.Game.IsGameAlreadyRunning()) { 30 | SendCommand(cmd); 31 | } else { 32 | Utils.Game.StartGame(false, Program.Arguments.ToString(), cmd); 33 | } 34 | } 35 | private static void SendCommand(string cmd) { 36 | string oldClip = null; 37 | try { 38 | oldClip = Clipboard.GetText(); 39 | Clipboard.Clear(); 40 | } catch (System.Runtime.InteropServices.ExternalException) { Logger.Warn("Unable to backup/clear clipboard"); } 41 | Clipboard.SetText(cmd); 42 | if (!string.IsNullOrWhiteSpace(oldClip)) { 43 | System.Threading.Thread.Sleep(100); 44 | try { Clipboard.SetText(oldClip); 45 | } catch (System.Runtime.InteropServices.ExternalException) { Logger.Warn("Unable to reset clipboard"); } 46 | } 47 | } 48 | public enum UserLocationType { Unknown, Empty, Offline, Private, Public } 49 | public class UserLocation 50 | { 51 | public UserLocationType Type { get; } 52 | public WorldInstanceID WorldInstance { get; } 53 | public UserLocation(string location) 54 | { 55 | if (location is null) { 56 | Type = UserLocationType.Empty; 57 | } else if (location.StartsWith("wrld_")) { 58 | Type = UserLocationType.Public; 59 | WorldInstance = new WorldInstanceID(location); 60 | } else if (location == "private") { 61 | Type = UserLocationType.Private; 62 | } else if (location == "offline") { 63 | Type = UserLocationType.Offline; 64 | } else if (location == "") { 65 | Type = UserLocationType.Empty; 66 | } else { Type = UserLocationType.Unknown; } 67 | } 68 | } 69 | public enum InstancePrivacyType { Unknown, Public, FriendsOnly, Hidden } 70 | private static Regex InstanceTagPattern = new Regex(@"(.*)\((.*)\)", RegexOptions.Compiled); 71 | public class WorldInstanceID { 72 | public InstancePrivacyType Type = InstancePrivacyType.Public; 73 | public string WorldID { get; } 74 | public ulong InstanceID { get; set; } 75 | public string CreatorID { get; } 76 | public string Nonce { get; } 77 | public WorldInstanceID(string worldinstanceid) { 78 | if (string.IsNullOrEmpty(worldinstanceid)) return; 79 | var splitted_world_instance = worldinstanceid.Split(":"); 80 | WorldID = splitted_world_instance[0]; 81 | if (splitted_world_instance.Length < 2) return; 82 | var splitted_instance = splitted_world_instance[1].Split("~").ToList(); 83 | InstanceID = ulong.Parse(splitted_instance.PopAt(0)); 84 | if (splitted_instance.Count < 1) return; 85 | foreach (var item in splitted_instance) 86 | { 87 | var match = InstanceTagPattern.Match(item); 88 | var key = match.Groups[1].Value.ToLower();var value = match.Groups[2].Value; 89 | if (key == "hidden") { Type = InstancePrivacyType.Hidden; CreatorID = value; } 90 | else if (key == "friends") { Type = InstancePrivacyType.FriendsOnly; CreatorID = value; } 91 | else if (key == "nonce") Nonce = value; 92 | } 93 | } 94 | public override string ToString() { 95 | var sb = new StringBuilder(WorldID); 96 | if (InstanceID > 0) { 97 | sb.Append(":"); sb.Append(InstanceID); 98 | switch (Type) 99 | { 100 | case InstancePrivacyType.FriendsOnly: 101 | sb.Append("~friends"); sb.Append(CreatorID.Enclose());break; 102 | case InstancePrivacyType.Hidden: 103 | sb.Append("~hidden"); sb.Append(CreatorID.Enclose());break; 104 | default: 105 | break; 106 | } 107 | if (!Nonce.IsNullOrEmpty()) { sb.Append("~nonce"); sb.Append(Nonce.Enclose()); } 108 | } 109 | return sb.ToString(); 110 | } 111 | } 112 | public enum CommandType { 113 | // [Description("")] 114 | Unknown, 115 | [Description("launch")] 116 | Launch, 117 | [Description("user/profile")] 118 | OpenProfile, 119 | [Description("user/add")] 120 | AddFriend, 121 | [Description("user/remove")] 122 | RemoveFriend, 123 | [Description("avatar/add")] 124 | FavoriteAvatar, 125 | [Description("avatar/remove")] 126 | UnFavoriteAvatar, 127 | [Description("avatar/select")] 128 | SelectAvatar 129 | } 130 | public class Command : UriBuilder { 131 | // vrchat://launch/?id=wrld_496b11e8-25a0 132 | //public new string Scheme = "vrchat"; 133 | public NameValueCollection Parameters { get; set; } 134 | public CommandType Type { get { return Extensions.GetValueFromDescription(Host, true); } set { Host = value.GetDescription(); } } 135 | public bool Force { get { return Parameters.GetBool("force"); } set { Parameters["force"] = value.ToString(); } } 136 | public bool SkipLauncher { get { return Parameters.GetBool("skiplauncher"); } set { Parameters["skiplauncher"] = value.ToString(); } } 137 | public string Referrer { get { return Parameters.GetString("ref"); } set { Parameters["ref"] = value; } } 138 | public WorldInstanceID WorldInstanceID { get { return new WorldInstanceID(Parameters.GetString("id")); } set { Console.WriteLine("Setting WorldInstanceID"); Parameters["id"] = value.ToString(); } } 139 | public new string Query { 140 | get { 141 | return Parameters.ToQueryString(); 142 | } 143 | set { 144 | base.Query = value; 145 | Parameters = HttpUtility.ParseQueryString(value); 146 | } 147 | } 148 | public Command(CommandType type = CommandType.Launch, NameValueCollection parameters = null, string url = "vrchat://") : base(url) { 149 | Scheme = "vrchat"; 150 | Type = type; 151 | if (parameters != null) Parameters = parameters; 152 | else Parameters = new NameValueCollection(); 153 | Logger.Trace(this.ToJson()); 154 | } 155 | public static Command FromString(string url) { 156 | try { return new Command(url: url.ToLower()); 157 | } catch (UriFormatException) { return null; } 158 | } 159 | public string ToDecodedString() { 160 | return HttpUtility.UrlDecode(Uri.ToString()); 161 | } 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /IPC/Launcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using VRChatLauncher.Utils; 7 | 8 | namespace VRChatLauncher.IPC 9 | { 10 | public partial class Launcher 11 | { 12 | tiesky.com.SharmIpc sm = null; 13 | public void Init() { 14 | if (sm != null) return; 15 | Logger.Log("Starting IPC Server"); 16 | sm = new tiesky.com.SharmIpc("vrchatlauncher" , this.RemoteCall); 17 | } 18 | 19 | public void Dispose() { 20 | if (sm != null) { 21 | Logger.Log("Disposing IPC Server"); 22 | sm.Dispose(); 23 | sm = null; 24 | } 25 | } 26 | Tuple RemoteCall(byte[] data) 27 | { 28 | var ret = new Message(string.Empty, false); 29 | if (data.Length < 1) return ret.ToTuple(); 30 | var str = ToString(data); 31 | Logger.Trace("Got IPC Message:", str); 32 | var cmd = str.Split(new[] { ' ' }, 2); 33 | var command = cmd[0].ToLower(); 34 | var argument = string.Empty; 35 | if (cmd.Length > 1) argument = cmd[1]; 36 | // Logger.Debug("Command:", command, "Argument:", argument, "IsCheck:", command == "islauncherrunning"); 37 | switch (command) { 38 | case "islauncherrunning": 39 | ret = new Message("yes"); 40 | Utils.Utils.BringSelfToFront(); 41 | CommandHandler.IsLauncherRunning(argument); 42 | break; 43 | default: 44 | Logger.Warn($"Recieved unknown IPC message: \"{command}\""); 45 | break; 46 | } 47 | Logger.Debug("Answering IPC call with (", ret.Send, ",", ret.Str, ")"); 48 | return ret.ToTuple(); 49 | } 50 | 51 | public void AsyncRemoteCallHandler(ulong msgId, byte[] data) 52 | { 53 | var ret = new Message(string.Empty, msgId); 54 | sm.AsyncAnswerOnRemoteCall(ret.Id, ret.ToTuple()); 55 | if (data.Length < 1) return; 56 | var str = ToString(data); 57 | Logger.Trace("Got IPC Message async:", str); 58 | } 59 | 60 | public Message MakeRemoteRequestWithResponse(Message message, int timeout = 100) { 61 | Logger.Trace("Awaiting sync IPC Message for:", message.Str); 62 | var data = sm.RemoteRequest(message.Bytes, null, timeout); 63 | var ret = new Message(data.Item2, data.Item1); 64 | Logger.Trace("Recieved sync IPC Response:", ret.Str); 65 | return ret; 66 | } 67 | /*public async Task MakeRemoteRequestWithResponseAsync(Message message) { 68 | Logger.Trace("Awaiting async IPC Message for:", message.Str); 69 | var data = await sm.RemoteRequestAsync(message.Bytes); 70 | var ret = new Message(data.Item2, data.Item1); 71 | Logger.Trace("Recieved async IPC Response:", ret.Str); 72 | return ret; 73 | }*/ 74 | 75 | public bool MakeRemoteRequestWithoutResponse(Message message) { 76 | Logger.Trace("Sending IPC Message:", message.Str); 77 | return sm.RemoteRequestWithoutResponse(message.Bytes); 78 | } 79 | 80 | private static string ToString(byte[] data) { 81 | if (data == null) return string.Empty; 82 | return Encoding.Default.GetString(data); 83 | } 84 | private static byte[] ToByteArray(string data) { 85 | if (string.IsNullOrEmpty(data)) return new byte[] {}; 86 | return Encoding.UTF8.GetBytes(data); 87 | } 88 | private static byte[] barray(byte[] data) 89 | { 90 | if (data == null) return new byte[] { }; 91 | return data; 92 | } 93 | public class Message { 94 | public bool Send; 95 | public byte[] Bytes = new byte[] { }; 96 | public string Str; 97 | public ulong Id; 98 | public Message(string data, bool send = true) { 99 | Send = send; Bytes = ToByteArray(data); Str = data; 100 | } 101 | public Message(byte[] data, bool send = true) { 102 | Send = send; Bytes = barray(data); Str = Launcher.ToString(data); 103 | } 104 | public Message(string data, ulong id) { 105 | Send = true; Bytes = ToByteArray(data); Str = data; ; Id = id; 106 | } 107 | public Message(byte[] data, ulong id) { 108 | Send = true; Bytes = barray(data); Str = Launcher.ToString(data); Id = id; 109 | } 110 | public Tuple ToTuple() { 111 | return new Tuple(Send, Bytes); 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /Launcher.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bluscream/VRChatLauncher/95d05ea24714912c6365676e9164644665cdf5a9/Launcher.ico -------------------------------------------------------------------------------- /Launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bluscream/VRChatLauncher/95d05ea24714912c6365676e9164644665cdf5a9/Launcher.png -------------------------------------------------------------------------------- /News/NewsItem.cs: -------------------------------------------------------------------------------- 1 | #region License Information (GPL v3) 2 | 3 | /* 4 | ShareX - A program that allows you to take screenshots and share any file type 5 | Copyright (c) 2007-2019 ShareX Team 6 | 7 | This program is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU General Public License 9 | as published by the Free Software Foundation; either version 2 10 | of the License, or (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | Optionally you can also view the license at . 22 | */ 23 | 24 | #endregion License Information (GPL v3) 25 | 26 | using Newtonsoft.Json; 27 | using System; 28 | 29 | namespace ShareX 30 | { 31 | public class NewsItem 32 | { 33 | public DateTime DateTime { get; set; } 34 | public string Text { get; set; } 35 | public string URL { get; set; } 36 | 37 | [JsonIgnore] 38 | public bool IsUnread { get; set; } 39 | } 40 | } -------------------------------------------------------------------------------- /News/NewsListControl.cs: -------------------------------------------------------------------------------- 1 | #region License Information (GPL v3) 2 | 3 | /* 4 | ShareX - A program that allows you to take screenshots and share any file type 5 | Copyright (c) 2007-2019 ShareX Team 6 | 7 | This program is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU General Public License 9 | as published by the Free Software Foundation; either version 2 10 | of the License, or (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | Optionally you can also view the license at . 22 | */ 23 | 24 | #endregion License Information (GPL v3) 25 | 26 | using ShareX.HelpersLib; 27 | using System; 28 | using System.Drawing; 29 | using System.Linq; 30 | using System.Threading.Tasks; 31 | using System.Windows.Forms; 32 | 33 | namespace ShareX 34 | { 35 | public partial class NewsListControl : UserControl 36 | { 37 | public event EventHandler NewsLoaded; 38 | 39 | public NewsManager NewsManager { get; private set; } 40 | 41 | public NewsListControl() 42 | { 43 | InitializeComponent(); 44 | dgvNews.AlternatingRowsDefaultCellStyle.BackColor = dgvNews.AlternatingRowsDefaultCellStyle.SelectionBackColor = 45 | ColorHelpers.DarkerColor(SystemColors.Window, 0.02f); 46 | dgvNews.GridColor = ProfessionalColors.SeparatorDark; 47 | dgvNews.DoubleBuffered(true); 48 | } 49 | 50 | public void Start() 51 | { 52 | Task.Run(() => 53 | { 54 | NewsManager = new NewsManager(); 55 | NewsManager.LastReadDate = Program.Settings.NewsLastReadDate; 56 | NewsManager.UpdateNews(); 57 | NewsManager.UpdateUnread(); 58 | }).ContinueInCurrentContext(() => 59 | { 60 | if (NewsManager != null && NewsManager.NewsItems != null) 61 | { 62 | SuspendLayout(); 63 | 64 | foreach (NewsItem item in NewsManager.NewsItems) 65 | { 66 | if (item != null) 67 | { 68 | AddNewsItem(item); 69 | } 70 | } 71 | 72 | UpdateUnreadStatus(); 73 | 74 | ResumeLayout(); 75 | 76 | OnNewsLoaded(); 77 | } 78 | }); 79 | } 80 | 81 | protected void OnNewsLoaded() 82 | { 83 | if (NewsLoaded != null) 84 | { 85 | NewsLoaded(this, EventArgs.Empty); 86 | } 87 | } 88 | 89 | public void MarkRead() 90 | { 91 | if (NewsManager != null && NewsManager.NewsItems != null && NewsManager.NewsItems.Count > 0) 92 | { 93 | DateTime latestDate = NewsManager.NewsItems.OrderByDescending(x => x.DateTime).First().DateTime; 94 | DateTime futureDate = DateTime.Now.AddMonths(1); 95 | 96 | if (latestDate < futureDate) 97 | { 98 | Program.Settings.NewsLastReadDate = NewsManager.LastReadDate = latestDate; 99 | NewsManager.UpdateUnread(); 100 | } 101 | } 102 | 103 | UpdateUnreadStatus(); 104 | } 105 | 106 | public void AddNewsItem(NewsItem item) 107 | { 108 | int index = dgvNews.Rows.Add(); 109 | DataGridViewRow row = dgvNews.Rows[index]; 110 | row.Tag = item; 111 | 112 | row.Cells[1].Value = item.DateTime.ToShortDateString(); 113 | 114 | string dateTimeTooltip; 115 | double days = (DateTime.Now - item.DateTime).TotalDays; 116 | 117 | if (days < 1) 118 | { 119 | dateTimeTooltip = "Today."; 120 | } 121 | else if (days < 2) 122 | { 123 | dateTimeTooltip = "Yesterday."; 124 | } 125 | else 126 | { 127 | dateTimeTooltip = (int)days + " days ago."; 128 | } 129 | 130 | row.Cells[1].ToolTipText = dateTimeTooltip; 131 | 132 | row.Cells[2].Value = item.Text; 133 | 134 | if (URLHelpers.IsValidURL(item.URL)) 135 | { 136 | row.Cells[2].ToolTipText = item.URL; 137 | } 138 | } 139 | 140 | private void UpdateUnreadStatus() 141 | { 142 | foreach (DataGridViewRow row in dgvNews.Rows) 143 | { 144 | NewsItem newsItem = row.Tag as NewsItem; 145 | if (newsItem != null && newsItem.IsUnread) 146 | { 147 | row.Cells[0].Style.BackColor = row.Cells[0].Style.SelectionBackColor = Color.LimeGreen; 148 | } 149 | else 150 | { 151 | row.Cells[0].Style = null; 152 | } 153 | } 154 | } 155 | 156 | private void dgvNews_CellMouseEnter(object sender, DataGridViewCellEventArgs e) 157 | { 158 | if (e.ColumnIndex == 2) 159 | { 160 | DataGridViewRow row = dgvNews.Rows[e.RowIndex]; 161 | NewsItem newsItem = row.Tag as NewsItem; 162 | if (newsItem != null && !string.IsNullOrEmpty(newsItem.URL)) 163 | { 164 | dgvNews.Cursor = Cursors.Hand; 165 | row.Cells[e.ColumnIndex].Style.ForeColor = row.Cells[e.ColumnIndex].Style.SelectionForeColor = SystemColors.HotTrack; 166 | } 167 | } 168 | } 169 | 170 | private void dgvNews_CellMouseLeave(object sender, DataGridViewCellEventArgs e) 171 | { 172 | if (e.ColumnIndex == 2) 173 | { 174 | DataGridViewRow row = dgvNews.Rows[e.RowIndex]; 175 | NewsItem newsItem = row.Tag as NewsItem; 176 | if (newsItem != null && !string.IsNullOrEmpty(newsItem.URL)) 177 | { 178 | row.Cells[e.ColumnIndex].Style.ForeColor = row.Cells[e.ColumnIndex].Style.SelectionForeColor = SystemColors.ControlText; 179 | } 180 | } 181 | 182 | dgvNews.Cursor = Cursors.Default; 183 | } 184 | 185 | private void dgvNews_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e) 186 | { 187 | if (e.Button == MouseButtons.Left && e.ColumnIndex == 2) 188 | { 189 | DataGridViewRow row = dgvNews.Rows[e.RowIndex]; 190 | NewsItem newsItem = row.Tag as NewsItem; 191 | if (newsItem != null && URLHelpers.IsValidURL(newsItem.URL)) 192 | { 193 | URLHelpers.OpenURL(newsItem.URL); 194 | } 195 | } 196 | } 197 | } 198 | } -------------------------------------------------------------------------------- /News/NewsListControl.designer.cs: -------------------------------------------------------------------------------- 1 | namespace ShareX 2 | { 3 | partial class NewsListControl 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle(); 32 | System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); 33 | System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle(); 34 | this.dgvNews = new System.Windows.Forms.DataGridView(); 35 | this.chIsUnread = new System.Windows.Forms.DataGridViewTextBoxColumn(); 36 | this.chDateTime = new System.Windows.Forms.DataGridViewTextBoxColumn(); 37 | this.chText = new System.Windows.Forms.DataGridViewTextBoxColumn(); 38 | ((System.ComponentModel.ISupportInitialize)(this.dgvNews)).BeginInit(); 39 | this.SuspendLayout(); 40 | // 41 | // dgvNews 42 | // 43 | this.dgvNews.AllowUserToAddRows = false; 44 | this.dgvNews.AllowUserToDeleteRows = false; 45 | this.dgvNews.AllowUserToResizeColumns = false; 46 | this.dgvNews.AllowUserToResizeRows = false; 47 | this.dgvNews.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells; 48 | this.dgvNews.BackgroundColor = System.Drawing.SystemColors.Window; 49 | this.dgvNews.BorderStyle = System.Windows.Forms.BorderStyle.None; 50 | this.dgvNews.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.SingleHorizontal; 51 | this.dgvNews.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; 52 | this.dgvNews.ColumnHeadersVisible = false; 53 | this.dgvNews.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { 54 | this.chIsUnread, 55 | this.chDateTime, 56 | this.chText}); 57 | dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; 58 | dataGridViewCellStyle3.BackColor = System.Drawing.SystemColors.Window; 59 | dataGridViewCellStyle3.Font = new System.Drawing.Font("Arial", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 60 | dataGridViewCellStyle3.ForeColor = System.Drawing.SystemColors.ControlText; 61 | dataGridViewCellStyle3.Padding = new System.Windows.Forms.Padding(5); 62 | dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Window; 63 | dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.ControlText; 64 | dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.False; 65 | this.dgvNews.DefaultCellStyle = dataGridViewCellStyle3; 66 | this.dgvNews.Dock = System.Windows.Forms.DockStyle.Fill; 67 | this.dgvNews.Location = new System.Drawing.Point(0, 0); 68 | this.dgvNews.Name = "dgvNews"; 69 | this.dgvNews.RowHeadersVisible = false; 70 | this.dgvNews.Size = new System.Drawing.Size(399, 363); 71 | this.dgvNews.TabIndex = 0; 72 | this.dgvNews.CellMouseClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.dgvNews_CellMouseClick); 73 | this.dgvNews.CellMouseEnter += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgvNews_CellMouseEnter); 74 | this.dgvNews.CellMouseLeave += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgvNews_CellMouseLeave); 75 | // 76 | // chIsUnread 77 | // 78 | this.chIsUnread.HeaderText = "IsUnread"; 79 | this.chIsUnread.Name = "chIsUnread"; 80 | this.chIsUnread.ReadOnly = true; 81 | this.chIsUnread.Width = 5; 82 | // 83 | // chDateTime 84 | // 85 | this.chDateTime.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; 86 | dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter; 87 | this.chDateTime.DefaultCellStyle = dataGridViewCellStyle1; 88 | this.chDateTime.HeaderText = "DateTime"; 89 | this.chDateTime.Name = "chDateTime"; 90 | this.chDateTime.ReadOnly = true; 91 | this.chDateTime.Width = 5; 92 | // 93 | // chText 94 | // 95 | this.chText.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; 96 | dataGridViewCellStyle2.Padding = new System.Windows.Forms.Padding(5); 97 | dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.True; 98 | this.chText.DefaultCellStyle = dataGridViewCellStyle2; 99 | this.chText.HeaderText = "Text"; 100 | this.chText.Name = "chText"; 101 | this.chText.ReadOnly = true; 102 | // 103 | // NewsListControl 104 | // 105 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 106 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 107 | this.BackColor = System.Drawing.SystemColors.Window; 108 | this.Controls.Add(this.dgvNews); 109 | this.Name = "NewsListControl"; 110 | this.Size = new System.Drawing.Size(399, 363); 111 | ((System.ComponentModel.ISupportInitialize)(this.dgvNews)).EndInit(); 112 | this.ResumeLayout(false); 113 | 114 | } 115 | 116 | #endregion 117 | 118 | private System.Windows.Forms.DataGridView dgvNews; 119 | private System.Windows.Forms.DataGridViewTextBoxColumn chIsUnread; 120 | private System.Windows.Forms.DataGridViewTextBoxColumn chDateTime; 121 | private System.Windows.Forms.DataGridViewTextBoxColumn chText; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /News/NewsListControl.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 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 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | True 122 | 123 | 124 | True 125 | 126 | 127 | True 128 | 129 | -------------------------------------------------------------------------------- /News/NewsManager.cs: -------------------------------------------------------------------------------- 1 | #region License Information (GPL v3) 2 | 3 | /* 4 | ShareX - A program that allows you to take screenshots and share any file type 5 | Copyright (c) 2007-2019 ShareX Team 6 | 7 | This program is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU General Public License 9 | as published by the Free Software Foundation; either version 2 10 | of the License, or (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | Optionally you can also view the license at . 22 | */ 23 | 24 | #endregion License Information (GPL v3) 25 | 26 | using Newtonsoft.Json; 27 | using ShareX.HelpersLib; 28 | using System; 29 | using System.Collections.Generic; 30 | using System.IO; 31 | using System.Linq; 32 | using System.Net; 33 | using System.Net.Cache; 34 | 35 | namespace ShareX 36 | { 37 | public class NewsManager 38 | { 39 | public List NewsItems { get; private set; } = new List(); 40 | public DateTime LastReadDate { get; set; } 41 | public bool IsUnread => UnreadCount > 0; 42 | public int UnreadCount => NewsItems != null ? NewsItems.Count(x => x.IsUnread) : 0; 43 | 44 | public void UpdateNews() 45 | { 46 | try 47 | { 48 | NewsItems = GetNews(); 49 | } 50 | catch (Exception e) 51 | { 52 | DebugHelper.WriteException(e); 53 | } 54 | } 55 | 56 | public void UpdateUnread() 57 | { 58 | if (NewsItems != null) 59 | { 60 | foreach (NewsItem newsItem in NewsItems) 61 | { 62 | newsItem.IsUnread = newsItem.DateTime > LastReadDate; 63 | } 64 | } 65 | } 66 | 67 | private List GetNews() 68 | { 69 | using (WebClient wc = new WebClient()) 70 | { 71 | wc.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore); 72 | wc.Headers.Add(HttpRequestHeader.UserAgent, ShareXResources.UserAgent); 73 | wc.Proxy = HelpersOptions.CurrentProxy.GetWebProxy(); 74 | 75 | string url = URLHelpers.CombineURL(Links.URL_WEBSITE, "news.json"); 76 | string response = wc.DownloadString(url); 77 | 78 | if (!string.IsNullOrEmpty(response)) 79 | { 80 | JsonSerializerSettings settings = new JsonSerializerSettings 81 | { 82 | DateTimeZoneHandling = DateTimeZoneHandling.Local 83 | }; 84 | 85 | return JsonConvert.DeserializeObject>(response, settings); 86 | } 87 | } 88 | 89 | return null; 90 | } 91 | 92 | private void ExportNews(List newsItems) 93 | { 94 | JsonSerializerSettings settings = new JsonSerializerSettings 95 | { 96 | DateTimeZoneHandling = DateTimeZoneHandling.Utc, 97 | Formatting = Formatting.Indented, 98 | NullValueHandling = NullValueHandling.Ignore 99 | }; 100 | 101 | string json = JsonConvert.SerializeObject(newsItems, settings); 102 | File.WriteAllText("news.json", json); 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Windows.Forms; 6 | using VRChatLauncher.Utils; 7 | 8 | namespace VRChatLauncher 9 | { 10 | public static class Program 11 | { 12 | public static Main mainWindow; 13 | public static IPC.Launcher ipc; 14 | public static Arguments Arguments; 15 | [STAThread] 16 | static void Main(string[] args) 17 | { 18 | AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnProcessExit); 19 | Arguments = Arguments.FromArgs(args.ToList()); 20 | Logger.Init(); 21 | Logger.Trace("START"); 22 | ipc = new IPC.Launcher(); ipc.Init(); 23 | // var args = Environment.GetCommandLineArgs().Skip(1).ToArray(); 24 | /*if (args.Length > 0) Logger.Warn("Catched command line arguments:"); 25 | for (int i = 0; i < args.Length; i++) 26 | { 27 | Logger.Warn($"[{i}]", args[i]); 28 | }*/ 29 | // Logger.Warn("Parsed Command Line Arguments", prog.Arguments.ToJson()); 30 | var msg = ipc.MakeRemoteRequestWithResponse(new IPC.Launcher.Message($"islauncherrunning {string.Join(" ", args)}"), 200); 31 | var launcher_running = Utils.Utils.IsLauncherAlreadyRunning(); 32 | var keep_open = Arguments.Launcher.KeepOpen.IsTrue; 33 | /*if (keep_open) { 34 | var firstAfter = args.SkipWhile(p => p != "--vrclauncher.keep").ElementAt(1); 35 | Logger.Warn("firstAfter", firstAfter); 36 | }*/ 37 | Logger.Log("Launcher already running:", launcher_running.ToString()); 38 | if ((msg.Str == "yes" || launcher_running) && !keep_open) { 39 | Logger.Fatal("Launcher already running, exiting..."); 40 | Utils.Utils.Exit();return; 41 | } 42 | Logger.ClearLog(); 43 | var game_running = Game.IsGameAlreadyRunning(); 44 | Logger.Log("Game already running:", game_running.ToString()); 45 | if(!game_running) { 46 | if (Arguments.Launcher.Skip.IsTrue) { 47 | Logger.Warn("Skiplauncher is set, tunneling directly..."); 48 | Game.StartGame(args: args); 49 | Utils.Utils.Exit(); 50 | } 51 | } 52 | Application.EnableVisualStyles(); 53 | Application.SetCompatibleTextRenderingDefault(false); 54 | mainWindow = new Main(); 55 | Application.Run(mainWindow); 56 | Logger.Trace("END"); 57 | OnProcessExit(false, new EventArgs()); 58 | } 59 | 60 | static void OnProcessExit(object sender, EventArgs e) 61 | { 62 | Logger.Log("Exiting..."); 63 | // LogReader.Dispose(); 64 | // IPC.Launcher.Dispose(); 65 | ExternalConsole.Dispose(); 66 | Application.Exit(); 67 | Process.GetCurrentProcess().Kill(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTitle("VRChat Launcher")] 10 | [assembly: AssemblyDescription("Launcher for VRChat")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("Bluscream")] 13 | [assembly: AssemblyProduct("VRChatLauncher")] 14 | [assembly: AssemblyCopyright("Copyright © 2019")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | 18 | // Setting ComVisible to false makes the types in this assembly not visible 19 | // to COM components. If you need to access a type in this assembly from 20 | // COM, set the ComVisible attribute to true on that type. 21 | [assembly: ComVisible(false)] 22 | 23 | // The following GUID is for the ID of the typelib if this project is exposed to COM 24 | [assembly: Guid("9a87a994-89f6-44f2-8224-3c27420c7998")] 25 | 26 | // Version information for an assembly consists of the following four values: 27 | // 28 | // Major Version 29 | // Minor Version 30 | // Build Number 31 | // Revision 32 | // 33 | // You can specify all the values or you can default the Build and Revision Numbers 34 | // by using the '*' as shown below: 35 | // [assembly: AssemblyVersion("1.0.*")] 36 | [assembly: AssemblyVersion("0.0.1.0")] 37 | [assembly: AssemblyFileVersion("1.0.0.0")] 38 | [assembly: NeutralResourcesLanguage("en")] 39 | 40 | -------------------------------------------------------------------------------- /Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace VRChatLauncher.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("VRChatLauncher.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized resource of type System.Drawing.Bitmap. 65 | /// 66 | internal static System.Drawing.Bitmap vrctoolspng { 67 | get { 68 | object obj = ResourceManager.GetObject("vrctoolspng", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 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 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\Resources\vrctools.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VRChatLauncher [![Build status](https://ci.appveyor.com/api/projects/status/v63b5slm5hdltwc2?svg=true)](https://ci.appveyor.com/project/Bluscream/vrchatlauncher) ![](https://img.shields.io/github/downloads/Bluscream/VRChatLauncher/total.svg) [![Discord](https://img.shields.io/discord/439093693769711616?label=Discord)](https://discord.gg/wyVh2vh) 2 | 3 | ## This program is deprecated, please use [VRCX](https://github.com/pypy-vrc/VRCX/) 4 | 5 | 8 | 9 |
10 | Screenshots 11 | 12 | ![Home/News](https://i.imgur.com/HCtxy1i.png) 13 | 14 | ![Users](https://i.imgur.com/jWkZ436.png) 15 | 16 | ![Avatars](https://i.imgur.com/3TthwXb.png) 17 | 18 | ![Worlds](https://i.imgur.com/leifwr4.png) 19 | 20 | ![Mods](https://i.imgur.com/sqoWBPM.png) 21 | 22 | ![Settings](https://i.imgur.com/5wg2lim.png) 23 | 24 | ![Logs](https://i.imgur.com/w5C1sDM.png) 25 | 26 |
27 | -------------------------------------------------------------------------------- /Resources/vrctools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bluscream/VRChatLauncher/95d05ea24714912c6365676e9164644665cdf5a9/Resources/vrctools.png -------------------------------------------------------------------------------- /Setup/Game.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Windows.Forms; 3 | using VRChatLauncher.Utils; 4 | 5 | namespace VRChatLauncher.Setup 6 | { 7 | class Game 8 | { 9 | public static bool CheckForGame() 10 | { 11 | // Logger.Trace("Start"); 12 | var gamePath = Utils.Utils.getGamePath(); 13 | Logger.Debug("Checking if", gamePath.FullName, "exists..."); 14 | return gamePath.Exists; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Setup/Mods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace VRChatLauncher.Setup 9 | { 10 | class Mods 11 | { 12 | public static bool IsModLoaderInstalled() 13 | { 14 | return (IsVRCModLoaderInstalled() || IsVRLoaderInstalled()); 15 | } 16 | public static FileInfo VRCModLoaderDLL() 17 | { 18 | var gamePath = Utils.Utils.getGamePath().DirectoryName; 19 | var dllPath = Path.Combine(gamePath, "VRChat_Data", "Managed", "VRCModLoader.dll"); 20 | return new FileInfo(dllPath); 21 | } 22 | public static bool IsVRCModLoaderInstalled() 23 | { 24 | return VRCModLoaderDLL().Exists; 25 | } 26 | 27 | public static FileInfo VRLoaderDLL() 28 | { 29 | var gamePath = Utils.Utils.getGamePath().DirectoryName; 30 | var dllPath = Path.Combine(gamePath, "VRLoader.dll"); 31 | return new FileInfo(dllPath); 32 | } 33 | public static bool IsVRLoaderInstalled() 34 | { 35 | return VRLoaderDLL().Exists; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Setup/URI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Windows.Forms; 4 | using Microsoft.Win32; 5 | using VRChatLauncher.Utils; 6 | 7 | namespace VRChatLauncher.Setup 8 | { 9 | class URI 10 | { 11 | public const string key = @"HKEY_CLASSES_ROOT\VRChat\shell\open\command"; 12 | public static URIResponse CheckURIRegistryKey() 13 | { 14 | var ret = new URIResponse(URIResponse.URIEnum.UNKNOWN, "", ""); 15 | ret.key = (string)Registry.GetValue(key, null, null); 16 | if (ret.key == null) return ret; 17 | // Logger.Trace("key=", ret.key); 18 | var steamFile =Path.Combine(Utils.Utils.getOwnPath().DirectoryName, "launch.bat"); 19 | var expected_steam = $@"""{steamFile}"" ""%1"""; // ""{ownPath}"" 20 | // Logger.Trace("expected_steam=", expected_steam); 21 | if (expected_steam == ret.key) { ret.match = URIResponse.URIEnum.DEFAULT; return ret; } 22 | ret.expected = $@"""{Utils.Utils.getOwnPath().FullName}"" ""%1"""; // ""{ownPath}"" 23 | // Logger.Trace("expected=", ret.expected); 24 | if (ret.key != ret.expected) ret.match = URIResponse.URIEnum.WRONG; 25 | else ret.match = URIResponse.URIEnum.INSTALLED; 26 | Logger.Debug(ret.ToJson()); 27 | // "G:\Steam\steamapps\common\VRChat\launch.bat" "G:\Steam\steamapps\common\VRChat" "%1" 28 | return ret; 29 | } 30 | public static bool InstallURI(string val = null) 31 | { 32 | if (val is null) 33 | { 34 | var ownPath = Path.GetDirectoryName(Application.ExecutablePath); 35 | var ownFile = Path.GetFullPath(Application.ExecutablePath); 36 | val = $@"""{ownFile}"" ""{ownPath}"" ""%1"""; 37 | } 38 | Logger.Debug(val); 39 | try { Registry.SetValue(key, null, val); 40 | } catch (Exception) { return false; } 41 | return true; 42 | } 43 | } 44 | public class URIResponse { 45 | public URIResponse(URIEnum item1, string item2, string item3) { 46 | match = item1; expected = item2; key = item3; 47 | } 48 | public URIEnum match { get; set; } 49 | public string expected { get; set; } 50 | public string key { get; set; } 51 | public enum URIEnum { 52 | INSTALLED, DEFAULT, WRONG, UNKNOWN 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Setup/VRCAPI/LoginModal.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace VRChatLauncher.Setup.VRCAPI 2 | { 3 | partial class LoginModal 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(LoginModal)); 32 | this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); 33 | this.label2 = new System.Windows.Forms.Label(); 34 | this.txt_password = new System.Windows.Forms.TextBox(); 35 | this.label1 = new System.Windows.Forms.Label(); 36 | this.txt_username = new System.Windows.Forms.TextBox(); 37 | this.btn_login = new System.Windows.Forms.Button(); 38 | this.btn_cancel = new System.Windows.Forms.Button(); 39 | this.tableLayoutPanel1.SuspendLayout(); 40 | this.SuspendLayout(); 41 | // 42 | // tableLayoutPanel1 43 | // 44 | this.tableLayoutPanel1.ColumnCount = 2; 45 | this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 37.5F)); 46 | this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 62.5F)); 47 | this.tableLayoutPanel1.Controls.Add(this.label2, 0, 1); 48 | this.tableLayoutPanel1.Controls.Add(this.txt_password, 1, 1); 49 | this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0); 50 | this.tableLayoutPanel1.Controls.Add(this.txt_username, 1, 0); 51 | this.tableLayoutPanel1.Controls.Add(this.btn_login, 0, 2); 52 | this.tableLayoutPanel1.Controls.Add(this.btn_cancel, 1, 2); 53 | this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; 54 | this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); 55 | this.tableLayoutPanel1.Name = "tableLayoutPanel1"; 56 | this.tableLayoutPanel1.RowCount = 3; 57 | this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 38.46154F)); 58 | this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 38.46154F)); 59 | this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 23.07693F)); 60 | this.tableLayoutPanel1.Size = new System.Drawing.Size(365, 110); 61 | this.tableLayoutPanel1.TabIndex = 0; 62 | // 63 | // label2 64 | // 65 | this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); 66 | this.label2.AutoSize = true; 67 | this.label2.Location = new System.Drawing.Point(3, 56); 68 | this.label2.Name = "label2"; 69 | this.label2.Size = new System.Drawing.Size(130, 13); 70 | this.label2.TabIndex = 2; 71 | this.label2.Text = "Password:"; 72 | // 73 | // txt_password 74 | // 75 | this.txt_password.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); 76 | this.txt_password.Location = new System.Drawing.Point(139, 53); 77 | this.txt_password.Name = "txt_password"; 78 | this.txt_password.Size = new System.Drawing.Size(223, 20); 79 | this.txt_password.TabIndex = 3; 80 | // 81 | // label1 82 | // 83 | this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); 84 | this.label1.AutoSize = true; 85 | this.label1.Location = new System.Drawing.Point(3, 14); 86 | this.label1.Name = "label1"; 87 | this.label1.Size = new System.Drawing.Size(130, 13); 88 | this.label1.TabIndex = 0; 89 | this.label1.Text = "Username:"; 90 | // 91 | // txt_username 92 | // 93 | this.txt_username.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); 94 | this.txt_username.Location = new System.Drawing.Point(139, 11); 95 | this.txt_username.Name = "txt_username"; 96 | this.txt_username.Size = new System.Drawing.Size(223, 20); 97 | this.txt_username.TabIndex = 1; 98 | // 99 | // btn_login 100 | // 101 | this.btn_login.Dock = System.Windows.Forms.DockStyle.Fill; 102 | this.btn_login.Location = new System.Drawing.Point(3, 87); 103 | this.btn_login.Name = "btn_login"; 104 | this.btn_login.Size = new System.Drawing.Size(130, 20); 105 | this.btn_login.TabIndex = 4; 106 | this.btn_login.Text = "Login"; 107 | this.btn_login.UseVisualStyleBackColor = true; 108 | this.btn_login.Click += new System.EventHandler(this.Btn_login_Click); 109 | // 110 | // btn_cancel 111 | // 112 | this.btn_cancel.Dock = System.Windows.Forms.DockStyle.Fill; 113 | this.btn_cancel.Location = new System.Drawing.Point(139, 87); 114 | this.btn_cancel.Name = "btn_cancel"; 115 | this.btn_cancel.Size = new System.Drawing.Size(223, 20); 116 | this.btn_cancel.TabIndex = 5; 117 | this.btn_cancel.Text = "Cancel"; 118 | this.btn_cancel.UseVisualStyleBackColor = true; 119 | this.btn_cancel.Click += new System.EventHandler(this.Btn_cancel_Click); 120 | // 121 | // LoginModal 122 | // 123 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 124 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 125 | this.ClientSize = new System.Drawing.Size(365, 110); 126 | this.Controls.Add(this.tableLayoutPanel1); 127 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 128 | this.Name = "LoginModal"; 129 | this.Text = "Login to VRChat"; 130 | this.tableLayoutPanel1.ResumeLayout(false); 131 | this.tableLayoutPanel1.PerformLayout(); 132 | this.ResumeLayout(false); 133 | 134 | } 135 | 136 | #endregion 137 | 138 | private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; 139 | private System.Windows.Forms.Label label2; 140 | private System.Windows.Forms.Label label1; 141 | private System.Windows.Forms.Button btn_login; 142 | private System.Windows.Forms.Button btn_cancel; 143 | public System.Windows.Forms.TextBox txt_username; 144 | public System.Windows.Forms.TextBox txt_password; 145 | } 146 | } -------------------------------------------------------------------------------- /Setup/VRCAPI/LoginModal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | 11 | namespace VRChatLauncher.Setup.VRCAPI 12 | { 13 | public partial class LoginModal : Form 14 | { 15 | public LoginModal() 16 | { 17 | InitializeComponent(); 18 | } 19 | 20 | private void Btn_login_Click(object sender, EventArgs e) 21 | { 22 | // Login(txt_username.Text, txt_password.Text); 23 | this.Close(); 24 | } 25 | 26 | private void Btn_cancel_Click(object sender, EventArgs e) 27 | { 28 | this.Close(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /UI/Chat.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace VRChatLauncher.UI 2 | { 3 | partial class ChatWindow 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ChatWindow)); 32 | this.panel1 = new System.Windows.Forms.Panel(); 33 | this.btn_send = new System.Windows.Forms.Button(); 34 | this.txt_chat_input = new System.Windows.Forms.TextBox(); 35 | this.tabs_chat = new System.Windows.Forms.TabControl(); 36 | this.panel1.SuspendLayout(); 37 | this.SuspendLayout(); 38 | // 39 | // panel1 40 | // 41 | this.panel1.Controls.Add(this.btn_send); 42 | this.panel1.Controls.Add(this.txt_chat_input); 43 | this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom; 44 | this.panel1.Location = new System.Drawing.Point(0, 275); 45 | this.panel1.Name = "panel1"; 46 | this.panel1.Size = new System.Drawing.Size(560, 27); 47 | this.panel1.TabIndex = 0; 48 | // 49 | // btn_send 50 | // 51 | this.btn_send.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 52 | | System.Windows.Forms.AnchorStyles.Right))); 53 | this.btn_send.Location = new System.Drawing.Point(464, 3); 54 | this.btn_send.Name = "btn_send"; 55 | this.btn_send.Size = new System.Drawing.Size(93, 23); 56 | this.btn_send.TabIndex = 1; 57 | this.btn_send.Text = "Send"; 58 | this.btn_send.UseVisualStyleBackColor = true; 59 | this.btn_send.Click += new System.EventHandler(this.Btn_send_Click); 60 | // 61 | // txt_chat_input 62 | // 63 | this.txt_chat_input.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 64 | | System.Windows.Forms.AnchorStyles.Left) 65 | | System.Windows.Forms.AnchorStyles.Right))); 66 | this.txt_chat_input.Location = new System.Drawing.Point(3, 3); 67 | this.txt_chat_input.Multiline = true; 68 | this.txt_chat_input.Name = "txt_chat_input"; 69 | this.txt_chat_input.Size = new System.Drawing.Size(455, 20); 70 | this.txt_chat_input.TabIndex = 0; 71 | this.txt_chat_input.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Txt_chat_input_KeyPress); 72 | // 73 | // tabs_chat 74 | // 75 | this.tabs_chat.Dock = System.Windows.Forms.DockStyle.Fill; 76 | this.tabs_chat.Location = new System.Drawing.Point(0, 0); 77 | this.tabs_chat.Name = "tabs_chat"; 78 | this.tabs_chat.SelectedIndex = 0; 79 | this.tabs_chat.Size = new System.Drawing.Size(560, 275); 80 | this.tabs_chat.TabIndex = 0; 81 | this.tabs_chat.Selected += new System.Windows.Forms.TabControlEventHandler(this.Tabs_chat_Selected); 82 | // 83 | // Chat 84 | // 85 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 86 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 87 | this.ClientSize = new System.Drawing.Size(560, 302); 88 | this.Controls.Add(this.tabs_chat); 89 | this.Controls.Add(this.panel1); 90 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 91 | this.Name = "Chat"; 92 | this.Text = "Chat"; 93 | this.panel1.ResumeLayout(false); 94 | this.panel1.PerformLayout(); 95 | this.ResumeLayout(false); 96 | 97 | } 98 | 99 | #endregion 100 | 101 | private System.Windows.Forms.Panel panel1; 102 | private System.Windows.Forms.Button btn_send; 103 | private System.Windows.Forms.TextBox txt_chat_input; 104 | private System.Windows.Forms.TabControl tabs_chat; 105 | } 106 | } -------------------------------------------------------------------------------- /UI/Chat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | using VRChatApi.Classes; 11 | using VRChatLauncher.Utils; 12 | 13 | namespace VRChatLauncher.UI 14 | { 15 | public partial class ChatWindow : Form 16 | { 17 | private UserResponse me; private VRChatApi.VRChatApi vrcapi; 18 | public ChatWindow(VRChatApi.VRChatApi Vrcapi, UserResponse Me) 19 | { 20 | me = Me; vrcapi = Vrcapi; 21 | InitializeComponent(); 22 | } 23 | public void AddChat(UserBriefResponse user) 24 | { 25 | BringUp(); 26 | var chatId = $"chat_{user.id}"; 27 | if (tabs_chat.TabPages.ContainsKey(chatId)) 28 | { 29 | tabs_chat.SelectedIndex = tabs_chat.TabPages.IndexOfKey(chatId); 30 | return; 31 | } 32 | var tab = new TabPage(); 33 | tab.Text = user.displayName; 34 | tab.Name = chatId; 35 | tab.Tag = new TabTag(user, DateTime.Now, TargetType.User); 36 | tab.TabIndex = tabs_chat.TabPages.Count; 37 | tab.MouseClick += Tab_MouseClick; 38 | var lst_messages = new ListBox(); 39 | lst_messages.Dock = DockStyle.Fill; 40 | lst_messages.FormattingEnabled = true; 41 | lst_messages.Location = new Point(3, 3); 42 | // lst_messages.Name = 43 | lst_messages.Size = new Size(546, 243); 44 | tab.Controls.Add(lst_messages); 45 | tabs_chat.Controls.Add(tab); 46 | tabs_chat.SelectTab(tab.TabIndex); // Select Tab 47 | this.Text = $"Chat with {user.displayName}"; 48 | txt_chat_input.Focus(); 49 | var tag = (TabTag)tab.Tag; 50 | LogLine(chatId, tag.SessionStart, $"Chat session with {user.displayName.Quote()} started."); 51 | } 52 | 53 | private void BringUp() 54 | { 55 | if (WindowState == FormWindowState.Minimized) { 56 | WindowState = FormWindowState.Normal; 57 | } else { 58 | TopMost = true; 59 | Focus(); 60 | BringToFront(); 61 | TopMost = false; 62 | } 63 | } 64 | 65 | private void Tab_MouseClick(object sender, MouseEventArgs e) 66 | { 67 | Logger.Debug("Tab_MouseClick"); 68 | if (e.Button == MouseButtons.Middle) { 69 | Logger.Debug(e.Button); 70 | var tab = (TabPage)sender; 71 | Logger.Debug(tab.Name); 72 | tabs_chat.TabPages.RemoveByKey(tab.Name); 73 | } 74 | } 75 | 76 | private void Txt_chat_input_KeyPress(object sender, KeyEventArgs e) 77 | { 78 | if (e.KeyCode == Keys.Enter) 79 | { 80 | var chatId = tabs_chat.SelectedTab.Name; 81 | SendMessageAsync(chatId, txt_chat_input.Text); 82 | txt_chat_input.Clear(); 83 | e.Handled = true; 84 | } 85 | } 86 | private void Btn_send_Click(object sender, EventArgs e) 87 | { 88 | var chatId = tabs_chat.SelectedTab.Name; 89 | SendMessageAsync(chatId, txt_chat_input.Text); 90 | txt_chat_input.Clear(); 91 | } 92 | private async void SendMessageAsync(string chatId, string message) 93 | { 94 | if (string.IsNullOrWhiteSpace(message)) { return; } 95 | var timestamp = DateTime.Now; 96 | var fullmessage = $"| Chat Message from \"{me.displayName}\"\nAt: {timestamp}\n{message}"; 97 | var notification = await vrcapi.FriendsApi.SendMessage(chatId.Replace("chat_", ""), fullmessage, "Chatting provided by github.com/Bluscream/VRCLauncher"); 98 | Logger.Debug(notification); 99 | var line = $"{me.displayName}: {message}"; 100 | LogLine(chatId, timestamp, line); 101 | } 102 | private void LogLine(string chatId, DateTime dateTime, string text) 103 | { 104 | if (string.IsNullOrWhiteSpace(text)) { return; } 105 | string timestamp = dateTime.ToString("HH:mm:ss.fff", System.Globalization.CultureInfo.InvariantCulture); 106 | var line = $"[{timestamp}] {text}"; 107 | var chatList = ChatById(chatId).MessageList; 108 | chatList.Items.Add(line); 109 | } 110 | 111 | private Chat ChatById(string chatId) 112 | { 113 | var chatIndex = tabs_chat.TabPages.IndexOfKey(chatId); 114 | var tabTag = tabs_chat.TabPages[chatIndex].Tag; 115 | var messageList = tabs_chat.TabPages[chatIndex].Controls[0]; 116 | return new Chat(tabs_chat.TabPages[chatIndex], (TabTag)tabTag, (ListBox)messageList); 117 | } 118 | 119 | private void Tabs_chat_Selected(object sender, TabControlEventArgs e) 120 | { 121 | var tag = (TabTag)e.TabPage.Tag; 122 | this.Text = $"Chat with {tag.Target.displayName}"; 123 | } 124 | private class Chat 125 | { 126 | public TabPage Tab { get; set; } 127 | public TabTag Tag { get; set; } 128 | public ListBox MessageList { get; set; } 129 | public Chat(TabPage tab, TabTag tag, ListBox messageList) 130 | { 131 | Tab = tab; Tag = tag; MessageList = messageList; 132 | } 133 | } 134 | public enum TargetType 135 | { 136 | Unknown, User, Group, Instance, World, Global 137 | } 138 | public class TabTag 139 | { 140 | public TargetType Type { get; set; } 141 | public UserBriefResponse Target { get; set; } 142 | public DateTime SessionStart { get; set; } 143 | public TabTag(UserBriefResponse target, DateTime sessionStart, TargetType type = TargetType.Unknown) 144 | { 145 | Target = target; SessionStart = sessionStart; Type = type; 146 | } 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /UI/CommandLineArguments.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace VRChatLauncher.UI 2 | { 3 | partial class CommandLineArguments 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CommandLineArguments)); 32 | this.lst_arguments = new System.Windows.Forms.ListBox(); 33 | this.panel1 = new System.Windows.Forms.Panel(); 34 | this.btn_start = new System.Windows.Forms.Button(); 35 | this.txt_launch_path = new System.Windows.Forms.TextBox(); 36 | this.panel1.SuspendLayout(); 37 | this.SuspendLayout(); 38 | // 39 | // lst_arguments 40 | // 41 | this.lst_arguments.Dock = System.Windows.Forms.DockStyle.Top; 42 | this.lst_arguments.FormattingEnabled = true; 43 | this.lst_arguments.Location = new System.Drawing.Point(0, 0); 44 | this.lst_arguments.Name = "lst_arguments"; 45 | this.lst_arguments.Size = new System.Drawing.Size(800, 420); 46 | this.lst_arguments.TabIndex = 0; 47 | this.lst_arguments.SelectedIndexChanged += new System.EventHandler(this.Lst_arguments_SelectedIndexChanged); 48 | // 49 | // panel1 50 | // 51 | this.panel1.Controls.Add(this.txt_launch_path); 52 | this.panel1.Controls.Add(this.btn_start); 53 | this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom; 54 | this.panel1.Location = new System.Drawing.Point(0, 426); 55 | this.panel1.Name = "panel1"; 56 | this.panel1.Size = new System.Drawing.Size(800, 24); 57 | this.panel1.TabIndex = 1; 58 | // 59 | // btn_start 60 | // 61 | this.btn_start.Dock = System.Windows.Forms.DockStyle.Right; 62 | this.btn_start.Location = new System.Drawing.Point(725, 0); 63 | this.btn_start.Name = "btn_start"; 64 | this.btn_start.Size = new System.Drawing.Size(75, 24); 65 | this.btn_start.TabIndex = 0; 66 | this.btn_start.Text = "Start"; 67 | this.btn_start.UseVisualStyleBackColor = true; 68 | this.btn_start.Click += new System.EventHandler(this.Btn_start_Click); 69 | // 70 | // txt_launch_path 71 | // 72 | this.txt_launch_path.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 73 | | System.Windows.Forms.AnchorStyles.Left) 74 | | System.Windows.Forms.AnchorStyles.Right))); 75 | this.txt_launch_path.Location = new System.Drawing.Point(3, 1); 76 | this.txt_launch_path.Name = "txt_launch_path"; 77 | this.txt_launch_path.Size = new System.Drawing.Size(716, 20); 78 | this.txt_launch_path.TabIndex = 1; 79 | // 80 | // CommandLineArguments 81 | // 82 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 83 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 84 | this.ClientSize = new System.Drawing.Size(800, 450); 85 | this.Controls.Add(this.panel1); 86 | this.Controls.Add(this.lst_arguments); 87 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 88 | this.Name = "CommandLineArguments"; 89 | this.Text = "VRCLauncher - Command Line Arguments"; 90 | this.panel1.ResumeLayout(false); 91 | this.panel1.PerformLayout(); 92 | this.ResumeLayout(false); 93 | 94 | } 95 | 96 | #endregion 97 | 98 | private System.Windows.Forms.ListBox lst_arguments; 99 | private System.Windows.Forms.Panel panel1; 100 | private System.Windows.Forms.Button btn_start; 101 | private System.Windows.Forms.TextBox txt_launch_path; 102 | } 103 | } -------------------------------------------------------------------------------- /UI/CommandLineArguments.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | 11 | namespace VRChatLauncher.UI 12 | { 13 | public partial class CommandLineArguments : Form 14 | { 15 | public CommandLineArguments(string gamePath, string[] arguments) 16 | { 17 | InitializeComponent(); 18 | txt_launch_path.Text = gamePath; 19 | FillList(arguments); 20 | } 21 | 22 | private void FillList(string[] args) { 23 | lst_arguments.Items.Clear(); 24 | foreach (var arg in args) { 25 | lst_arguments.Items.Add(arg); 26 | } 27 | } 28 | 29 | private void Lst_arguments_SelectedIndexChanged(object sender, EventArgs e) 30 | { 31 | 32 | } 33 | 34 | private void Btn_start_Click(object sender, EventArgs e) 35 | { 36 | this.DialogResult = DialogResult.OK; 37 | this.Close(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /UI/InputBox.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Forms; 2 | using System.Drawing; 3 | using System; 4 | 5 | namespace VRChatLauncher.UI 6 | { 7 | class Utils 8 | { 9 | public static DialogResult InputBox(string title, string promptText, ref string value, bool multiline = false) 10 | { 11 | Form form = new Form(); 12 | Label label = new Label(); 13 | TextBox textBox = new TextBox(); 14 | Button buttonOk = new Button(); 15 | Button buttonCancel = new Button(); 16 | 17 | form.Text = title; 18 | label.Text = promptText; 19 | textBox.Text = value; 20 | if (multiline) textBox.Multiline = true; 21 | 22 | buttonOk.Text = "OK"; 23 | buttonCancel.Text = "Cancel"; 24 | buttonOk.DialogResult = DialogResult.OK; 25 | buttonCancel.DialogResult = DialogResult.Cancel; 26 | 27 | label.SetBounds(10, 20, 370, 15); 28 | if (multiline) 29 | { 30 | var btn_vertical = 130; 31 | buttonOk.SetBounds(230, btn_vertical, 75, 25); 32 | buttonCancel.SetBounds(310, btn_vertical, 75, 25); 33 | textBox.SetBounds(10, 35, 370, 90); 34 | } 35 | else 36 | { 37 | buttonOk.SetBounds(230, 70, 75, 25); 38 | buttonCancel.SetBounds(310, 70, 75, 25); 39 | textBox.SetBounds(10, 35, 370, 20); 40 | } 41 | 42 | label.AutoSize = true; 43 | textBox.Anchor = textBox.Anchor | AnchorStyles.Right; 44 | buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; 45 | buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; 46 | 47 | if (multiline) form.ClientSize = new Size(400, 160); 48 | else form.ClientSize = new Size(400, 100); 49 | form.Controls.AddRange(new Control[] { label, textBox, buttonOk, buttonCancel }); 50 | form.ClientSize = new Size(Math.Max(300, label.Right + 10), form.ClientSize.Height); 51 | form.FormBorderStyle = FormBorderStyle.FixedDialog; 52 | form.StartPosition = FormStartPosition.CenterScreen; 53 | form.MinimizeBox = false; 54 | form.MaximizeBox = false; 55 | form.AcceptButton = buttonOk; 56 | form.CancelButton = buttonCancel; 57 | 58 | DialogResult dialogResult = form.ShowDialog(); 59 | value = textBox.Text; 60 | textBox.Focus(); 61 | return dialogResult; 62 | } 63 | 64 | } 65 | } -------------------------------------------------------------------------------- /UI/InviteDialog.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace VRChatLauncher.UI 2 | { 3 | partial class InviteDialog 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(InviteDialog)); 32 | this.box_id = new System.Windows.Forms.ComboBox(); 33 | this.txt_name = new System.Windows.Forms.TextBox(); 34 | this.label2 = new System.Windows.Forms.Label(); 35 | this.panel1 = new System.Windows.Forms.Panel(); 36 | this.panel2 = new System.Windows.Forms.Panel(); 37 | this.label3 = new System.Windows.Forms.Label(); 38 | this.btn_send = new System.Windows.Forms.Button(); 39 | this.panel3 = new System.Windows.Forms.Panel(); 40 | this.panel4 = new System.Windows.Forms.Panel(); 41 | this.panel1.SuspendLayout(); 42 | this.panel2.SuspendLayout(); 43 | this.panel3.SuspendLayout(); 44 | this.panel4.SuspendLayout(); 45 | this.SuspendLayout(); 46 | // 47 | // box_id 48 | // 49 | this.box_id.Dock = System.Windows.Forms.DockStyle.Right; 50 | this.box_id.FormattingEnabled = true; 51 | this.box_id.Location = new System.Drawing.Point(116, 0); 52 | this.box_id.Name = "box_id"; 53 | this.box_id.Size = new System.Drawing.Size(366, 21); 54 | this.box_id.TabIndex = 0; 55 | this.box_id.SelectionChangeCommitted += new System.EventHandler(this.Box_id_SelectionChangeCommitted); 56 | this.box_id.TextChanged += new System.EventHandler(this.Box_id_TextChanged); 57 | // 58 | // txt_name 59 | // 60 | this.txt_name.Dock = System.Windows.Forms.DockStyle.Right; 61 | this.txt_name.Location = new System.Drawing.Point(116, 0); 62 | this.txt_name.Name = "txt_name"; 63 | this.txt_name.Size = new System.Drawing.Size(366, 20); 64 | this.txt_name.TabIndex = 2; 65 | // 66 | // label2 67 | // 68 | this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); 69 | this.label2.AutoSize = true; 70 | this.label2.Location = new System.Drawing.Point(3, 4); 71 | this.label2.Name = "label2"; 72 | this.label2.Size = new System.Drawing.Size(52, 13); 73 | this.label2.TabIndex = 3; 74 | this.label2.Text = "World ID:"; 75 | // 76 | // panel1 77 | // 78 | this.panel1.Controls.Add(this.label2); 79 | this.panel1.Controls.Add(this.txt_name); 80 | this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom; 81 | this.panel1.Location = new System.Drawing.Point(0, 30); 82 | this.panel1.Name = "panel1"; 83 | this.panel1.Size = new System.Drawing.Size(482, 21); 84 | this.panel1.TabIndex = 4; 85 | // 86 | // panel2 87 | // 88 | this.panel2.Controls.Add(this.label3); 89 | this.panel2.Controls.Add(this.box_id); 90 | this.panel2.Dock = System.Windows.Forms.DockStyle.Top; 91 | this.panel2.Location = new System.Drawing.Point(0, 0); 92 | this.panel2.Name = "panel2"; 93 | this.panel2.Size = new System.Drawing.Size(482, 26); 94 | this.panel2.TabIndex = 5; 95 | // 96 | // label3 97 | // 98 | this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); 99 | this.label3.AutoSize = true; 100 | this.label3.Location = new System.Drawing.Point(3, 7); 101 | this.label3.Name = "label3"; 102 | this.label3.Size = new System.Drawing.Size(69, 13); 103 | this.label3.TabIndex = 3; 104 | this.label3.Text = "World Name:"; 105 | // 106 | // btn_send 107 | // 108 | this.btn_send.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 109 | this.btn_send.Location = new System.Drawing.Point(404, 5); 110 | this.btn_send.Name = "btn_send"; 111 | this.btn_send.Size = new System.Drawing.Size(75, 23); 112 | this.btn_send.TabIndex = 6; 113 | this.btn_send.Text = "Send"; 114 | this.btn_send.UseVisualStyleBackColor = true; 115 | this.btn_send.Click += new System.EventHandler(this.Btn_send_Click); 116 | // 117 | // panel3 118 | // 119 | this.panel3.Controls.Add(this.btn_send); 120 | this.panel3.Dock = System.Windows.Forms.DockStyle.Bottom; 121 | this.panel3.Location = new System.Drawing.Point(0, 54); 122 | this.panel3.Name = "panel3"; 123 | this.panel3.Size = new System.Drawing.Size(482, 31); 124 | this.panel3.TabIndex = 7; 125 | // 126 | // panel4 127 | // 128 | this.panel4.Controls.Add(this.panel2); 129 | this.panel4.Controls.Add(this.panel1); 130 | this.panel4.Dock = System.Windows.Forms.DockStyle.Top; 131 | this.panel4.Location = new System.Drawing.Point(0, 0); 132 | this.panel4.Name = "panel4"; 133 | this.panel4.Size = new System.Drawing.Size(482, 51); 134 | this.panel4.TabIndex = 8; 135 | // 136 | // InviteDialog 137 | // 138 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 139 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 140 | this.ClientSize = new System.Drawing.Size(482, 85); 141 | this.Controls.Add(this.panel3); 142 | this.Controls.Add(this.panel4); 143 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 144 | this.Name = "InviteDialog"; 145 | this.Text = "Invite"; 146 | this.panel1.ResumeLayout(false); 147 | this.panel1.PerformLayout(); 148 | this.panel2.ResumeLayout(false); 149 | this.panel2.PerformLayout(); 150 | this.panel3.ResumeLayout(false); 151 | this.panel4.ResumeLayout(false); 152 | this.ResumeLayout(false); 153 | 154 | } 155 | 156 | #endregion 157 | 158 | private System.Windows.Forms.ComboBox box_id; 159 | private System.Windows.Forms.TextBox txt_name; 160 | private System.Windows.Forms.Label label2; 161 | private System.Windows.Forms.Panel panel1; 162 | private System.Windows.Forms.Panel panel2; 163 | private System.Windows.Forms.Label label3; 164 | private System.Windows.Forms.Button btn_send; 165 | private System.Windows.Forms.Panel panel3; 166 | private System.Windows.Forms.Panel panel4; 167 | } 168 | } -------------------------------------------------------------------------------- /UI/InviteDialog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | using VRChatApi.Classes; 11 | using VRChatLauncher.Utils; 12 | 13 | namespace VRChatLauncher.UI 14 | { 15 | public partial class InviteDialog : Form 16 | { 17 | public class InviteDialogResult 18 | { 19 | public DialogResult DialogResult; 20 | public bool Continue; 21 | public string WorldId; 22 | public string WorldName; 23 | } 24 | 25 | public InviteDialogResult Result = new InviteDialogResult(); 26 | 27 | public static InviteDialogResult Get(string title = "", List worlds = null) 28 | { 29 | var mi = new InviteDialog(title, worlds); 30 | var diag = mi.ShowDialog(); 31 | /*InviteDialogResult Result = new InviteDialogResult(); 32 | Result.DialogResult = diag; 33 | Result.Continue = (diag == DialogResult.OK); 34 | Result.WorldName = mi.txt_name.Text; 35 | Result.WorldId = mi.box_id.Text;*/ 36 | return mi.Result; 37 | } 38 | 39 | public List Worlds; 40 | 41 | public class World 42 | { 43 | public string Name; 44 | public string Id; 45 | public World(string id, string name = "") { 46 | Id = id; 47 | if (!string.IsNullOrEmpty(name)) { Name = name.Trim(); } 48 | } 49 | public World fromWorldResponse(WorldBriefResponse worldResponse) { 50 | return new World(worldResponse.id, worldResponse.name); 51 | } 52 | public override string ToString() { 53 | return Name; 54 | } 55 | } 56 | 57 | public InviteDialog(string title, List worlds) 58 | { 59 | InitializeComponent(); 60 | Text = title; 61 | Worlds = worlds; 62 | foreach (var world in worlds) { 63 | box_id.Items.Add(world); 64 | } 65 | } 66 | 67 | private void Btn_cancel_Click(object sender, EventArgs e) 68 | { 69 | DialogResult = DialogResult.Cancel; 70 | Close(); 71 | } 72 | 73 | private void Btn_ok_Click(object sender, EventArgs e) 74 | { 75 | } 76 | 77 | private void accepted() 78 | { 79 | DialogResult = DialogResult.OK; 80 | Result.DialogResult = DialogResult; 81 | Result.Continue = true; 82 | Result.WorldId = txt_name.Text; 83 | Result.WorldName = box_id.Text; 84 | Close(); 85 | } 86 | 87 | private void Btn_send_Click(object sender, EventArgs e) 88 | { 89 | accepted(); 90 | } 91 | 92 | private bool BoxChanged = false; 93 | private void Box_id_SelectionChangeCommitted(object sender, EventArgs e) { 94 | BoxChanged = true; 95 | } 96 | 97 | private void Box_id_TextChanged(object sender, EventArgs e) 98 | { 99 | if (BoxChanged) 100 | { 101 | BoxChanged = false; 102 | var curtext = box_id.Text; 103 | Logger.Warn(2, curtext); 104 | var world = Worlds.Find(w => w.Name == curtext); 105 | Logger.Warn(3, world); 106 | if (world != null) 107 | { 108 | Logger.Warn("id:", world.Id, "name:", world.Name); 109 | Logger.Warn("box_id:", box_id.Text, "txt_name:", txt_name.Text); 110 | txt_name.Text = world.Id; 111 | Logger.Warn("box_id:", box_id.Text, "txt_name:", txt_name.Text); 112 | } 113 | } 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /UI/Main/Avatars.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Windows.Forms; 5 | using VRChatLauncher.Utils; 6 | using VRChatApi.Classes; 7 | using System.IO; 8 | 9 | namespace VRChatLauncher 10 | { 11 | partial class Main 12 | { 13 | public static List favorite_avatars;public static List personal_avatars; 14 | public static bool avatars_loading = false; 15 | 16 | private Color ColorFromReleaseStatus(string releaseStatus) 17 | { 18 | switch (releaseStatus) 19 | { 20 | case "private": 21 | return Color.OrangeRed; 22 | case "hidden": 23 | return Color.Gray; 24 | case "public": 25 | return Color.Green; 26 | default: 27 | return Color.Orange; 28 | } 29 | } 30 | private Color ColorFromReleaseStatus(ReleaseStatus releaseStatus) 31 | { 32 | switch (releaseStatus) 33 | { 34 | case ReleaseStatus.Public: 35 | return Color.Green; 36 | case ReleaseStatus.Private: 37 | return Color.OrangeRed; 38 | case ReleaseStatus.Hidden: 39 | return Color.Gray; 40 | default: 41 | return Color.Orange; 42 | } 43 | } 44 | 45 | public async void SetupAvatarsAsync(bool force = false) { 46 | if (avatars_loading) { Logger.Warn("Avatars are already loading, try again later"); return; } 47 | avatars_loading = true; 48 | tree_avatars.Nodes[0].Nodes.Clear(); 49 | tree_avatars.Nodes[1].Nodes.Clear(); 50 | tree_avatars.Nodes[2].Nodes.Clear(); 51 | if (personal_avatars == null || force) { 52 | personal_avatars = await vrcapi.AvatarApi.Personal(); 53 | Logger.Log("Downloaded list of", personal_avatars.Count, "official personal avatars"); 54 | } 55 | foreach (var avatar in personal_avatars) 56 | { 57 | var node = new TreeNode(avatar.name); 58 | node.Tag = new TreeNodeTag(type: NodeType.Avatar, id: avatar.id, avatar); 59 | node.ForeColor = ColorFromReleaseStatus(avatar.releaseStatus); 60 | tree_avatars.Nodes[0].Nodes.Add(node); 61 | } 62 | tree_avatars.Nodes[0].Text = $"Personal ({tree_avatars.Nodes[0].Nodes.Count})"; 63 | 64 | if (favorite_avatars == null || force) { 65 | favorite_avatars = await vrcapi.AvatarApi.Favorites(); 66 | Logger.Log("Downloaded list of", favorite_avatars.Count, "official favorite avatars"); 67 | } 68 | foreach (var avatar in favorite_avatars) 69 | { 70 | var node = new TreeNode(avatar.name); 71 | node.Tag = new TreeNodeTag(type: NodeType.Avatar, id: avatar.id, avatar); 72 | node.ForeColor = ColorFromReleaseStatus(avatar.releaseStatus); 73 | tree_avatars.Nodes[1].Nodes.Add(node); 74 | } 75 | tree_avatars.Nodes[1].Text = $"Official ({tree_avatars.Nodes[1].Nodes.Count} / 16)"; 76 | avatars_loading = false; 77 | } 78 | private void FillAvatar(AvatarResponse avatar) 79 | { 80 | txt_avatar_id.Text = avatar.id; 81 | txt_avatar_id.Tag = avatar; 82 | txt_avatar_name.Text = avatar.name; 83 | txt_avatar_version.Text = avatar.version.ToString(); 84 | txt_avatar_author.Text = $"{avatar.authorName} ({avatar.authorId})"; 85 | txt_avatar_asseturl.Text = avatar.assetUrl; 86 | txt_avatar_description.Text = avatar.description; 87 | } 88 | 89 | private void avatars_node_selected(object sender, TreeNodeMouseClickEventArgs e) 90 | { 91 | if (e.Node.Tag == null) return; 92 | var tag = (TreeNodeTag)e.Node.Tag; 93 | if (tag.Type != NodeType.Avatar) return; 94 | FillAvatar(tag.avatarResponse); 95 | } 96 | 97 | private void Btn_avatars_reload_Click(object sender, EventArgs e) 98 | { 99 | SetupAvatarsAsync(force: true); 100 | } 101 | 102 | private async void Btn_avatar_search_ClickAsync(object sender, EventArgs e) 103 | { 104 | var avatar = await vrcapi.AvatarApi.GetById(txt_avatar_id.Text); 105 | FillAvatar(avatar); 106 | } 107 | 108 | private async void Btn_avatars_profile_ClickAsync(object sender, EventArgs e) 109 | { 110 | var avatar = (AvatarResponse)txt_avatar_id.Tag; 111 | var user = await vrcapi.UserApi.GetById(avatar.authorId); 112 | FillUser(user); 113 | tabs_main.SelectTab(1); 114 | } 115 | 116 | // private List tmpAvatars; 117 | private void Btn_avatar_rip_Click(object sender, EventArgs e) 118 | { 119 | var avatar = (AvatarResponse)txt_avatar_id.Tag; 120 | if (avatar == null) return; 121 | FileInfo ripper; 122 | if (string.IsNullOrEmpty(config["Paths"]["Ripper"])) 123 | { 124 | var confirmResult = MessageBox.Show("You don't have set up a ripper yet, you can select one like uTinyRipper.exe that supports \"ripper.exe %file%\" if you click on OK", "Ripper not found!", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning); 125 | if (confirmResult == DialogResult.Cancel) return; 126 | var fileSelector = new OpenFileDialog(); 127 | fileSelector.Title = "Select the ripper to use"; 128 | fileSelector.Filter = "uTinyRipper|uTinyRipper.exe|All Executables|*.exe"; 129 | if (fileSelector.ShowDialog() == DialogResult.OK) 130 | { 131 | ripper = new FileInfo(fileSelector.FileName); 132 | config["Paths"]["Ripper"] = ripper.FullName; 133 | Config.Save(config); 134 | } 135 | } 136 | ripper = new FileInfo(config["Paths"]["Ripper"]); 137 | var tmpPath = new DirectoryInfo(Path.GetTempPath()); 138 | Logger.Log("Ripping avatar ", avatar.name, avatar.id.Enclose(), "to", tmpPath.FullName.Quote()+avatar.name.Ext("vrca")); 139 | var file = Utils.Utils.DownloadFile(avatar.assetUrl, tmpPath, avatar.name.Ext("vrca")); 140 | var proc = Utils.Utils.StartProcess(ripper, file.FullName.Quote()); 141 | // proc.Exited += Proc_Exited; 142 | } 143 | /*private void Proc_Exited(object sender, EventArgs e) { 144 | Logger.Trace("Process has exited", sender, e); 145 | var file = tmpAvatars.PopAt(0); 146 | file.Delete(); 147 | Logger.Log("Temp avatar file ", file.Name, "has been deleted"); 148 | }*/ 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /UI/Main/Log.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Forms; 2 | using VRChatLauncher.Utils; 3 | 4 | namespace VRChatLauncher 5 | { 6 | partial class Main 7 | { 8 | public static ListView selflog; 9 | public static ListView gamelog; 10 | public LogReader logReader; 11 | 12 | public void WriteGameLog(string message) { 13 | if (string.IsNullOrWhiteSpace(message)) return; 14 | if (lst_log_game.Items.Count > 500) lst_log_game.Items.RemoveAt(0); 15 | lst_log_game.Items.Add(message); 16 | } 17 | 18 | public void WriteLauncherLog(string message) 19 | { 20 | if (string.IsNullOrWhiteSpace(message)) return; 21 | if (lst_log_launcher.Items.Count > 200) lst_log_launcher.Items.RemoveAt(0); 22 | lst_log_launcher.Items.Add(message); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /UI/Main/Mods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Windows.Forms; 8 | using VRChatLauncher.Utils; 9 | using VRCModManager.Core; 10 | using static VRChatLauncher.Utils.Mods; 11 | 12 | namespace VRChatLauncher 13 | { 14 | partial class Main 15 | { 16 | public static List mods; 17 | 18 | public void SetupMods(bool force = false) 19 | { 20 | if (!force && !Setup.Mods.IsModLoaderInstalled()) 21 | { 22 | var confirmResult = MessageBox.Show("No modloader was found so we will warn you that everything you do beyond this message can lead to bans.\n\nUse mods responsibly!", "No Modloader installed", MessageBoxButtons.OKCancel, MessageBoxIcon.Stop); 23 | if (confirmResult != DialogResult.OK) { tabs_main.SelectTab(0); return; } 24 | } 25 | if (mods == null || force) { 26 | mods = GetMods(); 27 | mods.OrderBy(x => x.Name); 28 | CheckForUpdates(mods); 29 | if (Setup.Mods.IsVRCModLoaderInstalled()) { 30 | /* Skip 31 | Mod VRCModLoader = Updater.VRCModLoader.CheckForUpdate(); 32 | if (VRCModLoader.Update != null) MessageBox.Show("Update available for VRCModLoader"); 33 | */ 34 | } 35 | using (var wc = new WebClient()) 36 | { 37 | FetchUserCounts(wc); 38 | } 39 | } 40 | 41 | lst_mods.Clear(); 42 | mods = mods.OrderBy(o=>o.Name).ToList(); 43 | foreach (var mod in mods) 44 | { 45 | var item = new ListViewItem(); 46 | item.Tag = mod; 47 | // ImageList imageList = new ImageList(); 48 | // imageList.Images.Add(Properties.Resources.vrctoolspng); 49 | switch (mod.Type) 50 | { 51 | case ModLoaderType.VRCModloader: 52 | item.Text = mod.Name; 53 | item.ToolTipText = mod.File.FileNameWithoutExtension(); 54 | item.ImageIndex = 0;break; 55 | case ModLoaderType.VRLoader: 56 | item.Text = mod.Name; 57 | item.ToolTipText = mod.File.FileNameWithoutExtension(); 58 | break; 59 | case ModLoaderType.Unknown: 60 | default: 61 | item.Text = mod.File.FileNameWithoutExtension(); 62 | item.ForeColor = Color.Red; 63 | break; 64 | } 65 | if(!mod.Enabled) item.ForeColor = Color.Gray; 66 | lst_mods.Items.Add(item); 67 | } 68 | tabs_mods.TabPages[0].Text = $"Installed ({mods.Count})"; 69 | } 70 | 71 | private void mods_tab_changed(object sender, TabControlEventArgs e) 72 | { 73 | Logger.Trace(e.Action.ToString(), e.TabPage.Name, e.TabPageIndex.ToString()); 74 | switch (e.TabPage.Name) 75 | { 76 | case "tab_mods_installed": 77 | break; 78 | case "tab_mods_available": 79 | SetupAvailableMods(); 80 | break; 81 | default: 82 | break; 83 | } 84 | } 85 | 86 | bool finishedLoadingAvailable = false; 87 | 88 | private void SetupAvailableMods() 89 | { 90 | VRCModManager.FormMain embeddedForm = new VRCModManager.FormMain(); 91 | embeddedForm.TopLevel = false; 92 | embeddedForm.FormBorderStyle = FormBorderStyle.None; 93 | embeddedForm.Dock = DockStyle.Fill; 94 | tabs_mods.TabPages[1].Controls.Add(embeddedForm); 95 | embeddedForm.Show(); 96 | finishedLoadingAvailable = true; 97 | } 98 | 99 | int lastModIndex = 0; 100 | private void on_list_mods_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) 101 | { 102 | if (lastModIndex == e.ItemIndex) return; 103 | lastModIndex = e.ItemIndex; 104 | Mod mod = (Mod)e.Item.Tag; 105 | txt_mod_path.Text = mod.File.FullName; 106 | txt_mod_name.Text = mod.Name; 107 | txt_mod_version.Text = mod.Version; 108 | txt_mod_author.Text = mod.Author; 109 | txt_mod_type.Text = mod.Type.ToString(); 110 | if (mod.Error != null) txt_mod_type.Text += $" ({mod.Error})"; 111 | txt_mod_description.Text = mod.Description; 112 | } 113 | 114 | private void on_btn_mods_refresh_Click(object sender, EventArgs e) { 115 | SetupMods(force: true); 116 | } 117 | 118 | private void Menu_mod_Opening(object sender, System.ComponentModel.CancelEventArgs e) 119 | { 120 | if (lst_mods.SelectedItems.Count < 1) { e.Cancel = true; return; } 121 | var mod = (Mod)lst_mods.SelectedItems[0].Tag; 122 | if (mod == null) { e.Cancel = true; return; } 123 | menu_mod.Items[1].Text = mod.Enabled ? "Disable" : "Enable"; 124 | } 125 | 126 | private void ToggleModToolStripMenuItem_Click(object sender, EventArgs e) 127 | { 128 | var mod = (Mod)lst_mods.SelectedItems[0].Tag; 129 | if (mod == null) return; 130 | Logger.Debug(mod.ToJson()); 131 | if (mod.Enabled) 132 | { 133 | var confirmResult = MessageBox.Show($"Mod {mod.Name} will be moved to the Mods/Disabled/ folder, Are you sure?", "Disable mod?", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); 134 | if (confirmResult != DialogResult.OK) { return; } 135 | mod = DisableMod(mod); 136 | if (mod.Enabled) MessageBox.Show($"Failed to disable mod {mod.Name}, check logs/console!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 137 | } else { 138 | mod = EnableMod(mod); 139 | if (!mod.Enabled) MessageBox.Show($"Failed to enable mod {mod.Name}, check logs/console!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 140 | } 141 | SetupMods(true); 142 | } 143 | 144 | private void DeleteModToolStripMenuItem_Click(object sender, EventArgs e) 145 | { 146 | var mod = (Mod)lst_mods.SelectedItems[0].Tag; 147 | if (mod == null) return; 148 | var confirmResult = MessageBox.Show($"Mod {mod.Name} will be deleted, Are you sure?", "Disable mod?", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning); 149 | if (confirmResult != DialogResult.OK) { return; } 150 | mod.File.Delete(); 151 | SetupMods(true); 152 | } 153 | 154 | private void DecompileModToolStripMenuItem_Click(object sender, EventArgs e) 155 | { 156 | var mod = (Mod)lst_mods.SelectedItems[0].Tag; 157 | if (mod == null) return; 158 | FileInfo file; 159 | if (string.IsNullOrEmpty(config["Paths"]["Decompiler"])) 160 | { 161 | var confirmResult = MessageBox.Show("You don't have set up a decompiler yet, you can select one like dnSpy or ILSpy that supports \"compiler.exe %file%\" if you click on OK", "Decompiler not found!", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning); 162 | if (confirmResult == DialogResult.Cancel) return; 163 | var fileSelector = new OpenFileDialog(); 164 | fileSelector.Title = "Select the decompiler to use"; 165 | fileSelector.Filter = "dnSpy|dnSpy.exe|ILSpy|ILSpy.exe|All Executables|*.exe"; 166 | if (fileSelector.ShowDialog() == DialogResult.OK) 167 | { 168 | file = new FileInfo(fileSelector.FileName); 169 | config["Paths"]["Decompiler"] = file.FullName; 170 | Config.Save(config); 171 | } 172 | } 173 | file = new FileInfo(config["Paths"]["Decompiler"]); 174 | Utils.Utils.StartProcess(file, mod.File.FullName.Quote()); 175 | } 176 | 177 | private void OpenFolderToolStripMenuItem_Click(object sender, EventArgs e) 178 | { 179 | var mod = (Mod)lst_mods.SelectedItems[0].Tag; 180 | if (mod == null) return; 181 | Utils.Utils.ShowFileInExplorer(mod.File); 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /UI/Main/News.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Net; 4 | 5 | namespace VRChatLauncher 6 | { 7 | partial class Main 8 | { 9 | private const string newsurl = "https://github.com/Bluscream/VRChatLauncher/raw/master/News.rtf"; 10 | public void LoadNews() { 11 | try { 12 | 13 | using (var wc = new WebClient()) 14 | { 15 | SetNews(wc.DownloadString(newsurl)); 16 | } 17 | } catch (Exception ex) { 18 | Utils.Logger.Error(ex); 19 | SetNews("Could not load news, more infos in the console/log file."); 20 | } 21 | } 22 | public class NewsItem 23 | { 24 | public Notify Notify { get; set; } 25 | public string Author { get; set; } 26 | public string Title { get; set; } 27 | public string Description { get; set; } 28 | public string Url { get; set; } 29 | [JsonProperty(PropertyName = "timestamp")] 30 | public string time_stamp { get; set; } 31 | [JsonIgnore] 32 | public DateTime TimeStamp { get { return Convert.ToDateTime(time_stamp); } } 33 | } 34 | public class Notify { 35 | [JsonProperty(PropertyName = "add_to_feed")] 36 | public bool AddToFeed { get; set; } 37 | [JsonProperty(PropertyName = "show_notification")] 38 | public bool ShowNotification { get; set; } 39 | [JsonProperty(PropertyName = "show_modal")] 40 | public bool ShowModal { get; set; } 41 | [JsonProperty(PropertyName = "show_modal_while_game_running")] 42 | public bool ShowModalWhileGameRunning { get; set; } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /UI/Main/Settings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows.Forms; 4 | using VRChatLauncher.Utils; 5 | 6 | namespace VRChatLauncher 7 | { 8 | partial class Main 9 | { 10 | public bool settingsInitialized = false; 11 | 12 | public void SetupSettings() { 13 | flow_settings.Controls.Clear(); 14 | foreach (var section in config.Sections) 15 | { 16 | var group = new GroupBox(); 17 | group.Text = section.SectionName; 18 | group.AutoSizeMode = AutoSizeMode.GrowOnly; 19 | var table = new TableLayoutPanel(); 20 | table.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 40.00000F)); 21 | table.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 60.00000F)); 22 | table.Dock = DockStyle.Fill; 23 | // table.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F)); 24 | table.AutoSize = true; 25 | table.ColumnCount = section.Keys.Count; 26 | group.Controls.Add(table); 27 | var i = 0; 28 | foreach (var setting in section.Keys) 29 | { 30 | var label = new Label(); 31 | label.Text = setting.KeyName + ":"; 32 | label.AutoSize = true; 33 | label.Anchor = (AnchorStyles.Left); 34 | label.Dock = DockStyle.Fill; 35 | label.TabIndex = 0; 36 | table.Controls.Add(label, 0, i); 37 | var txt = new TextBox(); 38 | txt.Text = setting.Value; 39 | txt.AutoSize = true; 40 | txt.Anchor = (AnchorStyles.Right | AnchorStyles.Left); 41 | txt.Dock = DockStyle.Fill; 42 | // txt.Anchor = AnchorStyles.Right; 43 | txt.TabIndex = 1; 44 | table.Controls.Add(txt, 1, i); 45 | i++; 46 | } 47 | flow_settings.Controls.Add(group); 48 | // group.AutoSize = true; 49 | } 50 | } 51 | 52 | private void Btn_config_apply_Click(object sender, EventArgs e) // Todo: Clean up this clusterfuck 53 | { 54 | var sections = new Dictionary>>(); 55 | foreach (var _section in (flow_settings.Controls)) { 56 | if (!(_section is GroupBox)) continue; 57 | var section = (GroupBox)_section; 58 | if (!sections.ContainsKey(section.Text)) { 59 | sections.Add(section.Text, new Dictionary>()); 60 | } 61 | var table = (TableLayoutPanel)section.Controls[0]; 62 | var rowCount = table.Controls.Count / 2; 63 | // var rows = new Dictionary>(); 64 | var lastKey = ""; 65 | foreach (Control _setting in table.Controls) 66 | { 67 | var row = table.GetRow(_setting) + 1; 68 | if (!sections[section.Text].ContainsKey(row)) { 69 | sections[section.Text].Add(row, new Dictionary()); 70 | } 71 | if ((_setting is Label)) { 72 | var setting = (Label)_setting; 73 | var key = setting.Text.EndsWith(":") ? setting.Text.ReplaceLastOccurrence(":", "") : setting.Text; 74 | sections[section.Text][row].Add(key, ""); 75 | lastKey = key; 76 | } else if (_setting is TextBox) { 77 | var setting = (TextBox)_setting; 78 | var val = setting.Text; 79 | sections[section.Text][row][lastKey] = val; 80 | } 81 | } 82 | } 83 | Logger.Trace(sections.ToJson()); 84 | foreach (var section in sections) { 85 | foreach (var _row in sections[section.Key]) 86 | { 87 | foreach (var row in _row.Value) 88 | { 89 | config[section.Key][row.Key] = row.Value; 90 | } 91 | } 92 | } 93 | } 94 | 95 | private void Btn_config_save_Click(object sender, EventArgs e) { 96 | Config.Save(config); 97 | Logger.Log("Saved config to", Config.DefaultConfigFile.Quote(), $"{config.Sections.Count} sections".Enclose()); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /UI/MultilineInput.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace VRChatLauncher.UI 2 | { 3 | partial class MultilineInput 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MultilineInput)); 32 | this.btn_cancel = new System.Windows.Forms.Button(); 33 | this.btn_ok = new System.Windows.Forms.Button(); 34 | this.txt_input = new System.Windows.Forms.TextBox(); 35 | this.panel_buttons = new System.Windows.Forms.Panel(); 36 | this.panel_buttons.SuspendLayout(); 37 | this.SuspendLayout(); 38 | // 39 | // btn_cancel 40 | // 41 | this.btn_cancel.Dock = System.Windows.Forms.DockStyle.Left; 42 | this.btn_cancel.Location = new System.Drawing.Point(0, 0); 43 | this.btn_cancel.Name = "btn_cancel"; 44 | this.btn_cancel.Size = new System.Drawing.Size(75, 33); 45 | this.btn_cancel.TabIndex = 0; 46 | this.btn_cancel.Text = "Cancel"; 47 | this.btn_cancel.UseVisualStyleBackColor = true; 48 | this.btn_cancel.Click += new System.EventHandler(this.Btn_cancel_Click); 49 | // 50 | // btn_ok 51 | // 52 | this.btn_ok.Dock = System.Windows.Forms.DockStyle.Right; 53 | this.btn_ok.Location = new System.Drawing.Point(358, 0); 54 | this.btn_ok.Name = "btn_ok"; 55 | this.btn_ok.Size = new System.Drawing.Size(75, 33); 56 | this.btn_ok.TabIndex = 1; 57 | this.btn_ok.Text = "OK"; 58 | this.btn_ok.UseVisualStyleBackColor = true; 59 | this.btn_ok.Click += new System.EventHandler(this.Btn_ok_Click); 60 | // 61 | // txt_input 62 | // 63 | this.txt_input.Dock = System.Windows.Forms.DockStyle.Fill; 64 | this.txt_input.Location = new System.Drawing.Point(0, 0); 65 | this.txt_input.Multiline = true; 66 | this.txt_input.Name = "txt_input"; 67 | this.txt_input.Size = new System.Drawing.Size(433, 244); 68 | this.txt_input.TabIndex = 1; 69 | this.txt_input.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Txt_input_KeyDown); 70 | this.txt_input.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.Txt_input_KeyPress); 71 | // 72 | // panel_buttons 73 | // 74 | this.panel_buttons.Controls.Add(this.btn_ok); 75 | this.panel_buttons.Controls.Add(this.btn_cancel); 76 | this.panel_buttons.Dock = System.Windows.Forms.DockStyle.Bottom; 77 | this.panel_buttons.Location = new System.Drawing.Point(0, 211); 78 | this.panel_buttons.Name = "panel_buttons"; 79 | this.panel_buttons.Size = new System.Drawing.Size(433, 33); 80 | this.panel_buttons.TabIndex = 2; 81 | // 82 | // MultilineInput 83 | // 84 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 85 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 86 | this.ClientSize = new System.Drawing.Size(433, 244); 87 | this.Controls.Add(this.panel_buttons); 88 | this.Controls.Add(this.txt_input); 89 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 90 | this.Name = "MultilineInput"; 91 | this.panel_buttons.ResumeLayout(false); 92 | this.ResumeLayout(false); 93 | this.PerformLayout(); 94 | 95 | } 96 | 97 | #endregion 98 | 99 | private System.Windows.Forms.Button btn_cancel; 100 | private System.Windows.Forms.Button btn_ok; 101 | private System.Windows.Forms.TextBox txt_input; 102 | private System.Windows.Forms.Panel panel_buttons; 103 | } 104 | } -------------------------------------------------------------------------------- /UI/MultilineInput.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | 11 | namespace VRChatLauncher.UI 12 | { 13 | public partial class MultilineInput : Form 14 | { 15 | private string Value; 16 | 17 | public static string Get(string title = "", string placeholder = "") 18 | { 19 | var mi = new MultilineInput(title, placeholder); 20 | if (mi.ShowDialog() == DialogResult.OK) 21 | return mi.Value; 22 | return null; 23 | } 24 | 25 | public MultilineInput(string title, string placeholder) 26 | { 27 | InitializeComponent(); 28 | Text = title; 29 | txt_input.Text = placeholder; 30 | } 31 | 32 | private void Btn_cancel_Click(object sender, EventArgs e) 33 | { 34 | DialogResult = DialogResult.Cancel; 35 | Close(); 36 | } 37 | 38 | private void Btn_ok_Click(object sender, EventArgs e) 39 | { 40 | accepted(); 41 | } 42 | 43 | private void accepted() 44 | { 45 | DialogResult = DialogResult.OK; 46 | Value = txt_input.Text; 47 | Close(); 48 | } 49 | 50 | private bool keydowncalled = false; 51 | private void Txt_input_KeyDown(object sender, KeyEventArgs e) 52 | { 53 | keydowncalled = false; 54 | if (e.KeyData == (Keys.Control | Keys.Enter)) 55 | { 56 | keydowncalled = true; 57 | } 58 | } 59 | 60 | private void Txt_input_KeyPress(object sender, KeyPressEventArgs e) 61 | { 62 | if (keydowncalled == true) 63 | { 64 | keydowncalled = false; 65 | e.Handled = true; 66 | accepted(); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /UI/OperationsPanel.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace VRChatLauncher.UI 2 | { 3 | partial class OperationsPanel 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(OperationsPanel)); 32 | this.btn_startstop = new MaterialSkin.Controls.MaterialRaisedButton(); 33 | this.lst_operation = new System.Windows.Forms.ListView(); 34 | this.progress_operation = new System.Windows.Forms.ProgressBar(); 35 | this.grp_settings = new System.Windows.Forms.GroupBox(); 36 | this.panel1 = new System.Windows.Forms.Panel(); 37 | this.panel2 = new System.Windows.Forms.Panel(); 38 | this.materialSingleLineTextField1 = new MaterialSkin.Controls.MaterialSingleLineTextField(); 39 | this.panel3 = new System.Windows.Forms.Panel(); 40 | this.panel1.SuspendLayout(); 41 | this.panel2.SuspendLayout(); 42 | this.panel3.SuspendLayout(); 43 | this.SuspendLayout(); 44 | // 45 | // btn_startstop 46 | // 47 | this.btn_startstop.Depth = 0; 48 | this.btn_startstop.Location = new System.Drawing.Point(444, 3); 49 | this.btn_startstop.MouseState = MaterialSkin.MouseState.HOVER; 50 | this.btn_startstop.Name = "btn_startstop"; 51 | this.btn_startstop.Primary = true; 52 | this.btn_startstop.Size = new System.Drawing.Size(109, 52); 53 | this.btn_startstop.TabIndex = 0; 54 | this.btn_startstop.Text = "Start"; 55 | this.btn_startstop.TextAlign = System.Drawing.ContentAlignment.MiddleRight; 56 | this.btn_startstop.UseVisualStyleBackColor = true; 57 | this.btn_startstop.Click += new System.EventHandler(this.Btn_startstop_Click); 58 | // 59 | // lst_operation 60 | // 61 | this.lst_operation.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 62 | | System.Windows.Forms.AnchorStyles.Left) 63 | | System.Windows.Forms.AnchorStyles.Right))); 64 | this.lst_operation.Location = new System.Drawing.Point(10, 3); 65 | this.lst_operation.Name = "lst_operation"; 66 | this.lst_operation.Size = new System.Drawing.Size(549, 151); 67 | this.lst_operation.TabIndex = 1; 68 | this.lst_operation.UseCompatibleStateImageBehavior = false; 69 | // 70 | // progress_operation 71 | // 72 | this.progress_operation.Dock = System.Windows.Forms.DockStyle.Top; 73 | this.progress_operation.Location = new System.Drawing.Point(0, 0); 74 | this.progress_operation.Name = "progress_operation"; 75 | this.progress_operation.Size = new System.Drawing.Size(434, 23); 76 | this.progress_operation.TabIndex = 2; 77 | // 78 | // grp_settings 79 | // 80 | this.grp_settings.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 81 | | System.Windows.Forms.AnchorStyles.Left) 82 | | System.Windows.Forms.AnchorStyles.Right))); 83 | this.grp_settings.Location = new System.Drawing.Point(7, 0); 84 | this.grp_settings.Name = "grp_settings"; 85 | this.grp_settings.Size = new System.Drawing.Size(549, 154); 86 | this.grp_settings.TabIndex = 3; 87 | this.grp_settings.TabStop = false; 88 | this.grp_settings.Text = "Settings"; 89 | // 90 | // panel1 91 | // 92 | this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 93 | | System.Windows.Forms.AnchorStyles.Left) 94 | | System.Windows.Forms.AnchorStyles.Right))); 95 | this.panel1.Controls.Add(this.panel2); 96 | this.panel1.Controls.Add(this.grp_settings); 97 | this.panel1.Controls.Add(this.lst_operation); 98 | this.panel1.Location = new System.Drawing.Point(12, 76); 99 | this.panel1.Name = "panel1"; 100 | this.panel1.Size = new System.Drawing.Size(562, 221); 101 | this.panel1.TabIndex = 4; 102 | // 103 | // panel2 104 | // 105 | this.panel2.Controls.Add(this.panel3); 106 | this.panel2.Controls.Add(this.btn_startstop); 107 | this.panel2.Location = new System.Drawing.Point(3, 160); 108 | this.panel2.Name = "panel2"; 109 | this.panel2.Size = new System.Drawing.Size(556, 58); 110 | this.panel2.TabIndex = 4; 111 | // 112 | // materialSingleLineTextField1 113 | // 114 | this.materialSingleLineTextField1.Depth = 0; 115 | this.materialSingleLineTextField1.Dock = System.Windows.Forms.DockStyle.Bottom; 116 | this.materialSingleLineTextField1.Hint = ""; 117 | this.materialSingleLineTextField1.Location = new System.Drawing.Point(0, 29); 118 | this.materialSingleLineTextField1.MouseState = MaterialSkin.MouseState.HOVER; 119 | this.materialSingleLineTextField1.Name = "materialSingleLineTextField1"; 120 | this.materialSingleLineTextField1.PasswordChar = '\0'; 121 | this.materialSingleLineTextField1.SelectedText = ""; 122 | this.materialSingleLineTextField1.SelectionLength = 0; 123 | this.materialSingleLineTextField1.SelectionStart = 0; 124 | this.materialSingleLineTextField1.Size = new System.Drawing.Size(434, 23); 125 | this.materialSingleLineTextField1.TabIndex = 3; 126 | this.materialSingleLineTextField1.UseSystemPasswordChar = false; 127 | // 128 | // panel3 129 | // 130 | this.panel3.Controls.Add(this.materialSingleLineTextField1); 131 | this.panel3.Controls.Add(this.progress_operation); 132 | this.panel3.Location = new System.Drawing.Point(4, 3); 133 | this.panel3.Name = "panel3"; 134 | this.panel3.Size = new System.Drawing.Size(434, 52); 135 | this.panel3.TabIndex = 5; 136 | // 137 | // OperationsPanel 138 | // 139 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 140 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 141 | this.ClientSize = new System.Drawing.Size(586, 309); 142 | this.Controls.Add(this.panel1); 143 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 144 | this.MaximizeBox = false; 145 | this.Name = "OperationsPanel"; 146 | this.Text = "VRChat Launcher"; 147 | this.panel1.ResumeLayout(false); 148 | this.panel2.ResumeLayout(false); 149 | this.panel3.ResumeLayout(false); 150 | this.ResumeLayout(false); 151 | 152 | } 153 | 154 | #endregion 155 | 156 | private MaterialSkin.Controls.MaterialRaisedButton btn_startstop; 157 | private System.Windows.Forms.ListView lst_operation; 158 | private System.Windows.Forms.ProgressBar progress_operation; 159 | private System.Windows.Forms.GroupBox grp_settings; 160 | private System.Windows.Forms.Panel panel1; 161 | private System.Windows.Forms.Panel panel2; 162 | private System.Windows.Forms.Panel panel3; 163 | private MaterialSkin.Controls.MaterialSingleLineTextField materialSingleLineTextField1; 164 | } 165 | } -------------------------------------------------------------------------------- /UI/OperationsPanel.cs: -------------------------------------------------------------------------------- 1 | using MaterialSkin; 2 | using MaterialSkin.Controls; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Data; 7 | using System.Drawing; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Windows.Forms; 12 | 13 | namespace VRChatLauncher.UI 14 | { 15 | public partial class OperationsPanel : MaterialForm 16 | { 17 | MaterialSkinManager skinManager; 18 | bool running = false; 19 | public OperationsPanel() 20 | { 21 | InitializeComponent(); 22 | skinManager = MaterialSkinManager.Instance; 23 | skinManager.AddFormToManage(this); 24 | skinManager.Theme = MaterialSkinManager.Themes.LIGHT; 25 | skinManager.ColorScheme = new ColorScheme(Primary.BlueGrey800, Primary.BlueGrey900, Primary.BlueGrey500, Accent.LightBlue200, TextShade.WHITE); 26 | } 27 | 28 | private void Btn_startstop_Click(object sender, EventArgs e) 29 | { 30 | if (!running) { 31 | grp_settings.Visible = false; 32 | progress_operation.Value = 100; 33 | btn_startstop.Text = "Stop"; 34 | } else { 35 | grp_settings.Visible = true; 36 | progress_operation.Value = 0; 37 | btn_startstop.Text = "Start"; 38 | } 39 | running = !running; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Updater/AvatarFav.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace VRChatLauncher.Updater 8 | { 9 | class AvatarFav 10 | { 11 | // https://vrchat.survival-machines.fr/vrcmod/AvatarFavHashCheck.php?localhash=558615628ae9c76ffb82749629626b55 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Updater/Launcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace VRChatLauncher.Updater 8 | { 9 | class Launcher 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Updater/VRCModLoader.cs: -------------------------------------------------------------------------------- 1 | using SocketLibrary; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Newtonsoft.Json; 9 | using static VRChatLauncher.Utils.Mods; 10 | using VRChatLauncher.Utils; 11 | 12 | namespace VRChatLauncher.Updater 13 | { 14 | public class VRCModLoader 15 | { 16 | public static Mod CheckForUpdate() // https://download2.survival-machines.fr/vrcmodloader/VRCModLoaderHashCheck.php?localhash=b9a34bb327390008c235cefe8d8f4ada 17 | { 18 | var mod = GetMod(Setup.Mods.VRCModLoaderDLL().FullName); 19 | using (var socket = new ConnectedSocket("vrchat.survival-machines.fr", 26341)) 20 | { 21 | var request = new VRCTRequest(Type.GETINSTALLERVERSION, ""); 22 | socket.Send(request.toJSON()); 23 | //var data = socket.Receive(); 24 | //var response = new VRCTResponse(data); 25 | // Logger.Log("recieved:", data); 26 | } 27 | return mod; 28 | } 29 | public static Mod Update(Mod mod) 30 | { 31 | return mod; 32 | } 33 | } 34 | public class VRCTRequest { 35 | public string version = "1.1"; 36 | public string uuid; 37 | public string username; 38 | public string type; 39 | public string data; 40 | public VRCTRequest(string type, string data) { 41 | this.uuid = "usr_none"; 42 | this.username = ""; 43 | this.type = type; 44 | this.data = data; 45 | Logger.Debug(this.toJSON()); 46 | } 47 | public string toJSON() { 48 | return JsonConvert.SerializeObject(this); 49 | } 50 | } 51 | public class Type { 52 | public const string 53 | GETINSTALLERVERSION = "GETINSTALLERVERSION", 54 | GETDOWNLOADDATA = "GETDOWNLOADDATA"; 55 | } 56 | 57 | public class VRCTResponse 58 | { 59 | public ReturnCode returncode; 60 | public string data; 61 | public VRCTResponse(string json) { 62 | var obj = JsonConvert.DeserializeObject(json); 63 | } 64 | public VRCTResponse(int returncode, string data) { 65 | this.returncode = (ReturnCode)returncode; 66 | this.data = data; 67 | } 68 | public enum ReturnCode 69 | { 70 | ERROR_UNDEFINED = 0, 71 | SUCCESS = 1, 72 | ERROR_BAD_VERSION = 2, 73 | ERROR_UNKNOWN_REQUEST = 3, 74 | ERROR_INTERNAL_SERVER_ERROR = 4, 75 | ERROR_NOT_HANDLED = 5, 76 | AVATAR_ALREADY_IN_FAV = 6, 77 | WAITING_FOR_UPDATE = 7, 78 | BANNED_ADDRESS = 8, 79 | BANNED_ACCOUNT = 9, 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Utils/Arguments/game.json: -------------------------------------------------------------------------------- 1 | { 2 | "fps": { 3 | "argType": 2, 4 | "valueType": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 5 | "name": "fps", 6 | "description": "Sets the max FPS the game will lock on", 7 | "values": null, 8 | "value": null, 9 | "defaultValue": null, 10 | "isSet": false 11 | }, 12 | "noVR": { 13 | "argType": 2, 14 | "valueType": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 15 | "name": "no-vr", 16 | "description": "Sets the game to desktop-mode", 17 | "values": null, 18 | "value": null, 19 | "defaultValue": false, 20 | "isSet": false 21 | }, 22 | "osc": { 23 | "argType": 2, 24 | "valueType": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 25 | "name": "osc", 26 | "description": "Set additional arguments for osc", 27 | "values": null, 28 | "value": null, 29 | "defaultValue": null, 30 | "isSet": false 31 | }, 32 | "url": { 33 | "argType": 2, 34 | "valueType": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 35 | "name": "url", 36 | "description": "Same as applying a URL without this param", 37 | "values": null, 38 | "value": null, 39 | "defaultValue": null, 40 | "isSet": false 41 | }, 42 | "logDebugLevels": { 43 | "argType": 2, 44 | "valueType": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 45 | "name": "log-debug-levels", 46 | "description": "Sets log levels for the game", 47 | "values": null, 48 | "value": null, 49 | "defaultValue": null, 50 | "isSet": false 51 | }, 52 | "clearPlayerModerations": { 53 | "argType": 2, 54 | "valueType": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 55 | "name": "clear-player-moderations", 56 | "description": "Clears all player moderations, be careful with this!", 57 | "values": null, 58 | "value": null, 59 | "defaultValue": false, 60 | "isSet": false 61 | }, 62 | "autoLoginUsername": { 63 | "argType": 2, 64 | "valueType": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 65 | "name": "auto-login-un", 66 | "description": "Sets username for auto login", 67 | "values": null, 68 | "value": null, 69 | "defaultValue": null, 70 | "isSet": false 71 | }, 72 | "autoLoginPassword": { 73 | "argType": 2, 74 | "valueType": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 75 | "name": "auto-login-pw", 76 | "description": "Sets password for auto login", 77 | "values": null, 78 | "value": null, 79 | "defaultValue": null, 80 | "isSet": false 81 | }, 82 | "autoLogin2FA": { 83 | "argType": 2, 84 | "valueType": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 85 | "name": "auto-login-2fa", 86 | "description": "Sets two factor authentication code for auto login", 87 | "values": null, 88 | "value": null, 89 | "defaultValue": null, 90 | "isSet": false 91 | }, 92 | "spawnRadius": { 93 | "argType": 2, 94 | "valueType": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 95 | "name": "spawn-radius", 96 | "description": "N/A", 97 | "values": null, 98 | "value": null, 99 | "defaultValue": null, 100 | "isSet": false 101 | }, 102 | "profile": { 103 | "argType": 2, 104 | "valueType": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 105 | "name": "profile", 106 | "description": "N/A", 107 | "values": null, 108 | "value": null, 109 | "defaultValue": null, 110 | "isSet": false 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Utils/Arguments/launcher.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignoreSSLErrors": { 3 | "argType": 1, 4 | "valueType": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 5 | "name": "nossl", 6 | "description": "Instructs the launcher to ignore SSL errors", 7 | "values": null, 8 | "value": null, 9 | "defaultValue": false, 10 | "isSet": false 11 | }, 12 | "keepOpen": { 13 | "argType": 1, 14 | "valueType": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 15 | "name": "keep", 16 | "description": "Keeps the launcher open even if another instance of it is already running", 17 | "values": null, 18 | "value": null, 19 | "defaultValue": false, 20 | "isSet": false 21 | }, 22 | "verbose": { 23 | "argType": 1, 24 | "valueType": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 25 | "name": "verbose", 26 | "description": "Starts logging Trace messages", 27 | "values": null, 28 | "value": null, 29 | "defaultValue": false, 30 | "isSet": false 31 | }, 32 | "console": { 33 | "argType": 1, 34 | "valueType": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 35 | "name": "console", 36 | "description": "Opens a console on startup", 37 | "values": null, 38 | "value": null, 39 | "defaultValue": false, 40 | "isSet": false 41 | }, 42 | "skip": { 43 | "argType": 1, 44 | "valueType": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 45 | "name": "skiplauncher", 46 | "description": "Skip opening the launcher and start the game directly", 47 | "values": null, 48 | "value": null, 49 | "defaultValue": false, 50 | "isSet": false 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Utils/Arguments/vrctools.json: -------------------------------------------------------------------------------- 1 | { 2 | "forceUpdate": { 3 | "argType": 3, 4 | "valueType": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 5 | "name": "forceupdate", 6 | "description": "Forces a vrctools update", 7 | "values": null, 8 | "value": null, 9 | "defaultValue": false, 10 | "isSet": false 11 | }, 12 | "noUpdate": { 13 | "argType": 3, 14 | "valueType": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 15 | "name": "noupdate", 16 | "description": "Skips VRCTools and AvatarFav update check (Required to run a modified version of VRCTools)", 17 | "values": null, 18 | "value": null, 19 | "defaultValue": false, 20 | "isSet": false 21 | }, 22 | "enableNamePlateIcons": { 23 | "argType": 3, 24 | "valueType": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 25 | "name": "enablenameplateicons", 26 | "description": "Shows nameplate icons for VRCTools users", 27 | "values": null, 28 | "value": null, 29 | "defaultValue": false, 30 | "isSet": false 31 | }, 32 | "dev": { 33 | "argType": 3, 34 | "valueType": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 35 | "name": "dev", 36 | "description": "Changes VRCMN Server port from 26342 to 26345", 37 | "values": null, 38 | "value": null, 39 | "defaultValue": false, 40 | "isSet": false 41 | }, 42 | "verbose": { 43 | "argType": 0, 44 | "valueType": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 45 | "name": "verbose", 46 | "description": "Enables VRCTools Console", 47 | "values": null, 48 | "value": null, 49 | "defaultValue": false, 50 | "isSet": false 51 | }, 52 | "noModLoader": { 53 | "argType": 0, 54 | "valueType": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 55 | "name": "nomodloader", 56 | "description": "Disables vrctools mod loading", 57 | "values": null, 58 | "value": null, 59 | "defaultValue": false, 60 | "isSet": false 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Utils/AssemblyReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Reflection; 5 | 6 | namespace VRChatLauncher.Utils 7 | { 8 | class AssemblyReader 9 | { 10 | static Dictionary assemblies; 11 | internal static string LoadFromGame(string name) 12 | { 13 | if (assemblies == null) assemblies = new Dictionary(); 14 | if (assemblies.ContainsKey(name)) { Logger.Trace("Assembly", name, "already loaded"); return name; } 15 | var file = new FileInfo(Path.Combine(Game.gameAssembliesDir.FullName, name + ".dll")); 16 | if (file.Exists) 17 | { 18 | Logger.Debug("Loading assembly", file.Name.Quote(), "from", file.DirectoryName.Quote()); 19 | try { assemblies.Add(name, Assembly.LoadFile(file.FullName)); return name; } 20 | catch (Exception ex) { Logger.Error("Can't load assembly", file.FullName, ex.Message.Enclose());} 21 | } 22 | return string.Empty; 23 | } 24 | 25 | public static string GetBuildStoreID() 26 | { 27 | var assembly = LoadFromGame("Assembly-CSharp"); 28 | var VRCApplicationSetupInstance = assemblies[assembly].CreateInstance("VRCApplicationSetup"); 29 | var VRCApplicationSetupType = VRCApplicationSetupInstance.GetType(); 30 | var GetBuildStoreIDMethod = VRCApplicationSetupType.GetMethod("GetBuildStoreID", BindingFlags.Static | BindingFlags.Public); 31 | return (string)GetBuildStoreIDMethod.Invoke(null, null); 32 | } 33 | 34 | private static void ReadCommandLine() 35 | { 36 | var assembly = LoadFromGame("Assembly-CSharp"); 37 | var VRCFlowCommandLineInstance = assemblies[assembly].CreateInstance("VRCFlowCommandLine"); 38 | var VRCFlowCommandLineType = VRCFlowCommandLineInstance.GetType(); 39 | var ReadCommandLineMethod = VRCFlowCommandLineType.GetMethod("ReadCommandLine", BindingFlags.Public); 40 | ReadCommandLineMethod.Invoke(null, null); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Utils/Config.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using IniParser; 8 | using IniParser.Model; 9 | 10 | namespace VRChatLauncher.Utils 11 | { 12 | public class Config 13 | { 14 | public static IniData DefaultConfig = new IniData(); 15 | public static string DefaultConfigFile = Path.Combine(Utils.getOwnPath().DirectoryName, "Launcher.ini"); 16 | public static IniData Load(string file = null, FileIniDataParser parser = null) { 17 | if (file == null) file = DefaultConfigFile; 18 | if (parser == null) parser = new FileIniDataParser(); 19 | if (!File.Exists(file)) { 20 | Logger.Warn("Config did not exist, creating default..."); 21 | Save(DefaultConfig, parser: parser); 22 | return DefaultConfig; 23 | } 24 | Logger.Debug("Loading config from", file); 25 | return parser.ReadFile(file); 26 | } 27 | 28 | public static void Save(IniData config, string file = null, FileIniDataParser parser = null) { 29 | if (file == null) file = DefaultConfigFile; 30 | if (parser == null) parser = new FileIniDataParser(); 31 | if (config.Sections.Count < 1) return; 32 | Logger.Debug("Writing config to", file); 33 | parser.WriteFile(file, config); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Utils/ExternalConsole.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System; 3 | using System.IO; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | 7 | namespace VRChatLauncher.Utils 8 | { 9 | public class ExternalConsole 10 | { 11 | private const int STD_OUTPUT_HANDLE = -11; 12 | private const int MY_CODE_PAGE = 437; 13 | 14 | [DllImport("kernel32.dll", EntryPoint = "GetStdHandle", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] 15 | private static extern IntPtr GetStdHandle(int nStdHandle); 16 | [DllImport("kernel32.dll", EntryPoint = "AllocConsole", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] 17 | private static extern int AllocConsole(); 18 | [DllImport("kernel32.dll", EntryPoint = "FreeConsole", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] 19 | private static extern int FreeConsole(); 20 | 21 | public static void InitConsole() 22 | { 23 | AllocConsole(); 24 | IntPtr stdHandle=GetStdHandle(STD_OUTPUT_HANDLE); 25 | SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true); 26 | FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write); 27 | Encoding encoding = Encoding.GetEncoding(MY_CODE_PAGE); 28 | StreamWriter standardOutput = new StreamWriter(fileStream, encoding); 29 | standardOutput.AutoFlush = true; 30 | Console.SetOut(standardOutput); 31 | } 32 | public static void Dispose() 33 | { 34 | FreeConsole(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Utils/Game.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.IO; 3 | using System.Linq; 4 | 5 | namespace VRChatLauncher.Utils 6 | { 7 | class Game 8 | { 9 | 10 | internal const string gameBinary = "VRChat.exe"; 11 | internal static DirectoryInfo gameAssembliesDir = Utils.getGamePath().Directory.Combine("VRChat_Data", "Managed"); 12 | public static Process StartGame(bool steam = false, params string[] args) 13 | { 14 | // var ownPath = Path.GetDirectoryName(Application.ExecutablePath); 15 | // proc.WorkingDirectory = ownPath; 16 | if (System.Windows.Input.Keyboard.IsKeyDown(System.Windows.Input.Key.LeftShift)) { 17 | var argsWindow = new UI.CommandLineArguments(Utils.getGamePath().FullName, args.ToArray()); 18 | Logger.Debug(Program.Arguments.ToJson()); 19 | var result = argsWindow.ShowDialog(); 20 | if (result != System.Windows.Forms.DialogResult.OK) return null; 21 | } 22 | return Utils.StartProcess(Utils.getGamePath(), args); 23 | } 24 | public static bool IsGameAlreadyRunning() 25 | { 26 | Process[] pname = Process.GetProcessesByName("VRChat"); 27 | if (pname.Length == 0) return false; 28 | return true; 29 | } 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Utils/LogReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using Talifun.FileWatcher; 10 | 11 | namespace VRChatLauncher.Utils 12 | { 13 | public class LogReader 14 | { 15 | public static DirectoryInfo logDir; 16 | public const string logFilter = @"output_log_*-*-*_*.txt"; 17 | public static Regex logRegex = new Regex(@"output_log_\d{1,2}-\d{1,2}-\d{1,2}_(?:AM|PM)\.txt$"); 18 | private static readonly object Lock = new object(); 19 | public static IEnhancedFileSystemWatcher FSWatcher; 20 | public static List watching = new List(); 21 | public void Init() 22 | { 23 | logDir = new DirectoryInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "..", "LocalLow", "VRChat", "vrchat")); 24 | watching.Add(new Log(getLatestLog())); 25 | var filter = logRegex.ToString(); 26 | FSWatcher = EnhancedFileSystemWatcherFactory.Instance.CreateEnhancedFileSystemWatcher(logDir.FullName, filter, 1000, false); 27 | FSWatcher.Start(); 28 | FSWatcher.FileCreatedEvent += OnFileCreatedEvent; 29 | FSWatcher.FileChangedEvent += OnFileChangedEvent; 30 | FSWatcher.FileDeletedEvent += OnFileDeletedEvent; 31 | Logger.Debug("Initialised Log Watcher >", Environment.NewLine, "Folder:", logDir.FullName, Environment.NewLine, "Filter:", filter); 32 | } 33 | 34 | private static void OnFileCreatedEvent(object sender, FileCreatedEventArgs e) { 35 | lock (Lock) { 36 | var file = new FileInfo(e.FilePath); 37 | foreach (var log in watching) { 38 | if (log.File.Name == file.Name) { 39 | Logger.Debug("Already watching", file.Name); return; 40 | } 41 | } 42 | Logger.Debug("Now also watching", file.Name); 43 | watching.Add(new Log(file)); 44 | } 45 | } 46 | private static void OnFileDeletedEvent(object sender, FileDeletedEventArgs e) { 47 | lock (Lock) { 48 | var file = new FileInfo(e.FilePath); 49 | foreach (var log in watching) { 50 | if (log.File.Name == file.Name) { 51 | watching.Remove(log); 52 | Logger.Debug("No longer watching", file.Name); 53 | return; 54 | } 55 | } 56 | Logger.Debug("Not watching", file.Name); 57 | } 58 | } 59 | private static void OnFileChangedEvent(object sender, FileChangedEventArgs e) { 60 | //lock (Lock) { 61 | var file = new FileInfo(e.FilePath); 62 | foreach (var log in watching) { 63 | if (log.File.Name == file.Name) { 64 | var line = ReadLastLine(e.FilePath); 65 | if (string.IsNullOrWhiteSpace(line)) return; 66 | Logger.Trace(file.Name, line); 67 | return; 68 | } 69 | } 70 | //} 71 | } 72 | 73 | 74 | public static FileInfo getLatestLog() { 75 | return logDir.GetFiles(logFilter).OrderByDescending(f => f.LastWriteTime).First(); 76 | } 77 | /*public void ReadLogs() 78 | { 79 | if (logDir == null) Init(); 80 | var wh = new AutoResetEvent(false); 81 | var fsw = new FileSystemWatcher(); 82 | fsw.Filter = getLatestLog().FullName; 83 | fsw.EnableRaisingEvents = true; 84 | fsw.Changed += (s, e) => wh.Set(); 85 | 86 | var fs = new FileStream(fsw.Filter, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); 87 | //UInt64 i = 0; 88 | using (var sr = new StreamReader(fs)) 89 | { 90 | var s = ""; 91 | while (true) 92 | { 93 | s = sr.ReadLine(); 94 | if (s != null && !string.IsNullOrWhiteSpace(s)) 95 | { 96 | // if (!s.Contains("OnPlayerJoined") && !s.Contains("OnPlayerLeft") && !s.Contains("OnJoinedRoom") && !s.Contains("OnLeftRoom")) continue; 97 | /* 98 | LogLine line = ParseLine(s); 99 | if (joins_only) 100 | { 101 | if (line.Logger != Logger.NetworkManager) continue; 102 | if (line.EventType != EventType.OnPlayerJoined && line.EventType != EventType.OnPlayerLeft && 103 | line.EventType != EventType.OnJoinedRoom && line.EventType != EventType.OnLeftRoom) continue; 104 | } 105 | Console.WriteLine(@"<{0}> [{1}] {2} ({3}): {4}", line.DateTime.ToString(), line.Category.ToString(), line.Logger.ToString(), line.EventType.ToString(), line.Message); 106 | *\/ 107 | Logger.Log(s); 108 | //i += 1; 109 | } 110 | else 111 | { 112 | wh.WaitOne(50); 113 | } 114 | } 115 | } 116 | wh.Close(); 117 | }*/ 118 | 119 | public static string ReadLastLine(string path, Encoding encoding = null, string newline = null) 120 | { 121 | if (encoding == null) encoding = Encoding.UTF8; 122 | if (newline == null) newline = Environment.NewLine; 123 | int charsize = encoding.GetByteCount(newline); 124 | byte[] buffer = encoding.GetBytes(newline); 125 | using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 126 | { 127 | long endpos = stream.Length / charsize; 128 | for (long pos = charsize; pos < endpos; pos += charsize) 129 | { 130 | stream.Seek(-pos, SeekOrigin.End); 131 | stream.Read(buffer, 0, buffer.Length); 132 | if (encoding.GetString(buffer) == newline) 133 | { 134 | buffer = new byte[stream.Length - stream.Position]; 135 | stream.Read(buffer, 0, buffer.Length); 136 | return encoding.GetString(buffer); 137 | } 138 | } 139 | } 140 | return null; 141 | } 142 | public void Dispose() 143 | { 144 | if (FSWatcher != null) 145 | { 146 | FSWatcher.FileCreatedEvent -= OnFileCreatedEvent; 147 | FSWatcher.FileChangedEvent -= OnFileChangedEvent; 148 | FSWatcher.FileDeletedEvent -= OnFileDeletedEvent; 149 | FSWatcher.Dispose(); 150 | } 151 | } 152 | public class Log { 153 | public FileInfo File { get; set; } 154 | public string LastLine { get; set; } 155 | public Log(FileInfo file) { 156 | File = file; 157 | } 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /Utils/Logger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Drawing; 4 | using System.IO; 5 | using System.Linq; 6 | 7 | namespace VRChatLauncher.Utils 8 | { 9 | class Logger 10 | { 11 | private static FileInfo getLogFile(string fileName = "Launcher.log") { 12 | return Utils.getOwnPath().Combine(fileName); 13 | } 14 | public static void Init() { 15 | try { Console.Title = "VRChat Launcher Log"; } catch { } 16 | var args = Environment.GetCommandLineArgs().Skip(1).ToArray(); 17 | args = args.Select(s => s.ToLowerInvariant()).ToArray(); 18 | if (Program.Arguments.Launcher.Console.IsTrue) { ExternalConsole.InitConsole(); } 19 | } 20 | public static void ClearLog() { 21 | var log = getLogFile(); 22 | if (log.Exists && log.Length > 0) try { File.WriteAllText(log.FullName, string.Empty); } catch { } 23 | } 24 | private static Tuple ColorFromLogLevel(VRChatApi.Logging.LogLevel logLevel) { 25 | switch (logLevel) { 26 | case VRChatApi.Logging.LogLevel.Trace: 27 | return new Tuple(Color.Gray, ConsoleColor.Gray); 28 | case VRChatApi.Logging.LogLevel.Debug: 29 | return new Tuple(Color.Cyan, ConsoleColor.Cyan); 30 | case VRChatApi.Logging.LogLevel.Warn: 31 | return new Tuple(Color.Orange, ConsoleColor.DarkYellow); 32 | case VRChatApi.Logging.LogLevel.Error: 33 | return new Tuple(Color.Red, ConsoleColor.Red); 34 | case VRChatApi.Logging.LogLevel.Fatal: 35 | return new Tuple(Color.DarkRed, ConsoleColor.DarkRed); 36 | default: 37 | return new Tuple(Color.White, ConsoleColor.White); 38 | } 39 | } 40 | public static void Trace(params object[] msg) => log(VRChatApi.Logging.LogLevel.Trace, msgs: msg); 41 | public static void Debug(params object[] msg) => log(VRChatApi.Logging.LogLevel.Debug, msgs: msg); 42 | public static void Log(params object[] msg) => log(VRChatApi.Logging.LogLevel.Info, msgs: msg); 43 | public static void LogLines(params object[] msg) => log(VRChatApi.Logging.LogLevel.Info, lines: true, msgs: msg); 44 | public static void Warn(params object[] msg) => log(VRChatApi.Logging.LogLevel.Warn, msgs: msg); 45 | public static void Error(params object[] msg) => log(VRChatApi.Logging.LogLevel.Error, msgs: msg); 46 | public static void Fatal(params object[] msg) => log(VRChatApi.Logging.LogLevel.Fatal, msgs: msg); 47 | private static void log(VRChatApi.Logging.LogLevel logLevel, bool lines = false, params object[] msgs) // [CallerMemberName] string cName = "Unknown.Unknown", 48 | { 49 | string timestamp = DateTime.UtcNow.ToString("HH:mm:ss.fff", System.Globalization.CultureInfo.InvariantCulture); 50 | StackFrame frame = new StackFrame(2); var method = frame.GetMethod(); var cName = method.DeclaringType.Name; var mName = method.Name; 51 | System.ConsoleColor oldColor = ConsoleColor.White; 52 | try { oldColor = Console.ForegroundColor; 53 | } catch (IOException) { } 54 | var newColor = ColorFromLogLevel(logLevel); 55 | var item = new System.Windows.Forms.ListViewItem(); 56 | item.ForeColor = newColor.Item1; 57 | var str = ""; 58 | var seperator = lines ? Environment.NewLine : " "; 59 | foreach(var _msg in msgs) { 60 | var msg = _msg; 61 | try { 62 | var type = msg.GetType(); 63 | if (type.IsArray) msg = string.Join(", ", _msg).Brackets(); 64 | str += seperator + (string)msg; 65 | } catch (Exception) { 66 | // Console.WriteLine($"Error {ex.ToString()}"); 67 | str += seperator + msg.ToString(); 68 | } 69 | } 70 | var line = $"[{timestamp}] {logLevel} - {cName}.{mName}: {str}"; 71 | if (Main.selflog != null) { 72 | item.Text = line; 73 | try { Main.selflog.Items.Add(item); } catch (Exception) { } 74 | } 75 | if (Main.statusBar != null && logLevel > VRChatApi.Logging.LogLevel.Debug) { 76 | try { 77 | Main.statusBar.Text = line; 78 | Main.statusBar.ForeColor = newColor.Item1; 79 | } catch (InvalidOperationException) { } 80 | } 81 | getLogFile().AppendLine(line); 82 | bool IsVerbose = false; 83 | try { IsVerbose = Program.Arguments.Launcher.Verbose.IsTrue; } catch (Exception) { }; 84 | if (logLevel > VRChatApi.Logging.LogLevel.Trace || IsVerbose) { 85 | try { 86 | Console.ForegroundColor = newColor.Item2; 87 | Console.WriteLine(line); 88 | Console.ForegroundColor = oldColor; 89 | } catch { } 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Utils/Mods.cs: -------------------------------------------------------------------------------- 1 | using Mono.Cecil; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Runtime.InteropServices; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using Newtonsoft.Json; 11 | using Newtonsoft.Json.Converters; 12 | using System.Diagnostics; 13 | 14 | namespace VRChatLauncher.Utils 15 | { 16 | public class Mods 17 | { 18 | public static List CheckForUpdates(List mods) 19 | { 20 | for (int i = 0; i < mods.Count; i++) 21 | { 22 | mods[i] = CheckForUpdate(mods[i]); 23 | } 24 | return mods; 25 | } 26 | public static Mod CheckForUpdate(Mod mod) 27 | { 28 | var sb = new StringBuilder(); 29 | switch (mod.Name) 30 | { 31 | case "VRCTools": 32 | var _mod = Updater.VRCTools.CheckForUpdate(mod); 33 | if (_mod.Update != null) mod = Updater.VRCTools.Update(_mod); 34 | break; 35 | case "VRCModLoader": 36 | var dll = Setup.Mods.VRCModLoaderDLL(); 37 | _mod = Updater.VRCModLoader.CheckForUpdate(); 38 | if (_mod.Update != null) mod = Updater.VRCModLoader.Update(_mod); 39 | break; 40 | default: break; 41 | } 42 | return mod; 43 | } 44 | public static Mod EnableMod(Mod mod) 45 | { 46 | if (mod.File.Directory.Name != "Disabled") { 47 | Logger.Warn("Mod", mod.Name, "is already in folder", mod.File.Directory.Name); 48 | return mod; 49 | } 50 | var newPath = Path.Combine(mod.File.Directory.Parent.FullName, mod.File.Name); 51 | Logger.Debug("Moving mod", mod.Name, "to", newPath); 52 | mod.File.MoveTo(newPath); 53 | mod.File = new FileInfo(newPath); 54 | mod.Enabled = true; 55 | return mod; 56 | } 57 | public static Mod DisableMod(Mod mod) 58 | { 59 | if (mod.File.Directory.Name == "Disabled") { 60 | Logger.Warn("Mod", mod.Name, "is already in folder", mod.File.Directory.Name); 61 | return mod; 62 | } 63 | var newPath = Path.Combine(mod.File.DirectoryName, "Disabled", mod.File.Name); 64 | Logger.Debug("Moving mod", mod.Name, "to", newPath); 65 | mod.File.MoveTo(newPath); 66 | mod.File = new FileInfo(newPath); 67 | mod.Enabled = false; 68 | return mod; 69 | } 70 | public static List GetMods() 71 | { 72 | var ret = new List { }; 73 | var gamePath = Utils.getGamePath(); 74 | var modPaths = new List() { 75 | Path.Combine(gamePath.DirectoryName, "Mods"), 76 | Path.Combine(gamePath.DirectoryName, "Modules"), 77 | Path.Combine(gamePath.DirectoryName, "Plugins"), 78 | Path.Combine(gamePath.DirectoryName, "VRChat_Data", "Managed", "VRLoader", "Modules") 79 | }; 80 | foreach (var modPath in modPaths) 81 | { 82 | if (!Directory.Exists(modPath)) continue; 83 | foreach (var file in Directory.GetFiles(modPath, "*.dll", SearchOption.TopDirectoryOnly)) { 84 | var mod = GetMod(file); 85 | if (mod == null) continue; 86 | mod.Enabled = true; 87 | ret.Add(mod); 88 | } 89 | var disabledModPath = Path.Combine(modPath, "Disabled"); 90 | if (!Directory.Exists(disabledModPath)) continue; 91 | foreach (var file in Directory.GetFiles(disabledModPath, "*.dll", SearchOption.TopDirectoryOnly)) { 92 | var mod = GetMod(file); 93 | if (mod == null) continue; 94 | mod.Enabled = false; 95 | ret.Add(mod); 96 | } 97 | } 98 | Logger.Log("Loaded", ret.Count.ToString(), "mods from", modPaths.Count.ToString(), "folders"); 99 | return ret; 100 | } 101 | public static List GetTypes(Mod mod) 102 | { 103 | var ret = new List(){ }; 104 | ModuleDefinition module = ModuleDefinition.ReadModule(mod.File.FullName); 105 | ret = module.Types.ToList(); 106 | return ret; 107 | } 108 | public static Mod GetMod(string file) { 109 | var mod = new Mod(); 110 | // mod.Type = ModLoaderType.Unknown; 111 | mod.File = new FileInfo(file); 112 | if (mod.File.Extension.ToLower() != ".dll") return null; 113 | mod.Name = mod.File.FileNameWithoutExtension(); 114 | try 115 | { 116 | FileVersionInfo verInfo = FileVersionInfo.GetVersionInfo(mod.File.FullName); 117 | if (!string.IsNullOrEmpty(verInfo.ProductName)) mod.Name = verInfo.ProductName; 118 | if (!string.IsNullOrEmpty(verInfo.InternalName)) mod.Name += $" ({verInfo.InternalName})"; 119 | mod.Author = verInfo.CompanyName; 120 | mod.Version = string.IsNullOrEmpty(verInfo.ProductVersion) ? verInfo.FileVersion : verInfo.ProductVersion; 121 | mod.Description = verInfo.FileDescription; 122 | } catch (Exception ex) { 123 | mod.Error = ex.Message; 124 | } 125 | try { 126 | mod = GetModInfo(mod); 127 | } catch (Exception ex) { 128 | Logger.Error("Can't load mod info for", mod.File.Name.Quote(), ex.Message.Enclose(), Environment.NewLine, ex.StackTrace); 129 | mod.Error = ex.Message; 130 | } 131 | return mod; 132 | } 133 | public static Mod GetModInfo(Mod mod) { 134 | var types = GetTypes(mod); 135 | // [ModuleInfo("Freecam/Drone", "1.0", "CJ - Credit to the real Meep <3 Join today | https://discord.gg/xgPdrGP")] 136 | // [VRCModInfo("Single Instance", "2.1", "Bluscream")] 137 | foreach (var type in types) 138 | { 139 | CustomAttribute ignoreAttribute; 140 | if (TryGetCustomAttribute(type, "VRLoader.Attributes.ModuleInfoAttribute", out ignoreAttribute)) 141 | { 142 | mod.Type = ModLoaderType.VRLoader; 143 | mod.Name = (string)ignoreAttribute.ConstructorArguments[0].Value; 144 | mod.Version = (string)ignoreAttribute.ConstructorArguments[1].Value; 145 | mod.Author = (string)ignoreAttribute.ConstructorArguments[2].Value; 146 | return mod; 147 | } 148 | else if (TryGetCustomAttribute(type, "VRCModLoader.VRCModInfoAttribute", out ignoreAttribute)) 149 | { 150 | mod.Type = ModLoaderType.VRCModloader; 151 | mod.Name = (string)ignoreAttribute.ConstructorArguments[0].Value; 152 | mod.Version = (string)ignoreAttribute.ConstructorArguments[1].Value; 153 | mod.Author = (string)ignoreAttribute.ConstructorArguments[2].Value; 154 | return mod; 155 | } 156 | } 157 | return mod; 158 | } 159 | public static bool TryGetCustomAttribute(TypeDefinition type, string attributeType, out CustomAttribute result) 160 | { 161 | result = null; 162 | if (!type.HasCustomAttributes) return false; 163 | foreach (CustomAttribute attribute in type.CustomAttributes) { 164 | if (attribute.AttributeType.FullName != attributeType) 165 | continue; 166 | 167 | result = attribute; 168 | return true; 169 | } 170 | return false; 171 | } 172 | 173 | public class Mod { 174 | public FileInfo File { get; set; } 175 | public string Error { get; set; } 176 | public bool Enabled { get; set; } 177 | public string Name { get; set; } 178 | public ModLoaderType Type { get; set; } 179 | public string Version { get; set; } 180 | public string Author { get; set; } 181 | public string Description { get; set; } 182 | public ModUpdate Update { get; set; } 183 | } 184 | public class ModUpdate { 185 | public string newVersion { get; set; } 186 | public string hash { get; set; } 187 | public string downloadURL { get; set; } 188 | public bool isArchive { get; set; } 189 | } 190 | public enum ModLoaderType { 191 | Unknown, 192 | VRCModloader, 193 | VRLoader 194 | } 195 | } 196 | } -------------------------------------------------------------------------------- /Utils/URI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace VRChatLauncher.Utils 8 | { 9 | class URI 10 | { 11 | public static Uri Parse(string url) { 12 | return new Uri(url); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Utils/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Security.Principal; 6 | using System.Diagnostics; 7 | using System.Reflection; 8 | using System.Windows.Forms; 9 | using System.Net; 10 | 11 | namespace VRChatLauncher.Utils 12 | { 13 | public static partial class Utils 14 | { 15 | static FileInfo ownBinary; 16 | static FileInfo gameBinary; 17 | public static FileInfo getOwnPath() 18 | { 19 | if (ownBinary == null) { 20 | ownBinary = new FileInfo(Path.GetFullPath(Application.ExecutablePath)); 21 | } 22 | return ownBinary; 23 | } 24 | public static FileInfo getGamePath() 25 | { 26 | if (gameBinary == null) { 27 | var ownPath = Path.GetDirectoryName(Application.ExecutablePath); 28 | var gamePath = Path.Combine(ownPath, "VRChat.exe"); 29 | gameBinary = new FileInfo(gamePath); 30 | } 31 | return gameBinary; 32 | } 33 | /*[DllImport("User32.dll")] 34 | public static extern Int32 SetForegroundWindow(int hWnd);*/ 35 | public static void BringSelfToFront() 36 | { 37 | var window = Program.mainWindow; 38 | if (window.WindowState == FormWindowState.Minimized) 39 | window.WindowState = FormWindowState.Normal; 40 | else 41 | { 42 | window.TopMost = true; 43 | window.Focus(); 44 | window.BringToFront(); 45 | window.TopMost = false; 46 | } 47 | /*Program.mainWindow.Activate(); 48 | Program.mainWindow.Focus(); 49 | Program.mainWindow.Focus();*/ 50 | // SetForegroundWindow(SafeHandle.ToInt32()); 51 | } 52 | public static bool IsLauncherAlreadyRunning() 53 | { 54 | System.Threading.Mutex m = new System.Threading.Mutex(false, "Launcher"); 55 | if (m.WaitOne(1, false) == false) 56 | { 57 | return true; 58 | } 59 | return false; 60 | } 61 | internal static void Exit() 62 | { 63 | Application.Exit(); 64 | var currentP = Process.GetCurrentProcess(); 65 | currentP.Kill(); 66 | } 67 | public static void RestartAsAdmin() 68 | { 69 | if (IsAdmin()) return; 70 | ProcessStartInfo proc = new ProcessStartInfo(); 71 | proc.UseShellExecute = true; 72 | proc.WorkingDirectory = Environment.CurrentDirectory; 73 | proc.FileName = Assembly.GetEntryAssembly().CodeBase; 74 | proc.Arguments += Program.Arguments.ToString(); 75 | proc.Verb = "runas"; 76 | try 77 | { 78 | /*new Thread(() => 79 | { 80 | Thread.CurrentThread.IsBackground = true; 81 | Process.Start(proc); 82 | }).Start();*/ 83 | Process.Start(proc); 84 | Exit(); 85 | } 86 | catch (Exception ex) 87 | { 88 | Logger.Error("Unable to restart as admin!", ex.Message); 89 | MessageBox.Show("Unable to restart as admin for you. Please do this manually now!", "Can't restart as admin", MessageBoxButtons.OK, MessageBoxIcon.Error); 90 | Exit(); 91 | } 92 | } 93 | internal static bool IsAdmin() 94 | { 95 | bool isAdmin; 96 | try 97 | { 98 | WindowsIdentity user = WindowsIdentity.GetCurrent(); 99 | WindowsPrincipal principal = new WindowsPrincipal(user); 100 | isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator); 101 | } 102 | catch (UnauthorizedAccessException ex) 103 | { 104 | isAdmin = false; 105 | } 106 | catch (Exception ex) 107 | { 108 | isAdmin = false; 109 | } 110 | return isAdmin; 111 | } 112 | public static string Base64Encode(string plainText) { 113 | var plainTextBytes = Encoding.UTF8.GetBytes(plainText); 114 | return Convert.ToBase64String(plainTextBytes); 115 | } 116 | public static string Base64Decode(string base64EncodedData) { 117 | var base64EncodedBytes = Convert.FromBase64String(base64EncodedData); 118 | return Encoding.UTF8.GetString(base64EncodedBytes); 119 | } 120 | public static FileInfo DownloadFile(string url, DirectoryInfo destinationPath, string fileName = null) { 121 | if (fileName == null) fileName = url.Split('/').Last(); 122 | Main.webClient.DownloadFile(url, Path.Combine(destinationPath.FullName, fileName)); 123 | return new FileInfo(Path.Combine(destinationPath.FullName, fileName)); 124 | } 125 | public static void ShowFileInExplorer(FileInfo file) { 126 | StartProcess("explorer.exe", null, "/select, "+file.FullName.Quote()); 127 | } 128 | public static Process StartProcess(FileInfo file, params string[] args) => StartProcess(file.FullName, file.DirectoryName, args); 129 | public static Process StartProcess(string file, string workDir = null, params string[] args) { 130 | ProcessStartInfo proc = new ProcessStartInfo(); 131 | proc.FileName = file; 132 | proc.Arguments = string.Join(" ", args); 133 | Logger.Debug(proc.FileName, proc.Arguments); 134 | if (workDir != null) { 135 | proc.WorkingDirectory = workDir; 136 | Logger.Debug("WorkingDirectory:", proc.WorkingDirectory); 137 | } 138 | return Process.Start(proc); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Utils/VRCAPI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace VRChatLauncher.Utils { 8 | public class VRCAPI { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /VRCLauncher.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28809.33 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VRChatLauncher", "VRChatLauncher.csproj", "{9A87A994-89F6-44F2-8224-3C27420C7998}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VRChatApi", "..\..\VRChat.Net\VRChatApi\VRChatApi\VRChatApi.csproj", "{6C83FEED-B29A-4F7D-9371-5B02C5B4D551}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VRChatAutoConnect", "C:\Users\blusc\source\repos\VRChat\Tools\VRChatApps\VRChatAutoConnect\VRChatAutoConnect.csproj", "{1960C698-A6E7-42AB-9E1F-673566CF0C5F}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BeatSaberModManager", "C:\Users\blusc\source\repos\BeatSaberModInstaller\BeatSaberModManager\BeatSaberModManager.csproj", "{A053076C-1827-4FD0-B29C-35CDEC751687}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VRCAPIdotNet", "..\..\VRCAPIdotNet\VRCAPIdotNet\VRCAPIdotNet.csproj", "{CCC3A3F4-C581-4AF4-8EA9-39D47FA2E1D3}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VRCModManager", "..\VRChatModInstaller\VRCModManager\VRCModManager.csproj", "{66C53D4D-B5C5-4CA2-92FB-E53E370B1C23}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VRCDiscordExtractor", "..\VRCDiscordExtractor\VRCDiscordExtractor.csproj", "{8AFC4ED0-3996-42DA-9167-90A711126522}" 19 | EndProject 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VRCAPIdotNet.Example", "..\..\VRCAPIdotNet\VRCAPIdotNet.Example\VRCAPIdotNet.Example.csproj", "{23162648-7640-4119-8253-57C067009FA8}" 21 | EndProject 22 | Global 23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 24 | Debug|Any CPU = Debug|Any CPU 25 | Release and Pack|Any CPU = Release and Pack|Any CPU 26 | Release|Any CPU = Release|Any CPU 27 | EndGlobalSection 28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 29 | {9A87A994-89F6-44F2-8224-3C27420C7998}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {9A87A994-89F6-44F2-8224-3C27420C7998}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {9A87A994-89F6-44F2-8224-3C27420C7998}.Release and Pack|Any CPU.ActiveCfg = Release|Any CPU 32 | {9A87A994-89F6-44F2-8224-3C27420C7998}.Release and Pack|Any CPU.Build.0 = Release|Any CPU 33 | {9A87A994-89F6-44F2-8224-3C27420C7998}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {9A87A994-89F6-44F2-8224-3C27420C7998}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {6C83FEED-B29A-4F7D-9371-5B02C5B4D551}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {6C83FEED-B29A-4F7D-9371-5B02C5B4D551}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {6C83FEED-B29A-4F7D-9371-5B02C5B4D551}.Release and Pack|Any CPU.ActiveCfg = Release|Any CPU 38 | {6C83FEED-B29A-4F7D-9371-5B02C5B4D551}.Release and Pack|Any CPU.Build.0 = Release|Any CPU 39 | {6C83FEED-B29A-4F7D-9371-5B02C5B4D551}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {6C83FEED-B29A-4F7D-9371-5B02C5B4D551}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {1960C698-A6E7-42AB-9E1F-673566CF0C5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {1960C698-A6E7-42AB-9E1F-673566CF0C5F}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {1960C698-A6E7-42AB-9E1F-673566CF0C5F}.Release and Pack|Any CPU.ActiveCfg = Release|Any CPU 44 | {1960C698-A6E7-42AB-9E1F-673566CF0C5F}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {1960C698-A6E7-42AB-9E1F-673566CF0C5F}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {A053076C-1827-4FD0-B29C-35CDEC751687}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {A053076C-1827-4FD0-B29C-35CDEC751687}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {A053076C-1827-4FD0-B29C-35CDEC751687}.Release and Pack|Any CPU.ActiveCfg = Release and Pack|Any CPU 49 | {A053076C-1827-4FD0-B29C-35CDEC751687}.Release and Pack|Any CPU.Build.0 = Release and Pack|Any CPU 50 | {A053076C-1827-4FD0-B29C-35CDEC751687}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {A053076C-1827-4FD0-B29C-35CDEC751687}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {CCC3A3F4-C581-4AF4-8EA9-39D47FA2E1D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {CCC3A3F4-C581-4AF4-8EA9-39D47FA2E1D3}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {CCC3A3F4-C581-4AF4-8EA9-39D47FA2E1D3}.Release and Pack|Any CPU.ActiveCfg = Release|Any CPU 55 | {CCC3A3F4-C581-4AF4-8EA9-39D47FA2E1D3}.Release and Pack|Any CPU.Build.0 = Release|Any CPU 56 | {CCC3A3F4-C581-4AF4-8EA9-39D47FA2E1D3}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {CCC3A3F4-C581-4AF4-8EA9-39D47FA2E1D3}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {66C53D4D-B5C5-4CA2-92FB-E53E370B1C23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 59 | {66C53D4D-B5C5-4CA2-92FB-E53E370B1C23}.Debug|Any CPU.Build.0 = Debug|Any CPU 60 | {66C53D4D-B5C5-4CA2-92FB-E53E370B1C23}.Release and Pack|Any CPU.ActiveCfg = Release|Any CPU 61 | {66C53D4D-B5C5-4CA2-92FB-E53E370B1C23}.Release and Pack|Any CPU.Build.0 = Release|Any CPU 62 | {66C53D4D-B5C5-4CA2-92FB-E53E370B1C23}.Release|Any CPU.ActiveCfg = Release|Any CPU 63 | {66C53D4D-B5C5-4CA2-92FB-E53E370B1C23}.Release|Any CPU.Build.0 = Release|Any CPU 64 | {8AFC4ED0-3996-42DA-9167-90A711126522}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 65 | {8AFC4ED0-3996-42DA-9167-90A711126522}.Debug|Any CPU.Build.0 = Debug|Any CPU 66 | {8AFC4ED0-3996-42DA-9167-90A711126522}.Release and Pack|Any CPU.ActiveCfg = Release|Any CPU 67 | {8AFC4ED0-3996-42DA-9167-90A711126522}.Release and Pack|Any CPU.Build.0 = Release|Any CPU 68 | {8AFC4ED0-3996-42DA-9167-90A711126522}.Release|Any CPU.ActiveCfg = Release|Any CPU 69 | {8AFC4ED0-3996-42DA-9167-90A711126522}.Release|Any CPU.Build.0 = Release|Any CPU 70 | {23162648-7640-4119-8253-57C067009FA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 71 | {23162648-7640-4119-8253-57C067009FA8}.Debug|Any CPU.Build.0 = Debug|Any CPU 72 | {23162648-7640-4119-8253-57C067009FA8}.Release and Pack|Any CPU.ActiveCfg = Release|Any CPU 73 | {23162648-7640-4119-8253-57C067009FA8}.Release and Pack|Any CPU.Build.0 = Release|Any CPU 74 | {23162648-7640-4119-8253-57C067009FA8}.Release|Any CPU.ActiveCfg = Release|Any CPU 75 | {23162648-7640-4119-8253-57C067009FA8}.Release|Any CPU.Build.0 = Release|Any CPU 76 | EndGlobalSection 77 | GlobalSection(SolutionProperties) = preSolution 78 | HideSolutionNode = FALSE 79 | EndGlobalSection 80 | GlobalSection(ExtensibilityGlobals) = postSolution 81 | SolutionGuid = {77957431-4A9C-4B0E-9317-353651B1E97F} 82 | EndGlobalSection 83 | EndGlobal 84 | -------------------------------------------------------------------------------- /app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /res/Launcher.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bluscream/VRChatLauncher/95d05ea24714912c6365676e9164644665cdf5a9/res/Launcher.ico -------------------------------------------------------------------------------- /res/Launcher.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bluscream/VRChatLauncher/95d05ea24714912c6365676e9164644665cdf5a9/res/Launcher.pdn -------------------------------------------------------------------------------- /stats: -------------------------------------------------------------------------------- 1 | 2 | --------------------------------------------------------------------------------