Add some support for reading icons from archives.
parent
ac87659b2a
commit
b2ffdb9f23
|
@ -349,3 +349,4 @@ MigrationBackup/
|
||||||
# Ionide (cross platform F# VS Code tools) working folder
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
.ionide/
|
.ionide/
|
||||||
Upload/
|
Upload/
|
||||||
|
LANCommander/Icon/
|
||||||
|
|
|
@ -49,6 +49,8 @@ namespace LANCommander.Playnite.Extension
|
||||||
{
|
{
|
||||||
var existingGame = PlayniteApi.Database.Games.FirstOrDefault(g => g.GameId == game.Id.ToString() && g.PluginId == Id && g.IsInstalled);
|
var existingGame = PlayniteApi.Database.Games.FirstOrDefault(g => g.GameId == game.Id.ToString() && g.PluginId == Id && g.IsInstalled);
|
||||||
|
|
||||||
|
var iconUri = new Uri(new Uri(Settings.ServerAddress), $"Games/GetIcon/{game.Id}");
|
||||||
|
|
||||||
var metadata = new GameMetadata()
|
var metadata = new GameMetadata()
|
||||||
{
|
{
|
||||||
IsInstalled = existingGame != null,
|
IsInstalled = existingGame != null,
|
||||||
|
@ -57,7 +59,8 @@ namespace LANCommander.Playnite.Extension
|
||||||
Description = game.Description,
|
Description = game.Description,
|
||||||
GameId = game.Id.ToString(),
|
GameId = game.Id.ToString(),
|
||||||
ReleaseDate = new ReleaseDate(game.ReleasedOn),
|
ReleaseDate = new ReleaseDate(game.ReleasedOn),
|
||||||
Version = game.Archives.OrderByDescending(a => a.CreatedOn).FirstOrDefault().Version,
|
Version = game.Archives.OrderByDescending(a => a.CreatedOn).FirstOrDefault().Version,
|
||||||
|
Icon = new MetadataFile(iconUri.ToString())
|
||||||
};
|
};
|
||||||
|
|
||||||
gameMetadata.Add(metadata);
|
gameMetadata.Add(metadata);
|
||||||
|
|
|
@ -9,6 +9,8 @@ using Microsoft.EntityFrameworkCore;
|
||||||
using LANCommander.Data;
|
using LANCommander.Data;
|
||||||
using LANCommander.Data.Models;
|
using LANCommander.Data.Models;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using LANCommander.Services;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
namespace LANCommander.Controllers
|
namespace LANCommander.Controllers
|
||||||
{
|
{
|
||||||
|
@ -153,10 +155,14 @@ namespace LANCommander.Controllers
|
||||||
foreach (var archive in game.Archives.OrderByDescending(a => a.CreatedOn))
|
foreach (var archive in game.Archives.OrderByDescending(a => a.CreatedOn))
|
||||||
{
|
{
|
||||||
var archiveFile = Path.Combine("Upload", archive.ObjectKey);
|
var archiveFile = Path.Combine("Upload", archive.ObjectKey);
|
||||||
|
var iconFile = Path.Combine("Icon", $"{game.Id}.png");
|
||||||
|
|
||||||
if (System.IO.File.Exists(archiveFile))
|
if (System.IO.File.Exists(archiveFile))
|
||||||
System.IO.File.Delete(archiveFile);
|
System.IO.File.Delete(archiveFile);
|
||||||
|
|
||||||
|
if (System.IO.File.Exists(iconFile))
|
||||||
|
System.IO.File.Delete(iconFile);
|
||||||
|
|
||||||
archiveRepo.Delete(archive);
|
archiveRepo.Delete(archive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,6 +178,82 @@ namespace LANCommander.Controllers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> GetIcon(Guid id)
|
||||||
|
{
|
||||||
|
var cachedPath = Path.Combine("Icon", $"{id}.png");
|
||||||
|
|
||||||
|
if (!System.IO.Directory.Exists("Icon"))
|
||||||
|
System.IO.Directory.CreateDirectory("Icon");
|
||||||
|
|
||||||
|
if (System.IO.File.Exists(cachedPath))
|
||||||
|
{
|
||||||
|
return File(System.IO.File.ReadAllBytes(cachedPath), "image/png");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using (var repo = new Repository<Game>(Context, HttpContext))
|
||||||
|
{
|
||||||
|
var game = await repo.Find(id);
|
||||||
|
|
||||||
|
if (game.Archives == null || game.Archives.Count == 0)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
var archive = game.Archives.OrderByDescending(a => a.CreatedOn).FirstOrDefault();
|
||||||
|
|
||||||
|
Bitmap bitmap = null;
|
||||||
|
|
||||||
|
var manifest = ArchiveService.ReadManifest(archive.ObjectKey);
|
||||||
|
var iconReference = ArchiveService.ReadFile(archive.ObjectKey, manifest.Icon);
|
||||||
|
|
||||||
|
if (IsWinPEFile(iconReference))
|
||||||
|
{
|
||||||
|
var tmp = System.IO.Path.GetTempFileName();
|
||||||
|
|
||||||
|
System.IO.File.WriteAllBytes(tmp, iconReference);
|
||||||
|
|
||||||
|
var icon = System.Drawing.Icon.ExtractAssociatedIcon(tmp);
|
||||||
|
|
||||||
|
bitmap = icon.ToBitmap();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using (var ms = new MemoryStream(iconReference))
|
||||||
|
{
|
||||||
|
bitmap = (Bitmap)Bitmap.FromStream(ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var iconPng = ConvertToPng(bitmap);
|
||||||
|
|
||||||
|
System.IO.File.WriteAllBytes(cachedPath, iconPng);
|
||||||
|
|
||||||
|
return File(iconPng, "image/png");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsWinPEFile(byte[] file)
|
||||||
|
{
|
||||||
|
var mz = new byte[2];
|
||||||
|
|
||||||
|
using (var ms = new MemoryStream(file))
|
||||||
|
{
|
||||||
|
ms.Read(mz, 0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return System.Text.Encoding.UTF8.GetString(mz) == "MZ";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] ConvertToPng(Image img)
|
||||||
|
{
|
||||||
|
using (var stream = new MemoryStream())
|
||||||
|
{
|
||||||
|
img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
|
||||||
|
|
||||||
|
return stream.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool GameExists(Guid id)
|
private bool GameExists(Guid id)
|
||||||
{
|
{
|
||||||
return (Context.Games?.Any(e => e.Id == id)).GetValueOrDefault();
|
return (Context.Games?.Any(e => e.Id == id)).GetValueOrDefault();
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
using LANCommander.Models;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using YamlDotNet.Serialization;
|
||||||
|
using YamlDotNet.Serialization.NamingConventions;
|
||||||
|
|
||||||
|
namespace LANCommander.Services
|
||||||
|
{
|
||||||
|
public static class ArchiveService
|
||||||
|
{
|
||||||
|
public static GameManifest ReadManifest(string objectKey)
|
||||||
|
{
|
||||||
|
var upload = Path.Combine("Upload", objectKey);
|
||||||
|
|
||||||
|
string manifestContents = String.Empty;
|
||||||
|
|
||||||
|
if (!System.IO.File.Exists(upload))
|
||||||
|
throw new FileNotFoundException(upload);
|
||||||
|
|
||||||
|
using (ZipArchive zip = ZipFile.OpenRead(upload))
|
||||||
|
{
|
||||||
|
var entry = zip.Entries.FirstOrDefault(e => e.FullName == "_manifest.yml");
|
||||||
|
|
||||||
|
if (entry == null)
|
||||||
|
throw new FileNotFoundException("Manifest not found");
|
||||||
|
|
||||||
|
using (StreamReader sr = new StreamReader(entry.Open()))
|
||||||
|
{
|
||||||
|
manifestContents = sr.ReadToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var deserializer = new DeserializerBuilder()
|
||||||
|
.WithNamingConvention(PascalCaseNamingConvention.Instance)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
|
||||||
|
var manifest = deserializer.Deserialize<GameManifest>(manifestContents);
|
||||||
|
|
||||||
|
return manifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] ReadFile(string objectKey, string path)
|
||||||
|
{
|
||||||
|
var upload = Path.Combine("Upload", objectKey);
|
||||||
|
|
||||||
|
if (!System.IO.File.Exists(upload))
|
||||||
|
throw new FileNotFoundException(upload);
|
||||||
|
|
||||||
|
using (ZipArchive zip = ZipFile.OpenRead(upload))
|
||||||
|
{
|
||||||
|
var entry = zip.Entries.FirstOrDefault(e => e.FullName == path);
|
||||||
|
|
||||||
|
if (entry == null)
|
||||||
|
throw new FileNotFoundException(path);
|
||||||
|
|
||||||
|
using (var ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
entry.Open().CopyTo(ms);
|
||||||
|
|
||||||
|
return ms.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,6 +33,7 @@
|
||||||
<table class="table table-vcenter table-mobile-md card-table">
|
<table class="table table-vcenter table-mobile-md card-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th></th>
|
||||||
<th>
|
<th>
|
||||||
@Html.DisplayNameFor(model => model.Title)
|
@Html.DisplayNameFor(model => model.Title)
|
||||||
</th>
|
</th>
|
||||||
|
@ -65,6 +66,7 @@
|
||||||
@foreach (var item in Model)
|
@foreach (var item in Model)
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
|
<td><img src="@Url.Action("GetIcon", "Games", new { id = item.Id })" /></td>
|
||||||
<td>
|
<td>
|
||||||
@Html.DisplayFor(modelItem => item.Title)
|
@Html.DisplayFor(modelItem => item.Title)
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"runtimeOptions": {
|
||||||
|
"configProperties": {
|
||||||
|
"System.Drawing.EnableUnixSupport": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue