Compare commits

...

13 Commits

Author SHA1 Message Date
Strongleong 4f80b72603
Merge pull request #4 from Grumgog/refactor-modLauncher
refactor: modsLauncer and MainWindow
2022-02-16 19:54:54 +11:00
strongleong deffd05581 Bug fixes 2022-02-16 19:52:38 +11:00
Никонов Василий 25c3fd0425 refactor: modsLauncer and MainWindow
refactor some of code of this classes
2022-02-16 00:30:28 +11:00
strongleong 085320215a Removed unnecesarry stuff 2022-02-15 22:52:26 +11:00
strongleong 110029692d Fixed button 2022-02-15 22:52:11 +11:00
strongleong 8da74ffa6d Refactored methods that do filesystem stuff from `ScrapMod` to `ModsLauncher` 2022-02-15 22:51:37 +11:00
strongleong ca870eb66b Try-catched some exceptions 2022-02-15 22:50:56 +11:00
Strongleong 75333e8f68
Merge pull request #1 from Grumgog/refactor-Scrap-mod
(refactor): ScrapMod class code refactoring
2022-02-15 21:43:52 +11:00
Никонов Василий 90764c8636 do PR note 2022-02-14 09:46:59 +11:00
Никонов Василий 1e25aa210a Method of loading data from archive moved to Utils class.
Re-write property supportedGameVersionsDisplay more shortly and optimized.
Used var declaration where type is  obvious.

Property initialized in declaration.
constructor ScrapMod change to private, but LoadFromFile change to public.
LoadIcon moved to Utils and renamed to LoadImage.
2022-02-14 01:15:13 +11:00
strongleong 9deb717eea Added check for minimal launcher version 2022-02-13 01:30:17 +11:00
strongleong 515aa519a4 Added support for different files for different versions in mod 2022-02-13 01:26:00 +11:00
strongleong 02d9df8160 Changed config format from `xml` to `toml` 2022-02-13 00:09:27 +11:00
9 changed files with 383 additions and 304 deletions

View File

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

View File

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

View File

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

View File

@ -1,6 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using Ionic.Zip;
using Microsoft.Win32; using Microsoft.Win32;
@ -8,17 +12,17 @@ namespace ScrapModLoader
{ {
public class ModsLauncher public class ModsLauncher
{ {
public List<ScrapMod> Mods { get; private set; } public List<ScrapMod> Mods { get; private set; } = new List<ScrapMod>();
public String ScraplandPath { get; set; } public List<String> ModsPathes { get; set; } = Utils.StringCollectionToList(Settings.Default.ModsPathes);
public String ScraplandRemasteredPath { get; set; } public String ScraplandPath { get; set; } = Settings.Default.ScraplandPath;
public String SelectedGameVersion { get; set; } 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 ModsLauncher() public ModsLauncher()
{ {
Mods = new List<ScrapMod>();
ScraplandPath = Settings.Default.ScraplandPath;
ScraplandRemasteredPath = Settings.Default.ScraplandRemasteredPath;
SelectedGameVersion = "0.0";
} }
public void ScanMods() public void ScanMods()
@ -30,7 +34,7 @@ namespace ScrapModLoader
{ {
String[] files = Directory.GetFiles(folder, "*.sm", SearchOption.AllDirectories); String[] files = Directory.GetFiles(folder, "*.sm", SearchOption.AllDirectories);
foreach (String file in files) foreach (String file in files)
Mods.Add(new ScrapMod(file)); Mods.Add(ScrapMod.LoadFromFile(file));
} }
} }
} }
@ -51,26 +55,32 @@ namespace ScrapModLoader
if (key == null) if (key == null)
continue; continue;
foreach (String subkey_name in key.GetSubKeyNames()) foreach (String subKeyName in key.GetSubKeyNames())
{ {
using RegistryKey? subkey = key.OpenSubKey(subkey_name); using RegistryKey? subKey = key.OpenSubKey(subKeyName);
if (subkey == null) if (subKey == null)
continue; continue;
String? displayName = subkey.GetValue("DisplayName")?.ToString(); String? displayName = subKey.GetValue("DisplayName")?.ToString();
if (displayName == null) if (displayName == null)
continue; continue;
if (displayName == "Scrapland") if (displayName == "Scrapland" || displayName == "American McGee presents Scrapland")
{ {
ScraplandPath = subkey.GetValue("InstallLocation")?.ToString() ?? ""; ScraplandPath = subKey.GetValue("InstallLocation")?.ToString() ?? String.Empty;
if (String.IsNullOrEmpty(ScraplandPath))
throw new KeyNotFoundException("Installed Scrapland found, but unable to locate the instalation folder");
Settings.Default.ScraplandPath = ScraplandPath; Settings.Default.ScraplandPath = ScraplandPath;
isFound = true; isFound = true;
} }
if (displayName == "Scrapland Remastered") if (displayName == "Scrapland Remastered")
{ {
ScraplandRemasteredPath = subkey.GetValue("InstallLocation")?.ToString() ?? ""; 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");
Settings.Default.ScraplandRemasteredPath = ScraplandRemasteredPath; Settings.Default.ScraplandRemasteredPath = ScraplandRemasteredPath;
isFound = true; isFound = true;
} }
@ -82,22 +92,76 @@ namespace ScrapModLoader
public void LoadMods() public void LoadMods()
{ {
String gamePath = SelectedGameVersion == "1.0" ? ScraplandPath : ScraplandRemasteredPath; SelectedGamePath = SelectedGameVersion == "1.0" ? ScraplandPath : ScraplandRemasteredPath;
foreach (ScrapMod mod in Mods) foreach (ScrapMod mod in Mods)
{ {
if (mod.RequiredGame != SelectedGameVersion) // 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))
continue; continue;
if (mod.Checked) if (mod.Checked)
{ {
if (!mod.IsEnabled(gamePath)) if (!IsEnabled(mod))
mod.Enable(gamePath); Enable(mod);
} }
else else
{ {
if (mod.IsEnabled(gamePath)) if (IsEnabled(mod))
mod.Disable(gamePath); 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));
} }
} }
} }

