forked from Mirror/Ryujinx
e3b36db71c
* hle: Some cleanup This PR cleaned up a bit the HLE folder and the VirtualFileSystem one, since we use LibHac, we can use some class of it directly instead of duplicate things. The "Content" of VFS folder is removed since it should be handled in the NCM service directly. A larger cleanup should be done later since there is still be duplicated code here and there. * Fix Headless.SDL2 * Addresses gdkchan feedback
182 lines
No EOL
6.7 KiB
C#
182 lines
No EOL
6.7 KiB
C#
using LibHac.Common;
|
|
using LibHac.Fs;
|
|
using LibHac.Fs.Fsa;
|
|
using LibHac.FsSystem;
|
|
using LibHac.Ncm;
|
|
using LibHac.Tools.FsSystem;
|
|
using LibHac.Tools.FsSystem.NcaUtils;
|
|
using Ryujinx.HLE.Exceptions;
|
|
using Ryujinx.HLE.FileSystem;
|
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
|
using Ryujinx.HLE.HOS.Services.Sdb.Pl.Types;
|
|
using System;
|
|
using System.Buffers.Binary;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
|
|
namespace Ryujinx.HLE.HOS.Services.Sdb.Pl
|
|
{
|
|
class SharedFontManager
|
|
{
|
|
private static readonly uint FontKey = 0x06186249;
|
|
private static readonly uint BFTTFMagic = 0x18029a7f;
|
|
|
|
private readonly Switch _device;
|
|
private readonly SharedMemoryStorage _storage;
|
|
|
|
private struct FontInfo
|
|
{
|
|
public int Offset;
|
|
public int Size;
|
|
|
|
public FontInfo(int offset, int size)
|
|
{
|
|
Offset = offset;
|
|
Size = size;
|
|
}
|
|
}
|
|
|
|
private Dictionary<SharedFontType, FontInfo> _fontData;
|
|
|
|
public SharedFontManager(Switch device, SharedMemoryStorage storage)
|
|
{
|
|
_device = device;
|
|
_storage = storage;
|
|
}
|
|
|
|
public void Initialize()
|
|
{
|
|
_fontData?.Clear();
|
|
_fontData = null;
|
|
|
|
}
|
|
|
|
public void EnsureInitialized(ContentManager contentManager)
|
|
{
|
|
if (_fontData == null)
|
|
{
|
|
_storage.ZeroFill();
|
|
|
|
uint fontOffset = 0;
|
|
|
|
FontInfo CreateFont(string name)
|
|
{
|
|
if (contentManager.TryGetFontTitle(name, out ulong fontTitle) && contentManager.TryGetFontFilename(name, out string fontFilename))
|
|
{
|
|
string contentPath = contentManager.GetInstalledContentPath(fontTitle, StorageId.BuiltInSystem, NcaContentType.Data);
|
|
string fontPath = _device.FileSystem.SwitchPathToSystemPath(contentPath);
|
|
|
|
if (!string.IsNullOrWhiteSpace(fontPath))
|
|
{
|
|
byte[] data;
|
|
|
|
using (IStorage ncaFileStream = new LocalStorage(fontPath, FileAccess.Read, FileMode.Open))
|
|
{
|
|
Nca nca = new Nca(_device.System.KeySet, ncaFileStream);
|
|
IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel);
|
|
|
|
using var fontFile = new UniqueRef<IFile>();
|
|
|
|
romfs.OpenFile(ref fontFile.Ref(), ("/" + fontFilename).ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
|
|
|
data = DecryptFont(fontFile.Get.AsStream());
|
|
}
|
|
|
|
FontInfo info = new FontInfo((int)fontOffset, data.Length);
|
|
|
|
WriteMagicAndSize(fontOffset, data.Length);
|
|
|
|
fontOffset += 8;
|
|
|
|
uint start = fontOffset;
|
|
|
|
for (; fontOffset - start < data.Length; fontOffset++)
|
|
{
|
|
_storage.GetRef<byte>(fontOffset) = data[fontOffset - start];
|
|
}
|
|
|
|
return info;
|
|
}
|
|
else
|
|
{
|
|
if (!contentManager.TryGetSystemTitlesName(fontTitle, out string titleName))
|
|
{
|
|
titleName = "Unknown";
|
|
}
|
|
|
|
throw new InvalidSystemResourceException($"{titleName} ({fontTitle:x8}) system title not found! This font will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx#requirements for more information)");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new ArgumentException($"Unknown font \"{name}\"!");
|
|
}
|
|
}
|
|
|
|
_fontData = new Dictionary<SharedFontType, FontInfo>
|
|
{
|
|
{ SharedFontType.JapanUsEurope, CreateFont("FontStandard") },
|
|
{ SharedFontType.SimplifiedChinese, CreateFont("FontChineseSimplified") },
|
|
{ SharedFontType.SimplifiedChineseEx, CreateFont("FontExtendedChineseSimplified") },
|
|
{ SharedFontType.TraditionalChinese, CreateFont("FontChineseTraditional") },
|
|
{ SharedFontType.Korean, CreateFont("FontKorean") },
|
|
{ SharedFontType.NintendoEx, CreateFont("FontNintendoExtended") }
|
|
};
|
|
|
|
if (fontOffset > Horizon.FontSize)
|
|
{
|
|
throw new InvalidSystemResourceException("The sum of all fonts size exceed the shared memory size. " +
|
|
$"Please make sure that the fonts don't exceed {Horizon.FontSize} bytes in total. (actual size: {fontOffset} bytes).");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void WriteMagicAndSize(ulong offset, int size)
|
|
{
|
|
const int key = 0x49621806;
|
|
|
|
int encryptedSize = BinaryPrimitives.ReverseEndianness(size ^ key);
|
|
|
|
_storage.GetRef<int>(offset + 0) = (int)BFTTFMagic;
|
|
_storage.GetRef<int>(offset + 4) = encryptedSize;
|
|
}
|
|
|
|
public int GetFontSize(SharedFontType fontType)
|
|
{
|
|
EnsureInitialized(_device.System.ContentManager);
|
|
|
|
return _fontData[fontType].Size;
|
|
}
|
|
|
|
public int GetSharedMemoryAddressOffset(SharedFontType fontType)
|
|
{
|
|
EnsureInitialized(_device.System.ContentManager);
|
|
|
|
return _fontData[fontType].Offset + 8;
|
|
}
|
|
|
|
private static byte[] DecryptFont(Stream bfttfStream)
|
|
{
|
|
static uint KXor(uint data) => data ^ FontKey;
|
|
|
|
using (BinaryReader reader = new BinaryReader(bfttfStream))
|
|
using (MemoryStream ttfStream = new MemoryStream())
|
|
using (BinaryWriter output = new BinaryWriter(ttfStream))
|
|
{
|
|
if (KXor(reader.ReadUInt32()) != BFTTFMagic)
|
|
{
|
|
throw new InvalidDataException("Error: Input file is not in BFTTF format!");
|
|
}
|
|
|
|
bfttfStream.Position += 4;
|
|
|
|
for (int i = 0; i < (bfttfStream.Length - 8) / 4; i++)
|
|
{
|
|
output.Write(KXor(reader.ReadUInt32()));
|
|
}
|
|
|
|
return ttfStream.ToArray();
|
|
}
|
|
}
|
|
}
|
|
} |