Compare commits

..

No commits in common. "master" and "v0.0.1" have entirely different histories.

9 changed files with 303 additions and 382 deletions

View File

@ -1,4 +1,4 @@
WIP ScrapModLoader
WIP ScrapModLoader
==============
This applications is for managing mods for Scrapland.
@ -24,68 +24,45 @@ ScrapModLoader supports both original and remastered versions of Scrapland.
For now 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 |
| config.toml | Information about mod |
| <game_version\>\ | Folder that named as game version mod made for |
| <filename\>.packed | Container with all mod game assets |
| Filename | Description |
|--------------------|----------------------------------------------|
| icon.png | Icon for mod that will show up in mod loader |
| config.xml | Information about mod |
| <filename\>.packed | Container with all mod game assets |
You can have as many .packed files as you want. Mod loader will load everything.
### meta.ini sample
```xml
<?xml version="1.0" encoding="UTF-8"?>
<ScrapMod>
<Title>Mod Title</Title>
<Description>Mod Desciption</Description>
.packed files in the root of mod will be copied to the `Mods` folder of Scrapland.
.pakced files under game version folder will load only to the appopriate game version.
### .sm structure sample
```
│ icon.png
│ config.toml
│ mod_assets.packed
├──1.0/
│ only_for_original.packed
└──1.1/
only_for_remastered.packed
```
### config.toml sample
```toml
title = "Mod title"
description = "Mod description"
category = "Mod category"
version = "1.0"
requiredLauncher = "1.0"
supportedGameVersions = ["1.0", "1.1"]
authors = [
{ name = "Author 1" },
{ name = "Author 2" }
]
[[credits]]
group = "Group 1"
credits = [
{ name = "Author 1" },
{ name = "Author 2" },
{ name = "Author 3" }
]
[[credits]]
group = "Group 2"
credits = [
{ name = "Author 3" },
{ name = "Author 4" }
]
<Category>Category</Category>
<Version>1.0</Version>
<RequiredLauncher>1.0</RequiredLauncher>
<RequiredGame>1.1</RequiredGame>
<Author name="Author1" website="https://example.com" />
<Author name="Author2" />
<Credits group="Mod author">
<Credit name="Author1" />
</Credits>
<Credits group="Some credit" >
<Credit name="Credit1" />
<Credit name="Credit2" />
<Credit name="Credit3" />
</Credits>
</ScrapMod>
```
## TODO:
- [X] Support for custom *.packed
- [X] Supoprt for Scrapland Remastered
- [ ] Support for both Scrapland versions in single .sm file
- [ ] Support for custom game files (i.e. `\Traslation\` files or custom `QuickConsole.py`)
- [ ] Recompiling *.py to *.pyc
- [ ] Mod settings.
- [ ] More meta info in `config.toml`
- [ ] More meta info in `config.xml`
- [ ] Multilanguage support
- [ ] More mods :wink:

View File

@ -18,7 +18,7 @@
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Name="PreviewColumn" Width="0*" />
<ColumnDefinition Width="0*" />
</Grid.ColumnDefinitions>
<ListView d:ItemsSource="{d:SampleData ItemCount=5}" Name="ModsList" Initialized="ModsList_Initialized" MouseDown="ModsList_MouseDown">
<ListView.View>
@ -28,15 +28,14 @@
<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" />
<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="Category" DisplayMemberBinding="{Binding Category}" />
<GridViewColumn Header="Mod Version" DisplayMemberBinding="{Binding Version}" />
<GridViewColumn Header="Game Version" DisplayMemberBinding="{Binding SupportedGameVersionsDisplay}" />
<GridViewColumn Header="Game Version" DisplayMemberBinding="{Binding RequiredGame}" />
</GridView>
</ListView.View>
<ListView.ItemContainerStyle>
@ -71,8 +70,8 @@
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Left">
<ComboBox Name="ScraplandVersion" Margin="0,0,10,0" SelectionChanged="ScraplandVersion_SelectionChanged">
<ComboBoxItem Name="OriginalVersionItem" Content="Original" IsEnabled="False" />
<ComboBoxItem Name ="RemasteredVersionItem" Content="Remastered" />
<ComboBoxItem Content="Original" IsEnabled="False" />
<ComboBoxItem Content="Remastered" />
</ComboBox>
<CheckBox Name="Windowed" Content=" Windowed " Margin="0,0,10,0" HorizontalAlignment="Center" VerticalAlignment="Center" />
<CheckBox Name="CloseLauncher" Content=" Close launcher " Margin="0,0,10,0" HorizontalAlignment="Center" VerticalAlignment="Center" />

View File

@ -28,7 +28,8 @@ namespace ScrapModLoader
{
if (Settings.Default.ModsPathes.Count == 0)
{
String path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Scrapland mods");
String path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
+ Path.DirectorySeparatorChar + "Scrapland mods";
Settings.Default.ModsPathes.Add(path);
Directory.CreateDirectory(path);
}
@ -36,24 +37,16 @@ namespace ScrapModLoader
// TODO: Refactor it to separate window with pretty loading animation
if (modsLauncher.ScraplandPath == String.Empty && modsLauncher.ScraplandRemasteredPath == String.Empty)
{
try
Boolean isFoundScrapland = modsLauncher.SearchForScrapland();
if (!isFoundScrapland)
{
Boolean isFoundScrapland = modsLauncher.SearchForScrapland();
if (!isFoundScrapland)
{
ButtonRunScrapland.IsEnabled = false;
MessageBox.Show("Unable to find Scrapland instalation. Please, specify yours game installation folder in settings.",
"Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
catch (KeyNotFoundException ex)
{
MessageBox.Show(ex.Message);
ButtonRunScrapland.IsEnabled = false;
MessageBox.Show("Error: unable to find Scrapland instalation. Please, specify yours game installation folder in settings.");
}
}
OriginalVersionItem.IsEnabled = modsLauncher.ScraplandPath != String.Empty;
RemasteredVersionItem.IsEnabled = modsLauncher.ScraplandRemasteredPath != String.Empty;
((ComboBoxItem)ScraplandVersion.Items[0]).IsEnabled = modsLauncher.ScraplandPath != String.Empty;
((ComboBoxItem)ScraplandVersion.Items[1]).IsEnabled = modsLauncher.ScraplandRemasteredPath != String.Empty;
ScraplandVersion.SelectedIndex = modsLauncher.ScraplandRemasteredPath != String.Empty ? 1 : 0;
@ -62,18 +55,18 @@ namespace ScrapModLoader
private void ModsList_Initialized(Object sender, EventArgs e) => ModsList.ItemsSource = modsLauncher.Mods;
private void ModsList_MouseDown(Object sender, MouseButtonEventArgs e)
private void ModsList_MouseDown(Object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (PreviewColumn.Width.Value != 0)
if (MainGrid.ColumnDefinitions[2].Width.Value != 0)
{
gridLength = PreviewColumn.Width;
PreviewColumn.Width = new GridLength(0, GridUnitType.Star);
gridLength = MainGrid.ColumnDefinitions[2].Width;
MainGrid.ColumnDefinitions[2].Width = new GridLength(0, GridUnitType.Star);
}
}
private void ListViewItem_PreviewMouseLeftButtonDown(Object sender, MouseButtonEventArgs e)
{
PreviewColumn.Width = gridLength;
MainGrid.ColumnDefinitions[2].Width = gridLength;
if (sender is ListViewItem item)
{
@ -81,7 +74,7 @@ namespace ScrapModLoader
if (selectedModName == null)
throw new KeyNotFoundException(nameof(selectedModName));
ScrapMod? mod = modsLauncher.Mods.Find(mod => mod.Name == selectedModName);
ScrapMod ? mod = modsLauncher.Mods.Find(mod => mod.Name == selectedModName);
if (mod == null)
throw new KeyNotFoundException(nameof(mod));
@ -99,35 +92,34 @@ namespace ScrapModLoader
private void WriteModInfo(ScrapMod mod)
{
ModInfo.Document.Blocks.Clear();
Paragraph paragraph = new Paragraph();
Paragraph parahraph = new Paragraph();
paragraph.Inlines.Add(new Bold(new Run("Description:\n")));
paragraph.Inlines.Add(new Run(mod.Description));
parahraph.Inlines.Add(new Bold(new Run("Description:\n")));
parahraph.Inlines.Add(new Run(mod.Description));
paragraph.Inlines.Add(new Bold(new Run("\n\nAuthors:\n")));
parahraph.Inlines.Add(new Bold(new Run("\n\nAuthors:\n")));
foreach (String autor in mod.Authors)
paragraph.Inlines.Add(new Run(autor + "\n"));
parahraph.Inlines.Add(new Run(autor + "\n"));
ModInfo.Document.Blocks.Add(paragraph);
ModInfo.Document.Blocks.Add(parahraph);
ModCreditsTab.Visibility = Visibility.Visible;
if (mod.Credits.Count == 0)
ModCreditsTab.Visibility = Visibility.Hidden;
else
{
ModCreditsTab.Visibility = Visibility.Visible;
ModCredits.Document.Blocks.Clear();
paragraph = new Paragraph();
parahraph = new Paragraph();
foreach (KeyValuePair<String, List<String>> credit in mod.Credits)
{
paragraph.Inlines.Add(new Bold(new Run(credit.Key + "\n")));
parahraph.Inlines.Add(new Bold(new Run(credit.Key + "\n")));
foreach (String autor in credit.Value)
paragraph.Inlines.Add(new Run(autor + "\n"));
paragraph.Inlines.Add(new Run("\n"));
parahraph.Inlines.Add(new Run(autor + "\n"));
parahraph.Inlines.Add(new Run("\n"));
}
ModCredits.Document.Blocks.Add(paragraph);
ModCredits.Document.Blocks.Add(parahraph);
}
}
@ -140,8 +132,6 @@ namespace ScrapModLoader
throw new NullReferenceException(nameof(isChecked));
StackPanel parent = (StackPanel)checkbox.Parent;
// TODO: replace by find template
// https://docs.microsoft.com/ru-ru/dotnet/desktop/wpf/data/how-to-find-datatemplate-generated-elements?view=netframeworkdesktop-4.8
Label label = (Label)parent.Children[2];
String? selectedModName = label.Content.ToString();
@ -158,16 +148,11 @@ namespace ScrapModLoader
private void ButtonSettings_Click(Object sender, RoutedEventArgs e)
{
SettingsWindow settingsWindow = new SettingsWindow(modsLauncher);
SettingsWindow settingsWindow = new SettingsWindow();
settingsWindow.ShowDialog();
if (settingsWindow.Save)
modsLauncher.ScanMods();
ModsList.Items.Refresh();
OriginalVersionItem.IsEnabled = modsLauncher.ScraplandPath != String.Empty;
RemasteredVersionItem.IsEnabled = modsLauncher.ScraplandRemasteredPath != String.Empty;
ScraplandVersion.SelectedIndex = modsLauncher.ScraplandRemasteredPath != String.Empty ? 1 : 0;
}
private void ButtonRunScrapland_Click(Object sender, RoutedEventArgs e)
@ -181,7 +166,7 @@ namespace ScrapModLoader
String gamePath = modsLauncher.SelectedGameVersion == "1.0" ?
modsLauncher.ScraplandPath : modsLauncher.ScraplandRemasteredPath;
Process.Start(Path.Combine(gamePath, @"bin\Scrap.exe"), args);
Process.Start(gamePath + @"\bin\Scrap.exe", args);
if (CloseLauncher.IsChecked ?? false)
Close();

View File

@ -1,10 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using Ionic.Zip;
using Microsoft.Win32;
@ -12,17 +8,17 @@ namespace ScrapModLoader
{
public class ModsLauncher
{
public List<ScrapMod> Mods { get; private set; } = new List<ScrapMod>();
public List<String> ModsPathes { get; set; } = Utils.StringCollectionToList(Settings.Default.ModsPathes);
public String ScraplandPath { get; set; } = Settings.Default.ScraplandPath;
public String ScraplandRemasteredPath { get; set; } = Settings.Default.ScraplandRemasteredPath;
public String SelectedGameVersion { get; set; } = "0.0";
public String LauncherVersion { get; set; } = "0.3";
public String SelectedGamePath { get; set; } = String.Empty;
public List<ScrapMod> Mods { get; private set; }
public String ScraplandPath { get; set; }
public String ScraplandRemasteredPath { get; set; }
public String SelectedGameVersion { get; set; }
public ModsLauncher()
{
Mods = new List<ScrapMod>();
ScraplandPath = Settings.Default.ScraplandPath;
ScraplandRemasteredPath = Settings.Default.ScraplandRemasteredPath;
SelectedGameVersion = "0.0";
}
public void ScanMods()
@ -34,7 +30,7 @@ namespace ScrapModLoader
{
String[] files = Directory.GetFiles(folder, "*.sm", SearchOption.AllDirectories);
foreach (String file in files)
Mods.Add(ScrapMod.LoadFromFile(file));
Mods.Add(new ScrapMod(file));
}
}
}
@ -55,32 +51,26 @@ namespace ScrapModLoader
if (key == null)
continue;
foreach (String subKeyName in key.GetSubKeyNames())
foreach (String subkey_name in key.GetSubKeyNames())
{
using RegistryKey? subKey = key.OpenSubKey(subKeyName);
if (subKey == null)
using RegistryKey? subkey = key.OpenSubKey(subkey_name);
if (subkey == null)
continue;
String? displayName = subKey.GetValue("DisplayName")?.ToString();
String? displayName = subkey.GetValue("DisplayName")?.ToString();
if (displayName == null)
continue;
if (displayName == "Scrapland" || displayName == "American McGee presents Scrapland")
if (displayName == "Scrapland")
{
ScraplandPath = subKey.GetValue("InstallLocation")?.ToString() ?? String.Empty;
if (String.IsNullOrEmpty(ScraplandPath))
throw new KeyNotFoundException("Installed Scrapland found, but unable to locate the instalation folder");
ScraplandPath = subkey.GetValue("InstallLocation")?.ToString() ?? "";
Settings.Default.ScraplandPath = ScraplandPath;
isFound = true;
}
if (displayName == "Scrapland Remastered")
{
ScraplandRemasteredPath = subKey.GetValue("InstallLocation")?.ToString() ?? String.Empty;
if (String.IsNullOrEmpty(ScraplandRemasteredPath))
throw new KeyNotFoundException("Installed Scrapland Remastered found, but unable to locate the instalation folder");
ScraplandRemasteredPath = subkey.GetValue("InstallLocation")?.ToString() ?? "";
Settings.Default.ScraplandRemasteredPath = ScraplandRemasteredPath;
isFound = true;
}
@ -92,76 +82,22 @@ namespace ScrapModLoader
public void LoadMods()
{
SelectedGamePath = SelectedGameVersion == "1.0" ? ScraplandPath : ScraplandRemasteredPath;
String gamePath = SelectedGameVersion == "1.0" ? ScraplandPath : ScraplandRemasteredPath;
foreach (ScrapMod mod in Mods)
{
// TODO: Warning about not loading mods that not supports selected version
if (!mod.SupportedGameVersions.Contains(SelectedGameVersion) ||
Single.Parse(mod.RequiredLauncher, CultureInfo.InvariantCulture) < Single.Parse(LauncherVersion, CultureInfo.InvariantCulture))
if (mod.RequiredGame != SelectedGameVersion)
continue;
if (mod.Checked)
{
if (!IsEnabled(mod))
Enable(mod);
if (!mod.IsEnabled(gamePath))
mod.Enable(gamePath);
}
else
{
if (IsEnabled(mod))
Disable(mod);
}
}
}
private String ModPath(ScrapMod mod) =>
Path.Combine(SelectedGamePath, "Mods", mod.Name);
public Boolean IsLoaded(ScrapMod mod) =>
Directory.Exists(ModPath(mod));
public Boolean IsEnabled(ScrapMod mod)
{
if (!IsLoaded(mod))
return false;
return Directory.EnumerateFiles(ModPath(mod), "*.disabled", SearchOption.AllDirectories).FirstOrDefault() == null;
}
public void Enable(ScrapMod mod)
{
if (!IsLoaded(mod))
LoadModToGame(mod);
if (IsEnabled(mod))
return;
foreach (String file in Directory.EnumerateFiles(ModPath(mod), "*.disabled", SearchOption.AllDirectories))
File.Move(file, Path.ChangeExtension(file, null));
}
public void Disable(ScrapMod mod)
{
if (!IsEnabled(mod))
return;
foreach (String file in Directory.EnumerateFiles(ModPath(mod), "*.packed", SearchOption.AllDirectories))
File.Move(file, file + ".disabled");
}
private void LoadModToGame(ScrapMod mod)
{
Directory.CreateDirectory(ModPath(mod));
using (ZipFile zipFile = ZipFile.Read(mod.ModPath))
{
foreach (ZipEntry zipEntry in zipFile)
{
if (!Path.GetFullPath(zipEntry.FileName).Contains(SelectedGameVersion))
continue;
if (Path.GetExtension(zipEntry.FileName) == ".packed")
zipEntry.Extract(ModPath(mod));
if (mod.IsEnabled(gamePath))
mod.Disable(gamePath);
}
}
}

View File

@ -2,113 +2,199 @@
using System.Collections.Generic;
using System.IO;
using System.Windows.Media.Imaging;
using System.Xml;
using Ionic.Zip;
using Tommy;
namespace ScrapModLoader
{
public class ScrapMod
{
public String Name { get; private set; } = String.Empty;
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 String Description { get; private set; } = String.Empty;
public String ModPath { get; private set; } = String.Empty;
public BitmapImage Icon { get; private set; } = new BitmapImage();
public Boolean Checked { get; set; } = false;
public String Category { get; private set; } = String.Empty;
public String Version { get; private set; } = String.Empty;
public String RequiredLauncher { get; private set; } = String.Empty;
public List<String> SupportedGameVersions { get; private set; } = new List<String>();
public String SupportedGameVersionsDisplay => String.Join(", ", SupportedGameVersions);
public List<String> Authors { get; private set; } = new List<String>();
public Dictionary<String, List<String>> Credits { get; private set; } = new Dictionary<String, List<String>>();
private ScrapMod()
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);
}
public static ScrapMod LoadFromFile(String path)
public Boolean IsLoaded(String gamePath) => Directory.Exists(gamePath + @"Mods\" + Name);
public Boolean IsEnabled(String gamePath)
{
using ZipFile zipFile = ZipFile.Read(path);
Byte[] iconBuffer = Utils.ExtractFromZip(zipFile, "icon.png");
Byte[] confBuffer = Utils.ExtractFromZip(zipFile, "config.toml");
ScrapMod mod = new ScrapMod()
if (IsLoaded(gamePath))
{
ModPath = path,
Icon = Utils.LoadImage(iconBuffer)
};
foreach (String file in Directory.EnumerateFiles(gamePath + @"Mods\" + Name))
{
if (Path.GetExtension(file) == ".disabled")
return false;
}
LoadConfig(mod, confBuffer);
return mod;
return true;
}
return false;
}
private static void LoadConfig(ScrapMod mod, Byte[] buffer)
public void Enable(String gamePath)
{
using MemoryStream sourceStream = new MemoryStream(buffer);
using StreamReader reader = new StreamReader(sourceStream);
if (!IsLoaded(gamePath))
LoadModToGame(gamePath);
TomlTable config = TOML.Parse(reader);
if (IsEnabled(gamePath))
return;
CheckConfig(config);
mod.Name = config["title"];
mod.Description = config["description"];
mod.Category = config["category"];
mod.Version = config["version"];
mod.RequiredLauncher = config["requiredLauncher"];
foreach (TomlNode version in config["supportedGameVersions"])
mod.SupportedGameVersions.Add(version);
foreach (TomlNode author in config["authors"])
mod.Authors.Add(author["name"]);
foreach (TomlNode credit in config["credits"])
foreach (String file in Directory.EnumerateFiles(gamePath + @"Mods\" + Name))
{
List<String> entries = new List<String>();
foreach (TomlNode entry in credit["credits"])
entries.Add(entry["name"]);
mod.Credits.Add(credit["group"], entries);
if (Path.GetExtension(file) == ".disabled")
File.Move(file, Path.ChangeExtension(file, null));
}
}
private static void CheckConfig(TomlTable config)
public void Disable(String gamePath)
{
if (!config.HasKey("title"))
throw new FileFormatException("No 'title' key in 'config.toml'");
if (!IsEnabled(gamePath))
return;
if (!config.HasKey("description"))
throw new FileFormatException("No 'description' key in 'config.toml'");
foreach (String file in Directory.EnumerateFiles(gamePath + @"Mods\" + Name))
{
if (Path.GetExtension(file) == ".packed")
File.Move(file, file + ".disabled");
}
}
if (!config.HasKey("category"))
throw new FileFormatException("No 'category' key in 'config.toml'");
private void LoadModToGame(String gamePath)
{
gamePath += @"Mods\" + Name;
Directory.CreateDirectory(gamePath);
if (!config.HasKey("version"))
throw new FileFormatException("No 'version' key in 'config.toml'");
using (ZipFile zipFile = ZipFile.Read(ModPath))
{
foreach (ZipEntry zipEntry in zipFile)
{
if (Path.GetExtension(zipEntry.FileName) == ".packed")
{
zipEntry.Extract(gamePath);
}
}
}
}
if (!config.HasKey("requiredLauncher"))
throw new FileFormatException("No 'name' key in 'config.toml'");
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);
}
}
if (!config.HasKey("supportedGameVersions"))
throw new FileFormatException("No 'supportedGameVersions' key in 'config.toml'");
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

@ -16,7 +16,6 @@
<ItemGroup>
<PackageReference Include="DotNetZip" Version="1.16.0" />
<PackageReference Include="Tommy" Version="3.1.2" />
</ItemGroup>
<ItemGroup>

View File

@ -63,7 +63,7 @@
<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" IsEnabled="False"/>
<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>
@ -71,10 +71,9 @@
<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" IsEnabled="False"/>
<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>
<Button Grid.Column="2" Content=" Auto find " HorizontalAlignment="Right" Grid.Row="2" VerticalAlignment="Center" Margin="5,0,5,0" Name="ButtonAutoFind" Click="ButtonAutoFind_Click" />
</Grid>
</GroupBox>
</Grid>

View File

@ -12,28 +12,27 @@ namespace ScrapModLoader
/// </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; }
private ModsLauncher ModsLauncherInstance { get; set; }
public SettingsWindow(ModsLauncher modsLauncher)
public SettingsWindow()
{
InitializeComponent();
Save = false;
ModsLauncherInstance = modsLauncher;
ModsPathesList.ItemsSource = ModsLauncherInstance.ModsPathes;
ScraplandPathTextBox.Text = ModsLauncherInstance.ScraplandPath;
ScraplandRemasteredPathTextBox.Text = ModsLauncherInstance.ScraplandRemasteredPath;
ModsPathes = Utils.StringCollectionToList(Settings.Default.ModsPathes);
ScraplandPath = Settings.Default.ScraplandPath;
ScraplandRemasteredPath = Settings.Default.ScraplandRemasteredPath;
Boolean enable = ModsLauncherInstance.ScraplandPath != String.Empty;
ButtonShowScrap.IsEnabled = enable;
ButtonClearScrap.IsEnabled = enable;
ModsPathesList.ItemsSource = ModsPathes;
ScraplandPathTextBox.Text = ScraplandPath;
ScraplandRemasteredPathTextBox.Text = ScraplandRemasteredPath;
enable = ModsLauncherInstance.ScraplandRemasteredPath != String.Empty;
ButtonShowScrapRemaster.IsEnabled = enable;
ButtonClearScrapRemaster.IsEnabled = enable;
ButtonShowScrap.IsEnabled = ScraplandPath != String.Empty;
ButtonShowScrapRemaster.IsEnabled = ScraplandRemasteredPath != String.Empty;
}
// -------------------------------------------
@ -45,13 +44,13 @@ namespace ScrapModLoader
{
String folder = Utils.GetFolderDialog();
if (folder != String.Empty)
ModsLauncherInstance.ModsPathes.Add(folder);
ModsPathes.Add(folder);
ModsPathesList.Items.Refresh();
}
private void ButtonRemove_Click(Object sender, RoutedEventArgs e)
{
ModsLauncherInstance.ModsPathes.Remove((String)ModsPathesList.SelectedValue);
ModsPathes.Remove((String)ModsPathesList.SelectedValue);
ModsPathesList.Items.Refresh();
ButtonRemove.IsEnabled = false;
@ -65,21 +64,21 @@ namespace ScrapModLoader
if (index == 0)
return;
String? temp = ModsLauncherInstance.ModsPathes[index - 1];
ModsLauncherInstance.ModsPathes[index - 1] = ModsLauncherInstance.ModsPathes[index];
ModsLauncherInstance.ModsPathes[index] = temp;
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 == ModsLauncherInstance.ModsPathes.Count - 1)
if (index == ModsPathes.Count - 1)
return;
String? temp = ModsLauncherInstance.ModsPathes[index + 1];
ModsLauncherInstance.ModsPathes[index + 1] = ModsLauncherInstance.ModsPathes[index];
ModsLauncherInstance.ModsPathes[index] = temp;
String? temp = ModsPathes[index + 1];
ModsPathes[index + 1] = ModsPathes[index];
ModsPathes[index] = temp;
ModsPathesList.Items.Refresh();
}
@ -109,76 +108,50 @@ namespace ScrapModLoader
private void ButtonBrowseScrap_Click(Object sender, RoutedEventArgs e)
{
String ScraplandPath = Utils.GetFolderDialog();
if (ScraplandPath != String.Empty)
String scraplandPath = Utils.GetFolderDialog();
if (scraplandPath != String.Empty)
{
ScraplandPathTextBox.Text = ScraplandPath + "\\";
ModsLauncherInstance.ScraplandPath = ScraplandPath + "\\";
ScraplandPathTextBox.Text = scraplandPath;
ScraplandPath = scraplandPath;
ButtonShowScrap.IsEnabled = true;
}
}
private void ButtonBrowseScrapRemaster_Click(Object sender, RoutedEventArgs e)
{
String ScraplandRemasteredPath = Utils.GetFolderDialog();
if (ScraplandRemasteredPath != String.Empty)
String scraplandRemasteredPath = Utils.GetFolderDialog();
if (scraplandRemasteredPath != String.Empty)
{
ScraplandRemasteredPathTextBox.Text = ScraplandRemasteredPath + "\\";
ModsLauncherInstance.ScraplandRemasteredPath = ScraplandRemasteredPath + "\\";
ScraplandRemasteredPathTextBox.Text = scraplandRemasteredPath;
ScraplandRemasteredPath = scraplandRemasteredPath;
ButtonShowScrapRemaster.IsEnabled = true;
}
}
private void ButtonClearScrap_Click(Object sender, RoutedEventArgs e)
{
ScraplandPathTextBox.Text = String.Empty;
ButtonClearScrap.IsEnabled = false;
ButtonShowScrap.IsEnabled = false;
ModsLauncherInstance.ScraplandPath = String.Empty;
ScraplandPath = String.Empty;
}
private void ButtonClearScrapRemaster_Click(Object sender, RoutedEventArgs e)
{
ScraplandRemasteredPathTextBox.Text = String.Empty;
ButtonClearScrapRemaster.IsEnabled = false;
ButtonShowScrapRemaster.IsEnabled = false;
ModsLauncherInstance.ScraplandRemasteredPath = String.Empty;
ScraplandRemasteredPath = String.Empty;
}
private void ButtonShowScrap_Click(Object sender, RoutedEventArgs e)
{
String? path = Path.GetDirectoryName(ModsLauncherInstance.ScraplandPath);
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(ModsLauncherInstance.ScraplandRemasteredPath);
String? path = Path.GetDirectoryName(ScraplandRemasteredPath);
if (path == null)
throw new DirectoryNotFoundException("Cannot find direcotry for Scrapland");
Process.Start("explorer.exe", path);
}
private void ButtonAutoFind_Click(Object sender, RoutedEventArgs e)
{
try
{
Boolean isFoundScrapland = ModsLauncherInstance.SearchForScrapland();
if (!isFoundScrapland)
MessageBox.Show("Error: unable to find Scrapland instalation. Please, specify yours game installation folder in settings.");
}
catch (KeyNotFoundException ex)
{
MessageBox.Show(ex.Message);
}
ScraplandPathTextBox.Text = ModsLauncherInstance.ScraplandPath;
ScraplandRemasteredPathTextBox.Text = ModsLauncherInstance.ScraplandRemasteredPath;
Boolean enable = ModsLauncherInstance.ScraplandPath != String.Empty;
ButtonShowScrap.IsEnabled = enable;
ButtonClearScrap.IsEnabled = enable;
enable = ModsLauncherInstance.ScraplandRemasteredPath != String.Empty;
ButtonShowScrapRemaster.IsEnabled = enable;
ButtonClearScrapRemaster.IsEnabled = enable;
}
// -------------------------------------------
// Window contols buttons
@ -188,11 +161,10 @@ namespace ScrapModLoader
private void ButtonSave_Click(Object sender, RoutedEventArgs e)
{
Settings.Default.ModsPathes.Clear();
Settings.Default.ModsPathes.AddRange(ModsLauncherInstance.ModsPathes.ToArray());
Settings.Default.ScraplandPath = ModsLauncherInstance.ScraplandPath;
Settings.Default.ScraplandRemasteredPath = ModsLauncherInstance.ScraplandRemasteredPath;
Settings.Default.ModsPathes.AddRange(ModsPathes.ToArray());
Settings.Default.ScraplandPath = ScraplandPath;
Settings.Default.ScraplandRemasteredPath = ScraplandRemasteredPath;
Settings.Default.Save();
Save = true;
Close();
}

View File

@ -1,59 +1,27 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Windows.Media.Imaging;
using Ionic.Zip;
namespace ScrapModLoader;
internal static class Utils
namespace ScrapModLoader
{
public static String GetFolderDialog()
internal static class Utils
{
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)
public static String GetFolderDialog()
{
if (item != null)
list.Add(item);
using System.Windows.Forms.FolderBrowserDialog? dialog = new System.Windows.Forms.FolderBrowserDialog();
System.Windows.Forms.DialogResult result = dialog.ShowDialog();
return dialog.SelectedPath;
}
return list;
}
public static Byte[] ExtractFromZip(ZipFile zip, String entry_path)
{
ZipEntry? entry = zip[entry_path];
if (entry == null)
throw new FileFormatException($"No '{entry_path}' in {zip.Name} found");
Byte[] buffer = new Byte[entry.UncompressedSize];
using (MemoryStream zipStream = new MemoryStream(buffer))
entry.Extract(zipStream);
return buffer;
}
public static BitmapImage LoadImage(Byte[] buffer)
{
using MemoryStream sourceStream = new MemoryStream(buffer);
BitmapImage? image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = sourceStream;
image.EndInit();
return image;
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;
}
}
}