View File

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

View File

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

View File

@ -63,7 +63,7 @@
<TextBox Grid.Row="0" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Center" x:Name="ScraplandPathTextBox" IsEnabled="False" /> <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"> <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=" 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=" Clear " HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,5,0" Name="ButtonClearScrap" Click="ButtonClearScrap_Click" IsEnabled="False"/>
<Button Content=" Show In Explorer " HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,5,0" Name="ButtonShowScrap" IsEnabled="False" Click="ButtonShowScrap_Click" /> <Button Content=" Show In Explorer " HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,5,0" Name="ButtonShowScrap" IsEnabled="False" Click="ButtonShowScrap_Click" />
</StackPanel> </StackPanel>
@ -71,9 +71,10 @@
<TextBox Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Center" x:Name="ScraplandRemasteredPathTextBox" IsEnabled="False" /> <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"> <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=" 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=" Clear " HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,5,0" Name="ButtonClearScrapRemaster" Click="ButtonClearScrapRemaster_Click" IsEnabled="False"/>
<Button Content=" Show In Explorer " HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,5,0" Name="ButtonShowScrapRemaster" IsEnabled="False" Click="ButtonShowScrapRemaster_Click" /> <Button Content=" Show In Explorer " HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,5,0" Name="ButtonShowScrapRemaster" IsEnabled="False" Click="ButtonShowScrapRemaster_Click" />
</StackPanel> </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> </Grid>
</GroupBox> </GroupBox>
</Grid> </Grid>

View File

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

View File

@ -1,27 +1,59 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.IO;
using System.Windows.Media.Imaging;
namespace ScrapModLoader using Ionic.Zip;
namespace ScrapModLoader;
internal static class Utils
{ {
internal static class Utils public static String GetFolderDialog()
{ {
public static String GetFolderDialog() using System.Windows.Forms.FolderBrowserDialog dialog = new System.Windows.Forms.FolderBrowserDialog();
{ System.Windows.Forms.DialogResult result = dialog.ShowDialog();
using System.Windows.Forms.FolderBrowserDialog? dialog = new System.Windows.Forms.FolderBrowserDialog(); return dialog.SelectedPath;
System.Windows.Forms.DialogResult result = dialog.ShowDialog(); }
return dialog.SelectedPath;
}
public static List<String> StringCollectionToList(StringCollection collection) public static List<String> StringCollectionToList(StringCollection collection)
{
List<String> list = new List<String>();
foreach (String? item in collection)
{ {
List<String> list = new List<String>(); if (item != null)
foreach (String? item in collection) list.Add(item);
{
if (item != null)
list.Add(item);
}
return list;
} }
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;
} }
} }