diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c99567ba..66f8df70 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -82,6 +82,7 @@ jobs: - name: Test working-directory: ${{ github.workspace }}/build/lib/Release_${{ matrix.build_arch }}/tests run: | + ./CommonTests ./ObjCommonTests ./ObjCompilingTests ./ObjLoadingTests @@ -138,6 +139,8 @@ jobs: working-directory: ${{ github.workspace }}/build/lib/Release_${{ matrix.build_arch }}/tests run: | $combinedExitCode = 0 + ./CommonTests + $combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE) ./ObjCommonTests $combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE) ./ObjCompilingTests diff --git a/premake5.lua b/premake5.lua index 535668a0..5cfb7552 100644 --- a/premake5.lua +++ b/premake5.lua @@ -195,6 +195,7 @@ group "" -- Tests -- ======================== include "test/Catch2Common.lua" +include "test/CommonTests.lua" include "test/ObjCommonTestUtils.lua" include "test/ObjCommonTests.lua" include "test/ObjCompilingTests.lua" @@ -209,6 +210,7 @@ include "test/ZoneCommonTests.lua" -- Tests group: Unit test and other tests projects group "Tests" Catch2Common:project() + CommonTests:project() ObjCommonTestUtils:project() ObjCommonTests:project() ObjCompilingTests:project() diff --git a/src/Common/Game/IW3/CommonIW3.h b/src/Common/Game/IW3/CommonIW3.h index 43664b62..68097801 100644 --- a/src/Common/Game/IW3/CommonIW3.h +++ b/src/Common/Game/IW3/CommonIW3.h @@ -1,28 +1,24 @@ #pragma once #include "IW3.h" +#include "Utils/Djb2.h" + +#include namespace IW3 { class Common { 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 - char v3 = *string; // cl@1 - uint32_t result = hash; - - for (; *v2; v3 = *v2) - { - ++v2; - result = 33 * result ^ (v3 | 0x20); - } - return result; + return djb2_xor_nocase(str, hash); } 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); } diff --git a/src/Common/Game/IW4/CommonIW4.cpp b/src/Common/Game/IW4/CommonIW4.cpp index 6b3b6f97..7fdc4bc3 100644 --- a/src/Common/Game/IW4/CommonIW4.cpp +++ b/src/Common/Game/IW4/CommonIW4.cpp @@ -2,26 +2,8 @@ #include "Utils/Pack.h" -#include - 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]) { return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)}; diff --git a/src/Common/Game/IW4/CommonIW4.h b/src/Common/Game/IW4/CommonIW4.h index 9aaff02c..7d19edbf 100644 --- a/src/Common/Game/IW4/CommonIW4.h +++ b/src/Common/Game/IW4/CommonIW4.h @@ -1,33 +1,41 @@ #pragma once #include "IW4.h" +#include "Utils/Djb2.h" + +#include namespace IW4 { class Common { 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 - char v3 = *string; // cl@1 - uint32_t result = hash; + if (!str) + return 0; - for (; *v2; v3 = *v2) - { - ++v2; - result = 33 * result ^ (v3 | 0x20); - } - return result; + // Lets do djb2 with 31 instead of 33 because why not + // and leave out the starting value while we are at it + uint32_t hash = 0; + for (char c = *str; c; c = *++str) + hash = hash * 31 + std::tolower(c); + + return static_cast(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) { + // Using djb2 with a 0 starting value makes a worse hash func apparently + // but who am I to judge return R_HashString(string, 0u); } - static int StringTable_HashString(const char* str); - static PackedTexCoords Vec2PackTexCoords(const float (&in)[2]); static PackedUnitVec Vec3PackUnitVec(const float (&in)[3]); static GfxColor Vec4PackGfxColor(const float (&in)[4]); diff --git a/src/Common/Game/IW5/CommonIW5.cpp b/src/Common/Game/IW5/CommonIW5.cpp index 55b820fc..98d61d2f 100644 --- a/src/Common/Game/IW5/CommonIW5.cpp +++ b/src/Common/Game/IW5/CommonIW5.cpp @@ -2,26 +2,8 @@ #include "Utils/Pack.h" -#include - 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]) { return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)}; diff --git a/src/Common/Game/IW5/CommonIW5.h b/src/Common/Game/IW5/CommonIW5.h index 33fac614..5da87f86 100644 --- a/src/Common/Game/IW5/CommonIW5.h +++ b/src/Common/Game/IW5/CommonIW5.h @@ -1,26 +1,38 @@ #pragma once #include "IW5.h" +#include "Utils/Djb2.h" + +#include namespace IW5 { class Common { public: - static int StringTable_HashString(const char* str); - - static constexpr uint32_t R_HashString(const char* str, uint32_t hash) + static constexpr int StringTable_HashString(const char* str) { - for (const auto* pos = str; *pos; pos++) - { - hash = 33 * hash ^ (*pos | 0x20); - } + if (!str) + return 0; - return hash; + // Lets do djb2 with 31 instead of 33 because why not + // and leave out the starting value while we are at it + uint32_t hash = 0; + for (char c = *str; c; c = *++str) + hash = hash * 31 + std::tolower(c); + + return static_cast(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) { + // Using djb2 with a 0 starting value makes a worse hash func apparently + // but who am I to judge return R_HashString(string, 0u); } diff --git a/src/Common/Game/T5/CommonT5.cpp b/src/Common/Game/T5/CommonT5.cpp index 979f0300..c09ce3da 100644 --- a/src/Common/Game/T5/CommonT5.cpp +++ b/src/Common/Game/T5/CommonT5.cpp @@ -2,62 +2,8 @@ #include "Utils/Pack.h" -#include - 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]) { return PackedTexCoords{pack32::Vec2PackTexCoordsVU(in)}; diff --git a/src/Common/Game/T5/CommonT5.h b/src/Common/Game/T5/CommonT5.h index c5e4d762..c743ab96 100644 --- a/src/Common/Game/T5/CommonT5.h +++ b/src/Common/Game/T5/CommonT5.h @@ -1,28 +1,48 @@ #pragma once #include "T5.h" +#include "Utils/Djb2.h" namespace T5 { class Common { public: - static int Com_HashKey(const char* str, int maxLen); - static int Com_HashString(const char* str); - static int Com_HashString(const char* str, int len); - - static constexpr uint32_t R_HashString(const char* str, uint32_t hash) + static constexpr int Com_HashKey(const char* str, const int maxLen) { - for (const auto* pos = str; *pos; pos++) + if (str == nullptr) + return 0; + + int hash = 0; + for (int i = 0; i < maxLen; i++) { - hash = 33 * hash ^ (*pos | 0x20); + if (str[i] == '\0') + break; + + 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(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) { + // Using djb2 with a 0 starting value makes a worse hash func apparently + // but who am I to judge return R_HashString(string, 0u); } diff --git a/src/Common/Game/T6/CommonT6.h b/src/Common/Game/T6/CommonT6.h index 212a09d9..a04f657b 100644 --- a/src/Common/Game/T6/CommonT6.h +++ b/src/Common/Game/T6/CommonT6.h @@ -1,6 +1,7 @@ #pragma once #include "T6.h" +#include "Utils/Djb2.h" #include @@ -28,51 +29,22 @@ namespace T6 static constexpr int Com_HashString(const char* str) { + // Hashing aesthetics seem to be a thing 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; + return static_cast(djb2_lower(str)); } - static constexpr int Com_HashString(const char* str, const int len) + static constexpr uint32_t R_HashString(const char* str, const uint32_t hash) { - 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; - } - - 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; + return djb2_xor_nocase(str, hash); } 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); } @@ -81,6 +53,9 @@ namespace T6 if (!str || !*str) return 0; + // Seems to be somewhat based on sdbm + // http://www.cse.yorku.ca/~oz/hash.html + auto result = 0x1505; auto offset = 0u; diff --git a/src/Common/Utils/Djb2.h b/src/Common/Utils/Djb2.h new file mode 100644 index 00000000..163fe2a6 --- /dev/null +++ b/src/Common/Utils/Djb2.h @@ -0,0 +1,100 @@ +#pragma once + +#include +#include + +// 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); +} diff --git a/test/CommonTests.lua b/test/CommonTests.lua new file mode 100644 index 00000000..6300f2b1 --- /dev/null +++ b/test/CommonTests.lua @@ -0,0 +1,54 @@ +CommonTests = {} + +function CommonTests:include(includes) + if includes:handle(self:name()) then + includedirs { + path.join(TestFolder(), "CommonTests") + } + end +end + +function CommonTests:link(links) + +end + +function CommonTests:use() + +end + +function CommonTests:name() + return "CommonTests" +end + +function CommonTests:project() + local folder = TestFolder() + local includes = Includes:create() + local links = Links:create() + + project(self:name()) + targetdir(TargetDirectoryTest) + location "%{wks.location}/test/%{prj.name}" + kind "ConsoleApp" + language "C++" + + files { + path.join(folder, "CommonTests/**.h"), + path.join(folder, "CommonTests/**.cpp") + } + + vpaths { + ["*"] = { + path.join(folder, "CommonTests") + } + } + + self:include(includes) + Catch2Common:include(includes) + Common:include(includes) + catch2:include(includes) + + links:linkto(Common) + links:linkto(catch2) + links:linkto(Catch2Common) + links:linkall() +end diff --git a/test/CommonTests/Game/IW3/CommonIW3Tests.cpp b/test/CommonTests/Game/IW3/CommonIW3Tests.cpp new file mode 100644 index 00000000..f3736fd1 --- /dev/null +++ b/test/CommonTests/Game/IW3/CommonIW3Tests.cpp @@ -0,0 +1,28 @@ +#include "Game/IW3/CommonIW3.h" + +#include +#include + +TEST_CASE("IW3: Check checksums", "[iw3]") +{ + SECTION("for R_HashString") + { + const auto [str, expectedHash] = GENERATE(Catch::Generators::table({ + {"hello world", 0xe7d74060}, + {"universe2", 0x113fdcd7}, + {"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", 0xdd0153c5}, + {"AngularVelocityScale", 0x18f2cb6d}, + {"BakedLightingIntensity", 0xd627f218}, + {"Layer1OffsetBobbleDelay", 0xcd91b6ae}, + {"MaxDepth", 0x61ed5959}, + {"MomentumColor", 0xc80f3595}, + {"SparkleScale", 0x5488816a}, + {"TickMarkColorAndHarshness", 0xd6c718bd}, + {"worldViewProjectionMatrix", 0x7f661409}, + })); + + CAPTURE(str); + const auto hash = IW3::Common::R_HashString(str); + REQUIRE(hash == expectedHash); + } +} diff --git a/test/CommonTests/Game/IW4/CommonIW4Tests.cpp b/test/CommonTests/Game/IW4/CommonIW4Tests.cpp new file mode 100644 index 00000000..b715c7f7 --- /dev/null +++ b/test/CommonTests/Game/IW4/CommonIW4Tests.cpp @@ -0,0 +1,49 @@ +#include "Game/IW4/CommonIW4.h" + +#include +#include + +TEST_CASE("IW4: Check checksums", "[iw4]") +{ + SECTION("for StringTable_HashString") + { + const auto [str, expectedHash] = GENERATE(Catch::Generators::table({ + {"hello world", 0x6aefe2c4}, + {"universe2", 0xe796fe8d}, + {"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", 0x16c4d3f1}, + {"AngularVelocityScale", 0x573e9feb}, + {"BakedLightingIntensity", 0x15af785c}, + {"Layer1OffsetBobbleDelay", 0xfb83324a}, + {"MaxDepth", 0x1892e3df}, + {"MomentumColor", 0x5e7abdcb}, + {"SparkleScale", 0x9e4409f4}, + {"TickMarkColorAndHarshness", 0x5dfe4c47}, + {"worldViewProjectionMatrix", 0xa2501a07}, + })); + + CAPTURE(str); + const auto hash = IW4::Common::StringTable_HashString(str); + REQUIRE(hash == expectedHash); + } + + SECTION("for R_HashString") + { + const auto [str, expectedHash] = GENERATE(Catch::Generators::table({ + {"hello world", 0xe7d74060}, + {"universe2", 0x113fdcd7}, + {"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", 0xdd0153c5}, + {"AngularVelocityScale", 0x18f2cb6d}, + {"BakedLightingIntensity", 0xd627f218}, + {"Layer1OffsetBobbleDelay", 0xcd91b6ae}, + {"MaxDepth", 0x61ed5959}, + {"MomentumColor", 0xc80f3595}, + {"SparkleScale", 0x5488816a}, + {"TickMarkColorAndHarshness", 0xd6c718bd}, + {"worldViewProjectionMatrix", 0x7f661409}, + })); + + CAPTURE(str); + const auto hash = IW4::Common::R_HashString(str); + REQUIRE(hash == expectedHash); + } +} diff --git a/test/CommonTests/Game/IW5/CommonIW5Tests.cpp b/test/CommonTests/Game/IW5/CommonIW5Tests.cpp new file mode 100644 index 00000000..804a8017 --- /dev/null +++ b/test/CommonTests/Game/IW5/CommonIW5Tests.cpp @@ -0,0 +1,49 @@ +#include "Game/IW5/CommonIW5.h" + +#include +#include + +TEST_CASE("IW5: Check checksums", "[iw5]") +{ + SECTION("for StringTable_HashString") + { + const auto [str, expectedHash] = GENERATE(Catch::Generators::table({ + {"hello world", 0x6aefe2c4}, + {"universe2", 0xe796fe8d}, + {"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", 0x16c4d3f1}, + {"AngularVelocityScale", 0x573e9feb}, + {"BakedLightingIntensity", 0x15af785c}, + {"Layer1OffsetBobbleDelay", 0xfb83324a}, + {"MaxDepth", 0x1892e3df}, + {"MomentumColor", 0x5e7abdcb}, + {"SparkleScale", 0x9e4409f4}, + {"TickMarkColorAndHarshness", 0x5dfe4c47}, + {"worldViewProjectionMatrix", 0xa2501a07}, + })); + + CAPTURE(str); + const auto hash = IW5::Common::StringTable_HashString(str); + REQUIRE(hash == expectedHash); + } + + SECTION("for R_HashString") + { + const auto [str, expectedHash] = GENERATE(Catch::Generators::table({ + {"hello world", 0xe7d74060}, + {"universe2", 0x113fdcd7}, + {"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", 0xdd0153c5}, + {"AngularVelocityScale", 0x18f2cb6d}, + {"BakedLightingIntensity", 0xd627f218}, + {"Layer1OffsetBobbleDelay", 0xcd91b6ae}, + {"MaxDepth", 0x61ed5959}, + {"MomentumColor", 0xc80f3595}, + {"SparkleScale", 0x5488816a}, + {"TickMarkColorAndHarshness", 0xd6c718bd}, + {"worldViewProjectionMatrix", 0x7f661409}, + })); + + CAPTURE(str); + const auto hash = IW5::Common::R_HashString(str); + REQUIRE(hash == expectedHash); + } +} diff --git a/test/CommonTests/Game/T5/CommonT5Tests.cpp b/test/CommonTests/Game/T5/CommonT5Tests.cpp new file mode 100644 index 00000000..f80bde83 --- /dev/null +++ b/test/CommonTests/Game/T5/CommonT5Tests.cpp @@ -0,0 +1,70 @@ +#include "Game/T5/CommonT5.h" + +#include +#include + +TEST_CASE("T5: Check checksums", "[t5]") +{ + SECTION("for Com_HashKey") + { + const auto [str, expectedHash] = GENERATE(Catch::Generators::table({ + {"hello world", 0x21c3f}, + {"universe2", 0x1be55}, + {"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", 0x56e0f}, + {"AngularVelocityScale", 0x4043a}, + {"BakedLightingIntensity", 0x47a5c}, + {"Layer1OffsetBobbleDelay", 0x47950}, + {"MaxDepth", 0x17ca1}, + {"MomentumColor", 0x2998e}, + {"SparkleScale", 0x24cea}, + {"TickMarkColorAndHarshness", 0x50d40}, + {"worldViewProjectionMatrix", 0x548c4}, + })); + + CAPTURE(str); + const auto hash = T5::Common::Com_HashKey(str, 64); + REQUIRE(hash == expectedHash); + } + + SECTION("for Com_HashString") + { + const auto [str, expectedHash] = GENERATE(Catch::Generators::table({ + {"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 = T5::Common::Com_HashString(str); + REQUIRE(hash == expectedHash); + } + + SECTION("for R_HashString") + { + const auto [str, expectedHash] = GENERATE(Catch::Generators::table({ + {"hello world", 0xe7d74060}, + {"universe2", 0x113fdcd7}, + {"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", 0xdd0153c5}, + {"AngularVelocityScale", 0x18f2cb6d}, + {"BakedLightingIntensity", 0xd627f218}, + {"Layer1OffsetBobbleDelay", 0xcd91b6ae}, + {"MaxDepth", 0x61ed5959}, + {"MomentumColor", 0xc80f3595}, + {"SparkleScale", 0x5488816a}, + {"TickMarkColorAndHarshness", 0xd6c718bd}, + {"worldViewProjectionMatrix", 0x7f661409}, + })); + + CAPTURE(str); + const auto hash = T5::Common::R_HashString(str); + REQUIRE(hash == expectedHash); + } +} diff --git a/test/CommonTests/Game/T6/CommonT6Tests.cpp b/test/CommonTests/Game/T6/CommonT6Tests.cpp new file mode 100644 index 00000000..872220bb --- /dev/null +++ b/test/CommonTests/Game/T6/CommonT6Tests.cpp @@ -0,0 +1,91 @@ +#include "Game/T6/CommonT6.h" + +#include +#include + +TEST_CASE("T6: Check checksums", "[t6]") +{ + SECTION("for Com_HashKey") + { + const auto [str, expectedHash] = GENERATE(Catch::Generators::table({ + {"hello world", 0x21c3f}, + {"universe2", 0x1be55}, + {"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", 0x56e0f}, + {"AngularVelocityScale", 0x4043a}, + {"BakedLightingIntensity", 0x47a5c}, + {"Layer1OffsetBobbleDelay", 0x47950}, + {"MaxDepth", 0x17ca1}, + {"MomentumColor", 0x2998e}, + {"SparkleScale", 0x24cea}, + {"TickMarkColorAndHarshness", 0x50d40}, + {"worldViewProjectionMatrix", 0x548c4}, + })); + + CAPTURE(str); + const auto hash = T6::Common::Com_HashKey(str, 64); + REQUIRE(hash == expectedHash); + } + + SECTION("for Com_HashString") + { + const auto [str, expectedHash] = GENERATE(Catch::Generators::table({ + {"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 = T6::Common::Com_HashString(str); + REQUIRE(hash == expectedHash); + } + + SECTION("for R_HashString") + { + const auto [str, expectedHash] = GENERATE(Catch::Generators::table({ + {"hello world", 0xe7d74060}, + {"universe2", 0x113fdcd7}, + {"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", 0xdd0153c5}, + {"AngularVelocityScale", 0x18f2cb6d}, + {"BakedLightingIntensity", 0xd627f218}, + {"Layer1OffsetBobbleDelay", 0xcd91b6ae}, + {"MaxDepth", 0x61ed5959}, + {"MomentumColor", 0xc80f3595}, + {"SparkleScale", 0x5488816a}, + {"TickMarkColorAndHarshness", 0xd6c718bd}, + {"worldViewProjectionMatrix", 0x7f661409}, + })); + + CAPTURE(str); + const auto hash = T6::Common::R_HashString(str); + REQUIRE(hash == expectedHash); + } + + SECTION("for SND_HashName") + { + const auto [str, expectedHash] = GENERATE(Catch::Generators::table({ + {"hello world", 0x9e420d7f}, + {"universe2", 0xee605328}, + {"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", 0xc38575b6}, + {"AngularVelocityScale", 0x0c032bd0}, + {"BakedLightingIntensity", 0x57f1a221}, + {"Layer1OffsetBobbleDelay", 0x4f640b85}, + {"MaxDepth", 0x50d1cae4}, + {"MomentumColor", 0xbcab7666}, + {"SparkleScale", 0x162259d9}, + {"TickMarkColorAndHarshness", 0xbf3d7a42}, + {"worldViewProjectionMatrix", 0x28db35e2}, + })); + + CAPTURE(str); + const auto hash = T6::Common::SND_HashName(str); + REQUIRE(hash == expectedHash); + } +} diff --git a/test/CommonTests/Utils/Djb2Tests.cpp b/test/CommonTests/Utils/Djb2Tests.cpp new file mode 100644 index 00000000..f7e0aa8c --- /dev/null +++ b/test/CommonTests/Utils/Djb2Tests.cpp @@ -0,0 +1,133 @@ +#include "Utils/Djb2.h" + +#include +#include + +TEST_CASE("Djb2: Check checksums", "[djb2]") +{ + SECTION("for djb2") + { + const auto [str, expectedHash] = GENERATE(Catch::Generators::table({ + {"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({ + {"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({ + {"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({ + {"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({ + {"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({ + {"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); + } +}