Skip to content

Commit a3113bd

Browse files
committed
Create KojimaHash1.swift
1 parent defdd21 commit a3113bd

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright © 2025 Dustin Collins (Strega's Gate)
3+
* All Rights Reserved.
4+
*
5+
* http://stregasgate.com
6+
*/
7+
8+
/**
9+
Create a unique identifier from a file name.
10+
11+
This hashing function was used by Metal Gear Solid 1 to efficiently reference files at reduced performance cost and
12+
memory usage compared to string comparisons.
13+
This hashing method has been dubbed "KojimaHash" by the MGS reversing community.
14+
15+
- warning: Due to the lower bit width (16bit), this hash is more likeley to have collisions.
16+
Keep track of the generated hashes to ensure there are no collisions.
17+
*/
18+
public struct KojimaHash1 {
19+
public let value: UInt16
20+
21+
public init(_ hash: UInt16) {
22+
self.value = hash
23+
}
24+
25+
public init?(hexString: String) {
26+
self.value = Self.hash(from: hexString)
27+
}
28+
29+
public init?(filename: String) {
30+
self.value = Self.hash(from: filename)
31+
}
32+
33+
/// Create a hash value from a filename string
34+
public static func hash(from filename: String) -> UInt16 {
35+
var hash: UInt16 = 0
36+
for c in filename.split(separator: ".")[0].utf8 {
37+
hash = (( hash >> 11 ) | ( hash << 5 ))
38+
hash = hash &+ UInt16(c)
39+
}
40+
return hash
41+
}
42+
43+
public static func hash(from hexString: String) -> UInt16? {
44+
return UInt16(hexString, radix: 16)
45+
}
46+
47+
public static func string(from hashValue: UInt16) -> String {
48+
return String(hashValue, radix: 16, uppercase: true)
49+
}
50+
}
51+
52+
extension KojimaHash1: Codable {}
53+
extension KojimaHash1: Sendable {}
54+
55+
extension KojimaHash1: Identifiable {
56+
public var id: UInt16 {
57+
return self.value
58+
}
59+
}
60+
61+
extension KojimaHash1: Comparable {
62+
public static func < (lhs: KojimaHash1, rhs: KojimaHash1) -> Bool {
63+
return lhs.value < rhs.value
64+
}
65+
}
66+
67+
extension KojimaHash1: Equatable {
68+
public static func == (lhs: KojimaHash1, rhs: KojimaHash1) -> Bool {
69+
return lhs.value == rhs.value
70+
}
71+
}
72+
73+
extension KojimaHash1: Hashable {
74+
public func hash(into hasher: inout Hasher) {
75+
hasher.combine(self.value)
76+
}
77+
}
78+
79+
extension KojimaHash1: CustomStringConvertible, CustomDebugStringConvertible {
80+
public var description: String {
81+
return Self.string(from: self.value)
82+
}
83+
public var debugDescription: String {
84+
return "0x" + Self.string(from: self.value)
85+
}
86+
}
87+
88+
extension KojimaHash1: BinaryCodable {
89+
public func encode(into data: inout ContiguousArray<UInt8>, version: BinaryCodableVersion) throws {
90+
try self.value.encode(into: &data, version: version)
91+
}
92+
public init(decoding data: UnsafeRawBufferPointer, at offset: inout Int, version: BinaryCodableVersion) throws {
93+
self.value = try .init(decoding: data, at: &offset, version: version)
94+
}
95+
}
96+
97+
extension KojimaHash1: ExpressibleByIntegerLiteral {
98+
public typealias IntegerLiteralType = UInt16
99+
public init(integerLiteral value: UInt16) {
100+
self.init(value)
101+
}
102+
}

0 commit comments

Comments
 (0)