Added functionality:

- listing mods from custom mods folders
- Showing mods icon, name, and other attributes
- Settings window with settings for customizing mods folders and game instalation pathes
This commit is contained in:
strongleong 2022-02-06 14:29:03 +11:00
parent 4f47475dd4
commit e0c1beb5f1
14 changed files with 1105 additions and 15 deletions

185
.editorconfig Normal file
View file

@ -0,0 +1,185 @@
# Удалите строку ниже, если вы хотите наследовать параметры .editorconfig из каталогов, расположенных выше в иерархии
root = true
# Файлы C#
[*.cs]
#### Основные параметры EditorConfig ####
# Отступы и интервалы
indent_size = 4
indent_style = space
tab_width = 4
# Предпочтения для новых строк
end_of_line = crlf
insert_final_newline = false
#### Рекомендации по написанию кода .NET ####
# Упорядочение Using
dotnet_separate_import_directive_groups = true
dotnet_sort_system_directives_first = true
file_header_template = unset
# Предпочтения для this. и Me.
dotnet_style_qualification_for_event = false:suggestion
dotnet_style_qualification_for_field = false
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
# Параметры использования ключевых слов языка и типов BCL
dotnet_style_predefined_type_for_locals_parameters_members = false:suggestion
dotnet_style_predefined_type_for_member_access = false:suggestion
# Предпочтения для скобок
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion
# Предпочтения модификатора
dotnet_style_require_accessibility_modifiers = for_non_interface_members
# Выражения уровень предпочтения
dotnet_style_coalesce_expression = true
dotnet_style_collection_initializer = true
dotnet_style_explicit_tuple_names = true
dotnet_style_namespace_match_folder = true
dotnet_style_null_propagation = true
dotnet_style_object_initializer = true
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true
dotnet_style_prefer_compound_assignment = true
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true
dotnet_style_prefer_inferred_tuple_names = true
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
dotnet_style_prefer_simplified_boolean_expressions = true
dotnet_style_prefer_simplified_interpolation = true
# Предпочтения для полей
dotnet_style_readonly_field = true
# Настройки параметров
dotnet_code_quality_unused_parameters = all
# Параметры подавления
dotnet_remove_unnecessary_suppression_exclusions = none
# Предпочтения для новых строк
dotnet_style_allow_multiple_blank_lines_experimental = true
dotnet_style_allow_statement_immediately_after_block_experimental = true
#### Рекомендации по написанию кода C# ####
# Предпочтения var
csharp_style_var_elsewhere = false:suggestion
csharp_style_var_for_built_in_types = false:suggestion
csharp_style_var_when_type_is_apparent = false:suggestion
# Члены, заданные выражениями
csharp_style_expression_bodied_accessors = true
csharp_style_expression_bodied_constructors = true
csharp_style_expression_bodied_indexers = true
csharp_style_expression_bodied_lambdas = true
csharp_style_expression_bodied_local_functions = true
csharp_style_expression_bodied_methods = true
csharp_style_expression_bodied_operators = true
csharp_style_expression_bodied_properties = true
# Настройки соответствия шаблонов
csharp_style_pattern_matching_over_as_with_null_check = true
csharp_style_pattern_matching_over_is_with_cast_check = true
csharp_style_prefer_not_pattern = true
csharp_style_prefer_pattern_matching = false
csharp_style_prefer_switch_expression = true
# Настройки проверки на null
csharp_style_conditional_delegate_call = true
# Предпочтения модификатора
csharp_prefer_static_local_function = true
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
# Предпочтения для блоков кода
csharp_prefer_braces = false:suggestion
csharp_prefer_simple_using_statement = false
csharp_style_namespace_declarations = block_scoped
# Выражения уровень предпочтения
csharp_prefer_simple_default_expression = true
csharp_style_deconstructed_variable_declaration = true
csharp_style_implicit_object_creation_when_type_is_apparent = false
csharp_style_inlined_variable_declaration = true
csharp_style_pattern_local_over_anonymous_function = true
csharp_style_prefer_index_operator = true
csharp_style_prefer_null_check_over_type_check = true
csharp_style_prefer_range_operator = true
csharp_style_throw_expression = false
csharp_style_unused_value_assignment_preference = discard_variable:silent
csharp_style_unused_value_expression_statement_preference = discard_variable
# предпочтения для директивы using
csharp_using_directive_placement = outside_namespace:suggestion
# Предпочтения для новых строк
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:suggestion
csharp_style_allow_embedded_statements_on_same_line_experimental = false:suggestion
#### Правила форматирования C# ####
# Предпочтения для новых строк
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
# Предпочтения для отступов
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = false
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
# Предпочтения для интервалов
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = ignore
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Предпочтения переноса
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
#### Стили именования ####
# Правила именования
# Спецификации символов
# Стили именования

