Merge branch 'develop_official' into feature/xinput

This commit is contained in:
Louvenarde 2021-08-14 00:36:42 +02:00
commit ea926dfe67
28 changed files with 5814 additions and 217 deletions

17
.github/workflows/discord-notify.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Notify Discord
on:
push:
branches:
- "*"
issues:
jobs:
notify:
runs-on: ubuntu-latest
if: github.repository_owner == 'XLabsProject'
steps:
- name: Send notification to Discord
uses: Ilshidur/action-discord@master
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_CI_BOT_WEBHOOK }}

View File

@ -40,6 +40,29 @@
| `--disable-bitmessage` | Disable use of BitMessage completely. | | `--disable-bitmessage` | Disable use of BitMessage completely. |
| `--disable-base128` | Disable base128 encoding for minidumps. | | `--disable-base128` | Disable base128 encoding for minidumps. |
| `--no-new-structure` | Do not use new virtual path structure (separating headers and source files). | | `--no-new-structure` | Do not use new virtual path structure (separating headers and source files). |
| `--iw4x-zones` | Zonebuilder generates iw4x zones that cannot be loaded without IW4x specific patches. |
## Command line arguments
| Argument | Description |
|:----------------------------|:-----------------------------------------------|
| `-tests` | Perform unit tests. |
| `-entries` | Prints fast file info to the console. |
| `-stdout` | Redirect stdout to the external console. |
| `-console` | Enables external console. |
| `-dedicated` | Dedicated server. |
| `-scriptablehttp` | Adds HTTP console commands. |
| `-bigdumps` | Enables dumps. |
| `-reallybigdumps` | Unused. |
| `-bigminidumps` | Mini dumps. |
| `-reallybigminidumps` | Big mini dumps. |
| `-dump` | Prints asset info to a .ents file. |
| `-monitor` | Enables monitor. |
| `-nointro` | Skips game's intro. |
| `-version` | Prints IW4X version. |
| `-zonebuilder` | Enables zone builder. |
| `-nosteam` | Disables Steam features. |
## Disclaimer ## Disclaimer

5011
lib/include/stb_truetype.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -74,6 +74,11 @@ newoption {
description = "Upload minidumps even for Debug builds." description = "Upload minidumps even for Debug builds."
} }
newoption {
trigger = "iw4x-zones",
description = "Zonebuilder generates iw4x zones that cannot be loaded without IW4x specific patches."
}
newaction { newaction {
trigger = "version", trigger = "version",
description = "Returns the version string for the current commit of the source code.", description = "Returns the version string for the current commit of the source code.",
@ -324,6 +329,9 @@ workspace "iw4x"
if _OPTIONS["force-exception-handler"] then if _OPTIONS["force-exception-handler"] then
defines { "FORCE_EXCEPTION_HANDLER" } defines { "FORCE_EXCEPTION_HANDLER" }
end end
if _OPTIONS["iw4x-zones"] then
defines { "GENERATE_IW4X_SPECIFIC_ZONES" }
end
-- Pre-compiled header -- Pre-compiled header
pchheader "STDInclude.hpp" -- must be exactly same as used in #include directives pchheader "STDInclude.hpp" -- must be exactly same as used in #include directives

View File

@ -286,7 +286,7 @@ namespace Components
push [esp + 2Ch] push [esp + 2Ch]
push [esp + 2Ch] push [esp + 2Ch]
call AssetHandler::IsAssetEligible call AssetHandler::IsAssetEligible
add esp, 08h add esp, 8h
mov [esp + 20h], eax mov [esp + 20h], eax
popad popad
@ -295,13 +295,13 @@ namespace Components
test al, al test al, al
jz doNotLoad jz doNotLoad
mov eax, [esp + 8] mov eax, [esp + 8h]
sub esp, 14h sub esp, 14h
mov ecx, 5BB657h mov ecx, 5BB657h
jmp ecx jmp ecx
doNotLoad: doNotLoad:
mov eax, [esp + 8] mov eax, [esp + 8h]
retn retn
} }
} }

View File

@ -1,7 +1,82 @@
#include "STDInclude.hpp" #include "STDInclude.hpp"
#define STB_TRUETYPE_IMPLEMENTATION
#include <stb_truetype.h>
namespace Assets namespace Assets
{ {
namespace
{
int PackFonts(const uint8_t* data, std::vector<uint16_t>& charset, Game::Glyph* glyphs, float pixel_height, unsigned char* pixels, int pw, int ph, int yOffset)
{
stbtt_fontinfo f;
f.userdata = NULL;
if (!stbtt_InitFont(&f, data, 0))
return -1;
std::memset(pixels, 0, pw * ph);
int x = 1, y = 1, bottom_y = 1;
float scale = stbtt_ScaleForPixelHeight(&f, pixel_height);
int i = 0;
for (auto& ch : charset)
{
int advance, lsb, x0, y0, x1, y1, gw, gh;
int g = stbtt_FindGlyphIndex(&f, ch);
stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);
stbtt_GetGlyphBitmapBox(&f, g, scale, scale, &x0, &y0, &x1, &y1);
gw = x1 - x0;
gh = y1 - y0;
if (x + gw + 1 >= pw)
{
// Advance to next row
y = bottom_y;
x = 1;
}
if (y + gh + 1 >= ph)
{
// Check if we have ran out of the room
return -i;
}
stbtt_MakeGlyphBitmap(&f, pixels + x + y * pw, gw, gh, pw, scale, scale, g);
auto& glyph = glyphs[i++];
glyph.letter = ch;
glyph.s0 = x / static_cast<float>(pw);
glyph.s1 = (x + gw) / static_cast<float>(pw);
glyph.t0 = y / static_cast<float>(ph);
glyph.t1 = (y + gh) / static_cast<float>(ph);
glyph.pixelWidth = static_cast<char>(gw);
glyph.pixelHeight = static_cast<char>(gh);
glyph.x0 = static_cast<char>(x0);
glyph.y0 = static_cast<char>(y0 + yOffset);
glyph.dx = static_cast<char>(roundf(scale * advance));
// Advance to next col
x = x + gw + 1;
// Expand bottom of current row if current glyph is bigger
if (y + gh + 1 > bottom_y)
{
bottom_y = y + gh + 1;
}
}
return bottom_y;
}
}
void IFont_s::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) void IFont_s::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{ {
Game::Font_s *asset = header.font; Game::Font_s *asset = header.font;
@ -17,6 +92,157 @@ namespace Assets
} }
} }
void IFont_s::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone*)
{
Components::FileSystem::File fontDefFile(Utils::String::VA("%s.json", name.data()));
Components::FileSystem::File fontFile(Utils::String::VA("%s.ttf", name.data()));
if (fontDefFile.exists() && fontFile.exists())
{
std::string errors;
auto fontDef = json11::Json::parse(fontDefFile.getBuffer(), errors);
if (!errors.empty())
{
Components::Logger::Error("Font define %s is broken: %s.", name.data(), errors.data());
}
if (!fontDef.is_object())
{
Components::Logger::Error("Font define %s is invaild.", name.data(), errors.data());
}
int w = fontDef["textureWidth"].int_value();
int h = fontDef["textureHeight"].int_value();
int size = fontDef["size"].int_value();
int yOffset = fontDef["yOffset"].int_value();
uint8_t* pixels = Utils::Memory::AllocateArray<uint8_t>(w * h);
// Setup assets
auto* texName = Utils::Memory::DuplicateString(Utils::String::VA("if_%s", name.data() + 6 /* skip "fonts/" */));
auto* fontName = Utils::Memory::DuplicateString(name.data());
auto* glowMaterialName = Utils::Memory::DuplicateString(Utils::String::VA("%s_glow", name.data()));
auto* image = Utils::Memory::Duplicate(Game::DB_FindXAssetHeader(Game::ASSET_TYPE_IMAGE, "gamefonts_pc").image);
image->name = texName;
auto* material = Utils::Memory::Duplicate(Game::DB_FindXAssetHeader(Game::ASSET_TYPE_MATERIAL, "fonts/gamefonts_pc").material);
material->textureTable = Utils::Memory::Duplicate(material->textureTable);
material->textureTable->u.image = image;
material->info.name = fontName;
auto* glowMaterial = Utils::Memory::Duplicate(Game::DB_FindXAssetHeader(Game::ASSET_TYPE_MATERIAL, "fonts/gamefonts_pc_glow").material);
glowMaterial->textureTable = material->textureTable;
glowMaterial->info.name = glowMaterialName;
std::vector<uint16_t> charset;
if (fontDef["charset"].is_array())
{
for (auto& ch : fontDef["charset"].array_items())
charset.push_back(static_cast<uint16_t>(ch.int_value()));
// order matters
std::sort(charset.begin(), charset.end());
for (uint16_t i = 32; i < 128; i++)
{
if (std::find(charset.begin(), charset.end(), i) == charset.end())
{
Components::Logger::Error("Font %s missing codepoint %d.", name.data(), i);
}
}
}
else
{
for (uint16_t i = 32; i < 128; i++)
charset.push_back(i);
}
auto* font = Utils::Memory::Allocate<Game::Font_s>();
font->fontName = fontName;
font->pixelHeight = size;
font->material = material;
font->glowMaterial = glowMaterial;
font->glyphCount = charset.size();
font->glyphs = Utils::Memory::AllocateArray<Game::Glyph>(charset.size());
// Generate glyph data
int result = PackFonts(reinterpret_cast<const uint8_t*>(fontFile.getBuffer().data()), charset, font->glyphs, static_cast<float>(size), pixels, w, h, yOffset);
if (result == -1)
{
Components::Logger::Error("Truetype font %s is broken.", name.data());
}
else if (result < 0)
{
Components::Logger::Error("Texture size of font %s is not enough.", name.data());
}
else if(h - result > size)
{
Components::Logger::Print("Warn: Texture of font %s have too much left over space: %d\n", name.data(), h - result);
}
header->font = font;
// Save generated materials
Game::XAssetHeader tmpHeader;
tmpHeader.image = image;
Components::AssetHandler::StoreTemporaryAsset(Game::ASSET_TYPE_IMAGE, tmpHeader);
tmpHeader.material = material;
Components::AssetHandler::StoreTemporaryAsset(Game::ASSET_TYPE_MATERIAL, tmpHeader);
tmpHeader.material = glowMaterial;
Components::AssetHandler::StoreTemporaryAsset(Game::ASSET_TYPE_MATERIAL, tmpHeader);
// Save generated image
Utils::IO::CreateDir("userraw\\images");
int fileSize = w * h * 4;
int iwiHeaderSize = static_cast<int>(sizeof(Game::GfxImageFileHeader));
Game::GfxImageFileHeader iwiHeader =
{
{ 'I', 'W', 'i' },
/* version */
8,
/* flags */
2,
/* format */
Game::IMG_FORMAT_BITMAP_RGBA,
0,
/* dimensions(x, y, z) */
{ static_cast<short>(w), static_cast<short>(h), 1 },
/* fileSizeForPicmip (mipSize in bytes + sizeof(GfxImageFileHeader)) */
{ fileSize + iwiHeaderSize, fileSize, fileSize, fileSize }
};
std::string outIwi;
outIwi.resize(fileSize + sizeof(Game::GfxImageFileHeader));
std::memcpy(outIwi.data(), &iwiHeader, sizeof(Game::GfxImageFileHeader));
// Generate RGBA data
auto* rgbaPixels = outIwi.data() + sizeof(Game::GfxImageFileHeader);
for (int i = 0; i < w * h * 4; i += 4)
{
rgbaPixels[i + 0] = static_cast<char>(255);
rgbaPixels[i + 1] = static_cast<char>(255);
rgbaPixels[i + 2] = static_cast<char>(255);
rgbaPixels[i + 3] = static_cast<char>(pixels[i / 4]);
}
Utils::IO::WriteFile(Utils::String::VA("userraw\\images\\%s.iwi", texName), outIwi);
Utils::Memory::Free(pixels);
}
}
void IFont_s::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) void IFont_s::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{ {
AssertSize(Game::Font_s, 24); AssertSize(Game::Font_s, 24);

View File

@ -9,6 +9,6 @@ namespace Assets
virtual void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; virtual void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
virtual void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; virtual void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
// virtual void load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) override; virtual void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
}; };
} }

View File

@ -36,12 +36,13 @@ namespace Assets
{"effect_zfeather", "effect_zfeather_blend"}, {"effect_zfeather", "effect_zfeather_blend"},
{"wc_unlit_add", "wc_unlit_add_lin"}, {"wc_unlit_add", "wc_unlit_add_lin"},
{"wc_unlit_distfalloff", "wc_unlit_distfalloff_replace"},
{"wc_unlit_multiply", "wc_unlit_multiply_lin"}, {"wc_unlit_multiply", "wc_unlit_multiply_lin"},
{"wc_unlit_falloff_add", "wc_unlit_falloff_add_lin_ua"}, {"wc_unlit_falloff_add", "wc_unlit_falloff_add_lin_ua"},
{"wc_unlit", "wc_unlit_add_lin"}, {"wc_unlit", "wc_unlit_replace_lin"},
{"wc_unlit_alphatest", "wc_unlit_blend_lin_ua"}, {"wc_unlit_alphatest", "wc_unlit_blend_lin"},
{"wc_unlit_multiply_lin", "wc_unlit_multiply_lin"}, {"wc_unlit_multiply_lin", "wc_unlit_multiply_lin"},
{"wc_unlit_blend", "wc_unlit_blend_lin"}, {"wc_unlit_blend", "wc_unlit_blend_lin_ua"},
{"wc_unlit_replace", "wc_unlit_replace_lin"}, {"wc_unlit_replace", "wc_unlit_replace_lin"},
{"mc_unlit_replace", "mc_unlit_replace_lin"}, {"mc_unlit_replace", "mc_unlit_replace_lin"},
@ -83,38 +84,41 @@ namespace Assets
if (asset->techniqueSet) if (asset->techniqueSet)
{ {
std::string techset = reader.readString(); std::string techsetName = reader.readString();
if (!techset.empty() && techset.front() == ',') techset.erase(techset.begin()); if (!techsetName.empty() && techsetName.front() == ',') techsetName.erase(techsetName.begin());
asset->techniqueSet = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, techset.data(), builder).techniqueSet; asset->techniqueSet = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, techsetName.data(), builder).techniqueSet;
if (!asset->techniqueSet) if (!asset->techniqueSet)
{ {
// Workaround for effect techsets having _nofog suffix // Workaround for effect techsets having _nofog suffix
std::string suffix; std::string suffix;
if (Utils::String::StartsWith(techset, "effect_") && Utils::String::EndsWith(techset, "_nofog")) if (Utils::String::StartsWith(techsetName, "effect_") && Utils::String::EndsWith(techsetName, "_nofog"))
{ {
suffix = "_nofog"; suffix = "_nofog";
Utils::String::Replace(techset, suffix, ""); Utils::String::Replace(techsetName, suffix, "");
} }
for (int i = 0; i < ARRAYSIZE(techsetSuffix); ++i) for (int i = 0; i < ARRAYSIZE(techsetSuffix); ++i)
{ {
Game::MaterialTechniqueSet* techsetPtr = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, (techset + techsetSuffix[i] + suffix).data(), builder).techniqueSet; Game::MaterialTechniqueSet* techsetPtr = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, (techsetName + techsetSuffix[i] + suffix).data(), builder).techniqueSet;
if (techsetPtr) if (techsetPtr)
{ {
asset->techniqueSet = techsetPtr; asset->techniqueSet = techsetPtr;
if (asset->techniqueSet->name[0] == ',') continue; // Try to find a better one if (asset->techniqueSet->name[0] == ',') continue; // Try to find a better one
Components::Logger::Print("Techset '%s' has been mapped to '%s'\n", techset.data(), asset->techniqueSet->name); Components::Logger::Print("Techset '%s' has been mapped to '%s'\n", techsetName.data(), asset->techniqueSet->name);
break; break;
} }
} }
} }
else {
Components::Logger::Print("Techset %s exists with the same name in iw4, and was mapped 1:1 with %s\n", techsetName.data(), asset->techniqueSet->name);
}
if (!asset->techniqueSet) if (!asset->techniqueSet)
{ {
Components::Logger::Error("Missing techset: '%s' not found", techset.data()); Components::Logger::Error("Missing techset: '%s' not found", techsetName.data());
} }
} }
@ -254,7 +258,7 @@ namespace Assets
if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet) if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet)
{ {
Components::Logger::Print("Material %s with techset %s has been mapped to %s (last chance!)\n", asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name); Components::Logger::Print("Material %s with techset %s has been mapped to %s (last chance!), taking the sort key of material %s\n", asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name, header.material->info.name);
asset->info.sortKey = header.material->info.sortKey; asset->info.sortKey = header.material->info.sortKey;
asset->techniqueSet = iw4TechSet->asset.header.techniqueSet; asset->techniqueSet = iw4TechSet->asset.header.techniqueSet;

View File

@ -3,6 +3,10 @@
namespace Components namespace Components
{ {
Dvar::Var Colors::NewColors; Dvar::Var Colors::NewColors;
Dvar::Var Colors::ColorBlind;
Game::dvar_t* Colors::ColorAllyColorBlind;
Game::dvar_t* Colors::ColorEnemyColorBlind;
std::vector<DWORD> Colors::ColorTable; std::vector<DWORD> Colors::ColorTable;
DWORD Colors::HsvToRgb(Colors::HsvColor hsv) DWORD Colors::HsvToRgb(Colors::HsvColor hsv)
@ -218,8 +222,69 @@ namespace Components
} }
} }
// Patches team overhead normally
bool Colors::Dvar_GetUnpackedColorByName(const char* name, float* expandedColor)
{
if (Colors::ColorBlind.get<bool>())
{
const auto str = std::string(name);
if (str == "g_TeamColor_EnemyTeam")
{
// Dvar_GetUnpackedColor
auto* colorblindEnemy = Colors::ColorEnemyColorBlind->current.color;
expandedColor[0] = static_cast<float>(colorblindEnemy[0]) / 255.0f;
expandedColor[1] = static_cast<float>(colorblindEnemy[1]) / 255.0f;
expandedColor[2] = static_cast<float>(colorblindEnemy[2]) / 255.0f;
expandedColor[3] = static_cast<float>(colorblindEnemy[3]) / 255.0f;
return false;
}
else if (str == "g_TeamColor_MyTeam")
{
// Dvar_GetUnpackedColor
auto* colorblindAlly = Colors::ColorAllyColorBlind->current.color;
expandedColor[0] = static_cast<float>(colorblindAlly[0]) / 255.0f;
expandedColor[1] = static_cast<float>(colorblindAlly[1]) / 255.0f;
expandedColor[2] = static_cast<float>(colorblindAlly[2]) / 255.0f;
expandedColor[3] = static_cast<float>(colorblindAlly[3]) / 255.0f;
return false;
}
}
return true;
}
__declspec(naked) void Colors::GetUnpackedColorByNameStub()
{
__asm
{
push [esp + 8h]
push [esp + 8h]
call Colors::Dvar_GetUnpackedColorByName
add esp, 8h
test al, al
jnz continue
retn
continue:
push edi
mov edi, [esp + 8h]
push 406535h
retn
}
}
Colors::Colors() Colors::Colors()
{ {
// Add a colorblind mode for team colors
Colors::ColorBlind = Dvar::Register<bool>("r_colorBlindTeams", false, Game::dvar_flag::DVAR_FLAG_SAVED, "Use color-blindness-friendly colors for ingame team names");
// A dark red
Colors::ColorEnemyColorBlind = Game::Dvar_RegisterColor("g_ColorBlind_EnemyTeam", 0.659f, 0.088f, 0.145f, 1, Game::dvar_flag::DVAR_FLAG_SAVED, "Enemy team color for colorblind mode");
// A bright yellow
Colors::ColorAllyColorBlind = Game::Dvar_RegisterColor("g_ColorBlind_MyTeam", 1, 0.859f, 0.125f, 1, Game::dvar_flag::DVAR_FLAG_SAVED, "Ally team color for colorblind mode");
Utils::Hook(0x406530, Colors::GetUnpackedColorByNameStub, HOOK_JUMP).install()->quick();
// Disable SV_UpdateUserinfo_f, to block changing the name ingame // Disable SV_UpdateUserinfo_f, to block changing the name ingame
Utils::Hook::Set<BYTE>(0x6258D0, 0xC3); Utils::Hook::Set<BYTE>(0x6258D0, 0xC3);
@ -239,7 +304,7 @@ namespace Components
Utils::Hook(0x4AD470, Colors::CleanStrStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x4AD470, Colors::CleanStrStub, HOOK_JUMP).install()->quick();
// Register dvar // Register dvar
Colors::NewColors = Dvar::Register<bool>("cg_newColors", true, Game::dvar_flag::DVAR_FLAG_SAVED, "Use Warfare˛ color code style."); Colors::NewColors = Dvar::Register<bool>("cg_newColors", true, Game::dvar_flag::DVAR_FLAG_SAVED, "Use Warfare 2 color code style.");
Game::Dvar_RegisterColor("sv_customTextColor", 1, 0.7f, 0, 1, Game::dvar_flag::DVAR_FLAG_REPLICATED, "Color for the extended color code."); Game::Dvar_RegisterColor("sv_customTextColor", 1, 0.7f, 0, 1, Game::dvar_flag::DVAR_FLAG_REPLICATED, "Color for the extended color code.");
Dvar::Register<bool>("sv_allowColoredNames", true, Game::dvar_flag::DVAR_FLAG_NONE, "Allow colored names on the server"); Dvar::Register<bool>("sv_allowColoredNames", true, Game::dvar_flag::DVAR_FLAG_NONE, "Allow colored names on the server");

View File

@ -22,6 +22,9 @@ namespace Components
}; };
static Dvar::Var NewColors; static Dvar::Var NewColors;
static Dvar::Var ColorBlind;
static Game::dvar_t* ColorAllyColorBlind;
static Game::dvar_t* ColorEnemyColorBlind;
static DWORD HsvToRgb(HsvColor hsv); static DWORD HsvToRgb(HsvColor hsv);
@ -35,6 +38,8 @@ namespace Components
static void LookupColor(DWORD* color, char index); static void LookupColor(DWORD* color, char index);
static void LookupColorStub(); static void LookupColorStub();
static char* CleanStrStub(char* string); static char* CleanStrStub(char* string);
static bool Dvar_GetUnpackedColorByName(const char* name, float* expandedColor);
static void GetUnpackedColorByNameStub();
static std::vector<DWORD> ColorTable; static std::vector<DWORD> ColorTable;
}; };
} }

