|
|
@@ -0,0 +1,177 @@
|
|
|
+using ChocolArm64.Exceptions;
|
|
|
+using ChocolArm64.Memory;
|
|
|
+using Ryujinx.HLE.Logging;
|
|
|
+using Ryujinx.HLE.OsHle;
|
|
|
+using Ryujinx.HLE.OsHle.Handles;
|
|
|
+using Ryujinx.HLE.Resource;
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.IO;
|
|
|
+
|
|
|
+
|
|
|
+namespace Ryujinx.HLE.Font
|
|
|
+{
|
|
|
+ public class SharedFontManager
|
|
|
+ {
|
|
|
+ private const uint SharedMemorySize = 0x1100000;
|
|
|
+ private Logger Log;
|
|
|
+
|
|
|
+ private string FontsPath;
|
|
|
+
|
|
|
+ private object ShMemLock;
|
|
|
+
|
|
|
+ private (AMemory, long, long)[] ShMemPositions;
|
|
|
+
|
|
|
+ private Dictionary<SharedFontType, byte[]> FontData;
|
|
|
+
|
|
|
+ private uint[] LoadedFonts;
|
|
|
+
|
|
|
+ public SharedFontManager(Logger Log, string SystemPath)
|
|
|
+ {
|
|
|
+ this.Log = Log;
|
|
|
+ this.FontsPath = Path.Combine(SystemPath, "fonts");
|
|
|
+
|
|
|
+ ShMemLock = new object();
|
|
|
+
|
|
|
+ ShMemPositions = new(AMemory, long, long)[0];
|
|
|
+
|
|
|
+ FontData = new Dictionary<SharedFontType, byte[]>()
|
|
|
+ {
|
|
|
+ { SharedFontType.JapanUsEurope, GetData("FontStandard") },
|
|
|
+ { SharedFontType.SimplifiedChinese, GetData("FontChineseSimplified") },
|
|
|
+ { SharedFontType.SimplifiedChineseEx, GetData("FontExtendedChineseSimplified") },
|
|
|
+ { SharedFontType.TraditionalChinese, GetData("FontChineseTraditional") },
|
|
|
+ { SharedFontType.Korean, GetData("FontKorean") },
|
|
|
+ { SharedFontType.NintendoEx, GetData("FontNintendoExtended") }
|
|
|
+ };
|
|
|
+
|
|
|
+ int FontMemoryUsage = 0;
|
|
|
+ foreach (byte[] data in FontData.Values)
|
|
|
+ {
|
|
|
+ FontMemoryUsage += data.Length;
|
|
|
+ FontMemoryUsage += 0x8;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (FontMemoryUsage > SharedMemorySize)
|
|
|
+ {
|
|
|
+ throw new InvalidSystemResourceException($"The sum of all fonts size exceed the shared memory size. Please make sure that the fonts don't exceed {SharedMemorySize} bytes in total. (actual size: {FontMemoryUsage} bytes)");
|
|
|
+ }
|
|
|
+
|
|
|
+ LoadedFonts = new uint[FontData.Count];
|
|
|
+ }
|
|
|
+
|
|
|
+ public byte[] GetData(string FontName)
|
|
|
+ {
|
|
|
+ string FontFilePath = Path.Combine(FontsPath, $"{FontName}.ttf");
|
|
|
+ if (File.Exists(FontFilePath))
|
|
|
+ {
|
|
|
+ return File.ReadAllBytes(FontFilePath);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ throw new InvalidSystemResourceException($"Font \"{FontName}.ttf\" not found. Please provide it in \"{FontsPath}\".");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void MapFont(SharedFontType FontType, AMemory Memory, long Position)
|
|
|
+ {
|
|
|
+ uint SharedMemoryAddressOffset = GetSharedMemoryAddressOffset(FontType);
|
|
|
+ // TODO: find what are the 8 bytes before the font
|
|
|
+ Memory.WriteUInt64(Position + SharedMemoryAddressOffset - 8, 0);
|
|
|
+ Memory.WriteBytes(Position + SharedMemoryAddressOffset, FontData[FontType]);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void PropagateNewMapFont(SharedFontType Type)
|
|
|
+ {
|
|
|
+ lock (ShMemLock)
|
|
|
+ {
|
|
|
+ foreach ((AMemory Memory, long Position, long Size) in ShMemPositions)
|
|
|
+ {
|
|
|
+ AMemoryMapInfo MemoryInfo = Memory.Manager.GetMapInfo(Position);
|
|
|
+
|
|
|
+ if (MemoryInfo == null)
|
|
|
+ {
|
|
|
+ throw new VmmPageFaultException(Position);
|
|
|
+ }
|
|
|
+
|
|
|
+ // The memory is read only, we need to changes that to add the new font
|
|
|
+ AMemoryPerm originalPerms = MemoryInfo.Perm;
|
|
|
+ Memory.Manager.Reprotect(Position, Size, AMemoryPerm.RW);
|
|
|
+ MapFont(Type, Memory, Position);
|
|
|
+ Memory.Manager.Reprotect(Position, Size, originalPerms);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ internal void ShMemMap(object sender, EventArgs e)
|
|
|
+ {
|
|
|
+ HSharedMem SharedMem = (HSharedMem)sender;
|
|
|
+
|
|
|
+ lock (ShMemLock)
|
|
|
+ {
|
|
|
+ ShMemPositions = SharedMem.GetVirtualPositions();
|
|
|
+
|
|
|
+ (AMemory Memory, long Position, long Size) = ShMemPositions[ShMemPositions.Length - 1];
|
|
|
+
|
|
|
+ for (int Type = 0; Type < LoadedFonts.Length; Type++)
|
|
|
+ {
|
|
|
+ if (LoadedFonts[(int)Type] == 1)
|
|
|
+ {
|
|
|
+ MapFont((SharedFontType)Type, Memory, Position);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ internal void ShMemUnmap(object sender, EventArgs e)
|
|
|
+ {
|
|
|
+ HSharedMem SharedMem = (HSharedMem)sender;
|
|
|
+
|
|
|
+ lock (ShMemLock)
|
|
|
+ {
|
|
|
+ ShMemPositions = SharedMem.GetVirtualPositions();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Load(SharedFontType FontType)
|
|
|
+ {
|
|
|
+ if (LoadedFonts[(int)FontType] == 0)
|
|
|
+ {
|
|
|
+ PropagateNewMapFont(FontType);
|
|
|
+ }
|
|
|
+
|
|
|
+ LoadedFonts[(int)FontType] = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ public uint GetLoadState(SharedFontType FontType)
|
|
|
+ {
|
|
|
+ if (LoadedFonts[(int)FontType] != 1)
|
|
|
+ {
|
|
|
+ // Some games don't request a load, so we need to load it here.
|
|
|
+ Load(FontType);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return LoadedFonts[(int)FontType];
|
|
|
+ }
|
|
|
+
|
|
|
+ public uint GetFontSize(SharedFontType FontType)
|
|
|
+ {
|
|
|
+ return (uint)FontData[FontType].Length;
|
|
|
+ }
|
|
|
+
|
|
|
+ public uint GetSharedMemoryAddressOffset(SharedFontType FontType)
|
|
|
+ {
|
|
|
+ uint Pos = 0x8;
|
|
|
+
|
|
|
+ for (SharedFontType Type = SharedFontType.JapanUsEurope; Type < FontType; Type++)
|
|
|
+ {
|
|
|
+ Pos += GetFontSize(Type);
|
|
|
+ Pos += 0x8;
|
|
|
+ }
|
|
|
+
|
|
|
+ return Pos;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int Count => FontData.Count;
|
|
|
+ }
|
|
|
+}
|