feat: ship 1.0.8.0 release automation and runtime overhaul

- reduce ASAR IO overhead with streamed archive reads, buffered copies, faster relative-path handling and placeholder integrity records
- fix in-place app.asar.unpacked packing/extraction self-copy cases that caused locked-file failures
- tighten JS patch discovery with candidate bundle filters and search hints
- require prebuilt remote-panel dist artifacts and clean up embedded bridge/script packaging
- add unified build entrypoints for PowerShell, cmd and bash and move native CMake output under .tmp
- add release metadata validation, changelog section extraction, pre-commit hook and GitHub Actions validation/release pipelines
- make CHANGELOG the source of truth for release notes and document the tag-driven release flow
- add updater release notes UI with latest/full changelog loading and localize the new update strings
- modularize bridge renderer scripts, add installed apps and game status sync, and support remote launch/stop commands
- centralize bridge protocol, IPC and WebSocket constants and improve LAN IP selection for QR pairing
- refactor remote panel controls/state enums, persist accent color, polish library/session UI and refresh assets
This commit is contained in:
kitbyte
2026-05-04 22:59:33 +03:00
parent 3b2f373946
commit 13759b1db6
125 changed files with 8934 additions and 2839 deletions
+94 -41
View File
@@ -20,17 +20,14 @@ namespace WandEnhancer.Core
private const string AppAsarUnpackedBackupDirectoryName = "app.asar.unpacked.backup";
private const string WebPanelDirectoryName = "web-panel";
private const string WebPanelDistDirectoryName = "dist";
private const string WebPanelBridgeDirectoryName = "bridge";
private const string WebPanelScriptsDirectoryName = "scripts";
private const string DefaultScriptsDirectoryName = "default";
private const string LocalCustomScriptsDirectoryName = "renderer-scripts";
private const string RemotePanelDirectoryName = "remote-panel";
private const string RemoteBridgeSourceFileName = "wand-remote-bridge.cjs";
private const string RemoteBridgeTargetFileName = "bridge.cjs";
private const string RemoteRendererScriptsDirectoryName = "renderer-scripts";
private const string EmbeddedRemotePanelDistPrefix = "remote-panel/dist/";
private const string EmbeddedRemotePanelBridgeResourceName = "remote-panel/bridge.cjs";
private const string EmbeddedRemotePanelDefaultScriptsPrefix = "remote-panel/renderer-scripts/";
private const string AppBundleFilePrefix = "app-";
private const string AppBundleFileSuffix = ".bundle.js";
private const string IndexBundleFileName = "index.js";
private const string JavaScriptFileExtension = ".js";
private const string JavaScriptFileSearchPattern = "*.js";
private const string DuplicateScriptSuffix = ".custom";
@@ -56,52 +53,63 @@ namespace WandEnhancer.Core
_unpackedBackupPath = Path.Combine(weModConfig.RootDirectory, ResourcesDirectoryName, AppAsarUnpackedBackupDirectoryName);
}
private string ApplyJsPatch(string fileName, string js, EnhancerConfig.PatchEntry patch, EPatchType patchType)
private string ApplyJsPatch(string fileName, string js, EnhancerConfig.PatchEntry patch, EPatchType patchType, out bool patchApplied)
{
patchApplied = false;
if (patch.Applied)
{
return js;
}
if (!CanSearchPatchInFile(fileName, patch) || !ContainsSearchHint(js, patch.SearchHints))
{
return js;
}
var matches = patch.Target.Matches(js);
if (matches.Count == 0)
var match = patch.Target.Match(js);
if (!match.Success)
{
return js;
}
var prefix = $"[ENHANCER] [{patchType} -> {patch.Name}]";
if(matches.Count > 1 && patch.SingleMatch)
if(patch.SingleMatch && match.NextMatch().Success)
{
throw new Exception(
$"{prefix} Patch failed. Multiple target functions found. Looks like the version is not supported");
}
string patchSource = patch.Patch;
if (patch.Resolver != null)
{
string resolvedField = patch.Resolver.Handler(matches[0].Value);
string resolvedField = patch.Resolver.Handler(match.Value);
if (string.IsNullOrEmpty(resolvedField))
{
throw new Exception($"{prefix} Resolver failed to find field name");
}
patch.Patch = patch.Patch.Replace(patch.Resolver.Placeholder, resolvedField);
patchSource = patchSource.Replace(patch.Resolver.Placeholder, resolvedField);
}
_logger($"{prefix} Found target function in: " + Path.GetFileName(fileName), ELogType.Info);
string newJs = patch.Target.Replace(js, patch.Patch);
File.WriteAllText(fileName, newJs);
string newJs = patch.SingleMatch
? patch.Target.Replace(js, patchSource, 1)
: patch.Target.Replace(js, patchSource);
_logger($"{prefix} Patch applied", ELogType.Success);
patch.Applied = true;
patchApplied = true;
return newJs;
}
private void PatchAsar()
{
var items = Directory.EnumerateFiles(_unpackedPath)
.Where(file => !Directory.Exists(file) && Regex.IsMatch(Path.GetFileName(file), @"^app-\w+|index\.js"))
var items = Directory.EnumerateFiles(_unpackedPath, $"*{JavaScriptFileExtension}", SearchOption.TopDirectoryOnly)
.Where(IsCandidateBundleFile)
.ToList();
if (!items.Any())
@@ -118,15 +126,23 @@ namespace WandEnhancer.Core
{
break;
}
if (!CouldFileContainRemainingPatch(item, remainingPatches, enhancerConfig))
{
continue;
}
string data = File.ReadAllText(item);
bool fileChanged = false;
foreach (var entry in remainingPatches.ToList())
{
var entries = enhancerConfig[entry];
foreach (var patchEntry in entries)
{
data = ApplyJsPatch(item, data, patchEntry, entry);
bool patchApplied;
data = ApplyJsPatch(item, data, patchEntry, entry, out patchApplied);
fileChanged = fileChanged || patchApplied;
}
if (entries.All(x => x.Applied))
@@ -134,6 +150,11 @@ namespace WandEnhancer.Core
remainingPatches.Remove(entry);
}
}
if (fileChanged)
{
File.WriteAllText(item, data);
}
}
if(remainingPatches.Count > 0)
@@ -143,6 +164,56 @@ namespace WandEnhancer.Core
}
}
private static bool IsCandidateBundleFile(string filePath)
{
string fileName = Path.GetFileName(filePath);
return fileName.Equals(IndexBundleFileName, StringComparison.OrdinalIgnoreCase)
|| (fileName.StartsWith(AppBundleFilePrefix, StringComparison.OrdinalIgnoreCase)
&& fileName.EndsWith(AppBundleFileSuffix, StringComparison.OrdinalIgnoreCase));
}
private static bool CouldFileContainRemainingPatch(string filePath, IEnumerable<EPatchType> remainingPatches, Dictionary<EPatchType, EnhancerConfig.PatchEntry[]> enhancerConfig)
{
foreach (var patchType in remainingPatches)
{
foreach (var patchEntry in enhancerConfig[patchType])
{
if (patchEntry.Applied)
{
continue;
}
if (CanSearchPatchInFile(filePath, patchEntry))
{
return true;
}
}
}
return false;
}
private static bool CanSearchPatchInFile(string filePath, EnhancerConfig.PatchEntry patch)
{
if (patch.CandidateFileNames == null || patch.CandidateFileNames.Length == 0)
{
return true;
}
string fileName = Path.GetFileName(filePath);
return patch.CandidateFileNames.Any(candidate => fileName.Equals(candidate, StringComparison.OrdinalIgnoreCase));
}
private static bool ContainsSearchHint(string source, string[] searchHints)
{
if (searchHints == null || searchHints.Length == 0)
{
return true;
}
return searchHints.Any(searchHint => source.IndexOf(searchHint, StringComparison.Ordinal) >= 0);
}
private static string FindWorkspacePath(params string[] segments)
{
string current = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
@@ -257,26 +328,6 @@ namespace WandEnhancer.Core
return resourceNames.Count;
}
private static bool CopyEmbeddedFile(string resourceName, string destinationPath)
{
var assembly = Assembly.GetExecutingAssembly();
using (var resource = assembly.GetManifestResourceStream(resourceName))
{
if (resource == null)
{
return false;
}
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath) ?? ".");
using (var output = File.Create(destinationPath))
{
resource.CopyTo(output);
}
}
return true;
}
private static string FindLocalCustomScriptsPath()
{
string executableDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
@@ -335,15 +386,17 @@ namespace WandEnhancer.Core
CopyDirectory(FindWorkspacePath(WebPanelDirectoryName, WebPanelDistDirectoryName), targetRoot);
}
if (!CopyEmbeddedFile(EmbeddedRemotePanelBridgeResourceName, targetBridgePath))
if (!File.Exists(targetBridgePath))
{
File.Copy(FindWorkspacePath(WebPanelDirectoryName, WebPanelBridgeDirectoryName, RemoteBridgeSourceFileName), targetBridgePath, true);
throw new FileNotFoundException("[ENHANCER] Remote bridge artifact is missing. Run `cd web-panel && pnpm run build` before patching.", targetBridgePath);
}
int defaultScriptCount = CopyEmbeddedDirectory(EmbeddedRemotePanelDefaultScriptsPrefix, targetScriptsRoot);
int defaultScriptCount = Directory.Exists(targetScriptsRoot)
? Directory.GetFiles(targetScriptsRoot, JavaScriptFileSearchPattern, SearchOption.TopDirectoryOnly).Length
: 0;
if (defaultScriptCount == 0)
{
defaultScriptCount = CopyJavaScriptFiles(FindWorkspacePath(WebPanelDirectoryName, WebPanelScriptsDirectoryName, DefaultScriptsDirectoryName), targetScriptsRoot);
throw new FileNotFoundException("[ENHANCER] Remote renderer script artifacts are missing. Run `cd web-panel && pnpm run build` before patching.", targetScriptsRoot);
}
int selectedScriptCount = CopySelectedJavaScriptFiles(_config.CustomScriptPaths, targetScriptsRoot);
+19 -3
View File
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using WandEnhancer.Models;
@@ -23,6 +23,8 @@ namespace WandEnhancer.Core
public string Name { get; set; }
public bool Applied { get; set; }
public bool SingleMatch { get; set; } = true;
public string[] CandidateFileNames { get; set; }
public string[] SearchHints { get; set; }
public ResolveContext Resolver { get; set; }
}
@@ -36,6 +38,7 @@ namespace WandEnhancer.Core
{
new PatchEntry
{
SearchHints = new[] { "getUserAccount()", "/v3/account" },
Resolver = new ResolveContext
{
Handler = (targetFunction) =>
@@ -53,6 +56,7 @@ namespace WandEnhancer.Core
},
new PatchEntry
{
SearchHints = new[] { "setAccountWandBrandExperience()", "/v3/account/brand_experience_wand" },
Resolver = new ResolveContext
{
Handler = (targetFunction) =>
@@ -64,7 +68,7 @@ namespace WandEnhancer.Core
},
Name = "setAccountWandBrandExperience",
Target = new Regex(
@"setAccountWandBrandExperience\(\){.*?return\s+this\.#\w+\.post\(""/v3/account/brand_experience_wand""\)}",
@"setAccountWandBrandExperience\(\)\{.*?return\s+this\.#\w+\.post\(""/v3/account/brand_experience_wand""\)\}",
RegexOptions.Singleline),
Patch =
"setAccountWandBrandExperience(){return this.#<service_name>.post(\"/v3/account/brand_experience_wand\").then(response=>{response.subscription={period:\"yearly\",state:\"active\"};return response;})}"
@@ -77,6 +81,8 @@ namespace WandEnhancer.Core
{
new PatchEntry
{
CandidateFileNames = new[] { "index.js" },
SearchHints = new[] { "ACTION_CHECK_FOR_UPDATE" },
Target = new Regex(@"registerHandler\(""ACTION_CHECK_FOR_UPDATE"".*?\)\)\)\)",
RegexOptions.Singleline),
Patch = "registerHandler(\"ACTION_CHECK_FOR_UPDATE\",(e=>expectUpdateFeedUrl(e,(e=>null)))"
@@ -90,6 +96,8 @@ namespace WandEnhancer.Core
new PatchEntry
{
Name = "devToolsBeforeInputEvent",
CandidateFileNames = new[] { "index.js" },
SearchHints = new[] { "whenReady().then(" },
// Anchor on the Electron main-process `<app>.whenReady().then(`
// call. This site is far more stable than the minified renderer
// keydown listener that previously held the F12 -> ACTION_OPEN_DEV_TOOLS
@@ -109,42 +117,50 @@ namespace WandEnhancer.Core
new PatchEntry
{
Name = "remoteBridgeMainBoot",
CandidateFileNames = new[] { "index.js" },
SearchHints = new[] { "whenReady().then(run)" },
Target = new Regex(@"(?<app>\w+)\.whenReady\(\)\.then\(run\)"),
Patch = "${app}.whenReady().then(()=>{try{const p=require(\"node:path\");require(p.join(__dirname,\"remote-panel\",\"bridge.cjs\")).installWandRuntime(require(\"electron\"));}catch(e){try{const fs=require(\"node:fs\"),os=require(\"node:os\"),p=require(\"node:path\");fs.appendFileSync(p.join(os.tmpdir(),\"wand-remote-bridge.log\"),\"[\"+new Date().toISOString()+\"] [boot-error] \"+(e&&e.stack||e)+\"\\n\");}catch(_){}}return run()})"
},
new PatchEntry
{
Name = "remoteBridgeReset",
SearchHints = new[] { "client-state" },
Target = new Regex(@"#Je\(\)\{this\.#Oe&&\(this\.#Oe\.dispose\(\),this\.#Oe=null\),this\.#Pe=Date\.now\(\)\.toString\(\),this\.#ke=null,this\.#_e=\[],this\.#Ee=null\}"),
Patch = "#Je(){this.#Oe&&(this.#Oe.dispose(),this.#Oe=null),this.#Pe=Date.now().toString(),this.#ke=null,this.#_e=[],this.#Ee=null,this.__wandRemoteTrainerInfo=null,this.__wandRemoteBridge?.sync(null)}"
},
new PatchEntry
{
Name = "remoteBridgeSyncSnapshot",
SearchHints = new[] { "client-state" },
Target = new Regex(@"#Be\(\)\{if\(this\.status===i\.Connected\)\{let e,t=!1,s=this\.#Ee\?\.getMetadata\(h\.vO\)\?\.gameVersion\?\?null,i=!1;const n=this\.#Ve\[this\.#ke\?\?""""\]\|\|null;this\.#Re&&\(e=this\.#Ae\.getPreferredInstallationInfo\(this\.#Re\),e\.app&&\(t=!0,s\?\?=e\.version\?\?null,i=""number""==typeof e\.version&&!this\.#_e\.includes\(e\.version\)\)\),this\.#Me\?\.send\(""client-state"",\{instanceId:this\.#Pe,trainerId:this\.#ke,trainerLoading:this\.#Ee\?\.isLoading\(\),gameInstalled:t,gameVersion:s,needsCompatibilityWarning:i,values:this\.#Ke\(\),themeId:this\.#We,settings:R\(this\.settings\),language:this\.#Ne,accountUuid:this\.account\.uuid,notesReadHash:n,isTimeLimitExpired:""expired""===this\.#Fe\.timerState\}\)\}\}"),
Patch = "#Be(){let e,t=!1,s=this.#Ee?.getMetadata(h.vO)?.gameVersion??null,o=!1;const n=this.#Ve[this.#ke??\"\"]||null;this.#Re&&(e=this.#Ae.getPreferredInstallationInfo(this.#Re),e.app&&(t=!0,s??=e.version??null,o=\"number\"==typeof e.version&&!this.#_e.includes(e.version)));this.status===i.Connected&&this.#Me?.send(\"client-state\",{instanceId:this.#Pe,trainerId:this.#ke,trainerLoading:this.#Ee?.isLoading(),gameInstalled:t,gameVersion:s,needsCompatibilityWarning:o,values:this.#Ke(),themeId:this.#We,settings:R(this.settings),language:this.#Ne,accountUuid:this.account.uuid,notesReadHash:n,isTimeLimitExpired:\"expired\"===this.#Fe.timerState});this.__wandRemoteBridge?.sync({instanceId:this.#Pe,trainerId:this.#ke,trainerInfo:this.__wandRemoteTrainerInfo??null,metadata:this.#Ee?.getMetadata(h.vO)??null,trainerLoading:this.#Ee?.isLoading()??false,gameInstalled:t,gameVersion:s,needsCompatibilityWarning:o,language:this.#Ne,themeId:this.#We,notesReadHash:n,isTimeLimitExpired:\"expired\"===this.#Fe.timerState,values:this.#Ke()})}"
},
new PatchEntry
{
Name = "remoteBridgeBindHandler",
SearchHints = new[] { "client-state" },
Target = new Regex(@"setCurrentTrainer\(e,t=null\)\{const s=e\?\.trainerId\|\|null,i=\(s\?e\?\.gameId:null\)\|\|null,n=\(s\?e\?\.supportedVersions:null\)\|\|\[];if\(s===this\.#ke&&t===this\.#Ee\)return;"),
Patch = "setCurrentTrainer(e,t=null){this.__wandRemoteBridge||(this.__wandRemoteBridge=(()=>{try{const r=globalThis.require||require;const{ipcRenderer:c}=r(\"electron\");try{c.invoke(\"wand-remote-url\").then((u=>{u&&(globalThis.__wandRemoteBridgeUrl=u)}))}catch(e){}const send=(ch,p)=>{try{return c.invoke(ch,p&&JSON.parse(JSON.stringify(p)))}catch(e){}};return{sync:(s)=>send(\"wand-remote-sync\",s),valueChanged:(s)=>send(\"wand-remote-value-changed\",s),setHandler:(h)=>{if(this.__wandRemoteBridgeBound)return;this.__wandRemoteBridgeBound=true;try{c.invoke(\"wand-remote-set-handler-bind\")}catch(e){}c.on(\"wand-remote-set-value\",(_e,req)=>{try{h(req)}catch(e){}})}}}catch(e){try{const r=globalThis.require||require,fs=r(\"node:fs\"),os=r(\"node:os\"),p=r(\"node:path\");fs.appendFileSync(p.join(os.tmpdir(),\"wand-remote-bridge.log\"),\"[\"+new Date().toISOString()+\"] [renderer-bind-error] \"+(e&&e.stack||e)+\"\\n\");}catch(_){}return null}})());this.__wandRemoteBridge?.setHandler((e=>{if(!this.#Ee||!e?.target)return!1;return this.#Ee.isActive()?this.#Ee.setValue(e.target,e.value,g.kL.Remote,e.cheatId):!1}));this.__wandRemoteTrainerInfo=e??null;const s=e?.trainerId||null,i=(s?e?.gameId:null)||null,n=(s?e?.supportedVersions:null)||[];if(s===this.#ke&&t===this.#Ee)return;"
},
new PatchEntry
{
Name = "remoteBridgeValueDelta",
SearchHints = new[] { "client-value-changed" },
Target = new Regex(@"#ct\(e,t\)\{t\.push\(e\.onValueSet\(e=>\{this\.status===i\.Connected&&e\.source!==g\.kL\.Remote&&this\.#Me\?\.send\(""client-value-changed"",\{instanceId:this\.#Pe,name:e\.name,value:e\.value,cheatId:e\.cheatId\}\)\}\)\),this\.#Be\(\)\}"),
Patch = "#ct(e,t){t.push(e.onValueSet(e=>{this.status===i.Connected&&e.source!==g.kL.Remote&&this.#Me?.send(\"client-value-changed\",{instanceId:this.#Pe,name:e.name,value:e.value,cheatId:e.cheatId}),this.__wandRemoteBridge?.valueChanged({trainerId:this.#ke,target:e.name,value:e.value,oldValue:e.oldValue,source:String(e.source??\"desktop\"),cheatId:e.cheatId})})),this.#Be()}"
},
new PatchEntry
{
Name = "remoteTooltipPreviewUrl",
SearchHints = new[] { "remote_tooltip.scan_the_qr_code_or_visit_the_site", "remote_tooltip.connect_to_wand_remote" },
Target = new Regex(@"remoteUrl=""wemodwebsite://remote"""),
Patch = "remoteUrl=globalThis.__wandRemoteBridgeUrl||\"" + RemoteWebPanelFallbackUrl + "\""
},
new PatchEntry
{
Name = "remoteQrPreviewUrl",
SearchHints = new[] { "resources/elements/remote-qr-code" },
Resolver = new ResolveContext
{
Handler = (matchContent) =>
@@ -162,4 +178,4 @@ namespace WandEnhancer.Core
};
}
}
}
}
+8
View File
@@ -41,6 +41,14 @@
<!--#region UpdatePopup -->
<s:String x:Key="up_warning">Vor dem Update wird dringend empfohlen, Änderungen rückgängig zu machen, falls sie angewendet wurden</s:String>
<s:String x:Key="up_current_version">Aktuelle Version</s:String>
<s:String x:Key="up_latest_version">Neueste Version</s:String>
<s:String x:Key="up_release_notes">Versionshinweise</s:String>
<s:String x:Key="up_release_notes_unavailable">Für diese Version sind keine Versionshinweise verfügbar.</s:String>
<s:String x:Key="up_show_more">Gesamtes Changelog anzeigen</s:String>
<s:String x:Key="up_show_less">Nur aktuelle Hinweise anzeigen</s:String>
<s:String x:Key="up_loading_changelog">Changelog wird geladen...</s:String>
<s:String x:Key="up_changelog_failed">Das vollständige Changelog konnte nicht geladen werden. Stattdessen werden die aktuellen Hinweise angezeigt.</s:String>
<s:String x:Key="up_update_now">Jetzt aktualisieren</s:String>
<s:String x:Key="up_popup_title">Update verfügbar!</s:String>
<!--#endregion -->
+8
View File
@@ -41,6 +41,14 @@
<!--#region UpdatePopup -->
<s:String x:Key="up_warning">Before updating, it is strongly recommended to roll back modifications if they have been applied</s:String>
<s:String x:Key="up_current_version">Current version</s:String>
<s:String x:Key="up_latest_version">Latest version</s:String>
<s:String x:Key="up_release_notes">Release notes</s:String>
<s:String x:Key="up_release_notes_unavailable">Release notes are unavailable for this release.</s:String>
<s:String x:Key="up_show_more">Show full changelog</s:String>
<s:String x:Key="up_show_less">Show latest notes</s:String>
<s:String x:Key="up_loading_changelog">Loading changelog...</s:String>
<s:String x:Key="up_changelog_failed">Failed to load the full changelog. The latest notes are shown instead.</s:String>
<s:String x:Key="up_update_now">Update now</s:String>
<s:String x:Key="up_popup_title">Update available!</s:String>
<!--#endregion -->
+8
View File
@@ -41,6 +41,14 @@
<!--#region UpdatePopup -->
<s:String x:Key="up_warning">Antes de actualizar, se recomienda encarecidamente revertir las modificaciones si se han aplicado</s:String>
<s:String x:Key="up_current_version">Versión actual</s:String>
<s:String x:Key="up_latest_version">Última versión</s:String>
<s:String x:Key="up_release_notes">Notas de la versión</s:String>
<s:String x:Key="up_release_notes_unavailable">Las notas de la versión no están disponibles para esta versión.</s:String>
<s:String x:Key="up_show_more">Mostrar changelog completo</s:String>
<s:String x:Key="up_show_less">Mostrar solo las notas actuales</s:String>
<s:String x:Key="up_loading_changelog">Cargando changelog...</s:String>
<s:String x:Key="up_changelog_failed">No se pudo cargar el changelog completo. Se muestran las notas actuales.</s:String>
<s:String x:Key="up_update_now">Actualizar ahora</s:String>
<s:String x:Key="up_popup_title">¡Actualización disponible!</s:String>
<!--#endregion -->
+8
View File
@@ -41,6 +41,14 @@
<!--#region UpdatePopup -->
<s:String x:Key="up_warning">Avant la mise à jour, il est fortement recommandé d'annuler les modifications si elles ont été appliquées</s:String>
<s:String x:Key="up_current_version">Version actuelle</s:String>
<s:String x:Key="up_latest_version">Dernière version</s:String>
<s:String x:Key="up_release_notes">Notes de version</s:String>
<s:String x:Key="up_release_notes_unavailable">Les notes de version ne sont pas disponibles pour cette version.</s:String>
<s:String x:Key="up_show_more">Afficher le changelog complet</s:String>
<s:String x:Key="up_show_less">Afficher uniquement les notes actuelles</s:String>
<s:String x:Key="up_loading_changelog">Chargement du changelog...</s:String>
<s:String x:Key="up_changelog_failed">Impossible de charger le changelog complet. Les notes actuelles sont affichées à la place.</s:String>
<s:String x:Key="up_update_now">Mettre à jour maintenant</s:String>
<s:String x:Key="up_popup_title">Mise à jour disponible !</s:String>
<!--#endregion -->
+8
View File
@@ -41,6 +41,14 @@
<!--#region UpdatePopup -->
<s:String x:Key="up_warning">Prima dell'aggiornamento, si consiglia vivamente di annullare le modifiche se sono state applicate</s:String>
<s:String x:Key="up_current_version">Versione corrente</s:String>
<s:String x:Key="up_latest_version">Ultima versione</s:String>
<s:String x:Key="up_release_notes">Note di rilascio</s:String>
<s:String x:Key="up_release_notes_unavailable">Le note di rilascio non sono disponibili per questa versione.</s:String>
<s:String x:Key="up_show_more">Mostra il changelog completo</s:String>
<s:String x:Key="up_show_less">Mostra solo le note correnti</s:String>
<s:String x:Key="up_loading_changelog">Caricamento del changelog...</s:String>
<s:String x:Key="up_changelog_failed">Impossibile caricare il changelog completo. Vengono mostrate solo le note correnti.</s:String>
<s:String x:Key="up_update_now">Aggiorna ora</s:String>
<s:String x:Key="up_popup_title">Aggiornamento disponibile!</s:String>
<!--#endregion -->
+8
View File
@@ -41,6 +41,14 @@
<!--#region UpdatePopup -->
<s:String x:Key="up_warning">アップデート前に、変更が適用されている場合はロールバックすることを強くお勧めします</s:String>
<s:String x:Key="up_current_version">現在のバージョン</s:String>
<s:String x:Key="up_latest_version">最新バージョン</s:String>
<s:String x:Key="up_release_notes">リリースノート</s:String>
<s:String x:Key="up_release_notes_unavailable">このリリースのリリースノートは利用できません。</s:String>
<s:String x:Key="up_show_more">完全な変更履歴を表示</s:String>
<s:String x:Key="up_show_less">最新のリリースノートのみ表示</s:String>
<s:String x:Key="up_loading_changelog">変更履歴を読み込み中...</s:String>
<s:String x:Key="up_changelog_failed">完全な変更履歴を読み込めませんでした。代わりに最新のリリースノートを表示しています。</s:String>
<s:String x:Key="up_update_now">今すぐ更新</s:String>
<s:String x:Key="up_popup_title">アップデート利用可能!</s:String>
<!--#endregion -->
+8
View File
@@ -41,6 +41,14 @@
<!--#region UpdatePopup -->
<s:String x:Key="up_warning">Przed aktualizacją zdecydowanie zaleca się cofnięcie zmian, jeśli zostały zastosowane</s:String>
<s:String x:Key="up_current_version">Aktualna wersja</s:String>
<s:String x:Key="up_latest_version">Najnowsza wersja</s:String>
<s:String x:Key="up_release_notes">Informacje o wydaniu</s:String>
<s:String x:Key="up_release_notes_unavailable">Informacje o wydaniu są niedostępne dla tej wersji.</s:String>
<s:String x:Key="up_show_more">Pokaż cały changelog</s:String>
<s:String x:Key="up_show_less">Pokaż tylko bieżące zmiany</s:String>
<s:String x:Key="up_loading_changelog">Ładowanie changeloga...</s:String>
<s:String x:Key="up_changelog_failed">Nie udało się załadować pełnego changeloga. Zamiast tego wyświetlono bieżące zmiany.</s:String>
<s:String x:Key="up_update_now">Aktualizuj teraz</s:String>
<s:String x:Key="up_popup_title">Dostępna aktualizacja!</s:String>
<!--#endregion -->
+8
View File
@@ -41,6 +41,14 @@
<!--#region UpdatePopup -->
<s:String x:Key="up_warning">Antes de atualizar, é altamente recomendável reverter as modificações se elas foram aplicadas</s:String>
<s:String x:Key="up_current_version">Versão atual</s:String>
<s:String x:Key="up_latest_version">Versão mais recente</s:String>
<s:String x:Key="up_release_notes">Notas da versão</s:String>
<s:String x:Key="up_release_notes_unavailable">As notas da versão não estão disponíveis para esta versão.</s:String>
<s:String x:Key="up_show_more">Mostrar changelog completo</s:String>
<s:String x:Key="up_show_less">Mostrar apenas as notas atuais</s:String>
<s:String x:Key="up_loading_changelog">Carregando changelog...</s:String>
<s:String x:Key="up_changelog_failed">Falha ao carregar o changelog completo. As notas atuais estão sendo exibidas.</s:String>
<s:String x:Key="up_update_now">Atualizar agora</s:String>
<s:String x:Key="up_popup_title">Atualização disponível!</s:String>
<!--#endregion -->
+8
View File
@@ -41,6 +41,14 @@
<!--#region UpdatePopup -->
<s:String x:Key="up_warning">Перед обновлением настоятельно рекомендуется откатить изменения, если они были применены</s:String>
<s:String x:Key="up_current_version">Текущая версия</s:String>
<s:String x:Key="up_latest_version">Новая версия</s:String>
<s:String x:Key="up_release_notes">Что нового</s:String>
<s:String x:Key="up_release_notes_unavailable">Для этого релиза патчноуты недоступны.</s:String>
<s:String x:Key="up_show_more">Показать весь changelog</s:String>
<s:String x:Key="up_show_less">Показать только актуальные изменения</s:String>
<s:String x:Key="up_loading_changelog">Загрузка changelog...</s:String>
<s:String x:Key="up_changelog_failed">Не удалось загрузить полный changelog. Показаны только актуальные изменения.</s:String>
<s:String x:Key="up_update_now">Обновить сейчас</s:String>
<s:String x:Key="up_popup_title">Доступно обновление!</s:String>
<!--#endregion -->
+8
View File
@@ -41,6 +41,14 @@
<!--#region UpdatePopup -->
<s:String x:Key="up_warning">Güncellemeden önce, değişiklikler uygulandıysa geri almak şiddetle tavsiye edilir</s:String>
<s:String x:Key="up_current_version">Geçerli sürüm</s:String>
<s:String x:Key="up_latest_version">En son sürüm</s:String>
<s:String x:Key="up_release_notes">Sürüm notları</s:String>
<s:String x:Key="up_release_notes_unavailable">Bu sürüm için sürüm notları kullanılamıyor.</s:String>
<s:String x:Key="up_show_more">Tüm changelog'u göster</s:String>
<s:String x:Key="up_show_less">Yalnızca güncel notları göster</s:String>
<s:String x:Key="up_loading_changelog">Changelog yükleniyor...</s:String>
<s:String x:Key="up_changelog_failed">Tam changelog yüklenemedi. Bunun yerine güncel notlar gösteriliyor.</s:String>
<s:String x:Key="up_update_now">Şimdi güncelle</s:String>
<s:String x:Key="up_popup_title">Güncelleme mevcut!</s:String>
<!--#endregion -->
+8
View File
@@ -41,6 +41,14 @@
<!--#region UpdatePopup -->
<s:String x:Key="up_warning">Перед оновленням наполегливо рекомендується відкотити зміни, якщо вони були застосовані</s:String>
<s:String x:Key="up_current_version">Поточна версія</s:String>
<s:String x:Key="up_latest_version">Остання версія</s:String>
<s:String x:Key="up_release_notes">Нотатки до релізу</s:String>
<s:String x:Key="up_release_notes_unavailable">Нотатки до цього релізу недоступні.</s:String>
<s:String x:Key="up_show_more">Показати весь список змін</s:String>
<s:String x:Key="up_show_less">Показати лише актуальні зміни</s:String>
<s:String x:Key="up_loading_changelog">Завантаження списку змін...</s:String>
<s:String x:Key="up_changelog_failed">Не вдалося завантажити повний список змін. Натомість показано лише актуальні зміни.</s:String>
<s:String x:Key="up_update_now">Оновити зараз</s:String>
<s:String x:Key="up_popup_title">Доступне оновлення!</s:String>
<!--#endregion -->
+8
View File
@@ -41,6 +41,14 @@
<!--#region UpdatePopup -->
<s:String x:Key="up_warning">在更新之前,强烈建议回滚已应用的修改</s:String>
<s:String x:Key="up_current_version">当前版本</s:String>
<s:String x:Key="up_latest_version">最新版本</s:String>
<s:String x:Key="up_release_notes">更新说明</s:String>
<s:String x:Key="up_release_notes_unavailable">此版本的更新说明不可用。</s:String>
<s:String x:Key="up_show_more">显示完整更新日志</s:String>
<s:String x:Key="up_show_less">仅显示当前说明</s:String>
<s:String x:Key="up_loading_changelog">正在加载更新日志...</s:String>
<s:String x:Key="up_changelog_failed">无法加载完整更新日志。当前仅显示本次说明。</s:String>
<s:String x:Key="up_update_now">立即更新</s:String>
<s:String x:Key="up_popup_title">有更新可用!</s:String>
<!--#endregion -->
+2 -2
View File
@@ -51,5 +51,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.7.0")]
[assembly: AssemblyFileVersion("1.0.7.0")]
[assembly: AssemblyVersion("1.0.8.0")]
[assembly: AssemblyFileVersion("1.0.8.0")]
+155 -4
View File
@@ -1,15 +1,23 @@
using System;
using System.Globalization;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using System.Windows;
using Newtonsoft.Json;
namespace WandEnhancer.Utils
{
public class UpdateReleaseInfo
{
public string Version { get; set; }
public string LatestNotes { get; set; }
}
public class GitHubRelease
{
public class AssetsType
@@ -26,11 +34,19 @@ namespace WandEnhancer.Utils
[JsonProperty("assets")]
public AssetsType[] Assets { get; set; }
[JsonProperty("body")]
public string Body { get; set; }
[JsonProperty("published_at")]
public DateTimeOffset PublishedAt { get; set; }
}
public class Updater
{
private GitHubRelease _release = null;
private UpdateReleaseInfo _updateInfo = null;
private string _fullChangelog = null;
private static readonly HttpClient _httpClient = new HttpClient()
{
DefaultRequestHeaders =
@@ -40,6 +56,7 @@ namespace WandEnhancer.Utils
};
private static readonly string ApiUrl = $"https://api.github.com/repos/{Constants.Owner}/{Constants.RepoName}/releases/latest";
private static readonly string ReleasesApiUrl = $"https://api.github.com/repos/{Constants.Owner}/{Constants.RepoName}/releases?per_page=20";
public async Task<bool> CheckForUpdates()
{
try
@@ -48,22 +65,59 @@ namespace WandEnhancer.Utils
var response = await _httpClient.GetAsync(ApiUrl);
response.EnsureSuccessStatusCode();
_release = JsonConvert.DeserializeObject<GitHubRelease>(await response.Content.ReadAsStringAsync());
_updateInfo = null;
_fullChangelog = null;
if (_release == null)
{
return false;
}
var latestVersion = new Version(_release.TagName);
var latestVersion = ParseVersion(_release.TagName);
if (latestVersion <= currentVersion)
{
return false;
}
_updateInfo = new UpdateReleaseInfo
{
Version = NormalizeVersion(_release.TagName),
LatestNotes = NormalizeText(_release.Body)
};
return latestVersion > currentVersion;
return true;
}
catch (Exception e)
catch (Exception)
{
return false;
}
}
public async Task<UpdateReleaseInfo> GetUpdateInfoAsync()
{
if (_updateInfo != null)
{
return _updateInfo;
}
return await CheckForUpdates()
? _updateInfo
: null;
}
public async Task<string> GetFullChangelogAsync()
{
if (!string.IsNullOrWhiteSpace(_fullChangelog))
{
return NormalizeText(_fullChangelog);
}
_fullChangelog = await TryLoadFullChangelogAsync();
return NormalizeText(_fullChangelog);
}
public async Task Update()
{
if (_release == null)
@@ -125,6 +179,103 @@ namespace WandEnhancer.Utils
throw new Exception($"Update failed: {ex.Message}");
}
}
private static Version ParseVersion(string versionTag)
{
return new Version(NormalizeVersion(versionTag));
}
private static string NormalizeVersion(string versionTag)
{
if (string.IsNullOrWhiteSpace(versionTag))
{
throw new ArgumentException("Version tag cannot be empty.", nameof(versionTag));
}
return versionTag.Trim().TrimStart('v', 'V');
}
private static string NormalizeText(string text)
{
if (string.IsNullOrWhiteSpace(text))
{
return null;
}
return NormalizeLineEndings(text).Trim();
}
private static string NormalizeLineEndings(string text)
{
return text
.Replace("\r\n", "\n")
.Replace('\r', '\n');
}
private static async Task<string> TryLoadFullChangelogAsync()
{
return await TryBuildReleaseHistoryAsync();
}
private static async Task<string> TryBuildReleaseHistoryAsync()
{
try
{
var response = await _httpClient.GetAsync(ReleasesApiUrl);
if (!response.IsSuccessStatusCode)
{
return null;
}
var releases = JsonConvert.DeserializeObject<GitHubRelease[]>(await response.Content.ReadAsStringAsync());
if (releases == null || releases.Length == 0)
{
return null;
}
return BuildReleaseHistory(releases);
}
catch
{
return null;
}
}
private static string BuildReleaseHistory(GitHubRelease[] releases)
{
var builder = new StringBuilder();
foreach (var release in releases.Where(item => !string.IsNullOrWhiteSpace(item?.TagName)))
{
if (builder.Length > 0)
{
builder.AppendLine();
builder.AppendLine();
}
builder.Append("## [")
.Append(NormalizeVersion(release.TagName))
.Append("]");
if (release.PublishedAt != default(DateTimeOffset))
{
builder.Append(" - ")
.Append(release.PublishedAt.UtcDateTime.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture));
}
var notes = NormalizeText(release.Body);
if (string.IsNullOrWhiteSpace(notes))
{
continue;
}
builder.AppendLine();
builder.AppendLine();
builder.Append(notes);
}
return NormalizeText(builder.ToString());
}
}
}
+17 -4
View File
@@ -185,10 +185,19 @@ namespace WandEnhancer.View.MainWindow
});
}
private void OnUpdate(object param)
private async void OnUpdate(object param)
{
MainWindow.Instance.OpenPopup(new UpdatePopup(() =>
var updateInfo = await _updater.GetUpdateInfoAsync();
if (updateInfo == null)
{
Log("No update details are available right now.", ELogType.Warn);
return;
}
MainWindow.Instance.OpenPopup(new UpdatePopup(Constants.Version.ToString(), updateInfo.Version,
updateInfo.LatestNotes, () =>
{
MainWindow.Instance.ClosePopup();
Task.Run(async () =>
{
try
@@ -203,7 +212,7 @@ namespace WandEnhancer.View.MainWindow
Log("WandEnhancer updated successfully. Restarting...", ELogType.Success);
});
}), Application.Current.FindResource("up_popup_title") as string);
}, () => _updater.GetFullChangelogAsync()), Application.Current.FindResource("up_popup_title") as string);
}
private void OnOpenSettings(object param)
@@ -271,7 +280,11 @@ namespace WandEnhancer.View.MainWindow
public MainWindowVm(MainWindow view)
{
Task.Run(async () => IsUpdateAvailable = await _updater.CheckForUpdates());
Task.Run(async () =>
{
var isUpdateAvailable = await _updater.CheckForUpdates();
Application.Current.Dispatcher.Invoke(() => IsUpdateAvailable = isUpdateAvailable);
});
_view = view;
SetFolderPathCommand = new RelayCommand(OnFolderPathSelection);
ApplyPatchCommand = new RelayCommand(OnPatching);
+92 -8
View File
@@ -3,18 +3,102 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WandEnhancer.View.Popups"
mc:Ignorable="d"
d:DesignHeight="Auto" d:DesignWidth="Auto"
Background="{DynamicResource Background}"
Foreground="{DynamicResource MutedForeground}"
FontWeight="Medium"
FontSize="13">
<StackPanel>
<TextBlock Foreground="Red" MaxWidth="320" TextAlignment="Center" Text="{DynamicResource up_warning}" TextWrapping="Wrap" />
<Button Padding="0 5 0 5" Margin="0 15 0 0" Content="{DynamicResource up_update_now}"
Click="OnUpdateClick" />
</StackPanel>
<Grid MinWidth="500" MaxWidth="620">
<Grid.Resources>
<Style x:Key="UpdateActionButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="{DynamicResource Primary}" />
<Setter Property="BorderBrush" Value="{DynamicResource Primary}" />
<Setter Property="Foreground" Value="{DynamicResource PrimaryForeground}" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource Primary}" />
<Setter Property="BorderBrush" Value="{DynamicResource Primary}" />
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Grid.Row="0" HorizontalAlignment="Left" MaxWidth="560"
Padding="9 4" CornerRadius="4"
Background="{DynamicResource Muted}">
<DockPanel LastChildFill="True">
<Ellipse Width="7" Height="7" Fill="{DynamicResource Destructive}"
Margin="0 0 8 0" VerticalAlignment="Center" />
<TextBlock Foreground="{DynamicResource Foreground}" FontSize="11.5"
Text="{DynamicResource up_warning}" TextWrapping="Wrap" />
</DockPanel>
</Border>
<WrapPanel Grid.Row="1" Margin="0 10 0 0" Orientation="Horizontal">
<Border Padding="8 4" Margin="0 0 8 0" CornerRadius="4"
Background="{DynamicResource Muted}" BorderBrush="{DynamicResource Border}" BorderThickness="1">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0 0 7 0" VerticalAlignment="Center"
Foreground="{DynamicResource MutedForeground}" FontSize="10.5"
Text="{DynamicResource up_current_version}" />
<TextBlock x:Name="CurrentVersionValue" VerticalAlignment="Center"
Foreground="{DynamicResource Foreground}" FontSize="12.5" FontWeight="Bold" />
</StackPanel>
</Border>
<Border Padding="8 4" CornerRadius="4"
Background="{DynamicResource Primary}" BorderThickness="1">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0 0 7 0" VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryForeground}" FontSize="10.5"
Text="{DynamicResource up_latest_version}" />
<TextBlock x:Name="LatestVersionValue" VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryForeground}" FontSize="12.5" FontWeight="Bold" />
</StackPanel>
</Border>
</WrapPanel>
<TextBlock Grid.Row="2" Margin="0 12 0 6" Foreground="{DynamicResource Foreground}"
FontSize="14" FontWeight="Bold" Text="{DynamicResource up_release_notes}" />
<Border Grid.Row="3"
Background="{DynamicResource Muted}" BorderBrush="{DynamicResource Border}" BorderThickness="1" CornerRadius="4">
<ScrollViewer x:Name="NotesScrollViewer"
VerticalScrollBarVisibility="Hidden"
HorizontalScrollBarVisibility="Disabled"
CanContentScroll="False">
<TextBox x:Name="NotesTextBlock" Background="Transparent"
BorderBrush="Transparent"
Padding="12"
BorderThickness="0" IsReadOnly="True"
Foreground="{DynamicResource Foreground}"
TextWrapping="Wrap" />
</ScrollViewer>
</Border>
<Grid Grid.Row="4" Margin="0 12 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button x:Name="ShowMoreButton" Grid.Column="1" Margin="0 0 10 0"
Padding="12 5" Content="{DynamicResource up_show_more}"
Click="OnShowMoreClick" />
<Button Grid.Column="2" Padding="18 5" Style="{StaticResource UpdateActionButton}"
Content="{DynamicResource up_update_now}"
Click="OnUpdateClick" />
</Grid>
</Grid>
</UserControl>
+74 -1
View File
@@ -1,4 +1,5 @@
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
@@ -7,16 +8,88 @@ namespace WandEnhancer.View.Popups
public partial class UpdatePopup : UserControl
{
private readonly Action _onUpdate;
private readonly Func<Task<string>> _loadFullChangelog;
private readonly string _latestNotes;
private string _fullChangelog;
private bool _showingFullChangelog;
public UpdatePopup(Action onUpdate)
public UpdatePopup(string currentVersion, string latestVersion, string latestNotes, Action onUpdate,
Func<Task<string>> loadFullChangelog)
{
_onUpdate = onUpdate;
_loadFullChangelog = loadFullChangelog;
InitializeComponent();
CurrentVersionValue.Text = currentVersion;
LatestVersionValue.Text = latestVersion;
_latestNotes = string.IsNullOrWhiteSpace(latestNotes)
? GetResourceText("up_release_notes_unavailable")
: latestNotes;
SetNotesText(_latestNotes);
ShowMoreButton.Visibility = loadFullChangelog == null ? Visibility.Collapsed : Visibility.Visible;
}
private void OnUpdateClick(object sender, RoutedEventArgs e)
{
_onUpdate();
}
private async void OnShowMoreClick(object sender, RoutedEventArgs e)
{
if (_loadFullChangelog == null)
{
return;
}
if (_showingFullChangelog)
{
SetNotesText(_latestNotes);
ShowMoreButton.Content = GetResourceText("up_show_more");
_showingFullChangelog = false;
return;
}
if (string.IsNullOrWhiteSpace(_fullChangelog))
{
ShowMoreButton.IsEnabled = false;
ShowMoreButton.Content = GetResourceText("up_loading_changelog");
try
{
_fullChangelog = await _loadFullChangelog();
}
finally
{
ShowMoreButton.IsEnabled = true;
}
}
if (string.IsNullOrWhiteSpace(_fullChangelog))
{
ShowMoreButton.Content = GetResourceText("up_show_more");
SetNotesText(string.Concat(
_latestNotes,
Environment.NewLine,
Environment.NewLine,
GetResourceText("up_changelog_failed")));
return;
}
SetNotesText(_fullChangelog);
ShowMoreButton.Content = GetResourceText("up_show_less");
_showingFullChangelog = true;
}
private void SetNotesText(string text)
{
NotesTextBlock.Text = text ?? string.Empty;
NotesScrollViewer.ScrollToTop();
}
private static string GetResourceText(string key)
{
return Application.Current.TryFindResource(key) as string ?? string.Empty;
}
}
}
+11 -12
View File
@@ -40,8 +40,11 @@
<PropertyGroup>
<StartupObject>WandEnhancer.Program</StartupObject>
<CMakeSourceDir>..\tools\asar-fuses-bypass</CMakeSourceDir>
<CMakeBuildDir>$(CMakeSourceDir)\cmake-build-release</CMakeBuildDir>
<ProxyDllPath>$(CMakeBuildDir)\version.dll</ProxyDllPath>
<NativeBuildRoot>..\.tmp\cmake</NativeBuildRoot>
<NativeBuildConfiguration Condition="'$(Configuration)' == 'Debug'">Debug</NativeBuildConfiguration>
<NativeBuildConfiguration Condition="'$(NativeBuildConfiguration)' == ''">Release</NativeBuildConfiguration>
<CMakeBuildDir>$(NativeBuildRoot)\asar-fuses-bypass</CMakeBuildDir>
<ProxyDllPath>$(CMakeBuildDir)\$(NativeBuildConfiguration)\version.dll</ProxyDllPath>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
@@ -151,10 +154,10 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AsarSharp\AsarSharp.csproj">
<Project>{beaa604a-402a-4387-8903-a53fc913a26e}</Project>
<Project>{BEAA604A-402A-4387-8903-A53FC913A26E}</Project>
<Name>AsarSharp</Name>
</ProjectReference>
</ItemGroup>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(ProxyDllPath)">
<LogicalName>proxydll</LogicalName>
@@ -164,12 +167,6 @@
<EmbeddedResource Include="..\web-panel\dist\**\*.*" Condition="Exists('..\web-panel\dist\index.html')">
<LogicalName>remote-panel/dist/%(RecursiveDir)%(Filename)%(Extension)</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="..\web-panel\bridge\wand-remote-bridge.cjs" Condition="Exists('..\web-panel\bridge\wand-remote-bridge.cjs')">
<LogicalName>remote-panel/bridge.cjs</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="..\web-panel\scripts\default\*.js" Condition="Exists('..\web-panel\scripts\default')">
<LogicalName>remote-panel/renderer-scripts/%(Filename)%(Extension)</LogicalName>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
@@ -180,7 +177,7 @@
<Error Condition="!Exists('..\packages\ILRepack.2.0.41\build\ILRepack.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\ILRepack.2.0.41\build\ILRepack.props'))" />
</Target>
<Target Name="EmbedProxyDll" BeforeTargets="BeforeBuild">
<Target Name="ValidateNativeArtifacts" BeforeTargets="BeforeBuild">
<Error Text="Proxy DLL not found: $(ProxyDllPath)"
Condition="!Exists('$(ProxyDllPath)')" />
@@ -195,13 +192,15 @@
</PropertyGroup>
<ItemGroup>
<AssemblyList Include="$(OutputPath)*.dll" />
<AssemblyList Include="$(OutputPath)*.dll" />
</ItemGroup>
<PropertyGroup>
<DllList>@(AssemblyList->'%(FullPath)', ' ')</DllList>
</PropertyGroup>
<Delete Files="$(OutputPath)$(AssemblyName).pdb" ContinueOnError="true" />
<Exec Command="&quot;$(ILRepackExe)&quot; /allowMultiple /copyattrs /out:&quot;$(OutputPath)$(AssemblyName).exe&quot; &quot;$(MainAssembly)&quot; $(DllList)" />
<Delete Files="@(AssemblyList)" ContinueOnError="true" />
</Target>
</Project>