| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- 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;
- private byte[] _selectedImage;
- 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 => _selectedImage;
- private set => _selectedImage = value;
- }
- 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;
- }
- }
- }
- }
|