mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Implement DeliveryCacheFileService and supporting code
This commit is contained in:
parent
939c495db6
commit
ef36568a8d
13 changed files with 727 additions and 52 deletions
|
@ -279,15 +279,20 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
|
||||||
|
|
||||||
122,1,,InvalidArgument,
|
122,1,,InvalidArgument,
|
||||||
122,2,,NotFound,
|
122,2,,NotFound,
|
||||||
|
122,3,,TargetLocked,
|
||||||
|
122,6,,AlreadyOpen,
|
||||||
122,7,,NotOpen,
|
122,7,,NotOpen,
|
||||||
122,9,,ServiceOpenLimitReached,
|
122,9,,ServiceOpenLimitReached,
|
||||||
122,10,,SaveDataNotFount,
|
122,10,,SaveDataNotFound,
|
||||||
|
|
||||||
122,31,,NetworkServiceAccountNotAvailable,
|
122,31,,NetworkServiceAccountNotAvailable,
|
||||||
|
|
||||||
122,90,,PermissionDenied,
|
122,80,,PassphrasePathNotFound,
|
||||||
|
|
||||||
122,204,,InvalidStorageMetaVersion,
|
122,90,,PermissionDenied,
|
||||||
|
122,91,,AllocationFailed,
|
||||||
|
|
||||||
|
122,204,,InvalidDeliveryCacheStorageFile,
|
||||||
122,205,,StorageOpenLimitReached,
|
122,205,,StorageOpenLimitReached,
|
||||||
|
|
||||||
123,0,4999,SslService,
|
123,0,4999,SslService,
|
||||||
|
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using LibHac.Bcat.Detail.Ipc;
|
using LibHac.Bcat.Detail.Ipc;
|
||||||
using LibHac.Bcat.Detail.Service;
|
using LibHac.Bcat.Detail.Service;
|
||||||
|
using LibHac.Bcat.Detail.Service.Core;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ namespace LibHac.Bcat
|
||||||
return StorageManager;
|
return StorageManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
StorageManager = new DeliveryCacheStorageManager();
|
StorageManager = new DeliveryCacheStorageManager(this);
|
||||||
return StorageManager;
|
return StorageManager;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
|
||||||
|
namespace LibHac.Bcat.Detail.Service.Core
|
||||||
|
{
|
||||||
|
internal class DeliveryCacheFileMetaAccessor
|
||||||
|
{
|
||||||
|
private const int MaxEntryCount = 100;
|
||||||
|
private const int MetaFileHeaderValue = 1;
|
||||||
|
|
||||||
|
private BcatServer Server { get; }
|
||||||
|
private object Locker { get; } = new object();
|
||||||
|
private DeliveryCacheFileMetaEntry[] Entries { get; } = new DeliveryCacheFileMetaEntry[MaxEntryCount];
|
||||||
|
private int Count { get; set; }
|
||||||
|
|
||||||
|
public DeliveryCacheFileMetaAccessor(BcatServer server)
|
||||||
|
{
|
||||||
|
Server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result ReadApplicationFileMeta(ulong applicationId, ref DirectoryName directoryName,
|
||||||
|
bool allowMissingMetaFile)
|
||||||
|
{
|
||||||
|
Span<byte> metaPath = stackalloc byte[0x50];
|
||||||
|
Server.GetStorageManager().GetFilesMetaPath(metaPath, applicationId, ref directoryName);
|
||||||
|
|
||||||
|
return Read(new U8Span(metaPath), allowMissingMetaFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result FindEntry(out DeliveryCacheFileMetaEntry entry, ref FileName fileName)
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Count; i++)
|
||||||
|
{
|
||||||
|
if (StringUtils.CompareCaseInsensitive(Entries[i].Name.Bytes, fileName.Bytes) == 0)
|
||||||
|
{
|
||||||
|
entry = Entries[i];
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = default;
|
||||||
|
return ResultBcat.NotFound.Log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result Read(U8Span path, bool allowMissingMetaFile)
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
FileSystemClient fs = Server.GetFsClient();
|
||||||
|
|
||||||
|
Result rc = fs.OpenFile(out FileHandle handle, path, OpenMode.Read);
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
if (ResultFs.PathNotFound.Includes(rc))
|
||||||
|
{
|
||||||
|
if (allowMissingMetaFile)
|
||||||
|
{
|
||||||
|
Count = 0;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultBcat.NotFound.LogConverted(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Count = 0;
|
||||||
|
int header = 0;
|
||||||
|
|
||||||
|
// Verify the header value
|
||||||
|
rc = fs.ReadFile(out long bytesRead, handle, 0, SpanHelpers.AsByteSpan(ref header));
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
if (bytesRead != sizeof(int) || header != MetaFileHeaderValue)
|
||||||
|
return ResultBcat.InvalidDeliveryCacheStorageFile.Log();
|
||||||
|
|
||||||
|
// Read all the file entries
|
||||||
|
Span<byte> buffer = MemoryMarshal.Cast<DeliveryCacheFileMetaEntry, byte>(Entries);
|
||||||
|
rc = fs.ReadFile(out bytesRead, handle, 4, buffer);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
Count = (int)((uint)bytesRead / Unsafe.SizeOf<DeliveryCacheFileMetaEntry>());
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
fs.CloseFile(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Detail.Service
|
namespace LibHac.Bcat.Detail.Service.Core
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x80)]
|
[StructLayout(LayoutKind.Explicit, Size = 0x80)]
|
||||||
internal struct DeliveryCacheFileEntryMeta
|
internal struct DeliveryCacheFileMetaEntry
|
||||||
{
|
{
|
||||||
[FieldOffset(0x00)] public FileName Name;
|
[FieldOffset(0x00)] public FileName Name;
|
||||||
[FieldOffset(0x20)] public long Size;
|
[FieldOffset(0x20)] public long Size;
|
|
@ -0,0 +1,386 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Shim;
|
||||||
|
using LibHac.Ncm;
|
||||||
|
using static LibHac.Fs.StringTraits;
|
||||||
|
|
||||||
|
namespace LibHac.Bcat.Detail.Service.Core
|
||||||
|
{
|
||||||
|
internal class DeliveryCacheStorageManager
|
||||||
|
{
|
||||||
|
private const int MaxEntryCount = 4;
|
||||||
|
|
||||||
|
private BcatServer Server { get; }
|
||||||
|
|
||||||
|
private readonly object _locker = new object();
|
||||||
|
private Entry[] Entries { get; } = new Entry[MaxEntryCount];
|
||||||
|
private bool DisableStorage { get; set; }
|
||||||
|
|
||||||
|
private struct Entry
|
||||||
|
{
|
||||||
|
public ulong ApplicationId { get; set; }
|
||||||
|
public long RefCount { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeliveryCacheStorageManager(BcatServer server)
|
||||||
|
{
|
||||||
|
Server = server;
|
||||||
|
DisableStorage = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result Open(ulong applicationId)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
// Find an existing storage entry for this application ID or get an empty one
|
||||||
|
Result rc = FindOrGetUnusedEntry(out int index, applicationId);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
ref Entry entry = ref Entries[index];
|
||||||
|
|
||||||
|
if (entry.RefCount != 0)
|
||||||
|
{
|
||||||
|
return ResultBcat.TargetLocked.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the mount name
|
||||||
|
var mountName = new MountName();
|
||||||
|
|
||||||
|
new U8StringBuilder(mountName.Name)
|
||||||
|
.Append(DeliveryCacheMountNamePrefix)
|
||||||
|
.AppendFormat(index, 'd', 2);
|
||||||
|
|
||||||
|
// Mount the save if enabled
|
||||||
|
if (!DisableStorage)
|
||||||
|
{
|
||||||
|
rc = Server.GetFsClient()
|
||||||
|
.MountBcatSaveData(new U8Span(mountName.Name), new TitleId(applicationId));
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
if (ResultFs.TargetNotFound.Includes(rc))
|
||||||
|
return ResultBcat.SaveDataNotFound.LogConverted(rc);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the storage entry
|
||||||
|
entry.ApplicationId = applicationId;
|
||||||
|
entry.RefCount++;
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Release(ulong applicationId)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
int index = FindEntry(applicationId);
|
||||||
|
ref Entry entry = ref Entries[index];
|
||||||
|
|
||||||
|
entry.RefCount--;
|
||||||
|
|
||||||
|
// Free the entry if there are no more references
|
||||||
|
if (entry.RefCount == 0)
|
||||||
|
{
|
||||||
|
var mountName = new MountName();
|
||||||
|
|
||||||
|
new U8StringBuilder(mountName.Name)
|
||||||
|
.Append(DeliveryCacheMountNamePrefix)
|
||||||
|
.AppendFormat(index, 'd', 2);
|
||||||
|
|
||||||
|
// Unmount the entry's savedata
|
||||||
|
if (!DisableStorage)
|
||||||
|
{
|
||||||
|
Server.GetFsClient().Unmount(new U8Span(mountName.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the entry
|
||||||
|
entry.ApplicationId = 0;
|
||||||
|
|
||||||
|
// todo: Call nn::bcat::detail::service::core::PassphraseManager::Remove
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Commit(ulong applicationId)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
int index = FindEntry(applicationId);
|
||||||
|
|
||||||
|
var mountName = new MountName();
|
||||||
|
|
||||||
|
new U8StringBuilder(mountName.Name)
|
||||||
|
.Append(DeliveryCacheMountNamePrefix)
|
||||||
|
.AppendFormat(index, 'd', 2);
|
||||||
|
|
||||||
|
if (!DisableStorage)
|
||||||
|
{
|
||||||
|
Result rc = Server.GetFsClient().Commit(new U8Span(mountName.Name));
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
throw new HorizonResultException(rc, "Abort");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result GetFreeSpaceSize(out long size, ulong applicationId)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
Span<byte> path = stackalloc byte[0x20];
|
||||||
|
|
||||||
|
var sb = new U8StringBuilder(path);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
sb.Append(RootPath);
|
||||||
|
|
||||||
|
Result rc;
|
||||||
|
|
||||||
|
if (DisableStorage)
|
||||||
|
{
|
||||||
|
size = 0x4400000;
|
||||||
|
rc = Result.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rc = Server.GetFsClient().GetFreeSpaceSize(out size, new U8Span(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetPassphrasePath(Span<byte> pathBuffer, ulong applicationId)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
sb.Append(PassphrasePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetDeliveryListPath(Span<byte> pathBuffer, ulong applicationId)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
sb.Append(DeliveryListPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetEtagFilePath(Span<byte> pathBuffer, ulong applicationId)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
sb.Append(EtagPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetNaRequiredPath(Span<byte> pathBuffer, ulong applicationId)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
sb.Append(NaRequiredPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetIndexLockPath(Span<byte> pathBuffer, ulong applicationId)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
sb.Append(IndexLockPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetFilePath(Span<byte> pathBuffer, ulong applicationId, ref DirectoryName directoryName,
|
||||||
|
ref FileName fileName)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
|
||||||
|
sb.Append(DirectoriesPath);
|
||||||
|
sb.Append(DirectorySeparator).Append(directoryName.Bytes);
|
||||||
|
sb.Append(DirectorySeparator).Append(FilesDirectoryName);
|
||||||
|
sb.Append(DirectorySeparator).Append(fileName.Bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetFilesMetaPath(Span<byte> pathBuffer, ulong applicationId, ref DirectoryName directoryName)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
|
||||||
|
sb.Append(DirectoriesPath);
|
||||||
|
sb.Append(DirectorySeparator).Append(directoryName.Bytes);
|
||||||
|
sb.Append(DirectorySeparator).Append(FilesMetaFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetDirectoriesPath(Span<byte> pathBuffer, ulong applicationId)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
sb.Append(DirectoriesPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetDirectoryPath(Span<byte> pathBuffer, ulong applicationId, ref DirectoryName directoryName)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
|
||||||
|
sb.Append(DirectoriesPath);
|
||||||
|
sb.Append(DirectorySeparator).Append(directoryName.Bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetDirectoriesMetaPath(Span<byte> pathBuffer, ulong applicationId)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
|
||||||
|
sb.Append(DirectoriesMetaPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendMountName(ref U8StringBuilder sb, ulong applicationId)
|
||||||
|
{
|
||||||
|
int index = FindEntry(applicationId);
|
||||||
|
|
||||||
|
sb.Append(DeliveryCacheMountNamePrefix)
|
||||||
|
.AppendFormat(index, 'd', 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result FindOrGetUnusedEntry(out int entryIndex, ulong applicationId)
|
||||||
|
{
|
||||||
|
// Try to find an existing entry
|
||||||
|
for (int i = 0; i < Entries.Length; i++)
|
||||||
|
{
|
||||||
|
if (Entries[i].ApplicationId == applicationId)
|
||||||
|
{
|
||||||
|
entryIndex = i;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find an unused entry
|
||||||
|
for (int i = 0; i < Entries.Length; i++)
|
||||||
|
{
|
||||||
|
if (Entries[i].ApplicationId == 0)
|
||||||
|
{
|
||||||
|
entryIndex = i;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entryIndex = default;
|
||||||
|
return ResultBcat.StorageOpenLimitReached.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int FindEntry(ulong applicationId)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Entries.Length; i++)
|
||||||
|
{
|
||||||
|
if (Entries[i].ApplicationId == applicationId)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nintendo uses 1 as the entry index if it wasn't found
|
||||||
|
Debug.Assert(false, "Entry not found.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> DeliveryCacheMountNamePrefix => // bcat-dc-
|
||||||
|
new[] { (byte)'b', (byte)'c', (byte)'a', (byte)'t', (byte)'-', (byte)'d', (byte)'c', (byte)'-' };
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> RootPath => // :/
|
||||||
|
new[] { (byte)':', (byte)'/' };
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> PassphrasePath => // :/passphrase.bin
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte) ':', (byte) '/', (byte) 'p', (byte) 'a', (byte) 's', (byte) 's', (byte) 'p', (byte) 'h',
|
||||||
|
(byte) 'r', (byte) 'a', (byte) 's', (byte) 'e', (byte) '.', (byte) 'b', (byte) 'i', (byte) 'n'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> DeliveryListPath => // :/list.msgpack
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte) ':', (byte) '/', (byte) 'l', (byte) 'i', (byte) 's', (byte) 't', (byte) '.', (byte) 'm',
|
||||||
|
(byte) 's', (byte) 'g', (byte) 'p', (byte) 'a', (byte) 'c', (byte) 'k'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> EtagPath => // :/etag.bin
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte) ':', (byte) '/', (byte) 'e', (byte) 't', (byte) 'a', (byte) 'g', (byte) '.', (byte) 'b',
|
||||||
|
(byte) 'i', (byte) 'n'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> NaRequiredPath => // :/na_required
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte) ':', (byte) '/', (byte) 'n', (byte) 'a', (byte) '_', (byte) 'r', (byte) 'e', (byte) 'q',
|
||||||
|
(byte) 'u', (byte) 'i', (byte) 'r', (byte) 'e', (byte) 'd'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> IndexLockPath => // :/index.lock
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte) ':', (byte) '/', (byte) 'i', (byte) 'n', (byte) 'd', (byte) 'e', (byte) 'x', (byte) '.',
|
||||||
|
(byte) 'l', (byte) 'o', (byte) 'c', (byte) 'k'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> DirectoriesPath => // :/directories
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte) ':', (byte) '/', (byte) 'd', (byte) 'i', (byte) 'r', (byte) 'e', (byte) 'c', (byte) 't',
|
||||||
|
(byte) 'o', (byte) 'r', (byte) 'i', (byte) 'e', (byte) 's'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> FilesMetaFileName => // files.meta
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte) 'f', (byte) 'i', (byte) 'l', (byte) 'e', (byte) 's', (byte) '.', (byte) 'm', (byte) 'e',
|
||||||
|
(byte) 't', (byte) 'a'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> DirectoriesMetaPath => // :/directories.meta
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte) ':', (byte) '/', (byte) 'd', (byte) 'i', (byte) 'r', (byte) 'e', (byte) 'c', (byte) 't',
|
||||||
|
(byte) 'o', (byte) 'r', (byte) 'i', (byte) 'e', (byte) 's', (byte) '.', (byte) 'm', (byte) 'e',
|
||||||
|
(byte) 't', (byte) 'a'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> FilesDirectoryName => // files
|
||||||
|
new[] { (byte)'f', (byte)'i', (byte)'l', (byte)'e', (byte)'s' };
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ namespace LibHac.Bcat.Detail.Service
|
||||||
{
|
{
|
||||||
internal class DeliveryCacheDirectoryService : IDeliveryCacheDirectoryService
|
internal class DeliveryCacheDirectoryService : IDeliveryCacheDirectoryService
|
||||||
{
|
{
|
||||||
|
private BcatServer Server { get; }
|
||||||
public object Locker { get; } = new object();
|
public object Locker { get; } = new object();
|
||||||
private DeliveryCacheStorageService Parent { get; }
|
private DeliveryCacheStorageService Parent { get; }
|
||||||
private AccessControl Access { get; }
|
private AccessControl Access { get; }
|
||||||
|
@ -13,9 +14,10 @@ namespace LibHac.Bcat.Detail.Service
|
||||||
private bool IsDirectoryOpen { get; set; }
|
private bool IsDirectoryOpen { get; set; }
|
||||||
private int Count { get; set; }
|
private int Count { get; set; }
|
||||||
|
|
||||||
public DeliveryCacheDirectoryService(DeliveryCacheStorageService parent, ulong applicationId,
|
public DeliveryCacheDirectoryService(BcatServer server, DeliveryCacheStorageService parent, ulong applicationId,
|
||||||
AccessControl accessControl)
|
AccessControl accessControl)
|
||||||
{
|
{
|
||||||
|
Server = server;
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
ApplicationId = applicationId;
|
ApplicationId = applicationId;
|
||||||
Access = accessControl;
|
Access = accessControl;
|
||||||
|
|
|
@ -1,22 +1,26 @@
|
||||||
using System;
|
using System;
|
||||||
using LibHac.Bcat.Detail.Ipc;
|
using LibHac.Bcat.Detail.Ipc;
|
||||||
|
using LibHac.Bcat.Detail.Service.Core;
|
||||||
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Detail.Service
|
namespace LibHac.Bcat.Detail.Service
|
||||||
{
|
{
|
||||||
internal class DeliveryCacheFileService : IDeliveryCacheFileService
|
internal class DeliveryCacheFileService : IDeliveryCacheFileService, IDisposable
|
||||||
{
|
{
|
||||||
public object Locker { get; } = new object();
|
private BcatServer Server { get; }
|
||||||
|
private object Locker { get; } = new object();
|
||||||
private DeliveryCacheStorageService Parent { get; }
|
private DeliveryCacheStorageService Parent { get; }
|
||||||
private AccessControl Access { get; }
|
private AccessControl Access { get; }
|
||||||
private ulong ApplicationId { get; }
|
private ulong ApplicationId { get; }
|
||||||
private FileHandle Handle { get; set; }
|
private FileHandle _handle;
|
||||||
private DeliveryCacheFileEntryMeta _metaEntry;
|
private DeliveryCacheFileMetaEntry _metaEntry;
|
||||||
private bool IsFileOpen { get; set; }
|
private bool IsFileOpen { get; set; }
|
||||||
|
|
||||||
public DeliveryCacheFileService(DeliveryCacheStorageService parent, ulong applicationId,
|
public DeliveryCacheFileService(BcatServer server, DeliveryCacheStorageService parent, ulong applicationId,
|
||||||
AccessControl accessControl)
|
AccessControl accessControl)
|
||||||
{
|
{
|
||||||
|
Server = server;
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
ApplicationId = applicationId;
|
ApplicationId = applicationId;
|
||||||
Access = accessControl;
|
Access = accessControl;
|
||||||
|
@ -24,22 +28,91 @@ namespace LibHac.Bcat.Detail.Service
|
||||||
|
|
||||||
public Result Open(ref DirectoryName directoryName, ref FileName fileName)
|
public Result Open(ref DirectoryName directoryName, ref FileName fileName)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
if (!directoryName.IsValid())
|
||||||
|
return ResultBcat.InvalidArgument.Log();
|
||||||
|
|
||||||
|
if (!fileName.IsValid())
|
||||||
|
return ResultBcat.InvalidArgument.Log();
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
if (IsFileOpen)
|
||||||
|
return ResultBcat.AlreadyOpen.Log();
|
||||||
|
|
||||||
|
var metaReader = new DeliveryCacheFileMetaAccessor(Server);
|
||||||
|
Result rc = metaReader.ReadApplicationFileMeta(ApplicationId, ref directoryName, true);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = metaReader.FindEntry(out DeliveryCacheFileMetaEntry entry, ref fileName);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
Span<byte> filePath = stackalloc byte[0x80];
|
||||||
|
Server.GetStorageManager().GetFilePath(filePath, ApplicationId, ref directoryName, ref fileName);
|
||||||
|
|
||||||
|
rc = Server.GetFsClient().OpenFile(out _handle, new U8Span(filePath), OpenMode.Read);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
_metaEntry = entry;
|
||||||
|
IsFileOpen = true;
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Read(out long bytesRead, long offset, Span<byte> destination)
|
public Result Read(out long bytesRead, long offset, Span<byte> destination)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
lock (Locker)
|
||||||
|
{
|
||||||
|
bytesRead = 0;
|
||||||
|
|
||||||
|
if (!IsFileOpen)
|
||||||
|
return ResultBcat.NotOpen.Log();
|
||||||
|
|
||||||
|
Result rc = Server.GetFsClient().ReadFile(out long read, _handle, offset, destination);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
bytesRead = read;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result GetSize(out long size)
|
public Result GetSize(out long size)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
lock (Locker)
|
||||||
|
{
|
||||||
|
if (!IsFileOpen)
|
||||||
|
{
|
||||||
|
size = default;
|
||||||
|
return ResultBcat.NotOpen.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Server.GetFsClient().GetFileSize(out size, _handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result GetDigest(out Digest digest)
|
public Result GetDigest(out Digest digest)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
lock (Locker)
|
||||||
|
{
|
||||||
|
if (!IsFileOpen)
|
||||||
|
{
|
||||||
|
digest = default;
|
||||||
|
return ResultBcat.NotOpen.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
digest = _metaEntry.Digest;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (IsFileOpen)
|
||||||
|
{
|
||||||
|
Server.GetFsClient().CloseFile(_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Parent.NotifyCloseFile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
namespace LibHac.Bcat.Detail.Service
|
|
||||||
{
|
|
||||||
internal class DeliveryCacheStorageManager
|
|
||||||
{
|
|
||||||
private const int MaxEntryCount = 4;
|
|
||||||
|
|
||||||
private readonly object _locker = new object();
|
|
||||||
private Entry[] Entries { get; set; } = new Entry[MaxEntryCount];
|
|
||||||
private bool UseRealStorage { get; set; }
|
|
||||||
|
|
||||||
private struct Entry
|
|
||||||
{
|
|
||||||
public ulong ApplicationId { get; set; }
|
|
||||||
public long RefCount { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +1,87 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using LibHac.Bcat.Detail.Ipc;
|
using LibHac.Bcat.Detail.Ipc;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Detail.Service
|
namespace LibHac.Bcat.Detail.Service
|
||||||
{
|
{
|
||||||
internal class DeliveryCacheStorageService : IDeliveryCacheStorageService
|
internal class DeliveryCacheStorageService : IDeliveryCacheStorageService, IDisposable
|
||||||
{
|
{
|
||||||
|
private const int MaxOpenCount = 8;
|
||||||
|
private BcatServer Server { get; }
|
||||||
|
|
||||||
public object Locker { get; } = new object();
|
public object Locker { get; } = new object();
|
||||||
private AccessControl Access { get; }
|
private AccessControl Access { get; }
|
||||||
private ulong ApplicationId { get; }
|
private ulong ApplicationId { get; }
|
||||||
private int OpenFileServiceCount { get; set; }
|
private int FileServiceOpenCount { get; set; }
|
||||||
private int OpenDirectoryServiceCount { get; set; }
|
private int DirectoryServiceOpenCount { get; set; }
|
||||||
|
|
||||||
public DeliveryCacheStorageService(ulong applicationId, AccessControl accessControl)
|
public DeliveryCacheStorageService(BcatServer server, ulong applicationId, AccessControl accessControl)
|
||||||
{
|
{
|
||||||
|
Server = server;
|
||||||
ApplicationId = applicationId;
|
ApplicationId = applicationId;
|
||||||
Access = accessControl;
|
Access = accessControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result CreateFileService(out IDeliveryCacheFileService fileService)
|
public Result CreateFileService(out IDeliveryCacheFileService service)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
lock (Locker)
|
||||||
|
{
|
||||||
|
service = default;
|
||||||
|
|
||||||
|
if (FileServiceOpenCount >= MaxOpenCount)
|
||||||
|
return ResultBcat.ServiceOpenLimitReached.Log();
|
||||||
|
|
||||||
|
service = new DeliveryCacheFileService(Server, this, ApplicationId, Access);
|
||||||
|
|
||||||
|
FileServiceOpenCount++;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result CreateDirectoryService(out IDeliveryCacheDirectoryService directoryService)
|
public Result CreateDirectoryService(out IDeliveryCacheDirectoryService service)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
lock (Locker)
|
||||||
|
{
|
||||||
|
service = default;
|
||||||
|
|
||||||
|
if (DirectoryServiceOpenCount >= MaxOpenCount)
|
||||||
|
return ResultBcat.ServiceOpenLimitReached.Log();
|
||||||
|
|
||||||
|
service = new DeliveryCacheDirectoryService(Server, this, ApplicationId, Access);
|
||||||
|
|
||||||
|
DirectoryServiceOpenCount++;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result EnumerateDeliveryCacheDirectory(out int namesRead, Span<DirectoryName> nameBuffer)
|
public Result EnumerateDeliveryCacheDirectory(out int namesRead, Span<DirectoryName> nameBuffer)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void NotifyCloseFile()
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
FileServiceOpenCount--;
|
||||||
|
|
||||||
|
Debug.Assert(FileServiceOpenCount >= 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void NotifyCloseDirectory()
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
DirectoryServiceOpenCount--;
|
||||||
|
|
||||||
|
Debug.Assert(DirectoryServiceOpenCount >= 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Server.GetStorageManager().Release(ApplicationId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
using System;
|
using LibHac.Bcat.Detail.Ipc;
|
||||||
using LibHac.Bcat.Detail.Ipc;
|
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Detail.Service
|
namespace LibHac.Bcat.Detail.Service
|
||||||
{
|
{
|
||||||
internal class ServiceCreator : IServiceCreator
|
internal class ServiceCreator : IServiceCreator
|
||||||
{
|
{
|
||||||
private BcatServer Parent { get; }
|
private BcatServer Server { get; }
|
||||||
private BcatServiceType ServiceType { get; }
|
private BcatServiceType ServiceType { get; }
|
||||||
private AccessControl AccessControl { get; }
|
private AccessControl AccessControl { get; }
|
||||||
|
|
||||||
public ServiceCreator(BcatServer parentServer, BcatServiceType type, AccessControl accessControl)
|
public ServiceCreator(BcatServer server, BcatServiceType type, AccessControl accessControl)
|
||||||
{
|
{
|
||||||
Parent = parentServer;
|
Server = server;
|
||||||
ServiceType = type;
|
ServiceType = type;
|
||||||
AccessControl = accessControl;
|
AccessControl = accessControl;
|
||||||
}
|
}
|
||||||
|
@ -31,7 +30,23 @@ namespace LibHac.Bcat.Detail.Service
|
||||||
private Result CreateDeliveryCacheStorageServiceImpl(out IDeliveryCacheStorageService service,
|
private Result CreateDeliveryCacheStorageServiceImpl(out IDeliveryCacheStorageService service,
|
||||||
TitleId applicationId)
|
TitleId applicationId)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
service = default;
|
||||||
|
|
||||||
|
Result rc = Server.GetStorageManager().Open(applicationId.Value);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// todo: Check if network account required
|
||||||
|
|
||||||
|
service = new DeliveryCacheStorageService(Server, applicationId.Value, AccessControl);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Server.GetStorageManager().Release(applicationId.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,11 @@ using LibHac.Common;
|
||||||
namespace LibHac.Bcat
|
namespace LibHac.Bcat
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{ToString()}")]
|
[DebuggerDisplay("{ToString()}")]
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 32)]
|
[StructLayout(LayoutKind.Sequential, Size = MaxSize)]
|
||||||
public struct DirectoryName
|
public struct DirectoryName
|
||||||
{
|
{
|
||||||
|
private const int MaxSize = 0x20;
|
||||||
|
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy2;
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy2;
|
||||||
|
@ -22,6 +24,26 @@ namespace LibHac.Bcat
|
||||||
|
|
||||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||||
|
|
||||||
|
public bool IsValid()
|
||||||
|
{
|
||||||
|
Span<byte> name = Bytes;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < name.Length; i++)
|
||||||
|
{
|
||||||
|
if (name[i] == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!StringUtils.IsDigit(name[i]) && !StringUtils.IsAlpha(name[i]) && name[i] != '_' && name[i] != '-')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0 || i == MaxSize)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return name[i] == 0;
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return StringUtils.Utf8ZToString(Bytes);
|
return StringUtils.Utf8ZToString(Bytes);
|
||||||
|
|
|
@ -6,9 +6,11 @@ using LibHac.Common;
|
||||||
namespace LibHac.Bcat
|
namespace LibHac.Bcat
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{ToString()}")]
|
[DebuggerDisplay("{ToString()}")]
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 32)]
|
[StructLayout(LayoutKind.Sequential, Size = MaxSize)]
|
||||||
public struct FileName
|
public struct FileName
|
||||||
{
|
{
|
||||||
|
private const int MaxSize = 0x20;
|
||||||
|
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy2;
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy2;
|
||||||
|
@ -22,6 +24,29 @@ namespace LibHac.Bcat
|
||||||
|
|
||||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||||
|
|
||||||
|
public bool IsValid()
|
||||||
|
{
|
||||||
|
Span<byte> name = Bytes;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < name.Length; i++)
|
||||||
|
{
|
||||||
|
if (name[i] == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!StringUtils.IsDigit(name[i]) && !StringUtils.IsAlpha(name[i]) && name[i] != '_' && name[i] != '.')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0 || i == MaxSize)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (name[i] != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return name[i - 1] != '.';
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return StringUtils.Utf8ZToString(Bytes);
|
return StringUtils.Utf8ZToString(Bytes);
|
||||||
|
|
|
@ -19,18 +19,26 @@ namespace LibHac.Bcat
|
||||||
public static Result.Base InvalidArgument => new Result.Base(ModuleBcat, 1);
|
public static Result.Base InvalidArgument => new Result.Base(ModuleBcat, 1);
|
||||||
/// <summary>Error code: 2122-0002; Inner value: 0x47a</summary>
|
/// <summary>Error code: 2122-0002; Inner value: 0x47a</summary>
|
||||||
public static Result.Base NotFound => new Result.Base(ModuleBcat, 2);
|
public static Result.Base NotFound => new Result.Base(ModuleBcat, 2);
|
||||||
|
/// <summary>Error code: 2122-0003; Inner value: 0x67a</summary>
|
||||||
|
public static Result.Base TargetLocked => new Result.Base(ModuleBcat, 3);
|
||||||
|
/// <summary>Error code: 2122-0006; Inner value: 0xc7a</summary>
|
||||||
|
public static Result.Base AlreadyOpen => new Result.Base(ModuleBcat, 6);
|
||||||
/// <summary>Error code: 2122-0007; Inner value: 0xe7a</summary>
|
/// <summary>Error code: 2122-0007; Inner value: 0xe7a</summary>
|
||||||
public static Result.Base NotOpen => new Result.Base(ModuleBcat, 7);
|
public static Result.Base NotOpen => new Result.Base(ModuleBcat, 7);
|
||||||
/// <summary>Error code: 2122-0009; Inner value: 0x127a</summary>
|
/// <summary>Error code: 2122-0009; Inner value: 0x127a</summary>
|
||||||
public static Result.Base ServiceOpenLimitReached => new Result.Base(ModuleBcat, 9);
|
public static Result.Base ServiceOpenLimitReached => new Result.Base(ModuleBcat, 9);
|
||||||
/// <summary>Error code: 2122-0010; Inner value: 0x147a</summary>
|
/// <summary>Error code: 2122-0010; Inner value: 0x147a</summary>
|
||||||
public static Result.Base SaveDataNotFount => new Result.Base(ModuleBcat, 10);
|
public static Result.Base SaveDataNotFound => new Result.Base(ModuleBcat, 10);
|
||||||
/// <summary>Error code: 2122-0031; Inner value: 0x3e7a</summary>
|
/// <summary>Error code: 2122-0031; Inner value: 0x3e7a</summary>
|
||||||
public static Result.Base NetworkServiceAccountNotAvailable => new Result.Base(ModuleBcat, 31);
|
public static Result.Base NetworkServiceAccountNotAvailable => new Result.Base(ModuleBcat, 31);
|
||||||
|
/// <summary>Error code: 2122-0080; Inner value: 0xa07a</summary>
|
||||||
|
public static Result.Base PassphrasePathNotFound => new Result.Base(ModuleBcat, 80);
|
||||||
/// <summary>Error code: 2122-0090; Inner value: 0xb47a</summary>
|
/// <summary>Error code: 2122-0090; Inner value: 0xb47a</summary>
|
||||||
public static Result.Base PermissionDenied => new Result.Base(ModuleBcat, 90);
|
public static Result.Base PermissionDenied => new Result.Base(ModuleBcat, 90);
|
||||||
|
/// <summary>Error code: 2122-0091; Inner value: 0xb67a</summary>
|
||||||
|
public static Result.Base AllocationFailed => new Result.Base(ModuleBcat, 91);
|
||||||
/// <summary>Error code: 2122-0204; Inner value: 0x1987a</summary>
|
/// <summary>Error code: 2122-0204; Inner value: 0x1987a</summary>
|
||||||
public static Result.Base InvalidStorageMetaVersion => new Result.Base(ModuleBcat, 204);
|
public static Result.Base InvalidDeliveryCacheStorageFile => new Result.Base(ModuleBcat, 204);
|
||||||
/// <summary>Error code: 2122-0205; Inner value: 0x19a7a</summary>
|
/// <summary>Error code: 2122-0205; Inner value: 0x19a7a</summary>
|
||||||
public static Result.Base StorageOpenLimitReached => new Result.Base(ModuleBcat, 205);
|
public static Result.Base StorageOpenLimitReached => new Result.Base(ModuleBcat, 205);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue