mirror of
https://github.com/kwsch/NHSE.git
synced 2026-03-22 09:44:49 -05:00
Updates the Field Item Editor to render layers based on the entire map, and the per-patch positioning of each layer. Import/export will gracefully handle upgrade/downgrade, and viewport import/export will gracefully update tiles rather than a per-acre basis. Performance has also been slightly improved; no allocation is done anymore when updating the image.
90 lines
2.9 KiB
C#
90 lines
2.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using static System.Buffers.Binary.BinaryPrimitives;
|
|
|
|
namespace NHSE.Core;
|
|
|
|
/// <summary>
|
|
/// Represents two files -- <see cref="NameData"/> and <see cref="NameHeader"/> and their decrypted data.
|
|
/// </summary>
|
|
public abstract class EncryptedFilePair
|
|
{
|
|
private readonly byte[] RawData;
|
|
private readonly byte[] RawHeader;
|
|
private readonly ISaveFileProvider Provider;
|
|
|
|
protected Memory<byte> Raw => RawData;
|
|
public Span<byte> Data => RawData;
|
|
public Span<byte> Header => RawHeader;
|
|
|
|
public readonly FileHeaderInfo Info;
|
|
|
|
public readonly string NameData;
|
|
public readonly string NameHeader;
|
|
|
|
/// <summary>
|
|
/// Checks if the file pair exists in the specified provider.
|
|
/// </summary>
|
|
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<FileHeaderInfo>();
|
|
}
|
|
|
|
public void Save(uint seed)
|
|
{
|
|
var encrypt = Encryption.Encrypt(Data, seed, Header);
|
|
Provider.WriteFile(NameData, encrypt.Data.Span);
|
|
Provider.WriteFile(NameHeader, encrypt.Header.Span);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Updates all hashes of <see cref="Data"/>.
|
|
/// </summary>
|
|
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<FileHashRegion> 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);
|
|
} |