165 lines
5.6 KiB
C#
165 lines
5.6 KiB
C#
using AnotherReplayReader.ReplayFile;
|
||
using Pfim;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Runtime.InteropServices.ComTypes;
|
||
using System.Threading.Tasks;
|
||
using System.Windows.Media;
|
||
using System.Windows.Media.Imaging;
|
||
using TechnologyAssembler.Core.IO;
|
||
|
||
namespace AnotherReplayReader
|
||
{
|
||
internal sealed class MinimapReader
|
||
{
|
||
public static readonly IReadOnlyDictionary<ImageFormat, PixelFormat> FormatMapper = new Dictionary<ImageFormat, PixelFormat>
|
||
{
|
||
{ ImageFormat.Rgb24, PixelFormats.Bgr24 },
|
||
{ ImageFormat.Rgba32, PixelFormats.Bgr32 },
|
||
{ ImageFormat.Rgb8, PixelFormats.Gray8 },
|
||
{ ImageFormat.R5g5b5a1, PixelFormats.Bgr555 },
|
||
{ ImageFormat.R5g5b5, PixelFormats.Bgr555 },
|
||
{ ImageFormat.R5g6b5, PixelFormats.Bgr565 },
|
||
};
|
||
|
||
private readonly BigMinimapCache _cache;
|
||
private readonly string _mapFolderPath;
|
||
private readonly string _modFolderPath;
|
||
|
||
public MinimapReader(BigMinimapCache cache, string mapFolderPath, string modFolderPath)
|
||
{
|
||
_cache = cache;
|
||
_mapFolderPath = mapFolderPath;
|
||
_modFolderPath = modFolderPath;
|
||
}
|
||
|
||
public Task<BitmapSource?> TryReadTargaAsync(Replay replay, double dpiX = 96.0, double dpiY = 96.0)
|
||
{
|
||
var mapPath = replay.MapPath.TrimEnd('/');
|
||
var mapName = mapPath.Substring(mapPath.LastIndexOf('/') + 1);
|
||
var minimapPath = $"{mapPath}/{mapName}_art.tga";
|
||
return Task.Run(() => TryReadTarga(minimapPath, replay.Mod, dpiX, dpiY));
|
||
}
|
||
|
||
public BitmapSource? TryReadTarga(string path, Mod mod, double dpiX = 96.0, double dpiY = 96.0)
|
||
{
|
||
using var memoryStream = new MemoryStream();
|
||
{
|
||
using var stream = TryGetStream(path, mod);
|
||
if (stream is null)
|
||
{
|
||
return null;
|
||
}
|
||
stream.CopyTo(memoryStream);
|
||
}
|
||
|
||
bool isJpgPng = IsJpgPng(memoryStream);
|
||
memoryStream.Position = 0;
|
||
if (isJpgPng)
|
||
{
|
||
try
|
||
{
|
||
var decoder = BitmapDecoder.Create(memoryStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
|
||
return decoder.Frames[0];
|
||
}
|
||
catch { }
|
||
}
|
||
memoryStream.Position = 0;
|
||
using var targa = Targa.Create(memoryStream, new PfimConfig());
|
||
if (targa == null)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
try
|
||
{
|
||
var bitmap = BitmapSource.Create(targa.Width, targa.Height, dpiX, dpiY, FormatMapper[targa.Format], null, targa.Data, targa.Stride);
|
||
bitmap.Freeze();
|
||
return bitmap;
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
Debug.Instance.DebugMessage += $"Exception creating BitmapSource from minimap:\r\n {exception}\r\n";
|
||
return null;
|
||
}
|
||
}
|
||
|
||
private Stream? TryGetStream(string path, Mod mod)
|
||
{
|
||
const string customMapPrefix = "data/maps/internal/";
|
||
if (Directory.Exists(_mapFolderPath) && path.StartsWith(customMapPrefix))
|
||
{
|
||
var minimapPath = Path.Combine(_mapFolderPath, path.Substring(customMapPrefix.Length));
|
||
if (File.Exists(minimapPath))
|
||
{
|
||
return File.OpenRead(minimapPath);
|
||
}
|
||
}
|
||
|
||
if (!mod.IsRA3)
|
||
{
|
||
var modSkudefPaths = Enumerable.Empty<string>();
|
||
foreach (var subFolder in Directory.EnumerateDirectories(_modFolderPath))
|
||
{
|
||
modSkudefPaths = modSkudefPaths.Concat(Directory.EnumerateFiles(subFolder, $"{mod.ModName}_{mod.ModVersion}.SkuDef"));
|
||
}
|
||
|
||
DronePlatform.BuildTechnologyAssembler();
|
||
foreach (var modSkudefPath in modSkudefPaths)
|
||
{
|
||
try
|
||
{
|
||
using var fs = new SkuDefFileSystemProvider("modConfig", modSkudefPath);
|
||
if (!fs.FileExists(path))
|
||
{
|
||
continue;
|
||
}
|
||
return fs.OpenStream(path, VirtualFileModeType.Open);
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
Debug.Instance.DebugMessage += $"Exception when reading minimap from mod bigs:\r\n {exception}\r\n";
|
||
}
|
||
}
|
||
}
|
||
|
||
if (_cache != null && _cache.TryGetEntry(path, out var big))
|
||
{
|
||
return big;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private static bool IsJpgPng(Stream stream)
|
||
{
|
||
var header = new byte[4];
|
||
var pos = stream.Position;
|
||
|
||
stream.Read(header, 0, header.Length);
|
||
stream.Position = pos;
|
||
|
||
// PNG
|
||
if (header[0] == 0x89 &&
|
||
header[1] == 0x50 &&
|
||
header[2] == 0x4E &&
|
||
header[3] == 0x47)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
// JPEG
|
||
if (header[0] == 0xFF &&
|
||
header[1] == 0xD8)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
// TGA(很粗略判断:通常无统一magic,只能 fallback)
|
||
return false;
|
||
}
|
||
}
|
||
}
|