diff --git a/html/.eslintrc.json b/.eslintrc.json
similarity index 98%
rename from html/.eslintrc.json
rename to .eslintrc.json
index c8279f6a..9b748fc6 100644
--- a/html/.eslintrc.json
+++ b/.eslintrc.json
@@ -32,7 +32,7 @@
"AppApiVr": "readonly",
"SharedVariable": "readonly",
"WebApi": "readonly",
- "AssetBundleCacher": "readonly"
+ "AssetBundleManager": "readonly"
},
"rules": {
"arrow-body-style": 0,
diff --git a/.github/workflows/github_actions.yml b/.github/workflows/github_actions.yml
index afe4f383..5d05bad9 100644
--- a/.github/workflows/github_actions.yml
+++ b/.github/workflows/github_actions.yml
@@ -3,45 +3,174 @@ name: VRCX
on:
- workflow_dispatch
+concurrency:
+ group: ${{ github.ref }}
+ cancel-in-progress: true
+
jobs:
- build_dotnet:
- runs-on: windows-latest
+ set_version:
+ runs-on: ubuntu-latest
+ outputs:
+ version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v3
+ - id: version
+ run: |
+ git_hash=$(git rev-parse --short "$GITHUB_SHA")
+ export FILENAME="$(date '+%Y-%m-%dT%H.%M')-${git_hash}"
+ echo "::set-output name=version::${FILENAME}"
+
+ build_dotnet_windows:
+ runs-on: windows-latest
+ needs: set_version
+
+ steps:
+ - uses: actions/checkout@v3
+
- name: Setup .NET 8
uses: actions/setup-dotnet@v3
with:
dotnet-version: "8.0.x"
- - name: Build .NET Application
- run: dotnet build -p:Configuration=Release -p:Platform=x64 -p:EnableWindowsTargeting=true --self-contained
- - uses: actions/upload-artifact@v3
+ - name: Set version
+ run: |
+ echo "${{ needs.set_version.outputs.version }}" > Version
+ cat Version
+ - name: Build Cef .NET Application
+ run: dotnet build Dotnet\VRCX-Cef.csproj -p:Configuration=Release -p:Platform=x64 -p:RestorePackagesConfig=true -t:"Restore;Clean;Build" -m --self-contained
+ - name: Upload Cef dotnet artifacts
+ uses: actions/upload-artifact@v3
with:
- name: vrcx
- path: bin/x64/Release
+ name: Cef-Release
+ path: build/Cef
- build_node:
+ build_dotnet_linux:
runs-on: ubuntu-latest
- defaults:
- run:
- working-directory: html
+ needs: set_version
steps:
- uses: actions/checkout@v3
+
+ - name: Setup .NET 8
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: "8.0.x"
+ - name: Set version
+ run: |
+ echo "${{ needs.set_version.outputs.version }}" > Version
+ cat Version
+ - name: Build Electron .NET Application
+ run: dotnet build 'Dotnet/VRCX-Electron.csproj' -p:Configuration=Release -p:Platform=x64 -p:RestorePackagesConfig=true -t:"Restore;Clean;Build" -m --self-contained
+ - name: Upload Electron dotnet artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: Electron-Release
+ path: build/Electron
+
+ build_node:
+ runs-on: ubuntu-latest
+ needs: [set_version, build_dotnet_linux]
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Set version
+ run: |
+ echo "${{ needs.set_version.outputs.version }}" > Version
+ cat Version
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Restore dependencies
run: npm ci
- - name: Build
- run: npm run production
- - name: Fix folder structure
- id: fix-folders
- run: |
- mkdir upload
- mv dist upload/html
- - uses: actions/upload-artifact@v3
+ - name: Build Cef-html
+ run: npm run prod
+ - name: Upload Cef-html artifacts
+ uses: actions/upload-artifact@v3
with:
- name: vrcx
- path: html/upload
+ name: Cef-html
+ path: build/html
+
+ - name: Build Electron-html
+ run: npm run prod-linux
+ - name: Download Electron dotnet artifacts
+ uses: actions/download-artifact@v3
+ with:
+ name: Electron-Release
+ path: build/Electron
+ - name: Build AppImage
+ run: npm run build-electron
+ - name: Upload Electron AppImage artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: Electron-AppImage
+ path: "build/VRCX_${{ needs.set_version.outputs.version }}.AppImage"
+
+ create_setup:
+ runs-on: ubuntu-latest
+ needs: [set_version, build_node, build_dotnet_windows, build_dotnet_linux]
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Install 7-zip and makensis
+ run: sudo apt update && sudo apt install -y p7zip-full nsis nsis-pluginapi
+ - name: Set plugin permissions
+ run: sudo chown -R $(whoami) /usr/share/nsis/Plugins/
+ - name: Download Cef dotnet artifacts
+ uses: actions/download-artifact@v3
+ with:
+ name: Cef-Release
+ path: build/Cef
+ - name: Download Cef-html artifacts
+ uses: actions/download-artifact@v3
+ with:
+ name: Cef-html
+ path: build/Cef/html
+ - name: Create nsis installer
+ uses: joncloud/makensis-action@v4
+ with:
+ script-file: Installer/installer.nsi
+ additional-plugin-paths: Installer/Plugins
+ - name: Rename setup
+ run: |
+ file_name="VRCX_${{ needs.set_version.outputs.version }}_Setup.exe"
+ echo "Setup FileName: ${file_name}"
+ mv Installer/VRCX_Setup.exe $file_name
+ - name: Make zip
+ run: |
+ file_name="VRCX_${{ needs.set_version.outputs.version }}.zip"
+ cd build/Cef
+ 7z a -tzip ${file_name} * -mx=7 -xr0!*.log
+ mv ${file_name} ../../${file_name}
+ echo "Zip FileName: ${file_name}"
+
+ - name: Download Electron AppImage artifacts
+ uses: actions/download-artifact@v3
+ with:
+ name: Electron-AppImage
+
+ - name: Generate hashes
+ run: |
+ sha256sum "VRCX_${{ needs.set_version.outputs.version }}_Setup.exe" "VRCX_${{ needs.set_version.outputs.version }}.AppImage" > "SHA256SUMS.txt"
+ - name: Create Release
+ id: create_release
+ uses: actions/create-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: "${{ needs.set_version.outputs.version }}"
+ release_name: "VRCX Nightly ${{ needs.set_version.outputs.version }}"
+ draft: false
+ prerelease: false
+ - name: Upload multiple assets to release
+ uses: csexton/release-asset-action@v2
+ with:
+ files: |
+ VRCX_${{ needs.set_version.outputs.version }}_Setup.exe
+ VRCX_${{ needs.set_version.outputs.version }}.zip
+ VRCX_${{ needs.set_version.outputs.version }}.AppImage
+ SHA256SUMS.txt
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ release-url: ${{ steps.create_release.outputs.upload_url }}
diff --git a/.gitignore b/.gitignore
index 16c5761d..ea52ac12 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,262 +1,7 @@
-## Ignore Visual Studio temporary files, build results, and
-## files generated by popular Visual Studio add-ons.
-
-# User-specific files
-*.suo
-*.user
-*.userosscache
-*.sln.docstates
-
-# User-specific files (MonoDevelop/Xamarin Studio)
-*.userprefs
-
-# Build results
-[Dd]ebug/
-[Dd]ebugPublic/
-[Rr]elease/
-[Rr]eleases/
-x64/
-x86/
-bld/
-[Bb]in/
-[Oo]bj/
-[Ll]og/
-
-# Visual Studio 2015 cache/options directory
-.vs/
-# Uncomment if you have tasks that create the project's static files in wwwroot
-#wwwroot/
-
-# MSTest test Results
-[Tt]est[Rr]esult*/
-[Bb]uild[Ll]og.*
-
-# NUNIT
-*.VisualState.xml
-TestResult.xml
-
-# Build Results of an ATL Project
-[Dd]ebugPS/
-[Rr]eleasePS/
-dlldata.c
-
-# DNX
-project.lock.json
-project.fragment.lock.json
-artifacts/
-
-*_i.c
-*_p.c
-*_i.h
-*.ilk
-*.meta
-*.obj
-*.pch
-*.pdb
-*.pgc
-*.pgd
-*.rsp
-*.sbr
-*.tlb
-*.tli
-*.tlh
-*.tmp
-*.tmp_proj
-*.log
-*.vspscc
-*.vssscc
-.builds
-*.pidb
-*.svclog
-*.scc
-
-# Chutzpah Test files
-_Chutzpah*
-
-# Visual C++ cache files
-ipch/
-*.aps
-*.ncb
-*.opendb
-*.opensdf
-*.sdf
-*.cachefile
-*.VC.db
-*.VC.VC.opendb
-
-# Visual Studio profiler
-*.psess
-*.vsp
-*.vspx
-*.sap
-
-# TFS 2012 Local Workspace
-$tf/
-
-# Guidance Automation Toolkit
-*.gpState
-
-# ReSharper is a .NET coding add-in
-_ReSharper*/
-*.[Rr]e[Ss]harper
-*.DotSettings.user
-
-# JustCode is a .NET coding add-in
-.JustCode
-
-# TeamCity is a build add-in
-_TeamCity*
-
-# DotCover is a Code Coverage Tool
-*.dotCover
-
-# NCrunch
-_NCrunch_*
-.*crunch*.local.xml
-nCrunchTemp_*
-
-# MightyMoose
-*.mm.*
-AutoTest.Net/
-
-# Web workbench (sass)
-.sass-cache/
-
-# Installshield output folder
-[Ee]xpress/
-
-# DocProject is a documentation generator add-in
-DocProject/buildhelp/
-DocProject/Help/*.HxT
-DocProject/Help/*.HxC
-DocProject/Help/*.hhc
-DocProject/Help/*.hhk
-DocProject/Help/*.hhp
-DocProject/Help/Html2
-DocProject/Help/html
-
-# Click-Once directory
-publish/
-
-# Publish Web Output
-*.[Pp]ublish.xml
-*.azurePubxml
-# TODO: Comment the next line if you want to checkin your web deploy settings
-# but database connection strings (with potential passwords) will be unencrypted
-#*.pubxml
-*.publishproj
-
-# Microsoft Azure Web App publish settings. Comment the next line if you want to
-# checkin your Azure Web App publish settings, but sensitive information contained
-# in these scripts will be unencrypted
-PublishScripts/
-
-# NuGet Packages
-*.nupkg
-# The packages folder can be ignored because of Package Restore
-**/packages/*
-# except build/, which is used as an MSBuild target.
-!**/packages/build/
-# Uncomment if necessary however generally it will be regenerated when needed
-#!**/packages/repositories.config
-# NuGet v3's project.json files produces more ignoreable files
-*.nuget.props
-*.nuget.targets
-
-# Microsoft Azure Build Output
-csx/
-*.build.csdef
-
-# Microsoft Azure Emulator
-ecf/
-rcf/
-
-# Windows Store app package directories and files
-AppPackages/
-BundleArtifacts/
-Package.StoreAssociation.xml
-_pkginfo.txt
-
-# Visual Studio cache files
-# files ending in .cache can be ignored
-*.[Cc]ache
-# but keep track of directories ending in .cache
-!*.[Cc]ache/
-
-# Others
-ClientBin/
-~$*
-*~
-*.dbmdl
-*.dbproj.schemaview
-*.jfm
-*.pfx
-*.publishsettings
node_modules/
-orleans.codegen.cs
-
-# Since there are multiple workflows, uncomment next line to ignore bower_components
-# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
-#bower_components/
-
-# RIA/Silverlight projects
-Generated_Code/
-
-# Backup & report files from converting an old project file
-# to a newer Visual Studio version. Backup files are not needed,
-# because we have git ;-)
-_UpgradeReport_Files/
-Backup*/
-UpgradeLog*.XML
-UpgradeLog*.htm
-
-# SQL Server files
-*.mdf
-*.ldf
-
-# Business Intelligence projects
-*.rdl.data
-*.bim.layout
-*.bim_*.settings
-
-# Microsoft Fakes
-FakesAssemblies/
-
-# GhostDoc plugin setting file
-*.GhostDoc.xml
-
-# Node.js Tools for Visual Studio
-.ntvs_analysis.dat
-
-# Visual Studio 6 build log
-*.plg
-
-# Visual Studio 6 workspace options file
-*.opt
-
-# Visual Studio LightSwitch build output
-**/*.HTMLClient/GeneratedArtifacts
-**/*.DesktopClient/GeneratedArtifacts
-**/*.DesktopClient/ModelManifest.xml
-**/*.Server/GeneratedArtifacts
-**/*.Server/ModelManifest.xml
-_Pvt_Extensions
-
-# Paket dependency manager
-.paket/paket.exe
-paket-files/
-
-# FAKE - F# Make
-.fake/
-
-# JetBrains Rider
+build/
+obj/
+obj1/
.idea/
-*.sln.iml
-*.DotSettings
-
-# CodeRush
-.cr/
-
-# Python Tools for Visual Studio (PTVS)
-__pycache__/
-*.pyc
\ No newline at end of file
+*.log
+*.DotSettings.user
\ No newline at end of file
diff --git a/html/.prettierrc.json b/.prettierrc.json
similarity index 100%
rename from html/.prettierrc.json
rename to .prettierrc.json
diff --git a/.vscode/settings.json b/.vscode/settings.json
index eaa7fd9d..75a4a965 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,12 +1,12 @@
{
- "i18n-ally.localesPaths": ["html/src/localization"],
- "i18n-ally.keystyle": "nested",
- "i18n-ally.sourceLanguage": "en",
- "editor.defaultFormatter": "esbenp.prettier-vscode",
- "editor.formatOnSave": true,
- "omnisharp.enableRoslynAnalyzers": true,
- "omnisharp.useModernNet": false,
- "[csharp]": {
- "editor.defaultFormatter": "ms-dotnettools.csharp"
- }
+ "i18n-ally.localesPaths": ["src/localization"],
+ "i18n-ally.keystyle": "nested",
+ "i18n-ally.sourceLanguage": "en",
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.formatOnSave": true,
+ "omnisharp.enableRoslynAnalyzers": true,
+ "omnisharp.useModernNet": false,
+ "[csharp]": {
+ "editor.defaultFormatter": "ms-dotnettools.csharp"
+ }
}
diff --git a/Dotnet/AppApi/AppApi.cs b/Dotnet/AppApi/AppApi.cs
deleted file mode 100644
index 014bfb05..00000000
--- a/Dotnet/AppApi/AppApi.cs
+++ /dev/null
@@ -1,727 +0,0 @@
-// Copyright(c) 2019-2022 pypy, Natsumi and individual contributors.
-// All rights reserved.
-//
-// This work is licensed under the terms of the MIT license.
-// For a copy, see .
-
-using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.Diagnostics;
-using System.Drawing;
-using System.Globalization;
-using System.IO;
-using System.Security.Cryptography;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-using CefSharp;
-using librsync.net;
-using Microsoft.Toolkit.Uwp.Notifications;
-using Microsoft.Win32;
-using NLog;
-
-namespace VRCX
-{
- public partial class AppApi
- {
- public static readonly AppApi Instance;
-
- private static readonly Logger logger = LogManager.GetCurrentClassLogger();
- private static readonly MD5 _hasher = MD5.Create();
-
- static AppApi()
- {
- Instance = new AppApi();
-
- ProcessMonitor.Instance.ProcessStarted += Instance.OnProcessStateChanged;
- ProcessMonitor.Instance.ProcessExited += Instance.OnProcessStateChanged;
- }
-
- public void Init()
- {
- // Create Instance before Cef tries to bind it
- }
-
- ///
- /// Computes the MD5 hash of the file represented by the specified base64-encoded string.
- ///
- /// The base64-encoded string representing the file.
- /// The MD5 hash of the file as a base64-encoded string.
- public string MD5File(string Blob)
- {
- var fileData = Convert.FromBase64CharArray(Blob.ToCharArray(), 0, Blob.Length);
- using (var md5 = MD5.Create())
- {
- var md5Hash = md5.ComputeHash(fileData);
- return Convert.ToBase64String(md5Hash);
- }
- }
-
- public string ResizeImageToFitLimits(string base64data)
- {
- return Convert.ToBase64String(ResizeImageToFitLimits(Convert.FromBase64String(base64data), false));
- }
-
- public byte[] ResizeImageToFitLimits(byte[] imageData, bool matchingDimensions, int maxWidth = 2000, int maxHeight = 2000, long maxSize = 10_000_000)
- {
- using var fileMemoryStream = new MemoryStream(imageData);
- var image = new Bitmap(fileMemoryStream);
-
- // for APNG, check if image is png format and less than maxSize
- if ((!matchingDimensions || image.Width == image.Height) &&
- image.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Png) &&
- imageData.Length < maxSize &&
- image.Width <= maxWidth &&
- image.Height <= maxHeight)
- {
- return imageData;
- }
-
- if (image.Width > maxWidth)
- {
- var sizingFactor = image.Width / (double)maxWidth;
- var newHeight = (int)Math.Round(image.Height / sizingFactor);
- image = new Bitmap(image, maxWidth, newHeight);
- }
- if (image.Height > maxHeight)
- {
- var sizingFactor = image.Height / (double)maxHeight;
- var newWidth = (int)Math.Round(image.Width / sizingFactor);
- image = new Bitmap(image, newWidth, maxHeight);
- }
- if (matchingDimensions && image.Width != image.Height)
- {
- var newSize = Math.Max(image.Width, image.Height);
- var newImage = new Bitmap(newSize, newSize);
- using var graphics = Graphics.FromImage(newImage);
- graphics.Clear(Color.Transparent);
- graphics.DrawImage(image, new Rectangle((newSize - image.Width) / 2, (newSize - image.Height) / 2, image.Width, image.Height));
- image.Dispose();
- image = newImage;
- }
-
- SaveToFileToUpload();
- for (int i = 0; i < 250 && imageData.Length > maxSize; i++)
- {
- SaveToFileToUpload();
- if (imageData.Length < maxSize)
- break;
-
- int newWidth;
- int newHeight;
- if (image.Width > image.Height)
- {
- newWidth = image.Width - 25;
- newHeight = (int)Math.Round(image.Height / (image.Width / (double)newWidth));
- }
- else
- {
- newHeight = image.Height - 25;
- newWidth = (int)Math.Round(image.Width / (image.Height / (double)newHeight));
- }
- image = new Bitmap(image, newWidth, newHeight);
- }
-
- if (imageData.Length > maxSize)
- {
- throw new Exception("Failed to get image into target filesize.");
- }
-
- return imageData;
-
- void SaveToFileToUpload()
- {
- using var imageSaveMemoryStream = new MemoryStream();
- image.Save(imageSaveMemoryStream, System.Drawing.Imaging.ImageFormat.Png);
- imageData = imageSaveMemoryStream.ToArray();
- }
- }
-
- public byte[] ResizePrintImage(byte[] imageData)
- {
- const int desiredWidth = 1920;
- const int desiredHeight = 1080;
-
- using var fileMemoryStream = new MemoryStream(imageData);
- var image = new Bitmap(fileMemoryStream);
-
- // increase size to 1920x1080
- if (image.Width < desiredWidth || image.Height < desiredHeight)
- {
- var newHeight = image.Height;
- var newWidth = image.Width;
- if (image.Width < desiredWidth)
- {
- var testHeight = (int)Math.Round(image.Height / (image.Width / (double)desiredWidth));
- if (testHeight <= desiredHeight)
- {
- newWidth = desiredWidth;
- newHeight = testHeight;
- }
- }
- if (image.Height < desiredHeight)
- {
- var testWidth = (int)Math.Round(image.Width / (image.Height / (double)desiredHeight));
- if (testWidth <= desiredWidth)
- {
- newHeight = desiredHeight;
- newWidth = testWidth;
- }
- }
- var resizedImage = new Bitmap(desiredWidth, desiredHeight);
- using var graphics1 = Graphics.FromImage(resizedImage);
- graphics1.Clear(Color.White);
- var x = (desiredWidth - newWidth) / 2;
- var y = (desiredHeight - newHeight) / 2;
- graphics1.DrawImage(image, new Rectangle(x, y, newWidth, newHeight));
- image.Dispose();
- image = resizedImage;
- }
-
- // limit size to 1920x1080
- if (image.Width > desiredWidth)
- {
- var sizingFactor = image.Width / (double)desiredWidth;
- var newHeight = (int)Math.Round(image.Height / sizingFactor);
- image = new Bitmap(image, desiredWidth, newHeight);
- }
- if (image.Height > desiredHeight)
- {
- var sizingFactor = image.Height / (double)desiredHeight;
- var newWidth = (int)Math.Round(image.Width / sizingFactor);
- image = new Bitmap(image, newWidth, desiredHeight);
- }
-
- // add white border
- // wtf are these magic numbers
- const int xOffset = 64; // 2048 / 32
- const int yOffset = 69; // 1440 / 20.869
- var newImage = new Bitmap(2048, 1440);
- using var graphics = Graphics.FromImage(newImage);
- graphics.Clear(Color.White);
- // graphics.DrawImage(image, new Rectangle(xOffset, yOffset, image.Width, image.Height));
- var newX = (2048 - image.Width) / 2;
- var newY = yOffset;
- graphics.DrawImage(image, new Rectangle(newX, newY, image.Width, image.Height));
- image.Dispose();
- image = newImage;
-
- using var imageSaveMemoryStream = new MemoryStream();
- image.Save(imageSaveMemoryStream, System.Drawing.Imaging.ImageFormat.Png);
- return imageSaveMemoryStream.ToArray();
- }
-
- public async Task CropAllPrints(string ugcFolderPath)
- {
- var folder = Path.Combine(GetUGCPhotoLocation(ugcFolderPath), "Prints");
- var files = Directory.GetFiles(folder, "*.png", SearchOption.AllDirectories);
- foreach (var file in files)
- {
- await CropPrintImage(file);
- }
- }
-
- public async Task CropPrintImage(string path)
- {
- var tempPath = path + ".temp";
- var bytes = await File.ReadAllBytesAsync(path);
- var ms = new MemoryStream(bytes);
- Bitmap print = new Bitmap(ms);
- // validation step to ensure image is actually a print
- if (print.Width != 2048 || print.Height != 1440)
- {
- return false;
- }
- var point = new Point(64, 69);
- var size = new Size(1920, 1080);
- var rectangle = new Rectangle(point, size);
- Bitmap cropped = print.Clone(rectangle, print.PixelFormat);
- cropped.Save(tempPath);
- if (ScreenshotHelper.HasTXt(path))
- {
- var success = ScreenshotHelper.CopyTXt(path, tempPath);
- if (!success)
- {
- File.Delete(tempPath);
- return false;
- }
- }
- File.Move(tempPath, path, true);
- return true;
- }
-
- ///
- /// Computes the signature of the file represented by the specified base64-encoded string using the librsync library.
- ///
- /// The base64-encoded string representing the file.
- /// The signature of the file as a base64-encoded string.
- public string SignFile(string Blob)
- {
- var fileData = Convert.FromBase64String(Blob);
- using (var sig = Librsync.ComputeSignature(new MemoryStream(fileData)))
- using (var memoryStream = new MemoryStream())
- {
- sig.CopyTo(memoryStream);
- var sigBytes = memoryStream.ToArray();
- return Convert.ToBase64String(sigBytes);
- }
- }
-
- ///
- /// Returns the length of the file represented by the specified base64-encoded string.
- ///
- /// The base64-encoded string representing the file.
- /// The length of the file in bytes.
- public string FileLength(string Blob)
- {
- var fileData = Convert.FromBase64String(Blob);
- return fileData.Length.ToString();
- }
-
- ///
- /// Shows the developer tools for the main browser window.
- ///
- public void ShowDevTools()
- {
- MainForm.Instance.Browser.ShowDevTools();
- }
-
- ///
- /// Deletes all cookies from the global cef cookie manager.
- ///
- public void DeleteAllCookies()
- {
- Cef.GetGlobalCookieManager().DeleteCookies();
- }
-
- ///
- /// Opens the specified URL in the default browser.
- ///
- /// The URL to open.
- public void OpenLink(string url)
- {
- if (url.StartsWith("http://") ||
- url.StartsWith("https://"))
- {
- Process.Start(new ProcessStartInfo(url)
- {
- UseShellExecute = true
- });
- }
- }
-
- // broken since adding ExecuteVrFeedFunction(
- // public void ShowVRForm()
- // {
- // try
- // {
- // MainForm.Instance.BeginInvoke(new MethodInvoker(() =>
- // {
- // if (VRForm.Instance == null)
- // {
- // new VRForm().Show();
- // }
- // }));
- // }
- // catch
- // {
- // }
- // }
-
- public void SetVR(bool active, bool hmdOverlay, bool wristOverlay, bool menuButton, int overlayHand)
- {
- Program.VRCXVRInstance.SetActive(active, hmdOverlay, wristOverlay, menuButton, overlayHand);
- }
-
- public void RefreshVR()
- {
- Program.VRCXVRInstance.Restart();
- }
-
- public void RestartVR()
- {
- Program.VRCXVRInstance.Restart();
- }
-
- public void SetZoom(double zoomLevel)
- {
- MainForm.Instance.Browser.SetZoomLevel(zoomLevel);
- }
-
- public async Task GetZoom()
- {
- return await MainForm.Instance.Browser.GetZoomLevelAsync();
- }
-
- ///
- /// Retrieves an image from the VRChat API and caches it for future use. The function will return the cached image if it already exists.
- ///
- /// The URL of the image to retrieve.
- /// The ID of the file associated with the image.
- /// The version of the file associated with the image.
- /// A string representing the file location of the cached image.
- public async Task GetImage(string url, string fileId, string version)
- {
- return await ImageCache.GetImage(url, fileId, version);
- }
-
- ///
- /// Displays a desktop notification with the specified bold text, optional text, and optional image.
- ///
- /// The bold text to display in the notification.
- /// The optional text to display in the notification.
- /// The optional image to display in the notification.
- public void DesktopNotification(string BoldText, string Text = "", string Image = "")
- {
- try
- {
- ToastContentBuilder builder = new ToastContentBuilder();
-
- if (Uri.TryCreate(Image, UriKind.Absolute, out Uri uri))
- builder.AddAppLogoOverride(uri);
-
- if (!string.IsNullOrEmpty(BoldText))
- builder.AddText(BoldText);
-
- if (!string.IsNullOrEmpty(Text))
- builder.AddText(Text);
-
- builder.Show();
- }
- catch (System.AccessViolationException ex)
- {
- logger.Warn(ex, "Unable to send desktop notification");
- }
- catch (Exception ex)
- {
- logger.Error(ex, "Unknown error when sending desktop notification");
- }
- }
-
- ///
- /// Restarts the VRCX application for an update by launching a new process with the upgrade argument and exiting the current process.
- ///
- public void RestartApplication(bool isUpgrade)
- {
- var args = new List();
-
- if (isUpgrade)
- args.Add(StartupArgs.VrcxLaunchArguments.IsUpgradePrefix);
-
- if (StartupArgs.LaunchArguments.IsDebug)
- args.Add(StartupArgs.VrcxLaunchArguments.IsDebugPrefix);
-
- if (!string.IsNullOrWhiteSpace(StartupArgs.LaunchArguments.ConfigDirectory))
- args.Add($"{StartupArgs.VrcxLaunchArguments.ConfigDirectoryPrefix}={StartupArgs.LaunchArguments.ConfigDirectory}");
-
- if (!string.IsNullOrWhiteSpace(StartupArgs.LaunchArguments.ProxyUrl))
- args.Add($"{StartupArgs.VrcxLaunchArguments.ProxyUrlPrefix}={StartupArgs.LaunchArguments.ProxyUrl}");
-
- var vrcxProcess = new Process
- {
- StartInfo = new ProcessStartInfo
- {
- FileName = Path.Combine(Program.BaseDirectory, "VRCX.exe"),
- Arguments = string.Join(' ', args),
- UseShellExecute = true,
- WorkingDirectory = Program.BaseDirectory
- }
- };
- vrcxProcess.Start();
- Environment.Exit(0);
- }
-
- ///
- /// Checks if the VRCX update executable exists in the AppData directory.
- ///
- /// True if the update executable exists, false otherwise.
- public bool CheckForUpdateExe()
- {
- if (File.Exists(Path.Combine(Program.AppDataDirectory, "update.exe")))
- return true;
- return false;
- }
-
- ///
- /// Sends an IPC packet to announce the start of VRCX.
- ///
- public void IPCAnnounceStart()
- {
- IPCServer.Send(new IPCPacket
- {
- Type = "VRCXLaunch",
- MsgType = "VRCXLaunch"
- });
- }
-
- ///
- /// Sends an IPC packet with a specified message type and data.
- ///
- /// The message type to send.
- /// The data to send.
- public void SendIpc(string type, string data)
- {
- IPCServer.Send(new IPCPacket
- {
- Type = "VrcxMessage",
- MsgType = type,
- Data = data
- });
- }
-
- public void ExecuteAppFunction(string function, string json)
- {
- if (MainForm.Instance?.Browser != null && !MainForm.Instance.Browser.IsLoading && MainForm.Instance.Browser.CanExecuteJavascriptInMainFrame)
- MainForm.Instance.Browser.ExecuteScriptAsync($"$app.{function}", json);
- }
-
- public void ExecuteVrFeedFunction(string function, string json)
- {
- Program.VRCXVRInstance.ExecuteVrFeedFunction(function, json);
- }
-
- public void ExecuteVrOverlayFunction(string function, string json)
- {
- Program.VRCXVRInstance.ExecuteVrOverlayFunction(function, json);
- }
-
- ///
- /// Gets the launch command from the startup arguments and clears the launch command.
- ///
- /// The launch command.
- public string GetLaunchCommand()
- {
- var command = StartupArgs.LaunchArguments.LaunchCommand;
- StartupArgs.LaunchArguments.LaunchCommand = string.Empty;
- return command;
- }
-
- ///
- /// Focuses the main window of the VRCX application.
- ///
- public void FocusWindow()
- {
- MainForm.Instance.Invoke(new Action(() => { MainForm.Instance.Focus_Window(); }));
- }
-
- ///
- /// Returns the file path of the custom user CSS file, if it exists.
- ///
- /// The file path of the custom user CSS file, or an empty string if it doesn't exist.
- public string CustomCssPath()
- {
- var output = string.Empty;
- var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "VRCX\\custom.css");
- if (File.Exists(filePath))
- output = filePath;
- return output;
- }
-
- ///
- /// Returns the file path of the custom user js file, if it exists.
- ///
- /// The file path of the custom user js file, or an empty string if it doesn't exist.
- public string CustomScriptPath()
- {
- var output = string.Empty;
- var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "VRCX\\custom.js");
- if (File.Exists(filePath))
- output = filePath;
- return output;
- }
-
- public string CurrentCulture()
- {
- return CultureInfo.CurrentCulture.ToString();
- }
-
- public string CurrentLanguage()
- {
- return CultureInfo.InstalledUICulture.Name;
- }
-
- public string GetVersion()
- {
- return Program.Version;
- }
-
- ///
- /// Returns whether or not the VRChat client was last closed gracefully. According to the log file, anyway.
- ///
- /// True if the VRChat client was last closed gracefully, false otherwise.
- public bool VrcClosedGracefully()
- {
- return LogWatcher.Instance.VrcClosedGracefully;
- }
-
- public void ChangeTheme(int value)
- {
- WinformThemer.SetGlobalTheme(value);
- }
-
- public void DoFunny()
- {
- WinformThemer.DoFunny();
- }
-
- ///
- /// Returns a color value derived from the given user ID.
- /// This is, essentially, and is used for, random colors.
- ///
- /// The user ID to derive the color value from.
- /// A color value derived from the given user ID.
- public int GetColourFromUserID(string userId)
- {
- var hash = _hasher.ComputeHash(Encoding.UTF8.GetBytes(userId));
- return (hash[3] << 8) | hash[4];
- }
-
- ///
- /// Returns a dictionary of color values derived from the given list of user IDs.
- ///
- /// The list of user IDs to derive the color values from.
- /// A dictionary of color values derived from the given list of user IDs.
- public Dictionary GetColourBulk(List