2
0
mirror of https://github.com/Laupetin/OpenAssetTools.git synced 2026-03-03 03:23:03 +00:00

chore: call common djb2 implementations in game hashing funcs

This commit is contained in:
Jan Laupetin
2026-02-21 10:52:36 +00:00
parent 9502ebfc26
commit f7e0cb3c45
10 changed files with 318 additions and 164 deletions

View File

@@ -1,28 +1,24 @@
#pragma once #pragma once
#include "IW3.h" #include "IW3.h"
#include "Utils/Djb2.h"
#include <iostream>
namespace IW3 namespace IW3
{ {
class Common class Common
{ {
public: public:
static constexpr uint32_t R_HashString(const char* string, const uint32_t hash) static constexpr uint32_t R_HashString(const char* str, const uint32_t hash)
{ {
const char* v2 = string; // edx@1 return djb2_xor_nocase(str, hash);
char v3 = *string; // cl@1
uint32_t result = hash;
for (; *v2; v3 = *v2)
{
++v2;
result = 33 * result ^ (v3 | 0x20);
}
return result;
} }
static constexpr uint32_t R_HashString(const char* string) static constexpr uint32_t R_HashString(const char* string)
{ {
// Using djb2 with a 0 starting value makes a worse hash func apparently
// but who am I to judge
return R_HashString(string, 0u); return R_HashString(string, 0u);
} }

View File

@@ -2,26 +2,8 @@
#include "Utils/Pack.h" #include "Utils/Pack.h"
#include <cctype>
using namespace IW4; using namespace IW4;
int Common::StringTable_HashString(const char* str)
{
if (!str)
return 0;
auto result = 0;
auto offset = 0;
while (str[offset])
{
const auto c = tolower(str[offset++]);
result = c + 31 * result;
}
return result;
}
PackedTexCoords Common::Vec2PackTexCoords(const float (&in)[2]) PackedTexCoords Common::Vec2PackTexCoords(const float (&in)[2])
{ {
return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)}; return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)};

View File

@@ -1,33 +1,41 @@
#pragma once #pragma once
#include "IW4.h" #include "IW4.h"
#include "Utils/Djb2.h"
#include <cctype>
namespace IW4 namespace IW4
{ {
class Common class Common
{ {
public: public:
static constexpr uint32_t R_HashString(const char* string, const uint32_t hash) static constexpr int StringTable_HashString(const char* str)
{ {
const char* v2 = string; // edx@1 if (!str)
char v3 = *string; // cl@1 return 0;
uint32_t result = hash;
for (; *v2; v3 = *v2) // Lets do djb2 with 31 instead of 33 because why not
{ // and leave out the starting value while we are at it
++v2; uint32_t hash = 0;
result = 33 * result ^ (v3 | 0x20); for (char c = *str; c; c = *++str)
hash = hash * 31 + std::tolower(c);
return static_cast<int>(hash);
} }
return result;
static constexpr uint32_t R_HashString(const char* str, const uint32_t hash)
{
return djb2_xor_nocase(str, hash);
} }
static constexpr uint32_t R_HashString(const char* string) static constexpr uint32_t R_HashString(const char* string)
{ {
// Using djb2 with a 0 starting value makes a worse hash func apparently
// but who am I to judge
return R_HashString(string, 0u); return R_HashString(string, 0u);
} }
static int StringTable_HashString(const char* str);
static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]); static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]);
static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]); static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]);
static GfxColor Vec4PackGfxColor(const float (&in)[4]); static GfxColor Vec4PackGfxColor(const float (&in)[4]);

View File

@@ -2,26 +2,8 @@
#include "Utils/Pack.h" #include "Utils/Pack.h"
#include <cctype>
using namespace IW5; using namespace IW5;
int Common::StringTable_HashString(const char* str)
{
if (!str)
return 0;
auto result = 0;
auto offset = 0;
while (str[offset])
{
const auto c = tolower(str[offset++]);
result = c + 31 * result;
}
return result;
}
PackedTexCoords Common::Vec2PackTexCoords(const float (&in)[2]) PackedTexCoords Common::Vec2PackTexCoords(const float (&in)[2])
{ {
return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)}; return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)};

View File