View File

@ -252,7 +252,7 @@ namespace Components
Game::TeleportPlayer(&Game::g_entities[clientNum], pos, orientation); Game::TeleportPlayer(&Game::g_entities[clientNum], pos, orientation);
// Logging that will spam the console and screen if people use cinematics //Logging that will spam the console and screen if people use cinematics
//Logger::Print("Successfully teleported player!\n"); //Logger::Print("Successfully teleported player!\n");
//Toast::Show("cardicon_abduction", "Success", "You have been teleported!", toastDurationShort); //Toast::Show("cardicon_abduction", "Success", "You have been teleported!", toastDurationShort);
}); });

View File

@ -193,6 +193,7 @@ namespace Components
"Killera", "Killera",
"Lithium", "Lithium",
"Louvenarde", "Louvenarde",
"FutureRave",
"OneFourOne", "OneFourOne",
"quaK", "quaK",
"RaidMax", "RaidMax",

View File

@ -60,7 +60,7 @@ namespace Components
UIScript::Add("visitWiki", [](UIScript::Token) UIScript::Add("visitWiki", [](UIScript::Token)
{ {
//Utils::OpenUrl(Utils::Cache::GetStaticUrl("/wiki/")); //Utils::OpenUrl(Utils::Cache::GetStaticUrl("/wiki/"));
Utils::OpenUrl("https://github.com/Jawesome99/IW4x/wiki"); Utils::OpenUrl("https://github.com/Emosewaj/IW4x/wiki");
}); });
UIScript::Add("visitDiscord", [](UIScript::Token) UIScript::Add("visitDiscord", [](UIScript::Token)

View File

@ -341,9 +341,6 @@ namespace Components
// original code // original code
mov eax, dword ptr[esp + 0xa0]; mov eax, dword ptr[esp + 0xa0];
jmp collide;
collide:
push 0x00478376; push 0x00478376;
retn; retn;
@ -374,11 +371,6 @@ namespace Components
// dont eject if g_playerEjection is set to 0 // dont eject if g_playerEjection is set to 0
je donteject; je donteject;
// original code
cmp dword ptr[ebx + 19ch], edi;
jle eject;
eject:
push 0x005d8152; push 0x005d8152;
retn; retn;
@ -388,12 +380,32 @@ namespace Components
} }
} }
template <typename T> std::function < T > ImportFunction(const std::string& dll, const std::string& function) BOOL QuickPatch::IsDynClassnameStub(char* a1)
{ {
auto dllHandle = GetModuleHandleA(&dll[0]); auto version = Zones::GetEntitiesZoneVersion();
auto procAddr = GetProcAddress(dllHandle, &function[0]);
return std::function < T >(reinterpret_cast<T*>(procAddr)); if (version >= VERSION_LATEST_CODO)
{
for (auto i = 0; i < Game::spawnVars->numSpawnVars; i++)
{
char** kvPair = Game::spawnVars->spawnVars[i];
auto key = kvPair[0];
auto val = kvPair[1];
bool isSpecOps = strncmp(key, "script_specialops", 17) == 0;
bool isSpecOpsOnly = val[0] == '1' && val[1] == '\0';
if (isSpecOps && isSpecOpsOnly)
{
// This will prevent spawning of any entity that contains "script_specialops: '1'"
// It removes extra hitboxes / meshes on 461+ CODO multiplayer maps
return TRUE;
}
}
}
// Passthrough to the game's own IsDynClassname
return Utils::Hook::Call<BOOL(char*)>(0x444810)(a1);
} }
QuickPatch::QuickPatch() QuickPatch::QuickPatch()
@ -407,38 +419,15 @@ namespace Components
// quit_hard // quit_hard
Command::Add("quit_hard", [](Command::Params*) Command::Add("quit_hard", [](Command::Params*)
{ {
typedef enum _HARDERROR_RESPONSE_OPTION { int data = false;
OptionAbortRetryIgnore, const Utils::Library ntdll("ntdll.dll");
OptionOk, ntdll.InvokePascal<void>("RtlAdjustPrivilege", 19, true, false, &data);
OptionOkCancel, ntdll.InvokePascal<void>("NtRaiseHardError", 0xC000007B, 0, nullptr, nullptr, 6, &data);
OptionRetryCancel,
OptionYesNo,
OptionYesNoCancel,
OptionShutdownSystem
} HARDERROR_RESPONSE_OPTION, *PHARDERROR_RESPONSE_OPTION;
typedef enum _HARDERROR_RESPONSE {
ResponseReturnToCaller,
ResponseNotHandled,
ResponseAbort,
ResponseCancel,
ResponseIgnore,
ResponseNo,
ResponseOk,
ResponseRetry,
ResponseYes
} HARDERROR_RESPONSE, *PHARDERROR_RESPONSE;
BOOLEAN hasPerms;
HARDERROR_RESPONSE response;
auto result = ImportFunction<NTSTATUS __stdcall(ULONG, BOOLEAN, BOOLEAN, PBOOLEAN)>("ntdll.dll", "RtlAdjustPrivilege")
(19, true, false, &hasPerms);
result = ImportFunction<NTSTATUS __stdcall(NTSTATUS, ULONG, LPCSTR, PVOID, HARDERROR_RESPONSE_OPTION, PHARDERROR_RESPONSE)>("ntdll.dll", "NtRaiseHardError")
(0xC000007B /*0x0000000A*/, 0, nullptr, nullptr, OptionShutdownSystem, &response);
}); });
// Filtering any mapents that is intended for Spec:Ops gamemode (CODO) and prevent them from spawning
Utils::Hook(0x5FBD6E, QuickPatch::IsDynClassnameStub, HOOK_CALL).install()->quick();
// bounce dvar // bounce dvar
sv_enableBounces = Game::Dvar_RegisterBool("sv_enableBounces", false, Game::DVAR_FLAG_REPLICATED, "Enables bouncing on the server"); sv_enableBounces = Game::Dvar_RegisterBool("sv_enableBounces", false, Game::DVAR_FLAG_REPLICATED, "Enables bouncing on the server");
Utils::Hook(0x4B1B2D, QuickPatch::BounceStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x4B1B2D, QuickPatch::BounceStub, HOOK_JUMP).install()->quick();

View File

@ -47,5 +47,6 @@ namespace Components
static void PlayerCollisionStub(); static void PlayerCollisionStub();
static Game::dvar_t* g_playerEjection; static Game::dvar_t* g_playerEjection;
static void PlayerEjectionStub(); static void PlayerEjectionStub();
static BOOL IsDynClassnameStub(char* a1);
}; };
} }

View File

