From eeb626947eac81b9d9d9a90ad6e29035c21d54a7 Mon Sep 17 00:00:00 2001 From: Thomas Guillemard Date: Sat, 4 Aug 2018 23:38:49 +0200 Subject: [PATCH] Implement Shared Fonts (#215) * Implement Shared Fonts This fully implements shared fonts. This commit is provided without fonts. This commit also add Size to HSharedMem.Positions to be able to add fonts to shared zones when RequestLoad is called. * Require the user to provide fonts in RyuFS/system * Use File.Exits instead of relying ona try/catch and change system resource exception format a bit * Make sure that font sum doesn't exceed 17MB Also rename font data dictionary for coherence. --- Ryujinx.HLE/Font/SharedFontManager.cs | 177 ++++++++++++++++++ .../Services/Pl => Font}/SharedFontType.cs | 4 +- Ryujinx.HLE/Hid/Hid.cs | 10 +- Ryujinx.HLE/Logging/LogClass.cs | 1 + Ryujinx.HLE/OsHle/Handles/HSharedMem.cs | 14 +- Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs | 8 +- Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs | 10 +- Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs | 1 - .../OsHle/Services/Pl/ISharedFontManager.cs | 72 ++++++- .../InvalidSystemResourceException.cs | 13 ++ Ryujinx.HLE/Ryujinx.HLE.csproj | 2 +- Ryujinx.HLE/Switch.cs | 12 +- Ryujinx.HLE/VirtualFileSystem.cs | 3 + 13 files changed, 292 insertions(+), 35 deletions(-) create mode 100644 Ryujinx.HLE/Font/SharedFontManager.cs rename Ryujinx.HLE/{OsHle/Services/Pl => Font}/SharedFontType.cs (76%) create mode 100644 Ryujinx.HLE/Resource/InvalidSystemResourceException.cs diff --git a/Ryujinx.HLE/Font/SharedFontManager.cs b/Ryujinx.HLE/Font/SharedFontManager.cs new file mode 100644 index 000000000..fce270de8 --- /dev/null +++ b/Ryujinx.HLE/Font/SharedFontManager.cs @@ -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 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.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; + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Pl/SharedFontType.cs b/Ryujinx.HLE/Font/SharedFontType.cs similarity index 76% rename from Ryujinx.HLE/OsHle/Services/Pl/SharedFontType.cs rename to Ryujinx.HLE/Font/SharedFontType.cs index 97fd95dc9..ca8a42b0c 100644 --- a/Ryujinx.HLE/OsHle/Services/Pl/SharedFontType.cs +++ b/Ryujinx.HLE/Font/SharedFontType.cs @@ -1,6 +1,6 @@ -namespace Ryujinx.HLE.OsHle.Services.Pl +namespace Ryujinx.HLE.Font { - enum SharedFontType + public enum SharedFontType { JapanUsEurope = 0, SimplifiedChinese = 1, diff --git a/Ryujinx.HLE/Hid/Hid.cs b/Ryujinx.HLE/Hid/Hid.cs index 2f007f1fb..43c040bb5 100644 --- a/Ryujinx.HLE/Hid/Hid.cs +++ b/Ryujinx.HLE/Hid/Hid.cs @@ -67,7 +67,7 @@ namespace Ryujinx.HLE.Input private object ShMemLock; - private (AMemory, long)[] ShMemPositions; + private (AMemory, long, long)[] ShMemPositions; public Hid(Logger Log) { @@ -75,7 +75,7 @@ namespace Ryujinx.HLE.Input ShMemLock = new object(); - ShMemPositions = new (AMemory, long)[0]; + ShMemPositions = new (AMemory, long, long)[0]; } internal void ShMemMap(object sender, EventArgs e) @@ -86,7 +86,7 @@ namespace Ryujinx.HLE.Input { ShMemPositions = SharedMem.GetVirtualPositions(); - (AMemory Memory, long Position) = ShMemPositions[ShMemPositions.Length - 1]; + (AMemory Memory, long Position, long Size) = ShMemPositions[ShMemPositions.Length - 1]; for (long Offset = 0; Offset < Horizon.HidSize; Offset += 8) { @@ -167,7 +167,7 @@ namespace Ryujinx.HLE.Input { lock (ShMemLock) { - foreach ((AMemory Memory, long Position) in ShMemPositions) + foreach ((AMemory Memory, long Position, long Size) in ShMemPositions) { long ControllerOffset = Position + HidControllersOffset; @@ -218,7 +218,7 @@ namespace Ryujinx.HLE.Input { lock (ShMemLock) { - foreach ((AMemory Memory, long Position) in ShMemPositions) + foreach ((AMemory Memory, long Position, long Size) in ShMemPositions) { long TouchScreenOffset = Position + HidTouchScreenOffset; diff --git a/Ryujinx.HLE/Logging/LogClass.cs b/Ryujinx.HLE/Logging/LogClass.cs index c377ace66..95cae7e0a 100644 --- a/Ryujinx.HLE/Logging/LogClass.cs +++ b/Ryujinx.HLE/Logging/LogClass.cs @@ -4,6 +4,7 @@ namespace Ryujinx.HLE.Logging { Audio, Cpu, + Font, Gpu, Hid, Kernel, diff --git a/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs b/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs index 6426e585e..cd3d82237 100644 --- a/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs +++ b/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs @@ -6,37 +6,37 @@ namespace Ryujinx.HLE.OsHle.Handles { class HSharedMem { - private List<(AMemory, long)> Positions; + private List<(AMemory, long, long)> Positions; public EventHandler MemoryMapped; public EventHandler MemoryUnmapped; public HSharedMem() { - Positions = new List<(AMemory, long)>(); + Positions = new List<(AMemory, long, long)>(); } - public void AddVirtualPosition(AMemory Memory, long Position) + public void AddVirtualPosition(AMemory Memory, long Position, long Size) { lock (Positions) { - Positions.Add((Memory, Position)); + Positions.Add((Memory, Position, Size)); MemoryMapped?.Invoke(this, EventArgs.Empty); } } - public void RemoveVirtualPosition(AMemory Memory, long Position) + public void RemoveVirtualPosition(AMemory Memory, long Position, long Size) { lock (Positions) { - Positions.Remove((Memory, Position)); + Positions.Remove((Memory, Position, Size)); MemoryUnmapped?.Invoke(this, EventArgs.Empty); } } - public (AMemory, long)[] GetVirtualPositions() + public (AMemory, long, long)[] GetVirtualPositions() { return Positions.ToArray(); } diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs b/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs index e816c44ec..6f7bc42fd 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs @@ -22,7 +22,7 @@ namespace Ryujinx.HLE.OsHle.Kernel private ConcurrentDictionary SyncWaits; - private HashSet<(HSharedMem, long)> MappedSharedMems; + private HashSet<(HSharedMem, long, long)> MappedSharedMems; private ulong CurrentHeapSize; @@ -83,7 +83,7 @@ namespace Ryujinx.HLE.OsHle.Kernel SyncWaits = new ConcurrentDictionary(); - MappedSharedMems = new HashSet<(HSharedMem, long)>(); + MappedSharedMems = new HashSet<(HSharedMem, long, long)>(); } static SvcHandler() @@ -138,9 +138,9 @@ namespace Ryujinx.HLE.OsHle.Kernel { lock (MappedSharedMems) { - foreach ((HSharedMem SharedMem, long Position) in MappedSharedMems) + foreach ((HSharedMem SharedMem, long Position, long Size) in MappedSharedMems) { - SharedMem.RemoveVirtualPosition(Memory, Position); + SharedMem.RemoveVirtualPosition(Memory, Position, Size); } MappedSharedMems.Clear(); diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs b/Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs index bb73f1ea1..f10cad7a2 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs @@ -174,15 +174,15 @@ namespace Ryujinx.HLE.OsHle.Kernel AMemoryHelper.FillWithZeros(Memory, Src, (int)Size); + SharedMem.AddVirtualPosition(Memory, Src, Size); + Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); lock (MappedSharedMems) { - MappedSharedMems.Add((SharedMem, Src)); + MappedSharedMems.Add((SharedMem, Src, Size)); } - SharedMem.AddVirtualPosition(Memory, Src); - ThreadState.X0 = 0; } @@ -210,11 +210,11 @@ namespace Ryujinx.HLE.OsHle.Kernel { Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory); - SharedMem.RemoveVirtualPosition(Memory, Src); + SharedMem.RemoveVirtualPosition(Memory, Src, Size); lock (MappedSharedMems) { - MappedSharedMems.Remove((SharedMem, Src)); + MappedSharedMems.Remove((SharedMem, Src, Size)); } ThreadState.X0 = 0; diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs b/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs index 08305522f..a968a1dbc 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs @@ -242,7 +242,6 @@ namespace Ryujinx.HLE.OsHle.Kernel Process.Scheduler.Suspend(CurrThread); IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr); - long Result = IpcHandler.IpcCall(Ns, Process, Memory, Session, Cmd, CmdPtr); Thread.Yield(); diff --git a/Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs b/Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs index 9f85f3d10..b8447ac65 100644 --- a/Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs +++ b/Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs @@ -1,3 +1,4 @@ +using Ryujinx.HLE.Font; using Ryujinx.HLE.OsHle.Ipc; using System.Collections.Generic; @@ -13,11 +14,12 @@ namespace Ryujinx.HLE.OsHle.Services.Pl { m_Commands = new Dictionary() { - { 0, RequestLoad }, - { 1, GetLoadState }, - { 2, GetFontSize }, - { 3, GetSharedMemoryAddressOffset }, - { 4, GetSharedMemoryNativeHandle } + { 0, RequestLoad }, + { 1, GetLoadState }, + { 2, GetFontSize }, + { 3, GetSharedMemoryAddressOffset }, + { 4, GetSharedMemoryNativeHandle }, + { 5, GetSharedFontInOrderOfPriority } }; } @@ -25,26 +27,34 @@ namespace Ryujinx.HLE.OsHle.Services.Pl { SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); + Context.Ns.Font.Load(FontType); + return 0; } public long GetLoadState(ServiceCtx Context) { - Context.ResponseData.Write(1); //Loaded + SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); + + Context.ResponseData.Write(Context.Ns.Font.GetLoadState(FontType)); return 0; } public long GetFontSize(ServiceCtx Context) { - Context.ResponseData.Write(Horizon.FontSize); + SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); + + Context.ResponseData.Write(Context.Ns.Font.GetFontSize(FontType)); return 0; } public long GetSharedMemoryAddressOffset(ServiceCtx Context) { - Context.ResponseData.Write(0); + SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); + + Context.ResponseData.Write(Context.Ns.Font.GetSharedMemoryAddressOffset(FontType)); return 0; } @@ -57,5 +67,51 @@ namespace Ryujinx.HLE.OsHle.Services.Pl return 0; } + + private uint AddFontToOrderOfPriorityList(ServiceCtx Context, SharedFontType FontType, uint BufferPos, out uint LoadState) + { + long TypesPosition = Context.Request.ReceiveBuff[0].Position; + long TypesSize = Context.Request.ReceiveBuff[0].Size; + + long OffsetsPosition = Context.Request.ReceiveBuff[1].Position; + long OffsetsSize = Context.Request.ReceiveBuff[1].Size; + + long FontSizeBufferPosition = Context.Request.ReceiveBuff[2].Position; + long FontSizeBufferSize = Context.Request.ReceiveBuff[2].Size; + + LoadState = Context.Ns.Font.GetLoadState(FontType); + + if (BufferPos >= TypesSize || BufferPos >= OffsetsSize || BufferPos >= FontSizeBufferSize) + { + return 0; + } + + Context.Memory.WriteUInt32(TypesPosition + BufferPos, (uint)FontType); + Context.Memory.WriteUInt32(OffsetsPosition + BufferPos, Context.Ns.Font.GetSharedMemoryAddressOffset(FontType)); + Context.Memory.WriteUInt32(FontSizeBufferPosition + BufferPos, Context.Ns.Font.GetFontSize(FontType)); + + BufferPos += 4; + + return BufferPos; + } + + public long GetSharedFontInOrderOfPriority(ServiceCtx Context) + { + ulong LanguageCode = Context.RequestData.ReadUInt64(); + uint LoadedCount = 0; + uint BufferPos = 0; + uint Loaded = 0; + + for (int Type = 0; Type < Context.Ns.Font.Count; Type++) + { + BufferPos = AddFontToOrderOfPriorityList(Context, (SharedFontType)Type, BufferPos, out Loaded); + LoadedCount += Loaded; + } + + Context.ResponseData.Write(LoadedCount); + Context.ResponseData.Write(Context.Ns.Font.Count); + + return 0; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/Resource/InvalidSystemResourceException.cs b/Ryujinx.HLE/Resource/InvalidSystemResourceException.cs new file mode 100644 index 000000000..35c4874a3 --- /dev/null +++ b/Ryujinx.HLE/Resource/InvalidSystemResourceException.cs @@ -0,0 +1,13 @@ +using System; + +namespace Ryujinx.HLE.Resource +{ + public class InvalidSystemResourceException : Exception + { + public InvalidSystemResourceException(string message) + : base(message) + { + } + + } +} diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index acef4be9e..f7fb84a58 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index 74c0564a9..a80ca86c1 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -1,5 +1,6 @@ using Ryujinx.Audio; using Ryujinx.Graphics.Gal; +using Ryujinx.HLE.Font; using Ryujinx.HLE.Gpu; using Ryujinx.HLE.Input; using Ryujinx.HLE.Logging; @@ -27,6 +28,8 @@ namespace Ryujinx.HLE public Hid Hid { get; private set; } + public SharedFontManager Font { get; private set; } + public event EventHandler Finish; public Switch(IGalRenderer Renderer, IAalOutput AudioOut) @@ -57,8 +60,13 @@ namespace Ryujinx.HLE Hid = new Hid(Log); - Os.HidSharedMem.MemoryMapped += Hid.ShMemMap; - Os.HidSharedMem.MemoryUnmapped += Hid.ShMemUnmap; + Font = new SharedFontManager(Log, VFs.GetSystemPath()); + + Os.HidSharedMem.MemoryMapped += Hid.ShMemMap; + Os.HidSharedMem.MemoryUnmapped += Hid.ShMemUnmap; + + Os.FontSharedMem.MemoryMapped += Font.ShMemMap; + Os.FontSharedMem.MemoryUnmapped += Font.ShMemUnmap; } public void LoadCart(string ExeFsDir, string RomFsFile = null) diff --git a/Ryujinx.HLE/VirtualFileSystem.cs b/Ryujinx.HLE/VirtualFileSystem.cs index df1fc9db1..31b8e184c 100644 --- a/Ryujinx.HLE/VirtualFileSystem.cs +++ b/Ryujinx.HLE/VirtualFileSystem.cs @@ -8,6 +8,7 @@ namespace Ryujinx.HLE private const string BasePath = "RyuFs"; private const string NandPath = "nand"; private const string SdCardPath = "sdmc"; + private const string SystemPath = "system"; public Stream RomFs { get; private set; } @@ -45,6 +46,8 @@ namespace Ryujinx.HLE public string GetGameSavesPath() => MakeDirAndGetFullPath(NandPath); + public string GetSystemPath() => MakeDirAndGetFullPath(SystemPath); + public string SwitchPathToSystemPath(string SwitchPath) { string[] Parts = SwitchPath.Split(":");