* dotnet format style --severity info Some changes were manually reverted. * dotnet format analyzers --serverity info Some changes have been minimally adapted. * Restore a few unused methods and variables * Silence dotnet format IDE0060 warnings * Silence dotnet format IDE0052 warnings * Address or silence dotnet format IDE1006 warnings * Address dotnet format CA1816 warnings * Address or silence dotnet format CA2208 warnings * Address or silence dotnet format CA1806 and a few CA1854 warnings * Address dotnet format CA2211 warnings * Address dotnet format CA1822 warnings * Address or silence dotnet format CA1069 warnings * Make dotnet format succeed in style mode * Address or silence dotnet format CA2211 warnings * Address review comments * Address dotnet format CA2208 warnings properly * Make ProcessResult readonly * Address most dotnet format whitespace warnings * Apply dotnet format whitespace formatting A few of them have been manually reverted and the corresponding warning was silenced * Add previously silenced warnings back I have no clue how these disappeared * Revert formatting changes for while and for-loops * Format if-blocks correctly * Run dotnet format style after rebase * Run dotnet format whitespace after rebase * Run dotnet format style after rebase * Run dotnet format analyzers after rebase * Run dotnet format after rebase and remove unused usings - analyzers - style - whitespace * Disable 'prefer switch expression' rule * Add comments to disabled warnings * Fix a few disabled warnings * Fix naming rule violation, Convert shader properties to auto-property and convert values to const * Simplify properties and array initialization, Use const when possible, Remove trailing commas * Start working on disabled warnings * Fix and silence a few dotnet-format warnings again * Run dotnet format after rebase * Use using declaration instead of block syntax * Address IDE0251 warnings * Address a few disabled IDE0060 warnings * Silence IDE0060 in .editorconfig * Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas" This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e. * dotnet format whitespace after rebase * First dotnet format pass * Fix naming rule violations * Fix typo * Add trailing commas, use targeted new and use array initializer * Fix build issues * Fix remaining build issues * Remove SuppressMessage for CA1069 where possible * Address dotnet format issues * Address formatting issues Co-authored-by: Ac_K <acoustik666@gmail.com> * Add GetHashCode implementation for RenderingSurfaceInfo * Explicitly silence CA1822 for every affected method in Syscall * Address formatting issues in Demangler.cs * Address review feedback Co-authored-by: Ac_K <acoustik666@gmail.com> * Revert marking service methods as static * Next dotnet format pass * Address review feedback --------- Co-authored-by: Ac_K <acoustik666@gmail.com>
222 lines
6.7 KiB
C#
222 lines
6.7 KiB
C#
using Avalonia.Media;
|
|
using LibHac.Common;
|
|
using LibHac.Fs;
|
|
using LibHac.Fs.Fsa;
|
|
using LibHac.FsSystem;
|
|
using LibHac.Ncm;
|
|
using LibHac.Tools.Fs;
|
|
using LibHac.Tools.FsSystem;
|
|
using LibHac.Tools.FsSystem.NcaUtils;
|
|
using Ryujinx.Ava.UI.Models;
|
|
using Ryujinx.HLE.FileSystem;
|
|
using SixLabors.ImageSharp;
|
|
using SixLabors.ImageSharp.PixelFormats;
|
|
using System;
|
|
using System.Buffers.Binary;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.IO;
|
|
using Color = Avalonia.Media.Color;
|
|
|
|
namespace Ryujinx.Ava.UI.ViewModels
|
|
{
|
|
internal class UserFirmwareAvatarSelectorViewModel : BaseModel
|
|
{
|
|
private static readonly Dictionary<string, byte[]> _avatarStore = new();
|
|
|
|
private ObservableCollection<ProfileImageModel> _images;
|
|
private Color _backgroundColor = Colors.White;
|
|
|
|
private int _selectedIndex;
|
|
|
|
public UserFirmwareAvatarSelectorViewModel()
|
|
{
|
|
_images = new ObservableCollection<ProfileImageModel>();
|
|
|
|
LoadImagesFromStore();
|
|
}
|
|
|
|
public Color BackgroundColor
|
|
{
|
|
get => _backgroundColor;
|
|
set
|
|
{
|
|
_backgroundColor = value;
|
|
OnPropertyChanged();
|
|
ChangeImageBackground();
|
|
}
|
|
}
|
|
|
|
public ObservableCollection<ProfileImageModel> Images
|
|
{
|
|
get => _images;
|
|
set
|
|
{
|
|
_images = value;
|
|
OnPropertyChanged();
|
|
}
|
|
}
|
|
|
|
public int SelectedIndex
|
|
{
|
|
get => _selectedIndex;
|
|
set
|
|
{
|
|
_selectedIndex = value;
|
|
|
|
if (_selectedIndex == -1)
|
|
{
|
|
SelectedImage = null;
|
|
}
|
|
else
|
|
{
|
|
SelectedImage = _images[_selectedIndex].Data;
|
|
}
|
|
|
|
OnPropertyChanged();
|
|
}
|
|
}
|
|
|
|
public byte[] SelectedImage { get; private set; }
|
|
|
|
private void LoadImagesFromStore()
|
|
{
|
|
Images.Clear();
|
|
|
|
foreach (var image in _avatarStore)
|
|
{
|
|
Images.Add(new ProfileImageModel(image.Key, image.Value));
|
|
}
|
|
}
|
|
|
|
private void ChangeImageBackground()
|
|
{
|
|
foreach (var image in Images)
|
|
{
|
|
image.BackgroundColor = new SolidColorBrush(BackgroundColor);
|
|
}
|
|
}
|
|
|
|
public static void PreloadAvatars(ContentManager contentManager, VirtualFileSystem virtualFileSystem)
|
|
{
|
|
if (_avatarStore.Count > 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string contentPath = contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.BuiltInSystem, NcaContentType.Data);
|
|
string avatarPath = VirtualFileSystem.SwitchPathToSystemPath(contentPath);
|
|
|
|
if (!string.IsNullOrWhiteSpace(avatarPath))
|
|
{
|
|
using IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open);
|
|
|
|
Nca nca = new(virtualFileSystem.KeySet, ncaFileStream);
|
|
IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
|
|
|
foreach (DirectoryEntryEx item in romfs.EnumerateEntries())
|
|
{
|
|
// TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy.
|
|
if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") && item.FullPath.Contains("szs"))
|
|
{
|
|
using var file = new UniqueRef<IFile>();
|
|
|
|
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
|
|
|
using MemoryStream stream = new();
|
|
using MemoryStream streamPng = new();
|
|
|
|
file.Get.AsStream().CopyTo(stream);
|
|
|
|
stream.Position = 0;
|
|
|
|
Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256);
|
|
|
|
avatarImage.SaveAsPng(streamPng);
|
|
|
|
_avatarStore.Add(item.FullPath, streamPng.ToArray());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static byte[] DecompressYaz0(Stream stream)
|
|
{
|
|
using BinaryReader reader = new(stream);
|
|
|
|
reader.ReadInt32(); // Magic
|
|
|
|
uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32());
|
|
|
|
reader.ReadInt64(); // Padding
|
|
|
|
byte[] input = new byte[stream.Length - stream.Position];
|
|
stream.Read(input, 0, input.Length);
|
|
|
|
uint inputOffset = 0;
|
|
|
|
byte[] output = new byte[decodedLength];
|
|
uint outputOffset = 0;
|
|
|
|
ushort mask = 0;
|
|
byte header = 0;
|
|
|
|
while (outputOffset < decodedLength)
|
|
{
|
|
if ((mask >>= 1) == 0)
|
|
{
|
|
header = input[inputOffset++];
|
|
mask = 0x80;
|
|
}
|
|
|
|
if ((header & mask) != 0)
|
|
{
|
|
if (outputOffset == output.Length)
|
|
{
|
|
break;
|
|
}
|
|
|
|
output[outputOffset++] = input[inputOffset++];
|
|
}
|
|
else
|
|
{
|
|
byte byte1 = input[inputOffset++];
|
|
byte byte2 = input[inputOffset++];
|
|
|
|
uint dist = (uint)((byte1 & 0xF) << 8) | byte2;
|
|
uint position = outputOffset - (dist + 1);
|
|
|
|
uint length = (uint)byte1 >> 4;
|
|
if (length == 0)
|
|
{
|
|
length = (uint)input[inputOffset++] + 0x12;
|
|
}
|
|
else
|
|
{
|
|
length += 2;
|
|
}
|
|
|
|
uint gap = outputOffset - position;
|
|
uint nonOverlappingLength = length;
|
|
|
|
if (nonOverlappingLength > gap)
|
|
{
|
|
nonOverlappingLength = gap;
|
|
}
|
|
|
|
Buffer.BlockCopy(output, (int)position, output, (int)outputOffset, (int)nonOverlappingLength);
|
|
outputOffset += nonOverlappingLength;
|
|
position += nonOverlappingLength;
|
|
length -= nonOverlappingLength;
|
|
|
|
while (length-- > 0)
|
|
{
|
|
output[outputOffset++] = output[position++];
|
|
}
|
|
}
|
|
}
|
|
|
|
return output;
|
|
}
|
|
}
|
|
}
|