forked from ericlangedijk/Lemmix
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLevel.Hash.pas
104 lines (88 loc) · 2.49 KB
/
Level.Hash.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
unit Level.Hash;
{$include lem_directives.inc}
interface
uses
SysUtils, MD5,
Base.Utils,
Dos.Structures;
// Note: after release never change the hashing of levelcodes
type
TLevelHasher = class sealed
private
type
TShortHash = array[0..9] of Byte;
public
class function LongHash(const LVL: TLVLRec): TBytes; static; inline; // 16
class function ShortHash(const LVL: TLVLRec): UInt64; static; // 8
class function GetLevelCode(const hash: UInt64): string; overload; static; // 10
class function GetLevelCode(const LVL: TLVLRec): string; overload; static; // 10
end;
implementation
{ TLevelHasher }
class function TLevelHasher.LongHash(const LVL: TLVLRec): TBytes;
var
H: TMD5Context;
D: TMD5Digest;
L: TLVLRec;
begin
MD5Init(H);
L := LVL;
MD5.MD5Update(H, L, SizeOf(LVL));
MD5.MD5Final(H, D);
SetLength(Result, 16);
Move(D, Result[0], 16);
{$if defined(paranoid)}
if Length(Result) <> 16 then Throw('TLevelHasher.LongHash length error');
{$ifend}
end;
class function TLevelHasher.ShortHash(const LVL: TLVLRec): UInt64;
var
U: Int64Rec absolute Result;
hash: TBytes;
i: Integer;
begin
hash := LongHash(LVL);
for i := 0 to 7 do
U.Bytes[i] := hash[i] xor hash[i + 8];
end;
class function TLevelHasher.GetLevelCode(const hash: UInt64): string;
const
Vowels: array[0..4] of Char = ('A','E','I','O','U');
NonVowels: array[0..19] of Char = ('B','C','D','F','G','H','J','K','L','M','N','P', 'R', 'S','T','V','W','X','Y','Z'); // Q is omitted on purpose
var
U: Int64Rec absolute hash;
Sum: Integer;
i: Integer;
Vowel: Boolean;
b: Byte;
begin
Sum := 0;
for i := 0 to 7 do
Inc(Sum, Integer(U.Bytes[i]));
Vowel := Odd(Sum);
SetLength(Result, 10);
for i := 0 to 7 do begin
if Vowel
then Result[i + 1] := Vowels[U.Bytes[i] mod 5]
else Result[i + 1] := NonVowels[U.Bytes[i] mod 20];
Vowel := not Vowel;
end;
if Odd(Sum)
then b := U.Bytes[0] xor U.Bytes[1]
else b := U.Bytes[6] xor U.Bytes[7];
if Vowel
then Result[9] := Vowels[b mod 5]
else Result[9] := NonVowels[b mod 20];
Vowel := not Vowel;
if Odd(Sum)
then b := U.Bytes[2] xor U.Bytes[3]
else b := U.Bytes[4] xor U.Bytes[5];
if Vowel
then Result[10] := Vowels[b mod 5]
else Result[10] := NonVowels[b mod 20];
end;
class function TLevelHasher.GetLevelCode(const LVL: TLVLRec): string;
begin
Result := GetLevelCode(ShortHash(LVL));
end;
end.