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(":");