Compare commits

...

9 Commits

Author SHA1 Message Date
e9824c9053 Comment AppImage builds
It randomly started erroring in GitHub actions with exit code 8 but only sometimes, and I don't have the patience to debug it. I don't even use linux lol
2025-07-28 19:35:47 -05:00
d2037da65f Nullify Locales (ryubing/ryujinx!83)
See merge request ryubing/ryujinx!83
2025-07-28 19:35:47 -05:00
8fe7d54f85 Changes to uk_UA (ryubing/ryujinx!84)
See merge request ryubing/ryujinx!84
2025-07-28 19:35:47 -05:00
b8f9f3e16a Edit TileIDs.cs (ryubing/ryujinx!81)
See merge request ryubing/ryujinx!81
2025-07-28 19:35:47 -05:00
7b1c8717ef Edit compatibility.csv (ryubing/ryujinx!80)
See merge request ryubing/ryujinx!80
2025-07-28 19:35:47 -05:00
6122fa204f simplify completion callback 2025-07-28 17:58:54 -05:00
217fd90568 Use a single parser execution per text box update
Before it would: parse, compile, then execute for getting the formatted result, then run the suggestions function which did parsing on its own. It has now been moved to the new ParseAndGetCompletions function which returns the used ParserResult, saving some work.
2025-07-27 17:35:06 -05:00
95157c0cfd make ApplicationLibrary implement IStarscriptObject 2025-07-27 17:32:39 -05:00
8a3ccaafe3 basic starscript support + a textbox that provides the starscript executed result & compiled script as well as suggestions 2025-07-27 01:09:59 -05:00
17 changed files with 1074 additions and 686 deletions

View File

@ -105,46 +105,48 @@ jobs:
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz"
shell: bash
# If anyone wants to look into why appimagetool randomly errors with exit code 8, that would be cool
- name: Build AppImage (Linux)
if: matrix.platform.os == 'ubuntu-latest'
run: |
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
PLATFORM_NAME="${{ matrix.platform.name }}"
sudo apt install -y zsync desktop-file-utils appstream
mkdir -p tools
export PATH="$PATH:$(readlink -f tools)"
# Setup appimagetool
wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
chmod +x tools/appimagetool
chmod +x distribution/linux/appimage/build-appimage.sh
# Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
if [ "$PLATFORM_NAME" = "linux-x64" ]; then
ARCH_NAME=x64
export ARCH=x86_64
elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
ARCH_NAME=arm64
export ARCH=aarch64
else
echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
exit 1
fi
export UFLAG="gh-releases-zsync|${{ secrets.RC_OWNER }}${{ secrets.RC_CANARY_NAME }}|latest|*-$ARCH_NAME.AppImage.zsync"
BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
pushd publish_appimage
mv Ryujinx.AppImage ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage
mv Ryujinx.AppImage.zsync ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
popd
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage"
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync"
shell: bash
# - name: Build AppImage (Linux)
# if: matrix.platform.os == 'ubuntu-latest'
# run: |
# BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
# PLATFORM_NAME="${{ matrix.platform.name }}"
#
# sudo apt install -y zsync desktop-file-utils appstream
#
# mkdir -p tools
# export PATH="$PATH:$(readlink -f tools)"
#
# # Setup appimagetool
# wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
# chmod +x tools/appimagetool
# chmod +x distribution/linux/appimage/build-appimage.sh
#
# # Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
# if [ "$PLATFORM_NAME" = "linux-x64" ]; then
# ARCH_NAME=x64
# export ARCH=x86_64
# elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
# ARCH_NAME=arm64
# export ARCH=aarch64
# else
# echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
# exit 1
# fi
#
# export UFLAG="gh-releases-zsync|${{ secrets.RC_OWNER }}${{ secrets.RC_CANARY_NAME }}|latest|*-$ARCH_NAME.AppImage.zsync"
# BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
#
# pushd publish_appimage
# mv Ryujinx.AppImage ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage
# mv Ryujinx.AppImage.zsync ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
# popd
#
# gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage"
# gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync"
# shell: bash
macos_release:
name: Release MacOS universal

View File

