95 lines
2.8 KiB
C#
95 lines
2.8 KiB
C#
using System;
|
|
using System.Text;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Concurrent;
|
|
using Konscious.Security.Cryptography;
|
|
|
|
namespace RobloxFiles.DataTypes
|
|
{
|
|
// SharedString is a datatype that takes a sequence of bytes and stores it in a
|
|
// lookup table that is shared by the entire file. It originally used MD5 for the
|
|
// hashing, but Roblox now uses Blake2B to avoid the obvious problems with using MD5.
|
|
|
|
// In practice the value of a SharedString does not have to match the hash of the
|
|
// data it represents, it just needs to be distinct and MUST be 16 bytes long.
|
|
// The XML format still uses 'md5' as its attribute key to the lookup table.
|
|
|
|
public class SharedString
|
|
{
|
|
private static readonly ConcurrentDictionary<string, byte[]> Lookup = new ConcurrentDictionary<string, byte[]>();
|
|
public string Key { get; internal set; }
|
|
public string ComputedKey { get; internal set; }
|
|
|
|
public byte[] SharedValue => Find(ComputedKey ?? Key);
|
|
public override string ToString() => $"Key: {ComputedKey ?? Key}";
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return Key.GetHashCode();
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (!(obj is SharedString other))
|
|
return false;
|
|
|
|
return Key.Equals(other.Key);
|
|
}
|
|
|
|
internal SharedString(string key)
|
|
{
|
|
Key = key;
|
|
}
|
|
|
|
internal static void Register(string key, byte[] buffer)
|
|
{
|
|
if (Lookup.ContainsKey(key))
|
|
return;
|
|
|
|
Lookup.TryAdd(key, buffer);
|
|
}
|
|
|
|
private SharedString(byte[] buffer)
|
|
{
|
|
using (var blake2B = new HMACBlake2B(16 * 8))
|
|
{
|
|
byte[] hash = blake2B.ComputeHash(buffer);
|
|
ComputedKey = Convert.ToBase64String(hash);
|
|
Key = ComputedKey;
|
|
}
|
|
|
|
if (Lookup.ContainsKey(ComputedKey))
|
|
return;
|
|
|
|
Register(ComputedKey, buffer);
|
|
}
|
|
|
|
public static byte[] Find(string key)
|
|
{
|
|
byte[] result = null;
|
|
|
|
if (Lookup.ContainsKey(key))
|
|
result = Lookup[key];
|
|
|
|
return result;
|
|
}
|
|
|
|
public static SharedString FromBuffer(byte[] buffer)
|
|
{
|
|
return new SharedString(buffer ?? Array.Empty<byte>());
|
|
}
|
|
|
|
public static SharedString FromString(string value)
|
|
{
|
|
byte[] buffer = Encoding.UTF8.GetBytes(value);
|
|
return new SharedString(buffer);
|
|
}
|
|
|
|
public static SharedString FromBase64(string base64)
|
|
{
|
|
byte[] buffer = Convert.FromBase64String(base64);
|
|
return new SharedString(buffer);
|
|
}
|
|
}
|
|
}
|