From 6f7ebb43b1b52bcdb70092566f2cf4a671e03c04 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 5 Jun 2017 00:00:46 +0200 Subject: [PATCH 1/5] [Friends] Steam avatars --- src/Components/Modules/Friends.cpp | 66 ++++++++++++++++++++++++++ src/Components/Modules/Friends.hpp | 8 ++++ src/Components/Modules/Materials.cpp | 71 ++++++++++++++++++++++++++++ src/Components/Modules/Materials.hpp | 6 +++ src/Game/Functions.cpp | 25 ++++++++++ src/Game/Functions.hpp | 2 + 6 files changed, 178 insertions(+) diff --git a/src/Components/Modules/Friends.cpp b/src/Components/Modules/Friends.cpp index 2bd98479..0d77918c 100644 --- a/src/Components/Modules/Friends.cpp +++ b/src/Components/Modules/Friends.cpp @@ -554,6 +554,13 @@ namespace Components Command::Execute("openmenu popup_friends", true); }, HOOK_JUMP).install()->quick(); + // Avatar callback +// Steam::Proxy::RegisterCallback(333, [](void* data) +// { +// MessageBoxA(0, 0, 0, 0); +// +// }); + // Callback to update user information Steam::Proxy::RegisterCallback(336, [](void* data) { @@ -687,6 +694,64 @@ namespace Components Friends::UpdateFriends(); }); + + Command::Add("testavatar", [](Command::Params*) + { + std::lock_guard _(Friends::Mutex); + + for(auto user : Friends::FriendsList) + { + Logger::Print("Fetching %s...\n", user.name.data()); + int index = Steam::Proxy::SteamFriends->GetSmallFriendAvatar(user.userId); + + + + if (!Steam::Proxy::SteamUtils) return; + + static Game::GfxImage image = { nullptr }; + if (image.map) Game::Image_Release(&image); + static Game::Material* material = nullptr; + + unsigned int width, height; + Steam::Proxy::SteamUtils->GetImageSize(index, &width, &height); + Game::Image_Setup(&image, static_cast(width), static_cast(height), 1, 0x1000003, D3DFMT_A8R8G8B8); + + D3DLOCKED_RECT lockedRect; + image.map->LockRect(0, &lockedRect, nullptr, 0); + + unsigned char* buffer = static_cast(lockedRect.pBits); + + Steam::Proxy::SteamUtils->GetImageRGBA(index, buffer, width * height * 4); + + for (unsigned int i = 0; i < width * height * 4; i += 4) + { + unsigned char r = buffer[i + 0]; + unsigned char g = buffer[i + 1]; + unsigned char b = buffer[i + 2]; + unsigned char a = buffer[i + 3]; + + buffer[i + 0] = a; + buffer[i + 1] = r; + buffer[i + 2] = g; + buffer[i + 3] = b; + } + + image.map->UnlockRect(0); + + if (!material) + { + material = Materials::Create("avatar", &image); + + Scheduler::OnFrame([]() + { + float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + Game::CL_DrawStretchPicPhysical(10.0f, 10.0f, 100.0f, 100.0f, 0, 0, 1.0f, 1.0f, color, material); + }); + } + + break; + } + }); } Friends::~Friends() @@ -695,6 +760,7 @@ namespace Components Friends::StoreFriendsList(); + //Steam::Proxy::UnregisterCallback(333); Steam::Proxy::UnregisterCallback(336); Steam::Proxy::UnregisterCallback(304); diff --git a/src/Components/Modules/Friends.hpp b/src/Components/Modules/Friends.hpp index 5c4dfb4b..34ae5712 100644 --- a/src/Components/Modules/Friends.hpp +++ b/src/Components/Modules/Friends.hpp @@ -32,6 +32,14 @@ namespace Components }; #pragma pack(pop) + struct AvatarImageLoaded + { + SteamID m_steamID; // steamid the avatar has been loaded for + int m_iImage; // the image index of the now loaded image + int m_iWide; // width of the loaded image + int m_iTall; // height of the loaded image + }; + struct PersonaStateChange { SteamID m_ulSteamID; // steamID of the friend who changed diff --git a/src/Components/Modules/Materials.cpp b/src/Components/Modules/Materials.cpp index b79c0a26..07591916 100644 --- a/src/Components/Modules/Materials.cpp +++ b/src/Components/Modules/Materials.cpp @@ -5,6 +5,75 @@ namespace Components int Materials::ImageNameLength; Utils::Hook Materials::ImageVersionCheckHook; + std::vector Materials::MaterialTable; + + Game::Material* Materials::Create(std::string name, Game::GfxImage* image) + { + Game::Material* material = Utils::Memory::GetAllocator()->allocate(); + Game::MaterialTextureDef* texture = Utils::Memory::GetAllocator()->allocate(); + + material->textureCount = 1; + material->textureTable = texture; + + material->name = Utils::Memory::GetAllocator()->duplicateString(name); + material->sortKey = 0x22; + material->textureAtlasColumnCount = 1; + material->textureAtlasRowCount = 1; + + for(int i = 0; i < 48; ++i) + { + if(i != 4) material->stateBitsEntry[i] = -1; + } + + material->stateFlags = 3; + material->cameraRegion = 4; + material->techniqueSet = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_TECHNIQUE_SET, "2d").techniqueSet; + + material->textureTable->nameHash = Game::R_HashString("colorMap"); + material->textureTable->nameStart = 'c'; + material->textureTable->nameEnd = 'p'; + material->textureTable->sampleState = -30; + material->textureTable->info.image = image; + + Game::Material* cursor = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_MATERIAL, "ui_cursor").material; + if(cursor) + { + material->stateBitTable = cursor->stateBitTable; + material->stateBitsCount = cursor->stateBitsCount; + } + + Materials::MaterialTable.push_back(material); + + return material; + } + + void Materials::Delete(Game::Material* material) + { + if (!material) return; + + auto mat = std::find(Materials::MaterialTable.begin(), Materials::MaterialTable.end(), material); + if(mat != Materials::MaterialTable.end()) + { + Materials::MaterialTable.erase(mat); + } + + Utils::Memory::GetAllocator()->free(material->textureTable); + Utils::Memory::GetAllocator()->free(material->name); + Utils::Memory::GetAllocator()->free(material); + } + + void Materials::DeleteAll() + { + std::vector materials; + Utils::Merge(&materials, Materials::MaterialTable); + Materials::MaterialTable.clear(); + + for(auto& material : materials) + { + Materials::Delete(material); + } + } + __declspec(naked) void Materials::ImageVersionCheck() { __asm @@ -198,6 +267,8 @@ namespace Components Materials::~Materials() { + Materials::DeleteAll(); + Materials::ImageVersionCheckHook.uninstall(); } } diff --git a/src/Components/Modules/Materials.hpp b/src/Components/Modules/Materials.hpp index fe9ccd6a..0e6cd392 100644 --- a/src/Components/Modules/Materials.hpp +++ b/src/Components/Modules/Materials.hpp @@ -10,7 +10,11 @@ namespace Components static int FormatImagePath(char* buffer, size_t size, int, int, const char* image); + static Game::Material* Create(std::string name, Game::GfxImage* image); + static void Delete(Game::Material* material); + private: + static std::vector MaterialTable; static int ImageNameLength; static Utils::Hook ImageVersionCheckHook; @@ -29,5 +33,7 @@ namespace Components #endif static int MaterialComparePrint(Game::Material* m1, Game::Material* m2); + + static void DeleteAll(); }; } diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index b3a3efbc..4d854e73 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -870,6 +870,31 @@ namespace Game return atoi(StringTable_Lookup(rankTable, 0, maxrank, 7)); } + __declspec(naked) void Image_Setup(GfxImage* /*image*/, unsigned int /*width*/, unsigned int /*height*/, unsigned int /*depth*/, unsigned int /*flags*/, int /*format*/) + { + __asm + { + pushad + xor edi, edi + + mov eax, [esp + 24h] // image + mov di, word ptr [esp + 28h] // width + push [esp + 38h] // format + push [esp + 38h] // flags + push 0 + push [esp + 3Ch] // depth + push [esp + 3Ch] // height + + mov ecx, 54AF50h + call ecx + + add esp, 14h + + popad + retn + } + } + void SortWorldSurfaces(GfxWorld* world) { DWORD* specular1 = reinterpret_cast(0x69F105C); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 68471344..cb7f79f5 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -845,6 +845,8 @@ namespace Game int CL_GetMaxXP(); + void Image_Setup(GfxImage* image, unsigned int width, unsigned int height, unsigned int depth, unsigned int flags, int format); + void SortWorldSurfaces(GfxWorld* world); void R_AddDebugLine(float* color, float* v1, float* v2); void R_AddDebugString(float *color, float *pos, float scale, const char *str); From 6cc4fa582aa5a3395ef8a7ff19dc01dce77fd510 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 5 Jun 2017 12:53:26 +0200 Subject: [PATCH 2/5] [Friends] First version of working avatars --- src/Components/Modules/Colors.cpp | 2 +- src/Components/Modules/Friends.cpp | 60 ++-- src/Components/Modules/Friends.hpp | 2 + src/Components/Modules/Materials.cpp | 30 +- src/Components/Modules/Materials.hpp | 5 +- src/Components/Modules/Toast.cpp | 14 +- src/Components/Modules/Toast.hpp | 9 +- src/Game/Functions.cpp | 468 +++++++++++++-------------- src/Game/Functions.hpp | 2 +- 9 files changed, 325 insertions(+), 267 deletions(-) diff --git a/src/Components/Modules/Colors.cpp b/src/Components/Modules/Colors.cpp index b549c198..82695c9c 100644 --- a/src/Components/Modules/Colors.cpp +++ b/src/Components/Modules/Colors.cpp @@ -195,7 +195,7 @@ namespace Components __asm { push ebx - push[esp + 8h] // Index + push [esp + 8h] // Index push esi // Color ref call Colors::LookupColor add esp, 8h diff --git a/src/Components/Modules/Friends.cpp b/src/Components/Modules/Friends.cpp index 0d77918c..d19690ab 100644 --- a/src/Components/Modules/Friends.cpp +++ b/src/Components/Modules/Friends.cpp @@ -117,7 +117,11 @@ namespace Components int notify = Dvar::Var("cl_notifyFriendState").get(); if(gotOnline && (notify == -1 || (notify == 1 && !Game::CL_IsCgameInitialized())) && !Dvar::Var("ui_streamFriendly").get()) { - Toast::Show("cardicon_joystick", entry->name, "is playing IW4x", 3000); + Game::Material* material = Friends::CreateAvatar(user); + Toast::Show(material, entry->name, "is playing IW4x", 3000, [material]() + { + Materials::Delete(material, true); + }); } } @@ -530,6 +534,34 @@ namespace Components Utils::IO::WriteFile("players/friends.dat", list.SerializeAsString()); } + Game::Material* Friends::CreateAvatar(SteamID user) + { + if (!Steam::Proxy::SteamUtils || !Steam::Proxy::SteamFriends) return nullptr; + + int index = Steam::Proxy::SteamFriends->GetMediumFriendAvatar(user); + + unsigned int width, height; + Steam::Proxy::SteamUtils->GetImageSize(index, &width, &height); + + Game::GfxImage* image = Materials::CreateImage(Utils::String::VA("texture_%llX", user.bits), width, height, 1, 0x1000003, D3DFMT_A8R8G8B8); + + D3DLOCKED_RECT lockedRect; + image->map->LockRect(0, &lockedRect, nullptr, 0); + + unsigned char* buffer = static_cast(lockedRect.pBits); + Steam::Proxy::SteamUtils->GetImageRGBA(index, buffer, width * height * 4); + + // Swap red and blue channel + for (unsigned int i = 0; i < width * height * 4; i += 4) + { + std::swap(buffer[i + 0], buffer[i + 2]); + } + + image->map->UnlockRect(0); + + return Materials::Create(Utils::String::VA("avatar_%llX", user.bits), image); + } + Friends::Friends() { Friends::LoggedOn = false; @@ -554,13 +586,6 @@ namespace Components Command::Execute("openmenu popup_friends", true); }, HOOK_JUMP).install()->quick(); - // Avatar callback -// Steam::Proxy::RegisterCallback(333, [](void* data) -// { -// MessageBoxA(0, 0, 0, 0); -// -// }); - // Callback to update user information Steam::Proxy::RegisterCallback(336, [](void* data) { @@ -699,11 +724,10 @@ namespace Components { std::lock_guard _(Friends::Mutex); - for(auto user : Friends::FriendsList) + for (auto user : Friends::FriendsList) { Logger::Print("Fetching %s...\n", user.name.data()); - int index = Steam::Proxy::SteamFriends->GetSmallFriendAvatar(user.userId); - + int index = Steam::Proxy::SteamFriends->GetMediumFriendAvatar(user.userId); if (!Steam::Proxy::SteamUtils) return; @@ -715,25 +739,18 @@ namespace Components unsigned int width, height; Steam::Proxy::SteamUtils->GetImageSize(index, &width, &height); Game::Image_Setup(&image, static_cast(width), static_cast(height), 1, 0x1000003, D3DFMT_A8R8G8B8); + Logger::Print("%dx%d\n", width, height); D3DLOCKED_RECT lockedRect; image.map->LockRect(0, &lockedRect, nullptr, 0); unsigned char* buffer = static_cast(lockedRect.pBits); - Steam::Proxy::SteamUtils->GetImageRGBA(index, buffer, width * height * 4); + // Swap red and blue channel for (unsigned int i = 0; i < width * height * 4; i += 4) { - unsigned char r = buffer[i + 0]; - unsigned char g = buffer[i + 1]; - unsigned char b = buffer[i + 2]; - unsigned char a = buffer[i + 3]; - - buffer[i + 0] = a; - buffer[i + 1] = r; - buffer[i + 2] = g; - buffer[i + 3] = b; + std::swap(buffer[i + 0], buffer[i + 2]); } image.map->UnlockRect(0); @@ -760,7 +777,6 @@ namespace Components Friends::StoreFriendsList(); - //Steam::Proxy::UnregisterCallback(333); Steam::Proxy::UnregisterCallback(336); Steam::Proxy::UnregisterCallback(304); diff --git a/src/Components/Modules/Friends.hpp b/src/Components/Modules/Friends.hpp index 34ae5712..00f0c447 100644 --- a/src/Components/Modules/Friends.hpp +++ b/src/Components/Modules/Friends.hpp @@ -95,5 +95,7 @@ namespace Components static void SetRawPresence(const char* key, const char* value); static std::vector GetAppIdList(); + + static Game::Material* CreateAvatar(SteamID user); }; } diff --git a/src/Components/Modules/Materials.cpp b/src/Components/Modules/Materials.cpp index 07591916..f303ca65 100644 --- a/src/Components/Modules/Materials.cpp +++ b/src/Components/Modules/Materials.cpp @@ -47,7 +47,7 @@ namespace Components return material; } - void Materials::Delete(Game::Material* material) + void Materials::Delete(Game::Material* material, bool deleteImage) { if (!material) return; @@ -57,11 +57,39 @@ namespace Components Materials::MaterialTable.erase(mat); } + if (deleteImage) + { + for (char i = 0; i < material->textureCount; ++i) + { + Materials::DeleteImage(material->textureTable[i].info.image); + } + } + Utils::Memory::GetAllocator()->free(material->textureTable); Utils::Memory::GetAllocator()->free(material->name); Utils::Memory::GetAllocator()->free(material); } + Game::GfxImage* Materials::CreateImage(std::string name, unsigned int width, unsigned int height, unsigned int depth, unsigned int flags, _D3DFORMAT format) + { + Game::GfxImage* image = Utils::Memory::GetAllocator()->allocate(); + image->name = Utils::Memory::GetAllocator()->duplicateString(name); + + Game::Image_Setup(image, width, height, depth, flags, format); + + return image; + } + + void Materials::DeleteImage(Game::GfxImage* image) + { + if (!image) return; + + Game::Image_Release(image); + + Utils::Memory::GetAllocator()->free(image->name); + Utils::Memory::GetAllocator()->free(image); + } + void Materials::DeleteAll() { std::vector materials; diff --git a/src/Components/Modules/Materials.hpp b/src/Components/Modules/Materials.hpp index 0e6cd392..02d66ae2 100644 --- a/src/Components/Modules/Materials.hpp +++ b/src/Components/Modules/Materials.hpp @@ -11,7 +11,10 @@ namespace Components static int FormatImagePath(char* buffer, size_t size, int, int, const char* image); static Game::Material* Create(std::string name, Game::GfxImage* image); - static void Delete(Game::Material* material); + static void Delete(Game::Material* material, bool deleteImage = false); + + static Game::GfxImage* CreateImage(std::string name, unsigned int width, unsigned int height, unsigned int depth, unsigned int flags, _D3DFORMAT format); + static void DeleteImage(Game::GfxImage* image); private: static std::vector MaterialTable; diff --git a/src/Components/Modules/Toast.cpp b/src/Components/Modules/Toast.cpp index b544bcb0..12dbbc13 100644 --- a/src/Components/Modules/Toast.cpp +++ b/src/Components/Modules/Toast.cpp @@ -7,10 +7,16 @@ namespace Components Toast::WinToastHandler* Toast::ToastHandler = nullptr; - void Toast::Show(std::string image, std::string title, std::string description, int length) + void Toast::Show(std::string image, std::string title, std::string description, int length, Utils::Slot callback) + { + Game::Material* material = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, image.data()).material; + return Show(material, title, description, length, callback); + } + + void Toast::Show(Game::Material* material, std::string title, std::string description, int length, Utils::Slot callback) { Toast::Mutex.lock(); - Toast::Queue.push({ image, Utils::String::ToUpper(title), description, length, 0 }); + Toast::Queue.push({ material, Utils::String::ToUpper(title), description, length, 0, callback }); Toast::Mutex.unlock(); } @@ -54,7 +60,6 @@ namespace Components float descSize = 0.9f; Game::Material* white = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, "white").material; - Game::Material* image = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, toast->image.data()).material; Game::Font* font = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FONT, "fonts/objectiveFont").font; Game::Font* descfont = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FONT, "fonts/normalFont").font; Game::vec4_t wColor = { 1.0f, 1.0f, 1.0f, 1.0f }; @@ -111,7 +116,7 @@ namespace Components Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2), static_cast(height + bHeight / 2), bWidth * 1.0f, border * 1.0f, 0, 0, 1.0f, 1.0f, borderColor, white); // Bottom // Image - Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2 + iOffsetLeft), static_cast(height - bHeight / 2 + iOffset), imgDim * 1.0f, imgDim * 1.0f, 0, 0, 1.0f, 1.0f, wColor, image); + Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2 + iOffsetLeft), static_cast(height - bHeight / 2 + iOffset), imgDim * 1.0f, imgDim * 1.0f, 0, 0, 1.0f, 1.0f, wColor, toast->image); // Text float leftText = width / 2 - bWidth / 2 - cornerSize + iOffsetLeft * 2 + imgDim; @@ -136,6 +141,7 @@ namespace Components if ((toast->start + toast->length) < Game::Sys_Milliseconds()) { + if(toast->callback) toast->callback(); Toast::Queue.pop(); } else diff --git a/src/Components/Modules/Toast.hpp b/src/Components/Modules/Toast.hpp index dc59ec0f..03559435 100644 --- a/src/Components/Modules/Toast.hpp +++ b/src/Components/Modules/Toast.hpp @@ -11,7 +11,8 @@ namespace Components typedef WinToastLib::WinToastTemplate Template; - static void Show(std::string image, std::string title, std::string description, int length); + static void Show(std::string image, std::string title, std::string description, int length, Utils::Slot callback = Utils::Slot()); + static void Show(Game::Material* material, std::string title, std::string description, int length, Utils::Slot callback = Utils::Slot()); static bool ShowNative(const WinToastLib::WinToastTemplate& toast); static std::string GetIcon(); @@ -20,14 +21,16 @@ namespace Components class UIToast { public: - std::string image; + Game::Material* image; std::string title; std::string desc; int length; int start; + Utils::Slot callback; }; - class WinToastHandler: public WinToastLib::IWinToastHandler { + class WinToastHandler: public WinToastLib::IWinToastHandler + { public: void toastActivated() const override {}; void toastDismissed(WinToastLib::IWinToastHandler::WinToastDismissalReason /*state*/) const override {}; diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 4d854e73..20ce5ed8 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -394,19 +394,6 @@ namespace Game return poolEntry; } - __declspec(naked) void Menu_FreeItemMemory(Game::itemDef_t* /*item*/) - { - __asm - { - pushad - mov edi, [esp + 24h] - mov eax, 63D880h - call eax - popad - retn - } - } - const char* TableLookup(StringTable* stringtable, int row, int column) { if (!stringtable || !stringtable->values || row >= stringtable->rowCount || column >= stringtable->columnCount) return ""; @@ -454,24 +441,6 @@ namespace Game return gameType; } - __declspec(naked) float UI_GetScoreboardLeft(void* /*a1*/) - { - __asm - { - push eax - pushad - - mov ecx, 590390h - mov eax, [esp + 28h] - call ecx - mov[esp + 20h], eax - popad - pop eax - - retn - } - } - const char *DB_GetXAssetName(XAsset *asset) { if (!asset) return ""; @@ -572,6 +541,219 @@ namespace Game if(lock) InterlockedDecrement(lockVar); } + // this cant be MessageBox because windows.h has a define that converts it to MessageBoxW. which is just stupid + void ShowMessageBox(std::string message, std::string title) + { + if (!Game::CL_IsCgameInitialized()) + { + Dvar_SetStringByName("com_errorMessage", message.data()); + Dvar_SetStringByName("com_errorTitle", title.data()); + Cbuf_AddText(0, "openmenu error_popmenu_lobby"); + } + } + + unsigned int R_HashString(const char* string) + { + unsigned int hash = 0; + + while (*string) + { + hash = (*string | 0x20) ^ (33 * hash); + ++string; + } + + return hash; + } + + void SV_KickClientError(client_t* client, std::string reason) + { + if (client->state < 5) + { + Components::Network::SendCommand(client->addr, "error", reason); + } + + SV_KickClient(client, reason.data()); + } + + void Scr_iPrintLn(int clientNum, std::string message) + { + Game::SV_GameSendServerCommand(clientNum, 0, Utils::String::VA("%c \"%s\"", 0x66, message.data())); + } + + void Scr_iPrintLnBold(int clientNum, std::string message) + { + Game::SV_GameSendServerCommand(clientNum, 0, Utils::String::VA("%c \"%s\"", 0x67, message.data())); + } + + int FS_FOpenFileReadCurrentThread(const char* file, int* fh) + { + if (GetCurrentThreadId() == *reinterpret_cast(0x1CDE7FC)) + { + return FS_FOpenFileRead(file, fh); + } + else if (GetCurrentThreadId() == *reinterpret_cast(0x1CDE814)) + { + return FS_FOpenFileReadDatabase(file, fh); + } + else + { + *fh = NULL; + return -1; + } + } + + void Load_IndexBuffer(void* data, IDirect3DIndexBuffer9** storeHere, int count) + { + if (Components::Dvar::Var("r_loadForRenderer").get()) + { + void* buffer = R_AllocStaticIndexBuffer(storeHere, 2 * count); + std::memcpy(buffer, data, 2 * count); + + if (storeHere && *storeHere) + { + (*storeHere)->Unlock(); + } + } + } + + char* Com_GetParseThreadInfo() + { + if (Game::Sys_IsMainThread()) + { + return reinterpret_cast(0x6466628); + } + else if (Game::Sys_IsRenderThread()) + { + return reinterpret_cast(0x646AC34); + } + else if (Game::Sys_IsServerThread()) + { + return reinterpret_cast(0x646F240); + } + else if(Game::Sys_IsDatabaseThread()) + { + return reinterpret_cast(0x647384C); + } + else + { + return nullptr; + } + } + + void Com_SetParseNegativeNumbers(int parse) + { + char* g_parse = Com_GetParseThreadInfo(); + + if (g_parse) + { + g_parse[1056 * *(reinterpret_cast(g_parse) + 4224) + 1032] = parse != 0; + } + } + + int CL_GetMaxXP() + { + StringTable* rankTable = DB_FindXAssetHeader(ASSET_TYPE_STRINGTABLE, "mp/rankTable.csv").stringTable; + const char* maxrank = StringTable_Lookup(rankTable, 0, "maxrank", 1); + return atoi(StringTable_Lookup(rankTable, 0, maxrank, 7)); + } + + void SortWorldSurfaces(GfxWorld* world) + { + DWORD* specular1 = reinterpret_cast(0x69F105C); + DWORD* specular2 = reinterpret_cast(0x69F92D4); + DWORD saveSpecular1 = *specular1; + DWORD saveSpecular2 = *specular2; + + GfxWorld** gameWorld = reinterpret_cast(0x66DEE94); + GfxWorld* saveWorld = *gameWorld; + + *specular1 = 1; + *specular2 = 1; + *gameWorld = world; + + R_SortWorldSurfaces(); + + *gameWorld = saveWorld; + *specular1 = saveSpecular1; + *specular2 = saveSpecular2; + } + + void R_AddDebugBounds(float* color, Bounds* b) + { + Game::vec3_t v1, v2, v3, v4, v5, v6, v7, v8; + float* center = b->midPoint; + float* halfSize = b->halfSize; + + v1[0] = center[0] - halfSize[0]; + v1[1] = center[1] - halfSize[1]; + v1[2] = center[2] - halfSize[2]; + + v2[0] = center[0] + halfSize[0]; + v2[1] = center[1] - halfSize[1]; + v2[2] = center[2] - halfSize[2]; + + v3[0] = center[0] - halfSize[0]; + v3[1] = center[1] + halfSize[1]; + v3[2] = center[2] - halfSize[2]; + + v4[0] = center[0] + halfSize[0]; + v4[1] = center[1] + halfSize[1]; + v4[2] = center[2] - halfSize[2]; + + v5[0] = center[0] - halfSize[0]; + v5[1] = center[1] - halfSize[1]; + v5[2] = center[2] + halfSize[2]; + + v6[0] = center[0] + halfSize[0]; + v6[1] = center[1] - halfSize[1]; + v6[2] = center[2] + halfSize[2]; + + v7[0] = center[0] - halfSize[0]; + v7[1] = center[1] + halfSize[1]; + v7[2] = center[2] + halfSize[2]; + + v8[0] = center[0] + halfSize[0]; + v8[1] = center[1] + halfSize[1]; + v8[2] = center[2] + halfSize[2]; + + // bottom + Game::R_AddDebugLine(color, v1, v2); + Game::R_AddDebugLine(color, v2, v4); + Game::R_AddDebugLine(color, v4, v3); + Game::R_AddDebugLine(color, v3, v1); + + // top + Game::R_AddDebugLine(color, v5, v6); + Game::R_AddDebugLine(color, v6, v8); + Game::R_AddDebugLine(color, v8, v7); + Game::R_AddDebugLine(color, v7, v5); + + // verticals + Game::R_AddDebugLine(color, v1, v5); + Game::R_AddDebugLine(color, v2, v6); + Game::R_AddDebugLine(color, v3, v7); + Game::R_AddDebugLine(color, v4, v8); + } + +#pragma optimize("", off) + __declspec(naked) float UI_GetScoreboardLeft(void* /*a1*/) + { + __asm + { + push eax + pushad + + mov ecx, 590390h + mov eax, [esp + 28h] + call ecx + mov[esp + 20h], eax + popad + pop eax + + retn + } + } + __declspec(naked) XAssetHeader DB_FindXAssetDefaultHeaderInternal(XAssetType /*type*/) { __asm @@ -583,7 +765,7 @@ namespace Game mov edi, [esp + 28h] call eax - mov [esp + 20h], eax + mov[esp + 20h], eax popad pop eax @@ -633,30 +815,6 @@ namespace Game } } - // this cant be MessageBox because windows.h has a define that converts it to MessageBoxW. which is just stupid - void ShowMessageBox(std::string message, std::string title) - { - if (!Game::CL_IsCgameInitialized()) - { - Dvar_SetStringByName("com_errorMessage", message.data()); - Dvar_SetStringByName("com_errorTitle", title.data()); - Cbuf_AddText(0, "openmenu error_popmenu_lobby"); - } - } - - unsigned int R_HashString(const char* string) - { - unsigned int hash = 0; - - while (*string) - { - hash = (*string | 0x20) ^ (33 * hash); - ++string; - } - - return hash; - } - __declspec(naked) void R_LoadSunThroughDvars(const char* /*mapname*/, sunflare_t* /*sun*/) { __asm @@ -697,7 +855,7 @@ namespace Game mov edi, 0 mov esi, [esp + 24h] - push[esp + 28h] + push [esp + 28h] push 0 push 0 @@ -711,26 +869,6 @@ namespace Game } } - void SV_KickClientError(client_t* client, std::string reason) - { - if (client->state < 5) - { - Components::Network::SendCommand(client->addr, "error", reason); - } - - SV_KickClient(client, reason.data()); - } - - void Scr_iPrintLn(int clientNum, std::string message) - { - Game::SV_GameSendServerCommand(clientNum, 0, Utils::String::VA("%c \"%s\"", 0x66, message.data())); - } - - void Scr_iPrintLnBold(int clientNum, std::string message) - { - Game::SV_GameSendServerCommand(clientNum, 0, Utils::String::VA("%c \"%s\"", 0x67, message.data())); - } - __declspec(naked) void Scr_NotifyId(unsigned int /*id*/, unsigned __int16 /*stringValue*/, unsigned int /*paramcount*/) { __asm @@ -778,37 +916,6 @@ namespace Game } } - int FS_FOpenFileReadCurrentThread(const char* file, int* fh) - { - if (GetCurrentThreadId() == *reinterpret_cast(0x1CDE7FC)) - { - return FS_FOpenFileRead(file, fh); - } - else if (GetCurrentThreadId() == *reinterpret_cast(0x1CDE814)) - { - return FS_FOpenFileReadDatabase(file, fh); - } - else - { - *fh = NULL; - return -1; - } - } - - void Load_IndexBuffer(void* data, IDirect3DIndexBuffer9** storeHere, int count) - { - if (Components::Dvar::Var("r_loadForRenderer").get()) - { - void* buffer = R_AllocStaticIndexBuffer(storeHere, 2 * count); - std::memcpy(buffer, data, 2 * count); - - if (storeHere && *storeHere) - { - (*storeHere)->Unlock(); - } - } - } - __declspec(naked) void Load_VertexBuffer(void* /*data*/, IDirect3DVertexBuffer9** /*where*/, int /*len*/) { __asm @@ -829,91 +936,40 @@ namespace Game } } - char* Com_GetParseThreadInfo() - { - if (Game::Sys_IsMainThread()) - { - return reinterpret_cast(0x6466628); - } - else if (Game::Sys_IsRenderThread()) - { - return reinterpret_cast(0x646AC34); - } - else if (Game::Sys_IsServerThread()) - { - return reinterpret_cast(0x646F240); - } - else if(Game::Sys_IsDatabaseThread()) - { - return reinterpret_cast(0x647384C); - } - else - { - return nullptr; - } - } - - void Com_SetParseNegativeNumbers(int parse) - { - char* g_parse = Com_GetParseThreadInfo(); - - if (g_parse) - { - g_parse[1056 * *(reinterpret_cast(g_parse) + 4224) + 1032] = parse != 0; - } - } - - int CL_GetMaxXP() - { - StringTable* rankTable = DB_FindXAssetHeader(ASSET_TYPE_STRINGTABLE, "mp/rankTable.csv").stringTable; - const char* maxrank = StringTable_Lookup(rankTable, 0, "maxrank", 1); - return atoi(StringTable_Lookup(rankTable, 0, maxrank, 7)); - } - - __declspec(naked) void Image_Setup(GfxImage* /*image*/, unsigned int /*width*/, unsigned int /*height*/, unsigned int /*depth*/, unsigned int /*flags*/, int /*format*/) + __declspec(naked) void Image_Setup(GfxImage* /*image*/, unsigned int /*width*/, unsigned int /*height*/, unsigned int /*depth*/, unsigned int /*flags*/, _D3DFORMAT /*format*/) { __asm { pushad - xor edi, edi - mov eax, [esp + 24h] // image - mov di, word ptr [esp + 28h] // width - push [esp + 38h] // format - push [esp + 38h] // flags - push 0 - push [esp + 3Ch] // depth - push [esp + 3Ch] // height + mov eax, [esp + 24h] // image + mov edi, [esp + 28h] // width + push [esp + 38h] // format + push [esp + 38h] // flags + push [esp + 38h] // depth + push [esp + 38h] // height mov ecx, 54AF50h call ecx - add esp, 14h + add esp, 10h popad retn } } - void SortWorldSurfaces(GfxWorld* world) + __declspec(naked) void Menu_FreeItemMemory(itemDef_t* /*item*/) { - DWORD* specular1 = reinterpret_cast(0x69F105C); - DWORD* specular2 = reinterpret_cast(0x69F92D4); - DWORD saveSpecular1 = *specular1; - DWORD saveSpecular2 = *specular2; - - GfxWorld** gameWorld = reinterpret_cast(0x66DEE94); - GfxWorld* saveWorld = *gameWorld; - - *specular1 = 1; - *specular2 = 1; - *gameWorld = world; - - R_SortWorldSurfaces(); - - *gameWorld = saveWorld; - *specular1 = saveSpecular1; - *specular2 = saveSpecular2; + __asm + { + pushad + mov edi, [esp + 24h] + mov eax, 63D880h + call eax + popad + retn + } } __declspec(naked) void R_AddDebugLine(float* /*color*/, float* /*v1*/, float* /*v2*/) @@ -975,61 +1031,5 @@ namespace Game retn } } - - void R_AddDebugBounds(float* color, Bounds* b) - { - Game::vec3_t v1, v2, v3, v4, v5, v6, v7, v8; - float* center = b->midPoint; - float* halfSize = b->halfSize; - - v1[0] = center[0] - halfSize[0]; - v1[1] = center[1] - halfSize[1]; - v1[2] = center[2] - halfSize[2]; - - v2[0] = center[0] + halfSize[0]; - v2[1] = center[1] - halfSize[1]; - v2[2] = center[2] - halfSize[2]; - - v3[0] = center[0] - halfSize[0]; - v3[1] = center[1] + halfSize[1]; - v3[2] = center[2] - halfSize[2]; - - v4[0] = center[0] + halfSize[0]; - v4[1] = center[1] + halfSize[1]; - v4[2] = center[2] - halfSize[2]; - - v5[0] = center[0] - halfSize[0]; - v5[1] = center[1] - halfSize[1]; - v5[2] = center[2] + halfSize[2]; - - v6[0] = center[0] + halfSize[0]; - v6[1] = center[1] - halfSize[1]; - v6[2] = center[2] + halfSize[2]; - - v7[0] = center[0] - halfSize[0]; - v7[1] = center[1] + halfSize[1]; - v7[2] = center[2] + halfSize[2]; - - v8[0] = center[0] + halfSize[0]; - v8[1] = center[1] + halfSize[1]; - v8[2] = center[2] + halfSize[2]; - - // bottom - Game::R_AddDebugLine(color, v1, v2); - Game::R_AddDebugLine(color, v2, v4); - Game::R_AddDebugLine(color, v4, v3); - Game::R_AddDebugLine(color, v3, v1); - - // top - Game::R_AddDebugLine(color, v5, v6); - Game::R_AddDebugLine(color, v6, v8); - Game::R_AddDebugLine(color, v8, v7); - Game::R_AddDebugLine(color, v7, v5); - - // verticals - Game::R_AddDebugLine(color, v1, v5); - Game::R_AddDebugLine(color, v2, v6); - Game::R_AddDebugLine(color, v3, v7); - Game::R_AddDebugLine(color, v4, v8); - } +#pragma optimize("", on) } diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index cb7f79f5..345664a3 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -845,7 +845,7 @@ namespace Game int CL_GetMaxXP(); - void Image_Setup(GfxImage* image, unsigned int width, unsigned int height, unsigned int depth, unsigned int flags, int format); + void Image_Setup(GfxImage* image, unsigned int width, unsigned int height, unsigned int depth, unsigned int flags, _D3DFORMAT format); void SortWorldSurfaces(GfxWorld* world); void R_AddDebugLine(float* color, float* v1, float* v2); From 78e7d6375dbde493342226776d9fd486d16482be Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 5 Jun 2017 13:47:00 +0200 Subject: [PATCH 3/5] [Friends] Fix avatar corners --- src/Components/Modules/Friends.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Components/Modules/Friends.cpp b/src/Components/Modules/Friends.cpp index d19690ab..df4d89f7 100644 --- a/src/Components/Modules/Friends.cpp +++ b/src/Components/Modules/Friends.cpp @@ -557,6 +557,12 @@ namespace Components std::swap(buffer[i + 0], buffer[i + 2]); } + // Steam rounds the corners and somehow fuck up the pixels there + buffer[3] = 0; // top-left + buffer[(width - 1) * 4 + 3] = 0; // top-right + buffer[((height - 1) * width * 4) + 3] = 0; // bottom-left + buffer[((height - 1) * width * 4) + (width - 1) * 4 + 3] = 0; // bottom-right + image->map->UnlockRect(0); return Materials::Create(Utils::String::VA("avatar_%llX", user.bits), image); From 38b64628ef5b08a276bc6abe0c37101290ffab5d Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 5 Jun 2017 14:31:45 +0200 Subject: [PATCH 4/5] [Materials] Correctly cleanup images --- src/Components/Modules/Friends.cpp | 2 +- src/Components/Modules/Materials.cpp | 53 +++++++++++++++++++++------- src/Components/Modules/Materials.hpp | 1 + 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/Components/Modules/Friends.cpp b/src/Components/Modules/Friends.cpp index df4d89f7..1edc3e07 100644 --- a/src/Components/Modules/Friends.cpp +++ b/src/Components/Modules/Friends.cpp @@ -115,7 +115,7 @@ namespace Components Friends::SortList(); int notify = Dvar::Var("cl_notifyFriendState").get(); - if(gotOnline && (notify == -1 || (notify == 1 && !Game::CL_IsCgameInitialized())) && !Dvar::Var("ui_streamFriendly").get()) + if (gotOnline && (notify == -1 || (notify == 1 && !Game::CL_IsCgameInitialized())) && !Dvar::Var("ui_streamFriendly").get()) { Game::Material* material = Friends::CreateAvatar(user); Toast::Show(material, entry->name, "is playing IW4x", 3000, [material]() diff --git a/src/Components/Modules/Materials.cpp b/src/Components/Modules/Materials.cpp index f303ca65..a1e2528e 100644 --- a/src/Components/Modules/Materials.cpp +++ b/src/Components/Modules/Materials.cpp @@ -5,6 +5,7 @@ namespace Components int Materials::ImageNameLength; Utils::Hook Materials::ImageVersionCheckHook; + std::vector Materials::ImageTable; std::vector Materials::MaterialTable; Game::Material* Materials::Create(std::string name, Game::GfxImage* image) @@ -51,12 +52,6 @@ namespace Components { if (!material) return; - auto mat = std::find(Materials::MaterialTable.begin(), Materials::MaterialTable.end(), material); - if(mat != Materials::MaterialTable.end()) - { - Materials::MaterialTable.erase(mat); - } - if (deleteImage) { for (char i = 0; i < material->textureCount; ++i) @@ -68,6 +63,12 @@ namespace Components Utils::Memory::GetAllocator()->free(material->textureTable); Utils::Memory::GetAllocator()->free(material->name); Utils::Memory::GetAllocator()->free(material); + + auto mat = std::find(Materials::MaterialTable.begin(), Materials::MaterialTable.end(), material); + if (mat != Materials::MaterialTable.end()) + { + Materials::MaterialTable.erase(mat); + } } Game::GfxImage* Materials::CreateImage(std::string name, unsigned int width, unsigned int height, unsigned int depth, unsigned int flags, _D3DFORMAT format) @@ -77,6 +78,8 @@ namespace Components Game::Image_Setup(image, width, height, depth, flags, format); + Materials::ImageTable.push_back(image); + return image; } @@ -88,6 +91,12 @@ namespace Components Utils::Memory::GetAllocator()->free(image->name); Utils::Memory::GetAllocator()->free(image); + + auto img = std::find(Materials::ImageTable.begin(), Materials::ImageTable.end(), image); + if (img != Materials::ImageTable.end()) + { + Materials::ImageTable.erase(img); + } } void Materials::DeleteAll() @@ -100,6 +109,15 @@ namespace Components { Materials::Delete(material); } + + std::vector images; + Utils::Merge(&images, Materials::ImageTable); + Materials::ImageTable.clear(); + + for (auto& image : images) + { + Materials::DeleteImage(image); + } } __declspec(naked) void Materials::ImageVersionCheck() @@ -284,13 +302,22 @@ namespace Components } #endif -// Scheduler::OnFrame([] () -// { -// Game::Font* font = Game::R_RegisterFont("fonts/normalFont"); -// float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; -// -// Game::R_AddCmdDrawText("test^==preview_mp_rustzob", 0x7FFFFFFF, font, 500.0f, 150.0f, 1.0f, 1.0f, 0.0f, color, Game::ITEM_TEXTSTYLE_SHADOWED); -// }, true); + Renderer::OnDeviceRecoveryBegin([]() + { + for (auto& image : Materials::ImageTable) + { + Game::Image_Release(image); + image->map = nullptr; + } + }); + + Renderer::OnDeviceRecoveryEnd([]() + { + for (auto& image : Materials::ImageTable) + { + Utils::Hook::Call(0x51F7B0)(image); + } + }); } Materials::~Materials() diff --git a/src/Components/Modules/Materials.hpp b/src/Components/Modules/Materials.hpp index 02d66ae2..c08755af 100644 --- a/src/Components/Modules/Materials.hpp +++ b/src/Components/Modules/Materials.hpp @@ -17,6 +17,7 @@ namespace Components static void DeleteImage(Game::GfxImage* image); private: + static std::vector ImageTable; static std::vector MaterialTable; static int ImageNameLength; From 13fc91179f77d4de97318ff0675a4a0d273d8363 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 5 Jun 2017 14:39:12 +0200 Subject: [PATCH 5/5] [Toast] Make sure image is valid before drawing --- src/Components/Modules/Toast.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Components/Modules/Toast.cpp b/src/Components/Modules/Toast.cpp index 12dbbc13..00a7af9f 100644 --- a/src/Components/Modules/Toast.cpp +++ b/src/Components/Modules/Toast.cpp @@ -116,7 +116,10 @@ namespace Components Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2), static_cast(height + bHeight / 2), bWidth * 1.0f, border * 1.0f, 0, 0, 1.0f, 1.0f, borderColor, white); // Bottom // Image - Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2 + iOffsetLeft), static_cast(height - bHeight / 2 + iOffset), imgDim * 1.0f, imgDim * 1.0f, 0, 0, 1.0f, 1.0f, wColor, toast->image); + if (toast->image && toast->image->textureTable && toast->image->textureCount && toast->image->textureTable->info.image && toast->image->textureTable->info.image->map) + { + Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2 + iOffsetLeft), static_cast(height - bHeight / 2 + iOffset), imgDim * 1.0f, imgDim * 1.0f, 0, 0, 1.0f, 1.0f, wColor, toast->image); + } // Text float leftText = width / 2 - bWidth / 2 - cornerSize + iOffsetLeft * 2 + imgDim;