@ -96,46 +96,48 @@ jobs:
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build AppImage (Linux)
if: matrix.platform.os == 'ubuntu-latest'
run: |
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
PLATFORM_NAME="${{ matrix.platform.name }}"
sudo apt install -y zsync desktop-file-utils appstream
mkdir -p tools
export PATH="$PATH:$(readlink -f tools)"
# Setup appimagetool
wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
chmod +x tools/appimagetool
chmod +x distribution/linux/appimage/build-appimage.sh
# Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
if [ "$PLATFORM_NAME" = "linux-x64" ]; then
ARCH_NAME=x64
export ARCH=x86_64
elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
ARCH_NAME=arm64
export ARCH=aarch64
else
echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
exit 1
fi
export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync"
BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
pushd publish_appimage
mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
mv Ryujinx.AppImage.zsync ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
popd
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage"
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync"
shell: bash
# If anyone wants to look into why appimagetool randomly errors with exit code 8, that would be cool
# - name: Build AppImage (Linux)
# if: matrix.platform.os == 'ubuntu-latest'
# run: |
# BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
# PLATFORM_NAME="${{ matrix.platform.name }}"
#
# sudo apt install -y zsync desktop-file-utils appstream
#
# mkdir -p tools
# export PATH="$PATH:$(readlink -f tools)"
#
# # Setup appimagetool
# wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
# chmod +x tools/appimagetool
# chmod +x distribution/linux/appimage/build-appimage.sh
#
# # Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
# if [ "$PLATFORM_NAME" = "linux-x64" ]; then
# ARCH_NAME=x64
# export ARCH=x86_64
# elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
# ARCH_NAME=arm64
# export ARCH=aarch64
# else
# echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
# exit 1
# fi
#
# export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync"
# BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
#
# pushd publish_appimage
# mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
# mv Ryujinx.AppImage.zsync ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
# popd
#
# gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage"
# gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync"
# shell: bash
macos_release:
name: Release MacOS universal

View File

@ -55,6 +55,7 @@
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
<PackageVersion Include="SPB" Version="0.0.4-build32" />
<PackageVersion Include="Starscript.Net" Version="1.0.36" />
<PackageVersion Include="System.IO.Hashing" Version="9.0.2" />
<PackageVersion Include="System.Management" Version="9.0.2" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />

File diff suppressed because it is too large Load Diff

View File

@ -2257,6 +2257,7 @@
010086F0064CE000,"Poi: Explorer Edition",nvdec,playable,2021-01-21 19:32:00
0100EB6012FD2000,"Poison Control",,playable,2021-05-16 14:01:54
010072400E04A000,"Pokémon Café ReMix",,playable,2021-08-17 20:00:04
010008c01e742000,"Pokémon Friends",crash;services,menus,2025-07-24 13:32:00
01003D200BAA2000,"Pokémon Mystery Dungeon™: Rescue Team DX",mac-bug,playable,2024-01-21 00:16:32
01008DB008C2C000,"Pokémon Shield + Pokémon Shield Expansion Pass",deadlock;crash;online-broken;ldn-works;LAN,ingame,2024-08-12 07:20:22
0100ABF008968000,"Pokémon Sword + Pokémon Sword Expansion Pass",deadlock;crash;online-broken;ldn-works;LAN,ingame,2024-08-26 15:40:37

1 title_id game_name labels status last_updated
2257 010086F0064CE000 Poi: Explorer Edition nvdec playable 2021-01-21 19:32:00
2258 0100EB6012FD2000 Poison Control playable 2021-05-16 14:01:54
2259 010072400E04A000 Pokémon Café ReMix playable 2021-08-17 20:00:04
2260 010008c01e742000 Pokémon Friends crash;services menus 2025-07-24 13:32:00
2261 01003D200BAA2000 Pokémon Mystery Dungeon™: Rescue Team DX mac-bug playable 2024-01-21 00:16:32
2262 01008DB008C2C000 Pokémon Shield + Pokémon Shield Expansion Pass deadlock;crash;online-broken;ldn-works;LAN ingame 2024-08-12 07:20:22
2263 0100ABF008968000 Pokémon Sword + Pokémon Sword Expansion Pass deadlock;crash;online-broken;ldn-works;LAN ingame 2024-08-26 15:40:37

View File

@ -33,7 +33,7 @@ namespace Ryujinx.BuildValidationTasks
LocalesJson json;
if (isGitRunner && data.Contains("\r\n"))
throw new FormatException("locales.json is using CRLF line endings! It should be using LF line endings, build locally to fix...");
throw new FormatException("locales.json is using CRLF line endings! It should be using LF line endings, rebuild locally to fix...");
try
{
@ -86,7 +86,7 @@ namespace Ryujinx.BuildValidationTasks
}
if (isGitRunner && encounteredIssue)
throw new JsonException("1 or more locales are invalid!");
throw new JsonException("1 or more locales are invalid! Rebuild locally to fix...");
string jsonString = JsonSerializer.Serialize(json, _jsonOptions);
@ -102,6 +102,7 @@ namespace Ryujinx.BuildValidationTasks
struct LocalesJson
{
public Dictionary<string, string> Info { get; set; }
public List<string> Languages { get; set; }
public List<LocalesEntry> Locales { get; set; }
}

