private Result ParseStage1() { // Erista package1ldr is stored unencrypted, so we can always directly read the size // field at the end of package1ldr. // Mariko package1ldr is stored encrypted. If we're able to decrypt it, // directly read the size field at the end of package1ldr. IsModern = !IsLegacyImpl(); int stage1Size = IsModern ? ModernStage1Size : LegacyStage1Size; if (IsMariko && !IsDecrypted) { // If we're not able to decrypt the Mariko package1ldr, calculate the size by subtracting // the known package1ldr size from the total size in the OEM header. Pk11Size = MarikoOemHeader.Size - stage1Size; return(Result.Success); } // Read the package1ldr footer int footerOffset = stage1Size - Unsafe.SizeOf <Package1Stage1Footer>(); Result rc = BodyStorage.Read(footerOffset, SpanHelpers.AsByteSpan(ref _stage1Footer)); if (rc.IsFailure()) { return(rc); } // Get the PK11 size from the field in the unencrypted stage 1 footer Pk11Size = _stage1Footer.Pk11Size; return(Result.Success); }
private bool TryFindEristaKeyRevision() { // Package1 has no indication of which key it's encrypted with, // so we must test each known key to find one that works var decHeader = new Package1Pk11Header(); int start = IsModern ? 6 : 0; int end = IsModern ? 0x20 : 6; Decryptor decryptor = IsModern ? Crypto.Aes.DecryptCbc128 : (Decryptor)Crypto.Aes.DecryptCtr128; for (int i = start; i < end; i++) { decryptor(SpanHelpers.AsByteSpan(ref _pk11Header), SpanHelpers.AsByteSpan(ref decHeader), KeySet.Package1Keys[i], _stage1Footer.Iv); if (decHeader.Magic == Package1Pk11Header.ExpectedMagic) { KeyRevision = (byte)i; _pk11Header = decHeader; return(true); } } return(false); }
private Result CreateExtraDataForTest(IFileSystem fileSystem, U8Span path, int saveDataSize) { fileSystem.DeleteFile(path).IgnoreResult(); Result rc = fileSystem.CreateFile(path, Unsafe.SizeOf <SaveDataExtraData>()); if (rc.IsFailure()) { return(rc); } var extraData = new SaveDataExtraData(); extraData.DataSize = saveDataSize; rc = fileSystem.OpenFile(out IFile file, path, OpenMode.ReadWrite); if (rc.IsFailure()) { return(rc); } using (file) { rc = file.Write(0, SpanHelpers.AsByteSpan(ref extraData), WriteOption.Flush); if (rc.IsFailure()) { return(rc); } } return(Result.Success); }
public Result ReadEntryCount(out int count) { UnsafeHelpers.SkipParamInit(out count); // This should only be called at the start of reading stream. Assert.SdkRequiresEqual(_offset, 0); // Read and validate header. var header = new KeyValueArchiveHeader(); Result rc = Read(SpanHelpers.AsByteSpan(ref header)); if (rc.IsFailure()) { return(rc); } if (!header.IsValid()) { return(ResultKvdb.InvalidKeyValue.Log()); } count = header.EntryCount; return(Result.Success); }
public Result ReadDatabaseFromBuffer(ReadOnlySpan <byte> data) { KvDict.Clear(); var reader = new ImkvdbReader(data); Result rc = reader.ReadHeader(out int entryCount); if (rc.IsFailure()) { return(rc); } for (int i = 0; i < entryCount; i++) { rc = reader.ReadEntry(out ReadOnlySpan <byte> keyBytes, out ReadOnlySpan <byte> valueBytes); if (rc.IsFailure()) { return(rc); } Debug.Assert(keyBytes.Length == Unsafe.SizeOf <TKey>()); var key = new TKey(); keyBytes.CopyTo(SpanHelpers.AsByteSpan(ref key)); byte[] value = valueBytes.ToArray(); KvDict.Add(key, value); } return(Result.Success); }
public Result Initialize(IStorage kipData) { if (kipData is null) { return(ResultLibHac.NullArgument.Log()); } // Verify there's enough data to read the header Result rc = kipData.GetSize(out long kipSize); if (rc.IsFailure()) { return(rc); } if (kipSize < Unsafe.SizeOf <KipHeader>()) { return(ResultLibHac.InvalidKipFileSize.Log()); } rc = kipData.Read(0, SpanHelpers.AsByteSpan(ref _header)); if (rc.IsFailure()) { return(rc); } if (!_header.IsValid) { return(ResultLibHac.InvalidKipMagic.Log()); } KipStorage = kipData; return(Result.Success); }
public static Result ConvertToFspPath(out FspPath fspPath, ReadOnlySpan <byte> path) { UnsafeHelpers.SkipParamInit(out fspPath); int length = StringUtils.Copy(SpanHelpers.AsByteSpan(ref fspPath), path, PathTool.EntryNameLengthMax + 1); if (length >= PathTool.EntryNameLengthMax + 1) { return(ResultFs.TooLongPath.Log()); } Result rc = PathFormatter.SkipMountName(out ReadOnlySpan <byte> pathWithoutMountName, out _, new U8Span(path)); if (rc.IsFailure()) { return(rc); } if (!WindowsPath12.IsWindowsPath(pathWithoutMountName, true)) { Replace(SpanHelpers.AsByteSpan(ref fspPath).Slice(0, 0x300), AltDirectorySeparator, DirectorySeparator); } else if (fspPath.Str[0] == DirectorySeparator && fspPath.Str[1] == DirectorySeparator) { SpanHelpers.AsByteSpan(ref fspPath)[0] = AltDirectorySeparator; SpanHelpers.AsByteSpan(ref fspPath)[1] = AltDirectorySeparator; } return(Result.Success); }
public void Initialize_InitialExtraDataIsEmpty() { (IFileSystem _, DirectorySaveDataFileSystem saveFs) = CreateFileSystemInternal(); Assert.Success(saveFs.ReadExtraData(out SaveDataExtraData extraData)); Assert.True(SpanHelpers.AsByteSpan(ref extraData).IsZeros()); }
public Result SetCommittedProvisionally() { IFile contextFile = null; try { Result rc = _fileSystem.OpenFile(out contextFile, ContextFileName, OpenMode.ReadWrite); if (rc.IsFailure()) { return(rc); } _context.State = CommitState.ProvisionallyCommitted; rc = contextFile.Write(0, SpanHelpers.AsByteSpan(ref _context), WriteOption.None); if (rc.IsFailure()) { return(rc); } rc = contextFile.Flush(); if (rc.IsFailure()) { return(rc); } } finally { contextFile?.Dispose(); } return(_fileSystem.Commit()); }
public Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size) { UnsafeHelpers.SkipParamInit(out rangeInfo); rangeInfo.Clear(); if (operationId == (int)OperationId.InvalidateCache) { Result rc = BaseFile.OperateRange(Span <byte> .Empty, OperationId.InvalidateCache, offset, size, ReadOnlySpan <byte> .Empty); if (rc.IsFailure()) { return(rc); } } else if (operationId == (int)OperationId.QueryRange) { Unsafe.SkipInit(out QueryRangeInfo info); Result rc = BaseFile.OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.QueryRange, offset, size, ReadOnlySpan <byte> .Empty); if (rc.IsFailure()) { return(rc); } rangeInfo.Merge(in info); } return(Result.Success); }
public Result GetKeyValueSize(out int keySize, out int valueSize) { UnsafeHelpers.SkipParamInit(out keySize, out valueSize); // This should only be called after ReadEntryCount. Assert.SdkNotEqual(_offset, 0); // Peek the next entry header. Unsafe.SkipInit(out KeyValueArchiveEntryHeader header); Result rc = Peek(SpanHelpers.AsByteSpan(ref header)); if (rc.IsFailure()) { return(rc); } if (!header.IsValid()) { return(ResultKvdb.InvalidKeyValue.Log()); } keySize = header.KeySize; valueSize = header.ValueSize; return(Result.Success); }
public Result Get(out SaveDataIndexerValue value, ref SaveDataAttribute key) { value = default; lock (Locker) { Result rc = Initialize(); if (rc.IsFailure()) { return(rc); } rc = EnsureKvDatabaseLoaded(false); if (rc.IsFailure()) { return(rc); } rc = KvDatabase.Get(ref key, SpanHelpers.AsByteSpan(ref value)); if (rc.IsFailure()) { return(ResultFs.TargetNotFound.LogConverted(rc)); } return(Result.Success); } }
public Result Initialize(IFile kipFile) { if (kipFile is null) { return(ResultLibHac.NullArgument.Log()); } Result rc = kipFile.Read(out long bytesRead, 0, SpanHelpers.AsByteSpan(ref _header), ReadOption.None); if (rc.IsFailure()) { return(rc); } if (bytesRead != Unsafe.SizeOf <KipHeader>()) { return(ResultLibHac.InvalidKipFileSize.Log()); } if (!_header.IsValid) { return(ResultLibHac.InvalidKipMagic.Log()); } KipFile = kipFile; return(Result.Success); }
private bool IsUpdateApplied(string titleId, out string version) { string updatePath = "(unknown)"; try { (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateData(_virtualFileSystem, titleId, 0, out updatePath); if (patchNca != null && controlNca != null) { ApplicationControlProperty controlData = new ApplicationControlProperty(); controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(out IFile nacpFile, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); nacpFile.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); version = controlData.DisplayVersion.ToString(); return(true); } } catch (InvalidDataException) { Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {updatePath}"); } catch (MissingKeyException exception) { Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}. Errored File: {updatePath}"); } version = ""; return(false); }
public IStorage OpenIni1() { if (Header.SectionSizes[1] == 0) { IStorage kernelStorage = OpenKernel(); var reader = new BinaryReader(kernelStorage.AsStream()); for (int i = 0; i < (Header.SectionSizes[0] / sizeof(uint)) - 1; i++) { if (reader.ReadUInt32() == 0xD51C403E) // end indicator for KernelMap { break; } } reader.BaseStream.Seek(-Unsafe.SizeOf <KernelMap>(), SeekOrigin.Current); KernelMap map = new KernelMap(); reader.Read(SpanHelpers.AsByteSpan(ref map)); return(new CachedStorage(kernelStorage.Slice(map.Ini1StartOffset), 0x4000, 4, true)); } else { return(new CachedStorage(OpenSection(1), 0x4000, 4, true)); } }
// nn::friends::GetPlayHistoryRegistrationKey(b8 unknown, nn::account::Uid) -> buffer<nn::friends::PlayHistoryRegistrationKey, 0x1a> public ResultCode GetPlayHistoryRegistrationKey(ServiceCtx context) { bool unknownBool = context.RequestData.ReadBoolean(); UserId userId = context.RequestData.ReadStruct <UserId>(); context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x40UL); ulong bufferPosition = context.Request.RecvListBuff[0].Position; if (userId.IsNull) { return(ResultCode.InvalidArgument); } // NOTE: Calls nn::friends::detail::service::core::PlayHistoryManager::GetInstance and stores the instance. byte[] randomBytes = new byte[8]; Random random = new Random(); random.NextBytes(randomBytes); // NOTE: Calls nn::friends::detail::service::core::UuidManager::GetInstance and stores the instance. // Then call nn::friends::detail::service::core::AccountStorageManager::GetInstance and store the instance. // Then it checks if an Uuid is already stored for the UserId, if not it generates a random Uuid. // And store it in the savedata 8000000000000080 in the friends:/uid.bin file. Array16 <byte> randomGuid = new Array16 <byte>(); Guid.NewGuid().ToByteArray().AsSpan().CopyTo(randomGuid.ToSpan()); PlayHistoryRegistrationKey playHistoryRegistrationKey = new PlayHistoryRegistrationKey { Type = 0x101, KeyIndex = (byte)(randomBytes[0] & 7), UserIdBool = 0, // TODO: Find it. UnknownBool = (byte)(unknownBool ? 1 : 0), // TODO: Find it. Reserved = new Array11 <byte>(), Uuid = randomGuid }; ReadOnlySpan <byte> playHistoryRegistrationKeyBuffer = SpanHelpers.AsByteSpan(ref playHistoryRegistrationKey); /* * * NOTE: The service uses the KeyIndex to get a random key from a keys buffer (since the key index is stored in the returned buffer). * We currently don't support play history and online services so we can use a blank key for now. * Code for reference: * * byte[] hmacKey = new byte[0x20]; * * HMACSHA256 hmacSha256 = new HMACSHA256(hmacKey); * byte[] hmacHash = hmacSha256.ComputeHash(playHistoryRegistrationKeyBuffer); * */ context.Memory.Write(bufferPosition, playHistoryRegistrationKeyBuffer); context.Memory.Write(bufferPosition + 0x20, new byte[0x20]); // HmacHash return(ResultCode.Success); }
public Result Initialize(SubStorage tableStorage) { // Read and verify the bucket tree header. // note: skip init var header = new BucketTree.Header(); Result rc = tableStorage.Read(0, SpanHelpers.AsByteSpan(ref header)); if (rc.IsFailure()) { return(rc); } rc = header.Verify(); if (rc.IsFailure()) { return(rc); } // Determine extents. long nodeStorageSize = QueryNodeStorageSize(header.EntryCount); long entryStorageSize = QueryEntryStorageSize(header.EntryCount); long nodeStorageOffset = QueryHeaderStorageSize(); long entryStorageOffset = nodeStorageOffset + nodeStorageSize; // Initialize. var nodeStorage = new SubStorage(tableStorage, nodeStorageOffset, nodeStorageSize); var entryStorage = new SubStorage(tableStorage, entryStorageOffset, entryStorageSize); return(Initialize(nodeStorage, entryStorage, header.EntryCount)); }
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); } } }
public static unsafe Result CopyDirectoryRecursively(IFileSystem destFileSystem, IFileSystem sourceFileSystem, U8Span destPath, U8Span sourcePath, Span <byte> workBuffer) { var destPathBuf = new FsPath(); int originalSize = StringUtils.Copy(destPathBuf.Str, destPath); Abort.DoAbortUnless(originalSize < Unsafe.SizeOf <FsPath>()); // Pin and recreate the span because C# can't use byref-like types in a closure int workBufferSize = workBuffer.Length; fixed(byte *pWorkBuffer = workBuffer) { // Copy the pointer to workaround CS1764. // IterateDirectoryRecursively won't store the delegate anywhere, so it should be safe byte *pWorkBuffer2 = pWorkBuffer; Result OnEnterDir(U8Span path, ref DirectoryEntry entry) { // Update path, create new dir. StringUtils.Concat(SpanHelpers.AsByteSpan(ref destPathBuf), entry.Name); StringUtils.Concat(SpanHelpers.AsByteSpan(ref destPathBuf), DirectorySeparator); return(destFileSystem.CreateDirectory(destPathBuf)); } Result OnExitDir(U8Span path, ref DirectoryEntry entry) { // Check we have a parent directory. int len = StringUtils.GetLength(SpanHelpers.AsByteSpan(ref destPathBuf)); if (len < 2) { return(ResultFs.InvalidPathFormat.Log()); } // Find previous separator, add null terminator int cur = len - 2; while (!PathTool.IsSeparator(SpanHelpers.AsByteSpan(ref destPathBuf)[cur]) && cur > 0) { cur--; } SpanHelpers.AsByteSpan(ref destPathBuf)[cur + 1] = StringTraits.NullTerminator; return(Result.Success); } Result OnFile(U8Span path, ref DirectoryEntry entry) { var buffer = new Span <byte>(pWorkBuffer2, workBufferSize); return(CopyFile(destFileSystem, sourceFileSystem, destPathBuf, path, ref entry, buffer)); } return(IterateDirectoryRecursively(sourceFileSystem, sourcePath, OnEnterDir, OnExitDir, OnFile)); } }
private ushort CalculateDeviceCrc() { UInt128 deviceId = Helper.GetDeviceId(); ushort deviceIdCrc16 = Helper.CalculateCrc16(SpanHelpers.AsByteSpan(ref deviceId), 0, false); return(Helper.CalculateCrc16(AsSpan(), deviceIdCrc16, true)); }
public bool IsValidDeviceCrc() { UInt128 deviceId = Helper.GetDeviceId(); ushort deviceIdCrc16 = Helper.CalculateCrc16(SpanHelpers.AsByteSpan(ref deviceId), 0, false); return(Helper.CalculateCrc16(AsSpan(), deviceIdCrc16, false) == 0); }
private ushort CalculateDeviceCrc() { UInt128 deviceId = Helper.GetDeviceId(); ushort deviceIdCrc16 = Helper.CalculateCrc16BE(SpanHelpers.AsByteSpan(ref deviceId)); return(Helper.CalculateCrc16BE(AsSpanWithoutDeviceCrc(), deviceIdCrc16)); }
/// <summary> /// Initializes the bucket tree builder. /// </summary> /// <param name="headerStorage">The <see cref="SubStorage"/> the tree's header will be written to.Must be at least the size in bytes returned by <see cref="QueryHeaderStorageSize"/>.</param> /// <param name="nodeStorage">The <see cref="SubStorage"/> the tree's nodes will be written to. Must be at least the size in bytes returned by <see cref="QueryNodeStorageSize"/>.</param> /// <param name="entryStorage">The <see cref="SubStorage"/> the tree's entries will be written to. Must be at least the size in bytes returned by <see cref="QueryEntryStorageSize"/>.</param> /// <param name="nodeSize">The size of each node in the bucket tree. Must be a power of 2.</param> /// <param name="entrySize">The size of each entry that will be stored in the bucket tree.</param> /// <param name="entryCount">The exact number of entries that will be added to the bucket tree.</param> /// <returns>The <see cref="Result"/> of the operation.</returns> public Result Initialize(SubStorage headerStorage, SubStorage nodeStorage, SubStorage entryStorage, int nodeSize, int entrySize, int entryCount) { Assert.True(entrySize >= sizeof(long)); Assert.True(nodeSize >= entrySize + Unsafe.SizeOf <NodeHeader>()); Assert.True(NodeSizeMin <= nodeSize && nodeSize <= NodeSizeMax); Assert.True(BitUtil.IsPowerOfTwo(nodeSize)); if (headerStorage is null || nodeStorage is null || entryStorage is null) { return(ResultFs.NullptrArgument.Log()); } // Set the builder parameters NodeSize = nodeSize; EntrySize = entrySize; EntryCount = entryCount; EntriesPerEntrySet = GetEntryCount(nodeSize, entrySize); OffsetsPerNode = GetOffsetCount(nodeSize); CurrentL2OffsetIndex = GetNodeL2Count(nodeSize, entrySize, entryCount); // Create and write the header var header = new Header(); header.Format(entryCount); Result rc = headerStorage.Write(0, SpanHelpers.AsByteSpan(ref header)); if (rc.IsFailure()) { return(rc); } // Allocate buffers for the L1 node and entry sets _l1Node.Allocate(nodeSize); _entrySet.Allocate(nodeSize); int entrySetCount = GetEntrySetCount(nodeSize, entrySize, entryCount); // Allocate an L2 node buffer if there are more entry sets than will fit in the L1 node if (OffsetsPerNode < entrySetCount) { _l2Node.Allocate(nodeSize); } _l1Node.FillZero(); _l2Node.FillZero(); _entrySet.FillZero(); NodeStorage = nodeStorage; EntryStorage = entryStorage; // Set the initial position CurrentEntryIndex = 0; CurrentOffset = -1; return(Result.Success); }
private void DecryptHeader(ReadOnlySpan <byte> key, ref Package2Meta source, ref Package2Meta dest) { Buffer16 iv = source.HeaderIv; Aes.DecryptCtr128(SpanHelpers.AsByteSpan(ref source), SpanHelpers.AsByteSpan(ref dest), key, iv); // Copy the IV to the output because the IV field will be garbage after "decrypting" it Unsafe.As <Package2Meta, Buffer16>(ref dest) = iv; }
public void WriteHeader(int entryCount) { // This should only be called at start of write. Assert.SdkEqual(_offset, 0); var header = new KeyValueArchiveHeader(entryCount); Write(SpanHelpers.AsByteSpan(ref header)); }
private void AddUpdate(string path, bool showErrorDialog = true) { if (File.Exists(path)) { using (FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read)) { PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); try { (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, nsp, _titleId, 0); if (controlNca != null && patchNca != null) { ApplicationControlProperty controlData = new ApplicationControlProperty(); controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(out IFile nacpFile, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); nacpFile.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); RadioButton radioButton = new RadioButton($"Version {controlData.DisplayVersion.ToString()} - {path}"); radioButton.JoinGroup(_noUpdateRadioButton); _availableUpdatesBox.Add(radioButton); _radioButtonToPathDictionary.Add(radioButton, path); radioButton.Show(); radioButton.Active = true; } else { GtkDialog.CreateErrorDialog("The specified file does not contain an update for the selected title!"); } } catch (InvalidDataException exception) { Logger.Error?.Print(LogClass.Application, $"{exception.Message}. Errored File: {path}"); if (showErrorDialog) { GtkDialog.CreateInfoDialog("Ryujinx - Error", "Add Update Failed!", "The NCA header content type check has failed. This is usually because the header key is incorrect or missing."); } } catch (MissingKeyException exception) { Logger.Error?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}. Errored File: {path}"); if (showErrorDialog) { GtkDialog.CreateInfoDialog("Ryujinx - Error", "Add Update Failed!", $"Your key set is missing a key with the name: {exception.Name}"); } } } } }
public void WriteEntry(ReadOnlySpan <byte> key, ReadOnlySpan <byte> value) { // This should only be called after writing header. Assert.SdkNotEqual(_offset, 0); var header = new KeyValueArchiveEntryHeader(key.Length, value.Length); Write(SpanHelpers.AsByteSpan(ref header)); Write(key); Write(value); }
public static ServiceName Encode(ReadOnlySpan <char> name) { var outName = new ServiceName(); int length = Math.Min(MaxLength, name.Length); for (int i = 0; i < length; i++) { SpanHelpers.AsByteSpan(ref outName)[i] = (byte)name[i]; } return(outName); }
public Result Add(out ulong saveDataId, ref SaveDataAttribute key) { saveDataId = default; lock (Locker) { Result rc = Initialize(); if (rc.IsFailure()) { return(rc); } rc = EnsureKvDatabaseLoaded(false); if (rc.IsFailure()) { return(rc); } SaveDataIndexerValue value = default; rc = KvDatabase.Get(ref key, SpanHelpers.AsByteSpan(ref value)); if (rc.IsSuccess()) { return(ResultFs.SaveDataPathAlreadyExists.Log()); } LastPublishedId++; ulong newSaveDataId = LastPublishedId; value = new SaveDataIndexerValue { SaveDataId = newSaveDataId }; rc = KvDatabase.Set(ref key, SpanHelpers.AsByteSpan(ref value)); if (rc.IsFailure()) { LastPublishedId--; return(rc); } rc = AdjustOpenedInfoReaders(ref key); if (rc.IsFailure()) { return(rc); } saveDataId = newSaveDataId; return(Result.Success); } }
/// <summary> /// Verifies the signature of the package. /// </summary> /// <returns>The <see cref="Result"/> of the operation. /// <see cref="Result.Success"/> if the signature is valid.</returns> public Result VerifySignature() { Unsafe.SkipInit(out Package2Meta meta); Span <byte> metaBytes = SpanHelpers.AsByteSpan(ref meta); Result rc = _storage.Read(Package2Header.SignatureSize, metaBytes); if (rc.IsFailure()) { return(rc); } return(_header.VerifySignature(_keySet.Package2SigningKeyParams.Modulus, metaBytes)); }