Repo actions & fixes

This commit is contained in:
rei-kes 2024-05-25 12:16:49 -04:00
parent 6decf28aaa
commit 4bf401a1ad
14 changed files with 352 additions and 34 deletions

6
.github/ISSUE_TEMPLATE vendored Normal file
View File

@ -0,0 +1,6 @@
<!--
If applicable, include the following:
Description of the issue
Config used
Steps to reproduce said issue
-->

BIN
.github/assets/download.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

11
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "nuget" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

36
.github/workflows/msbuild.yml vendored Normal file
View File

@ -0,0 +1,36 @@
name: MSBuild
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
env:
SOLUTION_FILE_PATH: .
BUILD_PLATFORM: x64
BUILD_CONFIGURATION: Release
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- name: Add MSBuild to PATH
uses: microsoft/setup-msbuild@v1.1
- name: Restore NuGet packages
working-directory: ${{env.GITHUB_WORKSPACE}}
run: nuget restore ${{env.SOLUTION_FILE_PATH}}
- name: Build
working-directory: ${{env.GITHUB_WORKSPACE}}
run: msbuild /m /p:Platform=${{env.BUILD_PLATFORM}} /p:Configuration=${{env.BUILD_CONFIGURATION}} ${{env.SOLUTION_FILE_PATH}}
- uses: actions/upload-artifact@v3
with:
name: Amalgam
path: ${{env.SOLUTION_FILE_PATH}}/output/${{env.BUILD_PLATFORM}}/${{env.BUILD_CONFIGURATION}}/*.dll

View File

@ -243,7 +243,7 @@
<ClCompile Include="src\Features\EnginePrediction\EnginePrediction.cpp" /> <ClCompile Include="src\Features\EnginePrediction\EnginePrediction.cpp" />
<ClCompile Include="src\Features\ImGui\Render.cpp" /> <ClCompile Include="src\Features\ImGui\Render.cpp" />
<ClCompile Include="src\Features\ImGui\Menu\Menu.cpp" /> <ClCompile Include="src\Features\ImGui\Menu\Menu.cpp" />
<ClCompile Include="src\Features\Logs\Logs.cpp" /> <ClCompile Include="src\Features\Records\Records.cpp" />
<ClCompile Include="src\Features\Misc\Misc.cpp" /> <ClCompile Include="src\Features\Misc\Misc.cpp" />
<ClCompile Include="src\Features\NetworkFix\NetworkFix.cpp" /> <ClCompile Include="src\Features\NetworkFix\NetworkFix.cpp" />
<ClCompile Include="src\Features\NoSpread\NoSpread.cpp" /> <ClCompile Include="src\Features\NoSpread\NoSpread.cpp" />
@ -494,7 +494,7 @@
<ClInclude Include="src\Features\ImGui\MaterialDesign\MaterialIcons.h" /> <ClInclude Include="src\Features\ImGui\MaterialDesign\MaterialIcons.h" />
<ClInclude Include="src\Features\ImGui\Menu\Components.h" /> <ClInclude Include="src\Features\ImGui\Menu\Components.h" />
<ClInclude Include="src\Features\ImGui\Menu\Menu.h" /> <ClInclude Include="src\Features\ImGui\Menu\Menu.h" />
<ClInclude Include="src\Features\Logs\Logs.h" /> <ClInclude Include="src\Features\Records\Records.h" />
<ClInclude Include="src\Features\Players\PlayerCore.h" /> <ClInclude Include="src\Features\Players\PlayerCore.h" />
<ClInclude Include="src\Features\Players\PlayerUtils.h" /> <ClInclude Include="src\Features\Players\PlayerUtils.h" />
<ClInclude Include="src\Features\Visuals\Materials\Materials.h" /> <ClInclude Include="src\Features\Visuals\Materials\Materials.h" />

View File

@ -6,7 +6,7 @@
<ClCompile Include="src\Features\Conditions\Conditions.cpp" /> <ClCompile Include="src\Features\Conditions\Conditions.cpp" />
<ClCompile Include="src\Features\Configs\Configs.cpp" /> <ClCompile Include="src\Features\Configs\Configs.cpp" />
<ClCompile Include="src\Features\ImGui\Menu\Menu.cpp" /> <ClCompile Include="src\Features\ImGui\Menu\Menu.cpp" />
<ClCompile Include="src\Features\Logs\Logs.cpp" /> <ClCompile Include="src\Features\Records\Records.cpp" />
<ClCompile Include="src\Features\Players\PlayerCore.cpp" /> <ClCompile Include="src\Features\Players\PlayerCore.cpp" />
<ClCompile Include="src\Features\Players\PlayerUtils.cpp" /> <ClCompile Include="src\Features\Players\PlayerUtils.cpp" />
<ClCompile Include="src\Features\Visuals\Materials\Materials.cpp" /> <ClCompile Include="src\Features\Visuals\Materials\Materials.cpp" />
@ -174,7 +174,7 @@
<ClInclude Include="src\Features\ImGui\MaterialDesign\MaterialIcons.h" /> <ClInclude Include="src\Features\ImGui\MaterialDesign\MaterialIcons.h" />
<ClInclude Include="src\Features\ImGui\Menu\Components.h" /> <ClInclude Include="src\Features\ImGui\Menu\Components.h" />
<ClInclude Include="src\Features\ImGui\Menu\Menu.h" /> <ClInclude Include="src\Features\ImGui\Menu\Menu.h" />
<ClInclude Include="src\Features\Logs\Logs.h" /> <ClInclude Include="src\Features\Records\Records.h" />
<ClInclude Include="src\Features\Players\PlayerCore.h" /> <ClInclude Include="src\Features\Players\PlayerCore.h" />
<ClInclude Include="src\Features\Players\PlayerUtils.h" /> <ClInclude Include="src\Features\Players\PlayerUtils.h" />
<ClInclude Include="src\Features\Visuals\Materials\Materials.h" /> <ClInclude Include="src\Features\Visuals\Materials\Materials.h" />

View File

@ -1,7 +1,7 @@
#include "CheaterDetection.h" #include "CheaterDetection.h"
#include "../Players/PlayerUtils.h" #include "../Players/PlayerUtils.h"
#include "../Logs/Logs.h" #include "../Records/Records.h"
bool CCheaterDetection::ShouldScan() bool CCheaterDetection::ShouldScan()
{ {
@ -82,7 +82,7 @@ void CCheaterDetection::Infract(CTFPlayer* pEntity, std::string sReason)
mData[pEntity].iDetections++; mData[pEntity].iDetections++;
const bool bMark = mData[pEntity].iDetections >= Vars::CheaterDetection::DetectionsRequired.Value; const bool bMark = mData[pEntity].iDetections >= Vars::CheaterDetection::DetectionsRequired.Value;
F::Logs.CheatDetection(mData[pEntity].sName, bMark ? "marked" : "infracted", sReason); F::Records.CheatDetection(mData[pEntity].sName, bMark ? "marked" : "infracted", sReason);
if (bMark) if (bMark)
{ {
mData[pEntity].iDetections = 0; mData[pEntity].iDetections = 0;

View File

@ -1,7 +1,7 @@
#include "PlayerUtils.h" #include "PlayerUtils.h"
#include "../../SDK/Definitions/Types.h" #include "../../SDK/Definitions/Types.h"
#include "../Logs/Logs.h" #include "../Records/Records.h"
uint32_t GetFriendsID(int iIndex) uint32_t GetFriendsID(int iIndex)
{ {
@ -40,7 +40,7 @@ void CPlayerlistUtils::AddTag(uint32_t friendsID, std::string sTag, bool bSave,
{ {
PriorityLabel_t plTag; PriorityLabel_t plTag;
if (GetTag(sTag, &plTag)) if (GetTag(sTag, &plTag))
F::Logs.TagsChanged(sName, "Added", plTag.Color.ToHexA(), sTag); F::Records.TagsChanged(sName, "Added", plTag.Color.ToHexA(), sTag);
} }
} }
} }
@ -67,7 +67,7 @@ void CPlayerlistUtils::RemoveTag(uint32_t friendsID, std::string sTag, bool bSav
{ {
PriorityLabel_t plTag; PriorityLabel_t plTag;
if (GetTag(sTag, &plTag)) if (GetTag(sTag, &plTag))
F::Logs.TagsChanged(sName, "Removed", plTag.Color.ToHexA(), sTag); F::Records.TagsChanged(sName, "Removed", plTag.Color.ToHexA(), sTag);
} }
break; break;
} }

View File

@ -0,0 +1,255 @@
#include "Records.h"
#include "../Visuals/Notifications/Notifications.h"
#include "../Players/PlayerUtils.h"
static std::string white({ '\x7', 'F', 'F', 'F', 'F', 'F', 'F' }); //FFFFFF
static std::string red({ '\x7', 'F', 'F', '3', 'A', '3', 'A' }); //FF3A3A
static std::string green({ '\x7', '3', 'A', 'F', 'F', '4', 'D' }); //3AFF4D
static std::string blue({ '\x7', '0', 'D', '9', '2', 'F', 'F' }); //0D92FF
static std::string yellow({ '\x7', 'C', '8', 'A', '9', '0', '0' }); //C8A900
static std::string gsred({ '\x7', '7', '5', '7', '5', '7', '5' }); //757575
static std::string gsgreen({ '\x7', 'B', '0', 'B', '0', 'B', '0' }); //b0b0b0
static std::string gsblue({ '\x7', '7', '6', '7', '6', '7', '6' }); //767676
static std::string gsyellow({ '\x7', 'C', 'A', 'C', 'A', 'C', 'A' }); //CACACA
void CRecords::OutputInfo(int flags, std::string name, std::string string, std::string cstring)
{
if (flags & 1 << 0)
F::Notifications.Add(string);
if (flags & 1 << 1)
I::ClientModeShared->m_pChatElement->ChatPrintf(0, std::format("{}{}\x1 {}", Vars::Menu::Theme::Accent.Value.ToHex(), Vars::Menu::CheatPrefix.Value, cstring).c_str());
if (flags & 1 << 2)
I::EngineClient->ClientCmd_Unrestricted(std::format("tf_party_chat \"{}\"", string).c_str());
if (flags & 1 << 3)
SDK::Output(name.c_str(), string.c_str(), Vars::Menu::Theme::Accent.Value);
}
// Event info
void CRecords::Event(IGameEvent* pEvent, const FNV1A_t uHash, CTFPlayer* pLocal)
{
if (uHash == FNV1A::HashConst("game_newmap"))
{
bTagsOnJoin = true;
return;
}
if (!I::EngineClient->IsConnected() || !I::EngineClient->IsInGame() || !pLocal)
return;
switch (uHash)
{
case FNV1A::HashConst("vote_cast"): // Voting
{
if (!(Vars::Logging::Logs.Value & 1 << 1))
return;
const int iIndex = pEvent->GetInt("entityid");
const auto pEntity = I::ClientEntityList->GetClientEntity(iIndex);
if (!pEntity || pEntity->GetClassID() != ETFClassID::CTFPlayer)
return;
const bool bVotedYes = pEvent->GetInt("vote_option") == 0;
const bool bSameTeam = pEntity->As<CTFPlayer>()->m_iTeamNum() == pLocal->m_iTeamNum();
PlayerInfo_t pi{};
if (!I::EngineClient->GetPlayerInfo(iIndex, &pi))
return;
std::string string = std::format("{}{} voted {}", (bSameTeam ? "" : "[Enemy] "), (pi.name), (bVotedYes ? "Yes" : "No"));
std::string cstring = std::format("{}{}{}\x1 voted {}{}", (bSameTeam ? "" : "[Enemy] "), (yellow), (pi.name), (bVotedYes ? green : red), (bVotedYes ? "Yes" : "No"));
OutputInfo(Vars::Logging::VoteCast::LogTo.Value, "Vote Cast", string, cstring);
return;
}
case FNV1A::HashConst("player_changeclass"): // Class change
{
if (!(Vars::Logging::Logs.Value & 1 << 2))
return;
const int iIndex = I::EngineClient->GetPlayerForUserID(pEvent->GetInt("userid"));
const auto pEntity = I::ClientEntityList->GetClientEntity(iIndex);
if (!pEntity || iIndex == pLocal->entindex())
return;
const bool bSameTeam = pEntity->As<CTFPlayer>()->m_iTeamNum() == pLocal->m_iTeamNum();
PlayerInfo_t pi{};
if (!I::EngineClient->GetPlayerInfo(iIndex, &pi) || pi.fakeplayer)
return; // dont spam chat by giving class changes for bots
std::string string = std::format("{}{} changed class to {}", (bSameTeam ? "" : "[Enemy] "), (pi.name), (SDK::GetClassByIndex(pEvent->GetInt("class"))));
std::string cstring = std::format("{}{}{}\x1 changed class to {}{}", (bSameTeam ? "" : "[Enemy] "), (yellow), (pi.name), (yellow), (SDK::GetClassByIndex(pEvent->GetInt("class"))));
OutputInfo(Vars::Logging::ClassChange::LogTo.Value, "Class Change", string, cstring);
return;
}
case FNV1A::HashConst("player_hurt"): // Damage
{
if (!(Vars::Logging::Logs.Value & 1 << 3))
return;
const int iIndex = I::EngineClient->GetPlayerForUserID(pEvent->GetInt("userid"));
const auto pEntity = I::ClientEntityList->GetClientEntity(iIndex);
if (!pEntity || iIndex == pLocal->entindex())
return;
const int nAttacker = pEvent->GetInt("attacker");
const int nHealth = pEvent->GetInt("health");
const int nDamage = pEvent->GetInt("damageamount");
const bool bCrit = pEvent->GetBool("crit");
const int maxHealth = pEntity->As<CTFPlayer>()->m_iMaxHealth();
PlayerInfo_t pi{};
if (!I::EngineClient->GetPlayerInfo(I::EngineClient->GetLocalPlayer(), &pi) || nAttacker != pi.userID ||
!I::EngineClient->GetPlayerInfo(iIndex, &pi))
return;
std::string string = std::format("You hit {} for {} damage{}({} / {})", (pi.name), (nDamage), (bCrit ? " (crit) " : " "), (nHealth), (maxHealth));
std::string cstring = std::format("You hit {}{}\x1 for {}{} damage{}{}({} / {})", (yellow), (pi.name), (red), (nDamage), (bCrit ? " (crit) " : " "), (yellow), (nHealth), (maxHealth));
OutputInfo(Vars::Logging::Damage::LogTo.Value, "Damage", string, cstring);
return;
}
case FNV1A::HashConst("player_activate"): // Tags (player join)
{
if (!(Vars::Logging::Logs.Value & 1 << 5) || bTagsOnJoin)
return;
const int iIndex = I::EngineClient->GetPlayerForUserID(pEvent->GetInt("userid"));
if (iIndex == pLocal->entindex())
return;
PlayerInfo_t pi{};
if (!I::EngineClient->GetPlayerInfo(iIndex, &pi) || pi.fakeplayer)
return;
TagsOnJoin(pi.name, pi.friendsID);
return;
}
case FNV1A::HashConst("player_spawn"): // Tags (local join)
{
if (!(Vars::Logging::Logs.Value & 1 << 5) || !bTagsOnJoin)
return;
const int iIndex = I::EngineClient->GetPlayerForUserID(pEvent->GetInt("userid"));
if (iIndex != pLocal->entindex())
return;
bTagsOnJoin = false;
for (int n = 1; n <= I::EngineClient->GetMaxClients(); n++)
{
PlayerInfo_t pi{};
if (n == pLocal->entindex() || !I::EngineClient->GetPlayerInfo(n, &pi) || pi.fakeplayer)
continue;
TagsOnJoin(pi.name, pi.friendsID);
}
}
}
}
// Vote start
void CRecords::UserMessage(bf_read& msgData)
{
if (!(Vars::Logging::Logs.Value & 1 << 0))
return;
auto pLocal = H::Entities.GetLocal();
if (!pLocal)
return;
const int team = msgData.ReadByte();
const int voteID = msgData.ReadLong();
const int caller = msgData.ReadByte();
char reason[64], voteTarget[64];
msgData.ReadString(reason, 64);
msgData.ReadString(voteTarget, 64);
const int target = static_cast<unsigned char>(msgData.ReadByte()) >> 1;
const bool bSameTeam = team == pLocal->m_iTeamNum();
PlayerInfo_t piTarget{}, piCaller{};
if (!caller || !target || !I::EngineClient->GetPlayerInfo(target, &piTarget) || !I::EngineClient->GetPlayerInfo(caller, &piCaller))
return;
std::string string = std::format("{}{} called a vote on {}", (bSameTeam ? "" : "[Enemy] "), (piCaller.name), (piTarget.name));
std::string cstring = std::format("{}{}{}\x1 called a vote on {}{}", (bSameTeam ? "" : "[Enemy] "), (yellow), (piCaller.name), (yellow), (piTarget.name));
OutputInfo(Vars::Logging::VoteStart::LogTo.Value, "Vote Start", string, cstring);
}
// Cheat detection
void CRecords::CheatDetection(std::string name, std::string action, std::string reason)
{
if (!(Vars::Logging::Logs.Value & 1 << 4))
return;
std::string string = std::format("{} {} for {}", (name), (action), (reason));
std::string cstring = std::format("{}{}\x1 {} for {}{}", (yellow), (name), (action), (yellow), (reason));
OutputInfo(Vars::Logging::CheatDetection::LogTo.Value, "Cheat Detection", string, cstring);
}
// Tags
void CRecords::TagsOnJoin(std::string name, uint32_t friendsID)
{
const auto& vTags = F::PlayerUtils.mPlayerTags[friendsID];
std::vector<std::pair<std::string, std::string>> vColorsTags = {};
for (auto& sTag : vTags)
{
PriorityLabel_t lbTag;
if (!F::PlayerUtils.GetTag(sTag, &lbTag))
continue;
vColorsTags.push_back({ lbTag.Color.ToHexA(), sTag });
}
std::string tagtext, ctagtext;
switch (vColorsTags.size())
{
case 0: return;
case 1:
{
auto& pColorTag = *vColorsTags.begin();
tagtext = pColorTag.second;
ctagtext = std::format("{}{}", pColorTag.first, pColorTag.second);
break;
}
case 2:
{
auto& pColorTag1 = *vColorsTags.begin(), &pColorTag2 = *(vColorsTags.begin() + 1);
tagtext = std::format("{} and ", pColorTag1.second, pColorTag2.second);
ctagtext = std::format("{}{}\x1 and {}{}", pColorTag1.first, pColorTag1.second, pColorTag2.first, pColorTag2.second);
break;
}
default:
{
for (auto it = vColorsTags.begin(); it != vColorsTags.end(); it++)
{
auto& pColorTag = *it;
if (it + 1 != vColorsTags.end())
{
tagtext += std::format("{}, ", pColorTag.second);
ctagtext += std::format("{}{}\x1, ", pColorTag.first, pColorTag.second);
}
else
{
tagtext += std::format("and {}", pColorTag.second);
ctagtext += std::format("and {}{}", pColorTag.first, pColorTag.second);
}
}
}
}
std::string string = std::format("{} has the {} {}", (name), (vColorsTags.size() == 1 ? "tag" : "tags"), (tagtext));
std::string cstring = std::format("{}{}\x1 has the {} {}", (yellow), (name), (vColorsTags.size() == 1 ? "tag" : "tags"), (ctagtext));
OutputInfo(Vars::Logging::Tags::LogTo.Value, "Tags", string, cstring);
}
void CRecords::TagsChanged(std::string name, std::string action, std::string color, std::string tag)
{
if (!(Vars::Logging::Logs.Value & 1 << 5))
return;
auto uHash = FNV1A::Hash(action.c_str());
std::string string = std::format("{} tag {} {} {}", (action), (tag), (uHash == FNV1A::HashConst("Added") ? "to" : "from"), (name));
std::string cstring = std::format("{} tag {}{}\x1 {} {}{}", (action), (color), (tag), (uHash == FNV1A::HashConst("Added") ? "to" : "from"), (yellow), (name));
OutputInfo(Vars::Logging::Tags::LogTo.Value, "Tags", string, cstring);
}

View File

@ -0,0 +1,18 @@
#pragma once
#include "../../SDK/SDK.h"
class CRecords
{
void OutputInfo(int flags, std::string name, std::string string, std::string cstring);
void TagsOnJoin(std::string name, uint32_t friendsID);
bool bTagsOnJoin = false;
public:
void Event(IGameEvent* pEvent, FNV1A_t uHash, CTFPlayer* pLocal);
void UserMessage(bf_read& msgData);
void CheatDetection(std::string name, std::string action, std::string reason);
void TagsChanged(std::string name, std::string action, std::string color, std::string tag);
};
ADD_FEATURE(CRecords, Records)

View File

@ -1,7 +1,7 @@
#include "../SDK/SDK.h" #include "../SDK/SDK.h"
#include "../Features/Misc/Misc.h" #include "../Features/Misc/Misc.h"
#include "../Features/Logs/Logs.h" #include "../Features/Records/Records.h"
#include "../Features/NoSpread/NoSpreadHitscan/NoSpreadHitscan.h" #include "../Features/NoSpread/NoSpreadHitscan/NoSpreadHitscan.h"
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
@ -16,7 +16,7 @@ MAKE_HOOK(BaseClientDLL_DispatchUserMessage, U::Memory.GetVFunc(I::BaseClientDLL
switch (type) switch (type)
{ {
case VoteStart: case VoteStart:
F::Logs.UserMessage(msgData); F::Records.UserMessage(msgData);
break; break;
/* /*

View File

@ -3,7 +3,7 @@
#include "../Features/Backtrack/Backtrack.h" #include "../Features/Backtrack/Backtrack.h"
#include "../Features/CheaterDetection/CheaterDetection.h" #include "../Features/CheaterDetection/CheaterDetection.h"
#include "../Features/CritHack/CritHack.h" #include "../Features/CritHack/CritHack.h"
#include "../Features/Logs/Logs.h" #include "../Features/Records/Records.h"
#include "../Features/Misc/Misc.h" #include "../Features/Misc/Misc.h"
#include "../Features/PacketManip/AntiAim/AntiAim.h" #include "../Features/PacketManip/AntiAim/AntiAim.h"
#include "../Features/Resolver/Resolver.h" #include "../Features/Resolver/Resolver.h"
@ -22,7 +22,7 @@ MAKE_HOOK(CGameEventManager_FireEventIntern, S::CGameEventManager_FireEventInter
auto pLocal = H::Entities.GetLocal(); auto pLocal = H::Entities.GetLocal();
auto uHash = FNV1A::Hash(pEvent->GetName()); auto uHash = FNV1A::Hash(pEvent->GetName());
F::Logs.Event(pEvent, uHash, pLocal); F::Records.Event(pEvent, uHash, pLocal);
F::CritHack.Event(pEvent, uHash, pLocal); F::CritHack.Event(pEvent, uHash, pLocal);
F::Misc.Event(pEvent, uHash); F::Misc.Event(pEvent, uHash);
switch (uHash) switch (uHash)

13
LICENSE.md Normal file
View File

@ -0,0 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) [year] [fullname]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.