View File

@ -10,8 +10,18 @@
<Exec WorkingDirectory="$(ProjectDir)bin\Debug\$(TargetFramework)\"
Command="dotnet Ryujinx.BuildValidationTasks.dll &quot;$(ProjectDir)..\..\\&quot;"
ConsoleToMsBuild="true"
Condition="'$(RuntimeIdentifier)' == ''"
/>
Condition="'$(RuntimeIdentifier)' == ''"
IgnoreExitCode="true">
<Output TaskParameter="ConsoleOutput" PropertyName="OutputOfExec" />
<Output TaskParameter="ExitCode" PropertyName="BuildExitCode"/>
</Exec>
<PropertyGroup Condition=" '$(OutputOfExec.IndexOf(Unhandled exception))' != '-1'">
<ErrorOutput>$(OutputOfExec.Substring($(OutputOfExec.IndexOf("Unhandled exception"))))</ErrorOutput>
<ErrorOutput>$(ErrorOutput.Substring(0, $(ErrorOutput.IndexOf(';'))))</ErrorOutput>
</PropertyGroup>
<Error Text="$(ErrorOutput)" Condition=" '$(BuildExitCode)' != '0'"/>
</Target>
</Project>
</Project>

View File

@ -93,6 +93,7 @@ namespace Ryujinx.Common
//The Pokémon Franchise
"0100f4300bf2c000", // New Pokémon Snap
"0100000011d90000", // Pokémon Brilliant Diamond
"010008c01e742000", // Pokémon Friends
"01001f5010dfa000", // Pokémon Legends: Arceus
"01003d200baa2000", // Pokémon Mystery Dungeon - Rescue Team DX
"0100a3d008c5c000", // Pokémon Scarlet

View File

@ -73,6 +73,7 @@
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
<PackageReference Include="SPB" />
<PackageReference Include="Starscript.Net"/>
<PackageReference Include="SharpZipLib" />
</ItemGroup>

View File

@ -14,6 +14,7 @@ using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Ava.Systems.Configuration.System;
using Ryujinx.Ava.Systems.Starscript;
using Ryujinx.Ava.Utilities;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
@ -25,6 +26,7 @@ using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Loaders.Npdm;
using Ryujinx.HLE.Loaders.Processes.Extensions;
using Ryujinx.HLE.Utilities;
using Starscript;
using System;
using System.Collections.Generic;
using System.IO;
@ -41,7 +43,7 @@ using TimeSpan = System.TimeSpan;
namespace Ryujinx.Ava.Systems.AppLibrary
{
public class ApplicationLibrary
public class ApplicationLibrary : IStarscriptObject
{
public Language DesiredLanguage { get; set; }
public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated;
@ -1611,5 +1613,14 @@ namespace Ryujinx.Ava.Systems.AppLibrary
ApplicationData newApplication = newApplications.First(it => it.IdBase == appIdBase);
_applications.AddOrUpdate(newApplication);
}
private ValueMap _starscriptMap;
public ValueMap ToStarscript()
{
_starscriptMap ??= StarscriptHelper.Wrap(this);
return _starscriptMap;
}
}
}

View File

@ -0,0 +1,29 @@
using Ryujinx.Ava.Systems.AppLibrary;
using Ryujinx.Common;
using Starscript;
namespace Ryujinx.Ava.Systems.Starscript
{
public static class RyujinxStarscript
{
public static readonly StarscriptHypervisor Hypervisor = StarscriptHypervisor.Create().WithStandardLibrary(true);
static RyujinxStarscript()
{
Hypervisor.Set("ryujinx.releaseChannel",
ReleaseInformation.IsCanaryBuild
? "Canary"
: ReleaseInformation.IsReleaseBuild
? "Stable"
: "Custom");
Hypervisor.Set("ryujinx.version", Program.Version);
Hypervisor.Set("appLibrary", RyujinxApp.MainWindow.ApplicationLibrary);
Hypervisor.Set("currentApplication", () =>
RyujinxApp.MainWindow.ApplicationLibrary.FindApplication(
RyujinxApp.MainWindow.ViewModel.AppHost?.ApplicationId ?? 0,
out ApplicationData appData)
? StarscriptHelper.Wrap(appData)
: Value.Null);
}
}
}

