Client LuaCsForBarotrauma
Md5Hash.cs
1 #nullable enable
2 
3 using Barotrauma.IO;
4 using System;
5 using System.Collections.Generic;
6 using System.Security.Cryptography;
7 using System.Text;
8 using System.Text.RegularExpressions;
9 
10 namespace Barotrauma
11 {
12  public class Md5Hash
13  {
14  public static readonly Md5Hash Blank = new Md5Hash(new string('0', 32));
15 
16  private static string RemoveWhitespace(string s)
17  {
18  StringBuilder sb = new StringBuilder(s.Length / 2); // Reserve half the size of the original string because
19  // that's probably close enough to the size of the result
20  for (int i = 0; i < s.Length; i++)
21  {
22  if (char.IsWhiteSpace(s[i])) { continue; }
23  sb.Append(s[i]);
24  }
25  return sb.ToString();
26  }
27 
28  //thanks to Jlobblet for this regex
29  private static readonly Regex stringHashRegex = new Regex(@"^[0-9a-fA-F]{7,32}$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
30 
31  public readonly byte[] ByteRepresentation;
32  public readonly string StringRepresentation;
33  public readonly string ShortRepresentation;
34 
35  private static void CalculateHash(byte[] bytes, out string stringRepresentation, out byte[] byteRepresentation)
36  {
37  using (MD5 md5 = MD5.Create())
38  {
39  byte[] byteHash = md5.ComputeHash(bytes);
40 
41  byteRepresentation = byteHash;
42  stringRepresentation = ByteRepresentationToStringRepresentation(byteHash);
43  }
44  }
45 
46  private static string ByteRepresentationToStringRepresentation(byte[] byteHash)
47  => ToolBoxCore.ByteArrayToHexString(byteHash);
48 
49  private static byte[] StringRepresentationToByteRepresentation(string strHash)
50  => ToolBoxCore.HexStringToByteArray(strHash);
51 
52  public static string GetShortHash(string fullHash)
53  {
54  return fullHash.Length < 7 ? fullHash : fullHash.Substring(0, 7);
55  }
56 
57  private Md5Hash(string md5Hash)
58  {
59  StringRepresentation = md5Hash;
60  ByteRepresentation = StringRepresentationToByteRepresentation(StringRepresentation);
61 
63  }
64 
65  private Md5Hash(byte[] bytes, bool calculate)
66  {
67  if (calculate)
68  {
69  CalculateHash(bytes, out StringRepresentation, out ByteRepresentation);
70  }
71  else
72  {
73  StringRepresentation = ByteRepresentationToStringRepresentation(bytes);
74  ByteRepresentation = bytes;
75  }
76 
78  }
79 
80  public static Md5Hash StringAsHash(string hash)
81  {
82  if (!stringHashRegex.IsMatch(hash)) { throw new ArgumentException($"{hash} is not a valid hash"); }
83  return new Md5Hash(hash);
84  }
85 
86  public static Md5Hash MergeHashes(IEnumerable<Md5Hash> hashes)
87  {
88  using IncrementalHash incrementalHash = IncrementalHash.CreateHash(HashAlgorithmName.MD5);
89  foreach (var hash in hashes)
90  {
91  incrementalHash.AppendData(hash.ByteRepresentation);
92  }
93  return BytesAsHash(incrementalHash.GetHashAndReset());
94  }
95 
96  public static Md5Hash CalculateForBytes(byte[] bytes)
97  {
98  return new Md5Hash(bytes, calculate: true);
99  }
100 
101  public static Md5Hash BytesAsHash(byte[] bytes)
102  {
103  return new Md5Hash(bytes, calculate: false);
104  }
105 
106  [Flags]
107  public enum StringHashOptions
108  {
109  BytePerfect = 0,
110  IgnoreCase = 0x1,
111  IgnoreWhitespace = 0x2
112  }
113 
114  public static Md5Hash CalculateForFile(string path, StringHashOptions options)
115  {
116  if (options.HasFlag(StringHashOptions.IgnoreWhitespace) || options.HasFlag(StringHashOptions.IgnoreCase))
117  {
118  string str = File.ReadAllText(path, Encoding.UTF8);
119  return CalculateForString(str, options);
120  }
121  else
122  {
123  byte[] bytes = File.ReadAllBytes(path);
124  return CalculateForBytes(bytes);
125  }
126  }
127 
128  public static Md5Hash CalculateForString(string str, StringHashOptions options)
129  {
130  if (options.HasFlag(StringHashOptions.IgnoreCase))
131  {
132  str = str.ToLowerInvariant();
133  }
134  if (options.HasFlag(StringHashOptions.IgnoreWhitespace))
135  {
136  str = RemoveWhitespace(str);
137  }
138  byte[] bytes = Encoding.UTF8.GetBytes(str);
139  return CalculateForBytes(bytes);
140  }
141 
142  public override string ToString()
143  {
144  return StringRepresentation;
145  }
146 
147  public override bool Equals(object? obj)
148  {
149  if (obj is Md5Hash { StringRepresentation: { } otherStr })
150  {
151  string selfStr = otherStr.Length < StringRepresentation.Length
152  ? StringRepresentation[..otherStr.Length]
154  otherStr = StringRepresentation.Length < otherStr.Length
155  ? otherStr[..StringRepresentation.Length]
156  : otherStr;
157  return selfStr.Equals(otherStr, StringComparison.OrdinalIgnoreCase);
158  }
159  return false;
160  }
161 
162  public override int GetHashCode()
163  {
164  return ShortRepresentation.GetHashCode(StringComparison.OrdinalIgnoreCase);
165  }
166 
167  public static bool operator ==(Md5Hash? a, Md5Hash? b)
168  => Equals(a, b);
169 
170  public static bool operator !=(Md5Hash? a, Md5Hash? b) => !(a == b);
171  }
172 }
static Md5Hash MergeHashes(IEnumerable< Md5Hash > hashes)
Definition: Md5Hash.cs:86
static Md5Hash BytesAsHash(byte[] bytes)
Definition: Md5Hash.cs:101
static bool operator==(Md5Hash? a, Md5Hash? b)
static bool operator!=(Md5Hash? a, Md5Hash? b)
static Md5Hash CalculateForFile(string path, StringHashOptions options)
Definition: Md5Hash.cs:114
static Md5Hash StringAsHash(string hash)
Definition: Md5Hash.cs:80
override bool Equals(object? obj)
Definition: Md5Hash.cs:147
override string ToString()
Definition: Md5Hash.cs:142
static Md5Hash CalculateForBytes(byte[] bytes)
Definition: Md5Hash.cs:96
override int GetHashCode()
Definition: Md5Hash.cs:162
readonly string StringRepresentation
Definition: Md5Hash.cs:32
readonly byte[] ByteRepresentation
Definition: Md5Hash.cs:31
readonly string ShortRepresentation
Definition: Md5Hash.cs:33
static Md5Hash CalculateForString(string str, StringHashOptions options)
Definition: Md5Hash.cs:128
static readonly Md5Hash Blank
Definition: Md5Hash.cs:14
static string GetShortHash(string fullHash)
Definition: Md5Hash.cs:52