@@ -1,26 +1,38 @@
#pragma once #pragma once
#include "IW5.h" #include "IW5.h"
#include "Utils/Djb2.h"
#include <cctype>
namespace IW5 namespace IW5
{ {
class Common class Common
{ {
public: public:
static int StringTable_HashString(const char* str); static constexpr int StringTable_HashString(const char* str)
{
if (!str)
return 0;
static constexpr uint32_t R_HashString(const char* str, uint32_t hash) // Lets do djb2 with 31 instead of 33 because why not
{ // and leave out the starting value while we are at it
for (const auto* pos = str; *pos; pos++) uint32_t hash = 0;
{ for (char c = *str; c; c = *++str)
hash = 33 * hash ^ (*pos | 0x20); hash = hash * 31 + std::tolower(c);
return static_cast<int>(hash);
} }
return hash; static constexpr uint32_t R_HashString(const char* str, const uint32_t hash)
{
return djb2_xor_nocase(str, hash);
} }
static constexpr uint32_t R_HashString(const char* string) static constexpr uint32_t R_HashString(const char* string)
{ {
// Using djb2 with a 0 starting value makes a worse hash func apparently
// but who am I to judge
return R_HashString(string, 0u); return R_HashString(string, 0u);
} }

View File

@@ -2,62 +2,8 @@
#include "Utils/Pack.h" #include "Utils/Pack.h"
#include <cctype>
using namespace T5; using namespace T5;
int Common::Com_HashKey(const char* str, const int maxLen)
{
if (str == nullptr)
return 0;
int hash = 0;
for (int i = 0; i < maxLen; i++)
{
if (str[i] == '\0')
break;
hash += str[i] * (0x77 + i);
}
return hash ^ ((hash ^ (hash >> 10)) >> 10);
}
int Common::Com_HashString(const char* str)
{
if (!str)
return 0;
auto result = 0x1505;
auto offset = 0;
while (str[offset])
{
const auto c = tolower(str[offset++]);
result = c + 33 * result;
}
return result;
}
int Common::Com_HashString(const char* str, const int len)
{
if (!str)
return 0;
int result = 0x1505;
int offset = 0;
while (str[offset])
{
if (len > 0 && offset >= len)
break;
const int c = tolower(str[offset++]);
result = c + 33 * result;
}
return result;
}
PackedTexCoords Common::Vec2PackTexCoords(const float (&in)[2]) PackedTexCoords Common::Vec2PackTexCoords(const float (&in)[2])
{ {
return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)}; return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)};

View File

@@ -1,28 +1,48 @@
#pragma once #pragma once
#include "T5.h" #include "T5.h"
#include "Utils/Djb2.h"
namespace T5 namespace T5
{ {
class Common class Common
{ {
public: public:
static int Com_HashKey(const char* str, int maxLen); static constexpr int Com_HashKey(const char* str, const int maxLen)
static int Com_HashString(const char* str); {
static int Com_HashString(const char* str, int len); if (str == nullptr)
return 0;
static constexpr uint32_t R_HashString(const char* str, uint32_t hash) int hash = 0;
for (int i = 0; i < maxLen; i++)
{ {
for (const auto* pos = str; *pos; pos++) if (str[i] == '\0')
{ break;
hash = 33 * hash ^ (*pos | 0x20);
hash += str[i] * (0x77 + i);
} }
return hash; return hash ^ ((hash ^ (hash >> 10)) >> 10);
}
static constexpr int Com_HashString(const char* str)
{
// Hashing aesthetics seem to be a thing
if (!str)
return 0;
return static_cast<int>(djb2_lower(str));
}
static constexpr uint32_t R_HashString(const char* str, const uint32_t hash)
{
return djb2_xor_nocase(str, hash);
} }
static constexpr uint32_t R_HashString(const char* string) static constexpr uint32_t R_HashString(const char* string)
{ {
// Using djb2 with a 0 starting value makes a worse hash func apparently
// but who am I to judge
return R_HashString(string, 0u); return R_HashString(string, 0u);
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "T6.h" #include "T6.h"
#include "Utils/Djb2.h"
#include <cctype> #include <cctype>
@@ -28,51 +29,22 @@ namespace T6
static constexpr int Com_HashString(const char* str) static constexpr int Com_HashString(const char* str)
{ {
// Hashing aesthetics seem to be a thing
if (!str) if (!str)
return 0; return 0;
auto result = 0x1505; return static_cast<int>(djb2_lower(str));
auto offset = 0;
while (str[offset])
{
const auto c = tolower(str[offset++]);
result = c + 33 * result;
} }
return result; static constexpr uint32_t R_HashString(const char* str, const uint32_t hash)
}
static constexpr int Com_HashString(const char* str, const int len)
{ {
if (!str) return djb2_xor_nocase(str, hash);
return 0;
int result = 0x1505;
int offset = 0;
while (str[offset])
{
if (len > 0 && offset >= len)
break;
const int c = tolower(str[offset++]);
result = c + 33 * result;
}
return result;
}
static constexpr uint32_t R_HashString(const char* str, uint32_t hash)
{
for (const auto* pos = str; *pos; pos++)
{
hash = 33 * hash ^ (*pos | 0x20);
}
return hash;
} }
static constexpr uint32_t R_HashString(const char* string) static constexpr uint32_t R_HashString(const char* string)
{ {
// Using djb2 with a 0 starting value makes a worse hash func apparently
// but who am I to judge
return R_HashString(string, 0u); return R_HashString(string, 0u);
} }
@@ -81,6 +53,9 @@ namespace T6
if (!str || !*str) if (!str || !*str)
return 0; return 0;
// Seems to be somewhat based on sdbm
// http://www.cse.yorku.ca/~oz/hash.html
auto result = 0x1505; auto result = 0x1505;
auto offset = 0u; auto offset = 0u;

100
src/Common/Utils/Djb2.h Normal file
View File

@@ -0,0 +1,100 @@
#pragma once
#include <cctype>
#include <cstdint>
// This header contains multiple varying implementations of the DJB2 algorithm:
// http://www.cse.yorku.ca/~oz/hash.html
constexpr uint32_t DJB2_STARTING_VALUE = 5381;
static constexpr uint32_t djb2(const char* str, uint32_t hash)
{
for (char c = *str; c; c = *++str)
{
// hash * 33 + c
hash = ((hash << 5) + hash) + c;
}
return hash;
}
static constexpr uint32_t djb2(const char* str)
{
return djb2(str, DJB2_STARTING_VALUE);
}
static constexpr uint32_t djb2_nocase(const char* str, uint32_t hash)
{
for (char c = *str; c; c = *++str)
{
// Or with 0x20 makes the string case-insensitive
// but also messes up non-letters
hash = ((hash << 5) + hash) + (c | 0x20);
}
return hash;
}
static constexpr uint32_t djb2_nocase(const char* str)
{
return djb2_nocase(str, DJB2_STARTING_VALUE);
}
static constexpr uint32_t djb2_lower(const char* str, uint32_t hash)
{
for (char c = *str; c; c = *++str)
hash = ((hash << 5) + hash) + std::tolower(c);
return hash;
}
static constexpr uint32_t djb2_lower(const char* str)
{
return djb2_lower(str, DJB2_STARTING_VALUE);
}
static constexpr uint32_t djb2_xor(const char* str, uint32_t hash)
{
for (char c = *str; c; c = *++str)
{
hash = ((hash << 5) + hash) ^ c;
}
return hash;
}
static constexpr uint32_t djb2_xor(const char* str)
{
return djb2_xor(str, DJB2_STARTING_VALUE);
}
static constexpr uint32_t djb2_xor_nocase(const char* str, uint32_t hash)
{
for (char c = *str; c; c = *++str)
{
// Or with 0x20 makes the string case-insensitive
// but also messes up non-letters
hash = ((hash << 5) + hash) ^ (c | 0x20);
}
return hash;
}
static constexpr uint32_t djb2_xor_nocase(const char* str)
{
return djb2_xor_nocase(str, DJB2_STARTING_VALUE);
}
static constexpr uint32_t djb2_xor_lower(const char* str, uint32_t hash)
{
for (char c = *str; c; c = *++str)
hash = ((hash << 5) + hash) ^ std::tolower(c);
return hash;
}
static constexpr uint32_t djb2_xor_lower(const char* str)
{
return djb2_xor_lower(str, DJB2_STARTING_VALUE);
}

View File

@@ -0,0 +1,133 @@
#include "Utils/Djb2.h"
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
TEST_CASE("Djb2: Check checksums", "[djb2]")
{
SECTION("for djb2")
{
const auto [str, expectedHash] = GENERATE(Catch::Generators::table<const char*, uint32_t>({
{"hello world", 0x3551c8c1},
{"universe2", 0x608d72a8},
{"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", 0xf93c46aa},
{"AngularVelocityScale", 0xbba68366},
{"BakedLightingIntensity", 0x98a9e159},
{"Layer1OffsetBobbleDelay", 0x319c46af},
{"MaxDepth", 0xd34432a0},
{"MomentumColor", 0x33a353d6},
{"SparkleScale", 0x2249309f},
{"TickMarkColorAndHarshness", 0x0d77a53c},
{"worldViewProjectionMatrix", 0xcf668f9a},
}));
CAPTURE(str);
const auto hash = djb2(str);
REQUIRE(hash == expectedHash);
}
SECTION("for djb2_nocase")
{
const auto [str, expectedHash] = GENERATE(Catch::Generators::table<const char*, uint32_t>({
{"hello world", 0x3551c8c1},
{"universe2", 0x608d72a8},
{"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", 0xd12da30a},
{"AngularVelocityScale", 0x2eb08fc6},
{"BakedLightingIntensity", 0xd8fd95b9},
{"Layer1OffsetBobbleDelay", 0x4166172f},
{"MaxDepth", 0x5dc1dee0},
{"MomentumColor", 0x21c79416},
{"SparkleScale", 0x42f7ecdf},
{"TickMarkColorAndHarshness", 0xbb5761dc},
{"worldViewProjectionMatrix", 0x25962bfa},
}));
CAPTURE(str);
const auto hash = djb2_nocase(str);
REQUIRE(hash == expectedHash);
}
SECTION("for djb2_lower")
{
const auto [str, expectedHash] = GENERATE(Catch::Generators::table<const char*, uint32_t>({
{"hello world", 0x3551c8c1},
{"universe2", 0x608d72a8},
{"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", 0xf93c46aa},
{"AngularVelocityScale", 0x2eb08fc6},
{"BakedLightingIntensity", 0xd8fd95b9},
{"Layer1OffsetBobbleDelay", 0x4166172f},
{"MaxDepth", 0x5dc1dee0},
{"MomentumColor", 0x21c79416},
{"SparkleScale", 0x42f7ecdf},
{"TickMarkColorAndHarshness", 0xbb5761dc},
{"worldViewProjectionMatrix", 0x25962bfa},
}));
CAPTURE(str);
const auto hash = djb2_lower(str);
REQUIRE(hash == expectedHash);
}
SECTION("for djb2_xor")
{
const auto [str, expectedHash] = GENERATE(Catch::Generators::table<const char*, uint32_t>({
{"hello world", 0xf8c65345},
{"universe2", 0xdce33172},
{"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", 0x7e4ed3e0},
{"AngularVelocityScale", 0xf65bbb88},
{"BakedLightingIntensity", 0xd79467bd},
{"Layer1OffsetBobbleDelay", 0xf033afcb},
{"MaxDepth", 0x190e08dc},
{"MomentumColor", 0xf4ded370},
{"SparkleScale", 0x9358a5ef},
{"TickMarkColorAndHarshness", 0x12d2e5f8},
{"worldViewProjectionMatrix", 0x88669e4c},
}));
CAPTURE(str);
const auto hash = djb2_xor(str);
REQUIRE(hash == expectedHash);
}
SECTION("for djb2_xor_nocase")
{
const auto [str, expectedHash] = GENERATE(Catch::Generators::table<const char*, uint32_t>({
{"hello world", 0xf8c65345},
{"universe2", 0xdce33172},
{"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", 0x3cc06840},
{"AngularVelocityScale", 0x7efc36a8},
{"BakedLightingIntensity", 0x3d52c39d},
{"Layer1OffsetBobbleDelay", 0x48574d8b},
{"MaxDepth", 0x3c5b84dc},
{"MomentumColor", 0x4d32f230},
{"SparkleScale", 0x7656c02f},
{"TickMarkColorAndHarshness", 0x917d8218},
{"worldViewProjectionMatrix", 0x721c412c},
}));
CAPTURE(str);
const auto hash = djb2_xor_nocase(str);
REQUIRE(hash == expectedHash);
}
SECTION("for djb2_xor_lower")
{
const auto [str, expectedHash] = GENERATE(Catch::Generators::table<const char*, uint32_t>({
{"hello world", 0xf8c65345},
{"universe2", 0xdce33172},
{"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", 0x7e4ed3e0},
{"AngularVelocityScale", 0x7efc36a8},
{"BakedLightingIntensity", 0x3d52c39d},
{"Layer1OffsetBobbleDelay", 0x48574d8b},
{"MaxDepth", 0x3c5b84dc},
{"MomentumColor", 0x4d32f230},
{"SparkleScale", 0x7656c02f},
{"TickMarkColorAndHarshness", 0x917d8218},
{"worldViewProjectionMatrix", 0x721c412c},
}));
CAPTURE(str);
const auto hash = djb2_xor_lower(str);
REQUIRE(hash == expectedHash);
}
}