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-base128` | Disable base128 encoding for minidumps. |
| `--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

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."
}
newoption {
trigger = "iw4x-zones",
description = "Zonebuilder generates iw4x zones that cannot be loaded without IW4x specific patches."
}
newaction {
trigger = "version",
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
defines { "FORCE_EXCEPTION_HANDLER" }
end
if _OPTIONS["iw4x-zones"] then
defines { "GENERATE_IW4X_SPECIFIC_ZONES" }
end
-- Pre-compiled header
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]
call AssetHandler::IsAssetEligible
add esp, 08h
add esp, 8h
mov [esp + 20h], eax
popad
@ -295,13 +295,13 @@ namespace Components
test al, al
jz doNotLoad
mov eax, [esp + 8]
mov eax, [esp + 8h]
sub esp, 14h
mov ecx, 5BB657h
jmp ecx
doNotLoad:
mov eax, [esp + 8]
mov eax, [esp + 8h]
retn
}
}

View File

@ -1,7 +1,82 @@
#include "STDInclude.hpp"
#define STB_TRUETYPE_IMPLEMENTATION
#include <stb_truetype.h>
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)
{
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)
{
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 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"},
{"wc_unlit_add", "wc_unlit_add_lin"},
{"wc_unlit_distfalloff", "wc_unlit_distfalloff_replace"},
{"wc_unlit_multiply", "wc_unlit_multiply_lin"},
{"wc_unlit_falloff_add", "wc_unlit_falloff_add_lin_ua"},
{"wc_unlit", "wc_unlit_add_lin"},
{"wc_unlit_alphatest", "wc_unlit_blend_lin_ua"},
{"wc_unlit", "wc_unlit_replace_lin"},
{"wc_unlit_alphatest", "wc_unlit_blend_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"},
{"mc_unlit_replace", "mc_unlit_replace_lin"},
@ -83,38 +84,41 @@ namespace Assets
if (asset->techniqueSet)
{
std::string techset = reader.readString();
if (!techset.empty() && techset.front() == ',') techset.erase(techset.begin());
asset->techniqueSet = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, techset.data(), builder).techniqueSet;
std::string techsetName = reader.readString();
if (!techsetName.empty() && techsetName.front() == ',') techsetName.erase(techsetName.begin());
asset->techniqueSet = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, techsetName.data(), builder).techniqueSet;
if (!asset->techniqueSet)
{
// Workaround for effect techsets having _nofog 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";
Utils::String::Replace(techset, suffix, "");
Utils::String::Replace(techsetName, suffix, "");
}
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)
{
asset->techniqueSet = techsetPtr;
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;
}
}
}
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)
{
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)
{
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->techniqueSet = iw4TechSet->asset.header.techniqueSet;

View File

@ -3,6 +3,10 @@
namespace Components
{
Dvar::Var Colors::NewColors;
Dvar::Var Colors::ColorBlind;
Game::dvar_t* Colors::ColorAllyColorBlind;
Game::dvar_t* Colors::ColorEnemyColorBlind;
std::vector<DWORD> Colors::ColorTable;
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()
{
// 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
Utils::Hook::Set<BYTE>(0x6258D0, 0xC3);
@ -239,7 +304,7 @@ namespace Components
Utils::Hook(0x4AD470, Colors::CleanStrStub, HOOK_JUMP).install()->quick();
// 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.");
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 ColorBlind;
static Game::dvar_t* ColorAllyColorBlind;
static Game::dvar_t* ColorEnemyColorBlind;
static DWORD HsvToRgb(HsvColor hsv);
@ -35,6 +38,8 @@ namespace Components
static void LookupColor(DWORD* color, char index);
static void LookupColorStub();
static char* CleanStrStub(char* string);
static bool Dvar_GetUnpackedColorByName(const char* name, float* expandedColor);
static void GetUnpackedColorByNameStub();
static std::vector<DWORD> ColorTable;
};
}

View File

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

View File

@ -60,7 +60,7 @@ namespace Components
UIScript::Add("visitWiki", [](UIScript::Token)
{
//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)

View File

@ -341,9 +341,6 @@ namespace Components
// original code
mov eax, dword ptr[esp + 0xa0];
jmp collide;
collide:
push 0x00478376;
retn;
@ -374,11 +371,6 @@ namespace Components
// dont eject if g_playerEjection is set to 0
je donteject;
// original code
cmp dword ptr[ebx + 19ch], edi;
jle eject;
eject:
push 0x005d8152;
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 procAddr = GetProcAddress(dllHandle, &function[0]);
auto version = Zones::GetEntitiesZoneVersion();
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()
@ -407,38 +419,15 @@ namespace Components
// quit_hard
Command::Add("quit_hard", [](Command::Params*)
{
typedef enum _HARDERROR_RESPONSE_OPTION {
OptionAbortRetryIgnore,
OptionOk,
OptionOkCancel,
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);
int data = false;
const Utils::Library ntdll("ntdll.dll");
ntdll.InvokePascal<void>("RtlAdjustPrivilege", 19, true, false, &data);
ntdll.InvokePascal<void>("NtRaiseHardError", 0xC000007B, 0, nullptr, nullptr, 6, &data);
});
// 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
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();

View File

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

View File

@ -25,9 +25,14 @@ namespace Components
}
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())
{
@ -71,6 +76,7 @@ namespace Components
Dvar::OnInit([]()
{
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)
@ -107,9 +113,12 @@ namespace Components
static std::string outputBuffer;
outputBuffer.clear();
#ifdef DEBUG
Logger::Print("Executing RCon request from %s: %s\n", address.getCString(), command.data());
#ifndef DEBUG
if (Dvar::Var("log_rcon_requests").get<bool>())
#endif
{
Logger::Print("Executing RCon request from %s: %s\n", address.getCString(), command.data());
}
Logger::PipeOutput([](const std::string& output)
{

View File

@ -378,7 +378,7 @@ namespace Components
Game::XFileHeader header =
{
#ifdef DEBUG
#ifndef GENERATE_IW4X_SPECIFIC_ZONES
XFILE_MAGIC_UNSIGNED,
#else
XFILE_HEADER_IW4X | (static_cast<unsigned __int64>(XFILE_VERSION_IW4X) << 32),
@ -394,7 +394,7 @@ namespace Components
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
zoneBuffer.insert(zoneBuffer.begin(), static_cast<char>(Utils::Cryptography::Rand::GenerateInt()));
@ -844,7 +844,7 @@ namespace Components
Command::Add("quit", [](Command::Params*)
{
ZoneBuilder::Quit();
Game::Com_Quitf_t();
});
Command::Add("error", [](Command::Params*)
@ -921,12 +921,6 @@ namespace Components
return 0;
}
void ZoneBuilder::Quit()
{
//TerminateProcess(GetCurrentProcess(), 0);
ExitProcess(0);
}
void ZoneBuilder::HandleError(int level, const char* format, ...)
{
char buffer[256] = { 0 };
@ -1088,9 +1082,6 @@ namespace Components
// set new entry point
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
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 int __stdcall EntryPoint(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/);
static void Quit();
static void HandleError(int level, const char* format, ...);
static void SoftErrorAssetOverflow();

View File

@ -4,10 +4,63 @@
namespace Components
{
int Zones::ZoneVersion;
int Zones::EntitiesVersion;
int Zones::FxEffectIndex;
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 previousAssetType = Game::XAssetType::ASSET_TYPE_INVALID;
@ -223,30 +276,20 @@ namespace Components
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;
if (Zones::ZoneVersion >= 332)
{
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;
}
}
}
}
}
int offsetShift = (Zones::ZoneVersion >= 461) ? 4 : 0;
// and do the stuff
Game::Load_Stream(true, varWeaponCompleteDef, size);
@ -270,7 +313,7 @@ namespace Components
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)
{
*Game::varXModelPtr = reinterpret_cast<Game::XModel**>(varWeaponCompleteDef + offset);
@ -299,7 +342,7 @@ namespace Components
if (Zones::ZoneVersion >= 359)
{
auto stringCount = (Zones::Version() == 460) ? 62 : 52;
auto stringCount = (Zones::Version() >= 460) ? 62 : 52;
auto arraySize = stringCount * 4;
// 236
@ -395,8 +438,8 @@ namespace Components
// 980
if (Zones::ZoneVersion >= 359)
{
auto offset = (Zones::Version() == 460) ? 1476 : 916;
auto count = (Zones::Version() == 460) ? 57 : 52;
auto offset = (Zones::Version() >= 460) ? 1476 : 916;
auto count = (Zones::Version() >= 461) ? 58 : (Zones::Version() >= 460) ? 57 : 52;
// 53 soundalias name references; up to and including 1124
for (int i = 0; i < count; ++i, offset += 4)
@ -809,7 +852,7 @@ namespace Components
void* vec2 = Game::DB_AllocStreamPos(3);
*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);
@ -820,7 +863,7 @@ namespace Components
void* vec2 = Game::DB_AllocStreamPos(3);
*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)
@ -868,22 +911,22 @@ namespace Components
if (Zones::Version() >= 460)
{
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3288);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3288 + offsetShift);
Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3292);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3292 + offsetShift);
Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3324);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3324 + offsetShift);
Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3328);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3328 + offsetShift);
Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3484);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3484 + offsetShift);
Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3488);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3488 + offsetShift);
Game::Load_XString(false);
}
else if (Zones::ZoneVersion >= 359)
@ -929,37 +972,37 @@ namespace Components
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::varTracerDefPtr = reinterpret_cast<Game::TracerDef * *>(varWeaponCompleteDef + 3496);
*Game::varTracerDefPtr = reinterpret_cast<Game::TracerDef * *>(varWeaponCompleteDef + 3496 + offsetShift);
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::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::varFxEffectDefHandle = reinterpret_cast<Game::FxEffectDef * *>(varWeaponCompleteDef + 3532);
*Game::varFxEffectDefHandle = reinterpret_cast<Game::FxEffectDef * *>(varWeaponCompleteDef + 3532 + offsetShift);
Game::Load_FxEffectDefHandle(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3536);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3536 + offsetShift);
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::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::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::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::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);
}
else if (Zones::ZoneVersion >= 359)
@ -1023,7 +1066,7 @@ namespace Components
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::Load_SndAliasCustom(*Game::varsnd_alias_list_name);
@ -1058,25 +1101,25 @@ namespace Components
if (Zones::Version() >= 460)
{
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3712);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3712 + offsetShift);
Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3728);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3728 + offsetShift);
Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3732);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3732 + offsetShift);
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::varMaterialHandle = reinterpret_cast<Game::Material * *>(varWeaponCompleteDef + 3744);
*Game::varMaterialHandle = reinterpret_cast<Game::Material * *>(varWeaponCompleteDef + 3744 + offsetShift);
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::varMaterialHandle = reinterpret_cast<Game::Material * *>(varWeaponCompleteDef + 3752);
*Game::varMaterialHandle = reinterpret_cast<Game::Material * *>(varWeaponCompleteDef + 3752 + offsetShift);
Game::Load_MaterialHandle(false);
}
else if (Zones::ZoneVersion >= 359)
@ -1127,35 +1170,35 @@ namespace Components
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);
*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);
*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::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3880);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3880 + offsetShift);
Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3884);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3884 + offsetShift);
Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3996);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 3996 + offsetShift);
Game::Load_XString(false);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 4012);
*Game::varXString = reinterpret_cast<char**>(varWeaponCompleteDef + 4012 + offsetShift);
Game::Load_XString(false);
}
else if (Zones::ZoneVersion >= 359)
@ -1381,7 +1424,7 @@ namespace Components
char* varWeaponAttach = *reinterpret_cast<char**>(0x112ADE0); // varAddonMapEnts
// and do the stuff
if (Zones::Version() >= 448)
if (Zones::Version() >= 446)
{
Game::Load_Stream(true, varWeaponAttach, 20);
@ -1426,7 +1469,7 @@ namespace Components
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);
Game::MaterialPass* curPass = *Game::varMaterialPass;
@ -1436,13 +1479,13 @@ namespace Components
{
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)
{
continue;
}
if (Zones::Version() < 446)
{
// should be min 68 currently
// >= 58 fixes foliage without bad side effects
// >= 53 still has broken shadow mapping
@ -1489,7 +1532,100 @@ namespace Components
{
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 = {
{ 22, 21 },
@ -1741,8 +1877,8 @@ namespace Components
{
// 359 and above adds an extra remapped techset ptr
if (Zones::ZoneVersion >= 359) size += 4;
// 448 amd above adds an additional technique
if (Zones::ZoneVersion >= 448) size += 4;
// 446 amd above adds an additional technique
if (Zones::ZoneVersion >= 446) size += 4;
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
int shiftTest = 4;
std::memmove(buffer + 8 + shiftTest, buffer + 12 + shiftTest, (Zones::Version() >= 448) ? 200 : 196 - shiftTest);
AssetHandler::Relocate(buffer + 12 + shiftTest, buffer + 8 + 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() >= 446) ? 200 : 196 - shiftTest);
}
return result;
}
int Zones::LoadMaterialTechniqueArray(bool atStreamStart, int count)
{
if (Zones::Version() >= 448)
if (Zones::Version() >= 446)
{
count += 1;
}
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 varMaterialTechniqueSet = **reinterpret_cast<Game::MaterialTechniqueSet * **>(0x112B070);
@ -1783,11 +1919,11 @@ namespace Components
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];
memcpy(codol_material, buffer, 104);
@ -1990,7 +2126,7 @@ namespace Components
void Zones::LoadImpactFx(bool atStreamStart, char* buffer, int size)
{
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;
Game::Load_Stream(atStreamStart, buffer, size);
@ -2007,7 +2143,7 @@ namespace Components
int Zones::ImpactFxArrayCount()
{
if (Zones::Version() >= 448)
if (Zones::Version() >= 446)
{
return 19;
}
@ -2170,7 +2306,7 @@ namespace Components
int Zones::LoadRandomFxGarbage(bool atStreamStart, char* buffer, int size)
{
int count = 0;
if (Zones::Version() >= 448)
if (Zones::Version() >= 446)
{
size /= 48;
count = size;
@ -2179,7 +2315,7 @@ namespace Components
const auto retval = Game::Load_Stream(atStreamStart, buffer, size);
if (Zones::Version() >= 448)
if (Zones::Version() >= 446)
{
for (auto i = 0; i < count; i++)
{
@ -2258,7 +2394,7 @@ namespace Components
{
int count = 0;
if (Zones::Version() >= 448)
if (Zones::Version() >= 446)
{
size /= 12;
count = size;
@ -2267,7 +2403,7 @@ namespace Components
auto retval = Game::Load_Stream(atStreamStart, buffer, size);
if (Zones::Version() >= 448)
if (Zones::Version() >= 446)
{
for (int i = 0; i < count; i++)
{
@ -2332,8 +2468,8 @@ namespace Components
cmp dword ptr[eax], 0xFFFFFFFF;
je loadAssetData;
// check if FF is below 448, still load data in that case
cmp Zones::ZoneVersion, 448;
// check if FF is below 446, still load data in that case
cmp Zones::ZoneVersion, 446;
jl loadAssetData;
// offset to pointer magic
@ -2426,7 +2562,9 @@ namespace Components
int Zones::LoadMapEnts(bool atStreamStart, Game::MapEnts* buffer, int size)
{
if (Zones::Version() >= 448)
EntitiesVersion = Zones::Version();
if (Zones::Version() >= 446)
{
size /= 44;
size *= 36;
@ -2583,7 +2721,7 @@ namespace Components
int Zones::LoadClipMap(bool atStreamStart)
{
if (Zones::Version() >= 448)
if (Zones::Version() >= 446)
{
AssertOffset(codolClipMap_t, pInfo, 72);
@ -3139,8 +3277,8 @@ namespace Components
cmp dword ptr[edx + 4], 0xFFFFFFFF;
je loadAssetData;
// check if FF is below 448, still load data in that case
cmp Zones::ZoneVersion, 448;
// check if FF is below 446, still load data in that case
cmp Zones::ZoneVersion, 446;
jl loadAssetData;
// offset to pointer magic
@ -3171,8 +3309,8 @@ namespace Components
cmp dword ptr[eax + 0Ch], 0xFFFFFFFF;
je loadAssetData;
// check if FF is below 448, still load data in that case
cmp Zones::ZoneVersion, 448;
// check if FF is below 446, still load data in that case
cmp Zones::ZoneVersion, 446;
jl loadAssetData;
// offset to pointer magic
@ -3203,8 +3341,8 @@ namespace Components
cmp dword ptr[eax + 14h], 0xFFFFFFFF;
je loadAssetData;
// check if FF is below 448, still load data in that case
cmp Zones::ZoneVersion, 448;
// check if FF is below 446, still load data in that case
cmp Zones::ZoneVersion, 446;
jl loadAssetData;
// offset to pointer magic
@ -3273,11 +3411,11 @@ namespace Components
void Zones::LoadXModelAsset(Game::XModel** asset)
{
if (Zones::Version() >= 448)
if (Zones::Version() >= 446)
{
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 fx_model = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_XMODEL, "void").model;
@ -3303,7 +3441,7 @@ namespace Components
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 = {
"gfx_fxt_debris_wind_ash_z10",
@ -3353,6 +3491,29 @@ namespace Components
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::ZoneVersion = 0;
@ -3499,6 +3660,9 @@ namespace Components
Utils::Hook(0x418998, Zones::GameMapSpPatchStub, 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, [] ()
{
if (Zones::ZoneVersion >= VERSION_ALPHA3)
@ -3521,6 +3685,10 @@ namespace Components
Utils::Hook(0x4597DD, Zones::LoadStatement, HOOK_CALL).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
// Easy dirty disk debugging
Utils::Hook::Set<WORD>(0x4CF7F0, 0xC3CC);

View File

@ -3,6 +3,7 @@
#define VERSION_ALPHA2 316
#define VERSION_ALPHA3 318//319
#define VERSION_ALPHA3_DEC 319
#define VERSION_LATEST_CODO 461
namespace Components
{
@ -23,9 +24,12 @@ namespace Components
static int Version() { return Zones::ZoneVersion; };
static int GetEntitiesZoneVersion() { return Zones::EntitiesVersion; };
private:
static int ZoneVersion;
static int EntitiesVersion;
static int FxEffectIndex;
static char* FxEffectStrings[64];
@ -101,5 +105,7 @@ namespace Components
static void LoadTracerDef(bool atStreamStart, Game::TracerDef* tracer, int size);
static void LoadTracerDefFxEffect();
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_MatchToken_t Com_MatchToken = Com_MatchToken_t(0x447130);
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_DrawSolidConsole_t Con_DrawSolidConsole = Con_DrawSolidConsole_t(0x5A5040);
@ -99,11 +100,11 @@ namespace Game
Dvar_RegisterFloat_t Dvar_RegisterFloat = Dvar_RegisterFloat_t(0x648440);
Dvar_RegisterVec2_t Dvar_RegisterVec2 = Dvar_RegisterVec2_t(0x4F6070);
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_RegisterEnum_t Dvar_RegisterEnum = Dvar_RegisterEnum_t(0x412E40);
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_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);
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);
extern Con_DrawMiniConsole_t Con_DrawMiniConsole;

View File

@ -3049,7 +3049,7 @@ namespace Game
float value;
float vector[4];
const char *string;
char color[4];
unsigned char color[4];
};
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)
// Submodules
// Ignore the warnings, it's no our code!
// Ignore the warnings, it's not our code!
#pragma warning(push)
#pragma warning(disable: 4005)
#pragma warning(disable: 4091)
@ -152,6 +152,7 @@ template <size_t S> class Sizer { };
#pragma comment(lib, "Advapi32.lib")
#pragma comment(lib, "rpcrt4.lib")
#pragma comment(lib, "dbghelp.lib")
#pragma comment(lib, "ntdll.lib")
// Enable additional literals
using namespace std::literals;

View File

@ -384,25 +384,25 @@ namespace Steam
Proxy::LaunchWatchGuard();
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);
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;
Proxy::SteamBGetCallback = Proxy::Client.get<Proxy::SteamBGetCallbackFn>("Steam_BGetCallback");
Proxy::SteamBGetCallback = Proxy::Client.Get<Proxy::SteamBGetCallbackFn>("Steam_BGetCallback");
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;
Proxy::SteamGetAPICallResult = Proxy::Client.get<Proxy::SteamGetAPICallResultFn>("Steam_GetAPICallResult");
Proxy::SteamGetAPICallResult = Proxy::Client.Get<Proxy::SteamGetAPICallResultFn>("Steam_GetAPICallResult");
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;
Proxy::SteamPipe = Proxy::SteamClient->CreateSteamPipe();
@ -411,7 +411,7 @@ namespace Steam
Proxy::SteamUser = Proxy::SteamClient->ConnectToGlobalUser(Proxy::SteamPipe);
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;
Proxy::ClientUser = Proxy::ClientEngine->GetIClientUser(Proxy::SteamUser, Proxy::SteamPipe);
@ -526,17 +526,17 @@ namespace Steam
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()
{
if (Proxy::Overlay.valid())
if (Proxy::Overlay.IsValid())
{
return Proxy::Overlay.get<bool()>("IsOverlayEnabled")();
return Proxy::Overlay.Get<bool()>("IsOverlayEnabled")();
}
return false;
@ -544,9 +544,9 @@ namespace Steam
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;

View File

@ -2,34 +2,63 @@
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)
{
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()
{
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;
}
void Library::free()
void Library::Free()
{
if (this->valid())
if (this->IsValid())
{
FreeLibrary(this->getModule());
FreeLibrary(this->_module);
}
this->_module = nullptr;

View File

@ -5,25 +5,58 @@ namespace Utils
class Library
{
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(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();
bool valid();
HMODULE getModule();
bool IsValid() const;
HMODULE GetModule();
template <typename T>
std::function<T> get(const std::string& process)
T GetProc(const std::string& process) const
{
if (!this->valid())
{
throw std::runtime_error("Library not loaded!");
if (!this->IsValid()) T{};
return reinterpret_cast<T>(GetProcAddress(this->_module, process.data()));
}
return reinterpret_cast<T*>(GetProcAddress(this->getModule(), process.data()));
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));
}
void free();
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:
HMODULE _module;

View File

@ -142,6 +142,13 @@ namespace Utils
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 void Free(void* data);