using System; using System.Collections.Generic; using static System.Buffers.Binary.BinaryPrimitives; namespace NHSE.Core; /// /// Represents two files -- and and their decrypted data. /// public abstract class EncryptedFilePair { private readonly byte[] RawData; private readonly byte[] RawHeader; private readonly ISaveFileProvider Provider; protected Memory Raw => RawData; public Span Data => RawData; public Span Header => RawHeader; public readonly FileHeaderInfo Info; public readonly string NameData; public readonly string NameHeader; /// /// Checks if the file pair exists in the specified provider. /// public static bool Exists(ISaveFileProvider provider, string name) { var nameData = $"{name}.dat"; var nameHeader = $"{name}Header.dat"; return provider.FileExists(nameHeader) && provider.FileExists(nameData); } protected EncryptedFilePair(ISaveFileProvider provider, string name) { Provider = provider; NameData = $"{name}.dat"; NameHeader = $"{name}Header.dat"; var hd = provider.ReadFile(NameHeader); var md = provider.ReadFile(NameData); Encryption.Decrypt(hd, md); RawHeader = hd; RawData = md; Info = Header[..FileHeaderInfo.SIZE].ToArray().ToClass(); } public void Save(uint seed) { var encrypt = Encryption.Encrypt(Data, seed, Header); Provider.WriteFile(NameData, encrypt.Data.Span); Provider.WriteFile(NameHeader, encrypt.Header.Span); } /// /// Updates all hashes of . /// public void Hash() { var ver = Info.GetKnownRevisionIndex(); var hash = RevisionChecker.HashInfo[ver]; var details = hash.GetFile(NameData); ArgumentNullException.ThrowIfNull(details, nameof(NameData)); foreach (var h in details.HashRegions) WriteUInt32LittleEndian(Data[h.HashOffset..], Murmur3.Hash(Data[h.HashedRange])); } public IEnumerable InvalidHashes() { var ver = Info.GetKnownRevisionIndex(); var hash = RevisionChecker.HashInfo[ver]; var details = hash.GetFile(NameData); ArgumentNullException.ThrowIfNull(details, nameof(NameData)); foreach (var h in details.HashRegions) { var current = Murmur3.Hash(Data[h.HashedRange]); var saved = ReadUInt32LittleEndian(Data[h.HashOffset..]); if (current != saved) yield return h; } } protected string GetString(int offset, int maxLength) => StringUtil.GetString(Data, offset, maxLength); protected static byte[] GetBytes(string value, int maxLength) => StringUtil.GetBytes(value, maxLength); }