diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 310c2aff..a8f23722 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,6 +9,10 @@ on: - "*" types: [opened, synchronize, reopened] +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + jobs: build: name: Build binaries @@ -18,15 +22,7 @@ jobs: configuration: - Debug - Release - steps: - - name: Wait for previous workflows - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - uses: softprops/turnstyle@v1 - with: - poll-interval-seconds: 10 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - + steps: - name: Check out files uses: actions/checkout@v3 with: @@ -83,13 +79,6 @@ jobs: - name: Add known hosts run: ssh-keyscan -H ${{ secrets.XLABS_MASTER_SSH_ADDRESS }} >> ~/.ssh/known_hosts - - - name: Wait for previous workflows - uses: softprops/turnstyle@v1 - with: - poll-interval-seconds: 10 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # - name: Remove old data files # run: ssh ${{ secrets.XLABS_MASTER_SSH_USER }}@${{ secrets.XLABS_MASTER_SSH_ADDRESS }} rm -rf ${{ env.XLABS_MASTER_PATH }}/iw4x/data/* diff --git a/.gitmodules b/.gitmodules index 7839a17e..4387e293 100644 --- a/.gitmodules +++ b/.gitmodules @@ -32,5 +32,6 @@ path = deps/GSL url = https://github.com/microsoft/GSL.git [submodule "deps/nlohmannjson"] - path = deps/nlohmannjson + path = deps/json url = https://github.com/nlohmann/json.git + branch = v3.11.2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fed073f..358e25a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,51 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.3.0/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [0.7.7] - 2022-12-31 + +### Added + +- Add `r_forceTechnique` Dvar to debug techniques on materials. +- Add `IsSprinting` GSC method (#587) +- Add `StorageLoad` GSC function (#595) +- Add `bg_climbAnything` Dvar (#663) +- Add ClanTag support for bots (#645) +- Add `sv_randomBotNames` Dvar (#665) +- Add support for parsing localized strings files (.str & .json) (#621) +- Add `callvote` menus (#613) +- IW3x-port converts Technique Sets between CoD4 and CoD6 properly. +- Add new map porting utility tool that makes the map porting process between CoD4 to CoD6 easy. + +### Changed + +- `r_drawModelNames` now uses ground lighting when available. +- Speculars are now enabled on custom maps. +- Techset(s) get loaded from disk first rather than memory. +- Use CSO format for Vertex Shaders & Pixel Shaders on ZoneBuilder to allow replacement/swapping. +- New map porting utility tool builds teams directly into the map. + +### Fixed + +- Fix bug where ZoneBuilder would not build loaded sounds correctly. +- Fix bug where ZoneBuilder no longer differentiates assets depending on their name. +- Fix building FX with ZoneBuilder. +- Fix branding in ZoneBuilder generated zones. +- Fix Script String crash when building zones. +- Fix the changelog menu (#583) +- Fix bug when adding commands (#609) +- Fix bug where some ported maps would either crash or lag (mp_zavod, mp_kowloon, ...) +- Fix GSC conversion from CoD4 to CoD6 (Specular Scale, Create Exp Fog, Create FX, ...) +- The map porting process from CoD4 to IW4x has improved. +- Ported map zones are about 40% lighter than before. +- Static models are now lit correctly depending on their position and ground lighting. +- New map porting utility ports sounds and effects properly. + +### Known issues + +- HTTPS is not supported for fast downloads at the moment. +- Sound issue fix is experimental as the bug is not fully understood. +- `reloadmenus` command does not free resources used by custom menus. + ## [0.7.6] - 2022-11-22 ### Added diff --git a/README.md b/README.md index 448d11c9..fa6f3612 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,6 @@ | `--copy-to=PATH` | Optional, copy the DLL to a custom folder after build, define the path here if wanted. | | `--copy-pdb` | Copy debug information for binaries as well to the path given via --copy-to. | | `--force-unit-tests` | Always compile unit tests. | -| `--force-exception-handler` | Install custom unhandled exception handler even for Debug builds. | | `--disable-binary-check` | Do not perform integrity checks on the exe. | ## Command line arguments diff --git a/deps/GSL b/deps/GSL index 517ed292..f94c1f6f 160000 --- a/deps/GSL +++ b/deps/GSL @@ -1 +1 @@ -Subproject commit 517ed29228d18cf2c5004d10826090108e06f049 +Subproject commit f94c1f6f2b5e141d5f6eb3d284cd4a8cf9a81aac diff --git a/deps/json b/deps/json new file mode 160000 index 00000000..bc889afb --- /dev/null +++ b/deps/json @@ -0,0 +1 @@ +Subproject commit bc889afb4c5bf1c0d8ee29ef35eaaf4c8bef8a5d diff --git a/deps/mongoose b/deps/mongoose index db81c30d..73813a83 160000 --- a/deps/mongoose +++ b/deps/mongoose @@ -1 +1 @@ -Subproject commit db81c30d24df98031e41e33423a5eef0e89ba8c6 +Subproject commit 73813a838386f6ebca447eb54c626803163ee257 diff --git a/deps/nlohmannjson b/deps/nlohmannjson deleted file mode 160000 index a3e6e26d..00000000 --- a/deps/nlohmannjson +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a3e6e26dc83a726b292f5be0492fcc408663ce55 diff --git a/deps/premake/json.lua b/deps/premake/json.lua new file mode 100644 index 00000000..c4731982 --- /dev/null +++ b/deps/premake/json.lua @@ -0,0 +1,19 @@ +json = { + source = path.join(dependencies.basePath, "json"), +} + +function json.import() + json.includes() +end + +function json.includes() + includedirs { + path.join(json.source, "single_include/nlohmann") + } +end + +function json.project() + +end + +table.insert(dependencies, json) diff --git a/deps/premake/libtomcrypt.lua b/deps/premake/libtomcrypt.lua index 51860cfa..6e8cf0f3 100644 --- a/deps/premake/libtomcrypt.lua +++ b/deps/premake/libtomcrypt.lua @@ -17,6 +17,8 @@ function libtomcrypt.includes() "LTC_NO_FAST", "LTC_NO_PROTOTYPES", "LTC_NO_RSA_BLINDING", + "LTC_NO_FILE", + "ARGTYPE=4", } end @@ -32,6 +34,7 @@ function libtomcrypt.project() } removefiles { + path.join(libtomcrypt.source, "src/**/*_test.c"), path.join(libtomcrypt.source, "src/**/*tab.c"), path.join(libtomcrypt.source, "src/encauth/ocb3/**.c"), } diff --git a/deps/premake/nlohmannjson.lua b/deps/premake/nlohmannjson.lua deleted file mode 100644 index 8cdf6217..00000000 --- a/deps/premake/nlohmannjson.lua +++ /dev/null @@ -1,18 +0,0 @@ -nlohmannjson = { - source = path.join(dependencies.basePath, "nlohmannjson"), -} - -function nlohmannjson.import() - nlohmannjson.includes() -end - -function nlohmannjson.includes() - includedirs { - path.join(nlohmannjson.source, "single_include/nlohmann") - } -end - -function nlohmannjson.project() -end - -table.insert(dependencies, nlohmannjson) diff --git a/deps/protobuf b/deps/protobuf index 57786d12..8d5fdedd 160000 --- a/deps/protobuf +++ b/deps/protobuf @@ -1 +1 @@ -Subproject commit 57786d126249b5ed4f42b579047941805e742949 +Subproject commit 8d5fdedd42ef361dcfc1531fba4f33470273f375 diff --git a/deps/zlib b/deps/zlib index 04f42cec..02a6049e 160000 --- a/deps/zlib +++ b/deps/zlib @@ -1 +1 @@ -Subproject commit 04f42ceca40f73e2978b50e93806c2a18c1281fc +Subproject commit 02a6049eb3884c430268bb0fe3296d597a03174c diff --git a/premake5.lua b/premake5.lua index 3f36f4bd..a311c929 100644 --- a/premake5.lua +++ b/premake5.lua @@ -76,11 +76,6 @@ newoption { description = "Always compile unit tests." } -newoption { - trigger = "force-exception-handler", - description = "Install custom unhandled exception handler even for Debug builds." -} - newoption { trigger = "disable-binary-check", description = "Do not perform integrity checks on the exe." @@ -257,9 +252,6 @@ workspace "iw4x" if _OPTIONS["force-unit-tests"] then defines {"FORCE_UNIT_TESTS"} end - if _OPTIONS["force-exception-handler"] then - defines {"FORCE_EXCEPTION_HANDLER"} - end if _OPTIONS["disable-binary-check"] then defines {"DISABLE_BINARY_CHECK"} end diff --git a/src/Components/Loader.cpp b/src/Components/Loader.cpp index b4ad3c7d..5c244e66 100644 --- a/src/Components/Loader.cpp +++ b/src/Components/Loader.cpp @@ -1,5 +1,55 @@ #include +#include "Modules/Bans.hpp" +#include "Modules/Bots.hpp" +#include "Modules/Branding.hpp" +#include "Modules/Bullet.hpp" +#include "Modules/CardTitles.hpp" +#include "Modules/Ceg.hpp" +#include "Modules/Changelog.hpp" +#include "Modules/Chat.hpp" +#include "Modules/ClanTags.hpp" +#include "Modules/ClientCommand.hpp" +#include "Modules/ConnectProtocol.hpp" +#include "Modules/Console.hpp" +#include "Modules/D3D9Ex.hpp" +#include "Modules/Debug.hpp" +#include "Modules/Discovery.hpp" +#include "Modules/Download.hpp" +#include "Modules/Elevators.hpp" +#include "Modules/Gamepad.hpp" +#include "Modules/Lean.hpp" +#include "Modules/MapDump.hpp" +#include "Modules/MapRotation.hpp" +#include "Modules/NetworkDebug.hpp" +#include "Modules/News.hpp" +#include "Modules/PlayerMovement.hpp" +#include "Modules/PlayerName.hpp" +#include "Modules/Playlist.hpp" +#include "Modules/QuickPatch.hpp" +#include "Modules/RawFiles.hpp" +#include "Modules/RawMouse.hpp" +#include "Modules/RCon.hpp" +#include "Modules/Security.hpp" +#include "Modules/ServerCommands.hpp" +#include "Modules/ServerInfo.hpp" +#include "Modules/ServerList.hpp" +#include "Modules/Session.hpp" +#include "Modules/SlowMotion.hpp" +#include "Modules/SoundMutexFix.hpp" +#include "Modules/StartupMessages.hpp" +#include "Modules/Stats.hpp" +#include "Modules/StringTable.hpp" +#include "Modules/StructuredData.hpp" +#include "Modules/Theatre.hpp" +#include "Modules/Threading.hpp" +#include "Modules/UIFeeder.hpp" +#include "Modules/UserInfo.hpp" +#include "Modules/VisionFile.hpp" +#include "Modules/Voice.hpp" +#include "Modules/Vote.hpp" +#include "Modules/Weapon.hpp" + namespace Components { bool Loader::Pregame = true; @@ -9,126 +59,127 @@ namespace Components bool Loader::IsPregame() { - return Loader::Pregame; + return Pregame; } bool Loader::IsPostgame() { - return Loader::Postgame; + return Postgame; } bool Loader::IsUninitializing() { - return Loader::Uninitializing; + return Uninitializing; } void Loader::Initialize() { - Loader::Pregame = true; - Loader::Postgame = false; - Loader::Uninitializing = false; + Pregame = true; + Postgame = false; + Uninitializing = false; Utils::Memory::GetAllocator()->clear(); - Loader::Register(new Flags()); - Loader::Register(new Singleton()); - // Install our exception handler as early as posssible to get better debug dumps from startup crashes - Loader::Register(new Exception()); - Loader::Register(new Auth()); - Loader::Register(new Bans()); - Loader::Register(new Bots()); - Loader::Register(new Dvar()); - Loader::Register(new Lean()); - Loader::Register(new Maps()); - Loader::Register(new News()); - Loader::Register(new Node()); - Loader::Register(new RCon()); - Loader::Register(new Stats()); - Loader::Register(new Menus()); - Loader::Register(new Toast()); - Loader::Register(new Party()); - Loader::Register(new Zones()); - Loader::Register(new D3D9Ex()); - Loader::Register(new Logger()); - Loader::Register(new Weapon()); - Loader::Register(new Window()); - Loader::Register(new Command()); - Loader::Register(new Console()); - Loader::Register(new Friends()); - Loader::Register(new IPCPipe()); - Loader::Register(new MapDump()); - Loader::Register(new ModList()); - Loader::Register(new Network()); - Loader::Register(new Session()); - Loader::Register(new Theatre()); - Loader::Register(new ClanTags()); - Loader::Register(new Download()); - Loader::Register(new Playlist()); - Loader::Register(new RawFiles()); - Loader::Register(new Renderer()); - Loader::Register(new UIFeeder()); - Loader::Register(new UIScript()); - Loader::Register(new Changelog()); - Loader::Register(new Dedicated()); - Loader::Register(new Discovery()); - Loader::Register(new FastFiles()); - Loader::Register(new Gametypes()); - Loader::Register(new Materials()); - Loader::Register(new Scheduler()); - Loader::Register(new CardTitles()); - Loader::Register(new FileSystem()); - Loader::Register(new ModelSurfs()); - Loader::Register(new PlayerName()); - Loader::Register(new QuickPatch()); - Loader::Register(new Security()); - Loader::Register(new ServerInfo()); - Loader::Register(new ServerList()); - Loader::Register(new SlowMotion()); - Loader::Register(new ArenaLength()); - Loader::Register(new StringTable()); - Loader::Register(new ZoneBuilder()); - Loader::Register(new AssetHandler()); - Loader::Register(new Localization()); - Loader::Register(new ServerCommands()); - Loader::Register(new StructuredData()); - Loader::Register(new ConnectProtocol()); - Loader::Register(new StartupMessages()); - Loader::Register(new SoundMutexFix()); - Loader::Register(new Gamepad()); - Loader::Register(new Chat()); - Loader::Register(new TextRenderer()); - Loader::Register(new Movement()); - Loader::Register(new Elevators()); - Loader::Register(new ClientCommand()); - Loader::Register(new VisionFile()); - Loader::Register(new Branding()); - Loader::Register(new Debug()); - Loader::Register(new RawMouse()); - Loader::Register(new Bullet()); - Loader::Register(new MapRotation()); - Loader::Register(new Ceg()); - Loader::Register(new UserInfo()); - Loader::Register(new Events()); - Loader::Register(new Voice()); - Loader::Register(new Vote()); + Register(new Auth()); + Register(new Command()); + Register(new Dvar()); + Register(new Exception()); // Install our exception handler as early as posssible to get better debug dumps from startup crashes + Register(new Flags()); + Register(new Network()); + Register(new Logger()); + Register(new Singleton()); + Register(new UIScript()); + Register(new ZoneBuilder()); + + Register(new ArenaLength()); + Register(new AssetHandler()); + Register(new Bans()); + Register(new Bots()); + Register(new Branding()); + Register(new Bullet()); + Register(new CardTitles()); + Register(new Ceg()); + Register(new Changelog()); + Register(new Chat()); + Register(new ClanTags()); + Register(new ClientCommand()); + Register(new ConnectProtocol()); + Register(new Console()); + Register(new D3D9Ex()); + Register(new Debug()); + Register(new Dedicated()); + Register(new Discovery()); + Register(new Download()); + Register(new Elevators()); + Register(new Events()); + Register(new FastFiles()); + Register(new FileSystem()); + Register(new Friends()); + Register(new Gamepad()); + Register(new IPCPipe()); + Register(new Lean()); + Register(new Localization()); + Register(new MapDump()); + Register(new MapRotation()); + Register(new Maps()); + Register(new Materials()); + Register(new Menus()); + Register(new ModList()); + Register(new ModelSurfs()); + Register(new NetworkDebug()); + Register(new News()); + Register(new Node()); + Register(new Party()); + Register(new PlayerMovement()); + Register(new PlayerName()); + Register(new Playlist()); + Register(new QuickPatch()); + Register(new RawFiles()); + Register(new RawMouse()); + Register(new RCon()); + Register(new Renderer()); + Register(new Scheduler()); + Register(new Security()); + Register(new ServerCommands()); + Register(new ServerInfo()); + Register(new ServerList()); + Register(new Session()); + Register(new SlowMotion()); + Register(new SoundMutexFix()); + Register(new StartupMessages()); + Register(new Stats()); + Register(new StringTable()); + Register(new StructuredData()); + Register(new TextRenderer()); + Register(new Theatre()); + Register(new Threading()); + Register(new Toast()); + Register(new UIFeeder()); + Register(new UserInfo()); + Register(new VisionFile()); + Register(new Voice()); + Register(new Vote()); + Register(new Weapon()); + Register(new Window()); + Register(new Zones()); - Loader::Register(new GSC()); + Register(new GSC()); - Loader::Pregame = false; + Pregame = false; // Make sure preDestroy is called when the game shuts down - Scheduler::OnGameShutdown(Loader::PreDestroy); + Scheduler::OnGameShutdown(PreDestroy); } void Loader::Uninitialize() { - Loader::Uninitializing = true; - Loader::PreDestroyNoPostGame(); + Uninitializing = true; + PreDestroyNoPostGame(); - std::reverse(Loader::Components.begin(), Loader::Components.end()); - for (auto component : Loader::Components) + std::reverse(Components.begin(), Components.end()); + for (auto& component : Components) { #ifdef DEBUG - if (!Loader::IsPerformingUnitTests()) + if (!IsPerformingUnitTests()) { Logger::Print("Unregister component: {}\n", component->getName()); } @@ -136,21 +187,21 @@ namespace Components delete component; } - Loader::Components.clear(); + Components.clear(); Utils::Memory::GetAllocator()->clear(); - Loader::Uninitializing = false; + Uninitializing = false; } void Loader::PreDestroy() { - if (!Loader::Postgame) + if (!Postgame) { - Loader::Postgame = true; + Postgame = true; - auto components = Loader::Components; + auto components = Components; std::reverse(components.begin(), components.end()); - for (auto component : components) + for (auto& component : components) { component->preDestroy(); } @@ -159,17 +210,17 @@ namespace Components void Loader::PreDestroyNoPostGame() { - if (!Loader::Postgame) + if (!Postgame) { - auto components = Loader::Components; + auto components = Components; std::reverse(components.begin(), components.end()); - for (auto component : components) + for (auto& component : components) { component->preDestroy(); } - Loader::Postgame = true; + Postgame = true; } } @@ -179,7 +230,7 @@ namespace Components Logger::Print("Performing unit tests for components:\n"); - for (const auto component : Loader::Components) + for (const auto& component : Components) { #if defined(FORCE_UNIT_TESTS) Logger::Debug("Testing '{}'...\n", component->getName()); @@ -208,12 +259,12 @@ namespace Components if (component) { #if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - if (!Loader::IsPerformingUnitTests()) + if (!IsPerformingUnitTests()) { Logger::Print("Component registered: {}\n", component->getName()); } #endif - Loader::Components.push_back(component); + Components.push_back(component); } } } diff --git a/src/Components/Loader.hpp b/src/Components/Loader.hpp index 5017eff3..4957c9df 100644 --- a/src/Components/Loader.hpp +++ b/src/Components/Loader.hpp @@ -42,7 +42,7 @@ namespace Components template static T* GetInstance() { - for (auto& component : Loader::Components) + for (auto& component : Components) { if (typeid(*component) == typeid(T)) { @@ -61,85 +61,39 @@ namespace Components }; } -#include "Modules/Scheduler.hpp" +// Priority #include "Modules/Auth.hpp" -#include "Modules/Bans.hpp" -#include "Modules/Bots.hpp" -#include "Modules/Dvar.hpp" -#include "Modules/Lean.hpp" -#include "Modules/Maps.hpp" -#include "Modules/News.hpp" -#include "Modules/Flags.hpp" -#include "Modules/Menus.hpp" -#include "Modules/Toast.hpp" -#include "Modules/Zones.hpp" -#include "Modules/D3D9Ex.hpp" -#include "Modules/Weapon.hpp" -#include "Modules/Window.hpp" #include "Modules/Command.hpp" -#include "Modules/Console.hpp" -#include "Modules/UIScript.hpp" -#include "Modules/ModList.hpp" +#include "Modules/Dvar.hpp" +#include "Modules/Exception.hpp" +#include "Modules/Flags.hpp" #include "Modules/Network.hpp" -#include "Modules/Theatre.hpp" -#include "Modules/QuickPatch.hpp" -#include "Modules/Security.hpp" -#include "Modules/Node.hpp" -#include "Modules/RCon.hpp" -#include "Modules/Party.hpp" // Destroys the order, but requires network classes :D #include "Modules/Logger.hpp" +#include "Modules/Singleton.hpp" +#include "Modules/UIScript.hpp" +#include "Modules/ZoneBuilder.hpp" + +#include "Modules/ArenaLength.hpp" +#include "Modules/AssetHandler.hpp" +#include "Modules/Dedicated.hpp" +#include "Modules/Events.hpp" +#include "Modules/FastFiles.hpp" +#include "Modules/FileSystem.hpp" #include "Modules/Friends.hpp" #include "Modules/IPCPipe.hpp" -#include "Modules/MapDump.hpp" -#include "Modules/Session.hpp" -#include "Modules/ClanTags.hpp" -#include "Modules/Download.hpp" -#include "Modules/Playlist.hpp" -#include "Modules/RawFiles.hpp" -#include "Modules/Renderer.hpp" -#include "Modules/UIFeeder.hpp" -#include "Modules/Changelog.hpp" -#include "Modules/Dedicated.hpp" -#include "Modules/Discovery.hpp" -#include "Modules/Exception.hpp" -#include "Modules/FastFiles.hpp" -#include "Modules/Gametypes.hpp" -#include "Modules/Materials.hpp" -#include "Modules/Singleton.hpp" -#include "Modules/CardTitles.hpp" -#include "Modules/FileSystem.hpp" -#include "Modules/ModelSurfs.hpp" -#include "Modules/PlayerName.hpp" -#include "Modules/ServerInfo.hpp" -#include "Modules/ServerList.hpp" -#include "Modules/SlowMotion.hpp" -#include "Modules/ArenaLength.hpp" -#include "Modules/StringTable.hpp" -#include "Modules/ZoneBuilder.hpp" -#include "Modules/AssetHandler.hpp" #include "Modules/Localization.hpp" -#include "Modules/ServerCommands.hpp" -#include "Modules/StructuredData.hpp" -#include "Modules/ConnectProtocol.hpp" -#include "Modules/StartupMessages.hpp" -#include "Modules/Stats.hpp" -#include "Modules/SoundMutexFix.hpp" -#include "Modules/Chat.hpp" +#include "Modules/Maps.hpp" +#include "Modules/Materials.hpp" +#include "Modules/Menus.hpp" +#include "Modules/ModList.hpp" +#include "Modules/ModelSurfs.hpp" +#include "Modules/Node.hpp" +#include "Modules/Party.hpp" +#include "Modules/Renderer.hpp" +#include "Modules/Scheduler.hpp" #include "Modules/TextRenderer.hpp" -#include "Modules/Movement.hpp" -#include "Modules/Elevators.hpp" -#include "Modules/ClientCommand.hpp" -#include "Modules/VisionFile.hpp" -#include "Modules/Gamepad.hpp" -#include "Modules/Branding.hpp" -#include "Modules/Debug.hpp" -#include "Modules/RawMouse.hpp" -#include "Modules/Bullet.hpp" -#include "Modules/MapRotation.hpp" -#include "Modules/Ceg.hpp" -#include "Modules/UserInfo.hpp" -#include "Modules/Events.hpp" -#include "Modules/Voice.hpp" -#include "Modules/Vote.hpp" +#include "Modules/Toast.hpp" +#include "Modules/Window.hpp" +#include "Modules/Zones.hpp" #include "Modules/GSC/GSC.hpp" diff --git a/src/Components/Modules/AssetHandler.cpp b/src/Components/Modules/AssetHandler.cpp index d450d50b..baf089bf 100644 --- a/src/Components/Modules/AssetHandler.cpp +++ b/src/Components/Modules/AssetHandler.cpp @@ -1,4 +1,5 @@ #include +#include "Weapon.hpp" #include "AssetInterfaces/IFont_s.hpp" #include "AssetInterfaces/IWeapon.hpp" @@ -257,7 +258,7 @@ namespace Components for (int k = 0; k < (pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount); ++k) { - if (pass->args[k].type == D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_CONSTINT) + if (pass->args[k].type == Game::MaterialShaderArgumentType::MTL_ARG_LITERAL_PIXEL_CONST) { if (pass->args[k].u.codeConst.index == -28132) { diff --git a/src/Components/Modules/AssetInterfaces/IComWorld.cpp b/src/Components/Modules/AssetInterfaces/IComWorld.cpp index bb01b757..ae7aff94 100644 --- a/src/Components/Modules/AssetInterfaces/IComWorld.cpp +++ b/src/Components/Modules/AssetInterfaces/IComWorld.cpp @@ -11,7 +11,7 @@ namespace Assets Utils::String::Replace(name, "maps/mp/", ""); Utils::String::Replace(name, ".d3dbsp", ""); - Components::FileSystem::File mapFile(Utils::String::VA("comworld/%s.iw4xComWorld", name.data())); + Components::FileSystem::File mapFile(std::format("comworld/{}.iw4xComWorld", name)); if (mapFile.exists()) { diff --git a/src/Components/Modules/AssetInterfaces/IFont_s.cpp b/src/Components/Modules/AssetInterfaces/IFont_s.cpp index 0840b809..b2988164 100644 --- a/src/Components/Modules/AssetInterfaces/IFont_s.cpp +++ b/src/Components/Modules/AssetInterfaces/IFont_s.cpp @@ -95,22 +95,22 @@ namespace Assets void IFont_s::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File fontDefFile(Utils::String::VA("%s.json", name.data())); - Components::FileSystem::File fontFile(Utils::String::VA("%s.ttf", name.data())); + Components::FileSystem::File fontDefFile(std::format("{}.json", name)); + Components::FileSystem::File fontFile(std::format("{}.ttf", name)); if (!fontDefFile.exists() || !fontFile.exists()) { return; } - nlohmann::json fontDef = nlohmann::json::parse(fontDefFile.getBuffer()); + nlohmann::json fontDef; try { fontDef = nlohmann::json::parse(fontDefFile.getBuffer()); } catch (const nlohmann::json::parse_error& ex) { - Components::Logger::Error(Game::ERR_FATAL, "Json Parse Error: {}. Font {} is invalid", ex.what(), name); + Components::Logger::Error(Game::ERR_FATAL, "Json Parse Error: {}. Font {} is invalid\n", ex.what(), name); return; } @@ -254,7 +254,7 @@ namespace Assets rgbaPixels[i + 3] = static_cast(pixels[i / 4]); } - Utils::IO::WriteFile(Utils::String::VA("userraw\\images\\%s.iwi", texName), outIwi); + Utils::IO::WriteFile(std::format("userraw\\images\\{}.iwi", texName), outIwi); } void IFont_s::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) diff --git a/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp b/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp index 80778bb6..994a6349 100644 --- a/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp +++ b/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp @@ -64,7 +64,7 @@ namespace Assets void IFxEffectDef::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File fxFile(Utils::String::VA("fx/%s.iw4xFx", name.data())); + Components::FileSystem::File fxFile(std::format("fx/{}.iw4xFx", name)); if (fxFile.exists()) { @@ -391,7 +391,7 @@ namespace Assets switch (elemType) { - case 7: + case Game::FX_ELEM_TYPE_MODEL: { if (visuals->model) { @@ -401,11 +401,11 @@ namespace Assets break; } - case 8: - case 9: + case Game::FX_ELEM_TYPE_OMNI_LIGHT: + case Game::FX_ELEM_TYPE_SPOT_LIGHT: break; - case 0xA: + case Game::FX_ELEM_TYPE_SOUND: { if (visuals->soundName) { @@ -416,7 +416,7 @@ namespace Assets break; } - case 0xC: + case Game::FX_ELEM_TYPE_RUNNER: { if (visuals->effectDef.handle) { @@ -491,7 +491,7 @@ namespace Assets // Save_FxElemDefVisuals { - if (elemDef->elemType == 11) + if (elemDef->elemType == Game::FX_ELEM_TYPE_DECAL) { if (elemDef->visuals.markArray) { @@ -501,7 +501,7 @@ namespace Assets Game::FxElemMarkVisuals* destMarkArray = buffer->dest(); buffer->saveArray(elemDef->visuals.markArray, elemDef->visualCount); - for (char j = 0; j < elemDef->visualCount; ++j) + for (auto j = 0; j < elemDef->visualCount; ++j) { if (elemDef->visuals.markArray[j].materials[0]) { @@ -563,7 +563,7 @@ namespace Assets { AssertSize(Game::FxElemExtendedDefPtr, 4); - if (elemDef->elemType == 3) + if (elemDef->elemType == Game::FX_ELEM_TYPE_TRAIL) { // Save_FxTrailDef { @@ -597,7 +597,7 @@ namespace Assets } } } - else if (elemDef->elemType == 6) + else if (elemDef->elemType == Game::FX_ELEM_TYPE_SPARK_FOUNTAIN) { if (elemDef->extended.sparkFountainDef) { diff --git a/src/Components/Modules/AssetInterfaces/IFxWorld.cpp b/src/Components/Modules/AssetInterfaces/IFxWorld.cpp index cec5b4dc..6988543f 100644 --- a/src/Components/Modules/AssetInterfaces/IFxWorld.cpp +++ b/src/Components/Modules/AssetInterfaces/IFxWorld.cpp @@ -186,11 +186,20 @@ namespace Assets } } - void IFxWorld::load(Game::XAssetHeader* /*header*/, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/) + void IFxWorld::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { Game::FxWorld* map = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FXWORLD, name.data()).fxWorld; if (map) return; - Components::Logger::Error(Game::ERR_FATAL, "Missing fx_map {}... you can't make them yet you idiot.", name); + // Generate + map = builder->getAllocator()->allocate(); + map->name = builder->getAllocator()->duplicateString(name); + + // No glass for you! + ZeroMemory(&map->glassSys, sizeof(map->glassSys)); + + map->glassSys.firstFreePiece = 0xFFFF; // That's how rust has it (no glass) + + header->fxWorld = map; } } diff --git a/src/Components/Modules/AssetInterfaces/IGameWorldMp.cpp b/src/Components/Modules/AssetInterfaces/IGameWorldMp.cpp index 64c53039..1656d480 100644 --- a/src/Components/Modules/AssetInterfaces/IGameWorldMp.cpp +++ b/src/Components/Modules/AssetInterfaces/IGameWorldMp.cpp @@ -1,6 +1,8 @@ #include #include "IGameWorldMp.hpp" +#define IW4X_GAMEWORLD_VERSION 1 + namespace Assets { void IGameWorldMp::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) @@ -75,4 +77,118 @@ namespace Assets buffer->popBlock(); } + + void IGameWorldMp::load(Game::XAssetHeader* header, const std::string& _name, Components::ZoneBuilder::Zone* builder) + { + std::string name = _name; + Utils::String::Replace(name, "maps/mp/", ""); + Utils::String::Replace(name, ".d3dbsp", ""); + + Components::FileSystem::File gameWorld(std::format("gameworld/{}.iw4x.json", name)); + + if (gameWorld.exists()) + { + nlohmann::json gameWorldJson; + + try + { + + gameWorldJson = nlohmann::json::parse(gameWorld.getBuffer()); + } + catch (const std::exception& e) + { + Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid JSON for gameworld {}! {}", name, e.what()); + return; + } + + auto* asset = builder->getAllocator()->allocate(); + + if (!gameWorldJson.is_object()) + { + Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid GameWorldMp json for {}\n", name); + return; + } + + auto version = gameWorldJson["version"].is_number() ? gameWorldJson["version"].get() : 0; + if (version != IW4X_GAMEWORLD_VERSION) + { + Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid GameWorld json version for {}, expected {} and got {}\n", name, IW4X_GAMEWORLD_VERSION, version); + return; + } + + if (!gameWorldJson["name"].is_string()) + { + Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Missing gameworld name! on {}\n", name); + return; + } + + asset->name = builder->getAllocator()->duplicateString(gameWorldJson["name"].get()); + auto glassData = builder->getAllocator()->allocate(); + + if (gameWorldJson["glassData"].is_object()) + { + auto jsonGlassData = gameWorldJson["glassData"]; + + try + { + glassData->damageToDestroy = jsonGlassData["damageToDestroy"].get(); + glassData->damageToWeaken = jsonGlassData["damageToWeaken"].get(); + + if (jsonGlassData["glassNames"].is_array()) + { + nlohmann::json::array_t glassNames = jsonGlassData["glassNames"]; + glassData->glassNameCount = glassNames.size(); + glassData->glassNames = builder->getAllocator()->allocateArray(glassData->glassNameCount); + + for (size_t i = 0; i < glassData->glassNameCount; i++) + { + auto jsonGlassName = glassNames[i]; + glassData->glassNames[i].nameStr = builder->getAllocator()->duplicateString(jsonGlassName["nameStr"]); + + glassData->glassNames[i].name = jsonGlassName["name"].get(); + + if (jsonGlassName["piecesIndices"].is_array()) + { + nlohmann::json::array_t jsonPiecesIndices = jsonGlassName["piecesIndices"]; + glassData->glassNames[i].pieceCount = static_cast(jsonPiecesIndices.size()); + + for (size_t j = 0; j < glassData->glassNames[i].pieceCount; j++) + { + glassData->glassNames[i].pieceIndices[j] = jsonPiecesIndices[j].get(); + } + } + } + } + + if (gameWorldJson["glassPieces"].is_array()) + { + nlohmann::json::array_t glassPieces = gameWorldJson["glassPieces"]; + glassData->pieceCount = glassPieces.size(); + glassData->glassPieces = builder->getAllocator()->allocateArray(glassData->pieceCount); + + for (size_t i = 0; i < glassData->pieceCount; i++) + { + glassData->glassPieces[i].collapseTime = glassPieces[i]["collapseTime"].get(); + glassData->glassPieces[i].damageTaken = glassPieces[i]["damageTaken"].get(); + glassData->glassPieces[i].lastStateChangeTime = glassPieces[i]["lastStateChangeTime"].get(); + glassData->glassPieces[i].impactDir = glassPieces[i]["impactDir"].get(); + + nlohmann::json::array_t jsonPos = glassPieces[i]["impactPos"]; + glassData->glassPieces[i].impactPos[0] = jsonPos[0].get(); + glassData->glassPieces[i].impactPos[1] = jsonPos[1].get(); + } + } + } + catch (const nlohmann::json::exception& e) + { + Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Malformed GameWorldMp json for {} ({})\n", name, e.what()); + return; + } + } + + asset->g_glassData = glassData; + + header->gameWorldMp = asset; + } + } } diff --git a/src/Components/Modules/AssetInterfaces/IGameWorldMp.hpp b/src/Components/Modules/AssetInterfaces/IGameWorldMp.hpp index 243378ef..f19b8d58 100644 --- a/src/Components/Modules/AssetInterfaces/IGameWorldMp.hpp +++ b/src/Components/Modules/AssetInterfaces/IGameWorldMp.hpp @@ -8,5 +8,6 @@ namespace Assets Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP; } void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; + void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override; }; } diff --git a/src/Components/Modules/AssetInterfaces/IGameWorldSp.cpp b/src/Components/Modules/AssetInterfaces/IGameWorldSp.cpp index 56205bce..0b06b0c2 100644 --- a/src/Components/Modules/AssetInterfaces/IGameWorldSp.cpp +++ b/src/Components/Modules/AssetInterfaces/IGameWorldSp.cpp @@ -151,11 +151,11 @@ namespace Assets void IGameWorldSp::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) { - AssertSize(Game::GameWorldMp, 8); + AssertSize(Game::GameWorldSp, 0x38); Utils::Stream* buffer = builder->getBuffer(); - Game::GameWorldSp* asset = header.gameWorldSp; - Game::GameWorldSp* dest = buffer->dest(); + auto* asset = header.gameWorldSp; + auto* dest = buffer->dest(); buffer->save(asset); buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); @@ -187,7 +187,7 @@ namespace Assets for (char j = 0; j < 5; ++j) { - builder->mapScriptString(&(&node->constant.targetname)[j]); + builder->mapScriptString((&node->constant.targetname)[j]); } if (node->constant.Links) diff --git a/src/Components/Modules/AssetInterfaces/IGfxImage.cpp b/src/Components/Modules/AssetInterfaces/IGfxImage.cpp index e7be7614..3c7bfee4 100644 --- a/src/Components/Modules/AssetInterfaces/IGfxImage.cpp +++ b/src/Components/Modules/AssetInterfaces/IGfxImage.cpp @@ -18,12 +18,12 @@ namespace Assets } image->name = builder->getAllocator()->duplicateString(name); - image->semantic = 2; + image->semantic = Game::TextureSemantic::TS_COLOR_MAP; const char* tempName = image->name; if (tempName[0] == '*') tempName++; - Components::FileSystem::File imageFile(Utils::String::VA("images/%s.iw4xImage", tempName)); + Components::FileSystem::File imageFile(std::format("images/{}.iw4xImage", tempName)); if (imageFile.exists()) { Utils::Stream::Reader reader(builder->getAllocator(), imageFile.getBuffer()); @@ -35,7 +35,7 @@ namespace Assets } image->mapType = reader.read(); - image->semantic = reader.read(); + image->semantic = reader.read(); image->category = reader.read(); int dataLength = reader.read(); diff --git a/src/Components/Modules/AssetInterfaces/IGfxLightDef.cpp b/src/Components/Modules/AssetInterfaces/IGfxLightDef.cpp index 6347f993..c2e58c70 100644 --- a/src/Components/Modules/AssetInterfaces/IGfxLightDef.cpp +++ b/src/Components/Modules/AssetInterfaces/IGfxLightDef.cpp @@ -7,7 +7,7 @@ namespace Assets { void IGfxLightDef::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File mapFile(Utils::String::VA("lights/%s.iw4xLight", name.data())); + Components::FileSystem::File mapFile(std::format("lights/{}.iw4xLight", name)); if (mapFile.exists()) { diff --git a/src/Components/Modules/AssetInterfaces/IGfxWorld.cpp b/src/Components/Modules/AssetInterfaces/IGfxWorld.cpp index 556303be..8548c309 100644 --- a/src/Components/Modules/AssetInterfaces/IGfxWorld.cpp +++ b/src/Components/Modules/AssetInterfaces/IGfxWorld.cpp @@ -3,6 +3,27 @@ #define IW4X_GFXMAP_VERSION 1 +// The xmodel vehicle_small_hatch_green_destructible_mp causes EXTREME lag +// when placed in the world, for reasons unknown. +// +// Something happens with the SModelSurfIterator which makes it load garbage +// as an XSurface in the middle of otherwise valid surfaces. This bug is very +// easy to reproduce with an empty map and just this car in the middle +// +// As of know we do not know why the iterator corruption occurs or what causes +// it. It doesn't seem linked to the SModel, nor to the materials or techsets, +// nor to the sortkeys, nor to the tilemode, boneinfo, and so on. So for now +// and to make it work for majority of users, we just swap the car. (no, using +// the identical car from iw4's favela_escape doesn't work either!) +// +// Two other models have this problem: ch_apartment_9story_noentry_02 and +// ch_apartment_5story_noentry_01 +// But these exist in mp_vacant in slightly different versions, and can be +// swapped safely by deleting the two .iw4XModel files and requiring mp_vacant +// or a minimal zone containing just these two models. +// +#define SWAP_GREEN_VEHICLE_XMODEL 1 + namespace Assets { void IGfxWorld::loadGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader) @@ -47,7 +68,11 @@ namespace Assets if (model->model) { - model->model = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, reader->readString().data(), builder).model; + auto name = reader->readString(); + + model->model = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, name.data(), builder).model; + + assert(model->model); } } } @@ -129,7 +154,7 @@ namespace Assets Utils::String::Replace(name, "maps/mp/", ""); Utils::String::Replace(name, ".d3dbsp", ""); - Components::FileSystem::File mapFile(Utils::String::VA("gfxworld/%s.iw4xGfxWorld", name.data())); + Components::FileSystem::File mapFile(std::format("gfxworld/{}.iw4xGfxWorld", name)); if (mapFile.exists()) { diff --git a/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp b/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp index 6e282c15..d651e99a 100644 --- a/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp +++ b/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp @@ -5,7 +5,7 @@ namespace Assets { void ILoadedSound::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File soundFile(Utils::String::VA("loaded_sound/%s", name.data())); + Components::FileSystem::File soundFile(std::format("loaded_sound/{}", name)); if (!soundFile.exists()) { header->loadSnd = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).loadSnd; @@ -120,17 +120,8 @@ namespace Assets if (asset->sound.data) { - if (builder->hasPointer(asset->sound.data)) - { - dest->sound.data = builder->getPointer(asset->sound.data); - } - else - { - builder->storePointer(asset->sound.data); - - buffer->saveArray(asset->sound.data, asset->sound.info.data_len); - Utils::Stream::ClearPointer(&dest->sound.data); - } + buffer->saveArray(asset->sound.data, asset->sound.info.data_len); + Utils::Stream::ClearPointer(&dest->sound.data); } buffer->popBlock(); diff --git a/src/Components/Modules/AssetInterfaces/ILocalizeEntry.cpp b/src/Components/Modules/AssetInterfaces/ILocalizeEntry.cpp index 5b5eb82a..243c0958 100644 --- a/src/Components/Modules/AssetInterfaces/ILocalizeEntry.cpp +++ b/src/Components/Modules/AssetInterfaces/ILocalizeEntry.cpp @@ -81,4 +81,51 @@ namespace Assets builder->addRawAsset(type, entry); } } + + void ILocalizeEntry::ParseLocalizedStringsJson(Components::ZoneBuilder::Zone* builder, Components::FileSystem::File& file) + { + nlohmann::json localize; + try + { + Components::Logger::Debug("Parsing localized string \"{}\"...", file.getName()); + localize = nlohmann::json::parse(file.getBuffer()); + } + catch (const std::exception& ex) + { + Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}\n", ex.what()); + return; + } + + if (!localize.is_object()) + { + Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Localized strings json file '{}' should be an object!", file.getName()); + return; + } + + std::vector assets; + + try + { + for (const auto& [key, value] : localize.items()) + { + const auto valueStr = value.get(); + + auto* entry = builder->getAllocator()->allocate(); + entry->name = builder->getAllocator()->duplicateString(key); + entry->value = builder->getAllocator()->duplicateString(valueStr); + + assets.emplace_back(entry); + } + } + catch (const std::exception& ex) + { + Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}: Localized strings json file '{}' contains invalid data!", ex.what(), file.getName()); + } + + auto type = Game::DB_GetXAssetNameType("localize"); + for (const auto& entry : assets) + { + builder->addRawAsset(type, entry); + } + } } diff --git a/src/Components/Modules/AssetInterfaces/ILocalizeEntry.hpp b/src/Components/Modules/AssetInterfaces/ILocalizeEntry.hpp index b01b9c81..3818e1d4 100644 --- a/src/Components/Modules/AssetInterfaces/ILocalizeEntry.hpp +++ b/src/Components/Modules/AssetInterfaces/ILocalizeEntry.hpp @@ -11,5 +11,6 @@ namespace Assets void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; static void ParseLocalizedStringsFile(Components::ZoneBuilder::Zone* builder, const std::string& name, const std::string& filename); + static void ParseLocalizedStringsJson(Components::ZoneBuilder::Zone* builder, Components::FileSystem::File& file); }; } diff --git a/src/Components/Modules/AssetInterfaces/IMapEnts.cpp b/src/Components/Modules/AssetInterfaces/IMapEnts.cpp index 9cd343a0..11f4ac0a 100644 --- a/src/Components/Modules/AssetInterfaces/IMapEnts.cpp +++ b/src/Components/Modules/AssetInterfaces/IMapEnts.cpp @@ -10,7 +10,7 @@ namespace Assets Utils::String::Replace(name, "mp/", ""); Utils::String::Replace(name, ".d3dbsp", ""); - Components::FileSystem::File ents(Utils::String::VA("mapents/%s.ents", name.data())); + Components::FileSystem::File ents(std::format("mapents/{}.ents", name)); if (ents.exists()) { Game::MapEnts* entites = builder->getAllocator()->allocate(); @@ -48,7 +48,7 @@ namespace Assets std::string entityString = ents.getBuffer(); - entites->name = builder->getAllocator()->duplicateString(Utils::String::VA("maps/mp/%s.d3dbsp", name.data())); + entites->name = builder->getAllocator()->duplicateString(std::format("maps/mp/{}.d3dbsp", name)); entites->entityString = builder->getAllocator()->duplicateString(entityString); entites->numEntityChars = entityString.size() + 1; diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index 499acf36..42b63a8e 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -1,15 +1,439 @@ #include #include "IMaterial.hpp" -#define IW4X_MAT_VERSION "1" +#define IW4X_MAT_BIN_VERSION "1" +#define IW4X_MAT_JSON_VERSION 1 namespace Assets { + const std::unordered_map techSetCorrespondance = + { + {"effect", "effect_blend"}, + {"effect", "effect_blend"}, + {"effect_nofog", "effect_blend_nofog"}, + {"effect_zfeather", "effect_zfeather_blend"}, + {"effect_zfeather_falloff", "effect_zfeather_falloff_add"}, + {"effect_zfeather_nofog", "effect_zfeather_add_nofog"}, + + {"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"}, + {"wc_unlit", "wc_unlit_replace_lin"}, + {"wc_unlit_alphatest", "wc_unlit_blend_lin"}, + {"wc_unlit_blend", "wc_unlit_blend_lin"}, + {"wc_unlit_replace", "wc_unlit_replace_lin"}, + {"wc_unlit_nofog", "wc_unlit_replace_lin_nofog_nocast" }, + + {"mc_unlit_replace", "mc_unlit_replace_lin"}, + {"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"}, + {"mc_unlit", "mc_unlit_replace_lin_nocast"}, + {"mc_unlit_alphatest", "mc_unlit_blend_lin"}, + {"mc_effect_nofog", "mc_effect_blend_nofog"}, + {"mc_effect_falloff_add_nofog", "mc_effect_falloff_add_nofog_eyeoffset"}, + }; + void IMaterial::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - if (!header->data) this->loadJson(header, name, builder); // Check if we want to override materials + if (!header->data) this->loadJson(header, name, builder); // Check if we want to load a material from disk + if (!header->data) this->loadBinary(header, name, builder); // Check if we want to load a material from disk (binary format) if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one - if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game + } + + + void IMaterial::loadJson(Game::XAssetHeader* header, const std::string& name, [[maybe_unused]] Components::ZoneBuilder::Zone* builder) + { + Components::FileSystem::File materialInfo(std::format("materials/{}.iw4x.json", name)); + + if (!materialInfo.exists()) return; + + Game::Material* asset = builder->getAllocator()->allocate(); + + + nlohmann::json materialJson; + try + { + materialJson = nlohmann::json::parse(materialInfo.getBuffer()); + } + catch (const std::exception& e) + { + Components::Logger::Print("Invalid material json for {} (broken json {})\n", name, e.what()); + } + + if (!materialJson.is_object()) + { + Components::Logger::Print("Invalid material json for {} (Is it zonebuilder format?)\n", name); + return; + } + + if (materialJson["version"].get() != IW4X_MAT_JSON_VERSION) + { + Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid material json version for {}, expected {} and got {}\n", name, IW4X_MAT_JSON_VERSION, materialJson["version"].get()); + return; + } + + try + { + asset->info.name = builder->getAllocator()->duplicateString(materialJson["name"].get()); + asset->info.gameFlags = static_cast(Utils::Json::ReadFlags(materialJson["gameFlags"].get(), sizeof(char))); + + asset->info.sortKey = materialJson["sortKey"].get(); + // * We do techset later * // + asset->info.textureAtlasRowCount = materialJson["textureAtlasRowCount"].get(); + asset->info.textureAtlasColumnCount = materialJson["textureAtlasColumnCount"].get(); + asset->info.surfaceTypeBits = static_cast(Utils::Json::ReadFlags(materialJson["surfaceTypeBits"].get(), sizeof(int))); + asset->info.hashIndex = materialJson["hashIndex"].get(); + asset->cameraRegion = materialJson["cameraRegion"].get(); + } + catch (const nlohmann::json::exception& e) + { + Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid material json for {} (broken json {})\n", name, e.what()); + return; + } + + if (materialJson["gfxDrawSurface"].is_object()) + { + asset->info.drawSurf.fields.customIndex = materialJson["gfxDrawSurface"]["customIndex"].get(); + asset->info.drawSurf.fields.hasGfxEntIndex = materialJson["gfxDrawSurface"]["hasGfxEntIndex"].get(); + asset->info.drawSurf.fields.materialSortedIndex = materialJson["gfxDrawSurface"]["materialSortedIndex"].get(); + asset->info.drawSurf.fields.objectId = materialJson["gfxDrawSurface"]["objectId"].get(); + asset->info.drawSurf.fields.prepass = materialJson["gfxDrawSurface"]["prepass"].get(); + asset->info.drawSurf.fields.primarySortKey = materialJson["gfxDrawSurface"]["primarySortKey"].get(); + asset->info.drawSurf.fields.reflectionProbeIndex = materialJson["gfxDrawSurface"]["reflectionProbeIndex"].get(); + asset->info.drawSurf.fields.sceneLightIndex = materialJson["gfxDrawSurface"]["sceneLightIndex"].get(); + asset->info.drawSurf.fields.surfType = materialJson["gfxDrawSurface"]["surfType"].get(); + asset->info.drawSurf.fields.unused = materialJson["gfxDrawSurface"]["unused"].get(); + asset->info.drawSurf.fields.useHeroLighting = materialJson["gfxDrawSurface"]["useHeroLighting"].get(); + } + + asset->stateFlags = static_cast(Utils::Json::ReadFlags(materialJson["stateFlags"].get(), sizeof(char))); + + if (materialJson["textureTable"].is_array()) + { + nlohmann::json::array_t textureTable = materialJson["textureTable"]; + asset->textureCount = static_cast(textureTable.size()); + asset->textureTable = builder->getAllocator()->allocateArray(asset->textureCount); + + for (size_t i = 0; i < textureTable.size(); i++) + { + auto& textureJson = textureTable[i]; + if (textureJson.is_object()) + { + Game::MaterialTextureDef* textureDef = &asset->textureTable[i]; + textureDef->semantic = textureJson["semantic"].get(); + textureDef->samplerState = textureJson["samplerState"].get(); + textureDef->nameStart = textureJson["nameStart"].get(); + textureDef->nameEnd = textureJson["nameEnd"].get(); + textureDef->nameHash = textureJson["nameHash"].get(); + + if (textureDef->semantic == Game::TextureSemantic::TS_WATER_MAP) + { + Game::water_t* water = builder->getAllocator()->allocate(); + + if (textureJson["water"].is_object()) + { + auto& waterJson = textureJson["water"]; + + if (waterJson["image"].is_string()) + { + auto imageName = waterJson["image"].get(); + + water->image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, imageName.data(), builder).image; + } + + water->amplitude = waterJson["amplitude"].get(); + water->M = waterJson["M"].get(); + water->N = waterJson["N"].get(); + water->Lx = waterJson["Lx"].get(); + water->Lz = waterJson["Lz"].get(); + water->gravity = waterJson["gravity"].get(); + water->windvel = waterJson["windvel"].get(); + + auto winddir = waterJson["winddir"].get>(); + if (winddir.size() == 2) + { + std::copy(winddir.begin(), winddir.end(), water->winddir); + } + + auto codeConstant = waterJson["codeConstant"].get>(); + + if (codeConstant.size() == 4) + { + std::copy(codeConstant.begin(), codeConstant.end(), water->codeConstant); + } + + /// H0 + [[maybe_unused]] auto idealSize = water->M * water->N * sizeof(Game::complex_s); + auto h064 = waterJson["H0"].get(); + auto predictedSize = static_cast(std::ceilf((h064.size() / 4.f) * 3.f)); + assert(predictedSize >= idealSize); + + auto h0 = reinterpret_cast(builder->getAllocator()->allocate(predictedSize)); + + [[maybe_unused]] auto h0Result = base64_decode( + h064.data(), + h064.size(), + reinterpret_cast(h0), + &predictedSize + ); + + assert(h0Result == CRYPT_OK); + water->H0 = h0; + + /// WTerm + auto wTerm64 = waterJson["wTerm"].get(); + auto predictedWTermSize = static_cast(std::ceilf((wTerm64.size() / 4.f) * 3.f)); + + auto wTerm = reinterpret_cast(builder->getAllocator()->allocate(predictedWTermSize)); + + [[maybe_unused]] auto wTermResult = base64_decode( + wTerm64.data(), + wTerm64.size(), + reinterpret_cast(wTerm), + &predictedWTermSize + ); + + assert(wTermResult == CRYPT_OK); + water->wTerm = wTerm; + } + + textureDef->u.water = water; + } + else + { + textureDef->u.image = nullptr; + if (textureJson["image"].is_string()) + { + textureDef->u.image = Components::AssetHandler::FindAssetForZone + ( + Game::XAssetType::ASSET_TYPE_IMAGE, + textureJson["image"].get(), + builder + ).image; + } + } + } + } + } + + // Statebits + if (materialJson["stateBitsEntry"].is_array()) + { + nlohmann::json::array_t stateBitsEntry = materialJson["stateBitsEntry"]; + + for (size_t i = 0; i < std::min(stateBitsEntry.size(), 48u); i++) + { + asset->stateBitsEntry[i] = stateBitsEntry[i].get(); + } + } + + if (materialJson["stateBitsTable"].is_array()) + { + nlohmann::json::array_t array = materialJson["stateBitsTable"]; + asset->stateBitsCount = static_cast(array.size()); + + asset->stateBitsTable = builder->getAllocator()->allocateArray(array.size()); + + size_t statebitTableIndex = 0; + for (auto& jsonStateBitEntry : array) + { + auto stateBit = &asset->stateBitsTable[statebitTableIndex++]; + + unsigned int loadbits0 = 0; + unsigned int loadbits1 = 0; + +#define READ_INT_LB_FROM_JSON(x) unsigned int x = jsonStateBitEntry[#x].get() +#define READ_BOOL_LB_FROM_JSON(x) bool x = jsonStateBitEntry[#x].get() + + READ_INT_LB_FROM_JSON(srcBlendRgb); + READ_INT_LB_FROM_JSON(dstBlendRgb); + READ_INT_LB_FROM_JSON(blendOpRgb); + READ_INT_LB_FROM_JSON(srcBlendAlpha); + READ_INT_LB_FROM_JSON(dstBlendAlpha); + READ_INT_LB_FROM_JSON(blendOpAlpha); + READ_INT_LB_FROM_JSON(depthTest); + READ_INT_LB_FROM_JSON(polygonOffset); + + const auto alphaTest = jsonStateBitEntry["alphaTest"].get(); + const auto cullFace = jsonStateBitEntry["cullFace"].get(); + + READ_BOOL_LB_FROM_JSON(colorWriteRgb); + READ_BOOL_LB_FROM_JSON(colorWriteAlpha); + READ_BOOL_LB_FROM_JSON(polymodeLine); + + READ_BOOL_LB_FROM_JSON(gammaWrite); + READ_BOOL_LB_FROM_JSON(depthWrite); + READ_BOOL_LB_FROM_JSON(stencilFrontEnabled); + READ_BOOL_LB_FROM_JSON(stencilBackEnabled); + + READ_INT_LB_FROM_JSON(stencilFrontPass); + READ_INT_LB_FROM_JSON(stencilFrontFail); + READ_INT_LB_FROM_JSON(stencilFrontZFail); + READ_INT_LB_FROM_JSON(stencilFrontFunc); + READ_INT_LB_FROM_JSON(stencilBackPass); + READ_INT_LB_FROM_JSON(stencilBackFail); + READ_INT_LB_FROM_JSON(stencilBackZFail); + READ_INT_LB_FROM_JSON(stencilBackFunc); + + loadbits0 |= srcBlendRgb << Game::GFXS0_SRCBLEND_RGB_SHIFT; + loadbits0 |= dstBlendRgb << Game::GFXS0_DSTBLEND_RGB_SHIFT; + loadbits0 |= blendOpRgb << Game::GFXS0_BLENDOP_RGB_SHIFT; + loadbits0 |= srcBlendAlpha << Game::GFXS0_SRCBLEND_ALPHA_SHIFT; + loadbits0 |= dstBlendAlpha << Game::GFXS0_DSTBLEND_ALPHA_SHIFT; + loadbits0 |= blendOpAlpha << Game::GFXS0_BLENDOP_ALPHA_SHIFT; + + if (depthTest == -1) + { + loadbits1 |= Game::GFXS1_DEPTHTEST_DISABLE; + } + else + { + loadbits1 |= depthTest << Game::GFXS1_DEPTHTEST_SHIFT; + } + + loadbits1 |= polygonOffset << Game::GFXS1_POLYGON_OFFSET_SHIFT; + + if (alphaTest == "disable") + { + loadbits0 |= Game::GFXS0_ATEST_DISABLE; + } + else if (alphaTest == ">0") + { + loadbits0 |= Game::GFXS0_ATEST_GT_0; + } + else if (alphaTest == "<128") + { + loadbits0 |= Game::GFXS0_ATEST_LT_128; + } + else if (alphaTest == ">=128") + { + loadbits0 |= Game::GFXS0_ATEST_GE_128; + } + else + { + Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid alphatest loadbit0 '{}' in material {}\n", alphaTest, name); + return; + } + + if (cullFace == "none") + { + loadbits0 |= Game::GFXS0_CULL_NONE; + } + else if (cullFace == "back") + { + loadbits0 |= Game::GFXS0_CULL_BACK; + } + else if (cullFace == "front") + { + loadbits0 |= Game::GFXS0_CULL_FRONT; + } + else + { + Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid cullFace loadbit0 '{}' in material {}\n", cullFace, name); + return; + } + + if (gammaWrite) + { + loadbits0 |= Game::GFXS0_GAMMAWRITE; + } + + if (colorWriteAlpha) + { + loadbits0 |= Game::GFXS0_COLORWRITE_ALPHA; + } + + if (colorWriteRgb) + { + loadbits0 |= Game::GFXS0_COLORWRITE_RGB; + } + + if (polymodeLine) + { + loadbits0 |= Game::GFXS0_POLYMODE_LINE; + } + + if (depthWrite) + { + loadbits1 |= Game::GFXS1_DEPTHWRITE; + } + if (stencilFrontEnabled) + { + loadbits1 |= Game::GFXS1_STENCIL_FRONT_ENABLE; + } + if (stencilBackEnabled) + { + loadbits1 |= Game::GFXS1_STENCIL_BACK_ENABLE; + } + + loadbits1 |= stencilFrontPass << Game::GFXS1_STENCIL_FRONT_PASS_SHIFT; + loadbits1 |= stencilFrontFail << Game::GFXS1_STENCIL_FRONT_FAIL_SHIFT; + loadbits1 |= stencilFrontZFail << Game::GFXS1_STENCIL_FRONT_ZFAIL_SHIFT; + loadbits1 |= stencilFrontFunc << Game::GFXS1_STENCIL_FRONT_FUNC_SHIFT; + loadbits1 |= stencilBackPass << Game::GFXS1_STENCIL_BACK_PASS_SHIFT; + loadbits1 |= stencilBackFail << Game::GFXS1_STENCIL_BACK_FAIL_SHIFT; + loadbits1 |= stencilBackZFail << Game::GFXS1_STENCIL_BACK_ZFAIL_SHIFT; + loadbits1 |= stencilBackFunc << Game::GFXS1_STENCIL_BACK_FUNC_SHIFT; + + stateBit->loadBits[0] = loadbits0; + stateBit->loadBits[1] = loadbits1; + } + + // Constant table + if (materialJson["constantTable"].is_array()) + { + + nlohmann::json::array_t constants = materialJson["constantTable"]; + asset->constantCount = static_cast(constants.size()); + auto table = builder->getAllocator()->allocateArray(asset->constantCount); + + for (size_t constantIndex = 0; constantIndex < asset->constantCount; constantIndex++) + { + auto& constant = constants[constantIndex]; + auto entry = &table[constantIndex]; + + auto litVec = constant["literal"].get>(); + std::copy(litVec.begin(), litVec.end(), entry->literal); + + auto constantName = constant["name"].get(); + std::copy(constantName.begin(), constantName.end(), entry->name); + + entry->nameHash = constant["nameHash"].get(); + } + + asset->constantTable = table; + } + + if (materialJson["techniqueSet"].is_string()) + { + const std::string techsetName = materialJson["techniqueSet"].get(); + asset->techniqueSet = findWorkingTechset(techsetName, asset, builder); + + if (asset->techniqueSet == nullptr) + { + assert(false); + } + } + + header->material = asset; + } + } + + Game::MaterialTechniqueSet* IMaterial::findWorkingTechset(std::string techsetName, [[maybe_unused]] Game::Material* material, Components::ZoneBuilder::Zone* builder) const + { + Game::MaterialTechniqueSet* techset; + + // Pass 1: Identical techset (1:1) + techset = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, techsetName.data(), builder).techniqueSet; + if (techset != nullptr) + { + return techset; + } + + // We do no more cause we use CoD4 techset and they should always be present + // If one day we want to go back to mw2 fallback we can add extra steps here! + + return nullptr; } void IMaterial::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) @@ -30,30 +454,7 @@ namespace Assets "_add_lin_nofog", }; - static std::unordered_map techSetCorrespondance = - { - {"effect", "effect_blend"}, - {"effect", "effect_blend"}, - {"effect_nofog", "effect_blend_nofog"}, - {"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_replace_lin"}, - {"wc_unlit_alphatest", "wc_unlit_blend_lin"}, - {"wc_unlit_blend", "wc_unlit_blend_lin_ua"}, - {"wc_unlit_replace", "wc_unlit_replace_lin"}, - {"wc_unlit_nofog", "wc_unlit_replace_lin_nofog_nocast" }, - - {"mc_unlit_replace", "mc_unlit_replace_lin"}, - {"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"}, - {"mc_unlit", "mc_unlit_replace_lin_nocast"}, - {"mc_unlit_alphatest", "mc_unlit_blend_lin"} - }; - - Components::FileSystem::File materialFile(Utils::String::VA("materials/%s.iw4xMaterial", name.data())); + Components::FileSystem::File materialFile(std::format("materials/{}.iw4xMaterial", name)); if (!materialFile.exists()) return; Utils::Stream::Reader reader(builder->getAllocator(), materialFile.getBuffer()); @@ -66,9 +467,9 @@ namespace Assets std::string version; version.push_back(reader.read()); - if (version != IW4X_MAT_VERSION) + if (version != IW4X_MAT_BIN_VERSION) { - Components::Logger::Error(Game::ERR_FATAL, "Reading material '{}' failed, expected version is {}, but it was {}!", name, IW4X_MAT_VERSION, version); + Components::Logger::Error(Game::ERR_FATAL, "Reading material '{}' failed, expected version is {}, but it was {}!", name, IW4X_MAT_BIN_VERSION, version); } auto* asset = reader.readObject(); @@ -127,7 +528,7 @@ namespace Assets { Game::MaterialTextureDef* textureDef = &asset->textureTable[i]; - if (textureDef->semantic == SEMANTIC_WATER_MAP) + if (textureDef->semantic == Game::TextureSemantic::TS_WATER_MAP) { if (textureDef->u.water) { @@ -175,29 +576,29 @@ namespace Assets // Find correct sortkey by comparing techsets Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset](Game::XAssetEntry* entry) - { - if (!replacementFound) { - Game::XAssetHeader header = entry->asset.header; - - const char* name = asset->techniqueSet->name; - if (name[0] == ',') ++name; - - if (std::string(name) == header.material->techniqueSet->name) + if (!replacementFound) { - asset->info.sortKey = header.material->info.sortKey; + Game::XAssetHeader header = entry->asset.header; - // This is temp, as nobody has time to fix materials - asset->stateBitsCount = header.material->stateBitsCount; - asset->stateBitsTable = header.material->stateBitsTable; - std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48); - asset->constantCount = header.material->constantCount; - asset->constantTable = header.material->constantTable; - Components::Logger::Print("For {}, copied constants & statebits from {}\n", asset->info.name, header.material->info.name); - replacementFound = true; + const char* name = asset->techniqueSet->name; + if (name[0] == ',') ++name; + + if (std::string(name) == header.material->techniqueSet->name) + { + asset->info.sortKey = header.material->info.sortKey; + + // This is temp, as nobody has time to fix materials + asset->stateBitsCount = header.material->stateBitsCount; + asset->stateBitsTable = header.material->stateBitsTable; + std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, ARRAYSIZE(asset->stateBitsEntry)); + asset->constantCount = header.material->constantCount; + asset->constantTable = header.material->constantTable; + Components::Logger::Print("For {}, copied constants & statebits from {}\n", asset->info.name, header.material->info.name); + replacementFound = true; + } } - } - }, false); + }, false); if (!replacementFound) { @@ -222,70 +623,72 @@ namespace Assets Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, techsetMatches](Game::XAssetEntry* entry) - { - if (!replacementFound) { - Game::XAssetHeader header = entry->asset.header; - - if (techsetMatches(header.material, asset)) + if (!replacementFound) { - Components::Logger::Print("Material {} with techset {} has been mapped to {}\n", asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name); - asset->info.sortKey = header.material->info.sortKey; - replacementFound = true; + Game::XAssetHeader header = entry->asset.header; + + if (techsetMatches(header.material, asset)) + { + Components::Logger::Print("Material {} with techset {} has been mapped to {}\n", asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name); + asset->info.sortKey = header.material->info.sortKey; + replacementFound = true; + } } - } - }, false); + }, false); } if (!replacementFound && asset->techniqueSet) { Components::Logger::Print("No replacement found for material {} with techset {}\n", asset->info.name, asset->techniqueSet->name); std::string techName = asset->techniqueSet->name; - if (techSetCorrespondance.contains(techName)) + if (const auto itr = techSetCorrespondance.find(techName); itr != techSetCorrespondance.end()) { - auto iw4TechSetName = techSetCorrespondance[techName]; - Game::XAssetEntry* iw4TechSet = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data()); + auto& iw4TechSetName = itr->second; + auto* iw4TechSet = Game::DB_FindXAssetEntry(Game::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data()); - if (iw4TechSet) + if (iw4TechSet) { Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, iw4TechSet](Game::XAssetEntry* entry) - { - if (!replacementFound) { - Game::XAssetHeader header = entry->asset.header; - - if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet - && std::string(header.material->info.name).find("icon") == std::string::npos) // Yeah this has a tendency to fuck up a LOT of transparent materials + if (!replacementFound) { + Game::XAssetHeader header = entry->asset.header; Components::Logger::Print("Material {} with techset {} has been mapped to {} (last chance!), taking the sort key of material {}\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; + // Yeah this has a tendency to fuck up a LOT of transparent materials + if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet && std::string(header.material->info.name).find("icon") != std::string::npos) + { + Components::Logger::Print("Material {} with techset {} has been mapped to {} (last chance!), taking the sort key of material {}\n", + asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name, header.material->info.name); - // this is terrible! - asset->stateBitsCount = header.material->stateBitsCount; - asset->stateBitsTable = header.material->stateBitsTable; - std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48); - asset->constantCount = header.material->constantCount; - asset->constantTable = header.material->constantTable; + asset->info.sortKey = header.material->info.sortKey; + asset->techniqueSet = iw4TechSet->asset.header.techniqueSet; - replacementFound = true; + // this is terrible! + asset->stateBitsCount = header.material->stateBitsCount; + asset->stateBitsTable = header.material->stateBitsTable; + std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48); + asset->constantCount = header.material->constantCount; + asset->constantTable = header.material->constantTable; + + replacementFound = true; + } } - } - }, false); + }, false); - if (!replacementFound) + if (!replacementFound) { Components::Logger::Print("Could not find any loaded material with techset {} (in replacement of {}), so I cannot set the sortkey for material {}\n", iw4TechSetName, asset->techniqueSet->name, asset->info.name); } } - else + else { Components::Logger::Print("Could not find any loaded techset with iw4 name {} for iw3 techset {}\n", iw4TechSetName, asset->techniqueSet->name); } } - else + else { Components::Logger::Print("Could not match iw3 techset {} with any of the techsets I know! This is a critical error, there's a good chance the map will not be playable.\n", techName); } @@ -327,15 +730,6 @@ namespace Assets header->material = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).material; } - void IMaterial::loadJson(Game::XAssetHeader* header, const std::string& name, [[maybe_unused]] Components::ZoneBuilder::Zone* builder) - { - Components::FileSystem::File materialInfo(Utils::String::VA("materials/%s.json", name.data())); - - if (!materialInfo.exists()) return; - - header->material = nullptr; - } - void IMaterial::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) { Game::Material* asset = header.material; @@ -351,7 +745,7 @@ namespace Assets { if (asset->textureTable[i].u.image) { - if (asset->textureTable[i].semantic == SEMANTIC_WATER_MAP) + if (asset->textureTable[i].semantic == Game::TextureSemantic::TS_WATER_MAP) { if (asset->textureTable[i].u.water->image) { @@ -411,7 +805,7 @@ namespace Assets auto* destTextureDef = &destTextureTable[i]; auto* textureDef = &asset->textureTable[i]; - if (textureDef->semantic == SEMANTIC_WATER_MAP) + if (textureDef->semantic == Game::TextureSemantic::TS_WATER_MAP) { AssertSize(Game::water_t, 68); diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.hpp b/src/Components/Modules/AssetInterfaces/IMaterial.hpp index 54c10449..2aa645b7 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.hpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.hpp @@ -13,5 +13,8 @@ namespace Assets void loadJson(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); void loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); void loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); + + private: + Game::MaterialTechniqueSet* findWorkingTechset(const std::string techsetName, Game::Material* material, Components::ZoneBuilder::Zone* builder) const; }; } diff --git a/src/Components/Modules/AssetInterfaces/IMaterialPixelShader.cpp b/src/Components/Modules/AssetInterfaces/IMaterialPixelShader.cpp index fd91bae6..7c3737e4 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterialPixelShader.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterialPixelShader.cpp @@ -1,15 +1,15 @@ #include #include "IMaterialPixelShader.hpp" -#define IW4X_TECHSET_VERSION "0" +#define GFX_RENDERER_SHADER_SM3 0 namespace Assets { void IMaterialPixelShader::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game + if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one } void IMaterialPixelShader::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/) @@ -19,36 +19,19 @@ namespace Assets void IMaterialPixelShader::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File psFile(Utils::String::VA("ps/%s.iw4xPS", name.data())); + Components::FileSystem::File psFile(std::format("ps/{}.cso", name)); if (!psFile.exists()) return; - Utils::Stream::Reader reader(builder->getAllocator(), psFile.getBuffer()); + auto buff = psFile.getBuffer(); + auto programSize = buff.size() / 4; + Game::MaterialPixelShader* asset = builder->getAllocator()->allocate(); - char* magic = reader.readArray(8); - if (std::memcmp(magic, "IW4xPIXL", 8)) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading pixel shader '{}' failed, header is invalid!", name); - } + asset->name = builder->getAllocator()->duplicateString(name); + asset->prog.loadDef.loadForRenderer = GFX_RENDERER_SHADER_SM3; + asset->prog.loadDef.programSize = static_cast(programSize); + asset->prog.loadDef.program = builder->getAllocator()->allocateArray(programSize); + memcpy_s(asset->prog.loadDef.program, buff.size(), buff.data(), buff.size()); - std::string version; - version.push_back(reader.read()); - if (version != IW4X_TECHSET_VERSION) - { - Components::Logger::Error(Game::ERR_FATAL, - "Reading pixel shader '{}' failed, expected version is {}, but it was {}!", name, IW4X_TECHSET_VERSION, version); - } - - Game::MaterialPixelShader* asset = reader.readObject(); - - if (asset->name) - { - asset->name = reader.readCString(); - } - - if (asset->prog.loadDef.program) - { - asset->prog.loadDef.program = reader.readArray(asset->prog.loadDef.programSize); - } header->pixelShader = asset; } diff --git a/src/Components/Modules/AssetInterfaces/IMaterialTechniqueSet.cpp b/src/Components/Modules/AssetInterfaces/IMaterialTechniqueSet.cpp index 30364266..839c3fb0 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterialTechniqueSet.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterialTechniqueSet.cpp @@ -1,14 +1,14 @@ #include #include "IMaterialTechniqueSet.hpp" -#define IW4X_TECHSET_VERSION "0" +#define IW4X_TECHSET_VERSION 1 namespace Assets { void IMaterialTechniqueSet::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { + if (!header->data) this->loadFromDisk(header, name, builder); // Check if we need to import a new one into the game if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one - if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game } void IMaterialTechniqueSet::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/) @@ -16,132 +16,202 @@ namespace Assets header->techniqueSet = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).techniqueSet; } - void IMaterialTechniqueSet::loadBinaryTechnique(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder) + void IMaterialTechniqueSet::loadTechniqueFromDisk(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder) { AssertSize(Game::MaterialPass, 20); - Components::FileSystem::File techFile(Utils::String::VA("techniques/%s.iw4xTech", name.data())); - if (!techFile.exists()) { + Components::FileSystem::File techFile(std::format("techniques/{}.iw4x.json", name)); + if (!techFile.exists()) + { *tech = nullptr; + Components::Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Missing technique '{}'\n", name); return; } - Utils::Stream::Reader reader(builder->getAllocator(), techFile.getBuffer()); + nlohmann::json technique; - char* magic = reader.readArray(8); - if (std::memcmp(magic, "IW4xTECH", 8)) + try { - Components::Logger::Error(Game::ERR_FATAL, "Reading technique '{}' failed, header is invalid!", name); + technique = nlohmann::json::parse(techFile.getBuffer()); + } + catch (std::exception& e) + { + Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, file is messed up! {}", name, e.what()); } - std::string version; - version.push_back(reader.read()); + int version = technique["version"].get(); + if (version != IW4X_TECHSET_VERSION) { Components::Logger::Error(Game::ERR_FATAL, - "Reading technique '{}' failed, expected version is {}, but it was {}!", name, IW4X_TECHSET_VERSION, version.data()); + "Reading technique '{}' failed, expected version is {}, but it was {}!", name, IW4X_TECHSET_VERSION, version); } - unsigned short flags = reader.read(); - unsigned short passCount = reader.read(); + unsigned short flags = static_cast(Utils::Json::ReadFlags(technique["flags"].get(), sizeof(short))); - Game::MaterialTechnique* asset = (Game::MaterialTechnique*)builder->getAllocator()->allocateArray(sizeof(Game::MaterialTechnique) + (sizeof(Game::MaterialPass) * (passCount - 1))); + if (technique["passArray"].is_array()) + { + nlohmann::json::array_t passArray = technique["passArray"]; - asset->name = builder->getAllocator()->duplicateString(name); - asset->flags = flags; - asset->passCount = passCount; + Game::MaterialTechnique* asset = (Game::MaterialTechnique*)builder->getAllocator()->allocateArray(sizeof(Game::MaterialTechnique) + (sizeof(Game::MaterialPass) * (passArray.size() - 1))); - Game::MaterialPass* passes = reader.readArray(passCount); - std::memcpy(asset->passArray, passes, sizeof(Game::MaterialPass) * passCount); + asset->name = builder->getAllocator()->duplicateString(name); + asset->flags = flags; + asset->passCount = static_cast(passArray.size()); - for (unsigned short i = 0; i < asset->passCount; i++) - { - Game::MaterialPass* pass = &asset->passArray[i]; + Game::MaterialPass* passes = builder->getAllocator()->allocateArray(asset->passCount); + std::memcpy(asset->passArray, passes, sizeof(Game::MaterialPass) * asset->passCount); - if (pass->vertexDecl) + for (unsigned short i = 0; i < asset->passCount; i++) { - const char* declName = reader.readCString(); - pass->vertexDecl = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXDECL, declName, builder).vertexDecl; - } + Game::MaterialPass* pass = &asset->passArray[i]; + auto jsonPass = passArray[i]; - if (pass->vertexShader) - { - const char* vsName = reader.readCString(); - pass->vertexShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, vsName, builder).vertexShader; - - } - - if (pass->pixelShader) - { - const char* psName = reader.readCString(); - pass->pixelShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PIXELSHADER, psName, builder).pixelShader; - } - - pass->args = reader.readArray(pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount); - - for (int j = 0; j < pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount; j++) - { - if (pass->args[j].type == 1 || pass->args[j].type == 7) + if (jsonPass["vertexDeclaration"].is_string()) { - pass->args[j].u.literalConst = reader.readArray(4); + auto declName = jsonPass["vertexDeclaration"].get(); + pass->vertexDecl = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXDECL, declName, builder).vertexDecl; } - if (pass->args[j].type == 3 || pass->args[j].type == 5) + if (jsonPass["vertexShader"].is_string()) { - pass->args[j].u.codeConst.index = *reader.readObject(); - pass->args[j].u.codeConst.firstRow = *reader.readObject(); - pass->args[j].u.codeConst.rowCount = *reader.readObject(); + auto vsName = jsonPass["vertexShader"].get(); + pass->vertexShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, vsName, builder).vertexShader; + } + + if (jsonPass["pixelShader"].is_string()) + { + auto psName = jsonPass["pixelShader"].get(); + pass->pixelShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PIXELSHADER, psName, builder).pixelShader; + } + + pass->perPrimArgCount = jsonPass["perPrimArgCount"].get(); + pass->perObjArgCount = jsonPass["perObjArgCount"].get(); + pass->stableArgCount = jsonPass["stableArgCount"].get(); + pass->customSamplerFlags = jsonPass["customSamplerFlags"].get(); + + + if (jsonPass["arguments"].is_array()) + { + nlohmann::json::array_t jsonAguments = jsonPass["arguments"]; + + pass->args = builder->getAllocator()->allocateArray(jsonAguments.size()); + + for (size_t j = 0; j < jsonAguments.size(); j++) + { + auto jsonArgument = jsonAguments[j]; + Game::MaterialShaderArgument* argument = &pass->args[j]; + + argument->type = jsonArgument["type"].get(); + argument->dest = jsonArgument["dest"].get(); + + if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_LITERAL_VERTEX_CONST || + argument->type == Game::MaterialShaderArgumentType::MTL_ARG_LITERAL_PIXEL_CONST) + { + argument->u.literalConst = builder->getAllocator()->allocateArray(4); + + auto literals = jsonArgument["literals"].get>(); + std::copy(literals.begin(), literals.end(), argument->u.literalConst); + } + else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_VERTEX_CONST || + argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_PIXEL_CONST) + { + if (jsonArgument["codeConst"].is_object()) + { + auto codeConst = jsonArgument["codeConst"]; + + argument->u.codeConst.index = codeConst["index"].get(); + argument->u.codeConst.firstRow = codeConst["firstRow"].get(); + argument->u.codeConst.rowCount = codeConst["rowCount"].get(); + } + } + else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_PIXEL_SAMPLER || + argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_VERTEX_CONST || + argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_PIXEL_CONST) + { + argument->u.nameHash = jsonArgument["nameHash"].get(); + } + else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_PIXEL_SAMPLER) + { + argument->u.codeSampler = jsonArgument["codeSampler"].get(); + } + } } } + + *tech = asset; } - - *tech = asset; } - void IMaterialTechniqueSet::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) + void IMaterialTechniqueSet::loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File tsFile(Utils::String::VA("techsets/%s.iw4xTS", name.data())); + Components::FileSystem::File tsFile(std::format("techsets/{}.iw4x.json", name)); if (!tsFile.exists()) return; - Utils::Stream::Reader reader(builder->getAllocator(), tsFile.getBuffer()); + nlohmann::json techset; - char* magic = reader.readArray(8); - if (std::memcmp(magic, "IW4xTSET", 8)) + try { - Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, header is invalid!", name); + techset = nlohmann::json::parse(tsFile.getBuffer()); + } + catch (std::exception& e) + { + Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, file is messed up! {}", name, e.what()); } - std::string version; - version.push_back(reader.read()); + auto version = techset["version"].get(); if (version != IW4X_TECHSET_VERSION) { Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, expected version is {}, but it was {}!", name, IW4X_TECHSET_VERSION, version); } - Game::MaterialTechniqueSet* asset = reader.readObject(); + Game::MaterialTechniqueSet* asset = builder->getAllocator()->allocate(); - if (asset->name) + if (asset == nullptr) { - asset->name = reader.readCString(); + Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, allocation failed!", name); + return; } - for (int i = 0; i < 48; i++) + if (techset["name"].is_string()) { - if (asset->techniques[i]) + asset->name = builder->getAllocator()->duplicateString(techset["name"].get()); + } + + asset->hasBeenUploaded = techset["hasBeenUploaded"].get(); + asset->worldVertFormat = techset["worldVertFormat"].get(); + + + if (techset["remappedTechniqueSet"].is_string()) + { + auto remapped = techset["remappedTechniqueSet"].get(); + + if (remapped != asset->name) { - const char* techName = reader.readCString(); - this->loadBinaryTechnique(&asset->techniques[i], techName, builder); + builder->loadAssetByName(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, remapped, false); } } + if (techset["techniques"].is_object()) + { + for (int i = 0; i < Game::TECHNIQUE_COUNT; i++) + { + auto technique = techset["techniques"].at(std::to_string(i)); + + if (technique.is_string()) + { + this->loadTechniqueFromDisk(&asset->techniques[i], technique.get(), builder); + } + } + } header->techniqueSet = asset; } void IMaterialTechniqueSet::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { + { Game::MaterialTechniqueSet* asset = header.techniqueSet; for (int i = 0; i < ARRAYSIZE(Game::MaterialTechniqueSet::techniques); ++i) @@ -177,8 +247,10 @@ namespace Assets AssertSize(Game::MaterialTechniqueSet, 204); Utils::Stream* buffer = builder->getBuffer(); + Game::MaterialTechniqueSet* asset = header.techniqueSet; Game::MaterialTechniqueSet* dest = buffer->dest(); + buffer->save(asset); buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); diff --git a/src/Components/Modules/AssetInterfaces/IMaterialTechniqueSet.hpp b/src/Components/Modules/AssetInterfaces/IMaterialTechniqueSet.hpp index 9978b6cd..60275dc4 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterialTechniqueSet.hpp +++ b/src/Components/Modules/AssetInterfaces/IMaterialTechniqueSet.hpp @@ -12,8 +12,8 @@ namespace Assets void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override; void loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); - void loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); + void loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); - void loadBinaryTechnique(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder); + void loadTechniqueFromDisk(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder); }; } diff --git a/src/Components/Modules/AssetInterfaces/IMaterialVertexDeclaration.cpp b/src/Components/Modules/AssetInterfaces/IMaterialVertexDeclaration.cpp index 2b506f05..26b16d11 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterialVertexDeclaration.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterialVertexDeclaration.cpp @@ -1,14 +1,14 @@ #include #include "IMaterialVertexDeclaration.hpp" -#define IW4X_TECHSET_VERSION "0" +#define IW4X_TECHSET_VERSION 1 namespace Assets { void IMaterialVertexDeclaration::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game + if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one } void IMaterialVertexDeclaration::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/) @@ -18,7 +18,7 @@ namespace Assets void IMaterialVertexDeclaration::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File declFile(Utils::String::VA("decl/%s.iw4xDECL", name.data())); + Components::FileSystem::File declFile(std::format("decl/{}.iw4xDECL", name)); if (!declFile.exists()) return; Utils::Stream::Reader reader(builder->getAllocator(), declFile.getBuffer()); @@ -29,12 +29,11 @@ namespace Assets Components::Logger::Error(Game::ERR_FATAL, "Reading vertex declaration '{}' failed, header is invalid!", name); } - std::string version; - version.push_back(reader.read()); + auto version = reader.read(); if (version != IW4X_TECHSET_VERSION) { - Components::Logger::Error(Game::ERR_FATAL, "Reading vertex declaration '{}' failed, expected version is {}, but it was {}!", - name, IW4X_TECHSET_VERSION, version.data()); + Components::Logger::Error(Game::ERR_FATAL, "Reading vertex declaration '{}' failed, expected version is {}, but it was {:d}!", + name, IW4X_TECHSET_VERSION, version); } Game::MaterialVertexDeclaration* asset = reader.readObject(); diff --git a/src/Components/Modules/AssetInterfaces/IMaterialVertexShader.cpp b/src/Components/Modules/AssetInterfaces/IMaterialVertexShader.cpp index bf786e52..e4d7c9b5 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterialVertexShader.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterialVertexShader.cpp @@ -1,14 +1,14 @@ #include #include "IMaterialVertexShader.hpp" -#define IW4X_TECHSET_VERSION "0" +#define GFX_RENDERER_SHADER_SM3 0 namespace Assets { void IMaterialVertexShader::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game + if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one } void IMaterialVertexShader::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/) @@ -18,36 +18,18 @@ namespace Assets void IMaterialVertexShader::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File vsFile(Utils::String::VA("vs/%s.iw4xVS", name.data())); + Components::FileSystem::File vsFile(std::format("vs/{}.cso", name)); if (!vsFile.exists()) return; - Utils::Stream::Reader reader(builder->getAllocator(), vsFile.getBuffer()); + auto buff = vsFile.getBuffer(); + auto programSize = buff.size() / 4; + Game::MaterialVertexShader* asset = builder->getAllocator()->allocate(); - char* magic = reader.readArray(8); - if (std::memcmp(magic, "IW4xVERT", 8)) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading vertex shader '{}' failed, header is invalid!", name); - } - - std::string version; - version.push_back(reader.read()); - if (version != IW4X_TECHSET_VERSION) - { - Components::Logger::Error(Game::ERR_FATAL, "Reading vertex shader '{}' failed, expected version is {}, but it was {}!", - name, IW4X_TECHSET_VERSION, version); - } - - Game::MaterialVertexShader* asset = reader.readObject(); - - if (asset->name) - { - asset->name = reader.readCString(); - } - - if (asset->prog.loadDef.program) - { - asset->prog.loadDef.program = reader.readArray(asset->prog.loadDef.programSize); - } + asset->name = builder->getAllocator()->duplicateString(name); + asset->prog.loadDef.loadForRenderer = GFX_RENDERER_SHADER_SM3; + asset->prog.loadDef.programSize = static_cast(programSize); + asset->prog.loadDef.program = builder->getAllocator()->allocateArray(programSize); + memcpy_s(asset->prog.loadDef.program, buff.size(), buff.data(), buff.size()); header->vertexShader = asset; } diff --git a/src/Components/Modules/AssetInterfaces/IMenuList.cpp b/src/Components/Modules/AssetInterfaces/IMenuList.cpp index dd9fd1fe..4cfc63d0 100644 --- a/src/Components/Modules/AssetInterfaces/IMenuList.cpp +++ b/src/Components/Modules/AssetInterfaces/IMenuList.cpp @@ -12,7 +12,7 @@ namespace Assets if (menus.empty()) return; // Allocate new menu list - Game::MenuList* newList = allocator->allocate(); + auto* newList = allocator->allocate(); if (!newList) return; newList->menus = allocator->allocateArray(menus.size()); @@ -35,7 +35,7 @@ namespace Assets } void IMenuList::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) { - Game::MenuList *asset = header.menuList; + auto* asset = header.menuList; for (int i = 0; i < asset->menuCount; ++i) { @@ -51,7 +51,7 @@ namespace Assets Utils::Stream* buffer = builder->getBuffer(); Game::MenuList* asset = header.menuList; - Game::MenuList* dest = buffer->dest(); + auto* dest = buffer->dest(); buffer->save(asset); @@ -67,7 +67,7 @@ namespace Assets { buffer->align(Utils::Stream::ALIGN_4); - Game::menuDef_t **destMenus = buffer->dest(); + auto** destMenus = buffer->dest(); buffer->saveArray(asset->menus, asset->menuCount); for (int i = 0; i < asset->menuCount; ++i) diff --git a/src/Components/Modules/AssetInterfaces/IRawFile.cpp b/src/Components/Modules/AssetInterfaces/IRawFile.cpp index 1b531f62..5a254acd 100644 --- a/src/Components/Modules/AssetInterfaces/IRawFile.cpp +++ b/src/Components/Modules/AssetInterfaces/IRawFile.cpp @@ -18,23 +18,24 @@ namespace Assets return; } - const auto data = Utils::Compression::ZLib::Compress(rawFile.getBuffer()); - asset->name = builder->getAllocator()->duplicateString(name); + asset->len = static_cast(rawFile.getBuffer().size()); - if (data.size() < rawFile.getBuffer().size()) + const auto compressedData = Utils::Compression::ZLib::Compress(rawFile.getBuffer()); + // Only save the compressed buffer if we gained space + if (compressedData.size() < rawFile.getBuffer().size()) { - asset->buffer = builder->getAllocator()->duplicateString(data); - asset->compressedLen = static_cast(data.size()); + asset->buffer = builder->getAllocator()->allocateArray(compressedData.size()); + std::memcpy(const_cast(asset->buffer), compressedData.data(), compressedData.size()); + asset->compressedLen = static_cast(compressedData.size()); } else { - asset->buffer = builder->getAllocator()->duplicateString(rawFile.getBuffer()); + asset->buffer = builder->getAllocator()->allocateArray(rawFile.getBuffer().size() + 1); + std::memcpy(const_cast(asset->buffer), rawFile.getBuffer().data(), rawFile.getBuffer().size()); asset->compressedLen = 0; } - asset->len = static_cast(rawFile.getBuffer().size()); - header->rawfile = asset; } diff --git a/src/Components/Modules/AssetInterfaces/IWeapon.cpp b/src/Components/Modules/AssetInterfaces/IWeapon.cpp index be6f6946..40e8fdd5 100644 --- a/src/Components/Modules/AssetInterfaces/IWeapon.cpp +++ b/src/Components/Modules/AssetInterfaces/IWeapon.cpp @@ -6,7 +6,7 @@ namespace Assets void IWeapon::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/) { // Try loading raw weapon - if (Components::FileSystem::File(Utils::String::VA("weapons/mp/%s", name.data())).exists()) + if (Components::FileSystem::File(std::format("weapons/mp/{}", name))) { // let the function see temporary assets when calling DB_FindXAssetHeader during the loading function // otherwise it fails to link things properly @@ -280,7 +280,7 @@ namespace Assets unsigned short* scriptStringTable = buffer->dest(); buffer->saveArray(def->notetrackSoundMapKeys, 16); for (int i = 0; i < 16; i++) { - builder->mapScriptString(&scriptStringTable[i]); + builder->mapScriptString(scriptStringTable[i]); } Utils::Stream::ClearPointer(&dest->notetrackSoundMapKeys); @@ -292,7 +292,7 @@ namespace Assets unsigned short* scriptStringTable = buffer->dest(); buffer->saveArray(def->notetrackSoundMapValues, 16); for (int i = 0; i < 16; i++) { - builder->mapScriptString(&scriptStringTable[i]); + builder->mapScriptString(scriptStringTable[i]); } Utils::Stream::ClearPointer(&dest->notetrackSoundMapValues); @@ -304,7 +304,7 @@ namespace Assets unsigned short* scriptStringTable = buffer->dest(); buffer->saveArray(def->notetrackRumbleMapKeys, 16); for (int i = 0; i < 16; i++) { - builder->mapScriptString(&scriptStringTable[i]); + builder->mapScriptString(scriptStringTable[i]); } Utils::Stream::ClearPointer(&dest->notetrackRumbleMapKeys); @@ -316,7 +316,7 @@ namespace Assets unsigned short* scriptStringTable = buffer->dest(); buffer->saveArray(def->notetrackRumbleMapValues, 16); for (int i = 0; i < 16; i++) { - builder->mapScriptString(&scriptStringTable[i]); + builder->mapScriptString(scriptStringTable[i]); } Utils::Stream::ClearPointer(&dest->notetrackRumbleMapValues); @@ -725,7 +725,7 @@ namespace Assets unsigned short* scriptStringTable = buffer->dest(); buffer->saveArray(asset->hideTags, 32); for (int i = 0; i < 32; i++) { - builder->mapScriptString(&scriptStringTable[i]); + builder->mapScriptString(scriptStringTable[i]); } Utils::Stream::ClearPointer(&dest->hideTags); diff --git a/src/Components/Modules/AssetInterfaces/IXAnimParts.cpp b/src/Components/Modules/AssetInterfaces/IXAnimParts.cpp index 736b0a40..0bd5e2ce 100644 --- a/src/Components/Modules/AssetInterfaces/IXAnimParts.cpp +++ b/src/Components/Modules/AssetInterfaces/IXAnimParts.cpp @@ -7,7 +7,7 @@ namespace Assets { void IXAnimParts::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File animFile(Utils::String::VA("xanim/%s.iw4xAnim", name.data())); + Components::FileSystem::File animFile(std::format("xanim/{}.iw4xAnim", name)); if (animFile.exists()) { @@ -39,7 +39,7 @@ namespace Assets xanim->names = builder->getAllocator()->allocateArray(xanim->boneCount[Game::PART_TYPE_ALL]); for (int i = 0; i < xanim->boneCount[Game::PART_TYPE_ALL]; ++i) { - xanim->names[i] = Game::SL_GetString(reader.readCString(), 0); + xanim->names[i] = static_cast(Game::SL_GetString(reader.readCString(), 0)); } } @@ -49,7 +49,7 @@ namespace Assets for (int i = 0; i < xanim->notifyCount; ++i) { - xanim->notify[i].name = Game::SL_GetString(reader.readCString(), 0); + xanim->notify[i].name = static_cast(Game::SL_GetString(reader.readCString(), 0)); } } @@ -264,7 +264,7 @@ namespace Assets for (char i = 0; i < asset->boneCount[Game::PART_TYPE_ALL]; ++i) { - builder->mapScriptString(&destTagnames[i]); + builder->mapScriptString(destTagnames[i]); } Utils::Stream::ClearPointer(&dest->names); @@ -280,7 +280,7 @@ namespace Assets for (char i = 0; i < asset->notifyCount; ++i) { - builder->mapScriptString(&destNotetracks[i].name); + builder->mapScriptString(destNotetracks[i].name); } Utils::Stream::ClearPointer(&dest->notify); diff --git a/src/Components/Modules/AssetInterfaces/IXModel.cpp b/src/Components/Modules/AssetInterfaces/IXModel.cpp index c10751fd..305a624c 100644 --- a/src/Components/Modules/AssetInterfaces/IXModel.cpp +++ b/src/Components/Modules/AssetInterfaces/IXModel.cpp @@ -1,7 +1,7 @@ #include #include "IXModel.hpp" -#define IW4X_MODEL_VERSION 5 +#define IW4X_MODEL_VERSION 8 namespace Assets { @@ -9,32 +9,32 @@ namespace Assets { if (entry->nodes) { - entry->nodes = reader->readArray(entry->nodeCount); + entry->nodes = reader->readArrayOnce(entry->nodeCount); } if (entry->leafs) { - entry->leafs = reader->readArray(entry->leafCount); + entry->leafs = reader->readArrayOnce(entry->leafCount); } } - void IXModel::loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader, Components::ZoneBuilder::Zone* builder) + void IXModel::loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader, [[maybe_unused]] Components::ZoneBuilder::Zone* builder) { if (surf->vertInfo.vertsBlend) { - surf->vertInfo.vertsBlend = reader->readArray(surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7)); + surf->vertInfo.vertsBlend = reader->readArrayOnce(surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7)); } // Access vertex block if (surf->verts0) { - surf->verts0 = reader->readArray(surf->vertCount); + surf->verts0 = reader->readArrayOnce(surf->vertCount); } // Save_XRigidVertListArray if (surf->vertList) { - surf->vertList = reader->readArray(surf->vertListCount); + surf->vertList = reader->readArrayOnce(surf->vertListCount); for (unsigned int i = 0; i < surf->vertListCount; ++i) { @@ -51,17 +51,7 @@ namespace Assets // Access index block if (surf->triIndices) { - void* oldPtr = surf->triIndices; - surf->triIndices = reader->readArray(surf->triCount * 3); - - if (builder->getAllocator()->isPointerMapped(oldPtr)) - { - surf->triIndices = builder->getAllocator()->getPointer(oldPtr); - } - else - { - builder->getAllocator()->mapPointer(oldPtr, surf->triIndices); - } + surf->triIndices = reader->readArrayOnce(surf->triCount * 3); } } @@ -74,7 +64,7 @@ namespace Assets if (asset->surfs) { - asset->surfs = reader->readArray(asset->numsurfs); + asset->surfs = reader->readArrayOnce(asset->numsurfs); for (int i = 0; i < asset->numsurfs; ++i) { @@ -85,7 +75,7 @@ namespace Assets void IXModel::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File modelFile(Utils::String::VA("xmodel/%s.iw4xModel", name.data())); + Components::FileSystem::File modelFile(std::format("xmodel/{}.iw4xModel", name)); if (!builder->isPrimaryAsset() && (!Components::ZoneBuilder::PreferDiskAssetsDvar.get() || !modelFile.exists())) { @@ -110,11 +100,6 @@ namespace Assets Components::Logger::Error(Game::ERR_FATAL, "Reading model '{}' failed, expected version is {}, but it was {}!", name, IW4X_MODEL_VERSION, version); } - if (version == 4) - { - Components::Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Model '{}' is in legacy format, please update it!\n", name); - } - Game::XModel* asset = reader.readObject(); if (asset->name) @@ -128,33 +113,33 @@ namespace Assets for (char i = 0; i < asset->numBones; ++i) { - asset->boneNames[i] = Game::SL_GetString(reader.readCString(), 0); + asset->boneNames[i] = static_cast(Game::SL_GetString(reader.readCString(), 0)); } } if (asset->parentList) { - asset->parentList = reader.readArray(asset->numBones - asset->numRootBones); + asset->parentList = reader.readArrayOnce(asset->numBones - asset->numRootBones); } if (asset->quats) { - asset->quats = reader.readArray((asset->numBones - asset->numRootBones) * 4); + asset->quats = reader.readArrayOnce((asset->numBones - asset->numRootBones) * 4); } if (asset->trans) { - asset->trans = reader.readArray((asset->numBones - asset->numRootBones) * 3); + asset->trans = reader.readArrayOnce((asset->numBones - asset->numRootBones) * 3); } if (asset->partClassification) { - asset->partClassification = reader.readArray(asset->numBones); + asset->partClassification = reader.readArrayOnce(asset->numBones); } if (asset->baseMat) { - asset->baseMat = reader.readArray(asset->numBones); + asset->baseMat = reader.readArrayOnce(asset->numBones); } if (asset->materialHandles) @@ -172,7 +157,7 @@ namespace Assets // Save_XModelLodInfoArray { - for (int i = 0; i < 4; ++i) + for (unsigned int i = 0; i < 4; ++i) { if (asset->lodInfo[i].modelSurfs) { @@ -247,60 +232,53 @@ namespace Assets if (asset->physCollmap) { - if (version == 4) + Game::PhysCollmap* collmap = reader.readObject(); + asset->physCollmap = collmap; + + if (collmap->name) { - asset->physCollmap = nullptr; + collmap->name = reader.readCString(); } - else + + if (collmap->geoms) { - Game::PhysCollmap* collmap = reader.readObject(); - asset->physCollmap = collmap; + collmap->geoms = reader.readArray(collmap->count); - if (collmap->name) + for (unsigned int i = 0; i < collmap->count; ++i) { - collmap->name = reader.readCString(); - } + Game::PhysGeomInfo* geom = &collmap->geoms[i]; - if (collmap->geoms) - { - collmap->geoms = reader.readArray(collmap->count); - - for (unsigned int i = 0; i < collmap->count; ++i) + if (geom->brushWrapper) { - Game::PhysGeomInfo* geom = &collmap->geoms[i]; - - if (geom->brushWrapper) + Game::BrushWrapper* brush = reader.readObject(); + geom->brushWrapper = brush; { - Game::BrushWrapper* brush = reader.readObject(); - geom->brushWrapper = brush; + if (brush->brush.sides) { - if (brush->brush.sides) + brush->brush.sides = reader.readArray(brush->brush.numsides); + for (unsigned short j = 0; j < brush->brush.numsides; ++j) { - brush->brush.sides = reader.readArray(brush->brush.numsides); - for (unsigned short j = 0; j < brush->brush.numsides; ++j) - { - Game::cbrushside_t* side = &brush->brush.sides[j]; + Game::cbrushside_t* side = &brush->brush.sides[j]; - // TODO: Add pointer support - if (side->plane) - { - side->plane = reader.readObject(); - } + // TODO: Add pointer support + if (side->plane) + { + side->plane = reader.readObject(); } } - - if (brush->brush.baseAdjacentSide) - { - brush->brush.baseAdjacentSide = reader.readArray(brush->totalEdgeCount); - } } - // TODO: Add pointer support - if (brush->planes) + if (brush->brush.baseAdjacentSide) { - brush->planes = reader.readArray(brush->brush.numsides); + brush->brush.baseAdjacentSide = reader.readArray(brush->totalEdgeCount); } } + + // TODO: Add pointer support + if (brush->planes) + { + brush->planes = reader.readArray(brush->brush.numsides); + } } } @@ -386,7 +364,7 @@ namespace Assets for (char i = 0; i < asset->numBones; ++i) { - builder->mapScriptString(&destBoneNames[i]); + builder->mapScriptString(destBoneNames[i]); } Utils::Stream::ClearPointer(&dest->boneNames); @@ -394,37 +372,77 @@ namespace Assets if (asset->parentList) { - buffer->save(asset->parentList, asset->numBones - asset->numRootBones); - Utils::Stream::ClearPointer(&dest->parentList); + if (builder->hasPointer(asset->parentList)) + { + dest->parentList = builder->getPointer(asset->parentList); + } + else + { + builder->storePointer(asset->parentList); + buffer->save(asset->parentList, asset->numBones - asset->numRootBones); + Utils::Stream::ClearPointer(&dest->parentList); + } } if (asset->quats) { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(asset->quats, (asset->numBones - asset->numRootBones) * 4); - Utils::Stream::ClearPointer(&dest->quats); + if (builder->hasPointer(asset->quats)) + { + dest->quats = builder->getPointer(asset->quats); + } + else + { + buffer->align(Utils::Stream::ALIGN_2); + builder->storePointer(asset->quats); + buffer->saveArray(asset->quats, (asset->numBones - asset->numRootBones) * 4); + Utils::Stream::ClearPointer(&dest->quats); + } } if (asset->trans) { - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->trans, (asset->numBones - asset->numRootBones) * 3); - Utils::Stream::ClearPointer(&dest->trans); + if (builder->hasPointer(asset->trans)) + { + dest->trans = builder->getPointer(asset->trans); + } + else + { + buffer->align(Utils::Stream::ALIGN_4); + builder->storePointer(asset->trans); + buffer->saveArray(asset->trans, (asset->numBones - asset->numRootBones) * 3); + Utils::Stream::ClearPointer(&dest->trans); + } } if (asset->partClassification) { - buffer->save(asset->partClassification, asset->numBones); - Utils::Stream::ClearPointer(&dest->partClassification); + if (builder->hasPointer(asset->partClassification)) + { + dest->partClassification = builder->getPointer(asset->partClassification); + } + else + { + builder->storePointer(asset->partClassification); + buffer->save(asset->partClassification, asset->numBones); + Utils::Stream::ClearPointer(&dest->partClassification); + } } if (asset->baseMat) { AssertSize(Game::DObjAnimMat, 32); + if (builder->hasPointer(asset->baseMat)) + { + dest->baseMat = builder->getPointer(asset->baseMat); + } + else + { + buffer->align(Utils::Stream::ALIGN_4); + builder->storePointer(asset->baseMat); + buffer->saveArray(asset->baseMat, asset->numBones); + Utils::Stream::ClearPointer(&dest->baseMat); + } - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->baseMat, asset->numBones); - Utils::Stream::ClearPointer(&dest->baseMat); } if (asset->materialHandles) diff --git a/src/Components/Modules/AssetInterfaces/IXModelSurfs.cpp b/src/Components/Modules/AssetInterfaces/IXModelSurfs.cpp index bbe4b96a..15c4333a 100644 --- a/src/Components/Modules/AssetInterfaces/IXModelSurfs.cpp +++ b/src/Components/Modules/AssetInterfaces/IXModelSurfs.cpp @@ -37,9 +37,18 @@ namespace Assets if (surf->vertInfo.vertsBlend) { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(surf->vertInfo.vertsBlend, surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7)); - Utils::Stream::ClearPointer(&destSurf->vertInfo.vertsBlend); + if (builder->hasPointer(surf->vertInfo.vertsBlend)) + { + destSurf->vertInfo.vertsBlend = builder->getPointer(surf->vertInfo.vertsBlend); + } + else + { + + buffer->align(Utils::Stream::ALIGN_2); + builder->storePointer(surf->vertInfo.vertsBlend); + buffer->saveArray(surf->vertInfo.vertsBlend, surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7)); + Utils::Stream::ClearPointer(&destSurf->vertInfo.vertsBlend); + } } // Access vertex block @@ -48,9 +57,17 @@ namespace Assets { AssertSize(Game::GfxPackedVertex, 32); - buffer->align(Utils::Stream::ALIGN_16); - buffer->saveArray(surf->verts0, surf->vertCount); - Utils::Stream::ClearPointer(&destSurf->verts0); + if (builder->hasPointer(surf->verts0)) + { + destSurf->verts0 = builder->getPointer(surf->verts0); + } + else + { + buffer->align(Utils::Stream::ALIGN_16); + builder->storePointer(surf->verts0); + buffer->saveArray(surf->verts0, surf->vertCount); + Utils::Stream::ClearPointer(&destSurf->verts0); + } } buffer->popBlock(); @@ -59,25 +76,40 @@ namespace Assets { AssertSize(Game::XRigidVertList, 12); - buffer->align(Utils::Stream::ALIGN_4); - - Game::XRigidVertList* destCt = buffer->dest(); - buffer->saveArray(surf->vertList, surf->vertListCount); - - for (unsigned int i = 0; i < surf->vertListCount; ++i) + if (builder->hasPointer(surf->vertList)) { - Game::XRigidVertList* destRigidVertList = &destCt[i]; - Game::XRigidVertList* rigidVertList = &surf->vertList[i]; - - if (rigidVertList->collisionTree) - { - buffer->align(Utils::Stream::ALIGN_4); - this->saveXSurfaceCollisionTree(rigidVertList->collisionTree, builder); - Utils::Stream::ClearPointer(&destRigidVertList->collisionTree); - } + destSurf->vertList = builder->getPointer(surf->vertList); } + else + { + buffer->align(Utils::Stream::ALIGN_4); + builder->storePointer(surf->vertList); - Utils::Stream::ClearPointer(&destSurf->vertList); + Game::XRigidVertList* destCt = buffer->dest(); + buffer->saveArray(surf->vertList, surf->vertListCount); + + for (unsigned int i = 0; i < surf->vertListCount; ++i) + { + Game::XRigidVertList* destRigidVertList = &destCt[i]; + Game::XRigidVertList* rigidVertList = &surf->vertList[i]; + + if (rigidVertList->collisionTree) + { + if (builder->hasPointer(rigidVertList->collisionTree)) + { + destRigidVertList->collisionTree = builder->getPointer(rigidVertList->collisionTree); + } + else { + buffer->align(Utils::Stream::ALIGN_4); + builder->storePointer(rigidVertList->collisionTree); + this->saveXSurfaceCollisionTree(rigidVertList->collisionTree, builder); + Utils::Stream::ClearPointer(&destRigidVertList->collisionTree); + } + } + } + + Utils::Stream::ClearPointer(&destSurf->vertList); + } } // Access index block @@ -89,6 +121,7 @@ namespace Assets else { buffer->align(Utils::Stream::ALIGN_16); + builder->storePointer(surf->triIndices); buffer->saveArray(surf->triIndices, surf->triCount * 3); Utils::Stream::ClearPointer(&destSurf->triIndices); } diff --git a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp index ae28d788..f0524227 100644 --- a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp +++ b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp @@ -573,7 +573,7 @@ namespace Assets Utils::String::Replace(name, "maps/mp/", ""); Utils::String::Replace(name, ".d3dbsp", ""); - Components::FileSystem::File clipFile(Utils::String::VA("clipmap/%s.iw4xClipMap", name.data())); + Components::FileSystem::File clipFile(std::format("clipmap/{}.iw4xClipMap", name)); if (!clipFile.exists()) { return; @@ -882,7 +882,7 @@ namespace Assets clipMap->smodelNodeCount = reader.read(); clipMap->smodelNodes = reader.readArray(clipMap->smodelNodeCount); - clipMap->mapEnts = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MAP_ENTS, Utils::String::VA("maps/mp/%s.d3dbsp", name.data()), builder).mapEnts; + clipMap->mapEnts = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MAP_ENTS, std::format("maps/mp/{}.d3dbsp", name), builder).mapEnts; // add triggers to mapEnts if (version >= 2) { diff --git a/src/Components/Modules/AssetInterfaces/ImenuDef_t.cpp b/src/Components/Modules/AssetInterfaces/ImenuDef_t.cpp index c9b6fc8e..65a10757 100644 --- a/src/Components/Modules/AssetInterfaces/ImenuDef_t.cpp +++ b/src/Components/Modules/AssetInterfaces/ImenuDef_t.cpp @@ -9,9 +9,9 @@ namespace Assets void ImenuDef_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/) { // load from disk - auto menus = Components::Menus::LoadMenu(Utils::String::VA("ui_mp/%s.menu", name.data())); + auto menus = Components::Menus::LoadMenu(std::format("ui_mp/{}.menu", name)); - if (menus.size() == 0) return; + if (menus.empty()) return; if (menus.size() > 1) Components::Logger::Print("Menu '{}' on disk has more than one menudef in it. Only saving the first one\n", name); header->menu = menus[0].second; @@ -20,7 +20,7 @@ namespace Assets void ImenuDef_t::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) { - Game::menuDef_t *asset = header.menu; + auto* asset = header.menu; if (asset->window.background) { @@ -59,14 +59,14 @@ namespace Assets buffer->align(Utils::Stream::ALIGN_4); - Game::ExpressionSupportingData *dest = buffer->dest(); + auto* dest = buffer->dest(); buffer->save(asset); if (asset->uifunctions.functions) { buffer->align(Utils::Stream::ALIGN_4); - Game::Statement_s **destStatement = buffer->dest(); + auto** destStatement = buffer->dest(); buffer->saveArray(asset->uifunctions.functions, asset->uifunctions.totalFunctions); for (int i = 0; i < asset->uifunctions.totalFunctions; ++i) @@ -87,17 +87,17 @@ namespace Assets { buffer->align(Utils::Stream::ALIGN_4); - Game::StaticDvar **destStaticDvars = buffer->dest(); + auto** destStaticDvars = buffer->dest(); buffer->saveArray(asset->staticDvarList.staticDvars, asset->staticDvarList.numStaticDvars); - for (int i = 0; i < asset->staticDvarList.numStaticDvars; ++i) + for (auto i = 0; i < asset->staticDvarList.numStaticDvars; ++i) { if (asset->staticDvarList.staticDvars[i]) { Utils::Stream::ClearPointer(&destStaticDvars[i]); buffer->align(Utils::Stream::ALIGN_4); - Game::StaticDvar *destStaticDvar = buffer->dest(); + auto* destStaticDvar = buffer->dest(); buffer->save(asset->staticDvarList.staticDvars[i]); if (asset->staticDvarList.staticDvars[i]->dvarName) @@ -115,7 +115,7 @@ namespace Assets { buffer->align(Utils::Stream::ALIGN_4); - const char **destuiStrings = buffer->dest(); + const auto** destUIStrings = buffer->dest(); buffer->saveArray(asset->uiStrings.strings, asset->uiStrings.totalStrings); for (int i = 0; i < asset->uiStrings.totalStrings; ++i) @@ -123,7 +123,7 @@ namespace Assets if (asset->uiStrings.strings[i]) { buffer->saveString(asset->uiStrings.strings[i]); - Utils::Stream::ClearPointer(&destuiStrings[i]); + Utils::Stream::ClearPointer(&destUIStrings[i]); } } } @@ -143,7 +143,7 @@ namespace Assets #endif // Write header data - Game::Statement_s *dest = buffer->dest(); + auto* dest = buffer->dest(); buffer->save(asset); // Write statement entries @@ -155,7 +155,7 @@ namespace Assets buffer->align(Utils::Stream::ALIGN_4); // Write entries - Game::expressionEntry *destEntries = buffer->dest(); + auto* destEntries = buffer->dest(); buffer->save(asset->entries, sizeof(Game::expressionEntry), asset->numEntries); // Loop through entries @@ -222,7 +222,7 @@ namespace Assets #endif // Write header data - Game::MenuEventHandlerSet *destset = buffer->dest(); + auto* destset = buffer->dest(); buffer->save(asset); // Event handlers @@ -234,7 +234,7 @@ namespace Assets buffer->save(asset->eventHandlers, sizeof(Game::MenuEventHandler*), asset->eventHandlerCount); // Loop through eventHandlers - for (int i = 0; i < asset->eventHandlerCount; ++i) + for (auto i = 0; i < asset->eventHandlerCount; ++i) { if (asset->eventHandlers[i]) { @@ -244,7 +244,7 @@ namespace Assets #endif // Write menu event handler - Game::MenuEventHandler *dest = buffer->dest(); + auto* dest = buffer->dest(); buffer->save(asset->eventHandlers[i]); // Write additional data based on type @@ -264,7 +264,7 @@ namespace Assets if (asset->eventHandlers[i]->eventData.conditionalScript) { buffer->align(Utils::Stream::ALIGN_4); - Game::ConditionalScript *destConditionalScript = buffer->dest(); + auto* destConditionalScript = buffer->dest(); buffer->save(asset->eventHandlers[i]->eventData.conditionalScript); // eventExpression @@ -307,7 +307,7 @@ namespace Assets buffer->align(Utils::Stream::ALIGN_4); // header data - Game::SetLocalVarData *destLocalVarData = buffer->dest(); + auto* destLocalVarData = buffer->dest(); buffer->save(asset->eventHandlers[i]->eventData.setLocalVarData); // localVarName @@ -354,7 +354,7 @@ namespace Assets while (asset) { // Write header - Game::ItemKeyHandler* dest = buffer->dest(); + auto* dest = buffer->dest(); buffer->save(asset); // MenuEventHandlerSet @@ -367,7 +367,7 @@ namespace Assets if (asset->next) { - // align every indice, besides the first one? + // align every index, besides the first one? buffer->align(Utils::Stream::ALIGN_4); } @@ -379,20 +379,20 @@ namespace Assets #endif } -#define EVENTHANDLERSET(__indice) \ - if (asset->__indice) \ +#define EVENTHANDLERSET(__index) \ + if (asset->__index) \ { \ buffer->align(Utils::Stream::ALIGN_4); \ - this->save_MenuEventHandlerSet(asset->__indice, builder); \ - Utils::Stream::ClearPointer(&dest->__indice); \ + this->save_MenuEventHandlerSet(asset->__index, builder); \ + Utils::Stream::ClearPointer(&dest->__index); \ } -#define STATEMENT(__indice) \ - if (asset->__indice) \ +#define STATEMENT(__index) \ + if (asset->__index) \ { \ buffer->align(Utils::Stream::ALIGN_4); \ - this->save_Statement_s(asset->__indice, builder); \ - Utils::Stream::ClearPointer(&dest->__indice); \ + this->save_Statement_s(asset->__index, builder); \ + Utils::Stream::ClearPointer(&dest->__index); \ } void ImenuDef_t::save_itemDefData_t(Game::itemDefData_t* asset, int type, Game::itemDef_s* dest, Components::ZoneBuilder::Zone* builder) @@ -412,7 +412,7 @@ namespace Assets if (type == 6) { buffer->align(Utils::Stream::ALIGN_4); - Game::listBoxDef_s* destlb = buffer->dest(); + auto* destlb = buffer->dest(); buffer->save(asset->listBox); if (asset->listBox->onDoubleClick) @@ -427,17 +427,7 @@ namespace Assets } } // HexRays spaghetti - else if (type != 4 - && type != 9 - && type != 16 - && type != 18 - && type != 11 - && type != 14 - && type != 10 - && type != 17 - && type != 22 - && type != 23 - && type != 0) + else if (type != 4 && type != 9 && type != 16 && type != 18 && type != 11 && type != 14 && type != 10 && type != 17 && type != 22 && type != 23 && type != 0) { switch (type) { @@ -457,7 +447,7 @@ namespace Assets break; case 12: buffer->align(Utils::Stream::ALIGN_4); - Game::multiDef_s* destdef = buffer->dest(); + auto* destdef = buffer->dest(); buffer->save(asset->multi); for (int i = 0; i < 32; ++i) @@ -497,10 +487,10 @@ namespace Assets void ImenuDef_t::save_itemDef_s(Game::itemDef_s *asset, Components::ZoneBuilder::Zone* builder) { - AssertSize(Game::itemDef_s, 380); + AssertSize(Game::itemDef_s, 0x17C); Utils::Stream* buffer = builder->getBuffer(); - Game::itemDef_s* dest = buffer->dest(); + auto* dest = buffer->dest(); #ifdef WRITE_LOGS if (asset->window.name) @@ -587,7 +577,7 @@ namespace Assets buffer->enterStruct("floatExpressions"); #endif - Game::ItemFloatExpression* destExp = buffer->dest(); + auto* destExp = buffer->dest(); buffer->saveArray(asset->floatExpressions, asset->floatExpressionCount); for (int i = 0; i < asset->floatExpressionCount; ++i) @@ -618,14 +608,15 @@ namespace Assets void ImenuDef_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) { AssertSize(Game::menuDef_t, 400); + AssertSize(Game::windowDef_t, 0xA4); #ifdef WRITE_LOGS buffer->enterStruct("ImenuDef_t"); #endif Utils::Stream* buffer = builder->getBuffer(); - Game::menuDef_t* asset = header.menu; - Game::menuDef_t* dest = buffer->dest(); + auto* asset = header.menu; + auto* dest = buffer->dest(); buffer->save(asset); buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); diff --git a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp index 021781b3..21a88b27 100644 --- a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp +++ b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp @@ -5,7 +5,7 @@ namespace Assets { void Isnd_alias_list_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File aliasFile(Utils::String::VA("sounds/%s.json", name.data())); + Components::FileSystem::File aliasFile(std::format("sounds/{}.json", name)); if (!aliasFile.exists()) { header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).sound; diff --git a/src/Components/Modules/Auth.cpp b/src/Components/Modules/Auth.cpp index e4c61044..9bf90a68 100644 --- a/src/Components/Modules/Auth.cpp +++ b/src/Components/Modules/Auth.cpp @@ -1,4 +1,5 @@ #include +#include "Bans.hpp" namespace Components { diff --git a/src/Components/Modules/Bans.cpp b/src/Components/Modules/Bans.cpp index e6674800..955fbe81 100644 --- a/src/Components/Modules/Bans.cpp +++ b/src/Components/Modules/Bans.cpp @@ -1,4 +1,5 @@ #include +#include "Bans.hpp" namespace Components { diff --git a/src/Components/Modules/Bots.cpp b/src/Components/Modules/Bots.cpp index 2b7132f8..3eb92273 100644 --- a/src/Components/Modules/Bots.cpp +++ b/src/Components/Modules/Bots.cpp @@ -1,16 +1,20 @@ #include +#include "Bots.hpp" + #include "GSC/Script.hpp" namespace Components { - std::vector Bots::BotNames; + std::vector Bots::BotNames; + + Dvar::Var Bots::SVRandomBotNames; struct BotMovementInfo { - int buttons; // Actions - int8_t forward; - int8_t right; - uint16_t weapon; + std::int32_t buttons; // Actions + std::int8_t forward; + std::int8_t right; + std::uint16_t weapon; bool active; }; @@ -19,67 +23,94 @@ namespace Components struct BotAction { std::string action; - int key; + std::int32_t key; }; static const BotAction BotActions[] = { - { "gostand", Game::usercmdButtonBits::CMD_BUTTON_UP }, - { "gocrouch", Game::usercmdButtonBits::CMD_BUTTON_CROUCH }, - { "goprone", Game::usercmdButtonBits::CMD_BUTTON_PRONE }, - { "fire", Game::usercmdButtonBits::CMD_BUTTON_ATTACK }, - { "melee", Game::usercmdButtonBits::CMD_BUTTON_MELEE }, - { "frag", Game::usercmdButtonBits::CMD_BUTTON_FRAG }, - { "smoke", Game::usercmdButtonBits::CMD_BUTTON_OFFHAND_SECONDARY }, - { "reload", Game::usercmdButtonBits::CMD_BUTTON_RELOAD }, - { "sprint", Game::usercmdButtonBits::CMD_BUTTON_SPRINT }, - { "leanleft", Game::usercmdButtonBits::CMD_BUTTON_LEAN_LEFT }, - { "leanright", Game::usercmdButtonBits::CMD_BUTTON_LEAN_RIGHT }, - { "ads", Game::usercmdButtonBits::CMD_BUTTON_ADS }, - { "holdbreath", Game::usercmdButtonBits::CMD_BUTTON_BREATH }, - { "usereload", Game::usercmdButtonBits::CMD_BUTTON_USE_RELOAD }, - { "activate", Game::usercmdButtonBits::CMD_BUTTON_ACTIVATE }, + { "gostand", Game::CMD_BUTTON_UP }, + { "gocrouch", Game::CMD_BUTTON_CROUCH }, + { "goprone", Game::CMD_BUTTON_PRONE }, + { "fire", Game::CMD_BUTTON_ATTACK }, + { "melee", Game::CMD_BUTTON_MELEE }, + { "frag", Game::CMD_BUTTON_FRAG }, + { "smoke", Game::CMD_BUTTON_OFFHAND_SECONDARY }, + { "reload", Game::CMD_BUTTON_RELOAD }, + { "sprint", Game::CMD_BUTTON_SPRINT }, + { "leanleft", Game::CMD_BUTTON_LEAN_LEFT }, + { "leanright", Game::CMD_BUTTON_LEAN_RIGHT }, + { "ads", Game::CMD_BUTTON_ADS }, + { "holdbreath", Game::CMD_BUTTON_BREATH }, + { "usereload", Game::CMD_BUTTON_USE_RELOAD }, + { "activate", Game::CMD_BUTTON_ACTIVATE }, }; int Bots::BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port) { - static size_t botId = 0; + static size_t botId = 0; // Loop over the BotNames vector static bool loadedNames = false; // Load file only once const char* botName; + const char* clanName; - if (Bots::BotNames.empty() && !loadedNames) + if (BotNames.empty() && !loadedNames) { FileSystem::File bots("bots.txt"); loadedNames = true; if (bots.exists()) { - auto names = Utils::String::Split(bots.getBuffer(), '\n'); + auto data = Utils::String::Split(bots.getBuffer(), '\n'); - for (auto& name : names) + if (SVRandomBotNames.get()) { - Utils::String::Replace(name, "\r", ""); - name = Utils::String::Trim(name); + std::random_device rd; + std::mt19937 gen(rd()); + std::ranges::shuffle(data, gen); + } - if (!name.empty()) + for (auto& entry : data) + { + // Take into account for CR line endings + Utils::String::Replace(entry, "\r", ""); + // Remove whitespace + Utils::String::Trim(entry); + + if (!entry.empty()) { - Bots::BotNames.push_back(name); + std::string clanAbbrev; + + // Check if there is a clan tag + if (const auto pos = entry.find(','); pos != std::string::npos) + { + // Only start copying over from non-null characters (otherwise it can be "<=") + if ((pos + 1) < entry.size()) + { + clanAbbrev = entry.substr(pos + 1); + } + + entry = entry.substr(0, pos); + } + + BotNames.emplace_back(std::make_pair(entry, clanAbbrev)); } } } } - if (!Bots::BotNames.empty()) + if (!BotNames.empty()) { - botId %= Bots::BotNames.size(); - botName = Bots::BotNames[botId++].data(); + botId %= BotNames.size(); + const auto index = botId++; + botName = BotNames[index].first.data(); + clanName = BotNames[index].second.data(); } else { botName = Utils::String::VA("bot%d", ++botId); + clanName = "BOT"; } - return _snprintf_s(buffer, 0x400, _TRUNCATE, connectString, num, botName, protocol, checksum, statVer, statStuff, port); + return _snprintf_s(buffer, 0x400, _TRUNCATE, connectString, num, botName, clanName, protocol, checksum, statVer, statStuff, port); } void Bots::Spawn(unsigned int count) @@ -96,13 +127,13 @@ namespace Components { Game::Scr_AddString("autoassign"); Game::Scr_AddString("team_marinesopfor"); - Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2); + Game::Scr_Notify(ent, static_cast(Game::SL_GetString("menuresponse", 0)), 2); Scheduler::Once([ent] { Game::Scr_AddString(Utils::String::VA("class%u", Utils::Cryptography::Rand::GenerateInt() % 5u)); Game::Scr_AddString("changeclass"); - Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2); + Game::Scr_Notify(ent, static_cast(Game::SL_GetString("menuresponse", 0)), 2); }, Scheduler::Pipeline::SERVER, 1s); }, Scheduler::Pipeline::SERVER, 1s); @@ -111,16 +142,21 @@ namespace Components } } - void Bots::GScr_isTestClient(Game::scr_entref_t entref) + void Bots::GScr_isTestClient(const Game::scr_entref_t entref) { - const auto* ent = Game::GetPlayerEntity(entref); + const auto* ent = Game::GetEntity(entref); + if (!ent->client) + { + Game::Scr_Error("isTestClient: entity must be a player entity"); + return; + } + Game::Scr_AddBool(Game::SV_IsTestClient(ent->s.number) != 0); } void Bots::AddMethods() { - Script::AddMethod("IsBot", Bots::GScr_isTestClient); // Usage: self IsBot(); - Script::AddMethod("IsTestClient", Bots::GScr_isTestClient); // Usage: self IsTestClient(); + Script::AddMethMultiple(GScr_isTestClient, false, {"IsTestClient", "IsBot"}); // Usage: self IsTestClient(); Script::AddMethod("BotStop", [](Game::scr_entref_t entref) // Usage: BotStop(); { @@ -234,7 +270,8 @@ namespace Components return; } - Game::usercmd_s userCmd = {0}; + Game::usercmd_s userCmd; + ZeroMemory(&userCmd, sizeof(Game::usercmd_s)); userCmd.serverTime = *Game::svs_time; @@ -254,7 +291,7 @@ namespace Components pushad push edi - call Bots::BotAiAction + call BotAiAction add esp, 4 popad @@ -278,7 +315,7 @@ namespace Components push [esp + 0x20 + 0x8] push [esp + 0x20 + 0x8] - call Bots::G_SelectWeaponIndex + call G_SelectWeaponIndex add esp, 0x8 popad @@ -298,15 +335,17 @@ namespace Components AssertOffset(Game::client_t, ping, 0x212C8); // Replace connect string - Utils::Hook::Set(0x48ADA6, "connect bot%d \"\\cg_predictItems\\1\\cl_anonymous\\0\\color\\4\\head\\default\\model\\multi\\snaps\\20\\rate\\5000\\name\\%s\\protocol\\%d\\checksum\\%d\\statver\\%d %u\\qport\\%d\""); + Utils::Hook::Set(0x48ADA6, "connect bot%d \"\\cg_predictItems\\1\\cl_anonymous\\0\\color\\4\\head\\default\\model\\multi\\snaps\\20\\rate\\5000\\name\\%s\\clanAbbrev\\%s\\protocol\\%d\\checksum\\%d\\statver\\%d %u\\qport\\%d\""); // Intercept sprintf for the connect string - Utils::Hook(0x48ADAB, Bots::BuildConnectString, HOOK_CALL).install()->quick(); + Utils::Hook(0x48ADAB, BuildConnectString, HOOK_CALL).install()->quick(); - Utils::Hook(0x627021, Bots::SV_BotUserMove_Hk, HOOK_CALL).install()->quick(); - Utils::Hook(0x627241, Bots::SV_BotUserMove_Hk, HOOK_CALL).install()->quick(); + Utils::Hook(0x627021, SV_BotUserMove_Hk, HOOK_CALL).install()->quick(); + Utils::Hook(0x627241, SV_BotUserMove_Hk, HOOK_CALL).install()->quick(); - Utils::Hook(0x441B80, Bots::G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick(); + Utils::Hook(0x441B80, G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick(); + + SVRandomBotNames = Dvar::Register("sv_randomBotNames", false, Game::DVAR_NONE, "Randomize the bots' names"); // Reset BotMovementInfo.active when client is dropped Events::OnClientDisconnect([](const int clientNum) @@ -359,10 +398,10 @@ namespace Components Toast::Show("cardicon_headshot", "^2Success", Utils::String::VA("Spawning %d %s...", count, (count == 1 ? "bot" : "bots")), 3000); Logger::Debug("Spawning {} {}", count, (count == 1 ? "bot" : "bots")); - Bots::Spawn(count); + Spawn(count); }); - Bots::AddMethods(); + AddMethods(); // In case a loaded mod didn't call "BotStop" before the VM shutdown Events::OnVMShutdown([] diff --git a/src/Components/Modules/Bots.hpp b/src/Components/Modules/Bots.hpp index bc295969..f47f7a9a 100644 --- a/src/Components/Modules/Bots.hpp +++ b/src/Components/Modules/Bots.hpp @@ -8,7 +8,10 @@ namespace Components Bots(); private: - static std::vector BotNames; + using botData = std::pair< std::string, std::string>; + static std::vector BotNames; + + static Dvar::Var SVRandomBotNames; static int BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port); diff --git a/src/Components/Modules/Branding.cpp b/src/Components/Modules/Branding.cpp index 6f1c3fbf..0c293357 100644 --- a/src/Components/Modules/Branding.cpp +++ b/src/Components/Modules/Branding.cpp @@ -1,4 +1,7 @@ #include +#include "Branding.hpp" + +#include namespace Components { @@ -125,5 +128,19 @@ namespace Components // Hook CG_DrawFullScreenDebugOverlays so we may render the version when it's appropriate Utils::Hook(0x5AC975, Branding::CG_DrawVersion_Hk, HOOK_CALL).install()->quick(); + + // Console title + if (ZoneBuilder::IsEnabled()) + { + Utils::Hook::Set(0x4289E8, "IW4x (" VERSION "): ZoneBuilder"); + } + else if (Dedicated::IsEnabled()) + { + Utils::Hook::Set(0x4289E8, "IW4x (" VERSION "): Dedicated"); + } + else + { + Utils::Hook::Set(0x4289E8, "IW4x (" VERSION "): Console"); + } } } diff --git a/src/Components/Modules/Bullet.cpp b/src/Components/Modules/Bullet.cpp index d804b93b..d3310830 100644 --- a/src/Components/Modules/Bullet.cpp +++ b/src/Components/Modules/Bullet.cpp @@ -1,10 +1,19 @@ #include +#include "Bullet.hpp" namespace Components { Dvar::Var Bullet::BGSurfacePenetration; Game::dvar_t* Bullet::BGBulletRange; + float Bullet::ContactPointSave[3]; + float Bullet::VCSave[3]; + float Bullet::CalcRicochetSave[3]; + + float Bullet::ColorYellow[] = {1.0f, 1.0f, 0.0f, 1.0f}; + float Bullet::ColorBlue[] = {0.0f, 0.0f, 1.0f, 1.0f}; + float Bullet::ColorOrange[] = {1.0f, 0.7f, 0.0f, 1.0f}; + float Bullet::BG_GetSurfacePenetrationDepthStub(const Game::WeaponDef* weapDef, int surfaceType) { assert(weapDef); @@ -47,6 +56,63 @@ namespace Components *pHoldrand = static_cast(std::rand()); } + void Bullet::BulletRicochet_Save(const float* contactPoint) + { + std::memcpy(ContactPointSave, contactPoint, sizeof(float[3])); + } + + __declspec(naked) void Bullet::BulletRicochet_Stub() + { + __asm + { + pushad + push [esp + 0x20 + 0xC] + call BulletRicochet_Save + add esp, 0x4 + popad + + // Game's code + sub esp, 0x4C + push ebp + mov ebp, dword ptr [esp + 0x60] + + push 0x5D5B08 + ret + } + } + + void Bullet::_VectorMA_Stub(float* va, float scale, float* vb, float* vc) + { + vc[0] = va[0] + scale * vb[0]; + vc[1] = va[1] + scale * vb[1]; + vc[2] = va[2] + scale * vb[2]; + + std::memcpy(VCSave, vc, sizeof(float[3])); + } + + void Bullet::CalcRicochet_Stub(const float* incoming, const float* normal, float* result) + { + Utils::Hook::Call(0x5D59F0)(incoming, normal, result); + std::memcpy(CalcRicochetSave, result, sizeof(float[3])); + } + + int Bullet::Bullet_Fire_Stub(Game::gentity_s* attacker, [[maybe_unused]] float spread, Game::weaponParms* wp, Game::gentity_s* weaponEnt, Game::PlayerHandIndex hand, int gameTime) + { + float tmp[3]; + + Game::G_DebugStar(ContactPointSave, ColorYellow); + + tmp[0] = (CalcRicochetSave[0] * 100.0f) + VCSave[0]; + tmp[1] = (CalcRicochetSave[1] * 100.0f) + VCSave[1]; + tmp[2] = (CalcRicochetSave[2] * 100.0f) + VCSave[1]; + + Game::G_DebugLineWithDuration(VCSave, tmp, ColorOrange, 1, 100); + Game::G_DebugStar(tmp, ColorBlue); + + // Set the spread to 0 when drawing + return Game::Bullet_Fire(attacker, 0.0f, wp, weaponEnt, hand, gameTime); + } + Bullet::Bullet() { BGSurfacePenetration = Dvar::Register("bg_surfacePenetration", 0.0f, @@ -58,5 +124,20 @@ namespace Components Utils::Hook(0x440340, Bullet_FireStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x440368, BG_srand_Hk, HOOK_CALL).install()->quick(); + + std::memset(ContactPointSave, 0, sizeof(float[3])); + std::memset(VCSave, 0, sizeof(float[3])); + std::memset(CalcRicochetSave, 0, sizeof(float[3])); + +#ifdef DEBUG_RIOT_SHIELD + Utils::Hook(0x5D5B00, BulletRicochet_Stub, HOOK_JUMP).install()->quick(); + Utils::Hook::Nop(0x5D5B00 + 5, 3); + + Utils::Hook(0x5D5BBA, CalcRicochet_Stub, HOOK_CALL).install()->quick(); + + Utils::Hook(0x5D5BD7, _VectorMA_Stub, HOOK_CALL).install()->quick(); + + Utils::Hook(0x5D5C0B, Bullet_Fire_Stub, HOOK_CALL).install()->quick(); +#endif } } diff --git a/src/Components/Modules/Bullet.hpp b/src/Components/Modules/Bullet.hpp index 84083e3b..8a0abed4 100644 --- a/src/Components/Modules/Bullet.hpp +++ b/src/Components/Modules/Bullet.hpp @@ -12,10 +12,27 @@ namespace Components // Can't use Var class inside assembly stubs static Game::dvar_t* BGBulletRange; + static float ContactPointSave[]; + static float VCSave[]; + static float CalcRicochetSave[]; + + static float ColorYellow[]; + static float ColorBlue[]; + static float ColorOrange[]; + static float BG_GetSurfacePenetrationDepthStub(const Game::WeaponDef* weapDef, int surfaceType); static void Bullet_FireStub(); static void BG_srand_Hk(unsigned int* pHoldrand); + + static void BulletRicochet_Save(const float* contactPoint); + static void BulletRicochet_Stub(); + + static void CalcRicochet_Stub(const float* incoming, const float* normal, float* result); + + static void _VectorMA_Stub(float* va, float scale, float* vb, float* vc); + + static int Bullet_Fire_Stub(Game::gentity_s* attacker, float spread, Game::weaponParms* wp, Game::gentity_s* weaponEnt, Game::PlayerHandIndex hand, int gameTime); }; } diff --git a/src/Components/Modules/CardTitles.cpp b/src/Components/Modules/CardTitles.cpp index da34aa24..37e9045c 100644 --- a/src/Components/Modules/CardTitles.cpp +++ b/src/Components/Modules/CardTitles.cpp @@ -1,8 +1,10 @@ #include +#include "CardTitles.hpp" +#include "ServerCommands.hpp" namespace Components { - std::string CardTitles::CustomTitles[18]; + char CardTitles::CustomTitles[Game::MAX_CLIENTS][18]; Dvar::Var CardTitles::CustomTitle; CClient* CardTitles::GetClientByIndex(std::uint32_t index) @@ -10,35 +12,34 @@ namespace Components return &reinterpret_cast(0x8E77B0)[index]; } - std::int32_t CardTitles::GetPlayerCardClientInfo(std::int32_t lookupResult, Game::PlayerCardData* data) + int CardTitles::GetPlayerCardClientInfo(int lookupResult, Game::PlayerCardData* data) { - std::int32_t returnResult = lookupResult; + auto result = lookupResult; - std::string username = Dvar::Var("name").get(); - - if (data->name == username) + const auto* username = Dvar::Var("name").get(); + if (std::strcmp(data->name, username) == 0) { - returnResult += 0xFE000000; + result += 0xFE000000; } else { - for (std::size_t clientNum = 0; clientNum < Game::MAX_CLIENTS; ++clientNum) + for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i) { - CClient* c = GetClientByIndex(clientNum); + CClient* c = GetClientByIndex(i); if (c != nullptr) { if (!std::strcmp(data->name, c->Name)) { // Since a 4 byte integer is overkill for a row num: We can use it to store the customprefix + clientNum and use a 2 byte integer for the row number - returnResult += 0xFF000000; - returnResult += clientNum * 0x10000; + result += 0xFF000000; + result += i * 0x10000; break; } } } } - return returnResult; + return result; } void __declspec(naked) CardTitles::GetPlayerCardClientInfoStub() @@ -71,7 +72,7 @@ namespace Components std::uint8_t prefix = (request->tableRow >> (8 * 3)) & 0xFF; std::uint8_t data = (request->tableRow >> (8 * 2)) & 0xFF; - if (data >= ARRAYSIZE(CardTitles::CustomTitles)) return nullptr; + if (data >= Game::MAX_CLIENTS) return nullptr; if (request->tablename == "mp/cardTitleTable.csv"s) { @@ -82,10 +83,10 @@ namespace Components { if (prefix == 0xFE) { - if (!CardTitles::CustomTitle.get().empty()) + if (!CustomTitle.get().empty()) { // 0xFF in front of the title to skip localization. Or else it will wait for a couple of seconds for the asset of type localize - const char* title = Utils::String::VA("\x15%s", CardTitles::CustomTitle.get()); + const auto* title = Utils::String::VA("\x15%s", CustomTitle.get()); // prepare return value operand->internals.stringVal.string = title; @@ -96,9 +97,9 @@ namespace Components } else if (prefix == 0xFF) { - if (!CardTitles::CustomTitles[data].empty()) + if (CustomTitles[data][0] != '\0') { - const char* title = Utils::String::VA("\x15%s", CardTitles::CustomTitles[data].data()); + const auto* title = Utils::String::VA("\x15%s", CustomTitles[data]); // prepare return value operand->internals.stringVal.string = title; @@ -156,11 +157,11 @@ namespace Components { std::string list; - for (std::size_t i = 0; i < Game::MAX_CLIENTS; i++) + for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i) { - char playerTitle[18]; + char playerTitle[18]{}; - if (Game::svs_clients[i].header.state >= Game::CS_CONNECTED) + if (Game::svs_clients[i].userinfo[0] != '\0') { strncpy_s(playerTitle, Game::Info_ValueForKey(Game::svs_clients[i].userinfo, "customTitle"), _TRUNCATE); } @@ -169,10 +170,10 @@ namespace Components playerTitle[0] = '\0'; } - list.append(Utils::String::VA("\\%s\\%s", std::to_string(i).data(), playerTitle)); + list.append(std::format("\\{}\\{}", std::to_string(i), playerTitle)); } - const auto* command = Utils::String::VA("%c customTitles \"%s\"", 21, list.data()); + const auto* command = Utils::String::Format("{:c} customTitles \"{}\"", 21, list); Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, command); } @@ -180,10 +181,17 @@ namespace Components { for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i) { - const char* playerTitle = Game::Info_ValueForKey(msg, std::to_string(i).c_str()); + const auto index = std::to_string(i); + const auto* playerTitle = Game::Info_ValueForKey(msg, index.data()); - if (playerTitle) CardTitles::CustomTitles[i] = playerTitle; - else CardTitles::CustomTitles[i].clear(); + if (playerTitle[0] == '\0') + { + CustomTitles[i][0] = '\0'; + } + else + { + Game::I_strncpyz(CustomTitles[i], playerTitle, sizeof(CustomTitles[0]) / sizeof(char)); + } } } @@ -191,16 +199,18 @@ namespace Components { Scheduler::Once([] { - CardTitles::CustomTitle = Dvar::Register("customTitle", "", Game::DVAR_USERINFO | Game::DVAR_ARCHIVE, "Custom card title"); + CustomTitle = Dvar::Register("customTitle", "", Game::DVAR_USERINFO | Game::DVAR_ARCHIVE, "Custom card title"); }, Scheduler::Pipeline::MAIN); + std::memset(&CustomTitles, 0, sizeof(char[Game::MAX_CLIENTS][18])); + ServerCommands::OnCommand(21, [](Command::Params* params) { - if (params->get(1) == "customTitles"s && !Dedicated::IsEnabled()) + if (std::strcmp(params->get(1), "customTitles") == 0) { if (params->size() == 3) { - CardTitles::ParseCustomTitles(params->get(2)); + ParseCustomTitles(params->get(2)); return true; } } @@ -209,10 +219,10 @@ namespace Components }); - Utils::Hook(0x62EB26, CardTitles::GetPlayerCardClientInfoStub).install()->quick(); + Utils::Hook(0x62EB26, GetPlayerCardClientInfoStub).install()->quick(); // Table lookup stuff - Utils::Hook(0x62DCC1, CardTitles::TableLookupByRowHookStub).install()->quick(); + Utils::Hook(0x62DCC1, TableLookupByRowHookStub).install()->quick(); Utils::Hook::Nop(0x62DCC6, 1); } } diff --git a/src/Components/Modules/CardTitles.hpp b/src/Components/Modules/CardTitles.hpp index 7dcf5ce8..67b64495 100644 --- a/src/Components/Modules/CardTitles.hpp +++ b/src/Components/Modules/CardTitles.hpp @@ -14,27 +14,27 @@ namespace Components struct CClient { - std::uint32_t IsValid; // 0x0000 - std::uint32_t IsValid2; // 0x0004 - std::uint32_t ClientNumber; // 0x0008 - char Name[16]; // 0x000C - std::uint32_t Team; // 0x001C - std::uint32_t Team2; // 0x0020 - std::uint32_t Rank; // 0x0024 (rank - 1) - std::uint32_t Prestige; // 0x0028 - std::uint32_t Perks; // 0x002C - std::uint32_t Kills; // 0x0030 - std::uint32_t Score; // 0x0034 + std::uint32_t IsValid; // 0x0000 + std::uint32_t IsValid2; // 0x0004 + std::uint32_t ClientNumber; // 0x0008 + char Name[16]; // 0x000C + std::uint32_t Team; // 0x001C + std::uint32_t Team2; // 0x0020 + std::uint32_t Rank; // 0x0024 (rank - 1) + std::uint32_t Prestige; // 0x0028 + std::uint32_t Perks; // 0x002C + std::uint32_t Kills; // 0x0030 + std::uint32_t Score; // 0x0034 std::uint8_t _0x0038[968]; - std::uint32_t ViewAngles; // 0x0400 + std::uint32_t ViewAngles; // 0x0400 std::uint8_t _0x040C[136]; - std::uint32_t IsShooting; // 0x0494 + std::uint32_t IsShooting; // 0x0494 std::uint8_t _0x0498[4]; - std::uint32_t IsZoomed; // 0x049C + std::uint32_t IsZoomed; // 0x049C std::uint8_t _0x04A0[68]; - std::uint32_t weaponID; // 0x04E4 + std::uint32_t weaponID; // 0x04E4 std::uint8_t _0x04E8[24]; - std::uint32_t weaponID2; // 0x0500 + std::uint32_t weaponID2; // 0x0500 std::uint8_t _0x0504[40]; std::uint8_t _padding[8]; }; @@ -44,19 +44,20 @@ namespace Components public: AssertOffset(Game::PlayerCardData, Game::PlayerCardData::name, 0x1C); - static Dvar::Var CustomTitle; - static std::string CustomTitles[18]; - static void SendCustomTitlesToClients(); - static void ParseCustomTitles(const char* msg); CardTitles(); private: + static Dvar::Var CustomTitle; + static char CustomTitles[Game::MAX_CLIENTS][18]; + static CClient* GetClientByIndex(std::uint32_t index); - static std::int32_t GetPlayerCardClientInfo(std::int32_t lookupResult, Game::PlayerCardData* data); + static int GetPlayerCardClientInfo(int lookupResult, Game::PlayerCardData* data); static void GetPlayerCardClientInfoStub(); static const char* TableLookupByRowHook(Game::Operand* operand, tablelookuprequest_s* request); static void TableLookupByRowHookStub(); + + static void ParseCustomTitles(const char* msg); }; } diff --git a/src/Components/Modules/Ceg.cpp b/src/Components/Modules/Ceg.cpp index ad5b21cc..cf765d2b 100644 --- a/src/Components/Modules/Ceg.cpp +++ b/src/Components/Modules/Ceg.cpp @@ -1,4 +1,5 @@ #include +#include "Ceg.hpp" namespace Components { diff --git a/src/Components/Modules/Changelog.cpp b/src/Components/Modules/Changelog.cpp index 93b5974d..10851346 100644 --- a/src/Components/Modules/Changelog.cpp +++ b/src/Components/Modules/Changelog.cpp @@ -1,4 +1,6 @@ #include +#include "Changelog.hpp" +#include "UIFeeder.hpp" namespace Components { @@ -7,44 +9,44 @@ namespace Components void Changelog::LoadChangelog() { - //if (!Changelog::Lines.empty()) - // return; + std::lock_guard _(Mutex); + Lines.clear(); - std::lock_guard _(Changelog::Mutex); - Changelog::Lines.clear(); - std::string data = Utils::Cache::GetFile("/develop/CHANGELOG.md"); + const auto data = Utils::Cache::GetFile("/develop/CHANGELOG.md"); if (data.empty()) { - data = "^1Unable to get changelog."; + Lines.emplace_back("^1Unable to get changelog."); + return; } - Changelog::Lines = Utils::String::Split(data, '\n'); - - for (auto& line : Changelog::Lines) + auto buffer = Utils::String::Split(data, '\n'); + for (auto& line : buffer) { Utils::String::Replace(line, "\r", ""); } + + Lines = buffer; } unsigned int Changelog::GetChangelogCount() { - return Changelog::Lines.size(); + return Lines.size(); } // Omit column here - const char* Changelog::GetChangelogText(unsigned int item, int /*column*/) + const char* Changelog::GetChangelogText(unsigned int item, [[maybe_unused]] int column) { - std::lock_guard _(Changelog::Mutex); - if (item < Changelog::Lines.size()) + std::lock_guard _(Mutex); + if (item < Lines.size()) { - return Utils::String::VA("%s", Changelog::Lines[item].data()); + return Utils::String::Format("{}", Lines[item]); } return ""; } - void Changelog::SelectChangelog(unsigned int /*index*/) + void Changelog::SelectChangelog([[maybe_unused]] unsigned int index) { // Don't do anything in here } @@ -54,6 +56,6 @@ namespace Components if (Dedicated::IsEnabled()) return; // Changelog - UIFeeder::Add(62.0f, Changelog::GetChangelogCount, Changelog::GetChangelogText, Changelog::SelectChangelog); + UIFeeder::Add(62.0f, GetChangelogCount, GetChangelogText, SelectChangelog); } } diff --git a/src/Components/Modules/Chat.cpp b/src/Components/Modules/Chat.cpp index 924cb498..ef7e8eb3 100644 --- a/src/Components/Modules/Chat.cpp +++ b/src/Components/Modules/Chat.cpp @@ -1,4 +1,8 @@ #include +#include "Chat.hpp" +#include "PlayerName.hpp" +#include "Voice.hpp" + #include "GSC/Script.hpp" namespace Components @@ -73,7 +77,7 @@ namespace Components Game::Scr_AddEntity(player); Game::Scr_AddString(text + msgIndex); - Game::Scr_NotifyLevel(Game::SL_GetString("say", 0), 2); + Game::Scr_NotifyLevel(static_cast(Game::SL_GetString("say", 0)), 2); return text; } @@ -268,8 +272,7 @@ namespace Components }); Logger::Print("{} was muted\n", client->name); - Game::SV_GameSendServerCommand(client - Game::svs_clients, Game::SV_CMD_CAN_IGNORE, - Utils::String::VA("%c \"You were muted\"", 0x65)); + Game::SV_GameSendServerCommand(client - Game::svs_clients, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"You were muted\"", 0x65)); } void Chat::UnmuteClient(const Game::client_t* client) @@ -277,8 +280,7 @@ namespace Components UnmuteInternal(client->steamID); Logger::Print("{} was unmuted\n", client->name); - Game::SV_GameSendServerCommand(client - Game::svs_clients, Game::SV_CMD_CAN_IGNORE, - Utils::String::VA("%c \"You were unmuted\"", 0x65)); + Game::SV_GameSendServerCommand(client - Game::svs_clients, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"You were unmuted\"", 0x65)); } void Chat::UnmuteInternal(const std::uint64_t id, bool everyone) @@ -369,12 +371,12 @@ namespace Components if (!name.empty()) { - Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s: %s\"", 0x68, name.data(), message.data())); + Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::Format("{:c} \"{}: {}\"", 0x68, name, message)); Logger::Print(Game::CON_CHANNEL_SERVER, "{}: {}\n", name, message); } else { - Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"Console: %s\"", 0x68, message.data())); + Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::Format("{:c} \"Console: {}\"", 0x68, message)); Logger::Print(Game::CON_CHANNEL_SERVER, "Console: {}\n", message); } }); @@ -395,12 +397,12 @@ namespace Components if (!name.empty()) { - Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s: %s\"", 0x68, name.data(), message.data())); + Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::Format("{:c} \"{}: {}\"", 0x68, name.data(), message)); Logger::Print(Game::CON_CHANNEL_SERVER, "{} -> {}: {}\n", name, client, message); } else { - Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"Console: %s\"", 104, message.data())); + Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::Format("{:c} \"Console: {}\"", 0x68, message)); Logger::Print(Game::CON_CHANNEL_SERVER, "Console -> {}: {}\n", client, message); } }); @@ -416,7 +418,7 @@ namespace Components if (params->size() < 2) return; auto message = params->join(1); - Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x68, message.data())); + Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::Format("{:c} \"{}\"", 0x68, message)); Logger::Print(Game::CON_CHANNEL_SERVER, "Raw: {}\n", message); }); @@ -432,7 +434,7 @@ namespace Components const auto client = atoi(params->get(1)); std::string message = params->join(2); - Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x68, message.data())); + Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::Format("{:c} \"{}\"", 0x68, message)); Logger::Print(Game::CON_CHANNEL_SERVER, "Raw -> {}: {}\n", client, message); }); diff --git a/src/Components/Modules/ClanTags.cpp b/src/Components/Modules/ClanTags.cpp index 7394e55d..dfa3a664 100644 --- a/src/Components/Modules/ClanTags.cpp +++ b/src/Components/Modules/ClanTags.cpp @@ -1,4 +1,7 @@ #include +#include "ClanTags.hpp" +#include "PlayerName.hpp" +#include "ServerCommands.hpp" namespace Components { @@ -28,15 +31,15 @@ namespace Components list.append(std::format("\\{}\\{}", std::to_string(i), ClientState[i])); } - const auto* command = Utils::String::VA("%c clanNames \"%s\"", 22, list.data()); - Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, command); + Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::Format("{:c} clanNames \"{}\"", 22, list)); } void ClanTags::ParseClanTags(const char* infoString) { for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i) { - const auto* clanTag = Game::Info_ValueForKey(infoString, std::to_string(i).data()); + const auto index = std::to_string(i); + const auto* clanTag = Game::Info_ValueForKey(infoString, index.data()); if (clanTag[0] == '\0') { @@ -70,9 +73,7 @@ namespace Components void ClanTags::CL_SanitizeClanName() { - char saneNameBuf[5]; - std::memset(saneNameBuf, 0, sizeof(saneNameBuf)); - + char saneNameBuf[5]{}; auto* saneName = saneNameBuf; assert(ClanName); diff --git a/src/Components/Modules/ClientCommand.cpp b/src/Components/Modules/ClientCommand.cpp index fd6c3cc6..5de247e1 100644 --- a/src/Components/Modules/ClientCommand.cpp +++ b/src/Components/Modules/ClientCommand.cpp @@ -1,6 +1,10 @@ #include +#include "ClientCommand.hpp" + #include "GSC/Script.hpp" +using namespace Utils::String; + namespace Components { std::unordered_map> ClientCommand::HandlersSV; @@ -12,14 +16,14 @@ namespace Components if (!(*Game::g_cheats)->current.enabled) { Logger::Debug("Cheats are disabled!"); - Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"GAME_CHEATSNOTENABLED\"", 0x65)); + Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_CHEATSNOTENABLED\"", 0x65)); return false; } if (ent->health < 1) { Logger::Debug("Entity {} must be alive to use this command!", entNum); - Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"GAME_MUSTBEALIVECOMMAND\"", 0x65)); + Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_MUSTBEALIVECOMMAND\"", 0x65)); return false; } @@ -37,9 +41,9 @@ namespace Components { const auto ent = &Game::g_entities[clientNum]; - if (ent->client == nullptr) + if (!ent->client) { - Logger::Debug("ClientCommand: client {} is not fully in game yet", clientNum); + Logger::Debug("ClientCommand: client {} is not fully connected", clientNum); return; } @@ -67,8 +71,7 @@ namespace Components const auto entNum = ent->s.number; Logger::Debug("Noclip toggled for entity {}", entNum); - Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65, - (ent->client->flags & Game::PLAYER_FLAG_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF")); + Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"%s\"", 0x65, (ent->client->flags & Game::PLAYER_FLAG_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF")); }); Add("ufo", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) @@ -81,8 +84,7 @@ namespace Components const auto entNum = ent->s.number; Logger::Debug("UFO toggled for entity {}", entNum); - Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65, - (ent->client->flags & Game::PLAYER_FLAG_UFO) ? "GAME_UFOON" : "GAME_UFOOFF")); + Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"%s\"", 0x65, (ent->client->flags & Game::PLAYER_FLAG_UFO) ? "GAME_UFOON" : "GAME_UFOOFF")); }); Add("god", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) @@ -95,8 +97,7 @@ namespace Components const auto entNum = ent->s.number; Logger::Debug("God toggled for entity {}", entNum); - Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65, - (ent->flags & Game::FL_GODMODE) ? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF")); + Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"%s\"", 0x65, (ent->flags & Game::FL_GODMODE) ? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF")); }); Add("demigod", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) @@ -109,8 +110,7 @@ namespace Components const auto entNum = ent->s.number; Logger::Debug("Demigod toggled for entity {}", entNum); - Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65, - (ent->flags & Game::FL_DEMI_GODMODE) ? "GAME_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF")); + Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"%s\"", 0x65, (ent->flags & Game::FL_DEMI_GODMODE) ? "GAME_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF")); }); Add("notarget", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) @@ -123,13 +123,12 @@ namespace Components const auto entNum = ent->s.number; Logger::Debug("Notarget toggled for entity {}", entNum); - Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65, - (ent->flags & Game::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF")); + Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"%s\"", 0x65, (ent->flags & Game::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF")); }); Add("setviewpos", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { - assert(ent != nullptr); + assert(ent); if (!CheatsOk(ent)) return; @@ -138,8 +137,7 @@ namespace Components if (params->size() < 4 || params->size() > 6) { - Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_CAN_IGNORE, - Utils::String::VA("%c \"GAME_USAGE\x15: setviewpos x y z [yaw] [pitch]\n\"", 0x65)); + Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_USAGE\x15: setviewpos x y z [yaw] [pitch]\n\"", 0x65)); return; } @@ -170,8 +168,7 @@ namespace Components if (params->size() < 2) { - Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_CAN_IGNORE, - Utils::String::VA("%c \"GAME_USAGE\x15: give \"", 0x65)); + Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_USAGE\x15: give \"", 0x65)); return; } @@ -245,19 +242,26 @@ namespace Components Add("kill", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { - assert(ent->client != nullptr); + assert(ent->client); assert(ent->client->sess.connected != Game::CON_DISCONNECTED); if (ent->client->sess.sessionState != Game::SESS_STATE_PLAYING || !CheatsOk(ent)) return; - Scheduler::Once([ent] - { - ent->flags &= ~(Game::FL_GODMODE | Game::FL_DEMI_GODMODE); - ent->health = 0; - ent->client->ps.stats[0] = 0; - Game::player_die(ent, ent, ent, 100000, Game::MOD_SUICIDE, 0, nullptr, Game::HITLOC_NONE, 0); - }, Scheduler::Pipeline::SERVER); + auto** bgs = Game::Sys::GetTls(Game::Sys::TLS_OFFSET::LEVEL_BGS); + + assert(*bgs == nullptr); + + *bgs = Game::level_bgs; + + ent->flags &= ~(Game::FL_GODMODE | Game::FL_DEMI_GODMODE); + ent->health = 0; + ent->client->ps.stats[0] = 0; + Game::player_die(ent, ent, ent, 100000, Game::MOD_SUICIDE, 0, nullptr, Game::HITLOC_NONE, 0); + + assert(*bgs == Game::level_bgs); + + *bgs = nullptr; }); } @@ -299,7 +303,7 @@ namespace Components duration = static_cast(std::floorf(input * 1000.0f + 0.5f)); } - assert(ent->client != nullptr); + assert(ent->client); constexpr auto visMode = Game::visionSetMode_t::VISIONSET_NORMAL; const auto* name = params->get(1); @@ -308,8 +312,7 @@ namespace Components strncpy_s(ent->client->visionName[visMode], sizeof(Game::gclient_t::visionName[0]) / sizeof(char), name, _TRUNCATE); - Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_RELIABLE, - Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration)); + Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_RELIABLE, VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration)); }); Add("visionsetnight", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) @@ -327,7 +330,7 @@ namespace Components duration = static_cast(std::floorf(input * 1000.0f + 0.5f)); } - assert(ent->client != nullptr); + assert(ent->client); constexpr auto visMode = Game::visionSetMode_t::VISIONSET_NIGHT; const auto* name = params->get(1); @@ -336,13 +339,12 @@ namespace Components strncpy_s(ent->client->visionName[visMode], sizeof(Game::gclient_t::visionName[0]) / sizeof(char), name, _TRUNCATE); - Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_RELIABLE, - Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration)); + Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_RELIABLE, VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration)); }); Add("g_testCmd", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { - assert(ent != nullptr); + assert(ent); ent->client->ps.stunTime = 1000 + Game::level->time; // 1000 is the default test stun time Logger::Debug("playerState_s.stunTime is {}", ent->client->ps.stunTime); @@ -406,7 +408,7 @@ namespace Components // See description of the format string in the function G_DumpEntityDebugInfoToCSV // If empty it means the item does not exist in the current version of the game or it was not possible to reverse it - return Utils::String::VA("%i,%s,%.0f,%s,%s,%s,%s,%s,%s,%s,%s,%s,%.0f %.0f %.0f,%.0f %.0f %.0f,%i\n", + return VA("%i,%s,%.0f,%s,%s,%s,%s,%s,%s,%s,%s,%s,%.0f %.0f %.0f,%.0f %.0f %.0f,%i\n", entNum, eventType, distance, classname, codeClassname, (model) ? model->name : "", targetName, target, "", scriptLinkName, team, "", point[0], point[1], point[2], angles[0], angles[1], angles[2], 0); @@ -442,7 +444,7 @@ namespace Components { assert(filenameSuffix); - const auto* fileName = Utils::String::VA("%s%s%s%s", "EntInfo", (*filenameSuffix) ? "_" : "", filenameSuffix, ".csv"); + const auto* fileName = VA("%s%s%s%s", "EntInfo", (*filenameSuffix) ? "_" : "", filenameSuffix, ".csv"); Logger::Print(Game::CON_CHANNEL_SERVER, "Opening file \"{}\" for writing.\n", fileName); auto h = Game::FS_FOpenTextFileWrite(fileName); @@ -464,9 +466,11 @@ namespace Components const auto* line = EntInfoLine(i); const auto lineLen = std::strlen(line); + assert(line); assert(lineLen); - Game::FS_Write(line, lineLen, h); + + Game::FS_Write(line, static_cast(lineLen), h); } Game::FS_FCloseFile(h); diff --git a/src/Components/Modules/ConnectProtocol.cpp b/src/Components/Modules/ConnectProtocol.cpp index a3323a9b..1542b28e 100644 --- a/src/Components/Modules/ConnectProtocol.cpp +++ b/src/Components/Modules/ConnectProtocol.cpp @@ -1,4 +1,5 @@ #include +#include "ConnectProtocol.hpp" namespace Components { @@ -7,17 +8,17 @@ namespace Components bool ConnectProtocol::IsEvaluated() { - return ConnectProtocol::Evaluated; + return Evaluated; } bool ConnectProtocol::Used() { - if (!ConnectProtocol::IsEvaluated()) + if (!IsEvaluated()) { - ConnectProtocol::EvaluateProtocol(); + EvaluateProtocol(); } - return (!ConnectProtocol::ConnectString.empty()); + return (!ConnectString.empty()); } bool ConnectProtocol::InstallProtocol() @@ -25,8 +26,8 @@ namespace Components HKEY hKey = nullptr; std::string data; - char ownPth[MAX_PATH] = {0}; - char workdir[MAX_PATH] = {0}; + char ownPth[MAX_PATH]{}; + char workdir[MAX_PATH]{}; DWORD dwsize = MAX_PATH; HMODULE hModule = GetModuleHandleA(nullptr); @@ -65,13 +66,13 @@ namespace Components LONG openRes = RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE\\Classes\\iw4x\\shell\\open\\command", 0, KEY_ALL_ACCESS, &hKey); if (openRes == ERROR_SUCCESS) { - char regred[MAX_PATH] = { 0 }; + char regred[MAX_PATH]{}; // Check if the game has been moved. openRes = RegQueryValueExA(hKey, nullptr, nullptr, nullptr, reinterpret_cast(regred), &dwsize); if (openRes == ERROR_SUCCESS) { - char* endPtr = strstr(regred, "\" \"%1\""); + auto* endPtr = std::strstr(regred, "\" \"%1\""); if (endPtr != nullptr) { *endPtr = 0; @@ -82,7 +83,8 @@ namespace Components } RegCloseKey(hKey); - if (strcmp(regred + 1, ownPth)) + + if (std::strcmp(regred + 1, ownPth) != 0) { RegDeleteKeyA(HKEY_CURRENT_USER, "SOFTWARE\\Classes\\iw4x"); } @@ -173,8 +175,8 @@ namespace Components void ConnectProtocol::EvaluateProtocol() { - if (ConnectProtocol::Evaluated) return; - ConnectProtocol::Evaluated = true; + if (Evaluated) return; + Evaluated = true; std::string cmdLine = GetCommandLineA(); @@ -190,15 +192,15 @@ namespace Components cmdLine = cmdLine.substr(0, pos); } - ConnectProtocol::ConnectString = cmdLine; + ConnectString = cmdLine; } } void ConnectProtocol::Invocation() { - if (ConnectProtocol::Used()) + if (Used()) { - Command::Execute(Utils::String::VA("connect %s", ConnectProtocol::ConnectString.data()), false); + Command::Execute(std::format("connect {}", ConnectString), false); } } @@ -209,28 +211,28 @@ namespace Components // IPC handler IPCPipe::On("connect", [](const std::string& data) { - Command::Execute(Utils::String::VA("connect %s", data.data()), false); + Command::Execute(std::format("connect {}", data), false); }); // Invocation handler - Scheduler::OnGameInitialized(ConnectProtocol::Invocation, Scheduler::Pipeline::MAIN); + Scheduler::OnGameInitialized(Invocation, Scheduler::Pipeline::MAIN); - ConnectProtocol::InstallProtocol(); - ConnectProtocol::EvaluateProtocol(); + InstallProtocol(); + EvaluateProtocol(); // Fire protocol handlers // Make sure this happens after the pipe-initialization! - if (ConnectProtocol::Used()) + if (Used()) { if (!Singleton::IsFirstInstance()) { - IPCPipe::Write("connect", ConnectProtocol::ConnectString); + IPCPipe::Write("connect", ConnectString); ExitProcess(0); } else { // Only skip intro here, invocation will be done later. - Utils::Hook::Set(0x60BECF, 0xEB); + Utils::Hook::Set(0x60BECF, 0xEB); Scheduler::Once([] { diff --git a/src/Components/Modules/Console.cpp b/src/Components/Modules/Console.cpp index 7f501d0b..04c5b418 100644 --- a/src/Components/Modules/Console.cpp +++ b/src/Components/Modules/Console.cpp @@ -1,5 +1,12 @@ #include -#undef MOUSE_MOVED +#include "Console.hpp" + +#include + +#ifdef MOUSE_MOVED + #undef MOUSE_MOVED +#endif + #include #define REMOVE_HEADERBAR 1 diff --git a/src/Components/Modules/D3D9Ex.cpp b/src/Components/Modules/D3D9Ex.cpp index 6903e23a..ca5f296f 100644 --- a/src/Components/Modules/D3D9Ex.cpp +++ b/src/Components/Modules/D3D9Ex.cpp @@ -1,4 +1,5 @@ #include +#include "D3D9Ex.hpp" namespace Components { diff --git a/src/Components/Modules/Debug.cpp b/src/Components/Modules/Debug.cpp index 0d968983..9e742f22 100644 --- a/src/Components/Modules/Debug.cpp +++ b/src/Components/Modules/Debug.cpp @@ -1,4 +1,6 @@ #include +#include "Debug.hpp" + #include "Game/Engine/ScopedCriticalSection.hpp" namespace Components @@ -6,7 +8,7 @@ namespace Components const Game::dvar_t* Debug::DebugOverlay; const Game::dvar_t* Debug::BugName; - Game::dvar_t** Debug::PlayerDebugHealth = reinterpret_cast(0x7A9F7C); + const Game::dvar_t* Debug::PlayerDebugHealth; const char* Debug::PMFlagsValues[] = { @@ -92,15 +94,15 @@ namespace Components "EF_SOFT", }; - const char Debug::strButtons[] = + const char Debug::StrButtons[] = { '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x0E', '\x0F', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\0' }; - const char Debug::strTemplate[] = "%s: %s All those moments will be lost in time, like tears in rain."; + const char Debug::StrTemplate[] = "%s: %s All those moments will be lost in time, like tears in rain."; - const float Debug::colorWhite[] = {1.0f, 1.0f, 1.0f, 1.0f}; + const float Debug::ColorWhite[] = {1.0f, 1.0f, 1.0f, 1.0f}; std::string Debug::BuildPMFlagsString(const Game::playerState_s* ps) { @@ -161,19 +163,19 @@ namespace Components auto* const font2 = Game::UI_GetFontHandle(scrPlace, 6, MY_SCALE2); Game::UI_DrawText(scrPlace, "Client View of Flags", maxChars, font2, -60.0f, 0, 1, 1, - MY_SCALE2, colorWhite, 1); + MY_SCALE2, ColorWhite, 1); const auto pmf = BuildPMFlagsString(&cgameGlob->predictedPlayerState); - Game::UI_DrawText(scrPlace, pmf.data(), maxChars, font1, 30.0f, MY_Y, 1, 1, MY_SCALE_2, colorWhite, 3); + Game::UI_DrawText(scrPlace, pmf.data(), maxChars, font1, 30.0f, MY_Y, 1, 1, MY_SCALE_2, ColorWhite, 3); const auto pof = BuildPOFlagsString(&cgameGlob->predictedPlayerState); - Game::UI_DrawText(scrPlace, pof.data(), maxChars, font1, 350.0f, MY_Y, 1, 1, MY_SCALE_2, colorWhite, 3); + Game::UI_DrawText(scrPlace, pof.data(), maxChars, font1, 350.0f, MY_Y, 1, 1, MY_SCALE_2, ColorWhite, 3); const auto plf = BuildPLFlagsString(&cgameGlob->predictedPlayerState); - Game::UI_DrawText(scrPlace, plf.data(), maxChars, font1, 350.0f, 250.0f, 1, 1, MY_SCALE_2, colorWhite, 3); + Game::UI_DrawText(scrPlace, plf.data(), maxChars, font1, 350.0f, 250.0f, 1, 1, MY_SCALE_2, ColorWhite, 3); const auto pef = BuildPEFlagsString(&cgameGlob->predictedPlayerState); - Game::UI_DrawText(scrPlace, pef.data(), maxChars, font1, 525.0f, MY_Y, 1, 1, MY_SCALE_2, colorWhite, 3); + Game::UI_DrawText(scrPlace, pef.data(), maxChars, font1, 525.0f, MY_Y, 1, 1, MY_SCALE_2, ColorWhite, 3); } void Debug::CG_DrawDebugPlayerHealth(const int localClientNum) @@ -182,7 +184,7 @@ namespace Components constexpr float color1[] = {0.0f, 0.0f, 0.0f, 1.0f}; constexpr float color2[] = {0.0f, 1.0f, 0.0f, 1.0f}; - assert((*PlayerDebugHealth)->current.enabled); + assert(PlayerDebugHealth->current.enabled); const auto* cgameGlob = Game::cgArray; if (cgameGlob->predictedPlayerState.stats[0] && cgameGlob->predictedPlayerState.stats[2]) @@ -219,25 +221,25 @@ namespace Components auto* const font5 = Game::UI_GetFontHandle(scrPlace, 5, 0.4f); auto* const font6 = Game::UI_GetFontHandle(scrPlace, 6, 0.4f); - sprintf_s(strFinal, strTemplate, font1->fontName, strButtons); + sprintf_s(strFinal, StrTemplate, font1->fontName, StrButtons); Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal)); - Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits::max(), font1, MY_X, 10.0f, 1, 1, 0.4f, colorWhite, 3); + Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits::max(), font1, MY_X, 10.0f, 1, 1, 0.4f, ColorWhite, 3); - sprintf_s(strFinal, strTemplate, font2->fontName, strButtons); + sprintf_s(strFinal, StrTemplate, font2->fontName, StrButtons); Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal)); - Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits::max(), font2, MY_X, 35.0f, 1, 1, 0.4f, colorWhite, 3); + Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits::max(), font2, MY_X, 35.0f, 1, 1, 0.4f, ColorWhite, 3); - sprintf_s(strFinal, strTemplate, font3->fontName, strButtons); + sprintf_s(strFinal, StrTemplate, font3->fontName, StrButtons); Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal)); - Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits::max(), font3, MY_X, 60.0f, 1, 1, 0.4f, colorWhite, 3); + Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits::max(), font3, MY_X, 60.0f, 1, 1, 0.4f, ColorWhite, 3); - sprintf_s(strFinal, strTemplate, font5->fontName, strButtons); + sprintf_s(strFinal, StrTemplate, font5->fontName, StrButtons); Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal)); - Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits::max(), font5, MY_X, 85.0f, 1, 1, 0.4f, colorWhite, 3); + Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits::max(), font5, MY_X, 85.0f, 1, 1, 0.4f, ColorWhite, 3); - sprintf_s(strFinal, strTemplate, font6->fontName, strButtons); + sprintf_s(strFinal, StrTemplate, font6->fontName, StrButtons); Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal)); - Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits::max(), font6, MY_X, 110.0f, 1, 1, 0.4f, colorWhite, 3); + Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits::max(), font6, MY_X, 110.0f, 1, 1, 0.4f, ColorWhite, 3); } void Debug::CG_DrawDebugOverlays_Hk(const int localClientNum) @@ -254,7 +256,7 @@ namespace Components break; } - if ((*PlayerDebugHealth)->current.enabled) + if (PlayerDebugHealth->current.enabled) { CG_DrawDebugPlayerHealth(localClientNum); } @@ -304,10 +306,31 @@ namespace Components if (!result) { - Logger::PrintError(1, "CopyFile failed({}) {} {}\n", GetLastError(), "console_mp.log", newFileName); + Logger::PrintError(Game::CON_CHANNEL_ERROR, "CopyFile failed({}) {} {}\n", GetLastError(), "console_mp.log", newFileName); } } + void Debug::Com_BugNameInc_f() + { + char buf[260]{}; + + if (std::strlen(BugName->current.string) < 4) + { + Game::Dvar_SetString(BugName, "bug0"); + return; + } + + if (std::strncmp(BugName->current.string, "bug", 3) != 0) + { + Game::Dvar_SetString(BugName, "bug0"); + return; + } + + const auto n = std::strtol(BugName->current.string + 3, nullptr, 10); + sprintf_s(buf, "bug%d", n + 1); + Game::Dvar_SetString(BugName, buf); + } + void Debug::CL_InitDebugDvars() { static const char* debugOverlayNames_0[] = @@ -327,6 +350,12 @@ namespace Components Game::DVAR_CHEAT | Game::DVAR_CODINFO, "Name appended to the copied console log"); } + const Game::dvar_t* Debug::Dvar_Register_PlayerDebugHealth(const char* name, bool value, [[maybe_unused]] std::uint16_t flags, const char* description) + { + PlayerDebugHealth = Game::Dvar_RegisterBool(name, value, Game::DVAR_NONE, description); + return PlayerDebugHealth; + } + Debug::Debug() { Scheduler::Once(CL_InitDebugDvars, Scheduler::Pipeline::MAIN); @@ -336,8 +365,11 @@ namespace Components Utils::Hook::Set(0x60BCEA, Com_Assert_f); + Utils::Hook(0x4487F7, Dvar_Register_PlayerDebugHealth, HOOK_CALL).install()->quick(); + #ifdef _DEBUG Command::Add("bug", Com_Bug_f); + Command::Add("bug_name_inc", Com_BugNameInc_f); #endif } } diff --git a/src/Components/Modules/Debug.hpp b/src/Components/Modules/Debug.hpp index 3eaf8a78..ac18b2c5 100644 --- a/src/Components/Modules/Debug.hpp +++ b/src/Components/Modules/Debug.hpp @@ -12,15 +12,15 @@ namespace Components static const Game::dvar_t* BugName; // Game dvars - static Game::dvar_t** PlayerDebugHealth; + static const Game::dvar_t* PlayerDebugHealth; static const char* PMFlagsValues[]; static const char* POFlagsValues[]; static const char* PLFlagsValues[]; static const char* PEFlagsValues[]; - static const char strButtons[]; - static const char strTemplate[]; + static const char StrButtons[]; + static const char StrTemplate[]; static constexpr auto MY_SCALE2 = 0.5f; static constexpr auto MY_SCALE_2 = 0.201f; @@ -28,7 +28,7 @@ namespace Components static constexpr auto MY_X = -25.0f; static constexpr auto MY_Y = 20.0f; - static const float colorWhite[]; + static const float ColorWhite[]; static std::string BuildPMFlagsString(const Game::playerState_s* ps); static std::string BuildPOFlagsString(const Game::playerState_s* ps); @@ -43,7 +43,9 @@ namespace Components static void Com_Assert_f(); static void Com_Bug_f(Command::Params* params); + static void Com_BugNameInc_f(); static void CL_InitDebugDvars(); + static const Game::dvar_t* Dvar_Register_PlayerDebugHealth(const char* name, bool value, std::uint16_t flags, const char* description); }; } diff --git a/src/Components/Modules/Dedicated.cpp b/src/Components/Modules/Dedicated.cpp index 547ac20b..589aa997 100644 --- a/src/Components/Modules/Dedicated.cpp +++ b/src/Components/Modules/Dedicated.cpp @@ -1,4 +1,7 @@ #include +#include "CardTitles.hpp" +#include "ClanTags.hpp" +#include "ServerCommands.hpp" namespace Components { @@ -114,7 +117,7 @@ namespace Components if (!partyEnable) // Time wrapping should not occur in party servers, but yeah... { if (mapname.empty()) mapname = "mp_rust"; - Command::Execute(Utils::String::VA("map %s", mapname.data()), true); + Command::Execute(std::format("map {}", mapname), true); } }, Scheduler::Pipeline::SERVER); diff --git a/src/Components/Modules/Discovery.cpp b/src/Components/Modules/Discovery.cpp index 3545ab07..86bee2ee 100644 --- a/src/Components/Modules/Discovery.cpp +++ b/src/Components/Modules/Discovery.cpp @@ -1,4 +1,6 @@ #include +#include "Discovery.hpp" +#include "ServerList.hpp" namespace Components { @@ -7,39 +9,42 @@ namespace Components std::thread Discovery::Thread; std::string Discovery::Challenge; + Dvar::Var Discovery::NetDiscoveryPortRangeMin; + Dvar::Var Discovery::NetDiscoveryPortRangeMax; + void Discovery::Perform() { - Discovery::IsPerforming = true; + IsPerforming = true; } Discovery::Discovery() { - Dvar::Register("net_discoveryPortRangeMin", 25000, 0, 65535, Game::DVAR_ARCHIVE, "Minimum scan range port for local server discovery"); - Dvar::Register("net_discoveryPortRangeMax", 35000, 1, 65536, Game::DVAR_ARCHIVE, "Maximum scan range port for local server discovery"); + NetDiscoveryPortRangeMin = Dvar::Register("net_discoveryPortRangeMin", 25000, 0, 65535, Game::DVAR_NONE, "Minimum scan range port for local server discovery"); + NetDiscoveryPortRangeMax = Dvar::Register("net_discoveryPortRangeMax", 35000, 1, 65536, Game::DVAR_NONE, "Maximum scan range port for local server discovery"); // An additional thread prevents lags // Not sure if that's the best way though - Discovery::IsPerforming = false; - Discovery::IsTerminating = false; - Discovery::Thread = std::thread([]() + IsPerforming = false; + IsTerminating = false; + Thread = std::thread([] { - while (!Discovery::IsTerminating) + while (!IsTerminating) { - if (Discovery::IsPerforming) + if (IsPerforming) { - int start = Game::Sys_Milliseconds(); + const auto start = Game::Sys_Milliseconds(); Logger::Print("Starting local server discovery...\n"); - Discovery::Challenge = Utils::Cryptography::Rand::GenerateChallenge(); + Challenge = Utils::Cryptography::Rand::GenerateChallenge(); - unsigned int minPort = Dvar::Var("net_discoveryPortRangeMin").get(); - unsigned int maxPort = Dvar::Var("net_discoveryPortRangeMax").get(); - Network::BroadcastRange(minPort, maxPort, Utils::String::VA("discovery %s", Discovery::Challenge.data())); + const auto minPort = NetDiscoveryPortRangeMin.get(); + const auto maxPort = NetDiscoveryPortRangeMax.get(); + Network::BroadcastRange(minPort, maxPort, std::format("discovery {}", Challenge)); Logger::Print("Discovery sent within {}ms, awaiting responses...\n", Game::Sys_Milliseconds() - start); - Discovery::IsPerforming = false; + IsPerforming = false; } std::this_thread::sleep_for(50ms); @@ -70,7 +75,7 @@ namespace Components return; } - if (Utils::ParseChallenge(data) != Discovery::Challenge) + if (Utils::ParseChallenge(data) != Challenge) { Logger::Print("Received discovery with invalid challenge from: {}\n", address.getString()); return; @@ -87,12 +92,12 @@ namespace Components void Discovery::preDestroy() { - Discovery::IsPerforming = false; - Discovery::IsTerminating = true; + IsPerforming = false; + IsTerminating = true; - if (Discovery::Thread.joinable()) + if (Thread.joinable()) { - Discovery::Thread.join(); + Thread.join(); } } } diff --git a/src/Components/Modules/Discovery.hpp b/src/Components/Modules/Discovery.hpp index 63f69b1b..2c00d284 100644 --- a/src/Components/Modules/Discovery.hpp +++ b/src/Components/Modules/Discovery.hpp @@ -16,5 +16,8 @@ namespace Components static bool IsPerforming; static std::thread Thread; static std::string Challenge; + + static Dvar::Var NetDiscoveryPortRangeMin; + static Dvar::Var NetDiscoveryPortRangeMax; }; } diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index 9283045e..a1fc0c8e 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -1,5 +1,6 @@ #include -#include "GSC/Script.hpp" +#include "Download.hpp" +#include "ServerInfo.hpp" #include @@ -10,19 +11,19 @@ namespace Components Download::ClientDownload Download::CLDownload; std::thread Download::ServerThread; - bool Download::Terminate; + volatile bool Download::Terminate; bool Download::ServerRunning; #pragma region Client void Download::InitiateMapDownload(const std::string& map, bool needPassword) { - Download::InitiateClientDownload(map, needPassword, true); + InitiateClientDownload(map, needPassword, true); } void Download::InitiateClientDownload(const std::string& mod, bool needPassword, bool map) { - if (Download::CLDownload.running) return; + if (CLDownload.running) return; Scheduler::Once([] { @@ -43,20 +44,20 @@ namespace Components return; } - Download::CLDownload.hashedPassword = Utils::String::DumpHex(Utils::Cryptography::SHA256::Compute(password), ""); + CLDownload.hashedPassword = Utils::String::DumpHex(Utils::Cryptography::SHA256::Compute(password), ""); } - Download::CLDownload.running = true; - Download::CLDownload.isMap = map; - Download::CLDownload.mod = mod; - Download::CLDownload.terminateThread = false; - Download::CLDownload.totalBytes = 0; - Download::CLDownload.lastTimeStamp = 0; - Download::CLDownload.downBytes = 0; - Download::CLDownload.timeStampBytes = 0; - Download::CLDownload.isPrivate = needPassword; - Download::CLDownload.target = Party::Target(); - Download::CLDownload.thread = std::thread(Download::ModDownloader, &Download::CLDownload); + CLDownload.running = true; + CLDownload.isMap = map; + CLDownload.mod = mod; + CLDownload.terminateThread = false; + CLDownload.totalBytes = 0; + CLDownload.lastTimeStamp = 0; + CLDownload.downBytes = 0; + CLDownload.timeStampBytes = 0; + CLDownload.isPrivate = needPassword; + CLDownload.target = Party::Target(); + CLDownload.thread = std::thread(ModDownloader, &CLDownload); } bool Download::ParseModList(ClientDownload* download, const std::string& list) @@ -93,7 +94,7 @@ namespace Components const auto name = file.at("name").get(); const auto size = file.at("size").get(); - Download::ClientDownload::File fileEntry; + ClientDownload::File fileEntry; fileEntry.name = name; fileEntry.hash = hash; fileEntry.size = size; @@ -120,7 +121,7 @@ namespace Components auto file = download->files[index]; - std::string path = download->mod + "/" + file.name; + auto path = download->mod + "/" + file.name; if (download->isMap) { path = "usermaps/" + path; @@ -128,8 +129,7 @@ namespace Components if (Utils::IO::FileExists(path)) { - std::string data = Utils::IO::ReadFile(path); - + auto data = Utils::IO::ReadFile(path); if (data.size() == file.size && Utils::String::DumpHex(Utils::Cryptography::SHA256::Compute(data), "") == file.hash) { download->totalBytes += file.size; @@ -186,7 +186,7 @@ namespace Components Logger::Print("Downloading from url {}\n", url); - Download::FileDownload fDownload; + FileDownload fDownload; fDownload.file = file; fDownload.index = index; fDownload.download = download; @@ -200,7 +200,7 @@ namespace Components fDownload.downloading = true; Utils::WebIO webIO; - webIO.setProgressCallback([&fDownload, &webIO](size_t bytes, size_t) + webIO.setProgressCallback([&fDownload, &webIO](std::size_t bytes, std::size_t) { if(!fDownload.downloading || fDownload.download->terminateThread) { @@ -208,7 +208,7 @@ namespace Components return; } - Download::DownloadProgress(&fDownload, bytes - fDownload.receivedBytes); + DownloadProgress(&fDownload, bytes - fDownload.receivedBytes); }); bool result = false; @@ -232,13 +232,13 @@ namespace Components void Download::ModDownloader(ClientDownload* download) { - if (!download) download = &Download::CLDownload; + if (!download) download = &CLDownload; - std::string host = "http://" + download->target.getString(); + const auto host = "http://" + download->target.getString(); - std::string listUrl = host + (download->isMap ? "/map" : "/list") + (download->isPrivate ? ("?password=" + download->hashedPassword) : ""); + const auto listUrl = host + (download->isMap ? "/map" : "/list") + (download->isPrivate ? ("?password=" + download->hashedPassword) : ""); - std::string list = Utils::WebIO("IW4x", listUrl).setTimeout(5000)->get(); + const auto list = Utils::WebIO("IW4x", listUrl).setTimeout(5000)->get(); if (list.empty()) { if (download->terminateThread) return; @@ -257,7 +257,7 @@ namespace Components if (download->terminateThread) return; - if (!Download::ParseModList(download, list)) + if (!ParseModList(download, list)) { if (download->terminateThread) return; @@ -278,15 +278,15 @@ namespace Components static std::string mod; mod = download->mod; - for (unsigned int i = 0; i < download->files.size(); ++i) + for (std::size_t i = 0; i < download->files.size(); ++i) { if (download->terminateThread) return; - if (!Download::DownloadFile(download, i)) + if (!DownloadFile(download, i)) { if (download->terminateThread) return; - mod = Utils::String::VA("Failed to download file: %s!", download->files[i].name.data()); + mod = std::format("Failed to download file: {}!", download->files[i].name); download->thread.detach(); download->clear(); @@ -321,7 +321,6 @@ namespace Components Scheduler::Once([] { Game::Dvar_SetString(*Game::fs_gameDirVar, mod.data()); - const_cast(*Game::fs_gameDirVar)->modified = true; mod.clear(); @@ -370,7 +369,7 @@ namespace Components }, Scheduler::Pipeline::CLIENT); } - int delta = Game::Sys_Milliseconds() - fDownload->download->lastTimeStamp; + auto delta = Game::Sys_Milliseconds() - fDownload->download->lastTimeStamp; if (delta > 300) { bool doFormat = fDownload->download->lastTimeStamp != 0; @@ -387,7 +386,7 @@ namespace Components if (doFormat) { - static size_t dlTsBytes; + static std::size_t dlTsBytes; static int dlDelta, dlTimeLeft; dlTimeLeft = timeLeft; dlDelta = delta; @@ -435,7 +434,7 @@ namespace Components { // Score and ping are irrelevant const auto* name = Game::PartyHost_GetMemberName(Game::g_lobbyData, i); - if (!name || *name == '\0') continue; + if (name == nullptr || *name == '\0') continue; playerInfo["name"] = name; } @@ -452,7 +451,7 @@ namespace Components static nlohmann::json jsonList; static auto handled = false; - const std::string fs_gameDirVar = (*Game::fs_gameDirVar)->current.string; + const std::filesystem::path fs_gameDirVar((*Game::fs_gameDirVar)->current.string); if (!fs_gameDirVar.empty() && !handled) { @@ -460,28 +459,30 @@ namespace Components std::vector fileList; - const auto path = Dvar::Var("fs_basepath").get() + "\\" + fs_gameDirVar; - auto list = FileSystem::GetSysFileList(path, "iwd", false); + const auto path = Dvar::Var("fs_basepath").get() / fs_gameDirVar; + auto list = FileSystem::GetSysFileList(path.generic_string(), "iwd", false); list.emplace_back("mod.ff"); for (const auto& file : list) { - std::string filename = path + "\\" + file; - if (file.find("_svr_") == std::string::npos) + auto filename = path / file; + if (file.find("_svr_") != std::string::npos) { - std::unordered_map jsonFileList; - std::string fileBuffer = Utils::IO::ReadFile(filename); - if (fileBuffer.empty()) - { - continue; - } - - jsonFileList["name"] = file; - jsonFileList["size"] = fileBuffer.size(); - jsonFileList["hash"] = Utils::Cryptography::SHA256::Compute(fileBuffer, true); - - fileList.emplace_back(jsonFileList); + continue; } + + std::unordered_map jsonFileList; + auto fileBuffer = Utils::IO::ReadFile(filename.generic_string()); + if (fileBuffer.empty()) + { + continue; + } + + jsonFileList["name"] = file; + jsonFileList["size"] = fileBuffer.size(); + jsonFileList["hash"] = Utils::Cryptography::SHA256::Compute(fileBuffer, true); + + fileList.emplace_back(jsonFileList); } jsonList = fileList; @@ -507,14 +508,15 @@ namespace Components mapNamePre = mapName; - const auto path = Dvar::Var("fs_basepath").get() + "\\usermaps\\" + mapName; + const std::filesystem::path basePath(Dvar::Var("fs_basepath").get()); + const auto path = basePath / "usermaps" / mapName; - for (auto i = 0; i < ARRAYSIZE(Maps::UserMapFiles); ++i) + for (std::size_t i = 0; i < ARRAYSIZE(Maps::UserMapFiles); ++i) { - const auto filename = path + "\\" + mapName + Maps::UserMapFiles[i]; - - std::map file; - std::string fileBuffer = Utils::IO::ReadFile(filename); + const auto filename = std::format("{}\\{}{}", path.generic_string(), mapName, Maps::UserMapFiles[i]); + + std::unordered_map file; + auto fileBuffer = Utils::IO::ReadFile(filename); if (fileBuffer.empty()) { continue; @@ -551,7 +553,7 @@ namespace Components auto mapName = (Party::IsInUserMapLobby() ? Dvar::Var("ui_mapname").get() : Maps::GetUserMap()->getName()); auto isValidFile = false; - for (auto i = 0; i < ARRAYSIZE(Maps::UserMapFiles); ++i) + for (std::size_t i = 0; i < ARRAYSIZE(Maps::UserMapFiles); ++i) { if (url == (mapName + Maps::UserMapFiles[i])) { @@ -672,6 +674,10 @@ namespace Components Download::Download() { + AssertSize(Game::va_info_t, 0x804); + AssertSize(jmp_buf, 0x40); + AssertSize(Game::TraceThreadInfo, 0x8); + if (Dedicated::IsEnabled()) { mg_mgr_init(&Mgr); @@ -685,11 +691,13 @@ namespace Components } }); - Download::ServerRunning = true; - Download::Terminate = false; - Download::ServerThread = std::thread([] + ServerRunning = true; + Terminate = false; + ServerThread = Utils::Thread::CreateNamedThread("Mongoose", [] { - while (!Download::Terminate) + Com_InitThreadData(); + + while (!Terminate) { mg_mgr_poll(&Mgr, 100); } @@ -706,7 +714,7 @@ namespace Components UIScript::Add("mod_download_cancel", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) { - Download::CLDownload.clear(); + CLDownload.clear(); }); } @@ -715,14 +723,11 @@ namespace Components Dvar::Register("sv_wwwDownload", false, Game::DVAR_NONE, "Set to true to enable downloading maps/mods from an external server."); Dvar::Register("sv_wwwBaseUrl", "", Game::DVAR_NONE, "Set to the base url for the external map download."); }, Scheduler::Pipeline::MAIN); - - Script::AddFunction("HttpGet", Script::ShowDeprecationWarning); - Script::AddFunction("HttpCancel", Script::ShowDeprecationWarning); } Download::~Download() { - if (Download::ServerRunning) + if (ServerRunning) { mg_mgr_free(&Mgr); } @@ -730,15 +735,15 @@ namespace Components void Download::preDestroy() { - Download::Terminate = true; - if (Download::ServerThread.joinable()) + Terminate = true; + if (ServerThread.joinable()) { - Download::ServerThread.join(); + ServerThread.join(); } if (!Dedicated::IsEnabled()) { - Download::CLDownload.clear(); + CLDownload.clear(); } } } diff --git a/src/Components/Modules/Download.hpp b/src/Components/Modules/Download.hpp index ac896e2a..84ccfd93 100644 --- a/src/Components/Modules/Download.hpp +++ b/src/Components/Modules/Download.hpp @@ -81,7 +81,7 @@ namespace Components static ClientDownload CLDownload; static std::thread ServerThread; - static bool Terminate; + static volatile bool Terminate; static bool ServerRunning; static void DownloadProgress(FileDownload* fDownload, std::size_t bytes); diff --git a/src/Components/Modules/Dvar.cpp b/src/Components/Modules/Dvar.cpp index a01a6314..b15e1721 100644 --- a/src/Components/Modules/Dvar.cpp +++ b/src/Components/Modules/Dvar.cpp @@ -186,24 +186,24 @@ namespace Components } } - template<> Dvar::Var Dvar::Register(const char* dvarName, bool value, Flag flag, const char* description) + template<> Dvar::Var Dvar::Register(const char* dvarName, bool value, std::uint16_t flag, const char* description) { - return Game::Dvar_RegisterBool(dvarName, value, flag.val, description); + return Game::Dvar_RegisterBool(dvarName, value, flag, description); } - template<> Dvar::Var Dvar::Register(const char* dvarName, const char* value, Flag flag, const char* description) + template<> Dvar::Var Dvar::Register(const char* dvarName, const char* value, std::uint16_t flag, const char* description) { - return Game::Dvar_RegisterString(dvarName, value, flag.val, description); + return Game::Dvar_RegisterString(dvarName, value, flag, description); } - template<> Dvar::Var Dvar::Register(const char* dvarName, int value, int min, int max, Flag flag, const char* description) + template<> Dvar::Var Dvar::Register(const char* dvarName, int value, int min, int max, std::uint16_t flag, const char* description) { - return Game::Dvar_RegisterInt(dvarName, value, min, max, flag.val, description); + return Game::Dvar_RegisterInt(dvarName, value, min, max, flag, description); } - template<> Dvar::Var Dvar::Register(const char* dvarName, float value, float min, float max, Flag flag, const char* description) + template<> Dvar::Var Dvar::Register(const char* dvarName, float value, float min, float max, std::uint16_t flag, const char* description) { - return Game::Dvar_RegisterFloat(dvarName, value, min, max, flag.val, description); + return Game::Dvar_RegisterFloat(dvarName, value, min, max, flag, description); } void Dvar::ResetDvarsValue() @@ -229,7 +229,8 @@ namespace Components // Don't perform any checks if name didn't change if (name == lastValidName) return; - std::string saneName = TextRenderer::StripAllTextIcons(TextRenderer::StripColors(Utils::String::Trim(name))); + Utils::String::Trim(name); + auto saneName = TextRenderer::StripAllTextIcons(TextRenderer::StripColors(name)); if (saneName.size() < 3 || (saneName[0] == '[' && saneName[1] == '{')) { Logger::PrintError(Game::CON_CHANNEL_ERROR, "Username '{}' is invalid. It must at least be 3 characters long and not appear empty!\n", name); @@ -240,7 +241,7 @@ namespace Components lastValidName = name; Friends::UpdateName(); } - }, Scheduler::CLIENT, 3s); // Don't need to do this every frame + }, Scheduler::Pipeline::CLIENT, 3s); // Don't need to do this every frame } std::string username = "Unknown Soldier"; @@ -306,10 +307,10 @@ namespace Components { if (!Utils::IO::FileExists(ArchiveDvarPath)) { - Utils::IO::WriteFile(ArchiveDvarPath, "// generated by IW4x, do not modify\n"); + Utils::IO::WriteFile(ArchiveDvarPath, "// generated by IW4x, do not modify\n", false); } - Utils::IO::WriteFile(ArchiveDvarPath, Utils::String::VA("seta %s \"%s\"\n", var->name, Game::Dvar_DisplayableValue(var)), true); + Utils::IO::WriteFile(ArchiveDvarPath, std::format("set {} \"{}\"\n", var->name, Game::Dvar_DisplayableValue(var)), true); } void Dvar::DvarSetFromStringByName_Stub(const char* dvarName, const char* value) @@ -320,7 +321,7 @@ namespace Components { if (AreArchiveDvarsProtected()) { - Logger::Print(Game::CON_CHANNEL_CONSOLEONLY, "Not allowing server to override saved dvar '{}'\n", dvarName); + Logger::Print(Game::CON_CHANNEL_CONSOLEONLY, "Not allowing server to override saved dvar '{}'\n", dvar->name); return; } @@ -330,6 +331,12 @@ namespace Components SaveArchiveDvar(dvar); } + if (dvar != nullptr && std::strcmp(dvar->name, "com_errorResolveCommand") == 0) + { + Logger::Print(Game::CON_CHANNEL_CONSOLEONLY, "Not allowing server to set '{}'\n", dvar->name); + return; + } + Utils::Hook::Call(0x4F52E0)(dvarName, value); } @@ -392,15 +399,29 @@ namespace Components // un-cheat cg_fovscale and add archive flags Utils::Hook::Xor(0x4F8E68, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE); + // un-cheat cg_fovMin and add archive flags + Utils::Hook::Xor(0x4F8E9D, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE); + // un-cheat cg_debugInfoCornerOffset and add archive flags Utils::Hook::Xor(0x4F8FC2, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE); - // remove archive flags for cg_hudchatposition + // un-cheat cg_drawGun + Utils::Hook::Set(0x4F8DC6, Game::DVAR_NONE); + + // un-cheat cg_draw2D + Utils::Hook::Set(0x4F8EEE, Game::DVAR_NONE); + + // remove archive flags for cg_hudChatPosition Utils::Hook::Xor(0x4F9992, Game::DVAR_ARCHIVE); // remove write protection from fs_game Utils::Hook::Xor(0x6431EA, Game::DVAR_INIT); + // cheat protect g_hardcore + Utils::Hook::Xor(0x5E374F, Game::DVAR_CHEAT); + Utils::Hook::Xor(0x4D3689, Game::DVAR_CHEAT); + Utils::Hook::Xor(0x4197C3, Game::DVAR_CHEAT); + // set cg_fov max to 160.0 // because that's the max on SP static float cg_Fov = 160.0f; diff --git a/src/Components/Modules/Dvar.hpp b/src/Components/Modules/Dvar.hpp index 7f97a842..a34154c6 100644 --- a/src/Components/Modules/Dvar.hpp +++ b/src/Components/Modules/Dvar.hpp @@ -5,15 +5,6 @@ namespace Components class Dvar : public Component { public: - class Flag - { - public: - Flag(Game::DvarFlags flag) : val(flag) {} - Flag(std::uint16_t flag) : Flag(static_cast(flag)) {} - - Game::DvarFlags val; - }; - class Var { public: @@ -44,8 +35,8 @@ namespace Components ~Dvar(); // Only strings and bools use this type of declaration - template static Var Register(const char* dvarName, T value, Flag flag, const char* description); - template static Var Register(const char* dvarName, T value, T min, T max, Flag flag, const char* description); + template static Var Register(const char* dvarName, T value, std::uint16_t flag, const char* description); + template static Var Register(const char* dvarName, T value, T min, T max, std::uint16_t flag, const char* description); static void ResetDvarsValue(); diff --git a/src/Components/Modules/Elevators.cpp b/src/Components/Modules/Elevators.cpp index dfd09a33..5c4d80da 100644 --- a/src/Components/Modules/Elevators.cpp +++ b/src/Components/Modules/Elevators.cpp @@ -1,4 +1,5 @@ #include +#include "Elevators.hpp" namespace Components { diff --git a/src/Components/Modules/Exception.cpp b/src/Components/Modules/Exception.cpp index 4234b081..f02c307b 100644 --- a/src/Components/Modules/Exception.cpp +++ b/src/Components/Modules/Exception.cpp @@ -1,24 +1,17 @@ #include +#include "Console.hpp" + +#include namespace Components { Utils::Hook Exception::SetFilterHook; int Exception::MiniDumpType; - __declspec(noreturn) void Exception::ErrorLongJmp(jmp_buf _Buf, int _Value) - { - if (!*reinterpret_cast(0x1AD7EB4)) - { - TerminateProcess(GetCurrentProcess(), 1337); - } - - longjmp(_Buf, _Value); - } - - __declspec(noreturn) void Exception::LongJmp(jmp_buf _Buf, int _Value) + __declspec(noreturn) void Exception::LongJmp_Internal_Stub(jmp_buf env, int status) { AssetHandler::ResetBypassState(); - longjmp(_Buf, _Value); + Game::longjmp_internal(env, status); } void Exception::SuspendProcess() @@ -74,7 +67,7 @@ namespace Components return; } - auto lock = GlobalLock(hMem); + auto* lock = GlobalLock(hMem); if (lock != nullptr) { std::memcpy(lock, error.data(), error.size() + 1); @@ -105,17 +98,15 @@ namespace Components errorStr = Utils::String::VA("Fatal error (0x%08X) at 0x%08X.\nCopy exception address to clipboard?", ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo->ExceptionRecord->ExceptionAddress); } - //Exception::SuspendProcess(); - // Message should be copied to the keyboard if no button is pressed if (MessageBoxA(nullptr, errorStr.data(), nullptr, MB_YESNO | MB_ICONERROR) == IDYES) { - Exception::CopyMessageToClipboard(Utils::String::VA("0x%08X", ExceptionInfo->ExceptionRecord->ExceptionAddress)); + CopyMessageToClipboard(Utils::String::VA("0x%08X", ExceptionInfo->ExceptionRecord->ExceptionAddress)); } if (Flags::HasFlag("bigminidumps")) { - Exception::SetMiniDumpType(true, false); + SetMiniDumpType(true, false); } // Current executable name @@ -125,30 +116,29 @@ namespace Components PathRemoveExtensionA(exeFileName); // Generate filename - char filenameFriendlyTime[MAX_PATH]; + char filenameFriendlyTime[MAX_PATH]{}; __time64_t time; tm ltime; _time64(&time); _localtime64_s(<ime, &time); strftime(filenameFriendlyTime, sizeof(filenameFriendlyTime) - 1, "%Y%m%d%H%M%S", <ime); - // Combine with queuedMinidumpsFolder - char filename[MAX_PATH] = { 0 }; - Utils::IO::CreateDir("minidumps"); + // Combine with queued MinidumpsFolder + char filename[MAX_PATH]{}; + CreateDirectoryA("minidumps", nullptr); PathCombineA(filename, "minidumps\\", Utils::String::VA("%s-" VERSION "-%s.dmp", exeFileName, filenameFriendlyTime)); - DWORD fileShare = FILE_SHARE_READ | FILE_SHARE_WRITE; + constexpr auto fileShare = FILE_SHARE_READ | FILE_SHARE_WRITE; HANDLE hFile = CreateFileA(filename, GENERIC_WRITE | GENERIC_READ, fileShare, nullptr, (fileShare & FILE_SHARE_WRITE) > 0 ? OPEN_ALWAYS : OPEN_EXISTING, NULL, nullptr); MINIDUMP_EXCEPTION_INFORMATION ex = { GetCurrentThreadId(), ExceptionInfo, FALSE }; - if (!MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, static_cast(Exception::MiniDumpType), &ex, nullptr, nullptr)) + if (!MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, static_cast(MiniDumpType), &ex, nullptr, nullptr)) { - MessageBoxA(nullptr, Utils::String::VA("There was an error creating the minidump (%s)! Hit OK to close the program.", Utils::GetLastWindowsError().data()), "Minidump Error", MB_OK | MB_ICONERROR); + MessageBoxA(nullptr, Utils::String::Format("There was an error creating the minidump ({})! Hit OK to close the program.", Utils::GetLastWindowsError()), "ERROR", MB_OK | MB_ICONERROR); OutputDebugStringA("Failed to create new minidump!"); Utils::OutputDebugLastError(); TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode); } - //if (ExceptionInfo->ExceptionRecord->ExceptionFlags == EXCEPTION_NONCONTINUABLE) { TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode); } @@ -156,54 +146,50 @@ namespace Components return EXCEPTION_CONTINUE_SEARCH; } - LPTOP_LEVEL_EXCEPTION_FILTER WINAPI Exception::SetUnhandledExceptionFilterStub(LPTOP_LEVEL_EXCEPTION_FILTER) - { - Exception::SetFilterHook.uninstall(); - LPTOP_LEVEL_EXCEPTION_FILTER retval = SetUnhandledExceptionFilter(&Exception::ExceptionFilter); - Exception::SetFilterHook.install(); - return retval; - } - - LPTOP_LEVEL_EXCEPTION_FILTER Exception::Hook() - { - return SetUnhandledExceptionFilter(&Exception::ExceptionFilter); - } - void Exception::SetMiniDumpType(bool codeseg, bool dataseg) { - Exception::MiniDumpType = MiniDumpIgnoreInaccessibleMemory; - Exception::MiniDumpType |= MiniDumpWithHandleData; - Exception::MiniDumpType |= MiniDumpScanMemory; - Exception::MiniDumpType |= MiniDumpWithProcessThreadData; - Exception::MiniDumpType |= MiniDumpWithFullMemoryInfo; - Exception::MiniDumpType |= MiniDumpWithThreadInfo; - //Exception::MiniDumpType |= MiniDumpWithModuleHeaders; + MiniDumpType = MiniDumpIgnoreInaccessibleMemory; + MiniDumpType |= MiniDumpWithHandleData; + MiniDumpType |= MiniDumpScanMemory; + MiniDumpType |= MiniDumpWithProcessThreadData; + MiniDumpType |= MiniDumpWithFullMemoryInfo; + MiniDumpType |= MiniDumpWithThreadInfo; if (codeseg) { - Exception::MiniDumpType |= MiniDumpWithCodeSegs; + MiniDumpType |= MiniDumpWithCodeSegs; } + if (dataseg) { - Exception::MiniDumpType |= MiniDumpWithDataSegs; + MiniDumpType |= MiniDumpWithDataSegs; } } + LPTOP_LEVEL_EXCEPTION_FILTER WINAPI Exception::SetUnhandledExceptionFilter_Stub(LPTOP_LEVEL_EXCEPTION_FILTER) + { + SetFilterHook.uninstall(); + LPTOP_LEVEL_EXCEPTION_FILTER result = ::SetUnhandledExceptionFilter(&ExceptionFilter); + SetFilterHook.install(); + return result; + } + Exception::Exception() { - Exception::SetMiniDumpType(Flags::HasFlag("bigminidumps"), Flags::HasFlag("reallybigminidumps")); + SetMiniDumpType(Flags::HasFlag("bigminidumps"), Flags::HasFlag("reallybigminidumps")); -#if !defined(DEBUG) || defined(FORCE_EXCEPTION_HANDLER) - Exception::SetFilterHook.initialize(SetUnhandledExceptionFilter, Exception::SetUnhandledExceptionFilterStub, HOOK_JUMP); - Exception::SetFilterHook.install(); + SetFilterHook.initialize(::SetUnhandledExceptionFilter, SetUnhandledExceptionFilter_Stub, HOOK_JUMP); + SetFilterHook.install(); - SetUnhandledExceptionFilter(&Exception::ExceptionFilter); -#endif + ::SetUnhandledExceptionFilter(&ExceptionFilter); - //Utils::Hook(0x4B241F, Exception::ErrorLongJmp, HOOK_CALL).install()->quick(); - Utils::Hook(0x6B8898, Exception::LongJmp, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4B241F, LongJmp_Internal_Stub, HOOK_CALL).install()->quick(); + Utils::Hook(0x61DB44, LongJmp_Internal_Stub, HOOK_CALL).install()->quick(); + Utils::Hook(0x61F17D, LongJmp_Internal_Stub, HOOK_CALL).install()->quick(); + Utils::Hook(0x61F248, LongJmp_Internal_Stub, HOOK_CALL).install()->quick(); + Utils::Hook(0x61F5E7, LongJmp_Internal_Stub, HOOK_CALL).install()->quick(); -#ifdef _DEBUG +#ifdef MAP_TEST Command::Add("mapTest", [](Command::Params* params) { Game::UI_UpdateArenas(); @@ -225,6 +211,6 @@ namespace Components Exception::~Exception() { - Exception::SetFilterHook.uninstall(); + SetFilterHook.uninstall(); } } diff --git a/src/Components/Modules/Exception.hpp b/src/Components/Modules/Exception.hpp index 55a04c1f..40b5e820 100644 --- a/src/Components/Modules/Exception.hpp +++ b/src/Components/Modules/Exception.hpp @@ -15,12 +15,12 @@ namespace Components private: static void SuspendProcess(); static LONG WINAPI ExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo); - static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilterStub(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter); - static __declspec(noreturn) void ErrorLongJmp(jmp_buf _Buf, int _Value); - static __declspec(noreturn) void LongJmp(jmp_buf _Buf, int _Value); + static __declspec(noreturn) void LongJmp_Internal_Stub(jmp_buf env, int status); static void CopyMessageToClipboard(const std::string& error); + static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter_Stub(LPTOP_LEVEL_EXCEPTION_FILTER); + static int MiniDumpType; static Utils::Hook SetFilterHook; }; diff --git a/src/Components/Modules/FastFiles.cpp b/src/Components/Modules/FastFiles.cpp index fedb64d3..c3b3cb01 100644 --- a/src/Components/Modules/FastFiles.cpp +++ b/src/Components/Modules/FastFiles.cpp @@ -235,10 +235,11 @@ namespace Components const char* dir = Dvar::Var("fs_basepath").get(); std::vector paths; - std::string modDir = Dvar::Var("fs_game").get(); + auto modDir = Dvar::Var("fs_game").get(); + if ((file == "mod"s || file == "mod.ff"s) && !modDir.empty()) { - paths.push_back(Utils::String::VA("%s\\", modDir.data())); + paths.push_back(std::format("{}\\", modDir)); } if (Utils::String::StartsWith(file, "mp_")) @@ -256,17 +257,17 @@ namespace Components Utils::String::Replace(zone, "_load", ""); } - if (Utils::IO::FileExists(Utils::String::VA("usermaps\\%s\\%s.ff", zone.data(), filename.data()))) + if (Utils::IO::FileExists(std::format("usermaps\\{}\\{}.ff", zone, filename))) { - return Utils::String::VA("usermaps\\%s\\", zone.data()); + return Utils::String::Format("usermaps\\{}\\", zone); } } Utils::Merge(&paths, FastFiles::ZonePaths); - for (auto &path : paths) + for (auto& path : paths) { - std::string absoluteFile = Utils::String::VA("%s\\%s%s", dir, path.data(), file); + auto absoluteFile = std::format("{}\\{}{}", dir, path, file); // No ".ff" appended, append it manually if (!Utils::String::EndsWith(absoluteFile, ".ff")) @@ -277,11 +278,11 @@ namespace Components // Check if FastFile exists if (Utils::IO::FileExists(absoluteFile)) { - return Utils::String::VA("%s", path.data()); + return Utils::String::Format("{}", path); } } - return Utils::String::VA("zone\\%s\\", Game::Win_GetLanguage()); + return Utils::String::Format("zone\\{}\\", Game::Win_GetLanguage()); } void FastFiles::AddZonePath(const std::string& path) diff --git a/src/Components/Modules/FileSystem.cpp b/src/Components/Modules/FileSystem.cpp index 473a38ac..46ae0ca3 100644 --- a/src/Components/Modules/FileSystem.cpp +++ b/src/Components/Modules/FileSystem.cpp @@ -78,19 +78,19 @@ namespace Components Utils::Memory::Allocator allocator; if (!this->exists()) return std::string(); - int position = Game::FS_FTell(this->handle); + const auto position = Game::FS_FTell(this->handle); this->seek(0, Game::FS_SEEK_SET); char* buffer = allocator.allocateArray(this->size); if (!this->read(buffer, this->size)) { this->seek(position, Game::FS_SEEK_SET); - return std::string(); + return {}; } this->seek(position, Game::FS_SEEK_SET); - return std::string(buffer, this->size); + return {buffer, static_cast(this->size)}; } bool FileSystem::FileReader::read(void* buffer, size_t _size) diff --git a/src/Components/Modules/FileSystem.hpp b/src/Components/Modules/FileSystem.hpp index 50573b40..bcdbd0e1 100644 --- a/src/Components/Modules/FileSystem.hpp +++ b/src/Components/Modules/FileSystem.hpp @@ -13,6 +13,11 @@ namespace Components virtual bool exists() = 0; virtual std::string getName() = 0; virtual std::string& getBuffer() = 0; + + virtual explicit operator bool() + { + return this->exists(); + } }; class File : public AbstractFile diff --git a/src/Components/Modules/Flags.cpp b/src/Components/Modules/Flags.cpp index 87400340..a41a942f 100644 --- a/src/Components/Modules/Flags.cpp +++ b/src/Components/Modules/Flags.cpp @@ -6,9 +6,9 @@ namespace Components bool Flags::HasFlag(const std::string& flag) { - Flags::ParseFlags(); + ParseFlags(); - for (const auto& entry : Flags::EnabledFlags) + for (const auto& entry : EnabledFlags) { if (Utils::String::ToLower(entry) == Utils::String::ToLower(flag)) { @@ -40,7 +40,7 @@ namespace Components if (wFlag[0] == L'-') { wFlag.erase(wFlag.begin()); - Flags::EnabledFlags.emplace_back(Utils::String::Convert(wFlag)); + EnabledFlags.emplace_back(Utils::String::Convert(wFlag)); } } @@ -48,9 +48,9 @@ namespace Components } // Workaround for wine - if (Utils::IsWineEnvironment() && Dedicated::IsEnabled() && !Flags::HasFlag("console") && !Flags::HasFlag("stdout")) + if (Utils::IsWineEnvironment() && Dedicated::IsEnabled() && !HasFlag("console") && !HasFlag("stdout")) { - Flags::EnabledFlags.emplace_back("stdout"); + EnabledFlags.emplace_back("stdout"); } } } diff --git a/src/Components/Modules/Friends.cpp b/src/Components/Modules/Friends.cpp index 29c3f77a..db4a9c27 100644 --- a/src/Components/Modules/Friends.cpp +++ b/src/Components/Modules/Friends.cpp @@ -1,4 +1,5 @@ #include +#include "UIFeeder.hpp" namespace Components { diff --git a/src/Components/Modules/GSC/GSC.cpp b/src/Components/Modules/GSC/GSC.cpp index 3970b014..0cf74f21 100644 --- a/src/Components/Modules/GSC/GSC.cpp +++ b/src/Components/Modules/GSC/GSC.cpp @@ -4,6 +4,7 @@ #include "IO.hpp" #include "Script.hpp" #include "ScriptExtension.hpp" +#include "ScriptPatches.hpp" #include "ScriptStorage.hpp" namespace Components @@ -14,6 +15,7 @@ namespace Components Loader::Register(new IO()); Loader::Register(new Script()); Loader::Register(new ScriptExtension()); + Loader::Register(new ScriptPatches()); Loader::Register(new ScriptStorage()); } } diff --git a/src/Components/Modules/GSC/IO.cpp b/src/Components/Modules/GSC/IO.cpp index e3a420db..5116d32c 100644 --- a/src/Components/Modules/GSC/IO.cpp +++ b/src/Components/Modules/GSC/IO.cpp @@ -126,7 +126,7 @@ namespace Components } } - const auto p = "scriptdata" / std::filesystem::path(path); + const auto p = "scriptdata"s / std::filesystem::path(path); const auto folder = p.parent_path().string(); const auto file = p.filename().string(); Game::Scr_AddInt(FileSystem::_DeleteFile(folder, file)); diff --git a/src/Components/Modules/GSC/Script.cpp b/src/Components/Modules/GSC/Script.cpp index 9de55849..e662ccf6 100644 --- a/src/Components/Modules/GSC/Script.cpp +++ b/src/Components/Modules/GSC/Script.cpp @@ -3,8 +3,8 @@ namespace Components { - std::unordered_map Script::CustomScrFunctions; - std::unordered_map Script::CustomScrMethods; + std::vector Script::CustomScrFunctions; + std::vector Script::CustomScrMethods; std::string Script::ScriptName; std::vector Script::ScriptNameStack; @@ -15,29 +15,21 @@ namespace Components std::unordered_map Script::ReplacedFunctions; const char* Script::ReplacedPos = nullptr; - std::vector Script::ScriptMainHandles; - std::vector Script::ScriptInitHandles; - - void Script::ShowDeprecationWarning() - { - Toast::Show("cardicon_gumby", "WARNING!", "You are using deprecated HttpGet/HttpCancel GSC function.", 2048); - Logger::Print(Game::CON_CHANNEL_SCRIPT, "*** DEPRECATION WARNING ***\n"); - Logger::PrintError(Game::CON_CHANNEL_ERROR, "Attempted to execute deprecated built-in HttpGet/HttpCancel! These functions have been deemed unsafe and are scheduled for removal. Please update your mod!\n"); - Logger::Print(Game::CON_CHANNEL_SCRIPT, "***************************\n"); - } + std::unordered_map Script::ScriptMainHandles; + std::unordered_map Script::ScriptInitHandles; void Script::FunctionError() { - const auto* funcName = Game::SL_ConvertToString(Script::FunctionName); + const auto* funcName = Game::SL_ConvertToString(FunctionName); Game::Scr_ShutdownAllocNode(); Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "\n"); Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "******* script compile error *******\n"); - Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "Error: unknown function {} in {}\n", funcName, Script::ScriptName); + Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "Error: unknown function {} in {}\n", funcName, ScriptName); Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "************************************\n"); - Logger::Error(Game::ERR_SCRIPT_DROP, "script compile error\nunknown function {}\n{}\n\n", funcName, Script::ScriptName); + Logger::Error(Game::ERR_SCRIPT_DROP, "script compile error\nunknown function {}\n{}\n\n", funcName, ScriptName); } __declspec(naked) void Script::StoreFunctionNameStub() @@ -45,7 +37,7 @@ namespace Components __asm { mov eax, [esp - 8h] - mov Script::FunctionName, ax + mov FunctionName, ax sub esp, 0Ch push 0 @@ -87,12 +79,12 @@ namespace Components void Script::StoreScriptName(const char* name) { - Script::ScriptNameStack.push_back(Script::ScriptName); - Script::ScriptName = name; + ScriptNameStack.push_back(ScriptName); + ScriptName = name; - if (!Utils::String::EndsWith(Script::ScriptName, ".gsc")) + if (!Utils::String::EndsWith(ScriptName, ".gsc")) { - Script::ScriptName.append(".gsc"); + ScriptName.append(".gsc"); } } @@ -105,7 +97,7 @@ namespace Components lea ecx, [esp + 30h] push ecx - call Script::StoreScriptName + call StoreScriptName add esp, 4h popad @@ -120,8 +112,8 @@ namespace Components void Script::RestoreScriptName() { - Script::ScriptName = Script::ScriptNameStack.back(); - Script::ScriptNameStack.pop_back(); + ScriptName = ScriptNameStack.back(); + ScriptNameStack.pop_back(); } __declspec(naked) void Script::RestoreScriptNameStub() @@ -129,7 +121,7 @@ namespace Components __asm { pushad - call Script::RestoreScriptName + call RestoreScriptName popad mov ds:1CDEAA8h, ebp @@ -205,17 +197,19 @@ namespace Components Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "\n"); Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "******* script compile error *******\n"); Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "Error: {} ", msgbuf); - Script::PrintSourcePos(Script::ScriptName.data(), offset); + PrintSourcePos(ScriptName.data(), offset); Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "************************************\n\n"); - Logger::Error(Game::ERR_SCRIPT_DROP, "script compile error\n{}\n{}\n(see console for actual details)\n", msgbuf, Script::ScriptName); + Logger::Error(Game::ERR_SCRIPT_DROP, "script compile error\n{}\n{}\n(see console for actual details)\n", msgbuf, ScriptName); } void Script::Scr_LoadGameType_Stub() { - for (const auto& handle : Script::ScriptMainHandles) + for (const auto& handle : ScriptMainHandles) { - const auto id = Game::Scr_ExecThread(handle, 0); + Logger::Print("Executing '{}::main'\n", handle.first.data()); + + const auto id = Game::Scr_ExecThread(handle.second, 0); Game::Scr_FreeThread(static_cast(id)); } @@ -224,9 +218,11 @@ namespace Components void Script::Scr_StartupGameType_Stub() { - for (const auto& handle : Script::ScriptInitHandles) + for (const auto& handle : ScriptInitHandles) { - const auto id = Game::Scr_ExecThread(handle, 0); + Logger::Print("Executing '{}::init'\n", handle.first.data()); + + const auto id = Game::Scr_ExecThread(handle.second, 0); Game::Scr_FreeThread(static_cast(id)); } @@ -237,8 +233,8 @@ namespace Components void Script::GScr_LoadGameTypeScript_Stub() { // Clear handles (from previous GSC loading session) - Script::ScriptMainHandles.clear(); - Script::ScriptInitHandles.clear(); + ScriptMainHandles.clear(); + ScriptInitHandles.clear(); char path[MAX_PATH]{}; @@ -262,18 +258,17 @@ namespace Components } Logger::Print("Script {}.gsc loaded successfully.\n", path); - Logger::Debug("Finding script handle main or init..."); const auto initHandle = Game::Scr_GetFunctionHandle(path, "init"); if (initHandle != 0) { - Script::ScriptInitHandles.push_back(initHandle); + ScriptInitHandles.insert_or_assign(path, initHandle); } const auto mainHandle = Game::Scr_GetFunctionHandle(path, "main"); if (mainHandle != 0) { - Script::ScriptMainHandles.push_back(mainHandle); + ScriptMainHandles.insert_or_assign(path, mainHandle); } // Allow scripts with no handles @@ -283,43 +278,74 @@ namespace Components Game::GScr_LoadGameTypeScript(); } - void Script::AddFunction(const std::string& name, Game::BuiltinFunction func, bool type) + void Script::AddFunction(const std::string& name, const Game::BuiltinFunction func, const bool type) { - Script::ScriptFunction toAdd; + ScriptFunction toAdd; toAdd.actionFunc = func; toAdd.type = type; + toAdd.aliases.push_back({Utils::String::ToLower(name)}); - CustomScrFunctions.insert_or_assign(Utils::String::ToLower(name), toAdd); + CustomScrFunctions.emplace_back(toAdd); } - void Script::AddMethod(const std::string& name, Game::BuiltinMethod func, bool type) + void Script::AddMethod(const std::string& name, const Game::BuiltinMethod func, const bool type) { - Script::ScriptMethod toAdd; + ScriptMethod toAdd; toAdd.actionFunc = func; toAdd.type = type; + toAdd.aliases.push_back({Utils::String::ToLower(name)}); - CustomScrMethods.insert_or_assign(Utils::String::ToLower(name), toAdd); + CustomScrMethods.emplace_back(toAdd); + } + + void Script::AddFuncMultiple(Game::BuiltinFunction func, bool type, scriptNames aliases) + { + ScriptFunction toAdd; + auto aliasesToAdd = Utils::String::ApplyToLower(aliases); + + toAdd.actionFunc = func; + toAdd.type = type; + toAdd.aliases = std::move(aliasesToAdd); + + CustomScrFunctions.emplace_back(toAdd); + } + + void Script::AddMethMultiple(Game::BuiltinMethod func, bool type, scriptNames aliases) + { + ScriptMethod toAdd; + auto aliasesToAdd = Utils::String::ApplyToLower(aliases); + + toAdd.actionFunc = func; + toAdd.type = type; + toAdd.aliases = std::move(aliasesToAdd); + + CustomScrMethods.emplace_back(toAdd); } Game::BuiltinFunction Script::BuiltIn_GetFunctionStub(const char** pName, int* type) { if (pName != nullptr) { - // If no function was found let's call game's function - if (const auto itr = Script::CustomScrFunctions.find(Utils::String::ToLower(*pName)); itr != Script::CustomScrFunctions.end()) + const auto name = Utils::String::ToLower(*pName); + for (const auto& func : CustomScrFunctions) { - *type = itr->second.type; - return itr->second.actionFunc; - } + if (Utils::Contains(&func.aliases, name)) + { + *type = func.type; + return func.actionFunc; + } + } } else { - for (const auto& [name, builtin] : Script::CustomScrFunctions) + for (const auto& func : CustomScrFunctions) { - Game::Scr_RegisterFunction(reinterpret_cast(builtin.actionFunc), name.data()); + const auto& name = func.aliases.at(0); + Game::Scr_RegisterFunction(reinterpret_cast(func.actionFunc), name.data()); } } + // If no function was found let's call game's function return Utils::Hook::Call(0x5FA2B0)(pName, type); // BuiltIn_GetFunction } @@ -327,27 +353,32 @@ namespace Components { if (pName != nullptr) { - // If no method was found let's call game's function - if (const auto itr = Script::CustomScrMethods.find(Utils::String::ToLower(*pName)); itr != Script::CustomScrMethods.end()) + const auto name = Utils::String::ToLower(*pName); + for (const auto& meth : CustomScrMethods) { - *type = itr->second.type; - return itr->second.actionFunc; + if (Utils::Contains(&meth.aliases, name)) + { + *type = meth.type; + return meth.actionFunc; + } } } else { - for (const auto& [name, builtin] : Script::CustomScrMethods) + for (const auto& meth : CustomScrMethods) { - Game::Scr_RegisterFunction(reinterpret_cast(builtin.actionFunc), name.data()); + const auto& name = meth.aliases.at(0); + Game::Scr_RegisterFunction(reinterpret_cast(meth.actionFunc), name.data()); } } + // If no method was found let's call game's function return Utils::Hook::Call(0x5FA360)(pName, type); // Player_GetMethod } void Script::StoreScriptBaseProgramNum() { - Script::ScriptBaseProgramNum.insert_or_assign(Utils::Hook::Get(0x1CFEEF8), Script::ScriptName); + ScriptBaseProgramNum.insert_or_assign(Utils::Hook::Get(0x1CFEEF8), ScriptName); } void Script::Scr_PrintPrevCodePos(int scriptPos) @@ -355,7 +386,7 @@ namespace Components auto bestCodePos = -1, nextCodePos = -1, offset = -1; std::string file; - for (const auto& [key, value] : Script::ScriptBaseProgramNum) + for (const auto& [key, value] : ScriptBaseProgramNum) { const auto codePos = key; @@ -388,7 +419,7 @@ namespace Components __asm { push esi - call Script::Scr_PrintPrevCodePos + call Scr_PrintPrevCodePos add esp, 4h pop esi @@ -402,7 +433,7 @@ namespace Components { // execute our hook pushad - call Script::StoreScriptBaseProgramNum + call StoreScriptBaseProgramNum popad // execute overwritten code caused by the jump hook @@ -452,9 +483,9 @@ namespace Components void Script::GetReplacedPos(const char* pos) { - if (Script::ReplacedFunctions.contains(pos)) + if (ReplacedFunctions.contains(pos)) { - Script::ReplacedPos = Script::ReplacedFunctions[pos]; + ReplacedPos = ReplacedFunctions[pos]; } } @@ -466,12 +497,12 @@ namespace Components return; } - if (Script::ReplacedFunctions.contains(what)) + if (ReplacedFunctions.contains(what)) { Logger::Warning(Game::CON_CHANNEL_SCRIPT, "ReplacedFunctions already contains codePosValue for a function\n"); } - Script::ReplacedFunctions[what] = with; + ReplacedFunctions[what] = with; } __declspec(naked) void Script::VMExecuteInternalStub() @@ -481,12 +512,12 @@ namespace Components pushad push edx - call Script::GetReplacedPos + call GetReplacedPos pop edx popad - cmp Script::ReplacedPos, 0 + cmp ReplacedPos, 0 jne SetPos movzx eax, byte ptr [edx] @@ -509,8 +540,8 @@ namespace Components retn SetPos: - mov edx, Script::ReplacedPos - mov Script::ReplacedPos, 0 + mov edx, ReplacedPos + mov ReplacedPos, 0 movzx eax, byte ptr [edx] inc edx @@ -521,7 +552,7 @@ namespace Components Game::client_t* Script::GetClient(const Game::gentity_t* ent) { - assert(ent != nullptr); + assert(ent); if (ent->client == nullptr) { @@ -529,7 +560,7 @@ namespace Components return nullptr; } - if (ent->s.number >= *Game::svs_clientCount) + if (static_cast(ent->s.number) >= Game::MAX_CLIENTS) { Game::Scr_ObjectError(Utils::String::VA("Entity %i is out of bounds", ent->s.number)); return nullptr; @@ -540,7 +571,7 @@ namespace Components void Script::AddFunctions() { - Script::AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(, ) + AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(, ) { if (Game::Scr_GetNumParam() != 2) { @@ -548,14 +579,14 @@ namespace Components return; } - const auto what = Script::GetCodePosForParam(0); - const auto with = Script::GetCodePosForParam(1); + const auto what = GetCodePosForParam(0); + const auto with = GetCodePosForParam(1); - Script::SetReplacedPos(what, with); + SetReplacedPos(what, with); }); // System time - Script::AddFunction("GetSystemMilliseconds", [] // gsc: GetSystemMilliseconds() + AddFunction("GetSystemMilliseconds", [] // gsc: GetSystemMilliseconds() { SYSTEMTIME time; GetSystemTime(&time); @@ -564,7 +595,7 @@ namespace Components }); // Executes command to the console - Script::AddFunction("Exec", [] // gsc: Exec() + AddFunction("Exec", [] // gsc: Exec() { const auto str = Game::Scr_GetString(0); @@ -578,7 +609,7 @@ namespace Components }); // Allow printing to the console even when developer is 0 - Script::AddFunction("PrintConsole", [] // gsc: PrintConsole() + AddFunction("PrintConsole", [] // gsc: PrintConsole() { for (std::size_t i = 0; i < Game::Scr_GetNumParam(); ++i) { @@ -595,9 +626,9 @@ namespace Components }); // PlayerCmd_AreControlsFrozen GSC function from Black Ops 2 - Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen(); + AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen(); { - const auto* ent = Game::GetPlayerEntity(entref); + const auto* ent = Scr_GetPlayerEntity(entref); Game::Scr_AddBool((ent->client->flags & Game::PLAYER_FLAG_FROZEN) != 0); }); @@ -605,34 +636,34 @@ namespace Components Script::Script() { - Utils::Hook(0x612DB0, Script::StoreFunctionNameStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x427E71, Script::RestoreScriptNameStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x427DBC, Script::StoreScriptNameStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x426C2D, Script::StoreScriptBaseProgramNumStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x42281B, Script::Scr_PrintPrevCodePosStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x612DB0, StoreFunctionNameStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x427E71, RestoreScriptNameStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x427DBC, StoreScriptNameStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x426C2D, StoreScriptBaseProgramNumStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x42281B, Scr_PrintPrevCodePosStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x61E3AD, Script::RuntimeError, HOOK_CALL).install()->quick(); - Utils::Hook(0x621976, Script::RuntimeError, HOOK_CALL).install()->quick(); - Utils::Hook(0x62246E, Script::RuntimeError, HOOK_CALL).install()->quick(); + Utils::Hook(0x61E3AD, RuntimeError, HOOK_CALL).install()->quick(); + Utils::Hook(0x621976, RuntimeError, HOOK_CALL).install()->quick(); + Utils::Hook(0x62246E, RuntimeError, HOOK_CALL).install()->quick(); // Skip check in GScr_CheckAllowedToSetPersistentData to prevent log spam in RuntimeError. // On IW5 the function is entirely nullsubbed - Utils::Hook::Set(0x5F8DBF, 0xEB); + Utils::Hook::Set(0x5F8DBF, 0xEB); - Utils::Hook(0x612E8D, Script::FunctionError, HOOK_CALL).install()->quick(); - Utils::Hook(0x612EA2, Script::FunctionError, HOOK_CALL).install()->quick(); - Utils::Hook(0x434260, Script::CompileError, HOOK_JUMP).install()->quick(); + Utils::Hook(0x612E8D, FunctionError, HOOK_CALL).install()->quick(); + Utils::Hook(0x612EA2, FunctionError, HOOK_CALL).install()->quick(); + Utils::Hook(0x434260, CompileError, HOOK_JUMP).install()->quick(); - Utils::Hook(0x48EFFE, Script::Scr_LoadGameType_Stub, HOOK_CALL).install()->quick(); - Utils::Hook(0x48F008, Script::Scr_StartupGameType_Stub, HOOK_CALL).install()->quick(); - Utils::Hook(0x45D44A, Script::GScr_LoadGameTypeScript_Stub, HOOK_CALL).install()->quick(); + Utils::Hook(0x48EFFE, Scr_LoadGameType_Stub, HOOK_CALL).install()->quick(); + Utils::Hook(0x48F008, Scr_StartupGameType_Stub, HOOK_CALL).install()->quick(); + Utils::Hook(0x45D44A, GScr_LoadGameTypeScript_Stub, HOOK_CALL).install()->quick(); // Fetch custom functions - Utils::Hook(0x44E72E, Script::BuiltIn_GetFunctionStub, HOOK_CALL).install()->quick(); // Scr_GetFunction - Utils::Hook(0x4EC8DD, Script::BuiltIn_GetMethodStub, HOOK_CALL).install()->quick(); // Scr_GetMethod + Utils::Hook(0x44E72E, BuiltIn_GetFunctionStub, HOOK_CALL).install()->quick(); // Scr_GetFunction + Utils::Hook(0x4EC8DD, BuiltIn_GetMethodStub, HOOK_CALL).install()->quick(); // Scr_GetMethod - Utils::Hook(0x5F41A3, Script::SetExpFogStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x5F41A3, SetExpFogStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x61E92E, Script::VMExecuteInternalStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x61E92E, VMExecuteInternalStub, HOOK_JUMP).install()->quick(); Utils::Hook::Nop(0x61E933, 1); Scheduler::Loop([] @@ -642,9 +673,9 @@ namespace Components const auto nowMs = Game::Sys_Milliseconds(); - if (Script::LastFrameTime != -1) + if (LastFrameTime != -1) { - const auto timeTaken = (nowMs - Script::LastFrameTime) * static_cast((*Game::com_timescale)->current.value); + const auto timeTaken = (nowMs - LastFrameTime) * static_cast((*Game::com_timescale)->current.value); if (timeTaken >= 500) { @@ -652,11 +683,11 @@ namespace Components } } - Script::LastFrameTime = nowMs; + LastFrameTime = nowMs; }, Scheduler::Pipeline::SERVER); #ifdef _DEBUG - Script::AddFunction("DebugBox", [] + AddFunction("DebugBox", [] { const auto* message = Game::Scr_GetString(0); @@ -666,14 +697,14 @@ namespace Components } MessageBoxA(nullptr, message, "DEBUG", MB_OK); - }, 1); + }, true); #endif - Script::AddFunctions(); + AddFunctions(); Events::OnVMShutdown([] { - Script::ReplacedFunctions.clear(); + ReplacedFunctions.clear(); }); } } diff --git a/src/Components/Modules/GSC/Script.hpp b/src/Components/Modules/GSC/Script.hpp index d80f952e..c3a27a00 100644 --- a/src/Components/Modules/GSC/Script.hpp +++ b/src/Components/Modules/GSC/Script.hpp @@ -7,30 +7,55 @@ namespace Components public: Script(); + using scriptNames = std::vector; static void AddFunction(const std::string& name, Game::BuiltinFunction func, bool type = false); static void AddMethod(const std::string& name, Game::BuiltinMethod func, bool type = false); + static void AddFuncMultiple(Game::BuiltinFunction func, bool type, scriptNames); + static void AddMethMultiple(Game::BuiltinMethod func, bool type, scriptNames); + static Game::client_t* GetClient(const Game::gentity_t* gentity); static const char* GetCodePosForParam(int index); - static void ShowDeprecationWarning(); + // Probably a macro 'originally' but this is fine + static Game::gentity_s* Scr_GetPlayerEntity(Game::scr_entref_t entref) + { + if (entref.classnum != 0) + { + Game::Scr_ObjectError("not an entity"); + return nullptr; + } + + assert(entref.entnum < Game::MAX_GENTITIES); + + auto* ent = &Game::g_entities[entref.entnum]; + if (ent->client == nullptr) + { + Game::Scr_ObjectError(Utils::String::VA("entity %i is not a player", entref.entnum)); + return nullptr; + } + + return ent; + } private: struct ScriptFunction { Game::BuiltinFunction actionFunc; bool type; + scriptNames aliases; }; struct ScriptMethod { Game::BuiltinMethod actionFunc; bool type; + scriptNames aliases; }; - static std::unordered_map CustomScrFunctions; - static std::unordered_map CustomScrMethods; + static std::vector CustomScrFunctions; + static std::vector CustomScrMethods; static std::string ScriptName; static std::vector ScriptNameStack; @@ -38,8 +63,8 @@ namespace Components static std::unordered_map ScriptBaseProgramNum; static int LastFrameTime; - static std::vector ScriptMainHandles; - static std::vector ScriptInitHandles; + static std::unordered_map ScriptMainHandles; + static std::unordered_map ScriptInitHandles; static std::unordered_map ReplacedFunctions; static const char* ReplacedPos; diff --git a/src/Components/Modules/GSC/ScriptExtension.cpp b/src/Components/Modules/GSC/ScriptExtension.cpp index 94e08aa3..0b30a4f0 100644 --- a/src/Components/Modules/GSC/ScriptExtension.cpp +++ b/src/Components/Modules/GSC/ScriptExtension.cpp @@ -273,30 +273,6 @@ namespace Components }); } - void ScriptExtension::Scr_TableLookupIStringByRow() - { - if (Game::Scr_GetNumParam() < 3) - { - Game::Scr_Error("USAGE: tableLookupIStringByRow( filename, rowNum, returnValueColumnNum )\n"); - return; - } - - const auto* fileName = Game::Scr_GetString(0); - const auto rowNum = Game::Scr_GetInt(1); - const auto returnValueColumnNum = Game::Scr_GetInt(2); - - const auto* table = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_STRINGTABLE, fileName).stringTable; - - if (table == nullptr) - { - Game::Scr_ParamError(0, Utils::String::VA("%s does not exist\n", fileName)); - return; - } - - const auto* value = Game::StringTable_GetColumnValueForRow(table, rowNum, returnValueColumnNum); - Game::Scr_AddIString(value); - } - void ScriptExtension::AddEntityFields() { AddEntityField("entityflags", Game::F_INT, @@ -330,16 +306,10 @@ namespace Components AddEntityFields(); AddClientFields(); - // Correct builtin function pointer - Utils::Hook::Set(0x79A90C, Scr_TableLookupIStringByRow); - Utils::Hook(0x4EC721, GScr_AddFieldsForEntityStub, HOOK_CALL).install()->quick(); // GScr_AddFieldsForEntity Utils::Hook(0x41BED2, Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue Utils::Hook(0x5FBF01, Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField Utils::Hook(0x4FF413, Scr_GetEntityFieldStub, HOOK_CALL).install()->quick(); // Scr_GetObjectField - - // Fix format string in Scr_RandomFloatRange - Utils::Hook::Set(0x5F10C6, "Scr_RandomFloatRange parms: %f %f "); } } diff --git a/src/Components/Modules/GSC/ScriptExtension.hpp b/src/Components/Modules/GSC/ScriptExtension.hpp index cc6cb694..c153f24c 100644 --- a/src/Components/Modules/GSC/ScriptExtension.hpp +++ b/src/Components/Modules/GSC/ScriptExtension.hpp @@ -27,6 +27,5 @@ namespace Components static void AddMethods(); static void AddEntityFields(); static void AddClientFields(); - static void Scr_TableLookupIStringByRow(); }; } diff --git a/src/Components/Modules/GSC/ScriptPatches.cpp b/src/Components/Modules/GSC/ScriptPatches.cpp new file mode 100644 index 00000000..3c2b6197 --- /dev/null +++ b/src/Components/Modules/GSC/ScriptPatches.cpp @@ -0,0 +1,66 @@ +#include +#include "ScriptPatches.hpp" +#include "Script.hpp" + +using namespace Utils::String; + +namespace Components +{ + constexpr auto offset = 511; + + Game::game_hudelem_s* ScriptPatches::HECmd_GetHudElem(Game::scr_entref_t entref) + { + if (entref.classnum != 1) + { + Game::Scr_ObjectError("not a hud element"); + return nullptr; + } + + assert(entref.entnum < 1024); + return &Game::g_hudelems[entref.entnum]; + } + + void ScriptPatches::Scr_TableLookupIStringByRow_Hk() + { + if (Game::Scr_GetNumParam() < 3) + { + Game::Scr_Error("USAGE: tableLookupIStringByRow( filename, rowNum, returnValueColumnNum )\n"); + return; + } + + const auto* fileName = Game::Scr_GetString(0); + const auto rowNum = Game::Scr_GetInt(1); + const auto returnValueColumnNum = Game::Scr_GetInt(2); + + const auto* table = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_STRINGTABLE, fileName).stringTable; + + if (table == nullptr) + { + Game::Scr_ParamError(0, Utils::String::VA("%s does not exist\n", fileName)); + return; + } + + const auto* value = Game::StringTable_GetColumnValueForRow(table, rowNum, returnValueColumnNum); + Game::Scr_AddIString(value); + } + + ScriptPatches::ScriptPatches() + { + // Fix format string in Scr_RandomFloatRange + Utils::Hook::Set(0x5F10C6, "Scr_RandomFloatRange parms: %f %f "); + + // Correct builtin function pointer + Utils::Hook::Set(0x79A90C, Scr_TableLookupIStringByRow_Hk); + + Script::AddMethod("ClearHudText", [](Game::scr_entref_t entref) -> void + { + auto* hud = HECmd_GetHudElem(entref); + + // Frees config string up + if ((hud->elem).text) + { + Game::SV_SetConfigstring((hud->elem).text + offset, nullptr); + } + }); + } +} diff --git a/src/Components/Modules/GSC/ScriptPatches.hpp b/src/Components/Modules/GSC/ScriptPatches.hpp new file mode 100644 index 00000000..6d69a2fd --- /dev/null +++ b/src/Components/Modules/GSC/ScriptPatches.hpp @@ -0,0 +1,15 @@ +#pragma once + +namespace Components +{ + class ScriptPatches : public Component + { + public: + ScriptPatches(); + + private: + static Game::game_hudelem_s* HECmd_GetHudElem(Game::scr_entref_t entref); + + static void Scr_TableLookupIStringByRow_Hk(); + }; +} diff --git a/src/Components/Modules/GSC/ScriptStorage.cpp b/src/Components/Modules/GSC/ScriptStorage.cpp index 95879747..e89425d7 100644 --- a/src/Components/Modules/GSC/ScriptStorage.cpp +++ b/src/Components/Modules/GSC/ScriptStorage.cpp @@ -86,6 +86,27 @@ namespace Components FileSystem::FileWriter("scriptdata/scriptstorage.json").write(json.dump()); }); + Script::AddFunction("StorageLoad", [] // gsc: StorageLoad(); + { + FileSystem::File storageFile("scriptdata/scriptstorage.json"); + if (!storageFile.exists()) + { + return; + } + + const auto& buffer = storageFile.getBuffer(); + try + { + const nlohmann::json storageDef = nlohmann::json::parse(buffer); + const auto& newData = storageDef.get>(); + Data.insert(newData.begin(), newData.end()); + } + catch (const std::exception& ex) + { + Logger::PrintError(Game::CON_CHANNEL_ERROR, "Json Parse Error: {}. File {} is invalid\n", ex.what(), storageFile.getName()); + } + }); + Script::AddFunction("StorageClear", [] // gsc: StorageClear(); { Data.clear(); diff --git a/src/Components/Modules/Gamepad.cpp b/src/Components/Modules/Gamepad.cpp index 4942ff58..edb34245 100644 --- a/src/Components/Modules/Gamepad.cpp +++ b/src/Components/Modules/Gamepad.cpp @@ -1,4 +1,6 @@ #include +#include "Gamepad.hpp" +#include "RawMouse.hpp" namespace Components { @@ -749,7 +751,7 @@ namespace Components -yawRight }; - Game::cgArray[0].selectedLocationAngle = Game::AngleNormalize360(Game::vectoyaw(&vec)); + Game::cgArray[0].selectedLocationAngle = Game::AngleNormalize360(Game::vectoryaw(&vec)); Game::cgArray[0].selectedAngleLocation[0] = Game::cgArray[0].selectedLocation[0]; Game::cgArray[0].selectedAngleLocation[1] = Game::cgArray[0].selectedLocation[1]; } @@ -788,7 +790,7 @@ namespace Components bool Gamepad::CG_ShouldUpdateViewAngles(const int localClientNum) { - return !Game::Key_IsKeyCatcherActive(localClientNum, Game::KEYCATCH_MASK_ANY) || Game::UI_GetActiveMenu(localClientNum) == Game::UIMENU_SCOREBOARD; + return !Game::Key_IsCatcherActive(localClientNum, Game::KEYCATCH_MASK_ANY) || Game::UI_GetActiveMenu(localClientNum) == Game::UIMENU_SCOREBOARD; } float Gamepad::CL_GamepadAxisValue(const int gamePadIndex, const Game::GamepadVirtualAxis virtualAxis) @@ -1096,7 +1098,7 @@ namespace Components auto& gamePadGlobal = gamePadGlobals[gamePadIndex]; - if (Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_UI)) + if (Game::Key_IsCatcherActive(gamePadIndex, Game::KEYCATCH_UI)) { const int scrollDelayFirst = gpad_menu_scroll_delay_first.get(); const int scrollDelayRest = gpad_menu_scroll_delay_rest.get(); @@ -1149,7 +1151,7 @@ namespace Components if (pressedOrUpdated && CL_CheckForIgnoreDueToRepeat(gamePadIndex, key, keyState.keys[key].repeats, time)) return; - if (Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_LOCATION_SELECTION) && pressedOrUpdated) + if (Game::Key_IsCatcherActive(gamePadIndex, Game::KEYCATCH_LOCATION_SELECTION) && pressedOrUpdated) { if (key == Game::K_BUTTON_B || keyState.keys[key].binding && strcmp(keyState.keys[key].binding, "+actionslot 4") == 0) { @@ -1176,7 +1178,7 @@ namespace Components char cmd[1024]; if (pressedOrUpdated) { - if (Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_UI)) + if (Game::Key_IsCatcherActive(gamePadIndex, Game::KEYCATCH_UI)) { UI_GamepadKeyEvent(gamePadIndex, key, pressedOrUpdated); return; @@ -1203,7 +1205,7 @@ namespace Components Game::Cbuf_AddText(gamePadIndex, cmd); } - if (Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_UI)) + if (Game::Key_IsCatcherActive(gamePadIndex, Game::KEYCATCH_UI)) { UI_GamepadKeyEvent(gamePadIndex, key, pressedOrUpdated); } @@ -1218,7 +1220,7 @@ namespace Components gamePad.inUse = true; gpad_in_use.setRaw(true); - if (Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_UI)) + if (Game::Key_IsCatcherActive(gamePadIndex, Game::KEYCATCH_UI)) CL_GamepadResetMenuScrollTime(gamePadIndex, key, buttonEvent == Game::GPAD_BUTTON_PRESSED, time); diff --git a/src/Components/Modules/Gametypes.cpp b/src/Components/Modules/Gametypes.cpp deleted file mode 100644 index 93c580ba..00000000 --- a/src/Components/Modules/Gametypes.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include - -namespace Components -{ - unsigned int Gametypes::GetGametypeCount() - { - return *Game::gameTypeCount; - } - - const char* Gametypes::GetGametypeText(unsigned int index, int) - { - if (static_cast(*Game::gameTypeCount) > index) - { - return Game::SEH_StringEd_GetString(Game::gameTypes[index].uiName); - } - - return ""; - } - - void Gametypes::SelectGametype(unsigned int index) - { - if (!*Game::gameTypeCount) return; - if (static_cast(*Game::gameTypeCount) <= index) index = 0; - - std::string gametype = Game::gameTypes[index].gameType; - - Dvar::Var("ui_gametype").set(gametype); - //Dvar::Var("g_gametype").set(gametype); - } - - void* Gametypes::BuildGametypeList(const char*, void* buffer, size_t size) - { - std::vector gametypes; - - auto pushGametype = [&](std::string gametype) - { - auto pos = gametype.find_last_of("/\\"); - if (pos != std::string::npos) - { - gametype = gametype.substr(pos + 1); - } - - if (Utils::String::EndsWith(gametype, ".txt")) - { - gametype = gametype.substr(0, gametype.size() - 4); - } - - // No need for that :) - if (gametype == "_gametypes") return; - - if (std::find(gametypes.begin(), gametypes.end(), gametype) == gametypes.end()) - { - gametypes.push_back(gametype); - } - }; - - // Get the gametypes we can find in the filesystem - std::vector rawGametypes = FileSystem::GetFileList("maps/mp/gametypes/", "txt"); - - // Get the gametypes we can find in the database - Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_RAWFILE, [](Game::XAssetHeader header, void* data) - { - std::string name = header.rawfile->name; - std::vector* rawGametypes = reinterpret_cast*>(data); - - if (Utils::String::StartsWith(name, "maps/mp/gametypes/") && Utils::String::EndsWith(name, ".txt")) - { - if (std::count(name.begin(), name.end(), '/') == 3 && std::count(name.begin(), name.end(), '\\') == 0) - { - rawGametypes->push_back(name); - } - } - - }, &rawGametypes, false); - - std::for_each(rawGametypes.begin(), rawGametypes.end(), pushGametype); - - std::string data; - for (auto& gametype : gametypes) - { - if (Game::Scr_AddSourceBuffer(nullptr, Utils::String::VA("maps/mp/gametypes/%s.txt", gametype.data()), nullptr, false)) - { - data.append(gametype); - data.append("\r\n"); - } - } - - // Copy to the actual buffer - std::memcpy(buffer, data.data(), std::min(size, data.size() + 1)); - - return (data.empty() ? nullptr : buffer); - } - - Gametypes::Gametypes() - { - UIFeeder::Add(29.0f, Gametypes::GetGametypeCount, Gametypes::GetGametypeText, Gametypes::SelectGametype); - - // Dynamically grab gametypes - Utils::Hook(0x5FA46C, Gametypes::BuildGametypeList, HOOK_CALL).install()->quick(); // Scr_UpdateGameTypeList - Utils::Hook(0x632155, Gametypes::BuildGametypeList, HOOK_CALL).install()->quick(); // UI_UpdateGameTypesList - } -} diff --git a/src/Components/Modules/Gametypes.hpp b/src/Components/Modules/Gametypes.hpp deleted file mode 100644 index 5576512e..00000000 --- a/src/Components/Modules/Gametypes.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -namespace Components -{ - class Gametypes : public Component - { - public: - Gametypes(); - - private: - static unsigned int GetGametypeCount(); - static const char* GetGametypeText(unsigned int index, int column); - static void SelectGametype(unsigned int index); - - static void* BuildGametypeList(const char* file, void* buffer, size_t size); - }; -} diff --git a/src/Components/Modules/Lean.cpp b/src/Components/Modules/Lean.cpp index 90050a73..29b1403e 100644 --- a/src/Components/Modules/Lean.cpp +++ b/src/Components/Modules/Lean.cpp @@ -1,4 +1,5 @@ #include +#include "Lean.hpp" namespace Components { diff --git a/src/Components/Modules/Logger.cpp b/src/Components/Modules/Logger.cpp index 7d24c198..c1e446b7 100644 --- a/src/Components/Modules/Logger.cpp +++ b/src/Components/Modules/Logger.cpp @@ -1,4 +1,5 @@ #include +#include "Console.hpp" namespace Components { @@ -6,7 +7,7 @@ namespace Components std::vector Logger::MessageQueue; std::vector Logger::LoggingAddresses[2]; - std::function Logger::PipeCallback; + void(*Logger::PipeCallback)(const std::string&) = nullptr;; bool Logger::IsConsoleReady() { @@ -19,10 +20,10 @@ namespace Components va_list va; va_start(va, message); - _vsnprintf_s(buf, _TRUNCATE, message, va); + vsnprintf_s(buf, _TRUNCATE, message, va); va_end(va); - Logger::MessagePrint(channel, {buf}); + MessagePrint(channel, {buf}); } void Logger::MessagePrint(const int channel, const std::string& msg) @@ -42,14 +43,14 @@ namespace Components return; } - if (!Logger::IsConsoleReady()) + if (!IsConsoleReady()) { OutputDebugStringA(out.data()); } if (!Game::Sys_IsMainThread()) { - Logger::EnqueueMessage(msg); + EnqueueMessage(msg); } else { @@ -57,7 +58,7 @@ namespace Components } } - void Logger::DebugInternal(std::string_view fmt, std::format_args&& args, [[maybe_unused]] const std::source_location& loc) + void Logger::DebugInternal(const std::string_view& fmt, std::format_args&& args, [[maybe_unused]] const std::source_location& loc) { #ifdef LOGGER_TRACE const auto msg = std::vformat(fmt, args); @@ -67,17 +68,17 @@ namespace Components const auto out = std::format("^2{}\n", msg); #endif - Logger::MessagePrint(Game::CON_CHANNEL_DONT_FILTER, out); + MessagePrint(Game::CON_CHANNEL_DONT_FILTER, out); } - void Logger::PrintInternal(int channel, std::string_view fmt, std::format_args&& args) + void Logger::PrintInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args) { const auto msg = std::vformat(fmt, args); - Logger::MessagePrint(channel, msg); + MessagePrint(channel, msg); } - void Logger::ErrorInternal(const Game::errorParm_t error, const std::string_view fmt, std::format_args&& args) + void Logger::ErrorInternal(const Game::errorParm_t error, const std::string_view& fmt, std::format_args&& args) { #ifdef _DEBUG if (IsDebuggerPresent()) __debugbreak(); @@ -87,53 +88,53 @@ namespace Components Game::Com_Error(error, "%s", msg.data()); } - void Logger::PrintErrorInternal(int channel, std::string_view fmt, std::format_args&& args) + void Logger::PrintErrorInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args) { const auto msg = "^1Error: " + std::vformat(fmt, args); ++(*Game::com_errorPrintsCount); - Logger::MessagePrint(channel, msg); + MessagePrint(channel, msg); - if (*Game::cls_uiStarted != 0 && (*Game::com_fixedConsolePosition == 0)) + if (Game::cls->uiStarted != 0 && (*Game::com_fixedConsolePosition == 0)) { Game::CL_ConsoleFixPosition(); } } - void Logger::WarningInternal(int channel, std::string_view fmt, std::format_args&& args) + void Logger::WarningInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args) { const auto msg = "^3" + std::vformat(fmt, args); - Logger::MessagePrint(channel, msg); + MessagePrint(channel, msg); } void Logger::Frame() { - std::unique_lock _(Logger::MessageMutex); + std::unique_lock _(MessageMutex); - for (auto i = Logger::MessageQueue.begin(); i != Logger::MessageQueue.end();) + for (auto i = MessageQueue.begin(); i != MessageQueue.end();) { Game::Com_PrintMessage(Game::CON_CHANNEL_DONT_FILTER, i->data(), 0); - if (!Logger::IsConsoleReady()) + if (!IsConsoleReady()) { OutputDebugStringA(i->data()); } - i = Logger::MessageQueue.erase(i); + i = MessageQueue.erase(i); } } - void Logger::PipeOutput(const std::function& callback) + void Logger::PipeOutput(void(*callback)(const std::string&)) { - Logger::PipeCallback = callback; + PipeCallback = callback; } void Logger::PrintMessagePipe(const char* data) { - if (Logger::PipeCallback) + if (PipeCallback) { - Logger::PipeCallback(data); + PipeCallback(data); } } @@ -144,7 +145,7 @@ namespace Components return; } - for (const auto& addr : Logger::LoggingAddresses[gLog & 1]) + for (const auto& addr : LoggingAddresses[gLog & 1]) { Network::SendCommand(addr, "print", data); } @@ -169,20 +170,20 @@ namespace Components } // Allow the network log to run even if logFile was not opened - Logger::NetworkLog(string, true); + NetworkLog(string, true); } __declspec(naked) void Logger::PrintMessage_Stub() { __asm { - mov eax, Logger::PipeCallback + mov eax, PipeCallback test eax, eax jz returnPrint pushad push [esp + 28h] - call Logger::PrintMessagePipe + call PrintMessagePipe add esp, 4h popad retn @@ -191,7 +192,7 @@ namespace Components pushad push 0 push [esp + 2Ch] - call Logger::NetworkLog + call NetworkLog add esp, 8h popad @@ -205,8 +206,8 @@ namespace Components void Logger::EnqueueMessage(const std::string& message) { - std::unique_lock _(Logger::MessageMutex); - Logger::MessageQueue.push_back(message); + std::unique_lock _(MessageMutex); + MessageQueue.push_back(message); } void Logger::RedirectOSPath(const char* file, char* folder) @@ -232,7 +233,7 @@ namespace Components push [esp + 28h] push [esp + 30h] - call Logger::RedirectOSPath + call RedirectOSPath add esp, 8h @@ -256,9 +257,9 @@ namespace Components Network::Address addr(params->get(1)); - if (std::find(Logger::LoggingAddresses[0].begin(), Logger::LoggingAddresses[0].end(), addr) == Logger::LoggingAddresses[0].end()) + if (std::find(LoggingAddresses[0].begin(), LoggingAddresses[0].end(), addr) == LoggingAddresses[0].end()) { - Logger::LoggingAddresses[0].push_back(addr); + LoggingAddresses[0].push_back(addr); } }); @@ -266,38 +267,38 @@ namespace Components { if (params->size() < 2) return; - const auto num = atoi(params->get(1)); - if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast(num) < Logger::LoggingAddresses[0].size()) + const auto num = std::atoi(params->get(1)); + if (!std::strcmp(Utils::String::VA("%i", num), params->get(1)) && static_cast(num) < LoggingAddresses[0].size()) { auto addr = Logger::LoggingAddresses[0].begin() + num; - Logger::Print("Address {} removed\n", addr->getString()); - Logger::LoggingAddresses[0].erase(addr); + Print("Address {} removed\n", addr->getString()); + LoggingAddresses[0].erase(addr); } else { Network::Address addr(params->get(1)); - const auto i = std::find(Logger::LoggingAddresses[0].begin(), Logger::LoggingAddresses[0].end(), addr); - if (i != Logger::LoggingAddresses[0].end()) + const auto i = std::find(LoggingAddresses[0].begin(), LoggingAddresses[0].end(), addr); + if (i != LoggingAddresses[0].end()) { - Logger::LoggingAddresses[0].erase(i); - Logger::Print("Address {} removed\n", addr.getString()); + LoggingAddresses[0].erase(i); + Print("Address {} removed\n", addr.getString()); } else { - Logger::Print("Address {} not found!\n", addr.getString()); + Print("Address {} not found!\n", addr.getString()); } } }); - Command::AddSV("log_list", [](Command::Params*) + Command::AddSV("log_list", []([[maybe_unused]] Command::Params* params) { - Logger::Print("# ID: Address\n"); - Logger::Print("-------------\n"); + Print("# ID: Address\n"); + Print("-------------\n"); - for (unsigned int i = 0; i < Logger::LoggingAddresses[0].size(); ++i) + for (unsigned int i = 0; i < LoggingAddresses[0].size(); ++i) { - Logger::Print("#{:03d}: {}\n", i, Logger::LoggingAddresses[0][i].getString()); + Print("#{:03d}: {}\n", i, LoggingAddresses[0][i].getString()); } }); @@ -307,9 +308,9 @@ namespace Components const Network::Address addr(params->get(1)); - if (std::find(Logger::LoggingAddresses[1].begin(), Logger::LoggingAddresses[1].end(), addr) == Logger::LoggingAddresses[1].end()) + if (std::find(LoggingAddresses[1].begin(), LoggingAddresses[1].end(), addr) == LoggingAddresses[1].end()) { - Logger::LoggingAddresses[1].push_back(addr); + LoggingAddresses[1].push_back(addr); } }); @@ -318,37 +319,37 @@ namespace Components if (params->size() < 2) return; const auto num = std::atoi(params->get(1)); - if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast(num) < Logger::LoggingAddresses[1].size()) + if (!std::strcmp(Utils::String::VA("%i", num), params->get(1)) && static_cast(num) < LoggingAddresses[1].size()) { - const auto addr = Logger::LoggingAddresses[1].begin() + num; - Logger::Print("Address {} removed\n", addr->getString()); - Logger::LoggingAddresses[1].erase(addr); + const auto addr = LoggingAddresses[1].begin() + num; + Print("Address {} removed\n", addr->getString()); + LoggingAddresses[1].erase(addr); } else { const Network::Address addr(params->get(1)); - const auto i = std::find(Logger::LoggingAddresses[1].begin(), Logger::LoggingAddresses[1].end(), addr); - if (i != Logger::LoggingAddresses[1].end()) + const auto i = std::ranges::find(LoggingAddresses[1].begin(), LoggingAddresses[1].end(), addr); + if (i != LoggingAddresses[1].end()) { - Logger::LoggingAddresses[1].erase(i); - Logger::Print("Address {} removed\n", addr.getString()); + LoggingAddresses[1].erase(i); + Print("Address {} removed\n", addr.getString()); } else { - Logger::Print("Address {} not found!\n", addr.getString()); + Print("Address {} not found!\n", addr.getString()); } } }); Command::AddSV("g_log_list", [](Command::Params*) { - Logger::Print("# ID: Address\n"); - Logger::Print("-------------\n"); + Print("# ID: Address\n"); + Print("-------------\n"); - for (std::size_t i = 0; i < Logger::LoggingAddresses[1].size(); ++i) + for (std::size_t i = 0; i < LoggingAddresses[1].size(); ++i) { - Logger::Print("#{:03d}: {}\n", i, Logger::LoggingAddresses[1][i].getString()); + Print("#{:03d}: {}\n", i, LoggingAddresses[1][i].getString()); } }); } @@ -356,30 +357,28 @@ namespace Components Logger::Logger() { Dvar::Register("iw4x_onelog", false, Game::DVAR_LATCH | Game::DVAR_ARCHIVE, "Only write the game log to the 'userraw' OS folder"); - Utils::Hook(0x642139, Logger::BuildOSPath_Stub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x642139, BuildOSPath_Stub, HOOK_JUMP).install()->quick(); - Logger::PipeOutput(nullptr); + Scheduler::Loop(Frame, Scheduler::Pipeline::SERVER); - Scheduler::Loop(Logger::Frame, Scheduler::Pipeline::SERVER); - - Utils::Hook(Game::G_LogPrintf, Logger::G_LogPrintf_Hk, HOOK_JUMP).install()->quick(); - Utils::Hook(Game::Com_PrintMessage, Logger::PrintMessage_Stub, HOOK_JUMP).install()->quick(); + Utils::Hook(Game::G_LogPrintf, G_LogPrintf_Hk, HOOK_JUMP).install()->quick(); + Utils::Hook(Game::Com_PrintMessage, PrintMessage_Stub, HOOK_JUMP).install()->quick(); if (Loader::IsPerformingUnitTests()) { - Utils::Hook(Game::Com_Printf, Logger::Print_Stub, HOOK_JUMP).install()->quick(); + Utils::Hook(Game::Com_Printf, Print_Stub, HOOK_JUMP).install()->quick(); } - Events::OnSVInit(Logger::AddServerCommands); + Events::OnSVInit(AddServerCommands); } Logger::~Logger() { - Logger::LoggingAddresses[0].clear(); - Logger::LoggingAddresses[1].clear(); + LoggingAddresses[0].clear(); + LoggingAddresses[1].clear(); - std::unique_lock lock(Logger::MessageMutex); - Logger::MessageQueue.clear(); + std::unique_lock lock(MessageMutex); + MessageQueue.clear(); lock.unlock(); // Flush the console log diff --git a/src/Components/Modules/Logger.hpp b/src/Components/Modules/Logger.hpp index c794ecbd..8bc8f454 100644 --- a/src/Components/Modules/Logger.hpp +++ b/src/Components/Modules/Logger.hpp @@ -12,90 +12,107 @@ namespace Components static void Print_Stub(int channel, const char* message, ...); - static void PipeOutput(const std::function& callback); + static void PipeOutput(void(*callback)(const std::string&)); - static void PrintInternal(int channel, std::string_view fmt, std::format_args&& args); - static void ErrorInternal(Game::errorParm_t error, std::string_view fmt, std::format_args&& args); - static void PrintErrorInternal(int channel, std::string_view fmt, std::format_args&& args); - static void WarningInternal(int channel, std::string_view fmt, std::format_args&& args); - static void DebugInternal(std::string_view fmt, std::format_args&& args, const std::source_location& loc); + static void PrintInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args); + static void ErrorInternal(Game::errorParm_t error, const std::string_view& fmt, std::format_args&& args); + static void PrintErrorInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args); + static void WarningInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args); + static void DebugInternal(const std::string_view& fmt, std::format_args&& args, const std::source_location& loc); - static void Print(std::string_view fmt) + static void Print(const std::string_view& fmt) { PrintInternal(Game::CON_CHANNEL_DONT_FILTER, fmt, std::make_format_args(0)); } - static void Print(int channel, std::string_view fmt) + static void Print(Game::conChannel_t channel, const std::string_view& fmt) { PrintInternal(channel, fmt, std::make_format_args(0)); } template - static void Print(std::string_view fmt, Args&&... args) + static void Print(const std::string_view& fmt, Args&&... args) { + (Utils::String::SanitizeFormatArgs(args), ...); PrintInternal(Game::CON_CHANNEL_DONT_FILTER, fmt, std::make_format_args(args...)); } template - static void Print(int channel, std::string_view fmt, Args&&... args) + static void Print(Game::conChannel_t channel, const std::string_view& fmt, Args&&... args) { + (Utils::String::SanitizeFormatArgs(args), ...); PrintInternal(channel, fmt, std::make_format_args(args...)); } - static void Error(Game::errorParm_t error, std::string_view fmt) + static void Error(Game::errorParm_t error, const std::string_view& fmt) { ErrorInternal(error, fmt, std::make_format_args(0)); } template - static void Error(Game::errorParm_t error, std::string_view fmt, Args&&... args) + static void Error(Game::errorParm_t error, const std::string_view& fmt, Args&&... args) { + (Utils::String::SanitizeFormatArgs(args), ...); ErrorInternal(error, fmt, std::make_format_args(args...)); } - static void Warning(int channel, std::string_view fmt) + static void Warning(Game::conChannel_t channel, const std::string_view& fmt) { WarningInternal(channel, fmt, std::make_format_args(0)); } template - static void Warning(int channel, std::string_view fmt, Args&&... args) + static void Warning(Game::conChannel_t channel, const std::string_view& fmt, Args&&... args) { + (Utils::String::SanitizeFormatArgs(args), ...); WarningInternal(channel, fmt, std::make_format_args(args...)); } - static void PrintError(int channel, std::string_view fmt) + static void PrintError(Game::conChannel_t channel, const std::string_view& fmt) { PrintErrorInternal(channel, fmt, std::make_format_args(0)); } template - static void PrintError(int channel, std::string_view fmt, Args&&... args) + static void PrintError(Game::conChannel_t channel, const std::string_view& fmt, Args&&... args) { + (Utils::String::SanitizeFormatArgs(args), ...); PrintErrorInternal(channel, fmt, std::make_format_args(args...)); } - template - class Debug + struct FormatWithLocation { - public: - Debug([[maybe_unused]] std::string_view fmt, [[maybe_unused]] const Args&... args, [[maybe_unused]] const std::source_location& loc = std::source_location::current()) + std::string_view format; + std::source_location location; + + FormatWithLocation(const std::string_view& fmt, std::source_location loc = std::source_location::current()) + : format(fmt) + , location(std::move(loc)) + { + } + + FormatWithLocation(const char* fmt, std::source_location loc = std::source_location::current()) + : format(fmt) + , location(std::move(loc)) { -#ifdef _DEBUG - DebugInternal(fmt, std::make_format_args(args...), loc); -#endif } }; template - Debug(std::string_view fmt, const Args&... args) -> Debug; + static void Debug([[maybe_unused]] const FormatWithLocation& f, [[maybe_unused]] const Args&... args) + { +#ifdef _DEBUG + (Utils::String::SanitizeFormatArgs(args), ...); + DebugInternal(f.format, std::make_format_args(args...), f.location); +#endif + } private: static std::mutex MessageMutex; static std::vector MessageQueue; static std::vector LoggingAddresses[2]; - static std::function PipeCallback; + static void(*PipeCallback)(const std::string&); static void MessagePrint(int channel, const std::string& msg); diff --git a/src/Components/Modules/MapDump.cpp b/src/Components/Modules/MapDump.cpp index 1271b43c..db84fa6e 100644 --- a/src/Components/Modules/MapDump.cpp +++ b/src/Components/Modules/MapDump.cpp @@ -1,4 +1,5 @@ #include +#include "MapDump.hpp" namespace Components { @@ -362,27 +363,8 @@ namespace Components return image; } - // TODO: This is still wrong. - if (image->mapType == 5 && false) - { - for (auto i = 0; i < 6; ++i) - { - IDirect3DSurface9* surface = nullptr; - image->texture.cubemap->GetCubeMapSurface(D3DCUBEMAP_FACES(i), 0, &surface); - - if (surface) - { - std::string _name = Utils::String::VA("raw/mapdump/%s/textures/%s_%i.png", this->world_->baseName, image->name, i); - D3DXSaveSurfaceToFileA(_name.data(), D3DXIFF_PNG, surface, nullptr, nullptr); - surface->Release(); - } - } - } - else - { - std::string _name = Utils::String::VA("raw/mapdump/%s/textures/%s.png", this->world_->baseName, image->name); - D3DXSaveTextureToFileA(_name.data(), D3DXIFF_PNG, image->texture.map, nullptr); - } + std::string _name = Utils::String::VA("raw/mapdump/%s/textures/%s.png", this->world_->baseName, image->name); + D3DXSaveTextureToFileA(_name.data(), D3DXIFF_PNG, image->texture.map, nullptr); return image; } diff --git a/src/Components/Modules/MapRotation.cpp b/src/Components/Modules/MapRotation.cpp index 2c3da9cf..1367ed41 100644 --- a/src/Components/Modules/MapRotation.cpp +++ b/src/Components/Modules/MapRotation.cpp @@ -1,4 +1,5 @@ #include +#include "MapRotation.hpp" namespace Components { @@ -26,7 +27,7 @@ namespace Components this->rotationEntries_.emplace_back(std::make_pair(key, value)); } - std::size_t MapRotation::RotationData::getEntriesSize() const + std::size_t MapRotation::RotationData::getEntriesSize() const noexcept { return this->rotationEntries_.size(); } @@ -47,7 +48,7 @@ namespace Components const auto& key = tokens[i]; const auto& value = tokens[i + 1]; - if (key == "map" || key == "gametype") + if (key == "map"s || key == "gametype"s) { this->addEntry(key, value); } @@ -58,13 +59,17 @@ namespace Components } } + bool MapRotation::RotationData::empty() const noexcept + { + return this->rotationEntries_.empty(); + } + bool MapRotation::RotationData::contains(const std::string& key, const std::string& value) const { - return std::ranges::any_of(this->rotationEntries_, - [&](const auto& entry) - { - return entry.first == key && entry.second == value; - }); + return std::ranges::any_of(this->rotationEntries_, [&](const auto& entry) + { + return entry.first == key && entry.second == value; + }); } nlohmann::json MapRotation::RotationData::to_json() const @@ -74,18 +79,18 @@ namespace Components for (const auto& [key, val] : this->rotationEntries_) { - if (key == "map") + if (key == "map"s) { mapVector.emplace_back(val); } - else if (key == "gametype") + else if (key == "gametype"s) { gametypeVector.emplace_back(val); } } - nlohmann::json mapRotationJson = nlohmann::json + auto mapRotationJson = nlohmann::json { {"maps", mapVector}, {"gametypes", gametypeVector}, @@ -194,7 +199,7 @@ namespace Components void MapRotation::ApplyGametype(const std::string& gametype) { assert(!gametype.empty()); - Dvar::Var("g_gametype").set(gametype.data()); + Dvar::Var("g_gametype").set(gametype); } void MapRotation::RestartCurrentMap() @@ -204,7 +209,7 @@ namespace Components if (svMapname.empty()) { Logger::Print(Game::CON_CHANNEL_SERVER, "mapname dvar is empty! Defaulting to mp_afghan\n"); - svMapname = "mp_afghan"; + svMapname = "mp_afghan"s; } ApplyMap(svMapname); @@ -212,7 +217,7 @@ namespace Components void MapRotation::ApplyRotation(RotationData& rotation) { - assert(rotation.getEntriesSize() != 0); + assert(!rotation.empty()); // Continue to apply gametype until a map is found auto foundMap = false; @@ -222,7 +227,7 @@ namespace Components { const auto& entry = rotation.getNextEntry(); - if (entry.first == "map") + if (entry.first == "map"s) { Logger::Debug("Loading new map: '{}'", entry.second); ApplyMap(entry.second); @@ -230,7 +235,7 @@ namespace Components // Map was found so we exit the loop foundMap = true; } - else if (entry.first == "gametype") + else if (entry.first == "gametype"s) { Logger::Debug("Applying new gametype: '{}'", entry.second); ApplyGametype(entry.second); @@ -260,7 +265,7 @@ namespace Components Game::Dvar_SetString(*Game::sv_mapRotationCurrent, ""); - if (rotationCurrent.getEntriesSize() == 0) + if (rotationCurrent.empty()) { Logger::Print(Game::CON_CHANNEL_SERVER, "{} is empty or contains invalid data. Restarting map\n", (*Game::sv_mapRotationCurrent)->name); RestartCurrentMap(); @@ -302,7 +307,7 @@ namespace Components } LoadMapRotation(); - if (DedicatedRotation.getEntriesSize() == 0) + if (DedicatedRotation.empty()) { Logger::Print(Game::CON_CHANNEL_SERVER, "{} is empty or contains invalid data. Restarting map\n", (*Game::sv_mapRotation)->name); RestartCurrentMap(); @@ -339,7 +344,7 @@ namespace Components } catch (const std::exception& ex) { - Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}: parsing of 'normal' failed", ex.what()); + Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}: parsing of 'normal' failed\n", ex.what()); return false; } diff --git a/src/Components/Modules/MapRotation.hpp b/src/Components/Modules/MapRotation.hpp index 4a57d1a0..09da7517 100644 --- a/src/Components/Modules/MapRotation.hpp +++ b/src/Components/Modules/MapRotation.hpp @@ -30,11 +30,12 @@ namespace Components // this method should be called to add a new entry (gamemode/map & value) void addEntry(const std::string& key, const std::string& value); - [[nodiscard]] std::size_t getEntriesSize() const; + [[nodiscard]] std::size_t getEntriesSize() const noexcept; rotationEntry& getNextEntry(); void parse(const std::string& data); + [[nodiscard]] bool empty() const noexcept; [[nodiscard]] bool contains(const std::string& key, const std::string& value) const; [[nodiscard]] nlohmann::json to_json() const; diff --git a/src/Components/Modules/Maps.cpp b/src/Components/Modules/Maps.cpp index 266e9a9f..b3786320 100644 --- a/src/Components/Modules/Maps.cpp +++ b/src/Components/Modules/Maps.cpp @@ -1,4 +1,8 @@ #include +#include "RawFiles.hpp" +#include "StartupMessages.hpp" +#include "Theatre.hpp" +#include "UIFeeder.hpp" namespace Components { @@ -29,8 +33,8 @@ namespace Components { if (this->isValid() && !this->searchPath.iwd) { - std::string iwdName = Utils::String::VA("%s.iwd", this->mapname.data()); - std::string path = Utils::String::VA("%s\\usermaps\\%s\\%s", Dvar::Var("fs_basepath").get(), this->mapname.data(), iwdName.data()); + auto iwdName = std::format("{}.iwd", this->mapname); + auto path = std::format("{}\\usermaps\\{}\\{}", Dvar::Var("fs_basepath").get(), this->mapname, iwdName); if (Utils::IO::FileExists(path)) { @@ -146,10 +150,10 @@ namespace Components team.allocFlags = zoneInfo->allocFlags; team.freeFlags = zoneInfo->freeFlags; - team.name = allocator.duplicateString(Utils::String::VA("iw4x_team_%s", teams.first.data())); + team.name = allocator.duplicateString(std::format("iw4x_team_{}", teams.first)); data.push_back(team); - team.name = allocator.duplicateString(Utils::String::VA("iw4x_team_%s", teams.second.data())); + team.name = allocator.duplicateString(std::format("iw4x_team_{}", teams.second)); data.push_back(team); } @@ -166,7 +170,7 @@ namespace Components } // Load patch files - std::string patchZone = Utils::String::VA("patch_%s", zoneInfo->name); + auto patchZone = std::format("patch_{}", zoneInfo->name); if (FastFiles::Exists(patchZone)) { data.push_back({patchZone.data(), zoneInfo->allocFlags, zoneInfo->freeFlags}); @@ -301,7 +305,7 @@ namespace Components Game::GfxWorld* world = *reinterpret_cast(0x66DEE94); - if (FileSystem::File(Utils::String::VA("sun/%s.sun", Maps::CurrentMainZone.data())).exists()) + if (FileSystem::File(std::format("sun/{}.sun", Maps::CurrentMainZone))) { Game::R_LoadSunThroughDvars(Maps::CurrentMainZone.data(), &world->sun); } @@ -404,13 +408,13 @@ namespace Components unsigned int Maps::GetUsermapHash(const std::string& map) { - if (Utils::IO::DirectoryExists(Utils::String::VA("usermaps/%s", map.data()))) + if (Utils::IO::DirectoryExists(std::format("usermaps/{}", map))) { std::string hash; for (std::size_t i = 0; i < ARRAYSIZE(Maps::UserMapFiles); ++i) { - std::string filePath = Utils::String::VA("usermaps/%s/%s%s", map.data(), map.data(), Maps::UserMapFiles[i]); + auto filePath = std::format("usermaps/{}/{}{}", map, map, Maps::UserMapFiles[i]); if (Utils::IO::FileExists(filePath)) { hash.append(Utils::Cryptography::SHA256::Compute(Utils::IO::ReadFile(filePath))); @@ -564,13 +568,13 @@ namespace Components bool Maps::IsCustomMap() { Game::GfxWorld*& gameWorld = *reinterpret_cast(0x66DEE94); - if(gameWorld) return gameWorld->checksum == 0xDEADBEEF; + if (gameWorld) return (gameWorld->checksum & 0xFFFF0000) == 0xC0D40000; return false; } bool Maps::IsUserMap(const std::string& mapname) { - return Utils::IO::DirectoryExists(Utils::String::VA("usermaps/%s", mapname.data())) && Utils::IO::FileExists(Utils::String::VA("usermaps/%s/%s.ff", mapname.data(), mapname.data())); + return Utils::IO::DirectoryExists(std::format("usermaps/{}", mapname)) && Utils::IO::FileExists(std::format("usermaps/{}/{}.ff", mapname, mapname)); } Game::XAssetEntry* Maps::GetAssetEntryPool() @@ -619,7 +623,7 @@ namespace Components for (unsigned int i = 0; i < gameWorld->dpvs.smodelCount; ++i) { - if (gameWorld->dpvs.smodelDrawInsts[i].model->name == model) + if (model == "all"s || gameWorld->dpvs.smodelDrawInsts[i].model->name == model) { gameWorld->dpvs.smodelVisData[0][i] = 0; gameWorld->dpvs.smodelVisData[1][i] = 0; @@ -641,86 +645,6 @@ namespace Components } } - Game::dvar_t* Maps::GetDistortionDvar() - { - Game::dvar_t*& r_distortion = *reinterpret_cast(0x69F0DCC); - - if(Maps::IsCustomMap()) - { - static Game::dvar_t noDistortion; - ZeroMemory(&noDistortion, sizeof noDistortion); - return &noDistortion; - } - - return r_distortion; - } - - __declspec(naked) void Maps::SetDistortionStub() - { - __asm - { - push eax - pushad - call Maps::GetDistortionDvar - - mov [esp + 20h], eax - popad - - pop eax - retn - } - } - - Game::dvar_t* Maps::GetSpecularDvar() - { - Game::dvar_t*& r_specular = *reinterpret_cast(0x69F0D94); - static Game::dvar_t* r_specularCustomMaps = Game::Dvar_RegisterBool("r_specularCustomMaps", false, Game::DVAR_ARCHIVE, "Allows shaders to use phong specular lighting on custom maps"); - - if (Maps::IsCustomMap()) - { - if (!r_specularCustomMaps->current.enabled) - { - static Game::dvar_t noSpecular; - ZeroMemory(&noSpecular, sizeof noSpecular); - return &noSpecular; - } - } - - return r_specular; - } - - __declspec(naked) void Maps::SetSpecularStub1() - { - __asm - { - push eax - pushad - call Maps::GetSpecularDvar - - mov [esp + 20h], eax - popad - - pop eax - retn - } - } - - __declspec(naked) void Maps::SetSpecularStub2() - { - __asm - { - push eax - pushad - call Maps::GetSpecularDvar - - mov [esp + 20h], eax - popad - - pop edx - retn - } - } - void Maps::G_SpawnTurretHook(Game::gentity_s* ent, int unk, int unk2) { if (Maps::CurrentMainZone == "mp_backlot_sh"s || Maps::CurrentMainZone == "mp_con_spring"s || @@ -764,7 +688,7 @@ namespace Components Maps::AddDlc({ 6, "Freighter", {"mp_cargoship_sh"} }); Maps::AddDlc({ 7, "Resurrection Pack", {"mp_shipment_long", "mp_rust_long", "mp_firingrange"} }); Maps::AddDlc({ 8, "Recycled Pack", {"mp_bloc_sh", "mp_crash_tropical", "mp_estate_tropical", "mp_fav_tropical", "mp_storm_spring"} }); - Maps::AddDlc({ 9, "Classics Pack #3", {"mp_farm", "mp_backlot", "mp_pipeline", "mp_countdown", "mp_crash_snow", "mp_carentan"}}); + Maps::AddDlc({ 9, "Classics Pack #3", {"mp_farm", "mp_backlot", "mp_pipeline", "mp_countdown", "mp_crash_snow", "mp_carentan", "mp_broadcast", "mp_showdown", "mp_convoy"} }); Maps::UpdateDlcStatus(); @@ -842,16 +766,6 @@ namespace Components // Allow loading raw suns Utils::Hook(0x51B46A, Maps::LoadRawSun, HOOK_CALL).install()->quick(); - // Disable distortion on custom maps - //Utils::Hook(0x50AA47, Maps::SetDistortionStub, HOOK_CALL).install()->quick(); - - // Disable speculars on custom maps - Utils::Hook(0x525EA6, Maps::SetSpecularStub1, HOOK_CALL).install()->quick(); - Utils::Hook(0x51FBC7, Maps::SetSpecularStub2, HOOK_CALL).install()->quick(); - Utils::Hook(0x522A2E, Maps::SetSpecularStub2, HOOK_CALL).install()->quick(); - Utils::Hook::Nop(0x51FBCC, 1); - Utils::Hook::Nop(0x522A33, 1); - // Intercept map loading for usermap initialization Utils::Hook(0x6245E3, Maps::SpawnServerStub, HOOK_CALL).install()->quick(); Utils::Hook(0x62493E, Maps::SpawnServerStub, HOOK_CALL).install()->quick(); @@ -916,7 +830,7 @@ namespace Components unsigned int i = 0; for (auto& model : models) { - Game::R_AddCmdDrawText(Utils::String::VA("%d %s", model.second, model.first.data()), 0x7FFFFFFF, font, 15.0f, (height * scale + 1) * (i++ + 1) + 15.0f, scale, scale, 0.0f, color, Game::ITEM_TEXTSTYLE_NORMAL); + Game::R_AddCmdDrawText(Utils::String::VA("%d %s", model.second, model.first.data()), std::numeric_limits::max(), font, 15.0f, (height * scale + 1) * (i++ + 1) + 15.0f, scale, scale, 0.0f, color, Game::ITEM_TEXTSTYLE_NORMAL); } }, Scheduler::Pipeline::RENDERER); } diff --git a/src/Components/Modules/Maps.hpp b/src/Components/Modules/Maps.hpp index 5a9a26c8..ea47c28b 100644 --- a/src/Components/Modules/Maps.hpp +++ b/src/Components/Modules/Maps.hpp @@ -119,12 +119,6 @@ namespace Components static void HideModel(); static void HideModelStub(); - static Game::dvar_t* GetDistortionDvar(); - static void SetDistortionStub(); - - static Game::dvar_t* GetSpecularDvar(); - static void SetSpecularStub1(); - static void SetSpecularStub2(); static void G_SpawnTurretHook(Game::gentity_s* ent, int unk, int unk2); static bool SV_SetTriggerModelHook(Game::gentity_s* ent); static int16 CM_TriggerModelBounds(int brushModelPointer, Game::Bounds* bounds); diff --git a/src/Components/Modules/Menus.cpp b/src/Components/Modules/Menus.cpp index ef726aed..e9adf249 100644 --- a/src/Components/Modules/Menus.cpp +++ b/src/Components/Modules/Menus.cpp @@ -6,6 +6,31 @@ namespace Components std::unordered_map Menus::MenuList; std::unordered_map Menus::MenuListList; + Game::KeywordHashEntry** menuParseKeywordHash; + + template + static int KeywordHashKey(const char* keyword) + { + auto hash = 0; + for (auto i = 0; keyword[i]; ++i) + { + hash += (i + HASH_SEED) * std::tolower(static_cast(keyword[i])); + } + return (hash + (hash >> 8)) & (128 - 1); + } + + template + static Game::KeywordHashEntry* KeywordHashFind(Game::KeywordHashEntry** table, const char* keyword) + { + auto hash = KeywordHashKey(keyword); + Game::KeywordHashEntry* key = table[hash]; + if (key && !_stricmp(key->keyword, keyword)) + { + return key; + } + return nullptr; + } + int Menus::ReserveSourceHandle() { // Check if a free slot is available @@ -27,7 +52,7 @@ namespace Components Game::script_t* Menus::LoadMenuScript(const std::string& name, const std::string& buffer) { - Game::script_t* script = Game::Script_Alloc(sizeof(Game::script_t) + 1 + buffer.length()); + auto* script = static_cast(Game::GetClearedMemory(sizeof(Game::script_t) + 1 + buffer.length())); if (!script) return nullptr; strcpy_s(script->filename, sizeof(script->filename), name.data()); @@ -37,18 +62,18 @@ namespace Components script->script_p = script->buffer; script->lastscript_p = script->buffer; - script->length = buffer.length(); + script->length = static_cast(buffer.length()); script->end_p = &script->buffer[buffer.length()]; script->line = 1; script->lastline = 1; script->tokenavailable = 0; - Game::Script_SetupTokens(script, reinterpret_cast(0x797F80)); - script->punctuations = reinterpret_cast(0x797F80); + Game::PS_CreatePunctuationTable(script, Game::default_punctuations); + script->punctuations = Game::default_punctuations; std::memcpy(script->buffer, buffer.data(), script->length + 1); - script->length = Game::Script_CleanString(script->buffer); + script->length = Game::Com_Compress(script->buffer); return script; } @@ -57,10 +82,10 @@ namespace Components { Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator(); - int handle = Menus::ReserveSourceHandle(); - if (!Menus::IsValidSourceHandle(handle)) return 0; // No free source slot! + const auto handle = ReserveSourceHandle(); + if (!IsValidSourceHandle(handle)) return 0; // No free source slot! - Game::script_t* script = Menus::LoadMenuScript(name, buffer); + auto* script = LoadMenuScript(name, buffer); if (!script) { @@ -68,8 +93,6 @@ namespace Components return 0; } - script->next = nullptr; - auto* source = allocator->allocate(); if (!source) { @@ -77,13 +100,17 @@ namespace Components return 0; } - strncpy_s(source->filename, 64, "string", 64); + std::memset(source, 0, sizeof(Game::source_s)); + + script->next = nullptr; + + strncpy_s(source->filename, "string", _TRUNCATE); source->scriptstack = script; source->tokens = nullptr; source->defines = nullptr; source->indentstack = nullptr; source->skip = 0; - source->definehash = static_cast(allocator->allocate(4096)); + source->definehash = static_cast(Game::GetClearedMemory(1024 * sizeof(Game::define_s*))); Game::sourceFiles[handle] = source; @@ -95,27 +122,9 @@ namespace Components return (handle > 0 && handle < MAX_SOURCEFILES && Game::sourceFiles[handle]); } - int Menus::KeywordHash(char* key) - { - int hash = 0; - - if (*key) - { - int sub = 3523 - reinterpret_cast(key); - do - { - char _chr = *key; - hash += reinterpret_cast(&(key++)[sub]) * tolower(_chr); - } while (*key); - } - - return (static_cast(hash) + static_cast(hash >> 8)) & 0x7F; - } - Game::menuDef_t* Menus::ParseMenu(int handle) { Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator(); - auto* menu = allocator->allocate(); if (!menu) return nullptr; @@ -149,10 +158,7 @@ namespace Components break; // Success } - int idx = Menus::KeywordHash(token.string); - - Game::keywordHash_t* key = Game::menuParseKeywordHash[idx]; - + auto* key = KeywordHashFind(menuParseKeywordHash, token.string); if (!key) { Game::PC_SourceError(handle, "unknown menu keyword %s", token.string); @@ -166,9 +172,17 @@ namespace Components } } - Menus::OverrideMenu(menu); - Menus::RemoveMenu(menu->window.name); - Menus::MenuList[menu->window.name] = menu; + if (!menu->window.name) + { + Game::PC_SourceError(handle, "menu has no name"); + allocator->free(menu->items); + allocator->free(menu); + return nullptr; + } + + OverrideMenu(menu); + RemoveMenu(menu->window.name); + MenuList[menu->window.name] = menu; return menu; } @@ -181,9 +195,9 @@ namespace Components if (!menuFile.exists()) return nullptr; Game::pc_token_t token; - int handle = Menus::LoadMenuSource(menu, menuFile.getBuffer()); + int handle = LoadMenuSource(menu, menuFile.getBuffer()); - if (Menus::IsValidSourceHandle(handle)) + if (IsValidSourceHandle(handle)) { while (true) { @@ -198,23 +212,24 @@ namespace Components { Game::PC_ReadTokenHandle(handle, &token); - Utils::Merge(&menus, Menus::LoadMenu(Utils::String::VA("ui_mp\\%s.menu", token.string))); + auto* filename = Utils::String::VA("ui_mp\\%s.menu", token.string); + Utils::Merge(&menus, LoadMenu(filename)); } if (!_stricmp(token.string, "menudef")) { - Game::menuDef_t* menudef = Menus::ParseMenu(handle); - if (menudef) menus.push_back({ true, menudef }); // Custom menu + auto* menudef = ParseMenu(handle); + if (menudef) menus.emplace_back(std::make_pair(true, menudef)); // Custom menu } } - Menus::FreeMenuSource(handle); + FreeMenuSource(handle); } if (menus.empty()) return nullptr; // Allocate new menu list - Game::MenuList* list = allocator->allocate(); + auto* list = allocator->allocate(); if (!list) return nullptr; list->menus = allocator->allocateArray(menus.size()); @@ -225,10 +240,10 @@ namespace Components } list->name = allocator->duplicateString(menu); - list->menuCount = menus.size(); + list->menuCount = static_cast(menus.size()); // Copy new menus - for (unsigned int i = 0; i < menus.size(); ++i) + for (std::size_t i = 0; i < menus.size(); ++i) { list->menus[i] = menus[i].second; } @@ -244,9 +259,9 @@ namespace Components if (menuFile.exists()) { Game::pc_token_t token; - int handle = Menus::LoadMenuSource(menu, menuFile.getBuffer()); + const auto handle = LoadMenuSource(menu, menuFile.getBuffer()); - if (Menus::IsValidSourceHandle(handle)) + if (IsValidSourceHandle(handle)) { while (true) { @@ -261,17 +276,17 @@ namespace Components { Game::PC_ReadTokenHandle(handle, &token); - Utils::Merge(&menus, Menus::LoadMenu(Utils::String::VA("ui_mp\\%s.menu", token.string))); + Utils::Merge(&menus, LoadMenu(Utils::String::VA("ui_mp\\%s.menu", token.string))); } if (!_stricmp(token.string, "menudef")) { - Game::menuDef_t* menudef = Menus::ParseMenu(handle); - if (menudef) menus.push_back({ true, menudef }); // Custom menu + auto* menudef = ParseMenu(handle); + if (menudef) menus.emplace_back(std::make_pair(true, menudef)); // Custom menu } } - Menus::FreeMenuSource(handle); + FreeMenuSource(handle); } } @@ -280,11 +295,13 @@ namespace Components std::vector> Menus::LoadMenu(Game::menuDef_t* menudef) { - std::vector> menus = Menus::LoadMenu(Utils::String::VA("ui_mp\\%s.menu", menudef->window.name)); + assert(menudef->window.name); + + std::vector> menus = LoadMenu(Utils::String::VA("ui_mp\\%s.menu", menudef->window.name)); if (menus.empty()) { - menus.push_back({ false, menudef }); // Native menu + menus.emplace_back(std::make_pair(false, menudef)); // Native menu } return menus; @@ -294,7 +311,7 @@ namespace Components { Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator(); - std::vector> menus = Menus::LoadMenu(menu); + auto menus = LoadMenu(menu); if (menus.empty()) return nullptr; // Allocate new menu list @@ -317,8 +334,8 @@ namespace Components newList->menus[i] = menus[i].second; } - Menus::RemoveMenuList(newList->name); - Menus::MenuListList[newList->name] = newList; + RemoveMenuList(newList->name); + MenuListList[newList->name] = newList; return newList; } @@ -358,7 +375,7 @@ namespace Components { if (i->second->window.name == std::string(newMenu.second->window.name)) { - Menus::RemoveMenu(i->second); + RemoveMenu(i->second); i = menus->erase(i); increment = false; @@ -381,13 +398,13 @@ namespace Components for (int i = 0; i < menuList->menuCount; ++i) { if (!menuList->menus[i]) continue; - Menus::SafeMergeMenus(&menus, Menus::LoadMenu(menuList->menus[i])); + SafeMergeMenus(&menus, LoadMenu(menuList->menus[i])); } // Load custom menus if (menuList->name == "ui_mp/code.txt"s) // Should be menus, but code is loaded ingame { - for (auto menu : Menus::CustomMenus) + for (auto menu : CustomMenus) { bool hasMenu = false; for (auto& loadedMenu : menus) @@ -399,15 +416,15 @@ namespace Components } } - if (!hasMenu) Menus::SafeMergeMenus(&menus, Menus::LoadMenu(menu)); + if (!hasMenu) SafeMergeMenus(&menus, LoadMenu(menu)); } } // Allocate new menu list - Game::MenuList* newList = allocator->allocate(); + auto* newList = allocator->allocate(); if (!newList) return menuList; - size_t size = menus.size(); + auto size = menus.size(); newList->menus = allocator->allocateArray(size); if (!newList->menus) { @@ -424,8 +441,8 @@ namespace Components newList->menus[i] = menus[i].second; } - Menus::RemoveMenuList(newList->name); - Menus::MenuListList[newList->name] = newList; + RemoveMenuList(newList->name); + MenuListList[newList->name] = newList; return newList; } @@ -434,7 +451,7 @@ namespace Components { Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator(); - if (!Menus::IsValidSourceHandle(handle)) return; + if (!IsValidSourceHandle(handle)) return; Game::source_t* source = Game::sourceFiles[handle]; @@ -473,28 +490,44 @@ namespace Components Game::sourceFiles[handle] = nullptr; } - void Menus::FreeMenu(Game::menuDef_t* menudef) + void Menus::Menu_FreeItemMemory(Game::itemDef_s* item) + { + AssertOffset(Game::itemDef_s, floatExpressionCount, 0x13C); + + for (auto i = 0; i < item->floatExpressionCount; ++i) + { + Game::free_expression(item->floatExpressions[i].expression); + } + + item->floatExpressionCount = 0; + } + + void Menus::FreeMenu(Game::menuDef_t* menu) { Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator(); - // Do i need to free expressions and strings? - // Or does the game take care of it? - // Seems like it does... - - if (menudef->items) + if (menu->items) { - // Seems like this is obsolete as well, - // as the game handles the memory + for (int i = 0; i < menu->itemCount; ++i) + { +#if 0 + Menu_FreeItemMemory(menu->items[i]); +#endif + } - //for (int i = 0; i < menudef->itemCount; ++i) - //{ - // Game::Menu_FreeItemMemory(menudef->items[i]); - //} - - allocator->free(menudef->items); + allocator->free(menu->items); } +#if 0 + Game::free_expression(menu->visibleExp); + Game::free_expression(menu->rectXExp); + Game::free_expression(menu->rectYExp); + Game::free_expression(menu->rectWExp); + Game::free_expression(menu->rectHExp); + Game::free_expression(menu->openSoundExp); + Game::free_expression(menu->closeSoundExp); +#endif - allocator->free(menudef); + allocator->free(menu); } void Menus::FreeMenuList(Game::MenuList* menuList) @@ -505,37 +538,29 @@ namespace Components // Keep our compiler happy Game::MenuList list = { menuList->name, menuList->menuCount, menuList->menus }; - if (list.name) - { - allocator->free(list.name); - } - - if (list.menus) - { - allocator->free(list.menus); - } - + allocator->free(list.name); + allocator->free(list.menus); allocator->free(menuList); } void Menus::RemoveMenu(const std::string& menu) { - auto i = Menus::MenuList.find(menu); - if (i != Menus::MenuList.end()) + auto i = MenuList.find(menu); + if (i != MenuList.end()) { - if (i->second) Menus::FreeMenu(i->second); - i = Menus::MenuList.erase(i); + if (i->second) FreeMenu(i->second); + i = MenuList.erase(i); } } void Menus::RemoveMenu(Game::menuDef_t* menudef) { - for (auto i = Menus::MenuList.begin(); i != Menus::MenuList.end();) + for (auto i = MenuList.begin(); i != MenuList.end();) { if (i->second == menudef) { - Menus::FreeMenu(menudef); - i = Menus::MenuList.erase(i); + FreeMenu(menudef); + i = MenuList.erase(i); } else { @@ -546,20 +571,20 @@ namespace Components void Menus::RemoveMenuList(const std::string& menuList) { - auto i = Menus::MenuListList.find(menuList); - if (i != Menus::MenuListList.end()) + auto i = MenuListList.find(menuList); + if (i != MenuListList.end()) { if (i->second) { for (auto j = 0; j < i->second->menuCount; ++j) { - Menus::RemoveMenu(i->second->menus[j]); + RemoveMenu(i->second->menus[j]); } - Menus::FreeMenuList(i->second); + FreeMenuList(i->second); } - i = Menus::MenuListList.erase(i); + i = MenuListList.erase(i); } } @@ -576,8 +601,7 @@ namespace Components std::string name = menu->window.name; // Find the old menu - auto i = Menus::MenuList.find(name); - if (i != Menus::MenuList.end()) + if (auto i = MenuList.find(name); i != MenuList.end()) { // We have found it, *yay* Game::menuDef_t* oldMenu = i->second; @@ -592,7 +616,7 @@ namespace Components } // Replace every old instance with our new one in our menu lists - for (auto j = Menus::MenuListList.begin(); j != Menus::MenuListList.end(); ++j) + for (auto j = MenuListList.begin(); j != MenuListList.end(); ++j) { Game::MenuList* list = j->second; @@ -613,24 +637,25 @@ namespace Components void Menus::RemoveMenuList(Game::MenuList* menuList) { if (!menuList || !menuList->name) return; - Menus::RemoveMenuList(menuList->name); + RemoveMenuList(menuList->name); } + // In your dreams void Menus::FreeEverything() { - for (auto i = Menus::MenuListList.begin(); i != Menus::MenuListList.end(); ++i) + for (auto i = MenuListList.begin(); i != MenuListList.end(); ++i) { - Menus::FreeMenuList(i->second); + FreeMenuList(i->second); } - Menus::MenuListList.clear(); + MenuListList.clear(); - for (auto i = Menus::MenuList.begin(); i != Menus::MenuList.end(); ++i) + for (auto i = MenuList.begin(); i != MenuList.end(); ++i) { - Menus::FreeMenu(i->second); + FreeMenu(i->second); } - Menus::MenuList.clear(); + MenuList.clear(); } Game::XAssetHeader Menus::MenuFindHook(Game::XAssetType /*type*/, const std::string& filename) @@ -643,23 +668,23 @@ namespace Components Game::XAssetHeader header = { nullptr }; // Free the last menulist and ui context, as we have to rebuild it with the new menus - if (Menus::MenuListList.find(filename) != Menus::MenuListList.end()) + if (MenuListList.find(filename) != MenuListList.end()) { - Game::MenuList* list = Menus::MenuListList[filename]; + Game::MenuList* list = MenuListList[filename]; for (int i = 0; list && list->menus && i < list->menuCount; ++i) { - Menus::RemoveMenuFromContext(Game::uiContext, list->menus[i]); + RemoveMenuFromContext(Game::uiContext, list->menus[i]); } - Menus::RemoveMenuList(filename); + RemoveMenuList(filename); } if (Utils::String::EndsWith(filename, ".menu")) { if (FileSystem::File(filename).exists()) { - header.menuList = Menus::LoadScriptMenu(filename.data()); + header.menuList = LoadScriptMenu(filename.data()); if (header.menuList) return header; } } @@ -674,7 +699,7 @@ namespace Components { if (FileSystem::File(filename).exists()) { - header.menuList = Menus::LoadScriptMenu(filename.data()); + header.menuList = LoadScriptMenu(filename.data()); // Reset, if we didn't find scriptmenus if (!header.menuList) @@ -685,7 +710,7 @@ namespace Components } else { - header.menuList = Menus::LoadMenuList(menuList); + header.menuList = LoadMenuList(menuList); } } else @@ -706,7 +731,7 @@ namespace Components if (originalConnect == menu) // Check if we draw the original loadscreen { - if (Menus::MenuList.find("connect") != Menus::MenuList.end()) // Check if we have a custom loadscreen, to prevent drawing the original one on top + if (MenuList.find("connect") != Menus::MenuList.end()) // Check if we have a custom loadscreen, to prevent drawing the original one on top { return false; } @@ -744,40 +769,29 @@ namespace Components void Menus::Add(const std::string& menu) { - Menus::CustomMenus.push_back(menu); - } - - void Menus::RegisterCustomMenusHook() - { - Utils::Hook::Call(0x401700)(); // call original load functions - -#ifdef _DEBUG - for (int i = 0; i < Game::uiContext->menuCount; i++) - { - OutputDebugStringA(Utils::String::VA("%s\n", Game::uiContext->Menus[i]->window.name)); - } -#endif + CustomMenus.push_back(menu); } Menus::Menus() { + menuParseKeywordHash = reinterpret_cast**>(0x63AE928); + + if (ZoneBuilder::IsEnabled()) + { + Game::Menu_Setup(Game::uiContext); + } + if (Dedicated::IsEnabled()) return; - // Ensure everything is zero'ed - Menus::FreeEverything(); - // Intercept asset finding - AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_MENU, Menus::MenuFindHook); - AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_MENULIST, Menus::MenuListFindHook); + AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_MENU, MenuFindHook); + AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_MENULIST, MenuListFindHook); // Don't open connect menu - //Utils::Hook::Nop(0x428E48, 5); - - // register custom menufiles if they exist - Utils::Hook(0x4A58C3, Menus::RegisterCustomMenusHook, HOOK_CALL).install()->quick(); + // Utils::Hook::Nop(0x428E48, 5); // Use the connect menu open call to update server motds - Utils::Hook(0x428E48, []() + Utils::Hook(0x428E48, [] { if (!Party::GetMotd().empty() && Party::Target() == *Game::connectedHost) { @@ -786,10 +800,10 @@ namespace Components }, HOOK_CALL).install()->quick(); // Intercept menu painting - Utils::Hook(0x4FFBDF, Menus::IsMenuVisible, HOOK_CALL).install()->quick(); + Utils::Hook(0x4FFBDF, IsMenuVisible, HOOK_CALL).install()->quick(); - // disable the 2 new tokens in ItemParse_rect - Utils::Hook::Set(0x640693, 0xEB); + // disable the 2 new tokens in ItemParse_rect (Fix by NTA. Probably because he didn't want to update the menus) + Utils::Hook::Set(0x640693, 0xEB); // don't load ASSET_TYPE_MENU assets for every menu (might cause patch menus to fail) Utils::Hook::Nop(0x453406, 5); @@ -808,19 +822,19 @@ namespace Components // Not quite sure if we want to do this if we're not ingame, but it's only needed for ingame menus. if ((*Game::cl_ingame)->current.enabled) { - Game::Key_SetCatcher(0, 16); + Game::Key_SetCatcher(0, Game::KEYCATCH_UI); } Game::Menus_OpenByName(Game::uiContext, params->get(1)); }); - Command::Add("reloadmenus", [](Command::Params*) + Command::Add("reloadmenus", []([[maybe_unused]] Command::Params* params) { // Close all menus Game::Menus_CloseAll(Game::uiContext); - // Free custom menus - Menus::FreeEverything(); + // Free custom menus (Get pranked) + FreeEverything(); // Only disconnect if in-game, context is updated automatically! if (Game::CL_IsCgameInitialized()) @@ -843,26 +857,27 @@ namespace Components }); // Define custom menus here - Menus::Add("ui_mp/changelog.menu"); - Menus::Add("ui_mp/theater_menu.menu"); - Menus::Add("ui_mp/pc_options_multi.menu"); - Menus::Add("ui_mp/pc_options_game.menu"); - Menus::Add("ui_mp/pc_options_gamepad.menu"); - Menus::Add("ui_mp/stats_reset.menu"); - Menus::Add("ui_mp/stats_unlock.menu"); - Menus::Add("ui_mp/security_increase_popmenu.menu"); - Menus::Add("ui_mp/mod_download_popmenu.menu"); - Menus::Add("ui_mp/popup_friends.menu"); - Menus::Add("ui_mp/menu_first_launch.menu"); - Menus::Add("ui_mp/startup_messages.menu"); - Menus::Add("ui_mp/iw4x_credits.menu"); - Menus::Add("ui_mp/resetclass.menu"); - Menus::Add("ui_mp/popup_customtitle.menu"); - Menus::Add("ui_mp/popup_customclan.menu"); + Add("ui_mp/changelog.menu"); + Add("ui_mp/theater_menu.menu"); + Add("ui_mp/pc_options_multi.menu"); + Add("ui_mp/pc_options_game.menu"); + Add("ui_mp/pc_options_gamepad.menu"); + Add("ui_mp/stats_reset.menu"); + Add("ui_mp/stats_unlock.menu"); + Add("ui_mp/security_increase_popmenu.menu"); + Add("ui_mp/mod_download_popmenu.menu"); + Add("ui_mp/popup_friends.menu"); + Add("ui_mp/menu_first_launch.menu"); + Add("ui_mp/startup_messages.menu"); + Add("ui_mp/iw4x_credits.menu"); + Add("ui_mp/resetclass.menu"); + Add("ui_mp/popup_customtitle.menu"); + Add("ui_mp/popup_customclan.menu"); } Menus::~Menus() { + // Let Windows handle the memory leaks for you! Menus::FreeEverything(); } } diff --git a/src/Components/Modules/Menus.hpp b/src/Components/Modules/Menus.hpp index fd13fe13..45ff1639 100644 --- a/src/Components/Modules/Menus.hpp +++ b/src/Components/Modules/Menus.hpp @@ -43,6 +43,7 @@ namespace Components static void FreeMenuSource(int handle); static void FreeMenuList(Game::MenuList* menuList); + static void Menu_FreeItemMemory(Game::itemDef_s* item); static void FreeMenu(Game::menuDef_t* menudef); static void RemoveMenu(const std::string& menu); @@ -55,10 +56,5 @@ namespace Components static bool IsMenuVisible(Game::UiContext* dc, Game::menuDef_t* menu); static void RemoveMenuFromContext(Game::UiContext* dc, Game::menuDef_t* menu); - - static void RegisterCustomMenusHook(); - - // Ugly! - static int KeywordHash(char* key); }; } diff --git a/src/Components/Modules/ModList.cpp b/src/Components/Modules/ModList.cpp index 5dc9d38c..2b3a07f6 100644 --- a/src/Components/Modules/ModList.cpp +++ b/src/Components/Modules/ModList.cpp @@ -1,4 +1,5 @@ #include +#include "UIFeeder.hpp" namespace Components { @@ -60,7 +61,6 @@ namespace Components { auto fsGame = Dvar::Var("fs_game"); fsGame.set(""); - fsGame.get()->modified = true; if (Dvar::Var("cl_modVidRestart").get()) { @@ -75,8 +75,7 @@ namespace Components void ModList::RunMod(const std::string& mod) { auto fsGame = Dvar::Var("fs_game"); - fsGame.set(Utils::String::VA("mods/%s", mod.data())); - fsGame.get()->modified = true; + fsGame.set(std::format("mods/{}", mod)); if (Dvar::Var("cl_modVidRestart").get()) { diff --git a/src/Components/Modules/ModelSurfs.cpp b/src/Components/Modules/ModelSurfs.cpp index 40c61812..4c5424a4 100644 --- a/src/Components/Modules/ModelSurfs.cpp +++ b/src/Components/Modules/ModelSurfs.cpp @@ -38,7 +38,7 @@ namespace Components Game::XModelSurfs* ModelSurfs::LoadXModelSurfaces(const std::string& name) { Utils::Memory::Allocator allocator; - FileSystem::FileReader model(Utils::String::VA("models/%s", name.data())); + FileSystem::FileReader model(std::format("models/{}", name)); if (!model.exists()) { diff --git a/src/Components/Modules/Movement.cpp b/src/Components/Modules/Movement.cpp deleted file mode 100644 index 6197387f..00000000 --- a/src/Components/Modules/Movement.cpp +++ /dev/null @@ -1,302 +0,0 @@ -#include - -namespace Components -{ - Dvar::Var Movement::PlayerSpectateSpeedScale; - Dvar::Var Movement::CGUfoScaler; - Dvar::Var Movement::CGNoclipScaler; - Dvar::Var Movement::BGBouncesAllAngles; - Dvar::Var Movement::BGRocketJump; - Dvar::Var Movement::BGRocketJumpScale; - Dvar::Var Movement::BGPlayerEjection; - Dvar::Var Movement::BGPlayerCollision; - Game::dvar_t* Movement::BGBounces; - Game::dvar_t* Movement::PlayerDuckedSpeedScale; - Game::dvar_t* Movement::PlayerProneSpeedScale; - - __declspec(naked) void Movement::PM_PlayerDuckedSpeedScaleStub() - { - __asm - { - push eax - mov eax, Movement::PlayerDuckedSpeedScale - fld dword ptr [eax + 0x10] // dvar_t.current.value - pop eax - - // Game's code - pop ecx - ret - } - } - - __declspec(naked) void Movement::PM_PlayerProneSpeedScaleStub() - { - __asm - { - push eax - mov eax, Movement::PlayerProneSpeedScale - fld dword ptr [eax + 0x10] // dvar_t.current.value - pop eax - - // Game's code - pop ecx - ret - } - } - - float Movement::PM_MoveScale(Game::playerState_s* ps, float fmove, - float rmove, float umove) - { - assert(ps != nullptr); - - auto max = std::fabsf(fmove) < std::fabsf(rmove) - ? std::fabsf(rmove) : std::fabsf(fmove); - - if (std::fabsf(umove) > max) - { - max = std::fabsf(umove); - } - - if (max == 0.0f) - { - return 0.0f; - } - - auto total = std::sqrtf(fmove * fmove - + rmove * rmove + umove * umove); - auto scale = (static_cast(ps->speed) * max) / (127.0f * total); - - if (ps->pm_flags & Game::PMF_WALKING || ps->leanf != 0.0f) - { - scale *= 0.4f; - } - - switch (ps->pm_type) - { - case Game::pmtype_t::PM_NOCLIP: - scale *= Movement::CGNoclipScaler.get(); - break; - case Game::pmtype_t::PM_UFO: - scale *= Movement::CGUfoScaler.get(); - break; - case Game::pmtype_t::PM_SPECTATOR: - scale *= Movement::PlayerSpectateSpeedScale.get(); - break; - default: - break; - } - - return scale; - } - - __declspec(naked) void Movement::PM_MoveScaleStub() - { - __asm - { - pushad - - push [esp + 0xC + 0x20] // umove - push [esp + 0xC + 0x20] // rmove - push [esp + 0xC + 0x20] // fmove - push esi // ps - call Movement::PM_MoveScale - add esp, 0x10 - - popad - ret - } - } - - __declspec(naked) void Movement::PM_StepSlideMoveStub() - { - __asm - { - // Check the value of BGBounces - push ecx - push eax - - mov eax, Movement::BGBounces - mov ecx, dword ptr [eax + 0x10] - test ecx, ecx - - pop eax - pop ecx - - // Do not bounce if BGBounces is 0 - jle noBounce - - // Bounce - push 0x4B1B34 - retn - - noBounce: - // Original game code - cmp dword ptr [esp + 0x24], 0 - push 0x4B1B48 - retn - } - } - - void Movement::PM_ProjectVelocityStub(const float* velIn, const float* normal, float* velOut) - { - const auto lengthSquared2D = velIn[0] * velIn[0] + velIn[1] * velIn[1]; - - if (std::fabsf(normal[2]) < 0.001f || lengthSquared2D == 0.0) - { - velOut[0] = velIn[0]; - velOut[1] = velIn[1]; - velOut[2] = velIn[2]; - return; - } - - auto newZ = velIn[0] * normal[0] + velIn[1] * normal[1]; - newZ = -newZ / normal[2]; - const auto lengthScale = std::sqrtf((velIn[2] * velIn[2] + lengthSquared2D) - / (newZ * newZ + lengthSquared2D)); - - if (Movement::BGBouncesAllAngles.get() - || (lengthScale < 1.f || newZ < 0.f || velIn[2] > 0.f)) - { - velOut[0] = velIn[0] * lengthScale; - velOut[1] = velIn[1] * lengthScale; - velOut[2] = newZ * lengthScale; - } - } - - // Double bounces - void Movement::Jump_ClearState_Hk(Game::playerState_s* ps) - { - if (Movement::BGBounces->current.integer != Movement::DOUBLE) - { - Game::Jump_ClearState(ps); - } - } - - Game::gentity_s* Movement::Weapon_RocketLauncher_Fire_Hk(Game::gentity_s* ent, unsigned int weaponIndex, - float spread, Game::weaponParms* wp, const float* gunVel, Game::lockonFireParms* lockParms, bool magicBullet) - { - auto* result = Game::Weapon_RocketLauncher_Fire(ent, weaponIndex, spread, wp, gunVel, lockParms, magicBullet); - - if (ent->client != nullptr && BGRocketJump.get() && - wp->weapDef->inventoryType != Game::WEAPINVENTORY_EXCLUSIVE) - { - const auto scale = Movement::BGRocketJumpScale.get(); - ent->client->ps.velocity[0] += (0.0f - wp->forward[0]) * scale; - ent->client->ps.velocity[1] += (0.0f - wp->forward[1]) * scale; - ent->client->ps.velocity[2] += (0.0f - wp->forward[2]) * scale; - } - - return result; - } - - int Movement::StuckInClient_Hk(Game::gentity_s* self) - { - if (Movement::BGPlayerEjection.get()) - { - return Utils::Hook::Call(0x402D30)(self); // StuckInClient - } - - return 0; - } - - void Movement::CM_TransformedCapsuleTrace_Hk(Game::trace_t* results, const float* start, const float* end, - const Game::Bounds* bounds, const Game::Bounds* capsule, int contents, const float* origin, const float* angles) - { - if (Movement::BGPlayerCollision.get()) - { - Utils::Hook::Call - (0x478300) - (results, start, end, bounds, capsule, contents, origin, angles); // CM_TransformedCapsuleTrace - } - } - - Game::dvar_t* Movement::Dvar_RegisterSpectateSpeedScale(const char* dvarName, float value, - float min, float max, unsigned __int16 /*flags*/, const char* description) - { - Movement::PlayerSpectateSpeedScale = Dvar::Register(dvarName, value, - min, max, Game::DVAR_CHEAT | Game::DVAR_CODINFO, description); - - return Movement::PlayerSpectateSpeedScale.get(); - } - - void Movement::RegisterMovementDvars() - { - Movement::PlayerDuckedSpeedScale = Game::Dvar_RegisterFloat("player_duckedSpeedScale", - 0.65f, 0.0f, 5.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO, - "The scale applied to the player speed when ducking"); - - Movement::PlayerProneSpeedScale = Game::Dvar_RegisterFloat("player_proneSpeedScale", - 0.15f, 0.0f, 5.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO, - "The scale applied to the player speed when crawling"); - - // 3arc naming convention - Movement::CGUfoScaler = Dvar::Register("cg_ufo_scaler", - 6.0f, 0.001f, 1000.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO, - "The speed at which ufo camera moves"); - - Movement::CGNoclipScaler = Dvar::Register("cg_noclip_scaler", - 3.0f, 0.001f, 1000.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO, - "The speed at which noclip camera moves"); - - Movement::BGBouncesAllAngles = Dvar::Register("bg_bouncesAllAngles", - false, Game::DVAR_CODINFO, "Force bounce from all angles"); - - Movement::BGRocketJump = Dvar::Register("bg_rocketJump", - false, Game::DVAR_CODINFO, "Enable CoD4 rocket jumps"); - - Movement::BGRocketJumpScale = Dvar::Register("bg_rocketJumpScale", - 64.0f, 1.0f, std::numeric_limits::max(), Game::DVAR_CODINFO, - "The scale applied to the pushback force of a rocket"); - - Movement::BGPlayerEjection = Dvar::Register("bg_playerEjection", - true, Game::DVAR_CODINFO, "Push intersecting players away from each other"); - - Movement::BGPlayerCollision = Dvar::Register("bg_playerCollision", - true, Game::DVAR_CODINFO, "Push intersecting players away from each other"); - } - - Movement::Movement() - { - Scheduler::Once([] - { - static const char* bg_bouncesValues[] = - { - "disabled", - "enabled", - "double", - nullptr - }; - - Movement::BGBounces = Game::Dvar_RegisterEnum("bg_bounces", - bg_bouncesValues, Movement::DISABLED, Game::DVAR_CODINFO, "Bounce glitch settings"); - }, Scheduler::Pipeline::MAIN); - - // Hook Dvar_RegisterFloat. Only thing that's changed is that the 0x80 flag is not used. - Utils::Hook(0x448990, Movement::Dvar_RegisterSpectateSpeedScale, HOOK_CALL).install()->quick(); - - // PM_CmdScaleForStance - Utils::Hook(0x572D9B, Movement::PM_PlayerDuckedSpeedScaleStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x572DA5, Movement::PM_PlayerProneSpeedScaleStub, HOOK_JUMP).install()->quick(); - - // Hook PM_MoveScale so we can add custom speed scale for Ufo and Noclip - Utils::Hook(0x56F845, Movement::PM_MoveScaleStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x56FABD, Movement::PM_MoveScaleStub, HOOK_CALL).install()->quick(); - - // Bounce logic - Utils::Hook(0x4B1B2D, Movement::PM_StepSlideMoveStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x57383E, Movement::Jump_ClearState_Hk, HOOK_CALL).install()->quick(); - Utils::Hook(0x4B1B97, Movement::PM_ProjectVelocityStub, HOOK_CALL).install()->quick(); - - // Rocket jump - Utils::Hook(0x4A4F9B, Movement::Weapon_RocketLauncher_Fire_Hk, HOOK_CALL).install()->quick(); // FireWeapon - - // Hook StuckInClient & CM_TransformedCapsuleTrace - // so we can prevent intersecting players from being pushed away from each other - Utils::Hook(0x5D8153, Movement::StuckInClient_Hk, HOOK_CALL).install()->quick(); - Utils::Hook(0x45A5BF, Movement::CM_TransformedCapsuleTrace_Hk, HOOK_CALL).install()->quick(); // SV_ClipMoveToEntity - Utils::Hook(0x5A0CAD, Movement::CM_TransformedCapsuleTrace_Hk, HOOK_CALL).install()->quick(); // CG_ClipMoveToEntity - - Movement::RegisterMovementDvars(); - } -} diff --git a/src/Components/Modules/Network.cpp b/src/Components/Modules/Network.cpp index 9b198743..9e415552 100644 --- a/src/Components/Modules/Network.cpp +++ b/src/Components/Modules/Network.cpp @@ -263,7 +263,7 @@ namespace Components { if (client->reliableAcknowledge < 0) { - Logger::Print(Game::conChannel_t::CON_CHANNEL_NETWORK, "Negative reliableAcknowledge from {} - cl->reliableSequence is {}, reliableAcknowledge is {}\n", + Logger::Print(Game::CON_CHANNEL_NETWORK, "Negative reliableAcknowledge from {} - cl->reliableSequence is {}, reliableAcknowledge is {}\n", client->name, client->reliableSequence, client->reliableAcknowledge); client->reliableAcknowledge = client->reliableSequence; SendCommand(Game::NS_SERVER, client->header.netchan.remoteAddress, "error", "EXE_LOSTRELIABLECOMMANDS"); diff --git a/src/Components/Modules/NetworkDebug.cpp b/src/Components/Modules/NetworkDebug.cpp new file mode 100644 index 00000000..97c8bdfa --- /dev/null +++ b/src/Components/Modules/NetworkDebug.cpp @@ -0,0 +1,48 @@ +#include +#include "NetworkDebug.hpp" + +namespace Components +{ + void NetworkDebug::CL_ParseServerMessage_Hk(Game::msg_t* msg) + { + auto file = Game::FS_FOpenFileWrite("badpacket.dat"); + if (file) + { + Game::FS_Write(msg->data, msg->cursize, file); + Game::FS_FCloseFile(file); + } + Game::MSG_Discard(msg); + } + + void NetworkDebug::CL_ParseBadPacket_f() + { + Game::msg_t msg; + unsigned char* file; + + auto fileSize = Game::FS_ReadFile("badpacket.dat", reinterpret_cast(&file)); + if (fileSize < 0) + { + return; + } + + ZeroMemory(&msg, sizeof(msg)); + msg.cursize = fileSize; + msg.data = file; + Game::MSG_ReadLong(&msg); + Game::MSG_ReadLong(&msg); + assert(0 && "Time to debug this packet, baby!"); + Game::CL_ParseServerMessage(0, &msg); + Game::FS_FreeFile(file); + } + + NetworkDebug::NetworkDebug() + { +#ifdef _DEBUG + Utils::Hook(0x4AA06A, CL_ParseServerMessage_Hk, HOOK_CALL).install()->quick(); + Command::Add("parseBadPacket", CL_ParseBadPacket_f); +#endif + + // Backport updates from IW5 + Utils::Hook::Set(0x45D112, "CL_PacketEvent - ignoring illegible message\n"); + } +} diff --git a/src/Components/Modules/NetworkDebug.hpp b/src/Components/Modules/NetworkDebug.hpp new file mode 100644 index 00000000..34ec8344 --- /dev/null +++ b/src/Components/Modules/NetworkDebug.hpp @@ -0,0 +1,15 @@ +#pragma once + +namespace Components +{ + class NetworkDebug : public Component + { + public: + NetworkDebug(); + + private: + static void CL_ParseServerMessage_Hk(Game::msg_t* msg); + + static void CL_ParseBadPacket_f(); + }; +} diff --git a/src/Components/Modules/News.cpp b/src/Components/Modules/News.cpp index 6816d8e2..4f3c72df 100644 --- a/src/Components/Modules/News.cpp +++ b/src/Components/Modules/News.cpp @@ -1,4 +1,8 @@ #include +#include "Changelog.hpp" +#include "News.hpp" + +#include #define NEWS_MOTD_DEFAULT "Welcome to IW4x Multiplayer!" @@ -11,12 +15,12 @@ namespace Components { bool result = true; - if (News::Thread.joinable()) + if (Thread.joinable()) { Logger::Debug("Awaiting thread termination..."); - News::Thread.join(); + Thread.join(); - if (!strcmp(Localization::Get("MPUI_MOTD_TEXT"), NEWS_MOTD_DEFAULT)) + if (!std::strcmp(Localization::Get("MPUI_MOTD_TEXT"), NEWS_MOTD_DEFAULT)) { Logger::Print("Failed to fetch motd!\n"); result = false; @@ -41,20 +45,18 @@ namespace Components Dvar::Register("g_firstLaunch", true, Game::DVAR_ARCHIVE, ""); - Dvar::Register("cl_updateoldversion", REVISION, REVISION, REVISION, Game::DVAR_INIT, "Current version number."); - UIScript::Add("checkFirstLaunch", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) { if (Dvar::Var("g_firstLaunch").get()) { Command::Execute("openmenu menu_first_launch", false); - //Dvar::Var("g_firstLaunch").set(false); + //Dvar::Var("g_firstLaunch").set(false); // The menus should set it } }); UIScript::Add("visitWebsite", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) { - Utils::OpenUrl(Utils::Cache::GetStaticUrl("")); + Utils::OpenUrl(Utils::Cache::GetUrl(Utils::Cache::Urls[1], {})); }); Localization::Set("MPUI_CHANGELOG_TEXT", "Loading..."); @@ -65,15 +67,15 @@ namespace Components // hook for getting the news ticker string Utils::Hook::Nop(0x6388BB, 2); // skip the "if (item->text[0] == '@')" localize check - Utils::Hook(0x6388C1, News::GetNewsText, HOOK_CALL).install()->quick(); + Utils::Hook(0x6388C1, GetNewsText, HOOK_CALL).install()->quick(); if (!Utils::IsWineEnvironment() && !Loader::IsPerformingUnitTests()) { - News::Terminate = false; - News::Thread = std::thread([]() + Terminate = false; + Thread = std::thread([]() { Changelog::LoadChangelog(); - if (News::Terminate) return; + if (Terminate) return; std::string data = Utils::Cache::GetFile("/iw4/motd.txt"); if (!data.empty()) @@ -81,12 +83,12 @@ namespace Components Localization::Set("MPUI_MOTD_TEXT", data); } - if (!Loader::IsPerformingUnitTests() && !News::Terminate) + if (!Loader::IsPerformingUnitTests() && !Terminate) { - while (!News::Terminate) + while (!Terminate) { // Sleep for 3 minutes - for (int i = 0; i < 180 && !News::Terminate; ++i) + for (int i = 0; i < 180 && !Terminate; ++i) { std::this_thread::sleep_for(1s); } @@ -98,13 +100,11 @@ namespace Components void News::preDestroy() { - News::Terminate = true; + Terminate = true; - if (News::Thread.joinable()) + if (Thread.joinable()) { - News::Thread.join(); + Thread.join(); } - - News::Thread = std::thread(); } } diff --git a/src/Components/Modules/Node.cpp b/src/Components/Modules/Node.cpp index 6ebe6811..5ad1dc5d 100644 --- a/src/Components/Modules/Node.cpp +++ b/src/Components/Modules/Node.cpp @@ -1,4 +1,6 @@ #include +#include "ServerList.hpp" +#include "Session.hpp" namespace Components { @@ -50,20 +52,6 @@ namespace Components this->lastRequest.reset(); } - void Node::LoadNodeRemotePreset() - { - std::string nodes = Utils::Cache::GetFile("/iw4/nodes.txt"); - if (nodes.empty()) return; - - auto nodeList = Utils::String::Split(nodes, '\n'); - for (auto& node : nodeList) - { - Utils::String::Replace(node, "\r", ""); - node = Utils::String::Trim(node); - Node::Add(node); - } - } - void Node::LoadNodePreset() { Proto::Node::List list; @@ -350,14 +338,6 @@ namespace Components Scheduler::OnGameInitialized(loadNodes, Scheduler::Pipeline::MAIN); - Network::OnStart([] - { - std::thread([] - { - Node::LoadNodeRemotePreset(); - }).detach(); - }); - Command::Add("listnodes", [](Command::Params*) { Logger::Print("Nodes: {}\n", Node::Nodes.size()); diff --git a/src/Components/Modules/Node.hpp b/src/Components/Modules/Node.hpp index 389a8945..e6e992ad 100644 --- a/src/Components/Modules/Node.hpp +++ b/src/Components/Modules/Node.hpp @@ -41,8 +41,6 @@ namespace Components static void RunFrame(); static void Synchronize(); - static void LoadNodeRemotePreset(); - private: static std::recursive_mutex Mutex; static std::vector Nodes; diff --git a/src/Components/Modules/Party.cpp b/src/Components/Modules/Party.cpp index fbf30e6c..4126726f 100644 --- a/src/Components/Modules/Party.cpp +++ b/src/Components/Modules/Party.cpp @@ -1,4 +1,11 @@ #include +#include "Download.hpp" +#include "Gamepad.hpp" +#include "ServerList.hpp" +#include "Stats.hpp" +#include "Voice.hpp" + +#include namespace Components { @@ -75,7 +82,7 @@ namespace Components return Party::Container.motd; } - Game::dvar_t* Party::RegisterMinPlayers(const char* name, int /*value*/, int /*min*/, int max, Game::DvarFlags flag, const char* description) + Game::dvar_t* Party::RegisterMinPlayers(const char* name, int /*value*/, int /*min*/, int max, unsigned __int16 flag, const char* description) { return Dvar::Register(name, 1, 1, max, Game::DVAR_INIT | flag, description).get(); } @@ -313,13 +320,15 @@ namespace Components // Basic info handler Network::OnClientPacket("getInfo", [](const Network::Address& address, [[maybe_unused]] const std::string& data) { - int botCount = 0; - int clientCount = 0; - int maxclientCount = *Game::svs_clientCount; + auto botCount = 0; + auto clientCount = 0; + auto maxClientCount = *Game::svs_clientCount; + const auto securityLevel = Dvar::Var("sv_securityLevel").get(); + const auto* password = *Game::g_password ? (*Game::g_password)->current.string : ""; - if (maxclientCount) + if (maxClientCount) { - for (int i = 0; i < maxclientCount; ++i) + for (int i = 0; i < maxClientCount; ++i) { if (Game::svs_clients[i].header.state >= Game::CS_CONNECTED) { @@ -330,7 +339,7 @@ namespace Components } else { - maxclientCount = Dvar::Var("party_maxplayers").get(); + maxClientCount = Dvar::Var("party_maxplayers").get(); clientCount = Game::PartyHost_CountMembers(reinterpret_cast(0x1081C00)); } @@ -341,16 +350,16 @@ namespace Components info.set("gametype", (*Game::sv_gametype)->current.string); info.set("fs_game", (*Game::fs_gameDirVar)->current.string); info.set("xuid", Utils::String::VA("%llX", Steam::SteamUser()->GetSteamID().bits)); - info.set("clients", Utils::String::VA("%i", clientCount)); - info.set("bots", Utils::String::VA("%i", botCount)); - info.set("sv_maxclients", Utils::String::VA("%i", maxclientCount)); - info.set("protocol", Utils::String::VA("%i", PROTOCOL)); + info.set("clients", std::to_string(clientCount)); + info.set("bots", std::to_string(botCount)); + info.set("sv_maxclients", std::to_string(maxClientCount)); + info.set("protocol", std::to_string(PROTOCOL)); info.set("shortversion", SHORTVERSION); - info.set("checksum", Utils::String::VA("%d", Game::Sys_Milliseconds())); - info.set("mapname", Dvar::Var("mapname").get()); - info.set("isPrivate", (Dvar::Var("g_password").get().size() ? "1" : "0")); + info.set("checksum", std::to_string(Game::Sys_Milliseconds())); + info.set("mapname", Dvar::Var("mapname").get()); + info.set("isPrivate", *password ? "1" : "0"); info.set("hc", (Dvar::Var("g_hardcore").get() ? "1" : "0")); - info.set("securityLevel", Utils::String::VA("%i", Dvar::Var("sv_securityLevel").get())); + info.set("securityLevel", std::to_string(securityLevel)); info.set("sv_running", (Dedicated::IsRunning() ? "1" : "0")); info.set("aimAssist", (Gamepad::sv_allowAimAssist.get() ? "1" : "0")); info.set("voiceChat", (Voice::SV_VoiceEnabled() ? "1" : "0")); diff --git a/src/Components/Modules/Party.hpp b/src/Components/Modules/Party.hpp index dada08a8..3287cecd 100644 --- a/src/Components/Modules/Party.hpp +++ b/src/Components/Modules/Party.hpp @@ -50,7 +50,7 @@ namespace Components static SteamID GenerateLobbyId(); - static Game::dvar_t* RegisterMinPlayers(const char* name, int value, int min, int max, Game::DvarFlags flag, const char* description); + static Game::dvar_t* RegisterMinPlayers(const char* name, int value, int min, int max, unsigned __int16 flag, const char* description); static DWORD UIDvarIntStub(char* dvar); }; diff --git a/src/Components/Modules/PlayerMovement.cpp b/src/Components/Modules/PlayerMovement.cpp new file mode 100644 index 00000000..749f15df --- /dev/null +++ b/src/Components/Modules/PlayerMovement.cpp @@ -0,0 +1,315 @@ +#include +#include "PlayerMovement.hpp" + +#include "GSC/Script.hpp" + +namespace Components +{ + Dvar::Var PlayerMovement::BGRocketJump; + Dvar::Var PlayerMovement::BGRocketJumpScale; + Dvar::Var PlayerMovement::BGPlayerEjection; + Dvar::Var PlayerMovement::BGPlayerCollision; + Dvar::Var PlayerMovement::BGClimbAnything; + const Game::dvar_t* PlayerMovement::CGNoclipScaler; + const Game::dvar_t* PlayerMovement::CGUfoScaler; + const Game::dvar_t* PlayerMovement::PlayerSpectateSpeedScale; + const Game::dvar_t* PlayerMovement::BGBounces; + const Game::dvar_t* PlayerMovement::BGBouncesAllAngles; + const Game::dvar_t* PlayerMovement::PlayerDuckedSpeedScale; + const Game::dvar_t* PlayerMovement::PlayerProneSpeedScale; + + void PlayerMovement::PM_PlayerTraceStub(Game::pmove_s* pm, Game::trace_t* results, const float* start, const float* end, Game::Bounds* bounds, int passEntityNum, int contentMask) + { + Game::PM_playerTrace(pm, results, start, end, bounds, passEntityNum, contentMask); + + if (results && BGClimbAnything.get()) + { + results[0].surfaceFlags |= SURF_LADDER; + } + } + + __declspec(naked) void PlayerMovement::PM_PlayerDuckedSpeedScaleStub() + { + __asm + { + push eax + mov eax, PlayerDuckedSpeedScale + fld dword ptr [eax + 0x10] // dvar_t.current.value + pop eax + + // Game's code + pop ecx + ret + } + } + + __declspec(naked) void PlayerMovement::PM_PlayerProneSpeedScaleStub() + { + __asm + { + push eax + mov eax, PlayerProneSpeedScale + fld dword ptr [eax + 0x10] // dvar_t.current.value + pop eax + + // Game's code + pop ecx + ret + } + } + + __declspec(naked) void PlayerMovement::PM_MoveScale_Noclip() + { + __asm + { + mov eax, CGNoclipScaler + fld dword ptr [eax + 0x10] // dvar_t.current.value + fmul dword ptr [esp + 0xC] + fstp dword ptr [esp + 0xC] + + push 0x56F43A + ret + } + } + + __declspec(naked) void PlayerMovement::PM_MoveScale_Ufo() + { + __asm + { + mov eax, CGUfoScaler + fld dword ptr [eax + 0x10] // dvar_t.current.value + fmul dword ptr [esp + 0xC] + fstp dword ptr [esp + 0xC] + + push 0x56F44D + ret + } + } + + __declspec(naked) void PlayerMovement::PM_MoveScale_Spectate() + { + __asm + { + mov eax, PlayerSpectateSpeedScale + fld dword ptr [eax + 0x10] // dvar_t.current.value + fmul dword ptr [esp + 0xC] + fstp dword ptr [esp + 0xC] + + push 0x56F462 + ret + } + } + + __declspec(naked) void PlayerMovement::PM_StepSlideMoveStub() + { + __asm + { + // Check the value of BGBounces + push ecx + push eax + + mov eax, BGBounces + mov ecx, dword ptr [eax + 0x10] + test ecx, ecx + + pop eax + pop ecx + + // Do not bounce if BGBounces is 0 + jle noBounce + + // Bounce + push 0x4B1B34 + ret + + noBounce: + // Original game code + cmp dword ptr [esp + 0x24], 0 + push 0x4B1B48 + ret + } + } + + // Double bounces + void PlayerMovement::Jump_ClearState_Hk(Game::playerState_s* ps) + { + if (BGBounces->current.integer != DOUBLE) + { + Game::Jump_ClearState(ps); + } + } + + __declspec(naked) void PlayerMovement::PM_ProjectVelocityStub() + { + __asm + { + push eax + mov eax, BGBouncesAllAngles + cmp byte ptr [eax + 0x10], 1 + pop eax + + je bounce + + fstp ST(0) + pop esi + add esp, 0x10 + ret + + bounce: + push 0x417B6F + ret + } + } + + Game::gentity_s* PlayerMovement::Weapon_RocketLauncher_Fire_Hk(Game::gentity_s* ent, unsigned int weaponIndex, + float spread, Game::weaponParms* wp, const float* gunVel, Game::lockonFireParms* lockParms, bool magicBullet) + { + auto* result = Game::Weapon_RocketLauncher_Fire(ent, weaponIndex, spread, wp, gunVel, lockParms, magicBullet); + + if (ent->client != nullptr && BGRocketJump.get() && + wp->weapDef->inventoryType != Game::WEAPINVENTORY_EXCLUSIVE) + { + const auto scale = BGRocketJumpScale.get(); + ent->client->ps.velocity[0] += (0.0f - wp->forward[0]) * scale; + ent->client->ps.velocity[1] += (0.0f - wp->forward[1]) * scale; + ent->client->ps.velocity[2] += (0.0f - wp->forward[2]) * scale; + } + + return result; + } + + int PlayerMovement::StuckInClient_Hk(Game::gentity_s* self) + { + if (BGPlayerEjection.get()) + { + return Utils::Hook::Call(0x402D30)(self); // StuckInClient + } + + return 0; + } + + void PlayerMovement::CM_TransformedCapsuleTrace_Hk(Game::trace_t* results, const float* start, const float* end, + const Game::Bounds* bounds, const Game::Bounds* capsule, int contents, const float* origin, const float* angles) + { + if (BGPlayerCollision.get()) + { + Utils::Hook::Call + (0x478300) + (results, start, end, bounds, capsule, contents, origin, angles); // CM_TransformedCapsuleTrace + } + } + + void PlayerMovement::GScr_IsSprinting(const Game::scr_entref_t entref) + { + const auto* client = Game::GetEntity(entref)->client; + if (!client) + { + Game::Scr_Error("IsSprinting can only be called on a player"); + return; + } + + Game::Scr_AddBool(Game::PM_IsSprinting(&client->ps)); + } + + const Game::dvar_t* PlayerMovement::Dvar_RegisterSpectateSpeedScale(const char* dvarName, float value, + float min, float max, unsigned __int16 /*flags*/, const char* description) + { + PlayerSpectateSpeedScale = Game::Dvar_RegisterFloat(dvarName, value, + min, max, Game::DVAR_CHEAT | Game::DVAR_CODINFO, description); + + return PlayerSpectateSpeedScale; + } + + void PlayerMovement::RegisterMovementDvars() + { + PlayerDuckedSpeedScale = Game::Dvar_RegisterFloat("player_duckedSpeedScale", + 0.65f, 0.0f, 5.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO, + "The scale applied to the player speed when ducking"); + + PlayerProneSpeedScale = Game::Dvar_RegisterFloat("player_proneSpeedScale", + 0.15f, 0.0f, 5.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO, + "The scale applied to the player speed when crawling"); + + // 3arc naming convention + CGUfoScaler = Game::Dvar_RegisterFloat("cg_ufo_scaler", + 6.0f, 0.001f, 1000.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO, + "The speed at which ufo camera moves"); + + CGNoclipScaler = Game::Dvar_RegisterFloat("cg_noclip_scaler", + 3.0f, 0.001f, 1000.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO, + "The speed at which noclip camera moves"); + + BGBouncesAllAngles = Game::Dvar_RegisterBool("bg_bouncesAllAngles", + false, Game::DVAR_CODINFO, "Force bounce from all angles"); + + BGRocketJump = Dvar::Register("bg_rocketJump", + false, Game::DVAR_CODINFO, "Enable CoD4 rocket jumps"); + + BGRocketJumpScale = Dvar::Register("bg_rocketJumpScale", + 64.0f, 1.0f, std::numeric_limits::max(), Game::DVAR_CODINFO, + "The scale applied to the pushback force of a rocket"); + + BGPlayerEjection = Dvar::Register("bg_playerEjection", + true, Game::DVAR_CODINFO, "Push intersecting players away from each other"); + + BGPlayerCollision = Dvar::Register("bg_playerCollision", + true, Game::DVAR_CODINFO, "Push intersecting players away from each other"); + + BGClimbAnything = Dvar::Register("bg_climbAnything", + false, Game::DVAR_CODINFO, "Treat any surface as a ladder"); + } + + PlayerMovement::PlayerMovement() + { + AssertOffset(Game::playerState_s, eFlags, 0xB0); + AssertOffset(Game::playerState_s, pm_flags, 0xC); + + Scheduler::Once([] + { + static const char* bg_bouncesValues[] = + { + "disabled", + "enabled", + "double", + nullptr + }; + + BGBounces = Game::Dvar_RegisterEnum("bg_bounces", + bg_bouncesValues, DISABLED, Game::DVAR_CODINFO, "Bounce glitch settings"); + }, Scheduler::Pipeline::MAIN); + + // Hook Dvar_RegisterFloat. Only thing that's changed is that the 0x80 flag is not used. + Utils::Hook(0x448990, Dvar_RegisterSpectateSpeedScale, HOOK_CALL).install()->quick(); + + // PM_CmdScaleForStance + Utils::Hook(0x572D9B, PM_PlayerDuckedSpeedScaleStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x572DA5, PM_PlayerProneSpeedScaleStub, HOOK_JUMP).install()->quick(); + + // Hook PM_MoveScale so we can add custom speed scale for Ufo and Noclip + Utils::Hook(0x56F42C, PM_MoveScale_Noclip, HOOK_JUMP).install()->quick(); + Utils::Hook(0x56F43F, PM_MoveScale_Ufo, HOOK_JUMP).install()->quick(); + Utils::Hook(0x56F452, PM_MoveScale_Spectate, HOOK_JUMP).install()->quick(); + + // Bounce logic + Utils::Hook(0x4B1B2D, PM_StepSlideMoveStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x57383E, Jump_ClearState_Hk, HOOK_CALL).install()->quick(); + Utils::Hook(0x417B66, PM_ProjectVelocityStub, HOOK_JUMP).install()->quick(); + + // Rocket jump + Utils::Hook(0x4A4F9B, Weapon_RocketLauncher_Fire_Hk, HOOK_CALL).install()->quick(); // FireWeapon + + // Hook StuckInClient & CM_TransformedCapsuleTrace + // so we can prevent intersecting players from being pushed away from each other + Utils::Hook(0x5D8153, StuckInClient_Hk, HOOK_CALL).install()->quick(); + Utils::Hook(0x45A5BF, CM_TransformedCapsuleTrace_Hk, HOOK_CALL).install()->quick(); // SV_ClipMoveToEntity + Utils::Hook(0x5A0CAD, CM_TransformedCapsuleTrace_Hk, HOOK_CALL).install()->quick(); // CG_ClipMoveToEntity + + Utils::Hook(0x573F39, PM_PlayerTraceStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x573E93, PM_PlayerTraceStub, HOOK_CALL).install()->quick(); + + Script::AddMethod("IsSprinting", GScr_IsSprinting); + + RegisterMovementDvars(); + } +} diff --git a/src/Components/Modules/Movement.hpp b/src/Components/Modules/PlayerMovement.hpp similarity index 50% rename from src/Components/Modules/Movement.hpp rename to src/Components/Modules/PlayerMovement.hpp index ddcd7bf2..d86d8394 100644 --- a/src/Components/Modules/Movement.hpp +++ b/src/Components/Modules/PlayerMovement.hpp @@ -2,36 +2,41 @@ namespace Components { - class Movement : public Component + class PlayerMovement : public Component { public: - Movement(); + PlayerMovement(); private: enum BouncesSettings { DISABLED, ENABLED, DOUBLE }; - static Dvar::Var PlayerSpectateSpeedScale; - static Dvar::Var CGUfoScaler; - static Dvar::Var CGNoclipScaler; - static Dvar::Var BGBouncesAllAngles; + static constexpr auto SURF_LADDER = 0x8; + static Dvar::Var BGRocketJump; static Dvar::Var BGRocketJumpScale; static Dvar::Var BGPlayerEjection; static Dvar::Var BGPlayerCollision; + static Dvar::Var BGClimbAnything; // Can't use Var class inside assembly stubs - static Game::dvar_t* BGBounces; - static Game::dvar_t* PlayerDuckedSpeedScale; - static Game::dvar_t* PlayerProneSpeedScale; - + static const Game::dvar_t* CGNoclipScaler; + static const Game::dvar_t* CGUfoScaler; + static const Game::dvar_t* PlayerSpectateSpeedScale; + static const Game::dvar_t* BGBounces; + static const Game::dvar_t* BGBouncesAllAngles; + static const Game::dvar_t* PlayerDuckedSpeedScale; + static const Game::dvar_t* PlayerProneSpeedScale; + + static void PM_PlayerTraceStub(Game::pmove_s* pm, Game::trace_t* results, const float* start, const float* end, Game::Bounds* bounds, int passEntityNum, int contentMask); static void PM_PlayerDuckedSpeedScaleStub(); static void PM_PlayerProneSpeedScaleStub(); - static float PM_MoveScale(Game::playerState_s* ps, float fmove, float rmove, float umove); - static void PM_MoveScaleStub(); + static void PM_MoveScale_Noclip(); + static void PM_MoveScale_Ufo(); + static void PM_MoveScale_Spectate(); // Bounce logic static void PM_StepSlideMoveStub(); - static void PM_ProjectVelocityStub(const float* velIn, const float* normal, float* velOut); + static void PM_ProjectVelocityStub(); static void Jump_ClearState_Hk(Game::playerState_s* ps); static Game::gentity_s* Weapon_RocketLauncher_Fire_Hk(Game::gentity_s* ent, unsigned int weaponIndex, float spread, Game::weaponParms* wp, const float* gunVel, Game::lockonFireParms* lockParms, bool a7); @@ -40,7 +45,9 @@ namespace Components static int StuckInClient_Hk(Game::gentity_s* self); static void CM_TransformedCapsuleTrace_Hk(Game::trace_t* results, const float* start, const float* end, const Game::Bounds* bounds, const Game::Bounds* capsule, int contents, const float* origin, const float* angles); - static Game::dvar_t* Dvar_RegisterSpectateSpeedScale(const char* dvarName, float value, float min, float max, unsigned __int16 flags, const char* description); + static void GScr_IsSprinting(Game::scr_entref_t entref); + + static const Game::dvar_t* Dvar_RegisterSpectateSpeedScale(const char* dvarName, float value, float min, float max, unsigned __int16 flags, const char* description); static void RegisterMovementDvars(); }; diff --git a/src/Components/Modules/PlayerName.cpp b/src/Components/Modules/PlayerName.cpp index 11ca774d..2d32fd47 100644 --- a/src/Components/Modules/PlayerName.cpp +++ b/src/Components/Modules/PlayerName.cpp @@ -1,4 +1,6 @@ #include +#include "ClanTags.hpp" +#include "PlayerName.hpp" namespace Components { @@ -18,7 +20,7 @@ namespace Components } std::string readablePlayerName(buffer); - readablePlayerName = Utils::String::Trim(readablePlayerName); + Utils::String::Trim(readablePlayerName); if (readablePlayerName.size() < 3) { diff --git a/src/Components/Modules/Playlist.cpp b/src/Components/Modules/Playlist.cpp index 8e25475f..52aa12b5 100644 --- a/src/Components/Modules/Playlist.cpp +++ b/src/Components/Modules/Playlist.cpp @@ -1,4 +1,5 @@ #include +#include "Playlist.hpp" namespace Components { @@ -9,26 +10,26 @@ namespace Components void Playlist::LoadPlaylist() { // Check if playlist already loaded - if (Utils::Hook::Get(0x1AD3680)) return; + if (*Game::s_havePlaylists) return; // Don't load playlists when dedi and no party if (Dedicated::IsEnabled() && !Dvar::Var("party_enable").get()) { - Utils::Hook::Set(0x1AD3680, true); // Set received to true + *Game::s_havePlaylists = true; Dvar::Var("xblive_privateserver").set(true); return; } Dvar::Var("xblive_privateserver").set(false); - auto playlistFilename = Dvar::Var("playlistFilename").get(); + const auto playlistFilename = Dvar::Var("playlistFilename").get(); FileSystem::File playlist(playlistFilename); if (playlist.exists()) { Logger::Print("Parsing playlist '{}'...\n", playlist.getName()); Game::Playlist_ParsePlaylists(playlist.getBuffer().data()); - Utils::Hook::Set(0x1AD3680, true); // Playlist loaded + *Game::s_havePlaylists = true; } else { @@ -36,18 +37,18 @@ namespace Components } } - DWORD Playlist::StorePlaylistStub(const char** buffer) + char* Playlist::Com_ParseOnLine_Hk(const char** data_p) { - Playlist::MapRelocation.clear(); - Playlist::CurrentPlaylistBuffer = Utils::Compression::ZLib::Compress(*buffer); - return Utils::Hook::Call(0x4C0350)(buffer); + MapRelocation.clear(); + CurrentPlaylistBuffer = Utils::Compression::ZLib::Compress(*data_p); + return Game::Com_ParseOnLine(data_p); } void Playlist::PlaylistRequest(const Network::Address& address, [[maybe_unused]] const std::string& data) { - const auto password = Dvar::Var("g_password").get(); + const auto* password = *Game::g_password ? (*Game::g_password)->current.string : ""; - if (password.length()) + if (*password) { if (password != data) { @@ -58,7 +59,7 @@ namespace Components Logger::Print("Received playlist request, sending currently stored buffer.\n"); - std::string compressedList = Playlist::CurrentPlaylistBuffer; + std::string compressedList = CurrentPlaylistBuffer; Proto::Party::Playlist list; list.set_hash(Utils::Cryptography::JenkinsOneAtATime::Compute(compressedList)); @@ -67,51 +68,48 @@ namespace Components Network::SendCommand(address, "playlistResponse", list.SerializeAsString()); } - void Playlist::PlaylistReponse(const Network::Address& address, [[maybe_unused]] const std::string& data) + void Playlist::PlaylistResponse(const Network::Address& address, [[maybe_unused]] const std::string& data) { - if (Party::PlaylistAwaiting()) + if (!Party::PlaylistAwaiting()) { - if (address == Party::Target()) - { - Proto::Party::Playlist list; + Logger::Print("Received stray playlist response, ignoring it.\n"); + return; + } + + if (address != Party::Target()) + { + Logger::Print("Received playlist from someone else than our target host, ignoring it.\n"); + return; + } + + Proto::Party::Playlist list; - if (!list.ParseFromString(data)) - { - Party::PlaylistError(Utils::String::VA("Received playlist response from %s, but it is invalid.", address.getCString())); - Playlist::ReceivedPlaylistBuffer.clear(); - return; - } - else - { - // Generate buffer and hash - const auto& compressedData = list.buffer(); - const auto hash = Utils::Cryptography::JenkinsOneAtATime::Compute(compressedData); - - //Validate hashes - if (hash != list.hash()) - { - Party::PlaylistError(Utils::String::VA("Received playlist response from %s, but the checksum did not match (%X != %X).", address.getCString(), list.hash(), hash)); - Playlist::ReceivedPlaylistBuffer.clear(); - return; - } - - // Decompress buffer - Playlist::ReceivedPlaylistBuffer = Utils::Compression::ZLib::Decompress(compressedData); - - // Load and continue connection - Logger::Print("Received playlist, loading and continuing connection...\n"); - Game::Playlist_ParsePlaylists(Playlist::ReceivedPlaylistBuffer.data()); - Party::PlaylistContinue(); - } - } - else - { - Logger::Print("Received playlist from someone else than our target host, ignoring it.\n"); - } + if (!list.ParseFromString(data)) + { + Party::PlaylistError(std::format("Received playlist response from {}, but it is invalid.", address.getString())); + ReceivedPlaylistBuffer.clear(); } else { - Logger::Print("Received stray playlist response, ignoring it.\n"); + // Generate buffer and hash + const auto& compressedData = list.buffer(); + const auto hash = Utils::Cryptography::JenkinsOneAtATime::Compute(compressedData); + + // Validate hashes + if (hash != list.hash()) + { + Party::PlaylistError(std::format("Received playlist response from {}, but the checksum did not match ({} != {}).", address.getString(), list.hash(), hash)); + ReceivedPlaylistBuffer.clear(); + return; + } + + // Decompress buffer + ReceivedPlaylistBuffer = Utils::Compression::ZLib::Decompress(compressedData); + + // Load and continue connection + Logger::Print("Received playlist, loading and continuing connection...\n"); + Game::Playlist_ParsePlaylists(ReceivedPlaylistBuffer.data()); + Party::PlaylistContinue(); } } @@ -120,27 +118,27 @@ namespace Components Party::PlaylistError("Error: Invalid Password for Party."); } - void Playlist::MapNameCopy(char *dest, const char *src, int destsize) + void Playlist::MapNameCopy(char* dest, const char* src, int destsize) { Utils::Hook::Call(0x4D6F80)(dest, src, destsize); - Playlist::MapRelocation[dest] = src; + MapRelocation[dest] = src; } - void Playlist::SetMapName(const char* cvar, const char* value) + void Playlist::SetMapName(const char* dvarName, const char* value) { - auto i = Playlist::MapRelocation.find(value); - if (i != Playlist::MapRelocation.end()) + auto i = MapRelocation.find(value); + if (i != MapRelocation.end()) { value = i->second.data(); } - Game::Dvar_SetStringByName(cvar, value); + Game::Dvar_SetStringByName(dvarName, value); } int Playlist::GetMapIndex(const char* mapname) { - auto i = Playlist::MapRelocation.find(mapname); - if (i != Playlist::MapRelocation.end()) + auto i = MapRelocation.find(mapname); + if (i != MapRelocation.end()) { mapname = i->second.data(); } @@ -153,7 +151,7 @@ namespace Components // Default playlists Utils::Hook::Set(0x60B06E, "playlists_default.info"); - // disable playlist download function + // Disable playlist download function Utils::Hook::Set(0x4D4790, 0xC3); // Load playlist, but don't delete it @@ -161,34 +159,34 @@ namespace Components Utils::Hook::Nop(0x4D6E67, 5); Utils::Hook::Nop(0x4D6E71, 2); - // playlist dvar 'validity check' + // Disable Playlist_ValidatePlaylistNum Utils::Hook::Set(0x4B1170, 0xC3); - // disable playlist checking - Utils::Hook::Set(0x5B69E9, 0xEB); // too new - Utils::Hook::Set(0x5B696E, 0xEB); // too old + // Disable playlist checking + Utils::Hook::Set(0x5B69E9, 0xEB); // Too new + Utils::Hook::Set(0x5B696E, 0xEB); // Too old - //Got playlists is true + // Got playlists is true //Utils::Hook::Set(0x1AD3680, true); - Utils::Hook(0x497DB5, Playlist::GetMapIndex, HOOK_CALL).install()->quick(); - Utils::Hook(0x42A19D, Playlist::MapNameCopy, HOOK_CALL).install()->quick(); - Utils::Hook(0x4A6FEE, Playlist::SetMapName, HOOK_CALL).install()->quick(); + Utils::Hook(0x497DB5, GetMapIndex, HOOK_CALL).install()->quick(); + Utils::Hook(0x42A19D, MapNameCopy, HOOK_CALL).install()->quick(); + Utils::Hook(0x4A6FEE, SetMapName, HOOK_CALL).install()->quick(); // Store playlist buffer on load - Utils::Hook(0x42961C, Playlist::StorePlaylistStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x42961C, Com_ParseOnLine_Hk, HOOK_CALL).install()->quick(); // Playlist_ParsePlaylists //if (Dedicated::IsDedicated()) { // Custom playlist loading - Utils::Hook(0x420B5A, Playlist::LoadPlaylist, HOOK_JUMP).install()->quick(); + Utils::Hook(0x420B5A, LoadPlaylist, HOOK_JUMP).install()->quick(); - // disable playlist.ff loading function - Utils::Hook::Set(0x4D6E60, 0xC3); + // disable playlist.ff loading function (Win_LoadPlaylistFastfile) + Utils::Hook::Set(0x4D6E60, 0xC3); } Network::OnClientPacket("getPlaylist", PlaylistRequest); - Network::OnClientPacket("playlistResponse", PlaylistReponse); + Network::OnClientPacket("playlistResponse", PlaylistResponse); Network::OnClientPacket("playlistInvalidPassword", PlaylistInvalidPassword); } } diff --git a/src/Components/Modules/Playlist.hpp b/src/Components/Modules/Playlist.hpp index f4948d0e..397fd807 100644 --- a/src/Components/Modules/Playlist.hpp +++ b/src/Components/Modules/Playlist.hpp @@ -17,14 +17,14 @@ namespace Components static std::string CurrentPlaylistBuffer; static std::unordered_map MapRelocation; - static DWORD StorePlaylistStub(const char** buffer); + static char* Com_ParseOnLine_Hk(const char** data_p); static void PlaylistRequest(const Network::Address& address, const std::string& data); - static void PlaylistReponse(const Network::Address& address, const std::string& data); + static void PlaylistResponse(const Network::Address& address, const std::string& data); static void PlaylistInvalidPassword(const Network::Address& address, const std::string& data); - static void MapNameCopy(char *dest, const char *src, int destsize); - static void SetMapName(const char* cvar, const char* value); + static void MapNameCopy(char* dest, const char* src, int destsize); + static void SetMapName(const char* dvarName, const char* value); static int GetMapIndex(const char* mapname); }; } diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index 22076555..5631fcca 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -1,4 +1,5 @@ #include +#include "QuickPatch.hpp" namespace Components { @@ -47,20 +48,6 @@ namespace Components } } - __declspec(naked) void QuickPatch::JavelinResetHook_Stub() - { - __asm - { - mov eax, 577A10h; - call eax; - pop edi; - mov dword ptr [esi+34h], 0; - pop esi; - pop ebx; - retn; - } - } - Game::dvar_t* QuickPatch::g_antilag; __declspec(naked) void QuickPatch::ClientEventsFireWeapon_Stub() { @@ -315,9 +302,6 @@ namespace Components Utils::Hook(0x5D6D56, QuickPatch::ClientEventsFireWeapon_Stub, HOOK_JUMP).install()->quick(); Utils::Hook(0x5D6D6A, QuickPatch::ClientEventsFireWeaponMelee_Stub, HOOK_JUMP).install()->quick(); - // Javelin fix - Utils::Hook(0x578F52, QuickPatch::JavelinResetHook_Stub, HOOK_JUMP).install()->quick(); - // Add ultrawide support Utils::Hook(0x51B13B, QuickPatch::Dvar_RegisterAspectRatioDvar, HOOK_CALL).install()->quick(); Utils::Hook(0x5063F3, QuickPatch::SetAspectRatio_Stub, HOOK_JUMP).install()->quick(); @@ -332,9 +316,6 @@ namespace Components // Fix crash as nullptr goes unchecked Utils::Hook(0x437CAD, QuickPatch::SND_GetAliasOffset_Stub, HOOK_JUMP).install()->quick(); - // Make VA thread safe - Utils::Hook(0x4785B0, Utils::String::VA, HOOK_JUMP).install()->quick(); - // protocol version (workaround for hacks) Utils::Hook::Set(0x4FB501, PROTOCOL); @@ -370,20 +351,6 @@ namespace Components // fs_basegame Utils::Hook::Set(0x6431D1, BASEGAME); - // console title - if (ZoneBuilder::IsEnabled()) - { - Utils::Hook::Set(0x4289E8, "IW4x (" VERSION "): ZoneBuilder"); - } - else if (Dedicated::IsEnabled()) - { - Utils::Hook::Set(0x4289E8, "IW4x (" VERSION "): Dedicated"); - } - else - { - Utils::Hook::Set(0x4289E8, "IW4x (" VERSION "): Console"); - } - // window title Utils::Hook::Set(0x5076A0, "IW4x: Multiplayer"); @@ -610,7 +577,8 @@ namespace Components formatString = "userraw/shader_bin/%.vs"; } - if (Utils::IO::FileExists(Utils::String::VA(formatString, name.data()))) return; + const auto path = std::format("{}{}", formatString, name); + if (Utils::IO::FileExists(path)) return; Utils::Stream buffer(0x1000); auto* dest = buffer.dest(); @@ -622,7 +590,7 @@ namespace Components Utils::Stream::ClearPointer(&dest->prog.loadDef.program); } - Utils::IO::WriteFile(Utils::String::VA(formatString, name.data()), buffer.toBuffer()); + Utils::IO::WriteFile(path, buffer.toBuffer()); } if (type == Game::ASSET_TYPE_TECHNIQUE_SET) diff --git a/src/Components/Modules/QuickPatch.hpp b/src/Components/Modules/QuickPatch.hpp index 64fa0793..58679706 100644 --- a/src/Components/Modules/QuickPatch.hpp +++ b/src/Components/Modules/QuickPatch.hpp @@ -12,8 +12,6 @@ namespace Components static void UnlockStats(); private: - static void JavelinResetHook_Stub(); - static Dvar::Var r_customAspectRatio; static Game::dvar_t* Dvar_RegisterAspectRatioDvar(const char* dvarName, const char** valueList, int defaultIndex, unsigned __int16 flags, const char* description); static void SetAspectRatio_Stub(); diff --git a/src/Components/Modules/RCon.cpp b/src/Components/Modules/RCon.cpp index 57fa3324..56cf17ba 100644 --- a/src/Components/Modules/RCon.cpp +++ b/src/Components/Modules/RCon.cpp @@ -1,7 +1,10 @@ #include +#include "RCon.hpp" namespace Components { + std::unordered_map RCon::RateLimit; + RCon::Container RCon::RconContainer; Utils::Cryptography::ECC::Key RCon::RconKey; @@ -71,6 +74,43 @@ namespace Components }); } + bool RCon::RateLimitCheck(const Network::Address& address, const int time) + { + const auto ip = address.getIP(); + + if (!RateLimit.contains(ip.full)) + { + RateLimit[ip.full] = 0; + } + + const auto lastTime = RateLimit[ip.full]; + + // Only one request every 500ms + if (lastTime && (time - lastTime) < 500) + { + return false; // Flooding + } + + RateLimit[ip.full] = time; + return true; + } + + void RCon::RateLimitCleanup(const int time) + { + for (auto i = RateLimit.begin(); i != RateLimit.end();) + { + // No longer at risk of flooding, remove + if ((time - i->second) > 500) + { + i = RateLimit.erase(i); + } + else + { + ++i; + } + } + } + RCon::RCon() { AddCommands(); @@ -126,9 +166,17 @@ namespace Components Network::OnClientPacket("rcon", [](const Network::Address& address, [[maybe_unused]] const std::string& data) { - std::string data_ = data; + const auto time = Game::Sys_Milliseconds(); + if (!RateLimitCheck(address, time)) + { + return; + } + RateLimitCleanup(time); + + std::string data_ = data; Utils::String::Trim(data_); + const auto pos = data.find_first_of(' '); if (pos == std::string::npos) { diff --git a/src/Components/Modules/RCon.hpp b/src/Components/Modules/RCon.hpp index 78b07dea..22c8cfc0 100644 --- a/src/Components/Modules/RCon.hpp +++ b/src/Components/Modules/RCon.hpp @@ -29,6 +29,8 @@ namespace Components static Utils::Cryptography::ECC::Key GetKeyInternal(); }; + static std::unordered_map RateLimit; + static Container RconContainer; static Utils::Cryptography::ECC::Key RconKey; @@ -38,5 +40,8 @@ namespace Components static Dvar::Var RconLogRequests; static void AddCommands(); + + static bool RateLimitCheck(const Network::Address& address, int time); + static void RateLimitCleanup(int time); }; } diff --git a/src/Components/Modules/RawFiles.cpp b/src/Components/Modules/RawFiles.cpp index e3c3848a..95a8556f 100644 --- a/src/Components/Modules/RawFiles.cpp +++ b/src/Components/Modules/RawFiles.cpp @@ -1,4 +1,5 @@ #include +#include "RawFiles.hpp" namespace Components { diff --git a/src/Components/Modules/RawMouse.cpp b/src/Components/Modules/RawMouse.cpp index f48d8f33..bc046d7b 100644 --- a/src/Components/Modules/RawMouse.cpp +++ b/src/Components/Modules/RawMouse.cpp @@ -1,4 +1,6 @@ #include +#include "Gamepad.hpp" +#include "RawMouse.hpp" namespace Components { @@ -80,7 +82,9 @@ namespace Components if (GetForegroundWindow() == Window::GetWindow()) { if (r_fullscreen.get()) + { IN_ClampMouseMove(); + } static auto oldX = 0, oldY = 0; @@ -109,7 +113,7 @@ namespace Components void RawMouse::IN_RawMouse_Init() { - if (Window::GetWindow() && RawMouse::M_RawInput.get()) + if (Window::GetWindow() && M_RawInput.get()) { Logger::Debug("Raw Mouse Init"); @@ -131,7 +135,7 @@ namespace Components void RawMouse::IN_MouseMove() { - if (RawMouse::M_RawInput.get()) + if (M_RawInput.get()) { IN_RawMouseMove(); } @@ -143,15 +147,15 @@ namespace Components RawMouse::RawMouse() { - Utils::Hook(0x475E65, RawMouse::IN_MouseMove, HOOK_JUMP).install()->quick(); - Utils::Hook(0x475E8D, RawMouse::IN_MouseMove, HOOK_JUMP).install()->quick(); + Utils::Hook(0x475E65, IN_MouseMove, HOOK_JUMP).install()->quick(); + Utils::Hook(0x475E8D, IN_MouseMove, HOOK_JUMP).install()->quick(); - Utils::Hook(0x467C03, RawMouse::IN_Init, HOOK_CALL).install()->quick(); - Utils::Hook(0x64D095, RawMouse::IN_Init, HOOK_JUMP).install()->quick(); + Utils::Hook(0x467C03, IN_Init, HOOK_CALL).install()->quick(); + Utils::Hook(0x64D095, IN_Init, HOOK_JUMP).install()->quick(); - RawMouse::M_RawInput = Dvar::Register("m_rawinput", true, Game::DVAR_ARCHIVE, "Use raw mouse input, Improves accuracy & has better support for higher polling rates. Use in_restart to take effect if not enabled."); + M_RawInput = Dvar::Register("m_rawinput", true, Game::DVAR_ARCHIVE, "Use raw mouse input, Improves accuracy & has better support for higher polling rates"); - Window::OnWndMessage(WM_INPUT, RawMouse::OnRawInput); - Window::OnCreate(RawMouse::IN_RawMouse_Init); + Window::OnWndMessage(WM_INPUT, OnRawInput); + Window::OnCreate(IN_RawMouse_Init); } } diff --git a/src/Components/Modules/Renderer.cpp b/src/Components/Modules/Renderer.cpp index 16bb5b66..853e1d78 100644 --- a/src/Components/Modules/Renderer.cpp +++ b/src/Components/Modules/Renderer.cpp @@ -14,6 +14,7 @@ namespace Components Dvar::Var Renderer::r_drawModelNames; Dvar::Var Renderer::r_drawAABBTrees; Dvar::Var Renderer::r_playerDrawDebugDistance; + Dvar::Var Renderer::r_forceTechnique; float cyan[4] = { 0.0f, 0.5f, 0.5f, 1.0f }; float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; @@ -171,6 +172,32 @@ namespace Components return Utils::Hook::Call(0x005033E0)(a1, a2, a3, Utils::String::VA("%s (^3%s^7)", mat->info.name, mat->techniqueSet->name), color, a6); } + void ListSamplers() + { + static auto* source = reinterpret_cast(0x6CAF080); + + Game::Font_s* font = Game::R_RegisterFont("fonts/smallFont", 0); + auto height = Game::R_TextHeight(font); + auto scale = 1.0f; + float color[4] = {0.0f, 1.0f, 0.0f, 1.0f}; + + for (std::size_t i = 0; i < 27; ++i) + { + if (source->input.codeImages[i] == nullptr) + { + color[0] = 1.f; + } + else + { + color[0] = 0.f; + } + + std::stringstream str; + str << std::format("{}/{:#X} => ", i, i) << (source->input.codeImages[i] == nullptr ? "---" : source->input.codeImages[i]->name) << " " << std::to_string(source->input.codeImageSamplerStates[i]); + Game::R_AddCmdDrawText(str.str().data(), std::numeric_limits::max(), font, 15.0f, (height * scale + 1) * (i + 1) + 14.0f, scale, scale, 0.0f, color, Game::ITEM_TEXTSTYLE_NORMAL); + } + } + void Renderer::DebugDrawTriggers() { if (!r_drawTriggers.get()) return; @@ -326,21 +353,15 @@ namespace Components // Static models for (size_t i = 0; i < world->dpvs.smodelCount; i++) { - auto staticModel = world->dpvs.smodelDrawInsts[i]; + auto staticModel = &world->dpvs.smodelDrawInsts[i]; + Game::Bounds* b = &world->dpvs.smodelInsts[i].bounds; - if (Utils::Maths::Vec3SqrDistance(playerPosition, staticModel.placement.origin) < sqrDist) + if (Utils::Maths::Vec3SqrDistance(playerPosition, staticModel->placement.origin) < sqrDist) { - if (staticModel.model) + if (staticModel->model) { - Game::Bounds b = staticModel.model->bounds; - b.midPoint[0] += staticModel.placement.origin[0]; - b.midPoint[1] += staticModel.placement.origin[1]; - b.midPoint[2] += staticModel.placement.origin[2]; - b.halfSize[0] *= staticModel.placement.scale; - b.halfSize[1] *= staticModel.placement.scale; - b.halfSize[2] *= staticModel.placement.scale; - Game::R_AddDebugBounds(staticModelsColor, &b); + Game::R_AddDebugBounds(staticModelsColor, b); } } } @@ -419,7 +440,15 @@ namespace Components const auto dist = Utils::Maths::Vec3SqrDistance(playerPosition, staticModel.placement.origin); if (dist < static_cast(sqrDist)) { - Game::R_AddDebugString(staticModelsColor, staticModel.placement.origin, 1.0, staticModel.model->name); + float rgb01Color[] = + { + staticModel.groundLighting.array[0] / 255.f, + staticModel.groundLighting.array[1] / 255.f, + staticModel.groundLighting.array[2] / 255.f, + 1.f, + }; + + Game::R_AddDebugString(staticModel.flags & 0x20 ? rgb01Color : staticModelsColor, staticModel.placement.origin, 1.0f, staticModel.model->name); } } } @@ -447,6 +476,29 @@ namespace Components } } + void Renderer::ForceTechnique() + { + auto forceTechnique = r_forceTechnique.get(); + + if (forceTechnique > 0) + { + Utils::Hook::Set(0x6FABDF4, forceTechnique); + } + } + + int Renderer::FixSunShadowPartitionSize(Game::GfxCamera* camera, Game::GfxSunShadowMapMetrics* mapMetrics, Game::GfxSunShadow* sunShadow, Game::GfxSunShadowClip* clip, float* partitionFraction) + { + auto result = Utils::Hook::Call(0x5463B0)(camera, mapMetrics, sunShadow, clip, partitionFraction); + + if (Maps::IsCustomMap()) + { + // Fixes shadowmap viewport which fixes pixel adjustment shadowmap bug - partly, because the real problem lies within the way CoD4 shaders are programmed + sunShadow->partition[Game::SunShadowPartition::R_SUNSHADOW_FAR].viewportParms.viewport = sunShadow->partition[Game::SunShadowPartition::R_SUNSHADOW_NEAR].viewportParms.viewport; + } + + return result; + } + Renderer::Renderer() { if (Dedicated::IsEnabled()) return; @@ -460,9 +512,14 @@ namespace Components DebugDrawModelBoundingBoxes(); DebugDrawSceneModelCollisions(); DebugDrawTriggers(); + ForceTechnique(); } }, Scheduler::Pipeline::RENDERER); + // COD4 Map Fixes + // The day map porting is perfect we should be able to remove these + Utils::Hook(0x546A09, FixSunShadowPartitionSize, HOOK_CALL).install()->quick(); + // Log broken materials Utils::Hook(0x0054CAAA, Renderer::StoreGfxBufContextPtrStub1, HOOK_JUMP).install()->quick(); Utils::Hook(0x0054CF8D, Renderer::StoreGfxBufContextPtrStub2, HOOK_JUMP).install()->quick(); @@ -510,6 +567,7 @@ namespace Components Renderer::r_drawModelNames = Game::Dvar_RegisterEnum("r_drawModelNames", values, 0, Game::DVAR_CHEAT, "Draw all model names"); Renderer::r_drawAABBTrees = Game::Dvar_RegisterBool("r_drawAabbTrees", false, Game::DVAR_CHEAT, "Draw aabb trees"); Renderer::r_playerDrawDebugDistance = Game::Dvar_RegisterInt("r_drawDebugDistance", 1000, 0, 50000, Game::DVAR_ARCHIVE, "r_draw debug functions draw distance, relative to the player"); + Renderer::r_forceTechnique = Game::Dvar_RegisterInt("r_forceTechnique", 0, 0, 14, Game::DVAR_NONE, "Force a base technique on the renderer"); }, Scheduler::Pipeline::MAIN); } diff --git a/src/Components/Modules/Renderer.hpp b/src/Components/Modules/Renderer.hpp index 11c799cd..c856376e 100644 --- a/src/Components/Modules/Renderer.hpp +++ b/src/Components/Modules/Renderer.hpp @@ -39,6 +39,9 @@ namespace Components static void DebugDrawModelBoundingBoxes(); static void DebugDrawModelNames(); static void DebugDrawAABBTrees(); + static void ForceTechnique(); + + static int FixSunShadowPartitionSize(Game::GfxCamera* camera, Game::GfxSunShadowMapMetrics* mapMetrics, Game::GfxSunShadow* sunShadow, Game::GfxSunShadowClip* clip, float* partitionFraction); static Utils::Signal EndRecoverDeviceSignal; static Utils::Signal BeginRecoverDeviceSignal; @@ -52,5 +55,6 @@ namespace Components static Dvar::Var r_drawModelNames; static Dvar::Var r_drawAABBTrees; static Dvar::Var r_playerDrawDebugDistance; + static Dvar::Var r_forceTechnique; }; } diff --git a/src/Components/Modules/Scheduler.cpp b/src/Components/Modules/Scheduler.cpp index bc29402e..7b1e5649 100644 --- a/src/Components/Modules/Scheduler.cpp +++ b/src/Components/Modules/Scheduler.cpp @@ -7,7 +7,7 @@ namespace Components { std::thread Scheduler::Thread; volatile bool Scheduler::Kill = false; - Scheduler::TaskPipeline Scheduler::Pipelines[Pipeline::COUNT]; + Scheduler::TaskPipeline Scheduler::Pipelines[static_cast>(Pipeline::COUNT)]; void Scheduler::TaskPipeline::add(Task&& task) { @@ -64,7 +64,8 @@ namespace Components void Scheduler::Execute(Pipeline type) { assert(type < Pipeline::COUNT); - Pipelines[type].execute(); + const auto index = static_cast>(type); + Pipelines[index].execute(); } void Scheduler::ScrPlace_EndFrame_Hk() @@ -107,7 +108,8 @@ namespace Components task.interval = delay; task.lastCall = std::chrono::high_resolution_clock::now(); - Pipelines[type].add(std::move(task)); + const auto index = static_cast>(type); + Pipelines[index].add(std::move(task)); } void Scheduler::Loop(const std::function& callback, const Pipeline type, @@ -156,7 +158,7 @@ namespace Components Scheduler::Scheduler() { - Thread = Utils::Thread::createNamedThread("Async Scheduler", [] + Thread = Utils::Thread::CreateNamedThread("Async Scheduler", [] { while (!Kill) { diff --git a/src/Components/Modules/Scheduler.hpp b/src/Components/Modules/Scheduler.hpp index 72715b06..d9a7a57b 100644 --- a/src/Components/Modules/Scheduler.hpp +++ b/src/Components/Modules/Scheduler.hpp @@ -5,7 +5,7 @@ namespace Components class Scheduler : public Component { public: - enum Pipeline + enum class Pipeline : int { ASYNC, RENDERER, diff --git a/src/Components/Modules/Security.cpp b/src/Components/Modules/Security.cpp index 07193220..9c45e24a 100644 --- a/src/Components/Modules/Security.cpp +++ b/src/Components/Modules/Security.cpp @@ -1,10 +1,11 @@ #include +#include "Security.hpp" namespace Components { - int Security::MsgReadBitsCompressCheckSV(const char* from, char* to, int size) + int Security::MsgReadBitsCompressCheckSV(const unsigned char* from, unsigned char* to, int size) { - static char buffer[0x8000]; + static unsigned char buffer[0x8000]; if (size > 0x800) return 0; size = Game::MSG_ReadBitsCompress(from, buffer, size); @@ -15,9 +16,9 @@ namespace Components return size; } - int Security::MsgReadBitsCompressCheckCL(const char* from, char* to, int size) + int Security::MsgReadBitsCompressCheckCL(const unsigned char* from, unsigned char* to, int size) { - static char buffer[0x100000]; + static unsigned char buffer[0x100000]; if (size > 0x20000) return 0; size = Game::MSG_ReadBitsCompress(from, buffer, size); diff --git a/src/Components/Modules/Security.hpp b/src/Components/Modules/Security.hpp index 23ceb4d6..081cbd90 100644 --- a/src/Components/Modules/Security.hpp +++ b/src/Components/Modules/Security.hpp @@ -7,10 +7,10 @@ namespace Components public: Security(); - private: - static int MsgReadBitsCompressCheckSV(const char* from, char* to, int size); - static int MsgReadBitsCompressCheckCL(const char* from, char* to, int size); + static int MsgReadBitsCompressCheckSV(const unsigned char* from, unsigned char* to, int size); + static int MsgReadBitsCompressCheckCL(const unsigned char* from, unsigned char* to, int size); + private: static int SVCanReplaceServerCommand(Game::client_t* client, const char* cmd); static long AtolAdjustPlayerLimit(const char* string); diff --git a/src/Components/Modules/ServerCommands.cpp b/src/Components/Modules/ServerCommands.cpp index f5f8863a..fd547914 100644 --- a/src/Components/Modules/ServerCommands.cpp +++ b/src/Components/Modules/ServerCommands.cpp @@ -1,4 +1,5 @@ #include +#include "ServerCommands.hpp" namespace Components { diff --git a/src/Components/Modules/ServerInfo.cpp b/src/Components/Modules/ServerInfo.cpp index ff90f9b4..1dfcba0c 100644 --- a/src/Components/Modules/ServerInfo.cpp +++ b/src/Components/Modules/ServerInfo.cpp @@ -1,4 +1,11 @@ #include +#include "Gamepad.hpp" +#include "ServerInfo.hpp" +#include "ServerList.hpp" +#include "UIFeeder.hpp" +#include "Voice.hpp" + +#include namespace Components { @@ -129,6 +136,7 @@ namespace Components Utils::InfoString ServerInfo::GetInfo() { auto maxClientCount = *Game::svs_clientCount; + const auto* password = *Game::g_password ? (*Game::g_password)->current.string : ""; if (!maxClientCount) { @@ -137,13 +145,13 @@ namespace Components Utils::InfoString info(Game::Dvar_InfoString_Big(Game::DVAR_SERVERINFO)); info.set("gamename", "IW4"); - info.set("sv_maxclients", Utils::String::VA("%i", maxClientCount)); - info.set("protocol", Utils::String::VA("%i", PROTOCOL)); + info.set("sv_maxclients", std::to_string(maxClientCount)); + info.set("protocol", std::to_string(PROTOCOL)); info.set("shortversion", SHORTVERSION); info.set("version", (*Game::version)->current.string); info.set("mapname", (*Game::sv_mapname)->current.string); - info.set("isPrivate", (Dvar::Var("g_password").get().empty() ? "0" : "1")); - info.set("checksum", Utils::String::VA("%X", Utils::Cryptography::JenkinsOneAtATime::Compute(Utils::String::VA("%u", Game::Sys_Milliseconds())))); + info.set("isPrivate", *password ? "1" : "0"); + info.set("checksum", Utils::String::VA("%X", Utils::Cryptography::JenkinsOneAtATime::Compute(std::to_string(Game::Sys_Milliseconds())))); info.set("aimAssist", (Gamepad::sv_allowAimAssist.get() ? "1" : "0")); info.set("voiceChat", (Voice::SV_VoiceEnabled() ? "1" : "0")); @@ -252,7 +260,7 @@ namespace Components Dvar::Var("uiSi_aimAssist").set(info.get("aimAssist") == "0" ? "@MENU_DISABLED" : "@MENU_ENABLED"); Dvar::Var("uiSi_voiceChat").set(info.get("voiceChat") == "0" ? "@MENU_DISABLED" : "@MENU_ENABLED"); - switch (atoi(info.get("scr_team_fftype").data())) + switch (std::strtol(info.get("scr_team_fftype").data(), nullptr, 10)) { default: Dvar::Var("uiSi_ffType").set("@MENU_DISABLED"); @@ -268,12 +276,13 @@ namespace Components break; } - if (info.get("fs_game").size() > 5) + if (Utils::String::StartsWith(info.get("fs_game"), "mods/")) { - Dvar::Var("uiSi_ModName").set(info.get("fs_game").data() + 5); + auto mod = info.get("fs_game"); + Dvar::Var("uiSi_ModName").set(mod.substr(5)); } - auto lines = Utils::String::Split(data, '\n'); + const auto lines = Utils::String::Split(data, '\n'); if (lines.size() <= 1) return; diff --git a/src/Components/Modules/ServerList.cpp b/src/Components/Modules/ServerList.cpp index 18ef579f..e50011bc 100644 --- a/src/Components/Modules/ServerList.cpp +++ b/src/Components/Modules/ServerList.cpp @@ -1,9 +1,14 @@ #include +#include "Discovery.hpp" +#include "ServerList.hpp" +#include "UIFeeder.hpp" + +#include namespace Components { bool ServerList::SortAsc = true; - int ServerList::SortKey = ServerList::Column::Ping; + int ServerList::SortKey = static_cast>(Column::Ping); unsigned int ServerList::CurrentServer = 0; ServerList::Container ServerList::RefreshContainer; @@ -23,17 +28,19 @@ namespace Components std::vector* ServerList::GetList() { - if (ServerList::IsOnlineList()) + if (IsOnlineList()) { - return &ServerList::OnlineList; + return &OnlineList; } - if (ServerList::IsOfflineList()) + + if (IsOfflineList()) { - return &ServerList::OfflineList; + return &OfflineList; } - if (ServerList::IsFavouriteList()) + + if (IsFavouriteList()) { - return &ServerList::FavouriteList; + return &FavouriteList; } return nullptr; @@ -41,41 +48,41 @@ namespace Components bool ServerList::IsFavouriteList() { - return (Dvar::Var("ui_netSource").get() == 2); + return (*Game::ui_netSource)->current.integer == 2; } bool ServerList::IsOfflineList() { - return (Dvar::Var("ui_netSource").get() == 0); + return (*Game::ui_netSource)->current.integer == 0; } bool ServerList::IsOnlineList() { - return (Dvar::Var("ui_netSource").get() == 1); + return (*Game::ui_netSource)->current.integer == 1; } unsigned int ServerList::GetServerCount() { - return ServerList::VisibleList.size(); + return VisibleList.size(); } const char* ServerList::GetServerText(unsigned int index, int column) { - ServerList::ServerInfo* info = ServerList::GetServer(index); + auto* info = GetServer(index); if (info) { - return ServerList::GetServerInfoText(info, column); + return GetServerInfoText(info, column); } return ""; } - const char* ServerList::GetServerInfoText(ServerList::ServerInfo* server, int column, bool sorting) + const char* ServerList::GetServerInfoText(ServerInfo* server, int column, bool sorting) { if (!server) return ""; - switch (column) + switch (static_cast(column)) { case Column::Password: { @@ -110,12 +117,11 @@ namespace Components { return Utils::String::VA("^1%s", Game::UI_LocalizeMapName(server->mapname.data())); } + return Game::UI_LocalizeMapName(server->mapname.data()); } - else - { - return Utils::String::VA("^3%s", Game::UI_LocalizeMapName(server->mapname.data())); - } + + return Utils::String::VA("^3%s", Game::UI_LocalizeMapName(server->mapname.data())); } case Column::Players: @@ -130,8 +136,9 @@ namespace Components case Column::Mod: { - if (server->mod != "") + if (Utils::String::StartsWith(server->mod, "mods/")) { + // Can point to '\0' which is fine return (server->mod.data() + 5); } @@ -144,20 +151,18 @@ namespace Components { return Utils::String::VA("^2%i", server->ping); } - else if (server->ping < 150) // Below this is a medium ping + + if (server->ping < 150) // Below this is a medium ping { return Utils::String::VA("^3%i", server->ping); } - else - { - return Utils::String::VA("^1%i", server->ping); - } - } + return Utils::String::VA("^1%i", server->ping); + } default: { break; - }; + } } return ""; @@ -165,75 +170,75 @@ namespace Components void ServerList::SelectServer(unsigned int index) { - ServerList::CurrentServer = index; + CurrentServer = index; - ServerList::ServerInfo* info = ServerList::GetCurrentServer(); + auto* info = GetCurrentServer(); if (info) { - ServerList::UIServerSelected.set(true); - ServerList::UIServerSelectedMap.set(info->mapname); + UIServerSelected.set(true); + UIServerSelectedMap.set(info->mapname); Dvar::Var("ui_serverSelectedGametype").set(info->gametype); } else { - ServerList::UIServerSelected.set(false); + UIServerSelected.set(false); } } void ServerList::UpdateVisibleList([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) { - auto list = ServerList::GetList(); + auto* list = GetList(); if (!list) return; - std::vector tempList(*list); + std::vector tempList(*list); if (tempList.empty()) { - ServerList::Refresh(UIScript::Token(), info); + Refresh(UIScript::Token(), info); } else { list->clear(); - std::lock_guard _(ServerList::RefreshContainer.mutex); + std::lock_guard _(RefreshContainer.mutex); - ServerList::RefreshContainer.sendCount = 0; - ServerList::RefreshContainer.sentCount = 0; + RefreshContainer.sendCount = 0; + RefreshContainer.sentCount = 0; for (auto& server : tempList) { - ServerList::InsertRequest(server.addr); + InsertRequest(server.addr); } } } void ServerList::RefreshVisibleList([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) { - ServerList::RefreshVisibleListInternal(UIScript::Token(), info); + RefreshVisibleListInternal(UIScript::Token(), info); } void ServerList::RefreshVisibleListInternal([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info, bool refresh) { - Dvar::Var("ui_serverSelected").set(false); + Game::Dvar_SetBoolByName("ui_serverSelected", false); - ServerList::VisibleList.clear(); + VisibleList.clear(); - auto list = ServerList::GetList(); + auto* list = GetList(); if (!list) return; if (refresh) { - ServerList::Refresh(UIScript::Token(), info); + Refresh(UIScript::Token(), info); return; } - auto ui_browserShowFull = Dvar::Var("ui_browserShowFull").get(); - auto ui_browserShowEmpty = Dvar::Var("ui_browserShowEmpty").get(); - auto ui_browserShowHardcore = Dvar::Var("ui_browserKillcam").get(); - auto ui_browserShowPassword = Dvar::Var("ui_browserShowPassword").get(); - auto ui_browserMod = Dvar::Var("ui_browserMod").get(); - auto ui_joinGametype = Dvar::Var("ui_joinGametype").get(); + auto ui_browserShowFull = Dvar::Var("ui_browserShowFull").get(); + auto ui_browserShowEmpty = Dvar::Var("ui_browserShowEmpty").get(); + auto ui_browserShowHardcore = Dvar::Var("ui_browserKillcam").get(); + auto ui_browserShowPassword = Dvar::Var("ui_browserShowPassword").get(); + auto ui_browserMod = Dvar::Var("ui_browserMod").get(); + auto ui_joinGametype = (*Game::ui_joinGametype)->current.integer; for (unsigned int i = 0; i < list->size(); ++i) { @@ -252,15 +257,15 @@ namespace Components if ((ui_browserShowPassword == 0 && serverInfo->password) || (ui_browserShowPassword == 1 && !serverInfo->password)) continue; // Don't show modded servers - if ((ui_browserMod == 0 && serverInfo->mod.size()) || (ui_browserMod == 1 && !serverInfo->mod.size())) continue; + if ((ui_browserMod == 0 && static_cast(serverInfo->mod.size())) || (ui_browserMod == 1 && serverInfo->mod.empty())) continue; // Filter by gametype if (ui_joinGametype > 0 && (ui_joinGametype - 1) < *Game::gameTypeCount && Game::gameTypes[(ui_joinGametype - 1)].gameType != serverInfo->gametype) continue; - ServerList::VisibleList.push_back(i); + VisibleList.push_back(i); } - ServerList::SortList(); + SortList(); } void ServerList::Refresh([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) @@ -268,37 +273,38 @@ namespace Components Dvar::Var("ui_serverSelected").set(false); //Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0"); -// ServerList::OnlineList.clear(); -// ServerList::OfflineList.clear(); -// ServerList::FavouriteList.clear(); - - auto list = ServerList::GetList(); +#if 0 + OnlineList.clear(); + OfflineList.clear(); + FavouriteList.clear(); +#endif + auto* list = GetList(); if (list) list->clear(); - ServerList::VisibleList.clear(); + VisibleList.clear(); { - std::lock_guard _(ServerList::RefreshContainer.mutex); - ServerList::RefreshContainer.servers.clear(); - ServerList::RefreshContainer.sendCount = 0; - ServerList::RefreshContainer.sentCount = 0; + std::lock_guard _(RefreshContainer.mutex); + RefreshContainer.servers.clear(); + RefreshContainer.sendCount = 0; + RefreshContainer.sentCount = 0; } - if (ServerList::IsOfflineList()) + if (IsOfflineList()) { Discovery::Perform(); } - else if (ServerList::IsOnlineList()) + else if (IsOnlineList()) { const auto masterPort = Dvar::Var("masterPort").get(); const auto masterServerName = Dvar::Var("masterServerName").get(); // Check if our dvars can properly convert to a address Game::netadr_t masterServerAddr; - if (!ServerList::GetMasterServer(masterServerName, masterPort, masterServerAddr)) + if (!GetMasterServer(masterServerName, masterPort, masterServerAddr)) { Logger::Print("Could not resolve address for {}:{}", masterServerName, masterPort); - Toast::Show("cardicon_headshot", "^1Error", Utils::String::VA("Could not resolve address for %s:%u", masterServerName, masterPort), 5000); + Toast::Show("cardicon_headshot", "^1Error", Utils::String::VA("Could not resolve address for %s:%i", masterServerName, masterPort), 5000); UseMasterServer = false; return; } @@ -307,16 +313,16 @@ namespace Components UseMasterServer = true; - ServerList::RefreshContainer.awatingList = true; - ServerList::RefreshContainer.awaitTime = Game::Sys_Milliseconds(); - ServerList::RefreshContainer.host = Network::Address(Utils::String::VA("%s:%u", masterServerName, masterPort)); + RefreshContainer.awatingList = true; + RefreshContainer.awaitTime = Game::Sys_Milliseconds(); + RefreshContainer.host = Network::Address(Utils::String::VA("%s:%u", masterServerName, masterPort)); Logger::Print("Sending serverlist request to master\n"); - Network::SendCommand(ServerList::RefreshContainer.host, "getservers", Utils::String::VA("IW4 %i full empty", PROTOCOL)); + Network::SendCommand(RefreshContainer.host, "getservers", Utils::String::VA("IW4 %i full empty", PROTOCOL)); } - else if (ServerList::IsFavouriteList()) + else if (IsFavouriteList()) { - ServerList::LoadFavourties(); + LoadFavourties(); } } @@ -404,22 +410,22 @@ namespace Components const auto data = nlohmann::json(servers); Utils::IO::WriteFile(FavouriteFile, data.dump()); - auto list = ServerList::GetList(); + auto* list = GetList(); if (list) list->clear(); - ServerList::RefreshVisibleListInternal(UIScript::Token(), nullptr); + RefreshVisibleListInternal(UIScript::Token(), nullptr); Game::ShowMessageBox("Server removed from favourites.", "Success"); } void ServerList::LoadFavourties() { - if (!ServerList::IsFavouriteList()) + if (!IsFavouriteList()) { return; } - auto list = ServerList::GetList(); + auto* list = GetList(); if (list) list->clear(); const auto parseData = Utils::IO::ReadFile(FavouriteFile); @@ -450,20 +456,20 @@ namespace Components for (const auto& server : servers) { if (!server.is_string()) continue; - ServerList::InsertRequest(server.get()); + InsertRequest(server.get()); } } void ServerList::InsertRequest(Network::Address address) { - std::lock_guard _(ServerList::RefreshContainer.mutex); + std::lock_guard _(RefreshContainer.mutex); - ServerList::Container::ServerContainer container; + Container::ServerContainer container; container.sent = false; container.target = address; bool alreadyInserted = false; - for (auto &server : ServerList::RefreshContainer.servers) + for (auto &server : RefreshContainer.servers) { if (server.target == container.target) { @@ -474,34 +480,34 @@ namespace Components if (!alreadyInserted) { - ServerList::RefreshContainer.servers.push_back(container); + RefreshContainer.servers.push_back(container); - auto list = ServerList::GetList(); + auto* list = GetList(); if (list) { for (auto& server : *list) { if (server.addr == container.target) { - --ServerList::RefreshContainer.sendCount; - --ServerList::RefreshContainer.sentCount; + --RefreshContainer.sendCount; + --RefreshContainer.sentCount; break; } } } - ++ServerList::RefreshContainer.sendCount; + ++RefreshContainer.sendCount; } } void ServerList::Insert(const Network::Address& address, const Utils::InfoString& info) { - std::lock_guard _(ServerList::RefreshContainer.mutex); + std::lock_guard _(RefreshContainer.mutex); - for (auto i = ServerList::RefreshContainer.servers.begin(); i != ServerList::RefreshContainer.servers.end();) + for (auto i = RefreshContainer.servers.begin(); i != RefreshContainer.servers.end();) { // Our desired server - if (i->target == address && i->sent) + if ((i->target == address) && i->sent) { // Challenge did not match if (i->challenge != info.get("challenge")) @@ -518,16 +524,16 @@ namespace Components server.gametype = info.get("gametype"); server.shortversion = info.get("shortversion"); server.mod = info.get("fs_game"); - server.matchType = atoi(info.get("matchtype").data()); - server.clients = atoi(info.get("clients").data()); - server.bots = atoi(info.get("bots").data()); - server.securityLevel = atoi(info.get("securityLevel").data()); - server.maxClients = atoi(info.get("sv_maxclients").data()); - server.password = (atoi(info.get("isPrivate").data()) != 0); - server.aimassist = (atoi(info.get("aimAssist").data()) != 0); - server.voice = (atoi(info.get("voiceChat").data()) != 0); - server.hardcore = (atoi(info.get("hc").data()) != 0); - server.svRunning = (atoi(info.get("sv_running").data()) != 0); + server.matchType = std::strtol(info.get("matchtype").data(), nullptr, 10); + server.clients = std::strtol(info.get("clients").data(), nullptr, 10); + server.bots = std::strtol(info.get("bots").data(), nullptr, 10); + server.securityLevel = std::strtol(info.get("securityLevel").data(), nullptr, 10); + server.maxClients = std::strtol(info.get("sv_maxclients").data(), nullptr, 10); + server.password = info.get("isPrivate") == "1"s; + server.aimassist = info.get("aimAssist") == "1"; + server.voice = info.get("voiceChat") == "1"s; + server.hardcore = info.get("hc") == "1"s; + server.svRunning = info.get("sv_running") == "1"s; server.ping = (Game::Sys_Milliseconds() - i->sendTime); server.addr = address; @@ -537,18 +543,20 @@ namespace Components server.mod = TextRenderer::StripMaterialTextIcons(server.mod); // Remove server from queue - i = ServerList::RefreshContainer.servers.erase(i); + i = RefreshContainer.servers.erase(i); // Servers with more than 18 players or less than 0 players are faking for sure // So lets ignore those if (server.clients > 18 || server.maxClients > 18 || server.clients < 0 || server.maxClients < 0) + { return; + } // Check if already inserted and remove - auto list = ServerList::GetList(); + auto* list = GetList(); if (!list) return; - unsigned int k = 0; + std::size_t k = 0; for (auto j = list->begin(); j != list->end(); ++k) { if (j->addr == address) @@ -562,11 +570,11 @@ namespace Components } // Also remove from visible list - for (auto j = ServerList::VisibleList.begin(); j != ServerList::VisibleList.end();) + for (auto j = VisibleList.begin(); j != VisibleList.end();) { if (*j == k) { - j = ServerList::VisibleList.erase(j); + j = VisibleList.erase(j); } else { @@ -574,18 +582,17 @@ namespace Components } } - if (info.get("gamename") == "IW4" - && server.matchType + if (info.get("gamename") == "IW4"s && server.matchType #if !defined(DEBUG) && defined(VERSION_FILTER) - && ServerList::CompareVersion(server.shortversion, SHORTVERSION) + && CompareVersion(server.shortversion, SHORTVERSION) #endif ) { - auto lList = ServerList::GetList(); + auto* lList = GetList(); if (lList) { lList->push_back(server); - ServerList::RefreshVisibleListInternal(UIScript::Token(), nullptr); + RefreshVisibleListInternal(UIScript::Token(), nullptr); } } } @@ -605,10 +612,18 @@ namespace Components while (subVersions2.size() >= 3) subVersions2.pop_back(); if (subVersions1.size() != subVersions2.size()) return false; - for (unsigned int i = 0; i < subVersions1.size(); ++i) + for (std::size_t i = 0; i < subVersions1.size(); ++i) { - if (atoi(subVersions1[i].data()) != atoi(subVersions2[i].data())) + try { + if (std::stoi(subVersions1[i]) != std::stoi(subVersions2[i])) + { + return false; + } + } + catch (const std::exception& ex) + { + Logger::Warning(Game::CON_CHANNEL_CONSOLEONLY, "{} while performing numeric comparison between {} and {}\n", ex.what(), subVersions1[i], subVersions2[i]); return false; } } @@ -618,20 +633,20 @@ namespace Components ServerList::ServerInfo* ServerList::GetCurrentServer() { - return ServerList::GetServer(ServerList::CurrentServer); + return GetServer(CurrentServer); } void ServerList::SortList() { // Only sort when the serverlist is open - if (!ServerList::IsServerListOpen()) return; + if (!IsServerListOpen()) return; - std::stable_sort(ServerList::VisibleList.begin(), ServerList::VisibleList.end(), [](const unsigned int &server1, const unsigned int &server2) -> bool + std::ranges::stable_sort(VisibleList, [](const unsigned int& server1, const unsigned int& server2) -> bool { ServerInfo* info1 = nullptr; ServerInfo* info2 = nullptr; - auto list = ServerList::GetList(); + auto* list = GetList(); if (!list) return false; if (list->size() > server1) info1 = &(*list)[server1]; @@ -641,35 +656,36 @@ namespace Components if (!info2) return false; // Numerical comparisons - if (ServerList::SortKey == ServerList::Column::Ping) + if (SortKey == static_cast>(Column::Ping)) { return info1->ping < info2->ping; } - else if (ServerList::SortKey == ServerList::Column::Players) + + if (SortKey == static_cast>(Column::Players)) { return info1->clients < info2->clients; } - std::string text1 = Utils::String::ToLower(TextRenderer::StripColors(ServerList::GetServerInfoText(info1, ServerList::SortKey, true))); - std::string text2 = Utils::String::ToLower(TextRenderer::StripColors(ServerList::GetServerInfoText(info2, ServerList::SortKey, true))); + auto text1 = Utils::String::ToLower(TextRenderer::StripColors(GetServerInfoText(info1, SortKey, true))); + auto text2 = Utils::String::ToLower(TextRenderer::StripColors(GetServerInfoText(info2, SortKey, true))); // ASCII-based comparison return text1.compare(text2) < 0; }); - if (!ServerList::SortAsc) std::reverse(ServerList::VisibleList.begin(), ServerList::VisibleList.end()); + if (!SortAsc) std::ranges::reverse(VisibleList); } ServerList::ServerInfo* ServerList::GetServer(unsigned int index) { - if (ServerList::VisibleList.size() > index) + if (VisibleList.size() > index) { - auto list = ServerList::GetList(); + auto* list = GetList(); if (!list) return nullptr; - if (list->size() > ServerList::VisibleList[index]) + if (list->size() > VisibleList[index]) { - return &(*list)[ServerList::VisibleList[index]]; + return &(*list)[VisibleList[index]]; } } @@ -679,40 +695,42 @@ namespace Components void ServerList::Frame() { static Utils::Time::Interval frameLimit; - const auto interval = static_cast(1000.0f / ServerList::NETServerFrames.get()); + const auto interval = static_cast(1000.0f / static_cast(NETServerFrames.get())); if (!frameLimit.elapsed(std::chrono::milliseconds(interval))) + { return; + } frameLimit.update(); - std::lock_guard _(ServerList::RefreshContainer.mutex); + std::lock_guard _(RefreshContainer.mutex); - if (ServerList::RefreshContainer.awatingList) + if (RefreshContainer.awatingList) { // Stop counting if we are out of the server browser menu - if (!ServerList::IsServerListOpen()) + if (!IsServerListOpen()) { - ServerList::RefreshContainer.awatingList = false; + RefreshContainer.awatingList = false; } // Check if we haven't got a response within 5 seconds - if (Game::Sys_Milliseconds() - ServerList::RefreshContainer.awaitTime > 5000) + if (Game::Sys_Milliseconds() - RefreshContainer.awaitTime > 5000) { - ServerList::RefreshContainer.awatingList = false; + RefreshContainer.awatingList = false; - Logger::Print("We haven't received a response from the master within {} seconds!\n", (Game::Sys_Milliseconds() - ServerList::RefreshContainer.awaitTime) / 1000); - Toast::Show("cardicon_headshot", "^1Error", "Failed to reach master server, using node servers instead.", 5000); + Logger::Print("We haven't received a response from the master within {} seconds!\n", (Game::Sys_Milliseconds() - RefreshContainer.awaitTime) / 1000); + Toast::Show("cardicon_headshot", "^3Warning", "Failed to reach master server. Using node system instead.", 5000); UseMasterServer = false; Node::Synchronize(); } } - auto requestLimit = ServerList::NETServerQueryLimit.get(); - for (unsigned int i = 0; i < ServerList::RefreshContainer.servers.size() && requestLimit > 0; ++i) + auto requestLimit = NETServerQueryLimit.get(); + for (std::size_t i = 0; i < RefreshContainer.servers.size() && requestLimit > 0; ++i) { - ServerList::Container::ServerContainer* server = &ServerList::RefreshContainer.servers[i]; + auto* server = &RefreshContainer.servers[i]; if (server->sent) continue; // Found server we can send a request to @@ -722,47 +740,43 @@ namespace Components server->sendTime = Game::Sys_Milliseconds(); server->challenge = Utils::Cryptography::Rand::GenerateChallenge(); - ++ServerList::RefreshContainer.sentCount; + ++RefreshContainer.sentCount; Network::SendCommand(server->target, "getinfo", server->challenge); - // Display in the menu, like in COD4 + // Display in the menu, like in CoD4 - Disabled to avoid spamming? //Localization::Set("MPUI_SERVERQUERIED", Utils::String::VA("Sent requests: %d/%d", ServerList::RefreshContainer.sentCount, ServerList::RefreshContainer.sendCount)); } - ServerList::UpdateVisibleInfo(); + UpdateVisibleInfo(); } void ServerList::UpdateSource() { - Dvar::Var netSource("ui_netSource"); + auto source = (*Game::ui_netSource)->current.integer; - int source = netSource.get(); - - if (++source > netSource.get()->domain.integer.max) + if (++source > (*Game::ui_netSource)->domain.integer.max) { source = 0; } - netSource.set(source); + Game::Dvar_SetInt(*Game::ui_netSource, source); - ServerList::RefreshVisibleListInternal(UIScript::Token(), nullptr, true); + RefreshVisibleListInternal(UIScript::Token(), nullptr, true); } void ServerList::UpdateGameType() { - Dvar::Var joinGametype("ui_joinGametype"); - - int gametype = joinGametype.get(); + auto gametype = (*Game::ui_joinGametype)->current.integer; if (++gametype > *Game::gameTypeCount) { gametype = 0; } - joinGametype.set(gametype); + Game::Dvar_SetInt(*Game::ui_joinGametype, gametype); - ServerList::RefreshVisibleListInternal(UIScript::Token(), nullptr); + RefreshVisibleListInternal(UIScript::Token(), nullptr); } void ServerList::UpdateVisibleInfo() @@ -771,7 +785,7 @@ namespace Components static int players = 0; static int bots = 0; - auto list = ServerList::GetList(); + auto list = GetList(); if (list) { @@ -779,7 +793,7 @@ namespace Components int newPlayers = 0; int newBots = 0; - for (unsigned int i = 0; i < list->size(); ++i) + for (std::size_t i = 0; i < list->size(); ++i) { newPlayers += list->at(i).clients; newBots += list->at(i).bots; @@ -812,47 +826,45 @@ namespace Components ServerList::ServerList() { - ServerList::OnlineList.clear(); - ServerList::OfflineList.clear(); - ServerList::FavouriteList.clear(); - ServerList::VisibleList.clear(); + OnlineList.clear(); + OfflineList.clear(); + FavouriteList.clear(); + VisibleList.clear(); Scheduler::Once([] { - ServerList::UIServerSelected = Dvar::Register("ui_serverSelected", false, + UIServerSelected = Dvar::Register("ui_serverSelected", false, Game::DVAR_NONE, "Whether a server has been selected in the serverlist"); - ServerList::UIServerSelectedMap = Dvar::Register("ui_serverSelectedMap", "mp_afghan", + UIServerSelectedMap = Dvar::Register("ui_serverSelectedMap", "mp_afghan", Game::DVAR_NONE, "Map of the selected server"); - ServerList::NETServerQueryLimit = Dvar::Register("net_serverQueryLimit", 1, + NETServerQueryLimit = Dvar::Register("net_serverQueryLimit", 1, 1, 10, Dedicated::IsEnabled() ? Game::DVAR_NONE : Game::DVAR_ARCHIVE, "Amount of server queries per frame"); - ServerList::NETServerFrames = Dvar::Register("net_serverFrames", 30, + NETServerFrames = Dvar::Register("net_serverFrames", 30, 1, 60, Dedicated::IsEnabled() ? Game::DVAR_NONE : Game::DVAR_ARCHIVE, "Amount of server query frames per second"); }, Scheduler::Pipeline::MAIN); // Fix ui_netsource dvar Utils::Hook::Nop(0x4CDEEC, 5); // Don't reset the netsource when gametypes aren't loaded - Dvar::Register("ui_netSource", 1, 0, 2, Game::DVAR_ARCHIVE, reinterpret_cast(0x6D9F08)); - //Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0"); Localization::Set("MPUI_SERVERQUERIED", "Servers: 0\nPlayers: 0 (0)"); Network::OnClientPacket("getServersResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data) { - if (ServerList::RefreshContainer.host != address) return; // Only parse from host we sent to + if (RefreshContainer.host != address) return; // Only parse from host we sent to - ServerList::RefreshContainer.awatingList = false; + RefreshContainer.awatingList = false; - std::lock_guard _(ServerList::RefreshContainer.mutex); + std::lock_guard _(RefreshContainer.mutex); int offset = 0; - int count = ServerList::RefreshContainer.servers.size(); - ServerList::MasterEntry* entry = nullptr; + auto count = RefreshContainer.servers.size(); + MasterEntry* entry = nullptr; // Find first entry do { - entry = reinterpret_cast(const_cast(data.data()) + offset++); + entry = reinterpret_cast(const_cast(data.data()) + offset++); } while (!entry->HasSeparator() && !entry->IsEndToken()); for (int i = 0; !entry[i].IsEndToken() && entry[i].HasSeparator(); ++i) @@ -862,29 +874,29 @@ namespace Components serverAddr.setPort(ntohs(entry[i].port)); serverAddr.setType(Game::NA_IP); - ServerList::InsertRequest(serverAddr); + InsertRequest(serverAddr); } - Logger::Print("Parsed {} servers from master\n", ServerList::RefreshContainer.servers.size() - count); + Logger::Print("Parsed {} servers from master\n", RefreshContainer.servers.size() - count); }); // Set default masterServerName + port and save it Utils::Hook::Set(0x60AD92, "master.xlabs.dev"); - Utils::Hook::Set(0x60AD90, Game::DVAR_NONE); // masterServerName - Utils::Hook::Set(0x60ADC6, Game::DVAR_NONE); // masterPort + Utils::Hook::Set(0x60AD90, Game::DVAR_NONE); // masterServerName + Utils::Hook::Set(0x60ADC6, Game::DVAR_NONE); // masterPort // Add server list feeder - UIFeeder::Add(2.0f, ServerList::GetServerCount, ServerList::GetServerText, ServerList::SelectServer); + UIFeeder::Add(2.0f, GetServerCount, GetServerText, SelectServer); // Add required UIScripts - UIScript::Add("UpdateFilter", ServerList::RefreshVisibleList); - UIScript::Add("RefreshFilter", ServerList::UpdateVisibleList); + UIScript::Add("UpdateFilter", RefreshVisibleList); + UIScript::Add("RefreshFilter", UpdateVisibleList); - UIScript::Add("RefreshServers", ServerList::Refresh); + UIScript::Add("RefreshServers", Refresh); UIScript::Add("JoinServer", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) { - auto* serverInfo = ServerList::GetServer(ServerList::CurrentServer); + auto* serverInfo = GetServer(CurrentServer); if (serverInfo) { Party::Connect(serverInfo->addr); @@ -893,53 +905,53 @@ namespace Components UIScript::Add("ServerSort", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) { - auto key = token.get(); - if (ServerList::SortKey == key) + const auto key = token.get(); + if (SortKey == key) { - ServerList::SortAsc = !ServerList::SortAsc; + SortAsc = !SortAsc; } else { - ServerList::SortKey = key; - ServerList::SortAsc = true; + SortKey = key; + SortAsc = true; } - Logger::Print("Sorting server list by token: {}\n", ServerList::SortKey); - ServerList::SortList(); + Logger::Print("Sorting server list by token: {}\n", SortKey); + SortList(); }); UIScript::Add("CreateListFavorite", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) { - auto* serverInfo = ServerList::GetCurrentServer(); + auto* serverInfo = GetCurrentServer(); if (info) { - ServerList::StoreFavourite(serverInfo->addr.getString()); + StoreFavourite(serverInfo->addr.getString()); } }); UIScript::Add("CreateFavorite", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) { - ServerList::StoreFavourite(Dvar::Var("ui_favoriteAddress").get()); + StoreFavourite(Dvar::Var("ui_favoriteAddress").get()); }); UIScript::Add("CreateCurrentServerFavorite", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) { if (Game::CL_IsCgameInitialized()) { - std::string addressText = Network::Address(*Game::connectedHost).getString(); - if (addressText != "0.0.0.0:0" && addressText != "loopback") + const auto addressText = Network::Address(*Game::connectedHost).getString(); + if (addressText != "0.0.0.0:0"s && addressText != "loopback"s) { - ServerList::StoreFavourite(addressText); + StoreFavourite(addressText); } } }); UIScript::Add("DeleteFavorite", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) { - auto* serverInfo = ServerList::GetCurrentServer(); + auto* serverInfo = GetCurrentServer(); if (serverInfo) { - ServerList::RemoveFavourite(serverInfo->addr.getString()); + RemoveFavourite(serverInfo->addr.getString()); } }); @@ -947,7 +959,7 @@ namespace Components Command::Add("playerCount", [](Command::Params*) { auto count = 0; - for (const auto& server : ServerList::OnlineList) + for (const auto& server : OnlineList) { count += server.clients; } @@ -956,17 +968,17 @@ namespace Components }); #endif // Add required ownerDraws - UIScript::AddOwnerDraw(220, ServerList::UpdateSource); - UIScript::AddOwnerDraw(253, ServerList::UpdateGameType); + UIScript::AddOwnerDraw(220, UpdateSource); + UIScript::AddOwnerDraw(253, UpdateGameType); // Add frame callback - Scheduler::Loop(ServerList::Frame, Scheduler::Pipeline::CLIENT); + Scheduler::Loop(Frame, Scheduler::Pipeline::CLIENT); } ServerList::~ServerList() { - std::lock_guard _(ServerList::RefreshContainer.mutex); - ServerList::RefreshContainer.awatingList = false; - ServerList::RefreshContainer.servers.clear(); + std::lock_guard _(RefreshContainer.mutex); + RefreshContainer.awatingList = false; + RefreshContainer.servers.clear(); } } diff --git a/src/Components/Modules/ServerList.hpp b/src/Components/Modules/ServerList.hpp index b65036e0..834f90e5 100644 --- a/src/Components/Modules/ServerList.hpp +++ b/src/Components/Modules/ServerList.hpp @@ -57,7 +57,7 @@ namespace Components static bool UseMasterServer; private: - enum Column + enum class Column : int { Password, Matchtype, @@ -69,6 +69,8 @@ namespace Components Gametype, Mod, Ping, + + Count }; static constexpr auto* FavouriteFile = "players/favourites.json"; @@ -83,13 +85,13 @@ namespace Components uint16_t port; }; - bool IsEndToken() + bool IsEndToken() const { // End of transmission or file token return (token[0] == 'E' && token[1] == 'O' && (token[2] == 'T' || token[2] == 'F')); } - bool HasSeparator() + bool HasSeparator() const { return (token[6] == '\\'); } diff --git a/src/Components/Modules/Session.cpp b/src/Components/Modules/Session.cpp index 1c788437..096ccd24 100644 --- a/src/Components/Modules/Session.cpp +++ b/src/Components/Modules/Session.cpp @@ -1,4 +1,5 @@ #include +#include "Session.hpp" namespace Components { diff --git a/src/Components/Modules/Singleton.cpp b/src/Components/Modules/Singleton.cpp index 148bc825..3986a930 100644 --- a/src/Components/Modules/Singleton.cpp +++ b/src/Components/Modules/Singleton.cpp @@ -1,4 +1,8 @@ #include +#include "ConnectProtocol.hpp" +#include "Console.hpp" + +#include namespace Components { @@ -6,27 +10,27 @@ namespace Components bool Singleton::IsFirstInstance() { - return Singleton::FirstInstance; + return FirstInstance; } Singleton::Singleton() { if (Flags::HasFlag("version")) { - printf("IW4x " VERSION " (built " __DATE__ " " __TIME__ ")\n"); + printf("%s", "IW4x " VERSION " (built " __DATE__ " " __TIME__ ")\n"); printf("%d\n", REVISION); - ExitProcess(0); + std::exit(0); } Console::FreeNativeConsole(); if (Loader::IsPerformingUnitTests() || Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) return; - Singleton::FirstInstance = (CreateMutexA(nullptr, FALSE, "iw4x_mutex") && GetLastError() != ERROR_ALREADY_EXISTS); + FirstInstance = (CreateMutexA(nullptr, FALSE, "iw4x_mutex") && GetLastError() != ERROR_ALREADY_EXISTS); - if (!Singleton::FirstInstance && !ConnectProtocol::Used() && MessageBoxA(nullptr, "Do you want to start another instance?\nNot all features will be available!", "Game already running", MB_ICONEXCLAMATION | MB_YESNO) == IDNO) + if (!FirstInstance && !ConnectProtocol::Used() && MessageBoxA(nullptr, "Do you want to start another instance?\nNot all features will be available!", "Game already running", MB_ICONEXCLAMATION | MB_YESNO) == IDNO) { - ExitProcess(0); + std::exit(0); } } } diff --git a/src/Components/Modules/SlowMotion.cpp b/src/Components/Modules/SlowMotion.cpp index 0d57f701..09df94fc 100644 --- a/src/Components/Modules/SlowMotion.cpp +++ b/src/Components/Modules/SlowMotion.cpp @@ -1,4 +1,5 @@ #include +#include "SlowMotion.hpp" namespace Components { diff --git a/src/Components/Modules/SoundMutexFix.cpp b/src/Components/Modules/SoundMutexFix.cpp index 69662c7b..935ffefd 100644 --- a/src/Components/Modules/SoundMutexFix.cpp +++ b/src/Components/Modules/SoundMutexFix.cpp @@ -1,4 +1,5 @@ #include +#include "SoundMutexFix.hpp" namespace Components { @@ -26,7 +27,7 @@ namespace Components void __stdcall SoundMutexFix::LockSoundMutex(int unk) { - std::lock_guard lock(SoundMutexFix::SNDMutex); + std::lock_guard lock(SNDMutex); DWORD funcPtr = *reinterpret_cast(0x6D7554); // AIL_close_stream Utils::Hook::Call(funcPtr)(unk); @@ -34,6 +35,6 @@ namespace Components SoundMutexFix::SoundMutexFix() { - Utils::Hook(0x689EFE, &SoundMutexFix::LockSoundMutex, HOOK_JUMP).install()->quick(); + Utils::Hook(0x689EFE, &LockSoundMutex, HOOK_JUMP).install()->quick(); } } \ No newline at end of file diff --git a/src/Components/Modules/StartupMessages.cpp b/src/Components/Modules/StartupMessages.cpp index 333171a6..b9b5765e 100644 --- a/src/Components/Modules/StartupMessages.cpp +++ b/src/Components/Modules/StartupMessages.cpp @@ -1,4 +1,5 @@ #include +#include "StartupMessages.hpp" namespace Components { diff --git a/src/Components/Modules/Stats.cpp b/src/Components/Modules/Stats.cpp index 62a61b50..a6ab031e 100644 --- a/src/Components/Modules/Stats.cpp +++ b/src/Components/Modules/Stats.cpp @@ -1,4 +1,7 @@ #include +#include "ServerCommands.hpp" +#include "Stats.hpp" + #include "GSC/Script.hpp" namespace Components diff --git a/src/Components/Modules/StringTable.cpp b/src/Components/Modules/StringTable.cpp index e9c82836..44b8a61b 100644 --- a/src/Components/Modules/StringTable.cpp +++ b/src/Components/Modules/StringTable.cpp @@ -1,4 +1,5 @@ #include +#include "StringTable.hpp" namespace Components { @@ -22,8 +23,8 @@ namespace Components if (table) { table->name = allocator->duplicateString(filename); - table->columnCount = parsedTable.getColumns(); - table->rowCount = parsedTable.getRows(); + table->columnCount = static_cast(parsedTable.getColumns()); + table->rowCount = static_cast(parsedTable.getRows()); table->values = allocator->allocateArray(table->columnCount * table->rowCount); @@ -45,12 +46,12 @@ namespace Components } } - StringTable::StringTableMap[filename] = table; + StringTableMap[filename] = table; } } else { - StringTable::StringTableMap[filename] = nullptr; + StringTableMap[filename] = nullptr; } return table; @@ -64,13 +65,13 @@ namespace Components std::string filename = Utils::String::ToLower(_filename); - if (StringTable::StringTableMap.contains(filename)) + if (StringTableMap.contains(filename)) { - header.stringTable = StringTable::StringTableMap[filename]; + header.stringTable = StringTableMap[filename]; } else { - header.stringTable = StringTable::LoadObject(filename); + header.stringTable = LoadObject(filename); } return header; diff --git a/src/Components/Modules/StructuredData.cpp b/src/Components/Modules/StructuredData.cpp index 42ead9e1..f60437c7 100644 --- a/src/Components/Modules/StructuredData.cpp +++ b/src/Components/Modules/StructuredData.cpp @@ -1,4 +1,5 @@ #include +#include "StructuredData.hpp" namespace Components { @@ -199,7 +200,7 @@ namespace Components for (int i = 156;; ++i) { // We're on DB thread (OnLoad) so use DB thread for FS - FileSystem::File definition(Utils::String::VA("%s/%d.json", filename.data(), i), Game::FsThread::FS_THREAD_DATABASE); + FileSystem::File definition(std::format("{}/{}.json", filename, i), Game::FsThread::FS_THREAD_DATABASE); if (!definition.exists()) break; std::vector> enumContainer; diff --git a/src/Components/Modules/Theatre.cpp b/src/Components/Modules/Theatre.cpp index a62826d2..a48a5b07 100644 --- a/src/Components/Modules/Theatre.cpp +++ b/src/Components/Modules/Theatre.cpp @@ -1,4 +1,6 @@ #include +#include "Theatre.hpp" +#include "UIFeeder.hpp" namespace Components { @@ -25,11 +27,11 @@ namespace Components void Theatre::StoreBaseline(PBYTE snapshotMsg) { // Store offset and length - Theatre::BaselineSnapshotMsgLen = *reinterpret_cast(snapshotMsg + 20); - Theatre::BaselineSnapshotMsgOff = *reinterpret_cast(snapshotMsg + 28) - 7; + BaselineSnapshotMsgLen = *reinterpret_cast(snapshotMsg + 20); + BaselineSnapshotMsgOff = *reinterpret_cast(snapshotMsg + 28) - 7; // Copy to our snapshot buffer - std::memcpy(Theatre::BaselineSnapshot, *reinterpret_cast(snapshotMsg + 8), *reinterpret_cast(snapshotMsg + 20)); + std::memcpy(BaselineSnapshot, *reinterpret_cast(snapshotMsg + 8), *reinterpret_cast(snapshotMsg + 20)); } __declspec(naked) void Theatre::BaselineStoreStub() @@ -37,7 +39,7 @@ namespace Components _asm { push edi - call Theatre::StoreBaseline + call StoreBaseline pop edi mov edx, 5ABEF5h @@ -48,15 +50,15 @@ namespace Components void Theatre::WriteBaseline() { static unsigned char bufData[131072]; - static char cmpData[131072]; + static unsigned char cmpData[131072]; Game::msg_t buf; Game::MSG_Init(&buf, bufData, 131072); - Game::MSG_WriteData(&buf, &Theatre::BaselineSnapshot[Theatre::BaselineSnapshotMsgOff], Theatre::BaselineSnapshotMsgLen - Theatre::BaselineSnapshotMsgOff); + Game::MSG_WriteData(&buf, &BaselineSnapshot[BaselineSnapshotMsgOff], BaselineSnapshotMsgLen - BaselineSnapshotMsgOff); Game::MSG_WriteByte(&buf, 6); - const auto compressedSize = Game::MSG_WriteBitsCompress(false, reinterpret_cast(buf.data), cmpData, buf.cursize); + const auto compressedSize = Game::MSG_WriteBitsCompress(false, buf.data, cmpData, buf.cursize); const auto fileCompressedSize = compressedSize + 4; int byte8 = 8; @@ -86,7 +88,7 @@ namespace Components __asm { pushad - call Theatre::WriteBaseline + call WriteBaseline popad // Restore overwritten operation @@ -161,12 +163,12 @@ namespace Components { Game::Com_Printf(channel, message, file); - Theatre::CurrentInfo.name = file; - Theatre::CurrentInfo.mapname = (*Game::sv_mapname)->current.string; - Theatre::CurrentInfo.gametype = (*Game::sv_gametype)->current.string; - Theatre::CurrentInfo.author = Steam::SteamFriends()->GetPersonaName(); - Theatre::CurrentInfo.length = Game::Sys_Milliseconds(); - std::time(&Theatre::CurrentInfo.timeStamp); + CurrentInfo.name = file; + CurrentInfo.mapname = (*Game::sv_mapname)->current.string; + CurrentInfo.gametype = (*Game::sv_gametype)->current.string; + CurrentInfo.author = Steam::SteamFriends()->GetPersonaName(); + CurrentInfo.length = Game::Sys_Milliseconds(); + std::time(&CurrentInfo.timeStamp); } void Theatre::StopRecordStub(int channel, char* message) @@ -174,59 +176,56 @@ namespace Components Game::Com_Printf(channel, message); // Store correct length - Theatre::CurrentInfo.length = Game::Sys_Milliseconds() - Theatre::CurrentInfo.length; + CurrentInfo.length = Game::Sys_Milliseconds() - CurrentInfo.length; // Write metadata - FileSystem::FileWriter meta(Utils::String::VA("%s.json", Theatre::CurrentInfo.name.data())); - meta.write(nlohmann::json(Theatre::CurrentInfo.to_json()).dump()); + FileSystem::FileWriter meta(std::format("{}.json", CurrentInfo.name)); + meta.write(nlohmann::json(CurrentInfo.to_json()).dump()); } void Theatre::LoadDemos([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) { - Theatre::CurrentSelection = 0; - Theatre::Demos.clear(); + CurrentSelection = 0; + Demos.clear(); const auto demos = FileSystem::GetFileList("demos/", "dm_13"); for (auto demo : demos) { - FileSystem::File meta(Utils::String::VA("demos/%s.json", demo.data())); - - if (meta.exists()) + if (FileSystem::File meta = std::format("demos/{}.json", demo)) { nlohmann::json metaObject; try { metaObject = nlohmann::json::parse(meta.getBuffer()); + + DemoInfo demoInfo; + demoInfo.name = demo.substr(0, demo.find_last_of(".")); + demoInfo.author = metaObject["author"].get(); + demoInfo.gametype = metaObject["gametype"].get(); + demoInfo.mapname = metaObject["mapname"].get(); + demoInfo.length = metaObject["length"].get(); + auto timestamp = metaObject["timestamp"].get(); + demoInfo.timeStamp = std::strtoll(timestamp.data(), nullptr, 10); + + Demos.push_back(demoInfo); } catch (const nlohmann::json::parse_error& ex) { Logger::PrintError(Game::CON_CHANNEL_ERROR, "Json Parse Error: {}\n", ex.what()); - continue; } - - Theatre::DemoInfo demoInfo; - demoInfo.name = demo.substr(0, demo.find_last_of(".")); - demoInfo.author = metaObject["author"].get(); - demoInfo.gametype = metaObject["gametype"].get(); - demoInfo.mapname = metaObject["mapname"].get(); - demoInfo.length = metaObject["length"].get(); - auto timestamp = metaObject["timestamp"].get(); - demoInfo.timeStamp = _atoi64(timestamp.data()); - - Theatre::Demos.push_back(demoInfo); } } // Reverse, latest demo first! - std::ranges::reverse(Theatre::Demos); + std::ranges::reverse(Demos); } void Theatre::DeleteDemo([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) { - if (Theatre::CurrentSelection < Theatre::Demos.size()) + if (CurrentSelection < Demos.size()) { - Theatre::DemoInfo demoInfo = Theatre::Demos[Theatre::CurrentSelection]; + auto demoInfo = Demos.at(CurrentSelection); Logger::Print("Deleting demo {}...\n", demoInfo.name); @@ -242,31 +241,30 @@ namespace Components Dvar::Var("ui_demo_date").set(""); // Reload demos - Theatre::LoadDemos(UIScript::Token(), info); + LoadDemos(UIScript::Token(), info); } } void Theatre::PlayDemo([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) { - if (Theatre::CurrentSelection < Theatre::Demos.size()) + if (CurrentSelection < Demos.size()) { - Command::Execute(Utils::String::VA("demo %s", Theatre::Demos[Theatre::CurrentSelection].name.data()), true); + Command::Execute(std::format("demo {}", Demos[CurrentSelection].name), true); Command::Execute("demoback", false); } } unsigned int Theatre::GetDemoCount() { - return Theatre::Demos.size(); + return Demos.size(); } // Omit column here const char* Theatre::GetDemoText(unsigned int item, int /*column*/) { - if (item < Theatre::Demos.size()) + if (item < Demos.size()) { - Theatre::DemoInfo info = Theatre::Demos[item]; - + auto info = Demos.at(item); return Utils::String::VA("%s on %s", Game::UI_LocalizeGameType(info.gametype.data()), Game::UI_LocalizeMapName(info.mapname.data())); } @@ -275,13 +273,13 @@ namespace Components void Theatre::SelectDemo(unsigned int index) { - if (index < Theatre::Demos.size()) + if (index < Demos.size()) { - Theatre::CurrentSelection = index; - Theatre::DemoInfo info = Theatre::Demos[index]; + CurrentSelection = index; + auto info = Demos.at(index); tm time; - char buffer[1000] = { 0 }; + char buffer[1000] = {0}; localtime_s(&time, &info.timeStamp); asctime_s(buffer, sizeof buffer, &time); @@ -299,7 +297,7 @@ namespace Components if (Dvar::Var("cl_autoRecord").get() && !*Game::demoPlaying) { std::vector files; - std::vector demos = FileSystem::GetFileList("demos/", "dm_13"); + auto demos = FileSystem::GetFileList("demos/", "dm_13"); for (auto demo : demos) { @@ -309,16 +307,16 @@ namespace Components } } - int numDel = files.size() - Dvar::Var("cl_demosKeep").get(); + auto numDel = static_cast(files.size()) - Dvar::Var("cl_demosKeep").get(); - for (int i = 0; i < numDel; ++i) + for (auto i = 0; i < numDel; ++i) { Logger::Print("Deleting old demo {}\n", files[i]); - FileSystem::_DeleteFile("demos", files[i].data()); - FileSystem::_DeleteFile("demos", Utils::String::VA("%s.json", files[i].data())); + FileSystem::_DeleteFile("demos", files[i]); + FileSystem::_DeleteFile("demos", std::format("%s.json", files[i])); } - Command::Execute(Utils::String::VA("record auto_%lld", time(nullptr)), true); + Command::Execute(Utils::String::VA("record auto_%lld", std::time(nullptr)), true); } return Utils::Hook::Call(0x42BBB0)(); @@ -326,17 +324,10 @@ namespace Components void Theatre::MapChangeStub() { - Theatre::StopRecording(); + StopRecording(); Utils::Hook::Call(0x464A60)(); } - // DANGEROUS, DON'T USE THIS ONE! - void Theatre::MapChangeSVStub(char* a1, char* a2) - { - Theatre::StopRecording(); - Utils::Hook::Call(0x487C50)(a1, a2); - } - void Theatre::StopRecording() { if (*Game::demoRecording) @@ -350,30 +341,29 @@ namespace Components Dvar::Register("cl_autoRecord", true, Game::DVAR_ARCHIVE, "Automatically record games."); Dvar::Register("cl_demosKeep", 30, 1, 999, Game::DVAR_ARCHIVE, "How many demos to keep with autorecord."); - Utils::Hook(0x5A8370, Theatre::GamestateWriteStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x5A85D2, Theatre::RecordGamestateStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x5ABE36, Theatre::BaselineStoreStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x5A8630, Theatre::BaselineToFileStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x4CB3EF, Theatre::UISetActiveMenuStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x50320E, Theatre::AdjustTimeDeltaStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x5A8E03, Theatre::ServerTimedOutStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x5A8370, GamestateWriteStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x5A85D2, RecordGamestateStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x5ABE36, BaselineStoreStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x5A8630, BaselineToFileStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4CB3EF, UISetActiveMenuStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x50320E, AdjustTimeDeltaStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x5A8E03, ServerTimedOutStub, HOOK_JUMP).install()->quick(); // Hook commands to enforce metadata generation - Utils::Hook(0x5A82AE, Theatre::RecordStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x5A8156, Theatre::StopRecordStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x5A82AE, RecordStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x5A8156, StopRecordStub, HOOK_CALL).install()->quick(); // Autorecording - Utils::Hook(0x5A1D6A, Theatre::InitCGameStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x4A712A, Theatre::MapChangeStub, HOOK_CALL).install()->quick(); - //Utils::Hook(0x5AA91C, Theatre::MapChangeSVStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x5A1D6A, InitCGameStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x4A712A, MapChangeStub, HOOK_CALL).install()->quick(); // UIScripts - UIScript::Add("loadDemos", Theatre::LoadDemos); - UIScript::Add("launchDemo", Theatre::PlayDemo); - UIScript::Add("deleteDemo", Theatre::DeleteDemo); + UIScript::Add("loadDemos", LoadDemos); + UIScript::Add("launchDemo", PlayDemo); + UIScript::Add("deleteDemo", DeleteDemo); // Feeder - UIFeeder::Add(10.0f, Theatre::GetDemoCount, Theatre::GetDemoText, Theatre::SelectDemo); + UIFeeder::Add(10.0f, GetDemoCount, GetDemoText, SelectDemo); // set the configstrings stuff to load the default (empty) string table; this should allow demo recording on all gametypes/maps if (!Dedicated::IsEnabled()) Utils::Hook::Set(0x47440B, "mp/defaultStringTable.csv"); @@ -381,11 +371,5 @@ namespace Components // Change font size Utils::Hook::Set(0x5AC854, 2); Utils::Hook::Set(0x5AC85A, 2); - -// Command::Add("democycle", [] (Command::Params params) -// { -// // Cmd_FollowCycle_f -// Utils::Hook::Call(0x458ED0)(Game::g_entities, -1); -// }); } } diff --git a/src/Components/Modules/Theatre.hpp b/src/Components/Modules/Theatre.hpp index 34c4e76b..0a880c68 100644 --- a/src/Components/Modules/Theatre.hpp +++ b/src/Components/Modules/Theatre.hpp @@ -62,7 +62,6 @@ namespace Components static uint32_t InitCGameStub(); static void MapChangeStub(); - static void MapChangeSVStub(char* a1, char* a2); static void RecordStub(int channel, char* message, char* file); static void StopRecordStub(int channel, char* message); diff --git a/src/Components/Modules/Threading.cpp b/src/Components/Modules/Threading.cpp new file mode 100644 index 00000000..0f77dea2 --- /dev/null +++ b/src/Components/Modules/Threading.cpp @@ -0,0 +1,171 @@ +#include +#include "Threading.hpp" + +namespace Components +{ + namespace FrameTime + { + void NetSleep(int mSec) + { + if (mSec < 0) mSec = 0; + + fd_set fdr; + FD_ZERO(&fdr); + + auto highestFd = INVALID_SOCKET; + if (*Game::ip_socket != INVALID_SOCKET) + { + FD_SET(*Game::ip_socket, &fdr); + highestFd = *Game::ip_socket; + } + + if (highestFd == INVALID_SOCKET) + { + // windows ain't happy when select is called without valid FDs + std::this_thread::sleep_for(std::chrono::milliseconds(mSec)); + return; + } + + timeval timeout; + timeout.tv_sec = mSec / 1000; + timeout.tv_usec = (mSec % 1000) * 1000; + + const auto retVal = ::select(highestFd + 1, &fdr, nullptr, nullptr, &timeout); + + if (retVal == SOCKET_ERROR) + { + Logger::Warning(Game::CON_CHANNEL_SYSTEM, "WinAPI: select failed: {}\n", Game::NET_ErrorString()); + } + else if (retVal > 0) + { + if (Dedicated::IsRunning()) + { + Game::Com_ServerPacketEvent(); + } + else + { + Game::Com_ClientPacketEvent(); + } + } + } + + void SVFrameWaitFunc() + { + if (*Game::sv_timeResidual < 50) + { + NetSleep(50 - *Game::sv_timeResidual); + } + } + + void __declspec(naked) SVFrameWaitStub() + { + __asm + { + pushad + call SVFrameWaitFunc + popad + + push 4CD420h + ret + } + } + + int ComTimeVal(int minMsec) + { + const auto timeVal = Game::Sys_Milliseconds() - *Game::com_frameTime; + return (timeVal >= minMsec ? 0 : minMsec - timeVal); + } + + int ComFrameWait(int minMsec) + { + do + { + const auto timeVal = ComTimeVal(minMsec); + NetSleep(timeVal < 1 ? 0 : timeVal - 1); + } while (ComTimeVal(minMsec)); + + const auto lastTime = *Game::com_frameTime; + Game::Com_EventLoop(); + *Game::com_frameTime = Game::Sys_Milliseconds(); + + return *Game::com_frameTime - lastTime; + } + + void __declspec(naked) ComFrameWaitStub() + { + __asm + { + push ecx + pushad + + push edi + call ComFrameWait + add esp, 4 + + mov [esp + 20h], eax + popad + pop eax + mov ecx, eax + + mov edx, 1AD7934h // com_sv_running + cmp byte ptr [edx + 10h], 0 + + push 47DDC1h + ret + } + } + } + + __declspec(naked) void Threading::FrameEpilogueStub() + { + __asm + { + pop edi + pop esi + pop ebx + mov esp, ebp + pop ebp + retn + } + } + + __declspec(naked) void Threading::PacketEventStub() + { + __asm + { + mov eax, 49F0B0h + call eax + + mov eax, 458160h + jmp eax + } + } + + Threading::Threading() + { + // remove starting of server thread from Com_Init_Try_Block_Function + Utils::Hook::Nop(0x60BEC0, 5); + + // make server thread function jump to per-frame stuff + Utils::Hook(0x627049, 0x6271CE, HOOK_JUMP).install()->quick(); + + // make SV_WaitServer insta-return + Utils::Hook::Set(0x4256F0, 0xC3); + + // dvar setting function, unknown stuff related to server thread sync + Utils::Hook::Set(0x647781, 0xEB); + + Utils::Hook(0x627695, 0x627040, HOOK_CALL).install()->quick(); + Utils::Hook(0x43D1C7, PacketEventStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x6272E3, FrameEpilogueStub, HOOK_JUMP).install()->quick(); + + if (Dedicated::IsEnabled()) + { + Utils::Hook(0x4BAAAD, FrameTime::SVFrameWaitStub, HOOK_CALL).install()->quick(); + } + else + { + Utils::Hook(0x47DD80, FrameTime::ComFrameWaitStub, HOOK_JUMP).install()->quick(); + } + } +} diff --git a/src/Components/Modules/Threading.hpp b/src/Components/Modules/Threading.hpp new file mode 100644 index 00000000..2e4354d3 --- /dev/null +++ b/src/Components/Modules/Threading.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace Components +{ + class Threading : public Component + { + public: + Threading(); + + private: + static void FrameEpilogueStub(); + static void PacketEventStub(); + }; +} diff --git a/src/Components/Modules/UIFeeder.cpp b/src/Components/Modules/UIFeeder.cpp index 03c445fe..028e82f7 100644 --- a/src/Components/Modules/UIFeeder.cpp +++ b/src/Components/Modules/UIFeeder.cpp @@ -1,4 +1,5 @@ #include +#include "UIFeeder.hpp" namespace Components { diff --git a/src/Components/Modules/UIScript.cpp b/src/Components/Modules/UIScript.cpp index 835d4682..36abb50f 100644 --- a/src/Components/Modules/UIScript.cpp +++ b/src/Components/Modules/UIScript.cpp @@ -9,7 +9,7 @@ namespace Components { if (this->isValid()) { - return std::atoi(this->token); + return std::strtol(this->token, nullptr, 0); } return 0; @@ -49,22 +49,22 @@ namespace Components return &Game::uiInfoArray[localClientNum]; } - void UIScript::Add(const std::string& name, const UIScript::UIScriptHandler& callback) + void UIScript::Add(const std::string& name, const UIScriptHandler& callback) { - UIScript::UIScripts[name] = callback; + UIScripts[name] = callback; } void UIScript::AddOwnerDraw(int ownerdraw, const std::function& callback) { - UIScript::UIOwnerDraws[ownerdraw] = callback; + UIOwnerDraws[ownerdraw] = callback; } bool UIScript::RunMenuScript(const char* name, const char** args) { - if (const auto itr = UIScript::UIScripts.find(name); itr != UIScript::UIScripts.end()) + if (const auto itr = UIScripts.find(name); itr != UIScripts.end()) { - const auto* info = UIScript::UI_GetClientInfo(0); - itr->second(UIScript::Token(args), info); + const auto* info = UI_GetClientInfo(0); + itr->second(Token(args), info); return true; } @@ -75,7 +75,7 @@ namespace Components { if (key == 200 || key == 201) // mouse buttons { - for (auto i = UIScript::UIOwnerDraws.begin(); i != UIScript::UIOwnerDraws.end(); ++i) + for (auto i = UIOwnerDraws.begin(); i != UIOwnerDraws.end(); ++i) { if (i->first == ownerDraw) { @@ -93,12 +93,12 @@ namespace Components { mov eax, esp add eax, 8h - mov edx, eax // UIScript name + mov edx, eax // UIScript name mov eax, [esp + 0C10h] // UIScript args push eax push edx - call UIScript::RunMenuScript + call RunMenuScript add esp, 8h test al, al @@ -123,15 +123,15 @@ namespace Components if (Dedicated::IsEnabled()) return; // Install handler - Utils::Hook::RedirectJump(0x45EC59, UIScript::RunMenuScriptStub); + Utils::Hook::RedirectJump(0x45EC59, RunMenuScriptStub); // Install ownerdraw handler - Utils::Hook(0x63D233, UIScript::OwnerDrawHandleKeyStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x63D233, OwnerDrawHandleKeyStub, HOOK_CALL).install()->quick(); } UIScript::~UIScript() { - UIScript::UIScripts.clear(); - UIScript::UIOwnerDraws.clear(); + UIScripts.clear(); + UIOwnerDraws.clear(); } } diff --git a/src/Components/Modules/UserInfo.cpp b/src/Components/Modules/UserInfo.cpp index ab425dda..4b71931b 100644 --- a/src/Components/Modules/UserInfo.cpp +++ b/src/Components/Modules/UserInfo.cpp @@ -1,4 +1,6 @@ #include +#include "UserInfo.hpp" + #include "GSC/Script.hpp" namespace Components diff --git a/src/Components/Modules/VisionFile.cpp b/src/Components/Modules/VisionFile.cpp index 04ef18aa..398d29b3 100644 --- a/src/Components/Modules/VisionFile.cpp +++ b/src/Components/Modules/VisionFile.cpp @@ -1,4 +1,5 @@ #include +#include "VisionFile.hpp" namespace Components { diff --git a/src/Components/Modules/Voice.cpp b/src/Components/Modules/Voice.cpp index edc84cca..10e40a21 100644 --- a/src/Components/Modules/Voice.cpp +++ b/src/Components/Modules/Voice.cpp @@ -1,5 +1,5 @@ #include -#include "Game/Engine/LargeLocal.hpp" +#include "Voice.hpp" namespace Components { @@ -40,8 +40,8 @@ namespace Components Game::msg_t msg{}; const auto clientNum = client - Game::svs_clients; - const Game::Engine::LargeLocal msg_buf_large_local(0x10000); - auto* msg_buf = static_cast(msg_buf_large_local.GetBuf()); + const auto msg_buf_large = std::make_unique(0x10000); + auto* msg_buf = msg_buf_large.get(); assert(VoicePacketCount[clientNum] >= 0); diff --git a/src/Components/Modules/Vote.cpp b/src/Components/Modules/Vote.cpp index 3a74336b..9679907d 100644 --- a/src/Components/Modules/Vote.cpp +++ b/src/Components/Modules/Vote.cpp @@ -1,4 +1,7 @@ #include +#include "ClientCommand.hpp" +#include "MapRotation.hpp" +#include "Vote.hpp" using namespace Utils::String; @@ -316,11 +319,14 @@ namespace Components Vote::Vote() { // Replicate g_allowVote - Utils::Hook::Set(0x5E3A4F, Game::DVAR_INTERNAL | Game::DVAR_CODINFO); + Utils::Hook::Set(0x5E3A4F, Game::DVAR_INTERNAL | Game::DVAR_CODINFO); ClientCommand::Add("callvote", Cmd_CallVote_f); ClientCommand::Add("vote", Cmd_Vote_f); + Menus::Add("ui_mp/scriptmenus/callvote.menu"); + Menus::Add("ui_mp/scriptmenus/kickplayer.menu"); + UIScript::Add("voteKick", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) { if (info->playerIndex >= 0 && info->playerIndex < Game::sharedUiInfo->playerCount) diff --git a/src/Components/Modules/Weapon.cpp b/src/Components/Modules/Weapon.cpp index ae82ada9..01af8241 100644 --- a/src/Components/Modules/Weapon.cpp +++ b/src/Components/Modules/Weapon.cpp @@ -1,8 +1,12 @@ #include +#include "Weapon.hpp" + #include "GSC/Script.hpp" namespace Components { + const Game::dvar_t* Weapon::BGWeaponOffHandFix; + Game::WeaponCompleteDef* Weapon::LoadWeaponCompleteDef(const char* name) { if (auto* rawWeaponFile = Game::BG_LoadWeaponCompleteDefInternal("mp", name)) @@ -481,7 +485,7 @@ namespace Components // Game code push 0x48BB2D - retn + ret null: push 0x48BB1F // Exit function @@ -499,7 +503,7 @@ namespace Components jz touched push 0x56E82C - retn + ret touched: test dword ptr [edi + 0x2BC], PWF_DISABLE_WEAPON_PICKUP @@ -515,7 +519,32 @@ namespace Components continue_func: push 0x56E84C - retn + ret + } + } + + __declspec(naked) void Weapon::JavelinResetHook_Stub() + { + static DWORD PM_Weapon_OffHandEnd_t = 0x577A10; + + __asm + { + call PM_Weapon_OffHandEnd_t + + push eax + mov eax, BGWeaponOffHandFix + cmp byte ptr [eax + 0x10], 1 + pop eax + + jne safeReturn + + mov dword ptr [esi + 0x34], 0 // playerState_s.grenadeTimeLeft + + safeReturn: + pop edi + pop esi + pop ebx + ret } } @@ -565,9 +594,14 @@ namespace Components Utils::Hook(0x59E341, CG_UpdatePrimaryForAltModeWeapon_Stub, HOOK_JUMP).install()->quick(); Utils::Hook(0x48BB25, CG_SelectWeaponIndex_Stub, HOOK_JUMP).install()->quick(); - AddScriptMethods(); - AssertOffset(Game::playerState_s, Game::playerState_s::weapCommon.weapFlags, 0x2BC); Utils::Hook(0x56E825, WeaponEntCanBeGrabbed_Stub, HOOK_JUMP).install()->quick(); + + // Javelin fix (PM_Weapon_OffHandEnd) + AssertOffset(Game::playerState_s, grenadeTimeLeft, 0x34); + BGWeaponOffHandFix = Game::Dvar_RegisterBool("bg_weaponOffHandFix", true, Game::DVAR_CODINFO, "Reset grenadeTimeLeft after using off hand weapon"); + Utils::Hook(0x578F52, JavelinResetHook_Stub, HOOK_JUMP).install()->quick(); + + AddScriptMethods(); } } diff --git a/src/Components/Modules/Weapon.hpp b/src/Components/Modules/Weapon.hpp index 3b6d14d5..7e5a235a 100644 --- a/src/Components/Modules/Weapon.hpp +++ b/src/Components/Modules/Weapon.hpp @@ -13,6 +13,8 @@ namespace Components Weapon(); private: + static const Game::dvar_t* BGWeaponOffHandFix; + static Game::WeaponCompleteDef* LoadWeaponCompleteDef(const char* name); static void PatchLimit(); static void* LoadNoneWeaponHook(); @@ -29,6 +31,8 @@ namespace Components static void CG_UpdatePrimaryForAltModeWeapon_Stub(); static void CG_SelectWeaponIndex_Stub(); + static void JavelinResetHook_Stub(); + static void WeaponEntCanBeGrabbed_Stub(); static void AddScriptMethods(); }; diff --git a/src/Components/Modules/ZoneBuilder.cpp b/src/Components/Modules/ZoneBuilder.cpp index 8b55e5f8..4d10a89d 100644 --- a/src/Components/Modules/ZoneBuilder.cpp +++ b/src/Components/Modules/ZoneBuilder.cpp @@ -1,4 +1,8 @@ #include +#include "Console.hpp" + +#include + #include "AssetInterfaces/ILocalizeEntry.hpp" namespace Components @@ -8,7 +12,8 @@ namespace Components bool ZoneBuilder::MainThreadInterrupted; DWORD ZoneBuilder::InterruptingThreadId; - volatile bool ZoneBuilder::Terminate = false; + + volatile bool ZoneBuilder::CommandThreadTerminate = false; std::thread ZoneBuilder::CommandThread; Dvar::Var ZoneBuilder::PreferDiskAssetsDvar; @@ -20,7 +25,7 @@ namespace Components // Side note: if you need a fastfile larger than 100MB, you're doing it wrong- // Well, decompressed maps can get way larger than 100MB, so let's increase that. buffer(0xC800000), - zoneName(name), dataMap("zone_source/" + name + ".csv"), branding{ nullptr }, assetDepth(0) + zoneName(name), dataMap("zone_source/" + name + ".csv"), branding{nullptr}, assetDepth(0) { } @@ -43,7 +48,7 @@ namespace Components if (!found) { - Logger::Print("Asset {} of type {} was loaded, but not written!", name, Game::DB_GetXAssetTypeName(subAsset.type)); + Logger::Print("Asset {} of type {} was loaded, but not written!\n", name, Game::DB_GetXAssetTypeName(subAsset.type)); } } @@ -63,17 +68,18 @@ namespace Components if (!found) { - Logger::Error(Game::ERR_FATAL, "Asset {} of type {} was written, but not loaded!", name, Game::DB_GetXAssetTypeName(alias.first.type)); + Logger::Error(Game::ERR_FATAL, "Asset {} of type {} was written, but not loaded!\n", name, Game::DB_GetXAssetTypeName(alias.first.type)); } } #endif // Unload our fastfiles - Game::XZoneInfo info; + Game::XZoneInfo info{}; info.name = nullptr; info.allocFlags = 0; info.freeFlags = 0x20; + Game::DB_LoadXAssets(&info, 1, true); AssetHandler::ClearTemporaryAssets(); @@ -116,7 +122,7 @@ namespace Components this->writeZone(); } - void ZoneBuilder::Zone::loadFastFiles() + void ZoneBuilder::Zone::loadFastFiles() const { Logger::Print("Loading required FastFiles...\n"); @@ -155,12 +161,17 @@ namespace Components if (this->dataMap.getElementAt(i, 0) == "localize"s) { const auto filename = this->dataMap.getElementAt(i, 1); - FileSystem::File file(std::format("localizedstrings/{}.str", filename)); - if (file.exists()) + if (FileSystem::File file = std::format("localizedstrings/{}.str", filename)) { Assets::ILocalizeEntry::ParseLocalizedStringsFile(this, filename, file.getName()); continue; } + + if (FileSystem::File file = std::format("localizedstrings/{}.json", filename)) + { + Assets::ILocalizeEntry::ParseLocalizedStringsJson(this, file); + continue; + } } if (this->dataMap.getColumns(i) > 2) @@ -217,6 +228,9 @@ namespace Components // Sanitize name for empty assets if (name[0] == ',') name.erase(name.begin()); + // Fix forward slashes for FXEffectDef (and probably other assets) + std::replace(name.begin(), name.end(), '\\', '/'); + if (this->findAsset(type, name) != -1 || this->findSubAsset(type, name).data) return true; if (type == Game::XAssetType::ASSET_TYPE_INVALID || type >= Game::XAssetType::ASSET_TYPE_COUNT) @@ -237,6 +251,9 @@ namespace Components asset.type = type; asset.header = assetHeader; + // Handle script strings + AssetHandler::ZoneMark(asset, this); + if (isSubAsset) { this->loadedSubAssets.push_back(asset); @@ -246,8 +263,6 @@ namespace Components this->loadedAssets.push_back(asset); } - // Handle script strings - AssetHandler::ZoneMark(asset, this); return true; } @@ -262,8 +277,9 @@ namespace Components if (asset->type != type) continue; - const char* assetName = Game::DB_GetXAssetName(asset); - if (assetName[0] == ',') ++assetName; + const auto* assetName = Game::DB_GetXAssetName(asset); + if (!assetName) return -1; + if (assetName[0] == ',' && assetName[1] != '\0') ++assetName; if (this->getAssetName(type, assetName) == name) { @@ -414,6 +430,10 @@ namespace Components #endif Utils::IO::WriteFile("uncompressed", zoneBuffer); + const auto _0 = gsl::finally([] + { + Utils::IO::RemoveFile("uncompressed"); + }); zoneBuffer = Utils::Compression::ZLib::Compress(zoneBuffer); outBuffer.append(zoneBuffer); @@ -433,38 +453,30 @@ namespace Components Utils::Stream::ClearPointer(&zoneHeader.assetList.assets); // Increment ScriptStrings count (for empty script string) if available - if (!this->scriptStrings.empty()) - { - zoneHeader.assetList.stringList.count = this->scriptStrings.size() + 1; - Utils::Stream::ClearPointer(&zoneHeader.assetList.stringList.strings); - } + zoneHeader.assetList.stringList.count = this->scriptStrings.size() + 1; + Utils::Stream::ClearPointer(&zoneHeader.assetList.stringList.strings); // Write header this->buffer.save(&zoneHeader, sizeof(Game::ZoneHeader)); this->buffer.pushBlock(Game::XFILE_BLOCK_VIRTUAL); // Push main stream onto the stream stack // Write ScriptStrings, if available - if (!this->scriptStrings.empty()) + this->buffer.saveNull(4); + // Empty script string? + // This actually represents a NULL string, but as scriptString. + // So scriptString loading for NULL scriptStrings from fastfile results in a NULL scriptString. + // That's the reason why the count is incremented by 1, if scriptStrings are available. + + // Write ScriptString pointer table + for (std::size_t i = 0; i < this->scriptStrings.size(); ++i) { - this->buffer.saveNull(4); - // Empty script string? - // This actually represents a NULL string, but as scriptString. - // So scriptString loading for NULL scriptStrings from fastfile results in a NULL scriptString. - // That's the reason why the count is incremented by 1, if scriptStrings are available. + this->buffer.saveMax(4); + } - // Write ScriptString pointer table - for (std::size_t i = 0; i < this->scriptStrings.size(); ++i) - { - this->buffer.saveMax(4); - } - - this->buffer.align(Utils::Stream::ALIGN_4); - - // Write ScriptStrings - for (auto ScriptString : this->scriptStrings) - { - this->buffer.saveString(ScriptString.data()); - } + // Write ScriptStrings + for (auto ScriptString : this->scriptStrings) + { + this->buffer.saveString(ScriptString.data()); } // Align buffer (4 bytes) to get correct offsets for pointers @@ -513,8 +525,10 @@ namespace Components // Add branding asset void ZoneBuilder::Zone::addBranding() { - const char* data = "FastFile built using the IW4x ZoneBuilder!"; - this->branding = { this->zoneName.data(), static_cast(strlen(data)), 0, data }; + constexpr auto* data = "Built using the IW4x Zone:B:uilder Version 4"; + auto dataLen = std::strlen(data); // + 1 is added by the save code + + this->branding = {this->zoneName.data(), 0, static_cast(dataLen), data}; if (this->findAsset(Game::XAssetType::ASSET_TYPE_RAWFILE, this->branding.name) != -1) { @@ -573,7 +587,7 @@ namespace Components int ZoneBuilder::Zone::addScriptString(const std::string& str) { - return this->addScriptString(Game::SL_GetString(str.data(), 0)); + return this->addScriptString(static_cast(Game::SL_GetString(str.data(), 0))); } // Mark a scriptString for writing and map it. @@ -625,9 +639,9 @@ namespace Components } // Remap a scriptString to it's corresponding value in the local scriptString table. - void ZoneBuilder::Zone::mapScriptString(unsigned short* gameIndex) + void ZoneBuilder::Zone::mapScriptString(unsigned short& gameIndex) { - *gameIndex = 0xFFFF & this->scriptStringMap[*gameIndex]; + gameIndex = 0xFFFF & this->scriptStringMap[gameIndex]; } // Store a new name for a given asset @@ -781,7 +795,8 @@ namespace Components return GetCurrentThreadId() == Utils::Hook::Get(0x1CDE7FC); } - static Game::XZoneInfo baseZones_old[] = { + static Game::XZoneInfo baseZones_old[] = + { { "code_pre_gfx_mp", Game::DB_ZONE_CODE, 0 }, { "localized_code_pre_gfx_mp", Game::DB_ZONE_CODE_LOC, 0 }, { "code_post_gfx_mp", Game::DB_ZONE_CODE, 0 }, @@ -793,7 +808,8 @@ namespace Components }; - static Game::XZoneInfo baseZones[] = { + static Game::XZoneInfo baseZones[] = + { { "defaults", Game::DB_ZONE_CODE, 0 }, { "techsets", Game::DB_ZONE_CODE, 0 }, { "common_mp", Game::DB_ZONE_COMMON, 0 }, @@ -807,50 +823,55 @@ namespace Components ExitProcess(0); } - int APIENTRY ZoneBuilder::EntryPoint(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/) + void ZoneBuilder::CommandThreadCallback() { - Utils::Hook::Call(0x42F0A0)(); // Com_InitCriticalSections - Utils::Hook::Call(0x4301B0)(); // Com_InitMainThread - Utils::Hook::Call(0x406D10)(0); // Win_InitLocalization - Utils::Hook::Call(0x4FF220)(); // Com_InitParse - Utils::Hook::Call(0x4D8220)(); // Dvar_Init - Utils::Hook::Call(0x4D2280)(); // SL_Init - Utils::Hook::Call(0x48F660)(); // Cmd_Init - Utils::Hook::Call(0x4D9210)(); // Cbuf_Init - Utils::Hook::Call(0x47F390)(); // Swap_Init - Utils::Hook::Call(0x60AD10)(); // Com_InitDvars - Utils::Hook::Call(0x420830)(); // Com_InitHunkMemory - Utils::Hook::Call(0x4A62A0)(); // LargeLocalInit - Utils::Hook::Call(0x4DCC10)(); // Sys_InitCmdEvents - Utils::Hook::Call(0x64A020)(); // PMem_Init + Com_InitThreadData(); + + while (!ZoneBuilder::CommandThreadTerminate) + { + ZoneBuilder::AssumeMainThreadRole(); + Utils::Hook::Call(0x4E2C80)(0, 0); // Cbuf_Execute + ZoneBuilder::ResetThreadRole(); + std::this_thread::sleep_for(1ms); + } + } + + BOOL APIENTRY ZoneBuilder::EntryPoint(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/) + { + Utils::Hook::Call(0x42F0A0)(); // Com_InitCriticalSections + Utils::Hook::Call(0x4301B0)(); // Com_InitMainThread + Utils::Hook::Call(0x406D10)(0); // Win_InitLocalization + Utils::Hook::Call(0x4FF220)(); // Com_InitParse + Utils::Hook::Call(0x4D8220)(); // Dvar_Init + Utils::Hook::Call(0x4D2280)(); // SL_Init + Utils::Hook::Call(0x48F660)(); // Cmd_Init + Utils::Hook::Call(0x4D9210)(); // Cbuf_Init + Utils::Hook::Call(0x47F390)(); // Swap_Init + Utils::Hook::Call(0x60AD10)(); // Com_InitDvars + Utils::Hook::Call(0x420830)(); // Com_InitHunkMemory + Utils::Hook::Call(0x4A62A0)(); // LargeLocalInit + Utils::Hook::Call(0x4DCC10)(); // Sys_InitCmdEvents + Utils::Hook::Call(0x64A020)(); // PMem_Init + if (!Flags::HasFlag("stdout")) { Console::ShowAsyncConsole(); Utils::Hook::Call(0x43D140)(); // Com_EventLoop } + + Utils::Hook::Call(0x502580)(static_cast(__rdtsc())); // Netchan_Init - Utils::Hook::Call(0x429080)(); // FS_InitFileSystem - Utils::Hook::Call(0x4BFBE0)(); // Con_InitChannels - Utils::Hook::Call(0x4E0FB0)(); // DB_InitThread - Utils::Hook::Call(0x5196C0)(); // R_RegisterDvars + Utils::Hook::Call(0x429080)(); // FS_InitFileSystem + Utils::Hook::Call(0x4BFBE0)(); // Con_InitChannels + Utils::Hook::Call(0x4E0FB0)(); // DB_InitThread + Utils::Hook::Call(0x5196C0)(); // R_RegisterDvars Game::NET_Init(); - Utils::Hook::Call(0x4F5090)(); // SND_InitDriver - Utils::Hook::Call(0x46A630)(); // SND_Init - //Utils::Hook::Call(0x4D3660)(); // SV_Init - //Utils::Hook::Call(0x4121E0)(); // SV_InitServerThread - //Utils::Hook::Call(0x464A90)(); // Com_ParseCommandLine + Utils::Hook::Call(0x4F5090)(); // SND_InitDriver + Utils::Hook::Call(0x46A630)(); // SND_Init Utils::Hook::Call(0x43D140)(); // Com_EventLoop - ZoneBuilder::CommandThread = std::thread([] - { - while (!ZoneBuilder::Terminate) - { - ZoneBuilder::AssumeMainThreadRole(); - Utils::Hook::Call(0x4E2C80)(0, 0); // Cbuf_Execute - ZoneBuilder::ResetThreadRole(); - std::this_thread::sleep_for(1ms); - } - }); + ZoneBuilder::CommandThread = Utils::Thread::CreateNamedThread("Command Thread", ZoneBuilder::CommandThreadCallback); + ZoneBuilder::CommandThread.detach(); Command::Add("quit", ZoneBuilder::Com_Quitf_t); @@ -861,8 +882,7 @@ namespace Components } else { - Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, - "Missing new init zones (defaults.ff & techsets.ff). You will need to load fastfiles to manually obtain techsets.\n"); + Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Missing new init zones (defaults.ff & techsets.ff). You will need to load fastfiles to manually obtain techsets.\n"); Game::DB_LoadXAssets(baseZones_old, ARRAYSIZE(baseZones_old), 0); } @@ -896,7 +916,7 @@ namespace Components } Logger::Print(" --------------------------------------------------------------------------------\n"); - Logger::Print(" IW4x ZoneBuilder (" VERSION ")\n"); + Logger::Print(" IW4x ZoneBuilder ({})\n", VERSION); Logger::Print(" Commands:\n"); Logger::Print("\t-buildzone [zone]: builds a zone from a csv located in zone_source\n"); Logger::Print("\t-buildall: builds all zones in zone_source\n"); @@ -990,6 +1010,31 @@ namespace Components return ""; } + void ZoneBuilder::ReallocateLoadedSounds(void*& data, [[maybe_unused]] void* a2) + { + assert(data); + auto* sound = Utils::Hook::Get(0x112AE04); + const auto length = sound->info.data_len; + const auto allocatedSpace = Game::Z_Malloc(static_cast(length)); + memcpy_s(allocatedSpace, length, data, length); + + data = allocatedSpace; + sound->data = static_cast(allocatedSpace); + sound->info.data_ptr = allocatedSpace; + } + + Game::Sys_File ZoneBuilder::Sys_CreateFile_Stub(const char* dir, const char* filename) + { + auto file = Game::Sys_CreateFile(dir, filename); + + if (file.handle == INVALID_HANDLE_VALUE) + { + file = Game::Sys_CreateFile("zone\\zonebuilder\\", filename); + } + + return file; + } + ZoneBuilder::ZoneBuilder() { // ReSharper disable CppStaticAssertFailure @@ -997,23 +1042,23 @@ namespace Components AssertSize(Game::XFile, 40); static_assert(Game::MAX_XFILE_COUNT == 8, "XFile block enum is invalid!"); - ZoneBuilder::EndAssetTrace(); - if (ZoneBuilder::IsEnabled()) { // Prevent loading textures (preserves loaddef) //Utils::Hook::Set(Game::Load_Texture, 0xC3); + Utils::Hook(0x5BC832, Sys_CreateFile_Stub, HOOK_CALL).install()->quick(); + // Store the loaddef Utils::Hook(Game::Load_Texture, StoreTexture, HOOK_JUMP).install()->quick(); // Release the loaddef Game::DB_ReleaseXAssetHandlers[Game::XAssetType::ASSET_TYPE_IMAGE] = ZoneBuilder::ReleaseTexture; - //r_loadForrenderer = 0 + // r_loadForrenderer = 0 Utils::Hook::Set(0x519DDF, 0); - //r_delayloadimage retn + // r_delayloadimage ret Utils::Hook::Set(0x51F450, 0xC3); // r_registerDvars hack @@ -1031,8 +1076,8 @@ namespace Components // Don't mark assets //Utils::Hook::Nop(0x5BB632, 5); - // Don't load sounds - //Utils::Hook::Set(0x413430, 0xC3); + // Load sounds + Utils::Hook(0x492EFC, ReallocateLoadedSounds, HOOK_CALL).install()->quick(); // Don't display errors when assets are missing (we might manually build those) Utils::Hook::Nop(0x5BB3F2, 5); @@ -1067,14 +1112,14 @@ namespace Components Utils::Hook::Set(0x64A029, 0x38400000); // 900 MiB Utils::Hook::Set(0x64A057, 0x38400000); - // change fs_game domain func + // change FS_GameDirDomainFunc Utils::Hook::Set(0x643203, [](Game::dvar_t* dvar, Game::DvarValue value) { int result = Utils::Hook::Call(0x642FC0)(dvar, value); if (result) { - if (std::string(value.string) != dvar->current.string) + if (std::strcmp(value.string, dvar->current.string) != 0) { dvar->current.string = value.string; Game::FS_Restart(0, 0); @@ -1093,7 +1138,7 @@ namespace Components // handle Com_error Calls Utils::Hook(Game::Com_Error, ZoneBuilder::HandleError, HOOK_JUMP).install()->quick(); - // thread fuckery hooks + // Sys_IsMainThread hook Utils::Hook(0x4C37D0, ZoneBuilder::IsThreadMainThreadHook, HOOK_JUMP).install()->quick(); // Don't exec startup config in fs_restart @@ -1109,31 +1154,16 @@ namespace Components { if (!ZoneBuilder::TraceZone.empty() && ZoneBuilder::TraceZone == FastFiles::Current()) { - ZoneBuilder::TraceAssets.push_back({ type, name }); - OutputDebugStringA((name + "\n").data()); + ZoneBuilder::TraceAssets.emplace_back(std::make_pair(type, name)); +#ifdef _DEBUG + OutputDebugStringA(Utils::String::Format("%s\n", name)); +#endif } }); Command::Add("verifyzone", [](Command::Params* params) { if (params->size() < 2) return; - /* - Utils::Hook(0x4AE9C2, [] - { - Game::WeaponCompleteDef** varPtr = (Game::WeaponCompleteDef**)0x112A9F4; - Game::WeaponCompleteDef* var = *varPtr; - OutputDebugStringA(""); - Utils::Hook::Call(0x4D1D60)(); // DB_PopStreamPos - }, HOOK_JUMP).install()->quick(); - - Utils::Hook(0x4AE9B4, [] - { - Game::WeaponCompleteDef** varPtr = (Game::WeaponCompleteDef**)0x112A9F4; - Game::WeaponCompleteDef* var = *varPtr; - OutputDebugStringA(""); - Utils::Hook::Call(0x4D1D60)(); // DB_PopStreamPos - }, HOOK_JUMP).install()->quick(); - */ std::string zone = params->get(1); @@ -1180,9 +1210,10 @@ namespace Components Zone(zoneName).build(); }); - Command::Add("buildall", [](Command::Params*) + Command::Add("buildall", []([[maybe_unused]] Command::Params* params) { - auto zoneSources = FileSystem::GetSysFileList(Dvar::Var("fs_basepath").get() + "\\zone_source", "csv", false); + auto path = std::format("{}\\zone_source", Dvar::Var("fs_basepath").get()); + auto zoneSources = FileSystem::GetSysFileList(path, "csv", false); for (auto source : zoneSources) { @@ -1191,7 +1222,7 @@ namespace Components source = source.substr(0, source.find(".csv")); } - Command::Execute(Utils::String::VA("buildzone %s", source.data()), true); + Command::Execute(std::format("buildzone {}", source), true); } }); @@ -1211,6 +1242,41 @@ namespace Components } }); + + AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, [[maybe_unused]] const std::string& name, [[maybe_unused]] bool* restrict) + { + if (type != Game::ASSET_TYPE_SOUND) + { + return; + } + + auto sound = asset.sound; + + for (size_t i = 0; i < sound->count; i++) + { + auto thisSound = sound->head[i]; + + if (thisSound.soundFile->type == Game::SAT_LOADED) + { + if (thisSound.soundFile->u.loadSnd->sound.data == nullptr) + { + // ouch + // This should never happen and will cause a memory leak + // Let's change it to a streamed sound instead + thisSound.soundFile->type = Game::SAT_STREAMED; + + auto virtualPath = std::filesystem::path(thisSound.soundFile->u.loadSnd->name); + + thisSound.soundFile->u.streamSnd.filename.info.raw.name = Utils::Memory::DuplicateString(virtualPath.filename().string()); + + auto dir = virtualPath.remove_filename().string(); + dir = dir.substr(0, dir.size() - 1); // remove / + thisSound.soundFile->u.streamSnd.filename.info.raw.dir = Utils::Memory::DuplicateString(dir); + } + } + } + }); + Command::Add("buildtechsets", [](Command::Params*) { Utils::IO::CreateDir("zone_source/techsets"); @@ -1512,12 +1578,12 @@ namespace Components { if (params->size() < 2) return; - std::string path = Utils::String::VA("%s\\mods\\%s\\images", Dvar::Var("fs_basepath").get(), params->get(1)); - std::vector images = FileSystem::GetSysFileList(path, "iwi", false); + auto path = std::format("{}\\mods\\{}\\images", Dvar::Var("fs_basepath").get(), params->get(1)); + auto images = FileSystem::GetSysFileList(path, "iwi", false); - for(auto i = images.begin(); i != images.end();) + for (auto i = images.begin(); i != images.end();) { - *i = Utils::String::VA("images/%s", i->data()); + *i = std::format("images/{}", *i); if (FileSystem::File(*i).exists()) { @@ -1540,7 +1606,7 @@ namespace Components ZoneBuilder::~ZoneBuilder() { - ZoneBuilder::Terminate = true; + ZoneBuilder::CommandThreadTerminate = true; if (ZoneBuilder::CommandThread.joinable()) { ZoneBuilder::CommandThread.join(); diff --git a/src/Components/Modules/ZoneBuilder.hpp b/src/Components/Modules/ZoneBuilder.hpp index ea18ed1a..f61b140f 100644 --- a/src/Components/Modules/ZoneBuilder.hpp +++ b/src/Components/Modules/ZoneBuilder.hpp @@ -60,7 +60,7 @@ namespace Components int findScriptString(const std::string& str); void addRawAsset(Game::XAssetType type, void* ptr); - void mapScriptString(unsigned short* gameIndex); + void mapScriptString(unsigned short& gameIndex); void renameAsset(Game::XAssetType type, const std::string& asset, const std::string& newName); std::string getAssetName(Game::XAssetType type, const std::string& asset); @@ -74,7 +74,7 @@ namespace Components bool isPrimaryAsset() { return this->assetDepth <= 1; } private: - void loadFastFiles(); + void loadFastFiles() const; bool loadAssets(); bool loadAssetByName(const std::string& type, std::string name, bool isSubAsset = true); @@ -138,8 +138,9 @@ namespace Components static void ReleaseTexture(Game::XAssetHeader header); static std::string FindMaterialByTechnique(const std::string& name); + static void ReallocateLoadedSounds(void*& data, void* a2); - static int __stdcall EntryPoint(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/); + static BOOL APIENTRY EntryPoint(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/); static void HandleError(Game::errorParm_t code, const char* fmt, ...); static void SoftErrorAssetOverflow(); @@ -147,13 +148,16 @@ namespace Components static void ResetThreadRole(); static bool IsThreadMainThreadHook(); + static Game::Sys_File Sys_CreateFile_Stub(const char* dir, const char* filename); static void Com_Quitf_t(); + static void CommandThreadCallback(); + static bool MainThreadInterrupted; static DWORD InterruptingThreadId; - static volatile bool Terminate; + static volatile bool CommandThreadTerminate; static std::thread CommandThread; }; } diff --git a/src/Components/Modules/Zones.cpp b/src/Components/Modules/Zones.cpp index 590718f1..6efd7be6 100644 --- a/src/Components/Modules/Zones.cpp +++ b/src/Components/Modules/Zones.cpp @@ -1456,7 +1456,7 @@ namespace Components { Game::MaterialShaderArgument* arg = &argument[i]; - if (arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_TEXTURE && arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_ATTROUT) + if (arg->type != Game::MaterialShaderArgumentType::MTL_ARG_CODE_VERTEX_CONST && arg->type != Game::MaterialShaderArgumentType::MTL_ARG_CODE_PIXEL_CONST) { continue; } @@ -1730,7 +1730,7 @@ namespace Components { Game::GfxImageLoadDef* texture; char mapType; - char semantic; + Game::TextureSemantic semantic; char category; char flags; int cardMemory; @@ -3515,7 +3515,7 @@ namespace Components Logger::Print("decrypted {} images!\n", images.size()); }); - Command::Add("decryptSounds", [](Command::Params*) + Command::Add("decryptSounds", []([[maybe_unused]] Command::Params* params) { auto sounds = FileSystem::GetSysFileList("iw4x/sound", "iwi"); Logger::Print("decrypting {} sounds...\n", sounds.size()); @@ -3523,19 +3523,19 @@ namespace Components for (auto& sound : sounds) { char* buffer = nullptr; - auto fileLength = Game::FS_ReadFile(Utils::String::VA("sound/%s", sound.data()), &buffer); + auto len = Game::FS_ReadFile(Utils::String::Format("sound/{}", sound), &buffer); - if (fileLength && buffer) + if (len && buffer) { auto path = std::filesystem::path(sound.data()); std::filesystem::create_directories("raw/sound" / path.parent_path()); - if (!std::filesystem::exists(Utils::String::VA("raw/sound/%s", sound.data()))) + if (!std::filesystem::exists(std::format("raw/sound/{}", sound))) { - const auto fp = fopen(Utils::String::VA("raw/sound/%s", sound.data()), "wb"); - if (fp) + FILE* fp; + if (!fopen_s(&fp, Utils::String::Format("raw/sound/{}", sound), "wb") && fp) { - fwrite(buffer, fileLength, 1, fp); + fwrite(buffer, len, 1, fp); fclose(fp); } } diff --git a/src/DllMain.cpp b/src/DllMain.cpp index 7c904bfe..eadda265 100644 --- a/src/DllMain.cpp +++ b/src/DllMain.cpp @@ -49,28 +49,29 @@ BOOL APIENTRY DllMain(HINSTANCE /*hinstDLL*/, DWORD fdwReason, LPVOID /*lpvReser if (fdwReason == DLL_PROCESS_ATTACH) { SetProcessDEPPolicy(PROCESS_DEP_ENABLE); - Steam::Proxy::RunMod(); std::srand(std::uint32_t(std::time(nullptr)) ^ ~(GetTickCount() * GetCurrentProcessId())); #ifndef DISABLE_BINARY_CHECK // Ensure we're working with our desired binary - auto* _module = reinterpret_cast(0x400000); - auto hash1 = Utils::Cryptography::JenkinsOneAtATime::Compute(_module + 0x1000, 0x2D531F); // .text - auto hash2 = Utils::Cryptography::JenkinsOneAtATime::Compute(_module + 0x2D75FC, 0xBDA04); // .rdata - if ((hash1 != 0x54684DBE -#ifdef DEBUG - && hash1 != 0x8AADE716 + +#ifndef DEBUG_BINARY_CHECK + const auto* binary = reinterpret_cast(0x6F9358); + if (binary == nullptr || std::strcmp(binary, BASEGAME_NAME) != 0) #endif - ) || hash2 != 0x8030ec53) { + MessageBoxA(nullptr, + "Failed to load game binary.\n" + "You did not install the iw4x-rawfiles!\n" + "Please use the XLabs launcher to run the game. For support, please visit https://xlabs.dev/support_iw4x_client", + "ERROR", + MB_ICONERROR + ); return FALSE; } - - DWORD oldProtect; - VirtualProtect(_module + 0x1000, 0x2D6000, PAGE_EXECUTE_READ, &oldProtect); // Protect the .text segment #endif + Steam::Proxy::RunMod(); // Install entry point hook Utils::Hook(0x6BAC0F, Main::EntryPoint, HOOK_JUMP).install()->quick(); } diff --git a/src/Game/Client.cpp b/src/Game/Client.cpp index 4798a21c..2a690e29 100644 --- a/src/Game/Client.cpp +++ b/src/Game/Client.cpp @@ -8,6 +8,10 @@ namespace Game CL_DownloadsComplete_t CL_DownloadsComplete = CL_DownloadsComplete_t(0x42CE90); CL_DrawStretchPicPhysical_t CL_DrawStretchPicPhysical = CL_DrawStretchPicPhysical_t(0x4FC120); CL_GetConfigString_t CL_GetConfigString = CL_GetConfigString_t(0x44ADB0); + CL_AddReliableCommand_t CL_AddReliableCommand = CL_AddReliableCommand_t(0x454F40); + CL_ParseGamestate_t CL_ParseGamestate = CL_ParseGamestate_t(0x5AC250); + CL_ParseSnapshot_t CL_ParseSnapshot = CL_ParseSnapshot_t(0x5ABD40); + CL_ParseServerMessage_t CL_ParseServerMessage = CL_ParseServerMessage_t(0x4A9E90); CL_GetMaxRank_t CL_GetMaxRank = CL_GetMaxRank_t(0x44BA30); CL_GetRankForXP_t CL_GetRankForXP = CL_GetRankForXP_t(0x4FF8A0); CL_GetRankIcon_t CL_GetRankIcon = CL_GetRankIcon_t(0x4A7B30); @@ -19,6 +23,9 @@ namespace Game CL_GetLocalClientActiveCount_t CL_GetLocalClientActiveCount = CL_GetLocalClientActiveCount_t(0x5BAD90); CL_ControllerIndexFromClientNum_t CL_ControllerIndexFromClientNum = CL_ControllerIndexFromClientNum_t(0x449E30); CL_MouseEvent_t CL_MouseEvent = CL_MouseEvent_t(0x4D7C50); + CL_WriteDemoClientArchive_t CL_WriteDemoClientArchive = CL_WriteDemoClientArchive_t(0x5A8020); + CL_WriteDemoMessage_t CL_WriteDemoMessage = CL_WriteDemoMessage_t(0x4707C0); + CL_AddDebugStarWithText_t CL_AddDebugStarWithText = CL_AddDebugStarWithText_t(0x4D03C0); float* cl_angles = reinterpret_cast(0xB2F8D0); @@ -28,13 +35,15 @@ namespace Game clientUIActive_t* clientUIActives = reinterpret_cast(0xB2BB88); + clientActive_t* clients = reinterpret_cast(0xB2C698); + voiceCommunication_t* cl_voiceCommunication = reinterpret_cast(0x1079DA0); 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)); + return std::atoi(StringTable_Lookup(rankTable, 0, maxrank, 7)); } clientConnection_t* CL_GetLocalClientConnection(const int localClientNum) @@ -58,4 +67,24 @@ namespace Game return cl_voiceCommunication; } + + clientUIActive_t* CL_GetLocalClientUIGlobals(const int localClientNum) + { + AssertIn(localClientNum, MAX_LOCAL_CLIENTS); + return &clientUIActives[localClientNum]; + } + + clientActive_t* CL_GetLocalClientGlobals(const int localClientNum) + { + AssertIn(localClientNum, MAX_LOCAL_CLIENTS); + assert(clients[localClientNum].alwaysFalse == false); + return &clients[localClientNum]; + } + + void CL_AddDebugStar(const float* point, const float* color, int duration, int fromServer) + { + static const float MY_NULLTEXTCOLOR[] = {0.0f, 0.0f, 0.0f, 0.0f}; + + CL_AddDebugStarWithText(point, color, MY_NULLTEXTCOLOR, nullptr, 1.0f, duration, fromServer); + } } diff --git a/src/Game/Client.hpp b/src/Game/Client.hpp index 834d73fa..13c96884 100644 --- a/src/Game/Client.hpp +++ b/src/Game/Client.hpp @@ -20,6 +20,18 @@ namespace Game typedef const char* (*CL_GetConfigString_t)(int index); extern CL_GetConfigString_t CL_GetConfigString; + typedef void(*CL_AddReliableCommand_t)(int localClientNum, const char* cmd); + extern CL_AddReliableCommand_t CL_AddReliableCommand; + + typedef void(*CL_ParseGamestate_t)(int localClientNum, msg_t* msg); + extern CL_ParseGamestate_t CL_ParseGamestate; + + typedef void(*CL_ParseSnapshot_t)(int localClientNum, msg_t* msg); + extern CL_ParseSnapshot_t CL_ParseSnapshot; + + typedef void(*CL_ParseServerMessage_t)(int localClientNum, msg_t* msg); + extern CL_ParseServerMessage_t CL_ParseServerMessage; + typedef int(*CL_GetMaxRank_t)(); extern CL_GetMaxRank_t CL_GetMaxRank; @@ -53,6 +65,15 @@ namespace Game typedef int(*CL_MouseEvent_t)(int x, int y, int dx, int dy); extern CL_MouseEvent_t CL_MouseEvent; + typedef void(*CL_WriteDemoClientArchive_t)(void (*write)(const void*, int, int), const playerState_s* ps, const float* viewangles, const float* selectedLocation, float selectedLocationAngle, int localClientNum, int index); + extern CL_WriteDemoClientArchive_t CL_WriteDemoClientArchive; + + typedef void(*CL_WriteDemoMessage_t)(void (*write)(const void*, int, int), int serverMessageSequence, unsigned char* data, int len, int localClientNum); + extern CL_WriteDemoMessage_t CL_WriteDemoMessage; + + typedef void(*CL_AddDebugStarWithText_t)(const float* point, const float* starColor, const float* textColor, const char* string, float fontsize, int duration, int fromServer); + extern CL_AddDebugStarWithText_t CL_AddDebugStarWithText; + extern float* cl_angles; extern clientConnection_t* clientConnections; @@ -61,10 +82,16 @@ namespace Game extern clientUIActive_t* clientUIActives; + extern clientActive_t* clients; + extern voiceCommunication_t* cl_voiceCommunication; extern int CL_GetMaxXP(); extern clientConnection_t* CL_GetLocalClientConnection(int localClientNum); extern connstate_t CL_GetLocalClientConnectionState(int localClientNum); extern voiceCommunication_t* CL_GetLocalClientVoiceCommunication(int localClientNum); + extern clientUIActive_t* CL_GetLocalClientUIGlobals(int localClientNum); + extern clientActive_t* CL_GetLocalClientGlobals(int localClientNum); + + extern void CL_AddDebugStar(const float* point, const float* color, int duration, int fromServer); } diff --git a/src/Game/Common.cpp b/src/Game/Common.cpp index b1c50c67..123fb859 100644 --- a/src/Game/Common.cpp +++ b/src/Game/Common.cpp @@ -23,6 +23,7 @@ namespace Game Com_Quitf_t Com_Quit_f = Com_Quitf_t(0x4D4000); Com_OpenLogFile_t Com_OpenLogFile = Com_OpenLogFile_t(0x60A8D0); Com_UpdateSlowMotion_t Com_UpdateSlowMotion = Com_UpdateSlowMotion_t(0x60B2D0); + Com_Compress_t Com_Compress = Com_Compress_t(0x498220); int* com_frameTime = reinterpret_cast(0x1AD8F3C); @@ -30,6 +31,8 @@ namespace Game int* com_errorPrintsCount = reinterpret_cast(0x1AD7910); + int* errorcode = reinterpret_cast(0x1AD7EB4); + char* Com_GetParseThreadInfo() { if (Sys_IsMainThread()) diff --git a/src/Game/Common.hpp b/src/Game/Common.hpp index 344bdfc6..8698d685 100644 --- a/src/Game/Common.hpp +++ b/src/Game/Common.hpp @@ -35,7 +35,7 @@ namespace Game typedef void(*Com_BeginParseSession_t)(const char* filename); extern Com_BeginParseSession_t Com_BeginParseSession; - typedef char* (*Com_ParseOnLine_t)(const char** data_p); + typedef char*(*Com_ParseOnLine_t)(const char** data_p); extern Com_ParseOnLine_t Com_ParseOnLine; typedef void(*Com_SkipRestOfLine_t)(const char** data); @@ -62,14 +62,33 @@ namespace Game typedef void(*Com_UpdateSlowMotion_t)(int msec); extern Com_UpdateSlowMotion_t Com_UpdateSlowMotion; + typedef int(*Com_Compress_t)(char* data_p); + extern Com_Compress_t Com_Compress; + extern int* com_frameTime; extern int* com_fixedConsolePosition; extern int* com_errorPrintsCount; + extern int* errorcode; + extern char* Com_GetParseThreadInfo(); extern void Com_SetParseNegativeNumbers(int parse); extern const char* Com_LoadInfoString_FastFile(const char* fileName, const char* fileDesc, const char* ident, char* loadBuffer); } + +#define Com_InitThreadData() \ +{ \ + static Game::ProfileStack profile_stack{}; \ + static Game::va_info_t va_info{}; \ + static jmp_buf g_com_error{}; \ + static Game::TraceThreadInfo g_trace_thread_info{}; \ + static void* g_thread_values[Game::THREAD_VALUE_COUNT]{}; \ + *(Game::Sys::GetTls(Game::Sys::TLS_OFFSET::THREAD_VALUES)) = g_thread_values; \ + Game::Sys_SetValue(Game::THREAD_VALUE_PROF_STACK, &profile_stack); \ + Game::Sys_SetValue(Game::THREAD_VALUE_VA, &va_info); \ + Game::Sys_SetValue(Game::THREAD_VALUE_COM_ERROR, &g_com_error); \ + Game::Sys_SetValue(Game::THREAD_VALUE_TRACE, &g_trace_thread_info); \ +} diff --git a/src/Game/Dvars.cpp b/src/Game/Dvars.cpp index 3e3fde5b..be172a8a 100644 --- a/src/Game/Dvars.cpp +++ b/src/Game/Dvars.cpp @@ -22,6 +22,15 @@ namespace Game Dvar_DisplayableValue_t Dvar_DisplayableValue = Dvar_DisplayableValue_t(0x4B5530); Dvar_Reset_t Dvar_Reset = Dvar_Reset_t(0x4FEFD0); + Dvar_SetFromStringByName_t Dvar_SetFromStringByName = Dvar_SetFromStringByName_t(0x4F52E0); + Dvar_SetFromStringByNameFromSource_t Dvar_SetFromStringByNameFromSource = Dvar_SetFromStringByNameFromSource_t(0x4FC770); + Dvar_SetStringByName_t Dvar_SetStringByName = Dvar_SetStringByName_t(0x44F060); + Dvar_SetString_t Dvar_SetString = Dvar_SetString_t(0x4A9580); + Dvar_SetBool_t Dvar_SetBool = Dvar_SetBool_t(0x4A9510); + Dvar_SetBoolByName_t Dvar_SetBoolByName = Dvar_SetBoolByName_t(0x45C4D0); + Dvar_SetFloat_t Dvar_SetFloat = Dvar_SetFloat_t(0x40BB20); + Dvar_SetInt_t Dvar_SetInt = Dvar_SetInt_t(0x421DA0); + const dvar_t** com_developer = reinterpret_cast(0x1AD78E8); const dvar_t** com_developer_script = reinterpret_cast(0x1AD8F10); const dvar_t** com_timescale = reinterpret_cast(0x1AD7920); @@ -45,12 +54,14 @@ namespace Game const dvar_t** cl_showSend = reinterpret_cast(0xA1E870); const dvar_t** cl_voice = reinterpret_cast(0xB2BB44); const dvar_t** cl_ingame = reinterpret_cast(0xB2BB80); + const dvar_t** cl_shownet = reinterpret_cast(0x1059008); const dvar_t** g_cheats = reinterpret_cast(0x1A45D54); const dvar_t** g_deadChat = reinterpret_cast(0x19BD5DC); const dvar_t** g_allowVote = reinterpret_cast(0x19BD644); const dvar_t** g_oldVoting = reinterpret_cast(0x1A45DEC); const dvar_t** g_gametype = reinterpret_cast(0x1A45DC8); + const dvar_t** g_password = reinterpret_cast(0x18835C0); const dvar_t** version = reinterpret_cast(0x1AD7930); @@ -59,7 +70,9 @@ namespace Game const dvar_t** ui_currentMap = reinterpret_cast(0x62E2834); const dvar_t** ui_gametype = reinterpret_cast(0x62E2828); const dvar_t** ui_mapname = reinterpret_cast(0x62E279C); + const dvar_t** ui_joinGametype = reinterpret_cast(0x62E2840); const dvar_t** ui_netGameType = reinterpret_cast(0x62E2838); + const dvar_t** ui_netSource = reinterpret_cast(0x62E27E8); const dvar_t** loc_warnings = reinterpret_cast(0x62C8700); const dvar_t** loc_warningsAsErrors = reinterpret_cast(0x62C86FC); diff --git a/src/Game/Dvars.hpp b/src/Game/Dvars.hpp index 61697af7..f54eaba7 100644 --- a/src/Game/Dvars.hpp +++ b/src/Game/Dvars.hpp @@ -48,6 +48,9 @@ namespace Game typedef void(*Dvar_SetBool_t)(const dvar_t* dvar, bool enabled); extern Dvar_SetBool_t Dvar_SetBool; + typedef void(*Dvar_SetBoolByName_t)(const char* dvarName, bool value); + extern Dvar_SetBoolByName_t Dvar_SetBoolByName; + typedef void(*Dvar_SetFloat_t)(const dvar_t* dvar, float value); extern Dvar_SetFloat_t Dvar_SetFloat; @@ -101,12 +104,14 @@ namespace Game extern const dvar_t** cl_showSend; extern const dvar_t** cl_voice; extern const dvar_t** cl_ingame; + extern const dvar_t** cl_shownet; extern const dvar_t** g_cheats; extern const dvar_t** g_deadChat; extern const dvar_t** g_allowVote; extern const dvar_t** g_oldVoting; extern const dvar_t** g_gametype; + extern const dvar_t** g_password; extern const dvar_t** version; @@ -115,7 +120,9 @@ namespace Game extern const dvar_t** ui_currentMap; extern const dvar_t** ui_gametype; extern const dvar_t** ui_mapname; + extern const dvar_t** ui_joinGametype; extern const dvar_t** ui_netGameType; + extern const dvar_t** ui_netSource; extern const dvar_t** loc_warnings; extern const dvar_t** loc_warningsAsErrors; diff --git a/src/Game/Engine/LargeLocal.cpp b/src/Game/Engine/LargeLocal.cpp index 638584c5..24445074 100644 --- a/src/Game/Engine/LargeLocal.cpp +++ b/src/Game/Engine/LargeLocal.cpp @@ -3,8 +3,6 @@ namespace Game::Engine { - #define CanUseServerLargeLocal() (SV_GetServerThreadOwnsGame() ? Sys_IsServerThread() : Sys_IsRenderThread()) - LargeLocal::LargeLocal(int sizeParam) { assert(sizeParam); @@ -89,4 +87,9 @@ namespace Game::Engine return &g_largeLocalBuf[startIndex]; } + + int CanUseServerLargeLocal() + { + return SV_GetServerThreadOwnsGame() ? Sys_IsServerThread() : Sys_IsRenderThread(); + } } diff --git a/src/Game/Engine/LargeLocal.hpp b/src/Game/Engine/LargeLocal.hpp index 12fbfe9d..11e8af23 100644 --- a/src/Game/Engine/LargeLocal.hpp +++ b/src/Game/Engine/LargeLocal.hpp @@ -2,28 +2,30 @@ namespace Game::Engine { - class LargeLocal - { - public: - explicit LargeLocal(int sizeParam); - ~LargeLocal(); + class LargeLocal + { + public: + explicit LargeLocal(int sizeParam); + ~LargeLocal(); - LargeLocal(LargeLocal&&) = delete; - LargeLocal(const LargeLocal&) = delete; - LargeLocal& operator=(LargeLocal&&) = delete; - LargeLocal& operator=(const LargeLocal&) = delete; + LargeLocal(LargeLocal&&) = delete; + LargeLocal(const LargeLocal&) = delete; + LargeLocal& operator=(LargeLocal&&) = delete; + LargeLocal& operator=(const LargeLocal&) = delete; - [[nodiscard]] void* GetBuf() const; + [[nodiscard]] void* GetBuf() const; - private: - void PopBuf(); + private: + void PopBuf(); - int startPos; - int size; - }; + int startPos; + int size; + }; - extern void LargeLocalEnd(int startPos); - extern void LargeLocalEndRight(int startPos); + extern void LargeLocalEnd(int startPos); + extern void LargeLocalEndRight(int startPos); - extern void* LargeLocalGetBuf(int startPos, int size); + extern void* LargeLocalGetBuf(int startPos, int size); + + extern int CanUseServerLargeLocal(); } diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 8a6e33c4..ed6f44c8 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -8,6 +8,7 @@ namespace Game Cbuf_AddServerText_f_t Cbuf_AddServerText_f = Cbuf_AddServerText_f_t(0x4BB9B0); Cbuf_AddText_t Cbuf_AddText = Cbuf_AddText_t(0x404B20); Cbuf_InsertText_t Cbuf_InsertText = Cbuf_InsertText_t(0x4940B0); + Cbuf_Execute_t Cbuf_Execute = Cbuf_Execute_t(0x4E2C80); CG_DrawDisconnect_t CG_DrawDisconnect = CG_DrawDisconnect_t(0x454A70); CG_NextWeapon_f_t CG_NextWeapon_f = CG_NextWeapon_f_t(0x449DE0); @@ -52,7 +53,7 @@ namespace Game Key_SetCatcher_t Key_SetCatcher = Key_SetCatcher_t(0x43BD00); Key_RemoveCatcher_t Key_RemoveCatcher = Key_RemoveCatcher_t(0x408260); - Key_IsKeyCatcherActive_t Key_IsKeyCatcherActive = Key_IsKeyCatcherActive_t(0x4DA010); + Key_IsCatcherActive_t Key_IsCatcherActive = Key_IsCatcherActive_t(0x4DA010); Key_SetBinding_t Key_SetBinding = Key_SetBinding_t(0x494C90); LargeLocalInit_t LargeLocalInit = LargeLocalInit_t(0x4A62A0); @@ -83,6 +84,7 @@ namespace Game Menus_MenuIsInStack_t Menus_MenuIsInStack = Menus_MenuIsInStack_t(0x47ACB0); Menu_HandleKey_t Menu_HandleKey = Menu_HandleKey_t(0x4C4A00); Menu_GetFocused_t Menu_GetFocused = Menu_GetFocused_t(0x4AFF10); + Menu_Setup_t Menu_Setup = Menu_Setup_t(0x415AD0); MSG_Init_t MSG_Init = MSG_Init_t(0x45FCA0); MSG_ReadBit_t MSG_ReadBit = MSG_ReadBit_t(0x476D20); @@ -98,10 +100,11 @@ namespace Game MSG_WriteLong_t MSG_WriteLong = MSG_WriteLong_t(0x41CA20); MSG_WriteShort_t MSG_WriteShort = MSG_WriteShort_t(0x503B90); MSG_WriteString_t MSG_WriteString = MSG_WriteString_t(0x463820); - MSG_ReadDeltaUsercmdKey_t MSG_ReadDeltaUsercmdKey = MSG_ReadDeltaUsercmdKey_t(0x491F00); - MSG_WriteBitsCompress_t MSG_WriteBitsCompress = MSG_WriteBitsCompress_t(0x4319D0); + MSG_Discard_t MSG_Discard = MSG_Discard_t(0x4F56D0); MSG_ReadByte_t MSG_ReadByte = MSG_ReadByte_t(0x4C1C20); + MSG_ReadDeltaUsercmdKey_t MSG_ReadDeltaUsercmdKey = MSG_ReadDeltaUsercmdKey_t(0x491F00); MSG_ReadBitsCompress_t MSG_ReadBitsCompress = MSG_ReadBitsCompress_t(0x4DCC30); + MSG_WriteBitsCompress_t MSG_WriteBitsCompress = MSG_WriteBitsCompress_t(0x4319D0); NetadrToSockadr_t NetadrToSockadr = NetadrToSockadr_t(0x4B4B40); @@ -150,9 +153,10 @@ namespace Game R_FlushSun_t R_FlushSun = R_FlushSun_t(0x53FB50); R_SortWorldSurfaces_t R_SortWorldSurfaces = R_SortWorldSurfaces_t(0x53DC10); - Script_Alloc_t Script_Alloc = Script_Alloc_t(0x422E70); - Script_SetupTokens_t Script_SetupTokens = Script_SetupTokens_t(0x4E6950); - Script_CleanString_t Script_CleanString = Script_CleanString_t(0x498220); + GetMemory_t GetMemory = GetMemory_t(0x4E67B0); + GetClearedMemory_t GetClearedMemory = GetClearedMemory_t(0x422E70); + PS_CreatePunctuationTable_t PS_CreatePunctuationTable = PS_CreatePunctuationTable_t(0x4E6950); + free_expression_t free_expression = free_expression_t(0x4ACC70); SE_Load_t SE_Load = SE_Load_t(0x502A30); @@ -160,19 +164,6 @@ namespace Game SEH_ReadCharFromString_t SEH_ReadCharFromString = SEH_ReadCharFromString_t(0x486560); SEH_GetCurrentLanguage_t SEH_GetCurrentLanguage = SEH_GetCurrentLanguage_t(0x4F6110); - Dvar_SetFromStringByName_t Dvar_SetFromStringByName = Dvar_SetFromStringByName_t(0x4F52E0); - Dvar_SetFromStringByNameFromSource_t Dvar_SetFromStringByNameFromSource = Dvar_SetFromStringByNameFromSource_t(0x4FC770); - Dvar_SetStringByName_t Dvar_SetStringByName = Dvar_SetStringByName_t(0x44F060); - Dvar_SetString_t Dvar_SetString = Dvar_SetString_t(0x4A9580); - Dvar_SetBool_t Dvar_SetBool = Dvar_SetBool_t(0x4A9510); - Dvar_SetFloat_t Dvar_SetFloat = Dvar_SetFloat_t(0x40BB20); - Dvar_SetInt_t Dvar_SetInt = Dvar_SetInt_t(0x421DA0); - - SL_ConvertToString_t SL_ConvertToString = SL_ConvertToString_t(0x4EC1D0); - SL_GetString_t SL_GetString = SL_GetString_t(0x4CDC10); - SL_AddRefToString_t SL_AddRefToString = SL_AddRefToString_t(0x4D9B00); - SL_RemoveRefToString_t SL_RemoveRefToString = SL_RemoveRefToString_t(0x47CD70); - SND_Init_t SND_Init = SND_Init_t(0x46A630); SND_InitDriver_t SND_InitDriver = SND_InitDriver_t(0x4F5090); @@ -207,8 +198,9 @@ namespace Game Win_GetLanguage_t Win_GetLanguage = Win_GetLanguage_t(0x45CBA0); Vec3UnpackUnitVec_t Vec3UnpackUnitVec = Vec3UnpackUnitVec_t(0x45CA90); - vectoyaw_t vectoyaw = vectoyaw_t(0x45AD10); + vectoryaw_t vectoryaw = vectoryaw_t(0x45AD10); AngleNormalize360_t AngleNormalize360 = AngleNormalize360_t(0x438DC0); + _VectorMA_t _VectorMA = _VectorMA_t(0x5084D0); unzClose_t unzClose = unzClose_t(0x41BF20); @@ -227,12 +219,14 @@ namespace Game AimAssist_ApplyAutoMelee_t AimAssist_ApplyAutoMelee = AimAssist_ApplyAutoMelee_t(0x56A360); Weapon_RocketLauncher_Fire_t Weapon_RocketLauncher_Fire = Weapon_RocketLauncher_Fire_t(0x424680); + Bullet_Fire_t Bullet_Fire = Bullet_Fire_t(0x4402C0); Jump_ClearState_t Jump_ClearState = Jump_ClearState_t(0x04B3890); PM_playerTrace_t PM_playerTrace = PM_playerTrace_t(0x458980); PM_Trace_t PM_Trace = PM_Trace_t(0x441F60); PM_GetEffectiveStance_t PM_GetEffectiveStance = PM_GetEffectiveStance_t(0x412540); PM_UpdateLean_t PM_UpdateLean = PM_UpdateLean_t(0x43DED0); + PM_IsSprinting_t PM_IsSprinting = PM_IsSprinting_t(0x4B3830); IN_RecenterMouse_t IN_RecenterMouse = IN_RecenterMouse_t(0x463D80); @@ -254,6 +248,7 @@ namespace Game Vec2NormalizeFast_t Vec2NormalizeFast = Vec2NormalizeFast_t(0x5FC830); Z_VirtualAlloc_t Z_VirtualAlloc = Z_VirtualAlloc_t(0x4CFBA0); + Z_Malloc_t Z_Malloc = Z_Malloc_t(0x4F3680); I_strncpyz_t I_strncpyz = I_strncpyz_t(0x4D6F80); I_CleanStr_t I_CleanStr = I_CleanStr_t(0x4AD470); @@ -265,14 +260,16 @@ namespace Game LargeLocalBegin_t LargeLocalBegin = LargeLocalBegin_t(0x4127A0); LargeLocalBeginRight_t LargeLocalBeginRight = LargeLocalBeginRight_t(0x644140); + LargeLocalReset_t LargeLocalReset = LargeLocalReset_t(0x430630); + + longjmp_internal_t longjmp_internal = longjmp_internal_t(0x6B8898); CmdArgs* cmd_args = reinterpret_cast(0x1AAC5D0); CmdArgs* sv_cmd_args = reinterpret_cast(0x1ACF8A0); cmd_function_t** cmd_functions = reinterpret_cast(0x1AAC658); - source_t **sourceFiles = reinterpret_cast(0x7C4A98); - keywordHash_t **menuParseKeywordHash = reinterpret_cast(0x63AE928); + source_t** sourceFiles = reinterpret_cast(0x7C4A98); float* cgameFOVSensitivityScale = reinterpret_cast(0xB2F884); @@ -324,6 +321,7 @@ namespace Game PhysPreset*** varPhysPresetPtr = reinterpret_cast(0x112B378); MaterialPass** varMaterialPass = reinterpret_cast(0x112A960); snd_alias_list_t*** varsnd_alias_list_name = reinterpret_cast(0x112AF38); + MaterialVertexShader** varMaterialVertexShader = reinterpret_cast(0x112B338); FxElemField* s_elemFields = reinterpret_cast(0x73B848); @@ -346,8 +344,6 @@ namespace Game ScreenPlacement* scrPlaceFull = reinterpret_cast(0x10843F0); ScreenPlacement* scrPlaceFullUnsafe = reinterpret_cast(0x1084460); ScreenPlacement* scrPlaceView = reinterpret_cast(0x1084378); - - clientActive_t* clients = reinterpret_cast(0xB2C698); cg_s* cgArray = reinterpret_cast(0x7F0F78); cgs_t* cgsArray = reinterpret_cast(0x7ED3B8); @@ -382,9 +378,9 @@ namespace Game Material** whiteMaterial = reinterpret_cast(0x8EE4B8); - unsigned long* _tls_index = reinterpret_cast(0x66D94A8); + unsigned long* g_dwTlsIndex = reinterpret_cast(0x66D94A8); - int* cls_uiStarted = reinterpret_cast(0xA7FFA0); + bgs_t* level_bgs = reinterpret_cast(0x19BD680); unsigned int* playerCardUIStringIndex = reinterpret_cast(0x62CD7A8); char (*playerCardUIStringBuf)[PLAYER_CARD_UI_STRING_COUNT][38] = reinterpret_cast(0x62CB4F8); @@ -403,6 +399,10 @@ namespace Game int* ui_numArenas = reinterpret_cast(0x62D2788); int* ui_arenaBufPos = reinterpret_cast(0x62D278C); + punctuation_s* default_punctuations = reinterpret_cast(0x797F80); + + bool* s_havePlaylists = reinterpret_cast(0x1AD3680); + const char* TableLookup(StringTable* stringtable, int row, int column) { if (!stringtable || !stringtable->values || row >= stringtable->rowCount || column >= stringtable->columnCount) return ""; @@ -930,19 +930,6 @@ namespace Game } } - __declspec(naked) void Menu_FreeItemMemory(itemDef_s* /*item*/) - { - __asm - { - pushad - mov edi, [esp + 24h] - mov eax, 63D880h - call eax - popad - retn - } - } - void Menu_SetNextCursorItem(UiContext* a1, menuDef_t* a2, int unk) { __asm diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 6c52b815..45ed25bf 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -15,6 +15,9 @@ namespace Game typedef void(*Cbuf_InsertText_t)(int localClientNum, const char* text); extern Cbuf_InsertText_t Cbuf_InsertText; + typedef void(*Cbuf_Execute_t)(int localClientNum, int controllerIndex); + extern Cbuf_Execute_t Cbuf_Execute; + typedef void(*CG_DrawDisconnect_t)(int localClientNum); extern CG_DrawDisconnect_t CG_DrawDisconnect; @@ -48,7 +51,7 @@ namespace Game typedef void(*CG_SetupWeaponDef_t)(int localClientNum, unsigned int weapIndex); extern CG_SetupWeaponDef_t CG_SetupWeaponDef; - typedef void(*Cmd_AddCommand_t)(const char* cmdName, void(*function), cmd_function_t* allocedCmd, bool isKey); + typedef void(*Cmd_AddCommand_t)(const char* cmdName, void(*function), cmd_function_t* allocedCmd, int isKey); extern Cmd_AddCommand_t Cmd_AddCommand; typedef void(*Cmd_AddServerCommand_t)(const char* name, void(*callback), cmd_function_t* data); @@ -104,10 +107,10 @@ namespace Game typedef void(*Key_RemoveCatcher_t)(int localClientNum, int andMask); extern Key_RemoveCatcher_t Key_RemoveCatcher; - typedef bool(*Key_IsKeyCatcherActive_t)(int localClientNum, int catcher); - extern Key_IsKeyCatcherActive_t Key_IsKeyCatcherActive; + typedef bool(*Key_IsCatcherActive_t)(int localClientNum, int mask); + extern Key_IsCatcherActive_t Key_IsCatcherActive; - typedef void(*Key_SetBinding_t)(int localClientNum, int keyNum, const char* binding); + typedef void(*Key_SetBinding_t)(int localClientNum, int keynum, const char* binding); extern Key_SetBinding_t Key_SetBinding; typedef void(*LargeLocalInit_t)(); @@ -173,7 +176,7 @@ namespace Game typedef int(*Menus_OpenByName_t)(UiContext* dc, const char* p); extern Menus_OpenByName_t Menus_OpenByName; - typedef menuDef_t *(*Menus_FindByName_t)(UiContext* dc, const char* name); + typedef menuDef_t*(*Menus_FindByName_t)(UiContext* dc, const char* name); extern Menus_FindByName_t Menus_FindByName; typedef bool(*Menu_IsVisible_t)(UiContext* dc, menuDef_t* menu); @@ -182,11 +185,14 @@ namespace Game typedef bool(*Menus_MenuIsInStack_t)(UiContext* dc, menuDef_t* menu); extern Menus_MenuIsInStack_t Menus_MenuIsInStack; + typedef void(*Menu_HandleKey_t)(UiContext* ctx, menuDef_t* menu, Game::keyNum_t key, int down); + extern Menu_HandleKey_t Menu_HandleKey; + typedef menuDef_t*(*Menu_GetFocused_t)(UiContext* ctx); extern Menu_GetFocused_t Menu_GetFocused; - typedef void(*Menu_HandleKey_t)(UiContext* ctx, menuDef_t* menu, Game::keyNum_t key, int down); - extern Menu_HandleKey_t Menu_HandleKey; + typedef void(*Menu_Setup_t)(UiContext* dc); + extern Menu_Setup_t Menu_Setup; typedef bool(*UI_KeyEvent_t)(int clientNum, int key, int down); extern UI_KeyEvent_t UI_KeyEvent; @@ -221,18 +227,12 @@ namespace Game typedef __int64(*MSG_ReadInt64_t)(msg_t* msg); extern MSG_ReadInt64_t MSG_ReadInt64; - typedef char* (*MSG_ReadString_t)(msg_t* msg); + typedef char*(*MSG_ReadString_t)(msg_t* msg, char* string, unsigned int maxChars); extern MSG_ReadString_t MSG_ReadString; - typedef char* (*MSG_ReadStringLine_t)(msg_t *msg, char *string, unsigned int maxChars); + typedef char*(*MSG_ReadStringLine_t)(msg_t *msg, char *string, unsigned int maxChars); extern MSG_ReadStringLine_t MSG_ReadStringLine; - typedef int(*MSG_ReadByte_t)(msg_t* msg); - extern MSG_ReadByte_t MSG_ReadByte; - - typedef int(*MSG_ReadBitsCompress_t)(const char *from, char *to, int size); - extern MSG_ReadBitsCompress_t MSG_ReadBitsCompress; - typedef void(*MSG_WriteByte_t)(msg_t* msg, int c); extern MSG_WriteByte_t MSG_WriteByte; @@ -248,10 +248,19 @@ namespace Game typedef void(*MSG_WriteString_t)(msg_t* msg, const char *str); extern MSG_WriteString_t MSG_WriteString; + typedef void(*MSG_Discard_t)(msg_t* msg); + extern MSG_Discard_t MSG_Discard; + + typedef int(*MSG_ReadByte_t)(msg_t* msg); + extern MSG_ReadByte_t MSG_ReadByte; + typedef bool(*MSG_ReadDeltaUsercmdKey_t)(msg_t* msg, int key, const usercmd_s* from, usercmd_s* to); extern MSG_ReadDeltaUsercmdKey_t MSG_ReadDeltaUsercmdKey; - typedef int(*MSG_WriteBitsCompress_t)(bool trainHuffman, const char *from, char *to, int size); + typedef int(*MSG_ReadBitsCompress_t)(const unsigned char* from, unsigned char* to, int size); + extern MSG_ReadBitsCompress_t MSG_ReadBitsCompress; + + typedef int(*MSG_WriteBitsCompress_t)(bool trainHuffman, const unsigned char* from, unsigned char* to, int size); extern MSG_WriteBitsCompress_t MSG_WriteBitsCompress; typedef void(*NetadrToSockadr_t)(netadr_t *a, sockaddr *s); @@ -368,14 +377,17 @@ namespace Game typedef GfxWorld*(*R_SortWorldSurfaces_t)(); extern R_SortWorldSurfaces_t R_SortWorldSurfaces; - typedef script_t*(*Script_Alloc_t)(int length); - extern Script_Alloc_t Script_Alloc; + typedef void* (*GetMemory_t)(unsigned int size); + extern GetMemory_t GetMemory; - typedef void(*Script_SetupTokens_t)(script_t* script, void* tokens); - extern Script_SetupTokens_t Script_SetupTokens; + typedef void*(*GetClearedMemory_t)(unsigned int size); + extern GetClearedMemory_t GetClearedMemory; - typedef int(*Script_CleanString_t)(char* buffer); - extern Script_CleanString_t Script_CleanString; + typedef void(*PS_CreatePunctuationTable_t)(script_s* script, punctuation_s* punctuations); + extern PS_CreatePunctuationTable_t PS_CreatePunctuationTable; + + typedef void(*free_expression_t)(Statement_s* statement); + extern free_expression_t free_expression; typedef char*(*SE_Load_t)(const char* psFileName, bool forceEnglish); extern SE_Load_t SE_Load; @@ -389,18 +401,6 @@ namespace Game typedef int(*SEH_GetCurrentLanguage_t)(); extern SEH_GetCurrentLanguage_t SEH_GetCurrentLanguage; - typedef const char*(*SL_ConvertToString_t)(scr_string_t stringValue); - extern SL_ConvertToString_t SL_ConvertToString; - - typedef short(*SL_GetString_t)(const char *str, unsigned int user); - extern SL_GetString_t SL_GetString; - - typedef void(*SL_AddRefToString_t)(unsigned int stringValue); - extern SL_AddRefToString_t SL_AddRefToString; - - typedef void(*SL_RemoveRefToString_t)(unsigned int stringValue); - extern SL_RemoveRefToString_t SL_RemoveRefToString; - typedef void(*SND_Init_t)(int a1, int a2, int a3); extern SND_Init_t SND_Init; @@ -470,12 +470,15 @@ namespace Game typedef void(*Vec3UnpackUnitVec_t)(PackedUnitVec, vec3_t*); extern Vec3UnpackUnitVec_t Vec3UnpackUnitVec; - typedef float(*vectoyaw_t)(vec2_t* vec); - extern vectoyaw_t vectoyaw; + typedef float(*vectoryaw_t)(vec2_t* vec); + extern vectoryaw_t vectoryaw; typedef float(*AngleNormalize360_t)(float val); extern AngleNormalize360_t AngleNormalize360; + typedef void(*_VectorMA_t)(float* va, float scale, float* vb, float* vc); + extern _VectorMA_t _VectorMA; + typedef void(*unzClose_t)(void* handle); extern unzClose_t unzClose; @@ -509,6 +512,9 @@ namespace Game typedef gentity_s*(*Weapon_RocketLauncher_Fire_t)(gentity_s* ent, unsigned int weaponIndex, float spread, weaponParms* wp, const float* gunVel, lockonFireParms* lockParms, bool magicBullet); extern Weapon_RocketLauncher_Fire_t Weapon_RocketLauncher_Fire; + typedef int(*Bullet_Fire_t)(gentity_s* attacker, float spread, weaponParms* wp, gentity_s* weaponEnt, PlayerHandIndex hand, int gameTime); + extern Bullet_Fire_t Bullet_Fire; + typedef void(*Jump_ClearState_t)(playerState_s* ps); extern Jump_ClearState_t Jump_ClearState; @@ -524,6 +530,9 @@ namespace Game typedef void(*PM_UpdateLean_t)(playerState_s* ps, float msec, usercmd_s* cmd, void(*capsuleTrace)(trace_t*, const float*, const float*, const Bounds*, int, int)); extern PM_UpdateLean_t PM_UpdateLean; + typedef bool(*PM_IsSprinting_t)(const playerState_s* ps); + extern PM_IsSprinting_t PM_IsSprinting; + typedef void(*IN_RecenterMouse_t)(); extern IN_RecenterMouse_t IN_RecenterMouse; @@ -563,6 +572,9 @@ namespace Game typedef void*(*Z_VirtualAlloc_t)(int size); extern Z_VirtualAlloc_t Z_VirtualAlloc; + typedef void*(*Z_Malloc_t)(int size); + extern Z_Malloc_t Z_Malloc; + typedef void(*I_strncpyz_t)(char* dest, const char* src, int destsize); extern I_strncpyz_t I_strncpyz; @@ -584,6 +596,12 @@ namespace Game typedef int(*LargeLocalBeginRight_t)(int size); extern LargeLocalBeginRight_t LargeLocalBeginRight; + typedef void(*LargeLocalReset_t)(); + extern LargeLocalReset_t LargeLocalReset; + + typedef void(*longjmp_internal_t)(jmp_buf env, int status); + extern longjmp_internal_t longjmp_internal; + constexpr std::size_t STATIC_MAX_LOCAL_CLIENTS = 1; constexpr std::size_t MAX_LOCAL_CLIENTS = 1; constexpr std::size_t MAX_CLIENTS = 18; @@ -596,8 +614,7 @@ namespace Game extern float* cgameFOVSensitivityScale; - extern source_t **sourceFiles; - extern keywordHash_t **menuParseKeywordHash; + extern source_t** sourceFiles; extern UiContext* uiContext; @@ -646,6 +663,7 @@ namespace Game extern PhysPreset*** varPhysPresetPtr; extern MaterialPass** varMaterialPass; extern snd_alias_list_t*** varsnd_alias_list_name; + extern MaterialVertexShader** varMaterialVertexShader; extern FxElemField* s_elemFields; @@ -668,8 +686,6 @@ namespace Game extern ScreenPlacement* scrPlaceFull; extern ScreenPlacement* scrPlaceFullUnsafe; extern ScreenPlacement* scrPlaceView; - - extern clientActive_t* clients; extern cg_s* cgArray; extern cgs_t* cgsArray; @@ -708,9 +724,9 @@ namespace Game extern Material** whiteMaterial; - extern unsigned long* _tls_index; + extern unsigned long* g_dwTlsIndex; - extern int* cls_uiStarted; + extern bgs_t* level_bgs; constexpr std::size_t PLAYER_CARD_UI_STRING_COUNT = 18; extern unsigned int* playerCardUIStringIndex; @@ -730,12 +746,17 @@ namespace Game extern int* ui_numArenas; extern int* ui_arenaBufPos; + extern punctuation_s* default_punctuations; + + extern bool* s_havePlaylists; + + constexpr auto MAX_MSGLEN = 0x20000; + ScreenPlacement* ScrPlace_GetFullPlacement(); ScreenPlacement* ScrPlace_GetUnsafeFullPlacement(); void UI_FilterStringForButtonAnimation(char* str, unsigned int strMaxSize); - void Menu_FreeItemMemory(itemDef_s* item); void Menu_SetNextCursorItem(UiContext* ctx, menuDef_t* currentMenu, int unk = 1); void Menu_SetPrevCursorItem(UiContext* ctx, menuDef_t* currentMenu, int unk = 1); const char* TableLookup(StringTable* stringtable, int row, int column); diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 876da5fa..750c2ccf 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -12,12 +12,23 @@ namespace Game G_PrintEntities_t G_PrintEntities = G_PrintEntities_t(0x4E6A50); G_GetEntityTypeName_t G_GetEntityTypeName = G_GetEntityTypeName_t(0x4EB810); + G_LocalizedStringIndex_t G_LocalizedStringIndex = G_LocalizedStringIndex_t(0x4582F0); + + G_DebugLineWithDuration_t G_DebugLineWithDuration = G_DebugLineWithDuration_t(0x4C3280); + gentity_t* g_entities = reinterpret_cast(0x18835D8); + const char* origErrorMsg = reinterpret_cast(0x79B124); + XModel* G_GetModel(const int index) { assert(index > 0); assert(index < MAX_MODELS); return cached_models[index]; } + + void G_DebugStar(const float* point, const float* color) + { + CL_AddDebugStar(point, color, 20, 1); + } } diff --git a/src/Game/Game.hpp b/src/Game/Game.hpp index b4d7d3b7..458d6b20 100644 --- a/src/Game/Game.hpp +++ b/src/Game/Game.hpp @@ -40,9 +40,19 @@ namespace Game typedef const char*(*G_GetEntityTypeName_t)(const gentity_s* ent); extern G_GetEntityTypeName_t G_GetEntityTypeName; + typedef int(*G_LocalizedStringIndex_t)(const char* string); + extern G_LocalizedStringIndex_t G_LocalizedStringIndex; + + typedef void(*G_DebugLineWithDuration_t)(const float* start, const float* end, const float* color, int depthTest, int duration); + extern G_DebugLineWithDuration_t G_DebugLineWithDuration; + constexpr std::size_t MAX_GENTITIES = 2048; constexpr std::size_t ENTITYNUM_NONE = MAX_GENTITIES - 1; extern gentity_t* g_entities; + extern const char* origErrorMsg; + extern XModel* G_GetModel(int index); + + extern void G_DebugStar(const float* point, const float* color); } diff --git a/src/Game/Script.cpp b/src/Game/Script.cpp index b7bb7457..4766e89b 100644 --- a/src/Game/Script.cpp +++ b/src/Game/Script.cpp @@ -56,6 +56,10 @@ namespace Game Scr_SetClientField_t Scr_SetClientField = Scr_SetClientField_t(0x4A6DF0); Scr_AddClassField_t Scr_AddClassField = Scr_AddClassField_t(0x4C0E70); + Scr_ConstructMessageString_t Scr_ConstructMessageString = Scr_ConstructMessageString_t(0x45F940); + + Scr_FreeHudElemConstStrings_t Scr_FreeHudElemConstStrings = Scr_FreeHudElemConstStrings_t(0x5E1120); + GetEntity_t GetEntity = GetEntity_t(0x4BC270); GetPlayerEntity_t GetPlayerEntity = GetPlayerEntity_t(0x49C4A0); @@ -63,11 +67,21 @@ namespace Game Scr_ShutdownAllocNode_t Scr_ShutdownAllocNode = Scr_ShutdownAllocNode_t(0x441650); Scr_IsSystemActive_t Scr_IsSystemActive = Scr_IsSystemActive_t(0x4B24E0); + SL_ConvertToString_t SL_ConvertToString = SL_ConvertToString_t(0x4EC1D0); + SL_GetString_t SL_GetString = SL_GetString_t(0x4CDC10); + SL_GetString__t SL_GetString_ = SL_GetString__t(0x47E310); + SL_FindString_t SL_FindString = SL_FindString_t(0x434EE0); + SL_FindLowercaseString_t SL_FindLowercaseString = SL_FindLowercaseString_t(0x4C63E0); + SL_AddRefToString_t SL_AddRefToString = SL_AddRefToString_t(0x4D9B00); + SL_RemoveRefToString_t SL_RemoveRefToString = SL_RemoveRefToString_t(0x47CD70); + scr_const_t* scr_const = reinterpret_cast(0x1AA2E00); scrVmPub_t* scrVmPub = reinterpret_cast(0x2040CF0); scrVarPub_t* scrVarPub = reinterpret_cast(0x201A408); + game_hudelem_s* g_hudelems = reinterpret_cast(0x18565A8); + void IncInParam() { Scr_ClearOutParams(); @@ -107,19 +121,22 @@ namespace Game } } - void Scr_NotifyId(unsigned int id, unsigned __int16 stringValue, unsigned int paramcount) + __declspec(naked) void Scr_NotifyId(unsigned int /*id*/, unsigned __int16 /*stringValue*/, unsigned int /*paramcount*/) { static DWORD Scr_NotifyId_t = 0x61E670; __asm { pushad - mov eax, paramcount - push stringValue - push id + + mov eax, [esp + 0x20 + 0xC] // paramcount + push [esp + 0x20 + 0x8] // stringValue + push [esp + 0x20 + 0x8] // id call Scr_NotifyId_t - add esp, 8h + add esp, 0x8 + popad + ret } } } diff --git a/src/Game/Script.hpp b/src/Game/Script.hpp index 145dccf6..7c998c50 100644 --- a/src/Game/Script.hpp +++ b/src/Game/Script.hpp @@ -119,10 +119,10 @@ namespace Game typedef int(*Scr_GetPointerType_t)(unsigned int index); extern Scr_GetPointerType_t Scr_GetPointerType; - typedef void(*Scr_Error_t)(const char*); + typedef void(*Scr_Error_t)(const char* error); extern Scr_Error_t Scr_Error; - typedef void(*Scr_ObjectError_t)(const char*); + typedef void(*Scr_ObjectError_t)(const char* error); extern Scr_ObjectError_t Scr_ObjectError; typedef void(*Scr_ParamError_t)(unsigned int paramIndex, const char*); @@ -143,12 +143,39 @@ namespace Game typedef void(*Scr_AddClassField_t)(unsigned int classnum, const char* name, unsigned int offset); extern Scr_AddClassField_t Scr_AddClassField; + typedef void(*Scr_ConstructMessageString_t)(int firstParmIndex, int lastParmIndex, const char* errorContext, char* string, unsigned int stringLimit); + extern Scr_ConstructMessageString_t Scr_ConstructMessageString; + + typedef void(*Scr_FreeHudElemConstStrings_t)(game_hudelem_s* hud); + extern Scr_FreeHudElemConstStrings_t Scr_FreeHudElemConstStrings; + typedef gentity_s*(*GetPlayerEntity_t)(scr_entref_t entref); extern GetPlayerEntity_t GetPlayerEntity; typedef gentity_s*(*GetEntity_t)(scr_entref_t entref); extern GetEntity_t GetEntity; + typedef const char*(*SL_ConvertToString_t)(scr_string_t stringValue); + extern SL_ConvertToString_t SL_ConvertToString; + + typedef unsigned int(*SL_GetString_t)(const char* str, unsigned int user); + extern SL_GetString_t SL_GetString; + + typedef unsigned int(*SL_GetString__t)(const char* str, unsigned int user, int type); + extern SL_GetString__t SL_GetString_; + + typedef unsigned int(*SL_FindString_t)(const char* name); + extern SL_FindString_t SL_FindString; + + typedef unsigned int(*SL_FindLowercaseString_t)(const char* str); + extern SL_FindLowercaseString_t SL_FindLowercaseString; + + typedef void(*SL_AddRefToString_t)(unsigned int stringValue); + extern SL_AddRefToString_t SL_AddRefToString; + + typedef void(*SL_RemoveRefToString_t)(unsigned int stringValue); + extern SL_RemoveRefToString_t SL_RemoveRefToString; + extern void IncInParam(); extern void Scr_AddBool(int value); @@ -161,4 +188,6 @@ namespace Game extern scrVmPub_t* scrVmPub; extern scrVarPub_t* scrVarPub; + + extern game_hudelem_s* g_hudelems; } diff --git a/src/Game/Server.cpp b/src/Game/Server.cpp index 9789f273..c64f5811 100644 --- a/src/Game/Server.cpp +++ b/src/Game/Server.cpp @@ -12,18 +12,24 @@ namespace Game SV_Cmd_ArgvBuffer_t SV_Cmd_ArgvBuffer = SV_Cmd_ArgvBuffer_t(0x40BB60); SV_DirectConnect_t SV_DirectConnect = SV_DirectConnect_t(0x460480); SV_SetConfigstring_t SV_SetConfigstring = SV_SetConfigstring_t(0x4982E0); + SV_GetConfigstringConst_t SV_GetConfigstringConst = SV_GetConfigstringConst_t(0x468500); SV_Loaded_t SV_Loaded = SV_Loaded_t(0x4EE3E0); SV_ClientThink_t SV_ClientThink = SV_ClientThink_t(0x44ADD0); SV_DropClient_t SV_DropClient = SV_DropClient_t(0x4D1600); SV_GetPlayerByName_t SV_GetPlayerByName = SV_GetPlayerByName_t(0x6242B0); SV_GetPlayerByNum_t SV_GetPlayerByNum = SV_GetPlayerByNum_t(0x624390); SV_FindClientByAddress_t SV_FindClientByAddress = SV_FindClientByAddress_t(0x44F450); + SV_WaitServer_t SV_WaitServer = SV_WaitServer_t(0x4256F0); int* svs_time = reinterpret_cast(0x31D9384); + int* sv_timeResidual = reinterpret_cast(0x2089E14); int* sv_serverId_value = reinterpret_cast(0x2089DC0); int* svs_clientCount = reinterpret_cast(0x31D938C); client_t* svs_clients = reinterpret_cast(0x31D9390); + unsigned short* sv_sconfigstrings = reinterpret_cast(0x208A632); + unsigned short* sv_emptyConfigString = reinterpret_cast(0x208A630); + volatile long* sv_thread_owns_game = reinterpret_cast(0x2089DB8); int SV_GetServerThreadOwnsGame() diff --git a/src/Game/Server.hpp b/src/Game/Server.hpp index dbd9d659..8fd62bba 100644 --- a/src/Game/Server.hpp +++ b/src/Game/Server.hpp @@ -29,6 +29,9 @@ namespace Game typedef void(*SV_SetConfigstring_t)(int index, const char* string); extern SV_SetConfigstring_t SV_SetConfigstring; + typedef unsigned int(*SV_GetConfigstringConst_t)(int index); + extern SV_GetConfigstringConst_t SV_GetConfigstringConst; + typedef void(*SV_DirectConnect_t)(netadr_t adr); extern SV_DirectConnect_t SV_DirectConnect; @@ -41,22 +44,29 @@ namespace Game typedef void(*SV_DropClient_t)(client_t* drop, const char* reason, bool tellThem); extern SV_DropClient_t SV_DropClient; - typedef client_t* (*SV_GetPlayerByName_t)(); + typedef client_t*(*SV_GetPlayerByName_t)(); extern SV_GetPlayerByName_t SV_GetPlayerByName; - typedef client_t* (*SV_GetPlayerByNum_t)(); + typedef client_t*(*SV_GetPlayerByNum_t)(); extern SV_GetPlayerByNum_t SV_GetPlayerByNum; - typedef client_t* (*SV_FindClientByAddress_t)(netadr_t from, int qport, int remoteClientIndex); + typedef client_t*(*SV_FindClientByAddress_t)(netadr_t from, int qport, int remoteClientIndex); extern SV_FindClientByAddress_t SV_FindClientByAddress; + typedef void(*SV_WaitServer_t)(); + extern SV_WaitServer_t SV_WaitServer; + constexpr auto MAX_STATPACKETS = 7; extern int* svs_time; + extern int* sv_timeResidual; extern int* sv_serverId_value; extern int* svs_clientCount; extern client_t* svs_clients; + extern unsigned short* sv_sconfigstrings; + extern unsigned short* sv_emptyConfigString; + extern volatile long* sv_thread_owns_game; extern int SV_GetServerThreadOwnsGame(); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 826fc6a8..f27366c1 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -2,7 +2,6 @@ #define PROTOCOL 0x96 #define NUM_CUSTOM_CLASSES 15 -#define SEMANTIC_WATER_MAP 11 #define FX_ELEM_FIELD_COUNT 90 // This allows us to compile our structures in IDA, for easier reversing :3 @@ -28,8 +27,8 @@ namespace Game unsigned __int16 classnum; }; - typedef void(__cdecl * BuiltinFunction)(); - typedef void(__cdecl * BuiltinMethod)(scr_entref_t); + typedef void(*BuiltinFunction)(); + typedef void(*BuiltinMethod)(scr_entref_t); struct BuiltinFunctionDef { @@ -96,6 +95,229 @@ namespace Game ASSET_TYPE_INVALID = -1, }; + enum GfxWarningType + { + R_WARN_FRONTEND_ENT_LIMIT = 0x0, + R_WARN_KNOWN_MODELS = 0x1, + R_WARN_KNOWN_PER_CLIENT_MODELS = 0x2, + R_WARN_KNOWN_SPECIAL_MODELS = 0x3, + R_WARN_MODEL_LIGHT_CACHE = 0x4, + R_WARN_SCENE_ENTITIES = 0x5, + R_WARN_TEMP_SKIN_BUF_SIZE = 0x6, + R_WARN_MAX_SKINNED_CACHE_VERTICES = 0x7, + R_WARN_MAX_SCENE_SURFS_SIZE = 0x8, + R_WARN_MAX_SURF_BUF = 0x9, + R_WARN_PORTAL_PLANES = 0xA, + R_WARN_MAX_CLOUDS = 0xB, + R_WARN_MAX_DLIGHTS = 0xC, + R_WARN_SMODEL_LIGHTING = 0xD, + R_WARN_SMODEL_VIS_DATA_LIMIT = 0xE, + R_WARN_SMODEL_SURF_LIMIT = 0xF, + R_WARN_SMODEL_SURF_DELAY_LIMIT = 0x10, + R_WARN_BSPSURF_DATA_LIMIT = 0x11, + R_WARN_BSPSURF_PRETESS_LIMIT = 0x12, + R_WARN_MAX_DRAWSURFS = 0x13, + R_WARN_GFX_CODE_EMISSIVE_SURF_LIMIT = 0x14, + R_WARN_GFX_CODE_TRANS_SURF_LIMIT = 0x15, + R_WARN_GFX_GLASS_SURF_LIMIT = 0x16, + R_WARN_GFX_MARK_SURF_LIMIT = 0x17, + R_WARN_GFX_SPARK_SURF_LIMIT = 0x18, + R_WARN_MAX_SCENE_DRAWSURFS = 0x19, + R_WARN_MAX_FX_DRAWSURFS = 0x1A, + R_WARN_NONEMISSIVE_FX_MATERIAL = 0x1B, + R_WARN_NONLIT_MARK_MATERIAL = 0x1C, + R_WARN_CMDBUF_OVERFLOW = 0x1D, + R_WARN_MISSING_DECL_NONDEBUG = 0x1E, + R_WARN_MAX_DYNENT_REFS = 0x1F, + R_WARN_MAX_SCENE_DOBJ_REFS = 0x20, + R_WARN_MAX_SCENE_MODEL_REFS = 0x21, + R_WARN_MAX_SCENE_BRUSH_REFS = 0x22, + R_WARN_MAX_CODE_EMISSIVE_INDS = 0x23, + R_WARN_MAX_CODE_EMISSIVE_VERTS = 0x24, + R_WARN_MAX_CODE_EMISSIVE_ARGS = 0x25, + R_WARN_MAX_CODE_TRANS_INDS = 0x26, + R_WARN_MAX_CODE_TRANS_VERTS = 0x27, + R_WARN_MAX_CODE_TRANS_ARGS = 0x28, + R_WARN_MAX_GLASS_INDS = 0x29, + R_WARN_MAX_GLASS_VERTS = 0x2A, + R_WARN_MAX_MARK_INDS = 0x2B, + R_WARN_MAX_MARK_VERTS = 0x2C, + R_WARN_MAX_SPARK_INDS = 0x2D, + R_WARN_MAX_SPARK_VERTS = 0x2E, + R_WARN_DEBUG_ALLOC = 0x2F, + R_WARN_SPOT_LIGHT_LIMIT = 0x30, + R_WARN_FX_ELEM_LIMIT = 0x31, + R_WARN_FX_BOLT_LIMIT = 0x32, + R_WARN_WORKER_CMD_SIZE = 0x33, + R_WARN_UNKNOWN_STATICMODEL_SHADER = 0x34, + R_WARN_UNKNOWN_XMODEL_SHADER = 0x35, + R_WARN_DYNAMIC_INDEX_BUFFER_SIZE = 0x36, + R_WARN_TOO_MANY_LIGHT_GRID_POINTS = 0x37, + R_WARN_FOGABLE_2DTEXT = 0x38, + R_WARN_FOGABLE_2DGLYPH = 0x39, + R_WARN_SUN_QUERY = 0x3A, + R_WARN_ESTIMATED_BOUNDS_TOO_SMALL = 0x3B, + R_WARN_COUNT = 0x3C, + }; + + enum surfType_t + { + R_SMODEL_SURFTYPE_RIGID = 0x0, + R_SMODEL_SURFTYPE_SKINNED = 0x1, + R_SMODEL_SURFTYPE_CACHED = 0x2, + R_SMODEL_SURFTYPE_PRETESS = 0x3, + R_SMODEL_SURFTYPE_COUNT = 0x4, + }; + + enum ShaderCodeConstants + { + CONST_SRC_CODE_MAYBE_DIRTY_PS_BEGIN = 0x0, + CONST_SRC_CODE_LIGHT_POSITION = 0x0, + CONST_SRC_CODE_LIGHT_DIFFUSE = 0x1, + CONST_SRC_CODE_LIGHT_SPECULAR = 0x2, + CONST_SRC_CODE_LIGHT_SPOTDIR = 0x3, + CONST_SRC_CODE_LIGHT_SPOTFACTORS = 0x4, + CONST_SRC_CODE_LIGHT_FALLOFF_PLACEMENT = 0x5, + CONST_SRC_CODE_PARTICLE_CLOUD_COLOR = 0x6, + CONST_SRC_CODE_GAMETIME = 0x7, + CONST_SRC_CODE_MAYBE_DIRTY_PS_END = 0x8, + CONST_SRC_CODE_ALWAYS_DIRTY_PS_BEGIN = 0x8, + CONST_SRC_CODE_PIXEL_COST_FRACS = 0x8, + CONST_SRC_CODE_PIXEL_COST_DECODE = 0x9, + CONST_SRC_CODE_FILTER_TAP_0 = 0xA, + CONST_SRC_CODE_FILTER_TAP_1 = 0xB, + CONST_SRC_CODE_FILTER_TAP_2 = 0xC, + CONST_SRC_CODE_FILTER_TAP_3 = 0xD, + CONST_SRC_CODE_FILTER_TAP_4 = 0xE, + CONST_SRC_CODE_FILTER_TAP_5 = 0xF, + CONST_SRC_CODE_FILTER_TAP_6 = 0x10, + CONST_SRC_CODE_FILTER_TAP_7 = 0x11, + CONST_SRC_CODE_COLOR_MATRIX_R = 0x12, + CONST_SRC_CODE_COLOR_MATRIX_G = 0x13, + CONST_SRC_CODE_COLOR_MATRIX_B = 0x14, + CONST_SRC_CODE_SHADOWMAP_POLYGON_OFFSET = 0x15, + CONST_SRC_CODE_RENDER_TARGET_SIZE = 0x16, + CONST_SRC_CODE_ALWAYS_DIRTY_PS_END = 0x17, + CONST_SRC_CODE_FIXED_PS_BEGIN = 0x17, + CONST_SRC_CODE_DOF_EQUATION_VIEWMODEL_AND_FAR_BLUR = 0x17, + CONST_SRC_CODE_DOF_EQUATION_SCENE = 0x18, + CONST_SRC_CODE_DOF_LERP_SCALE = 0x19, + CONST_SRC_CODE_DOF_LERP_BIAS = 0x1A, + CONST_SRC_CODE_DOF_ROW_DELTA = 0x1B, + CONST_SRC_CODE_MOTION_MATRIX_X = 0x1C, + CONST_SRC_CODE_MOTION_MATRIX_Y = 0x1D, + CONST_SRC_CODE_MOTION_MATRIX_W = 0x1E, + CONST_SRC_CODE_SHADOWMAP_SWITCH_PARTITION = 0x1F, + CONST_SRC_CODE_SHADOWMAP_SCALE = 0x20, + CONST_SRC_CODE_ZNEAR = 0x21, + CONST_SRC_CODE_LIGHTING_LOOKUP_SCALE = 0x22, + CONST_SRC_CODE_DEBUG_BUMPMAP = 0x23, + CONST_SRC_CODE_MATERIAL_COLOR = 0x24, + CONST_SRC_CODE_FOG = 0x25, + CONST_SRC_CODE_FOG_COLOR_LINEAR = 0x26, + CONST_SRC_CODE_FOG_COLOR_GAMMA = 0x27, + CONST_SRC_CODE_FOG_SUN_CONSTS = 0x28, + CONST_SRC_CODE_FOG_SUN_COLOR_LINEAR = 0x29, + CONST_SRC_CODE_FOG_SUN_COLOR_GAMMA = 0x2A, + CONST_SRC_CODE_FOG_SUN_DIR = 0x2B, + CONST_SRC_CODE_GLOW_SETUP = 0x2C, + CONST_SRC_CODE_GLOW_APPLY = 0x2D, + CONST_SRC_CODE_COLOR_BIAS = 0x2E, + CONST_SRC_CODE_COLOR_TINT_BASE = 0x2F, + CONST_SRC_CODE_COLOR_TINT_DELTA = 0x30, + CONST_SRC_CODE_COLOR_TINT_QUADRATIC_DELTA = 0x31, + CONST_SRC_CODE_OUTDOOR_FEATHER_PARMS = 0x32, + CONST_SRC_CODE_ENVMAP_PARMS = 0x33, + CONST_SRC_CODE_SUN_SHADOWMAP_PIXEL_ADJUST = 0x34, + CONST_SRC_CODE_SPOT_SHADOWMAP_PIXEL_ADJUST = 0x35, + CONST_SRC_CODE_COMPOSITE_FX_DISTORTION = 0x36, + CONST_SRC_CODE_POSTFX_FADE_EFFECT = 0x37, + CONST_SRC_CODE_VIEWPORT_DIMENSIONS = 0x38, + CONST_SRC_CODE_FRAMEBUFFER_READ = 0x39, + CONST_SRC_CODE_FIXED_PS_END = 0x3A, + CONST_SRC_CODE_NON_PS_BEGIN = 0x3A, + CONST_SRC_CODE_BASE_LIGHTING_COORDS = 0x3A, + CONST_SRC_CODE_LIGHT_PROBE_AMBIENT = 0x3B, + CONST_SRC_CODE_NEARPLANE_ORG = 0x3C, + CONST_SRC_CODE_NEARPLANE_DX = 0x3D, + CONST_SRC_CODE_NEARPLANE_DY = 0x3E, + CONST_SRC_CODE_CLIP_SPACE_LOOKUP_SCALE = 0x3F, + CONST_SRC_CODE_CLIP_SPACE_LOOKUP_OFFSET = 0x40, + CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX0 = 0x41, + CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX1 = 0x42, + CONST_SRC_CODE_PARTICLE_CLOUD_MATRIX2 = 0x43, + CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR0 = 0x44, + CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR1 = 0x45, + CONST_SRC_CODE_PARTICLE_CLOUD_SPARK_COLOR2 = 0x46, + CONST_SRC_CODE_PARTICLE_FOUNTAIN_PARM0 = 0x47, + CONST_SRC_CODE_PARTICLE_FOUNTAIN_PARM1 = 0x48, + CONST_SRC_CODE_DEPTH_FROM_CLIP = 0x49, + CONST_SRC_CODE_CODE_MESH_ARG_0 = 0x4A, + CONST_SRC_CODE_CODE_MESH_ARG_1 = 0x4B, + CONST_SRC_CODE_CODE_MESH_ARG_LAST = 0x4B, + CONST_SRC_CODE_NON_PS_END = 0x4C, + CONST_SRC_CODE_COUNT_FLOAT4 = 0x4C, + CONST_SRC_FIRST_CODE_MATRIX = 0x4C, + CONST_SRC_CODE_VIEW_MATRIX = 0x4C, + CONST_SRC_CODE_INVERSE_VIEW_MATRIX = 0x4D, + CONST_SRC_CODE_TRANSPOSE_VIEW_MATRIX = 0x4E, + CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_MATRIX = 0x4F, + CONST_SRC_CODE_PROJECTION_MATRIX = 0x50, + CONST_SRC_CODE_INVERSE_PROJECTION_MATRIX = 0x51, + CONST_SRC_CODE_TRANSPOSE_PROJECTION_MATRIX = 0x52, + CONST_SRC_CODE_INVERSE_TRANSPOSE_PROJECTION_MATRIX = 0x53, + CONST_SRC_CODE_VIEW_PROJECTION_MATRIX = 0x54, + CONST_SRC_CODE_INVERSE_VIEW_PROJECTION_MATRIX = 0x55, + CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX = 0x56, + CONST_SRC_CODE_INVERSE_TRANSPOSE_VIEW_PROJECTION_MATRIX = 0x57, + CONST_SRC_CODE_SHADOW_LOOKUP_MATRIX = 0x58, + CONST_SRC_CODE_INVERSE_SHADOW_LOOKUP_MATRIX = 0x59, + CONST_SRC_CODE_TRANSPOSE_SHADOW_LOOKUP_MATRIX = 0x5A, + CONST_SRC_CODE_INVERSE_TRANSPOSE_SHADOW_LOOKUP_MATRIX = 0x5B, + CONST_SRC_CODE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x5C, + CONST_SRC_CODE_INVERSE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x5D, + CONST_SRC_CODE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x5E, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_OUTDOOR_LOOKUP_MATRIX = 0x5F, + CONST_SRC_CODE_WORLD_MATRIX0 = 0x60, + CONST_SRC_CODE_INVERSE_WORLD_MATRIX0 = 0x61, + CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX0 = 0x62, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX0 = 0x63, + CONST_SRC_CODE_WORLD_VIEW_MATRIX0 = 0x64, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX0 = 0x65, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX0 = 0x66, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX0 = 0x67, + CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX0 = 0x68, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX0 = 0x69, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX0 = 0x6A, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX0 = 0x6B, + CONST_SRC_CODE_WORLD_MATRIX1 = 0x6C, + CONST_SRC_CODE_INVERSE_WORLD_MATRIX1 = 0x6D, + CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX1 = 0x6E, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX1 = 0x6F, + CONST_SRC_CODE_WORLD_VIEW_MATRIX1 = 0x70, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX1 = 0x71, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX1 = 0x72, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX1 = 0x73, + CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX1 = 0x74, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX1 = 0x75, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX1 = 0x76, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX1 = 0x77, + CONST_SRC_CODE_WORLD_MATRIX2 = 0x78, + CONST_SRC_CODE_INVERSE_WORLD_MATRIX2 = 0x79, + CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX2 = 0x7A, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_MATRIX2 = 0x7B, + CONST_SRC_CODE_WORLD_VIEW_MATRIX2 = 0x7C, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_MATRIX2 = 0x7D, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_MATRIX2 = 0x7E, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX2 = 0x7F, + CONST_SRC_CODE_WORLD_VIEW_PROJECTION_MATRIX2 = 0x80, + CONST_SRC_CODE_INVERSE_WORLD_VIEW_PROJECTION_MATRIX2 = 0x81, + CONST_SRC_CODE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX2 = 0x82, + CONST_SRC_CODE_INVERSE_TRANSPOSE_WORLD_VIEW_PROJECTION_MATRIX2 = 0x83, + CONST_SRC_TOTAL_COUNT = 0x84, + CONST_SRC_NONE = 0x85, + }; + enum FsThread { FS_THREAD_MAIN = 0x0, @@ -107,6 +329,23 @@ namespace Game FS_THREAD_INVALID = 0x6, }; + enum TextureSemantic : char + { + TS_2D = 0x0, + TS_FUNCTION = 0x1, + TS_COLOR_MAP = 0x2, + TS_DETAIL_MAP = 0x3, + TS_UNUSED_2 = 0x4, + TS_NORMAL_MAP = 0x5, + TS_UNUSED_3 = 0x6, + TS_UNUSED_4 = 0x7, + TS_SPECULAR_MAP = 0x8, + TS_UNUSED_5 = 0x9, + TS_UNUSED_6 = 0xA, + TS_WATER_MAP = 0xB, + }; + + enum materialSurfType_t { SURF_TYPE_DEFAULT, @@ -144,12 +383,12 @@ namespace Game SURF_TYPE_COUNT }; - enum DvarFlags : unsigned __int16 + enum : unsigned __int16 { DVAR_NONE = 0, // No flags DVAR_ARCHIVE = 1 << 0, // Set to cause it to be saved to config_mp.cfg of the client DVAR_LATCH = 1 << 1, // Will only change when C code next does a Dvar_Get(), so it can't be changed - // without proper initialization. Modified will be set, even though the value hasn't changed yet + // without proper initialization. Modified will be set, even though the value hasn't changed yet DVAR_CHEAT = 1 << 2, // Can not be changed if cheats are disabled DVAR_CODINFO = 1 << 3, // On change, this is sent to all clients (if you are host) DVAR_SCRIPTINFO = 1 << 4, @@ -252,6 +491,19 @@ namespace Game SS_GAME = 0x2, }; + enum GfxLightType + { + GFX_LIGHT_TYPE_NONE = 0x0, + GFX_LIGHT_TYPE_DIR = 0x1, + GFX_LIGHT_TYPE_SPOT = 0x2, + GFX_LIGHT_TYPE_OMNI = 0x3, + GFX_LIGHT_TYPE_COUNT = 0x4, + GFX_LIGHT_TYPE_DIR_SHADOWMAP = 0x4, + GFX_LIGHT_TYPE_SPOT_SHADOWMAP = 0x5, + GFX_LIGHT_TYPE_OMNI_SHADOWMAP = 0x6, + GFX_LIGHT_TYPE_COUNT_WITH_SHADOWMAP_VERSIONS = 0x7, + }; + enum errorParm_t { ERR_FATAL = 0x0, @@ -272,6 +524,7 @@ namespace Game CS_VOTE_NO = 0x14, CS_VOTE_MAPNAME = 0x15, CS_VOTE_GAMETYPE = 0x16, + CS_SHELLSHOCKS = 0x985, CS_ITEMS = 0x102A, }; // Incomplete @@ -413,11 +666,11 @@ namespace Game typedef struct cmd_function_s { - cmd_function_s *next; - const char *name; - const char *autoCompleteDir; - const char *autoCompleteExt; - void(__cdecl *function)(); + cmd_function_s* next; + const char* name; + const char* autoCompleteDir; + const char* autoCompleteExt; + void(__cdecl* function)(); int flags; } cmd_function_t; @@ -602,14 +855,14 @@ namespace Game struct __declspec(align(4)) PhysPreset { - const char *name; + const char* name; int type; float mass; float bounce; float friction; float bulletForceScale; float explosiveForceScale; - const char *sndAliasPrefix; + const char* sndAliasPrefix; float piecesSpreadFraction; float piecesUpwardVelocity; bool tempDefaultToCylinder; @@ -632,7 +885,7 @@ namespace Game struct cbrushside_t { - cplane_s *plane; + cplane_s* plane; unsigned __int16 materialNum; char firstAdjacentSideOffset; char edgeCount; @@ -642,8 +895,8 @@ namespace Game { unsigned __int16 numsides; unsigned __int16 glassPieceIndex; - cbrushside_t *sides; - char *baseAdjacentSide; + cbrushside_t* sides; + char* baseAdjacentSide; __int16 axialMaterialNum[2][3]; char firstAdjacentSideOffsets[2][3]; char edgeCount[2][3]; @@ -654,12 +907,12 @@ namespace Game Bounds bounds; cbrush_t brush; int totalEdgeCount; - cplane_s *planes; + cplane_s* planes; }; struct PhysGeomInfo { - BrushWrapper *brushWrapper; + BrushWrapper* brushWrapper; int type; float orientation[3][3]; Bounds bounds; @@ -674,18 +927,18 @@ namespace Game struct PhysCollmap { - const char *name; + const char* name; unsigned int count; - PhysGeomInfo *geoms; + PhysGeomInfo* geoms; PhysMass mass; Bounds bounds; }; union XAnimIndices { - char *_1; - unsigned __int16 *_2; - void *data; + char* _1; + unsigned __int16* _2; + void* data; }; struct XAnimNotifyInfo @@ -765,14 +1018,14 @@ namespace Game struct XAnimDeltaPart { - XAnimPartTrans *trans; - XAnimDeltaPartQuat2 *quat2; - XAnimDeltaPartQuat *quat; + XAnimPartTrans* trans; + XAnimDeltaPartQuat2* quat2; + XAnimDeltaPartQuat* quat; }; struct XAnimParts { - const char *name; + const char* name; unsigned __int16 dataByteCount; unsigned __int16 dataShortCount; unsigned __int16 dataIntCount; @@ -788,22 +1041,22 @@ namespace Game unsigned int indexCount; float framerate; float frequency; - unsigned __int16 *names; - char *dataByte; - __int16 *dataShort; - int *dataInt; - __int16 *randomDataShort; - char *randomDataByte; - int *randomDataInt; + unsigned __int16* names; + char* dataByte; + __int16* dataShort; + int* dataInt; + __int16* randomDataShort; + char* randomDataByte; + int* randomDataInt; XAnimIndices indices; - XAnimNotifyInfo *notify; - XAnimDeltaPart *deltaPart; + XAnimNotifyInfo* notify; + XAnimDeltaPart* deltaPart; }; struct XSurfaceVertexInfo { __int16 vertCount[4]; - unsigned __int16 *vertsBlend; + unsigned __int16* vertsBlend; }; union GfxColor @@ -856,9 +1109,9 @@ namespace Game float trans[3]; float scale[3]; unsigned int nodeCount; - XSurfaceCollisionNode *nodes; + XSurfaceCollisionNode* nodes; unsigned int leafCount; - XSurfaceCollisionLeaf *leafs; + XSurfaceCollisionLeaf* leafs; }; struct XRigidVertList @@ -867,7 +1120,7 @@ namespace Game unsigned __int16 vertCount; unsigned __int16 triOffset; unsigned __int16 triCount; - XSurfaceCollisionTree *collisionTree; + XSurfaceCollisionTree* collisionTree; }; struct XSurface @@ -879,18 +1132,18 @@ namespace Game char zoneHandle; unsigned __int16 baseTriIndex; unsigned __int16 baseVertIndex; - unsigned __int16 *triIndices; + unsigned __int16* triIndices; XSurfaceVertexInfo vertInfo; - GfxPackedVertex *verts0; + GfxPackedVertex* verts0; unsigned int vertListCount; - XRigidVertList *vertList; + XRigidVertList* vertList; int partBits[6]; }; struct XModelSurfs { - const char *name; - XSurface *surfs; + const char* name; + XSurface* surfs; unsigned __int16 numsurfs; int partBits[6]; }; @@ -923,11 +1176,11 @@ namespace Game unsigned __int64 packed; }; - struct /*__declspec(align(4))*/ MaterialInfo + struct MaterialInfo { - const char *name; - char gameFlags; - char sortKey; + const char* name; + unsigned char gameFlags; + unsigned char sortKey; unsigned char textureAtlasRowCount; unsigned char textureAtlasColumnCount; GfxDrawSurf drawSurf; @@ -944,12 +1197,12 @@ namespace Game struct MaterialVertexStreamRouting { MaterialStreamRouting data[13]; - IDirect3DVertexDeclaration9 *decl[16]; + IDirect3DVertexDeclaration9* decl[16]; }; struct MaterialVertexDeclaration { - const char *name; + const char* name; char streamCount; bool hasOptionalSource; MaterialVertexStreamRouting routing; @@ -957,42 +1210,59 @@ namespace Game struct GfxVertexShaderLoadDef { - unsigned int *program; + unsigned int* program; unsigned __int16 programSize; unsigned __int16 loadForRenderer; }; struct MaterialVertexShaderProgram { - IDirect3DVertexShader9 *vs; + IDirect3DVertexShader9* vs; GfxVertexShaderLoadDef loadDef; }; struct MaterialVertexShader { - const char *name; + const char* name; MaterialVertexShaderProgram prog; }; struct GfxPixelShaderLoadDef { - unsigned int *program; + unsigned int* program; unsigned __int16 programSize; unsigned __int16 loadForRenderer; }; struct MaterialPixelShaderProgram { - IDirect3DPixelShader9 *ps; + IDirect3DPixelShader9* ps; GfxPixelShaderLoadDef loadDef; }; struct MaterialPixelShader { - const char *name; + const char* name; MaterialPixelShaderProgram prog; }; + + enum MaterialShaderArgumentType : unsigned __int16 + { + MTL_ARG_MATERIAL_VERTEX_CONST = 0x0, + MTL_ARG_LITERAL_VERTEX_CONST = 0x1, + MTL_ARG_MATERIAL_PIXEL_SAMPLER = 0x2, + MTL_ARG_CODE_PRIM_BEGIN = 0x3, + MTL_ARG_CODE_VERTEX_CONST = 0x3, + MTL_ARG_CODE_PIXEL_SAMPLER = 0x4, + MTL_ARG_CODE_PIXEL_CONST = 0x5, + MTL_ARG_CODE_PRIM_END = 0x6, + MTL_ARG_MATERIAL_PIXEL_CONST = 0x6, + MTL_ARG_LITERAL_PIXEL_CONST = 0x7, + MTL_ARG_COUNT = 0x8, + }; + + struct MaterialArgumentCodeConst { unsigned __int16 index; @@ -1002,7 +1272,7 @@ namespace Game union MaterialArgumentDef { - const float *literalConst; + float* literalConst; MaterialArgumentCodeConst codeConst; unsigned int codeSampler; unsigned int nameHash; @@ -1010,21 +1280,21 @@ namespace Game struct MaterialShaderArgument { - unsigned __int16 type; + MaterialShaderArgumentType type; unsigned __int16 dest; MaterialArgumentDef u; }; struct MaterialPass { - MaterialVertexDeclaration *vertexDecl; - MaterialVertexShader *vertexShader; - MaterialPixelShader *pixelShader; + MaterialVertexDeclaration* vertexDecl; + MaterialVertexShader* vertexShader; + MaterialPixelShader* pixelShader; char perPrimArgCount; char perObjArgCount; char stableArgCount; char customSamplerFlags; - MaterialShaderArgument *args; + MaterialShaderArgument* args; }; struct visionSetVars_t @@ -1088,8 +1358,6 @@ namespace Game int sprintStartMaxLength; }; - - /* 1018 */ struct MantleState { float yaw; @@ -1098,7 +1366,6 @@ namespace Game int flags; }; - /* 1019 */ struct PlayerActiveWeaponState { int weapAnim; @@ -1110,7 +1377,6 @@ namespace Game unsigned int weaponShotCount; }; - /* 1020 */ struct PlayerEquippedWeaponState { bool usedBefore; @@ -1119,14 +1385,12 @@ namespace Game bool needsRechamber[2]; }; - /* 1021 */ struct GlobalAmmo { int ammoType; int ammoCount; }; - /* 1022 */ struct ClipAmmo { int clipIndex; @@ -1141,7 +1405,6 @@ namespace Game WEAPON_HAND_DEFAULT = 0x0, }; - /* 1023 */ struct PlayerWeaponCommonState { int offHandIndex; @@ -1173,13 +1436,11 @@ namespace Game ACTIONSLOTTYPECOUNT = 0x4, }; - /* 1024 */ struct ActionSlotParam_SpecifyWeapon { unsigned int index; }; - /* 1025 */ struct ActionSlotParam { ActionSlotParam_SpecifyWeapon specifyWeapon; @@ -1196,7 +1457,6 @@ namespace Game OBJST_NUMSTATES = 0x6, }; - /* 1026 */ struct objective_t { objectiveState_t state; @@ -1206,8 +1466,6 @@ namespace Game int icon; }; - - /* 104 */ enum he_type_t { HE_TYPE_FREE = 0x0, @@ -1237,7 +1495,6 @@ namespace Game char a; }; - /* 1028 */ union hudelem_color_t { hud_color __s0; @@ -1290,6 +1547,14 @@ namespace Game int flags; }; + struct game_hudelem_s + { + hudelem_s elem; + int clientNum; + int team; + int archived; + }; + enum { PMF_PRONE = 1 << 0, @@ -1594,6 +1859,8 @@ namespace Game int serverCommandNum; }; + static_assert(sizeof(clSnapshot_t) == 0x314C); + enum StanceState { CL_STANCE_STAND = 0x0, @@ -1679,23 +1946,23 @@ namespace Game enum usercmdButtonBits { - CMD_BUTTON_ATTACK = 0x1, - CMD_BUTTON_SPRINT = 0x2, - CMD_BUTTON_MELEE = 0x4, - CMD_BUTTON_ACTIVATE = 0x8, - CMD_BUTTON_RELOAD = 0x10, - CMD_BUTTON_USE_RELOAD = 0x20, - CMD_BUTTON_LEAN_LEFT = 0x40, - CMD_BUTTON_LEAN_RIGHT = 0x80, - CMD_BUTTON_PRONE = 0x100, - CMD_BUTTON_CROUCH = 0x200, - CMD_BUTTON_UP = 0x400, - CMD_BUTTON_ADS = 0x800, - CMD_BUTTON_DOWN = 0x1000, - CMD_BUTTON_BREATH = 0x2000, - CMD_BUTTON_FRAG = 0x4000, - CMD_BUTTON_OFFHAND_SECONDARY = 0x8000, - CMD_BUTTON_THROW = 0x80000 + CMD_BUTTON_ATTACK = 1 << 0, + CMD_BUTTON_SPRINT = 1 << 1, + CMD_BUTTON_MELEE = 1 << 2, + CMD_BUTTON_ACTIVATE = 1 << 3, + CMD_BUTTON_RELOAD = 1 << 4, + CMD_BUTTON_USE_RELOAD = 1 << 5, + CMD_BUTTON_LEAN_LEFT = 1 << 6, + CMD_BUTTON_LEAN_RIGHT = 1 << 7, + CMD_BUTTON_PRONE = 1 << 8, + CMD_BUTTON_CROUCH = 1 << 9, + CMD_BUTTON_UP = 1 << 10, + CMD_BUTTON_ADS = 1 << 11, + CMD_BUTTON_DOWN = 1 << 12, + CMD_BUTTON_BREATH = 1 << 13, + CMD_BUTTON_FRAG = 1 << 14, + CMD_BUTTON_OFFHAND_SECONDARY = 1 << 15, + CMD_BUTTON_THROW = 1 << 19, }; #pragma pack(push, 4) @@ -1871,7 +2138,7 @@ namespace Game struct MaterialTechnique { - const char *name; + const char* name; unsigned __int16 flags; unsigned __int16 passCount; MaterialPass passArray[1]; @@ -1879,12 +2146,12 @@ namespace Game struct MaterialTechniqueSet { - const char *name; + const char* name; char worldVertFormat; bool hasBeenUploaded; char unused[1]; - MaterialTechniqueSet *remappedTechniqueSet; - MaterialTechnique *techniques[48]; + MaterialTechniqueSet* remappedTechniqueSet; + MaterialTechnique* techniques[48]; }; struct GfxImageLoadDefIW3 @@ -1909,11 +2176,11 @@ namespace Game union GfxTexture { - IDirect3DBaseTexture9 *basemap; - IDirect3DTexture9 *map; - IDirect3DVolumeTexture9 *volmap; - IDirect3DCubeTexture9 *cubemap; - GfxImageLoadDef *loadDef; + IDirect3DBaseTexture9* basemap; + IDirect3DTexture9* map; + IDirect3DVolumeTexture9* volmap; + IDirect3DCubeTexture9* cubemap; + GfxImageLoadDef* loadDef; }; struct Picmip @@ -1930,7 +2197,7 @@ namespace Game { GfxTexture texture; char mapType; - char semantic; + TextureSemantic semantic; char category; bool useSrgbReads; Picmip picmip; @@ -1941,7 +2208,7 @@ namespace Game unsigned __int16 height; unsigned __int16 depth; bool delayLoadPixels; - const char *name; + const char* name; }; struct WaterWritable @@ -1958,8 +2225,8 @@ namespace Game struct water_t { WaterWritable writable; - complex_s *H0; - float *wTerm; + complex_s* H0; + float* wTerm; int M; int N; float Lx; @@ -1969,13 +2236,13 @@ namespace Game float winddir[2]; float amplitude; float codeConstant[4]; - GfxImage *image; + GfxImage* image; }; union MaterialTextureDefInfo { - GfxImage *image; - water_t *water; + GfxImage* image; + water_t* water; }; struct MaterialTextureDef @@ -1984,7 +2251,7 @@ namespace Game char nameStart; char nameEnd; char samplerState; - char semantic; + TextureSemantic semantic; MaterialTextureDefInfo u; }; @@ -1995,6 +2262,87 @@ namespace Game float literal[4]; }; + enum GfxSurfaceStatebitOp0 : unsigned int + { + GFXS0_SRCBLEND_RGB_SHIFT = 0x0, + GFXS0_SRCBLEND_RGB_MASK = 0xF, + GFXS0_DSTBLEND_RGB_SHIFT = 0x4, + GFXS0_DSTBLEND_RGB_MASK = 0xF0, + GFXS0_BLENDOP_RGB_SHIFT = 0x8, + GFXS0_BLENDOP_RGB_MASK = 0x700, + GFXS0_BLEND_RGB_MASK = 0x7FF, + GFXS0_ATEST_DISABLE = 0x800, + GFXS0_ATEST_GT_0 = 0x1000, + GFXS0_ATEST_LT_128 = 0x2000, + GFXS0_ATEST_GE_128 = 0x3000, + GFXS0_ATEST_MASK = 0x3000, + GFXS0_CULL_SHIFT = 0xE, + GFXS0_CULL_NONE = 0x4000, + GFXS0_CULL_BACK = 0x8000, + GFXS0_CULL_FRONT = 0xC000, + GFXS0_CULL_MASK = 0xC000, + GFXS0_SRCBLEND_ALPHA_SHIFT = 0x10, + GFXS0_SRCBLEND_ALPHA_MASK = 0xF0000, + GFXS0_DSTBLEND_ALPHA_SHIFT = 0x14, + GFXS0_DSTBLEND_ALPHA_MASK = 0xF00000, + GFXS0_BLENDOP_ALPHA_SHIFT = 0x18, + GFXS0_BLENDOP_ALPHA_MASK = 0x7000000, + GFXS0_BLEND_ALPHA_MASK = 0x7FF0000, + GFXS0_COLORWRITE_RGB = 0x8000000, + GFXS0_COLORWRITE_ALPHA = 0x10000000, + GFXS0_COLORWRITE_MASK = 0x18000000, + GFXS0_GAMMAWRITE = 0x40000000, + GFXS0_POLYMODE_LINE = 0x80000000 + }; + + enum GfxSurfaceStatebitOp1 : unsigned int + { + GFXS1_DEPTHWRITE = 0x1, + GFXS1_DEPTHTEST_DISABLE = 0x2, + GFXS1_DEPTHTEST_SHIFT = 0x2, + GFXS1_DEPTHTEST_ALWAYS = 0x0, + GFXS1_DEPTHTEST_LESS = 0x4, + GFXS1_DEPTHTEST_EQUAL = 0x8, + GFXS1_DEPTHTEST_LESSEQUAL = 0xC, + GFXS1_DEPTHTEST_MASK = 0xC, + GFXS1_POLYGON_OFFSET_SHIFT = 0x4, + GFXS1_POLYGON_OFFSET_0 = 0x0, + GFXS1_POLYGON_OFFSET_1 = 0x10, + GFXS1_POLYGON_OFFSET_2 = 0x20, + GFXS1_POLYGON_OFFSET_SHADOWMAP = 0x30, + GFXS1_POLYGON_OFFSET_MASK = 0x30, + GFXS1_STENCIL_FRONT_ENABLE = 0x40, + GFXS1_STENCIL_BACK_ENABLE = 0x80, + GFXS1_STENCIL_MASK = 0xC0, + GFXS1_STENCIL_FRONT_PASS_SHIFT = 0x8, + GFXS1_STENCIL_FRONT_FAIL_SHIFT = 0xB, + GFXS1_STENCIL_FRONT_ZFAIL_SHIFT = 0xE, + GFXS1_STENCIL_FRONT_FUNC_SHIFT = 0x11, + GFXS1_STENCIL_FRONT_MASK = 0xFFF00, + GFXS1_STENCIL_BACK_PASS_SHIFT = 0x14, + GFXS1_STENCIL_BACK_FAIL_SHIFT = 0x17, + GFXS1_STENCIL_BACK_ZFAIL_SHIFT = 0x1A, + GFXS1_STENCIL_BACK_FUNC_SHIFT = 0x1D, + GFXS1_STENCIL_BACK_MASK = 0xFFF00000, + GFXS1_STENCILFUNC_FRONTBACK_MASK = 0xE00E0000, + GFXS1_STENCILOP_FRONTBACK_MASK = 0x1FF1FF00, + }; + + enum GfxStencilOp + { + GFXS_STENCILOP_KEEP = 0x0, + GFXS_STENCILOP_ZERO = 0x1, + GFXS_STENCILOP_REPLACE = 0x2, + GFXS_STENCILOP_INCRSAT = 0x3, + GFXS_STENCILOP_DECRSAT = 0x4, + GFXS_STENCILOP_INVERT = 0x5, + GFXS_STENCILOP_INCR = 0x6, + GFXS_STENCILOP_DECR = 0x7, + + GFXS_STENCILOP_COUNT, + GFXS_STENCILOP_MASK = 0x7 + }; + struct GfxStateBits { unsigned int loadBits[2]; @@ -2009,10 +2357,10 @@ namespace Game unsigned char stateBitsCount; char stateFlags; char cameraRegion; - MaterialTechniqueSet *techniqueSet; - MaterialTextureDef *textureTable; - MaterialConstantDef *constantTable; - GfxStateBits *stateBitsTable; + MaterialTechniqueSet* techniqueSet; + MaterialTextureDef* textureTable; + MaterialConstantDef* constantTable; + GfxStateBits* stateBitsTable; }; struct XModelLodInfo @@ -2020,9 +2368,9 @@ namespace Game float dist; unsigned __int16 numsurfs; unsigned __int16 surfIndex; - XModelSurfs *modelSurfs; + XModelSurfs* modelSurfs; int partBits[6]; - XSurface *surfs; + XSurface* surfs; char lod; char smcBaseIndexPlusOne; char smcSubIndexMask; @@ -2038,7 +2386,7 @@ namespace Game struct XModelCollSurf_s { - XModelCollTri_s *collTris; + XModelCollTri_s* collTris; int numCollTris; Bounds bounds; int boneIdx; @@ -2054,66 +2402,66 @@ namespace Game struct XModel { - const char *name; - char numBones; - char numRootBones; + const char* name; + unsigned char numBones; + unsigned char numRootBones; unsigned char numsurfs; - char lodRampType; + unsigned char lodRampType; float scale; unsigned int noScalePartBits[6]; - unsigned __int16 *boneNames; - char *parentList; - __int16 *quats; - float *trans; - char *partClassification; - DObjAnimMat *baseMat; - Material **materialHandles; + unsigned __int16* boneNames; + unsigned char *parentList; + short* quats; + float* trans; + unsigned char* partClassification; + DObjAnimMat* baseMat; + Material** materialHandles; XModelLodInfo lodInfo[4]; - char maxLoadedLod; - char numLods; - char collLod; - char flags; - XModelCollSurf_s *collSurfs; + unsigned char maxLoadedLod; + unsigned char numLods; + unsigned char collLod; + unsigned char flags; + XModelCollSurf_s* collSurfs; int numCollSurfs; int contents; - XBoneInfo *boneInfo; + XBoneInfo* boneInfo; float radius; Bounds bounds; int memUsage; bool bad; - PhysPreset *physPreset; - PhysCollmap *physCollmap; + PhysPreset* physPreset; + PhysCollmap* physCollmap; }; struct _AILSOUNDINFO { int format; - const void *data_ptr; + const void* data_ptr; unsigned int data_len; unsigned int rate; int bits; int channels; unsigned int samples; unsigned int block_size; - const void *initial_ptr; + const void* initial_ptr; }; struct MssSound { _AILSOUNDINFO info; - char *data; + char* data; }; struct LoadedSound { - const char *name; + const char* name; MssSound sound; }; struct StreamFileNameRaw { - const char *dir; - const char *name; + const char* dir; + const char* name; }; union StreamFileInfo @@ -2133,7 +2481,7 @@ namespace Game union SoundFileRef { - LoadedSound *loadSnd; + LoadedSound* loadSnd; StreamedSound streamSnd; }; @@ -2161,7 +2509,7 @@ namespace Game struct SndCurve { - const char *filename; + const char* filename; unsigned __int16 knotCount; float knots[16][2]; }; @@ -2182,18 +2530,18 @@ namespace Game struct SpeakerMap { bool isDefault; - const char *name; + const char* name; MSSChannelMap channelMaps[2][2]; }; const struct snd_alias_t { - const char *aliasName; - const char *subtitle; - const char *secondaryAliasName; - const char *chainAliasName; - const char *mixerGroup; - SoundFile *soundFile; + const char* aliasName; + const char* subtitle; + const char* secondaryAliasName; + const char* chainAliasName; + const char* mixerGroup; + SoundFile* soundFile; int sequence; float volMin; float volMax; @@ -2208,23 +2556,36 @@ namespace Game float lfePercentage; float centerPercentage; int startDelay; - SndCurve *volumeFalloffCurve; + SndCurve* volumeFalloffCurve; float envelopMin; float envelopMax; float envelopPercentage; - SpeakerMap *speakerMap; + SpeakerMap* speakerMap; + }; + + struct Poly + { + float(*pts)[3]; + unsigned int ptCount; + }; + + enum SunShadowPartition + { + R_SUNSHADOW_NEAR = 0x0, + R_SUNSHADOW_FAR = 0x1, + R_SUNSHADOW_PARTITION_COUNT = 0x2, }; struct snd_alias_list_t { - const char *aliasName; - snd_alias_t *head; + const char* aliasName; + snd_alias_t* head; unsigned int count; }; struct cStaticModel_s { - XModel *xmodel; + XModel* xmodel; float origin[3]; float invScaledAxis[3][3]; Bounds absBounds; @@ -2232,14 +2593,14 @@ namespace Game struct ClipMaterial { - const char *name; + const char* name; int surfaceFlags; int contents; }; struct cNode_t { - cplane_s *plane; + cplane_s* plane; __int16 children[2]; }; @@ -2255,7 +2616,7 @@ namespace Game struct cLeafBrushNodeLeaf_t { - unsigned __int16 *brushes; + unsigned __int16* brushes; }; struct cLeafBrushNodeChildren_t @@ -2294,7 +2655,7 @@ namespace Game char borderCount; char firstVertSegment; int firstTri; - CollisionBorder *borders; + CollisionBorder* borders; }; union CollisionAabbTreeIndex @@ -2346,16 +2707,16 @@ namespace Game struct MapTriggers { unsigned int count; - TriggerModel *models; + TriggerModel* models; unsigned int hullCount; - TriggerHull *hulls; + TriggerHull* hulls; unsigned int slabCount; - TriggerSlab *slabs; + TriggerSlab* slabs; }; - struct /*__declspec(align(2))*/ Stage + struct Stage { - const char *name; + const char* name; float origin[3]; unsigned __int16 triggerIndex; char sunPrimaryLightIndex; @@ -2363,11 +2724,11 @@ namespace Game struct __declspec(align(4)) MapEnts { - const char *name; - const char *entityString; + const char* name; + const char* entityString; int numEntityChars; MapTriggers trigger; - Stage *stages; + Stage* stages; char stageCount; }; @@ -2467,28 +2828,28 @@ namespace Game struct FxElemMarkVisuals { - Material *materials[2]; + Material* materials[2]; }; union FxEffectDefRef { - FxEffectDef *handle; - const char *name; + FxEffectDef* handle; + const char* name; }; union FxElemVisuals { - const void *anonymous; - Material *material; - XModel *model; + const void* anonymous; + Material* material; + XModel* model; FxEffectDefRef effectDef; - const char *soundName; + const char* soundName; }; union FxElemDefVisuals { - FxElemMarkVisuals *markArray; - FxElemVisuals *array; + FxElemMarkVisuals* markArray; + FxElemVisuals* array; FxElemVisuals instance; }; @@ -2507,9 +2868,9 @@ namespace Game float invSplitArcDist; float invSplitTime; int vertCount; - FxTrailVertex *verts; + FxTrailVertex* verts; int indCount; - unsigned __int16 *inds; + unsigned __int16* inds; }; struct FxSparkFountainDef @@ -2531,9 +2892,9 @@ namespace Game union FxElemExtendedDefPtr { - FxTrailDef *trailDef; - FxSparkFountainDef *sparkFountainDef; - void *unknownDef; + FxTrailDef* trailDef; + FxSparkFountainDef* sparkFountainDef; + void* unknownDef; }; const struct FxElemDef @@ -2559,8 +2920,8 @@ namespace Game char visualCount; char velIntervalCount; char visStateIntervalCount; - FxElemVelStateSample *velSamples; - FxElemVisStateSample *visSamples; + FxElemVelStateSample* velSamples; + FxElemVisStateSample* visSamples; FxElemDefVisuals visuals; Bounds collBounds; FxEffectDefRef effectOnImpact; @@ -2577,25 +2938,25 @@ namespace Game struct FxEffectDef { - const char *name; + const char* name; int flags; int totalSize; int msecLoopingLife; int elemDefCountLooping; int elemDefCountOneShot; int elemDefCountEmission; - FxElemDef *elemDefs; + FxElemDef* elemDefs; }; struct DynEntityDef { DynEntityType type; GfxPlacement pose; - XModel *xModel; + XModel* xModel; unsigned __int16 brushModel; unsigned __int16 physicsBrushModel; - FxEffectDef *destroyFx; - PhysPreset *physPreset; + FxEffectDef* destroyFx; + PhysPreset* physPreset; int health; PhysMass mass; int contents; @@ -2627,53 +2988,53 @@ namespace Game #pragma warning(disable: 4324) struct __declspec(align(64)) clipMap_t { - const char *name; + const char* name; int isInUse; int planeCount; - cplane_s *planes; + cplane_s* planes; unsigned int numStaticModels; - cStaticModel_s *staticModelList; + cStaticModel_s* staticModelList; unsigned int numMaterials; - ClipMaterial *materials; + ClipMaterial* materials; unsigned int numBrushSides; - cbrushside_t *brushsides; + cbrushside_t* brushsides; unsigned int numBrushEdges; - char *brushEdges; + char* brushEdges; unsigned int numNodes; - cNode_t *nodes; + cNode_t* nodes; unsigned int numLeafs; - cLeaf_t *leafs; + cLeaf_t* leafs; unsigned int leafbrushNodesCount; - cLeafBrushNode_s *leafbrushNodes; + cLeafBrushNode_s* leafbrushNodes; unsigned int numLeafBrushes; - unsigned __int16 *leafbrushes; + unsigned __int16* leafbrushes; unsigned int numLeafSurfaces; - unsigned int *leafsurfaces; + unsigned int* leafsurfaces; unsigned int vertCount; float(*verts)[3]; int triCount; - unsigned __int16 *triIndices; - char *triEdgeIsWalkable; + unsigned __int16* triIndices; + char* triEdgeIsWalkable; int borderCount; - CollisionBorder *borders; + CollisionBorder* borders; int partitionCount; - CollisionPartition *partitions; + CollisionPartition* partitions; int aabbTreeCount; - CollisionAabbTree *aabbTrees; + CollisionAabbTree* aabbTrees; unsigned int numSubModels; - cmodel_t *cmodels; + cmodel_t* cmodels; unsigned __int16 numBrushes; - cbrush_t *brushes; - Bounds *brushBounds; - int *brushContents; - MapEnts *mapEnts; + cbrush_t* brushes; + Bounds* brushBounds; + int* brushContents; + MapEnts* mapEnts; unsigned __int16 smodelNodeCount; - SModelAabbNode *smodelNodes; + SModelAabbNode* smodelNodes; unsigned __int16 dynEntCount[2]; - DynEntityDef *dynEntDefList[2]; - DynEntityPose *dynEntPoseList[2]; - DynEntityClient *dynEntClientList[2]; - DynEntityColl *dynEntCollList[2]; + DynEntityDef* dynEntDefList[2]; + DynEntityPose* dynEntPoseList[2]; + DynEntityClient* dynEntClientList[2]; + DynEntityColl* dynEntCollList[2]; unsigned int checksum; }; #pragma warning(pop) @@ -2693,15 +3054,15 @@ namespace Game float cosHalfFovExpanded; float rotationLimit; float translationLimit; - const char *defName; + const char* defName; }; struct ComWorld { - const char *name; + const char* name; int isInUse; unsigned int primaryLightCount; - ComPrimaryLight *primaryLights; + ComPrimaryLight* primaryLights; }; enum nodeType @@ -2777,12 +3138,12 @@ namespace Game $23305223CFD097B6F79557BDD2047E6C ___u12; __int16 wOverlapNode[2]; unsigned __int16 totalLinkCount; - pathlink_s *Links; + pathlink_s* Links; }; struct pathnode_dynamic_t { - void *pOwner; + void* pOwner; int iFreeTime; int iValidTime[3]; int dangerousNodeTime[3]; @@ -2803,9 +3164,9 @@ namespace Game struct pathnode_transient_t { int iSearchFrame; - pathnode_t *pNextOpen; - pathnode_t *pPrevOpen; - pathnode_t *pParent; + pathnode_t* pNextOpen; + pathnode_t* pPrevOpen; + pathnode_t* pParent; float fCost; float fHeuristic; $73F238679C0419BE2C31C6559E8604FC ___u6; @@ -2827,12 +3188,12 @@ namespace Game struct pathnode_tree_nodes_t { int nodeCount; - unsigned __int16 *nodes; + unsigned __int16* nodes; }; union pathnode_tree_info_t { - pathnode_tree_t *child[2]; + pathnode_tree_t* child[2]; pathnode_tree_nodes_t s; }; @@ -2846,15 +3207,15 @@ namespace Game struct PathData { unsigned int nodeCount; - pathnode_t *nodes; - pathbasenode_t *basenodes; + pathnode_t* nodes; + pathbasenode_t* basenodes; unsigned int chainNodeCount; - unsigned __int16 *chainNodeForNode; - unsigned __int16 *nodeForChainNode; + unsigned __int16* chainNodeForNode; + unsigned __int16* nodeForChainNode; int visBytes; - char *pathVis; + char* pathVis; int nodeTreeCount; - pathnode_tree_t *nodeTree; + pathnode_tree_t* nodeTree; }; struct VehicleTrackObstacle @@ -2875,18 +3236,18 @@ namespace Game float sectorWidth; float totalPriorLength; float totalFollowingLength; - VehicleTrackObstacle *obstacles; + VehicleTrackObstacle* obstacles; unsigned int obstacleCount; }; struct VehicleTrackSegment { - const char *targetName; - VehicleTrackSector *sectors; + const char* targetName; + VehicleTrackSector* sectors; unsigned int sectorCount; - VehicleTrackSegment **nextBranches; + VehicleTrackSegment** nextBranches; unsigned int nextBranchesCount; - VehicleTrackSegment **prevBranches; + VehicleTrackSegment** prevBranches; unsigned int prevBranchesCount; float endEdgeDir[2]; float endEdgeDist; @@ -2895,11 +3256,11 @@ namespace Game struct VehicleTrack { - VehicleTrackSegment *segments; + VehicleTrackSegment* segments; unsigned int segmentCount; }; - struct /*__declspec(align(2))*/ G_GlassPiece + struct G_GlassPiece { unsigned __int16 damageTaken; unsigned __int16 collapseTime; @@ -2910,35 +3271,35 @@ namespace Game struct G_GlassName { - char *nameStr; + char* nameStr; unsigned __int16 name; unsigned __int16 pieceCount; - unsigned __int16 *pieceIndices; + unsigned __int16* pieceIndices; }; struct G_GlassData { - G_GlassPiece *glassPieces; + G_GlassPiece* glassPieces; unsigned int pieceCount; unsigned __int16 damageToWeaken; unsigned __int16 damageToDestroy; unsigned int glassNameCount; - G_GlassName *glassNames; + G_GlassName* glassNames; char pad[108]; }; struct GameWorldSp { - const char *name; + const char* name; PathData path; VehicleTrack vehicleTrack; - G_GlassData *g_glassData; + G_GlassData* g_glassData; }; struct GameWorldMp { - const char *name; - G_GlassData *g_glassData; + const char* name; + G_GlassData* g_glassData; }; struct FxGlassDef @@ -2946,9 +3307,9 @@ namespace Game float halfThickness; float texVecs[2][2]; GfxColor color; - Material *material; - Material *materialShattered; - PhysPreset *physPreset; + Material* material; + Material* materialShattered; + PhysPreset* physPreset; }; struct FxSpatialFrame @@ -3050,19 +3411,19 @@ namespace Game unsigned int geoDataLimit; unsigned int geoDataCount; unsigned int initGeoDataCount; - FxGlassDef *defs; - FxGlassPiecePlace *piecePlaces; - FxGlassPieceState *pieceStates; - FxGlassPieceDynamics *pieceDynamics; - FxGlassGeometryData *geoData; - unsigned int *isInUse; - unsigned int *cellBits; - char *visData; + FxGlassDef* defs; + FxGlassPiecePlace* piecePlaces; + FxGlassPieceState* pieceStates; + FxGlassPieceDynamics* pieceDynamics; + FxGlassGeometryData* geoData; + unsigned int* isInUse; + unsigned int* cellBits; + char* visData; float(*linkOrg)[3]; - float *halfThickness; - unsigned __int16 *lightingHandles; - FxGlassInitPieceState *initPieceStates; - FxGlassGeometryData *initGeoData; + float* halfThickness; + unsigned __int16* lightingHandles; + FxGlassInitPieceState* initPieceStates; + FxGlassGeometryData* initGeoData; bool needToCompactData; char initCount; float effectChanceAccum; @@ -3071,24 +3432,24 @@ namespace Game struct FxWorld { - const char *name; + const char* name; FxGlassSystem glassSys; }; struct __declspec(align(4)) GfxSky { int skySurfCount; - int *skyStartSurfs; - GfxImage *skyImage; + int* skyStartSurfs; + GfxImage* skyImage; char skySamplerState; }; struct GfxWorldDpvsPlanes { int cellCount; - cplane_s *planes; - unsigned __int16 *nodes; - unsigned int *sceneEntCellBits; + cplane_s* planes; + unsigned __int16* nodes; + unsigned int* sceneEntCellBits; }; struct GfxCellTreeCount @@ -3105,13 +3466,13 @@ namespace Game unsigned __int16 surfaceCountNoDecal; unsigned __int16 startSurfIndexNoDecal; unsigned __int16 smodelIndexCount; - unsigned __int16 *smodelIndexes; + unsigned __int16* smodelIndexes; int childrenOffset; }; struct GfxCellTree { - GfxAabbTree *aabbTree; + GfxAabbTree* aabbTree; }; struct GfxPortalWritable @@ -3121,7 +3482,7 @@ namespace Game char recursionDepth; char hullPointCount; float(*hullPoints)[2]; - GfxPortal *queuedParent; + GfxPortal* queuedParent; }; struct DpvsPlane @@ -3143,9 +3504,9 @@ namespace Game { Bounds bounds; int portalCount; - GfxPortal *portals; + GfxPortal* portals; char reflectionProbeCount; - char *reflectionProbes; + char* reflectionProbes; }; struct GfxReflectionProbe @@ -3155,8 +3516,8 @@ namespace Game struct GfxLightmapArray { - GfxImage *primary; - GfxImage *secondary; + GfxImage* primary; + GfxImage* secondary; }; struct GfxWorldVertex @@ -3172,34 +3533,34 @@ namespace Game struct GfxWorldVertexData { - GfxWorldVertex *vertices; - IDirect3DVertexBuffer9 *worldVb; + GfxWorldVertex* vertices; + IDirect3DVertexBuffer9* worldVb; }; struct GfxWorldVertexLayerData { - char *data; - IDirect3DVertexBuffer9 *layerVb; + char* data; + IDirect3DVertexBuffer9* layerVb; }; struct GfxWorldDraw { unsigned int reflectionProbeCount; - GfxImage **reflectionProbes; - GfxReflectionProbe *reflectionProbeOrigins; - GfxTexture *reflectionProbeTextures; + GfxImage** reflectionProbes; + GfxReflectionProbe* reflectionProbeOrigins; + GfxTexture* reflectionProbeTextures; int lightmapCount; - GfxLightmapArray *lightmaps; - GfxTexture *lightmapPrimaryTextures; - GfxTexture *lightmapSecondaryTextures; - GfxImage *lightmapOverridePrimary; - GfxImage *lightmapOverrideSecondary; + GfxLightmapArray* lightmaps; + GfxTexture* lightmapPrimaryTextures; + GfxTexture* lightmapSecondaryTextures; + GfxImage* lightmapOverridePrimary; + GfxImage* lightmapOverrideSecondary; unsigned int vertexCount; GfxWorldVertexData vd; unsigned int vertexLayerDataSize; GfxWorldVertexLayerData vld; unsigned int indexCount; - unsigned __int16 *indices; + unsigned __int16* indices; }; struct GfxLightGridEntry @@ -3222,13 +3583,20 @@ namespace Game unsigned __int16 maxs[3]; unsigned int rowAxis; unsigned int colAxis; - unsigned __int16 *rowDataStart; + unsigned __int16* rowDataStart; unsigned int rawRowDataSize; - char *rawRowData; + char* rawRowData; unsigned int entryCount; - GfxLightGridEntry *entries; + GfxLightGridEntry* entries; unsigned int colorCount; - GfxLightGridColors *colors; + GfxLightGridColors* colors; + }; + + struct GfxSModelSurfVisDataHeader + { + XSurface* surfs; + unsigned __int16 smodelCount; + unsigned __int16 smodelIndexes[1]; }; struct GfxBrushModelWritable @@ -3248,15 +3616,15 @@ namespace Game struct MaterialMemory { - Material *material; + Material* material; int memory; }; struct sunflare_t { bool hasValidData; - Material *spriteMaterial; - Material *flareMaterial; + Material* spriteMaterial; + Material* flareMaterial; float spriteSize; float flareMinSize; float flareMinDot; @@ -3306,8 +3674,8 @@ namespace Game { unsigned __int16 surfaceCount; unsigned __int16 smodelCount; - unsigned __int16 *sortedSurfIndex; - unsigned __int16 *smodelIndex; + unsigned __int16* sortedSurfIndex; + unsigned __int16* smodelIndex; }; struct GfxLightRegionAxis @@ -3322,13 +3690,13 @@ namespace Game float kdopMidPoint[9]; float kdopHalfSize[9]; unsigned int axisCount; - GfxLightRegionAxis *axis; + GfxLightRegionAxis* axis; }; struct GfxLightRegion { unsigned int hullCount; - GfxLightRegionHull *hulls; + GfxLightRegionHull* hulls; }; struct GfxStaticModelInst @@ -3348,10 +3716,10 @@ namespace Game struct GfxSurfaceLightingAndFlagsFields { - char lightmapIndex; - char reflectionProbeIndex; - char primaryLightIndex; - char flags; + unsigned char lightmapIndex; + unsigned char reflectionProbeIndex; + unsigned char primaryLightIndex; + unsigned char flags; }; union GfxSurfaceLightingAndFlags @@ -3363,7 +3731,7 @@ namespace Game struct GfxSurface { srfTriangles_t tris; - Material *material; + Material* material; GfxSurfaceLightingAndFlags laf; }; @@ -3382,13 +3750,13 @@ namespace Game struct GfxStaticModelDrawInst { GfxPackedPlacement placement; - XModel *model; + XModel* model; unsigned __int16 cullDist; unsigned __int16 lightingHandle; - char reflectionProbeIndex; - char primaryLightIndex; - char flags; - char firstMtlSkinIndex; + unsigned char reflectionProbeIndex; + unsigned char primaryLightIndex; + unsigned char flags; + unsigned char firstMtlSkinIndex; GfxColor groundLighting; unsigned __int16 cacheId[4]; }; @@ -3408,24 +3776,93 @@ namespace Game unsigned int emissiveSurfsEnd; unsigned int smodelVisDataCount; unsigned int surfaceVisDataCount; - char *smodelVisData[3]; - char *surfaceVisData[3]; - unsigned __int16 *sortedSurfIndex; - GfxStaticModelInst *smodelInsts; - GfxSurface *surfaces; - GfxSurfaceBounds *surfacesBounds; - GfxStaticModelDrawInst *smodelDrawInsts; - GfxDrawSurf *surfaceMaterials; - unsigned int *surfaceCastsSunShadow; + unsigned char* smodelVisData[3]; + unsigned char* surfaceVisData[3]; + unsigned __int16* sortedSurfIndex; + GfxStaticModelInst* smodelInsts; + GfxSurface* surfaces; + GfxSurfaceBounds* surfacesBounds; + GfxStaticModelDrawInst* smodelDrawInsts; + GfxDrawSurf* surfaceMaterials; + unsigned int* surfaceCastsSunShadow; volatile int usageCount; }; + struct GfxSModelSurfHeaderFields + { + char reflectionProbeIndex; + char sceneLightIndex; + unsigned __int16 materialSortedIndex : 12; + unsigned __int16 visDataRefCountLessOne : 4; + }; + + union GfxSModelSurfHeader + { + GfxSModelSurfHeaderFields fields; + unsigned int packed; + unsigned __int16 array[2]; + }; + + struct GfxSModelSurfVisDataRefFields + { + unsigned __int16 surfIndex : 4; + unsigned __int16 visDataIndexPacked : 12; + }; + + union GfxSModelSurfVisDataRef + { + GfxSModelSurfVisDataRefFields fields; + unsigned __int16 packed; + }; + + struct GfxSModelSurf + { + GfxSModelSurfHeader header; + GfxSModelSurfVisDataRef visDataRefs[1]; + }; + + struct GfxSModelSurfDelaySortFields + { + unsigned __int16 unused; + GfxSModelSurfVisDataRef visDataRef; + GfxSModelSurfHeader header; + }; + + union GfxSModelSurfDelaySort + { + GfxSModelSurfDelaySortFields fields; + unsigned __int64 packed; + }; + + struct GfxSModelSurfBuildList + { + GfxSModelSurfHeader lastSurfHeader; + GfxSModelSurf* lastSModelSurf; + unsigned int visDataRefCount; + char* surfDataBegin; + char* surfDataPos; + char* surfDataEnd; + GfxSModelSurfHeader minDelaySortKey; + GfxSModelSurfDelaySort* delaySurfList; + unsigned int delaySurfCount; + unsigned int delaySurfLimit; + }; + + struct GfxSModelSurfDrawData + { + unsigned int shadowCasterMaterialIndex; + char* visData; + unsigned int visDataUsed; + unsigned int visDataLimit; + GfxSModelSurfBuildList buildList[4][4]; + }; + struct GfxWorldDpvsDynamic { unsigned int dynEntClientWordCount[2]; unsigned int dynEntClientCount[2]; - unsigned int *dynEntCellBits[2]; - char *dynEntVisData[2][3]; + unsigned int* dynEntCellBits[2]; + char* dynEntVisData[2][3]; }; struct GfxHeroOnlyLight @@ -3443,13 +3880,13 @@ namespace Game struct __declspec(align(4)) GfxWorld { - const char *name; - const char *baseName; + const char* name; + const char* baseName; int planeCount; int nodeCount; unsigned int surfaceCount; int skyCount; - GfxSky *skies; + GfxSky* skies; unsigned int lastSunPrimaryLightIndex; unsigned int primaryLightCount; unsigned int sortKeyLitDecal; @@ -3457,46 +3894,46 @@ namespace Game unsigned int sortKeyEffectAuto; unsigned int sortKeyDistortion; GfxWorldDpvsPlanes dpvsPlanes; - GfxCellTreeCount *aabbTreeCounts; - GfxCellTree *aabbTrees; - GfxCell *cells; + GfxCellTreeCount* aabbTreeCounts; + GfxCellTree* aabbTrees; + GfxCell* cells; GfxWorldDraw draw; GfxLightGrid lightGrid; int modelCount; - GfxBrushModel *models; + GfxBrushModel* models; Bounds bounds; unsigned int checksum; int materialMemoryCount; - MaterialMemory *materialMemory; + MaterialMemory* materialMemory; sunflare_t sun; float outdoorLookupMatrix[4][4]; - GfxImage *outdoorImage; - unsigned int *cellCasterBits; - unsigned int *cellHasSunLitSurfsBits; - GfxSceneDynModel *sceneDynModel; - GfxSceneDynBrush *sceneDynBrush; - unsigned int *primaryLightEntityShadowVis; - unsigned int *primaryLightDynEntShadowVis[2]; - char *nonSunPrimaryLightForModelDynEnt; - GfxShadowGeometry *shadowGeom; - GfxLightRegion *lightRegion; + GfxImage* outdoorImage; + unsigned int* cellCasterBits; + unsigned int* cellHasSunLitSurfsBits; + GfxSceneDynModel* sceneDynModel; + GfxSceneDynBrush* sceneDynBrush; + unsigned int* primaryLightEntityShadowVis; + unsigned int* primaryLightDynEntShadowVis[2]; + char* nonSunPrimaryLightForModelDynEnt; + GfxShadowGeometry* shadowGeom; + GfxLightRegion* lightRegion; GfxWorldDpvsStatic dpvs; GfxWorldDpvsDynamic dpvsDyn; unsigned int mapVtxChecksum; unsigned int heroOnlyLightCount; - GfxHeroOnlyLight *heroOnlyLights; + GfxHeroOnlyLight* heroOnlyLights; char fogTypesAllowed; }; struct __declspec(align(4)) GfxLightImage { - GfxImage *image; + GfxImage* image; char samplerState; }; struct GfxLightDef { - const char *name; + const char* name; GfxLightImage attenuation; int lmapLookupStart; }; @@ -3517,15 +3954,15 @@ namespace Game struct Font_s { - const char *fontName; + const char* fontName; int pixelHeight; int glyphCount; - Material *material; - Material *glowMaterial; - Glyph *glyphs; + Material* material; + Material* glowMaterial; + Glyph* glyphs; }; - struct __declspec(align(4)) rectDef_s + struct rectDef_s { float x; float y; @@ -3535,12 +3972,14 @@ namespace Game char vertAlign; }; + static_assert(sizeof(rectDef_s) == 0x14); + struct windowDef_t { - const char *name; + const char* name; rectDef_s rect; rectDef_s rectClient; - const char *group; + const char* group; int style; int border; int ownerDraw; @@ -3554,9 +3993,11 @@ namespace Game float borderColor[4]; float outlineColor[4]; float disableColor[4]; - Material *background; + Material* background; }; + static_assert(sizeof(windowDef_t) == 0xA4); + enum expDataType { VAL_INT = 0x0, @@ -3569,7 +4010,7 @@ namespace Game struct ExpressionString { - const char *string; + const char* string; }; union operandInternalDataUnion @@ -3577,7 +4018,7 @@ namespace Game int intVal; float floatVal; ExpressionString stringVal; - Statement_s *function; + Statement_s* function; }; struct Operand @@ -3785,7 +4226,7 @@ namespace Game struct UIFunctionList { int totalFunctions; - Statement_s **functions; + Statement_s** functions; }; union DvarValue @@ -3840,7 +4281,7 @@ namespace Game DvarValue latched; DvarValue reset; DvarLimits domain; - bool(__cdecl * domainFunc)(dvar_t*, DvarValue); + bool(__cdecl* domainFunc)(dvar_t*, DvarValue); dvar_t* hashNext; }; @@ -3874,30 +4315,30 @@ namespace Game struct Statement_s { int numEntries; - expressionEntry *entries; - ExpressionSupportingData *supportingData; + expressionEntry* entries; + ExpressionSupportingData* supportingData; int lastExecuteTime; Operand lastResult; }; struct ConditionalScript { - MenuEventHandlerSet *eventHandlerSet; - Statement_s *eventExpression; + MenuEventHandlerSet* eventHandlerSet; + Statement_s* eventExpression; }; struct SetLocalVarData { - const char *localVarName; - Statement_s *expression; + const char* localVarName; + Statement_s* expression; }; union EventData { - const char *unconditionalScript; - ConditionalScript *conditionalScript; - MenuEventHandlerSet *elseScript; - SetLocalVarData *setLocalVarData; + const char* unconditionalScript; + ConditionalScript* conditionalScript; + MenuEventHandlerSet* elseScript; + SetLocalVarData* setLocalVarData; }; struct __declspec(align(4)) MenuEventHandler @@ -3909,14 +4350,14 @@ namespace Game struct MenuEventHandlerSet { int eventHandlerCount; - MenuEventHandler **eventHandlers; + MenuEventHandler** eventHandlers; }; struct ItemKeyHandler { int key; - MenuEventHandlerSet *action; - ItemKeyHandler *next; + MenuEventHandlerSet* action; + ItemKeyHandler* next; }; struct columnInfo_s @@ -3938,12 +4379,12 @@ namespace Game int elementStyle; int numColumns; columnInfo_s columnInfo[16]; - MenuEventHandlerSet *onDoubleClick; + MenuEventHandlerSet* onDoubleClick; int notselectable; int noScrollBars; int usePaging; float selectBorder[4]; - Material *selectIcon; + Material* selectIcon; }; struct editFieldDef_s @@ -3960,8 +4401,8 @@ namespace Game struct multiDef_s { - const char *dvarList[32]; - const char *dvarStr[32]; + const char* dvarList[32]; + const char* dvarStr[32]; float dvarValue[32]; int count; int strDef; @@ -3985,19 +4426,19 @@ namespace Game union itemDefData_t { - listBoxDef_s *listBox; - editFieldDef_s *editField; - multiDef_s *multi; - const char *enumDvarName; - newsTickerDef_s *ticker; - textScrollDef_s *scroll; - void *data; + listBoxDef_s* listBox; + editFieldDef_s* editField; + multiDef_s* multi; + const char* enumDvarName; + newsTickerDef_s* ticker; + textScrollDef_s* scroll; + void* data; }; struct ItemFloatExpression { int target; - Statement_s *expression; + Statement_s* expression; }; struct itemDef_s @@ -4015,34 +4456,34 @@ namespace Game int textStyle; int gameMsgWindowIndex; int gameMsgWindowMode; - const char *text; + const char* text; int itemFlags; - menuDef_t *parent; - MenuEventHandlerSet *mouseEnterText; - MenuEventHandlerSet *mouseExitText; - MenuEventHandlerSet *mouseEnter; - MenuEventHandlerSet *mouseExit; - MenuEventHandlerSet *action; - MenuEventHandlerSet *accept; - MenuEventHandlerSet *onFocus; - MenuEventHandlerSet *leaveFocus; - const char *dvar; - const char *dvarTest; - ItemKeyHandler *onKey; - const char *enableDvar; - const char *localVar; + menuDef_t* parent; + MenuEventHandlerSet* mouseEnterText; + MenuEventHandlerSet* mouseExitText; + MenuEventHandlerSet* mouseEnter; + MenuEventHandlerSet* mouseExit; + MenuEventHandlerSet* action; + MenuEventHandlerSet* accept; + MenuEventHandlerSet* onFocus; + MenuEventHandlerSet* leaveFocus; + const char* dvar; + const char* dvarTest; + ItemKeyHandler* onKey; + const char* enableDvar; + const char* localVar; int dvarFlags; - snd_alias_list_t *focusSound; + snd_alias_list_t* focusSound; float special; int cursorPos[1]; itemDefData_t typeData; int imageTrack; int floatExpressionCount; - ItemFloatExpression *floatExpressions; - Statement_s *visibleExp; - Statement_s *disabledExp; - Statement_s *textExp; - Statement_s *materialExp; + ItemFloatExpression* floatExpressions; + Statement_s* visibleExp; + Statement_s* disabledExp; + Statement_s* textExp; + Statement_s* materialExp; float glowColor[4]; bool decayActive; int fxBirthTime; @@ -4256,8 +4697,8 @@ namespace Game struct TracerDef { - const char *name; - Material *material; + const char* name; + Material* material; unsigned int drawInterval; float speed; float beamLength; @@ -4269,16 +4710,16 @@ namespace Game struct __declspec(align(4)) WeaponDef { - const char *szOverlayName; - XModel **gunXModel; - XModel *handXModel; - const char **szXAnimsRightHanded; - const char **szXAnimsLeftHanded; - const char *szModeName; - unsigned __int16 *notetrackSoundMapKeys; - unsigned __int16 *notetrackSoundMapValues; - unsigned __int16 *notetrackRumbleMapKeys; - unsigned __int16 *notetrackRumbleMapValues; + const char* szOverlayName; + XModel** gunXModel; + XModel* handXModel; + const char** szXAnimsRightHanded; + const char** szXAnimsLeftHanded; + const char* szModeName; + unsigned __int16* notetrackSoundMapKeys; + unsigned __int16* notetrackSoundMapValues; + unsigned __int16* notetrackRumbleMapKeys; + unsigned __int16* notetrackRumbleMapValues; int playerAnimType; weapType_t weapType; weapClass_t weapClass; @@ -4287,62 +4728,62 @@ namespace Game weapFireType_t fireType; OffhandClass offhandClass; weapStance_t stance; - FxEffectDef *viewFlashEffect; - FxEffectDef *worldFlashEffect; - snd_alias_list_t *pickupSound; - snd_alias_list_t *pickupSoundPlayer; - snd_alias_list_t *ammoPickupSound; - snd_alias_list_t *ammoPickupSoundPlayer; - snd_alias_list_t *projectileSound; - snd_alias_list_t *pullbackSound; - snd_alias_list_t *pullbackSoundPlayer; - snd_alias_list_t *fireSound; - snd_alias_list_t *fireSoundPlayer; - snd_alias_list_t *fireSoundPlayerAkimbo; - snd_alias_list_t *fireLoopSound; - snd_alias_list_t *fireLoopSoundPlayer; - snd_alias_list_t *fireStopSound; - snd_alias_list_t *fireStopSoundPlayer; - snd_alias_list_t *fireLastSound; - snd_alias_list_t *fireLastSoundPlayer; - snd_alias_list_t *emptyFireSound; - snd_alias_list_t *emptyFireSoundPlayer; - snd_alias_list_t *meleeSwipeSound; - snd_alias_list_t *meleeSwipeSoundPlayer; - snd_alias_list_t *meleeHitSound; - snd_alias_list_t *meleeMissSound; - snd_alias_list_t *rechamberSound; - snd_alias_list_t *rechamberSoundPlayer; - snd_alias_list_t *reloadSound; - snd_alias_list_t *reloadSoundPlayer; - snd_alias_list_t *reloadEmptySound; - snd_alias_list_t *reloadEmptySoundPlayer; - snd_alias_list_t *reloadStartSound; - snd_alias_list_t *reloadStartSoundPlayer; - snd_alias_list_t *reloadEndSound; - snd_alias_list_t *reloadEndSoundPlayer; - snd_alias_list_t *detonateSound; - snd_alias_list_t *detonateSoundPlayer; - snd_alias_list_t *nightVisionWearSound; - snd_alias_list_t *nightVisionWearSoundPlayer; - snd_alias_list_t *nightVisionRemoveSound; - snd_alias_list_t *nightVisionRemoveSoundPlayer; - snd_alias_list_t *altSwitchSound; - snd_alias_list_t *altSwitchSoundPlayer; - snd_alias_list_t *raiseSound; - snd_alias_list_t *raiseSoundPlayer; - snd_alias_list_t *firstRaiseSound; - snd_alias_list_t *firstRaiseSoundPlayer; - snd_alias_list_t *putawaySound; - snd_alias_list_t *putawaySoundPlayer; - snd_alias_list_t *scanSound; - snd_alias_list_t **bounceSound; - FxEffectDef *viewShellEjectEffect; - FxEffectDef *worldShellEjectEffect; - FxEffectDef *viewLastShotEjectEffect; - FxEffectDef *worldLastShotEjectEffect; - Material *reticleCenter; - Material *reticleSide; + FxEffectDef* viewFlashEffect; + FxEffectDef* worldFlashEffect; + snd_alias_list_t* pickupSound; + snd_alias_list_t* pickupSoundPlayer; + snd_alias_list_t* ammoPickupSound; + snd_alias_list_t* ammoPickupSoundPlayer; + snd_alias_list_t* projectileSound; + snd_alias_list_t* pullbackSound; + snd_alias_list_t* pullbackSoundPlayer; + snd_alias_list_t* fireSound; + snd_alias_list_t* fireSoundPlayer; + snd_alias_list_t* fireSoundPlayerAkimbo; + snd_alias_list_t* fireLoopSound; + snd_alias_list_t* fireLoopSoundPlayer; + snd_alias_list_t* fireStopSound; + snd_alias_list_t* fireStopSoundPlayer; + snd_alias_list_t* fireLastSound; + snd_alias_list_t* fireLastSoundPlayer; + snd_alias_list_t* emptyFireSound; + snd_alias_list_t* emptyFireSoundPlayer; + snd_alias_list_t* meleeSwipeSound; + snd_alias_list_t* meleeSwipeSoundPlayer; + snd_alias_list_t* meleeHitSound; + snd_alias_list_t* meleeMissSound; + snd_alias_list_t* rechamberSound; + snd_alias_list_t* rechamberSoundPlayer; + snd_alias_list_t* reloadSound; + snd_alias_list_t* reloadSoundPlayer; + snd_alias_list_t* reloadEmptySound; + snd_alias_list_t* reloadEmptySoundPlayer; + snd_alias_list_t* reloadStartSound; + snd_alias_list_t* reloadStartSoundPlayer; + snd_alias_list_t* reloadEndSound; + snd_alias_list_t* reloadEndSoundPlayer; + snd_alias_list_t* detonateSound; + snd_alias_list_t* detonateSoundPlayer; + snd_alias_list_t* nightVisionWearSound; + snd_alias_list_t* nightVisionWearSoundPlayer; + snd_alias_list_t* nightVisionRemoveSound; + snd_alias_list_t* nightVisionRemoveSoundPlayer; + snd_alias_list_t* altSwitchSound; + snd_alias_list_t* altSwitchSoundPlayer; + snd_alias_list_t* raiseSound; + snd_alias_list_t* raiseSoundPlayer; + snd_alias_list_t* firstRaiseSound; + snd_alias_list_t* firstRaiseSoundPlayer; + snd_alias_list_t* putawaySound; + snd_alias_list_t* putawaySoundPlayer; + snd_alias_list_t* scanSound; + snd_alias_list_t** bounceSound; + FxEffectDef* viewShellEjectEffect; + FxEffectDef* worldShellEjectEffect; + FxEffectDef* viewLastShotEjectEffect; + FxEffectDef* worldLastShotEjectEffect; + Material* reticleCenter; + Material* reticleSide; int iReticleCenterSize; int iReticleSideSize; int iReticleMinOfs; @@ -4367,26 +4808,26 @@ namespace Game float fStandRotMinSpeed; float fDuckedRotMinSpeed; float fProneRotMinSpeed; - XModel **worldModel; - XModel *worldClipModel; - XModel *rocketModel; - XModel *knifeModel; - XModel *worldKnifeModel; - Material *hudIcon; + XModel** worldModel; + XModel* worldClipModel; + XModel* rocketModel; + XModel* knifeModel; + XModel* worldKnifeModel; + Material* hudIcon; weaponIconRatioType_t hudIconRatio; - Material *pickupIcon; + Material* pickupIcon; weaponIconRatioType_t pickupIconRatio; - Material *ammoCounterIcon; + Material* ammoCounterIcon; weaponIconRatioType_t ammoCounterIconRatio; ammoCounterClipType_t ammoCounterClip; int iStartAmmo; - const char *szAmmoName; + const char* szAmmoName; int iAmmoIndex; - const char *szClipName; + const char* szClipName; int iClipIndex; int iMaxAmmo; int shotCount; - const char *szSharedAmmoCapName; + const char* szSharedAmmoCapName; int iSharedAmmoCapIndex; int iSharedAmmoCap; int damage; @@ -4443,10 +4884,10 @@ namespace Game float sprintDurationScale; float fAdsZoomInFrac; float fAdsZoomOutFrac; - Material *overlayMaterial; - Material *overlayMaterialLowRes; - Material *overlayMaterialEMP; - Material *overlayMaterialEMPLowRes; + Material* overlayMaterial; + Material* overlayMaterialLowRes; + Material* overlayMaterialEMP; + Material* overlayMaterialEMPLowRes; weapOverlayReticle_t overlayReticle; WeapOverlayInteface_t overlayInterface; float overlayWidth; @@ -4491,7 +4932,7 @@ namespace Game float adsSwayVertScale; float adsViewErrorMin; float adsViewErrorMax; - PhysCollmap *physCollmap; + PhysCollmap* physCollmap; float dualWieldViewModelOffset; weaponIconRatioType_t killIconRatio; int iReloadAmmoAdd; @@ -4513,25 +4954,25 @@ namespace Game float projLifetime; float timeToAccelerate; float projectileCurvature; - XModel *projectileModel; + XModel* projectileModel; weapProjExposion_t projExplosion; - FxEffectDef *projExplosionEffect; - FxEffectDef *projDudEffect; - snd_alias_list_t *projExplosionSound; - snd_alias_list_t *projDudSound; + FxEffectDef* projExplosionEffect; + FxEffectDef* projDudEffect; + snd_alias_list_t* projExplosionSound; + snd_alias_list_t* projDudSound; WeapStickinessType stickiness; float lowAmmoWarningThreshold; float ricochetChance; - float *parallelBounce; - float *perpendicularBounce; - FxEffectDef *projTrailEffect; - FxEffectDef *projBeaconEffect; + float* parallelBounce; + float* perpendicularBounce; + FxEffectDef* projTrailEffect; + FxEffectDef* projBeaconEffect; float vProjectileColor[3]; guidedMissileType_t guidedMissileType; float maxSteeringAccel; int projIgnitionDelay; - FxEffectDef *projIgnitionEffect; - snd_alias_list_t *projIgnitionSound; + FxEffectDef* projIgnitionEffect; + snd_alias_list_t* projIgnitionSound; float fAdsAimPitch; float fAdsCrosshairInFrac; float fAdsCrosshairOutFrac; @@ -4570,7 +5011,7 @@ namespace Game float fHipViewScatterMax; float fightDist; float maxDist; - const char *accuracyGraphName[2]; + const char* accuracyGraphName[2]; float(*originalAccuracyGraphKnots[2])[2]; unsigned __int16 originalAccuracyGraphKnotCount[2]; int iPositionReloadTransTime; @@ -4589,8 +5030,8 @@ namespace Game float maxRange; float fAnimHorRotateInc; float fPlayerPositionDist; - const char *szUseHintString; - const char *dropHintString; + const char* szUseHintString; + const char* dropHintString; int iUseHintStringIndex; int dropHintStringIndex; float horizViewJitter; @@ -4598,7 +5039,7 @@ namespace Game float scanSpeed; float scanAccel; int scanPauseTime; - const char *szScript; + const char* szScript; float fOOPosAnimLength[2]; int minDamage; int minPlayerDamage; @@ -4607,27 +5048,27 @@ namespace Game float destabilizationRateTime; float destabilizationCurvatureMax; int destabilizeDistance; - float *locationDamageMultipliers; - const char *fireRumble; - const char *meleeImpactRumble; - TracerDef *tracerType; + float* locationDamageMultipliers; + const char* fireRumble; + const char* meleeImpactRumble; + TracerDef* tracerType; float turretScopeZoomRate; float turretScopeZoomMin; float turretScopeZoomMax; float turretOverheatUpRate; float turretOverheatDownRate; float turretOverheatPenalty; - snd_alias_list_t *turretOverheatSound; - FxEffectDef *turretOverheatEffect; - const char *turretBarrelSpinRumble; + snd_alias_list_t* turretOverheatSound; + FxEffectDef* turretOverheatEffect; + const char* turretBarrelSpinRumble; float turretBarrelSpinSpeed; float turretBarrelSpinUpTime; float turretBarrelSpinDownTime; - snd_alias_list_t *turretBarrelSpinMaxSnd; - snd_alias_list_t *turretBarrelSpinUpSnd[4]; - snd_alias_list_t *turretBarrelSpinDownSnd[4]; - snd_alias_list_t *missileConeSoundAlias; - snd_alias_list_t *missileConeSoundAliasAtBase; + snd_alias_list_t* turretBarrelSpinMaxSnd; + snd_alias_list_t* turretBarrelSpinUpSnd[4]; + snd_alias_list_t* turretBarrelSpinDownSnd[4]; + snd_alias_list_t* missileConeSoundAlias; + snd_alias_list_t* missileConeSoundAliasAtBase; float missileConeSoundRadiusAtTop; float missileConeSoundRadiusAtBase; float missileConeSoundHeight; @@ -4707,11 +5148,11 @@ namespace Game struct WeaponCompleteDef { - const char *szInternalName; - WeaponDef *weapDef; - const char *szDisplayName; - unsigned __int16 *hideTags; - const char **szXAnims; + const char* szInternalName; + WeaponDef* weapDef; + const char* szDisplayName; + unsigned __int16* hideTags; + const char** szXAnims; float fAdsZoomFov; int iAdsTransInTime; int iAdsTransOutTime; @@ -4722,11 +5163,11 @@ namespace Game float penetrateMultiplier; float fAdsViewKickCenterSpeed; float fHipViewKickCenterSpeed; - const char *szAltWeaponName; + const char* szAltWeaponName; unsigned int altWeaponIndex; int iAltRaiseTime; - Material *killIcon; - Material *dpadIcon; + Material* killIcon; + Material* dpadIcon; int fireAnimLength; int iFirstRaiseTime; int ammoDropStockMax; @@ -4741,41 +5182,41 @@ namespace Game struct SndDriverGlobals { - const char *name; + const char* name; }; struct FxImpactEntry { - FxEffectDef *nonflesh[31]; - FxEffectDef *flesh[4]; + FxEffectDef* nonflesh[31]; + FxEffectDef* flesh[4]; }; struct FxImpactTable { - const char *name; - FxImpactEntry *table; + const char* name; + FxImpactEntry* table; }; struct RawFile { - const char *name; + const char* name; int compressedLen; int len; - const char *buffer; + const char* buffer; }; struct StringTableCell { - const char *string; + const char* string; int hash; }; struct StringTable { - const char *name; + const char* name; int columnCount; int rowCount; - StringTableCell *values; + StringTableCell* values; }; enum LbColType @@ -4800,11 +5241,11 @@ namespace Game struct LbColumnDef { - const char *name; + const char* name; int id; int propertyId; bool hidden; - const char *statName; + const char* statName; LbColType type; int precision; LbAggType agg; @@ -4812,17 +5253,17 @@ namespace Game struct LeaderboardDef { - const char *name; + const char* name; int id; int columnCount; int xpColId; int prestigeColId; - LbColumnDef *columns; + LbColumnDef* columns; }; struct __declspec(align(4)) StructuredDataEnumEntry { - const char *string; + const char* string; unsigned __int16 index; }; @@ -4830,7 +5271,7 @@ namespace Game { int entryCount; int reservedEntryCount; - StructuredDataEnumEntry *entries; + StructuredDataEnumEntry* entries; }; enum StructuredDataTypeCategory @@ -4865,7 +5306,7 @@ namespace Game struct StructuredDataStructProperty { - const char *name; + const char* name; StructuredDataType type; unsigned int offset; }; @@ -4873,7 +5314,7 @@ namespace Game struct StructuredDataStruct { int propertyCount; - StructuredDataStructProperty *properties; + StructuredDataStructProperty* properties; int size; unsigned int bitOffset; }; @@ -4897,27 +5338,27 @@ namespace Game int version; unsigned int formatChecksum; int enumCount; - StructuredDataEnum *enums; + StructuredDataEnum* enums; int structCount; - StructuredDataStruct *structs; + StructuredDataStruct* structs; int indexedArrayCount; - StructuredDataIndexedArray *indexedArrays; + StructuredDataIndexedArray* indexedArrays; int enumedArrayCount; - StructuredDataEnumedArray *enumedArrays; + StructuredDataEnumedArray* enumedArrays; StructuredDataType rootType; unsigned int size; }; struct StructuredDataDefSet { - const char *name; + const char* name; unsigned int defCount; - StructuredDataDef *defs; + StructuredDataDef* defs; }; struct StructuredDataBuffer { - char *data; + char* data; unsigned int size; }; @@ -4944,9 +5385,9 @@ namespace Game struct VehiclePhysDef { int physicsEnabled; - const char *physPresetName; - PhysPreset *physPreset; - const char *accelGraphName; + const char* physPresetName; + PhysPreset* physPreset; + const char* accelGraphName; VehicleAxleType steeringAxle; VehicleAxleType powerAxle; VehicleAxleType brakingAxle; @@ -4992,9 +5433,9 @@ namespace Game struct VehicleDef { - const char *name; + const char* name; VehicleType type; - const char *useHintString; + const char* useHintString; int health; int quadBarrel; float texScrollScale; @@ -5048,49 +5489,49 @@ namespace Game float camFovIncrease; float camFovOffset; float camFovSpeed; - const char *turretWeaponName; - WeaponCompleteDef *turretWeapon; + const char* turretWeaponName; + WeaponCompleteDef* turretWeapon; float turretHorizSpanLeft; float turretHorizSpanRight; float turretVertSpanUp; float turretVertSpanDown; float turretRotRate; - snd_alias_list_t *turretSpinSnd; - snd_alias_list_t *turretStopSnd; + snd_alias_list_t* turretSpinSnd; + snd_alias_list_t* turretStopSnd; int trophyEnabled; float trophyRadius; float trophyInactiveRadius; int trophyAmmoCount; float trophyReloadTime; unsigned __int16 trophyTags[4]; - Material *compassFriendlyIcon; - Material *compassEnemyIcon; + Material* compassFriendlyIcon; + Material* compassEnemyIcon; int compassIconWidth; int compassIconHeight; - snd_alias_list_t *idleLowSnd; - snd_alias_list_t *idleHighSnd; - snd_alias_list_t *engineLowSnd; - snd_alias_list_t *engineHighSnd; + snd_alias_list_t* idleLowSnd; + snd_alias_list_t* idleHighSnd; + snd_alias_list_t* engineLowSnd; + snd_alias_list_t* engineHighSnd; float engineSndSpeed; - snd_alias_list_t *engineStartUpSnd; + snd_alias_list_t* engineStartUpSnd; int engineStartUpLength; - snd_alias_list_t *engineShutdownSnd; - snd_alias_list_t *engineIdleSnd; - snd_alias_list_t *engineSustainSnd; - snd_alias_list_t *engineRampUpSnd; + snd_alias_list_t* engineShutdownSnd; + snd_alias_list_t* engineIdleSnd; + snd_alias_list_t* engineSustainSnd; + snd_alias_list_t* engineRampUpSnd; int engineRampUpLength; - snd_alias_list_t *engineRampDownSnd; + snd_alias_list_t* engineRampDownSnd; int engineRampDownLength; - snd_alias_list_t *suspensionSoftSnd; + snd_alias_list_t* suspensionSoftSnd; float suspensionSoftCompression; - snd_alias_list_t *suspensionHardSnd; + snd_alias_list_t* suspensionHardSnd; float suspensionHardCompression; - snd_alias_list_t *collisionSnd; + snd_alias_list_t* collisionSnd; float collisionBlendSpeed; - snd_alias_list_t *speedSnd; + snd_alias_list_t* speedSnd; float speedSndBlendSpeed; - const char *surfaceSndPrefix; - snd_alias_list_t *surfaceSnds[31]; + const char* surfaceSndPrefix; + snd_alias_list_t* surfaceSnds[31]; float surfaceSndBlendSpeed; float slideVolume; float slideBlendSpeed; @@ -5099,55 +5540,54 @@ namespace Game struct AddonMapEnts { - const char *name; - char *entityString; + const char* name; + char* entityString; int numEntityChars; MapTriggers trigger; }; union XAssetHeader { - void *data; - PhysPreset *physPreset; - PhysCollmap *physCollmap; - XAnimParts *parts; - XModelSurfs *modelSurfs; - XModel *model; - Material *material; - MaterialPixelShader *pixelShader; - MaterialVertexShader *vertexShader; - MaterialVertexDeclaration *vertexDecl; - MaterialTechniqueSet *techniqueSet; - GfxImage *image; - snd_alias_list_t *sound; - SndCurve *sndCurve; - LoadedSound *loadSnd; - clipMap_t *clipMap; - ComWorld *comWorld; - GameWorldSp *gameWorldSp; - GameWorldMp *gameWorldMp; - MapEnts *mapEnts; - FxWorld *fxWorld; - GfxWorld *gfxWorld; - GfxLightDef *lightDef; - Font_s *font; - MenuList *menuList; - menuDef_t *menu; - LocalizeEntry *localize; - WeaponCompleteDef *weapon; - SndDriverGlobals *sndDriverGlobals; - FxEffectDef *fx; - FxImpactTable *impactFx; - RawFile *rawfile; - StringTable *stringTable; - LeaderboardDef *leaderboardDef; - StructuredDataDefSet *structuredDataDefSet; - TracerDef *tracerDef; - VehicleDef *vehDef; - AddonMapEnts *addonMapEnts; + void* data; + PhysPreset* physPreset; + PhysCollmap* physCollmap; + XAnimParts* parts; + XModelSurfs* modelSurfs; + XModel* model; + Material* material; + MaterialPixelShader* pixelShader; + MaterialVertexShader* vertexShader; + MaterialVertexDeclaration* vertexDecl; + MaterialTechniqueSet* techniqueSet; + GfxImage* image; + snd_alias_list_t* sound; + SndCurve* sndCurve; + LoadedSound* loadSnd; + clipMap_t* clipMap; + ComWorld* comWorld; + GameWorldSp* gameWorldSp; + GameWorldMp* gameWorldMp; + MapEnts* mapEnts; + FxWorld* fxWorld; + GfxWorld* gfxWorld; + GfxLightDef* lightDef; + Font_s* font; + MenuList* menuList; + menuDef_t* menu; + LocalizeEntry* localize; + WeaponCompleteDef* weapon; + SndDriverGlobals* sndDriverGlobals; + FxEffectDef* fx; + FxImpactTable* impactFx; + RawFile* rawfile; + StringTable* stringTable; + LeaderboardDef* leaderboardDef; + StructuredDataDefSet* structuredDataDefSet; + TracerDef* tracerDef; + VehicleDef* vehDef; + AddonMapEnts* addonMapEnts; }; - /* 9210 */ struct weaponParms { float forward[3]; @@ -5168,7 +5608,7 @@ namespace Game struct XBlock { - char *data; + char* data; unsigned int size; }; @@ -5241,14 +5681,14 @@ namespace Game struct ScriptStringList { int count; - const char **strings; + const char** strings; }; struct XAssetList { ScriptStringList stringList; int assetCount; - XAsset *assets; + XAsset* assets; }; struct ZoneHeader @@ -5260,10 +5700,10 @@ namespace Game struct XZoneMemory { XBlock blocks[MAX_XFILE_COUNT]; - char *lockedVertexData; - char *lockedIndexData; - void *vertexBuffer; - void *indexBuffer; + char* lockedVertexData; + char* lockedIndexData; + void* vertexBuffer; + void* indexBuffer; }; struct XZone @@ -5303,6 +5743,27 @@ namespace Game XNKEY keyExchangeKey; }; + struct GfxSunShadowMapMetrics + { + unsigned int pixelsPerTile; + unsigned int tilesPerTexture; + unsigned int usefulSize; + unsigned int minCoord; + float nearClip; + float farClip; + }; + + struct GfxSunShadowProjectionSetup + { + float sunAxis[3][3]; + float nearShadowMinDist; + float frustumRayDistToEdgeOfNearMap; + float shadowOrg[2]; + float shadowOrgPixelCenter[2]; + float snappedShadowOrg[2][2]; + float sampleSize[2]; + }; + struct mapArena_t { char uiName[32]; @@ -5343,8 +5804,8 @@ namespace Game struct fileInIwd_s { unsigned int pos; - char *name; - fileInIwd_s *next; + char* name; + fileInIwd_s* next; }; struct iwd_t @@ -5352,15 +5813,15 @@ namespace Game char iwdFilename[256]; char iwdBasename[256]; char iwdGamename[256]; - char *handle; + char* handle; int checksum; int pure_checksum; volatile int hasOpenFile; int numfiles; char referenced; unsigned int hashSize; - fileInIwd_s **hashTable; - fileInIwd_s *buildBuffer; + fileInIwd_s** hashTable; + fileInIwd_s* buildBuffer; }; #ifdef IDA @@ -5369,8 +5830,8 @@ namespace Game union qfile_gus { - _iobuf *o; - char *z; + _iobuf* o; + char* z; }; struct qfile_us @@ -5385,7 +5846,7 @@ namespace Game int handleSync; int fileSize; int zipFilePos; - iwd_t *zipFile; + iwd_t* zipFile; int streamed; char name[256]; }; @@ -5398,7 +5859,7 @@ namespace Game typedef struct searchpath_s { searchpath_s* next; - iwd_t *iwd; + iwd_t* iwd; directory_t* dir; int bLocalized; int ignore; @@ -5422,7 +5883,7 @@ namespace Game { bool spawnVarsValid; int numSpawnVars; - char *spawnVars[64][2]; + char* spawnVars[64][2]; int numSpawnVarChars; char spawnVarChars[2048]; }; @@ -5448,7 +5909,7 @@ namespace Game float radiusOffset[3]; float boundingRadius; /*TraceThreadInfo*/ int threadInfo; - /*CM_WorldTraceCallbacks*/ void *callbacks; + /*CM_WorldTraceCallbacks*/ void* callbacks; }; struct gameState_t @@ -5475,7 +5936,7 @@ namespace Game struct VariableStackBuffer { - const char *pos; + const char* pos; unsigned __int16 size; unsigned __int16 bufLen; unsigned __int16 localId; @@ -5533,10 +5994,10 @@ namespace Game unsigned int uintValue; float floatValue; unsigned int stringValue; - const float *vectorValue; - const char *codePosValue; + const float* vectorValue; + const char* codePosValue; unsigned int pointerValue; - VariableStackBuffer *stackValue; + VariableStackBuffer* stackValue; unsigned int entityOffset; }; @@ -5926,8 +6387,8 @@ namespace Game { int overflowed; int readOnly; - unsigned char *data; - unsigned char *splitData; + unsigned char* data; + unsigned char* splitData; int maxsize; int cursize; int splitSize; @@ -5938,7 +6399,7 @@ namespace Game struct XZoneInfo { - const char *name; + const char* name; int allocFlags; int freeFlags; }; @@ -5949,6 +6410,17 @@ namespace Game FS_LIST_ALL = 0x1, }; + enum svc_ops_e + { + svc_nop = 0x0, + svc_gamestate = 0x1, + svc_configstring = 0x2, + svc_serverCommand = 0x3, + svc_matchdata = 0x4, + svc_snapshot = 0x5, + svc_EOF = 0x6, + }; + enum netsrc_t { NS_CLIENT1 = 0x0, @@ -5971,7 +6443,7 @@ namespace Game typedef union { unsigned char bytes[4]; - DWORD full; + std::uint32_t full; } netIP_t; struct netadr_t @@ -6062,23 +6534,23 @@ namespace Game FxFloatRange elasticity; FxEditorElemAtlas atlas; float velScale[2][3]; - FxCurve *velShape[2][3][2]; + FxCurve* velShape[2][3][2]; float rotationScale; - FxCurve *rotationShape[2]; + FxCurve* rotationShape[2]; float sizeScale[2]; - FxCurve *sizeShape[2][2]; + FxCurve* sizeShape[2][2]; float scaleScale; - FxCurve *scaleShape[2]; - FxCurve *color[2]; - FxCurve *alpha[2]; + FxCurve* scaleShape[2]; + FxCurve* color[2]; + FxCurve* alpha[2]; float lightingFrac; float decalFadeInTime; float collOffset[3]; float collRadius; - FxEffectDef *effectOnImpact; - FxEffectDef *effectOnDeath; + FxEffectDef* effectOnImpact; + FxEffectDef* effectOnDeath; int sortOrder; - FxEffectDef *emission; + FxEffectDef* emission; FxFloatRange emitDist; FxFloatRange emitDistVariance; char elemType; @@ -6118,8 +6590,8 @@ namespace Game struct FxElemField { - const char *keyName; - bool(__cdecl *handler)(const char **, FxEditorElemDef *); + const char* keyName; + bool(__cdecl* handler)(const char**, FxEditorElemDef*); }; enum $18B36A54AD92993D0728595D3504B7CB @@ -6144,7 +6616,7 @@ namespace Game struct infoParm_t { - char *name; + char* name; int clearSolid; int surfaceFlags; int contents; @@ -6487,8 +6959,8 @@ namespace Game const char* name; int ofs; fieldtype_t type; - void(__cdecl * setter)(gentity_s*, int); - void(__cdecl * getter)(gentity_s*, int); + void(__cdecl* setter)(gentity_s*, int); + void(__cdecl* getter)(gentity_s*, int); }; struct client_fields_s @@ -6496,12 +6968,12 @@ namespace Game const char* name; int ofs; fieldtype_t type; - void(__cdecl * setter)(gclient_s*, const client_fields_s*); - void(__cdecl * getter)(gclient_s*, const client_fields_s*); + void(__cdecl* setter)(gclient_s*, const client_fields_s*); + void(__cdecl* getter)(gclient_s*, const client_fields_s*); }; - typedef void(__cdecl * ScriptCallbackEnt)(gentity_s*, int); - typedef void(__cdecl * ScriptCallbackClient)(gclient_s*, const client_fields_s*); + typedef void(__cdecl* ScriptCallbackEnt)(gentity_s*, int); + typedef void(__cdecl* ScriptCallbackClient)(gclient_s*, const client_fields_s*); struct lockonFireParms { @@ -6681,9 +7153,9 @@ namespace Game typedef struct punctuation_s { - char *p; //punctuation character(s) - int n; //punctuation indication - struct punctuation_s *next; //next punctuation + char* p; //punctuation character(s) + int n; //punctuation indication + punctuation_s* next; //next punctuation } punctuation_t; #define MAX_TOKEN 1024 @@ -6691,68 +7163,68 @@ namespace Game typedef struct token_s { - char string[MAX_TOKEN]; //available token - int type; //last read token type - int subtype; //last read token sub type - unsigned long int intvalue; //integer value - long double floatvalue; //floating point value - char *whitespace_p; //start of white space before token - char *endwhitespace_p; //start of white space before token - int line; //line the token was on - int linescrossed; //lines crossed in white space - struct token_s *next; //next token in chain + char string[MAX_TOKEN]; //available token + int type; //last read token type + int subtype; //last read token sub type + unsigned long int intvalue; //integer value + long double floatvalue; //floating point value + char* whitespace_p; //start of white space before token + char* endwhitespace_p; //start of white space before token + int line; //line the token was on + int linescrossed; //lines crossed in white space + token_s* next; //next token in chain } token_t; typedef struct script_s { char filename[64]; //file name of the script - char *buffer; //buffer containing the script - char *script_p; //current pointer in the script - char *end_p; //pointer to the end of the script - char *lastscript_p; //script pointer before reading token - char *whitespace_p; //begin of the white space - char *endwhitespace_p; //end of the white space + char* buffer; //buffer containing the script + char* script_p; //current pointer in the script + char* end_p; //pointer to the end of the script + char* lastscript_p; //script pointer before reading token + char* whitespace_p; //begin of the white space + char* endwhitespace_p; //end of the white space int length; //length of the script in bytes int line; //current line in script int lastline; //line before reading token int tokenavailable; //set by UnreadLastToken int flags; //several script flags - punctuation_t *punctuations; //the punctuations used in the script - punctuation_t **punctuationtable; + punctuation_t* punctuations; //the punctuations used in the script + punctuation_t** punctuationtable; token_t token; //available token - struct script_s *next; //next script in a chain + struct script_s* next; //next script in a chain } script_t; typedef struct define_s { - char *name; //define name + char* name; //define name int flags; //define flags int builtin; // > 0 if builtin define int numparms; //number of define parameters - token_t *parms; //define parameters - token_t *tokens; //macro tokens (possibly containing parm tokens) - struct define_s *next; //next defined macro in a list - struct define_s *hashnext; //next define in the hash chain + token_t* parms; //define parameters + token_t* tokens; //macro tokens (possibly containing parm tokens) + struct define_s* next; //next defined macro in a list + struct define_s* hashnext; //next define in the hash chain } define_t; typedef struct indent_s { int type; //indent type int skip; //true if skipping current indent - script_t *script; //script the indent was in - struct indent_s *next; //next indent on the indent stack + script_t* script; //script the indent was in + struct indent_s* next; //next indent on the indent stack } indent_t; typedef struct source_s { char filename[64]; //file name of the script char includepath[64]; //path to include files - punctuation_t *punctuations; //punctuations to use - script_t *scriptstack; //stack with scripts of the source - token_t *tokens; //tokens to read first - define_t *defines; //list with macro definitions - define_t **definehash; //hash chain with defines - indent_t *indentstack; //stack with indents + punctuation_t* punctuations; //punctuations to use + script_t* scriptstack; //stack with scripts of the source + token_t* tokens; //tokens to read first + define_t* defines; //list with macro definitions + define_t** definehash; //hash chain with defines + indent_t* indentstack; //stack with indents int skip; // > 0 if skipping conditional code token_t token; //last read token } source_t; @@ -6766,12 +7238,12 @@ namespace Game char string[MAX_TOKENLENGTH]; } pc_token_t; - typedef struct keywordHash_s + template + struct KeywordHashEntry { - char *keyword; - bool(*func)(menuDef_t *item, int handle); - //struct keywordHash_s *next; - } keywordHash_t; + const char* keyword; + int (*func)(T*, int); + }; enum MaterialTechniqueType { @@ -6854,17 +7326,17 @@ namespace Game struct $A6DFE8F2BEFD3E7315B44D22E582538B { unsigned int stride; - IDirect3DVertexBuffer9 *vb; + IDirect3DVertexBuffer9* vb; unsigned int offset; }; struct GfxCmdBufPrimState { - IDirect3DDevice9 *device; - IDirect3DIndexBuffer9 *indexBuffer; + IDirect3DDevice9* device; + IDirect3DIndexBuffer9* indexBuffer; MaterialVertexDeclType vertDeclType; $A6DFE8F2BEFD3E7315B44D22E582538B streams[2]; - IDirect3DVertexDeclaration9 *vertexDecl; + IDirect3DVertexDeclaration9* vertexDecl; }; enum GfxDepthRangeType @@ -6900,16 +7372,16 @@ namespace Game R_RENDERTARGET_NONE = 0xD, }; - struct /*__declspec(align(16))*/ GfxCmdBufState + struct GfxCmdBufState { char refSamplerState[16]; unsigned int samplerState[16]; - GfxTexture *samplerTexture[16]; + GfxTexture* samplerTexture[16]; GfxCmdBufPrimState prim; - Material *material; + Material* material; MaterialTechniqueType techType; - MaterialTechnique *technique; - MaterialPass *pass; + MaterialTechnique* technique; + MaterialPass* pass; unsigned int passIndex; GfxDepthRangeType depthRangeType; float depthRangeNear; @@ -6919,26 +7391,60 @@ namespace Game char alphaRef; unsigned int refStateBits[2]; unsigned int activeStateBits[2]; - MaterialPixelShader *pixelShader; - MaterialVertexShader *vertexShader; + MaterialPixelShader* pixelShader; + MaterialVertexShader* vertexShader; unsigned int scissorX; unsigned int scissorY; unsigned int scissorW; unsigned int scissorH; unsigned int pixPrimarySortKey; unsigned int pixSceneLightIndex; - Material *pixMaterial; - MaterialTechnique *pixTechnique; + Material* pixMaterial; + MaterialTechnique* pixTechnique; int pixCombine; GfxViewport viewport; GfxRenderTargetId renderTargetId; - Material *origMaterial; + Material* origMaterial; MaterialTechniqueType origTechType; }; + enum MaterialTextureSource : unsigned int + { + TEXTURE_SRC_CODE_BLACK = 0x0, + TEXTURE_SRC_CODE_WHITE = 0x1, + TEXTURE_SRC_CODE_IDENTITY_NORMAL_MAP = 0x2, + TEXTURE_SRC_CODE_MODEL_LIGHTING = 0x3, + TEXTURE_SRC_CODE_LIGHTMAP_PRIMARY = 0x4, + TEXTURE_SRC_CODE_LIGHTMAP_SECONDARY = 0x5, + TEXTURE_SRC_CODE_SHADOWMAP_SUN = 0x6, + TEXTURE_SRC_CODE_SHADOWMAP_SPOT = 0x7, + TEXTURE_SRC_CODE_FEEDBACK = 0x8, + TEXTURE_SRC_CODE_RESOLVED_POST_SUN = 0x9, + TEXTURE_SRC_CODE_RESOLVED_SCENE = 0xA, + TEXTURE_SRC_CODE_POST_EFFECT_0 = 0xB, + TEXTURE_SRC_CODE_POST_EFFECT_1 = 0xC, + TEXTURE_SRC_CODE_LIGHT_ATTENUATION = 0xD, + TEXTURE_SRC_CODE_OUTDOOR = 0xE, + TEXTURE_SRC_CODE_FLOATZ = 0xF, + TEXTURE_SRC_CODE_PROCESSED_FLOATZ = 0x10, + TEXTURE_SRC_CODE_RAW_FLOATZ = 0x11, + TEXTURE_SRC_CODE_HALF_PARTICLES = 0x12, + TEXTURE_SRC_CODE_HALF_PARTICLES_Z = 0x13, + TEXTURE_SRC_CODE_CASE_TEXTURE = 0x14, + TEXTURE_SRC_CODE_CINEMATIC_Y = 0x15, + TEXTURE_SRC_CODE_CINEMATIC_CR = 0x16, + TEXTURE_SRC_CODE_CINEMATIC_CB = 0x17, + TEXTURE_SRC_CODE_CINEMATIC_A = 0x18, + TEXTURE_SRC_CODE_REFLECTION_PROBE = 0x19, + TEXTURE_SRC_CODE_ALTERNATE_SCENE = 0x1A, + TEXTURE_SRC_CODE_COUNT = 0x1B, + }; + + struct GfxCmdBufSourceState; + struct GfxCmdBufContext { - /*GfxCmdBufSourceState*/ void *source; + GfxCmdBufSourceState *source; GfxCmdBufState *state; }; @@ -7032,6 +7538,306 @@ namespace Game float sunShadowPixelAdjust[4]; }; + struct GfxMatrix + { + float m[4][4]; + }; + + struct GfxCodeMatrices + { + GfxMatrix matrix[56]; + }; + + struct GfxCamera + { + float origin[3]; + float axis[3][3]; + float subWindowMins[2]; + float subWindowMaxs[2]; + float tanHalfFovX; + float tanHalfFovY; + float zNear; + float depthHackNearClip; + }; + + struct GfxViewParms + { + GfxMatrix viewMatrix; + GfxMatrix projectionMatrix; + GfxMatrix viewProjectionMatrix; + GfxMatrix inverseViewProjectionMatrix; + GfxCamera camera; + }; + + struct GfxBackEndData; + struct GfxCmdBufInput + { + float consts[76][4]; + GfxImage* codeImages[27]; + char codeImageSamplerStates[27]; + GfxBackEndData* data; + }; + + + enum GfxViewMode + { + VIEW_MODE_NONE = 0x0, + VIEW_MODE_3D = 0x1, + VIEW_MODE_2D = 0x2, + VIEW_MODE_IDENTITY = 0x3, + }; + + enum GfxViewportBehavior + { + GFX_USE_VIEWPORT_FOR_VIEW = 0x0, + GFX_USE_VIEWPORT_FULL = 0x1, + }; + + enum ShadowType + { + SHADOW_NONE = 0x0, + SHADOW_MAP = 0x1, + }; + + struct GfxDepthOfField + { + float viewModelStart; + float viewModelEnd; + float nearStart; + float nearEnd; + float farStart; + float farEnd; + float nearBlur; + float farBlur; + }; + + struct GfxFilm + { + bool enabled; + float brightness; + float contrast; + float desaturation; + float desaturationDark; + bool invert; + float tintDark[3]; + float tintMedium[3]; + float tintLight[3]; + }; + + struct GfxGlow + { + bool enabled; + float bloomCutoff; + float bloomDesaturation; + float bloomIntensity; + float radius; + }; + + struct GfxLightScale + { + float diffuseScale; + float specularScale; + }; + + struct GfxStageInfo + { + Stage activeStage; + bool activeStageValid; + }; + + struct GfxCompositeFx + { + GfxFilm film; + float distortionScale[3]; + float blurRadius; + float distortionMagnitude; + float frameRate; + int lastUpdate; + int frame; + int startMSec; + int currentTime; + int duration; + bool enabled; + bool scriptEnabled; + }; + + struct GfxVertexBufferState + { + volatile int used; + int total; + IDirect3DVertexBuffer9* buffer; + char* verts; + }; + + struct GfxMeshData + { + unsigned int indexCount; + unsigned int totalIndexCount; + unsigned __int16* indices; + GfxVertexBufferState vb; + unsigned int vertSize; + }; + + struct GfxQuadMeshData + { + float x; + float y; + float width; + float height; + GfxMeshData meshData; + }; + + struct GfxSparkSurfList + { + GfxSparkSurf* surfs; + unsigned int count; + }; + + enum GfxCodeSurfListType + { + GFX_CODE_SURFLIST_INVALID = -1, + GFX_CODE_SURFLIST_TRANS = 0x0, + GFX_CODE_SURFLIST_EMISSIVE = 0x1, + GFX_CODE_SURFLIST_TYPE_COUNT = 0x2, + }; + + + struct GfxViewInfo; + + struct GfxDrawListInfo + { + MaterialTechniqueType baseTechType; + GfxViewInfo* viewInfo; + float eyeOffset[3]; + unsigned int sceneLightIndex; + int cameraView; + GfxCodeSurfListType codeSurfListType; + }; + + struct GfxBspSurfList + { + unsigned int count; + const unsigned __int16* stream; + }; + + struct GfxSModelSurfList + { + unsigned int surfDataBytes; + const char* surfData; + const char* visData; + }; + + struct GfxDrawSurfList + { + GfxDrawSurf* array; + unsigned int count; + }; + + struct GfxPreTessSurf + { + GfxDrawGroupSetup drawGroup; + char lightmapIndex; + char reflectionProbeIndex; + unsigned __int16 triCount; + unsigned int baseIndex; + unsigned int firstVertex; + IDirect3DVertexBuffer9* vb; + unsigned int vertexCount; + }; + + struct GfxPreTessSurfList + { + GfxPreTessSurf* surfs; + unsigned int count; + }; + + struct GfxCodeSurfList + { + GfxCodeSurf* surfs; + unsigned int count; + }; + + struct GfxMarkSurfList + { + GfxMarkSurf* surfs; + unsigned int count; + }; + + struct GfxGlassSurfList + { + GfxGlassSurf* surfs; + unsigned int count; + }; + + struct GfxScaledPlacement + { + GfxPlacement base; + float scale; + }; + + struct GfxParticleCloud + { + GfxScaledPlacement placement; + float endpos[3]; + GfxColor color; + float radius[2]; + unsigned int flags; + float timeOffset; + }; + + struct GfxCloudSurfList + { + GfxParticleCloud* particles; + GfxCloudSurf* surfs; + unsigned int count; + }; + + struct GfxDrawList + { + GfxBspSurfList bspSurfList; + GfxPreTessSurfList bspPreTessSurfList; + GfxSModelSurfList smodelSurfList[4]; + GfxDrawSurfList drawSurfList; + GfxCodeSurfList codeSurfList; + GfxMarkSurfList markSurfList; + GfxGlassSurfList glassSurfList; + GfxCloudSurfList cloudSurfList; + GfxSparkSurfList sparkSurfList; + GfxDrawListInfo info; + }; + + struct GfxViewInfo + { + GfxViewParms viewParms; + GfxViewport sceneViewport; + GfxViewport displayViewport; + GfxViewport scissorViewport; + GfxSceneDef sceneDef; + ShadowType dynamicShadowType; + char floatZUsage; + bool needsDistortionResolve; + bool viewModelHasDistortion; + char forceSunShadowsGenerate; + unsigned int sceneLightCount; + float blurRadius; + float frustumPlanes[4][4]; + GfxDepthOfField dof; + GfxFilm film; + GfxGlow glow; + GfxLightScale charPrimaryLightScale; + GfxStageInfo stageInfo; + GfxCompositeFx waterSheetingFx; + const void* displayCmds; + GfxQuadMeshData* fullSceneViewMesh; + GfxDrawList drawList[10]; + //__declspec(align(16)) GfxCmdBufInput input; + GfxRenderTargetId renderTargetId; + bool useShadows; + unsigned int sunShadowResolution; + GfxRenderTargetId sunShadowRenderTargetId; + unsigned int sunShadowTileCount; + }; + struct GfxLight { char type; @@ -7123,10 +7929,31 @@ namespace Game char reflectionProbeIndex; }; - struct GfxScaledPlacement + struct GfxCmdBufSourceState { - GfxPlacement base; - float scale; + GfxCodeMatrices matrices; + GfxCmdBufInput input; + GfxViewParms viewParms; + float eyeOffset[4]; + GfxMatrix shadowLookupMatrix; + unsigned __int16 constVersions[132]; + unsigned __int16 matrixVersions[14]; + unsigned int sceneLightForShadowLookupMatrix; + GfxPlacement* objectPlacement[3]; + GfxViewParms* viewParms3D; + unsigned int depthHackFlags; + GfxScaledPlacement skinnedPlacement; + int cameraView; + GfxViewMode viewMode; + GfxSceneDef sceneDef; + GfxViewport sceneViewport; + float materialTime; + GfxViewportBehavior viewportBehavior; + int renderTargetWidth; + int renderTargetHeight; + bool viewportIsDirty; + unsigned int sceneLightIndex; + bool useHeroLighting; }; struct GfxSceneModel @@ -7245,7 +8072,7 @@ namespace Game int updateSound; int allowAddDObj; }; - + enum TextRenderFlags { TEXT_RENDERFLAG_FORCEMONOSPACE = 0x1, @@ -7261,7 +8088,7 @@ namespace Game TEXT_RENDERFLAG_OUTLINE = 0x400, TEXT_RENDERFLAG_OUTLINE_EXTRA = 0x800, }; - + enum FontPassType { FONTPASS_NORMAL = 0x0, @@ -8088,7 +8915,7 @@ namespace Game int flashShotFadeTime; ShockViewTypes type; } screenBlend; - + struct { int fadeTime; @@ -8477,7 +9304,7 @@ namespace Game char* openScriptIOFileBuffers[1]; com_parse_mark_t currentScriptIOLineMark[1]; cached_tag_mat_t cachedTagMat; - int scriptPrintChannel; + conChannel_t scriptPrintChannel; float compassMapUpperLeft[2]; float compassMapWorldSize[2]; float compassNorth[2]; @@ -9153,6 +9980,490 @@ namespace Game ET_EVENTS = 0x12, }; + struct GfxBackEndPrimitiveData + { + int hasSunDirChanged; + }; + + struct GfxFog + { + int startTime; + int finishTime; + GfxColor color; + float fogStart; + float density; + float fogMaxOpacity; + bool sunFogEnabled; + GfxColor sunColor; + float sunDir[3]; + float sunBeginFadeAngle; + float sunEndFadeAngle; + float sunFogScale; + }; + + struct GfxCmdHeader + { + unsigned __int16 id; + unsigned __int16 byteCount; + }; + + struct GfxCmdArray + { + char* cmds; + int usedTotal; + int usedCritical; + GfxCmdHeader* lastCmd; + }; + + struct GfxCmdBuf + { + IDirect3DDevice9* device; + }; + + struct GfxDrawCallOutput + { + GfxCmdBuf cmdBuf; + }; + + struct __declspec(align(4)) GfxDebugPoly + { + float color[4]; + int firstVert; + int vertCount; + bool outline; + }; + + struct GfxDebugPlume + { + float origin[3]; + float color[4]; + int score; + int startTime; + int duration; + }; + + struct DebugGlobals + { + float(*verts)[3]; + int vertCount; + int vertLimit; + GfxDebugPoly* polys; + int polyCount; + int polyLimit; + trDebugString_t* strings; + int stringCount; + int stringLimit; + trDebugString_t* externStrings; + int externStringCount; + int externMaxStringCount; + trDebugLine_t* lines; + int lineCount; + int lineLimit; + trDebugLine_t* externLines; + int externLineCount; + int externMaxLineCount; + GfxDebugPlume* plumes; + int plumeCount; + int plumeLimit; + }; + + struct GfxSunShadowProjection + { + float switchPartition[4]; + float shadowmapScale[4]; + }; + + struct GfxSunShadowOverlaySetup + { + float shadowOrg[2]; + float frustumShadowRays[4][2]; + unsigned int clipPlaneCount[2]; + float clipPlanes[2][8][3]; + }; + + struct GfxViewportParms + { + GfxViewport viewport; + GfxViewParms viewParms; + }; + + struct GfxSunShadowPartition + { + GfxViewportParms viewportParms; + }; + + struct GfxSunShadow + { + GfxMatrix lookupMatrix; + GfxSunShadowProjection sunProj; + GfxSunShadowPartition partition[2]; + GfxSunShadowOverlaySetup overlaySetup; + }; + + struct GfxSpotShadowSceneLight + { + GfxMatrix lookupMatrix; + float fade; + GfxImage* image; + }; + + struct GfxDrawSurfIter + { + GfxDrawSurf* current; + GfxDrawSurf* end; + GfxDrawSurf* mark; + }; + + struct GfxCodeSurfIter + { + GfxCodeSurf* current; + GfxCodeSurf* end; + GfxCodeSurf* mark; + }; + + struct GfxMarkSurfIter + { + GfxMarkSurf* current; + GfxMarkSurf* end; + GfxMarkSurf* mark; + }; + + struct GfxGlassSurfIter + { + GfxGlassSurf* current; + GfxGlassSurf* end; + GfxGlassSurf* mark; + }; + + struct GfxCloudSurfIter + { + GfxCloudSurf* current; + GfxCloudSurf* end; + GfxCloudSurf* mark; + }; + + struct GfxSparkSurfIter + { + GfxSparkSurf* current; + GfxSparkSurf* end; + GfxSparkSurf* mark; + }; + + struct GfxSModelSurfIter + { + const char* visData; + unsigned __int16* current; + const char* end; + const char* mark; + }; + + struct GfxBspSurfIter + { + const unsigned __int16* current; + const unsigned __int16* end; + const unsigned __int16* mark; + }; + + struct GfxBspLightMapSurfIter : GfxBspSurfIter + { + }; + + struct GfxPreTessSurfIter + { + GfxPreTessSurf* current; + GfxPreTessSurf* end; + GfxPreTessSurf* mark; + }; + + struct GfxSunShadowClip + { + unsigned int planeCount[2]; + unsigned int frustumPlaneCount[2]; + DpvsPlane planes[2][10]; + }; + + struct GfxSModelCachedSurfIter : GfxSModelSurfIter + { + }; + + struct GfxSModelRigidSurfIter : GfxSModelSurfIter + { + }; + + struct GfxSModelSkinnedSurfIter : GfxSModelSurfIter + { + }; + + struct GfxSModelPreTessSurfIter : GfxSModelSurfIter + { + }; + + struct GfxCmdRingBuf + { + struct GfxDrawListIter* drawListIter; + unsigned int memoryPos; + unsigned int maxMemoryPos; + char* memoryPool; + unsigned int fencePosIndex; + volatile unsigned int fenceIndex; + unsigned int availIndex; + unsigned int availMemoryPos; + unsigned int reserveMemoryPos0; + unsigned int reserveMemoryPos1; + unsigned int fencePos[64]; + unsigned int fence[64]; + unsigned int checkMemoryPos; + }; + + struct GfxDrawListIter + { + GfxBspSurfIter bspSurfIter; + GfxPreTessSurfIter bspPreTessSurfIter; + GfxSModelRigidSurfIter smodelRigidSurfIter; + GfxSModelSkinnedSurfIter smodelSkinnedSurfIter; + GfxSModelCachedSurfIter smodelCachedSurfIter; + GfxSModelPreTessSurfIter smodelPreTessSurfIter; + GfxDrawSurfIter drawSurfIter; + GfxCodeSurfIter codeSurfIter; + GfxMarkSurfIter markSurfIter; + GfxGlassSurfIter glassSurfIter; + GfxCloudSurfIter cloudSurfIter; + GfxSparkSurfIter sparkSurfIter; + }; + + union GfxSurfsIteratorSortKey + { + struct + { + unsigned int spliceIndex; + unsigned int sortKey; + } fields; + unsigned __int64 packed; + }; + + struct GfxSpotShadow + { + GfxSpotShadowSceneLight sceneLight; + GfxViewportParms viewportParms; + char sceneLightIndex; + char pad[3]; + GfxLight* light; + GfxRenderTargetId renderTargetId; + float pixelAdjust[4]; + int clearScreen; + GfxMeshData* clearMesh; + }; + + struct GfxDrawListArgs + { + GfxCmdBufContext context; + GfxDrawListInfo* listInfo; + }; + + struct GfxSurfsIterator + { + GfxSurfsIteratorSortKey key; + unsigned int(__cdecl* GetSortKeyCallback)(GfxDrawListIter*); + bool(__cdecl* RenderDrawGroupCallback)(GfxDrawListArgs*, GfxDrawListIter*); + }; + + struct GfxSurfsIterGroup + { + unsigned int iteratorBegin; + unsigned int iteratorCount; + GfxDrawListIter drawListIter; + GfxSurfsIterator iteratorPool[11]; + }; + + struct GfxSpliceSurfs + { + unsigned int iteratorBegin; + unsigned int iteratorCount; + unsigned int spliceCount; + GfxDrawListIter drawListIter[5]; + GfxViewport scissorViewport[5]; + int isSceneScissorViewport[5]; + GfxDrawListInfo* dynLightDrawListInfo[5]; + GfxSurfsIterator iteratorPool[55]; + }; + + struct GfxStaticModelDrawStream + { + GfxSModelSurfIter* smodelSurfIter; + GfxSModelSurfHeader smodelSurfHeader; + const char* smodelSurfVisData; + GfxTexture* reflectionProbeTexture; + unsigned int customSamplerFlags; + XSurface* localSurf; + unsigned int smodelCount; + const unsigned __int16* smodelList; + }; + + struct FxSparkMeshData + { + unsigned int triCount; + unsigned __int16* indices; + unsigned int baseVertex; + char pad[4]; + GfxParticleCloud cloud; + }; + + struct GfxBackEndData + { + char sceneLightTechType[13][256]; + GfxSparkSurf sparkSurfs[64]; + GfxViewParms viewParms[4]; + GfxMeshData mesh[5]; + int localClientNum; + GfxBackEndPrimitiveData prim; + volatile int bspSurfDataUsed; + volatile int smodelSurfDataUsed; + volatile int smodelSurfVisDataUsed; + unsigned int sceneLightHasShadowMap[8]; + int drawSurfCount; + volatile int surfPos; + volatile int gfxEntCount; + unsigned int codeSurfCount[2]; + unsigned int codeSurfArgsCount[2]; + volatile int cloudDataCount; + unsigned int glassSurfCount; + unsigned int markSurfCount; + volatile int sparkSurfCount; + GfxVertexBufferState* skinnedCacheVb; + unsigned int endFence; + unsigned int endFrameFence; + int viewParmCount; + GfxFog fogSettings; + GfxCmdArray* commands; + unsigned int viewInfoIndex; + unsigned int viewInfoCount; + GfxViewInfo* viewInfo; + const void* cmds; + float sunShadowLightDir[3]; + int hasApproxSunDirChanged; + int cmdBufValid[18]; + GfxDrawCallOutput drawOutput[18]; + DebugGlobals debugGlobals; + unsigned int imageRendered[112]; + unsigned int drawType; + GfxDrawList dynLightDrawList[4]; + unsigned int dynLightCount; + GfxDrawList* emissiveSpotShadowDrawList[1]; + unsigned int emissiveSpotLightCount; + GfxSunShadow sunShadow; + unsigned int spotShadowCount; + GfxSpotShadow spotShadows[4]; + GfxSurfsIterGroup prepassIterGroup[5]; + GfxSpliceSurfs litTransSpliceSurfs; + char surfsBuffer[131072]; + float codeSurfArgs[288][4]; + GfxCodeSurf codeEmissiveSurfs[2048]; + GfxCodeSurf codeTransSurfs[640]; + GfxMarkSurf markSurfs[1536]; + GfxGlassSurf glassSurfs[768]; + unsigned __int16 bspSurfData[35328]; + char smodelSurfData[35840]; + char smodelSurfVisData[45056]; + GfxCloudSurf cloudSurfs[256]; + GfxEntity gfxEnts[128]; + FxSparkMeshData sparkData[64]; + GfxParticleCloud cloudData[256]; + GfxDrawSurf drawSurfs[16384]; + GfxLight sceneLights[253]; + }; + + enum + { + THREAD_VALUE_PROF_STACK = 0x0, + THREAD_VALUE_VA = 0x1, + THREAD_VALUE_COM_ERROR = 0x2, + THREAD_VALUE_TRACE = 0x3, + THREAD_VALUE_COUNT = 0x4, + }; + + struct va_info_t + { + char va_string[2][1024]; + int index; + }; + + struct TraceCheckCount + { + int global; + int* partitions; + }; + + struct TraceThreadInfo + { + TraceCheckCount checkcount; + }; + + struct ProfileAtom + { + unsigned int value[1]; + }; + + volatile struct ProfileReadable + { + unsigned int hits; + ProfileAtom total; + ProfileAtom self; + }; + + struct ProfileWritable + { + int nesting; + unsigned int hits; + ProfileAtom start[3]; + ProfileAtom total; + ProfileAtom child; + }; + + struct profile_t + { + ProfileWritable write; + ProfileReadable read; + }; + + struct profile_guard_t + { + int id; + profile_t** ppStack; + }; + + struct ProfileStack + { + profile_t prof_root; + profile_t* prof_pStack[16384]; + profile_t** prof_ppStack; + profile_t prof_array[443]; + ProfileAtom prof_overhead_internal; + ProfileAtom prof_overhead_external; + profile_guard_t prof_guardstack[32]; + int prof_guardpos; + float prof_timescale; + }; + + struct bgs_t + { + unsigned char __pad0[0x82950]; + }; + + static_assert(sizeof(bgs_t) == 0x82950); + + struct ZipInfo + { + int offsetCount; + int offsets[128]; + int size; + char* buffer; + }; + + struct Sys_File + { + HANDLE handle; + }; + #pragma endregion #ifndef IDA diff --git a/src/Game/System.cpp b/src/Game/System.cpp index 24cf1687..0068ff51 100644 --- a/src/Game/System.cpp +++ b/src/Game/System.cpp @@ -12,6 +12,7 @@ namespace Game Sys_ListFiles_t Sys_ListFiles = Sys_ListFiles_t(0x45A660); Sys_Milliseconds_t Sys_Milliseconds = Sys_Milliseconds_t(0x42A660); Sys_Error_t Sys_Error = Sys_Error_t(0x43D570); + Sys_Sleep_t Sys_Sleep = Sys_Sleep_t(0x4169C0); Sys_LockWrite_t Sys_LockWrite = Sys_LockWrite_t(0x435880); Sys_TempPriorityAtLeastNormalBegin_t Sys_TempPriorityAtLeastNormalBegin = Sys_TempPriorityAtLeastNormalBegin_t(0x478680); Sys_TempPriorityEnd_t Sys_TempPriorityEnd = Sys_TempPriorityEnd_t(0x4DCF00); @@ -21,6 +22,8 @@ namespace Game Sys_SendPacket_t Sys_SendPacket = Sys_SendPacket_t(0x60FDC0); Sys_ShowConsole_t Sys_ShowConsole = Sys_ShowConsole_t(0x4305E0); Sys_SuspendOtherThreads_t Sys_SuspendOtherThreads = Sys_SuspendOtherThreads_t(0x45A190); + Sys_SetValue_t Sys_SetValue = Sys_SetValue_t(0x4B2F50); + Sys_CreateFile_t Sys_CreateFile = Sys_CreateFile_t(0x4B2EF0); char(*sys_exitCmdLine)[1024] = reinterpret_cast(0x649FB68); @@ -29,7 +32,7 @@ namespace Game void Sys_LockRead(FastCriticalSection* critSect) { InterlockedIncrement(&critSect->readCount); - while (critSect->writeCount) std::this_thread::sleep_for(1ms); + while (critSect->writeCount) Sys_Sleep(1); } void Sys_UnlockRead(FastCriticalSection* critSect) @@ -38,10 +41,16 @@ namespace Game InterlockedDecrement(&critSect->readCount); } + void Sys_UnlockWrite(FastCriticalSection* critSect) + { + assert(critSect->writeCount > 0); + InterlockedDecrement(&critSect->writeCount); + Sys_TempPriorityEnd(&critSect->tempPriority); + } + bool Sys_TryEnterCriticalSection(CriticalSection critSect) { - assert(static_cast(critSect) < - static_cast(CRITSECT_COUNT)); + AssertIn(critSect, CRITSECT_COUNT); return TryEnterCriticalSection(&s_criticalSection[critSect]) != FALSE; } diff --git a/src/Game/System.hpp b/src/Game/System.hpp index 1232ba81..68eb6ee5 100644 --- a/src/Game/System.hpp +++ b/src/Game/System.hpp @@ -2,7 +2,6 @@ namespace Game { - typedef void(*Sys_Error_t)(const char* error, ...); extern Sys_Error_t Sys_Error; @@ -33,6 +32,9 @@ namespace Game typedef int(*Sys_Milliseconds_t)(); extern Sys_Milliseconds_t Sys_Milliseconds; + typedef void(*Sys_Sleep_t)(int msec); + extern Sys_Sleep_t Sys_Sleep; + typedef void(*Sys_LockWrite_t)(FastCriticalSection* critSect); extern Sys_LockWrite_t Sys_LockWrite; @@ -60,12 +62,40 @@ namespace Game typedef void(*Sys_SuspendOtherThreads_t)(); extern Sys_SuspendOtherThreads_t Sys_SuspendOtherThreads; + typedef void(*Sys_SetValue_t)(int valueIndex, void* data); + extern Sys_SetValue_t Sys_SetValue; + + typedef Sys_File(*Sys_CreateFile_t)(const char* dir, const char* filename); + extern Sys_CreateFile_t Sys_CreateFile; + extern char(*sys_exitCmdLine)[1024]; extern RTL_CRITICAL_SECTION* s_criticalSection; extern void Sys_LockRead(FastCriticalSection* critSect); extern void Sys_UnlockRead(FastCriticalSection* critSect); + extern void Sys_UnlockWrite(FastCriticalSection* critSect); extern bool Sys_TryEnterCriticalSection(CriticalSection critSect); + + class Sys + { + public: + enum class TLS_OFFSET : unsigned int + { + LEVEL_BGS = 0xC, + THREAD_VALUES = 0x14, + HUFFMAN_BLOC = 0x10, + DVAR_ALLOWED_MODIFIED_FLAGS = 0x1C, + DVAR_MODIFIED_FLAGS = 0x18, + ZIP_INFO = 0x20, + }; + + template + static T* GetTls(TLS_OFFSET offset) + { + const auto* tls = reinterpret_cast(__readfsdword(0x2C)); + return reinterpret_cast(tls[*g_dwTlsIndex] + static_cast>(offset)); + } + }; } diff --git a/src/Resource.rc b/src/Resource.rc index b04f455f..cda9e955 100644 --- a/src/Resource.rc +++ b/src/Resource.rc @@ -2,7 +2,8 @@ // #pragma code_page(65001) -#include "STDInclude.hpp" +#include +#include #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// @@ -70,7 +71,7 @@ BEGIN #endif VALUE "FileVersion", SHORTVERSION VALUE "InternalName", "iw4x" - VALUE "LegalCopyright", "Copyright 2017 The IW4x Team. All rights reserved." + VALUE "LegalCopyright", "Copyright 2022 The IW4x Team. All rights reserved." VALUE "OriginalFilename", "iw4x.dll" VALUE "ProductName", "IW4x" VALUE "ProductVersion", SHORTVERSION diff --git a/src/STDInclude.cpp b/src/STDInclude.cpp index 534f0160..81dd2b0a 100644 --- a/src/STDInclude.cpp +++ b/src/STDInclude.cpp @@ -78,37 +78,37 @@ static_assert(sizeof(intptr_t) == 4 && sizeof(void*) == 4 && sizeof(size_t) == 4 // ReSharper restore CppRedundantBooleanExpressionArgument #if !defined(_M_IX86) -#error "Invalid processor achritecture!" +#error "Invalid processor architecture!" #endif extern "C" { - // Disable telemetry data logging + // Disable telemetry data logging void __cdecl __vcrt_initialize_telemetry_provider() {} void __cdecl __telemetry_main_invoke_trigger() {} void __cdecl __telemetry_main_return_trigger() {} void __cdecl __vcrt_uninitialize_telemetry_provider() {} // Enable 'High Performance Graphics' - __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; + __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; // Tommath fixes - int s_read_arc4random(void*, size_t) + int s_read_arc4random(void*, std::size_t) { return -1; } - int s_read_getrandom(void*, size_t) + int s_read_getrandom(void*, std::size_t) { return -1; } - int s_read_urandom(void*, size_t) + int s_read_urandom(void*, std::size_t) { return -1; } - int s_read_ltm_rng(void*, size_t) + int s_read_ltm_rng(void*, std::size_t) { return -1; } diff --git a/src/STDInclude.hpp b/src/STDInclude.hpp index e1f4d8cc..776d1211 100644 --- a/src/STDInclude.hpp +++ b/src/STDInclude.hpp @@ -1,8 +1,5 @@ #pragma once -// Version number -#include "version.h" - #ifndef RC_INVOKED #define VC_EXTRALEAN @@ -19,7 +16,7 @@ #include #include #include -#include +#include #include #include #include @@ -31,23 +28,25 @@ #pragma warning(disable: 4244) #include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include +#include +#include #include #include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include #pragma warning(pop) @@ -81,7 +80,6 @@ #include #include -#include #include // Enable additional literals @@ -174,6 +172,7 @@ using namespace std::literals; #endif #define BASEGAME "iw4x" +#define BASEGAME_NAME "iw4mp_ceg.exe" #define CLIENT_CONFIG "iw4x_config.cfg" // Resource stuff diff --git a/src/Steam/Proxy.cpp b/src/Steam/Proxy.cpp index 4d352322..9ec04148 100644 --- a/src/Steam/Proxy.cpp +++ b/src/Steam/Proxy.cpp @@ -1,4 +1,5 @@ #include +#include namespace Steam { @@ -6,11 +7,11 @@ namespace Steam ::Utils::Library Proxy::Overlay; ISteamClient008* Proxy::SteamClient = nullptr; - IClientEngine* Proxy::ClientEngine = nullptr; - Interface Proxy::ClientUser; - Interface Proxy::ClientFriends; + IClientEngine* Proxy::ClientEngine = nullptr; + Interface Proxy::ClientUser; + Interface Proxy::ClientFriends; - Interface Proxy::Placeholder; + Interface Proxy::Placeholder; Proxy::Handle Proxy::SteamPipe = nullptr; Proxy::Handle Proxy::SteamUser = nullptr; @@ -237,7 +238,7 @@ namespace Steam { std::lock_guard _(Proxy::CallMutex); - for(auto i = Proxy::Calls.begin(); i != Proxy::Calls.end(); ++i) + for (auto i = Proxy::Calls.begin(); i != Proxy::Calls.end(); ++i) { if(i->handled) { diff --git a/src/Steam/Steam.cpp b/src/Steam/Steam.cpp index 3d1ff85f..9e206af9 100644 --- a/src/Steam/Steam.cpp +++ b/src/Steam/Steam.cpp @@ -1,4 +1,5 @@ #include +#include "Components/Modules/StartupMessages.hpp" namespace Steam { diff --git a/src/Utils/Cache.cpp b/src/Utils/Cache.cpp index 17390b83..99c79d0d 100644 --- a/src/Utils/Cache.cpp +++ b/src/Utils/Cache.cpp @@ -4,18 +4,13 @@ namespace Utils { const char* Cache::Urls[] = { + "https://raw.githubusercontent.com/XLabsProject/iw4x-client", "https://xlabs.dev", - "https://raw.githubusercontent.com/XLabsProject/iw4x-client" }; std::string Cache::ValidUrl; std::mutex Cache::CacheMutex; - std::string Cache::GetStaticUrl(const std::string& path) - { - return Cache::Urls[0] + path; - } - std::string Cache::GetUrl(const std::string& url, const std::string& path) { return url + path; @@ -23,29 +18,24 @@ namespace Utils std::string Cache::GetFile(const std::string& path, int timeout, const std::string& useragent) { - std::lock_guard _(Cache::CacheMutex); + std::lock_guard _(CacheMutex); - if (Cache::ValidUrl.empty()) + if (ValidUrl.empty()) { - InternetSetCookieA("https://onion.casa", "disclaimer_accepted", "1"); - InternetSetCookieA("https://hiddenservice.net", "disclaimer_accepted", "1"); - - for (int i = 0; i < ARRAYSIZE(Cache::Urls); ++i) + for (std::size_t i = 0; i < ARRAYSIZE(Urls); ++i) { - std::string result = Utils::WebIO(useragent, Cache::GetUrl(Cache::Urls[i], path)).setTimeout(timeout)->get(); + std::string result = WebIO(useragent, GetUrl(Urls[i], path)).setTimeout(timeout)->get(); if (!result.empty()) { - Cache::ValidUrl = Cache::Urls[i]; + ValidUrl = Urls[i]; return result; } } - return ""; - } - else - { - return Utils::WebIO(useragent, Cache::GetUrl(Cache::ValidUrl, path)).setTimeout(timeout)->get(); + return {}; } + + return WebIO(useragent, GetUrl(ValidUrl, path)).setTimeout(timeout)->get(); } } diff --git a/src/Utils/Cache.hpp b/src/Utils/Cache.hpp index 73b772fa..4c2b280f 100644 --- a/src/Utils/Cache.hpp +++ b/src/Utils/Cache.hpp @@ -5,13 +5,13 @@ namespace Utils class Cache { public: - static std::string GetStaticUrl(const std::string& path); + static const char* Urls[]; + + static std::string GetUrl(const std::string& url, const std::string& path); static std::string GetFile(const std::string& path, int timeout = 5000, const std::string& useragent = "IW4x"); private: static std::mutex CacheMutex; - static const char* Urls[]; static std::string ValidUrl; - static std::string GetUrl(const std::string& url, const std::string& path); }; } diff --git a/src/Utils/Compression.cpp b/src/Utils/Compression.cpp index a26fd668..adf5aefd 100644 --- a/src/Utils/Compression.cpp +++ b/src/Utils/Compression.cpp @@ -11,8 +11,15 @@ namespace Utils::Compression // Make sure the buffer is large enough if (length < 100) length *= 10; - char* buffer = allocator.allocateArray(length); - if (compress2(reinterpret_cast(buffer), &length, reinterpret_cast(const_cast(data.data())), data.size(), Z_BEST_COMPRESSION) != Z_OK) + auto* buffer = allocator.allocateArray(length); + +#ifdef _DEBUG + constexpr auto compression = Z_NO_COMPRESSION; +#else + constexpr auto compression = Z_BEST_COMPRESSION; +#endif + + if (compress2((Bytef*)(buffer), &length, (const Bytef*)(data.data()), data.size(), compression) != Z_OK) { return {}; } @@ -34,13 +41,13 @@ namespace Utils::Compression int ret; Memory::Allocator allocator; - std::uint8_t* dest = allocator.allocateArray(CHUNK); - const char* dataPtr = data.data(); + auto* dest = allocator.allocateArray(CHUNK); + const auto* dataPtr = data.data(); do { - stream.avail_in = std::min(static_cast(CHUNK), data.size() - (dataPtr - data.data())); - stream.next_in = reinterpret_cast(dataPtr); + stream.avail_in = std::min(static_cast(CHUNK), data.size() - (dataPtr - data.data())); + stream.next_in = reinterpret_cast(dataPtr); dataPtr += stream.avail_in; do diff --git a/src/Utils/Cryptography.cpp b/src/Utils/Cryptography.cpp index 26eb3813..c2e515cc 100644 --- a/src/Utils/Cryptography.cpp +++ b/src/Utils/Cryptography.cpp @@ -270,7 +270,7 @@ namespace Utils return Compute(data.data(), data.size()); } - unsigned int JenkinsOneAtATime::Compute(const char *key, size_t len) + unsigned int JenkinsOneAtATime::Compute(const char *key, std::size_t len) { unsigned int hash, i; for (hash = i = 0; i < len; ++i) diff --git a/src/Utils/Entities.cpp b/src/Utils/Entities.cpp index 5c31dfbe..cbc8b494 100644 --- a/src/Utils/Entities.cpp +++ b/src/Utils/Entities.cpp @@ -6,13 +6,13 @@ namespace Utils { std::string entityString; - for (auto& entity : this->entities) + for (const auto& entity : this->entities) { entityString.append("{\n"); - for (auto& property : entity) + for (const auto& property : entity) { - entityString.push_back('"'); + entityString.append("\""); entityString.append(property.first); entityString.append("\" \""); entityString.append(property.second); @@ -35,7 +35,9 @@ namespace Utils { const auto& model = itr->second; - if (!model.empty() && model[0] != '*' && model[0] != '?') // Skip brushmodels + if (!model.empty() && model[0] != '*' && model[0] != '?' && // Skip brushmodels + model != "com_plasticcase_green_big_us_dirt"s // Team zones + ) { if (std::find(models.begin(), models.end(), model) == models.end()) { diff --git a/src/Utils/InfoString.cpp b/src/Utils/InfoString.cpp index 9eb8c37b..bfefdff2 100644 --- a/src/Utils/InfoString.cpp +++ b/src/Utils/InfoString.cpp @@ -19,8 +19,7 @@ namespace Utils std::string InfoString::get(const std::string& key) const { - const auto value = this->keyValuePairs.find(key); - if (value != this->keyValuePairs.end()) + if (const auto value = this->keyValuePairs.find(key); value != this->keyValuePairs.end()) { return value->second; } diff --git a/src/Utils/Json.cpp b/src/Utils/Json.cpp index cbf963e3..eb36fff6 100644 --- a/src/Utils/Json.cpp +++ b/src/Utils/Json.cpp @@ -1,4 +1,5 @@ #include +#include namespace Utils::Json { @@ -31,4 +32,32 @@ namespace Utils::Json return "null"; } } + + unsigned long ReadFlags(const std::string binaryFlags, size_t size) + { + std::bitset<64> input; + const auto binarySize = size * 8; + + if (binaryFlags.size() > binarySize) + { + Components::Logger::Print("Flag {} might not be properly translated, it seems to contain an error (invalid length)\n", binaryFlags); + return 0; + } + + auto i = binarySize - 1; + for (char bit : binaryFlags) + { + if (i < 0) + { + Components::Logger::Print("Flag {} might not be properly translated, it seems to contain an error (invalid length)\n", binaryFlags); + break; + } + + bool isOne = bit == '1'; + input.set(i--, isOne); + } + + return input.to_ulong(); + } + } diff --git a/src/Utils/Json.hpp b/src/Utils/Json.hpp index 854811e1..415309e7 100644 --- a/src/Utils/Json.hpp +++ b/src/Utils/Json.hpp @@ -3,4 +3,6 @@ namespace Utils::Json { std::string TypeToString(nlohmann::json::value_t type); + + unsigned long ReadFlags(const std::string binaryFlags, size_t size); } diff --git a/src/Utils/Memory.cpp b/src/Utils/Memory.cpp index 9b3a0910..cf26b3ca 100644 --- a/src/Utils/Memory.cpp +++ b/src/Utils/Memory.cpp @@ -4,7 +4,7 @@ namespace Utils { Utils::Memory::Allocator Memory::MemAllocator; - void* Memory::AllocateAlign(size_t length, size_t alignment) + void* Memory::AllocateAlign(std::size_t length, std::size_t alignment) { auto* data = _aligned_malloc(length, alignment); assert(data); @@ -12,26 +12,23 @@ namespace Utils return data; } - void* Memory::Allocate(size_t length) + void* Memory::Allocate(std::size_t length) { - auto* data = calloc(length, 1); + auto* data = std::calloc(length, 1); assert(data); return data; } char* Memory::DuplicateString(const std::string& string) { - auto* newString = Memory::AllocateArray(string.size() + 1); + auto* newString = AllocateArray(string.size() + 1); std::memcpy(newString, string.data(), string.size()); return newString; } void Memory::Free(void* data) { - if (data) - { - ::free(data); - } + std::free(data); } void Memory::Free(const void* data) @@ -53,11 +50,11 @@ namespace Utils } // Complementary function for memset, which checks if memory is filled with a char - bool Memory::IsSet(void* mem, char chr, size_t length) + bool Memory::IsSet(void* mem, char chr, std::size_t length) { auto* memArr = static_cast(mem); - for (size_t i = 0; i < length; ++i) + for (std::size_t i = 0; i < length; ++i) { if (memArr[i] != chr) { diff --git a/src/Utils/Memory.hpp b/src/Utils/Memory.hpp index 7d3fc150..3e85c756 100644 --- a/src/Utils/Memory.hpp +++ b/src/Utils/Memory.hpp @@ -73,7 +73,7 @@ namespace Utils this->refMemory[memory] = callback; } - void* allocate(size_t length) + void* allocate(std::size_t length) { std::lock_guard _(this->mutex); @@ -87,7 +87,7 @@ namespace Utils return this->allocateArray(1); } - template T* allocateArray(size_t count = 1) + template T* allocateArray(std::size_t count = 1) { return static_cast(this->allocate(count * sizeof(T))); } @@ -133,13 +133,13 @@ namespace Utils std::unordered_map refMemory; }; - static void* AllocateAlign(size_t length, size_t alignment); - static void* Allocate(size_t length); + static void* AllocateAlign(std::size_t length, std::size_t alignment); + static void* Allocate(std::size_t length); template static T* Allocate() { return AllocateArray(1); } - template static T* AllocateArray(size_t count = 1) + template static T* AllocateArray(std::size_t count = 1) { return static_cast(Allocate(count * sizeof(T))); } @@ -159,7 +159,7 @@ namespace Utils static void FreeAlign(void* data); static void FreeAlign(const void* data); - static bool IsSet(void* mem, char chr, size_t length); + static bool IsSet(void* mem, char chr, std::size_t length); static bool IsBadReadPtr(const void* ptr); static bool IsBadCodePtr(const void* ptr); diff --git a/src/Utils/Stream.cpp b/src/Utils/Stream.cpp index 95d83dfc..dc50f51c 100644 --- a/src/Utils/Stream.cpp +++ b/src/Utils/Stream.cpp @@ -108,7 +108,7 @@ namespace Utils if (this->criticalSectionState != 0) { - MessageBoxA(nullptr, Utils::String::VA("Invalid critical section state '%i' for stream destruction!", this->criticalSectionState), "WARNING", MB_ICONEXCLAMATION); + MessageBoxA(nullptr, String::VA("Invalid critical section state '%i' for stream destruction!", this->criticalSectionState), "WARNING", MB_ICONEXCLAMATION); } }; @@ -163,7 +163,7 @@ namespace Utils if (this->isCriticalSection() && this->length() + (size * count) > this->capacity()) { - MessageBoxA(nullptr, Utils::String::VA("Potential stream reallocation during critical operation detected! Writing data of the length 0x%X exceeds the allocated stream size of 0x%X\n", (size * count), this->capacity()), "ERROR", MB_ICONERROR); + MessageBoxA(nullptr, String::VA("Potential stream reallocation during critical operation detected! Writing data of the length 0x%X exceeds the allocated stream size of 0x%X\n", (size * count), this->capacity()), "ERROR", MB_ICONERROR); __debugbreak(); } @@ -291,9 +291,9 @@ namespace Utils } #ifdef WRITE_LOGS - std::string data = Utils::String::VA("%*s%d\n", this->structLevel, "", size); - if (stream == Game::XFILE_BLOCK_RUNTIME) data = Utils::String::VA("%*s(%d)\n", this->structLevel, "", size); - Utils::IO::WriteFile("userraw/logs/zb_writes.log", data, true); + const auto* data = String::VA("%*s%u\n", this->structLevel, "", size); + if (stream == Game::XFILE_BLOCK_RUNTIME) data = String::VA("%*s(%u)\n", this->structLevel, "", size); + IO::WriteFile("userraw/logs/zb_writes.log", data, true); #endif } @@ -365,7 +365,7 @@ namespace Utils --this->criticalSectionState; } - bool Stream::isCriticalSection() + bool Stream::isCriticalSection() const { if (this->criticalSectionState < 0) { @@ -381,7 +381,7 @@ namespace Utils { if (this->structLevel >= 0) { - Utils::IO::WriteFile("userraw/logs/zb_writes.log", Utils::String::VA("%*s%s\n", this->structLevel++, "", structName), true); + IO::WriteFile("userraw/logs/zb_writes.log", String::VA("%*s%s\n", this->structLevel++, "", structName), true); } } @@ -393,7 +393,7 @@ namespace Utils return; } - Utils::IO::WriteFile("userraw/logs/zb_writes.log", Utils::String::VA("%*s-----\n", this->structLevel, ""), true); + IO::WriteFile("userraw/logs/zb_writes.log", String::VA("%*s-----\n", this->structLevel, ""), true); } #endif } diff --git a/src/Utils/Stream.hpp b/src/Utils/Stream.hpp index b63fc978..3739cc3a 100644 --- a/src/Utils/Stream.hpp +++ b/src/Utils/Stream.hpp @@ -1,7 +1,7 @@ #pragma once // write logs for ZoneBuilder -//#define WRITE_LOGS + #ifndef WRITE_LOGS // they take forever to run so only enable if needed #define SaveLogEnter(x) #define SaveLogExit() @@ -39,10 +39,44 @@ namespace Utils { return readArray(1); } + + template T* readArrayOnce(std::size_t count = 1) + { + constexpr auto POINTER = 255; + constexpr auto FOLLOWING = 254; + + auto b = static_cast(readByte()); + switch (b) + { + case POINTER: + { + auto ptr = read(); + auto* voidPtr = reinterpret_cast(ptr); + + if (allocator->isPointerMapped(voidPtr)) + { + return allocator->getPointer(voidPtr); + } + + throw std::runtime_error("Bad data: missing ptr"); + } + case FOLLOWING: + { + auto filePosition = position; + auto data = readArray(count); + allocator->mapPointer(reinterpret_cast(filePosition), data); + return data; + } + default: + throw std::runtime_error("Bad data"); + } + } + template T* readArray(std::size_t count = 1) { return static_cast(this->read(sizeof(T), count)); } + template T read() { T obj; @@ -67,7 +101,7 @@ namespace Utils unsigned int position; std::string buffer; std::map pointerMap; - Utils::Memory::Allocator* allocator; + Memory::Allocator* allocator; }; enum Alignment @@ -89,17 +123,19 @@ namespace Utils Stream(size_t size); ~Stream(); - std::size_t length() const; - std::size_t capacity() const; + [[nodiscard]] std::size_t length() const; + [[nodiscard]] std::size_t capacity() const; char* save(const void * _str, std::size_t size, std::size_t count = 1); char* save(Game::XFILE_BLOCK_TYPES stream, const void * _str, std::size_t size, std::size_t count); char* save(Game::XFILE_BLOCK_TYPES stream, int value, std::size_t count); - template inline char* save(T* object) + + template char* save(T* object) { return saveArray(object, 1); } - template inline char* saveArray(T* array, std::size_t count) + + template char* saveArray(T* array, std::size_t count) { return save(array, sizeof(T), count); } @@ -131,7 +167,8 @@ namespace Utils { return reinterpret_cast(this->at()); } - template static inline void ClearPointer(T** object) + + template static void ClearPointer(T** object) { *object = reinterpret_cast(-1); } @@ -151,7 +188,7 @@ namespace Utils // by editing the code though. void enterCriticalSection(); void leaveCriticalSection(); - bool isCriticalSection(); + bool isCriticalSection() const; // for recording zb writes #ifdef WRITE_LOGS diff --git a/src/Utils/String.cpp b/src/Utils/String.cpp index 502fe000..f14ecd31 100644 --- a/src/Utils/String.cpp +++ b/src/Utils/String.cpp @@ -5,7 +5,7 @@ namespace Utils::String { - const char *VA(const char *fmt, ...) + const char* VA(const char* fmt, ...) { static VAProvider<4, 100> globalProvider; static thread_local VAProvider<8, 256> provider; @@ -21,24 +21,26 @@ namespace Utils::String return result; } - std::string ToLower(std::string text) + std::string ToLower(const std::string& text) { - std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input) + std::string result; + std::ranges::transform(text, std::back_inserter(result), [](const unsigned char input) { return static_cast(std::tolower(input)); }); - return text; + return result; } - std::string ToUpper(std::string text) + std::string ToUpper(const std::string& text) { - std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input) + std::string result; + std::ranges::transform(text, std::back_inserter(result), [](const unsigned char input) { return static_cast(std::toupper(input)); }); - return text; + return result; } bool Compare(const std::string& lhs, const std::string& rhs) @@ -143,9 +145,9 @@ namespace Utils::String } // Trim from both ends - std::string& Trim(std::string& str) + void Trim(std::string& str) { - return LTrim(RTrim(str)); + LTrim(RTrim(str)); } std::string Convert(const std::wstring& wstr) diff --git a/src/Utils/String.hpp b/src/Utils/String.hpp index 7ff19f5c..88856d75 100644 --- a/src/Utils/String.hpp +++ b/src/Utils/String.hpp @@ -1,5 +1,8 @@ #pragma once +template +constexpr auto ARRAY_COUNT(Type(&)[n]) { return n; } + namespace Utils::String { template @@ -11,9 +14,9 @@ namespace Utils::String VAProvider() : currentBuffer(0) {} ~VAProvider() = default; - const char* get(const char* format, va_list ap) + [[nodiscard]] const char* get(const char* format, va_list ap) { - ++this->currentBuffer %= ARRAYSIZE(this->stringPool); + ++this->currentBuffer %= ARRAY_COUNT(this->stringPool); auto entry = &this->stringPool[this->currentBuffer]; if (!entry->size || !entry->buffer) @@ -70,33 +73,84 @@ namespace Utils::String Entry stringPool[Buffers]; }; - const char *VA(const char *fmt, ...); + template // This should display a nice "null" instead of a number + static void SanitizeFormatArgs(Arg& arg) + { + if constexpr (std::is_same_v || std::is_same_v) + { + if (arg == nullptr) + { + arg = const_cast("nullptr"); + } + } + } - std::string ToLower(std::string text); - std::string ToUpper(std::string text); - bool Compare(const std::string& lhs, const std::string& rhs); - std::vector Split(const std::string& str, char delim); + [[nodiscard]] const char* VA(const char* fmt, ...); + + template + [[nodiscard]] const char* Format(std::string_view fmt, Args&&... args) + { + static thread_local std::string vaBuffer; + vaBuffer.clear(); + + (SanitizeFormatArgs(args), ...); + std::vformat_to(std::back_inserter(vaBuffer), fmt, std::make_format_args(args...)); + return vaBuffer.data(); + } + + [[nodiscard]] std::string ToLower(const std::string& text); + [[nodiscard]] std::string ToUpper(const std::string& text); + + template + [[nodiscard]] OutputIter ApplyToLower(OutputIter container) + { + OutputIter result; + std::ranges::transform(container, std::back_inserter(result), [](const std::string& s) -> std::string + { + return ToLower(s); + }); + + return result; + } + + template + [[nodiscard]] OutputIter ApplyToUpper(OutputIter container) + { + OutputIter result; + std::ranges::transform(container, std::back_inserter(result), [](const std::string& s) -> std::string + { + return ToUpper(s); + }); + + return result; + } + + [[nodiscard]] bool Compare(const std::string& lhs, const std::string& rhs); + + [[nodiscard]] std::vector Split(const std::string& str, char delim); void Replace(std::string& str, const std::string& from, const std::string& to); - bool StartsWith(const std::string& haystack, const std::string& needle); - bool EndsWith(const std::string& haystack, const std::string& needle); - bool IsNumber(const std::string& str); + + [[nodiscard]] bool StartsWith(const std::string& haystack, const std::string& needle); + [[nodiscard]] bool EndsWith(const std::string& haystack, const std::string& needle); + + [[nodiscard]] bool IsNumber(const std::string& str); std::string& LTrim(std::string& str); std::string& RTrim(std::string& str); - std::string& Trim(std::string& str); + void Trim(std::string& str); - std::string Convert(const std::wstring& wstr); - std::wstring Convert(const std::string& str); + [[nodiscard]] std::string Convert(const std::wstring& wstr); + [[nodiscard]] std::wstring Convert(const std::string& str); - std::string FormatTimeSpan(int milliseconds); - std::string FormatBandwidth(std::size_t bytes, int milliseconds); + [[nodiscard]] std::string FormatTimeSpan(int milliseconds); + [[nodiscard]] std::string FormatBandwidth(std::size_t bytes, int milliseconds); - std::string DumpHex(const std::string& data, const std::string& separator = " "); + [[nodiscard]] std::string DumpHex(const std::string& data, const std::string& separator = " "); - std::string XOR(std::string str, char value); + [[nodiscard]] std::string XOR(std::string str, char value); - std::string EncodeBase64(const char* input, const unsigned long inputSize); - std::string EncodeBase64(const std::string& input); + [[nodiscard]] std::string EncodeBase64(const char* input, unsigned long inputSize); + [[nodiscard]] std::string EncodeBase64(const std::string& input); - std::string EncodeBase128(const std::string& input); + [[nodiscard]] std::string EncodeBase128(const std::string& input); } diff --git a/src/Utils/Thread.cpp b/src/Utils/Thread.cpp index e328ff30..c6e5423c 100644 --- a/src/Utils/Thread.cpp +++ b/src/Utils/Thread.cpp @@ -2,7 +2,7 @@ namespace Utils::Thread { - bool setName(const HANDLE t, const std::string& name) + bool SetName(const HANDLE t, const std::string& name) { const Library kernel32("kernel32.dll"); if (!kernel32) @@ -19,7 +19,7 @@ namespace Utils::Thread return SUCCEEDED(setDescription(t, String::Convert(name).data())); } - bool setName(const DWORD id, const std::string& name) + bool SetName(const DWORD id, const std::string& name) { auto* const t = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, id); if (!t) return false; @@ -29,20 +29,20 @@ namespace Utils::Thread CloseHandle(t); }); - return setName(t, name); + return SetName(t, name); } - bool setName(std::thread& t, const std::string& name) + bool SetName(std::thread& t, const std::string& name) { - return setName(t.native_handle(), name); + return SetName(t.native_handle(), name); } - bool setName(const std::string& name) + bool SetName(const std::string& name) { - return setName(GetCurrentThread(), name); + return SetName(GetCurrentThread(), name); } - std::vector getThreadIds() + std::vector GetThreadIds() { auto* const h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetCurrentProcessId()); if (h == INVALID_HANDLE_VALUE) @@ -79,9 +79,9 @@ namespace Utils::Thread return ids; } - void forEachThread(const std::function& callback) + void ForEachThread(const std::function& callback) { - const auto ids = getThreadIds(); + const auto ids = GetThreadIds(); for (const auto& id : ids) { @@ -98,9 +98,9 @@ namespace Utils::Thread } } - void suspendOtherThreads() + void SuspendOtherThreads() { - forEachThread([](const HANDLE thread) + ForEachThread([](const HANDLE thread) { if (GetThreadId(thread) != GetCurrentThreadId()) { @@ -109,9 +109,9 @@ namespace Utils::Thread }); } - void resumeOtherThreads() + void ResumeOtherThreads() { - forEachThread([](const HANDLE thread) + ForEachThread([](const HANDLE thread) { if (GetThreadId(thread) != GetCurrentThreadId()) { diff --git a/src/Utils/Thread.hpp b/src/Utils/Thread.hpp index 807633f4..084eb2ce 100644 --- a/src/Utils/Thread.hpp +++ b/src/Utils/Thread.hpp @@ -2,22 +2,22 @@ namespace Utils::Thread { - bool setName(HANDLE t, const std::string& name); - bool setName(DWORD id, const std::string& name); - bool setName(std::thread& t, const std::string& name); - bool setName(const std::string& name); + bool SetName(HANDLE t, const std::string& name); + bool SetName(DWORD id, const std::string& name); + bool SetName(std::thread& t, const std::string& name); + bool SetName(const std::string& name); template - std::thread createNamedThread(const std::string& name, Args&&... args) + std::thread CreateNamedThread(const std::string& name, Args&&... args) { auto t = std::thread(std::forward(args)...); - setName(t, name); + SetName(t, name); return t; } - std::vector getThreadIds(); - void forEachThread(const std::function& callback); + std::vector GetThreadIds(); + void ForEachThread(const std::function& callback); - void suspendOtherThreads(); - void resumeOtherThreads(); + void SuspendOtherThreads(); + void ResumeOtherThreads(); } diff --git a/src/Utils/Utils.hpp b/src/Utils/Utils.hpp index 702efd38..252def02 100644 --- a/src/Utils/Utils.hpp +++ b/src/Utils/Utils.hpp @@ -25,7 +25,8 @@ namespace Utils bool HasIntersection(unsigned int base1, unsigned int len1, unsigned int base2, unsigned int len2); - template void RotLeft(T& object, size_t bits) + template + void RotLeft(T& object, std::size_t bits) { bits %= sizeof(T) * 8; @@ -38,31 +39,40 @@ namespace Utils object |= T(negative) << ((sizeof(T) * 8 - 1 + bits) % (sizeof(T) * 8)); } - template void RotRight(T& object, size_t bits) + template + void RotRight(T& object, std::size_t bits) { bits %= (sizeof(T) * 8); RotLeft(object, ((sizeof(T) * 8) - bits)); } - template void Merge(std::vector* target, T* source, size_t length) + template + void Merge(std::vector* target, T* source, std::size_t length) { if (source) { - for (size_t i = 0; i < length; ++i) + for (std::size_t i = 0; i < length; ++i) { target->push_back(source[i]); } } } - template void Merge(std::vector* target, std::vector source) + template + void Merge(std::vector* target, std::vector source) { - for (auto &entry : source) + for (auto& entry : source) { target->push_back(entry); } } + template + bool Contains(const std::vector* haystack, T needle) + { + return std::ranges::find(*haystack, needle) != haystack->end(); + } + template using Slot = std::function; template class Signal