mirror of
https://github.com/GreemDev/Ryujinx.git
synced 2024-12-23 03:25:46 +00:00
Normalize all the line endings (#518)
This commit is contained in:
parent
59964f667c
commit
9b22e8af5e
6 changed files with 279 additions and 279 deletions
|
@ -1,53 +1,53 @@
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Npdm
|
namespace Ryujinx.HLE.Loaders.Npdm
|
||||||
{
|
{
|
||||||
class ACI0
|
class ACI0
|
||||||
{
|
{
|
||||||
private const int ACI0Magic = 'A' << 0 | 'C' << 8 | 'I' << 16 | '0' << 24;
|
private const int ACI0Magic = 'A' << 0 | 'C' << 8 | 'I' << 16 | '0' << 24;
|
||||||
|
|
||||||
public long TitleId { get; private set; }
|
public long TitleId { get; private set; }
|
||||||
|
|
||||||
public int FsVersion { get; private set; }
|
public int FsVersion { get; private set; }
|
||||||
public ulong FsPermissionsBitmask { get; private set; }
|
public ulong FsPermissionsBitmask { get; private set; }
|
||||||
|
|
||||||
public ServiceAccessControl ServiceAccessControl { get; private set; }
|
public ServiceAccessControl ServiceAccessControl { get; private set; }
|
||||||
public KernelAccessControl KernelAccessControl { get; private set; }
|
public KernelAccessControl KernelAccessControl { get; private set; }
|
||||||
|
|
||||||
public ACI0(Stream Stream, int Offset)
|
public ACI0(Stream Stream, int Offset)
|
||||||
{
|
{
|
||||||
Stream.Seek(Offset, SeekOrigin.Begin);
|
Stream.Seek(Offset, SeekOrigin.Begin);
|
||||||
|
|
||||||
BinaryReader Reader = new BinaryReader(Stream);
|
BinaryReader Reader = new BinaryReader(Stream);
|
||||||
|
|
||||||
if (Reader.ReadInt32() != ACI0Magic)
|
if (Reader.ReadInt32() != ACI0Magic)
|
||||||
{
|
{
|
||||||
throw new InvalidNpdmException("ACI0 Stream doesn't contain ACI0 section!");
|
throw new InvalidNpdmException("ACI0 Stream doesn't contain ACI0 section!");
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream.Seek(0xc, SeekOrigin.Current);
|
Stream.Seek(0xc, SeekOrigin.Current);
|
||||||
|
|
||||||
TitleId = Reader.ReadInt64();
|
TitleId = Reader.ReadInt64();
|
||||||
|
|
||||||
//Reserved.
|
//Reserved.
|
||||||
Stream.Seek(8, SeekOrigin.Current);
|
Stream.Seek(8, SeekOrigin.Current);
|
||||||
|
|
||||||
int FsAccessHeaderOffset = Reader.ReadInt32();
|
int FsAccessHeaderOffset = Reader.ReadInt32();
|
||||||
int FsAccessHeaderSize = Reader.ReadInt32();
|
int FsAccessHeaderSize = Reader.ReadInt32();
|
||||||
int ServiceAccessControlOffset = Reader.ReadInt32();
|
int ServiceAccessControlOffset = Reader.ReadInt32();
|
||||||
int ServiceAccessControlSize = Reader.ReadInt32();
|
int ServiceAccessControlSize = Reader.ReadInt32();
|
||||||
int KernelAccessControlOffset = Reader.ReadInt32();
|
int KernelAccessControlOffset = Reader.ReadInt32();
|
||||||
int KernelAccessControlSize = Reader.ReadInt32();
|
int KernelAccessControlSize = Reader.ReadInt32();
|
||||||
|
|
||||||
FsAccessHeader FsAccessHeader = new FsAccessHeader(Stream, Offset + FsAccessHeaderOffset, FsAccessHeaderSize);
|
FsAccessHeader FsAccessHeader = new FsAccessHeader(Stream, Offset + FsAccessHeaderOffset, FsAccessHeaderSize);
|
||||||
|
|
||||||
FsVersion = FsAccessHeader.Version;
|
FsVersion = FsAccessHeader.Version;
|
||||||
FsPermissionsBitmask = FsAccessHeader.PermissionsBitmask;
|
FsPermissionsBitmask = FsAccessHeader.PermissionsBitmask;
|
||||||
|
|
||||||
ServiceAccessControl = new ServiceAccessControl(Stream, Offset + ServiceAccessControlOffset, ServiceAccessControlSize);
|
ServiceAccessControl = new ServiceAccessControl(Stream, Offset + ServiceAccessControlOffset, ServiceAccessControlSize);
|
||||||
|
|
||||||
KernelAccessControl = new KernelAccessControl(Stream, Offset + KernelAccessControlOffset, KernelAccessControlSize);
|
KernelAccessControl = new KernelAccessControl(Stream, Offset + KernelAccessControlOffset, KernelAccessControlSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,61 +1,61 @@
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Npdm
|
namespace Ryujinx.HLE.Loaders.Npdm
|
||||||
{
|
{
|
||||||
class ACID
|
class ACID
|
||||||
{
|
{
|
||||||
private const int ACIDMagic = 'A' << 0 | 'C' << 8 | 'I' << 16 | 'D' << 24;
|
private const int ACIDMagic = 'A' << 0 | 'C' << 8 | 'I' << 16 | 'D' << 24;
|
||||||
|
|
||||||
public byte[] RSA2048Signature { get; private set; }
|
public byte[] RSA2048Signature { get; private set; }
|
||||||
public byte[] RSA2048Modulus { get; private set; }
|
public byte[] RSA2048Modulus { get; private set; }
|
||||||
public int Unknown1 { get; private set; }
|
public int Unknown1 { get; private set; }
|
||||||
public int Flags { get; private set; }
|
public int Flags { get; private set; }
|
||||||
|
|
||||||
public long TitleIdRangeMin { get; private set; }
|
public long TitleIdRangeMin { get; private set; }
|
||||||
public long TitleIdRangeMax { get; private set; }
|
public long TitleIdRangeMax { get; private set; }
|
||||||
|
|
||||||
public FsAccessControl FsAccessControl { get; private set; }
|
public FsAccessControl FsAccessControl { get; private set; }
|
||||||
public ServiceAccessControl ServiceAccessControl { get; private set; }
|
public ServiceAccessControl ServiceAccessControl { get; private set; }
|
||||||
public KernelAccessControl KernelAccessControl { get; private set; }
|
public KernelAccessControl KernelAccessControl { get; private set; }
|
||||||
|
|
||||||
public ACID(Stream Stream, int Offset)
|
public ACID(Stream Stream, int Offset)
|
||||||
{
|
{
|
||||||
Stream.Seek(Offset, SeekOrigin.Begin);
|
Stream.Seek(Offset, SeekOrigin.Begin);
|
||||||
|
|
||||||
BinaryReader Reader = new BinaryReader(Stream);
|
BinaryReader Reader = new BinaryReader(Stream);
|
||||||
|
|
||||||
RSA2048Signature = Reader.ReadBytes(0x100);
|
RSA2048Signature = Reader.ReadBytes(0x100);
|
||||||
RSA2048Modulus = Reader.ReadBytes(0x100);
|
RSA2048Modulus = Reader.ReadBytes(0x100);
|
||||||
|
|
||||||
if (Reader.ReadInt32() != ACIDMagic)
|
if (Reader.ReadInt32() != ACIDMagic)
|
||||||
{
|
{
|
||||||
throw new InvalidNpdmException("ACID Stream doesn't contain ACID section!");
|
throw new InvalidNpdmException("ACID Stream doesn't contain ACID section!");
|
||||||
}
|
}
|
||||||
|
|
||||||
//Size field used with the above signature (?).
|
//Size field used with the above signature (?).
|
||||||
Unknown1 = Reader.ReadInt32();
|
Unknown1 = Reader.ReadInt32();
|
||||||
|
|
||||||
Reader.ReadInt32();
|
Reader.ReadInt32();
|
||||||
|
|
||||||
//Bit0 must be 1 on retail, on devunit 0 is also allowed. Bit1 is unknown.
|
//Bit0 must be 1 on retail, on devunit 0 is also allowed. Bit1 is unknown.
|
||||||
Flags = Reader.ReadInt32();
|
Flags = Reader.ReadInt32();
|
||||||
|
|
||||||
TitleIdRangeMin = Reader.ReadInt64();
|
TitleIdRangeMin = Reader.ReadInt64();
|
||||||
TitleIdRangeMax = Reader.ReadInt64();
|
TitleIdRangeMax = Reader.ReadInt64();
|
||||||
|
|
||||||
int FsAccessControlOffset = Reader.ReadInt32();
|
int FsAccessControlOffset = Reader.ReadInt32();
|
||||||
int FsAccessControlSize = Reader.ReadInt32();
|
int FsAccessControlSize = Reader.ReadInt32();
|
||||||
int ServiceAccessControlOffset = Reader.ReadInt32();
|
int ServiceAccessControlOffset = Reader.ReadInt32();
|
||||||
int ServiceAccessControlSize = Reader.ReadInt32();
|
int ServiceAccessControlSize = Reader.ReadInt32();
|
||||||
int KernelAccessControlOffset = Reader.ReadInt32();
|
int KernelAccessControlOffset = Reader.ReadInt32();
|
||||||
int KernelAccessControlSize = Reader.ReadInt32();
|
int KernelAccessControlSize = Reader.ReadInt32();
|
||||||
|
|
||||||
FsAccessControl = new FsAccessControl(Stream, Offset + FsAccessControlOffset, FsAccessControlSize);
|
FsAccessControl = new FsAccessControl(Stream, Offset + FsAccessControlOffset, FsAccessControlSize);
|
||||||
|
|
||||||
ServiceAccessControl = new ServiceAccessControl(Stream, Offset + ServiceAccessControlOffset, ServiceAccessControlSize);
|
ServiceAccessControl = new ServiceAccessControl(Stream, Offset + ServiceAccessControlOffset, ServiceAccessControlSize);
|
||||||
|
|
||||||
KernelAccessControl = new KernelAccessControl(Stream, Offset + KernelAccessControlOffset, KernelAccessControlSize);
|
KernelAccessControl = new KernelAccessControl(Stream, Offset + KernelAccessControlOffset, KernelAccessControlSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Npdm
|
namespace Ryujinx.HLE.Loaders.Npdm
|
||||||
{
|
{
|
||||||
class KernelAccessControl
|
class KernelAccessControl
|
||||||
{
|
{
|
||||||
public int[] Capabilities { get; private set; }
|
public int[] Capabilities { get; private set; }
|
||||||
|
|
||||||
public KernelAccessControl(Stream Stream, int Offset, int Size)
|
public KernelAccessControl(Stream Stream, int Offset, int Size)
|
||||||
{
|
{
|
||||||
Stream.Seek(Offset, SeekOrigin.Begin);
|
Stream.Seek(Offset, SeekOrigin.Begin);
|
||||||
|
|
||||||
Capabilities = new int[Size / 4];
|
Capabilities = new int[Size / 4];
|
||||||
|
|
||||||
BinaryReader Reader = new BinaryReader(Stream);
|
BinaryReader Reader = new BinaryReader(Stream);
|
||||||
|
|
||||||
for (int Index = 0; Index < Capabilities.Length; Index++)
|
for (int Index = 0; Index < Capabilities.Length; Index++)
|
||||||
{
|
{
|
||||||
Capabilities[Index] = Reader.ReadInt32();
|
Capabilities[Index] = Reader.ReadInt32();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,72 +1,72 @@
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Npdm
|
namespace Ryujinx.HLE.Loaders.Npdm
|
||||||
{
|
{
|
||||||
//https://github.com/SciresM/hactool/blob/master/npdm.c
|
//https://github.com/SciresM/hactool/blob/master/npdm.c
|
||||||
//https://github.com/SciresM/hactool/blob/master/npdm.h
|
//https://github.com/SciresM/hactool/blob/master/npdm.h
|
||||||
//http://switchbrew.org/index.php?title=NPDM
|
//http://switchbrew.org/index.php?title=NPDM
|
||||||
class Npdm
|
class Npdm
|
||||||
{
|
{
|
||||||
private const int MetaMagic = 'M' << 0 | 'E' << 8 | 'T' << 16 | 'A' << 24;
|
private const int MetaMagic = 'M' << 0 | 'E' << 8 | 'T' << 16 | 'A' << 24;
|
||||||
|
|
||||||
public byte MmuFlags { get; private set; }
|
public byte MmuFlags { get; private set; }
|
||||||
public bool Is64Bits { get; private set; }
|
public bool Is64Bits { get; private set; }
|
||||||
public byte MainThreadPriority { get; private set; }
|
public byte MainThreadPriority { get; private set; }
|
||||||
public byte DefaultCpuId { get; private set; }
|
public byte DefaultCpuId { get; private set; }
|
||||||
public int PersonalMmHeapSize { get; private set; }
|
public int PersonalMmHeapSize { get; private set; }
|
||||||
public int ProcessCategory { get; private set; }
|
public int ProcessCategory { get; private set; }
|
||||||
public int MainThreadStackSize { get; private set; }
|
public int MainThreadStackSize { get; private set; }
|
||||||
public string TitleName { get; private set; }
|
public string TitleName { get; private set; }
|
||||||
public byte[] ProductCode { get; private set; }
|
public byte[] ProductCode { get; private set; }
|
||||||
|
|
||||||
public ACI0 ACI0 { get; private set; }
|
public ACI0 ACI0 { get; private set; }
|
||||||
public ACID ACID { get; private set; }
|
public ACID ACID { get; private set; }
|
||||||
|
|
||||||
public Npdm(Stream Stream)
|
public Npdm(Stream Stream)
|
||||||
{
|
{
|
||||||
BinaryReader Reader = new BinaryReader(Stream);
|
BinaryReader Reader = new BinaryReader(Stream);
|
||||||
|
|
||||||
if (Reader.ReadInt32() != MetaMagic)
|
if (Reader.ReadInt32() != MetaMagic)
|
||||||
{
|
{
|
||||||
throw new InvalidNpdmException("NPDM Stream doesn't contain NPDM file!");
|
throw new InvalidNpdmException("NPDM Stream doesn't contain NPDM file!");
|
||||||
}
|
}
|
||||||
|
|
||||||
Reader.ReadInt64();
|
Reader.ReadInt64();
|
||||||
|
|
||||||
MmuFlags = Reader.ReadByte();
|
MmuFlags = Reader.ReadByte();
|
||||||
|
|
||||||
Is64Bits = (MmuFlags & 1) != 0;
|
Is64Bits = (MmuFlags & 1) != 0;
|
||||||
|
|
||||||
Reader.ReadByte();
|
Reader.ReadByte();
|
||||||
|
|
||||||
MainThreadPriority = Reader.ReadByte();
|
MainThreadPriority = Reader.ReadByte();
|
||||||
DefaultCpuId = Reader.ReadByte();
|
DefaultCpuId = Reader.ReadByte();
|
||||||
|
|
||||||
Reader.ReadInt32();
|
Reader.ReadInt32();
|
||||||
|
|
||||||
PersonalMmHeapSize = Reader.ReadInt32();
|
PersonalMmHeapSize = Reader.ReadInt32();
|
||||||
|
|
||||||
ProcessCategory = Reader.ReadInt32();
|
ProcessCategory = Reader.ReadInt32();
|
||||||
|
|
||||||
MainThreadStackSize = Reader.ReadInt32();
|
MainThreadStackSize = Reader.ReadInt32();
|
||||||
|
|
||||||
byte[] TempTitleName = Reader.ReadBytes(0x10);
|
byte[] TempTitleName = Reader.ReadBytes(0x10);
|
||||||
|
|
||||||
TitleName = Encoding.UTF8.GetString(TempTitleName, 0, TempTitleName.Length).Trim('\0');
|
TitleName = Encoding.UTF8.GetString(TempTitleName, 0, TempTitleName.Length).Trim('\0');
|
||||||
|
|
||||||
ProductCode = Reader.ReadBytes(0x10);
|
ProductCode = Reader.ReadBytes(0x10);
|
||||||
|
|
||||||
Stream.Seek(0x30, SeekOrigin.Current);
|
Stream.Seek(0x30, SeekOrigin.Current);
|
||||||
|
|
||||||
int ACI0Offset = Reader.ReadInt32();
|
int ACI0Offset = Reader.ReadInt32();
|
||||||
int ACI0Size = Reader.ReadInt32();
|
int ACI0Size = Reader.ReadInt32();
|
||||||
int ACIDOffset = Reader.ReadInt32();
|
int ACIDOffset = Reader.ReadInt32();
|
||||||
int ACIDSize = Reader.ReadInt32();
|
int ACIDSize = Reader.ReadInt32();
|
||||||
|
|
||||||
ACI0 = new ACI0(Stream, ACI0Offset);
|
ACI0 = new ACI0(Stream, ACI0Offset);
|
||||||
ACID = new ACID(Stream, ACIDOffset);
|
ACID = new ACID(Stream, ACIDOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,42 +1,42 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Npdm
|
namespace Ryujinx.HLE.Loaders.Npdm
|
||||||
{
|
{
|
||||||
class ServiceAccessControl
|
class ServiceAccessControl
|
||||||
{
|
{
|
||||||
public IReadOnlyDictionary<string, bool> Services { get; private set; }
|
public IReadOnlyDictionary<string, bool> Services { get; private set; }
|
||||||
|
|
||||||
public ServiceAccessControl(Stream Stream, int Offset, int Size)
|
public ServiceAccessControl(Stream Stream, int Offset, int Size)
|
||||||
{
|
{
|
||||||
Stream.Seek(Offset, SeekOrigin.Begin);
|
Stream.Seek(Offset, SeekOrigin.Begin);
|
||||||
|
|
||||||
BinaryReader Reader = new BinaryReader(Stream);
|
BinaryReader Reader = new BinaryReader(Stream);
|
||||||
|
|
||||||
int ByteReaded = 0;
|
int ByteReaded = 0;
|
||||||
|
|
||||||
Dictionary<string, bool> Services = new Dictionary<string, bool>();
|
Dictionary<string, bool> Services = new Dictionary<string, bool>();
|
||||||
|
|
||||||
while (ByteReaded != Size)
|
while (ByteReaded != Size)
|
||||||
{
|
{
|
||||||
byte ControlByte = Reader.ReadByte();
|
byte ControlByte = Reader.ReadByte();
|
||||||
|
|
||||||
if (ControlByte == 0)
|
if (ControlByte == 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Length = (ControlByte & 0x07) + 1;
|
int Length = (ControlByte & 0x07) + 1;
|
||||||
bool RegisterAllowed = (ControlByte & 0x80) != 0;
|
bool RegisterAllowed = (ControlByte & 0x80) != 0;
|
||||||
|
|
||||||
Services.Add(Encoding.ASCII.GetString(Reader.ReadBytes(Length), 0, Length), RegisterAllowed);
|
Services.Add(Encoding.ASCII.GetString(Reader.ReadBytes(Length), 0, Length), RegisterAllowed);
|
||||||
|
|
||||||
ByteReaded += Length + 1;
|
ByteReaded += Length + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Services = new ReadOnlyDictionary<string, bool>(Services);
|
this.Services = new ReadOnlyDictionary<string, bool>(Services);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
56
appveyor.yml
56
appveyor.yml
|
@ -1,28 +1,28 @@
|
||||||
version: 1.0.{build}
|
version: 1.0.{build}
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
image: Visual Studio 2017
|
image: Visual Studio 2017
|
||||||
configuration: Release
|
configuration: Release
|
||||||
build_script:
|
build_script:
|
||||||
- ps: >-
|
- ps: >-
|
||||||
dotnet --version
|
dotnet --version
|
||||||
|
|
||||||
dotnet publish -c Release -r win-x64
|
dotnet publish -c Release -r win-x64
|
||||||
|
|
||||||
dotnet publish -c Release -r linux-x64
|
dotnet publish -c Release -r linux-x64
|
||||||
|
|
||||||
dotnet publish -c Release -r osx-x64
|
dotnet publish -c Release -r osx-x64
|
||||||
|
|
||||||
7z a ryujinx-$env:APPVEYOR_BUILD_VERSION-win_x64.zip $env:APPVEYOR_BUILD_FOLDER\Ryujinx\bin\Release\netcoreapp2.1\win-x64\publish\
|
7z a ryujinx-$env:APPVEYOR_BUILD_VERSION-win_x64.zip $env:APPVEYOR_BUILD_FOLDER\Ryujinx\bin\Release\netcoreapp2.1\win-x64\publish\
|
||||||
|
|
||||||
7z a ryujinx-$env:APPVEYOR_BUILD_VERSION-linux_x64.tar $env:APPVEYOR_BUILD_FOLDER\Ryujinx\bin\Release\netcoreapp2.1\linux-x64\publish\
|
7z a ryujinx-$env:APPVEYOR_BUILD_VERSION-linux_x64.tar $env:APPVEYOR_BUILD_FOLDER\Ryujinx\bin\Release\netcoreapp2.1\linux-x64\publish\
|
||||||
|
|
||||||
7z a ryujinx-$env:APPVEYOR_BUILD_VERSION-linux_x64.tar.gz ryujinx-$env:APPVEYOR_BUILD_VERSION-linux_x64.tar
|
7z a ryujinx-$env:APPVEYOR_BUILD_VERSION-linux_x64.tar.gz ryujinx-$env:APPVEYOR_BUILD_VERSION-linux_x64.tar
|
||||||
|
|
||||||
7z a ryujinx-$env:APPVEYOR_BUILD_VERSION-osx_x64.zip $env:APPVEYOR_BUILD_FOLDER\Ryujinx\bin\Release\netcoreapp2.1\osx-x64\publish\
|
7z a ryujinx-$env:APPVEYOR_BUILD_VERSION-osx_x64.zip $env:APPVEYOR_BUILD_FOLDER\Ryujinx\bin\Release\netcoreapp2.1\osx-x64\publish\
|
||||||
|
|
||||||
artifacts:
|
artifacts:
|
||||||
- path: ryujinx-%APPVEYOR_BUILD_VERSION%-win_x64.zip
|
- path: ryujinx-%APPVEYOR_BUILD_VERSION%-win_x64.zip
|
||||||
- path: ryujinx-%APPVEYOR_BUILD_VERSION%-linux_x64.tar.gz
|
- path: ryujinx-%APPVEYOR_BUILD_VERSION%-linux_x64.tar.gz
|
||||||
- path: ryujinx-%APPVEYOR_BUILD_VERSION%-osx_x64.zip
|
- path: ryujinx-%APPVEYOR_BUILD_VERSION%-osx_x64.zip
|
||||||
|
|
Loading…
Reference in a new issue