Skip to content

Commit

Permalink
Merge pull request #239 from anno-mods/devel/icon_embeds
Browse files Browse the repository at this point in the history
Assign an Icon for a tweakerfile
  • Loading branch information
taubenangriff authored Jul 5, 2023
2 parents 6e76549 + c32daf1 commit 2c57404
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 13 deletions.
7 changes: 7 additions & 0 deletions ModManager/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public App()
gameSetup.SetModDirectoryName(Settings.Default.ModDirectoryName);

services.AddSingleton<IImyaSetupService, ImyaSetupService>();
services.AddSingleton<IGameFilesService, GameFileService>();
services.AddTransient<ICyclicDependencyAttributeFactory, CyclicDependencyAttributeFactory>();
services.AddTransient<IMissingModinfoAttributeFactory, MissingModinfoAttributeFactory>();
services.AddTransient<IModCompabilityAttributeFactory, ModCompabilityAttributeFactory>();
Expand Down Expand Up @@ -150,6 +151,8 @@ public App()

services.AddTransient<DlcTextConverter>();
services.AddTransient<FilenameValidationConverter>();
services.AddTransient<FilepathToImageConverter>();

services.AddSingleton<SelfUpdater>();
})
.Build();
Expand Down Expand Up @@ -189,13 +192,17 @@ protected override async void OnStartup(StartupEventArgs e)
appSettings.Initialize();
var installationService = AppHost.Services.GetRequiredService<IInstallationService>();

var gamefileService = AppHost.Services.GetRequiredService<IGameFilesService>();
await gamefileService.LoadAsync();

if (appSettings.UseRateLimiting)
installationService.DownloadConfig.MaximumBytesPerSecond = appSettings.DownloadRateLimit;

await AppHost.StartAsync();

//hacky converters with dependencyinjection....
Resources.Add("DlcTextConverter", AppHost.Services.GetRequiredService<DlcTextConverter>());
Resources.Add("FilepathToImageConverter", AppHost.Services.GetRequiredService<FilepathToImageConverter>());
Resources.Add("FilenameValidationConverter", AppHost.Services.GetRequiredService<FilenameValidationConverter>());

var startupForm = AppHost.Services.GetRequiredService<MainWindow>();
Expand Down
4 changes: 4 additions & 0 deletions ModManager/ModManager_Views.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,17 @@
<PackageReference Include="FontAwesome.WPF" Version="4.7.0.9" />
<PackageReference Include="MaterialDesignThemes" Version="4.3.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Pfim" Version="0.11.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ModManager_Classes\ModManager_Classes.csproj" />
</ItemGroup>

<ItemGroup>
<Reference Include="AnnoRDA">
<HintPath>lib\AnnoRDA.dll</HintPath>
</Reference>
<Reference Include="Markdown.Xaml">
<HintPath>lib\Markdown.Xaml.dll</HintPath>
</Reference>
Expand Down
71 changes: 71 additions & 0 deletions ModManager/Services/GameFileService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using Imya.Models;
using Imya.Services.Interfaces;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Pfim;
using AnnoRDA;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using AnnoRDA.Builder;

namespace Imya.Services
{
public class GameFileService : IGameFilesService
{
private IGameSetupService _gameSetupService;

private FileSystem _fileSystem;

private bool isLoading = false;

public GameFileService(IGameSetupService gameSetupService)
{
_gameSetupService = gameSetupService;
_gameSetupService.GameRootPathChanged += async (newpath) => await LoadAsync();
}

public async Task LoadAsync()
{
if (!_gameSetupService.IsMaindataValid)
return;
isLoading = true;
await Task.Run(() =>
{
var fs = FileSystemBuilder.Create()
.FromPath(_gameSetupService.MaindataPath)
.WithDefaultSorting()
.OnlyArchivesMatchingWildcard(@"data*.rda")
.AddWhitelisted("*.png", "*_0.dds")
.Build();
_fileSystem = fs;
isLoading = false;
});
}

public Stream? OpenFile(string path)
{
if (isLoading)
return null;

return _fileSystem.OpenRead(path);
}

/// <summary>
/// Like OpenFile, but redirects *.png to *_0.dds
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public Stream? OpenIcon(string path)
{
if (path.EndsWith(".png"))
path = path[0..^4] + "_0.dds";
return OpenFile(path);
}
}
}
20 changes: 20 additions & 0 deletions ModManager/Services/IGameFilesService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Imya.Models;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;

namespace Imya.Services.Interfaces
{
public interface IGameFilesService
{
Task LoadAsync();
Stream? OpenFile(String filepath);
Stream? OpenIcon(String iconPath);

}
}
103 changes: 103 additions & 0 deletions ModManager/ValueConverters/FilepathToImageConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using Imya.Services.Interfaces;
using Pfim;
using System;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Imya.UI.ValueConverters
{
[ValueConversion(typeof(String), typeof(ImageSource))]
internal class FilepathToImageConverter : IValueConverter
{
private IGameFilesService _gameFilesService;
static string parameterregex = @"\b[0-9]+x[0-9]+\b";

public FilepathToImageConverter(IGameFilesService gameFilesService)
{
_gameFilesService = gameFilesService;
}

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (string.IsNullOrWhiteSpace(value as String))
return new Image();

IImage image = Pfimage.FromStream(_gameFilesService.OpenIcon((String)value));

if (image is null)
return new Image();

Point size;
bool UseMipmaps = false;
if (parameter is string parameter_str && Regex.IsMatch(parameter_str, parameterregex))
{
var desired_size = parameter_str.Split("x");
if (long.TryParse(desired_size[0], out var x) && long.TryParse(desired_size[1], out var y))
{
size = new Point(x, y);
UseMipmaps = true;
}
}
var wpfimg = UseMipmaps ? WpfImageMipmapped(image, size) : WpfImage(image);
return wpfimg;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}

private static ImageSource WpfImage(IImage image)
{
var pinnedArray = GCHandle.Alloc(image.Data, GCHandleType.Pinned);
var addr = pinnedArray.AddrOfPinnedObject();
var bsource = BitmapSource.Create(image.Width, image.Height, 96.0, 96.0,
PixelFormat(image), null, addr, image.DataLen, image.Stride);

return bsource;
}

private static ImageSource WpfImageMipmapped(IImage image, Point size)
{
var pinnedArray = GCHandle.Alloc(image.Data, GCHandleType.Pinned);
var addr = pinnedArray.AddrOfPinnedObject();

var mip = image.MipMaps.Where(x => x.Height >= size.X && x.Width >= size.Y).LastOrDefault();
if (mip is null)
return WpfImage(image);

var mipAddr = addr + mip.DataOffset;
var mipSource = BitmapSource.Create(mip.Width, mip.Height, 96.0, 96.0,
PixelFormat(image), null, mipAddr, mip.DataLen, mip.Stride);

return mipSource;
}

private static PixelFormat PixelFormat(IImage image)
{
switch (image.Format)
{
case ImageFormat.Rgb24:
return PixelFormats.Bgr24;
case ImageFormat.Rgba32:
return PixelFormats.Bgra32;
case ImageFormat.Rgb8:
return PixelFormats.Gray8;
case ImageFormat.R5g5b5a1:
case ImageFormat.R5g5b5:
return PixelFormats.Bgr555;
case ImageFormat.R5g6b5:
return PixelFormats.Bgr565;
default:
throw new Exception($"Unable to convert {image.Format} to WPF PixelFormat");
}
}
}
}
32 changes: 21 additions & 11 deletions ModManager/Views/Components/ModTweaker.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
TrueValue="Visible"
FalseValue="Hidden" />
<Converters:NegateBoolConverter x:Key="NegateBool" />
<Converters:IsEmptyToVisibility x:Key="CollapsedOnEmpty" OnEmpty="Collapsed" OnElse="Visible" />
<Converters:IsEmptyToVisibility x:Key="CollapsedOnEmpty"
OnEmpty="Collapsed"
OnElse="Visible" />
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
Expand Down Expand Up @@ -72,16 +74,24 @@
<DataTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Margin="5">
<TextBlock Style="{StaticResource IMYA_TEXT}"
Text="{Binding Title}"
FontWeight="Bold"
Margin="2"/>
<TextBlock Style="{StaticResource IMYA_TEXT}"
Text="{Binding Description}"
FontWeight="Light"
Margin="2" />