4
.gitignore vendored
View file

@ -360,4 +360,6 @@ MigrationBackup/
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
FodyWeavers.xsd
Mods/

52
README.md Normal file
View file

@ -0,0 +1,52 @@
ScrapModLoader
==============
This applications is for managing mods for Scrapland.
## How To Make Mod
Mod for Scrapland is a *.sm file that basically is a zip arhive with following content:
| Filename | Description |
|-------------|-------------------------------------------------|
| icon.png | Icon for mod that will show up in mod loader |
| meta.ini | Information about mod |
| data.packed | Container with all mod game assets |
### meta.ini structure
```ini
[Miscellaneous]
Title="Mod Title"
Description="A mod description"
Category=A category
Version=1.0
RequiredLauncher=1.0
RequiredGame=1.1
AuthorGroup=Group 1
AuthorGroup=Group 2
[Author]
Name=Mod author
Website=http://example.com
[Author]
Group=Group 1
Name=Mod author
[Author]
Group=Group 2
Name=Another author
[Setting]
Name=setting
Title=Setting titile
Tooltip=Some setting
Type=Int
Min=1
Max=10
Default=2
File=scripts/char/ditritus.py
Placeholder=<speed>
```

View file

@ -3,7 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.32014.148
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScrapModLoader", "ScrapModLoader\ScrapModLoader.csproj", "{11B9B565-DE2D-44EE-ACE8-6A8904A41E9F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScrapModLoader", "ScrapModLoader\ScrapModLoader.csproj", "{11B9B565-DE2D-44EE-ACE8-6A8904A41E9F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{01814211-97A4-4648-9BF1-A89690127C25}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
README.md = README.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

View file

@ -5,8 +5,74 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ScrapModLoader"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
Title="ScrapModLoader" Height="451" Width="800"
Initialized="Window_Initialized">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="45" />
</Grid.RowDefinitions>
<TabControl>
<TabItem Header="Mods">
<Grid Background="#FFFFFF" Name="MainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="0*" />
</Grid.ColumnDefinitions>
<ListView d:ItemsSource="{d:SampleData ItemCount=5}" Name="ModsList" Initialized="ModsList_Initialized" MouseDown="ModsList_MouseDown">
<ListView.View>
<GridView x:Name="ModsGrid">
<GridViewColumn Header="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Stretch" HorizontalAlignment="Left">
<CheckBox IsChecked="{Binding Checked}" VerticalAlignment="Center" HorizontalAlignment="Center" Checked="CheckBox_Checked" />
<Image Source="{Binding Icon}" Stretch="Fill" Width="16" Height="16" />
<Label Content="{Binding Name}" HorizontalAlignment="Stretch" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Mod Version" DisplayMemberBinding="{Binding Version}" />
<GridViewColumn Header="Game Version" DisplayMemberBinding="{Binding RequiredGame}" />
</GridView>
</ListView.View>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListViewItem_PreviewMouseLeftButtonDown" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" />
<TabControl Grid.Column="2" HorizontalContentAlignment="Center">
<TabItem Header="Description">
<RichTextBox Name="ModInfo" IsReadOnly="True" Background="#F0F0F0" />
</TabItem>
<TabItem Header="Credits" Visibility="Visible" Name="ModCreditsTab">
<RichTextBox Name="ModCredits" IsReadOnly="True" Background="#F0F0F0" Visibility="Visible" />
</TabItem>
</TabControl>
</Grid>
</TabItem>
<TabItem Header="Settings" Height="22" Margin="-2,0,-2,0" VerticalAlignment="Bottom">
<Grid Background="#FEFEFE"/>
</TabItem>
</TabControl>
<Grid Grid.Row="1" Margin="10,10,10,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content=" Settings " Click="Button_Click" />
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Left">
<CheckBox Content=" Windowed " Margin="0,0,10,0" HorizontalAlignment="Center" VerticalAlignment="Center" />
<CheckBox Content=" Close launcher " Margin="0,0,10,0" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Button Content=" Run Scrapland " Name="ButtonRunScrapland" />
</StackPanel>
</Grid>
</Grid>
</Window>

View file

@ -1,25 +1,148 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace ScrapModLoader {
namespace ScrapModLoader
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window {
public MainWindow() {
public partial class MainWindow : Window
{
private readonly ModsLauncher modsLauncher;
private GridLength gridLength = new GridLength(1, GridUnitType.Star);
public MainWindow()
{
modsLauncher = new ModsLauncher();
InitializeComponent();
}
private void Window_Initialized(Object sender, EventArgs e)
{
if (Settings.Default.ModsPathes.Count == 0)
{
String path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
+ Path.DirectorySeparatorChar + "Scrapland mods";
Settings.Default.ModsPathes.Add(path);
Directory.CreateDirectory(path);
}
// TODO: Refactor it to separate window with pretty loading animation
if (!modsLauncher.SearchForScrapland())
{
ButtonRunScrapland.IsEnabled = false;
MessageBox.Show("Error: unable to find Scrapland instalation. Please, specify yours game installation folder in settings.");
}
modsLauncher.ScanMods();
}
private void ModsList_Initialized(Object sender, EventArgs e) => ModsList.ItemsSource = modsLauncher.Mods;
private void ModsList_MouseDown(Object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (MainGrid.ColumnDefinitions[2].Width.Value != 0)
{
gridLength = MainGrid.ColumnDefinitions[2].Width;
MainGrid.ColumnDefinitions[2].Width = new GridLength(0, GridUnitType.Star);
}
}
private void ListViewItem_PreviewMouseLeftButtonDown(Object sender, MouseButtonEventArgs e)
{
MainGrid.ColumnDefinitions[2].Width = gridLength;
if (sender is ListViewItem item)
{
String? selectedModName = (item.Content as ScrapMod)?.Name;
if (selectedModName == null)
throw new KeyNotFoundException(nameof(selectedModName));
ScrapMod ? mod = modsLauncher.Mods.Find(mod => mod.Name == selectedModName);
if (mod == null)
throw new KeyNotFoundException(nameof(mod));
if (e.ClickCount == 2)
{
mod.Checked = !mod.Checked;
ModsList.Items.Refresh();
}
WriteModInfo(mod);
}
}
private void WriteModInfo(ScrapMod mod)
{
ModInfo.Document.Blocks.Clear();
Paragraph parahraph = new Paragraph();
parahraph.Inlines.Add(new Bold(new Run("Description:\n")));
parahraph.Inlines.Add(new Run(mod.Description));
parahraph.Inlines.Add(new Bold(new Run("\n\nAuthors:\n")));
foreach (String autor in mod.Authors)
parahraph.Inlines.Add(new Run(autor + "\n"));
ModInfo.Document.Blocks.Add(parahraph);
ModCreditsTab.Visibility = Visibility.Visible;
if (mod.Credits.Count == 0)
ModCreditsTab.Visibility = Visibility.Hidden;
else
{
ModCredits.Document.Blocks.Clear();
parahraph = new Paragraph();
foreach (KeyValuePair<String, List<String>> credit in mod.Credits)
{
parahraph.Inlines.Add(new Bold(new Run(credit.Key + "\n")));
foreach (String autor in credit.Value)
parahraph.Inlines.Add(new Run(autor + "\n"));
parahraph.Inlines.Add(new Run("\n"));
}
ModCredits.Document.Blocks.Add(parahraph);
}
}
private void CheckBox_Checked(Object sender, RoutedEventArgs e)
{
CheckBox checkbox = (CheckBox)sender;
Boolean? isChecked = checkbox.IsChecked;
if (isChecked == null)
throw new NullReferenceException(nameof(isChecked));
StackPanel parent = (StackPanel)checkbox.Parent;
Label label = (Label)parent.Children[2];
String? selectedModName = label.Content.ToString();
if (selectedModName == null)
throw new KeyNotFoundException(nameof(selectedModName));
ScrapMod? mod = modsLauncher.Mods.Find(mod => mod.Name == selectedModName);
if (mod == null)
throw new KeyNotFoundException(nameof(selectedModName));
mod.Checked = (Boolean)isChecked;
}
private void Button_Click(Object sender, RoutedEventArgs e)
{
SettingsWindow settingsWindow = new SettingsWindow(modsLauncher);
settingsWindow.ShowDialog();
if (settingsWindow.Save)
modsLauncher.ScanMods();
ModsList.Items.Refresh();
}
}
}

View file

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Win32;
namespace ScrapModLoader
{
public class ModsLauncher
{
public List<ScrapMod> Mods { get; private set; }
public String ScraplandPath { get; set; }
public String ScraplandRemasteredPath { get; set; }
public ModsLauncher()
{
Mods = new List<ScrapMod>();
ScraplandPath = Settings.Default.ScraplandPath;
ScraplandRemasteredPath = Settings.Default.ScraplandRemasteredPath;
}
public void ScanMods()
{
Mods.Clear();
foreach (String? folder in Settings.Default.ModsPathes)
{
if (folder != null)
{
String[] files = Directory.GetFiles(folder, "*.sm", SearchOption.AllDirectories);
foreach (String file in files)
Mods.Add(new ScrapMod(file));
}
}
}
public Boolean SearchForScrapland()
{
Boolean isFound = false;
String[] registryPathes =
{
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
};
foreach (String registryPath in registryPathes)
{
RegistryKey? key = Registry.LocalMachine.OpenSubKey(registryPath);
if (key == null)
continue;
foreach (String subkey_name in key.GetSubKeyNames())
{
using RegistryKey? subkey = key.OpenSubKey(subkey_name);
if (subkey == null)
continue;
String? displayName = subkey.GetValue("DisplayName")?.ToString();
if (displayName == null)
continue;
if (displayName == "Scrapland")
{
ScraplandPath = subkey.GetValue("InstallLocation")?.ToString() ?? "";
isFound = true;
}
if (displayName == "Scrapland Remastered")
{
ScraplandRemasteredPath = subkey.GetValue("InstallLocation")?.ToString() ?? "";
isFound = true;
}
}
}
return isFound;
}
}
}

138
ScrapModLoader/ScrapMod.cs Normal file
View file

@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Media.Imaging;
using System.Xml;
using Ionic.Zip;
namespace ScrapModLoader
{
public class ScrapMod
{
public String Name { get; private set; }
public String Description { get; private set; }
public String ModPath { get; private set; }
public BitmapImage Icon { get; private set; }
public Boolean Checked { get; set; }
public String Category { get; private set; }
public String Version { get; private set; }
public String RequiredLauncher { get; private set; }
public String RequiredGame { get; private set; }
public List<String> Authors { get; private set; }
public Dictionary<String, List<String>> Credits { get; private set; }
public ScrapMod(String path)
{
ModPath = path;
Name = Path.GetFileNameWithoutExtension(path);
Description = String.Empty;
Icon = new BitmapImage();
Checked = false;
Category = String.Empty;
Version = String.Empty;
RequiredLauncher = String.Empty;
RequiredGame = String.Empty;
Authors = new List<String>();
Credits = new Dictionary<String, List<String>>();
LoadFromFile(path);
}
private void LoadFromFile(String path)
{
using (ZipFile zipFile = ZipFile.Read(path))
{
Byte[] iconBuffer = ExtractFromZip(zipFile, "icon.png");
Byte[] confBuffer = ExtractFromZip(zipFile, "config.xml");
LoadIcon(iconBuffer);
LoadConfig(confBuffer);
}
}
private Byte[] ExtractFromZip(ZipFile zip, String entry_path)
{
ZipEntry? entry = zip[entry_path];
if (entry == null)
throw new FileFormatException($"No '{entry}' in {Name} found");
Byte[] buffer = new Byte[entry.UncompressedSize];
using (MemoryStream zipStream = new MemoryStream(buffer))
entry.Extract(zipStream);
return buffer;
}
private void LoadIcon(Byte[] buffer)
{
using (MemoryStream sourceStream = new MemoryStream(buffer))
{
Icon.BeginInit();
Icon.CacheOption = BitmapCacheOption.OnLoad;
Icon.StreamSource = sourceStream;
Icon.EndInit();
}
}
private void LoadConfig(Byte[] buffer)
{
using (MemoryStream sourceStream = new MemoryStream(buffer))
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(sourceStream);
ParseModInfoFromXml(xmlDocument);
ParseAuthorsFromXml(xmlDocument);
ParseCreditsFromXml(xmlDocument);
}
}
private void ParseModInfoFromXml(XmlDocument xmlDocument)
{
Description = xmlDocument.GetElementsByTagName("Description").Item(0)?.InnerText ??
throw new FileFormatException("No 'Description' tag in 'config.xml'");
Category = xmlDocument.GetElementsByTagName("Category").Item(0)?.InnerText ??
throw new FileFormatException("No 'Category' tag in 'config.xml'");
Version = xmlDocument.GetElementsByTagName("Version").Item(0)?.InnerText ??
throw new FileFormatException("No 'Version' tag in 'config.xml'");
RequiredLauncher = xmlDocument.GetElementsByTagName("RequiredLauncher").Item(0)?.InnerText ??
throw new FileFormatException("No 'RequiredLauncher' tag in 'config.xml'");
RequiredGame = xmlDocument.GetElementsByTagName("RequiredGame").Item(0)?.InnerText ??
throw new FileFormatException("No 'RequiredGame' tag in 'config.xml'");
}
private void ParseAuthorsFromXml(XmlDocument xmlDocument)
{
XmlNodeList authors = xmlDocument.GetElementsByTagName("Author");
foreach (XmlNode author in authors)
{
XmlAttribute? nameAttr = author.Attributes?["name"];
if (nameAttr == null)
throw new FileFormatException("No 'name' attribute in 'Author' tag in 'config.xml'");
Authors.Add(nameAttr.InnerText);
}
}
private void ParseCreditsFromXml(XmlDocument xmlDocument)
{
XmlNodeList credits = xmlDocument.GetElementsByTagName("Credits");
foreach (XmlNode credit in credits)
{
List<String> entries = new List<String>();
XmlAttribute? groupAttr = credit.Attributes?["group"];
if (groupAttr == null)
throw new FileFormatException("No 'group' attribute in 'Credits' tag in 'config.xml'");
foreach (XmlNode entry in credit)
{
XmlAttribute? nameAttr = entry.Attributes?["name"];
if (nameAttr == null)
throw new FileFormatException("No 'name' attribute in 'Author' tag in 'config.xml'");
entries.Add(nameAttr.InnerText);
}
Credits.Add(groupAttr.InnerText, entries);
}
}
}
}

View file

@ -6,6 +6,35 @@
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
<UseWindowsForms>True</UseWindowsForms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DotNetZip" Version="1.16.0" />
</ItemGroup>
<ItemGroup>
<Compile Update="Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="Mods\Speedy D-Tritus.sm">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Mods\Test2.sm">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Mods\TetsMod.sm">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
</Project>

87
ScrapModLoader/Settings.Designer.cs generated Normal file
View file

@ -0,0 +1,87 @@
//------------------------------------------------------------------------------
// <auto-generated>
// Этот код создан программой.
// Исполняемая версия:4.0.30319.42000
//
// Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае
// повторной генерации кода.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ScrapModLoader {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool Windowed {
get {
return ((bool)(this["Windowed"]));
}
set {
this["Windowed"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool Close {
get {
return ((bool)(this["Close"]));
}
set {
this["Close"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string ScraplandPath {
get {
return ((string)(this["ScraplandPath"]));
}
set {
this["ScraplandPath"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string ScraplandRemasteredPath {
get {
return ((string)(this["ScraplandRemasteredPath"]));
}
set {
this["ScraplandRemasteredPath"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n<ArrayOfString xmlns:xsd=\"http://www.w3." +
"org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" />")]
public global::System.Collections.Specialized.StringCollection ModsPathes {
get {
return ((global::System.Collections.Specialized.StringCollection)(this["ModsPathes"]));
}
set {
this["ModsPathes"] = value;
}
}
}
}

View file

@ -0,0 +1,22 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="ScrapModLoader" GeneratedClassName="Settings">
<Profiles />
<Settings>
<Setting Name="Windowed" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="Close" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="ScraplandPath" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="ScraplandRemasteredPath" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="ModsPathes" Type="System.Collections.Specialized.StringCollection" Scope="User">
<Value Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
&lt;ArrayOfString xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" /&gt;</Value>
</Setting>
</Settings>
</SettingsFile>

View file

@ -0,0 +1,87 @@
<Window x:Class="ScrapModLoader.SettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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="clr-namespace:ScrapModLoader"
mc:Ignorable="d"
Title="SettingsWindow" Height="386" Width="522">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<TabControl Grid.Column="1" Margin="0,10,0,0">
<TabItem Header="Mods">
<TabControl Margin="0,5,0,0" >
<TabItem Header="Mods Folders">
<Grid Background="#FFE5E5E5" Margin="0,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListBox Name="ModsPathesList" SelectionChanged="ModsPathesList_SelectionChanged" SelectionMode="Single" PreviewMouseLeftButtonDown="ModsPathesList_PreviewMouseLeftButtonDown">
</ListBox>
<StackPanel Grid.Column="2" Margin="10,10,10,10">
<Button Content="Add" Margin="0,0,0,10" Name="ButtonAdd" Click="ButtonAdd_Click" />
<Button Content="Remove" Margin="0,0,0,10" Name="ButtonRemove" Click="ButtonRemove_Click" IsEnabled="False" />
<Button Content="Up" Margin="0,0,0,10" Name="ButtonUp" Click="ButtonUp_Click" IsEnabled="False" />
<Button Content="Down" Margin="0,0,0,10" Name="ButtonDown" Click="ButtonDown_Click" IsEnabled="False" />
<Button Content="Open" Margin="0,0,0,10" Name="ButtonOpen" Click="ButtonOpen_Click" IsEnabled="False" />
</StackPanel>
</Grid>
</TabItem>
</TabControl>
</TabItem>
<TabItem Header="Game" Height="20" VerticalAlignment="Top">
<Grid Margin="0,5,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<GroupBox Grid.Row="0" Grid.Column="0" Header="Scrapland instalation path">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Content="Original:" VerticalAlignment="Center" HorizontalAlignment="Left" />
<TextBox Grid.Row="0" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Center" x:Name="ScraplandPathTextBox" IsEnabled="False" />
<StackPanel Grid.Row="0" Grid.Column="2" Orientation="Horizontal">
<Button Content=" Browse... " HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,5,0" Name="ButtonBrowseScrap" Click="ButtonBrowseScrap_Click"/>
<Button Content=" Clear " HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,5,0" Name="ButtonClearScrap" Click="ButtonClearScrap_Click"/>
<Button Content=" Show In Explorer " HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,5,0" Name="ButtonShowScrap" IsEnabled="False" Click="ButtonShowScrap_Click" />
</StackPanel>
<Label Grid.Row="1" Content="Remastered:" VerticalAlignment="Center" HorizontalAlignment="Left" />
<TextBox Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Center" x:Name="ScraplandRemasteredPathTextBox" IsEnabled="False" />
<StackPanel Grid.Row="1" Grid.Column="2" Orientation="Horizontal">
<Button Content=" Browse... " HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,5,0" Name="ButtonBrowseScrapRemaster" Click="ButtonBrowseScrapRemaster_Click"/>
<Button Content=" Clear " HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,5,0" Name="ButtonClearScrapRemaster" Click="ButtonClearScrapRemaster_Click"/>
<Button Content=" Show In Explorer " HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,5,0" Name="ButtonShowScrapRemaster" IsEnabled="False" Click="ButtonShowScrapRemaster_Click" />
</StackPanel>
</Grid>
</GroupBox>
</Grid>
</TabItem>
</TabControl>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="5,5,5,5">
<Button Content="Save" Margin="10,0,0,0" Width="50" Name="ButtonSave" Click="ButtonSave_Click" />
<Button Content="Cancel" Margin="10,0,0,0" Width="50" Name="ButtonCancel" Click="ButtonCancel_Click" />
</StackPanel>
</Grid>
</Window>

View file

@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Controls;
namespace ScrapModLoader
{
/// <summary>
/// Логика взаимодействия для SettingsWindow.xaml
/// </summary>
public partial class SettingsWindow : Window
{
public List<String> ModsPathes { get; set; }
public String ScraplandPath { get; set; }
public String ScraplandRemasteredPath { get; set; }
public Boolean Save { get; set; }
public SettingsWindow(ModsLauncher modsLauncher)
{
InitializeComponent();
Save = false;
ModsPathes = Utils.StringCollectionToList(Settings.Default.ModsPathes);
ScraplandPath = Settings.Default.ScraplandPath;
ScraplandRemasteredPath = Settings.Default.ScraplandRemasteredPath;
ModsPathesList.ItemsSource = ModsPathes;
ScraplandPathTextBox.Text = ScraplandPath;
ScraplandRemasteredPathTextBox.Text = ScraplandRemasteredPath;
ButtonShowScrap.IsEnabled = ScraplandPath != String.Empty;
ButtonShowScrapRemaster.IsEnabled = ScraplandRemasteredPath != String.Empty;
}
// -------------------------------------------
// Mods Pathes
// -------------------------------------------
// Left buttons handlers
private void ButtonAdd_Click(Object sender, RoutedEventArgs e)
{
String folder = Utils.GetFolderDialog();
if (folder != String.Empty)
ModsPathes.Add(folder);
ModsPathesList.Items.Refresh();
}
private void ButtonRemove_Click(Object sender, RoutedEventArgs e)
{
ModsPathes.Remove((String)ModsPathesList.SelectedValue);
ModsPathesList.Items.Refresh();
ButtonRemove.IsEnabled = false;
ButtonUp.IsEnabled = false;
ButtonDown.IsEnabled = false;
ButtonOpen.IsEnabled = false;
}
private void ButtonUp_Click(Object sender, RoutedEventArgs e)
{
Int32 index = ModsPathesList.SelectedIndex;
if (index == 0)
return;
String? temp = ModsPathes[index - 1];
ModsPathes[index - 1] = ModsPathes[index];
ModsPathes[index] = temp;
ModsPathesList.Items.Refresh();
}
private void ButtonDown_Click(Object sender, RoutedEventArgs e)
{
Int32 index = ModsPathesList.SelectedIndex;
if (index == ModsPathes.Count - 1)
return;
String? temp = ModsPathes[index + 1];
ModsPathes[index + 1] = ModsPathes[index];
ModsPathes[index] = temp;
ModsPathesList.Items.Refresh();
}
private void ButtonOpen_Click(Object sender, RoutedEventArgs e) =>
Process.Start("explorer.exe", (String)ModsPathesList.SelectedValue);
// Left buttons controls
private void ModsPathesList_SelectionChanged(Object sender, SelectionChangedEventArgs e)
{
ButtonRemove.IsEnabled = true;
ButtonUp.IsEnabled = true;
ButtonDown.IsEnabled = true;
ButtonOpen.IsEnabled = true;
}
private void ModsPathesList_PreviewMouseLeftButtonDown(Object sender, System.Windows.Input.MouseButtonEventArgs e)
{
ModsPathesList.SelectedItem = null;
ButtonRemove.IsEnabled = false;
ButtonUp.IsEnabled = false;
ButtonDown.IsEnabled = false;
ButtonOpen.IsEnabled = false;
}
// -------------------------------------------
// Game executabls
// -------------------------------------------
private void ButtonBrowseScrap_Click(Object sender, RoutedEventArgs e)
{
String scraplandPath = Utils.GetFolderDialog();
if (scraplandPath != String.Empty)
{
ScraplandPathTextBox.Text = scraplandPath;
ScraplandPath = scraplandPath;
ButtonShowScrap.IsEnabled = true;
}
}
private void ButtonBrowseScrapRemaster_Click(Object sender, RoutedEventArgs e)
{
String scraplandRemasteredPath = Utils.GetFilePath();
if (scraplandRemasteredPath != String.Empty)
{
ScraplandRemasteredPathTextBox.Text = scraplandRemasteredPath;
ScraplandRemasteredPath = scraplandRemasteredPath;
ButtonShowScrapRemaster.IsEnabled = true;
}
}
private void ButtonClearScrap_Click(Object sender, RoutedEventArgs e)
{
ScraplandPathTextBox.Text = String.Empty;
ButtonShowScrap.IsEnabled = false;
ScraplandPath = String.Empty;
}
private void ButtonClearScrapRemaster_Click(Object sender, RoutedEventArgs e)
{
ScraplandRemasteredPathTextBox.Text = String.Empty;
ButtonClearScrapRemaster.IsEnabled = false;
ScraplandRemasteredPath = String.Empty;
}
private void ButtonShowScrap_Click(Object sender, RoutedEventArgs e)
{
String? path = Path.GetDirectoryName(ScraplandPath);
if (path == null)
throw new DirectoryNotFoundException("Cannot find direcotry for Scrapland");
Process.Start("explorer.exe", path);
}
private void ButtonShowScrapRemaster_Click(Object sender, RoutedEventArgs e)
{
String? path = Path.GetDirectoryName(ScraplandRemasteredPath);
if (path == null)
throw new DirectoryNotFoundException("Cannot find direcotry for Scrapland");
Process.Start("explorer.exe", path);
}
// -------------------------------------------
// Window contols buttons
// -------------------------------------------
private void ButtonCancel_Click(Object sender, RoutedEventArgs e) =>
Close();
private void ButtonSave_Click(Object sender, RoutedEventArgs e)
{
Settings.Default.ModsPathes.Clear();
Settings.Default.ModsPathes.AddRange(ModsPathes.ToArray());
Settings.Default.ScraplandPath = ScraplandPath;
Settings.Default.ScraplandRemasteredPath = ScraplandRemasteredPath;
Settings.Default.Save();
Save = true;
Close();
}
}
}

42
ScrapModLoader/Utils.cs Normal file
View file

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using Microsoft.Win32;
namespace ScrapModLoader
{
internal static class Utils
{
public static String GetFilePath()
{
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog
{
DefaultExt = ".exe",
Filter = "Scrapland Executable File (*.exe)|*.exe"
};
Nullable<Boolean> result = dlg.ShowDialog();
return dlg.FileName;
}
public static String GetFolderDialog()
{
using System.Windows.Forms.FolderBrowserDialog? dialog = new System.Windows.Forms.FolderBrowserDialog();
System.Windows.Forms.DialogResult result = dialog.ShowDialog();
return dialog.SelectedPath;
}
public static List<String> StringCollectionToList(StringCollection collection)
{
List<String> list = new List<String>();
foreach (String? item in collection)
{
if (item != null)
list.Add(item);
}
return list;
}
}
}