View File

@ -0,0 +1,68 @@
using Gommon;
using Ryujinx.Ava.Systems.AppLibrary;
using Starscript;
using System;
namespace Ryujinx.Ava.Systems.Starscript
{
public static class StarscriptHelper
{
public static ValueMap Wrap(ApplicationLibrary appLib)
{
ValueMap lMap = new();
lMap.Set("appCount", () => appLib.Applications.Count);
lMap.Set("dlcCount", () => appLib.DownloadableContents.Count);
lMap.Set("updateCount", () => appLib.TitleUpdates.Count);
lMap.Set("has", ctx =>
{
ulong titleId;
try
{
titleId = ctx.Constrain(Constraint.ExactlyOneArgument).NextString(1).ToULong();
}
catch (FormatException)
{
throw ctx.Error(
$"Invalid input to {ctx.FormattedName}; input must be a hexadecimal number in a string.");
}
return appLib.FindApplication(titleId, out _);
});
lMap.Set("get", ctx =>
{
ulong titleId;
try
{
titleId = ctx.Constrain(Constraint.ExactlyOneArgument).NextString(1).ToULong();
}
catch (FormatException)
{
throw ctx.Error(
$"Invalid input to {ctx.FormattedName}; input must be a hexadecimal number in a string.");
}
return appLib.FindApplication(titleId,
out ApplicationData applicationData)
? Wrap(applicationData)
: null;
});
return lMap;
}
public static ValueMap Wrap(ApplicationData appData)
{
ValueMap aMap = new();
aMap.Set("name", appData.Name);
aMap.Set("version", appData.Version);
aMap.Set("developer", appData.Developer);
aMap.Set("fileExtension", appData.FileExtension);
aMap.Set("fileSize", appData.FileSizeString);
aMap.Set("hasLdnGames", appData.HasLdnGames);
aMap.Set("timePlayed", appData.TimePlayedString);
aMap.Set("isFavorite", appData.Favorite);
return aMap;
}
}
}

View File

@ -0,0 +1,21 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="using:Ryujinx.Ava.Systems.Starscript"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Ryujinx.Ava.Systems.Starscript.StarscriptTextBox"
x:DataType="local:StarscriptTextBoxViewModel">
<StackPanel Spacing="10">
<TextBlock Text="{Binding ErrorMessage}" IsVisible="{Binding HasError}"/>
<TextBlock Text="{Binding CurrentScriptResult}" IsVisible="{Binding !HasError}"/>
<AutoCompleteBox Name="InputBox"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
FilterMode="Custom"
MinimumPrefixLength="0"
MaxDropDownHeight="400">
</AutoCompleteBox>
</StackPanel>
</UserControl>

View File

@ -0,0 +1,87 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Styling;
using FluentAvalonia.UI.Controls;
using Humanizer;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Starscript;
using Starscript.Internal;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Ryujinx.Ava.Systems.Starscript
{
public partial class StarscriptTextBox : RyujinxControl<StarscriptTextBoxViewModel>
{
public IReadOnlyList<string> CurrentSuggestions => ViewModel.CurrentSuggestions;
public ParserResult CurrentScriptSource => ViewModel.CurrentScriptSource;
public Exception Exception => ViewModel.Exception;
public Script CurrentScript => ViewModel.CurrentScript;
public StringSegment CurrentScriptResult => ViewModel.CurrentScriptResult;
public StarscriptTextBox()
{
InitializeComponent();
InputBox.AsyncPopulator = GetSuggestionsAsync;
InputBox.MinimumPopulateDelay = 0.Seconds();
InputBox.TextFilter = (_, _) => true;
InputBox.TextSelector = (text, suggestion) =>
{
if (text is not null && suggestion is null)
return text;
if (text is null && suggestion is not null)
return suggestion;
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
if (text is null && suggestion is null)
return string.Empty;
var sb = new StringBuilder(text.Length + suggestion.Length + 1);
sb.Append(text);
for (int i = 0; i < suggestion.Length - 1; i++)
{
if (text.EndsWith(suggestion[..(suggestion.Length - i - 1)]))
{
suggestion = suggestion[(suggestion.Length - i - 1)..];
break;
}
}
sb.Append(suggestion);
return sb.ToString();
};
Style textStyle = new(x => x.OfType<AutoCompleteBox>().Descendant().OfType<TextBlock>());
textStyle.Setters.Add(new Setter(MarginProperty, new Thickness(0, 0)));
Styles.Add(textStyle);
}
private Task<IEnumerable<object>> GetSuggestionsAsync(string input, CancellationToken token)
=> Task.FromResult(ViewModel.GetSuggestions(input, token));
public static StarscriptTextBox Create(StarscriptHypervisor hv)
=> new() { ViewModel = new StarscriptTextBoxViewModel(hv) };
public static async Task Show()
{
ContentDialog contentDialog = new()
{
PrimaryButtonText = string.Empty,
SecondaryButtonText = string.Empty,
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
Content = new StarscriptTextBox { ViewModel = new() }
};
await ContentDialogHelper.ShowAsync(contentDialog.ApplyStyles());
}
}
}

