Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
625f5fb88a | ||
|
2382717600 | ||
|
30ee70a9bc | ||
|
232b1012b0 | ||
|
e747f5cd83 |
12
Ryujinx.Common/Memory/Box.cs
Normal file
12
Ryujinx.Common/Memory/Box.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Ryujinx.Common.Memory
|
||||||
|
{
|
||||||
|
public class Box<T> where T : unmanaged
|
||||||
|
{
|
||||||
|
public T Data;
|
||||||
|
|
||||||
|
public Box()
|
||||||
|
{
|
||||||
|
Data = new T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -101,6 +101,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <returns>The GPU resource with the given ID</returns>
|
/// <returns>The GPU resource with the given ID</returns>
|
||||||
public abstract T1 Get(int id);
|
public abstract T1 Get(int id);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a given ID is valid and inside the range of the pool.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">ID of the descriptor. This is effectively a zero-based index</param>
|
||||||
|
/// <returns>True if the specified ID is valid, false otherwise</returns>
|
||||||
|
public bool IsValidId(int id)
|
||||||
|
{
|
||||||
|
return (uint)id <= MaximumId;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Synchronizes host memory with guest memory.
|
/// Synchronizes host memory with guest memory.
|
||||||
/// This causes invalidation of pool entries,
|
/// This causes invalidation of pool entries,
|
||||||
|
@@ -32,6 +32,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
private readonly GpuChannel _channel;
|
private readonly GpuChannel _channel;
|
||||||
private readonly TexturePoolCache _texturePoolCache;
|
private readonly TexturePoolCache _texturePoolCache;
|
||||||
|
|
||||||
|
private TexturePool _cachedTexturePool;
|
||||||
|
private SamplerPool _cachedSamplerPool;
|
||||||
|
|
||||||
private readonly TextureBindingInfo[][] _textureBindings;
|
private readonly TextureBindingInfo[][] _textureBindings;
|
||||||
private readonly TextureBindingInfo[][] _imageBindings;
|
private readonly TextureBindingInfo[][] _imageBindings;
|
||||||
|
|
||||||
@@ -343,9 +346,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId)
|
? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
SamplerPool samplerPool = _samplerPool;
|
||||||
|
|
||||||
// Check if the texture pool has been modified since bindings were last committed.
|
// Check if the texture pool has been modified since bindings were last committed.
|
||||||
// If it wasn't, then it's possible to avoid looking up textures again when the handle remains the same.
|
// If it wasn't, then it's possible to avoid looking up textures again when the handle remains the same.
|
||||||
bool poolModified = false;
|
bool poolModified = _cachedTexturePool != texturePool || _cachedSamplerPool != samplerPool;
|
||||||
|
|
||||||
|
_cachedTexturePool = texturePool;
|
||||||
|
_cachedSamplerPool = samplerPool;
|
||||||
|
|
||||||
if (texturePool != null)
|
if (texturePool != null)
|
||||||
{
|
{
|
||||||
@@ -358,9 +366,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_samplerPool != null)
|
if (samplerPool != null)
|
||||||
{
|
{
|
||||||
int samplerPoolSequence = _samplerPool.CheckModified();
|
int samplerPoolSequence = samplerPool.CheckModified();
|
||||||
|
|
||||||
if (_samplerPoolSequence != samplerPoolSequence)
|
if (_samplerPoolSequence != samplerPoolSequence)
|
||||||
{
|
{
|
||||||
@@ -738,7 +746,22 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId);
|
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId);
|
||||||
|
|
||||||
return texturePool.GetDescriptor(textureId);
|
TextureDescriptor descriptor;
|
||||||
|
|
||||||
|
if (texturePool.IsValidId(textureId))
|
||||||
|
{
|
||||||
|
descriptor = texturePool.GetDescriptor(textureId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the ID is not valid, we just return a default descriptor with the most common state.
|
||||||
|
// Since this is used for shader specialization, doing so might avoid the need for recompilations.
|
||||||
|
descriptor = new TextureDescriptor();
|
||||||
|
descriptor.Word4 |= (uint)TextureTarget.Texture2D << 23;
|
||||||
|
descriptor.Word5 |= 1u << 31; // Coords normalized.
|
||||||
|
}
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -241,25 +241,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return (TextureMsaaMode)((Word7 >> 8) & 0xf);
|
return (TextureMsaaMode)((Word7 >> 8) & 0xf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create the equivalent of this TextureDescriptor for the shader cache.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The equivalent of this TextureDescriptor for the shader cache.</returns>
|
|
||||||
public GuestTextureDescriptor ToCache()
|
|
||||||
{
|
|
||||||
GuestTextureDescriptor result = new GuestTextureDescriptor
|
|
||||||
{
|
|
||||||
Handle = uint.MaxValue,
|
|
||||||
Format = UnpackFormat(),
|
|
||||||
Target = UnpackTextureTarget(),
|
|
||||||
IsSrgb = UnpackSrgb(),
|
|
||||||
IsTextureCoordNormalized = UnpackTextureCoordNormalized(),
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if two descriptors are equal.
|
/// Check if two descriptors are equal.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -579,14 +579,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
textureKey.StageIndex);
|
textureKey.StageIndex);
|
||||||
|
|
||||||
int packedId = TextureHandle.ReadPackedId(textureKey.Handle, cachedTextureBuffer, cachedSamplerBuffer);
|
int packedId = TextureHandle.ReadPackedId(textureKey.Handle, cachedTextureBuffer, cachedSamplerBuffer);
|
||||||
|
|
||||||
int textureId = TextureHandle.UnpackTextureId(packedId);
|
int textureId = TextureHandle.UnpackTextureId(packedId);
|
||||||
|
|
||||||
ref readonly Image.TextureDescriptor descriptor = ref pool.GetDescriptorRef(textureId);
|
if (pool.IsValidId(textureId))
|
||||||
|
|
||||||
if (!MatchesTexture(kv.Value, descriptor))
|
|
||||||
{
|
{
|
||||||
return false;
|
ref readonly Image.TextureDescriptor descriptor = ref pool.GetDescriptorRef(textureId);
|
||||||
|
|
||||||
|
if (!MatchesTexture(kv.Value, descriptor))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -966,6 +966,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
SignalExitToDebugExited();
|
SignalExitToDebugExited();
|
||||||
SignalExit();
|
SignalExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KernelStatic.GetCurrentThread().Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UnpauseAndTerminateAllThreadsExcept(KThread currentThread)
|
private void UnpauseAndTerminateAllThreadsExcept(KThread currentThread)
|
||||||
@@ -981,7 +983,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
foreach (KThread thread in _threads)
|
foreach (KThread thread in _threads)
|
||||||
{
|
{
|
||||||
if ((thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending)
|
if (thread != currentThread && (thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending)
|
||||||
{
|
{
|
||||||
thread.PrepareForTermination();
|
thread.PrepareForTermination();
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,10 @@ using Ryujinx.Common.Logging;
|
|||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time.StaticService
|
namespace Ryujinx.HLE.HOS.Services.Time.StaticService
|
||||||
@@ -100,15 +103,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
|
|||||||
|
|
||||||
string locationName = StringUtils.ReadInlinedAsciiString(context.RequestData, 0x24);
|
string locationName = StringUtils.ReadInlinedAsciiString(context.RequestData, 0x24);
|
||||||
|
|
||||||
ResultCode resultCode = _timeZoneContentManager.LoadTimeZoneRule(out TimeZoneRule rules, locationName);
|
using (WritableRegion region = context.Memory.GetWritableRegion(bufferPosition, Unsafe.SizeOf<TimeZoneRule>()))
|
||||||
|
|
||||||
// Write TimeZoneRule if success
|
|
||||||
if (resultCode == ResultCode.Success)
|
|
||||||
{
|
{
|
||||||
MemoryHelper.Write(context.Memory, bufferPosition, rules);
|
ref TimeZoneRule rules = ref MemoryMarshal.Cast<byte, TimeZoneRule>(region.Memory.Span)[0];
|
||||||
}
|
|
||||||
|
|
||||||
return resultCode;
|
return _timeZoneContentManager.LoadTimeZoneRule(ref rules, locationName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandHipc(100)]
|
[CommandHipc(100)]
|
||||||
|
@@ -4,9 +4,12 @@ using Ryujinx.Cpu;
|
|||||||
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||||
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time.StaticService
|
namespace Ryujinx.HLE.HOS.Services.Time.StaticService
|
||||||
{
|
{
|
||||||
@@ -165,11 +168,11 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
|
|||||||
|
|
||||||
using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp))
|
using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp))
|
||||||
{
|
{
|
||||||
result = _timeZoneManager.ParseTimeZoneRuleBinary(out TimeZoneRule timeZoneRule, timeZoneBinaryStream);
|
using (WritableRegion region = context.Memory.GetWritableRegion(timeZoneRuleBufferPosition, Unsafe.SizeOf<TimeZoneRule>()))
|
||||||
|
|
||||||
if (result == ResultCode.Success)
|
|
||||||
{
|
{
|
||||||
MemoryHelper.Write(context.Memory, timeZoneRuleBufferPosition, timeZoneRule);
|
ref TimeZoneRule rule = ref MemoryMarshal.Cast<byte, TimeZoneRule>(region.Memory.Span)[0];
|
||||||
|
|
||||||
|
result = _timeZoneManager.ParseTimeZoneRuleBinary(ref rule, timeZoneBinaryStream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,9 +202,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
|
|||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, bufferPosition);
|
ReadOnlySpan<TimeZoneRule> rules = MemoryMarshal.Cast<byte, TimeZoneRule>(context.Memory.GetSpan(bufferPosition, (int)bufferSize));
|
||||||
|
|
||||||
ResultCode resultCode = _timeZoneManager.ToCalendarTime(rules, posixTime, out CalendarInfo calendar);
|
ResultCode resultCode = _timeZoneManager.ToCalendarTime(in rules[0], posixTime, out CalendarInfo calendar);
|
||||||
|
|
||||||
if (resultCode == 0)
|
if (resultCode == 0)
|
||||||
{
|
{
|
||||||
@@ -244,9 +247,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
|
|||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, inBufferPosition);
|
ReadOnlySpan<TimeZoneRule> rules = MemoryMarshal.Cast<byte, TimeZoneRule>(context.Memory.GetSpan(inBufferPosition, (int)inBufferSize));
|
||||||
|
|
||||||
ResultCode resultCode = _timeZoneManager.ToPosixTime(rules, calendarTime, out long posixTime);
|
ResultCode resultCode = _timeZoneManager.ToPosixTime(in rules[0], calendarTime, out long posixTime);
|
||||||
|
|
||||||
if (resultCode == ResultCode.Success)
|
if (resultCode == ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using System;
|
using System;
|
||||||
@@ -38,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
new int[] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
|
new int[] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
|
||||||
};
|
};
|
||||||
|
|
||||||
private const string TimeZoneDefaultRule = ",M4.1.0,M10.5.0";
|
private static readonly byte[] TimeZoneDefaultRule = Encoding.ASCII.GetBytes(",M4.1.0,M10.5.0");
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 0x4, Size = 0x10)]
|
[StructLayout(LayoutKind.Sequential, Pack = 0x4, Size = 0x10)]
|
||||||
private struct CalendarTimeInternal
|
private struct CalendarTimeInternal
|
||||||
@@ -133,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return (t1 - t0) == SecondsPerRepeat;
|
return (t1 - t0) == SecondsPerRepeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TimeTypeEquals(TimeZoneRule outRules, byte aIndex, byte bIndex)
|
private static bool TimeTypeEquals(in TimeZoneRule outRules, byte aIndex, byte bIndex)
|
||||||
{
|
{
|
||||||
if (aIndex < 0 || aIndex >= outRules.TypeCount || bIndex < 0 || bIndex >= outRules.TypeCount)
|
if (aIndex < 0 || aIndex >= outRules.TypeCount || bIndex < 0 || bIndex >= outRules.TypeCount)
|
||||||
{
|
{
|
||||||
@@ -150,7 +151,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
StringUtils.CompareCStr(outRules.Chars[a.AbbreviationListIndex..], outRules.Chars[b.AbbreviationListIndex..]) == 0;
|
StringUtils.CompareCStr(outRules.Chars[a.AbbreviationListIndex..], outRules.Chars[b.AbbreviationListIndex..]) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetQZName(ReadOnlySpan<char> name, int namePosition, char delimiter)
|
private static int GetQZName(ReadOnlySpan<byte> name, int namePosition, char delimiter)
|
||||||
{
|
{
|
||||||
int i = namePosition;
|
int i = namePosition;
|
||||||
|
|
||||||
@@ -162,13 +163,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetTZName(char[] name, int namePosition)
|
private static int GetTZName(ReadOnlySpan<byte> name, int namePosition)
|
||||||
{
|
{
|
||||||
int i = namePosition;
|
int i = namePosition;
|
||||||
|
|
||||||
char c;
|
char c;
|
||||||
|
|
||||||
while ((c = name[i]) != '\0' && !char.IsDigit(c) && c != ',' && c != '-' && c != '+')
|
while ((c = (char)name[i]) != '\0' && !char.IsDigit(c) && c != ',' && c != '-' && c != '+')
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -176,7 +177,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetNum(char[] name, ref int namePosition, out int num, int min, int max)
|
private static bool GetNum(ReadOnlySpan<byte> name, ref int namePosition, out int num, int min, int max)
|
||||||
{
|
{
|
||||||
num = 0;
|
num = 0;
|
||||||
|
|
||||||
@@ -185,7 +186,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char c = name[namePosition];
|
char c = (char)name[namePosition];
|
||||||
|
|
||||||
if (!char.IsDigit(c))
|
if (!char.IsDigit(c))
|
||||||
{
|
{
|
||||||
@@ -205,7 +206,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
c = name[namePosition];
|
c = (char)name[namePosition];
|
||||||
}
|
}
|
||||||
while (char.IsDigit(c));
|
while (char.IsDigit(c));
|
||||||
|
|
||||||
@@ -217,7 +218,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetSeconds(char[] name, ref int namePosition, out int seconds)
|
private static bool GetSeconds(ReadOnlySpan<byte> name, ref int namePosition, out int seconds)
|
||||||
{
|
{
|
||||||
seconds = 0;
|
seconds = 0;
|
||||||
|
|
||||||
@@ -266,7 +267,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetOffset(char[] name, ref int namePosition, ref int offset)
|
private static bool GetOffset(ReadOnlySpan<byte> name, ref int namePosition, ref int offset)
|
||||||
{
|
{
|
||||||
bool isNegative = false;
|
bool isNegative = false;
|
||||||
|
|
||||||
@@ -304,7 +305,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetRule(char[] name, ref int namePosition, out Rule rule)
|
private static bool GetRule(ReadOnlySpan<byte> name, ref int namePosition, out Rule rule)
|
||||||
{
|
{
|
||||||
rule = new Rule();
|
rule = new Rule();
|
||||||
|
|
||||||
@@ -347,7 +348,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
|
|
||||||
isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerWekk - 1);
|
isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerWekk - 1);
|
||||||
}
|
}
|
||||||
else if (char.IsDigit(name[namePosition]))
|
else if (char.IsDigit((char)name[namePosition]))
|
||||||
{
|
{
|
||||||
rule.Type = RuleType.DayOfYear;
|
rule.Type = RuleType.DayOfYear;
|
||||||
isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerLYear - 1);
|
isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerLYear - 1);
|
||||||
@@ -385,19 +386,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool ParsePosixName(ReadOnlySpan<char> name, out TimeZoneRule outRules, bool lastDitch)
|
private static bool ParsePosixName(ReadOnlySpan<byte> name, ref TimeZoneRule outRules, bool lastDitch)
|
||||||
{
|
{
|
||||||
outRules = new TimeZoneRule
|
outRules = new TimeZoneRule();
|
||||||
{
|
|
||||||
Ats = new long[TzMaxTimes],
|
|
||||||
Types = new byte[TzMaxTimes],
|
|
||||||
Ttis = new TimeTypeInfo[TzMaxTypes],
|
|
||||||
Chars = new char[TzCharsArraySize]
|
|
||||||
};
|
|
||||||
|
|
||||||
int stdLen;
|
int stdLen;
|
||||||
|
|
||||||
ReadOnlySpan<char> stdName = name;
|
ReadOnlySpan<byte> stdName = name;
|
||||||
int namePosition = 0;
|
int namePosition = 0;
|
||||||
int stdOffset = 0;
|
int stdOffset = 0;
|
||||||
|
|
||||||
@@ -428,7 +423,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
namePosition = GetTZName(name.ToArray(), namePosition);
|
namePosition = GetTZName(name, namePosition);
|
||||||
stdLen = namePosition;
|
stdLen = namePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,7 +444,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
int destLen = 0;
|
int destLen = 0;
|
||||||
int dstOffset = 0;
|
int dstOffset = 0;
|
||||||
|
|
||||||
ReadOnlySpan<char> destName = name.Slice(namePosition);
|
ReadOnlySpan<byte> destName = name.Slice(namePosition);
|
||||||
|
|
||||||
if (TzCharsArraySize < charCount)
|
if (TzCharsArraySize < charCount)
|
||||||
{
|
{
|
||||||
@@ -476,7 +471,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
destName = name.Slice(namePosition);
|
destName = name.Slice(namePosition);
|
||||||
namePosition = GetTZName(name.ToArray(), namePosition);
|
namePosition = GetTZName(name, namePosition);
|
||||||
destLen = namePosition;
|
destLen = namePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,7 +502,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
|
|
||||||
if (name[namePosition] == '\0')
|
if (name[namePosition] == '\0')
|
||||||
{
|
{
|
||||||
name = TimeZoneDefaultRule.ToCharArray();
|
name = TimeZoneDefaultRule;
|
||||||
namePosition = 0;
|
namePosition = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,7 +510,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
{
|
{
|
||||||
namePosition++;
|
namePosition++;
|
||||||
|
|
||||||
bool IsRuleValid = GetRule(name.ToArray(), ref namePosition, out Rule start);
|
bool IsRuleValid = GetRule(name, ref namePosition, out Rule start);
|
||||||
if (!IsRuleValid)
|
if (!IsRuleValid)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -526,7 +521,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
IsRuleValid = GetRule(name.ToArray(), ref namePosition, out Rule end);
|
IsRuleValid = GetRule(name, ref namePosition, out Rule end);
|
||||||
if (!IsRuleValid)
|
if (!IsRuleValid)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -738,7 +733,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
}
|
}
|
||||||
|
|
||||||
charsPosition += stdLen;
|
charsPosition += stdLen;
|
||||||
outRules.Chars[charsPosition++] = '\0';
|
outRules.Chars[charsPosition++] = 0;
|
||||||
|
|
||||||
if (destLen != 0)
|
if (destLen != 0)
|
||||||
{
|
{
|
||||||
@@ -746,7 +741,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
{
|
{
|
||||||
outRules.Chars[charsPosition + i] = destName[i];
|
outRules.Chars[charsPosition + i] = destName[i];
|
||||||
}
|
}
|
||||||
outRules.Chars[charsPosition + destLen] = '\0';
|
outRules.Chars[charsPosition + destLen] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -882,20 +877,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool ParsePosixName(string name, out TimeZoneRule outRules)
|
internal static bool ParsePosixName(string name, ref TimeZoneRule outRules)
|
||||||
{
|
{
|
||||||
return ParsePosixName(name.ToCharArray(), out outRules, false);
|
return ParsePosixName(Encoding.ASCII.GetBytes(name), ref outRules, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool ParseTimeZoneBinary(out TimeZoneRule outRules, Stream inputData)
|
internal static bool ParseTimeZoneBinary(ref TimeZoneRule outRules, Stream inputData)
|
||||||
{
|
{
|
||||||
outRules = new TimeZoneRule
|
outRules = new TimeZoneRule();
|
||||||
{
|
|
||||||
Ats = new long[TzMaxTimes],
|
|
||||||
Types = new byte[TzMaxTimes],
|
|
||||||
Ttis = new TimeTypeInfo[TzMaxTypes],
|
|
||||||
Chars = new char[TzCharsArraySize]
|
|
||||||
};
|
|
||||||
|
|
||||||
BinaryReader reader = new BinaryReader(inputData);
|
BinaryReader reader = new BinaryReader(inputData);
|
||||||
|
|
||||||
@@ -1020,10 +1009,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
outRules.Ttis[i] = ttis;
|
outRules.Ttis[i] = ttis;
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoding.ASCII.GetChars(p[..outRules.CharCount].ToArray()).CopyTo(outRules.Chars.AsSpan());
|
p[..outRules.CharCount].CopyTo(outRules.Chars);
|
||||||
|
|
||||||
p = p[outRules.CharCount..];
|
p = p[outRules.CharCount..];
|
||||||
outRules.Chars[outRules.CharCount] = '\0';
|
outRules.Chars[outRules.CharCount] = 0;
|
||||||
|
|
||||||
for (int i = 0; i < outRules.TypeCount; i++)
|
for (int i = 0; i < outRules.TypeCount; i++)
|
||||||
{
|
{
|
||||||
@@ -1077,27 +1066,30 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
char[] tempName = new char[TzNameMax + 1];
|
byte[] tempName = new byte[TzNameMax + 1];
|
||||||
Array.Copy(workBuffer, position, tempName, 0, nRead);
|
Array.Copy(workBuffer, position, tempName, 0, nRead);
|
||||||
|
|
||||||
if (nRead > 2 && tempName[0] == '\n' && tempName[nRead - 1] == '\n' && outRules.TypeCount + 2 <= TzMaxTypes)
|
if (nRead > 2 && tempName[0] == '\n' && tempName[nRead - 1] == '\n' && outRules.TypeCount + 2 <= TzMaxTypes)
|
||||||
{
|
{
|
||||||
tempName[nRead - 1] = '\0';
|
tempName[nRead - 1] = 0;
|
||||||
|
|
||||||
char[] name = new char[TzNameMax];
|
byte[] name = new byte[TzNameMax];
|
||||||
Array.Copy(tempName, 1, name, 0, nRead - 1);
|
Array.Copy(tempName, 1, name, 0, nRead - 1);
|
||||||
|
|
||||||
if (ParsePosixName(name, out TimeZoneRule tempRules, false))
|
Box<TimeZoneRule> tempRulesBox = new Box<TimeZoneRule>();
|
||||||
|
ref TimeZoneRule tempRules = ref tempRulesBox.Data;
|
||||||
|
|
||||||
|
if (ParsePosixName(name, ref tempRulesBox.Data, false))
|
||||||
{
|
{
|
||||||
int abbreviationCount = 0;
|
int abbreviationCount = 0;
|
||||||
charCount = outRules.CharCount;
|
charCount = outRules.CharCount;
|
||||||
|
|
||||||
Span<char> chars = outRules.Chars;
|
Span<byte> chars = outRules.Chars;
|
||||||
|
|
||||||
for (int i = 0; i < tempRules.TypeCount; i++)
|
for (int i = 0; i < tempRules.TypeCount; i++)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<char> tempChars = tempRules.Chars;
|
ReadOnlySpan<byte> tempChars = tempRules.Chars;
|
||||||
ReadOnlySpan<char> tempAbbreviation = tempChars[tempRules.Ttis[i].AbbreviationListIndex..];
|
ReadOnlySpan<byte> tempAbbreviation = tempChars[tempRules.Ttis[i].AbbreviationListIndex..];
|
||||||
|
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
@@ -1175,7 +1167,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
{
|
{
|
||||||
for (int i = 1; i < outRules.TimeCount; i++)
|
for (int i = 1; i < outRules.TimeCount; i++)
|
||||||
{
|
{
|
||||||
if (TimeTypeEquals(outRules, outRules.Types[i], outRules.Types[0]) && DifferByRepeat(outRules.Ats[i], outRules.Ats[0]))
|
if (TimeTypeEquals(in outRules, outRules.Types[i], outRules.Types[0]) && DifferByRepeat(outRules.Ats[i], outRules.Ats[0]))
|
||||||
{
|
{
|
||||||
outRules.GoBack = true;
|
outRules.GoBack = true;
|
||||||
break;
|
break;
|
||||||
@@ -1184,7 +1176,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
|
|
||||||
for (int i = outRules.TimeCount - 2; i >= 0; i--)
|
for (int i = outRules.TimeCount - 2; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if (TimeTypeEquals(outRules, outRules.Types[outRules.TimeCount - 1], outRules.Types[i]) && DifferByRepeat(outRules.Ats[outRules.TimeCount - 1], outRules.Ats[i]))
|
if (TimeTypeEquals(in outRules, outRules.Types[outRules.TimeCount - 1], outRules.Types[i]) && DifferByRepeat(outRules.Ats[outRules.TimeCount - 1], outRules.Ats[i]))
|
||||||
{
|
{
|
||||||
outRules.GoAhead = true;
|
outRules.GoAhead = true;
|
||||||
break;
|
break;
|
||||||
@@ -1259,10 +1251,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
long remainingSeconds = time % SecondsPerDay;
|
long remainingSeconds = time % SecondsPerDay;
|
||||||
|
|
||||||
calendarTime = new CalendarTimeInternal();
|
calendarTime = new CalendarTimeInternal();
|
||||||
calendarAdditionalInfo = new CalendarAdditionalInfo()
|
calendarAdditionalInfo = new CalendarAdditionalInfo();
|
||||||
{
|
|
||||||
TimezoneName = new char[8]
|
|
||||||
};
|
|
||||||
|
|
||||||
while (timeDays < 0 || timeDays >= YearLengths[IsLeap((int)year)])
|
while (timeDays < 0 || timeDays >= YearLengths[IsLeap((int)year)])
|
||||||
{
|
{
|
||||||
@@ -1353,13 +1342,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResultCode ToCalendarTimeInternal(TimeZoneRule rules, long time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo)
|
private static ResultCode ToCalendarTimeInternal(in TimeZoneRule rules, long time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo)
|
||||||
{
|
{
|
||||||
calendarTime = new CalendarTimeInternal();
|
calendarTime = new CalendarTimeInternal();
|
||||||
calendarAdditionalInfo = new CalendarAdditionalInfo()
|
calendarAdditionalInfo = new CalendarAdditionalInfo();
|
||||||
{
|
|
||||||
TimezoneName = new char[8]
|
|
||||||
};
|
|
||||||
|
|
||||||
ResultCode result;
|
ResultCode result;
|
||||||
|
|
||||||
@@ -1398,7 +1384,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return ResultCode.TimeNotFound;
|
return ResultCode.TimeNotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = ToCalendarTimeInternal(rules, newTime, out calendarTime, out calendarAdditionalInfo);
|
result = ToCalendarTimeInternal(in rules, newTime, out calendarTime, out calendarAdditionalInfo);
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
@@ -1450,17 +1436,17 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
{
|
{
|
||||||
calendarAdditionalInfo.IsDaySavingTime = rules.Ttis[ttiIndex].IsDaySavingTime;
|
calendarAdditionalInfo.IsDaySavingTime = rules.Ttis[ttiIndex].IsDaySavingTime;
|
||||||
|
|
||||||
ReadOnlySpan<char> timeZoneAbbreviation = rules.Chars.AsSpan()[rules.Ttis[ttiIndex].AbbreviationListIndex..];
|
ReadOnlySpan<byte> timeZoneAbbreviation = rules.Chars[rules.Ttis[ttiIndex].AbbreviationListIndex..];
|
||||||
|
|
||||||
int timeZoneSize = Math.Min(StringUtils.LengthCstr(timeZoneAbbreviation), 8);
|
int timeZoneSize = Math.Min(StringUtils.LengthCstr(timeZoneAbbreviation), 8);
|
||||||
|
|
||||||
timeZoneAbbreviation[..timeZoneSize].CopyTo(calendarAdditionalInfo.TimezoneName.AsSpan());
|
timeZoneAbbreviation[..timeZoneSize].CopyTo(calendarAdditionalInfo.TimezoneName.ToSpan());
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResultCode ToPosixTimeInternal(TimeZoneRule rules, CalendarTimeInternal calendarTime, out long posixTime)
|
private static ResultCode ToPosixTimeInternal(in TimeZoneRule rules, CalendarTimeInternal calendarTime, out long posixTime)
|
||||||
{
|
{
|
||||||
posixTime = 0;
|
posixTime = 0;
|
||||||
|
|
||||||
@@ -1604,7 +1590,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
|
|
||||||
int direction;
|
int direction;
|
||||||
|
|
||||||
ResultCode result = ToCalendarTimeInternal(rules, pivot, out CalendarTimeInternal candidateCalendarTime, out _);
|
ResultCode result = ToCalendarTimeInternal(in rules, pivot, out CalendarTimeInternal candidateCalendarTime, out _);
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
{
|
{
|
||||||
if (pivot > 0)
|
if (pivot > 0)
|
||||||
@@ -1675,9 +1661,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static ResultCode ToCalendarTime(TimeZoneRule rules, long time, out CalendarInfo calendar)
|
internal static ResultCode ToCalendarTime(in TimeZoneRule rules, long time, out CalendarInfo calendar)
|
||||||
{
|
{
|
||||||
ResultCode result = ToCalendarTimeInternal(rules, time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo);
|
ResultCode result = ToCalendarTimeInternal(in rules, time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo);
|
||||||
|
|
||||||
calendar = new CalendarInfo()
|
calendar = new CalendarInfo()
|
||||||
{
|
{
|
||||||
@@ -1697,7 +1683,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static ResultCode ToPosixTime(TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
|
internal static ResultCode ToPosixTime(in TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
|
||||||
{
|
{
|
||||||
CalendarTimeInternal calendarTimeInternal = new CalendarTimeInternal()
|
CalendarTimeInternal calendarTimeInternal = new CalendarTimeInternal()
|
||||||
{
|
{
|
||||||
@@ -1710,7 +1696,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
Second = calendarTime.Second
|
Second = calendarTime.Second
|
||||||
};
|
};
|
||||||
|
|
||||||
return ToPosixTimeInternal(rules, calendarTimeInternal, out posixTime);
|
return ToPosixTimeInternal(in rules, calendarTimeInternal, out posixTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -16,7 +16,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
using static Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule;
|
using TimeZoneRuleBox = Ryujinx.Common.Memory.Box<Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule>;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
||||||
{
|
{
|
||||||
@@ -149,7 +149,11 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeZone.ParseTimeZoneBinary(out TimeZoneRule tzRule, tzif.Get.AsStream());
|
TimeZoneRuleBox tzRuleBox = new TimeZoneRuleBox();
|
||||||
|
ref TimeZoneRule tzRule = ref tzRuleBox.Data;
|
||||||
|
|
||||||
|
TimeZone.ParseTimeZoneBinary(ref tzRule, tzif.Get.AsStream());
|
||||||
|
|
||||||
|
|
||||||
TimeTypeInfo ttInfo;
|
TimeTypeInfo ttInfo;
|
||||||
if (tzRule.TimeCount > 0) // Find the current transition period
|
if (tzRule.TimeCount > 0) // Find the current transition period
|
||||||
@@ -174,10 +178,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var abbrStart = tzRule.Chars.AsSpan(ttInfo.AbbreviationListIndex);
|
var abbrStart = tzRule.Chars[ttInfo.AbbreviationListIndex..];
|
||||||
int abbrEnd = abbrStart.IndexOf('\0');
|
int abbrEnd = abbrStart.IndexOf((byte)0);
|
||||||
|
|
||||||
outList.Add((ttInfo.GmtOffset, locName, abbrStart.Slice(0, abbrEnd).ToString()));
|
outList.Add((ttInfo.GmtOffset, locName, abbrStart[..abbrEnd].ToString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,15 +280,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return (ResultCode)result.Value;
|
return (ResultCode)result.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ResultCode LoadTimeZoneRule(out TimeZoneRule outRules, string locationName)
|
internal ResultCode LoadTimeZoneRule(ref TimeZoneRule rules, string locationName)
|
||||||
{
|
{
|
||||||
outRules = new TimeZoneRule
|
rules = default;
|
||||||
{
|
|
||||||
Ats = new long[TzMaxTimes],
|
|
||||||
Types = new byte[TzMaxTimes],
|
|
||||||
Ttis = new TimeTypeInfo[TzMaxTypes],
|
|
||||||
Chars = new char[TzCharsArraySize]
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!HasTimeZoneBinaryTitle())
|
if (!HasTimeZoneBinaryTitle())
|
||||||
{
|
{
|
||||||
@@ -295,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
|
|
||||||
if (result == ResultCode.Success)
|
if (result == ResultCode.Success)
|
||||||
{
|
{
|
||||||
result = Manager.ParseTimeZoneRuleBinary(out outRules, timeZoneBinaryStream);
|
result = Manager.ParseTimeZoneRuleBinary(ref rules, timeZoneBinaryStream);
|
||||||
|
|
||||||
ncaFile.Dispose();
|
ncaFile.Dispose();
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,14 @@
|
|||||||
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using static Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
||||||
{
|
{
|
||||||
class TimeZoneManager
|
class TimeZoneManager
|
||||||
{
|
{
|
||||||
private bool _isInitialized;
|
private bool _isInitialized;
|
||||||
private TimeZoneRule _myRules;
|
private Box<TimeZoneRule> _myRules;
|
||||||
private string _deviceLocationName;
|
private string _deviceLocationName;
|
||||||
private UInt128 _timeZoneRuleVersion;
|
private UInt128 _timeZoneRuleVersion;
|
||||||
private uint _totalLocationNameCount;
|
private uint _totalLocationNameCount;
|
||||||
@@ -21,15 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
_deviceLocationName = "UTC";
|
_deviceLocationName = "UTC";
|
||||||
_timeZoneRuleVersion = new UInt128();
|
_timeZoneRuleVersion = new UInt128();
|
||||||
_lock = new object();
|
_lock = new object();
|
||||||
|
_myRules = new Box<TimeZoneRule>();
|
||||||
// Empty rules
|
|
||||||
_myRules = new TimeZoneRule
|
|
||||||
{
|
|
||||||
Ats = new long[TzMaxTimes],
|
|
||||||
Types = new byte[TzMaxTimes],
|
|
||||||
Ttis = new TimeTypeInfo[TzMaxTypes],
|
|
||||||
Chars = new char[TzCharsArraySize]
|
|
||||||
};
|
|
||||||
|
|
||||||
_timeZoneUpdateTimePoint = SteadyClockTimePoint.GetRandom();
|
_timeZoneUpdateTimePoint = SteadyClockTimePoint.GetRandom();
|
||||||
}
|
}
|
||||||
@@ -78,7 +70,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(out TimeZoneRule rules, timeZoneBinaryStream);
|
Box<TimeZoneRule> rules = new Box<TimeZoneRule>();
|
||||||
|
|
||||||
|
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(ref rules.Data, timeZoneBinaryStream);
|
||||||
|
|
||||||
if (timeZoneConversionSuccess)
|
if (timeZoneConversionSuccess)
|
||||||
{
|
{
|
||||||
@@ -154,13 +148,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResultCode ParseTimeZoneRuleBinary(out TimeZoneRule outRules, Stream timeZoneBinaryStream)
|
public ResultCode ParseTimeZoneRuleBinary(ref TimeZoneRule outRules, Stream timeZoneBinaryStream)
|
||||||
{
|
{
|
||||||
ResultCode result = ResultCode.Success;
|
ResultCode result = ResultCode.Success;
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(out outRules, timeZoneBinaryStream);
|
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(ref outRules, timeZoneBinaryStream);
|
||||||
|
|
||||||
if (!timeZoneConversionSuccess)
|
if (!timeZoneConversionSuccess)
|
||||||
{
|
{
|
||||||
@@ -208,7 +202,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
{
|
{
|
||||||
if (_isInitialized)
|
if (_isInitialized)
|
||||||
{
|
{
|
||||||
result = ToCalendarTime(_myRules, time, out calendar);
|
result = ToCalendarTime(in _myRules.Data, time, out calendar);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -220,13 +214,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResultCode ToCalendarTime(TimeZoneRule rules, long time, out CalendarInfo calendar)
|
public ResultCode ToCalendarTime(in TimeZoneRule rules, long time, out CalendarInfo calendar)
|
||||||
{
|
{
|
||||||
ResultCode result;
|
ResultCode result;
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
result = TimeZone.ToCalendarTime(rules, time, out calendar);
|
result = TimeZone.ToCalendarTime(in rules, time, out calendar);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -240,7 +234,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
{
|
{
|
||||||
if (_isInitialized)
|
if (_isInitialized)
|
||||||
{
|
{
|
||||||
result = ToPosixTime(_myRules, calendarTime, out posixTime);
|
result = ToPosixTime(in _myRules.Data, calendarTime, out posixTime);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -252,13 +246,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResultCode ToPosixTime(TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
|
public ResultCode ToPosixTime(in TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
|
||||||
{
|
{
|
||||||
ResultCode result;
|
ResultCode result;
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
result = TimeZone.ToPosixTime(rules, calendarTime, out posixTime);
|
result = TimeZone.ToPosixTime(in rules, calendarTime, out posixTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System.Runtime.InteropServices;
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
||||||
{
|
{
|
||||||
@@ -8,14 +9,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
public uint DayOfWeek;
|
public uint DayOfWeek;
|
||||||
public uint DayOfYear;
|
public uint DayOfYear;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
public Array8<byte> TimezoneName;
|
||||||
public char[] TimezoneName;
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
[MarshalAs(UnmanagedType.I1)]
|
||||||
public bool IsDaySavingTime;
|
public bool IsDaySavingTime;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
public Array3<byte> Padding;
|
||||||
public char[] Padding;
|
|
||||||
|
|
||||||
public int GmtOffset;
|
public int GmtOffset;
|
||||||
}
|
}
|
||||||
|
@@ -1,17 +1,19 @@
|
|||||||
using System.Runtime.InteropServices;
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)]
|
[StructLayout(LayoutKind.Sequential, Size = Size, Pack = 4)]
|
||||||
struct TimeTypeInfo
|
public struct TimeTypeInfo
|
||||||
{
|
{
|
||||||
|
public const int Size = 0x10;
|
||||||
|
|
||||||
public int GmtOffset;
|
public int GmtOffset;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
[MarshalAs(UnmanagedType.I1)]
|
||||||
public bool IsDaySavingTime;
|
public bool IsDaySavingTime;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
public Array3<byte> Padding1;
|
||||||
public char[] Padding1;
|
|
||||||
|
|
||||||
public int AbbreviationListIndex;
|
public int AbbreviationListIndex;
|
||||||
|
|
||||||
@@ -21,7 +23,6 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
[MarshalAs(UnmanagedType.I1)]
|
[MarshalAs(UnmanagedType.I1)]
|
||||||
public bool IsGMT;
|
public bool IsGMT;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
public ushort Padding2;
|
||||||
public char[] Padding2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,16 +1,18 @@
|
|||||||
using System.Runtime.InteropServices;
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0x4000, CharSet = CharSet.Ansi)]
|
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0x4000, CharSet = CharSet.Ansi)]
|
||||||
struct TimeZoneRule
|
public struct TimeZoneRule
|
||||||
{
|
{
|
||||||
public const int TzMaxTypes = 128;
|
public const int TzMaxTypes = 128;
|
||||||
public const int TzMaxChars = 50;
|
public const int TzMaxChars = 50;
|
||||||
public const int TzMaxLeaps = 50;
|
public const int TzMaxLeaps = 50;
|
||||||
public const int TzMaxTimes = 1000;
|
public const int TzMaxTimes = 1000;
|
||||||
public const int TzNameMax = 255;
|
public const int TzNameMax = 255;
|
||||||
public const int TzCharsArraySize = 2 * (TzNameMax + 1);
|
public const int TzCharsArraySize = 2 * (TzNameMax + 1);
|
||||||
|
|
||||||
public int TimeCount;
|
public int TimeCount;
|
||||||
public int TypeCount;
|
public int TypeCount;
|
||||||
@@ -22,17 +24,32 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
[MarshalAs(UnmanagedType.I1)]
|
[MarshalAs(UnmanagedType.I1)]
|
||||||
public bool GoAhead;
|
public bool GoAhead;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TzMaxTimes)]
|
[StructLayout(LayoutKind.Sequential, Size = sizeof(long) * TzMaxTimes)]
|
||||||
public long[] Ats;
|
private struct AtsStorageStruct { }
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TzMaxTimes)]
|
private AtsStorageStruct _ats;
|
||||||
public byte[] Types;
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TzMaxTypes)]
|
public Span<long> Ats => SpanHelpers.AsSpan<AtsStorageStruct, long>(ref _ats);
|
||||||
public TimeTypeInfo[] Ttis;
|
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TzCharsArraySize)]
|
[StructLayout(LayoutKind.Sequential, Size = sizeof(byte) * TzMaxTimes)]
|
||||||
public char[] Chars;
|
private struct TypesStorageStruct { }
|
||||||
|
|
||||||
|
private TypesStorageStruct _types;
|
||||||
|
|
||||||
|
public Span<byte> Types => SpanHelpers.AsByteSpan(ref _types);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = TimeTypeInfo.Size * TzMaxTypes)]
|
||||||
|
private struct TimeTypeInfoStorageStruct { }
|
||||||
|
|
||||||
|
private TimeTypeInfoStorageStruct _ttis;
|
||||||
|
|
||||||
|
public Span<TimeTypeInfo> Ttis => SpanHelpers.AsSpan<TimeTypeInfoStorageStruct, TimeTypeInfo>(ref _ttis);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = sizeof(byte) * TzCharsArraySize)]
|
||||||
|
private struct CharsStorageStruct { }
|
||||||
|
|
||||||
|
private CharsStorageStruct _chars;
|
||||||
|
public Span<byte> Chars => SpanHelpers.AsByteSpan(ref _chars);
|
||||||
|
|
||||||
public int DefaultType;
|
public int DefaultType;
|
||||||
}
|
}
|
||||||
|
@@ -128,7 +128,7 @@ namespace Ryujinx.HLE.Utilities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int CompareCStr(ReadOnlySpan<char> s1, ReadOnlySpan<char> s2)
|
public static int CompareCStr(ReadOnlySpan<byte> s1, ReadOnlySpan<byte> s2)
|
||||||
{
|
{
|
||||||
int s1Index = 0;
|
int s1Index = 0;
|
||||||
int s2Index = 0;
|
int s2Index = 0;
|
||||||
@@ -142,11 +142,11 @@ namespace Ryujinx.HLE.Utilities
|
|||||||
return s2[s2Index] - s1[s1Index];
|
return s2[s2Index] - s1[s1Index];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int LengthCstr(ReadOnlySpan<char> s)
|
public static int LengthCstr(ReadOnlySpan<byte> s)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
while (s[i] != '\0')
|
while (s[i] != 0)
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@@ -227,6 +227,8 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
// Look up the virtual region using the region list.
|
// Look up the virtual region using the region list.
|
||||||
// Signal up the chain to relevant handles.
|
// Signal up the chain to relevant handles.
|
||||||
|
|
||||||
|
bool shouldThrow = false;
|
||||||
|
|
||||||
lock (TrackingLock)
|
lock (TrackingLock)
|
||||||
{
|
{
|
||||||
ref var overlaps = ref ThreadStaticArray<VirtualRegion>.Get();
|
ref var overlaps = ref ThreadStaticArray<VirtualRegion>.Get();
|
||||||
@@ -235,32 +237,41 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
|
|
||||||
if (count == 0 && !precise)
|
if (count == 0 && !precise)
|
||||||
{
|
{
|
||||||
if (!_memoryManager.IsMapped(address))
|
if (_memoryManager.IsMapped(address))
|
||||||
{
|
{
|
||||||
_invalidAccessHandler?.Invoke(address);
|
_memoryManager.TrackingReprotect(address & ~(ulong)(_pageSize - 1), (ulong)_pageSize, MemoryPermission.ReadAndWrite);
|
||||||
|
return false; // We can't handle this - it's probably a real invalid access.
|
||||||
// We can't continue - it's impossible to remove protection from the page.
|
|
||||||
// Even if the access handler wants us to continue, we wouldn't be able to.
|
|
||||||
throw new InvalidMemoryRegionException();
|
|
||||||
}
|
|
||||||
|
|
||||||
_memoryManager.TrackingReprotect(address & ~(ulong)(_pageSize - 1), (ulong)_pageSize, MemoryPermission.ReadAndWrite);
|
|
||||||
return false; // We can't handle this - it's probably a real invalid access.
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
VirtualRegion region = overlaps[i];
|
|
||||||
|
|
||||||
if (precise)
|
|
||||||
{
|
|
||||||
region.SignalPrecise(address, size, write);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
region.Signal(address, size, write);
|
shouldThrow = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
VirtualRegion region = overlaps[i];
|
||||||
|
|
||||||
|
if (precise)
|
||||||
|
{
|
||||||
|
region.SignalPrecise(address, size, write);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
region.Signal(address, size, write);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldThrow)
|
||||||
|
{
|
||||||
|
_invalidAccessHandler?.Invoke(address);
|
||||||
|
|
||||||
|
// We can't continue - it's impossible to remove protection from the page.
|
||||||
|
// Even if the access handler wants us to continue, we wouldn't be able to.
|
||||||
|
throw new InvalidMemoryRegionException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@@ -24,6 +24,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" />
|
<ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj" />
|
<ProjectReference Include="..\Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj" />
|
||||||
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
|
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
|
||||||
|
18
Ryujinx.Tests/Time/TimeZoneRuleTests.cs
Normal file
18
Ryujinx.Tests/Time/TimeZoneRuleTests.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Tests.Time
|
||||||
|
{
|
||||||
|
internal class TimeZoneRuleTests
|
||||||
|
{
|
||||||
|
class EffectInfoParameterTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void EnsureTypeSize()
|
||||||
|
{
|
||||||
|
Assert.AreEqual(0x4000, Unsafe.SizeOf<TimeZoneRule>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user