Skip to content

Commit b8eb7fc

Browse files
Moved From Winforms to Avalonia UI
Migrated from Winforms to Avalonia UI this included refactoring the GUI code, remaking the code and fixing errors and bugs. Removed patches due to it not working (even though I remember it working before) and removed the updating system since it was flawed and only worked for Windows.
1 parent 990e865 commit b8eb7fc

24 files changed

Lines changed: 898 additions & 1087 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
## Mario Kart DS ARM9 Editor
2-
The Mario Kart DS ARM9 Editor is a C# .NET 9.0 GUI tool designed to allow users to edit values within Mario Kart DS' `arm9.bin`. With this tool, you can enhancing your Mario Kart DS experience and make better ROM hacks.
2+
The Mario Kart DS ARM9 Editor is a cross-platform C# .NET 10.0 Avalonia UI tool designed to allow users to edit values within Mario Kart DS' `arm9.bin`. With this tool, you can enhancing your Mario Kart DS experience and make better ROM hacks.
33

44
This tool uses stuff from: [ARM9 Music Editor](https://github.com/Ermelber/MKDS-ARM9-Music-Editor), [Weather Slot Documentation](https://wiki.dshack.org/Wiki.jsp?page=Swapping%20Weather%20Slots), and [Editable Online Track List](https://web.archive.org/web/20060218010753/http://leo.rampen.ca/m3wiki/index.php/Mario_Kart_DS_Hidden_Tracks_Online).
55

@@ -12,4 +12,4 @@ This tool uses stuff from: [ARM9 Music Editor](https://github.com/Ermelber/MKDS-
1212
Contributions to this project are welcome! If you find any issues or have suggestions for improvements, please open an issue or submit a pull request following the contribution guidelines.
1313

1414
### License
15-
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
15+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

src/ARM9Editor.csproj

Lines changed: 57 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,59 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2-
<PropertyGroup>
3-
<TargetFramework>net10.0-windows</TargetFramework>
4-
<OutputType>WinExe</OutputType>
5-
<RootNamespace>ARM9Editor</RootNamespace>
6-
<AssemblyName>ARM9Editor</AssemblyName>
7-
<Nullable>enable</Nullable>
8-
<UseWindowsForms>true</UseWindowsForms>
9-
<ImplicitUsings>enable</ImplicitUsings>
10-
<ApplicationHighDpiMode>SystemAware</ApplicationHighDpiMode>
11-
<ForceDesignerDpiUnaware>true</ForceDesignerDpiUnaware>
12-
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
13-
<PublishSingleFile>true</PublishSingleFile>
14-
<RuntimeIdentifiers>win-x86</RuntimeIdentifiers>
15-
<SelfContained>false</SelfContained>
16-
<PublishSingleFile>true</PublishSingleFile>
17-
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
18-
<StartupObject>ARM9Editor.Program</StartupObject>
19-
<ApplicationIcon>Icon.ico</ApplicationIcon>
20-
<PlatformTarget>AnyCPU</PlatformTarget>
21-
<GenerateDocumentationFile>False</GenerateDocumentationFile>
22-
<SignAssembly>True</SignAssembly>
23-
<AssemblyOriginatorKeyFile>ARM9Editor.snk</AssemblyOriginatorKeyFile>
24-
<ErrorReport>send</ErrorReport>
25-
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
26-
<NuGetAuditMode>direct</NuGetAuditMode>
27-
</PropertyGroup>
28-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
29-
<DebugType>none</DebugType>
30-
<NoWarn />
31-
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
32-
<WarningsAsErrors />
33-
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
34-
</PropertyGroup>
35-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
36-
<DebugType>none</DebugType>
37-
<NoWarn />
38-
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
39-
<WarningsAsErrors />
40-
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
41-
</PropertyGroup>
42-
<ItemGroup>
43-
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
44-
</ItemGroup>
45-
<ItemGroup>
46-
<None Remove="assets\json\character_offsets.json" />
47-
<None Remove="assets\json\course_offsets.json" />
48-
<None Remove="assets\json\emblem_offsets.json" />
49-
<None Remove="assets\json\kart_offsets.json" />
50-
<None Remove="assets\json\music_offsets.json" />
51-
<None Remove="assets\json\slot_offsets.json" />
52-
<None Remove="course_offsets.json" />
53-
<None Remove="emblem_offsets.json" />
54-
<None Remove="music_offsets.json" />
55-
<None Remove="slot_offsets.json" />
56-
</ItemGroup>
57-
<ItemGroup>
58-
<EmbeddedResource Include="Icon.ico">
59-
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
60-
</EmbeddedResource>
61-
</ItemGroup>
62-
<ItemGroup>
63-
<EmbeddedResource Include="assets\json\character_offsets.json" />
64-
<EmbeddedResource Include="assets\json\course_offsets.json" />
65-
<EmbeddedResource Include="assets\json\emblem_offsets.json" />
66-
<EmbeddedResource Include="assets\json\kart_offsets.json" />
67-
<EmbeddedResource Include="assets\json\music_offsets.json" />
68-
<EmbeddedResource Include="assets\json\slot_offsets.json" />
69-
</ItemGroup>
70-
<ItemGroup>
71-
<Compile Update="Properties\Settings.Designer.cs">
72-
<DesignTimeSharedInput>True</DesignTimeSharedInput>
73-
<AutoGen>True</AutoGen>
74-
<DependentUpon>Settings.settings</DependentUpon>
75-
</Compile>
76-
</ItemGroup>
77-
<ItemGroup>
78-
<None Update="Properties\Settings.settings">
79-
<Generator>SettingsSingleFileGenerator</Generator>
80-
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
81-
</None>
82-
</ItemGroup>
2+
<PropertyGroup>
3+
<OutputType>WinExe</OutputType>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
7+
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
8+
<ImplicitUsings>enable</ImplicitUsings>
9+
<AssemblyVersion>1.3.0</AssemblyVersion>
10+
<FileVersion>1.3.0.0</FileVersion>
11+
<Version>1.3.0</Version>
12+
<Company>LandonAndEmma</Company>
13+
<Product>Mario Kart DS ARM9 Editor</Product>
14+
<Description>Edit values in Mario Kart DS ARM9 files</Description>
15+
<Copyright>2025</Copyright>
16+
<AssemblyTitle>Mario Kart DS ARM9 Editor</AssemblyTitle>
17+
<NeutralLanguage>en-US</NeutralLanguage>
18+
<PublishTrimmed>true</PublishTrimmed>
19+
<TrimMode>link</TrimMode>
20+
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
21+
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
22+
<DebugType>none</DebugType>
23+
<DebugSymbols>false</DebugSymbols>
24+
<Optimize>true</Optimize>
25+
<PublishSingleFile>true</PublishSingleFile>
26+
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
27+
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
28+
<InvariantGlobalization>false</InvariantGlobalization>
29+
<EventSourceSupport>false</EventSourceSupport>
30+
<UseSystemResourceKeys>true</UseSystemResourceKeys>
31+
<IlcOptimizationPreference>Size</IlcOptimizationPreference>
32+
<IlcGenerateDgmlFile>false</IlcGenerateDgmlFile>
33+
<IlcGenerateMapFile>false</IlcGenerateMapFile>
34+
<StripSymbols>true</StripSymbols>
35+
</PropertyGroup>
36+
<PropertyGroup Condition="'$(OS)' == 'Windows_NT'">
37+
<ApplicationIcon>Icon.ico</ApplicationIcon>
38+
</PropertyGroup>
39+
<ItemGroup>
40+
<PackageReference Include="Avalonia.Desktop" Version="11.3.10" />
41+
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.10" />
42+
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.10" />
43+
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
44+
</ItemGroup>
45+
<ItemGroup>
46+
<EmbeddedResource Include="Icon.ico" />
47+
<EmbeddedResource Include="assets\json\character_offsets.json" />
48+
<EmbeddedResource Include="assets\json\course_offsets.json" />
49+
<EmbeddedResource Include="assets\json\emblem_offsets.json" />
50+
<EmbeddedResource Include="assets\json\kart_offsets.json" />
51+
<EmbeddedResource Include="assets\json\music_offsets.json" />
52+
<EmbeddedResource Include="assets\json\slot_offsets.json" />
53+
</ItemGroup>
54+
<ItemGroup>
55+
<TrimmerRootAssembly Include="Avalonia.Themes.Fluent" />
56+
<TrimmerRootAssembly Include="Newtonsoft.Json" />
57+
<TrimmerRootAssembly Include="ARM9Editor" />
58+
</ItemGroup>
8359
</Project>

src/ARM9Editor.snk

-596 Bytes
Binary file not shown.

src/App.axaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Application xmlns="https://github.com/avaloniaui"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
x:Class="ARM9Editor.App"
4+
RequestedThemeVariant="Dark">
5+
<Application.Styles>
6+
<FluentTheme />
7+
</Application.Styles>
8+
</Application>

src/App.axaml.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Avalonia;
2+
using Avalonia.Controls.ApplicationLifetimes;
3+
using Avalonia.Markup.Xaml;
4+
namespace ARM9Editor;
5+
public partial class App : Application
6+
{
7+
public override void Initialize()
8+
{
9+
AvaloniaXamlLoader.Load(this);
10+
}
11+
public override void OnFrameworkInitializationCompleted()
12+
{
13+
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
14+
{
15+
desktop.MainWindow = new MainWindow
16+
{
17+
DataContext = new MainWindowViewModel()
18+
};
19+
}
20+
base.OnFrameworkInitializationCompleted();
21+
}
22+
}

src/ConfigurationService.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using Newtonsoft.Json;
2+
using System.Diagnostics.CodeAnalysis;
3+
using System.Reflection;
4+
namespace ARM9Editor;
5+
public sealed class ConfigurationService
6+
{
7+
private static readonly Lazy<ConfigurationService> _instance = new(valueFactory: static () => new ConfigurationService());
8+
public static ConfigurationService Instance => _instance.Value;
9+
private readonly Dictionary<EditorTab, List<EditorConfig>> _configs = [];
10+
private readonly Dictionary<EditorTab, TabConfig> _tabConfigs = new()
11+
{
12+
[EditorTab.Music] = new("Music IDs", EditorTab.Music, "Music Track", "Music ID"),
13+
[EditorTab.Courses] = new("Course Filenames", EditorTab.Courses, "Course", "Filename"),
14+
[EditorTab.Weather] = new("Weather Slots", EditorTab.Weather, "Weather Slot", "Course ID"),
15+
[EditorTab.Emblems] = new("Emblem Filenames", EditorTab.Emblems, "Emblem", "Filename"),
16+
[EditorTab.Karts] = new("Kart Filenames", EditorTab.Karts, "Kart", "Filename"),
17+
[EditorTab.Characters] = new("Character Filenames", EditorTab.Characters, "Character", "Filename")
18+
};
19+
[RequiresUnreferencedCode("Calls ARM9Editor.ConfigurationService.LoadConfigurations()")]
20+
private ConfigurationService()
21+
{
22+
LoadConfigurations();
23+
}
24+
public TabConfig GetTabConfig(EditorTab tab)
25+
{
26+
return _tabConfigs[tab];
27+
}
28+
public IReadOnlyList<EditorConfig> GetEditorConfigs(EditorTab tab)
29+
{
30+
return _configs.TryGetValue(tab, out List<EditorConfig>? configs) ? configs : Array.Empty<EditorConfig>();
31+
}
32+
[RequiresUnreferencedCode("Calls ARM9Editor.ConfigurationService.LoadByteConfigs(String, Int32, Int32)")]
33+
private void LoadConfigurations()
34+
{
35+
_configs[EditorTab.Music] = LoadByteConfigs("music_offsets.json", 0, 75);
36+
_configs[EditorTab.Courses] = LoadStringRangeConfigs("course_offsets.json");
37+
_configs[EditorTab.Weather] = LoadByteConfigs("slot_offsets.json", 1, 54);
38+
_configs[EditorTab.Emblems] = LoadFixedStringConfigs("emblem_offsets.json", 2);
39+
_configs[EditorTab.Karts] = LoadFixedStringConfigs("kart_offsets.json", 4);
40+
_configs[EditorTab.Characters] = LoadFixedStringConfigs("character_offsets.json", 4);
41+
}
42+
[RequiresUnreferencedCode("Calls ARM9Editor.ConfigurationService.LoadResource<T>(String)")]
43+
private List<EditorConfig> LoadByteConfigs(string fileName, int min, int max)
44+
{
45+
Dictionary<string, int>? dict = LoadResource<Dictionary<string, int>>(fileName);
46+
return dict?.Select(kvp => new EditorConfig(kvp.Key, EditorType.Byte, kvp.Value, MinValue: min, MaxValue: max)).ToList() ?? [];
47+
}
48+
[RequiresUnreferencedCode("Calls ARM9Editor.ConfigurationService.LoadResource<T>(String)")]
49+
private List<EditorConfig> LoadFixedStringConfigs(string fileName, int length)
50+
{
51+
Dictionary<string, int>? dict = LoadResource<Dictionary<string, int>>(fileName);
52+
return dict?.Select(kvp => new EditorConfig(kvp.Key, EditorType.FixedString, kvp.Value, MaxLength: length)).ToList() ?? [];
53+
}
54+
[RequiresUnreferencedCode("Calls ARM9Editor.ConfigurationService.LoadResource<T>(String)")]
55+
private List<EditorConfig> LoadStringRangeConfigs(string fileName)
56+
{
57+
Dictionary<string, int[]>? dict = LoadResource<Dictionary<string, int[]>>(fileName);
58+
return dict?.Where(kvp => kvp.Value?.Length == 2).Select(kvp => new EditorConfig(kvp.Key, EditorType.VariableString, kvp.Value[0], MaxLength: kvp.Value[1] - kvp.Value[0])).ToList() ?? [];
59+
}
60+
[RequiresUnreferencedCode("Calls Newtonsoft.Json.JsonConvert.DeserializeObject<T>(String)")]
61+
private T? LoadResource<T>(string fileName)
62+
{
63+
try
64+
{
65+
string resourceName = $"ARM9Editor.assets.json.{fileName}";
66+
using Stream? stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
67+
if (stream == null)
68+
{
69+
return default;
70+
}
71+
using StreamReader reader = new(stream);
72+
return JsonConvert.DeserializeObject<T>(reader.ReadToEnd());
73+
}
74+
catch
75+
{
76+
return default;
77+
}
78+
}
79+
}

src/DialogService.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using Avalonia.Controls;
2+
namespace ARM9Editor;
3+
public static class DialogService
4+
{
5+
public static async Task ShowErrorAsync(Window? owner, string message)
6+
{
7+
await ShowMessageAsync(owner, "Error", message);
8+
}
9+
public static async Task ShowMessageAsync(Window? owner, string title, string message)
10+
{
11+
if (owner == null)
12+
{
13+
return;
14+
}
15+
Window dialog = new()
16+
{
17+
Title = title,
18+
Width = 400,
19+
Height = 150,
20+
CanResize = false,
21+
WindowStartupLocation = WindowStartupLocation.CenterOwner,
22+
SizeToContent = SizeToContent.Height
23+
};
24+
StackPanel stack = new()
25+
{
26+
Margin = new Avalonia.Thickness(20)
27+
};
28+
stack.Children.Add(new TextBlock
29+
{
30+
Text = message,
31+
TextWrapping = Avalonia.Media.TextWrapping.Wrap,
32+
Margin = new Avalonia.Thickness(0, 0, 0, 20)
33+
});
34+
Button button = new()
35+
{
36+
Content = "OK",
37+
Width = 80,
38+
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right
39+
};
40+
button.Click += (_, _) => dialog.Close();
41+
stack.Children.Add(button);
42+
dialog.Content = stack;
43+
await dialog.ShowDialog(owner);
44+
}
45+
}

0 commit comments

Comments
 (0)