View File

@ -0,0 +1,126 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Ryujinx.Ava.UI.ViewModels;
using Starscript;
using Starscript.Internal;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
namespace Ryujinx.Ava.Systems.Starscript
{
public partial class StarscriptTextBoxViewModel : BaseModel
{
private readonly StarscriptHypervisor _hv;
public StarscriptTextBoxViewModel(StarscriptHypervisor hv = null)
{
_hv = hv ?? RyujinxStarscript.Hypervisor;
}
public ObservableCollection<string> CurrentSuggestions { get; } = [];
[ObservableProperty] private bool _hasError;
[ObservableProperty] private StringSegment _currentScriptResult;
[ObservableProperty] private string _errorMessage;
private Exception _exception;
private ParserResult _currentScriptSource;
private Script _currentScript;
public Exception Exception
{
get => _exception;
set
{
ErrorMessage = (_exception = value) switch
{
ParseException pe => pe.Error.ToString(),
StarscriptException se => se.Message,
_ => string.Empty
};
HasError = value is not null;
OnPropertyChanged();
}
}
public ParserResult CurrentScriptSource
{
get => _currentScriptSource;
set
{
_currentScriptSource = value;
if (value is null)
{
CurrentScript = null;
CurrentScriptResult = null;
Exception = null;
return;
}
CurrentScript = Compiler.SingleCompile(value);
Exception = null;
OnPropertyChanged();
}
}
public Script CurrentScript
{
get => _currentScript;
private set
{
try
{
CurrentScriptResult = value?.Execute(_hv)!;
_currentScript = value;
Exception = null;
}
catch (StarscriptException se)
{
_currentScript = null;
CurrentScriptResult = null;
Exception = se;
}
OnPropertyChanged();
}
}
public void ReExecuteScript()
{
if (_currentScript is null) return;
try
{
CurrentScriptResult = _currentScript.Execute(_hv)!;
}
catch (StarscriptException se)
{
CurrentScriptResult = null;
Exception = se;
}
}
public IEnumerable<object> GetSuggestions(string input, CancellationToken token)
{
CurrentSuggestions.Clear();
CurrentScriptSource = _hv.ParseAndGetCompletions(input, input.Length, CompletionCallback, token);
if (CurrentScriptSource.HasErrors)
{
Exception = new ParseException(CurrentScriptSource.Errors.First());
}
OnPropertyChanged(nameof(CurrentSuggestions));
return CurrentSuggestions;
}
private void CompletionCallback(string result, bool isFunction) => CurrentSuggestions.Add(isFunction ? $"{result}(" : result);
}
}

View File

@ -253,6 +253,10 @@
Header="{ext:Locale MenuBarHelpAbout}"
Icon="{ext:Icon fa-solid fa-circle-info}"
ToolTip.Tip="{ext:Locale OpenAboutTooltip}" />
<MenuItem
Name="StarscriptDebugMenuItem"
Header="Debug Starscript"
Icon="{ext:Icon fa-solid fa-star}" />
<MenuItem
Name="UpdateMenuItem"
IsEnabled="{Binding CanUpdate}"

View File

@ -8,6 +8,7 @@ using LibHac.Ns;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Systems.AppLibrary;
using Ryujinx.Ava.Systems.Configuration;
using Ryujinx.Ava.Systems.Starscript;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
@ -51,6 +52,8 @@ namespace Ryujinx.Ava.UI.Views.Main
CompatibilityListMenuItem.Command = Commands.Create(() => CompatibilityListWindow.Show());
UpdateMenuItem.Command = MainWindowViewModel.UpdateCommand;
StarscriptDebugMenuItem.Command = Commands.Create(StarscriptTextBox.Show);
FaqMenuItem.Command =
SetupGuideMenuItem.Command =