@ -25,9 +25,14 @@ namespace Components
} }
else else
{ {
if (!RCon::Password.empty() && *reinterpret_cast<int*>(0xB2C540) >= 5) // Get our state auto addr = reinterpret_cast<Game::netadr_t*>(0xA5EA44);
if (!RCon::Password.empty())
{ {
Network::Address target(reinterpret_cast<Game::netadr_t*>(0xA5EA44)); Network::Address target(addr);
if (!target.isValid() || target.getIP().full == 0)
{
target = Party::Target();
}
if (target.isValid()) if (target.isValid())
{ {
@ -71,6 +76,7 @@ namespace Components
Dvar::OnInit([]() Dvar::OnInit([]()
{ {
Dvar::Register<const char*>("rcon_password", "", Game::dvar_flag::DVAR_FLAG_NONE, "The password for rcon"); Dvar::Register<const char*>("rcon_password", "", Game::dvar_flag::DVAR_FLAG_NONE, "The password for rcon");
Dvar::Register<bool>("log_rcon_requests", false, Game::dvar_flag::DVAR_FLAG_NONE, "Print remote commands in the output log");
}); });
Network::Handle("rcon", [](Network::Address address, const std::string& _data) Network::Handle("rcon", [](Network::Address address, const std::string& _data)
@ -107,9 +113,12 @@ namespace Components
static std::string outputBuffer; static std::string outputBuffer;
outputBuffer.clear(); outputBuffer.clear();
#ifdef DEBUG #ifndef DEBUG
Logger::Print("Executing RCon request from %s: %s\n", address.getCString(), command.data()); if (Dvar::Var("log_rcon_requests").get<bool>())
#endif #endif
{
Logger::Print("Executing RCon request from %s: %s\n", address.getCString(), command.data());
}
Logger::PipeOutput([](const std::string& output) Logger::PipeOutput([](const std::string& output)
{ {

View File

@ -378,7 +378,7 @@ namespace Components
Game::XFileHeader header = Game::XFileHeader header =
{ {
#ifdef DEBUG #ifndef GENERATE_IW4X_SPECIFIC_ZONES
XFILE_MAGIC_UNSIGNED, XFILE_MAGIC_UNSIGNED,
#else #else
XFILE_HEADER_IW4X | (static_cast<unsigned __int64>(XFILE_VERSION_IW4X) << 32), XFILE_HEADER_IW4X | (static_cast<unsigned __int64>(XFILE_VERSION_IW4X) << 32),
@ -394,7 +394,7 @@ namespace Components
std::string zoneBuffer = this->buffer.toBuffer(); std::string zoneBuffer = this->buffer.toBuffer();
#ifndef DEBUG #ifdef GENERATE_IW4X_SPECIFIC_ZONES
// Insert a random byte, this will destroy the whole alignment and result in a crash, if not handled // Insert a random byte, this will destroy the whole alignment and result in a crash, if not handled
zoneBuffer.insert(zoneBuffer.begin(), static_cast<char>(Utils::Cryptography::Rand::GenerateInt())); zoneBuffer.insert(zoneBuffer.begin(), static_cast<char>(Utils::Cryptography::Rand::GenerateInt()));
@ -844,7 +844,7 @@ namespace Components
Command::Add("quit", [](Command::Params*) Command::Add("quit", [](Command::Params*)
{ {
ZoneBuilder::Quit(); Game::Com_Quitf_t();
}); });
Command::Add("error", [](Command::Params*) Command::Add("error", [](Command::Params*)
@ -921,12 +921,6 @@ namespace Components
return 0; return 0;
} }
void ZoneBuilder::Quit()
{
//TerminateProcess(GetCurrentProcess(), 0);
ExitProcess(0);
}
void ZoneBuilder::HandleError(int level, const char* format, ...) void ZoneBuilder::HandleError(int level, const char* format, ...)
{ {
char buffer[256] = { 0 }; char buffer[256] = { 0 };
@ -1088,9 +1082,6 @@ namespace Components
// set new entry point // set new entry point
Utils::Hook(0x4513DA, ZoneBuilder::EntryPoint, HOOK_JUMP).install()->quick(); Utils::Hook(0x4513DA, ZoneBuilder::EntryPoint, HOOK_JUMP).install()->quick();
// set quit handler
Utils::Hook(0x4D4000, ZoneBuilder::Quit, HOOK_JUMP).install()->quick();
// handle com_error calls // handle com_error calls
Utils::Hook(0x4B22D0, ZoneBuilder::HandleError, HOOK_JUMP).install()->quick(); Utils::Hook(0x4B22D0, ZoneBuilder::HandleError, HOOK_JUMP).install()->quick();

View File

@ -138,7 +138,6 @@ namespace Components
static std::string FindMaterialByTechnique(const std::string& name); static std::string FindMaterialByTechnique(const std::string& name);
static int __stdcall EntryPoint(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/); static int __stdcall EntryPoint(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/);
static void Quit();
static void HandleError(int level, const char* format, ...); static void HandleError(int level, const char* format, ...);
static void SoftErrorAssetOverflow(); static void SoftErrorAssetOverflow();

View File

@ -4,10 +4,63 @@
namespace Components namespace Components
{ {
int Zones::ZoneVersion; int Zones::ZoneVersion;
int Zones::EntitiesVersion;
int Zones::FxEffectIndex; int Zones::FxEffectIndex;
char* Zones::FxEffectStrings[64]; char* Zones::FxEffectStrings[64];
static std::unordered_map<std::string, std::string> shellshock_replace_list = {
{ "66","bg_shock_screenType" },
{ "67","bg_shock_screenBlurBlendTime"},
{ "68","bg_shock_screenBlurBlendFadeTime"},
{ "69","bg_shock_screenFlashWhiteFadeTime"},
{ "70","bg_shock_screenFlashShotFadeTime"},
{ "71","bg_shock_viewKickPeriod"},
{ "72","bg_shock_viewKickRadius"},
{ "73","bg_shock_viewKickFadeTime"},
{ "78","bg_shock_sound"},
{ "74","bg_shock_soundLoop"},
{ "75","bg_shock_soundLoopSilent"},
{ "76","bg_shock_soundEnd"},
{ "77","bg_shock_soundEndAbort"},
{ "79","bg_shock_soundFadeInTime"},
{ "80","bg_shock_soundFadeOutTime"},
{ "81","bg_shock_soundLoopFadeTime"},
{ "82","bg_shock_soundLoopEndDelay"},
{ "83","bg_shock_soundRoomType"},
{ "84","bg_shock_soundDryLevel"},
{ "85","bg_shock_soundWetLevel"},
{ "86","bg_shock_soundModEndDelay"},
// guessed, not sure
{ "87","bg_shock_lookControl"},
{ "88","bg_shock_lookControl_maxpitchspeed"},
{ "89","bg_shock_lookControl_maxyawspeed"},
{ "90","bg_shock_lookControl_mousesensitivityscale"},
{ "91","bg_shock_lookControl_fadeTime"},
{ "92","bg_shock_movement"}
};
static std::unordered_map<std::string, std::string> vision_replace_list = {
{ "511","r_glow" },
{ "516","r_glowRadius0" },
{ "512","r_glowBloomCutoff" },
{ "513","r_glowBloomDesaturation" },
{ "514","r_glowBloomIntensity0" },
{ "520","r_filmEnable" },
{ "522","r_filmContrast" },
{ "521","r_filmBrightness" },
{ "523","r_filmDesaturation" },
{ "524","r_filmDesaturationDark" },
{ "525","r_filmInvert" },
{ "526","r_filmLightTint" },
{ "527","r_filmMediumTint" },
{ "528","r_filmDarkTint" },
{ "529","r_primaryLightUseTweaks" },
{ "530","r_primaryLightTweakDiffuseStrength" },
{ "531","r_primaryLightTweakSpecularStrength" },
};
Game::XAssetType currentAssetType = Game::XAssetType::ASSET_TYPE_INVALID; Game::XAssetType currentAssetType = Game::XAssetType::ASSET_TYPE_INVALID;
Game::XAssetType previousAssetType = Game::XAssetType::ASSET_TYPE_INVALID; Game::XAssetType previousAssetType = Game::XAssetType::ASSET_TYPE_INVALID;
@ -223,30 +276,20 @@ namespace Components
int size = 3112; int size = 3112;
if (Zones::ZoneVersion >= 318) if (Zones::ZoneVersion >= 461)
{ size = 4124;
else if (Zones::ZoneVersion >= 460)
size = 4120;
else if (Zones::ZoneVersion >= 365)
size = 3124;
else if (Zones::ZoneVersion >= 359)
size = 3120;
else if (Zones::ZoneVersion >= 332)
size = 3068;
else if (Zones::ZoneVersion >= 318)
size = 3156; size = 3156;
if (Zones::ZoneVersion >= 332) int offsetShift = (Zones::ZoneVersion >= 461) ? 4 : 0;
{
size = 3068; // We didn't adapt that, but who the fuck cares!
if (Zones::ZoneVersion >= 359)
{
size = 3120;
if (Zones::ZoneVersion >= 365)
{
size = 3124;
if (Zones::ZoneVersion >= 460)
{
size = 4120;
}
}
}
}
}
// and do the stuff // and do the stuff
Game::Load_Stream(true, varWeaponCompleteDef, size); Game::Load_Stream(true, varWeaponCompleteDef, size);
@ -270,7 +313,7 @@ namespace Components
if (Zones::ZoneVersion >= 359) if (Zones::ZoneVersion >= 359)
{ {
auto count = (Zones::Version() == 460) ? 52 : 56; auto count = (Zones::Version() >= 460) ? 52 : 56;
for (int offset = 20; offset <= count; offset += 4) for (int offset = 20; offset <= count; offset += 4)
{ {
*Game::varXModelPtr = reinterpret_cast<Game::XModel**>(varWeaponCompleteDef + offset); *Game::varXModelPtr = reinterpret_cast<Game::XModel**>(varWeaponCompleteDef + offset);
@ -299,7 +342,7 @@ namespace Components
if (Zones::ZoneVersion >= 359) if (Zones::ZoneVersion >= 359)
{ {
auto stringCount = (Zones::Version() == 460) ? 62 : 52; auto stringCount = (Zones::Version() >= 460) ? 62 : 52;
auto arraySize = stringCount * 4; auto arraySize = stringCount * 4;
// 236 // 236
@ -395,8 +438,8 @@ namespace Components
// 980 // 980
if (Zones::ZoneVersion >= 359) if (Zones::ZoneVersion >= 359)
{ {
auto offset = (Zones::Version() == 460) ? 1476 : 916; auto offset = (Zones::Version() >= 460) ? 1476 : 916;
auto count = (Zones::Version() == 460) ? 57 : 52; auto count = (Zones::Version() >= 461) ? 58 : (Zones::Version() >= 460) ? 57 : 52;
// 53 soundalias name references; up to and including 1124 // 53 soundalias name references; up to and including 1124
for (int i = 0; i < count; ++i, offset += 4) for (int i = 0; i < count; ++i, offset += 4)
@ -809,7 +852,7 @@ namespace Components
void* vec2 = Game::DB_AllocStreamPos(3); void* vec2 = Game::DB_AllocStreamPos(3);
*reinterpret_cast<void**>(varWeaponCompleteDef + 3204) = vec2; *reinterpret_cast<void**>(varWeaponCompleteDef + 3204) = vec2;
Game::Load_Stream(true, vec2, 8 * *reinterpret_cast<short*>(varWeaponCompleteDef + 3776)); Game::Load_Stream(true, vec2, 8 * *reinterpret_cast<short*>(varWeaponCompleteDef + 3776 + offsetShift));
} }
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3200); *Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3200);
@ -820,7 +863,7 @@ namespace Components
void* vec2 = Game::DB_AllocStreamPos(3); void* vec2 = Game::DB_AllocStreamPos(3);
*reinterpret_cast<void**>(varWeaponCompleteDef + 3208) = vec2; *reinterpret_cast<void**>(varWeaponCompleteDef + 3208) = vec2;
Game::Load_Stream(true, vec2, 8 * *reinterpret_cast<short*>(varWeaponCompleteDef + 3778)); Game::Load_Stream(true, vec2, 8 * *reinterpret_cast<short*>(varWeaponCompleteDef + 3778 + offsetShift));
} }
} }
else if (Zones::ZoneVersion >= 359) else if (Zones::ZoneVersion >= 359)
@ -868,22 +911,22 @@ namespace Components
if (Zones::Version() >= 460) if (Zones::Version() >= 460)
{ {
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3288); *Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3288 + offsetShift);
Game::Load_XString(false); Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3292); *Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3292 + offsetShift);
Game::Load_XString(false); Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3324); *Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3324 + offsetShift);
Game::Load_XString(false); Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3328); *Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3328 + offsetShift);
Game::Load_XString(false); Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3484); *Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3484 + offsetShift);
Game::Load_XString(false); Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3488); *Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3488 + offsetShift);
Game::Load_XString(false); Game::Load_XString(false);
} }
else if (Zones::ZoneVersion >= 359) else if (Zones::ZoneVersion >= 359)
@ -929,37 +972,37 @@ namespace Components
if (Zones::Version() >= 460) if (Zones::Version() >= 460)
{ {
*Game::varTracerDefPtr = reinterpret_cast<Game::TracerDef * *>(varWeaponCompleteDef + 3492); *Game::varTracerDefPtr = reinterpret_cast<Game::TracerDef * *>(varWeaponCompleteDef + 3492 + offsetShift);
Game::Load_TracerDefPtr(false); Game::Load_TracerDefPtr(false);
*Game::varTracerDefPtr = reinterpret_cast<Game::TracerDef * *>(varWeaponCompleteDef + 3496); *Game::varTracerDefPtr = reinterpret_cast<Game::TracerDef * *>(varWeaponCompleteDef + 3496 + offsetShift);
Game::Load_TracerDefPtr(false); Game::Load_TracerDefPtr(false);
*Game::varTracerDefPtr = reinterpret_cast<Game::TracerDef * *>(varWeaponCompleteDef + 3500); *Game::varTracerDefPtr = reinterpret_cast<Game::TracerDef * *>(varWeaponCompleteDef + 3500 + offsetShift);
Game::Load_TracerDefPtr(false); Game::Load_TracerDefPtr(false);
*Game::varsnd_alias_list_name = reinterpret_cast<Game::snd_alias_list_t * *>(varWeaponCompleteDef + 3528); *Game::varsnd_alias_list_name = reinterpret_cast<Game::snd_alias_list_t * *>(varWeaponCompleteDef + 3528 + offsetShift);
Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); // 2848 Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); // 2848
*Game::varFxEffectDefHandle = reinterpret_cast<Game::FxEffectDef * *>(varWeaponCompleteDef + 3532); *Game::varFxEffectDefHandle = reinterpret_cast<Game::FxEffectDef * *>(varWeaponCompleteDef + 3532 + offsetShift);
Game::Load_FxEffectDefHandle(false); Game::Load_FxEffectDefHandle(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3536); *Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3536 + offsetShift);
Game::Load_XString(false); Game::Load_XString(false);
*Game::varsnd_alias_list_name = reinterpret_cast<Game::snd_alias_list_t * *>(varWeaponCompleteDef + 3552); *Game::varsnd_alias_list_name = reinterpret_cast<Game::snd_alias_list_t * *>(varWeaponCompleteDef + 3552 + offsetShift);
Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name);
*Game::varsnd_alias_list_name = reinterpret_cast<Game::snd_alias_list_t * *>(varWeaponCompleteDef + 3556); *Game::varsnd_alias_list_name = reinterpret_cast<Game::snd_alias_list_t * *>(varWeaponCompleteDef + 3556 + offsetShift);
Game::Load_snd_alias_list_nameArray(false, 4); Game::Load_snd_alias_list_nameArray(false, 4);
*Game::varsnd_alias_list_name = reinterpret_cast<Game::snd_alias_list_t * *>(varWeaponCompleteDef + 3572); *Game::varsnd_alias_list_name = reinterpret_cast<Game::snd_alias_list_t * *>(varWeaponCompleteDef + 3572 + offsetShift);
Game::Load_snd_alias_list_nameArray(false, 4); Game::Load_snd_alias_list_nameArray(false, 4);
*Game::varsnd_alias_list_name = reinterpret_cast<Game::snd_alias_list_t * *>(varWeaponCompleteDef + 3588); *Game::varsnd_alias_list_name = reinterpret_cast<Game::snd_alias_list_t * *>(varWeaponCompleteDef + 3588 + offsetShift);
Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name);
*Game::varsnd_alias_list_name = reinterpret_cast<Game::snd_alias_list_t * *>(varWeaponCompleteDef + 3592); *Game::varsnd_alias_list_name = reinterpret_cast<Game::snd_alias_list_t * *>(varWeaponCompleteDef + 3592 + offsetShift);
Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name);
} }
else if (Zones::ZoneVersion >= 359) else if (Zones::ZoneVersion >= 359)
@ -1023,7 +1066,7 @@ namespace Components
if (Zones::Version() >= 460) if (Zones::Version() >= 460)
{ {
for (int i = 0, offset = 3660; i < 6; ++i, offset += 4) for (int i = 0, offset = 3660 + offsetShift; i < 6; ++i, offset += 4)
{ {
*Game::varsnd_alias_list_name = reinterpret_cast<Game::snd_alias_list_t * *>(varWeaponCompleteDef + offset); *Game::varsnd_alias_list_name = reinterpret_cast<Game::snd_alias_list_t * *>(varWeaponCompleteDef + offset);
Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name);
@ -1058,25 +1101,25 @@ namespace Components
if (Zones::Version() >= 460) if (Zones::Version() >= 460)
{ {
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3712); *Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3712 + offsetShift);
Game::Load_XString(false); Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3728); *Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3728 + offsetShift);
Game::Load_XString(false); Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3732); *Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3732 + offsetShift);
Game::Load_XString(false); Game::Load_XString(false);
*Game::varMaterialHandle = reinterpret_cast<Game::Material * *>(varWeaponCompleteDef + 3740); *Game::varMaterialHandle = reinterpret_cast<Game::Material * *>(varWeaponCompleteDef + 3740 + offsetShift);
Game::Load_MaterialHandle(false); Game::Load_MaterialHandle(false);
*Game::varMaterialHandle = reinterpret_cast<Game::Material * *>(varWeaponCompleteDef + 3744); *Game::varMaterialHandle = reinterpret_cast<Game::Material * *>(varWeaponCompleteDef + 3744 + offsetShift);
Game::Load_MaterialHandle(false); Game::Load_MaterialHandle(false);
*Game::varMaterialHandle = reinterpret_cast<Game::Material * *>(varWeaponCompleteDef + 3748); *Game::varMaterialHandle = reinterpret_cast<Game::Material * *>(varWeaponCompleteDef + 3748 + offsetShift);
Game::Load_MaterialHandle(false); Game::Load_MaterialHandle(false);
*Game::varMaterialHandle = reinterpret_cast<Game::Material * *>(varWeaponCompleteDef + 3752); *Game::varMaterialHandle = reinterpret_cast<Game::Material * *>(varWeaponCompleteDef + 3752 + offsetShift);
Game::Load_MaterialHandle(false); Game::Load_MaterialHandle(false);
} }
else if (Zones::ZoneVersion >= 359) else if (Zones::ZoneVersion >= 359)
@ -1127,35 +1170,35 @@ namespace Components
if (Zones::Version() >= 460) if (Zones::Version() >= 460)
{ {
if (*reinterpret_cast<DWORD*>(varWeaponCompleteDef + 3780) == -1) if (*reinterpret_cast<DWORD*>(varWeaponCompleteDef + 3780 + offsetShift) == -1)
{ {
void* vec2 = Game::DB_AllocStreamPos(3); void* vec2 = Game::DB_AllocStreamPos(3);
*reinterpret_cast<void**>(varWeaponCompleteDef + 3780) = vec2; *reinterpret_cast<void**>(varWeaponCompleteDef + 3780 + offsetShift) = vec2;
Game::Load_Stream(true, vec2, 8 * *reinterpret_cast<short*>(varWeaponCompleteDef + 3776)); Game::Load_Stream(true, vec2, 8 * *reinterpret_cast<short*>(varWeaponCompleteDef + 3776 + offsetShift));
} }
if (*reinterpret_cast<DWORD*>(varWeaponCompleteDef + 3784) == -1) if (*reinterpret_cast<DWORD*>(varWeaponCompleteDef + 3784 + offsetShift) == -1)
{ {
void* vec2 = Game::DB_AllocStreamPos(3); void* vec2 = Game::DB_AllocStreamPos(3);
*reinterpret_cast<void**>(varWeaponCompleteDef + 3784) = vec2; *reinterpret_cast<void**>(varWeaponCompleteDef + 3784 + offsetShift) = vec2;
Game::Load_Stream(true, vec2, 8 * *reinterpret_cast<short*>(varWeaponCompleteDef + 3778)); Game::Load_Stream(true, vec2, 8 * *reinterpret_cast<short*>(varWeaponCompleteDef + 3778 + offsetShift));
} }
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3876); *Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3876 + offsetShift);
Game::Load_XString(false); Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3880); *Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3880 + offsetShift);
Game::Load_XString(false); Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3884); *Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3884 + offsetShift);
Game::Load_XString(false); Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3996); *Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3996 + offsetShift);
Game::Load_XString(false); Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 4012); *Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 4012 + offsetShift);
Game::Load_XString(false); Game::Load_XString(false);
} }
else if (Zones::ZoneVersion >= 359) else if (Zones::ZoneVersion >= 359)
@ -1381,7 +1424,7 @@ namespace Components
char* varWeaponAttach = *reinterpret_cast<char**>(0x112ADE0); // varAddonMapEnts char* varWeaponAttach = *reinterpret_cast<char**>(0x112ADE0); // varAddonMapEnts
// and do the stuff // and do the stuff
if (Zones::Version() >= 448) if (Zones::Version() >= 446)
{ {
Game::Load_Stream(true, varWeaponAttach, 20); Game::Load_Stream(true, varWeaponAttach, 20);
@ -1426,7 +1469,7 @@ namespace Components
bool Zones::LoadMaterialShaderArgumentArray(bool atStreamStart, Game::MaterialShaderArgument* argument, int size) bool Zones::LoadMaterialShaderArgumentArray(bool atStreamStart, Game::MaterialShaderArgument* argument, int size)
{ {
// if (Zones::ZoneVersion >= 448 && currentAssetType == Game::XAssetType::ASSET_TYPE_FX) __debugbreak(); // if (Zones::ZoneVersion >= 446 && currentAssetType == Game::XAssetType::ASSET_TYPE_FX) __debugbreak();
bool result = Game::Load_Stream(atStreamStart, argument, size); bool result = Game::Load_Stream(atStreamStart, argument, size);
Game::MaterialPass* curPass = *Game::varMaterialPass; Game::MaterialPass* curPass = *Game::varMaterialPass;
@ -1436,13 +1479,13 @@ namespace Components
{ {
Game::MaterialShaderArgument* arg = &argument[i]; Game::MaterialShaderArgument* arg = &argument[i];
if (Zones::Version() < 448) if (arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_TEXTURE && arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_ATTROUT)
{ {
if (arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_TEXTURE && arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_ATTROUT) continue;
{ }
continue;
}
if (Zones::Version() < 446)
{
// should be min 68 currently // should be min 68 currently
// >= 58 fixes foliage without bad side effects // >= 58 fixes foliage without bad side effects
// >= 53 still has broken shadow mapping // >= 53 still has broken shadow mapping
@ -1489,7 +1532,100 @@ namespace Components
{ {
if (arg->type == 3 || arg->type == 5) if (arg->type == 3 || arg->type == 5)
{ {
if (Zones::Version() == 460 /*|| Zones::Version() == 448*/) // 448 is no longer compatible, needs correct mappings // 446 is from a special client version that had lot of
// unrelased/unfinished maps, is just enough for explore,
// trees had issue with it
if (Zones::Version() == 446)
{
static std::unordered_map<std::uint16_t, std::uint16_t> mapped_constants = {
{ 33, 31 },
{ 34, 32 },
{ 36, 34 },
{ 39, 37 },
{ 40, 38 },
{ 42, 40 },
{ 43, 41 },
{ 45, 43 },
{ 62, 52 },
{ 63, 53 },
{ 199, 58 },
{ 259, 86 },
{ 263, 90 },
{ 271, 98 },
{ 279, 106 },
};
const auto itr = mapped_constants.find(arg->u.codeConst.index);
if (itr != mapped_constants.end())
{
arg->u.codeConst.index = itr->second;
}
}
else if (Zones::Version() == 461)
{
static std::unordered_map<std::uint16_t, std::uint16_t> mapped_constants =
{
// mp_raid
{ 33, 31 },
{ 34, 32 },
{ 36, 34 },
{ 39, 37 },
{ 40, 38 },
{ 42, 40 },
{ 43, 41 },
{ 45, 43 },
{ 62, 52 },
{ 63, 53 },
{ 197, 58 },
{ 202, 63 },
{ 203, 64 },
{ 261, 90 },
{ 265, 94 },
{ 269, 98 },
{ 277, 106 },
// mp_dome
{ 38, 36 },
{ 40, 38 },
{ 118, 86 },
};
const auto itr = mapped_constants.find(arg->u.codeConst.index);
if (itr != mapped_constants.end())
{
arg->u.codeConst.index = itr->second;
}
if (arg->u.codeConst.index == 257)
{
auto techsetName = (*reinterpret_cast<Game::MaterialTechniqueSet**>(0x112AE8C))->name;
// dont know if this applies to 460 too, but I dont have 460 files to test
if (!strncmp(techsetName, "wc_unlit_add", 12) ||
!strncmp(techsetName, "wc_unlit_multiply", 17) )
{
// fixes glass and water
arg->u.codeConst.index = 116;
}
else
{
// anything else
arg->u.codeConst.index = 86;
}
}
else
{
// copy-paste from 460
if (arg->u.codeConst.index >= 259)
{
arg->u.codeConst.index -= 171;
}
else if (arg->u.codeConst.index >= 197)
{
arg->u.codeConst.index -= 139;
}
}
}
else if (Zones::Version() == 460 /*|| Zones::Version() == 446*/) // 446 is no longer compatible, needs correct mappings
{ {
static std::unordered_map<std::uint16_t, std::uint16_t> mapped_constants = { static std::unordered_map<std::uint16_t, std::uint16_t> mapped_constants = {
{ 22, 21 }, { 22, 21 },
@ -1741,8 +1877,8 @@ namespace Components
{ {
// 359 and above adds an extra remapped techset ptr // 359 and above adds an extra remapped techset ptr
if (Zones::ZoneVersion >= 359) size += 4; if (Zones::ZoneVersion >= 359) size += 4;
// 448 amd above adds an additional technique // 446 amd above adds an additional technique
if (Zones::ZoneVersion >= 448) size += 4; if (Zones::ZoneVersion >= 446) size += 4;
bool result = Game::Load_Stream(atStreamStart, buffer, size); bool result = Game::Load_Stream(atStreamStart, buffer, size);
@ -1754,22 +1890,22 @@ namespace Components
// As MW2 flags are only 1 byte large, this won't be possible anyways // As MW2 flags are only 1 byte large, this won't be possible anyways
int shiftTest = 4; int shiftTest = 4;
std::memmove(buffer + 8 + shiftTest, buffer + 12 + shiftTest, (Zones::Version() >= 448) ? 200 : 196 - shiftTest); std::memmove(buffer + 8 + shiftTest, buffer + 12 + shiftTest, (Zones::Version() >= 446) ? 200 : 196 - shiftTest);
AssetHandler::Relocate(buffer + 12 + shiftTest, buffer + 8 + shiftTest, (Zones::Version() >= 448) ? 200 : 196 - shiftTest); AssetHandler::Relocate(buffer + 12 + shiftTest, buffer + 8 + shiftTest, (Zones::Version() >= 446) ? 200 : 196 - shiftTest);
} }
return result; return result;
} }
int Zones::LoadMaterialTechniqueArray(bool atStreamStart, int count) int Zones::LoadMaterialTechniqueArray(bool atStreamStart, int count)
{ {
if (Zones::Version() >= 448) if (Zones::Version() >= 446)
{ {
count += 1; count += 1;
} }
auto retval = Utils::Hook::Call<int(bool, int)>(0x497020)(atStreamStart, count); auto retval = Utils::Hook::Call<int(bool, int)>(0x497020)(atStreamStart, count);
if (Zones::Version() >= 448) if (Zones::Version() >= 446)
{ {
auto lastTechnique = **reinterpret_cast<Game::MaterialTechnique * **>(0x112AEDC); auto lastTechnique = **reinterpret_cast<Game::MaterialTechnique * **>(0x112AEDC);
auto varMaterialTechniqueSet = **reinterpret_cast<Game::MaterialTechniqueSet * **>(0x112B070); auto varMaterialTechniqueSet = **reinterpret_cast<Game::MaterialTechniqueSet * **>(0x112B070);
@ -1783,11 +1919,11 @@ namespace Components
bool Zones::LoadMaterial(bool atStreamStart, char* buffer, int size) bool Zones::LoadMaterial(bool atStreamStart, char* buffer, int size)
{ {
// if (Zones::ZoneVersion >= 448 && currentAssetType == Game::ASSET_TYPE_XMODEL) __debugbreak(); // if (Zones::ZoneVersion >= 446 && currentAssetType == Game::ASSET_TYPE_XMODEL) __debugbreak();
bool result = Game::Load_Stream(atStreamStart, buffer, (Zones::Version() >= 448) ? 104 : size); bool result = Game::Load_Stream(atStreamStart, buffer, (Zones::Version() >= 446) ? 104 : size);
if (Zones::Version() >= 448) if (Zones::Version() >= 446)
{ {
char codol_material[104]; char codol_material[104];
memcpy(codol_material, buffer, 104); memcpy(codol_material, buffer, 104);
@ -1990,7 +2126,7 @@ namespace Components
void Zones::LoadImpactFx(bool atStreamStart, char* buffer, int size) void Zones::LoadImpactFx(bool atStreamStart, char* buffer, int size)
{ {
if (Zones::Version() >= 460) size = 0xB94; if (Zones::Version() >= 460) size = 0xB94;
else if (Zones::Version() >= 448) size = 0xA64; else if (Zones::Version() >= 446) size = 0xA64;
else if (Zones::Version() >= VERSION_ALPHA2) size = 0x8C0; else if (Zones::Version() >= VERSION_ALPHA2) size = 0x8C0;
Game::Load_Stream(atStreamStart, buffer, size); Game::Load_Stream(atStreamStart, buffer, size);
@ -2007,7 +2143,7 @@ namespace Components
int Zones::ImpactFxArrayCount() int Zones::ImpactFxArrayCount()
{ {
if (Zones::Version() >= 448) if (Zones::Version() >= 446)
{ {
return 19; return 19;
} }
@ -2170,7 +2306,7 @@ namespace Components
int Zones::LoadRandomFxGarbage(bool atStreamStart, char* buffer, int size) int Zones::LoadRandomFxGarbage(bool atStreamStart, char* buffer, int size)
{ {
int count = 0; int count = 0;
if (Zones::Version() >= 448) if (Zones::Version() >= 446)
{ {
size /= 48; size /= 48;
count = size; count = size;
@ -2179,7 +2315,7 @@ namespace Components
const auto retval = Game::Load_Stream(atStreamStart, buffer, size); const auto retval = Game::Load_Stream(atStreamStart, buffer, size);
if (Zones::Version() >= 448) if (Zones::Version() >= 446)
{ {
for (auto i = 0; i < count; i++) for (auto i = 0; i < count; i++)
{ {
@ -2258,7 +2394,7 @@ namespace Components
{ {
int count = 0; int count = 0;
if (Zones::Version() >= 448) if (Zones::Version() >= 446)
{ {
size /= 12; size /= 12;
count = size; count = size;
@ -2267,7 +2403,7 @@ namespace Components
auto retval = Game::Load_Stream(atStreamStart, buffer, size); auto retval = Game::Load_Stream(atStreamStart, buffer, size);
if (Zones::Version() >= 448) if (Zones::Version() >= 446)
{ {
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
@ -2332,8 +2468,8 @@ namespace Components
cmp dword ptr[eax], 0xFFFFFFFF; cmp dword ptr[eax], 0xFFFFFFFF;
je loadAssetData; je loadAssetData;
// check if FF is below 448, still load data in that case // check if FF is below 446, still load data in that case
cmp Zones::ZoneVersion, 448; cmp Zones::ZoneVersion, 446;
jl loadAssetData; jl loadAssetData;
// offset to pointer magic // offset to pointer magic
@ -2426,7 +2562,9 @@ namespace Components
int Zones::LoadMapEnts(bool atStreamStart, Game::MapEnts* buffer, int size) int Zones::LoadMapEnts(bool atStreamStart, Game::MapEnts* buffer, int size)
{ {
if (Zones::Version() >= 448) EntitiesVersion = Zones::Version();
if (Zones::Version() >= 446)
{ {
size /= 44; size /= 44;
size *= 36; size *= 36;
@ -2583,7 +2721,7 @@ namespace Components
int Zones::LoadClipMap(bool atStreamStart) int Zones::LoadClipMap(bool atStreamStart)
{ {
if (Zones::Version() >= 448) if (Zones::Version() >= 446)
{ {
AssertOffset(codolClipMap_t, pInfo, 72); AssertOffset(codolClipMap_t, pInfo, 72);
@ -3139,8 +3277,8 @@ namespace Components
cmp dword ptr[edx + 4], 0xFFFFFFFF; cmp dword ptr[edx + 4], 0xFFFFFFFF;
je loadAssetData; je loadAssetData;
// check if FF is below 448, still load data in that case // check if FF is below 446, still load data in that case
cmp Zones::ZoneVersion, 448; cmp Zones::ZoneVersion, 446;
jl loadAssetData; jl loadAssetData;
// offset to pointer magic // offset to pointer magic
@ -3171,8 +3309,8 @@ namespace Components
cmp dword ptr[eax + 0Ch], 0xFFFFFFFF; cmp dword ptr[eax + 0Ch], 0xFFFFFFFF;
je loadAssetData; je loadAssetData;
// check if FF is below 448, still load data in that case // check if FF is below 446, still load data in that case
cmp Zones::ZoneVersion, 448; cmp Zones::ZoneVersion, 446;
jl loadAssetData; jl loadAssetData;
// offset to pointer magic // offset to pointer magic
@ -3203,8 +3341,8 @@ namespace Components
cmp dword ptr[eax + 14h], 0xFFFFFFFF; cmp dword ptr[eax + 14h], 0xFFFFFFFF;
je loadAssetData; je loadAssetData;
// check if FF is below 448, still load data in that case // check if FF is below 446, still load data in that case
cmp Zones::ZoneVersion, 448; cmp Zones::ZoneVersion, 446;
jl loadAssetData; jl loadAssetData;
// offset to pointer magic // offset to pointer magic
@ -3273,11 +3411,11 @@ namespace Components
void Zones::LoadXModelAsset(Game::XModel** asset) void Zones::LoadXModelAsset(Game::XModel** asset)
{ {
if (Zones::Version() >= 448) if (Zones::Version() >= 446)
{ {
for (int i = 0; i < (*asset)->numLods; i++) for (int i = 0; i < (*asset)->numLods; i++)
{ {
if ((*asset)->lodInfo[i].surfs == nullptr && Zones::Version() >= 448) if ((*asset)->lodInfo[i].surfs == nullptr && Zones::Version() >= 446)
{ {
const auto name = (*asset)->name; const auto name = (*asset)->name;
const auto fx_model = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_XMODEL, "void").model; const auto fx_model = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_XMODEL, "void").model;
@ -3303,7 +3441,7 @@ namespace Components
void Zones::LoadMaterialAsset(Game::Material** asset) void Zones::LoadMaterialAsset(Game::Material** asset)
{ {
if (asset && *asset && Zones::Version() >= 448) if (asset && *asset && Zones::Version() >= 446)
{ {
static std::vector<std::string> broken_materials = { static std::vector<std::string> broken_materials = {
"gfx_fxt_debris_wind_ash_z10", "gfx_fxt_debris_wind_ash_z10",
@ -3353,6 +3491,29 @@ namespace Components
Game::DB_PopStreamPos(); Game::DB_PopStreamPos();
} }
char* Zones::ParseVision_Stub(const char** data_p)
{
auto token = Game::Com_Parse(data_p);
if (vision_replace_list.find(token) != vision_replace_list.end())
{
return vision_replace_list[token].data();
}
return token;
}
char* Zones::ParseShellShock_Stub(const char** data_p)
{
auto token = Game::Com_Parse(data_p);
if (shellshock_replace_list.find(token) != shellshock_replace_list.end())
{
return shellshock_replace_list[token].data();
}
return token;
}
Zones::Zones() Zones::Zones()
{ {
Zones::ZoneVersion = 0; Zones::ZoneVersion = 0;
@ -3499,6 +3660,9 @@ namespace Components
Utils::Hook(0x418998, Zones::GameMapSpPatchStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x418998, Zones::GameMapSpPatchStub, HOOK_JUMP).install()->quick();
Utils::Hook(0x427A1B, Zones::LoadPathDataTail, HOOK_JUMP).install()->quick(); Utils::Hook(0x427A1B, Zones::LoadPathDataTail, HOOK_JUMP).install()->quick();
Utils::Hook(0x4B4EA1, Zones::ParseShellShock_Stub, HOOK_CALL).install()->quick();
Utils::Hook(0x4B4F0C, Zones::ParseShellShock_Stub, HOOK_CALL).install()->quick();
Utils::Hook(0x4F4D3B, [] () Utils::Hook(0x4F4D3B, [] ()
{ {
if (Zones::ZoneVersion >= VERSION_ALPHA3) if (Zones::ZoneVersion >= VERSION_ALPHA3)
@ -3521,6 +3685,10 @@ namespace Components
Utils::Hook(0x4597DD, Zones::LoadStatement, HOOK_CALL).install()->quick(); Utils::Hook(0x4597DD, Zones::LoadStatement, HOOK_CALL).install()->quick();
Utils::Hook(0x471A39, Zones::LoadWindowImage, HOOK_JUMP).install()->quick(); Utils::Hook(0x471A39, Zones::LoadWindowImage, HOOK_JUMP).install()->quick();
// Fix newer vision file
Utils::Hook(0x59A849, ParseVision_Stub, HOOK_CALL).install()->quick();
Utils::Hook(0x59A8AD, ParseVision_Stub, HOOK_CALL).install()->quick();
#ifdef DEBUG #ifdef DEBUG
// Easy dirty disk debugging // Easy dirty disk debugging
Utils::Hook::Set<WORD>(0x4CF7F0, 0xC3CC); Utils::Hook::Set<WORD>(0x4CF7F0, 0xC3CC);

View File

@ -3,6 +3,7 @@
#define VERSION_ALPHA2 316 #define VERSION_ALPHA2 316
#define VERSION_ALPHA3 318//319 #define VERSION_ALPHA3 318//319
#define VERSION_ALPHA3_DEC 319 #define VERSION_ALPHA3_DEC 319
#define VERSION_LATEST_CODO 461
namespace Components namespace Components
{ {
@ -23,9 +24,12 @@ namespace Components
static int Version() { return Zones::ZoneVersion; }; static int Version() { return Zones::ZoneVersion; };
static int GetEntitiesZoneVersion() { return Zones::EntitiesVersion; };
private: private:
static int ZoneVersion; static int ZoneVersion;
static int EntitiesVersion;
static int FxEffectIndex; static int FxEffectIndex;
static char* FxEffectStrings[64]; static char* FxEffectStrings[64];
@ -101,5 +105,7 @@ namespace Components
static void LoadTracerDef(bool atStreamStart, Game::TracerDef* tracer, int size); static void LoadTracerDef(bool atStreamStart, Game::TracerDef* tracer, int size);
static void LoadTracerDefFxEffect(); static void LoadTracerDefFxEffect();
static void FixImageCategory(Game::GfxImage* image); static void FixImageCategory(Game::GfxImage* image);
static char* ParseShellShock_Stub(const char** data_p);
static char* ParseVision_Stub(const char** data_p);
}; };
} }

View File

@ -66,6 +66,7 @@ namespace Game
Com_Parse_t Com_Parse = Com_Parse_t(0x474D60); Com_Parse_t Com_Parse = Com_Parse_t(0x474D60);
Com_MatchToken_t Com_MatchToken = Com_MatchToken_t(0x447130); Com_MatchToken_t Com_MatchToken = Com_MatchToken_t(0x447130);
Com_SetSlowMotion_t Com_SetSlowMotion = Com_SetSlowMotion_t(0x446E20); Com_SetSlowMotion_t Com_SetSlowMotion = Com_SetSlowMotion_t(0x446E20);
Com_Quitf_t Com_Quit_f = Com_Quitf_t(0x4D4000);
Con_DrawMiniConsole_t Con_DrawMiniConsole = Con_DrawMiniConsole_t(0x464F30); Con_DrawMiniConsole_t Con_DrawMiniConsole = Con_DrawMiniConsole_t(0x464F30);
Con_DrawSolidConsole_t Con_DrawSolidConsole = Con_DrawSolidConsole_t(0x5A5040); Con_DrawSolidConsole_t Con_DrawSolidConsole = Con_DrawSolidConsole_t(0x5A5040);
@ -99,11 +100,11 @@ namespace Game
Dvar_RegisterFloat_t Dvar_RegisterFloat = Dvar_RegisterFloat_t(0x648440); Dvar_RegisterFloat_t Dvar_RegisterFloat = Dvar_RegisterFloat_t(0x648440);
Dvar_RegisterVec2_t Dvar_RegisterVec2 = Dvar_RegisterVec2_t(0x4F6070); Dvar_RegisterVec2_t Dvar_RegisterVec2 = Dvar_RegisterVec2_t(0x4F6070);
Dvar_RegisterVec3_t Dvar_RegisterVec3 = Dvar_RegisterVec3_t(0x4EF8E0); Dvar_RegisterVec3_t Dvar_RegisterVec3 = Dvar_RegisterVec3_t(0x4EF8E0);
Dvar_RegisterVec4_t Dvar_RegisterVec4 = Dvar_RegisterVec4_t(0x4F28E0); Dvar_RegisterVec4_t Dvar_RegisterVec4 = Dvar_RegisterVec4_t(0x471500);
Dvar_RegisterInt_t Dvar_RegisterInt = Dvar_RegisterInt_t(0x479830); Dvar_RegisterInt_t Dvar_RegisterInt = Dvar_RegisterInt_t(0x479830);
Dvar_RegisterEnum_t Dvar_RegisterEnum = Dvar_RegisterEnum_t(0x412E40); Dvar_RegisterEnum_t Dvar_RegisterEnum = Dvar_RegisterEnum_t(0x412E40);
Dvar_RegisterString_t Dvar_RegisterString = Dvar_RegisterString_t(0x4FC7E0); Dvar_RegisterString_t Dvar_RegisterString = Dvar_RegisterString_t(0x4FC7E0);
Dvar_RegisterColor_t Dvar_RegisterColor = Dvar_RegisterColor_t(0x4F28E0);//0x471500; Dvar_RegisterColor_t Dvar_RegisterColor = Dvar_RegisterColor_t(0x4F28E0);
Dvar_GetUnpackedColorByName_t Dvar_GetUnpackedColorByName = Dvar_GetUnpackedColorByName_t(0x406530); Dvar_GetUnpackedColorByName_t Dvar_GetUnpackedColorByName = Dvar_GetUnpackedColorByName_t(0x406530);
Dvar_FindVar_t Dvar_FindVar = Dvar_FindVar_t(0x4D5390); Dvar_FindVar_t Dvar_FindVar = Dvar_FindVar_t(0x4D5390);

View File

@ -133,6 +133,9 @@ namespace Game
typedef void(__cdecl * Com_SetSlowMotion_t)(float start, float end, int duration); typedef void(__cdecl * Com_SetSlowMotion_t)(float start, float end, int duration);
extern Com_SetSlowMotion_t Com_SetSlowMotion; extern Com_SetSlowMotion_t Com_SetSlowMotion;
typedef void(__cdecl * Com_Quitf_t)();
extern Com_Quitf_t Com_Quit_f;
typedef char* (__cdecl * Con_DrawMiniConsole_t)(int localClientNum, int xPos, int yPos, float alpha); typedef char* (__cdecl * Con_DrawMiniConsole_t)(int localClientNum, int xPos, int yPos, float alpha);
extern Con_DrawMiniConsole_t Con_DrawMiniConsole; extern Con_DrawMiniConsole_t Con_DrawMiniConsole;

View File

@ -3049,7 +3049,7 @@ namespace Game
float value; float value;
float vector[4]; float vector[4];
const char *string; const char *string;
char color[4]; unsigned char color[4];
}; };
struct $BFBB53559BEAC4289F32B924847E59CB struct $BFBB53559BEAC4289F32B924847E59CB

View File

@ -59,7 +59,7 @@ template <size_t S> class Sizer { };
#define Offset_Of(x, y, z) BindNum(offsetof(x, y), z) #define Offset_Of(x, y, z) BindNum(offsetof(x, y), z)
// Submodules // Submodules
// Ignore the warnings, it's no our code! // Ignore the warnings, it's not our code!
#pragma warning(push) #pragma warning(push)
#pragma warning(disable: 4005) #pragma warning(disable: 4005)
#pragma warning(disable: 4091) #pragma warning(disable: 4091)
@ -152,6 +152,7 @@ template <size_t S> class Sizer { };
#pragma comment(lib, "Advapi32.lib") #pragma comment(lib, "Advapi32.lib")
#pragma comment(lib, "rpcrt4.lib") #pragma comment(lib, "rpcrt4.lib")
#pragma comment(lib, "dbghelp.lib") #pragma comment(lib, "dbghelp.lib")
#pragma comment(lib, "ntdll.lib")
// Enable additional literals // Enable additional literals
using namespace std::literals; using namespace std::literals;

View File

@ -384,25 +384,25 @@ namespace Steam
Proxy::LaunchWatchGuard(); Proxy::LaunchWatchGuard();
Proxy::Overlay = ::Utils::Library(GAMEOVERLAY_LIB, false); Proxy::Overlay = ::Utils::Library(GAMEOVERLAY_LIB, false);
if (!Proxy::Overlay.valid()) return false; if (!Proxy::Overlay.IsValid()) return false;
} }
Proxy::Client = ::Utils::Library(STEAMCLIENT_LIB, false); Proxy::Client = ::Utils::Library(STEAMCLIENT_LIB, false);
if (!Proxy::Client.valid()) return false; if (!Proxy::Client.IsValid()) return false;
Proxy::SteamClient = Proxy::Client.get<ISteamClient008*(const char*, int*)>("CreateInterface")("SteamClient008", nullptr); Proxy::SteamClient = Proxy::Client.Get<ISteamClient008*(const char*, int*)>("CreateInterface")("SteamClient008", nullptr);
if(!Proxy::SteamClient) return false; if(!Proxy::SteamClient) return false;
Proxy::SteamBGetCallback = Proxy::Client.get<Proxy::SteamBGetCallbackFn>("Steam_BGetCallback"); Proxy::SteamBGetCallback = Proxy::Client.Get<Proxy::SteamBGetCallbackFn>("Steam_BGetCallback");
if (!Proxy::SteamBGetCallback) return false; if (!Proxy::SteamBGetCallback) return false;
Proxy::SteamFreeLastCallback = Proxy::Client.get<Proxy::SteamFreeLastCallbackFn>("Steam_FreeLastCallback"); Proxy::SteamFreeLastCallback = Proxy::Client.Get<Proxy::SteamFreeLastCallbackFn>("Steam_FreeLastCallback");
if (!Proxy::SteamFreeLastCallback) return false; if (!Proxy::SteamFreeLastCallback) return false;
Proxy::SteamGetAPICallResult = Proxy::Client.get<Proxy::SteamGetAPICallResultFn>("Steam_GetAPICallResult"); Proxy::SteamGetAPICallResult = Proxy::Client.Get<Proxy::SteamGetAPICallResultFn>("Steam_GetAPICallResult");
if (!Proxy::SteamGetAPICallResult) return false; if (!Proxy::SteamGetAPICallResult) return false;
Proxy::SteamClient = Proxy::Client.get<ISteamClient008*(const char*, int*)>("CreateInterface")("SteamClient008", nullptr); Proxy::SteamClient = Proxy::Client.Get<ISteamClient008*(const char*, int*)>("CreateInterface")("SteamClient008", nullptr);
if (!Proxy::SteamClient) return false; if (!Proxy::SteamClient) return false;
Proxy::SteamPipe = Proxy::SteamClient->CreateSteamPipe(); Proxy::SteamPipe = Proxy::SteamClient->CreateSteamPipe();
@ -411,7 +411,7 @@ namespace Steam
Proxy::SteamUser = Proxy::SteamClient->ConnectToGlobalUser(Proxy::SteamPipe); Proxy::SteamUser = Proxy::SteamClient->ConnectToGlobalUser(Proxy::SteamPipe);
if (!Proxy::SteamUser) return false; if (!Proxy::SteamUser) return false;
Proxy::ClientEngine = Proxy::Client.get<IClientEngine*(const char*, int*)>("CreateInterface")("CLIENTENGINE_INTERFACE_VERSION005", nullptr); Proxy::ClientEngine = Proxy::Client.Get<IClientEngine*(const char*, int*)>("CreateInterface")("CLIENTENGINE_INTERFACE_VERSION005", nullptr);
if (!Proxy::ClientEngine) return false; if (!Proxy::ClientEngine) return false;
Proxy::ClientUser = Proxy::ClientEngine->GetIClientUser(Proxy::SteamUser, Proxy::SteamPipe); Proxy::ClientUser = Proxy::ClientEngine->GetIClientUser(Proxy::SteamUser, Proxy::SteamPipe);
@ -526,17 +526,17 @@ namespace Steam
void Proxy::SetOverlayNotificationPosition(uint32_t eNotificationPosition) void Proxy::SetOverlayNotificationPosition(uint32_t eNotificationPosition)
{ {
if (Proxy::Overlay.valid()) if (Proxy::Overlay.IsValid())
{ {
Proxy::Overlay.get<void(uint32_t)>("SetNotificationPosition")(eNotificationPosition); Proxy::Overlay.Get<void(uint32_t)>("SetNotificationPosition")(eNotificationPosition);
} }
} }
bool Proxy::IsOverlayEnabled() bool Proxy::IsOverlayEnabled()
{ {
if (Proxy::Overlay.valid()) if (Proxy::Overlay.IsValid())
{ {
return Proxy::Overlay.get<bool()>("IsOverlayEnabled")(); return Proxy::Overlay.Get<bool()>("IsOverlayEnabled")();
} }
return false; return false;
@ -544,9 +544,9 @@ namespace Steam
bool Proxy::BOverlayNeedsPresent() bool Proxy::BOverlayNeedsPresent()
{ {
if (Proxy::Overlay.valid()) if (Proxy::Overlay.IsValid())
{ {
return Proxy::Overlay.get<bool()>("BOverlayNeedsPresent")(); return Proxy::Overlay.Get<bool()>("BOverlayNeedsPresent")();
} }
return false; return false;

View File

@ -2,34 +2,63 @@
namespace Utils namespace Utils
{ {
Library Library::Load(const std::string& name)
{
return Library(LoadLibraryA(name.data()));
}
Library Library::Load(const std::filesystem::path& path)
{
return Library::Load(path.generic_string());
}
Library Library::GetByAddress(void* address)
{
HMODULE handle = nullptr;
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(address), &handle);
return Library(handle);
}
Library::Library(const std::string& buffer, bool _freeOnDestroy) : _module(nullptr), freeOnDestroy(_freeOnDestroy) Library::Library(const std::string& buffer, bool _freeOnDestroy) : _module(nullptr), freeOnDestroy(_freeOnDestroy)
{ {
this->_module = LoadLibraryExA(buffer.data(), nullptr, 0); this->_module = LoadLibraryExA(buffer.data(), nullptr, 0);
} }
Library::Library(const std::string& buffer)
{
this->_module = GetModuleHandleA(buffer.data());
this->freeOnDestroy = true;
}
Library::Library(const HMODULE handle)
{
this->_module = handle;
this->freeOnDestroy = true;
}
Library::~Library() Library::~Library()
{ {
if (this->freeOnDestroy) if (this->freeOnDestroy)
{ {
this->free(); this->Free();
} }
} }
bool Library::valid() bool Library::IsValid() const
{ {
return (this->getModule() != nullptr); return this->_module != nullptr;
} }
HMODULE Library::getModule() HMODULE Library::GetModule()
{ {
return this->_module; return this->_module;
} }
void Library::free() void Library::Free()
{ {
if (this->valid()) if (this->IsValid())
{ {
FreeLibrary(this->getModule()); FreeLibrary(this->_module);
} }
this->_module = nullptr; this->_module = nullptr;

View File

@ -5,25 +5,58 @@ namespace Utils
class Library class Library
{ {
public: public:
static Library Load(const std::string& name);
static Library Load(const std::filesystem::path& path);
static Library GetByAddress(void* address);
Library() : _module(nullptr), freeOnDestroy(false) {}; Library() : _module(nullptr), freeOnDestroy(false) {};
Library(const std::string& buffer, bool freeOnDestroy = true); Library(const std::string& buffer, bool freeOnDestroy);
explicit Library(const std::string& name);
explicit Library(HMODULE handle);
~Library(); ~Library();
bool valid(); bool IsValid() const;
HMODULE getModule(); HMODULE GetModule();
template <typename T> template <typename T>
std::function<T> get(const std::string& process) T GetProc(const std::string& process) const
{ {
if (!this->valid()) if (!this->IsValid()) T{};
{ return reinterpret_cast<T>(GetProcAddress(this->_module, process.data()));
throw std::runtime_error("Library not loaded!");
}
return reinterpret_cast<T*>(GetProcAddress(this->getModule(), process.data()));
} }
void free(); template <typename T>
std::function<T> Get(const std::string& process) const
{
if (!this->IsValid()) return std::function<T>();
return static_cast<T*>(this->GetProc<void*>(process));
}
template <typename T, typename... Args>
T Invoke(const std::string& process, Args ... args) const
{
auto method = this->Get<T(__cdecl)(Args ...)>(process);
if (method) return method(args...);
return T();
}
template <typename T, typename... Args>
T InvokePascal(const std::string& process, Args ... args) const
{
auto method = this->Get<T(__stdcall)(Args ...)>(process);
if (method) return method(args...);
return T();
}
template <typename T, typename... Args>
T InvokeThis(const std::string& process, void* this_ptr, Args ... args) const
{
auto method = this->Get<T(__thiscall)(void*, Args ...)>(this_ptr, process);
if (method) return method(args...);
return T();
}
void Free();
private: private:
HMODULE _module; HMODULE _module;

View File

@ -142,6 +142,13 @@ namespace Utils
return static_cast<T*>(Allocate(count * sizeof(T))); return static_cast<T*>(Allocate(count * sizeof(T)));
} }
template <typename T> static inline T* Duplicate(T* original)
{
T* data = Memory::Allocate<T>();
std::memcpy(data, original, sizeof(T));
return data;
}
static char* DuplicateString(const std::string& string); static char* DuplicateString(const std::string& string);
static void Free(void* data); static void Free(void* data);