<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=IconFilename, Converter = {StaticResource FilepathToImageConverter}, UpdateSourceTrigger=Explicit, ConverterParameter=46x46}"
MaxWidth="46"
MaxHeight="46"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="{Binding HasIcon, Converter={StaticResource VisibleOnTrue}}"/>
<StackPanel VerticalAlignment="Center"
Margin="5">
<TextBlock Style="{StaticResource IMYA_TEXT}"
Text="{Binding Title}"
FontWeight="Bold"
Margin="2"/>
<TextBlock Style="{StaticResource IMYA_TEXT}"
Text="{Binding Description}"
FontWeight="Light"
Margin="2" />
</StackPanel>
</StackPanel>
</Expander.Header>

Expand Down
Binary file added ModManager/lib/AnnoRDA.dll
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ public class TweakerFile : PropertyChangedNotifier
public string EditFilename => Path.GetFileNameWithoutExtension(SourceFilename) + ".imyatweak.include.xml";

public string Title { get; private set; }

public string Description { get; private set; }
public string? IconFilename { get; private set; }

public bool hasIcon { get => IconFilename is not null; }

public ObservableCollection<IExposedModValue> Exposes
{
Expand Down Expand Up @@ -317,7 +319,8 @@ public static bool TryInit(string basePath, string filePath, out TweakerFile twe
var parser = new XmlPatchParser(doc);
var editables = parser.FetchExposes(tweakerFile);
tweakerFile.Title = parser.FetchTweakerFileTitle() ?? tweakerFile.Title;
tweakerFile.Description = parser.FetchTweakerFileDescription() ?? tweakerFile.Description;
tweakerFile.Description = parser.FetchTweakerFileDescription() ?? tweakerFile.Description;
tweakerFile.IconFilename = parser.FetchTweakerFileIcon() ?? tweakerFile.IconFilename;

tweakerFile.ModOps = new ObservableCollection<ModOp>(parser.FetchModOps(tweakerFile.TargetDocument));

Expand Down
9 changes: 9 additions & 0 deletions ModManager_Classes/Models/ModTweaker/IO/XmlPatchParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,14 @@ internal IEnumerable<ModOp> FetchModOps(XmlDocument ImportDocument)

return tweaks.Attributes?["Title"]?.Value;
}

public string? FetchTweakerFileIcon()
{
var tweaks = Document.SelectSingleNode("/ModOps/ImyaTweaks");
if (tweaks is null)
return null;

return tweaks.Attributes?["Icon"]?.Value;
}
}
}
4 changes: 4 additions & 0 deletions ModManager_Classes/Services/GameSetupService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ private set
}
private string _gameRootPath;

public String MaindataPath { get => Path.Combine(GameRootPath, "maindata"); }

public bool IsMaindataValid { get => Directory.Exists(MaindataPath); }

public string? ExecutablePath { get; private protected set; }
public string? ExecutableDir { get; private protected set; }

Expand Down
2 changes: 2 additions & 0 deletions ModManager_Classes/Services/Interfaces/IGameSetupService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ public interface IGameSetupService
bool IsGameRunning { get; set; }
bool IsModloaderInstalled { get; }
bool IsValidSetup { get; }
bool IsMaindataValid { get; }
string GameRootPath { get; }
string MaindataPath { get; }
string? ExecutablePath { get; }
string ModDirectoryName { get; }

Expand Down

0 comments on commit 2c57404

Please # to comment.