mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-08-19 21:52:22 +00:00
This reverts commit 85dbb9559a
.
This commit is contained in:
parent
85dbb9559a
commit
3615a70cae
|
@ -7,109 +7,109 @@ namespace Ryujinx.HLE
|
||||||
{
|
{
|
||||||
public const long RamSize = 4L * 1024 * 1024 * 1024;
|
public const long RamSize = 4L * 1024 * 1024 * 1024;
|
||||||
|
|
||||||
public IntPtr RamPointer { get; }
|
public IntPtr RamPointer { get; private set; }
|
||||||
|
|
||||||
private unsafe byte* _ramPtr;
|
private unsafe byte* RamPtr;
|
||||||
|
|
||||||
public unsafe DeviceMemory()
|
public unsafe DeviceMemory()
|
||||||
{
|
{
|
||||||
RamPointer = Marshal.AllocHGlobal(new IntPtr(RamSize));
|
RamPointer = Marshal.AllocHGlobal(new IntPtr(RamSize));
|
||||||
|
|
||||||
_ramPtr = (byte*)RamPointer;
|
RamPtr = (byte*)RamPointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sbyte ReadSByte(long position)
|
public sbyte ReadSByte(long Position)
|
||||||
{
|
{
|
||||||
return (sbyte)ReadByte(position);
|
return (sbyte)ReadByte(Position);
|
||||||
}
|
}
|
||||||
|
|
||||||
public short ReadInt16(long position)
|
public short ReadInt16(long Position)
|
||||||
{
|
{
|
||||||
return (short)ReadUInt16(position);
|
return (short)ReadUInt16(Position);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ReadInt32(long position)
|
public int ReadInt32(long Position)
|
||||||
{
|
{
|
||||||
return (int)ReadUInt32(position);
|
return (int)ReadUInt32(Position);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long ReadInt64(long position)
|
public long ReadInt64(long Position)
|
||||||
{
|
{
|
||||||
return (long)ReadUInt64(position);
|
return (long)ReadUInt64(Position);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe byte ReadByte(long position)
|
public unsafe byte ReadByte(long Position)
|
||||||
{
|
{
|
||||||
return *(_ramPtr + position);
|
return *((byte*)(RamPtr + Position));
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe ushort ReadUInt16(long position)
|
public unsafe ushort ReadUInt16(long Position)
|
||||||
{
|
{
|
||||||
return *((ushort*)(_ramPtr + position));
|
return *((ushort*)(RamPtr + Position));
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe uint ReadUInt32(long position)
|
public unsafe uint ReadUInt32(long Position)
|
||||||
{
|
{
|
||||||
return *((uint*)(_ramPtr + position));
|
return *((uint*)(RamPtr + Position));
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe ulong ReadUInt64(long position)
|
public unsafe ulong ReadUInt64(long Position)
|
||||||
{
|
{
|
||||||
return *((ulong*)(_ramPtr + position));
|
return *((ulong*)(RamPtr + Position));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteSByte(long position, sbyte value)
|
public void WriteSByte(long Position, sbyte Value)
|
||||||
{
|
{
|
||||||
WriteByte(position, (byte)value);
|
WriteByte(Position, (byte)Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteInt16(long position, short value)
|
public void WriteInt16(long Position, short Value)
|
||||||
{
|
{
|
||||||
WriteUInt16(position, (ushort)value);
|
WriteUInt16(Position, (ushort)Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteInt32(long position, int value)
|
public void WriteInt32(long Position, int Value)
|
||||||
{
|
{
|
||||||
WriteUInt32(position, (uint)value);
|
WriteUInt32(Position, (uint)Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteInt64(long position, long value)
|
public void WriteInt64(long Position, long Value)
|
||||||
{
|
{
|
||||||
WriteUInt64(position, (ulong)value);
|
WriteUInt64(Position, (ulong)Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void WriteByte(long position, byte value)
|
public unsafe void WriteByte(long Position, byte Value)
|
||||||
{
|
{
|
||||||
*(_ramPtr + position) = value;
|
*((byte*)(RamPtr + Position)) = Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void WriteUInt16(long position, ushort value)
|
public unsafe void WriteUInt16(long Position, ushort Value)
|
||||||
{
|
{
|
||||||
*((ushort*)(_ramPtr + position)) = value;
|
*((ushort*)(RamPtr + Position)) = Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void WriteUInt32(long position, uint value)
|
public unsafe void WriteUInt32(long Position, uint Value)
|
||||||
{
|
{
|
||||||
*((uint*)(_ramPtr + position)) = value;
|
*((uint*)(RamPtr + Position)) = Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void WriteUInt64(long position, ulong value)
|
public unsafe void WriteUInt64(long Position, ulong Value)
|
||||||
{
|
{
|
||||||
*((ulong*)(_ramPtr + position)) = value;
|
*((ulong*)(RamPtr + Position)) = Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void FillWithZeros(long position, int size)
|
public void FillWithZeros(long Position, int Size)
|
||||||
{
|
{
|
||||||
int size8 = size & ~(8 - 1);
|
int Size8 = Size & ~(8 - 1);
|
||||||
|
|
||||||
for (int offs = 0; offs < size8; offs += 8)
|
for (int Offs = 0; Offs < Size8; Offs += 8)
|
||||||
{
|
{
|
||||||
WriteInt64(position + offs, 0);
|
WriteInt64(Position + Offs, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int offs = size8; offs < (size - size8); offs++)
|
for (int Offs = Size8; Offs < (Size - Size8); Offs++)
|
||||||
{
|
{
|
||||||
WriteByte(position + offs, 0);
|
WriteByte(Position + Offs, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ namespace Ryujinx.HLE
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool Disposing)
|
||||||
{
|
{
|
||||||
Marshal.FreeHGlobal(RamPointer);
|
Marshal.FreeHGlobal(RamPointer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,6 @@ namespace Ryujinx.HLE.Exceptions
|
||||||
{
|
{
|
||||||
public class InvalidNpdmException : Exception
|
public class InvalidNpdmException : Exception
|
||||||
{
|
{
|
||||||
public InvalidNpdmException(string message) : base(message) { }
|
public InvalidNpdmException(string ExMsg) : base(ExMsg) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,6 @@ namespace Ryujinx.HLE.Exceptions
|
||||||
|
|
||||||
public UndefinedInstructionException() : base() { }
|
public UndefinedInstructionException() : base() { }
|
||||||
|
|
||||||
public UndefinedInstructionException(long position, int opCode) : base(string.Format(ExMsg, position, opCode)) { }
|
public UndefinedInstructionException(long Position, int OpCode) : base(string.Format(ExMsg, Position, OpCode)) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,297 +9,297 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
{
|
{
|
||||||
internal class ContentManager
|
internal class ContentManager
|
||||||
{
|
{
|
||||||
private Dictionary<StorageId, LinkedList<LocationEntry>> _locationEntries;
|
private Dictionary<StorageId, LinkedList<LocationEntry>> LocationEntries;
|
||||||
|
|
||||||
private Dictionary<string, long> _sharedFontTitleDictionary;
|
private Dictionary<string, long> SharedFontTitleDictionary;
|
||||||
|
|
||||||
private SortedDictionary<(ulong, ContentType), string> _contentDictionary;
|
private SortedDictionary<(ulong, ContentType), string> ContentDictionary;
|
||||||
|
|
||||||
private Switch _device;
|
private Switch Device;
|
||||||
|
|
||||||
public ContentManager(Switch device)
|
public ContentManager(Switch Device)
|
||||||
{
|
{
|
||||||
_contentDictionary = new SortedDictionary<(ulong, ContentType), string>();
|
ContentDictionary = new SortedDictionary<(ulong, ContentType), string>();
|
||||||
_locationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>();
|
LocationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>();
|
||||||
|
|
||||||
_sharedFontTitleDictionary = new Dictionary<string, long>
|
SharedFontTitleDictionary = new Dictionary<string, long>()
|
||||||
{
|
{
|
||||||
{ "FontStandard", 0x0100000000000811 },
|
{ "FontStandard", 0x0100000000000811 },
|
||||||
{ "FontChineseSimplified", 0x0100000000000814 },
|
{ "FontChineseSimplified", 0x0100000000000814 },
|
||||||
{ "FontExtendedChineseSimplified", 0x0100000000000814 },
|
{ "FontExtendedChineseSimplified", 0x0100000000000814 },
|
||||||
{ "FontKorean", 0x0100000000000812 },
|
{ "FontKorean", 0x0100000000000812 },
|
||||||
{ "FontChineseTraditional", 0x0100000000000813 },
|
{ "FontChineseTraditional", 0x0100000000000813 },
|
||||||
{ "FontNintendoExtended", 0x0100000000000810 }
|
{ "FontNintendoExtended" , 0x0100000000000810 },
|
||||||
};
|
};
|
||||||
|
|
||||||
_device = device;
|
this.Device = Device;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadEntries()
|
public void LoadEntries()
|
||||||
{
|
{
|
||||||
_contentDictionary = new SortedDictionary<(ulong, ContentType), string>();
|
ContentDictionary = new SortedDictionary<(ulong, ContentType), string>();
|
||||||
|
|
||||||
foreach (StorageId storageId in Enum.GetValues(typeof(StorageId)))
|
foreach (StorageId StorageId in Enum.GetValues(typeof(StorageId)))
|
||||||
{
|
{
|
||||||
string contentDirectory = null;
|
string ContentDirectory = null;
|
||||||
string contentPathString = null;
|
string ContentPathString = null;
|
||||||
string registeredDirectory = null;
|
string RegisteredDirectory = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
contentPathString = LocationHelper.GetContentRoot(storageId);
|
ContentPathString = LocationHelper.GetContentRoot(StorageId);
|
||||||
contentDirectory = LocationHelper.GetRealPath(_device.FileSystem, contentPathString);
|
ContentDirectory = LocationHelper.GetRealPath(Device.FileSystem, ContentPathString);
|
||||||
registeredDirectory = Path.Combine(contentDirectory, "registered");
|
RegisteredDirectory = Path.Combine(ContentDirectory, "registered");
|
||||||
}
|
}
|
||||||
catch (NotSupportedException)
|
catch (NotSupportedException NEx)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Directory.CreateDirectory(registeredDirectory);
|
Directory.CreateDirectory(RegisteredDirectory);
|
||||||
|
|
||||||
LinkedList<LocationEntry> locationList = new LinkedList<LocationEntry>();
|
LinkedList<LocationEntry> LocationList = new LinkedList<LocationEntry>();
|
||||||
|
|
||||||
void AddEntry(LocationEntry entry)
|
void AddEntry(LocationEntry Entry)
|
||||||
{
|
{
|
||||||
locationList.AddLast(entry);
|
LocationList.AddLast(Entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (string directoryPath in Directory.EnumerateDirectories(registeredDirectory))
|
foreach (string DirectoryPath in Directory.EnumerateDirectories(RegisteredDirectory))
|
||||||
{
|
{
|
||||||
if (Directory.GetFiles(directoryPath).Length > 0)
|
if (Directory.GetFiles(DirectoryPath).Length > 0)
|
||||||
{
|
{
|
||||||
string ncaName = new DirectoryInfo(directoryPath).Name.Replace(".nca", string.Empty);
|
string NcaName = new DirectoryInfo(DirectoryPath).Name.Replace(".nca", string.Empty);
|
||||||
|
|
||||||
using (FileStream ncaFile = new FileStream(Directory.GetFiles(directoryPath)[0], FileMode.Open, FileAccess.Read))
|
using (FileStream NcaFile = new FileStream(Directory.GetFiles(DirectoryPath)[0], FileMode.Open, FileAccess.Read))
|
||||||
{
|
{
|
||||||
Nca nca = new Nca(_device.System.KeySet, ncaFile, false);
|
Nca Nca = new Nca(Device.System.KeySet, NcaFile, false);
|
||||||
|
|
||||||
string switchPath = Path.Combine(contentPathString + ":",
|
string SwitchPath = Path.Combine(ContentPathString + ":",
|
||||||
ncaFile.Name.Replace(contentDirectory, string.Empty).TrimStart('\\'));
|
NcaFile.Name.Replace(ContentDirectory, string.Empty).TrimStart('\\'));
|
||||||
|
|
||||||
// Change path format to switch's
|
// Change path format to switch's
|
||||||
switchPath = switchPath.Replace('\\', '/');
|
SwitchPath = SwitchPath.Replace('\\', '/');
|
||||||
|
|
||||||
LocationEntry entry = new LocationEntry(switchPath,
|
LocationEntry Entry = new LocationEntry(SwitchPath,
|
||||||
0,
|
0,
|
||||||
(long)nca.Header.TitleId,
|
(long)Nca.Header.TitleId,
|
||||||
nca.Header.ContentType);
|
Nca.Header.ContentType);
|
||||||
|
|
||||||
AddEntry(entry);
|
AddEntry(Entry);
|
||||||
|
|
||||||
_contentDictionary.Add((nca.Header.TitleId, nca.Header.ContentType), ncaName);
|
ContentDictionary.Add((Nca.Header.TitleId, Nca.Header.ContentType), NcaName);
|
||||||
|
|
||||||
ncaFile.Close();
|
NcaFile.Close();
|
||||||
nca.Dispose();
|
Nca.Dispose();
|
||||||
ncaFile.Dispose();
|
NcaFile.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (string filePath in Directory.EnumerateFiles(contentDirectory))
|
foreach (string FilePath in Directory.EnumerateFiles(ContentDirectory))
|
||||||
{
|
{
|
||||||
if (Path.GetExtension(filePath) == ".nca")
|
if (Path.GetExtension(FilePath) == ".nca")
|
||||||
{
|
{
|
||||||
string ncaName = Path.GetFileNameWithoutExtension(filePath);
|
string NcaName = Path.GetFileNameWithoutExtension(FilePath);
|
||||||
|
|
||||||
using (FileStream ncaFile = new FileStream(filePath, FileMode.Open, FileAccess.Read))
|
using (FileStream NcaFile = new FileStream(FilePath, FileMode.Open, FileAccess.Read))
|
||||||
{
|
{
|
||||||
Nca nca = new Nca(_device.System.KeySet, ncaFile, false);
|
Nca Nca = new Nca(Device.System.KeySet, NcaFile, false);
|
||||||
|
|
||||||
string switchPath = Path.Combine(contentPathString + ":",
|
string SwitchPath = Path.Combine(ContentPathString + ":",
|
||||||
filePath.Replace(contentDirectory, string.Empty).TrimStart('\\'));
|
FilePath.Replace(ContentDirectory, string.Empty).TrimStart('\\'));
|
||||||
|
|
||||||
// Change path format to switch's
|
// Change path format to switch's
|
||||||
switchPath = switchPath.Replace('\\', '/');
|
SwitchPath = SwitchPath.Replace('\\', '/');
|
||||||
|
|
||||||
LocationEntry entry = new LocationEntry(switchPath,
|
LocationEntry Entry = new LocationEntry(SwitchPath,
|
||||||
0,
|
0,
|
||||||
(long)nca.Header.TitleId,
|
(long)Nca.Header.TitleId,
|
||||||
nca.Header.ContentType);
|
Nca.Header.ContentType);
|
||||||
|
|
||||||
AddEntry(entry);
|
AddEntry(Entry);
|
||||||
|
|
||||||
_contentDictionary.Add((nca.Header.TitleId, nca.Header.ContentType), ncaName);
|
ContentDictionary.Add((Nca.Header.TitleId, Nca.Header.ContentType), NcaName);
|
||||||
|
|
||||||
ncaFile.Close();
|
NcaFile.Close();
|
||||||
nca.Dispose();
|
Nca.Dispose();
|
||||||
ncaFile.Dispose();
|
NcaFile.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_locationEntries.ContainsKey(storageId) && _locationEntries[storageId]?.Count == 0)
|
if(LocationEntries.ContainsKey(StorageId) && LocationEntries[StorageId]?.Count == 0)
|
||||||
{
|
{
|
||||||
_locationEntries.Remove(storageId);
|
LocationEntries.Remove(StorageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_locationEntries.ContainsKey(storageId))
|
if (!LocationEntries.ContainsKey(StorageId))
|
||||||
{
|
{
|
||||||
_locationEntries.Add(storageId, locationList);
|
LocationEntries.Add(StorageId, LocationList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearEntry(long titleId, ContentType contentType,StorageId storageId)
|
public void ClearEntry(long TitleId, ContentType ContentType,StorageId StorageId)
|
||||||
{
|
{
|
||||||
RemoveLocationEntry(titleId, contentType, storageId);
|
RemoveLocationEntry(TitleId, ContentType, StorageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RefreshEntries(StorageId storageId, int flag)
|
public void RefreshEntries(StorageId StorageId, int Flag)
|
||||||
{
|
{
|
||||||
LinkedList<LocationEntry> locationList = _locationEntries[storageId];
|
LinkedList<LocationEntry> LocationList = LocationEntries[StorageId];
|
||||||
LinkedListNode<LocationEntry> locationEntry = locationList.First;
|
LinkedListNode<LocationEntry> LocationEntry = LocationList.First;
|
||||||
|
|
||||||
while (locationEntry != null)
|
while (LocationEntry != null)
|
||||||
{
|
{
|
||||||
LinkedListNode<LocationEntry> nextLocationEntry = locationEntry.Next;
|
LinkedListNode<LocationEntry> NextLocationEntry = LocationEntry.Next;
|
||||||
|
|
||||||
if (locationEntry.Value.Flag == flag)
|
if (LocationEntry.Value.Flag == Flag)
|
||||||
{
|
{
|
||||||
locationList.Remove(locationEntry.Value);
|
LocationList.Remove(LocationEntry.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
locationEntry = nextLocationEntry;
|
LocationEntry = NextLocationEntry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasNca(string ncaId, StorageId storageId)
|
public bool HasNca(string NcaId, StorageId StorageId)
|
||||||
{
|
{
|
||||||
if (_contentDictionary.ContainsValue(ncaId))
|
if (ContentDictionary.ContainsValue(NcaId))
|
||||||
{
|
{
|
||||||
var content = _contentDictionary.FirstOrDefault(x => x.Value == ncaId);
|
var Content = ContentDictionary.FirstOrDefault(x => x.Value == NcaId);
|
||||||
long titleId = (long)content.Key.Item1;
|
long TitleId = (long)Content.Key.Item1;
|
||||||
ContentType contentType = content.Key.Item2;
|
ContentType ContentType = Content.Key.Item2;
|
||||||
StorageId storage = GetInstalledStorage(titleId, contentType, storageId);
|
StorageId Storage = GetInstalledStorage(TitleId, ContentType, StorageId);
|
||||||
|
|
||||||
return storage == storageId;
|
return Storage == StorageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UInt128 GetInstalledNcaId(long titleId, ContentType contentType)
|
public UInt128 GetInstalledNcaId(long TitleId, ContentType ContentType)
|
||||||
{
|
{
|
||||||
if (_contentDictionary.ContainsKey(((ulong)titleId,contentType)))
|
if (ContentDictionary.ContainsKey(((ulong)TitleId,ContentType)))
|
||||||
{
|
{
|
||||||
return new UInt128(_contentDictionary[((ulong)titleId,contentType)]);
|
return new UInt128(ContentDictionary[((ulong)TitleId,ContentType)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new UInt128();
|
return new UInt128();
|
||||||
}
|
}
|
||||||
|
|
||||||
public StorageId GetInstalledStorage(long titleId, ContentType contentType, StorageId storageId)
|
public StorageId GetInstalledStorage(long TitleId, ContentType ContentType, StorageId StorageId)
|
||||||
{
|
{
|
||||||
LocationEntry locationEntry = GetLocation(titleId, contentType, storageId);
|
LocationEntry LocationEntry = GetLocation(TitleId, ContentType, StorageId);
|
||||||
|
|
||||||
return locationEntry.ContentPath != null ?
|
return LocationEntry.ContentPath != null ?
|
||||||
LocationHelper.GetStorageId(locationEntry.ContentPath) : StorageId.None;
|
LocationHelper.GetStorageId(LocationEntry.ContentPath) : StorageId.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetInstalledContentPath(long titleId, StorageId storageId, ContentType contentType)
|
public string GetInstalledContentPath(long TitleId, StorageId StorageId, ContentType ContentType)
|
||||||
{
|
{
|
||||||
LocationEntry locationEntry = GetLocation(titleId, contentType, storageId);
|
LocationEntry LocationEntry = GetLocation(TitleId, ContentType, StorageId);
|
||||||
|
|
||||||
if (VerifyContentType(locationEntry, contentType))
|
if (VerifyContentType(LocationEntry, ContentType))
|
||||||
{
|
{
|
||||||
return locationEntry.ContentPath;
|
return LocationEntry.ContentPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RedirectLocation(LocationEntry newEntry, StorageId storageId)
|
public void RedirectLocation(LocationEntry NewEntry, StorageId StorageId)
|
||||||
{
|
{
|
||||||
LocationEntry locationEntry = GetLocation(newEntry.TitleId, newEntry.ContentType, storageId);
|
LocationEntry LocationEntry = GetLocation(NewEntry.TitleId, NewEntry.ContentType, StorageId);
|
||||||
|
|
||||||
if (locationEntry.ContentPath != null)
|
if (LocationEntry.ContentPath != null)
|
||||||
{
|
{
|
||||||
RemoveLocationEntry(newEntry.TitleId, newEntry.ContentType, storageId);
|
RemoveLocationEntry(NewEntry.TitleId, NewEntry.ContentType, StorageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
AddLocationEntry(newEntry, storageId);
|
AddLocationEntry(NewEntry, StorageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool VerifyContentType(LocationEntry locationEntry, ContentType contentType)
|
private bool VerifyContentType(LocationEntry LocationEntry, ContentType ContentType)
|
||||||
{
|
{
|
||||||
if (locationEntry.ContentPath == null)
|
if (LocationEntry.ContentPath == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
StorageId storageId = LocationHelper.GetStorageId(locationEntry.ContentPath);
|
StorageId StorageId = LocationHelper.GetStorageId(LocationEntry.ContentPath);
|
||||||
string installedPath = _device.FileSystem.SwitchPathToSystemPath(locationEntry.ContentPath);
|
string InstalledPath = Device.FileSystem.SwitchPathToSystemPath(LocationEntry.ContentPath);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(installedPath))
|
if (!string.IsNullOrWhiteSpace(InstalledPath))
|
||||||
{
|
{
|
||||||
if (File.Exists(installedPath))
|
if (File.Exists(InstalledPath))
|
||||||
{
|
{
|
||||||
FileStream file = new FileStream(installedPath, FileMode.Open, FileAccess.Read);
|
FileStream File = new FileStream(InstalledPath, FileMode.Open, FileAccess.Read);
|
||||||
Nca nca = new Nca(_device.System.KeySet, file, false);
|
Nca Nca = new Nca(Device.System.KeySet, File, false);
|
||||||
bool contentCheck = nca.Header.ContentType == contentType;
|
bool ContentCheck = Nca.Header.ContentType == ContentType;
|
||||||
|
|
||||||
nca.Dispose();
|
Nca.Dispose();
|
||||||
file.Dispose();
|
File.Dispose();
|
||||||
|
|
||||||
return contentCheck;
|
return ContentCheck;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddLocationEntry(LocationEntry entry, StorageId storageId)
|
private void AddLocationEntry(LocationEntry Entry, StorageId StorageId)
|
||||||
{
|
{
|
||||||
LinkedList<LocationEntry> locationList = null;
|
LinkedList<LocationEntry> LocationList = null;
|
||||||
|
|
||||||
if (_locationEntries.ContainsKey(storageId))
|
if (LocationEntries.ContainsKey(StorageId))
|
||||||
{
|
{
|
||||||
locationList = _locationEntries[storageId];
|
LocationList = LocationEntries[StorageId];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (locationList != null)
|
if (LocationList != null)
|
||||||
{
|
{
|
||||||
if (locationList.Contains(entry))
|
if (LocationList.Contains(Entry))
|
||||||
{
|
{
|
||||||
locationList.Remove(entry);
|
LocationList.Remove(Entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
locationList.AddLast(entry);
|
LocationList.AddLast(Entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveLocationEntry(long titleId, ContentType contentType, StorageId storageId)
|
private void RemoveLocationEntry(long TitleId, ContentType ContentType, StorageId StorageId)
|
||||||
{
|
{
|
||||||
LinkedList<LocationEntry> locationList = null;
|
LinkedList<LocationEntry> LocationList = null;
|
||||||
|
|
||||||
if (_locationEntries.ContainsKey(storageId))
|
if (LocationEntries.ContainsKey(StorageId))
|
||||||
{
|
{
|
||||||
locationList = _locationEntries[storageId];
|
LocationList = LocationEntries[StorageId];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (locationList != null)
|
if (LocationList != null)
|
||||||
{
|
{
|
||||||
LocationEntry entry =
|
LocationEntry Entry =
|
||||||
locationList.ToList().Find(x => x.TitleId == titleId && x.ContentType == contentType);
|
LocationList.ToList().Find(x => x.TitleId == TitleId && x.ContentType == ContentType);
|
||||||
|
|
||||||
if (entry.ContentPath != null)
|
if (Entry.ContentPath != null)
|
||||||
{
|
{
|
||||||
locationList.Remove(entry);
|
LocationList.Remove(Entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetFontTitle(string fontName, out long titleId)
|
public bool TryGetFontTitle(string FontName, out long TitleId)
|
||||||
{
|
{
|
||||||
return _sharedFontTitleDictionary.TryGetValue(fontName, out titleId);
|
return SharedFontTitleDictionary.TryGetValue(FontName, out TitleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LocationEntry GetLocation(long titleId, ContentType contentType,StorageId storageId)
|
private LocationEntry GetLocation(long TitleId, ContentType ContentType,StorageId StorageId)
|
||||||
{
|
{
|
||||||
LinkedList<LocationEntry> locationList = _locationEntries[storageId];
|
LinkedList<LocationEntry> LocationList = LocationEntries[StorageId];
|
||||||
|
|
||||||
return locationList.ToList().Find(x => x.TitleId == titleId && x.ContentType == contentType);
|
return LocationList.ToList().Find(x => x.TitleId == TitleId && x.ContentType == ContentType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,28 @@
|
||||||
using LibHac;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using LibHac;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.FileSystem.Content
|
namespace Ryujinx.HLE.FileSystem.Content
|
||||||
{
|
{
|
||||||
public struct LocationEntry
|
public struct LocationEntry
|
||||||
{
|
{
|
||||||
public string ContentPath { get; }
|
public string ContentPath { get; private set; }
|
||||||
public int Flag { get; private set; }
|
public int Flag { get; private set; }
|
||||||
public long TitleId { get; }
|
public long TitleId { get; private set; }
|
||||||
public ContentType ContentType { get; }
|
public ContentType ContentType { get; private set; }
|
||||||
|
|
||||||
public LocationEntry(string contentPath, int flag, long titleId, ContentType contentType)
|
public LocationEntry(string ContentPath, int Flag, long TitleId, ContentType ContentType)
|
||||||
{
|
{
|
||||||
ContentPath = contentPath;
|
this.ContentPath = ContentPath;
|
||||||
Flag = flag;
|
this.Flag = Flag;
|
||||||
TitleId = titleId;
|
this.TitleId = TitleId;
|
||||||
ContentType = contentType;
|
this.ContentType = ContentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetFlag(int flag)
|
public void SetFlag(int Flag)
|
||||||
{
|
{
|
||||||
Flag = flag;
|
this.Flag = Flag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,30 +7,30 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
{
|
{
|
||||||
internal static class LocationHelper
|
internal static class LocationHelper
|
||||||
{
|
{
|
||||||
public static string GetRealPath(VirtualFileSystem fileSystem, string switchContentPath)
|
public static string GetRealPath(VirtualFileSystem FileSystem, string SwitchContentPath)
|
||||||
{
|
{
|
||||||
string basePath = fileSystem.GetBasePath();
|
string BasePath = FileSystem.GetBasePath();
|
||||||
|
|
||||||
switch (switchContentPath)
|
switch (SwitchContentPath)
|
||||||
{
|
{
|
||||||
case ContentPath.SystemContent:
|
case ContentPath.SystemContent:
|
||||||
return Path.Combine(fileSystem.GetBasePath(), SystemNandPath, "Contents");
|
return Path.Combine(FileSystem.GetBasePath(), SystemNandPath, "Contents");
|
||||||
case ContentPath.UserContent:
|
case ContentPath.UserContent:
|
||||||
return Path.Combine(fileSystem.GetBasePath(), UserNandPath, "Contents");
|
return Path.Combine(FileSystem.GetBasePath(), UserNandPath, "Contents");
|
||||||
case ContentPath.SdCardContent:
|
case ContentPath.SdCardContent:
|
||||||
return Path.Combine(fileSystem.GetSdCardPath(), "Nintendo", "Contents");
|
return Path.Combine(FileSystem.GetSdCardPath(), "Nintendo", "Contents");
|
||||||
case ContentPath.System:
|
case ContentPath.System:
|
||||||
return Path.Combine(basePath, SystemNandPath);
|
return Path.Combine(BasePath, SystemNandPath);
|
||||||
case ContentPath.User:
|
case ContentPath.User:
|
||||||
return Path.Combine(basePath, UserNandPath);
|
return Path.Combine(BasePath, UserNandPath);
|
||||||
default:
|
default:
|
||||||
throw new NotSupportedException($"Content Path `{switchContentPath}` is not supported.");
|
throw new NotSupportedException($"Content Path `{SwitchContentPath}` is not supported.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetContentPath(ContentStorageId contentStorageId)
|
public static string GetContentPath(ContentStorageId ContentStorageId)
|
||||||
{
|
{
|
||||||
switch (contentStorageId)
|
switch (ContentStorageId)
|
||||||
{
|
{
|
||||||
case ContentStorageId.NandSystem:
|
case ContentStorageId.NandSystem:
|
||||||
return ContentPath.SystemContent;
|
return ContentPath.SystemContent;
|
||||||
|
@ -39,13 +39,13 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
case ContentStorageId.SdCard:
|
case ContentStorageId.SdCard:
|
||||||
return ContentPath.SdCardContent;
|
return ContentPath.SdCardContent;
|
||||||
default:
|
default:
|
||||||
throw new NotSupportedException($"Content Storage `{contentStorageId}` is not supported.");
|
throw new NotSupportedException($"Content Storage `{ContentStorageId}` is not supported.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetContentRoot(StorageId storageId)
|
public static string GetContentRoot(StorageId StorageId)
|
||||||
{
|
{
|
||||||
switch (storageId)
|
switch (StorageId)
|
||||||
{
|
{
|
||||||
case StorageId.NandSystem:
|
case StorageId.NandSystem:
|
||||||
return ContentPath.SystemContent;
|
return ContentPath.SystemContent;
|
||||||
|
@ -54,15 +54,15 @@ namespace Ryujinx.HLE.FileSystem.Content
|
||||||
case StorageId.SdCard:
|
case StorageId.SdCard:
|
||||||
return ContentPath.SdCardContent;
|
return ContentPath.SdCardContent;
|
||||||
default:
|
default:
|
||||||
throw new NotSupportedException($"Storage Id `{storageId}` is not supported.");
|
throw new NotSupportedException($"Storage Id `{StorageId}` is not supported.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StorageId GetStorageId(string contentPathString)
|
public static StorageId GetStorageId(string ContentPathString)
|
||||||
{
|
{
|
||||||
string cleanedPath = contentPathString.Split(':')[0];
|
string CleanedPath = ContentPathString.Split(':')[0];
|
||||||
|
|
||||||
switch (cleanedPath)
|
switch (CleanedPath)
|
||||||
{
|
{
|
||||||
case ContentPath.SystemContent:
|
case ContentPath.SystemContent:
|
||||||
case ContentPath.System:
|
case ContentPath.System:
|
||||||
|
|
|
@ -10,228 +10,228 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
{
|
{
|
||||||
class FileSystemProvider : IFileSystemProvider
|
class FileSystemProvider : IFileSystemProvider
|
||||||
{
|
{
|
||||||
private readonly string _basePath;
|
private readonly string BasePath;
|
||||||
private readonly string _rootPath;
|
private readonly string RootPath;
|
||||||
|
|
||||||
public FileSystemProvider(string basePath, string rootPath)
|
public FileSystemProvider(string BasePath, string RootPath)
|
||||||
{
|
{
|
||||||
_basePath = basePath;
|
this.BasePath = BasePath;
|
||||||
_rootPath = rootPath;
|
this.RootPath = RootPath;
|
||||||
|
|
||||||
CheckIfDescendentOfRootPath(basePath);
|
CheckIfDescendentOfRootPath(BasePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long CreateDirectory(string name)
|
public long CreateDirectory(string Name)
|
||||||
{
|
{
|
||||||
CheckIfDescendentOfRootPath(name);
|
CheckIfDescendentOfRootPath(Name);
|
||||||
|
|
||||||
if (Directory.Exists(name))
|
if (Directory.Exists(Name))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
||||||
}
|
}
|
||||||
|
|
||||||
Directory.CreateDirectory(name);
|
Directory.CreateDirectory(Name);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long CreateFile(string name, long size)
|
public long CreateFile(string Name, long Size)
|
||||||
{
|
{
|
||||||
CheckIfDescendentOfRootPath(name);
|
CheckIfDescendentOfRootPath(Name);
|
||||||
|
|
||||||
if (File.Exists(name))
|
if (File.Exists(Name))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (FileStream newFile = File.Create(name))
|
using (FileStream NewFile = File.Create(Name))
|
||||||
{
|
{
|
||||||
newFile.SetLength(size);
|
NewFile.SetLength(Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long DeleteDirectory(string name, bool recursive)
|
public long DeleteDirectory(string Name, bool Recursive)
|
||||||
{
|
{
|
||||||
CheckIfDescendentOfRootPath(name);
|
CheckIfDescendentOfRootPath(Name);
|
||||||
|
|
||||||
string dirName = name;
|
string DirName = Name;
|
||||||
|
|
||||||
if (!Directory.Exists(dirName))
|
if (!Directory.Exists(DirName))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
Directory.Delete(dirName, recursive);
|
Directory.Delete(DirName, Recursive);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long DeleteFile(string name)
|
public long DeleteFile(string Name)
|
||||||
{
|
{
|
||||||
CheckIfDescendentOfRootPath(name);
|
CheckIfDescendentOfRootPath(Name);
|
||||||
|
|
||||||
if (!File.Exists(name))
|
if (!File.Exists(Name))
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
File.Delete(name);
|
File.Delete(Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DirectoryEntry[] GetDirectories(string path)
|
public DirectoryEntry[] GetDirectories(string Path)
|
||||||
{
|
{
|
||||||
CheckIfDescendentOfRootPath(path);
|
CheckIfDescendentOfRootPath(Path);
|
||||||
|
|
||||||
List<DirectoryEntry> entries = new List<DirectoryEntry>();
|
List<DirectoryEntry> Entries = new List<DirectoryEntry>();
|
||||||
|
|
||||||
foreach(string directory in Directory.EnumerateDirectories(path))
|
foreach(string Directory in Directory.EnumerateDirectories(Path))
|
||||||
{
|
{
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(directory, DirectoryEntryType.Directory);
|
DirectoryEntry DirectoryEntry = new DirectoryEntry(Directory, DirectoryEntryType.Directory);
|
||||||
|
|
||||||
entries.Add(directoryEntry);
|
Entries.Add(DirectoryEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries.ToArray();
|
return Entries.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DirectoryEntry[] GetEntries(string path)
|
public DirectoryEntry[] GetEntries(string Path)
|
||||||
{
|
{
|
||||||
CheckIfDescendentOfRootPath(path);
|
CheckIfDescendentOfRootPath(Path);
|
||||||
|
|
||||||
if (Directory.Exists(path))
|
if (Directory.Exists(Path))
|
||||||
{
|
{
|
||||||
List<DirectoryEntry> entries = new List<DirectoryEntry>();
|
List<DirectoryEntry> Entries = new List<DirectoryEntry>();
|
||||||
|
|
||||||
foreach (string directory in Directory.EnumerateDirectories(path))
|
foreach (string Directory in Directory.EnumerateDirectories(Path))
|
||||||
{
|
{
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(directory, DirectoryEntryType.Directory);
|
DirectoryEntry DirectoryEntry = new DirectoryEntry(Directory, DirectoryEntryType.Directory);
|
||||||
|
|
||||||
entries.Add(directoryEntry);
|
Entries.Add(DirectoryEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (string file in Directory.EnumerateFiles(path))
|
foreach (string File in Directory.EnumerateFiles(Path))
|
||||||
{
|
{
|
||||||
FileInfo fileInfo = new FileInfo(file);
|
FileInfo FileInfo = new FileInfo(File);
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(file, DirectoryEntryType.File, fileInfo.Length);
|
DirectoryEntry DirectoryEntry = new DirectoryEntry(File, DirectoryEntryType.File, FileInfo.Length);
|
||||||
|
|
||||||
entries.Add(directoryEntry);
|
Entries.Add(DirectoryEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DirectoryEntry[] GetFiles(string path)
|
public DirectoryEntry[] GetFiles(string Path)
|
||||||
{
|
{
|
||||||
CheckIfDescendentOfRootPath(path);
|
CheckIfDescendentOfRootPath(Path);
|
||||||
|
|
||||||
List<DirectoryEntry> entries = new List<DirectoryEntry>();
|
List<DirectoryEntry> Entries = new List<DirectoryEntry>();
|
||||||
|
|
||||||
foreach (string file in Directory.EnumerateFiles(path))
|
foreach (string File in Directory.EnumerateFiles(Path))
|
||||||
{
|
{
|
||||||
FileInfo fileInfo = new FileInfo(file);
|
FileInfo FileInfo = new FileInfo(File);
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(file, DirectoryEntryType.File, fileInfo.Length);
|
DirectoryEntry DirectoryEntry = new DirectoryEntry(File, DirectoryEntryType.File, FileInfo.Length);
|
||||||
|
|
||||||
entries.Add(directoryEntry);
|
Entries.Add(DirectoryEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries.ToArray();
|
return Entries.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetFreeSpace(ServiceCtx context)
|
public long GetFreeSpace(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
return context.Device.FileSystem.GetDrive().AvailableFreeSpace;
|
return Context.Device.FileSystem.GetDrive().AvailableFreeSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetFullPath(string name)
|
public string GetFullPath(string Name)
|
||||||
{
|
{
|
||||||
if (name.StartsWith("//"))
|
if (Name.StartsWith("//"))
|
||||||
{
|
{
|
||||||
name = name.Substring(2);
|
Name = Name.Substring(2);
|
||||||
}
|
}
|
||||||
else if (name.StartsWith('/'))
|
else if (Name.StartsWith('/'))
|
||||||
{
|
{
|
||||||
name = name.Substring(1);
|
Name = Name.Substring(1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
string fullPath = Path.Combine(_basePath, name);
|
string FullPath = Path.Combine(BasePath, Name);
|
||||||
|
|
||||||
CheckIfDescendentOfRootPath(fullPath);
|
CheckIfDescendentOfRootPath(FullPath);
|
||||||
|
|
||||||
return fullPath;
|
return FullPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetTotalSpace(ServiceCtx context)
|
public long GetTotalSpace(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
return context.Device.FileSystem.GetDrive().TotalSize;
|
return Context.Device.FileSystem.GetDrive().TotalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DirectoryExists(string name)
|
public bool DirectoryExists(string Name)
|
||||||
{
|
{
|
||||||
CheckIfDescendentOfRootPath(name);
|
CheckIfDescendentOfRootPath(Name);
|
||||||
|
|
||||||
return Directory.Exists(name);
|
return Directory.Exists(Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool FileExists(string name)
|
public bool FileExists(string Name)
|
||||||
{
|
{
|
||||||
CheckIfDescendentOfRootPath(name);
|
CheckIfDescendentOfRootPath(Name);
|
||||||
|
|
||||||
return File.Exists(name);
|
return File.Exists(Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long OpenDirectory(string name, int filterFlags, out IDirectory directoryInterface)
|
public long OpenDirectory(string Name, int FilterFlags, out IDirectory DirectoryInterface)
|
||||||
{
|
{
|
||||||
CheckIfDescendentOfRootPath(name);
|
CheckIfDescendentOfRootPath(Name);
|
||||||
|
|
||||||
if (Directory.Exists(name))
|
if (Directory.Exists(Name))
|
||||||
{
|
{
|
||||||
directoryInterface = new IDirectory(name, filterFlags, this);
|
DirectoryInterface = new IDirectory(Name, FilterFlags, this);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
directoryInterface = null;
|
DirectoryInterface = null;
|
||||||
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long OpenFile(string name, out IFile fileInterface)
|
public long OpenFile(string Name, out IFile FileInterface)
|
||||||
{
|
{
|
||||||
CheckIfDescendentOfRootPath(name);
|
CheckIfDescendentOfRootPath(Name);
|
||||||
|
|
||||||
if (File.Exists(name))
|
if (File.Exists(Name))
|
||||||
{
|
{
|
||||||
FileStream stream = new FileStream(name, FileMode.Open);
|
FileStream Stream = new FileStream(Name, FileMode.Open);
|
||||||
|
|
||||||
fileInterface = new IFile(stream, name);
|
FileInterface = new IFile(Stream, Name);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fileInterface = null;
|
FileInterface = null;
|
||||||
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long RenameDirectory(string oldName, string newName)
|
public long RenameDirectory(string OldName, string NewName)
|
||||||
{
|
{
|
||||||
CheckIfDescendentOfRootPath(oldName);
|
CheckIfDescendentOfRootPath(OldName);
|
||||||
CheckIfDescendentOfRootPath(newName);
|
CheckIfDescendentOfRootPath(NewName);
|
||||||
|
|
||||||
if (Directory.Exists(oldName))
|
if (Directory.Exists(OldName))
|
||||||
{
|
{
|
||||||
Directory.Move(oldName, newName);
|
Directory.Move(OldName, NewName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -241,14 +241,14 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long RenameFile(string oldName, string newName)
|
public long RenameFile(string OldName, string NewName)
|
||||||
{
|
{
|
||||||
CheckIfDescendentOfRootPath(oldName);
|
CheckIfDescendentOfRootPath(OldName);
|
||||||
CheckIfDescendentOfRootPath(newName);
|
CheckIfDescendentOfRootPath(NewName);
|
||||||
|
|
||||||
if (File.Exists(oldName))
|
if (File.Exists(OldName))
|
||||||
{
|
{
|
||||||
File.Move(oldName, newName);
|
File.Move(OldName, NewName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -258,24 +258,24 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CheckIfDescendentOfRootPath(string path)
|
public void CheckIfDescendentOfRootPath(string Path)
|
||||||
{
|
{
|
||||||
DirectoryInfo pathInfo = new DirectoryInfo(path);
|
DirectoryInfo PathInfo = new DirectoryInfo(Path);
|
||||||
DirectoryInfo rootInfo = new DirectoryInfo(_rootPath);
|
DirectoryInfo RootInfo = new DirectoryInfo(RootPath);
|
||||||
|
|
||||||
while (pathInfo.Parent != null)
|
while (PathInfo.Parent != null)
|
||||||
{
|
{
|
||||||
if (pathInfo.Parent.FullName == rootInfo.FullName)
|
if (PathInfo.Parent.FullName == RootInfo.FullName)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pathInfo = pathInfo.Parent;
|
PathInfo = PathInfo.Parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException($"Path {path} is not a child directory of {_rootPath}");
|
throw new InvalidOperationException($"Path {Path} is not a child directory of {RootPath}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,41 @@
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.HOS.Services.FspSrv;
|
using Ryujinx.HLE.HOS.Services.FspSrv;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.FileSystem
|
namespace Ryujinx.HLE.FileSystem
|
||||||
{
|
{
|
||||||
interface IFileSystemProvider
|
interface IFileSystemProvider
|
||||||
{
|
{
|
||||||
long CreateFile(string name, long size);
|
long CreateFile(string Name, long Size);
|
||||||
|
|
||||||
long CreateDirectory(string name);
|
long CreateDirectory(string Name);
|
||||||
|
|
||||||
long RenameFile(string oldName, string newName);
|
long RenameFile(string OldName, string NewName);
|
||||||
|
|
||||||
long RenameDirectory(string oldName, string newName);
|
long RenameDirectory(string OldName, string NewName);
|
||||||
|
|
||||||
DirectoryEntry[] GetEntries(string path);
|
DirectoryEntry[] GetEntries(string Path);
|
||||||
|
|
||||||
DirectoryEntry[] GetDirectories(string path);
|
DirectoryEntry[] GetDirectories(string Path);
|
||||||
|
|
||||||
DirectoryEntry[] GetFiles(string path);
|
DirectoryEntry[] GetFiles(string Path);
|
||||||
|
|
||||||
long DeleteFile(string name);
|
long DeleteFile(string Name);
|
||||||
|
|
||||||
long DeleteDirectory(string name, bool recursive);
|
long DeleteDirectory(string Name, bool Recursive);
|
||||||
|
|
||||||
bool FileExists(string name);
|
bool FileExists(string Name);
|
||||||
|
|
||||||
bool DirectoryExists(string name);
|
bool DirectoryExists(string Name);
|
||||||
|
|
||||||
long OpenFile(string name, out IFile fileInterface);
|
long OpenFile(string Name, out IFile FileInterface);
|
||||||
|
|
||||||
long OpenDirectory(string name, int filterFlags, out IDirectory directoryInterface);
|
long OpenDirectory(string Name, int FilterFlags, out IDirectory DirectoryInterface);
|
||||||
|
|
||||||
string GetFullPath(string name);
|
string GetFullPath(string Name);
|
||||||
|
|
||||||
long GetFreeSpace(ServiceCtx context);
|
long GetFreeSpace(ServiceCtx Context);
|
||||||
|
|
||||||
long GetTotalSpace(ServiceCtx context);
|
long GetTotalSpace(ServiceCtx Context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,98 +12,98 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
{
|
{
|
||||||
class PFsProvider : IFileSystemProvider
|
class PFsProvider : IFileSystemProvider
|
||||||
{
|
{
|
||||||
private Pfs _pfs;
|
private Pfs Pfs;
|
||||||
|
|
||||||
public PFsProvider(Pfs pfs)
|
public PFsProvider(Pfs Pfs)
|
||||||
{
|
{
|
||||||
_pfs = pfs;
|
this.Pfs = Pfs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long CreateDirectory(string name)
|
public long CreateDirectory(string Name)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long CreateFile(string name, long size)
|
public long CreateFile(string Name, long Size)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long DeleteDirectory(string name, bool recursive)
|
public long DeleteDirectory(string Name, bool Recursive)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long DeleteFile(string name)
|
public long DeleteFile(string Name)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DirectoryEntry[] GetDirectories(string path)
|
public DirectoryEntry[] GetDirectories(string Path)
|
||||||
{
|
{
|
||||||
return new DirectoryEntry[0];
|
return new DirectoryEntry[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public DirectoryEntry[] GetEntries(string path)
|
public DirectoryEntry[] GetEntries(string Path)
|
||||||
{
|
{
|
||||||
List<DirectoryEntry> entries = new List<DirectoryEntry>();
|
List<DirectoryEntry> Entries = new List<DirectoryEntry>();
|
||||||
|
|
||||||
foreach (PfsFileEntry file in _pfs.Files)
|
foreach (PfsFileEntry File in Pfs.Files)
|
||||||
{
|
{
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(file.Name, DirectoryEntryType.File, file.Size);
|
DirectoryEntry DirectoryEntry = new DirectoryEntry(File.Name, DirectoryEntryType.File, File.Size);
|
||||||
|
|
||||||
entries.Add(directoryEntry);
|
Entries.Add(DirectoryEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries.ToArray();
|
return Entries.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DirectoryEntry[] GetFiles(string path)
|
public DirectoryEntry[] GetFiles(string Path)
|
||||||
{
|
{
|
||||||
List<DirectoryEntry> entries = new List<DirectoryEntry>();
|
List<DirectoryEntry> Entries = new List<DirectoryEntry>();
|
||||||
|
|
||||||
foreach (PfsFileEntry file in _pfs.Files)
|
foreach (PfsFileEntry File in Pfs.Files)
|
||||||
{
|
{
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(file.Name, DirectoryEntryType.File, file.Size);
|
DirectoryEntry DirectoryEntry = new DirectoryEntry(File.Name, DirectoryEntryType.File, File.Size);
|
||||||
|
|
||||||
entries.Add(directoryEntry);
|
Entries.Add(DirectoryEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries.ToArray();
|
return Entries.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetFreeSpace(ServiceCtx context)
|
public long GetFreeSpace(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetFullPath(string name)
|
public string GetFullPath(string Name)
|
||||||
{
|
{
|
||||||
return name;
|
return Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetTotalSpace(ServiceCtx context)
|
public long GetTotalSpace(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
return _pfs.Files.Sum(x => x.Size);
|
return Pfs.Files.Sum(x => x.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DirectoryExists(string name)
|
public bool DirectoryExists(string Name)
|
||||||
{
|
{
|
||||||
return name == "/";
|
return Name == "/" ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool FileExists(string name)
|
public bool FileExists(string Name)
|
||||||
{
|
{
|
||||||
name = name.TrimStart('/');
|
Name = Name.TrimStart('/');
|
||||||
|
|
||||||
return _pfs.FileExists(name);
|
return Pfs.FileExists(Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long OpenDirectory(string name, int filterFlags, out IDirectory directoryInterface)
|
public long OpenDirectory(string Name, int FilterFlags, out IDirectory DirectoryInterface)
|
||||||
{
|
{
|
||||||
if (name == "/")
|
if (Name == "/")
|
||||||
{
|
{
|
||||||
directoryInterface = new IDirectory(name, filterFlags, this);
|
DirectoryInterface = new IDirectory(Name, FilterFlags, this);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -111,34 +111,34 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long OpenFile(string name, out IFile fileInterface)
|
public long OpenFile(string Name, out IFile FileInterface)
|
||||||
{
|
{
|
||||||
name = name.TrimStart('/');
|
Name = Name.TrimStart('/');
|
||||||
|
|
||||||
if (_pfs.FileExists(name))
|
if (Pfs.FileExists(Name))
|
||||||
{
|
{
|
||||||
Stream stream = _pfs.OpenFile(name);
|
Stream Stream = Pfs.OpenFile(Name);
|
||||||
fileInterface = new IFile(stream, name);
|
FileInterface = new IFile(Stream, Name);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fileInterface = null;
|
FileInterface = null;
|
||||||
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long RenameDirectory(string oldName, string newName)
|
public long RenameDirectory(string OldName, string NewName)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long RenameFile(string oldName, string newName)
|
public long RenameFile(string OldName, string NewName)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CheckIfOutsideBasePath(string path)
|
public void CheckIfOutsideBasePath(string Path)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,150 +12,150 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
{
|
{
|
||||||
class RomFsProvider : IFileSystemProvider
|
class RomFsProvider : IFileSystemProvider
|
||||||
{
|
{
|
||||||
private Romfs _romFs;
|
private Romfs RomFs;
|
||||||
|
|
||||||
public RomFsProvider(Stream storageStream)
|
public RomFsProvider(Stream StorageStream)
|
||||||
{
|
{
|
||||||
_romFs = new Romfs(storageStream);
|
RomFs = new Romfs(StorageStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long CreateDirectory(string name)
|
public long CreateDirectory(string Name)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long CreateFile(string name, long size)
|
public long CreateFile(string Name, long Size)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long DeleteDirectory(string name, bool recursive)
|
public long DeleteDirectory(string Name, bool Recursive)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long DeleteFile(string name)
|
public long DeleteFile(string Name)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DirectoryEntry[] GetDirectories(string path)
|
public DirectoryEntry[] GetDirectories(string Path)
|
||||||
{
|
{
|
||||||
List<DirectoryEntry> directories = new List<DirectoryEntry>();
|
List<DirectoryEntry> Directories = new List<DirectoryEntry>();
|
||||||
|
|
||||||
foreach(RomfsDir directory in _romFs.Directories)
|
foreach(RomfsDir Directory in RomFs.Directories)
|
||||||
{
|
{
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(directory.Name, DirectoryEntryType.Directory);
|
DirectoryEntry DirectoryEntry = new DirectoryEntry(Directory.Name, DirectoryEntryType.Directory);
|
||||||
|
|
||||||
directories.Add(directoryEntry);
|
Directories.Add(DirectoryEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
return directories.ToArray();
|
return Directories.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DirectoryEntry[] GetEntries(string path)
|
public DirectoryEntry[] GetEntries(string Path)
|
||||||
{
|
{
|
||||||
List<DirectoryEntry> entries = new List<DirectoryEntry>();
|
List<DirectoryEntry> Entries = new List<DirectoryEntry>();
|
||||||
|
|
||||||
foreach (RomfsDir directory in _romFs.Directories)
|
foreach (RomfsDir Directory in RomFs.Directories)
|
||||||
{
|
{
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(directory.Name, DirectoryEntryType.Directory);
|
DirectoryEntry DirectoryEntry = new DirectoryEntry(Directory.Name, DirectoryEntryType.Directory);
|
||||||
|
|
||||||
entries.Add(directoryEntry);
|
Entries.Add(DirectoryEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (RomfsFile file in _romFs.Files)
|
foreach (RomfsFile File in RomFs.Files)
|
||||||
{
|
{
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(file.Name, DirectoryEntryType.File, file.DataLength);
|
DirectoryEntry DirectoryEntry = new DirectoryEntry(File.Name, DirectoryEntryType.File, File.DataLength);
|
||||||
|
|
||||||
entries.Add(directoryEntry);
|
Entries.Add(DirectoryEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries.ToArray();
|
return Entries.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DirectoryEntry[] GetFiles(string path)
|
public DirectoryEntry[] GetFiles(string Path)
|
||||||
{
|
{
|
||||||
List<DirectoryEntry> files = new List<DirectoryEntry>();
|
List<DirectoryEntry> Files = new List<DirectoryEntry>();
|
||||||
|
|
||||||
foreach (RomfsFile file in _romFs.Files)
|
foreach (RomfsFile File in RomFs.Files)
|
||||||
{
|
{
|
||||||
DirectoryEntry directoryEntry = new DirectoryEntry(file.Name, DirectoryEntryType.File, file.DataLength);
|
DirectoryEntry DirectoryEntry = new DirectoryEntry(File.Name, DirectoryEntryType.File, File.DataLength);
|
||||||
|
|
||||||
files.Add(directoryEntry);
|
Files.Add(DirectoryEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
return files.ToArray();
|
return Files.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetFreeSpace(ServiceCtx context)
|
public long GetFreeSpace(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetFullPath(string name)
|
public string GetFullPath(string Name)
|
||||||
{
|
{
|
||||||
return name;
|
return Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetTotalSpace(ServiceCtx context)
|
public long GetTotalSpace(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
return _romFs.Files.Sum(x => x.DataLength);
|
return RomFs.Files.Sum(x => x.DataLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DirectoryExists(string name)
|
public bool DirectoryExists(string Name)
|
||||||
{
|
{
|
||||||
return _romFs.Directories.Exists(x=>x.Name == name);
|
return RomFs.Directories.Exists(x=>x.Name == Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool FileExists(string name)
|
public bool FileExists(string Name)
|
||||||
{
|
{
|
||||||
return _romFs.FileExists(name);
|
return RomFs.FileExists(Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long OpenDirectory(string name, int filterFlags, out IDirectory directoryInterface)
|
public long OpenDirectory(string Name, int FilterFlags, out IDirectory DirectoryInterface)
|
||||||
{
|
{
|
||||||
RomfsDir directory = _romFs.Directories.Find(x => x.Name == name);
|
RomfsDir Directory = RomFs.Directories.Find(x => x.Name == Name);
|
||||||
|
|
||||||
if (directory != null)
|
if (Directory != null)
|
||||||
{
|
{
|
||||||
directoryInterface = new IDirectory(name, filterFlags, this);
|
DirectoryInterface = new IDirectory(Name, FilterFlags, this);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
directoryInterface = null;
|
DirectoryInterface = null;
|
||||||
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long OpenFile(string name, out IFile fileInterface)
|
public long OpenFile(string Name, out IFile FileInterface)
|
||||||
{
|
{
|
||||||
if (_romFs.FileExists(name))
|
if (RomFs.FileExists(Name))
|
||||||
{
|
{
|
||||||
Stream stream = _romFs.OpenFile(name);
|
Stream Stream = RomFs.OpenFile(Name);
|
||||||
|
|
||||||
fileInterface = new IFile(stream, name);
|
FileInterface = new IFile(Stream, Name);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fileInterface = null;
|
FileInterface = null;
|
||||||
|
|
||||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long RenameDirectory(string oldName, string newName)
|
public long RenameDirectory(string OldName, string NewName)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long RenameFile(string oldName, string newName)
|
public long RenameFile(string OldName, string NewName)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CheckIfOutsideBasePath(string path)
|
public void CheckIfOutsideBasePath(string Path)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,39 +7,39 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
{
|
{
|
||||||
static class SaveHelper
|
static class SaveHelper
|
||||||
{
|
{
|
||||||
public static string GetSavePath(SaveInfo saveMetaData, ServiceCtx context)
|
public static string GetSavePath(SaveInfo SaveMetaData, ServiceCtx Context)
|
||||||
{
|
{
|
||||||
string baseSavePath = NandPath;
|
string BaseSavePath = NandPath;
|
||||||
long currentTitleId = saveMetaData.TitleId;
|
long CurrentTitleId = SaveMetaData.TitleId;
|
||||||
|
|
||||||
switch (saveMetaData.SaveSpaceId)
|
switch (SaveMetaData.SaveSpaceId)
|
||||||
{
|
{
|
||||||
case SaveSpaceId.NandUser:
|
case SaveSpaceId.NandUser:
|
||||||
baseSavePath = UserNandPath;
|
BaseSavePath = UserNandPath;
|
||||||
break;
|
break;
|
||||||
case SaveSpaceId.NandSystem:
|
case SaveSpaceId.NandSystem:
|
||||||
baseSavePath = SystemNandPath;
|
BaseSavePath = SystemNandPath;
|
||||||
break;
|
break;
|
||||||
case SaveSpaceId.SdCard:
|
case SaveSpaceId.SdCard:
|
||||||
baseSavePath = Path.Combine(SdCardPath, "Nintendo");
|
BaseSavePath = Path.Combine(SdCardPath, "Nintendo");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
baseSavePath = Path.Combine(baseSavePath, "save");
|
BaseSavePath = Path.Combine(BaseSavePath, "save");
|
||||||
|
|
||||||
if (saveMetaData.TitleId == 0 && saveMetaData.SaveDataType == SaveDataType.SaveData)
|
if (SaveMetaData.TitleId == 0 && SaveMetaData.SaveDataType == SaveDataType.SaveData)
|
||||||
{
|
{
|
||||||
currentTitleId = context.Process.TitleId;
|
CurrentTitleId = Context.Process.TitleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
string saveAccount = saveMetaData.UserId.IsZero() ? "savecommon" : saveMetaData.UserId.ToString();
|
string SaveAccount = SaveMetaData.UserId.IsZero() ? "savecommon" : SaveMetaData.UserId.ToString();
|
||||||
|
|
||||||
string savePath = Path.Combine(baseSavePath,
|
string SavePath = Path.Combine(BaseSavePath,
|
||||||
saveMetaData.SaveId.ToString("x16"),
|
SaveMetaData.SaveId.ToString("x16"),
|
||||||
saveAccount,
|
SaveAccount,
|
||||||
saveMetaData.SaveDataType == SaveDataType.SaveData ? currentTitleId.ToString("x16") : string.Empty);
|
SaveMetaData.SaveDataType == SaveDataType.SaveData ? CurrentTitleId.ToString("x16") : string.Empty);
|
||||||
|
|
||||||
return savePath;
|
return SavePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,25 +4,25 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
{
|
{
|
||||||
struct SaveInfo
|
struct SaveInfo
|
||||||
{
|
{
|
||||||
public long TitleId { get; }
|
public long TitleId { get; private set; }
|
||||||
public long SaveId { get; }
|
public long SaveId { get; private set; }
|
||||||
public UInt128 UserId { get; }
|
public UInt128 UserId { get; private set; }
|
||||||
|
|
||||||
public SaveDataType SaveDataType { get; }
|
public SaveDataType SaveDataType { get; private set; }
|
||||||
public SaveSpaceId SaveSpaceId { get; }
|
public SaveSpaceId SaveSpaceId { get; private set; }
|
||||||
|
|
||||||
public SaveInfo(
|
public SaveInfo(
|
||||||
long titleId,
|
long TitleId,
|
||||||
long saveId,
|
long SaveId,
|
||||||
SaveDataType saveDataType,
|
SaveDataType SaveDataType,
|
||||||
UInt128 userId,
|
UInt128 UserId,
|
||||||
SaveSpaceId saveSpaceId)
|
SaveSpaceId SaveSpaceId)
|
||||||
{
|
{
|
||||||
TitleId = titleId;
|
this.TitleId = TitleId;
|
||||||
UserId = userId;
|
this.UserId = UserId;
|
||||||
SaveId = saveId;
|
this.SaveId = SaveId;
|
||||||
SaveDataType = saveDataType;
|
this.SaveDataType = SaveDataType;
|
||||||
SaveSpaceId = saveSpaceId;
|
this.SaveSpaceId = SaveSpaceId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,40 +18,40 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
|
|
||||||
public Stream RomFs { get; private set; }
|
public Stream RomFs { get; private set; }
|
||||||
|
|
||||||
public void LoadRomFs(string fileName)
|
public void LoadRomFs(string FileName)
|
||||||
{
|
{
|
||||||
RomFs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
|
RomFs = new FileStream(FileName, FileMode.Open, FileAccess.Read);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetRomFs(Stream romfsStream)
|
public void SetRomFs(Stream RomfsStream)
|
||||||
{
|
{
|
||||||
RomFs?.Close();
|
RomFs?.Close();
|
||||||
RomFs = romfsStream;
|
RomFs = RomfsStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetFullPath(string basePath, string fileName)
|
public string GetFullPath(string BasePath, string FileName)
|
||||||
{
|
{
|
||||||
if (fileName.StartsWith("//"))
|
if (FileName.StartsWith("//"))
|
||||||
{
|
{
|
||||||
fileName = fileName.Substring(2);
|
FileName = FileName.Substring(2);
|
||||||
}
|
}
|
||||||
else if (fileName.StartsWith('/'))
|
else if (FileName.StartsWith('/'))
|
||||||
{
|
{
|
||||||
fileName = fileName.Substring(1);
|
FileName = FileName.Substring(1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
string fullPath = Path.GetFullPath(Path.Combine(basePath, fileName));
|
string FullPath = Path.GetFullPath(Path.Combine(BasePath, FileName));
|
||||||
|
|
||||||
if (!fullPath.StartsWith(GetBasePath()))
|
if (!FullPath.StartsWith(GetBasePath()))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fullPath;
|
return FullPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetSdCardPath() => MakeDirAndGetFullPath(SdCardPath);
|
public string GetSdCardPath() => MakeDirAndGetFullPath(SdCardPath);
|
||||||
|
@ -60,84 +60,84 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
|
|
||||||
public string GetSystemPath() => MakeDirAndGetFullPath(SystemPath);
|
public string GetSystemPath() => MakeDirAndGetFullPath(SystemPath);
|
||||||
|
|
||||||
public string GetGameSavePath(SaveInfo save, ServiceCtx context)
|
public string GetGameSavePath(SaveInfo Save, ServiceCtx Context)
|
||||||
{
|
{
|
||||||
return MakeDirAndGetFullPath(SaveHelper.GetSavePath(save, context));
|
return MakeDirAndGetFullPath(SaveHelper.GetSavePath(Save, Context));
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetFullPartitionPath(string partitionPath)
|
public string GetFullPartitionPath(string PartitionPath)
|
||||||
{
|
{
|
||||||
return MakeDirAndGetFullPath(partitionPath);
|
return MakeDirAndGetFullPath(PartitionPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SwitchPathToSystemPath(string switchPath)
|
public string SwitchPathToSystemPath(string SwitchPath)
|
||||||
{
|
{
|
||||||
string[] parts = switchPath.Split(":");
|
string[] Parts = SwitchPath.Split(":");
|
||||||
|
|
||||||
if (parts.Length != 2)
|
if (Parts.Length != 2)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetFullPath(MakeDirAndGetFullPath(parts[0]), parts[1]);
|
return GetFullPath(MakeDirAndGetFullPath(Parts[0]), Parts[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SystemPathToSwitchPath(string systemPath)
|
public string SystemPathToSwitchPath(string SystemPath)
|
||||||
{
|
{
|
||||||
string baseSystemPath = GetBasePath() + Path.DirectorySeparatorChar;
|
string BaseSystemPath = GetBasePath() + Path.DirectorySeparatorChar;
|
||||||
|
|
||||||
if (systemPath.StartsWith(baseSystemPath))
|
if (SystemPath.StartsWith(BaseSystemPath))
|
||||||
{
|
{
|
||||||
string rawPath = systemPath.Replace(baseSystemPath, "");
|
string RawPath = SystemPath.Replace(BaseSystemPath, "");
|
||||||
int firstSeparatorOffset = rawPath.IndexOf(Path.DirectorySeparatorChar);
|
int FirstSeparatorOffset = RawPath.IndexOf(Path.DirectorySeparatorChar);
|
||||||
|
|
||||||
if (firstSeparatorOffset == -1)
|
if (FirstSeparatorOffset == -1)
|
||||||
{
|
{
|
||||||
return $"{rawPath}:/";
|
return $"{RawPath}:/";
|
||||||
}
|
}
|
||||||
|
|
||||||
string basePath = rawPath.Substring(0, firstSeparatorOffset);
|
string BasePath = RawPath.Substring(0, FirstSeparatorOffset);
|
||||||
string fileName = rawPath.Substring(firstSeparatorOffset + 1);
|
string FileName = RawPath.Substring(FirstSeparatorOffset + 1);
|
||||||
|
|
||||||
return $"{basePath}:/{fileName}";
|
return $"{BasePath}:/{FileName}";
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string MakeDirAndGetFullPath(string dir)
|
private string MakeDirAndGetFullPath(string Dir)
|
||||||
{
|
{
|
||||||
// Handles Common Switch Content Paths
|
// Handles Common Switch Content Paths
|
||||||
switch (dir)
|
switch (Dir)
|
||||||
{
|
{
|
||||||
case ContentPath.SdCard:
|
case ContentPath.SdCard:
|
||||||
case "@Sdcard":
|
case "@Sdcard":
|
||||||
dir = SdCardPath;
|
Dir = SdCardPath;
|
||||||
break;
|
break;
|
||||||
case ContentPath.User:
|
case ContentPath.User:
|
||||||
dir = UserNandPath;
|
Dir = UserNandPath;
|
||||||
break;
|
break;
|
||||||
case ContentPath.System:
|
case ContentPath.System:
|
||||||
dir = SystemNandPath;
|
Dir = SystemNandPath;
|
||||||
break;
|
break;
|
||||||
case ContentPath.SdCardContent:
|
case ContentPath.SdCardContent:
|
||||||
dir = Path.Combine(SdCardPath, "Nintendo", "Contents");
|
Dir = Path.Combine(SdCardPath, "Nintendo", "Contents");
|
||||||
break;
|
break;
|
||||||
case ContentPath.UserContent:
|
case ContentPath.UserContent:
|
||||||
dir = Path.Combine(UserNandPath, "Contents");
|
Dir = Path.Combine(UserNandPath, "Contents");
|
||||||
break;
|
break;
|
||||||
case ContentPath.SystemContent:
|
case ContentPath.SystemContent:
|
||||||
dir = Path.Combine(SystemNandPath, "Contents");
|
Dir = Path.Combine(SystemNandPath, "Contents");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
string fullPath = Path.Combine(GetBasePath(), dir);
|
string FullPath = Path.Combine(GetBasePath(), Dir);
|
||||||
|
|
||||||
if (!Directory.Exists(fullPath))
|
if (!Directory.Exists(FullPath))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(fullPath);
|
Directory.CreateDirectory(FullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fullPath;
|
return FullPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DriveInfo GetDrive()
|
public DriveInfo GetDrive()
|
||||||
|
@ -147,9 +147,9 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
|
|
||||||
public string GetBasePath()
|
public string GetBasePath()
|
||||||
{
|
{
|
||||||
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
string AppDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||||
|
|
||||||
return Path.Combine(appDataPath, BasePath);
|
return Path.Combine(AppDataPath, BasePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
@ -4,22 +4,22 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class ArraySubscriptingExpression : BaseNode
|
public class ArraySubscriptingExpression : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _leftNode;
|
private BaseNode LeftNode;
|
||||||
private BaseNode _subscript;
|
private BaseNode Subscript;
|
||||||
|
|
||||||
public ArraySubscriptingExpression(BaseNode leftNode, BaseNode subscript) : base(NodeType.ArraySubscriptingExpression)
|
public ArraySubscriptingExpression(BaseNode LeftNode, BaseNode Subscript) : base(NodeType.ArraySubscriptingExpression)
|
||||||
{
|
{
|
||||||
_leftNode = leftNode;
|
this.LeftNode = LeftNode;
|
||||||
_subscript = subscript;
|
this.Subscript = Subscript;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
_leftNode.Print(writer);
|
LeftNode.Print(Writer);
|
||||||
writer.Write(")[");
|
Writer.Write(")[");
|
||||||
_subscript.Print(writer);
|
Subscript.Print(Writer);
|
||||||
writer.Write("]");
|
Writer.Write("]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,20 +4,20 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class ArrayType : BaseNode
|
public class ArrayType : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _base;
|
private BaseNode Base;
|
||||||
private BaseNode _dimensionExpression;
|
private BaseNode DimensionExpression;
|
||||||
private string _dimensionString;
|
private string DimensionString;
|
||||||
|
|
||||||
public ArrayType(BaseNode Base, BaseNode dimensionExpression = null) : base(NodeType.ArrayType)
|
public ArrayType(BaseNode Base, BaseNode DimensionExpression = null) : base(NodeType.ArrayType)
|
||||||
{
|
{
|
||||||
_base = Base;
|
this.Base = Base;
|
||||||
_dimensionExpression = dimensionExpression;
|
this.DimensionExpression = DimensionExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayType(BaseNode Base, string dimensionString) : base(NodeType.ArrayType)
|
public ArrayType(BaseNode Base, string DimensionString) : base(NodeType.ArrayType)
|
||||||
{
|
{
|
||||||
_base = Base;
|
this.Base = Base;
|
||||||
_dimensionString = dimensionString;
|
this.DimensionString = DimensionString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool HasRightPart()
|
public override bool HasRightPart()
|
||||||
|
@ -30,30 +30,30 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
_base.PrintLeft(writer);
|
Base.PrintLeft(Writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintRight(TextWriter writer)
|
public override void PrintRight(TextWriter Writer)
|
||||||
{
|
{
|
||||||
// FIXME: detect if previous char was a ].
|
// FIXME: detect if previous char was a ].
|
||||||
writer.Write(" ");
|
Writer.Write(" ");
|
||||||
|
|
||||||
writer.Write("[");
|
Writer.Write("[");
|
||||||
|
|
||||||
if (_dimensionString != null)
|
if (DimensionString != null)
|
||||||
{
|
{
|
||||||
writer.Write(_dimensionString);
|
Writer.Write(DimensionString);
|
||||||
}
|
}
|
||||||
else if (_dimensionExpression != null)
|
else if (DimensionExpression != null)
|
||||||
{
|
{
|
||||||
_dimensionExpression.Print(writer);
|
DimensionExpression.Print(Writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write("]");
|
Writer.Write("]");
|
||||||
|
|
||||||
_base.PrintRight(writer);
|
Base.PrintRight(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public enum NodeType
|
public enum NodeType
|
||||||
{
|
{
|
||||||
CvQualifierType,
|
CVQualifierType,
|
||||||
SimpleReferenceType,
|
SimpleReferenceType,
|
||||||
NameType,
|
NameType,
|
||||||
EncodedFunction,
|
EncodedFunction,
|
||||||
|
@ -62,22 +62,22 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public NodeType Type { get; protected set; }
|
public NodeType Type { get; protected set; }
|
||||||
|
|
||||||
public BaseNode(NodeType type)
|
public BaseNode(NodeType Type)
|
||||||
{
|
{
|
||||||
Type = type;
|
this.Type = Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Print(TextWriter writer)
|
public virtual void Print(TextWriter Writer)
|
||||||
{
|
{
|
||||||
PrintLeft(writer);
|
PrintLeft(Writer);
|
||||||
|
|
||||||
if (HasRightPart())
|
if (HasRightPart())
|
||||||
{
|
{
|
||||||
PrintRight(writer);
|
PrintRight(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void PrintLeft(TextWriter writer);
|
public abstract void PrintLeft(TextWriter Writer);
|
||||||
|
|
||||||
public virtual bool HasRightPart()
|
public virtual bool HasRightPart()
|
||||||
{
|
{
|
||||||
|
@ -99,15 +99,15 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void PrintRight(TextWriter writer) {}
|
public virtual void PrintRight(TextWriter Writer) {}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
StringWriter writer = new StringWriter();
|
StringWriter Writer = new StringWriter();
|
||||||
|
|
||||||
Print(writer);
|
Print(Writer);
|
||||||
|
|
||||||
return writer.ToString();
|
return Writer.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,37 +4,37 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class BinaryExpression : BaseNode
|
public class BinaryExpression : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _leftPart;
|
private BaseNode LeftPart;
|
||||||
private string _name;
|
private string Name;
|
||||||
private BaseNode _rightPart;
|
private BaseNode RightPart;
|
||||||
|
|
||||||
public BinaryExpression(BaseNode leftPart, string name, BaseNode rightPart) : base(NodeType.BinaryExpression)
|
public BinaryExpression(BaseNode LeftPart, string Name, BaseNode RightPart) : base(NodeType.BinaryExpression)
|
||||||
{
|
{
|
||||||
_leftPart = leftPart;
|
this.LeftPart = LeftPart;
|
||||||
_name = name;
|
this.Name = Name;
|
||||||
_rightPart = rightPart;
|
this.RightPart = RightPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if (_name.Equals(">"))
|
if (Name.Equals(">"))
|
||||||
{
|
{
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
_leftPart.Print(writer);
|
LeftPart.Print(Writer);
|
||||||
writer.Write(") ");
|
Writer.Write(") ");
|
||||||
|
|
||||||
writer.Write(_name);
|
Writer.Write(Name);
|
||||||
|
|
||||||
writer.Write(" (");
|
Writer.Write(" (");
|
||||||
_rightPart.Print(writer);
|
RightPart.Print(Writer);
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
|
|
||||||
if (_name.Equals(">"))
|
if (Name.Equals(">"))
|
||||||
{
|
{
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,37 +4,37 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class BracedExpression : BaseNode
|
public class BracedExpression : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _element;
|
private BaseNode Element;
|
||||||
private BaseNode _expression;
|
private BaseNode Expression;
|
||||||
private bool _isArrayExpression;
|
private bool IsArrayExpression;
|
||||||
|
|
||||||
public BracedExpression(BaseNode element, BaseNode expression, bool isArrayExpression) : base(NodeType.BracedExpression)
|
public BracedExpression(BaseNode Element, BaseNode Expression, bool IsArrayExpression) : base(NodeType.BracedExpression)
|
||||||
{
|
{
|
||||||
_element = element;
|
this.Element = Element;
|
||||||
_expression = expression;
|
this.Expression = Expression;
|
||||||
_isArrayExpression = isArrayExpression;
|
this.IsArrayExpression = IsArrayExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if (_isArrayExpression)
|
if (IsArrayExpression)
|
||||||
{
|
{
|
||||||
writer.Write("[");
|
Writer.Write("[");
|
||||||
_element.Print(writer);
|
Element.Print(Writer);
|
||||||
writer.Write("]");
|
Writer.Write("]");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
writer.Write(".");
|
Writer.Write(".");
|
||||||
_element.Print(writer);
|
Element.Print(Writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_expression.GetType().Equals(NodeType.BracedExpression) || !_expression.GetType().Equals(NodeType.BracedRangeExpression))
|
if (!Expression.GetType().Equals(NodeType.BracedExpression) || !Expression.GetType().Equals(NodeType.BracedRangeExpression))
|
||||||
{
|
{
|
||||||
writer.Write(" = ");
|
Writer.Write(" = ");
|
||||||
}
|
}
|
||||||
|
|
||||||
_expression.Print(writer);
|
Expression.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,31 +4,31 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class BracedRangeExpression : BaseNode
|
public class BracedRangeExpression : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _firstNode;
|
private BaseNode FirstNode;
|
||||||
private BaseNode _lastNode;
|
private BaseNode LastNode;
|
||||||
private BaseNode _expression;
|
private BaseNode Expression;
|
||||||
|
|
||||||
public BracedRangeExpression(BaseNode firstNode, BaseNode lastNode, BaseNode expression) : base(NodeType.BracedRangeExpression)
|
public BracedRangeExpression(BaseNode FirstNode, BaseNode LastNode, BaseNode Expression) : base(NodeType.BracedRangeExpression)
|
||||||
{
|
{
|
||||||
_firstNode = firstNode;
|
this.FirstNode = FirstNode;
|
||||||
_lastNode = lastNode;
|
this.LastNode = LastNode;
|
||||||
_expression = expression;
|
this.Expression = Expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("[");
|
Writer.Write("[");
|
||||||
_firstNode.Print(writer);
|
FirstNode.Print(Writer);
|
||||||
writer.Write(" ... ");
|
Writer.Write(" ... ");
|
||||||
_lastNode.Print(writer);
|
LastNode.Print(Writer);
|
||||||
writer.Write("]");
|
Writer.Write("]");
|
||||||
|
|
||||||
if (!_expression.GetType().Equals(NodeType.BracedExpression) || !_expression.GetType().Equals(NodeType.BracedRangeExpression))
|
if (!Expression.GetType().Equals(NodeType.BracedExpression) || !Expression.GetType().Equals(NodeType.BracedRangeExpression))
|
||||||
{
|
{
|
||||||
writer.Write(" = ");
|
Writer.Write(" = ");
|
||||||
}
|
}
|
||||||
|
|
||||||
_expression.Print(writer);
|
Expression.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,20 +5,20 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class CallExpression : NodeArray
|
public class CallExpression : NodeArray
|
||||||
{
|
{
|
||||||
private BaseNode _callee;
|
private BaseNode Callee;
|
||||||
|
|
||||||
public CallExpression(BaseNode callee, List<BaseNode> nodes) : base(nodes, NodeType.CallExpression)
|
public CallExpression(BaseNode Callee, List<BaseNode> Nodes) : base(Nodes, NodeType.CallExpression)
|
||||||
{
|
{
|
||||||
_callee = callee;
|
this.Callee = Callee;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
_callee.Print(writer);
|
Callee.Print(Writer);
|
||||||
|
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
writer.Write(string.Join<BaseNode>(", ", Nodes.ToArray()));
|
Writer.Write(string.Join<BaseNode>(", ", Nodes.ToArray()));
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,25 +4,25 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class CastExpression : BaseNode
|
public class CastExpression : BaseNode
|
||||||
{
|
{
|
||||||
private string _kind;
|
private string Kind;
|
||||||
private BaseNode _to;
|
private BaseNode To;
|
||||||
private BaseNode _from;
|
private BaseNode From;
|
||||||
|
|
||||||
public CastExpression(string kind, BaseNode to, BaseNode from) : base(NodeType.CastExpression)
|
public CastExpression(string Kind, BaseNode To, BaseNode From) : base(NodeType.CastExpression)
|
||||||
{
|
{
|
||||||
_kind = kind;
|
this.Kind = Kind;
|
||||||
_to = to;
|
this.To = To;
|
||||||
_from = from;
|
this.From = From;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write(_kind);
|
Writer.Write(Kind);
|
||||||
writer.Write("<");
|
Writer.Write("<");
|
||||||
_to.PrintLeft(writer);
|
To.PrintLeft(Writer);
|
||||||
writer.Write(">(");
|
Writer.Write(">(");
|
||||||
_from.PrintLeft(writer);
|
From.PrintLeft(Writer);
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,26 +4,26 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class ConditionalExpression : BaseNode
|
public class ConditionalExpression : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _thenNode;
|
private BaseNode ThenNode;
|
||||||
private BaseNode _elseNode;
|
private BaseNode ElseNode;
|
||||||
private BaseNode _conditionNode;
|
private BaseNode ConditionNode;
|
||||||
|
|
||||||
public ConditionalExpression(BaseNode conditionNode, BaseNode thenNode, BaseNode elseNode) : base(NodeType.ConditionalExpression)
|
public ConditionalExpression(BaseNode ConditionNode, BaseNode ThenNode, BaseNode ElseNode) : base(NodeType.ConditionalExpression)
|
||||||
{
|
{
|
||||||
_thenNode = thenNode;
|
this.ThenNode = ThenNode;
|
||||||
_conditionNode = conditionNode;
|
this.ConditionNode = ConditionNode;
|
||||||
_elseNode = elseNode;
|
this.ElseNode = ElseNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
_conditionNode.Print(writer);
|
ConditionNode.Print(Writer);
|
||||||
writer.Write(") ? (");
|
Writer.Write(") ? (");
|
||||||
_thenNode.Print(writer);
|
ThenNode.Print(Writer);
|
||||||
writer.Write(") : (");
|
Writer.Write(") : (");
|
||||||
_elseNode.Print(writer);
|
ElseNode.Print(Writer);
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,21 +4,21 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class ConversionExpression : BaseNode
|
public class ConversionExpression : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _typeNode;
|
private BaseNode TypeNode;
|
||||||
private BaseNode _expressions;
|
private BaseNode Expressions;
|
||||||
|
|
||||||
public ConversionExpression(BaseNode typeNode, BaseNode expressions) : base(NodeType.ConversionExpression)
|
public ConversionExpression(BaseNode TypeNode, BaseNode Expressions) : base(NodeType.ConversionExpression)
|
||||||
{
|
{
|
||||||
_typeNode = typeNode;
|
this.TypeNode = TypeNode;
|
||||||
_expressions = expressions;
|
this.Expressions = Expressions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
_typeNode.Print(writer);
|
TypeNode.Print(Writer);
|
||||||
writer.Write(")(");
|
Writer.Write(")(");
|
||||||
_expressions.Print(writer);
|
Expressions.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,12 +4,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class ConversionOperatorType : ParentNode
|
public class ConversionOperatorType : ParentNode
|
||||||
{
|
{
|
||||||
public ConversionOperatorType(BaseNode child) : base(NodeType.ConversionOperatorType, child) { }
|
public ConversionOperatorType(BaseNode Child) : base(NodeType.ConversionOperatorType, Child) { }
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("operator ");
|
Writer.Write("operator ");
|
||||||
Child.Print(writer);
|
Child.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,21 +4,21 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class CtorDtorNameType : ParentNode
|
public class CtorDtorNameType : ParentNode
|
||||||
{
|
{
|
||||||
private bool _isDestructor;
|
private bool IsDestructor;
|
||||||
|
|
||||||
public CtorDtorNameType(BaseNode name, bool isDestructor) : base(NodeType.CtorDtorNameType, name)
|
public CtorDtorNameType(BaseNode Name, bool IsDestructor) : base(NodeType.CtorDtorNameType, Name)
|
||||||
{
|
{
|
||||||
_isDestructor = isDestructor;
|
this.IsDestructor = IsDestructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if (_isDestructor)
|
if (IsDestructor)
|
||||||
{
|
{
|
||||||
writer.Write("~");
|
Writer.Write("~");
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write(Child.GetName());
|
Writer.Write(Child.GetName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,21 +4,21 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class CtorVtableSpecialName : BaseNode
|
public class CtorVtableSpecialName : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _firstType;
|
private BaseNode FirstType;
|
||||||
private BaseNode _secondType;
|
private BaseNode SecondType;
|
||||||
|
|
||||||
public CtorVtableSpecialName(BaseNode firstType, BaseNode secondType) : base(NodeType.CtorVtableSpecialName)
|
public CtorVtableSpecialName(BaseNode FirstType, BaseNode SecondType) : base(NodeType.CtorVtableSpecialName)
|
||||||
{
|
{
|
||||||
_firstType = firstType;
|
this.FirstType = FirstType;
|
||||||
_secondType = secondType;
|
this.SecondType = SecondType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("construction vtable for ");
|
Writer.Write("construction vtable for ");
|
||||||
_firstType.Print(writer);
|
FirstType.Print(Writer);
|
||||||
writer.Write("-in-");
|
Writer.Write("-in-");
|
||||||
_secondType.Print(writer);
|
SecondType.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,30 +4,30 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class DeleteExpression : ParentNode
|
public class DeleteExpression : ParentNode
|
||||||
{
|
{
|
||||||
private bool _isGlobal;
|
private bool IsGlobal;
|
||||||
private bool _isArrayExpression;
|
private bool IsArrayExpression;
|
||||||
|
|
||||||
public DeleteExpression(BaseNode child, bool isGlobal, bool isArrayExpression) : base(NodeType.DeleteExpression, child)
|
public DeleteExpression(BaseNode Child, bool IsGlobal, bool IsArrayExpression) : base(NodeType.DeleteExpression, Child)
|
||||||
{
|
{
|
||||||
_isGlobal = isGlobal;
|
this.IsGlobal = IsGlobal;
|
||||||
_isArrayExpression = isArrayExpression;
|
this.IsArrayExpression = IsArrayExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if (_isGlobal)
|
if (IsGlobal)
|
||||||
{
|
{
|
||||||
writer.Write("::");
|
Writer.Write("::");
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write("delete");
|
Writer.Write("delete");
|
||||||
|
|
||||||
if (_isArrayExpression)
|
if (IsArrayExpression)
|
||||||
{
|
{
|
||||||
writer.Write("[] ");
|
Writer.Write("[] ");
|
||||||
}
|
}
|
||||||
|
|
||||||
Child.Print(writer);
|
Child.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,12 +4,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class DtorName : ParentNode
|
public class DtorName : ParentNode
|
||||||
{
|
{
|
||||||
public DtorName(BaseNode name) : base(NodeType.DtOrName, name) { }
|
public DtorName(BaseNode Name) : base(NodeType.DtOrName, Name) { }
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("~");
|
Writer.Write("~");
|
||||||
Child.PrintLeft(writer);
|
Child.PrintLeft(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,13 +4,13 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class DynamicExceptionSpec : ParentNode
|
public class DynamicExceptionSpec : ParentNode
|
||||||
{
|
{
|
||||||
public DynamicExceptionSpec(BaseNode child) : base(NodeType.DynamicExceptionSpec, child) { }
|
public DynamicExceptionSpec(BaseNode Child) : base(NodeType.DynamicExceptionSpec, Child) { }
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("throw(");
|
Writer.Write("throw(");
|
||||||
Child.Print(writer);
|
Child.Print(Writer);
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,18 +4,18 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class ElaboratedType : ParentNode
|
public class ElaboratedType : ParentNode
|
||||||
{
|
{
|
||||||
private string _elaborated;
|
private string Elaborated;
|
||||||
|
|
||||||
public ElaboratedType(string elaborated, BaseNode type) : base(NodeType.ElaboratedType, type)
|
public ElaboratedType(string Elaborated, BaseNode Type) : base(NodeType.ElaboratedType, Type)
|
||||||
{
|
{
|
||||||
_elaborated = elaborated;
|
this.Elaborated = Elaborated;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write(_elaborated);
|
Writer.Write(Elaborated);
|
||||||
writer.Write(" ");
|
Writer.Write(" ");
|
||||||
Child.Print(writer);
|
Child.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,22 +4,22 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class EnclosedExpression : BaseNode
|
public class EnclosedExpression : BaseNode
|
||||||
{
|
{
|
||||||
private string _prefix;
|
private string Prefix;
|
||||||
private BaseNode _expression;
|
private BaseNode Expression;
|
||||||
private string _postfix;
|
private string Postfix;
|
||||||
|
|
||||||
public EnclosedExpression(string prefix, BaseNode expression, string postfix) : base(NodeType.EnclosedExpression)
|
public EnclosedExpression(string Prefix, BaseNode Expression, string Postfix) : base(NodeType.EnclosedExpression)
|
||||||
{
|
{
|
||||||
_prefix = prefix;
|
this.Prefix = Prefix;
|
||||||
_expression = expression;
|
this.Expression = Expression;
|
||||||
_postfix = postfix;
|
this.Postfix = Postfix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write(_prefix);
|
Writer.Write(Prefix);
|
||||||
_expression.Print(writer);
|
Expression.Print(Writer);
|
||||||
writer.Write(_postfix);
|
Writer.Write(Postfix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,36 +4,36 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class EncodedFunction : BaseNode
|
public class EncodedFunction : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _name;
|
private BaseNode Name;
|
||||||
private BaseNode _params;
|
private BaseNode Params;
|
||||||
private BaseNode _cv;
|
private BaseNode CV;
|
||||||
private BaseNode _ref;
|
private BaseNode Ref;
|
||||||
private BaseNode _attrs;
|
private BaseNode Attrs;
|
||||||
private BaseNode _ret;
|
private BaseNode Ret;
|
||||||
|
|
||||||
public EncodedFunction(BaseNode name, BaseNode Params, BaseNode cv, BaseNode Ref, BaseNode attrs, BaseNode ret) : base(NodeType.NameType)
|
public EncodedFunction(BaseNode Name, BaseNode Params, BaseNode CV, BaseNode Ref, BaseNode Attrs, BaseNode Ret) : base(NodeType.NameType)
|
||||||
{
|
{
|
||||||
_name = name;
|
this.Name = Name;
|
||||||
_params = Params;
|
this.Params = Params;
|
||||||
_cv = cv;
|
this.CV = CV;
|
||||||
_ref = Ref;
|
this.Ref = Ref;
|
||||||
_attrs = attrs;
|
this.Attrs = Attrs;
|
||||||
_ret = ret;
|
this.Ret = Ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if (_ret != null)
|
if (Ret != null)
|
||||||
{
|
{
|
||||||
_ret.PrintLeft(writer);
|
Ret.PrintLeft(Writer);
|
||||||
|
|
||||||
if (!_ret.HasRightPart())
|
if (!Ret.HasRightPart())
|
||||||
{
|
{
|
||||||
writer.Write(" ");
|
Writer.Write(" ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_name.Print(writer);
|
Name.Print(Writer);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,35 +42,35 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintRight(TextWriter writer)
|
public override void PrintRight(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
|
|
||||||
if (_params != null)
|
if (Params != null)
|
||||||
{
|
{
|
||||||
_params.Print(writer);
|
Params.Print(Writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
|
|
||||||
if (_ret != null)
|
if (Ret != null)
|
||||||
{
|
{
|
||||||
_ret.PrintRight(writer);
|
Ret.PrintRight(Writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_cv != null)
|
if (CV != null)
|
||||||
{
|
{
|
||||||
_cv.Print(writer);
|
CV.Print(Writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_ref != null)
|
if (Ref != null)
|
||||||
{
|
{
|
||||||
_ref.Print(writer);
|
Ref.Print(Writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_attrs != null)
|
if (Attrs != null)
|
||||||
{
|
{
|
||||||
_attrs.Print(writer);
|
Attrs.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,45 +4,45 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class FoldExpression : BaseNode
|
public class FoldExpression : BaseNode
|
||||||
{
|
{
|
||||||
private bool _isLeftFold;
|
private bool IsLeftFold;
|
||||||
private string _operatorName;
|
private string OperatorName;
|
||||||
private BaseNode _expression;
|
private BaseNode Expression;
|
||||||
private BaseNode _initializer;
|
private BaseNode Initializer;
|
||||||
|
|
||||||
public FoldExpression(bool isLeftFold, string operatorName, BaseNode expression, BaseNode initializer) : base(NodeType.FunctionParameter)
|
public FoldExpression(bool IsLeftFold, string OperatorName, BaseNode Expression, BaseNode Initializer) : base(NodeType.FunctionParameter)
|
||||||
{
|
{
|
||||||
_isLeftFold = isLeftFold;
|
this.IsLeftFold = IsLeftFold;
|
||||||
_operatorName = operatorName;
|
this.OperatorName = OperatorName;
|
||||||
_expression = expression;
|
this.Expression = Expression;
|
||||||
_initializer = initializer;
|
this.Initializer = Initializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
|
|
||||||
if (_isLeftFold && _initializer != null)
|
if (IsLeftFold && Initializer != null)
|
||||||
{
|
{
|
||||||
_initializer.Print(writer);
|
Initializer.Print(Writer);
|
||||||
writer.Write(" ");
|
Writer.Write(" ");
|
||||||
writer.Write(_operatorName);
|
Writer.Write(OperatorName);
|
||||||
writer.Write(" ");
|
Writer.Write(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write(_isLeftFold ? "... " : " ");
|
Writer.Write(IsLeftFold ? "... " : " ");
|
||||||
writer.Write(_operatorName);
|
Writer.Write(OperatorName);
|
||||||
writer.Write(!_isLeftFold ? " ..." : " ");
|
Writer.Write(!IsLeftFold ? " ..." : " ");
|
||||||
_expression.Print(writer);
|
Expression.Print(Writer);
|
||||||
|
|
||||||
if (!_isLeftFold && _initializer != null)
|
if (!IsLeftFold && Initializer != null)
|
||||||
{
|
{
|
||||||
_initializer.Print(writer);
|
Initializer.Print(Writer);
|
||||||
writer.Write(" ");
|
Writer.Write(" ");
|
||||||
writer.Write(_operatorName);
|
Writer.Write(OperatorName);
|
||||||
writer.Write(" ");
|
Writer.Write(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,11 +6,11 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
// TODO: Compute inside the Demangler
|
// TODO: Compute inside the Demangler
|
||||||
public BaseNode Reference;
|
public BaseNode Reference;
|
||||||
private int _index;
|
private int Index;
|
||||||
|
|
||||||
public ForwardTemplateReference(int index) : base(NodeType.ForwardTemplateReference)
|
public ForwardTemplateReference(int Index) : base(NodeType.ForwardTemplateReference)
|
||||||
{
|
{
|
||||||
_index = index;
|
this.Index = Index;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string GetName()
|
public override string GetName()
|
||||||
|
@ -18,14 +18,14 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
return Reference.GetName();
|
return Reference.GetName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
Reference.PrintLeft(writer);
|
Reference.PrintLeft(Writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintRight(TextWriter writer)
|
public override void PrintRight(TextWriter Writer)
|
||||||
{
|
{
|
||||||
Reference.PrintRight(writer);
|
Reference.PrintRight(Writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool HasRightPart()
|
public override bool HasRightPart()
|
||||||
|
|
|
@ -4,20 +4,20 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class FunctionParameter : BaseNode
|
public class FunctionParameter : BaseNode
|
||||||
{
|
{
|
||||||
private string _number;
|
private string Number;
|
||||||
|
|
||||||
public FunctionParameter(string number) : base(NodeType.FunctionParameter)
|
public FunctionParameter(string Number) : base(NodeType.FunctionParameter)
|
||||||
{
|
{
|
||||||
_number = number;
|
this.Number = Number;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("fp ");
|
Writer.Write("fp ");
|
||||||
|
|
||||||
if (_number != null)
|
if (Number != null)
|
||||||
{
|
{
|
||||||
writer.Write(_number);
|
Writer.Write(Number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,47 +4,47 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class FunctionType : BaseNode
|
public class FunctionType : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _returnType;
|
private BaseNode ReturnType;
|
||||||
private BaseNode _params;
|
private BaseNode Params;
|
||||||
private BaseNode _cvQualifier;
|
private BaseNode CVQualifier;
|
||||||
private SimpleReferenceType _referenceQualifier;
|
private SimpleReferenceType ReferenceQualifier;
|
||||||
private BaseNode _exceptionSpec;
|
private BaseNode ExceptionSpec;
|
||||||
|
|
||||||
public FunctionType(BaseNode returnType, BaseNode Params, BaseNode cvQualifier, SimpleReferenceType referenceQualifier, BaseNode exceptionSpec) : base(NodeType.FunctionType)
|
public FunctionType(BaseNode ReturnType, BaseNode Params, BaseNode CVQualifier, SimpleReferenceType ReferenceQualifier, BaseNode ExceptionSpec) : base(NodeType.FunctionType)
|
||||||
{
|
{
|
||||||
_returnType = returnType;
|
this.ReturnType = ReturnType;
|
||||||
_params = Params;
|
this.Params = Params;
|
||||||
_cvQualifier = cvQualifier;
|
this.CVQualifier = CVQualifier;
|
||||||
_referenceQualifier = referenceQualifier;
|
this.ReferenceQualifier = ReferenceQualifier;
|
||||||
_exceptionSpec = exceptionSpec;
|
this.ExceptionSpec = ExceptionSpec;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
_returnType.PrintLeft(writer);
|
ReturnType.PrintLeft(Writer);
|
||||||
writer.Write(" ");
|
Writer.Write(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintRight(TextWriter writer)
|
public override void PrintRight(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
_params.Print(writer);
|
Params.Print(Writer);
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
|
|
||||||
_returnType.PrintRight(writer);
|
ReturnType.PrintRight(Writer);
|
||||||
|
|
||||||
_cvQualifier.Print(writer);
|
CVQualifier.Print(Writer);
|
||||||
|
|
||||||
if (_referenceQualifier.Qualifier != Reference.None)
|
if (ReferenceQualifier.Qualifier != Reference.None)
|
||||||
{
|
{
|
||||||
writer.Write(" ");
|
Writer.Write(" ");
|
||||||
_referenceQualifier.PrintQualifier(writer);
|
ReferenceQualifier.PrintQualifier(Writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_exceptionSpec != null)
|
if (ExceptionSpec != null)
|
||||||
{
|
{
|
||||||
writer.Write(" ");
|
Writer.Write(" ");
|
||||||
_exceptionSpec.Print(writer);
|
ExceptionSpec.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class GlobalQualifiedName : ParentNode
|
public class GlobalQualifiedName : ParentNode
|
||||||
{
|
{
|
||||||
public GlobalQualifiedName(BaseNode child) : base(NodeType.GlobalQualifiedName, child) { }
|
public GlobalQualifiedName(BaseNode Child) : base(NodeType.GlobalQualifiedName, Child) { }
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("::");
|
Writer.Write("::");
|
||||||
Child.Print(writer);
|
Child.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,25 +5,25 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class InitListExpression : BaseNode
|
public class InitListExpression : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _typeNode;
|
private BaseNode TypeNode;
|
||||||
private List<BaseNode> _nodes;
|
private List<BaseNode> Nodes;
|
||||||
|
|
||||||
public InitListExpression(BaseNode typeNode, List<BaseNode> nodes) : base(NodeType.InitListExpression)
|
public InitListExpression(BaseNode TypeNode, List<BaseNode> Nodes) : base(NodeType.InitListExpression)
|
||||||
{
|
{
|
||||||
_typeNode = typeNode;
|
this.TypeNode = TypeNode;
|
||||||
_nodes = nodes;
|
this.Nodes = Nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if (_typeNode != null)
|
if (TypeNode != null)
|
||||||
{
|
{
|
||||||
_typeNode.Print(writer);
|
TypeNode.Print(Writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write("{");
|
Writer.Write("{");
|
||||||
writer.Write(string.Join<BaseNode>(", ", _nodes.ToArray()));
|
Writer.Write(string.Join<BaseNode>(", ", Nodes.ToArray()));
|
||||||
writer.Write("}");
|
Writer.Write("}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,19 +4,19 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class IntegerCastExpression : ParentNode
|
public class IntegerCastExpression : ParentNode
|
||||||
{
|
{
|
||||||
private string _number;
|
private string Number;
|
||||||
|
|
||||||
public IntegerCastExpression(BaseNode type, string number) : base(NodeType.IntegerCastExpression, type)
|
public IntegerCastExpression(BaseNode Type, string Number) : base(NodeType.IntegerCastExpression, Type)
|
||||||
{
|
{
|
||||||
_number = number;
|
this.Number = Number;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
Child.Print(writer);
|
Child.Print(Writer);
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
writer.Write(_number);
|
Writer.Write(Number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,37 +4,37 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class IntegerLiteral : BaseNode
|
public class IntegerLiteral : BaseNode
|
||||||
{
|
{
|
||||||
private string _literalName;
|
private string LitteralName;
|
||||||
private string _literalValue;
|
private string LitteralValue;
|
||||||
|
|
||||||
public IntegerLiteral(string literalName, string literalValue) : base(NodeType.IntegerLiteral)
|
public IntegerLiteral(string LitteralName, string LitteralValue) : base(NodeType.IntegerLiteral)
|
||||||
{
|
{
|
||||||
_literalValue = literalValue;
|
this.LitteralValue = LitteralValue;
|
||||||
_literalName = literalName;
|
this.LitteralName = LitteralName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if (_literalName.Length > 3)
|
if (LitteralName.Length > 3)
|
||||||
{
|
{
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
writer.Write(_literalName);
|
Writer.Write(LitteralName);
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_literalValue[0] == 'n')
|
if (LitteralValue[0] == 'n')
|
||||||
{
|
{
|
||||||
writer.Write("-");
|
Writer.Write("-");
|
||||||
writer.Write(_literalValue.Substring(1));
|
Writer.Write(LitteralValue.Substring(1));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
writer.Write(_literalValue);
|
Writer.Write(LitteralValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_literalName.Length <= 3)
|
if (LitteralName.Length <= 3)
|
||||||
{
|
{
|
||||||
writer.Write(_literalName);
|
Writer.Write(LitteralName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,13 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class LiteralOperator : ParentNode
|
public class LiteralOperator : ParentNode
|
||||||
{
|
{
|
||||||
public LiteralOperator(BaseNode child) : base(NodeType.LiteralOperator, child) { }
|
public LiteralOperator(BaseNode Child) : base(NodeType.LiteralOperator, Child) { }
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("operator \"");
|
Writer.Write("operator \"");
|
||||||
Child.PrintLeft(writer);
|
Child.PrintLeft(Writer);
|
||||||
writer.Write("\"");
|
Writer.Write("\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,20 +4,20 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class LocalName : BaseNode
|
public class LocalName : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _encoding;
|
private BaseNode Encoding;
|
||||||
private BaseNode _entity;
|
private BaseNode Entity;
|
||||||
|
|
||||||
public LocalName(BaseNode encoding, BaseNode entity) : base(NodeType.LocalName)
|
public LocalName(BaseNode Encoding, BaseNode Entity) : base(NodeType.LocalName)
|
||||||
{
|
{
|
||||||
_encoding = encoding;
|
this.Encoding = Encoding;
|
||||||
_entity = entity;
|
this.Entity = Entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
_encoding.Print(writer);
|
Encoding.Print(Writer);
|
||||||
writer.Write("::");
|
Writer.Write("::");
|
||||||
_entity.Print(writer);
|
Entity.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,22 +4,22 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class MemberExpression : BaseNode
|
public class MemberExpression : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _leftNode;
|
private BaseNode LeftNode;
|
||||||
private string _kind;
|
private string Kind;
|
||||||
private BaseNode _rightNode;
|
private BaseNode RightNode;
|
||||||
|
|
||||||
public MemberExpression(BaseNode leftNode, string kind, BaseNode rightNode) : base(NodeType.MemberExpression)
|
public MemberExpression(BaseNode LeftNode, string Kind, BaseNode RightNode) : base(NodeType.MemberExpression)
|
||||||
{
|
{
|
||||||
_leftNode = leftNode;
|
this.LeftNode = LeftNode;
|
||||||
_kind = kind;
|
this.Kind = Kind;
|
||||||
_rightNode = rightNode;
|
this.RightNode = RightNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
_leftNode.Print(writer);
|
LeftNode.Print(Writer);
|
||||||
writer.Write(_kind);
|
Writer.Write(Kind);
|
||||||
_rightNode.Print(writer);
|
RightNode.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,26 +4,26 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class NameType : BaseNode
|
public class NameType : BaseNode
|
||||||
{
|
{
|
||||||
private string _nameValue;
|
private string NameValue;
|
||||||
|
|
||||||
public NameType(string nameValue, NodeType type) : base(type)
|
public NameType(string NameValue, NodeType Type) : base(Type)
|
||||||
{
|
{
|
||||||
_nameValue = nameValue;
|
this.NameValue = NameValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NameType(string nameValue) : base(NodeType.NameType)
|
public NameType(string NameValue) : base(NodeType.NameType)
|
||||||
{
|
{
|
||||||
_nameValue = nameValue;
|
this.NameValue = NameValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string GetName()
|
public override string GetName()
|
||||||
{
|
{
|
||||||
return _nameValue;
|
return NameValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write(_nameValue);
|
Writer.Write(NameValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,24 +4,24 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class NameTypeWithTemplateArguments : BaseNode
|
public class NameTypeWithTemplateArguments : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _prev;
|
private BaseNode Prev;
|
||||||
private BaseNode _templateArgument;
|
private BaseNode TemplateArgument;
|
||||||
|
|
||||||
public NameTypeWithTemplateArguments(BaseNode prev, BaseNode templateArgument) : base(NodeType.NameTypeWithTemplateArguments)
|
public NameTypeWithTemplateArguments(BaseNode Prev, BaseNode TemplateArgument) : base(NodeType.NameTypeWithTemplateArguments)
|
||||||
{
|
{
|
||||||
_prev = prev;
|
this.Prev = Prev;
|
||||||
_templateArgument = templateArgument;
|
this.TemplateArgument = TemplateArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string GetName()
|
public override string GetName()
|
||||||
{
|
{
|
||||||
return _prev.GetName();
|
return Prev.GetName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
_prev.Print(writer);
|
Prev.Print(Writer);
|
||||||
_templateArgument.Print(writer);
|
TemplateArgument.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,23 +4,23 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class NestedName : ParentNode
|
public class NestedName : ParentNode
|
||||||
{
|
{
|
||||||
private BaseNode _name;
|
private BaseNode Name;
|
||||||
|
|
||||||
public NestedName(BaseNode name, BaseNode type) : base(NodeType.NestedName, type)
|
public NestedName(BaseNode Name, BaseNode Type) : base(NodeType.NestedName, Type)
|
||||||
{
|
{
|
||||||
_name = name;
|
this.Name = Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string GetName()
|
public override string GetName()
|
||||||
{
|
{
|
||||||
return _name.GetName();
|
return Name.GetName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
Child.Print(writer);
|
Child.Print(Writer);
|
||||||
writer.Write("::");
|
Writer.Write("::");
|
||||||
_name.Print(writer);
|
Name.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,51 +4,51 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class NewExpression : BaseNode
|
public class NewExpression : BaseNode
|
||||||
{
|
{
|
||||||
private NodeArray _expressions;
|
private NodeArray Expressions;
|
||||||
private BaseNode _typeNode;
|
private BaseNode TypeNode;
|
||||||
private NodeArray _initializers;
|
private NodeArray Initializers;
|
||||||
|
|
||||||
private bool _isGlobal;
|
private bool IsGlobal;
|
||||||
private bool _isArrayExpression;
|
private bool IsArrayExpression;
|
||||||
|
|
||||||
public NewExpression(NodeArray expressions, BaseNode typeNode, NodeArray initializers, bool isGlobal, bool isArrayExpression) : base(NodeType.NewExpression)
|
public NewExpression(NodeArray Expressions, BaseNode TypeNode, NodeArray Initializers, bool IsGlobal, bool IsArrayExpression) : base(NodeType.NewExpression)
|
||||||
{
|
{
|
||||||
_expressions = expressions;
|
this.Expressions = Expressions;
|
||||||
_typeNode = typeNode;
|
this.TypeNode = TypeNode;
|
||||||
_initializers = initializers;
|
this.Initializers = Initializers;
|
||||||
|
|
||||||
_isGlobal = isGlobal;
|
this.IsGlobal = IsGlobal;
|
||||||
_isArrayExpression = isArrayExpression;
|
this.IsArrayExpression = IsArrayExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if (_isGlobal)
|
if (IsGlobal)
|
||||||
{
|
{
|
||||||
writer.Write("::operator ");
|
Writer.Write("::operator ");
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write("new ");
|
Writer.Write("new ");
|
||||||
|
|
||||||
if (_isArrayExpression)
|
if (IsArrayExpression)
|
||||||
{
|
{
|
||||||
writer.Write("[] ");
|
Writer.Write("[] ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_expressions.Nodes.Count != 0)
|
if (Expressions.Nodes.Count != 0)
|
||||||
{
|
{
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
_expressions.Print(writer);
|
Expressions.Print(Writer);
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
_typeNode.Print(writer);
|
TypeNode.Print(Writer);
|
||||||
|
|
||||||
if (_initializers.Nodes.Count != 0)
|
if (Initializers.Nodes.Count != 0)
|
||||||
{
|
{
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
_initializers.Print(writer);
|
Initializers.Print(Writer);
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,14 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public List<BaseNode> Nodes { get; protected set; }
|
public List<BaseNode> Nodes { get; protected set; }
|
||||||
|
|
||||||
public NodeArray(List<BaseNode> nodes) : base(NodeType.NodeArray)
|
public NodeArray(List<BaseNode> Nodes) : base(NodeType.NodeArray)
|
||||||
{
|
{
|
||||||
Nodes = nodes;
|
this.Nodes = Nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NodeArray(List<BaseNode> nodes, NodeType type) : base(type)
|
public NodeArray(List<BaseNode> Nodes, NodeType Type) : base(Type)
|
||||||
{
|
{
|
||||||
Nodes = nodes;
|
this.Nodes = Nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsArray()
|
public override bool IsArray()
|
||||||
|
@ -22,9 +22,9 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write(string.Join<BaseNode>(", ", Nodes.ToArray()));
|
Writer.Write(string.Join<BaseNode>(", ", Nodes.ToArray()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,13 +4,13 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class NoexceptSpec : ParentNode
|
public class NoexceptSpec : ParentNode
|
||||||
{
|
{
|
||||||
public NoexceptSpec(BaseNode child) : base(NodeType.NoexceptSpec, child) { }
|
public NoexceptSpec(BaseNode Child) : base(NodeType.NoexceptSpec, Child) { }
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("noexcept(");
|
Writer.Write("noexcept(");
|
||||||
Child.Print(writer);
|
Child.Print(Writer);
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,29 +5,29 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class PackedTemplateParameter : NodeArray
|
public class PackedTemplateParameter : NodeArray
|
||||||
{
|
{
|
||||||
public PackedTemplateParameter(List<BaseNode> nodes) : base(nodes, NodeType.PackedTemplateParameter) { }
|
public PackedTemplateParameter(List<BaseNode> Nodes) : base(Nodes, NodeType.PackedTemplateParameter) { }
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
foreach (BaseNode node in Nodes)
|
foreach (BaseNode Node in Nodes)
|
||||||
{
|
{
|
||||||
node.PrintLeft(writer);
|
Node.PrintLeft(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintRight(TextWriter writer)
|
public override void PrintRight(TextWriter Writer)
|
||||||
{
|
{
|
||||||
foreach (BaseNode node in Nodes)
|
foreach (BaseNode Node in Nodes)
|
||||||
{
|
{
|
||||||
node.PrintLeft(writer);
|
Node.PrintLeft(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool HasRightPart()
|
public override bool HasRightPart()
|
||||||
{
|
{
|
||||||
foreach (BaseNode node in Nodes)
|
foreach (BaseNode Node in Nodes)
|
||||||
{
|
{
|
||||||
if (node.HasRightPart())
|
if (Node.HasRightPart())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,20 +4,20 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class PackedTemplateParameterExpansion : ParentNode
|
public class PackedTemplateParameterExpansion : ParentNode
|
||||||
{
|
{
|
||||||
public PackedTemplateParameterExpansion(BaseNode child) : base(NodeType.PackedTemplateParameterExpansion, child) {}
|
public PackedTemplateParameterExpansion(BaseNode Child) : base(NodeType.PackedTemplateParameterExpansion, Child) {}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if (Child is PackedTemplateParameter)
|
if (Child is PackedTemplateParameter)
|
||||||
{
|
{
|
||||||
if (((PackedTemplateParameter)Child).Nodes.Count != 0)
|
if (((PackedTemplateParameter)Child).Nodes.Count != 0)
|
||||||
{
|
{
|
||||||
Child.Print(writer);
|
Child.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
writer.Write("...");
|
Writer.Write("...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,11 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public abstract class ParentNode : BaseNode
|
public abstract class ParentNode : BaseNode
|
||||||
{
|
{
|
||||||
public BaseNode Child { get; }
|
public BaseNode Child { get; private set; }
|
||||||
|
|
||||||
public ParentNode(NodeType type, BaseNode child) : base(type)
|
public ParentNode(NodeType Type, BaseNode Child) : base(Type)
|
||||||
{
|
{
|
||||||
Child = child;
|
this.Child = Child;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string GetName()
|
public override string GetName()
|
||||||
|
|
|
@ -4,42 +4,42 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class PointerType : BaseNode
|
public class PointerType : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _child;
|
private BaseNode Child;
|
||||||
|
|
||||||
public PointerType(BaseNode child) : base(NodeType.PointerType)
|
public PointerType(BaseNode Child) : base(NodeType.PointerType)
|
||||||
{
|
{
|
||||||
_child = child;
|
this.Child = Child;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool HasRightPart()
|
public override bool HasRightPart()
|
||||||
{
|
{
|
||||||
return _child.HasRightPart();
|
return Child.HasRightPart();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
_child.PrintLeft(writer);
|
Child.PrintLeft(Writer);
|
||||||
if (_child.IsArray())
|
if (Child.IsArray())
|
||||||
{
|
{
|
||||||
writer.Write(" ");
|
Writer.Write(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_child.IsArray() || _child.HasFunctions())
|
if (Child.IsArray() || Child.HasFunctions())
|
||||||
{
|
{
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write("*");
|
Writer.Write("*");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintRight(TextWriter writer)
|
public override void PrintRight(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if (_child.IsArray() || _child.HasFunctions())
|
if (Child.IsArray() || Child.HasFunctions())
|
||||||
{
|
{
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
_child.PrintRight(writer);
|
Child.PrintRight(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,19 +4,19 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class PostfixExpression : ParentNode
|
public class PostfixExpression : ParentNode
|
||||||
{
|
{
|
||||||
private string _operator;
|
private string Operator;
|
||||||
|
|
||||||
public PostfixExpression(BaseNode type, string Operator) : base(NodeType.PostfixExpression, type)
|
public PostfixExpression(BaseNode Type, string Operator) : base(NodeType.PostfixExpression, Type)
|
||||||
{
|
{
|
||||||
_operator = Operator;
|
this.Operator = Operator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
Child.Print(writer);
|
Child.Print(Writer);
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
writer.Write(_operator);
|
Writer.Write(Operator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,17 +4,17 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class PostfixQualifiedType : ParentNode
|
public class PostfixQualifiedType : ParentNode
|
||||||
{
|
{
|
||||||
private string _postfixQualifier;
|
private string PostfixQualifier;
|
||||||
|
|
||||||
public PostfixQualifiedType(string postfixQualifier, BaseNode type) : base(NodeType.PostfixQualifiedType, type)
|
public PostfixQualifiedType(string PostfixQualifier, BaseNode Type) : base(NodeType.PostfixQualifiedType, Type)
|
||||||
{
|
{
|
||||||
_postfixQualifier = postfixQualifier;
|
this.PostfixQualifier = PostfixQualifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
Child.Print(writer);
|
Child.Print(Writer);
|
||||||
writer.Write(_postfixQualifier);
|
Writer.Write(PostfixQualifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,19 +4,19 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class PrefixExpression : ParentNode
|
public class PrefixExpression : ParentNode
|
||||||
{
|
{
|
||||||
private string _prefix;
|
private string Prefix;
|
||||||
|
|
||||||
public PrefixExpression(string prefix, BaseNode child) : base(NodeType.PrefixExpression, child)
|
public PrefixExpression(string Prefix, BaseNode Child) : base(NodeType.PrefixExpression, Child)
|
||||||
{
|
{
|
||||||
_prefix = prefix;
|
this.Prefix = Prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write(_prefix);
|
Writer.Write(Prefix);
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
Child.Print(writer);
|
Child.Print(Writer);
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,20 +4,20 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class QualifiedName : BaseNode
|
public class QualifiedName : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _qualifier;
|
private BaseNode Qualifier;
|
||||||
private BaseNode _name;
|
private BaseNode Name;
|
||||||
|
|
||||||
public QualifiedName(BaseNode qualifier, BaseNode name) : base(NodeType.QualifiedName)
|
public QualifiedName(BaseNode Qualifier, BaseNode Name) : base(NodeType.QualifiedName)
|
||||||
{
|
{
|
||||||
_qualifier = qualifier;
|
this.Qualifier = Qualifier;
|
||||||
_name = name;
|
this.Name = Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
_qualifier.Print(writer);
|
Qualifier.Print(Writer);
|
||||||
writer.Write("::");
|
Writer.Write("::");
|
||||||
_name.Print(writer);
|
Name.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@ using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public enum Cv
|
public enum CV
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
Const,
|
Const,
|
||||||
|
@ -17,41 +17,41 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
LValue
|
LValue
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CvType : ParentNode
|
public class CVType : ParentNode
|
||||||
{
|
{
|
||||||
public Cv Qualifier;
|
public CV Qualifier;
|
||||||
|
|
||||||
public CvType(Cv qualifier, BaseNode child) : base(NodeType.CvQualifierType, child)
|
public CVType(CV Qualifier, BaseNode Child) : base(NodeType.CVQualifierType, Child)
|
||||||
{
|
{
|
||||||
Qualifier = qualifier;
|
this.Qualifier = Qualifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrintQualifier(TextWriter writer)
|
public void PrintQualifier(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if ((Qualifier & Cv.Const) != 0)
|
if ((Qualifier & CV.Const) != 0)
|
||||||
{
|
{
|
||||||
writer.Write(" const");
|
Writer.Write(" const");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((Qualifier & Cv.Volatile) != 0)
|
if ((Qualifier & CV.Volatile) != 0)
|
||||||
{
|
{
|
||||||
writer.Write(" volatile");
|
Writer.Write(" volatile");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((Qualifier & Cv.Restricted) != 0)
|
if ((Qualifier & CV.Restricted) != 0)
|
||||||
{
|
{
|
||||||
writer.Write(" restrict");
|
Writer.Write(" restrict");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if (Child != null)
|
if (Child != null)
|
||||||
{
|
{
|
||||||
Child.PrintLeft(writer);
|
Child.PrintLeft(Writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintQualifier(writer);
|
PrintQualifier(Writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool HasRightPart()
|
public override bool HasRightPart()
|
||||||
|
@ -59,11 +59,11 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
return Child != null && Child.HasRightPart();
|
return Child != null && Child.HasRightPart();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintRight(TextWriter writer)
|
public override void PrintRight(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if (Child != null)
|
if (Child != null)
|
||||||
{
|
{
|
||||||
Child.PrintRight(writer);
|
Child.PrintRight(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,36 +72,36 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public Reference Qualifier;
|
public Reference Qualifier;
|
||||||
|
|
||||||
public SimpleReferenceType(Reference qualifier, BaseNode child) : base(NodeType.SimpleReferenceType, child)
|
public SimpleReferenceType(Reference Qualifier, BaseNode Child) : base(NodeType.SimpleReferenceType, Child)
|
||||||
{
|
{
|
||||||
Qualifier = qualifier;
|
this.Qualifier = Qualifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrintQualifier(TextWriter writer)
|
public void PrintQualifier(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if ((Qualifier & Reference.LValue) != 0)
|
if ((Qualifier & Reference.LValue) != 0)
|
||||||
{
|
{
|
||||||
writer.Write("&");
|
Writer.Write("&");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((Qualifier & Reference.RValue) != 0)
|
if ((Qualifier & Reference.RValue) != 0)
|
||||||
{
|
{
|
||||||
writer.Write("&&");
|
Writer.Write("&&");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if (Child != null)
|
if (Child != null)
|
||||||
{
|
{
|
||||||
Child.PrintLeft(writer);
|
Child.PrintLeft(Writer);
|
||||||
}
|
}
|
||||||
else if (Qualifier != Reference.None)
|
else if (Qualifier != Reference.None)
|
||||||
{
|
{
|
||||||
writer.Write(" ");
|
Writer.Write(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintQualifier(writer);
|
PrintQualifier(Writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool HasRightPart()
|
public override bool HasRightPart()
|
||||||
|
@ -109,11 +109,11 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
return Child != null && Child.HasRightPart();
|
return Child != null && Child.HasRightPart();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintRight(TextWriter writer)
|
public override void PrintRight(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if (Child != null)
|
if (Child != null)
|
||||||
{
|
{
|
||||||
Child.PrintRight(writer);
|
Child.PrintRight(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,44 +4,44 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class ReferenceType : BaseNode
|
public class ReferenceType : BaseNode
|
||||||
{
|
{
|
||||||
private string _reference;
|
private string Reference;
|
||||||
private BaseNode _child;
|
private BaseNode Child;
|
||||||
|
|
||||||
public ReferenceType(string reference, BaseNode child) : base(NodeType.ReferenceType)
|
public ReferenceType(string Reference, BaseNode Child) : base(NodeType.ReferenceType)
|
||||||
{
|
{
|
||||||
_reference = reference;
|
this.Reference = Reference;
|
||||||
_child = child;
|
this.Child = Child;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool HasRightPart()
|
public override bool HasRightPart()
|
||||||
{
|
{
|
||||||
return _child.HasRightPart();
|
return Child.HasRightPart();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
_child.PrintLeft(writer);
|
Child.PrintLeft(Writer);
|
||||||
|
|
||||||
if (_child.IsArray())
|
if (Child.IsArray())
|
||||||
{
|
{
|
||||||
writer.Write(" ");
|
Writer.Write(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_child.IsArray() || _child.HasFunctions())
|
if (Child.IsArray() || Child.HasFunctions())
|
||||||
{
|
{
|
||||||
writer.Write("(");
|
Writer.Write("(");
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write(_reference);
|
Writer.Write(Reference);
|
||||||
}
|
}
|
||||||
public override void PrintRight(TextWriter writer)
|
public override void PrintRight(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if (_child.IsArray() || _child.HasFunctions())
|
if (Child.IsArray() || Child.HasFunctions())
|
||||||
{
|
{
|
||||||
writer.Write(")");
|
Writer.Write(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
_child.PrintRight(writer);
|
Child.PrintRight(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,17 +4,17 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class SpecialName : ParentNode
|
public class SpecialName : ParentNode
|
||||||
{
|
{
|
||||||
private string _specialValue;
|
private string SpecialValue;
|
||||||
|
|
||||||
public SpecialName(string specialValue, BaseNode type) : base(NodeType.SpecialName, type)
|
public SpecialName(string SpecialValue, BaseNode Type) : base(NodeType.SpecialName, Type)
|
||||||
{
|
{
|
||||||
_specialValue = specialValue;
|
this.SpecialValue = SpecialValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write(_specialValue);
|
Writer.Write(SpecialValue);
|
||||||
Child.Print(writer);
|
Child.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,14 +11,14 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
String,
|
String,
|
||||||
IStream,
|
IStream,
|
||||||
OStream,
|
OStream,
|
||||||
IOStream
|
IOStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
private SpecialType _specialSubstitutionKey;
|
private SpecialType SpecialSubstitutionKey;
|
||||||
|
|
||||||
public SpecialSubstitution(SpecialType specialSubstitutionKey) : base(NodeType.SpecialSubstitution)
|
public SpecialSubstitution(SpecialType SpecialSubstitutionKey) : base(NodeType.SpecialSubstitution)
|
||||||
{
|
{
|
||||||
_specialSubstitutionKey = specialSubstitutionKey;
|
this.SpecialSubstitutionKey = SpecialSubstitutionKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetExtended()
|
public void SetExtended()
|
||||||
|
@ -28,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
|
|
||||||
public override string GetName()
|
public override string GetName()
|
||||||
{
|
{
|
||||||
switch (_specialSubstitutionKey)
|
switch (SpecialSubstitutionKey)
|
||||||
{
|
{
|
||||||
case SpecialType.Allocator:
|
case SpecialType.Allocator:
|
||||||
return "allocator";
|
return "allocator";
|
||||||
|
@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
|
|
||||||
private string GetExtendedName()
|
private string GetExtendedName()
|
||||||
{
|
{
|
||||||
switch (_specialSubstitutionKey)
|
switch (SpecialSubstitutionKey)
|
||||||
{
|
{
|
||||||
case SpecialType.Allocator:
|
case SpecialType.Allocator:
|
||||||
return "std::allocator";
|
return "std::allocator";
|
||||||
|
@ -73,16 +73,16 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
if (Type == NodeType.ExpandedSpecialSubstitution)
|
if (Type == NodeType.ExpandedSpecialSubstitution)
|
||||||
{
|
{
|
||||||
writer.Write(GetExtendedName());
|
Writer.Write(GetExtendedName());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
writer.Write("std::");
|
Writer.Write("std::");
|
||||||
writer.Write(GetName());
|
Writer.Write(GetName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,12 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class StdQualifiedName : ParentNode
|
public class StdQualifiedName : ParentNode
|
||||||
{
|
{
|
||||||
public StdQualifiedName(BaseNode child) : base(NodeType.StdQualifiedName, child) { }
|
public StdQualifiedName(BaseNode Child) : base(NodeType.StdQualifiedName, Child) { }
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("std::");
|
Writer.Write("std::");
|
||||||
Child.Print(writer);
|
Child.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,22 +5,22 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class TemplateArguments : NodeArray
|
public class TemplateArguments : NodeArray
|
||||||
{
|
{
|
||||||
public TemplateArguments(List<BaseNode> nodes) : base(nodes, NodeType.TemplateArguments) { }
|
public TemplateArguments(List<BaseNode> Nodes) : base(Nodes, NodeType.TemplateArguments) { }
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
string Params = string.Join<BaseNode>(", ", Nodes.ToArray());
|
string Params = string.Join<BaseNode>(", ", Nodes.ToArray());
|
||||||
|
|
||||||
writer.Write("<");
|
Writer.Write("<");
|
||||||
|
|
||||||
writer.Write(Params);
|
Writer.Write(Params);
|
||||||
|
|
||||||
if (Params.EndsWith(">"))
|
if (Params.EndsWith(">"))
|
||||||
{
|
{
|
||||||
writer.Write(" ");
|
Writer.Write(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write(">");
|
Writer.Write(">");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,17 +4,17 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast
|
||||||
{
|
{
|
||||||
public class ThrowExpression : BaseNode
|
public class ThrowExpression : BaseNode
|
||||||
{
|
{
|
||||||
private BaseNode _expression;
|
private BaseNode Expression;
|
||||||
|
|
||||||
public ThrowExpression(BaseNode expression) : base(NodeType.ThrowExpression)
|
public ThrowExpression(BaseNode Expression) : base(NodeType.ThrowExpression)
|
||||||
{
|
{
|
||||||
_expression = expression;
|
this.Expression = Expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrintLeft(TextWriter writer)
|
public override void PrintLeft(TextWriter Writer)
|
||||||
{
|
{
|
||||||
writer.Write("throw ");
|
Writer.Write("throw ");
|
||||||
_expression.Print(writer);
|
Expression.Print(Writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -2,9 +2,9 @@ namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
static class ErrorCode
|
static class ErrorCode
|
||||||
{
|
{
|
||||||
public static uint MakeError(ErrorModule module, int code)
|
public static uint MakeError(ErrorModule Module, int Code)
|
||||||
{
|
{
|
||||||
return (uint)module | ((uint)code << 9);
|
return (uint)Module | ((uint)Code << 9);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,18 +8,18 @@ namespace Ryujinx.HLE.HOS
|
||||||
Htcs = 4,
|
Htcs = 4,
|
||||||
Ncm = 5,
|
Ncm = 5,
|
||||||
Dd = 6,
|
Dd = 6,
|
||||||
DebugMonitor = 7,
|
Debug_Monitor = 7,
|
||||||
Lr = 8,
|
Lr = 8,
|
||||||
Loader = 9,
|
Loader = 9,
|
||||||
IpcCommandInterface = 10,
|
IPC_Command_Interface = 10,
|
||||||
Ipc = 11,
|
IPC = 11,
|
||||||
Pm = 15,
|
Pm = 15,
|
||||||
Ns = 16,
|
Ns = 16,
|
||||||
Socket = 17,
|
Socket = 17,
|
||||||
Htc = 18,
|
Htc = 18,
|
||||||
NcmContent = 20,
|
Ncm_Content = 20,
|
||||||
Sm = 21,
|
Sm = 21,
|
||||||
RoUserland = 22,
|
RO_Userland = 22,
|
||||||
SdMmc = 24,
|
SdMmc = 24,
|
||||||
Ovln = 25,
|
Ovln = 25,
|
||||||
Spl = 26,
|
Spl = 26,
|
||||||
|
@ -41,13 +41,13 @@ namespace Ryujinx.HLE.HOS
|
||||||
Pcie = 120,
|
Pcie = 120,
|
||||||
Friends = 121,
|
Friends = 121,
|
||||||
Bcat = 122,
|
Bcat = 122,
|
||||||
Ssl = 123,
|
SSL = 123,
|
||||||
Account = 124,
|
Account = 124,
|
||||||
News = 125,
|
News = 125,
|
||||||
Mii = 126,
|
Mii = 126,
|
||||||
Nfc = 127,
|
Nfc = 127,
|
||||||
Am = 128,
|
Am = 128,
|
||||||
PlayReport = 129,
|
Play_Report = 129,
|
||||||
Ahid = 130,
|
Ahid = 130,
|
||||||
Qlaunch = 132,
|
Qlaunch = 132,
|
||||||
Pcv = 133,
|
Pcv = 133,
|
||||||
|
@ -64,23 +64,23 @@ namespace Ryujinx.HLE.HOS
|
||||||
Ec = 144,
|
Ec = 144,
|
||||||
ETicket = 145,
|
ETicket = 145,
|
||||||
Ngc = 146,
|
Ngc = 146,
|
||||||
ErrorReport = 147,
|
Error_Report = 147,
|
||||||
Apm = 148,
|
Apm = 148,
|
||||||
Profiler = 150,
|
Profiler = 150,
|
||||||
ErrorUpload = 151,
|
Error_Upload = 151,
|
||||||
Audio = 153,
|
Audio = 153,
|
||||||
Npns = 154,
|
Npns = 154,
|
||||||
NpnsHttpStream = 155,
|
Npns_Http_Stream = 155,
|
||||||
Arp = 157,
|
Arp = 157,
|
||||||
Swkbd = 158,
|
Swkbd = 158,
|
||||||
Boot = 159,
|
Boot = 159,
|
||||||
NfcMifare = 161,
|
Nfc_Mifare = 161,
|
||||||
UserlandAssert = 162,
|
Userland_Assert = 162,
|
||||||
Fatal = 163,
|
Fatal = 163,
|
||||||
NimShop = 164,
|
Nim_Shop = 164,
|
||||||
Spsm = 165,
|
Spsm = 165,
|
||||||
Bgtc = 167,
|
Bgtc = 167,
|
||||||
UserlandCrash = 168,
|
Userland_Crash = 168,
|
||||||
SRepo = 180,
|
SRepo = 180,
|
||||||
Dauth = 181,
|
Dauth = 181,
|
||||||
Hid = 202,
|
Hid = 202,
|
||||||
|
@ -92,10 +92,10 @@ namespace Ryujinx.HLE.HOS
|
||||||
Web = 210,
|
Web = 210,
|
||||||
Grc = 212,
|
Grc = 212,
|
||||||
Migration = 216,
|
Migration = 216,
|
||||||
MigrationLdcServer = 217,
|
Migration_Ldc_Server = 217,
|
||||||
GeneralWebApplet = 800,
|
General_Web_Applet = 800,
|
||||||
WifiWebAuthApplet = 809,
|
Wifi_Web_Auth_Applet = 809,
|
||||||
WhitelistedApplet = 810,
|
Whitelisted_Applet = 810,
|
||||||
ShopN = 811
|
ShopN = 811
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,116 +13,116 @@ namespace Ryujinx.HLE.HOS.Font
|
||||||
{
|
{
|
||||||
class SharedFontManager
|
class SharedFontManager
|
||||||
{
|
{
|
||||||
private Switch _device;
|
private Switch Device;
|
||||||
|
|
||||||
private long _physicalAddress;
|
private long PhysicalAddress;
|
||||||
|
|
||||||
private string _fontsPath;
|
private string FontsPath;
|
||||||
|
|
||||||
private struct FontInfo
|
private struct FontInfo
|
||||||
{
|
{
|
||||||
public int Offset;
|
public int Offset;
|
||||||
public int Size;
|
public int Size;
|
||||||
|
|
||||||
public FontInfo(int offset, int size)
|
public FontInfo(int Offset, int Size)
|
||||||
{
|
{
|
||||||
Offset = offset;
|
this.Offset = Offset;
|
||||||
Size = size;
|
this.Size = Size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dictionary<SharedFontType, FontInfo> _fontData;
|
private Dictionary<SharedFontType, FontInfo> FontData;
|
||||||
|
|
||||||
public SharedFontManager(Switch device, long physicalAddress)
|
public SharedFontManager(Switch Device, long PhysicalAddress)
|
||||||
{
|
{
|
||||||
_physicalAddress = physicalAddress;
|
this.PhysicalAddress = PhysicalAddress;
|
||||||
|
|
||||||
_device = device;
|
this.Device = Device;
|
||||||
|
|
||||||
_fontsPath = Path.Combine(device.FileSystem.GetSystemPath(), "fonts");
|
FontsPath = Path.Combine(Device.FileSystem.GetSystemPath(), "fonts");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnsureInitialized(ContentManager contentManager)
|
public void EnsureInitialized(ContentManager ContentManager)
|
||||||
{
|
{
|
||||||
if (_fontData == null)
|
if (FontData == null)
|
||||||
{
|
{
|
||||||
_device.Memory.FillWithZeros(_physicalAddress, Horizon.FontSize);
|
Device.Memory.FillWithZeros(PhysicalAddress, Horizon.FontSize);
|
||||||
|
|
||||||
uint fontOffset = 0;
|
uint FontOffset = 0;
|
||||||
|
|
||||||
FontInfo CreateFont(string name)
|
FontInfo CreateFont(string Name)
|
||||||
{
|
{
|
||||||
if (contentManager.TryGetFontTitle(name, out long fontTitle))
|
if (ContentManager.TryGetFontTitle(Name, out long FontTitle))
|
||||||
{
|
{
|
||||||
string contentPath = contentManager.GetInstalledContentPath(fontTitle, StorageId.NandSystem, ContentType.Data);
|
string ContentPath = ContentManager.GetInstalledContentPath(FontTitle, StorageId.NandSystem, ContentType.Data);
|
||||||
string fontPath = _device.FileSystem.SwitchPathToSystemPath(contentPath);
|
string FontPath = Device.FileSystem.SwitchPathToSystemPath(ContentPath);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(fontPath))
|
if (!string.IsNullOrWhiteSpace(FontPath))
|
||||||
{
|
{
|
||||||
int fileIndex = 0;
|
int FileIndex = 0;
|
||||||
|
|
||||||
//Use second file in Chinese Font title for standard
|
//Use second file in Chinese Font title for standard
|
||||||
if(name == "FontChineseSimplified")
|
if(Name == "FontChineseSimplified")
|
||||||
{
|
{
|
||||||
fileIndex = 1;
|
FileIndex = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileStream ncaFileStream = new FileStream(fontPath, FileMode.Open, FileAccess.Read);
|
FileStream NcaFileStream = new FileStream(FontPath, FileMode.Open, FileAccess.Read);
|
||||||
Nca nca = new Nca(_device.System.KeySet, ncaFileStream, false);
|
Nca Nca = new Nca(Device.System.KeySet, NcaFileStream, false);
|
||||||
NcaSection romfsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs);
|
NcaSection RomfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs);
|
||||||
Romfs romfs = new Romfs(nca.OpenSection(romfsSection.SectionNum, false, _device.System.FsIntegrityCheckLevel));
|
Romfs Romfs = new Romfs(Nca.OpenSection(RomfsSection.SectionNum, false, Device.System.FsIntegrityCheckLevel));
|
||||||
Stream fontFile = romfs.OpenFile(romfs.Files[fileIndex]);
|
Stream FontFile = Romfs.OpenFile(Romfs.Files[FileIndex]);
|
||||||
|
|
||||||
byte[] data = DecryptFont(fontFile);
|
byte[] Data = DecryptFont(FontFile);
|
||||||
|
|
||||||
FontInfo info = new FontInfo((int)fontOffset, data.Length);
|
FontInfo Info = new FontInfo((int)FontOffset, Data.Length);
|
||||||
|
|
||||||
WriteMagicAndSize(_physicalAddress + fontOffset, data.Length);
|
WriteMagicAndSize(PhysicalAddress + FontOffset, Data.Length);
|
||||||
|
|
||||||
fontOffset += 8;
|
FontOffset += 8;
|
||||||
|
|
||||||
uint start = fontOffset;
|
uint Start = FontOffset;
|
||||||
|
|
||||||
for (; fontOffset - start < data.Length; fontOffset++)
|
for (; FontOffset - Start < Data.Length; FontOffset++)
|
||||||
{
|
{
|
||||||
_device.Memory.WriteByte(_physicalAddress + fontOffset, data[fontOffset - start]);
|
Device.Memory.WriteByte(PhysicalAddress + FontOffset, Data[FontOffset - Start]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ncaFileStream.Dispose();
|
NcaFileStream.Dispose();
|
||||||
nca.Dispose();
|
Nca.Dispose();
|
||||||
|
|
||||||
return info;
|
return Info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string fontFilePath = Path.Combine(_fontsPath, name + ".ttf");
|
string FontFilePath = Path.Combine(FontsPath, Name + ".ttf");
|
||||||
|
|
||||||
if (File.Exists(fontFilePath))
|
if (File.Exists(FontFilePath))
|
||||||
{
|
{
|
||||||
byte[] data = File.ReadAllBytes(fontFilePath);
|
byte[] Data = File.ReadAllBytes(FontFilePath);
|
||||||
|
|
||||||
FontInfo info = new FontInfo((int)fontOffset, data.Length);
|
FontInfo Info = new FontInfo((int)FontOffset, Data.Length);
|
||||||
|
|
||||||
WriteMagicAndSize(_physicalAddress + fontOffset, data.Length);
|
WriteMagicAndSize(PhysicalAddress + FontOffset, Data.Length);
|
||||||
|
|
||||||
fontOffset += 8;
|
FontOffset += 8;
|
||||||
|
|
||||||
uint start = fontOffset;
|
uint Start = FontOffset;
|
||||||
|
|
||||||
for (; fontOffset - start < data.Length; fontOffset++)
|
for (; FontOffset - Start < Data.Length; FontOffset++)
|
||||||
{
|
{
|
||||||
_device.Memory.WriteByte(_physicalAddress + fontOffset, data[fontOffset - start]);
|
Device.Memory.WriteByte(PhysicalAddress + FontOffset, Data[FontOffset - Start]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return info;
|
return Info;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new InvalidSystemResourceException($"Font \"{name}.ttf\" not found. Please provide it in \"{_fontsPath}\".");
|
throw new InvalidSystemResourceException($"Font \"{Name}.ttf\" not found. Please provide it in \"{FontsPath}\".");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_fontData = new Dictionary<SharedFontType, FontInfo>
|
FontData = new Dictionary<SharedFontType, FontInfo>()
|
||||||
{
|
{
|
||||||
{ SharedFontType.JapanUsEurope, CreateFont("FontStandard") },
|
{ SharedFontType.JapanUsEurope, CreateFont("FontStandard") },
|
||||||
{ SharedFontType.SimplifiedChinese, CreateFont("FontChineseSimplified") },
|
{ SharedFontType.SimplifiedChinese, CreateFont("FontChineseSimplified") },
|
||||||
|
@ -132,39 +132,39 @@ namespace Ryujinx.HLE.HOS.Font
|
||||||
{ SharedFontType.NintendoEx, CreateFont("FontNintendoExtended") }
|
{ SharedFontType.NintendoEx, CreateFont("FontNintendoExtended") }
|
||||||
};
|
};
|
||||||
|
|
||||||
if (fontOffset > Horizon.FontSize)
|
if (FontOffset > Horizon.FontSize)
|
||||||
{
|
{
|
||||||
throw new InvalidSystemResourceException(
|
throw new InvalidSystemResourceException(
|
||||||
$"The sum of all fonts size exceed the shared memory size. " +
|
$"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. " +
|
$"Please make sure that the fonts don't exceed {Horizon.FontSize} bytes in total. " +
|
||||||
$"(actual size: {fontOffset} bytes).");
|
$"(actual size: {FontOffset} bytes).");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteMagicAndSize(long position, int size)
|
private void WriteMagicAndSize(long Position, int Size)
|
||||||
{
|
{
|
||||||
const int decMagic = 0x18029a7f;
|
const int DecMagic = 0x18029a7f;
|
||||||
const int key = 0x49621806;
|
const int Key = 0x49621806;
|
||||||
|
|
||||||
int encryptedSize = EndianSwap.Swap32(size ^ key);
|
int EncryptedSize = EndianSwap.Swap32(Size ^ Key);
|
||||||
|
|
||||||
_device.Memory.WriteInt32(position + 0, decMagic);
|
Device.Memory.WriteInt32(Position + 0, DecMagic);
|
||||||
_device.Memory.WriteInt32(position + 4, encryptedSize);
|
Device.Memory.WriteInt32(Position + 4, EncryptedSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetFontSize(SharedFontType fontType)
|
public int GetFontSize(SharedFontType FontType)
|
||||||
{
|
{
|
||||||
EnsureInitialized(_device.System.ContentManager);
|
EnsureInitialized(Device.System.ContentManager);
|
||||||
|
|
||||||
return _fontData[fontType].Size;
|
return FontData[FontType].Size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetSharedMemoryAddressOffset(SharedFontType fontType)
|
public int GetSharedMemoryAddressOffset(SharedFontType FontType)
|
||||||
{
|
{
|
||||||
EnsureInitialized(_device.System.ContentManager);
|
EnsureInitialized(Device.System.ContentManager);
|
||||||
|
|
||||||
return _fontData[fontType].Offset + 8;
|
return FontData[FontType].Offset + 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,62 +6,62 @@ namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
class GlobalStateTable
|
class GlobalStateTable
|
||||||
{
|
{
|
||||||
private ConcurrentDictionary<KProcess, IdDictionary> _dictByProcess;
|
private ConcurrentDictionary<KProcess, IdDictionary> DictByProcess;
|
||||||
|
|
||||||
public GlobalStateTable()
|
public GlobalStateTable()
|
||||||
{
|
{
|
||||||
_dictByProcess = new ConcurrentDictionary<KProcess, IdDictionary>();
|
DictByProcess = new ConcurrentDictionary<KProcess, IdDictionary>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Add(KProcess process, int id, object data)
|
public bool Add(KProcess Process, int Id, object Data)
|
||||||
{
|
{
|
||||||
IdDictionary dict = _dictByProcess.GetOrAdd(process, (key) => new IdDictionary());
|
IdDictionary Dict = DictByProcess.GetOrAdd(Process, (Key) => new IdDictionary());
|
||||||
|
|
||||||
return dict.Add(id, data);
|
return Dict.Add(Id, Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Add(KProcess process, object data)
|
public int Add(KProcess Process, object Data)
|
||||||
{
|
{
|
||||||
IdDictionary dict = _dictByProcess.GetOrAdd(process, (key) => new IdDictionary());
|
IdDictionary Dict = DictByProcess.GetOrAdd(Process, (Key) => new IdDictionary());
|
||||||
|
|
||||||
return dict.Add(data);
|
return Dict.Add(Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object GetData(KProcess process, int id)
|
public object GetData(KProcess Process, int Id)
|
||||||
{
|
{
|
||||||
if (_dictByProcess.TryGetValue(process, out IdDictionary dict))
|
if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
|
||||||
{
|
{
|
||||||
return dict.GetData(id);
|
return Dict.GetData(Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T GetData<T>(KProcess process, int id)
|
public T GetData<T>(KProcess Process, int Id)
|
||||||
{
|
{
|
||||||
if (_dictByProcess.TryGetValue(process, out IdDictionary dict))
|
if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
|
||||||
{
|
{
|
||||||
return dict.GetData<T>(id);
|
return Dict.GetData<T>(Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return default(T);
|
return default(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Delete(KProcess process, int id)
|
public object Delete(KProcess Process, int Id)
|
||||||
{
|
{
|
||||||
if (_dictByProcess.TryGetValue(process, out IdDictionary dict))
|
if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
|
||||||
{
|
{
|
||||||
return dict.Delete(id);
|
return Dict.Delete(Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICollection<object> DeleteProcess(KProcess process)
|
public ICollection<object> DeleteProcess(KProcess Process)
|
||||||
{
|
{
|
||||||
if (_dictByProcess.TryRemove(process, out IdDictionary dict))
|
if (DictByProcess.TryRemove(Process, out IdDictionary Dict))
|
||||||
{
|
{
|
||||||
return dict.Clear();
|
return Dict.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -8,70 +8,70 @@ namespace Ryujinx.HLE.HOS
|
||||||
public const string TemporaryNroSuffix = ".ryu_tmp.nro";
|
public const string TemporaryNroSuffix = ".ryu_tmp.nro";
|
||||||
|
|
||||||
//http://switchbrew.org/index.php?title=Homebrew_ABI
|
//http://switchbrew.org/index.php?title=Homebrew_ABI
|
||||||
public static void WriteHbAbiData(MemoryManager memory, long position, int mainThreadHandle, string switchPath)
|
public static void WriteHbAbiData(MemoryManager Memory, long Position, int MainThreadHandle, string SwitchPath)
|
||||||
{
|
{
|
||||||
//MainThreadHandle.
|
//MainThreadHandle.
|
||||||
WriteConfigEntry(memory, ref position, 1, 0, mainThreadHandle);
|
WriteConfigEntry(Memory, ref Position, 1, 0, MainThreadHandle);
|
||||||
|
|
||||||
//NextLoadPath.
|
//NextLoadPath.
|
||||||
WriteConfigEntry(memory, ref position, 2, 0, position + 0x200, position + 0x400);
|
WriteConfigEntry(Memory, ref Position, 2, 0, Position + 0x200, Position + 0x400);
|
||||||
|
|
||||||
//Argv.
|
//Argv.
|
||||||
long argvPosition = position + 0xC00;
|
long ArgvPosition = Position + 0xC00;
|
||||||
|
|
||||||
memory.WriteBytes(argvPosition, Encoding.ASCII.GetBytes(switchPath + "\0"));
|
Memory.WriteBytes(ArgvPosition, Encoding.ASCII.GetBytes(SwitchPath + "\0"));
|
||||||
|
|
||||||
WriteConfigEntry(memory, ref position, 5, 0, 0, argvPosition);
|
WriteConfigEntry(Memory, ref Position, 5, 0, 0, ArgvPosition);
|
||||||
|
|
||||||
//AppletType.
|
//AppletType.
|
||||||
WriteConfigEntry(memory, ref position, 7);
|
WriteConfigEntry(Memory, ref Position, 7);
|
||||||
|
|
||||||
//EndOfList.
|
//EndOfList.
|
||||||
WriteConfigEntry(memory, ref position, 0);
|
WriteConfigEntry(Memory, ref Position, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void WriteConfigEntry(
|
private static void WriteConfigEntry(
|
||||||
MemoryManager memory,
|
MemoryManager Memory,
|
||||||
ref long position,
|
ref long Position,
|
||||||
int key,
|
int Key,
|
||||||
int flags = 0,
|
int Flags = 0,
|
||||||
long value0 = 0,
|
long Value0 = 0,
|
||||||
long value1 = 0)
|
long Value1 = 0)
|
||||||
{
|
{
|
||||||
memory.WriteInt32(position + 0x00, key);
|
Memory.WriteInt32(Position + 0x00, Key);
|
||||||
memory.WriteInt32(position + 0x04, flags);
|
Memory.WriteInt32(Position + 0x04, Flags);
|
||||||
memory.WriteInt64(position + 0x08, value0);
|
Memory.WriteInt64(Position + 0x08, Value0);
|
||||||
memory.WriteInt64(position + 0x10, value1);
|
Memory.WriteInt64(Position + 0x10, Value1);
|
||||||
|
|
||||||
position += 0x18;
|
Position += 0x18;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ReadHbAbiNextLoadPath(MemoryManager memory, long position)
|
public static string ReadHbAbiNextLoadPath(MemoryManager Memory, long Position)
|
||||||
{
|
{
|
||||||
string fileName = null;
|
string FileName = null;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
long key = memory.ReadInt64(position);
|
long Key = Memory.ReadInt64(Position);
|
||||||
|
|
||||||
if (key == 2)
|
if (Key == 2)
|
||||||
{
|
{
|
||||||
long value0 = memory.ReadInt64(position + 0x08);
|
long Value0 = Memory.ReadInt64(Position + 0x08);
|
||||||
long value1 = memory.ReadInt64(position + 0x10);
|
long Value1 = Memory.ReadInt64(Position + 0x10);
|
||||||
|
|
||||||
fileName = MemoryHelper.ReadAsciiString(memory, value0, value1 - value0);
|
FileName = MemoryHelper.ReadAsciiString(Memory, Value0, Value1 - Value0);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (key == 0)
|
else if (Key == 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
position += 0x18;
|
Position += 0x18;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileName;
|
return FileName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,34 +35,34 @@ namespace Ryujinx.HLE.HOS
|
||||||
internal long PrivilegedProcessLowestId { get; set; } = 1;
|
internal long PrivilegedProcessLowestId { get; set; } = 1;
|
||||||
internal long PrivilegedProcessHighestId { get; set; } = 8;
|
internal long PrivilegedProcessHighestId { get; set; } = 8;
|
||||||
|
|
||||||
internal Switch Device { get; }
|
internal Switch Device { get; private set; }
|
||||||
|
|
||||||
public SystemStateMgr State { get; }
|
public SystemStateMgr State { get; private set; }
|
||||||
|
|
||||||
internal bool KernelInitialized { get; }
|
internal bool KernelInitialized { get; private set; }
|
||||||
|
|
||||||
internal KResourceLimit ResourceLimit { get; }
|
internal KResourceLimit ResourceLimit { get; private set; }
|
||||||
|
|
||||||
internal KMemoryRegionManager[] MemoryRegions { get; }
|
internal KMemoryRegionManager[] MemoryRegions { get; private set; }
|
||||||
|
|
||||||
internal KMemoryBlockAllocator LargeMemoryBlockAllocator { get; }
|
internal KMemoryBlockAllocator LargeMemoryBlockAllocator { get; private set; }
|
||||||
internal KMemoryBlockAllocator SmallMemoryBlockAllocator { get; }
|
internal KMemoryBlockAllocator SmallMemoryBlockAllocator { get; private set; }
|
||||||
|
|
||||||
internal KSlabHeap UserSlabHeapPages { get; }
|
internal KSlabHeap UserSlabHeapPages { get; private set; }
|
||||||
|
|
||||||
internal KCriticalSection CriticalSection { get; }
|
internal KCriticalSection CriticalSection { get; private set; }
|
||||||
|
|
||||||
internal KScheduler Scheduler { get; }
|
internal KScheduler Scheduler { get; private set; }
|
||||||
|
|
||||||
internal KTimeManager TimeManager { get; }
|
internal KTimeManager TimeManager { get; private set; }
|
||||||
|
|
||||||
internal KSynchronization Synchronization { get; }
|
internal KSynchronization Synchronization { get; private set; }
|
||||||
|
|
||||||
internal KContextIdManager ContextIdManager { get; }
|
internal KContextIdManager ContextIdManager { get; private set; }
|
||||||
|
|
||||||
private long _kipId;
|
private long KipId;
|
||||||
private long _processId;
|
private long ProcessId;
|
||||||
private long _threadUid;
|
private long ThreadUid;
|
||||||
|
|
||||||
internal CountdownEvent ThreadCounter;
|
internal CountdownEvent ThreadCounter;
|
||||||
|
|
||||||
|
@ -72,20 +72,20 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
internal bool EnableVersionChecks { get; private set; }
|
internal bool EnableVersionChecks { get; private set; }
|
||||||
|
|
||||||
internal AppletStateMgr AppletState { get; }
|
internal AppletStateMgr AppletState { get; private set; }
|
||||||
|
|
||||||
internal KSharedMemory HidSharedMem { get; }
|
internal KSharedMemory HidSharedMem { get; private set; }
|
||||||
internal KSharedMemory FontSharedMem { get; }
|
internal KSharedMemory FontSharedMem { get; private set; }
|
||||||
|
|
||||||
internal SharedFontManager Font { get; }
|
internal SharedFontManager Font { get; private set; }
|
||||||
|
|
||||||
internal ContentManager ContentManager { get; }
|
internal ContentManager ContentManager { get; private set; }
|
||||||
|
|
||||||
internal KEvent VsyncEvent { get; }
|
internal KEvent VsyncEvent { get; private set; }
|
||||||
|
|
||||||
internal Keyset KeySet { get; private set; }
|
internal Keyset KeySet { get; private set; }
|
||||||
|
|
||||||
private bool _hasStarted;
|
private bool HasStarted;
|
||||||
|
|
||||||
public Nacp ControlData { get; set; }
|
public Nacp ControlData { get; set; }
|
||||||
|
|
||||||
|
@ -93,11 +93,11 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
public IntegrityCheckLevel FsIntegrityCheckLevel { get; set; }
|
public IntegrityCheckLevel FsIntegrityCheckLevel { get; set; }
|
||||||
|
|
||||||
internal long HidBaseAddress { get; }
|
internal long HidBaseAddress { get; private set; }
|
||||||
|
|
||||||
public Horizon(Switch device)
|
public Horizon(Switch Device)
|
||||||
{
|
{
|
||||||
Device = device;
|
this.Device = Device;
|
||||||
|
|
||||||
State = new SystemStateMgr();
|
State = new SystemStateMgr();
|
||||||
|
|
||||||
|
@ -125,8 +125,8 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
ContextIdManager = new KContextIdManager();
|
ContextIdManager = new KContextIdManager();
|
||||||
|
|
||||||
_kipId = InitialKipId;
|
KipId = InitialKipId;
|
||||||
_processId = InitialProcessId;
|
ProcessId = InitialProcessId;
|
||||||
|
|
||||||
Scheduler.StartAutoPreemptionThread();
|
Scheduler.StartAutoPreemptionThread();
|
||||||
|
|
||||||
|
@ -140,90 +140,90 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
//Note: This is not really correct, but with HLE of services, the only memory
|
//Note: This is not really correct, but with HLE of services, the only memory
|
||||||
//region used that is used is Application, so we can use the other ones for anything.
|
//region used that is used is Application, so we can use the other ones for anything.
|
||||||
KMemoryRegionManager region = MemoryRegions[(int)MemoryRegion.NvServices];
|
KMemoryRegionManager Region = MemoryRegions[(int)MemoryRegion.NvServices];
|
||||||
|
|
||||||
ulong hidPa = region.Address;
|
ulong HidPa = Region.Address;
|
||||||
ulong fontPa = region.Address + HidSize;
|
ulong FontPa = Region.Address + HidSize;
|
||||||
|
|
||||||
HidBaseAddress = (long)(hidPa - DramMemoryMap.DramBase);
|
HidBaseAddress = (long)(HidPa - DramMemoryMap.DramBase);
|
||||||
|
|
||||||
KPageList hidPageList = new KPageList();
|
KPageList HidPageList = new KPageList();
|
||||||
KPageList fontPageList = new KPageList();
|
KPageList FontPageList = new KPageList();
|
||||||
|
|
||||||
hidPageList .AddRange(hidPa, HidSize / KMemoryManager.PageSize);
|
HidPageList .AddRange(HidPa, HidSize / KMemoryManager.PageSize);
|
||||||
fontPageList.AddRange(fontPa, FontSize / KMemoryManager.PageSize);
|
FontPageList.AddRange(FontPa, FontSize / KMemoryManager.PageSize);
|
||||||
|
|
||||||
HidSharedMem = new KSharedMemory(hidPageList, 0, 0, MemoryPermission.Read);
|
HidSharedMem = new KSharedMemory(HidPageList, 0, 0, MemoryPermission.Read);
|
||||||
FontSharedMem = new KSharedMemory(fontPageList, 0, 0, MemoryPermission.Read);
|
FontSharedMem = new KSharedMemory(FontPageList, 0, 0, MemoryPermission.Read);
|
||||||
|
|
||||||
AppletState = new AppletStateMgr(this);
|
AppletState = new AppletStateMgr(this);
|
||||||
|
|
||||||
AppletState.SetFocus(true);
|
AppletState.SetFocus(true);
|
||||||
|
|
||||||
Font = new SharedFontManager(device, (long)(fontPa - DramMemoryMap.DramBase));
|
Font = new SharedFontManager(Device, (long)(FontPa - DramMemoryMap.DramBase));
|
||||||
|
|
||||||
VsyncEvent = new KEvent(this);
|
VsyncEvent = new KEvent(this);
|
||||||
|
|
||||||
LoadKeySet();
|
LoadKeySet();
|
||||||
|
|
||||||
ContentManager = new ContentManager(device);
|
ContentManager = new ContentManager(Device);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadCart(string exeFsDir, string romFsFile = null)
|
public void LoadCart(string ExeFsDir, string RomFsFile = null)
|
||||||
{
|
{
|
||||||
if (romFsFile != null)
|
if (RomFsFile != null)
|
||||||
{
|
{
|
||||||
Device.FileSystem.LoadRomFs(romFsFile);
|
Device.FileSystem.LoadRomFs(RomFsFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
string npdmFileName = Path.Combine(exeFsDir, "main.npdm");
|
string NpdmFileName = Path.Combine(ExeFsDir, "main.npdm");
|
||||||
|
|
||||||
Npdm metaData = null;
|
Npdm MetaData = null;
|
||||||
|
|
||||||
if (File.Exists(npdmFileName))
|
if (File.Exists(NpdmFileName))
|
||||||
{
|
{
|
||||||
Logger.PrintInfo(LogClass.Loader, $"Loading main.npdm...");
|
Logger.PrintInfo(LogClass.Loader, $"Loading main.npdm...");
|
||||||
|
|
||||||
using (FileStream input = new FileStream(npdmFileName, FileMode.Open))
|
using (FileStream Input = new FileStream(NpdmFileName, FileMode.Open))
|
||||||
{
|
{
|
||||||
metaData = new Npdm(input);
|
MetaData = new Npdm(Input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
|
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
|
||||||
|
|
||||||
metaData = GetDefaultNpdm();
|
MetaData = GetDefaultNpdm();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<IExecutable> staticObjects = new List<IExecutable>();
|
List<IExecutable> StaticObjects = new List<IExecutable>();
|
||||||
|
|
||||||
void LoadNso(string searchPattern)
|
void LoadNso(string SearchPattern)
|
||||||
{
|
{
|
||||||
foreach (string file in Directory.GetFiles(exeFsDir, searchPattern))
|
foreach (string File in Directory.GetFiles(ExeFsDir, SearchPattern))
|
||||||
{
|
{
|
||||||
if (Path.GetExtension(file) != string.Empty)
|
if (Path.GetExtension(File) != string.Empty)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.PrintInfo(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(file)}...");
|
Logger.PrintInfo(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(File)}...");
|
||||||
|
|
||||||
using (FileStream input = new FileStream(file, FileMode.Open))
|
using (FileStream Input = new FileStream(File, FileMode.Open))
|
||||||
{
|
{
|
||||||
NxStaticObject staticObject = new NxStaticObject(input);
|
NxStaticObject StaticObject = new NxStaticObject(Input);
|
||||||
|
|
||||||
staticObjects.Add(staticObject);
|
StaticObjects.Add(StaticObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!metaData.Is64Bits)
|
if (!MetaData.Is64Bits)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException("32-bit titles are unsupported!");
|
throw new NotImplementedException("32-bit titles are unsupported!");
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
|
CurrentTitle = MetaData.ACI0.TitleId.ToString("x16");
|
||||||
|
|
||||||
LoadNso("rtld");
|
LoadNso("rtld");
|
||||||
LoadNso("main");
|
LoadNso("main");
|
||||||
|
@ -232,18 +232,18 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
ContentManager.LoadEntries();
|
ContentManager.LoadEntries();
|
||||||
|
|
||||||
ProgramLoader.LoadStaticObjects(this, metaData, staticObjects.ToArray());
|
ProgramLoader.LoadStaticObjects(this, MetaData, StaticObjects.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadXci(string xciFile)
|
public void LoadXci(string XciFile)
|
||||||
{
|
{
|
||||||
FileStream file = new FileStream(xciFile, FileMode.Open, FileAccess.Read);
|
FileStream File = new FileStream(XciFile, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
Xci xci = new Xci(KeySet, file);
|
Xci Xci = new Xci(KeySet, File);
|
||||||
|
|
||||||
(Nca mainNca, Nca controlNca) = GetXciGameData(xci);
|
(Nca MainNca, Nca ControlNca) = GetXciGameData(Xci);
|
||||||
|
|
||||||
if (mainNca == null)
|
if (MainNca == null)
|
||||||
{
|
{
|
||||||
Logger.PrintError(LogClass.Loader, "Unable to load XCI");
|
Logger.PrintError(LogClass.Loader, "Unable to load XCI");
|
||||||
|
|
||||||
|
@ -252,23 +252,23 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
ContentManager.LoadEntries();
|
ContentManager.LoadEntries();
|
||||||
|
|
||||||
LoadNca(mainNca, controlNca);
|
LoadNca(MainNca, ControlNca);
|
||||||
}
|
}
|
||||||
|
|
||||||
private (Nca Main, Nca Control) GetXciGameData(Xci xci)
|
private (Nca Main, Nca Control) GetXciGameData(Xci Xci)
|
||||||
{
|
{
|
||||||
if (xci.SecurePartition == null)
|
if (Xci.SecurePartition == null)
|
||||||
{
|
{
|
||||||
throw new InvalidDataException("Could not find XCI secure partition");
|
throw new InvalidDataException("Could not find XCI secure partition");
|
||||||
}
|
}
|
||||||
|
|
||||||
Nca mainNca = null;
|
Nca MainNca = null;
|
||||||
Nca patchNca = null;
|
Nca PatchNca = null;
|
||||||
Nca controlNca = null;
|
Nca ControlNca = null;
|
||||||
|
|
||||||
foreach (PfsFileEntry ticketEntry in xci.SecurePartition.Files.Where(x => x.Name.EndsWith(".tik")))
|
foreach (PfsFileEntry TicketEntry in Xci.SecurePartition.Files.Where(x => x.Name.EndsWith(".tik")))
|
||||||
{
|
{
|
||||||
Ticket ticket = new Ticket(xci.SecurePartition.OpenFile(ticketEntry));
|
Ticket ticket = new Ticket(Xci.SecurePartition.OpenFile(TicketEntry));
|
||||||
|
|
||||||
if (!KeySet.TitleKeys.ContainsKey(ticket.RightsId))
|
if (!KeySet.TitleKeys.ContainsKey(ticket.RightsId))
|
||||||
{
|
{
|
||||||
|
@ -276,107 +276,107 @@ namespace Ryujinx.HLE.HOS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (PfsFileEntry fileEntry in xci.SecurePartition.Files.Where(x => x.Name.EndsWith(".nca")))
|
foreach (PfsFileEntry FileEntry in Xci.SecurePartition.Files.Where(x => x.Name.EndsWith(".nca")))
|
||||||
{
|
{
|
||||||
Stream ncaStream = xci.SecurePartition.OpenFile(fileEntry);
|
Stream NcaStream = Xci.SecurePartition.OpenFile(FileEntry);
|
||||||
|
|
||||||
Nca nca = new Nca(KeySet, ncaStream, true);
|
Nca Nca = new Nca(KeySet, NcaStream, true);
|
||||||
|
|
||||||
if (nca.Header.ContentType == ContentType.Program)
|
if (Nca.Header.ContentType == ContentType.Program)
|
||||||
{
|
{
|
||||||
if (nca.Sections.Any(x => x?.Type == SectionType.Romfs))
|
if (Nca.Sections.Any(x => x?.Type == SectionType.Romfs))
|
||||||
{
|
{
|
||||||
mainNca = nca;
|
MainNca = Nca;
|
||||||
}
|
}
|
||||||
else if (nca.Sections.Any(x => x?.Type == SectionType.Bktr))
|
else if (Nca.Sections.Any(x => x?.Type == SectionType.Bktr))
|
||||||
{
|
{
|
||||||
patchNca = nca;
|
PatchNca = Nca;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (nca.Header.ContentType == ContentType.Control)
|
else if (Nca.Header.ContentType == ContentType.Control)
|
||||||
{
|
{
|
||||||
controlNca = nca;
|
ControlNca = Nca;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mainNca == null)
|
if (MainNca == null)
|
||||||
{
|
{
|
||||||
Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided XCI file");
|
Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided XCI file");
|
||||||
}
|
}
|
||||||
|
|
||||||
mainNca.SetBaseNca(patchNca);
|
MainNca.SetBaseNca(PatchNca);
|
||||||
|
|
||||||
if (controlNca != null)
|
if (ControlNca != null)
|
||||||
{
|
{
|
||||||
ReadControlData(controlNca);
|
ReadControlData(ControlNca);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (patchNca != null)
|
if (PatchNca != null)
|
||||||
{
|
{
|
||||||
patchNca.SetBaseNca(mainNca);
|
PatchNca.SetBaseNca(MainNca);
|
||||||
|
|
||||||
return (patchNca, controlNca);
|
return (PatchNca, ControlNca);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (mainNca, controlNca);
|
return (MainNca, ControlNca);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReadControlData(Nca controlNca)
|
public void ReadControlData(Nca ControlNca)
|
||||||
{
|
{
|
||||||
Romfs controlRomfs = new Romfs(controlNca.OpenSection(0, false, FsIntegrityCheckLevel));
|
Romfs ControlRomfs = new Romfs(ControlNca.OpenSection(0, false, FsIntegrityCheckLevel));
|
||||||
|
|
||||||
byte[] controlFile = controlRomfs.GetFile("/control.nacp");
|
byte[] ControlFile = ControlRomfs.GetFile("/control.nacp");
|
||||||
|
|
||||||
BinaryReader reader = new BinaryReader(new MemoryStream(controlFile));
|
BinaryReader Reader = new BinaryReader(new MemoryStream(ControlFile));
|
||||||
|
|
||||||
ControlData = new Nacp(reader);
|
ControlData = new Nacp(Reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadNca(string ncaFile)
|
public void LoadNca(string NcaFile)
|
||||||
{
|
{
|
||||||
FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read);
|
FileStream File = new FileStream(NcaFile, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
Nca nca = new Nca(KeySet, file, true);
|
Nca Nca = new Nca(KeySet, File, true);
|
||||||
|
|
||||||
LoadNca(nca, null);
|
LoadNca(Nca, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadNsp(string nspFile)
|
public void LoadNsp(string NspFile)
|
||||||
{
|
{
|
||||||
FileStream file = new FileStream(nspFile, FileMode.Open, FileAccess.Read);
|
FileStream File = new FileStream(NspFile, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
Pfs nsp = new Pfs(file);
|
Pfs Nsp = new Pfs(File);
|
||||||
|
|
||||||
PfsFileEntry ticketFile = nsp.Files.FirstOrDefault(x => x.Name.EndsWith(".tik"));
|
PfsFileEntry TicketFile = Nsp.Files.FirstOrDefault(x => x.Name.EndsWith(".tik"));
|
||||||
|
|
||||||
// Load title key from the NSP's ticket in case the user doesn't have a title key file
|
// Load title key from the NSP's ticket in case the user doesn't have a title key file
|
||||||
if (ticketFile != null)
|
if (TicketFile != null)
|
||||||
{
|
{
|
||||||
Ticket ticket = new Ticket(nsp.OpenFile(ticketFile));
|
Ticket Ticket = new Ticket(Nsp.OpenFile(TicketFile));
|
||||||
|
|
||||||
KeySet.TitleKeys[ticket.RightsId] = ticket.GetTitleKey(KeySet);
|
KeySet.TitleKeys[Ticket.RightsId] = Ticket.GetTitleKey(KeySet);
|
||||||
}
|
}
|
||||||
|
|
||||||
Nca mainNca = null;
|
Nca MainNca = null;
|
||||||
Nca controlNca = null;
|
Nca ControlNca = null;
|
||||||
|
|
||||||
foreach (PfsFileEntry ncaFile in nsp.Files.Where(x => x.Name.EndsWith(".nca")))
|
foreach (PfsFileEntry NcaFile in Nsp.Files.Where(x => x.Name.EndsWith(".nca")))
|
||||||
{
|
{
|
||||||
Nca nca = new Nca(KeySet, nsp.OpenFile(ncaFile), true);
|
Nca Nca = new Nca(KeySet, Nsp.OpenFile(NcaFile), true);
|
||||||
|
|
||||||
if (nca.Header.ContentType == ContentType.Program)
|
if (Nca.Header.ContentType == ContentType.Program)
|
||||||
{
|
{
|
||||||
mainNca = nca;
|
MainNca = Nca;
|
||||||
}
|
}
|
||||||
else if (nca.Header.ContentType == ContentType.Control)
|
else if (Nca.Header.ContentType == ContentType.Control)
|
||||||
{
|
{
|
||||||
controlNca = nca;
|
ControlNca = Nca;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mainNca != null)
|
if (MainNca != null)
|
||||||
{
|
{
|
||||||
LoadNca(mainNca, controlNca);
|
LoadNca(MainNca, ControlNca);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -384,100 +384,100 @@ namespace Ryujinx.HLE.HOS
|
||||||
Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file");
|
Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadNca(Nca mainNca, Nca controlNca)
|
public void LoadNca(Nca MainNca, Nca ControlNca)
|
||||||
{
|
{
|
||||||
if (mainNca.Header.ContentType != ContentType.Program)
|
if (MainNca.Header.ContentType != ContentType.Program)
|
||||||
{
|
{
|
||||||
Logger.PrintError(LogClass.Loader, "Selected NCA is not a \"Program\" NCA");
|
Logger.PrintError(LogClass.Loader, "Selected NCA is not a \"Program\" NCA");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream romfsStream = mainNca.OpenSection(ProgramPartitionType.Data, false, FsIntegrityCheckLevel);
|
Stream RomfsStream = MainNca.OpenSection(ProgramPartitionType.Data, false, FsIntegrityCheckLevel);
|
||||||
Stream exefsStream = mainNca.OpenSection(ProgramPartitionType.Code, false, FsIntegrityCheckLevel);
|
Stream ExefsStream = MainNca.OpenSection(ProgramPartitionType.Code, false, FsIntegrityCheckLevel);
|
||||||
|
|
||||||
if (exefsStream == null)
|
if (ExefsStream == null)
|
||||||
{
|
{
|
||||||
Logger.PrintError(LogClass.Loader, "No ExeFS found in NCA");
|
Logger.PrintError(LogClass.Loader, "No ExeFS found in NCA");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (romfsStream == null)
|
if (RomfsStream == null)
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.Loader, "No RomFS found in NCA");
|
Logger.PrintWarning(LogClass.Loader, "No RomFS found in NCA");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Device.FileSystem.SetRomFs(romfsStream);
|
Device.FileSystem.SetRomFs(RomfsStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pfs exefs = new Pfs(exefsStream);
|
Pfs Exefs = new Pfs(ExefsStream);
|
||||||
|
|
||||||
Npdm metaData = null;
|
Npdm MetaData = null;
|
||||||
|
|
||||||
if (exefs.FileExists("main.npdm"))
|
if (Exefs.FileExists("main.npdm"))
|
||||||
{
|
{
|
||||||
Logger.PrintInfo(LogClass.Loader, "Loading main.npdm...");
|
Logger.PrintInfo(LogClass.Loader, "Loading main.npdm...");
|
||||||
|
|
||||||
metaData = new Npdm(exefs.OpenFile("main.npdm"));
|
MetaData = new Npdm(Exefs.OpenFile("main.npdm"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
|
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
|
||||||
|
|
||||||
metaData = GetDefaultNpdm();
|
MetaData = GetDefaultNpdm();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<IExecutable> staticObjects = new List<IExecutable>();
|
List<IExecutable> StaticObjects = new List<IExecutable>();
|
||||||
|
|
||||||
void LoadNso(string filename)
|
void LoadNso(string Filename)
|
||||||
{
|
{
|
||||||
foreach (PfsFileEntry file in exefs.Files.Where(x => x.Name.StartsWith(filename)))
|
foreach (PfsFileEntry File in Exefs.Files.Where(x => x.Name.StartsWith(Filename)))
|
||||||
{
|
{
|
||||||
if (Path.GetExtension(file.Name) != string.Empty)
|
if (Path.GetExtension(File.Name) != string.Empty)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.PrintInfo(LogClass.Loader, $"Loading {filename}...");
|
Logger.PrintInfo(LogClass.Loader, $"Loading {Filename}...");
|
||||||
|
|
||||||
NxStaticObject staticObject = new NxStaticObject(exefs.OpenFile(file));
|
NxStaticObject StaticObject = new NxStaticObject(Exefs.OpenFile(File));
|
||||||
|
|
||||||
staticObjects.Add(staticObject);
|
StaticObjects.Add(StaticObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Nacp ReadControlData()
|
Nacp ReadControlData()
|
||||||
{
|
{
|
||||||
Romfs controlRomfs = new Romfs(controlNca.OpenSection(0, false, FsIntegrityCheckLevel));
|
Romfs ControlRomfs = new Romfs(ControlNca.OpenSection(0, false, FsIntegrityCheckLevel));
|
||||||
|
|
||||||
byte[] controlFile = controlRomfs.GetFile("/control.nacp");
|
byte[] ControlFile = ControlRomfs.GetFile("/control.nacp");
|
||||||
|
|
||||||
BinaryReader reader = new BinaryReader(new MemoryStream(controlFile));
|
BinaryReader Reader = new BinaryReader(new MemoryStream(ControlFile));
|
||||||
|
|
||||||
Nacp controlData = new Nacp(reader);
|
Nacp ControlData = new Nacp(Reader);
|
||||||
|
|
||||||
CurrentTitle = controlData.Languages[(int)State.DesiredTitleLanguage].Title;
|
CurrentTitle = ControlData.Languages[(int)State.DesiredTitleLanguage].Title;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(CurrentTitle))
|
if (string.IsNullOrWhiteSpace(CurrentTitle))
|
||||||
{
|
{
|
||||||
CurrentTitle = controlData.Languages.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title;
|
CurrentTitle = ControlData.Languages.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title;
|
||||||
}
|
}
|
||||||
|
|
||||||
return controlData;
|
return ControlData;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (controlNca != null)
|
if (ControlNca != null)
|
||||||
{
|
{
|
||||||
ReadControlData();
|
ReadControlData();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
|
CurrentTitle = MetaData.ACI0.TitleId.ToString("x16");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!metaData.Is64Bits)
|
if (!MetaData.Is64Bits)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException("32-bit titles are not supported!");
|
throw new NotImplementedException("32-bit titles are not supported!");
|
||||||
}
|
}
|
||||||
|
@ -489,67 +489,67 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
ContentManager.LoadEntries();
|
ContentManager.LoadEntries();
|
||||||
|
|
||||||
ProgramLoader.LoadStaticObjects(this, metaData, staticObjects.ToArray());
|
ProgramLoader.LoadStaticObjects(this, MetaData, StaticObjects.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadProgram(string filePath)
|
public void LoadProgram(string FilePath)
|
||||||
{
|
{
|
||||||
Npdm metaData = GetDefaultNpdm();
|
Npdm MetaData = GetDefaultNpdm();
|
||||||
|
|
||||||
bool isNro = Path.GetExtension(filePath).ToLower() == ".nro";
|
bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro";
|
||||||
|
|
||||||
using (FileStream input = new FileStream(filePath, FileMode.Open))
|
using (FileStream Input = new FileStream(FilePath, FileMode.Open))
|
||||||
{
|
{
|
||||||
IExecutable staticObject = isNro
|
IExecutable StaticObject = IsNro
|
||||||
? (IExecutable)new NxRelocatableObject(input)
|
? (IExecutable)new NxRelocatableObject(Input)
|
||||||
: new NxStaticObject(input);
|
: (IExecutable)new NxStaticObject(Input);
|
||||||
|
|
||||||
ProgramLoader.LoadStaticObjects(this, metaData, new IExecutable[] { staticObject });
|
ProgramLoader.LoadStaticObjects(this, MetaData, new IExecutable[] { StaticObject });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Npdm GetDefaultNpdm()
|
private Npdm GetDefaultNpdm()
|
||||||
{
|
{
|
||||||
Assembly asm = Assembly.GetCallingAssembly();
|
Assembly Asm = Assembly.GetCallingAssembly();
|
||||||
|
|
||||||
using (Stream npdmStream = asm.GetManifestResourceStream("Ryujinx.HLE.Homebrew.npdm"))
|
using (Stream NpdmStream = Asm.GetManifestResourceStream("Ryujinx.HLE.Homebrew.npdm"))
|
||||||
{
|
{
|
||||||
return new Npdm(npdmStream);
|
return new Npdm(NpdmStream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadKeySet()
|
public void LoadKeySet()
|
||||||
{
|
{
|
||||||
string keyFile = null;
|
string KeyFile = null;
|
||||||
string titleKeyFile = null;
|
string TitleKeyFile = null;
|
||||||
string consoleKeyFile = null;
|
string ConsoleKeyFile = null;
|
||||||
|
|
||||||
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
string Home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||||
|
|
||||||
LoadSetAtPath(Path.Combine(home, ".switch"));
|
LoadSetAtPath(Path.Combine(Home, ".switch"));
|
||||||
LoadSetAtPath(Device.FileSystem.GetSystemPath());
|
LoadSetAtPath(Device.FileSystem.GetSystemPath());
|
||||||
|
|
||||||
KeySet = ExternalKeys.ReadKeyFile(keyFile, titleKeyFile, consoleKeyFile);
|
KeySet = ExternalKeys.ReadKeyFile(KeyFile, TitleKeyFile, ConsoleKeyFile);
|
||||||
|
|
||||||
void LoadSetAtPath(string basePath)
|
void LoadSetAtPath(string BasePath)
|
||||||
{
|
{
|
||||||
string localKeyFile = Path.Combine(basePath, "prod.keys");
|
string LocalKeyFile = Path.Combine(BasePath, "prod.keys");
|
||||||
string localTitleKeyFile = Path.Combine(basePath, "title.keys");
|
string LocalTitleKeyFile = Path.Combine(BasePath, "title.keys");
|
||||||
string localConsoleKeyFile = Path.Combine(basePath, "console.keys");
|
string LocalConsoleKeyFile = Path.Combine(BasePath, "console.keys");
|
||||||
|
|
||||||
if (File.Exists(localKeyFile))
|
if (File.Exists(LocalKeyFile))
|
||||||
{
|
{
|
||||||
keyFile = localKeyFile;
|
KeyFile = LocalKeyFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (File.Exists(localTitleKeyFile))
|
if (File.Exists(LocalTitleKeyFile))
|
||||||
{
|
{
|
||||||
titleKeyFile = localTitleKeyFile;
|
TitleKeyFile = LocalTitleKeyFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (File.Exists(localConsoleKeyFile))
|
if (File.Exists(LocalConsoleKeyFile))
|
||||||
{
|
{
|
||||||
consoleKeyFile = localConsoleKeyFile;
|
ConsoleKeyFile = LocalConsoleKeyFile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -561,22 +561,22 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
internal long GetThreadUid()
|
internal long GetThreadUid()
|
||||||
{
|
{
|
||||||
return Interlocked.Increment(ref _threadUid) - 1;
|
return Interlocked.Increment(ref ThreadUid) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal long GetKipId()
|
internal long GetKipId()
|
||||||
{
|
{
|
||||||
return Interlocked.Increment(ref _kipId) - 1;
|
return Interlocked.Increment(ref KipId) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal long GetProcessId()
|
internal long GetProcessId()
|
||||||
{
|
{
|
||||||
return Interlocked.Increment(ref _processId) - 1;
|
return Interlocked.Increment(ref ProcessId) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnableMultiCoreScheduling()
|
public void EnableMultiCoreScheduling()
|
||||||
{
|
{
|
||||||
if (!_hasStarted)
|
if (!HasStarted)
|
||||||
{
|
{
|
||||||
Scheduler.MultiCoreScheduling = true;
|
Scheduler.MultiCoreScheduling = true;
|
||||||
}
|
}
|
||||||
|
@ -584,7 +584,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
public void DisableMultiCoreScheduling()
|
public void DisableMultiCoreScheduling()
|
||||||
{
|
{
|
||||||
if (!_hasStarted)
|
if (!HasStarted)
|
||||||
{
|
{
|
||||||
Scheduler.MultiCoreScheduling = false;
|
Scheduler.MultiCoreScheduling = false;
|
||||||
}
|
}
|
||||||
|
@ -595,16 +595,16 @@ namespace Ryujinx.HLE.HOS
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool Disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (Disposing)
|
||||||
{
|
{
|
||||||
//Force all threads to exit.
|
//Force all threads to exit.
|
||||||
lock (Processes)
|
lock (Processes)
|
||||||
{
|
{
|
||||||
foreach (KProcess process in Processes.Values)
|
foreach (KProcess Process in Processes.Values)
|
||||||
{
|
{
|
||||||
process.StopAllThreads();
|
Process.StopAllThreads();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,56 +6,56 @@ namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
class IdDictionary
|
class IdDictionary
|
||||||
{
|
{
|
||||||
private ConcurrentDictionary<int, object> _objs;
|
private ConcurrentDictionary<int, object> Objs;
|
||||||
|
|
||||||
public IdDictionary()
|
public IdDictionary()
|
||||||
{
|
{
|
||||||
_objs = new ConcurrentDictionary<int, object>();
|
Objs = new ConcurrentDictionary<int, object>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Add(int id, object data)
|
public bool Add(int Id, object Data)
|
||||||
{
|
{
|
||||||
return _objs.TryAdd(id, data);
|
return Objs.TryAdd(Id, Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Add(object data)
|
public int Add(object Data)
|
||||||
{
|
{
|
||||||
for (int id = 1; id < int.MaxValue; id++)
|
for (int Id = 1; Id < int.MaxValue; Id++)
|
||||||
{
|
{
|
||||||
if (_objs.TryAdd(id, data))
|
if (Objs.TryAdd(Id, Data))
|
||||||
{
|
{
|
||||||
return id;
|
return Id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public object GetData(int id)
|
public object GetData(int Id)
|
||||||
{
|
{
|
||||||
if (_objs.TryGetValue(id, out object data))
|
if (Objs.TryGetValue(Id, out object Data))
|
||||||
{
|
{
|
||||||
return data;
|
return Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T GetData<T>(int id)
|
public T GetData<T>(int Id)
|
||||||
{
|
{
|
||||||
if (_objs.TryGetValue(id, out object data) && data is T)
|
if (Objs.TryGetValue(Id, out object Data) && Data is T)
|
||||||
{
|
{
|
||||||
return (T)data;
|
return (T)Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
return default(T);
|
return default(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Delete(int id)
|
public object Delete(int Id)
|
||||||
{
|
{
|
||||||
if (_objs.TryRemove(id, out object obj))
|
if (Objs.TryRemove(Id, out object Obj))
|
||||||
{
|
{
|
||||||
return obj;
|
return Obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -63,11 +63,11 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
public ICollection<object> Clear()
|
public ICollection<object> Clear()
|
||||||
{
|
{
|
||||||
ICollection<object> values = _objs.Values;
|
ICollection<object> Values = Objs.Values;
|
||||||
|
|
||||||
_objs.Clear();
|
Objs.Clear();
|
||||||
|
|
||||||
return values;
|
return Values;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,24 +4,24 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
{
|
{
|
||||||
struct IpcBuffDesc
|
struct IpcBuffDesc
|
||||||
{
|
{
|
||||||
public long Position { get; }
|
public long Position { get; private set; }
|
||||||
public long Size { get; }
|
public long Size { get; private set; }
|
||||||
public int Flags { get; }
|
public int Flags { get; private set; }
|
||||||
|
|
||||||
public IpcBuffDesc(BinaryReader reader)
|
public IpcBuffDesc(BinaryReader Reader)
|
||||||
{
|
{
|
||||||
long word0 = reader.ReadUInt32();
|
long Word0 = Reader.ReadUInt32();
|
||||||
long word1 = reader.ReadUInt32();
|
long Word1 = Reader.ReadUInt32();
|
||||||
long word2 = reader.ReadUInt32();
|
long Word2 = Reader.ReadUInt32();
|
||||||
|
|
||||||
Position = word1;
|
Position = Word1;
|
||||||
Position |= (word2 << 4) & 0x0f00000000;
|
Position |= (Word2 << 4) & 0x0f00000000;
|
||||||
Position |= (word2 << 34) & 0x7000000000;
|
Position |= (Word2 << 34) & 0x7000000000;
|
||||||
|
|
||||||
Size = word0;
|
Size = Word0;
|
||||||
Size |= (word2 << 8) & 0xf00000000;
|
Size |= (Word2 << 8) & 0xf00000000;
|
||||||
|
|
||||||
Flags = (int)word2 & 3;
|
Flags = (int)Word2 & 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,87 +5,87 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
{
|
{
|
||||||
class IpcHandleDesc
|
class IpcHandleDesc
|
||||||
{
|
{
|
||||||
public bool HasPId { get; }
|
public bool HasPId { get; private set; }
|
||||||
|
|
||||||
public long PId { get; }
|
public long PId { get; private set; }
|
||||||
|
|
||||||
public int[] ToCopy { get; }
|
public int[] ToCopy { get; private set; }
|
||||||
public int[] ToMove { get; }
|
public int[] ToMove { get; private set; }
|
||||||
|
|
||||||
public IpcHandleDesc(BinaryReader reader)
|
public IpcHandleDesc(BinaryReader Reader)
|
||||||
{
|
{
|
||||||
int word = reader.ReadInt32();
|
int Word = Reader.ReadInt32();
|
||||||
|
|
||||||
HasPId = (word & 1) != 0;
|
HasPId = (Word & 1) != 0;
|
||||||
|
|
||||||
ToCopy = new int[(word >> 1) & 0xf];
|
ToCopy = new int[(Word >> 1) & 0xf];
|
||||||
ToMove = new int[(word >> 5) & 0xf];
|
ToMove = new int[(Word >> 5) & 0xf];
|
||||||
|
|
||||||
PId = HasPId ? reader.ReadInt64() : 0;
|
PId = HasPId ? Reader.ReadInt64() : 0;
|
||||||
|
|
||||||
for (int index = 0; index < ToCopy.Length; index++)
|
for (int Index = 0; Index < ToCopy.Length; Index++)
|
||||||
{
|
{
|
||||||
ToCopy[index] = reader.ReadInt32();
|
ToCopy[Index] = Reader.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int index = 0; index < ToMove.Length; index++)
|
for (int Index = 0; Index < ToMove.Length; Index++)
|
||||||
{
|
{
|
||||||
ToMove[index] = reader.ReadInt32();
|
ToMove[Index] = Reader.ReadInt32();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IpcHandleDesc(int[] copy, int[] move)
|
public IpcHandleDesc(int[] Copy, int[] Move)
|
||||||
{
|
{
|
||||||
ToCopy = copy ?? throw new ArgumentNullException(nameof(copy));
|
ToCopy = Copy ?? throw new ArgumentNullException(nameof(Copy));
|
||||||
ToMove = move ?? throw new ArgumentNullException(nameof(move));
|
ToMove = Move ?? throw new ArgumentNullException(nameof(Move));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IpcHandleDesc(int[] copy, int[] move, long pId) : this(copy, move)
|
public IpcHandleDesc(int[] Copy, int[] Move, long PId) : this(Copy, Move)
|
||||||
{
|
{
|
||||||
PId = pId;
|
this.PId = PId;
|
||||||
|
|
||||||
HasPId = true;
|
HasPId = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IpcHandleDesc MakeCopy(params int[] handles)
|
public static IpcHandleDesc MakeCopy(params int[] Handles)
|
||||||
{
|
{
|
||||||
return new IpcHandleDesc(handles, new int[0]);
|
return new IpcHandleDesc(Handles, new int[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IpcHandleDesc MakeMove(params int[] handles)
|
public static IpcHandleDesc MakeMove(params int[] Handles)
|
||||||
{
|
{
|
||||||
return new IpcHandleDesc(new int[0], handles);
|
return new IpcHandleDesc(new int[0], Handles);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] GetBytes()
|
public byte[] GetBytes()
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream())
|
using (MemoryStream MS = new MemoryStream())
|
||||||
{
|
{
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
BinaryWriter Writer = new BinaryWriter(MS);
|
||||||
|
|
||||||
int word = HasPId ? 1 : 0;
|
int Word = HasPId ? 1 : 0;
|
||||||
|
|
||||||
word |= (ToCopy.Length & 0xf) << 1;
|
Word |= (ToCopy.Length & 0xf) << 1;
|
||||||
word |= (ToMove.Length & 0xf) << 5;
|
Word |= (ToMove.Length & 0xf) << 5;
|
||||||
|
|
||||||
writer.Write(word);
|
Writer.Write(Word);
|
||||||
|
|
||||||
if (HasPId)
|
if (HasPId)
|
||||||
{
|
{
|
||||||
writer.Write(PId);
|
Writer.Write((long)PId);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (int handle in ToCopy)
|
foreach (int Handle in ToCopy)
|
||||||
{
|
{
|
||||||
writer.Write(handle);
|
Writer.Write(Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (int handle in ToMove)
|
foreach (int Handle in ToMove)
|
||||||
{
|
{
|
||||||
writer.Write(handle);
|
Writer.Write(Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ms.ToArray();
|
return MS.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,61 +8,61 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
static class IpcHandler
|
static class IpcHandler
|
||||||
{
|
{
|
||||||
public static long IpcCall(
|
public static long IpcCall(
|
||||||
Switch device,
|
Switch Device,
|
||||||
KProcess process,
|
KProcess Process,
|
||||||
MemoryManager memory,
|
MemoryManager Memory,
|
||||||
KSession session,
|
KSession Session,
|
||||||
IpcMessage request,
|
IpcMessage Request,
|
||||||
long cmdPtr)
|
long CmdPtr)
|
||||||
{
|
{
|
||||||
IpcMessage response = new IpcMessage();
|
IpcMessage Response = new IpcMessage();
|
||||||
|
|
||||||
using (MemoryStream raw = new MemoryStream(request.RawData))
|
using (MemoryStream Raw = new MemoryStream(Request.RawData))
|
||||||
{
|
{
|
||||||
BinaryReader reqReader = new BinaryReader(raw);
|
BinaryReader ReqReader = new BinaryReader(Raw);
|
||||||
|
|
||||||
if (request.Type == IpcMessageType.Request ||
|
if (Request.Type == IpcMessageType.Request ||
|
||||||
request.Type == IpcMessageType.RequestWithContext)
|
Request.Type == IpcMessageType.RequestWithContext)
|
||||||
{
|
{
|
||||||
response.Type = IpcMessageType.Response;
|
Response.Type = IpcMessageType.Response;
|
||||||
|
|
||||||
using (MemoryStream resMs = new MemoryStream())
|
using (MemoryStream ResMS = new MemoryStream())
|
||||||
{
|
{
|
||||||
BinaryWriter resWriter = new BinaryWriter(resMs);
|
BinaryWriter ResWriter = new BinaryWriter(ResMS);
|
||||||
|
|
||||||
ServiceCtx context = new ServiceCtx(
|
ServiceCtx Context = new ServiceCtx(
|
||||||
device,
|
Device,
|
||||||
process,
|
Process,
|
||||||
memory,
|
Memory,
|
||||||
session,
|
Session,
|
||||||
request,
|
Request,
|
||||||
response,
|
Response,
|
||||||
reqReader,
|
ReqReader,
|
||||||
resWriter);
|
ResWriter);
|
||||||
|
|
||||||
session.Service.CallMethod(context);
|
Session.Service.CallMethod(Context);
|
||||||
|
|
||||||
response.RawData = resMs.ToArray();
|
Response.RawData = ResMS.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (request.Type == IpcMessageType.Control ||
|
else if (Request.Type == IpcMessageType.Control ||
|
||||||
request.Type == IpcMessageType.ControlWithContext)
|
Request.Type == IpcMessageType.ControlWithContext)
|
||||||
{
|
{
|
||||||
long magic = reqReader.ReadInt64();
|
long Magic = ReqReader.ReadInt64();
|
||||||
long cmdId = reqReader.ReadInt64();
|
long CmdId = ReqReader.ReadInt64();
|
||||||
|
|
||||||
switch (cmdId)
|
switch (CmdId)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
request = FillResponse(response, 0, session.Service.ConvertToDomain());
|
Request = FillResponse(Response, 0, Session.Service.ConvertToDomain());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
{
|
{
|
||||||
request = FillResponse(response, 0, 0x500);
|
Request = FillResponse(Response, 0, 0x500);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -71,73 +71,73 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
case 2:
|
case 2:
|
||||||
case 4:
|
case 4:
|
||||||
{
|
{
|
||||||
int unknown = reqReader.ReadInt32();
|
int Unknown = ReqReader.ReadInt32();
|
||||||
|
|
||||||
if (process.HandleTable.GenerateHandle(session, out int handle) != KernelResult.Success)
|
if (Process.HandleTable.GenerateHandle(Session, out int Handle) != KernelResult.Success)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Out of handles!");
|
throw new InvalidOperationException("Out of handles!");
|
||||||
}
|
}
|
||||||
|
|
||||||
response.HandleDesc = IpcHandleDesc.MakeMove(handle);
|
Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
|
||||||
|
|
||||||
request = FillResponse(response, 0);
|
Request = FillResponse(Response, 0);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: throw new NotImplementedException(cmdId.ToString());
|
default: throw new NotImplementedException(CmdId.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (request.Type == IpcMessageType.CloseSession)
|
else if (Request.Type == IpcMessageType.CloseSession)
|
||||||
{
|
{
|
||||||
//TODO
|
//TODO
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new NotImplementedException(request.Type.ToString());
|
throw new NotImplementedException(Request.Type.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
memory.WriteBytes(cmdPtr, response.GetBytes(cmdPtr));
|
Memory.WriteBytes(CmdPtr, Response.GetBytes(CmdPtr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values)
|
private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values)
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream())
|
using (MemoryStream MS = new MemoryStream())
|
||||||
{
|
{
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
BinaryWriter Writer = new BinaryWriter(MS);
|
||||||
|
|
||||||
foreach (int value in values)
|
foreach (int Value in Values)
|
||||||
{
|
{
|
||||||
writer.Write(value);
|
Writer.Write(Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return FillResponse(response, result, ms.ToArray());
|
return FillResponse(Response, Result, MS.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IpcMessage FillResponse(IpcMessage response, long result, byte[] data = null)
|
private static IpcMessage FillResponse(IpcMessage Response, long Result, byte[] Data = null)
|
||||||
{
|
{
|
||||||
response.Type = IpcMessageType.Response;
|
Response.Type = IpcMessageType.Response;
|
||||||
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
using (MemoryStream MS = new MemoryStream())
|
||||||
{
|
{
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
BinaryWriter Writer = new BinaryWriter(MS);
|
||||||
|
|
||||||
writer.Write(IpcMagic.Sfco);
|
Writer.Write(IpcMagic.Sfco);
|
||||||
writer.Write(result);
|
Writer.Write(Result);
|
||||||
|
|
||||||
if (data != null)
|
if (Data != null)
|
||||||
{
|
{
|
||||||
writer.Write(data);
|
Writer.Write(Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.RawData = ms.ToArray();
|
Response.RawData = MS.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return Response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,13 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
|
|
||||||
public IpcHandleDesc HandleDesc { get; set; }
|
public IpcHandleDesc HandleDesc { get; set; }
|
||||||
|
|
||||||
public List<IpcPtrBuffDesc> PtrBuff { get; }
|
public List<IpcPtrBuffDesc> PtrBuff { get; private set; }
|
||||||
public List<IpcBuffDesc> SendBuff { get; }
|
public List<IpcBuffDesc> SendBuff { get; private set; }
|
||||||
public List<IpcBuffDesc> ReceiveBuff { get; }
|
public List<IpcBuffDesc> ReceiveBuff { get; private set; }
|
||||||
public List<IpcBuffDesc> ExchangeBuff { get; }
|
public List<IpcBuffDesc> ExchangeBuff { get; private set; }
|
||||||
public List<IpcRecvListBuffDesc> RecvListBuff { get; }
|
public List<IpcRecvListBuffDesc> RecvListBuff { get; private set; }
|
||||||
|
|
||||||
public List<int> ObjectIds { get; }
|
public List<int> ObjectIds { get; private set; }
|
||||||
|
|
||||||
public byte[] RawData { get; set; }
|
public byte[] RawData { get; set; }
|
||||||
|
|
||||||
|
@ -30,185 +30,183 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
ObjectIds = new List<int>();
|
ObjectIds = new List<int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IpcMessage(byte[] data, long cmdPtr) : this()
|
public IpcMessage(byte[] Data, long CmdPtr) : this()
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream(data))
|
using (MemoryStream MS = new MemoryStream(Data))
|
||||||
{
|
{
|
||||||
BinaryReader reader = new BinaryReader(ms);
|
BinaryReader Reader = new BinaryReader(MS);
|
||||||
|
|
||||||
Initialize(reader, cmdPtr);
|
Initialize(Reader, CmdPtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Initialize(BinaryReader reader, long cmdPtr)
|
private void Initialize(BinaryReader Reader, long CmdPtr)
|
||||||
{
|
{
|
||||||
int word0 = reader.ReadInt32();
|
int Word0 = Reader.ReadInt32();
|
||||||
int word1 = reader.ReadInt32();
|
int Word1 = Reader.ReadInt32();
|
||||||
|
|
||||||
Type = (IpcMessageType)(word0 & 0xffff);
|
Type = (IpcMessageType)(Word0 & 0xffff);
|
||||||
|
|
||||||
int ptrBuffCount = (word0 >> 16) & 0xf;
|
int PtrBuffCount = (Word0 >> 16) & 0xf;
|
||||||
int sendBuffCount = (word0 >> 20) & 0xf;
|
int SendBuffCount = (Word0 >> 20) & 0xf;
|
||||||
int recvBuffCount = (word0 >> 24) & 0xf;
|
int RecvBuffCount = (Word0 >> 24) & 0xf;
|
||||||
int xchgBuffCount = (word0 >> 28) & 0xf;
|
int XchgBuffCount = (Word0 >> 28) & 0xf;
|
||||||
|
|
||||||
int rawDataSize = (word1 >> 0) & 0x3ff;
|
int RawDataSize = (Word1 >> 0) & 0x3ff;
|
||||||
int recvListFlags = (word1 >> 10) & 0xf;
|
int RecvListFlags = (Word1 >> 10) & 0xf;
|
||||||
bool hndDescEnable = ((word1 >> 31) & 0x1) != 0;
|
bool HndDescEnable = ((Word1 >> 31) & 0x1) != 0;
|
||||||
|
|
||||||
if (hndDescEnable)
|
if (HndDescEnable)
|
||||||
{
|
{
|
||||||
HandleDesc = new IpcHandleDesc(reader);
|
HandleDesc = new IpcHandleDesc(Reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int index = 0; index < ptrBuffCount; index++)
|
for (int Index = 0; Index < PtrBuffCount; Index++)
|
||||||
{
|
{
|
||||||
PtrBuff.Add(new IpcPtrBuffDesc(reader));
|
PtrBuff.Add(new IpcPtrBuffDesc(Reader));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadBuff(List<IpcBuffDesc> buff, int count)
|
void ReadBuff(List<IpcBuffDesc> Buff, int Count)
|
||||||
{
|
{
|
||||||
for (int index = 0; index < count; index++)
|
for (int Index = 0; Index < Count; Index++)
|
||||||
{
|
{
|
||||||
buff.Add(new IpcBuffDesc(reader));
|
Buff.Add(new IpcBuffDesc(Reader));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadBuff(SendBuff, sendBuffCount);
|
ReadBuff(SendBuff, SendBuffCount);
|
||||||
ReadBuff(ReceiveBuff, recvBuffCount);
|
ReadBuff(ReceiveBuff, RecvBuffCount);
|
||||||
ReadBuff(ExchangeBuff, xchgBuffCount);
|
ReadBuff(ExchangeBuff, XchgBuffCount);
|
||||||
|
|
||||||
rawDataSize *= 4;
|
RawDataSize *= 4;
|
||||||
|
|
||||||
long recvListPos = reader.BaseStream.Position + rawDataSize;
|
long RecvListPos = Reader.BaseStream.Position + RawDataSize;
|
||||||
|
|
||||||
long pad0 = GetPadSize16(reader.BaseStream.Position + cmdPtr);
|
long Pad0 = GetPadSize16(Reader.BaseStream.Position + CmdPtr);
|
||||||
|
|
||||||
reader.BaseStream.Seek(pad0, SeekOrigin.Current);
|
Reader.BaseStream.Seek(Pad0, SeekOrigin.Current);
|
||||||
|
|
||||||
int recvListCount = recvListFlags - 2;
|
int RecvListCount = RecvListFlags - 2;
|
||||||
|
|
||||||
if (recvListCount == 0)
|
if (RecvListCount == 0)
|
||||||
{
|
{
|
||||||
recvListCount = 1;
|
RecvListCount = 1;
|
||||||
}
|
}
|
||||||
else if (recvListCount < 0)
|
else if (RecvListCount < 0)
|
||||||
{
|
{
|
||||||
recvListCount = 0;
|
RecvListCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
RawData = reader.ReadBytes(rawDataSize);
|
RawData = Reader.ReadBytes(RawDataSize);
|
||||||
|
|
||||||
reader.BaseStream.Seek(recvListPos, SeekOrigin.Begin);
|
Reader.BaseStream.Seek(RecvListPos, SeekOrigin.Begin);
|
||||||
|
|
||||||
for (int index = 0; index < recvListCount; index++)
|
for (int Index = 0; Index < RecvListCount; Index++)
|
||||||
{
|
{
|
||||||
RecvListBuff.Add(new IpcRecvListBuffDesc(reader));
|
RecvListBuff.Add(new IpcRecvListBuffDesc(Reader));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] GetBytes(long cmdPtr)
|
public byte[] GetBytes(long CmdPtr)
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream())
|
using (MemoryStream MS = new MemoryStream())
|
||||||
{
|
{
|
||||||
BinaryWriter writer = new BinaryWriter(ms);
|
BinaryWriter Writer = new BinaryWriter(MS);
|
||||||
|
|
||||||
int word0;
|
int Word0;
|
||||||
int word1;
|
int Word1;
|
||||||
|
|
||||||
word0 = (int)Type;
|
Word0 = (int)Type;
|
||||||
word0 |= (PtrBuff.Count & 0xf) << 16;
|
Word0 |= (PtrBuff.Count & 0xf) << 16;
|
||||||
word0 |= (SendBuff.Count & 0xf) << 20;
|
Word0 |= (SendBuff.Count & 0xf) << 20;
|
||||||
word0 |= (ReceiveBuff.Count & 0xf) << 24;
|
Word0 |= (ReceiveBuff.Count & 0xf) << 24;
|
||||||
word0 |= (ExchangeBuff.Count & 0xf) << 28;
|
Word0 |= (ExchangeBuff.Count & 0xf) << 28;
|
||||||
|
|
||||||
byte[] handleData = new byte[0];
|
byte[] HandleData = new byte[0];
|
||||||
|
|
||||||
if (HandleDesc != null)
|
if (HandleDesc != null)
|
||||||
{
|
{
|
||||||
handleData = HandleDesc.GetBytes();
|
HandleData = HandleDesc.GetBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
int dataLength = RawData?.Length ?? 0;
|
int DataLength = RawData?.Length ?? 0;
|
||||||
|
|
||||||
int pad0 = (int)GetPadSize16(cmdPtr + 8 + handleData.Length);
|
int Pad0 = (int)GetPadSize16(CmdPtr + 8 + HandleData.Length);
|
||||||
|
|
||||||
//Apparently, padding after Raw Data is 16 bytes, however when there is
|
//Apparently, padding after Raw Data is 16 bytes, however when there is
|
||||||
//padding before Raw Data too, we need to subtract the size of this padding.
|
//padding before Raw Data too, we need to subtract the size of this padding.
|
||||||
//This is the weirdest padding I've seen so far...
|
//This is the weirdest padding I've seen so far...
|
||||||
int pad1 = 0x10 - pad0;
|
int Pad1 = 0x10 - Pad0;
|
||||||
|
|
||||||
dataLength = (dataLength + pad0 + pad1) / 4;
|
DataLength = (DataLength + Pad0 + Pad1) / 4;
|
||||||
|
|
||||||
word1 = dataLength & 0x3ff;
|
Word1 = DataLength & 0x3ff;
|
||||||
|
|
||||||
if (HandleDesc != null)
|
if (HandleDesc != null)
|
||||||
{
|
{
|
||||||
word1 |= 1 << 31;
|
Word1 |= 1 << 31;
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write(word0);
|
Writer.Write(Word0);
|
||||||
writer.Write(word1);
|
Writer.Write(Word1);
|
||||||
writer.Write(handleData);
|
Writer.Write(HandleData);
|
||||||
|
|
||||||
ms.Seek(pad0, SeekOrigin.Current);
|
MS.Seek(Pad0, SeekOrigin.Current);
|
||||||
|
|
||||||
if (RawData != null)
|
if (RawData != null)
|
||||||
{
|
{
|
||||||
writer.Write(RawData);
|
Writer.Write(RawData);
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write(new byte[pad1]);
|
Writer.Write(new byte[Pad1]);
|
||||||
|
|
||||||
return ms.ToArray();
|
return MS.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private long GetPadSize16(long position)
|
private long GetPadSize16(long Position)
|
||||||
{
|
{
|
||||||
if ((position & 0xf) != 0)
|
if ((Position & 0xf) != 0)
|
||||||
{
|
{
|
||||||
return 0x10 - (position & 0xf);
|
return 0x10 - (Position & 0xf);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReSharper disable once InconsistentNaming
|
public (long Position, long Size) GetBufferType0x21(int Index = 0)
|
||||||
public (long Position, long Size) GetBufferType0x21(int index = 0)
|
|
||||||
{
|
{
|
||||||
if (PtrBuff.Count > index &&
|
if (PtrBuff.Count > Index &&
|
||||||
PtrBuff[index].Position != 0 &&
|
PtrBuff[Index].Position != 0 &&
|
||||||
PtrBuff[index].Size != 0)
|
PtrBuff[Index].Size != 0)
|
||||||
{
|
{
|
||||||
return (PtrBuff[index].Position, PtrBuff[index].Size);
|
return (PtrBuff[Index].Position, PtrBuff[Index].Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SendBuff.Count > index &&
|
if (SendBuff.Count > Index &&
|
||||||
SendBuff[index].Position != 0 &&
|
SendBuff[Index].Position != 0 &&
|
||||||
SendBuff[index].Size != 0)
|
SendBuff[Index].Size != 0)
|
||||||
{
|
{
|
||||||
return (SendBuff[index].Position, SendBuff[index].Size);
|
return (SendBuff[Index].Position, SendBuff[Index].Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (0, 0);
|
return (0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReSharper disable once InconsistentNaming
|
public (long Position, long Size) GetBufferType0x22(int Index = 0)
|
||||||
public (long Position, long Size) GetBufferType0x22(int index = 0)
|
|
||||||
{
|
{
|
||||||
if (RecvListBuff.Count > index &&
|
if (RecvListBuff.Count > Index &&
|
||||||
RecvListBuff[index].Position != 0 &&
|
RecvListBuff[Index].Position != 0 &&
|
||||||
RecvListBuff[index].Size != 0)
|
RecvListBuff[Index].Size != 0)
|
||||||
{
|
{
|
||||||
return (RecvListBuff[index].Position, RecvListBuff[index].Size);
|
return (RecvListBuff[Index].Position, RecvListBuff[Index].Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ReceiveBuff.Count > index &&
|
if (ReceiveBuff.Count > Index &&
|
||||||
ReceiveBuff[index].Position != 0 &&
|
ReceiveBuff[Index].Position != 0 &&
|
||||||
ReceiveBuff[index].Size != 0)
|
ReceiveBuff[Index].Size != 0)
|
||||||
{
|
{
|
||||||
return (ReceiveBuff[index].Position, ReceiveBuff[index].Size);
|
return (ReceiveBuff[Index].Position, ReceiveBuff[Index].Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (0, 0);
|
return (0, 0);
|
||||||
|
|
|
@ -4,23 +4,23 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
{
|
{
|
||||||
struct IpcPtrBuffDesc
|
struct IpcPtrBuffDesc
|
||||||
{
|
{
|
||||||
public long Position { get; }
|
public long Position { get; private set; }
|
||||||
public int Index { get; }
|
public int Index { get; private set; }
|
||||||
public long Size { get; }
|
public long Size { get; private set; }
|
||||||
|
|
||||||
public IpcPtrBuffDesc(BinaryReader reader)
|
public IpcPtrBuffDesc(BinaryReader Reader)
|
||||||
{
|
{
|
||||||
long word0 = reader.ReadUInt32();
|
long Word0 = Reader.ReadUInt32();
|
||||||
long word1 = reader.ReadUInt32();
|
long Word1 = Reader.ReadUInt32();
|
||||||
|
|
||||||
Position = word1;
|
Position = Word1;
|
||||||
Position |= (word0 << 20) & 0x0f00000000;
|
Position |= (Word0 << 20) & 0x0f00000000;
|
||||||
Position |= (word0 << 30) & 0x7000000000;
|
Position |= (Word0 << 30) & 0x7000000000;
|
||||||
|
|
||||||
Index = ((int)word0 >> 0) & 0x03f;
|
Index = ((int)Word0 >> 0) & 0x03f;
|
||||||
Index |= ((int)word0 >> 3) & 0x1c0;
|
Index |= ((int)Word0 >> 3) & 0x1c0;
|
||||||
|
|
||||||
Size = (ushort)(word0 >> 16);
|
Size = (ushort)(Word0 >> 16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,16 +4,16 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
{
|
{
|
||||||
struct IpcRecvListBuffDesc
|
struct IpcRecvListBuffDesc
|
||||||
{
|
{
|
||||||
public long Position { get; }
|
public long Position { get; private set; }
|
||||||
public long Size { get; }
|
public long Size { get; private set; }
|
||||||
|
|
||||||
public IpcRecvListBuffDesc(BinaryReader reader)
|
public IpcRecvListBuffDesc(BinaryReader Reader)
|
||||||
{
|
{
|
||||||
long value = reader.ReadInt64();
|
long Value = Reader.ReadInt64();
|
||||||
|
|
||||||
Position = value & 0xffffffffffff;
|
Position = Value & 0xffffffffffff;
|
||||||
|
|
||||||
Size = (ushort)(value >> 48);
|
Size = (ushort)(Value >> 48);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Ipc
|
namespace Ryujinx.HLE.HOS.Ipc
|
||||||
{
|
{
|
||||||
delegate long ServiceProcessRequest(ServiceCtx context);
|
delegate long ServiceProcessRequest(ServiceCtx Context);
|
||||||
}
|
}
|
|
@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
private class PausableThread
|
private class PausableThread
|
||||||
{
|
{
|
||||||
public ManualResetEvent Event { get; }
|
public ManualResetEvent Event { get; private set; }
|
||||||
|
|
||||||
public bool IsExiting { get; set; }
|
public bool IsExiting { get; set; }
|
||||||
|
|
||||||
|
@ -17,49 +17,49 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConcurrentDictionary<Thread, PausableThread> _threads;
|
private ConcurrentDictionary<Thread, PausableThread> Threads;
|
||||||
|
|
||||||
public HleCoreManager()
|
public HleCoreManager()
|
||||||
{
|
{
|
||||||
_threads = new ConcurrentDictionary<Thread, PausableThread>();
|
Threads = new ConcurrentDictionary<Thread, PausableThread>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Set(Thread thread)
|
public void Set(Thread Thread)
|
||||||
{
|
{
|
||||||
GetThread(thread).Event.Set();
|
GetThread(Thread).Event.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset(Thread thread)
|
public void Reset(Thread Thread)
|
||||||
{
|
{
|
||||||
GetThread(thread).Event.Reset();
|
GetThread(Thread).Event.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Wait(Thread thread)
|
public void Wait(Thread Thread)
|
||||||
{
|
{
|
||||||
PausableThread pausableThread = GetThread(thread);
|
PausableThread PausableThread = GetThread(Thread);
|
||||||
|
|
||||||
if (!pausableThread.IsExiting)
|
if (!PausableThread.IsExiting)
|
||||||
{
|
{
|
||||||
pausableThread.Event.WaitOne();
|
PausableThread.Event.WaitOne();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Exit(Thread thread)
|
public void Exit(Thread Thread)
|
||||||
{
|
{
|
||||||
GetThread(thread).IsExiting = true;
|
GetThread(Thread).IsExiting = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PausableThread GetThread(Thread thread)
|
private PausableThread GetThread(Thread Thread)
|
||||||
{
|
{
|
||||||
return _threads.GetOrAdd(thread, (key) => new PausableThread());
|
return Threads.GetOrAdd(Thread, (Key) => new PausableThread());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveThread(Thread thread)
|
public void RemoveThread(Thread Thread)
|
||||||
{
|
{
|
||||||
if (_threads.TryRemove(thread, out PausableThread pausableThread))
|
if (Threads.TryRemove(Thread, out PausableThread PausableThread))
|
||||||
{
|
{
|
||||||
pausableThread.Event.Set();
|
PausableThread.Event.Set();
|
||||||
pausableThread.Event.Dispose();
|
PausableThread.Event.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,138 +14,138 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
private const int Mod0 = 'M' << 0 | 'O' << 8 | 'D' << 16 | '0' << 24;
|
private const int Mod0 = 'M' << 0 | 'O' << 8 | 'D' << 16 | '0' << 24;
|
||||||
|
|
||||||
private KProcess _owner;
|
private KProcess Owner;
|
||||||
|
|
||||||
private class Image
|
private class Image
|
||||||
{
|
{
|
||||||
public long BaseAddress { get; }
|
public long BaseAddress { get; private set; }
|
||||||
|
|
||||||
public ElfSymbol[] Symbols { get; }
|
public ElfSymbol[] Symbols { get; private set; }
|
||||||
|
|
||||||
public Image(long baseAddress, ElfSymbol[] symbols)
|
public Image(long BaseAddress, ElfSymbol[] Symbols)
|
||||||
{
|
{
|
||||||
BaseAddress = baseAddress;
|
this.BaseAddress = BaseAddress;
|
||||||
Symbols = symbols;
|
this.Symbols = Symbols;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Image> _images;
|
private List<Image> Images;
|
||||||
|
|
||||||
private int _loaded;
|
private int Loaded;
|
||||||
|
|
||||||
public HleProcessDebugger(KProcess owner)
|
public HleProcessDebugger(KProcess Owner)
|
||||||
{
|
{
|
||||||
_owner = owner;
|
this.Owner = Owner;
|
||||||
|
|
||||||
_images = new List<Image>();
|
Images = new List<Image>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrintGuestStackTrace(CpuThreadState threadState)
|
public void PrintGuestStackTrace(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
EnsureLoaded();
|
EnsureLoaded();
|
||||||
|
|
||||||
StringBuilder trace = new StringBuilder();
|
StringBuilder Trace = new StringBuilder();
|
||||||
|
|
||||||
trace.AppendLine("Guest stack trace:");
|
Trace.AppendLine("Guest stack trace:");
|
||||||
|
|
||||||
void AppendTrace(long address)
|
void AppendTrace(long Address)
|
||||||
{
|
{
|
||||||
Image image = GetImage(address, out int imageIndex);
|
Image Image = GetImage(Address, out int ImageIndex);
|
||||||
|
|
||||||
if (image == null || !TryGetSubName(image, address, out string subName))
|
if (Image == null || !TryGetSubName(Image, Address, out string SubName))
|
||||||
{
|
{
|
||||||
subName = $"Sub{address:x16}";
|
SubName = $"Sub{Address:x16}";
|
||||||
}
|
}
|
||||||
else if (subName.StartsWith("_Z"))
|
else if (SubName.StartsWith("_Z"))
|
||||||
{
|
{
|
||||||
subName = Demangler.Parse(subName);
|
SubName = Demangler.Parse(SubName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image != null)
|
if (Image != null)
|
||||||
{
|
{
|
||||||
long offset = address - image.BaseAddress;
|
long Offset = Address - Image.BaseAddress;
|
||||||
|
|
||||||
string imageName = GetGuessedNsoNameFromIndex(imageIndex);
|
string ImageName = GetGuessedNsoNameFromIndex(ImageIndex);
|
||||||
|
|
||||||
string imageNameAndOffset = $"[{_owner.Name}] {imageName}:0x{offset:x8}";
|
string ImageNameAndOffset = $"[{Owner.Name}] {ImageName}:0x{Offset:x8}";
|
||||||
|
|
||||||
trace.AppendLine($" {imageNameAndOffset} {subName}");
|
Trace.AppendLine($" {ImageNameAndOffset} {SubName}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
trace.AppendLine($" [{_owner.Name}] ??? {subName}");
|
Trace.AppendLine($" [{Owner.Name}] ??? {SubName}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long framePointer = (long)threadState.X29;
|
long FramePointer = (long)ThreadState.X29;
|
||||||
|
|
||||||
while (framePointer != 0)
|
while (FramePointer != 0)
|
||||||
{
|
{
|
||||||
if ((framePointer & 7) != 0 ||
|
if ((FramePointer & 7) != 0 ||
|
||||||
!_owner.CpuMemory.IsMapped(framePointer) ||
|
!Owner.CpuMemory.IsMapped(FramePointer) ||
|
||||||
!_owner.CpuMemory.IsMapped(framePointer + 8))
|
!Owner.CpuMemory.IsMapped(FramePointer + 8))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Note: This is the return address, we need to subtract one instruction
|
//Note: This is the return address, we need to subtract one instruction
|
||||||
//worth of bytes to get the branch instruction address.
|
//worth of bytes to get the branch instruction address.
|
||||||
AppendTrace(_owner.CpuMemory.ReadInt64(framePointer + 8) - 4);
|
AppendTrace(Owner.CpuMemory.ReadInt64(FramePointer + 8) - 4);
|
||||||
|
|
||||||
framePointer = _owner.CpuMemory.ReadInt64(framePointer);
|
FramePointer = Owner.CpuMemory.ReadInt64(FramePointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.PrintInfo(LogClass.Cpu, trace.ToString());
|
Logger.PrintInfo(LogClass.Cpu, Trace.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetSubName(Image image, long address, out string name)
|
private bool TryGetSubName(Image Image, long Address, out string Name)
|
||||||
{
|
{
|
||||||
address -= image.BaseAddress;
|
Address -= Image.BaseAddress;
|
||||||
|
|
||||||
int left = 0;
|
int Left = 0;
|
||||||
int right = image.Symbols.Length - 1;
|
int Right = Image.Symbols.Length - 1;
|
||||||
|
|
||||||
while (left <= right)
|
while (Left <= Right)
|
||||||
{
|
{
|
||||||
int size = right - left;
|
int Size = Right - Left;
|
||||||
|
|
||||||
int middle = left + (size >> 1);
|
int Middle = Left + (Size >> 1);
|
||||||
|
|
||||||
ElfSymbol symbol = image.Symbols[middle];
|
ElfSymbol Symbol = Image.Symbols[Middle];
|
||||||
|
|
||||||
long endAddr = symbol.Value + symbol.Size;
|
long EndAddr = Symbol.Value + Symbol.Size;
|
||||||
|
|
||||||
if ((ulong)address >= (ulong)symbol.Value && (ulong)address < (ulong)endAddr)
|
if ((ulong)Address >= (ulong)Symbol.Value && (ulong)Address < (ulong)EndAddr)
|
||||||
{
|
{
|
||||||
name = symbol.Name;
|
Name = Symbol.Name;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ulong)address < (ulong)symbol.Value)
|
if ((ulong)Address < (ulong)Symbol.Value)
|
||||||
{
|
{
|
||||||
right = middle - 1;
|
Right = Middle - 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
left = middle + 1;
|
Left = Middle + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
name = null;
|
Name = null;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Image GetImage(long address, out int index)
|
private Image GetImage(long Address, out int Index)
|
||||||
{
|
{
|
||||||
lock (_images)
|
lock (Images)
|
||||||
{
|
{
|
||||||
for (index = _images.Count - 1; index >= 0; index--)
|
for (Index = Images.Count - 1; Index >= 0; Index--)
|
||||||
{
|
{
|
||||||
if ((ulong)address >= (ulong)_images[index].BaseAddress)
|
if ((ulong)Address >= (ulong)Images[Index].BaseAddress)
|
||||||
{
|
{
|
||||||
return _images[index];
|
return Images[Index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,42 +153,42 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetGuessedNsoNameFromIndex(int index)
|
private string GetGuessedNsoNameFromIndex(int Index)
|
||||||
{
|
{
|
||||||
if ((uint)index > 11)
|
if ((uint)Index > 11)
|
||||||
{
|
{
|
||||||
return "???";
|
return "???";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index == 0)
|
if (Index == 0)
|
||||||
{
|
{
|
||||||
return "rtld";
|
return "rtld";
|
||||||
}
|
}
|
||||||
else if (index == 1)
|
else if (Index == 1)
|
||||||
{
|
{
|
||||||
return "main";
|
return "main";
|
||||||
}
|
}
|
||||||
else if (index == GetImagesCount() - 1)
|
else if (Index == GetImagesCount() - 1)
|
||||||
{
|
{
|
||||||
return "sdk";
|
return "sdk";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return "subsdk" + (index - 2);
|
return "subsdk" + (Index - 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetImagesCount()
|
private int GetImagesCount()
|
||||||
{
|
{
|
||||||
lock (_images)
|
lock (Images)
|
||||||
{
|
{
|
||||||
return _images.Count;
|
return Images.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnsureLoaded()
|
private void EnsureLoaded()
|
||||||
{
|
{
|
||||||
if (Interlocked.CompareExchange(ref _loaded, 1, 0) == 0)
|
if (Interlocked.CompareExchange(ref Loaded, 1, 0) == 0)
|
||||||
{
|
{
|
||||||
ScanMemoryForTextSegments();
|
ScanMemoryForTextSegments();
|
||||||
}
|
}
|
||||||
|
@ -196,115 +196,115 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private void ScanMemoryForTextSegments()
|
private void ScanMemoryForTextSegments()
|
||||||
{
|
{
|
||||||
ulong oldAddress = 0;
|
ulong OldAddress = 0;
|
||||||
ulong address = 0;
|
ulong Address = 0;
|
||||||
|
|
||||||
while (address >= oldAddress)
|
while (Address >= OldAddress)
|
||||||
{
|
{
|
||||||
KMemoryInfo info = _owner.MemoryManager.QueryMemory(address);
|
KMemoryInfo Info = Owner.MemoryManager.QueryMemory(Address);
|
||||||
|
|
||||||
if (info.State == MemoryState.Reserved)
|
if (Info.State == MemoryState.Reserved)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.State == MemoryState.CodeStatic && info.Permission == MemoryPermission.ReadAndExecute)
|
if (Info.State == MemoryState.CodeStatic && Info.Permission == MemoryPermission.ReadAndExecute)
|
||||||
{
|
{
|
||||||
LoadMod0Symbols(_owner.CpuMemory, (long)info.Address);
|
LoadMod0Symbols(Owner.CpuMemory, (long)Info.Address);
|
||||||
}
|
}
|
||||||
|
|
||||||
oldAddress = address;
|
OldAddress = Address;
|
||||||
|
|
||||||
address = info.Address + info.Size;
|
Address = Info.Address + Info.Size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadMod0Symbols(MemoryManager memory, long textOffset)
|
private void LoadMod0Symbols(MemoryManager Memory, long TextOffset)
|
||||||
{
|
{
|
||||||
long mod0Offset = textOffset + memory.ReadUInt32(textOffset + 4);
|
long Mod0Offset = TextOffset + Memory.ReadUInt32(TextOffset + 4);
|
||||||
|
|
||||||
if (mod0Offset < textOffset || !memory.IsMapped(mod0Offset) || (mod0Offset & 3) != 0)
|
if (Mod0Offset < TextOffset || !Memory.IsMapped(Mod0Offset) || (Mod0Offset & 3) != 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Dictionary<ElfDynamicTag, long> dynamic = new Dictionary<ElfDynamicTag, long>();
|
Dictionary<ElfDynamicTag, long> Dynamic = new Dictionary<ElfDynamicTag, long>();
|
||||||
|
|
||||||
int mod0Magic = memory.ReadInt32(mod0Offset + 0x0);
|
int Mod0Magic = Memory.ReadInt32(Mod0Offset + 0x0);
|
||||||
|
|
||||||
if (mod0Magic != Mod0)
|
if (Mod0Magic != Mod0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long dynamicOffset = memory.ReadInt32(mod0Offset + 0x4) + mod0Offset;
|
long DynamicOffset = Memory.ReadInt32(Mod0Offset + 0x4) + Mod0Offset;
|
||||||
long bssStartOffset = memory.ReadInt32(mod0Offset + 0x8) + mod0Offset;
|
long BssStartOffset = Memory.ReadInt32(Mod0Offset + 0x8) + Mod0Offset;
|
||||||
long bssEndOffset = memory.ReadInt32(mod0Offset + 0xc) + mod0Offset;
|
long BssEndOffset = Memory.ReadInt32(Mod0Offset + 0xc) + Mod0Offset;
|
||||||
long ehHdrStartOffset = memory.ReadInt32(mod0Offset + 0x10) + mod0Offset;
|
long EhHdrStartOffset = Memory.ReadInt32(Mod0Offset + 0x10) + Mod0Offset;
|
||||||
long ehHdrEndOffset = memory.ReadInt32(mod0Offset + 0x14) + mod0Offset;
|
long EhHdrEndOffset = Memory.ReadInt32(Mod0Offset + 0x14) + Mod0Offset;
|
||||||
long modObjOffset = memory.ReadInt32(mod0Offset + 0x18) + mod0Offset;
|
long ModObjOffset = Memory.ReadInt32(Mod0Offset + 0x18) + Mod0Offset;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
long tagVal = memory.ReadInt64(dynamicOffset + 0);
|
long TagVal = Memory.ReadInt64(DynamicOffset + 0);
|
||||||
long value = memory.ReadInt64(dynamicOffset + 8);
|
long Value = Memory.ReadInt64(DynamicOffset + 8);
|
||||||
|
|
||||||
dynamicOffset += 0x10;
|
DynamicOffset += 0x10;
|
||||||
|
|
||||||
ElfDynamicTag tag = (ElfDynamicTag)tagVal;
|
ElfDynamicTag Tag = (ElfDynamicTag)TagVal;
|
||||||
|
|
||||||
if (tag == ElfDynamicTag.DT_NULL)
|
if (Tag == ElfDynamicTag.DT_NULL)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamic[tag] = value;
|
Dynamic[Tag] = Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dynamic.TryGetValue(ElfDynamicTag.DT_STRTAB, out long strTab) ||
|
if (!Dynamic.TryGetValue(ElfDynamicTag.DT_STRTAB, out long StrTab) ||
|
||||||
!dynamic.TryGetValue(ElfDynamicTag.DT_SYMTAB, out long symTab) ||
|
!Dynamic.TryGetValue(ElfDynamicTag.DT_SYMTAB, out long SymTab) ||
|
||||||
!dynamic.TryGetValue(ElfDynamicTag.DT_SYMENT, out long symEntSize))
|
!Dynamic.TryGetValue(ElfDynamicTag.DT_SYMENT, out long SymEntSize))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long strTblAddr = textOffset + strTab;
|
long StrTblAddr = TextOffset + StrTab;
|
||||||
long symTblAddr = textOffset + symTab;
|
long SymTblAddr = TextOffset + SymTab;
|
||||||
|
|
||||||
List<ElfSymbol> symbols = new List<ElfSymbol>();
|
List<ElfSymbol> Symbols = new List<ElfSymbol>();
|
||||||
|
|
||||||
while ((ulong)symTblAddr < (ulong)strTblAddr)
|
while ((ulong)SymTblAddr < (ulong)StrTblAddr)
|
||||||
{
|
{
|
||||||
ElfSymbol sym = GetSymbol(memory, symTblAddr, strTblAddr);
|
ElfSymbol Sym = GetSymbol(Memory, SymTblAddr, StrTblAddr);
|
||||||
|
|
||||||
symbols.Add(sym);
|
Symbols.Add(Sym);
|
||||||
|
|
||||||
symTblAddr += symEntSize;
|
SymTblAddr += SymEntSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_images)
|
lock (Images)
|
||||||
{
|
{
|
||||||
_images.Add(new Image(textOffset, symbols.OrderBy(x => x.Value).ToArray()));
|
Images.Add(new Image(TextOffset, Symbols.OrderBy(x => x.Value).ToArray()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ElfSymbol GetSymbol(MemoryManager memory, long address, long strTblAddr)
|
private ElfSymbol GetSymbol(MemoryManager Memory, long Address, long StrTblAddr)
|
||||||
{
|
{
|
||||||
int nameIndex = memory.ReadInt32(address + 0);
|
int NameIndex = Memory.ReadInt32(Address + 0);
|
||||||
int info = memory.ReadByte (address + 4);
|
int Info = Memory.ReadByte (Address + 4);
|
||||||
int other = memory.ReadByte (address + 5);
|
int Other = Memory.ReadByte (Address + 5);
|
||||||
int shIdx = memory.ReadInt16(address + 6);
|
int SHIdx = Memory.ReadInt16(Address + 6);
|
||||||
long value = memory.ReadInt64(address + 8);
|
long Value = Memory.ReadInt64(Address + 8);
|
||||||
long size = memory.ReadInt64(address + 16);
|
long Size = Memory.ReadInt64(Address + 16);
|
||||||
|
|
||||||
string name = string.Empty;
|
string Name = string.Empty;
|
||||||
|
|
||||||
for (int chr; (chr = memory.ReadByte(strTblAddr + nameIndex++)) != 0;)
|
for (int Chr; (Chr = Memory.ReadByte(StrTblAddr + NameIndex++)) != 0;)
|
||||||
{
|
{
|
||||||
name += (char)chr;
|
Name += (char)Chr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ElfSymbol(name, info, other, shIdx, value, size);
|
return new ElfSymbol(Name, Info, Other, SHIdx, Value, Size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,21 +7,21 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
private const int RoundRobinTimeQuantumMs = 10;
|
private const int RoundRobinTimeQuantumMs = 10;
|
||||||
|
|
||||||
private int _currentCore;
|
private int CurrentCore;
|
||||||
|
|
||||||
public bool MultiCoreScheduling { get; set; }
|
public bool MultiCoreScheduling { get; set; }
|
||||||
|
|
||||||
public HleCoreManager CoreManager { get; }
|
public HleCoreManager CoreManager { get; private set; }
|
||||||
|
|
||||||
private bool _keepPreempting;
|
private bool KeepPreempting;
|
||||||
|
|
||||||
public void StartAutoPreemptionThread()
|
public void StartAutoPreemptionThread()
|
||||||
{
|
{
|
||||||
Thread preemptionThread = new Thread(PreemptCurrentThread);
|
Thread PreemptionThread = new Thread(PreemptCurrentThread);
|
||||||
|
|
||||||
_keepPreempting = true;
|
KeepPreempting = true;
|
||||||
|
|
||||||
preemptionThread.Start();
|
PreemptionThread.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ContextSwitch()
|
public void ContextSwitch()
|
||||||
|
@ -30,28 +30,28 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
if (MultiCoreScheduling)
|
if (MultiCoreScheduling)
|
||||||
{
|
{
|
||||||
int selectedCount = 0;
|
int SelectedCount = 0;
|
||||||
|
|
||||||
for (int core = 0; core < CpuCoresCount; core++)
|
for (int Core = 0; Core < KScheduler.CpuCoresCount; Core++)
|
||||||
{
|
{
|
||||||
KCoreContext coreContext = CoreContexts[core];
|
KCoreContext CoreContext = CoreContexts[Core];
|
||||||
|
|
||||||
if (coreContext.ContextSwitchNeeded && (coreContext.CurrentThread?.Context.IsCurrentThread() ?? false))
|
if (CoreContext.ContextSwitchNeeded && (CoreContext.CurrentThread?.Context.IsCurrentThread() ?? false))
|
||||||
{
|
{
|
||||||
coreContext.ContextSwitch();
|
CoreContext.ContextSwitch();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coreContext.CurrentThread?.Context.IsCurrentThread() ?? false)
|
if (CoreContext.CurrentThread?.Context.IsCurrentThread() ?? false)
|
||||||
{
|
{
|
||||||
selectedCount++;
|
SelectedCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedCount == 0)
|
if (SelectedCount == 0)
|
||||||
{
|
{
|
||||||
CoreManager.Reset(Thread.CurrentThread);
|
CoreManager.Reset(Thread.CurrentThread);
|
||||||
}
|
}
|
||||||
else if (selectedCount == 1)
|
else if (SelectedCount == 1)
|
||||||
{
|
{
|
||||||
CoreManager.Set(Thread.CurrentThread);
|
CoreManager.Set(Thread.CurrentThread);
|
||||||
}
|
}
|
||||||
|
@ -62,41 +62,41 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
KThread currentThread = CoreContexts[_currentCore].CurrentThread;
|
KThread CurrentThread = CoreContexts[CurrentCore].CurrentThread;
|
||||||
|
|
||||||
bool hasThreadExecuting = currentThread != null;
|
bool HasThreadExecuting = CurrentThread != null;
|
||||||
|
|
||||||
if (hasThreadExecuting)
|
if (HasThreadExecuting)
|
||||||
{
|
{
|
||||||
//If this is not the thread that is currently executing, we need
|
//If this is not the thread that is currently executing, we need
|
||||||
//to request an interrupt to allow safely starting another thread.
|
//to request an interrupt to allow safely starting another thread.
|
||||||
if (!currentThread.Context.IsCurrentThread())
|
if (!CurrentThread.Context.IsCurrentThread())
|
||||||
{
|
{
|
||||||
currentThread.Context.RequestInterrupt();
|
CurrentThread.Context.RequestInterrupt();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CoreManager.Reset(currentThread.Context.Work);
|
CoreManager.Reset(CurrentThread.Context.Work);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Advance current core and try picking a thread,
|
//Advance current core and try picking a thread,
|
||||||
//keep advancing if it is null.
|
//keep advancing if it is null.
|
||||||
for (int core = 0; core < 4; core++)
|
for (int Core = 0; Core < 4; Core++)
|
||||||
{
|
{
|
||||||
_currentCore = (_currentCore + 1) % CpuCoresCount;
|
CurrentCore = (CurrentCore + 1) % CpuCoresCount;
|
||||||
|
|
||||||
KCoreContext coreContext = CoreContexts[_currentCore];
|
KCoreContext CoreContext = CoreContexts[CurrentCore];
|
||||||
|
|
||||||
coreContext.UpdateCurrentThread();
|
CoreContext.UpdateCurrentThread();
|
||||||
|
|
||||||
if (coreContext.CurrentThread != null)
|
if (CoreContext.CurrentThread != null)
|
||||||
{
|
{
|
||||||
coreContext.CurrentThread.ClearExclusive();
|
CoreContext.CurrentThread.ClearExclusive();
|
||||||
|
|
||||||
CoreManager.Set(coreContext.CurrentThread.Context.Work);
|
CoreManager.Set(CoreContext.CurrentThread.Context.Work);
|
||||||
|
|
||||||
coreContext.CurrentThread.Context.Execute();
|
CoreContext.CurrentThread.Context.Execute();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
//If nothing was running before, then we are on a "external"
|
//If nothing was running before, then we are on a "external"
|
||||||
//HLE thread, we don't need to wait.
|
//HLE thread, we don't need to wait.
|
||||||
if (!hasThreadExecuting)
|
if (!HasThreadExecuting)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -119,13 +119,13 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
//Preempts current thread every 10 milliseconds on a round-robin fashion,
|
//Preempts current thread every 10 milliseconds on a round-robin fashion,
|
||||||
//when multi core scheduling is disabled, to try ensuring that all threads
|
//when multi core scheduling is disabled, to try ensuring that all threads
|
||||||
//gets a chance to run.
|
//gets a chance to run.
|
||||||
while (_keepPreempting)
|
while (KeepPreempting)
|
||||||
{
|
{
|
||||||
lock (CoreContexts)
|
lock (CoreContexts)
|
||||||
{
|
{
|
||||||
KThread currentThread = CoreContexts[_currentCore].CurrentThread;
|
KThread CurrentThread = CoreContexts[CurrentCore].CurrentThread;
|
||||||
|
|
||||||
currentThread?.Context.RequestInterrupt();
|
CurrentThread?.Context.RequestInterrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
PreemptThreads();
|
PreemptThreads();
|
||||||
|
@ -134,16 +134,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ExitThread(KThread thread)
|
public void ExitThread(KThread Thread)
|
||||||
{
|
{
|
||||||
thread.Context.StopExecution();
|
Thread.Context.StopExecution();
|
||||||
|
|
||||||
CoreManager.Exit(thread.Context.Work);
|
CoreManager.Exit(Thread.Context.Work);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveThread(KThread thread)
|
public void RemoveThread(KThread Thread)
|
||||||
{
|
{
|
||||||
CoreManager.RemoveThread(thread.Context.Work);
|
CoreManager.RemoveThread(Thread.Context.Work);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,641 +9,641 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
private const int HasListenersMask = 0x40000000;
|
private const int HasListenersMask = 0x40000000;
|
||||||
|
|
||||||
private Horizon _system;
|
private Horizon System;
|
||||||
|
|
||||||
public List<KThread> CondVarThreads;
|
public List<KThread> CondVarThreads;
|
||||||
public List<KThread> ArbiterThreads;
|
public List<KThread> ArbiterThreads;
|
||||||
|
|
||||||
public KAddressArbiter(Horizon system)
|
public KAddressArbiter(Horizon System)
|
||||||
{
|
{
|
||||||
_system = system;
|
this.System = System;
|
||||||
|
|
||||||
CondVarThreads = new List<KThread>();
|
CondVarThreads = new List<KThread>();
|
||||||
ArbiterThreads = new List<KThread>();
|
ArbiterThreads = new List<KThread>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long ArbitrateLock(int ownerHandle, long mutexAddress, int requesterHandle)
|
public long ArbitrateLock(int OwnerHandle, long MutexAddress, int RequesterHandle)
|
||||||
{
|
{
|
||||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
_system.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
currentThread.SignaledObj = null;
|
CurrentThread.SignaledObj = null;
|
||||||
currentThread.ObjSyncResult = 0;
|
CurrentThread.ObjSyncResult = 0;
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
if (!KernelTransfer.UserToKernelInt32(_system, mutexAddress, out int mutexValue))
|
if (!KernelTransfer.UserToKernelInt32(System, MutexAddress, out int MutexValue))
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mutexValue != (ownerHandle | HasListenersMask))
|
if (MutexValue != (OwnerHandle | HasListenersMask))
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
KThread mutexOwner = currentProcess.HandleTable.GetObject<KThread>(ownerHandle);
|
KThread MutexOwner = CurrentProcess.HandleTable.GetObject<KThread>(OwnerHandle);
|
||||||
|
|
||||||
if (mutexOwner == null)
|
if (MutexOwner == null)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
return MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentThread.MutexAddress = mutexAddress;
|
CurrentThread.MutexAddress = MutexAddress;
|
||||||
currentThread.ThreadHandleForUserMutex = requesterHandle;
|
CurrentThread.ThreadHandleForUserMutex = RequesterHandle;
|
||||||
|
|
||||||
mutexOwner.AddMutexWaiter(currentThread);
|
MutexOwner.AddMutexWaiter(CurrentThread);
|
||||||
|
|
||||||
currentThread.Reschedule(ThreadSchedState.Paused);
|
CurrentThread.Reschedule(ThreadSchedState.Paused);
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
_system.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (currentThread.MutexOwner != null)
|
if (CurrentThread.MutexOwner != null)
|
||||||
{
|
{
|
||||||
currentThread.MutexOwner.RemoveMutexWaiter(currentThread);
|
CurrentThread.MutexOwner.RemoveMutexWaiter(CurrentThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return (uint)currentThread.ObjSyncResult;
|
return (uint)CurrentThread.ObjSyncResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long ArbitrateUnlock(long mutexAddress)
|
public long ArbitrateUnlock(long MutexAddress)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
(long result, KThread newOwnerThread) = MutexUnlock(currentThread, mutexAddress);
|
(long Result, KThread NewOwnerThread) = MutexUnlock(CurrentThread, MutexAddress);
|
||||||
|
|
||||||
if (result != 0 && newOwnerThread != null)
|
if (Result != 0 && NewOwnerThread != null)
|
||||||
{
|
{
|
||||||
newOwnerThread.SignaledObj = null;
|
NewOwnerThread.SignaledObj = null;
|
||||||
newOwnerThread.ObjSyncResult = (int)result;
|
NewOwnerThread.ObjSyncResult = (int)Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long WaitProcessWideKeyAtomic(
|
public long WaitProcessWideKeyAtomic(
|
||||||
long mutexAddress,
|
long MutexAddress,
|
||||||
long condVarAddress,
|
long CondVarAddress,
|
||||||
int threadHandle,
|
int ThreadHandle,
|
||||||
long timeout)
|
long Timeout)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
currentThread.SignaledObj = null;
|
CurrentThread.SignaledObj = null;
|
||||||
currentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||||
|
|
||||||
if (currentThread.ShallBeTerminated ||
|
if (CurrentThread.ShallBeTerminated ||
|
||||||
currentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
||||||
}
|
}
|
||||||
|
|
||||||
(long result, _) = MutexUnlock(currentThread, mutexAddress);
|
(long Result, _) = MutexUnlock(CurrentThread, MutexAddress);
|
||||||
|
|
||||||
if (result != 0)
|
if (Result != 0)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentThread.MutexAddress = mutexAddress;
|
CurrentThread.MutexAddress = MutexAddress;
|
||||||
currentThread.ThreadHandleForUserMutex = threadHandle;
|
CurrentThread.ThreadHandleForUserMutex = ThreadHandle;
|
||||||
currentThread.CondVarAddress = condVarAddress;
|
CurrentThread.CondVarAddress = CondVarAddress;
|
||||||
|
|
||||||
CondVarThreads.Add(currentThread);
|
CondVarThreads.Add(CurrentThread);
|
||||||
|
|
||||||
if (timeout != 0)
|
if (Timeout != 0)
|
||||||
{
|
{
|
||||||
currentThread.Reschedule(ThreadSchedState.Paused);
|
CurrentThread.Reschedule(ThreadSchedState.Paused);
|
||||||
|
|
||||||
if (timeout > 0)
|
if (Timeout > 0)
|
||||||
{
|
{
|
||||||
_system.TimeManager.ScheduleFutureInvocation(currentThread, timeout);
|
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
if (timeout > 0)
|
if (Timeout > 0)
|
||||||
{
|
{
|
||||||
_system.TimeManager.UnscheduleFutureInvocation(currentThread);
|
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
_system.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (currentThread.MutexOwner != null)
|
if (CurrentThread.MutexOwner != null)
|
||||||
{
|
{
|
||||||
currentThread.MutexOwner.RemoveMutexWaiter(currentThread);
|
CurrentThread.MutexOwner.RemoveMutexWaiter(CurrentThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
CondVarThreads.Remove(currentThread);
|
CondVarThreads.Remove(CurrentThread);
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return (uint)currentThread.ObjSyncResult;
|
return (uint)CurrentThread.ObjSyncResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
private (long, KThread) MutexUnlock(KThread currentThread, long mutexAddress)
|
private (long, KThread) MutexUnlock(KThread CurrentThread, long MutexAddress)
|
||||||
{
|
{
|
||||||
KThread newOwnerThread = currentThread.RelinquishMutex(mutexAddress, out int count);
|
KThread NewOwnerThread = CurrentThread.RelinquishMutex(MutexAddress, out int Count);
|
||||||
|
|
||||||
int mutexValue = 0;
|
int MutexValue = 0;
|
||||||
|
|
||||||
if (newOwnerThread != null)
|
if (NewOwnerThread != null)
|
||||||
{
|
{
|
||||||
mutexValue = newOwnerThread.ThreadHandleForUserMutex;
|
MutexValue = NewOwnerThread.ThreadHandleForUserMutex;
|
||||||
|
|
||||||
if (count >= 2)
|
if (Count >= 2)
|
||||||
{
|
{
|
||||||
mutexValue |= HasListenersMask;
|
MutexValue |= HasListenersMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
newOwnerThread.SignaledObj = null;
|
NewOwnerThread.SignaledObj = null;
|
||||||
newOwnerThread.ObjSyncResult = 0;
|
NewOwnerThread.ObjSyncResult = 0;
|
||||||
|
|
||||||
newOwnerThread.ReleaseAndResume();
|
NewOwnerThread.ReleaseAndResume();
|
||||||
}
|
}
|
||||||
|
|
||||||
long result = 0;
|
long Result = 0;
|
||||||
|
|
||||||
if (!KernelTransfer.KernelToUserInt32(_system, mutexAddress, mutexValue))
|
if (!KernelTransfer.KernelToUserInt32(System, MutexAddress, MutexValue))
|
||||||
{
|
{
|
||||||
result = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
Result = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (result, newOwnerThread);
|
return (Result, NewOwnerThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SignalProcessWideKey(long address, int count)
|
public void SignalProcessWideKey(long Address, int Count)
|
||||||
{
|
{
|
||||||
Queue<KThread> signaledThreads = new Queue<KThread>();
|
Queue<KThread> SignaledThreads = new Queue<KThread>();
|
||||||
|
|
||||||
_system.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
IOrderedEnumerable<KThread> sortedThreads = CondVarThreads.OrderBy(x => x.DynamicPriority);
|
IOrderedEnumerable<KThread> SortedThreads = CondVarThreads.OrderBy(x => x.DynamicPriority);
|
||||||
|
|
||||||
foreach (KThread thread in sortedThreads.Where(x => x.CondVarAddress == address))
|
foreach (KThread Thread in SortedThreads.Where(x => x.CondVarAddress == Address))
|
||||||
{
|
{
|
||||||
TryAcquireMutex(thread);
|
TryAcquireMutex(Thread);
|
||||||
|
|
||||||
signaledThreads.Enqueue(thread);
|
SignaledThreads.Enqueue(Thread);
|
||||||
|
|
||||||
//If the count is <= 0, we should signal all threads waiting.
|
//If the count is <= 0, we should signal all threads waiting.
|
||||||
if (count >= 1 && --count == 0)
|
if (Count >= 1 && --Count == 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (signaledThreads.TryDequeue(out KThread thread))
|
while (SignaledThreads.TryDequeue(out KThread Thread))
|
||||||
{
|
{
|
||||||
CondVarThreads.Remove(thread);
|
CondVarThreads.Remove(Thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
private KThread TryAcquireMutex(KThread requester)
|
private KThread TryAcquireMutex(KThread Requester)
|
||||||
{
|
{
|
||||||
long address = requester.MutexAddress;
|
long Address = Requester.MutexAddress;
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
currentProcess.CpuMemory.SetExclusive(0, address);
|
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||||
|
|
||||||
if (!KernelTransfer.UserToKernelInt32(_system, address, out int mutexValue))
|
if (!KernelTransfer.UserToKernelInt32(System, Address, out int MutexValue))
|
||||||
{
|
{
|
||||||
//Invalid address.
|
//Invalid address.
|
||||||
currentProcess.CpuMemory.ClearExclusive(0);
|
CurrentProcess.CpuMemory.ClearExclusive(0);
|
||||||
|
|
||||||
requester.SignaledObj = null;
|
Requester.SignaledObj = null;
|
||||||
requester.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
Requester.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (currentProcess.CpuMemory.TestExclusive(0, address))
|
if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
|
||||||
{
|
{
|
||||||
if (mutexValue != 0)
|
if (MutexValue != 0)
|
||||||
{
|
{
|
||||||
//Update value to indicate there is a mutex waiter now.
|
//Update value to indicate there is a mutex waiter now.
|
||||||
currentProcess.CpuMemory.WriteInt32(address, mutexValue | HasListenersMask);
|
CurrentProcess.CpuMemory.WriteInt32(Address, MutexValue | HasListenersMask);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//No thread owning the mutex, assign to requesting thread.
|
//No thread owning the mutex, assign to requesting thread.
|
||||||
currentProcess.CpuMemory.WriteInt32(address, requester.ThreadHandleForUserMutex);
|
CurrentProcess.CpuMemory.WriteInt32(Address, Requester.ThreadHandleForUserMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProcess.CpuMemory.ClearExclusiveForStore(0);
|
CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProcess.CpuMemory.SetExclusive(0, address);
|
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||||
|
|
||||||
mutexValue = currentProcess.CpuMemory.ReadInt32(address);
|
MutexValue = CurrentProcess.CpuMemory.ReadInt32(Address);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mutexValue == 0)
|
if (MutexValue == 0)
|
||||||
{
|
{
|
||||||
//We now own the mutex.
|
//We now own the mutex.
|
||||||
requester.SignaledObj = null;
|
Requester.SignaledObj = null;
|
||||||
requester.ObjSyncResult = 0;
|
Requester.ObjSyncResult = 0;
|
||||||
|
|
||||||
requester.ReleaseAndResume();
|
Requester.ReleaseAndResume();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutexValue &= ~HasListenersMask;
|
MutexValue &= ~HasListenersMask;
|
||||||
|
|
||||||
KThread mutexOwner = currentProcess.HandleTable.GetObject<KThread>(mutexValue);
|
KThread MutexOwner = CurrentProcess.HandleTable.GetObject<KThread>(MutexValue);
|
||||||
|
|
||||||
if (mutexOwner != null)
|
if (MutexOwner != null)
|
||||||
{
|
{
|
||||||
//Mutex already belongs to another thread, wait for it.
|
//Mutex already belongs to another thread, wait for it.
|
||||||
mutexOwner.AddMutexWaiter(requester);
|
MutexOwner.AddMutexWaiter(Requester);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Invalid mutex owner.
|
//Invalid mutex owner.
|
||||||
requester.SignaledObj = null;
|
Requester.SignaledObj = null;
|
||||||
requester.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
Requester.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||||
|
|
||||||
requester.ReleaseAndResume();
|
Requester.ReleaseAndResume();
|
||||||
}
|
}
|
||||||
|
|
||||||
return mutexOwner;
|
return MutexOwner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long WaitForAddressIfEqual(long address, int value, long timeout)
|
public long WaitForAddressIfEqual(long Address, int Value, long Timeout)
|
||||||
{
|
{
|
||||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
_system.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (currentThread.ShallBeTerminated ||
|
if (CurrentThread.ShallBeTerminated ||
|
||||||
currentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentThread.SignaledObj = null;
|
CurrentThread.SignaledObj = null;
|
||||||
currentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||||
|
|
||||||
if (!KernelTransfer.UserToKernelInt32(_system, address, out int currentValue))
|
if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentValue == value)
|
if (CurrentValue == Value)
|
||||||
{
|
{
|
||||||
if (timeout == 0)
|
if (Timeout == 0)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentThread.MutexAddress = address;
|
CurrentThread.MutexAddress = Address;
|
||||||
currentThread.WaitingInArbitration = true;
|
CurrentThread.WaitingInArbitration = true;
|
||||||
|
|
||||||
InsertSortedByPriority(ArbiterThreads, currentThread);
|
InsertSortedByPriority(ArbiterThreads, CurrentThread);
|
||||||
|
|
||||||
currentThread.Reschedule(ThreadSchedState.Paused);
|
CurrentThread.Reschedule(ThreadSchedState.Paused);
|
||||||
|
|
||||||
if (timeout > 0)
|
if (Timeout > 0)
|
||||||
{
|
{
|
||||||
_system.TimeManager.ScheduleFutureInvocation(currentThread, timeout);
|
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
if (timeout > 0)
|
if (Timeout > 0)
|
||||||
{
|
{
|
||||||
_system.TimeManager.UnscheduleFutureInvocation(currentThread);
|
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
_system.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (currentThread.WaitingInArbitration)
|
if (CurrentThread.WaitingInArbitration)
|
||||||
{
|
{
|
||||||
ArbiterThreads.Remove(currentThread);
|
ArbiterThreads.Remove(CurrentThread);
|
||||||
|
|
||||||
currentThread.WaitingInArbitration = false;
|
CurrentThread.WaitingInArbitration = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return currentThread.ObjSyncResult;
|
return CurrentThread.ObjSyncResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long WaitForAddressIfLessThan(long address, int value, bool shouldDecrement, long timeout)
|
public long WaitForAddressIfLessThan(long Address, int Value, bool ShouldDecrement, long Timeout)
|
||||||
{
|
{
|
||||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
_system.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (currentThread.ShallBeTerminated ||
|
if (CurrentThread.ShallBeTerminated ||
|
||||||
currentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentThread.SignaledObj = null;
|
CurrentThread.SignaledObj = null;
|
||||||
currentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
//If ShouldDecrement is true, do atomic decrement of the value at Address.
|
//If ShouldDecrement is true, do atomic decrement of the value at Address.
|
||||||
currentProcess.CpuMemory.SetExclusive(0, address);
|
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||||
|
|
||||||
if (!KernelTransfer.UserToKernelInt32(_system, address, out int currentValue))
|
if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldDecrement)
|
if (ShouldDecrement)
|
||||||
{
|
{
|
||||||
while (currentValue < value)
|
while (CurrentValue < Value)
|
||||||
{
|
{
|
||||||
if (currentProcess.CpuMemory.TestExclusive(0, address))
|
if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
|
||||||
{
|
{
|
||||||
currentProcess.CpuMemory.WriteInt32(address, currentValue - 1);
|
CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue - 1);
|
||||||
|
|
||||||
currentProcess.CpuMemory.ClearExclusiveForStore(0);
|
CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProcess.CpuMemory.SetExclusive(0, address);
|
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||||
|
|
||||||
currentValue = currentProcess.CpuMemory.ReadInt32(address);
|
CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProcess.CpuMemory.ClearExclusive(0);
|
CurrentProcess.CpuMemory.ClearExclusive(0);
|
||||||
|
|
||||||
if (currentValue < value)
|
if (CurrentValue < Value)
|
||||||
{
|
{
|
||||||
if (timeout == 0)
|
if (Timeout == 0)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentThread.MutexAddress = address;
|
CurrentThread.MutexAddress = Address;
|
||||||
currentThread.WaitingInArbitration = true;
|
CurrentThread.WaitingInArbitration = true;
|
||||||
|
|
||||||
InsertSortedByPriority(ArbiterThreads, currentThread);
|
InsertSortedByPriority(ArbiterThreads, CurrentThread);
|
||||||
|
|
||||||
currentThread.Reschedule(ThreadSchedState.Paused);
|
CurrentThread.Reschedule(ThreadSchedState.Paused);
|
||||||
|
|
||||||
if (timeout > 0)
|
if (Timeout > 0)
|
||||||
{
|
{
|
||||||
_system.TimeManager.ScheduleFutureInvocation(currentThread, timeout);
|
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
if (timeout > 0)
|
if (Timeout > 0)
|
||||||
{
|
{
|
||||||
_system.TimeManager.UnscheduleFutureInvocation(currentThread);
|
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
_system.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (currentThread.WaitingInArbitration)
|
if (CurrentThread.WaitingInArbitration)
|
||||||
{
|
{
|
||||||
ArbiterThreads.Remove(currentThread);
|
ArbiterThreads.Remove(CurrentThread);
|
||||||
|
|
||||||
currentThread.WaitingInArbitration = false;
|
CurrentThread.WaitingInArbitration = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return currentThread.ObjSyncResult;
|
return CurrentThread.ObjSyncResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InsertSortedByPriority(List<KThread> threads, KThread thread)
|
private void InsertSortedByPriority(List<KThread> Threads, KThread Thread)
|
||||||
{
|
{
|
||||||
int nextIndex = -1;
|
int NextIndex = -1;
|
||||||
|
|
||||||
for (int index = 0; index < threads.Count; index++)
|
for (int Index = 0; Index < Threads.Count; Index++)
|
||||||
{
|
{
|
||||||
if (threads[index].DynamicPriority > thread.DynamicPriority)
|
if (Threads[Index].DynamicPriority > Thread.DynamicPriority)
|
||||||
{
|
{
|
||||||
nextIndex = index;
|
NextIndex = Index;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextIndex != -1)
|
if (NextIndex != -1)
|
||||||
{
|
{
|
||||||
threads.Insert(nextIndex, thread);
|
Threads.Insert(NextIndex, Thread);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
threads.Add(thread);
|
Threads.Add(Thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long Signal(long address, int count)
|
public long Signal(long Address, int Count)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
WakeArbiterThreads(address, count);
|
WakeArbiterThreads(Address, Count);
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long SignalAndIncrementIfEqual(long address, int value, int count)
|
public long SignalAndIncrementIfEqual(long Address, int Value, int Count)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
currentProcess.CpuMemory.SetExclusive(0, address);
|
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||||
|
|
||||||
if (!KernelTransfer.UserToKernelInt32(_system, address, out int currentValue))
|
if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (currentValue == value)
|
while (CurrentValue == Value)
|
||||||
{
|
{
|
||||||
if (currentProcess.CpuMemory.TestExclusive(0, address))
|
if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
|
||||||
{
|
{
|
||||||
currentProcess.CpuMemory.WriteInt32(address, currentValue + 1);
|
CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue + 1);
|
||||||
|
|
||||||
currentProcess.CpuMemory.ClearExclusiveForStore(0);
|
CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProcess.CpuMemory.SetExclusive(0, address);
|
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||||
|
|
||||||
currentValue = currentProcess.CpuMemory.ReadInt32(address);
|
CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProcess.CpuMemory.ClearExclusive(0);
|
CurrentProcess.CpuMemory.ClearExclusive(0);
|
||||||
|
|
||||||
if (currentValue != value)
|
if (CurrentValue != Value)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||||
}
|
}
|
||||||
|
|
||||||
WakeArbiterThreads(address, count);
|
WakeArbiterThreads(Address, Count);
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long SignalAndModifyIfEqual(long address, int value, int count)
|
public long SignalAndModifyIfEqual(long Address, int Value, int Count)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
int offset;
|
int Offset;
|
||||||
|
|
||||||
//The value is decremented if the number of threads waiting is less
|
//The value is decremented if the number of threads waiting is less
|
||||||
//or equal to the Count of threads to be signaled, or Count is zero
|
//or equal to the Count of threads to be signaled, or Count is zero
|
||||||
//or negative. It is incremented if there are no threads waiting.
|
//or negative. It is incremented if there are no threads waiting.
|
||||||
int waitingCount = 0;
|
int WaitingCount = 0;
|
||||||
|
|
||||||
foreach (KThread thread in ArbiterThreads.Where(x => x.MutexAddress == address))
|
foreach (KThread Thread in ArbiterThreads.Where(x => x.MutexAddress == Address))
|
||||||
{
|
{
|
||||||
if (++waitingCount > count)
|
if (++WaitingCount > Count)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waitingCount > 0)
|
if (WaitingCount > 0)
|
||||||
{
|
{
|
||||||
offset = waitingCount <= count || count <= 0 ? -1 : 0;
|
Offset = WaitingCount <= Count || Count <= 0 ? -1 : 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
offset = 1;
|
Offset = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
KProcess currentProcess = _system.Scheduler.GetCurrentProcess();
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
currentProcess.CpuMemory.SetExclusive(0, address);
|
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||||
|
|
||||||
if (!KernelTransfer.UserToKernelInt32(_system, address, out int currentValue))
|
if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (currentValue == value)
|
while (CurrentValue == Value)
|
||||||
{
|
{
|
||||||
if (currentProcess.CpuMemory.TestExclusive(0, address))
|
if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
|
||||||
{
|
{
|
||||||
currentProcess.CpuMemory.WriteInt32(address, currentValue + offset);
|
CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue + Offset);
|
||||||
|
|
||||||
currentProcess.CpuMemory.ClearExclusiveForStore(0);
|
CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProcess.CpuMemory.SetExclusive(0, address);
|
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||||
|
|
||||||
currentValue = currentProcess.CpuMemory.ReadInt32(address);
|
CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProcess.CpuMemory.ClearExclusive(0);
|
CurrentProcess.CpuMemory.ClearExclusive(0);
|
||||||
|
|
||||||
if (currentValue != value)
|
if (CurrentValue != Value)
|
||||||
{
|
{
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||||
}
|
}
|
||||||
|
|
||||||
WakeArbiterThreads(address, count);
|
WakeArbiterThreads(Address, Count);
|
||||||
|
|
||||||
_system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WakeArbiterThreads(long address, int count)
|
private void WakeArbiterThreads(long Address, int Count)
|
||||||
{
|
{
|
||||||
Queue<KThread> signaledThreads = new Queue<KThread>();
|
Queue<KThread> SignaledThreads = new Queue<KThread>();
|
||||||
|
|
||||||
foreach (KThread thread in ArbiterThreads.Where(x => x.MutexAddress == address))
|
foreach (KThread Thread in ArbiterThreads.Where(x => x.MutexAddress == Address))
|
||||||
{
|
{
|
||||||
signaledThreads.Enqueue(thread);
|
SignaledThreads.Enqueue(Thread);
|
||||||
|
|
||||||
//If the count is <= 0, we should signal all threads waiting.
|
//If the count is <= 0, we should signal all threads waiting.
|
||||||
if (count >= 1 && --count == 0)
|
if (Count >= 1 && --Count == 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (signaledThreads.TryDequeue(out KThread thread))
|
while (SignaledThreads.TryDequeue(out KThread Thread))
|
||||||
{
|
{
|
||||||
thread.SignaledObj = null;
|
Thread.SignaledObj = null;
|
||||||
thread.ObjSyncResult = 0;
|
Thread.ObjSyncResult = 0;
|
||||||
|
|
||||||
thread.ReleaseAndResume();
|
Thread.ReleaseAndResume();
|
||||||
|
|
||||||
thread.WaitingInArbitration = false;
|
Thread.WaitingInArbitration = false;
|
||||||
|
|
||||||
ArbiterThreads.Remove(thread);
|
ArbiterThreads.Remove(Thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
protected Horizon System;
|
protected Horizon System;
|
||||||
|
|
||||||
public KAutoObject(Horizon system)
|
public KAutoObject(Horizon System)
|
||||||
{
|
{
|
||||||
System = system;
|
this.System = System;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual KernelResult SetName(string name)
|
public virtual KernelResult SetName(string Name)
|
||||||
{
|
{
|
||||||
if (!System.AutoObjectNames.TryAdd(name, this))
|
if (!System.AutoObjectNames.TryAdd(Name, this))
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidState;
|
return KernelResult.InvalidState;
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static KernelResult RemoveName(Horizon system, string name)
|
public static KernelResult RemoveName(Horizon System, string Name)
|
||||||
{
|
{
|
||||||
if (!system.AutoObjectNames.TryRemove(name, out _))
|
if (!System.AutoObjectNames.TryRemove(Name, out _))
|
||||||
{
|
{
|
||||||
return KernelResult.NotFound;
|
return KernelResult.NotFound;
|
||||||
}
|
}
|
||||||
|
@ -29,11 +29,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static KAutoObject FindNamedObject(Horizon system, string name)
|
public static KAutoObject FindNamedObject(Horizon System, string Name)
|
||||||
{
|
{
|
||||||
if (system.AutoObjectNames.TryGetValue(name, out KAutoObject obj))
|
if (System.AutoObjectNames.TryGetValue(Name, out KAutoObject Obj))
|
||||||
{
|
{
|
||||||
return obj;
|
return Obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -2,30 +2,30 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class KClientPort : KSynchronizationObject
|
class KClientPort : KSynchronizationObject
|
||||||
{
|
{
|
||||||
private int _sessionsCount;
|
private int SessionsCount;
|
||||||
private int _currentCapacity;
|
private int CurrentCapacity;
|
||||||
private int _maxSessions;
|
private int MaxSessions;
|
||||||
|
|
||||||
private KPort _parent;
|
private KPort Parent;
|
||||||
|
|
||||||
public KClientPort(Horizon system) : base(system) { }
|
public KClientPort(Horizon System) : base(System) { }
|
||||||
|
|
||||||
public void Initialize(KPort parent, int maxSessions)
|
public void Initialize(KPort Parent, int MaxSessions)
|
||||||
{
|
{
|
||||||
_maxSessions = maxSessions;
|
this.MaxSessions = MaxSessions;
|
||||||
_parent = parent;
|
this.Parent = Parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public new static KernelResult RemoveName(Horizon system, string name)
|
public new static KernelResult RemoveName(Horizon System, string Name)
|
||||||
{
|
{
|
||||||
KAutoObject foundObj = FindNamedObject(system, name);
|
KAutoObject FoundObj = KAutoObject.FindNamedObject(System, Name);
|
||||||
|
|
||||||
if (!(foundObj is KClientPort))
|
if (!(FoundObj is KClientPort))
|
||||||
{
|
{
|
||||||
return KernelResult.NotFound;
|
return KernelResult.NotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
return KAutoObject.RemoveName(system, name);
|
return KAutoObject.RemoveName(System, Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,67 +5,67 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
static class KConditionVariable
|
static class KConditionVariable
|
||||||
{
|
{
|
||||||
public static void Wait(Horizon system, LinkedList<KThread> threadList, object mutex, long timeout)
|
public static void Wait(Horizon System, LinkedList<KThread> ThreadList, object Mutex, long Timeout)
|
||||||
{
|
{
|
||||||
KThread currentThread = system.Scheduler.GetCurrentThread();
|
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
system.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
Monitor.Exit(mutex);
|
Monitor.Exit(Mutex);
|
||||||
|
|
||||||
currentThread.Withholder = threadList;
|
CurrentThread.Withholder = ThreadList;
|
||||||
|
|
||||||
currentThread.Reschedule(ThreadSchedState.Paused);
|
CurrentThread.Reschedule(ThreadSchedState.Paused);
|
||||||
|
|
||||||
currentThread.WithholderNode = threadList.AddLast(currentThread);
|
CurrentThread.WithholderNode = ThreadList.AddLast(CurrentThread);
|
||||||
|
|
||||||
if (currentThread.ShallBeTerminated ||
|
if (CurrentThread.ShallBeTerminated ||
|
||||||
currentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
||||||
{
|
{
|
||||||
threadList.Remove(currentThread.WithholderNode);
|
ThreadList.Remove(CurrentThread.WithholderNode);
|
||||||
|
|
||||||
currentThread.Reschedule(ThreadSchedState.Running);
|
CurrentThread.Reschedule(ThreadSchedState.Running);
|
||||||
|
|
||||||
currentThread.Withholder = null;
|
CurrentThread.Withholder = null;
|
||||||
|
|
||||||
system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (timeout > 0)
|
if (Timeout > 0)
|
||||||
{
|
{
|
||||||
system.TimeManager.ScheduleFutureInvocation(currentThread, timeout);
|
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
if (timeout > 0)
|
if (Timeout > 0)
|
||||||
{
|
{
|
||||||
system.TimeManager.UnscheduleFutureInvocation(currentThread);
|
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Monitor.Enter(mutex);
|
Monitor.Enter(Mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void NotifyAll(Horizon system, LinkedList<KThread> threadList)
|
public static void NotifyAll(Horizon System, LinkedList<KThread> ThreadList)
|
||||||
{
|
{
|
||||||
system.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
LinkedListNode<KThread> node = threadList.First;
|
LinkedListNode<KThread> Node = ThreadList.First;
|
||||||
|
|
||||||
for (; node != null; node = threadList.First)
|
for (; Node != null; Node = ThreadList.First)
|
||||||
{
|
{
|
||||||
KThread thread = node.Value;
|
KThread Thread = Node.Value;
|
||||||
|
|
||||||
threadList.Remove(thread.WithholderNode);
|
ThreadList.Remove(Thread.WithholderNode);
|
||||||
|
|
||||||
thread.Withholder = null;
|
Thread.Withholder = null;
|
||||||
|
|
||||||
thread.Reschedule(ThreadSchedState.Running);
|
Thread.Reschedule(ThreadSchedState.Running);
|
||||||
}
|
}
|
||||||
|
|
||||||
system.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,77 +7,77 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
private const int IdMasksCount = 8;
|
private const int IdMasksCount = 8;
|
||||||
|
|
||||||
private int[] _idMasks;
|
private int[] IdMasks;
|
||||||
|
|
||||||
private int _nextFreeBitHint;
|
private int NextFreeBitHint;
|
||||||
|
|
||||||
public KContextIdManager()
|
public KContextIdManager()
|
||||||
{
|
{
|
||||||
_idMasks = new int[IdMasksCount];
|
IdMasks = new int[IdMasksCount];
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetId()
|
public int GetId()
|
||||||
{
|
{
|
||||||
lock (_idMasks)
|
lock (IdMasks)
|
||||||
{
|
{
|
||||||
int id = 0;
|
int Id = 0;
|
||||||
|
|
||||||
if (!TestBit(_nextFreeBitHint))
|
if (!TestBit(NextFreeBitHint))
|
||||||
{
|
{
|
||||||
id = _nextFreeBitHint;
|
Id = NextFreeBitHint;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int index = 0; index < IdMasksCount; index++)
|
for (int Index = 0; Index < IdMasksCount; Index++)
|
||||||
{
|
{
|
||||||
int mask = _idMasks[index];
|
int Mask = IdMasks[Index];
|
||||||
|
|
||||||
int firstFreeBit = BitUtils.CountLeadingZeros32((mask + 1) & ~mask);
|
int FirstFreeBit = BitUtils.CountLeadingZeros32((Mask + 1) & ~Mask);
|
||||||
|
|
||||||
if (firstFreeBit < 32)
|
if (FirstFreeBit < 32)
|
||||||
{
|
{
|
||||||
int baseBit = index * 32 + 31;
|
int BaseBit = Index * 32 + 31;
|
||||||
|
|
||||||
id = baseBit - firstFreeBit;
|
Id = BaseBit - FirstFreeBit;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (index == IdMasksCount - 1)
|
else if (Index == IdMasksCount - 1)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Maximum number of Ids reached!");
|
throw new InvalidOperationException("Maximum number of Ids reached!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_nextFreeBitHint = id + 1;
|
NextFreeBitHint = Id + 1;
|
||||||
|
|
||||||
SetBit(id);
|
SetBit(Id);
|
||||||
|
|
||||||
return id;
|
return Id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PutId(int id)
|
public void PutId(int Id)
|
||||||
{
|
{
|
||||||
lock (_idMasks)
|
lock (IdMasks)
|
||||||
{
|
{
|
||||||
ClearBit(id);
|
ClearBit(Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TestBit(int bit)
|
private bool TestBit(int Bit)
|
||||||
{
|
{
|
||||||
return (_idMasks[_nextFreeBitHint / 32] & (1 << (_nextFreeBitHint & 31))) != 0;
|
return (IdMasks[NextFreeBitHint / 32] & (1 << (NextFreeBitHint & 31))) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetBit(int bit)
|
private void SetBit(int Bit)
|
||||||
{
|
{
|
||||||
_idMasks[_nextFreeBitHint / 32] |= (1 << (_nextFreeBitHint & 31));
|
IdMasks[NextFreeBitHint / 32] |= (1 << (NextFreeBitHint & 31));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearBit(int bit)
|
private void ClearBit(int Bit)
|
||||||
{
|
{
|
||||||
_idMasks[_nextFreeBitHint / 32] &= ~(1 << (_nextFreeBitHint & 31));
|
IdMasks[NextFreeBitHint / 32] &= ~(1 << (NextFreeBitHint & 31));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,9 +4,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class KCoreContext
|
class KCoreContext
|
||||||
{
|
{
|
||||||
private KScheduler _scheduler;
|
private KScheduler Scheduler;
|
||||||
|
|
||||||
private HleCoreManager _coreManager;
|
private HleCoreManager CoreManager;
|
||||||
|
|
||||||
public bool ContextSwitchNeeded { get; private set; }
|
public bool ContextSwitchNeeded { get; private set; }
|
||||||
|
|
||||||
|
@ -17,15 +17,15 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
public KThread CurrentThread { get; private set; }
|
public KThread CurrentThread { get; private set; }
|
||||||
public KThread SelectedThread { get; private set; }
|
public KThread SelectedThread { get; private set; }
|
||||||
|
|
||||||
public KCoreContext(KScheduler scheduler, HleCoreManager coreManager)
|
public KCoreContext(KScheduler Scheduler, HleCoreManager CoreManager)
|
||||||
{
|
{
|
||||||
_scheduler = scheduler;
|
this.Scheduler = Scheduler;
|
||||||
_coreManager = coreManager;
|
this.CoreManager = CoreManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SelectThread(KThread thread)
|
public void SelectThread(KThread Thread)
|
||||||
{
|
{
|
||||||
SelectedThread = thread;
|
SelectedThread = Thread;
|
||||||
|
|
||||||
if (SelectedThread != CurrentThread)
|
if (SelectedThread != CurrentThread)
|
||||||
{
|
{
|
||||||
|
@ -43,10 +43,10 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
if (CurrentThread != null)
|
if (CurrentThread != null)
|
||||||
{
|
{
|
||||||
long currentTime = PerformanceCounter.ElapsedMilliseconds;
|
long CurrentTime = PerformanceCounter.ElapsedMilliseconds;
|
||||||
|
|
||||||
CurrentThread.TotalTimeRunning += currentTime - CurrentThread.LastScheduledTime;
|
CurrentThread.TotalTimeRunning += CurrentTime - CurrentThread.LastScheduledTime;
|
||||||
CurrentThread.LastScheduledTime = currentTime;
|
CurrentThread.LastScheduledTime = CurrentTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,21 +58,21 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
if (CurrentThread != null)
|
if (CurrentThread != null)
|
||||||
{
|
{
|
||||||
_coreManager.Reset(CurrentThread.Context.Work);
|
CoreManager.Reset(CurrentThread.Context.Work);
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentThread = SelectedThread;
|
CurrentThread = SelectedThread;
|
||||||
|
|
||||||
if (CurrentThread != null)
|
if (CurrentThread != null)
|
||||||
{
|
{
|
||||||
long currentTime = PerformanceCounter.ElapsedMilliseconds;
|
long CurrentTime = PerformanceCounter.ElapsedMilliseconds;
|
||||||
|
|
||||||
CurrentThread.TotalTimeRunning += currentTime - CurrentThread.LastScheduledTime;
|
CurrentThread.TotalTimeRunning += CurrentTime - CurrentThread.LastScheduledTime;
|
||||||
CurrentThread.LastScheduledTime = currentTime;
|
CurrentThread.LastScheduledTime = CurrentTime;
|
||||||
|
|
||||||
CurrentThread.ClearExclusive();
|
CurrentThread.ClearExclusive();
|
||||||
|
|
||||||
_coreManager.Set(CurrentThread.Context.Work);
|
CoreManager.Set(CurrentThread.Context.Work);
|
||||||
|
|
||||||
CurrentThread.Context.Execute();
|
CurrentThread.Context.Execute();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,15 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class KCriticalSection
|
class KCriticalSection
|
||||||
{
|
{
|
||||||
private Horizon _system;
|
private Horizon System;
|
||||||
|
|
||||||
public object LockObj { get; }
|
public object LockObj { get; private set; }
|
||||||
|
|
||||||
private int _recursionCount;
|
private int RecursionCount;
|
||||||
|
|
||||||
public KCriticalSection(Horizon system)
|
public KCriticalSection(Horizon System)
|
||||||
{
|
{
|
||||||
_system = system;
|
this.System = System;
|
||||||
|
|
||||||
LockObj = new object();
|
LockObj = new object();
|
||||||
}
|
}
|
||||||
|
@ -22,53 +22,53 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
Monitor.Enter(LockObj);
|
Monitor.Enter(LockObj);
|
||||||
|
|
||||||
_recursionCount++;
|
RecursionCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Leave()
|
public void Leave()
|
||||||
{
|
{
|
||||||
if (_recursionCount == 0)
|
if (RecursionCount == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool doContextSwitch = false;
|
bool DoContextSwitch = false;
|
||||||
|
|
||||||
if (--_recursionCount == 0)
|
if (--RecursionCount == 0)
|
||||||
{
|
{
|
||||||
if (_system.Scheduler.ThreadReselectionRequested)
|
if (System.Scheduler.ThreadReselectionRequested)
|
||||||
{
|
{
|
||||||
_system.Scheduler.SelectThreads();
|
System.Scheduler.SelectThreads();
|
||||||
}
|
}
|
||||||
|
|
||||||
Monitor.Exit(LockObj);
|
Monitor.Exit(LockObj);
|
||||||
|
|
||||||
if (_system.Scheduler.MultiCoreScheduling)
|
if (System.Scheduler.MultiCoreScheduling)
|
||||||
{
|
{
|
||||||
lock (_system.Scheduler.CoreContexts)
|
lock (System.Scheduler.CoreContexts)
|
||||||
{
|
{
|
||||||
for (int core = 0; core < KScheduler.CpuCoresCount; core++)
|
for (int Core = 0; Core < KScheduler.CpuCoresCount; Core++)
|
||||||
{
|
{
|
||||||
KCoreContext coreContext = _system.Scheduler.CoreContexts[core];
|
KCoreContext CoreContext = System.Scheduler.CoreContexts[Core];
|
||||||
|
|
||||||
if (coreContext.ContextSwitchNeeded)
|
if (CoreContext.ContextSwitchNeeded)
|
||||||
{
|
{
|
||||||
CpuThread currentHleThread = coreContext.CurrentThread?.Context;
|
CpuThread CurrentHleThread = CoreContext.CurrentThread?.Context;
|
||||||
|
|
||||||
if (currentHleThread == null)
|
if (CurrentHleThread == null)
|
||||||
{
|
{
|
||||||
//Nothing is running, we can perform the context switch immediately.
|
//Nothing is running, we can perform the context switch immediately.
|
||||||
coreContext.ContextSwitch();
|
CoreContext.ContextSwitch();
|
||||||
}
|
}
|
||||||
else if (currentHleThread.IsCurrentThread())
|
else if (CurrentHleThread.IsCurrentThread())
|
||||||
{
|
{
|
||||||
//Thread running on the current core, context switch will block.
|
//Thread running on the current core, context switch will block.
|
||||||
doContextSwitch = true;
|
DoContextSwitch = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Thread running on another core, request a interrupt.
|
//Thread running on another core, request a interrupt.
|
||||||
currentHleThread.RequestInterrupt();
|
CurrentHleThread.RequestInterrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
doContextSwitch = true;
|
DoContextSwitch = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -84,9 +84,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
Monitor.Exit(LockObj);
|
Monitor.Exit(LockObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doContextSwitch)
|
if (DoContextSwitch)
|
||||||
{
|
{
|
||||||
_system.Scheduler.ContextSwitch();
|
System.Scheduler.ContextSwitch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class KEvent
|
class KEvent
|
||||||
{
|
{
|
||||||
public KReadableEvent ReadableEvent { get; }
|
public KReadableEvent ReadableEvent { get; private set; }
|
||||||
public KWritableEvent WritableEvent { get; }
|
public KWritableEvent WritableEvent { get; private set; }
|
||||||
|
|
||||||
public KEvent(Horizon system)
|
public KEvent(Horizon System)
|
||||||
{
|
{
|
||||||
ReadableEvent = new KReadableEvent(system, this);
|
ReadableEvent = new KReadableEvent(System, this);
|
||||||
WritableEvent = new KWritableEvent(this);
|
WritableEvent = new KWritableEvent(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
public KHandleEntry Next { get; set; }
|
public KHandleEntry Next { get; set; }
|
||||||
|
|
||||||
public int Index { get; }
|
public int Index { get; private set; }
|
||||||
|
|
||||||
public ushort HandleId { get; set; }
|
public ushort HandleId { get; set; }
|
||||||
public object Obj { get; set; }
|
public object Obj { get; set; }
|
||||||
|
|
||||||
public KHandleEntry(int index)
|
public KHandleEntry(int Index)
|
||||||
{
|
{
|
||||||
Index = index;
|
this.Index = Index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,148 +7,148 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
private const int SelfThreadHandle = (0x1ffff << 15) | 0;
|
private const int SelfThreadHandle = (0x1ffff << 15) | 0;
|
||||||
private const int SelfProcessHandle = (0x1ffff << 15) | 1;
|
private const int SelfProcessHandle = (0x1ffff << 15) | 1;
|
||||||
|
|
||||||
private Horizon _system;
|
private Horizon System;
|
||||||
|
|
||||||
private KHandleEntry[] _table;
|
private KHandleEntry[] Table;
|
||||||
|
|
||||||
private KHandleEntry _tableHead;
|
private KHandleEntry TableHead;
|
||||||
private KHandleEntry _nextFreeEntry;
|
private KHandleEntry NextFreeEntry;
|
||||||
|
|
||||||
private int _activeSlotsCount;
|
private int ActiveSlotsCount;
|
||||||
|
|
||||||
private int _size;
|
private int Size;
|
||||||
|
|
||||||
private ushort _idCounter;
|
private ushort IdCounter;
|
||||||
|
|
||||||
public KHandleTable(Horizon system)
|
public KHandleTable(Horizon System)
|
||||||
{
|
{
|
||||||
_system = system;
|
this.System = System;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult Initialize(int size)
|
public KernelResult Initialize(int Size)
|
||||||
{
|
{
|
||||||
if ((uint)size > 1024)
|
if ((uint)Size > 1024)
|
||||||
{
|
{
|
||||||
return KernelResult.OutOfMemory;
|
return KernelResult.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size < 1)
|
if (Size < 1)
|
||||||
{
|
{
|
||||||
size = 1024;
|
Size = 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
_size = size;
|
this.Size = Size;
|
||||||
|
|
||||||
_idCounter = 1;
|
IdCounter = 1;
|
||||||
|
|
||||||
_table = new KHandleEntry[size];
|
Table = new KHandleEntry[Size];
|
||||||
|
|
||||||
_tableHead = new KHandleEntry(0);
|
TableHead = new KHandleEntry(0);
|
||||||
|
|
||||||
KHandleEntry entry = _tableHead;
|
KHandleEntry Entry = TableHead;
|
||||||
|
|
||||||
for (int index = 0; index < size; index++)
|
for (int Index = 0; Index < Size; Index++)
|
||||||
{
|
{
|
||||||
_table[index] = entry;
|
Table[Index] = Entry;
|
||||||
|
|
||||||
entry.Next = new KHandleEntry(index + 1);
|
Entry.Next = new KHandleEntry(Index + 1);
|
||||||
|
|
||||||
entry = entry.Next;
|
Entry = Entry.Next;
|
||||||
}
|
}
|
||||||
|
|
||||||
_table[size - 1].Next = null;
|
Table[Size - 1].Next = null;
|
||||||
|
|
||||||
_nextFreeEntry = _tableHead;
|
NextFreeEntry = TableHead;
|
||||||
|
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult GenerateHandle(object obj, out int handle)
|
public KernelResult GenerateHandle(object Obj, out int Handle)
|
||||||
{
|
{
|
||||||
handle = 0;
|
Handle = 0;
|
||||||
|
|
||||||
lock (_table)
|
lock (Table)
|
||||||
{
|
{
|
||||||
if (_activeSlotsCount >= _size)
|
if (ActiveSlotsCount >= Size)
|
||||||
{
|
{
|
||||||
return KernelResult.HandleTableFull;
|
return KernelResult.HandleTableFull;
|
||||||
}
|
}
|
||||||
|
|
||||||
KHandleEntry entry = _nextFreeEntry;
|
KHandleEntry Entry = NextFreeEntry;
|
||||||
|
|
||||||
_nextFreeEntry = entry.Next;
|
NextFreeEntry = Entry.Next;
|
||||||
|
|
||||||
entry.Obj = obj;
|
Entry.Obj = Obj;
|
||||||
entry.HandleId = _idCounter;
|
Entry.HandleId = IdCounter;
|
||||||
|
|
||||||
_activeSlotsCount++;
|
ActiveSlotsCount++;
|
||||||
|
|
||||||
handle = (int)((_idCounter << 15) & 0xffff8000) | entry.Index;
|
Handle = (int)((IdCounter << 15) & (uint)0xffff8000) | Entry.Index;
|
||||||
|
|
||||||
if ((short)(_idCounter + 1) >= 0)
|
if ((short)(IdCounter + 1) >= 0)
|
||||||
{
|
{
|
||||||
_idCounter++;
|
IdCounter++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_idCounter = 1;
|
IdCounter = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CloseHandle(int handle)
|
public bool CloseHandle(int Handle)
|
||||||
{
|
{
|
||||||
if ((handle >> 30) != 0 ||
|
if ((Handle >> 30) != 0 ||
|
||||||
handle == SelfThreadHandle ||
|
Handle == SelfThreadHandle ||
|
||||||
handle == SelfProcessHandle)
|
Handle == SelfProcessHandle)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = (handle >> 0) & 0x7fff;
|
int Index = (Handle >> 0) & 0x7fff;
|
||||||
int handleId = (handle >> 15);
|
int HandleId = (Handle >> 15);
|
||||||
|
|
||||||
bool result = false;
|
bool Result = false;
|
||||||
|
|
||||||
lock (_table)
|
lock (Table)
|
||||||
{
|
{
|
||||||
if (handleId != 0 && index < _size)
|
if (HandleId != 0 && Index < Size)
|
||||||
{
|
{
|
||||||
KHandleEntry entry = _table[index];
|
KHandleEntry Entry = Table[Index];
|
||||||
|
|
||||||
if (entry.Obj != null && entry.HandleId == handleId)
|
if (Entry.Obj != null && Entry.HandleId == HandleId)
|
||||||
{
|
{
|
||||||
entry.Obj = null;
|
Entry.Obj = null;
|
||||||
entry.Next = _nextFreeEntry;
|
Entry.Next = NextFreeEntry;
|
||||||
|
|
||||||
_nextFreeEntry = entry;
|
NextFreeEntry = Entry;
|
||||||
|
|
||||||
_activeSlotsCount--;
|
ActiveSlotsCount--;
|
||||||
|
|
||||||
result = true;
|
Result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T GetObject<T>(int handle)
|
public T GetObject<T>(int Handle)
|
||||||
{
|
{
|
||||||
int index = (handle >> 0) & 0x7fff;
|
int Index = (Handle >> 0) & 0x7fff;
|
||||||
int handleId = (handle >> 15);
|
int HandleId = (Handle >> 15);
|
||||||
|
|
||||||
lock (_table)
|
lock (Table)
|
||||||
{
|
{
|
||||||
if ((handle >> 30) == 0 && handleId != 0)
|
if ((Handle >> 30) == 0 && HandleId != 0)
|
||||||
{
|
{
|
||||||
KHandleEntry entry = _table[index];
|
KHandleEntry Entry = Table[Index];
|
||||||
|
|
||||||
if (entry.HandleId == handleId && entry.Obj is T obj)
|
if (Entry.HandleId == HandleId && Entry.Obj is T Obj)
|
||||||
{
|
{
|
||||||
return obj;
|
return Obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,49 +156,49 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return default(T);
|
return default(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KThread GetKThread(int handle)
|
public KThread GetKThread(int Handle)
|
||||||
{
|
{
|
||||||
if (handle == SelfThreadHandle)
|
if (Handle == SelfThreadHandle)
|
||||||
{
|
{
|
||||||
return _system.Scheduler.GetCurrentThread();
|
return System.Scheduler.GetCurrentThread();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return GetObject<KThread>(handle);
|
return GetObject<KThread>(Handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public KProcess GetKProcess(int handle)
|
public KProcess GetKProcess(int Handle)
|
||||||
{
|
{
|
||||||
if (handle == SelfProcessHandle)
|
if (Handle == SelfProcessHandle)
|
||||||
{
|
{
|
||||||
return _system.Scheduler.GetCurrentProcess();
|
return System.Scheduler.GetCurrentProcess();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return GetObject<KProcess>(handle);
|
return GetObject<KProcess>(Handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Destroy()
|
public void Destroy()
|
||||||
{
|
{
|
||||||
lock (_table)
|
lock (Table)
|
||||||
{
|
{
|
||||||
for (int index = 0; index < _size; index++)
|
for (int Index = 0; Index < Size; Index++)
|
||||||
{
|
{
|
||||||
KHandleEntry entry = _table[index];
|
KHandleEntry Entry = Table[Index];
|
||||||
|
|
||||||
if (entry.Obj != null)
|
if (Entry.Obj != null)
|
||||||
{
|
{
|
||||||
if (entry.Obj is IDisposable disposableObj)
|
if (Entry.Obj is IDisposable DisposableObj)
|
||||||
{
|
{
|
||||||
disposableObj.Dispose();
|
DisposableObj.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.Obj = null;
|
Entry.Obj = null;
|
||||||
entry.Next = _nextFreeEntry;
|
Entry.Next = NextFreeEntry;
|
||||||
|
|
||||||
_nextFreeEntry = entry;
|
NextFreeEntry = Entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,21 +2,21 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class KMemoryArrange
|
class KMemoryArrange
|
||||||
{
|
{
|
||||||
public KMemoryArrangeRegion Service { get; }
|
public KMemoryArrangeRegion Service { get; private set; }
|
||||||
public KMemoryArrangeRegion NvServices { get; }
|
public KMemoryArrangeRegion NvServices { get; private set; }
|
||||||
public KMemoryArrangeRegion Applet { get; }
|
public KMemoryArrangeRegion Applet { get; private set; }
|
||||||
public KMemoryArrangeRegion Application { get; }
|
public KMemoryArrangeRegion Application { get; private set; }
|
||||||
|
|
||||||
public KMemoryArrange(
|
public KMemoryArrange(
|
||||||
KMemoryArrangeRegion service,
|
KMemoryArrangeRegion Service,
|
||||||
KMemoryArrangeRegion nvServices,
|
KMemoryArrangeRegion NvServices,
|
||||||
KMemoryArrangeRegion applet,
|
KMemoryArrangeRegion Applet,
|
||||||
KMemoryArrangeRegion application)
|
KMemoryArrangeRegion Application)
|
||||||
{
|
{
|
||||||
Service = service;
|
this.Service = Service;
|
||||||
NvServices = nvServices;
|
this.NvServices = NvServices;
|
||||||
Applet = applet;
|
this.Applet = Applet;
|
||||||
Application = application;
|
this.Application = Application;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,15 +2,15 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
struct KMemoryArrangeRegion
|
struct KMemoryArrangeRegion
|
||||||
{
|
{
|
||||||
public ulong Address { get; }
|
public ulong Address { get; private set; }
|
||||||
public ulong Size { get; }
|
public ulong Size { get; private set; }
|
||||||
|
|
||||||
public ulong EndAddr => Address + Size;
|
public ulong EndAddr => Address + Size;
|
||||||
|
|
||||||
public KMemoryArrangeRegion(ulong address, ulong size)
|
public KMemoryArrangeRegion(ulong Address, ulong Size)
|
||||||
{
|
{
|
||||||
Address = address;
|
this.Address = Address;
|
||||||
Size = size;
|
this.Size = Size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,26 +13,26 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
public int DeviceRefCount { get; set; }
|
public int DeviceRefCount { get; set; }
|
||||||
|
|
||||||
public KMemoryBlock(
|
public KMemoryBlock(
|
||||||
ulong baseAddress,
|
ulong BaseAddress,
|
||||||
ulong pagesCount,
|
ulong PagesCount,
|
||||||
MemoryState state,
|
MemoryState State,
|
||||||
MemoryPermission permission,
|
MemoryPermission Permission,
|
||||||
MemoryAttribute attribute)
|
MemoryAttribute Attribute)
|
||||||
{
|
{
|
||||||
BaseAddress = baseAddress;
|
this.BaseAddress = BaseAddress;
|
||||||
PagesCount = pagesCount;
|
this.PagesCount = PagesCount;
|
||||||
State = state;
|
this.State = State;
|
||||||
Attribute = attribute;
|
this.Attribute = Attribute;
|
||||||
Permission = permission;
|
this.Permission = Permission;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KMemoryInfo GetInfo()
|
public KMemoryInfo GetInfo()
|
||||||
{
|
{
|
||||||
ulong size = PagesCount * KMemoryManager.PageSize;
|
ulong Size = PagesCount * KMemoryManager.PageSize;
|
||||||
|
|
||||||
return new KMemoryInfo(
|
return new KMemoryInfo(
|
||||||
BaseAddress,
|
BaseAddress,
|
||||||
size,
|
Size,
|
||||||
State,
|
State,
|
||||||
Permission,
|
Permission,
|
||||||
Attribute,
|
Attribute,
|
||||||
|
|
|
@ -2,18 +2,18 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class KMemoryBlockAllocator
|
class KMemoryBlockAllocator
|
||||||
{
|
{
|
||||||
private ulong _capacityElements;
|
private ulong CapacityElements;
|
||||||
|
|
||||||
public int Count { get; set; }
|
public int Count { get; set; }
|
||||||
|
|
||||||
public KMemoryBlockAllocator(ulong capacityElements)
|
public KMemoryBlockAllocator(ulong CapacityElements)
|
||||||
{
|
{
|
||||||
_capacityElements = capacityElements;
|
this.CapacityElements = CapacityElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanAllocate(int count)
|
public bool CanAllocate(int Count)
|
||||||
{
|
{
|
||||||
return (ulong)(Count + count) <= _capacityElements;
|
return (ulong)(this.Count + Count) <= CapacityElements;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,32 +2,32 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class KMemoryInfo
|
class KMemoryInfo
|
||||||
{
|
{
|
||||||
public ulong Address { get; }
|
public ulong Address { get; private set; }
|
||||||
public ulong Size { get; }
|
public ulong Size { get; private set; }
|
||||||
|
|
||||||
public MemoryState State { get; }
|
public MemoryState State { get; private set; }
|
||||||
public MemoryPermission Permission { get; }
|
public MemoryPermission Permission { get; private set; }
|
||||||
public MemoryAttribute Attribute { get; }
|
public MemoryAttribute Attribute { get; private set; }
|
||||||
|
|
||||||
public int IpcRefCount { get; }
|
public int IpcRefCount { get; private set; }
|
||||||
public int DeviceRefCount { get; }
|
public int DeviceRefCount { get; private set; }
|
||||||
|
|
||||||
public KMemoryInfo(
|
public KMemoryInfo(
|
||||||
ulong address,
|
ulong Address,
|
||||||
ulong size,
|
ulong Size,
|
||||||
MemoryState state,
|
MemoryState State,
|
||||||
MemoryPermission permission,
|
MemoryPermission Permission,
|
||||||
MemoryAttribute attribute,
|
MemoryAttribute Attribute,
|
||||||
int ipcRefCount,
|
int IpcRefCount,
|
||||||
int deviceRefCount)
|
int DeviceRefCount)
|
||||||
{
|
{
|
||||||
Address = address;
|
this.Address = Address;
|
||||||
Size = size;
|
this.Size = Size;
|
||||||
State = state;
|
this.State = State;
|
||||||
Attribute = attribute;
|
this.Attribute = Attribute;
|
||||||
Permission = permission;
|
this.Permission = Permission;
|
||||||
IpcRefCount = ipcRefCount;
|
this.IpcRefCount = IpcRefCount;
|
||||||
DeviceRefCount = deviceRefCount;
|
this.DeviceRefCount = DeviceRefCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -12,30 +12,30 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
public int Order;
|
public int Order;
|
||||||
public int NextOrder;
|
public int NextOrder;
|
||||||
|
|
||||||
public bool TryCoalesce(int index, int size)
|
public bool TryCoalesce(int Index, int Size)
|
||||||
{
|
{
|
||||||
long mask = ((1L << size) - 1) << (index & 63);
|
long Mask = ((1L << Size) - 1) << (Index & 63);
|
||||||
|
|
||||||
index /= 64;
|
Index /= 64;
|
||||||
|
|
||||||
if ((mask & ~Masks[MaxLevel - 1][index]) != 0)
|
if ((Mask & ~Masks[MaxLevel - 1][Index]) != 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Masks[MaxLevel - 1][index] &= ~mask;
|
Masks[MaxLevel - 1][Index] &= ~Mask;
|
||||||
|
|
||||||
for (int level = MaxLevel - 2; level >= 0; level--, index /= 64)
|
for (int Level = MaxLevel - 2; Level >= 0; Level--, Index /= 64)
|
||||||
{
|
{
|
||||||
Masks[level][index / 64] &= ~(1L << (index & 63));
|
Masks[Level][Index / 64] &= ~(1L << (Index & 63));
|
||||||
|
|
||||||
if (Masks[level][index / 64] != 0)
|
if (Masks[Level][Index / 64] != 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FreeCount -= (ulong)size;
|
FreeCount -= (ulong)Size;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,404 +6,404 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
private static readonly int[] BlockOrders = new int[] { 12, 16, 21, 22, 25, 29, 30 };
|
private static readonly int[] BlockOrders = new int[] { 12, 16, 21, 22, 25, 29, 30 };
|
||||||
|
|
||||||
public ulong Address { get; }
|
public ulong Address { get; private set; }
|
||||||
public ulong EndAddr { get; }
|
public ulong EndAddr { get; private set; }
|
||||||
public ulong Size { get; }
|
public ulong Size { get; private set; }
|
||||||
|
|
||||||
private int _blockOrdersCount;
|
private int BlockOrdersCount;
|
||||||
|
|
||||||
private KMemoryRegionBlock[] _blocks;
|
private KMemoryRegionBlock[] Blocks;
|
||||||
|
|
||||||
public KMemoryRegionManager(ulong address, ulong size, ulong endAddr)
|
public KMemoryRegionManager(ulong Address, ulong Size, ulong EndAddr)
|
||||||
{
|
{
|
||||||
_blocks = new KMemoryRegionBlock[BlockOrders.Length];
|
Blocks = new KMemoryRegionBlock[BlockOrders.Length];
|
||||||
|
|
||||||
Address = address;
|
this.Address = Address;
|
||||||
Size = size;
|
this.Size = Size;
|
||||||
EndAddr = endAddr;
|
this.EndAddr = EndAddr;
|
||||||
|
|
||||||
_blockOrdersCount = BlockOrders.Length;
|
BlockOrdersCount = BlockOrders.Length;
|
||||||
|
|
||||||
for (int blockIndex = 0; blockIndex < _blockOrdersCount; blockIndex++)
|
for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++)
|
||||||
{
|
{
|
||||||
_blocks[blockIndex] = new KMemoryRegionBlock();
|
Blocks[BlockIndex] = new KMemoryRegionBlock();
|
||||||
|
|
||||||
_blocks[blockIndex].Order = BlockOrders[blockIndex];
|
Blocks[BlockIndex].Order = BlockOrders[BlockIndex];
|
||||||
|
|
||||||
int nextOrder = blockIndex == _blockOrdersCount - 1 ? 0 : BlockOrders[blockIndex + 1];
|
int NextOrder = BlockIndex == BlockOrdersCount - 1 ? 0 : BlockOrders[BlockIndex + 1];
|
||||||
|
|
||||||
_blocks[blockIndex].NextOrder = nextOrder;
|
Blocks[BlockIndex].NextOrder = NextOrder;
|
||||||
|
|
||||||
int currBlockSize = 1 << BlockOrders[blockIndex];
|
int CurrBlockSize = 1 << BlockOrders[BlockIndex];
|
||||||
int nextBlockSize = currBlockSize;
|
int NextBlockSize = CurrBlockSize;
|
||||||
|
|
||||||
if (nextOrder != 0)
|
if (NextOrder != 0)
|
||||||
{
|
{
|
||||||
nextBlockSize = 1 << nextOrder;
|
NextBlockSize = 1 << NextOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong startAligned = BitUtils.AlignDown(address, nextBlockSize);
|
ulong StartAligned = BitUtils.AlignDown(Address, NextBlockSize);
|
||||||
ulong endAddrAligned = BitUtils.AlignDown(endAddr, currBlockSize);
|
ulong EndAddrAligned = BitUtils.AlignDown(EndAddr, CurrBlockSize);
|
||||||
|
|
||||||
ulong sizeInBlocksTruncated = (endAddrAligned - startAligned) >> BlockOrders[blockIndex];
|
ulong SizeInBlocksTruncated = (EndAddrAligned - StartAligned) >> BlockOrders[BlockIndex];
|
||||||
|
|
||||||
ulong endAddrRounded = BitUtils.AlignUp(address + size, nextBlockSize);
|
ulong EndAddrRounded = BitUtils.AlignUp(Address + Size, NextBlockSize);
|
||||||
|
|
||||||
ulong sizeInBlocksRounded = (endAddrRounded - startAligned) >> BlockOrders[blockIndex];
|
ulong SizeInBlocksRounded = (EndAddrRounded - StartAligned) >> BlockOrders[BlockIndex];
|
||||||
|
|
||||||
_blocks[blockIndex].StartAligned = startAligned;
|
Blocks[BlockIndex].StartAligned = StartAligned;
|
||||||
_blocks[blockIndex].SizeInBlocksTruncated = sizeInBlocksTruncated;
|
Blocks[BlockIndex].SizeInBlocksTruncated = SizeInBlocksTruncated;
|
||||||
_blocks[blockIndex].SizeInBlocksRounded = sizeInBlocksRounded;
|
Blocks[BlockIndex].SizeInBlocksRounded = SizeInBlocksRounded;
|
||||||
|
|
||||||
ulong currSizeInBlocks = sizeInBlocksRounded;
|
ulong CurrSizeInBlocks = SizeInBlocksRounded;
|
||||||
|
|
||||||
int maxLevel = 0;
|
int MaxLevel = 0;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
maxLevel++;
|
MaxLevel++;
|
||||||
}
|
}
|
||||||
while ((currSizeInBlocks /= 64) != 0);
|
while ((CurrSizeInBlocks /= 64) != 0);
|
||||||
|
|
||||||
_blocks[blockIndex].MaxLevel = maxLevel;
|
Blocks[BlockIndex].MaxLevel = MaxLevel;
|
||||||
|
|
||||||
_blocks[blockIndex].Masks = new long[maxLevel][];
|
Blocks[BlockIndex].Masks = new long[MaxLevel][];
|
||||||
|
|
||||||
currSizeInBlocks = sizeInBlocksRounded;
|
CurrSizeInBlocks = SizeInBlocksRounded;
|
||||||
|
|
||||||
for (int level = maxLevel - 1; level >= 0; level--)
|
for (int Level = MaxLevel - 1; Level >= 0; Level--)
|
||||||
{
|
{
|
||||||
currSizeInBlocks = (currSizeInBlocks + 63) / 64;
|
CurrSizeInBlocks = (CurrSizeInBlocks + 63) / 64;
|
||||||
|
|
||||||
_blocks[blockIndex].Masks[level] = new long[currSizeInBlocks];
|
Blocks[BlockIndex].Masks[Level] = new long[CurrSizeInBlocks];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size != 0)
|
if (Size != 0)
|
||||||
{
|
{
|
||||||
FreePages(address, size / KMemoryManager.PageSize);
|
FreePages(Address, Size / KMemoryManager.PageSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult AllocatePages(ulong pagesCount, bool backwards, out KPageList pageList)
|
public KernelResult AllocatePages(ulong PagesCount, bool Backwards, out KPageList PageList)
|
||||||
{
|
{
|
||||||
lock (_blocks)
|
lock (Blocks)
|
||||||
{
|
{
|
||||||
return AllocatePagesImpl(pagesCount, backwards, out pageList);
|
return AllocatePagesImpl(PagesCount, Backwards, out PageList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private KernelResult AllocatePagesImpl(ulong pagesCount, bool backwards, out KPageList pageList)
|
private KernelResult AllocatePagesImpl(ulong PagesCount, bool Backwards, out KPageList PageList)
|
||||||
{
|
{
|
||||||
pageList = new KPageList();
|
PageList = new KPageList();
|
||||||
|
|
||||||
if (_blockOrdersCount > 0)
|
if (BlockOrdersCount > 0)
|
||||||
{
|
{
|
||||||
if (GetFreePagesImpl() < pagesCount)
|
if (GetFreePagesImpl() < PagesCount)
|
||||||
{
|
{
|
||||||
return KernelResult.OutOfMemory;
|
return KernelResult.OutOfMemory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (pagesCount != 0)
|
else if (PagesCount != 0)
|
||||||
{
|
{
|
||||||
return KernelResult.OutOfMemory;
|
return KernelResult.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int blockIndex = _blockOrdersCount - 1; blockIndex >= 0; blockIndex--)
|
for (int BlockIndex = BlockOrdersCount - 1; BlockIndex >= 0; BlockIndex--)
|
||||||
{
|
{
|
||||||
KMemoryRegionBlock block = _blocks[blockIndex];
|
KMemoryRegionBlock Block = Blocks[BlockIndex];
|
||||||
|
|
||||||
ulong bestFitBlockSize = 1UL << block.Order;
|
ulong BestFitBlockSize = 1UL << Block.Order;
|
||||||
|
|
||||||
ulong blockPagesCount = bestFitBlockSize / KMemoryManager.PageSize;
|
ulong BlockPagesCount = BestFitBlockSize / KMemoryManager.PageSize;
|
||||||
|
|
||||||
//Check if this is the best fit for this page size.
|
//Check if this is the best fit for this page size.
|
||||||
//If so, try allocating as much requested pages as possible.
|
//If so, try allocating as much requested pages as possible.
|
||||||
while (blockPagesCount <= pagesCount)
|
while (BlockPagesCount <= PagesCount)
|
||||||
{
|
{
|
||||||
ulong address = 0;
|
ulong Address = 0;
|
||||||
|
|
||||||
for (int currBlockIndex = blockIndex;
|
for (int CurrBlockIndex = BlockIndex;
|
||||||
currBlockIndex < _blockOrdersCount && address == 0;
|
CurrBlockIndex < BlockOrdersCount && Address == 0;
|
||||||
currBlockIndex++)
|
CurrBlockIndex++)
|
||||||
{
|
{
|
||||||
block = _blocks[currBlockIndex];
|
Block = Blocks[CurrBlockIndex];
|
||||||
|
|
||||||
int index = 0;
|
int Index = 0;
|
||||||
|
|
||||||
bool zeroMask = false;
|
bool ZeroMask = false;
|
||||||
|
|
||||||
for (int level = 0; level < block.MaxLevel; level++)
|
for (int Level = 0; Level < Block.MaxLevel; Level++)
|
||||||
{
|
{
|
||||||
long mask = block.Masks[level][index];
|
long Mask = Block.Masks[Level][Index];
|
||||||
|
|
||||||
if (mask == 0)
|
if (Mask == 0)
|
||||||
{
|
{
|
||||||
zeroMask = true;
|
ZeroMask = true;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (backwards)
|
if (Backwards)
|
||||||
{
|
{
|
||||||
index = (index * 64 + 63) - BitUtils.CountLeadingZeros64(mask);
|
Index = (Index * 64 + 63) - BitUtils.CountLeadingZeros64(Mask);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
index = index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(mask));
|
Index = Index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(Mask));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block.SizeInBlocksTruncated <= (ulong)index || zeroMask)
|
if (Block.SizeInBlocksTruncated <= (ulong)Index || ZeroMask)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
block.FreeCount--;
|
Block.FreeCount--;
|
||||||
|
|
||||||
int tempIdx = index;
|
int TempIdx = Index;
|
||||||
|
|
||||||
for (int level = block.MaxLevel - 1; level >= 0; level--, tempIdx /= 64)
|
for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, TempIdx /= 64)
|
||||||
{
|
{
|
||||||
block.Masks[level][tempIdx / 64] &= ~(1L << (tempIdx & 63));
|
Block.Masks[Level][TempIdx / 64] &= ~(1L << (TempIdx & 63));
|
||||||
|
|
||||||
if (block.Masks[level][tempIdx / 64] != 0)
|
if (Block.Masks[Level][TempIdx / 64] != 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
address = block.StartAligned + ((ulong)index << block.Order);
|
Address = Block.StartAligned + ((ulong)Index << Block.Order);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int currBlockIndex = blockIndex;
|
for (int CurrBlockIndex = BlockIndex;
|
||||||
currBlockIndex < _blockOrdersCount && address == 0;
|
CurrBlockIndex < BlockOrdersCount && Address == 0;
|
||||||
currBlockIndex++)
|
CurrBlockIndex++)
|
||||||
{
|
{
|
||||||
block = _blocks[currBlockIndex];
|
Block = Blocks[CurrBlockIndex];
|
||||||
|
|
||||||
int index = 0;
|
int Index = 0;
|
||||||
|
|
||||||
bool zeroMask = false;
|
bool ZeroMask = false;
|
||||||
|
|
||||||
for (int level = 0; level < block.MaxLevel; level++)
|
for (int Level = 0; Level < Block.MaxLevel; Level++)
|
||||||
{
|
{
|
||||||
long mask = block.Masks[level][index];
|
long Mask = Block.Masks[Level][Index];
|
||||||
|
|
||||||
if (mask == 0)
|
if (Mask == 0)
|
||||||
{
|
{
|
||||||
zeroMask = true;
|
ZeroMask = true;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (backwards)
|
if (Backwards)
|
||||||
{
|
{
|
||||||
index = index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(mask));
|
Index = Index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(Mask));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
index = (index * 64 + 63) - BitUtils.CountLeadingZeros64(mask);
|
Index = (Index * 64 + 63) - BitUtils.CountLeadingZeros64(Mask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block.SizeInBlocksTruncated <= (ulong)index || zeroMask)
|
if (Block.SizeInBlocksTruncated <= (ulong)Index || ZeroMask)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
block.FreeCount--;
|
Block.FreeCount--;
|
||||||
|
|
||||||
int tempIdx = index;
|
int TempIdx = Index;
|
||||||
|
|
||||||
for (int level = block.MaxLevel - 1; level >= 0; level--, tempIdx /= 64)
|
for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, TempIdx /= 64)
|
||||||
{
|
{
|
||||||
block.Masks[level][tempIdx / 64] &= ~(1L << (tempIdx & 63));
|
Block.Masks[Level][TempIdx / 64] &= ~(1L << (TempIdx & 63));
|
||||||
|
|
||||||
if (block.Masks[level][tempIdx / 64] != 0)
|
if (Block.Masks[Level][TempIdx / 64] != 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
address = block.StartAligned + ((ulong)index << block.Order);
|
Address = Block.StartAligned + ((ulong)Index << Block.Order);
|
||||||
}
|
}
|
||||||
|
|
||||||
//The address being zero means that no free space was found on that order,
|
//The address being zero means that no free space was found on that order,
|
||||||
//just give up and try with the next one.
|
//just give up and try with the next one.
|
||||||
if (address == 0)
|
if (Address == 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//If we are using a larger order than best fit, then we should
|
//If we are using a larger order than best fit, then we should
|
||||||
//split it into smaller blocks.
|
//split it into smaller blocks.
|
||||||
ulong firstFreeBlockSize = 1UL << block.Order;
|
ulong FirstFreeBlockSize = 1UL << Block.Order;
|
||||||
|
|
||||||
if (firstFreeBlockSize > bestFitBlockSize)
|
if (FirstFreeBlockSize > BestFitBlockSize)
|
||||||
{
|
{
|
||||||
FreePages(address + bestFitBlockSize, (firstFreeBlockSize - bestFitBlockSize) / KMemoryManager.PageSize);
|
FreePages(Address + BestFitBlockSize, (FirstFreeBlockSize - BestFitBlockSize) / KMemoryManager.PageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Add new allocated page(s) to the pages list.
|
//Add new allocated page(s) to the pages list.
|
||||||
//If an error occurs, then free all allocated pages and fail.
|
//If an error occurs, then free all allocated pages and fail.
|
||||||
KernelResult result = pageList.AddRange(address, blockPagesCount);
|
KernelResult Result = PageList.AddRange(Address, BlockPagesCount);
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (Result != KernelResult.Success)
|
||||||
{
|
{
|
||||||
FreePages(address, blockPagesCount);
|
FreePages(Address, BlockPagesCount);
|
||||||
|
|
||||||
foreach (KPageNode pageNode in pageList)
|
foreach (KPageNode PageNode in PageList)
|
||||||
{
|
{
|
||||||
FreePages(pageNode.Address, pageNode.PagesCount);
|
FreePages(PageNode.Address, PageNode.PagesCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pagesCount -= blockPagesCount;
|
PagesCount -= BlockPagesCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Success case, all requested pages were allocated successfully.
|
//Success case, all requested pages were allocated successfully.
|
||||||
if (pagesCount == 0)
|
if (PagesCount == 0)
|
||||||
{
|
{
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Error case, free allocated pages and return out of memory.
|
//Error case, free allocated pages and return out of memory.
|
||||||
foreach (KPageNode pageNode in pageList)
|
foreach (KPageNode PageNode in PageList)
|
||||||
{
|
{
|
||||||
FreePages(pageNode.Address, pageNode.PagesCount);
|
FreePages(PageNode.Address, PageNode.PagesCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
pageList = null;
|
PageList = null;
|
||||||
|
|
||||||
return KernelResult.OutOfMemory;
|
return KernelResult.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void FreePages(KPageList pageList)
|
public void FreePages(KPageList PageList)
|
||||||
{
|
{
|
||||||
lock (_blocks)
|
lock (Blocks)
|
||||||
{
|
{
|
||||||
foreach (KPageNode pageNode in pageList)
|
foreach (KPageNode PageNode in PageList)
|
||||||
{
|
{
|
||||||
FreePages(pageNode.Address, pageNode.PagesCount);
|
FreePages(PageNode.Address, PageNode.PagesCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FreePages(ulong address, ulong pagesCount)
|
private void FreePages(ulong Address, ulong PagesCount)
|
||||||
{
|
{
|
||||||
ulong endAddr = address + pagesCount * KMemoryManager.PageSize;
|
ulong EndAddr = Address + PagesCount * KMemoryManager.PageSize;
|
||||||
|
|
||||||
int blockIndex = _blockOrdersCount - 1;
|
int BlockIndex = BlockOrdersCount - 1;
|
||||||
|
|
||||||
ulong addressRounded = 0;
|
ulong AddressRounded = 0;
|
||||||
ulong endAddrTruncated = 0;
|
ulong EndAddrTruncated = 0;
|
||||||
|
|
||||||
for (; blockIndex >= 0; blockIndex--)
|
for (; BlockIndex >= 0; BlockIndex--)
|
||||||
{
|
{
|
||||||
KMemoryRegionBlock allocInfo = _blocks[blockIndex];
|
KMemoryRegionBlock AllocInfo = Blocks[BlockIndex];
|
||||||
|
|
||||||
int blockSize = 1 << allocInfo.Order;
|
int BlockSize = 1 << AllocInfo.Order;
|
||||||
|
|
||||||
addressRounded = BitUtils.AlignUp (address, blockSize);
|
AddressRounded = BitUtils.AlignUp (Address, BlockSize);
|
||||||
endAddrTruncated = BitUtils.AlignDown(endAddr, blockSize);
|
EndAddrTruncated = BitUtils.AlignDown(EndAddr, BlockSize);
|
||||||
|
|
||||||
if (addressRounded < endAddrTruncated)
|
if (AddressRounded < EndAddrTruncated)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreeRegion(ulong currAddress)
|
void FreeRegion(ulong CurrAddress)
|
||||||
{
|
{
|
||||||
for (int currBlockIndex = blockIndex;
|
for (int CurrBlockIndex = BlockIndex;
|
||||||
currBlockIndex < _blockOrdersCount && currAddress != 0;
|
CurrBlockIndex < BlockOrdersCount && CurrAddress != 0;
|
||||||
currBlockIndex++)
|
CurrBlockIndex++)
|
||||||
{
|
{
|
||||||
KMemoryRegionBlock block = _blocks[currBlockIndex];
|
KMemoryRegionBlock Block = Blocks[CurrBlockIndex];
|
||||||
|
|
||||||
block.FreeCount++;
|
Block.FreeCount++;
|
||||||
|
|
||||||
ulong freedBlocks = (currAddress - block.StartAligned) >> block.Order;
|
ulong FreedBlocks = (CurrAddress - Block.StartAligned) >> Block.Order;
|
||||||
|
|
||||||
int index = (int)freedBlocks;
|
int Index = (int)FreedBlocks;
|
||||||
|
|
||||||
for (int level = block.MaxLevel - 1; level >= 0; level--, index /= 64)
|
for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, Index /= 64)
|
||||||
{
|
{
|
||||||
long mask = block.Masks[level][index / 64];
|
long Mask = Block.Masks[Level][Index / 64];
|
||||||
|
|
||||||
block.Masks[level][index / 64] = mask | (1L << (index & 63));
|
Block.Masks[Level][Index / 64] = Mask | (1L << (Index & 63));
|
||||||
|
|
||||||
if (mask != 0)
|
if (Mask != 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int blockSizeDelta = 1 << (block.NextOrder - block.Order);
|
int BlockSizeDelta = 1 << (Block.NextOrder - Block.Order);
|
||||||
|
|
||||||
int freedBlocksTruncated = BitUtils.AlignDown((int)freedBlocks, blockSizeDelta);
|
int FreedBlocksTruncated = BitUtils.AlignDown((int)FreedBlocks, BlockSizeDelta);
|
||||||
|
|
||||||
if (!block.TryCoalesce(freedBlocksTruncated, blockSizeDelta))
|
if (!Block.TryCoalesce(FreedBlocksTruncated, BlockSizeDelta))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
currAddress = block.StartAligned + ((ulong)freedBlocksTruncated << block.Order);
|
CurrAddress = Block.StartAligned + ((ulong)FreedBlocksTruncated << Block.Order);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Free inside aligned region.
|
//Free inside aligned region.
|
||||||
ulong baseAddress = addressRounded;
|
ulong BaseAddress = AddressRounded;
|
||||||
|
|
||||||
while (baseAddress < endAddrTruncated)
|
while (BaseAddress < EndAddrTruncated)
|
||||||
{
|
{
|
||||||
ulong blockSize = 1UL << _blocks[blockIndex].Order;
|
ulong BlockSize = 1UL << Blocks[BlockIndex].Order;
|
||||||
|
|
||||||
FreeRegion(baseAddress);
|
FreeRegion(BaseAddress);
|
||||||
|
|
||||||
baseAddress += blockSize;
|
BaseAddress += BlockSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nextBlockIndex = blockIndex - 1;
|
int NextBlockIndex = BlockIndex - 1;
|
||||||
|
|
||||||
//Free region between Address and aligned region start.
|
//Free region between Address and aligned region start.
|
||||||
baseAddress = addressRounded;
|
BaseAddress = AddressRounded;
|
||||||
|
|
||||||
for (blockIndex = nextBlockIndex; blockIndex >= 0; blockIndex--)
|
for (BlockIndex = NextBlockIndex; BlockIndex >= 0; BlockIndex--)
|
||||||
{
|
{
|
||||||
ulong blockSize = 1UL << _blocks[blockIndex].Order;
|
ulong BlockSize = 1UL << Blocks[BlockIndex].Order;
|
||||||
|
|
||||||
while (baseAddress - blockSize >= address)
|
while (BaseAddress - BlockSize >= Address)
|
||||||
{
|
{
|
||||||
baseAddress -= blockSize;
|
BaseAddress -= BlockSize;
|
||||||
|
|
||||||
FreeRegion(baseAddress);
|
FreeRegion(BaseAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Free region between aligned region end and End Address.
|
//Free region between aligned region end and End Address.
|
||||||
baseAddress = endAddrTruncated;
|
BaseAddress = EndAddrTruncated;
|
||||||
|
|
||||||
for (blockIndex = nextBlockIndex; blockIndex >= 0; blockIndex--)
|
for (BlockIndex = NextBlockIndex; BlockIndex >= 0; BlockIndex--)
|
||||||
{
|
{
|
||||||
ulong blockSize = 1UL << _blocks[blockIndex].Order;
|
ulong BlockSize = 1UL << Blocks[BlockIndex].Order;
|
||||||
|
|
||||||
while (baseAddress + blockSize <= endAddr)
|
while (BaseAddress + BlockSize <= EndAddr)
|
||||||
{
|
{
|
||||||
FreeRegion(baseAddress);
|
FreeRegion(BaseAddress);
|
||||||
|
|
||||||
baseAddress += blockSize;
|
BaseAddress += BlockSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong GetFreePages()
|
public ulong GetFreePages()
|
||||||
{
|
{
|
||||||
lock (_blocks)
|
lock (Blocks)
|
||||||
{
|
{
|
||||||
return GetFreePagesImpl();
|
return GetFreePagesImpl();
|
||||||
}
|
}
|
||||||
|
@ -411,18 +411,18 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private ulong GetFreePagesImpl()
|
private ulong GetFreePagesImpl()
|
||||||
{
|
{
|
||||||
ulong availablePages = 0;
|
ulong AvailablePages = 0;
|
||||||
|
|
||||||
for (int blockIndex = 0; blockIndex < _blockOrdersCount; blockIndex++)
|
for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++)
|
||||||
{
|
{
|
||||||
KMemoryRegionBlock block = _blocks[blockIndex];
|
KMemoryRegionBlock Block = Blocks[BlockIndex];
|
||||||
|
|
||||||
ulong blockPagesCount = (1UL << block.Order) / KMemoryManager.PageSize;
|
ulong BlockPagesCount = (1UL << Block.Order) / KMemoryManager.PageSize;
|
||||||
|
|
||||||
availablePages += blockPagesCount * block.FreeCount;
|
AvailablePages += BlockPagesCount * Block.FreeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
return availablePages;
|
return AvailablePages;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue