Merge branch 'develop' into IntermissionTime
This commit is contained in:
commit
966ac99f7b
5
.gitmodules
vendored
5
.gitmodules
vendored
@ -25,7 +25,10 @@
|
||||
[submodule "deps/protobuf"]
|
||||
path = deps/protobuf
|
||||
url = https://github.com/google/protobuf.git
|
||||
branch = 3.6.x
|
||||
branch = 3.11.x
|
||||
[submodule "deps/udis86"]
|
||||
path = deps/udis86
|
||||
url = https://github.com/vmt/udis86.git
|
||||
[submodule "deps/dxsdk"]
|
||||
path = deps/dxsdk
|
||||
url = https://github.com/devKlausS/dxsdk.git
|
||||
|
@ -4,7 +4,7 @@
|
||||
![issues](https://img.shields.io/github/issues/IW4x/iw4x-client.svg)
|
||||
[![build status](https://ci.appveyor.com/api/projects/status/rvljq0ooxen0oexm/branch/develop?svg=true)](https://ci.appveyor.com/project/iw4x/iw4x-client/branch/develop)
|
||||
[![discord](https://img.shields.io/endpoint?url=https://momo5502.com/iw4x/members-badge.php)](https://discord.gg/sKeVmR3)
|
||||
[![patreon](https://img.shields.io/badge/patreon-donate-blue.svg?logo=patreon)](https://www.patreon.com/iw4x)
|
||||
[![patreon](https://img.shields.io/badge/patreon-support-blue.svg?logo=patreon)](https://www.patreon.com/iw4x)
|
||||
|
||||
# IW4x: Client
|
||||
|
||||
@ -40,7 +40,6 @@
|
||||
| `--disable-bitmessage` | Disable use of BitMessage completely. |
|
||||
| `--disable-base128` | Disable base128 encoding for minidumps. |
|
||||
| `--no-new-structure` | Do not use new virtual path structure (separating headers and source files). |
|
||||
| `--enable-dxsdk` | Enable DirectX SDK (required for GfxMap exporting). |
|
||||
|
||||
## Disclaimer
|
||||
|
||||
|
1
deps/dxsdk
vendored
Submodule
1
deps/dxsdk
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 996cf740444ce56178e6bc32e7fbe2c8b0f40f08
|
2
deps/json11
vendored
2
deps/json11
vendored
@ -1 +1 @@
|
||||
Subproject commit 8ccf1f0c5ecab6151a65f216e7eeccd8588e5457
|
||||
Subproject commit e2e3a11e99672b018e0e0657867e6a3439e180cf
|
2
deps/libtomcrypt
vendored
2
deps/libtomcrypt
vendored
@ -1 +1 @@
|
||||
Subproject commit a1f6312416ef6cd183ee62db58b640dc2d7ec1f4
|
||||
Subproject commit 1937f412605e1b04ddb41ef9c2f2f0aab7e61548
|
2
deps/libtommath
vendored
2
deps/libtommath
vendored
@ -1 +1 @@
|
||||
Subproject commit 13444a8af2d077eda8fd0be017cac2dad20465dc
|
||||
Subproject commit 6ac0b0c1b69b9a88e1b3b3002c2e3a9062ae99b4
|
2
deps/mongoose
vendored
2
deps/mongoose
vendored
@ -1 +1 @@
|
||||
Subproject commit ff4649fe42bfe93835a0bac77fbe8ab84c8b36e4
|
||||
Subproject commit cb602f178ccea7f0c790cf5510f7a29c017db954
|
2
deps/pdcurses
vendored
2
deps/pdcurses
vendored
@ -1 +1 @@
|
||||
Subproject commit b12c4a5212e97cf83f0074618c1473253fad4131
|
||||
Subproject commit 618e0aaa31b4728eb4df78ec4de6c2b873908eda
|
2
deps/protobuf
vendored
2
deps/protobuf
vendored
@ -1 +1 @@
|
||||
Subproject commit 66dc42d891a4fc8e9190c524fd67961688a37bbe
|
||||
Subproject commit 63cfdafacba6141717a2df97fc123dc0c14ba7c4
|
2
deps/zlib
vendored
2
deps/zlib
vendored
@ -1 +1 @@
|
||||
Subproject commit 79baebe50e4d6b73ae1f8b603f0ef41300110aa3
|
||||
Subproject commit d71dc66fa8a153fb6e7c626847095d9697a6cf42
|
35
premake/dxsdk.lua
Normal file
35
premake/dxsdk.lua
Normal file
@ -0,0 +1,35 @@
|
||||
dxsdk = {
|
||||
settings = nil
|
||||
}
|
||||
|
||||
function dxsdk.setup(settings)
|
||||
if not settings.source then error("Missing source.") end
|
||||
|
||||
dxsdk.settings = settings
|
||||
|
||||
if not dxsdk.settings.defines then dxsdk.settings.defines = {} end
|
||||
end
|
||||
|
||||
function dxsdk.import()
|
||||
if not dxsdk.settings then error("You need to call dxsdk.setup first") end
|
||||
|
||||
--filter "platforms:*32"
|
||||
libdirs { path.join(dxsdk.settings.source, "Lib/x86") }
|
||||
|
||||
--filter "platforms:*64"
|
||||
-- libdirs { path.join(dxsdk.settings.source, "Lib/x64") }
|
||||
|
||||
--filter {}
|
||||
|
||||
dxsdk.includes()
|
||||
end
|
||||
|
||||
function dxsdk.includes()
|
||||
if not dxsdk.settings then error("You need to call dxsdk.setup first") end
|
||||
|
||||
includedirs { path.join(dxsdk.settings.source, "Include") }
|
||||
defines(dxsdk.settings.defines)
|
||||
end
|
||||
|
||||
function dxsdk.project()
|
||||
end
|
19
premake5.lua
19
premake5.lua
@ -74,11 +74,6 @@ newoption {
|
||||
description = "Upload minidumps even for Debug builds."
|
||||
}
|
||||
|
||||
newoption {
|
||||
trigger = "enable-dxsdk",
|
||||
description = "Enable DirectX SDK (required for GfxMap exporting)."
|
||||
}
|
||||
|
||||
newaction {
|
||||
trigger = "version",
|
||||
description = "Returns the version string for the current commit of the source code.",
|
||||
@ -194,6 +189,7 @@ require "premake/protobuf"
|
||||
require "premake/zlib"
|
||||
require "premake/udis86"
|
||||
require "premake/iw4mvm"
|
||||
require "premake/dxsdk"
|
||||
|
||||
json11.setup
|
||||
{
|
||||
@ -248,8 +244,13 @@ iw4mvm.setup
|
||||
},
|
||||
source = path.join(depsBasePath, "iw4mvm"),
|
||||
}
|
||||
dxsdk.setup
|
||||
{
|
||||
source = path.join(depsBasePath, "dxsdk"),
|
||||
}
|
||||
|
||||
workspace "iw4x"
|
||||
startproject "iw4x"
|
||||
location "./build"
|
||||
objdir "%{wks.location}/obj"
|
||||
targetdir "%{wks.location}/bin/%{cfg.buildcfg}"
|
||||
@ -323,11 +324,6 @@ workspace "iw4x"
|
||||
if _OPTIONS["force-exception-handler"] then
|
||||
defines { "FORCE_EXCEPTION_HANDLER" }
|
||||
end
|
||||
if _OPTIONS["enable-dxsdk"] then
|
||||
defines { "ENABLE_DXSDK" }
|
||||
includedirs { "%DXSDK_DIR%Include" }
|
||||
libdirs { "%DXSDK_DIR%Lib/x86" }
|
||||
end
|
||||
|
||||
-- Pre-compiled header
|
||||
pchheader "STDInclude.hpp" -- must be exactly same as used in #include directives
|
||||
@ -344,6 +340,7 @@ workspace "iw4x"
|
||||
zlib.import()
|
||||
udis86.import()
|
||||
--iw4mvm.import()
|
||||
dxsdk.import()
|
||||
|
||||
-- fix vpaths for protobuf sources
|
||||
vpaths
|
||||
@ -462,7 +459,7 @@ workspace "*"
|
||||
buildoptions {
|
||||
"/std:c++latest"
|
||||
}
|
||||
systemversion "10.0.17763.0"
|
||||
systemversion "latest"
|
||||
defines { "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS" }
|
||||
|
||||
rule "ProtobufCompiler"
|
||||
|
@ -60,6 +60,7 @@ namespace Components
|
||||
Loader::Register(new Console());
|
||||
Loader::Register(new Friends());
|
||||
Loader::Register(new IPCPipe());
|
||||
Loader::Register(new MapDump());
|
||||
Loader::Register(new ModList());
|
||||
Loader::Register(new Monitor());
|
||||
Loader::Register(new Network());
|
||||
@ -101,6 +102,8 @@ namespace Components
|
||||
Loader::Register(new ConnectProtocol());
|
||||
Loader::Register(new StartupMessages());
|
||||
|
||||
Loader::Register(new Client());
|
||||
|
||||
Loader::Pregame = false;
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,7 @@ namespace Components
|
||||
#include "Modules/Logger.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"
|
||||
@ -129,3 +130,5 @@ namespace Components
|
||||
#include "Modules/ConnectProtocol.hpp"
|
||||
#include "Modules/StartupMessages.hpp"
|
||||
#include "Modules/Stats.hpp"
|
||||
|
||||
#include "Modules/Client.hpp"
|
@ -9,7 +9,9 @@ namespace Components
|
||||
Utils::Cryptography::ECC::Key Auth::GuidKey;
|
||||
|
||||
std::vector<std::uint64_t> Auth::BannedUids = {
|
||||
0xf4d2c30b712ac6e3
|
||||
0xf4d2c30b712ac6e3,
|
||||
0xf7e33c4081337fa3,
|
||||
0x6f5597f103cc50e9
|
||||
};
|
||||
|
||||
void Auth::Frame()
|
||||
|
176
src/Components/Modules/Client.cpp
Normal file
176
src/Components/Modules/Client.cpp
Normal file
@ -0,0 +1,176 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
void Client::AddFunctions()
|
||||
{
|
||||
//File functions
|
||||
|
||||
Script::AddFunction("fileWrite", [](Game::scr_entref_t) // gsc: fileWrite(<filepath>, <string>, <mode>)
|
||||
{
|
||||
std::string path = Game::Scr_GetString(0);
|
||||
auto text = Game::Scr_GetString(1);
|
||||
auto mode = Game::Scr_GetString(2);
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileWrite: filepath not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<const char*> queryStrings = { R"(..)", R"(../)", R"(..\)" };
|
||||
for (auto i = 0u; i < queryStrings.size(); i++)
|
||||
{
|
||||
if (path.find(queryStrings[i]) != std::string::npos)
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileWrite: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode != "append"s && mode != "write"s)
|
||||
{
|
||||
Game::Com_Printf(0, "^3fileWrite: mode not defined or was wrong, defaulting to 'write'\n");
|
||||
mode = "write";
|
||||
}
|
||||
|
||||
if (mode == "write"s)
|
||||
{
|
||||
FileSystem::FileWriter(path).write(text);
|
||||
}
|
||||
else if (mode == "append"s)
|
||||
{
|
||||
FileSystem::FileWriter(path, true).write(text);
|
||||
}
|
||||
});
|
||||
|
||||
Script::AddFunction("fileRead", [](Game::scr_entref_t) // gsc: fileRead(<filepath>)
|
||||
{
|
||||
std::string path = Game::Scr_GetString(0);
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileRead: filepath not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<const char*> queryStrings = { R"(..)", R"(../)", R"(..\)" };
|
||||
for (auto i = 0u; i < queryStrings.size(); i++)
|
||||
{
|
||||
if (path.find(queryStrings[i]) != std::string::npos)
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileRead: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!FileSystem::FileReader(path).exists())
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileRead: file not found!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::Scr_AddString(FileSystem::FileReader(path).getBuffer().data());
|
||||
});
|
||||
|
||||
Script::AddFunction("fileExists", [](Game::scr_entref_t) // gsc: fileExists(<filepath>)
|
||||
{
|
||||
std::string path = Game::Scr_GetString(0);
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileExists: filepath not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<const char*> queryStrings = { R"(..)", R"(../)", R"(..\)" };
|
||||
for (auto i = 0u; i < queryStrings.size(); i++)
|
||||
{
|
||||
if (path.find(queryStrings[i]) != std::string::npos)
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileExists: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Game::Scr_AddInt(FileSystem::FileReader(path).exists());
|
||||
});
|
||||
|
||||
Script::AddFunction("fileRemove", [](Game::scr_entref_t) // gsc: fileRemove(<filepath>)
|
||||
{
|
||||
std::string path = Game::Scr_GetString(0);
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileRemove: filepath not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<const char*> queryStrings = { R"(..)", R"(../)", R"(..\)" };
|
||||
for (auto i = 0u; i < queryStrings.size(); i++)
|
||||
{
|
||||
if (path.find(queryStrings[i]) != std::string::npos)
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileRemove: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto p = std::filesystem::path(path);
|
||||
std::string folder = p.parent_path().string();
|
||||
std::string file = p.filename().string();
|
||||
Game::Scr_AddInt(FileSystem::DeleteFile(folder, file));
|
||||
});
|
||||
}
|
||||
|
||||
void Client::AddMethods()
|
||||
{
|
||||
// Client methods
|
||||
|
||||
Script::AddFunction("getIp", [](Game::scr_entref_t id) // gsc: self getIp()
|
||||
{
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
|
||||
if (client->state >= 3)
|
||||
{
|
||||
std::string ip = Game::NET_AdrToString(client->addr);
|
||||
if (ip.find_first_of(":") != std::string::npos)
|
||||
ip.erase(ip.begin() + ip.find_first_of(":"), ip.end()); // erase port
|
||||
Game::Scr_AddString(ip.data());
|
||||
}
|
||||
});
|
||||
|
||||
Script::AddFunction("getPing", [](Game::scr_entref_t id) // gsc: self getPing()
|
||||
{
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
|
||||
if (client->state >= 3)
|
||||
{
|
||||
int ping = (int)client->ping;
|
||||
Game::Scr_AddInt(ping);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Client::AddCommands()
|
||||
{
|
||||
Command::Add("NULL", [](Command::Params*)
|
||||
{
|
||||
return NULL;
|
||||
});
|
||||
}
|
||||
|
||||
Client::Client()
|
||||
{
|
||||
Client::AddFunctions();
|
||||
Client::AddMethods();
|
||||
Client::AddCommands();
|
||||
}
|
||||
|
||||
Client::~Client()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
17
src/Components/Modules/Client.hpp
Normal file
17
src/Components/Modules/Client.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class Client : public Component
|
||||
{
|
||||
public:
|
||||
Client();
|
||||
~Client();
|
||||
|
||||
private:
|
||||
|
||||
static void AddFunctions();
|
||||
static void AddMethods();
|
||||
static void AddCommands();
|
||||
};
|
||||
}
|
@ -748,7 +748,7 @@ namespace Components
|
||||
{
|
||||
if (Dedicated::IsEnabled()) return;
|
||||
|
||||
Dvar::Register<bool>("r_useD3D9Ex", true, Game::dvar_flag::DVAR_FLAG_SAVED, "Use extended d3d9 interface!");
|
||||
Dvar::Register<bool>("r_useD3D9Ex", false, Game::dvar_flag::DVAR_FLAG_SAVED, "Use extended d3d9 interface!");
|
||||
|
||||
// Hook Interface creation
|
||||
Utils::Hook::Set(0x6D74D0, D3D9Ex::Direct3DCreate9Stub);
|
||||
|
@ -120,65 +120,7 @@ namespace Components
|
||||
if (ev == MG_EV_RECV)
|
||||
{
|
||||
size_t bytes = static_cast<size_t>(*reinterpret_cast<int*>(ev_data));
|
||||
fDownload->receivedBytes += bytes;
|
||||
fDownload->download->downBytes += bytes;
|
||||
fDownload->download->timeStampBytes += bytes;
|
||||
|
||||
double progress = 0;
|
||||
if (fDownload->download->totalBytes)
|
||||
{
|
||||
progress = (100.0 / fDownload->download->totalBytes) * fDownload->download->downBytes;
|
||||
}
|
||||
|
||||
static unsigned int dlIndex, dlSize, dlProgress;
|
||||
dlIndex = fDownload->index + 1;
|
||||
dlSize = fDownload->download->files.size();
|
||||
dlProgress = static_cast<unsigned int>(progress);
|
||||
|
||||
static bool framePushed = false;
|
||||
|
||||
if (!framePushed)
|
||||
{
|
||||
framePushed = true;
|
||||
Scheduler::Once([]()
|
||||
{
|
||||
framePushed = false;
|
||||
Dvar::Var("ui_dl_progress").set(Utils::String::VA("(%d/%d) %d%%", dlIndex, dlSize, dlProgress));
|
||||
});
|
||||
}
|
||||
|
||||
int delta = Game::Sys_Milliseconds() - fDownload->download->lastTimeStamp;
|
||||
if (delta > 300)
|
||||
{
|
||||
bool doFormat = fDownload->download->lastTimeStamp != 0;
|
||||
fDownload->download->lastTimeStamp = Game::Sys_Milliseconds();
|
||||
|
||||
size_t dataLeft = fDownload->download->totalBytes - fDownload->download->downBytes;
|
||||
|
||||
int timeLeft = 0;
|
||||
if (fDownload->download->timeStampBytes)
|
||||
{
|
||||
double timeLeftD = ((1.0 * dataLeft) / fDownload->download->timeStampBytes) * delta;
|
||||
timeLeft = static_cast<int>(timeLeftD);
|
||||
}
|
||||
|
||||
if (doFormat)
|
||||
{
|
||||
static size_t dlTsBytes;
|
||||
static int dlDelta, dlTimeLeft;
|
||||
dlTimeLeft = timeLeft;
|
||||
dlDelta = delta;
|
||||
dlTsBytes = fDownload->download->timeStampBytes;
|
||||
|
||||
Scheduler::Once([]()
|
||||
{
|
||||
Dvar::Var("ui_dl_timeLeft").set(Utils::String::FormatTimeSpan(dlTimeLeft));
|
||||
Dvar::Var("ui_dl_transRate").set(Utils::String::FormatBandwidth(dlTsBytes, dlDelta));
|
||||
});
|
||||
}
|
||||
|
||||
fDownload->download->timeStampBytes = 0;
|
||||
}
|
||||
Download::DownloadProgress(fDownload, bytes);
|
||||
}
|
||||
|
||||
if (ev == MG_EV_HTTP_REPLY)
|
||||
@ -259,7 +201,7 @@ namespace Components
|
||||
+ (download->isPrivate ? ("?password=" + download->hashedPassword) : "");
|
||||
}
|
||||
|
||||
Logger::Print("Downloading from url %s", url.data());
|
||||
Logger::Print("Downloading from url %s\n", url.data());
|
||||
|
||||
Download::FileDownload fDownload;
|
||||
fDownload.file = file;
|
||||
@ -270,17 +212,42 @@ namespace Components
|
||||
|
||||
Utils::String::Replace(url, " ", "%20");
|
||||
|
||||
// Just a speedtest ;)
|
||||
//download->totalBytes = 1048576000;
|
||||
//url = "http://speed.hetzner.de/1GB.bin";
|
||||
|
||||
download->valid = true;
|
||||
ZeroMemory(&download->mgr, sizeof download->mgr);
|
||||
/*ZeroMemory(&download->mgr, sizeof download->mgr);
|
||||
mg_mgr_init(&download->mgr, &fDownload);
|
||||
mg_connect_http(&download->mgr, Download::DownloadHandler, url.data(), nullptr, nullptr);
|
||||
|
||||
while (fDownload.downloading && !fDownload.download->terminateThread)
|
||||
{
|
||||
mg_mgr_poll(&download->mgr, 0);
|
||||
mg_mgr_poll(&download->mgr, 100);
|
||||
}
|
||||
|
||||
mg_mgr_free(&download->mgr);
|
||||
mg_mgr_free(&download->mgr);*/
|
||||
|
||||
fDownload.downloading = true;
|
||||
|
||||
Utils::WebIO webIO;
|
||||
webIO.setProgressCallback([&fDownload, &webIO](size_t bytes, size_t)
|
||||
{
|
||||
if(!fDownload.downloading || fDownload.download->terminateThread)
|
||||
{
|
||||
webIO.cancelDownload();
|
||||
return;
|
||||
}
|
||||
|
||||
Download::DownloadProgress(&fDownload, bytes - fDownload.receivedBytes);
|
||||
});
|
||||
|
||||
bool result = false;
|
||||
fDownload.buffer = webIO.get(url, &result);
|
||||
if (!result) fDownload.buffer.clear();
|
||||
|
||||
fDownload.downloading = false;
|
||||
|
||||
download->valid = false;
|
||||
|
||||
if (fDownload.buffer.size() != file.size || Utils::Cryptography::SHA256::Compute(fDownload.buffer, true) != file.hash)
|
||||
@ -430,6 +397,69 @@ namespace Components
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Download::DownloadProgress(FileDownload* fDownload, size_t bytes)
|
||||
{
|
||||
fDownload->receivedBytes += bytes;
|
||||
fDownload->download->downBytes += bytes;
|
||||
fDownload->download->timeStampBytes += bytes;
|
||||
|
||||
static volatile bool framePushed = false;
|
||||
|
||||
if (!framePushed)
|
||||
{
|
||||
double progress = 0;
|
||||
if (fDownload->download->totalBytes)
|
||||
{
|
||||
progress = (100.0 / fDownload->download->totalBytes) * fDownload->download->downBytes;
|
||||
}
|
||||
|
||||
static unsigned int dlIndex, dlSize, dlProgress;
|
||||
dlIndex = fDownload->index + 1;
|
||||
dlSize = fDownload->download->files.size();
|
||||
dlProgress = static_cast<unsigned int>(progress);
|
||||
|
||||
framePushed = true;
|
||||
Scheduler::Once([]()
|
||||
{
|
||||
framePushed = false;
|
||||
Dvar::Var("ui_dl_progress").set(Utils::String::VA("(%d/%d) %d%%", dlIndex, dlSize, dlProgress));
|
||||
});
|
||||
}
|
||||
|
||||
int delta = Game::Sys_Milliseconds() - fDownload->download->lastTimeStamp;
|
||||
if (delta > 300)
|
||||
{
|
||||
bool doFormat = fDownload->download->lastTimeStamp != 0;
|
||||
fDownload->download->lastTimeStamp = Game::Sys_Milliseconds();
|
||||
|
||||
auto dataLeft = fDownload->download->totalBytes - fDownload->download->downBytes;
|
||||
|
||||
int timeLeft = 0;
|
||||
if (fDownload->download->timeStampBytes)
|
||||
{
|
||||
double timeLeftD = ((1.0 * dataLeft) / fDownload->download->timeStampBytes) * delta;
|
||||
timeLeft = static_cast<int>(timeLeftD);
|
||||
}
|
||||
|
||||
if (doFormat)
|
||||
{
|
||||
static size_t dlTsBytes;
|
||||
static int dlDelta, dlTimeLeft;
|
||||
dlTimeLeft = timeLeft;
|
||||
dlDelta = delta;
|
||||
dlTsBytes = fDownload->download->timeStampBytes;
|
||||
|
||||
Scheduler::Once([]()
|
||||
{
|
||||
Dvar::Var("ui_dl_timeLeft").set(Utils::String::FormatTimeSpan(dlTimeLeft));
|
||||
Dvar::Var("ui_dl_transRate").set(Utils::String::FormatBandwidth(dlTsBytes, dlDelta));
|
||||
});
|
||||
}
|
||||
|
||||
fDownload->download->timeStampBytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Download::VerifyPassword(mg_connection *nc, http_message* message)
|
||||
{
|
||||
std::string g_password = Dvar::Var("g_password").get<std::string>();
|
||||
|
@ -26,7 +26,7 @@ namespace Components
|
||||
bool terminateThread;
|
||||
bool isMap;
|
||||
bool isPrivate;
|
||||
mg_mgr mgr;
|
||||
//mg_mgr mgr;
|
||||
Network::Address target;
|
||||
std::string hashedPassword;
|
||||
std::string mod;
|
||||
@ -64,7 +64,7 @@ namespace Components
|
||||
if (this->valid)
|
||||
{
|
||||
this->valid = false;
|
||||
mg_mgr_free(&(this->mgr));
|
||||
//mg_mgr_free(&(this->mgr));
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -212,6 +212,8 @@ namespace Components
|
||||
static bool Terminate;
|
||||
static bool ServerRunning;
|
||||
|
||||
static void DownloadProgress(FileDownload* fDownload, size_t bytes);
|
||||
|
||||
static bool VerifyPassword(mg_connection *nc, http_message* message);
|
||||
|
||||
static void EventHandler(mg_connection *nc, int ev, void *ev_data);
|
||||
|
@ -138,6 +138,10 @@ namespace Components
|
||||
{
|
||||
return Game::Dvar_RegisterInt(name, value, min, max, flag.val, description);
|
||||
}
|
||||
template<> static Dvar::Var Dvar::Register(const char* name, float value, float min, float max, Dvar::Flag flag, const char* description)
|
||||
{
|
||||
return Game::Dvar_RegisterFloat(name, value, min, max, flag.val, description);
|
||||
}
|
||||
|
||||
void Dvar::OnInit(Utils::Slot<Scheduler::Callback> callback)
|
||||
{
|
||||
|
@ -76,7 +76,7 @@ namespace Components
|
||||
errorStr = Utils::String::VA("Fatal error (0x%08X) at 0x%08X.", ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo->ExceptionRecord->ExceptionAddress);
|
||||
}
|
||||
|
||||
Exception::SuspendProcess();
|
||||
//Exception::SuspendProcess();
|
||||
|
||||
bool doFullDump = Flags::HasFlag("bigdumps") || Flags::HasFlag("reallybigdumps");
|
||||
/*if (!doFullDump)
|
||||
@ -131,15 +131,15 @@ namespace Components
|
||||
TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode);
|
||||
}
|
||||
|
||||
#ifndef DISABLE_ANTICHEAT
|
||||
AntiCheat::InstallLibHook();
|
||||
#endif
|
||||
|
||||
if (ExceptionInfo->ExceptionRecord->ExceptionFlags == EXCEPTION_NONCONTINUABLE)
|
||||
//if (ExceptionInfo->ExceptionRecord->ExceptionFlags == EXCEPTION_NONCONTINUABLE)
|
||||
{
|
||||
TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode);
|
||||
}
|
||||
|
||||
#ifndef DISABLE_ANTICHEAT
|
||||
AntiCheat::InstallLibHook();
|
||||
#endif
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
|
@ -172,11 +172,11 @@ namespace Components
|
||||
return fileList;
|
||||
}
|
||||
|
||||
void FileSystem::DeleteFile(const std::string& folder, const std::string& file)
|
||||
bool FileSystem::DeleteFile(const std::string& folder, const std::string& file)
|
||||
{
|
||||
char path[MAX_PATH] = { 0 };
|
||||
Game::FS_BuildPathToFile(Dvar::Var("fs_basepath").get<const char*>(), reinterpret_cast<char*>(0x63D0BB8), Utils::String::VA("%s/%s", folder.data(), file.data()), reinterpret_cast<char**>(&path));
|
||||
Game::FS_Remove(path);
|
||||
return Game::FS_Remove(path);
|
||||
}
|
||||
|
||||
int FileSystem::ReadFile(const char* path, char** buffer)
|
||||
@ -263,10 +263,18 @@ namespace Components
|
||||
void FileSystem::FsRestartSync(int a1, int a2)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(FileSystem::FSMutex);
|
||||
Maps::GetUserMap()->freeIwd();
|
||||
Utils::Hook::Call<void(int, int)>(0x461A50)(a1, a2); // FS_Restart
|
||||
Maps::GetUserMap()->reloadIwd();
|
||||
}
|
||||
|
||||
void FileSystem::FsShutdownSync(int a1)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(FileSystem::FSMutex);
|
||||
Maps::GetUserMap()->freeIwd();
|
||||
Utils::Hook::Call<void(int)>(0x4A46C0)(a1); // FS_Shutdown
|
||||
}
|
||||
|
||||
void FileSystem::DelayLoadImagesSync()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(FileSystem::FSMutex);
|
||||
@ -322,6 +330,10 @@ namespace Components
|
||||
Utils::Hook(0x4C8609, FileSystem::FsRestartSync, HOOK_CALL).install()->quick(); // FS_ConditionalRestart
|
||||
Utils::Hook(0x5AC68E, FileSystem::FsRestartSync, HOOK_CALL).install()->quick(); // CL_ParseServerMessage
|
||||
|
||||
// Synchronize filesystem stops
|
||||
Utils::Hook(0x461A55, FileSystem::FsShutdownSync, HOOK_CALL).install()->quick(); // FS_Restart
|
||||
Utils::Hook(0x4D40DB, FileSystem::FsShutdownSync, HOOK_CALL).install()->quick(); // Com_Quitf
|
||||
|
||||
// Synchronize db image loading
|
||||
Utils::Hook(0x415AB8, FileSystem::DelayLoadImagesSync, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x4D32BC, FileSystem::LoadTextureSync, HOOK_CALL).install()->quick();
|
||||
|
@ -90,7 +90,7 @@ namespace Components
|
||||
|
||||
static std::vector<std::string> GetFileList(const std::string& path, const std::string& extension);
|
||||
static std::vector<std::string> GetSysFileList(const std::string& path, const std::string& extension, bool folders = false);
|
||||
static void DeleteFile(const std::string& folder, const std::string& file);
|
||||
static bool DeleteFile(const std::string& folder, const std::string& file);
|
||||
|
||||
private:
|
||||
static std::mutex Mutex;
|
||||
@ -109,6 +109,7 @@ namespace Components
|
||||
|
||||
static void FsStartupSync(const char* a1);
|
||||
static void FsRestartSync(int a1, int a2);
|
||||
static void FsShutdownSync(int a1);
|
||||
static void DelayLoadImagesSync();
|
||||
static int LoadTextureSync(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image);
|
||||
|
||||
|
451
src/Components/Modules/MapDump.cpp
Normal file
451
src/Components/Modules/MapDump.cpp
Normal file
@ -0,0 +1,451 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class MapDumper
|
||||
{
|
||||
public:
|
||||
MapDumper(Game::GfxWorld* world) : world_(world)
|
||||
{
|
||||
}
|
||||
|
||||
void dump()
|
||||
{
|
||||
if (!this->world_) return;
|
||||
|
||||
Logger::Print("Exporting '%s'...\n", this->world_->baseName);
|
||||
|
||||
this->parseVertices();
|
||||
this->parseFaces();
|
||||
this->parseStaticModels();
|
||||
|
||||
this->write();
|
||||
}
|
||||
|
||||
private:
|
||||
struct Vertex
|
||||
{
|
||||
Game::vec3_t coordinate;
|
||||
Game::vec2_t texture;
|
||||
Game::vec3_t normal;
|
||||
};
|
||||
|
||||
struct Face
|
||||
{
|
||||
int a{};
|
||||
int b{};
|
||||
int c{};
|
||||
};
|
||||
|
||||
struct FaceList
|
||||
{
|
||||
std::vector<Face> indices{};
|
||||
};
|
||||
|
||||
class File
|
||||
{
|
||||
public:
|
||||
File() {}
|
||||
|
||||
File(const std::string& file)
|
||||
{
|
||||
Utils::IO::WriteFile(file, {});
|
||||
this->stream_ = std::ofstream(file, std::ofstream::out);
|
||||
}
|
||||
|
||||
void append(const std::string& str)
|
||||
{
|
||||
this->stream_.write(str.data(), str.size());
|
||||
}
|
||||
|
||||
private:
|
||||
std::ofstream stream_{};
|
||||
};
|
||||
|
||||
Game::GfxWorld* world_{};
|
||||
std::vector<Vertex> vertices_{};
|
||||
std::unordered_map<Game::Material*, FaceList> faces_{};
|
||||
std::vector<Game::Material*> facesOrder_{};
|
||||
|
||||
File object_{};
|
||||
File material_{};
|
||||
|
||||
void transformAxes(Game::vec3_t& vec) const
|
||||
{
|
||||
std::swap(vec[0], vec[1]);
|
||||
std::swap(vec[1], vec[2]);
|
||||
}
|
||||
|
||||
void parseVertices()
|
||||
{
|
||||
Logger::Print("Parsing vertices...\n");
|
||||
|
||||
for (unsigned int i = 0; i < this->world_->draw.vertexCount; ++i)
|
||||
{
|
||||
const auto* vertex = &this->world_->draw.vd.vertices[i];
|
||||
|
||||
Vertex v{};
|
||||
|
||||
v.coordinate[0] = vertex->xyz[0];
|
||||
v.coordinate[1] = vertex->xyz[1];
|
||||
v.coordinate[2] = vertex->xyz[2];
|
||||
this->transformAxes(v.coordinate);
|
||||
|
||||
v.texture[0] = vertex->texCoord[0];
|
||||
v.texture[1] = -vertex->texCoord[1];
|
||||
|
||||
Game::Vec3UnpackUnitVec(vertex->normal, &v.normal);
|
||||
this->transformAxes(v.normal);
|
||||
|
||||
this->vertices_.push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
void parseFaces()
|
||||
{
|
||||
Logger::Print("Parsing faces...\n");
|
||||
|
||||
for (unsigned int i = 0; i < this->world_->dpvs.staticSurfaceCount; ++i)
|
||||
{
|
||||
const auto* surface = &this->world_->dpvs.surfaces[i];
|
||||
|
||||
const unsigned int vertOffset = surface->tris.firstVertex + 1;
|
||||
const unsigned int indexOffset = surface->tris.baseIndex;
|
||||
|
||||
auto& f = this->getFaceList(surface->material);
|
||||
|
||||
for (unsigned short j = 0; j < surface->tris.triCount; ++j)
|
||||
{
|
||||
Face face{};
|
||||
face.a = this->world_->draw.indices[indexOffset + j * 3 + 0] + vertOffset;
|
||||
face.b = this->world_->draw.indices[indexOffset + j * 3 + 1] + vertOffset;
|
||||
face.c = this->world_->draw.indices[indexOffset + j * 3 + 2] + vertOffset;
|
||||
|
||||
f.indices.push_back(face);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FaceList& getFaceList(Game::Material* material)
|
||||
{
|
||||
auto& faceList = this->faces_[material];
|
||||
|
||||
if (this->facesOrder_.size() < this->faces_.size())
|
||||
{
|
||||
this->facesOrder_.push_back(material);
|
||||
}
|
||||
|
||||
return faceList;
|
||||
}
|
||||
|
||||
void performWorldTransformation(const Game::GfxPackedPlacement& placement, Vertex& v) const
|
||||
{
|
||||
Game::MatrixVecMultiply(placement.axis, v.normal, v.normal);
|
||||
Game::Vec3Normalize(v.normal);
|
||||
|
||||
Game::MatrixVecMultiply(placement.axis, v.coordinate, v.coordinate);
|
||||
v.coordinate[0] = v.coordinate[0] * placement.scale + placement.origin[0];
|
||||
v.coordinate[1] = v.coordinate[1] * placement.scale + placement.origin[1];
|
||||
v.coordinate[2] = v.coordinate[2] * placement.scale + placement.origin[2];
|
||||
}
|
||||
|
||||
std::vector<Vertex> parseSurfaceVertices(const Game::XSurface* surface, const Game::GfxPackedPlacement& placement)
|
||||
{
|
||||
std::vector<Vertex> vertices;
|
||||
|
||||
for (unsigned short j = 0; j < surface->vertCount; j++)
|
||||
{
|
||||
const auto *vertex = &surface->verts0[j];
|
||||
|
||||
Vertex v{};
|
||||
|
||||
v.coordinate[0] = vertex->xyz[0];
|
||||
v.coordinate[1] = vertex->xyz[1];
|
||||
v.coordinate[2] = vertex->xyz[2];
|
||||
|
||||
// Why...
|
||||
Game::Vec2UnpackTexCoords(vertex->texCoord, &v.texture);
|
||||
std::swap(v.texture[0], v.texture[1]);
|
||||
v.texture[1] *= -1;
|
||||
|
||||
Game::Vec3UnpackUnitVec(vertex->normal, &v.normal);
|
||||
|
||||
this->performWorldTransformation(placement, v);
|
||||
this->transformAxes(v.coordinate);
|
||||
this->transformAxes(v.normal);
|
||||
|
||||
vertices.push_back(v);
|
||||
}
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
std::vector<Face> parseSurfaceFaces(const Game::XSurface* surface) const
|
||||
{
|
||||
std::vector<Face> faces;
|
||||
|
||||
for (unsigned short j = 0; j < surface->triCount; ++j)
|
||||
{
|
||||
Face face{};
|
||||
face.a = surface->triIndices[j * 3 + 0];
|
||||
face.b = surface->triIndices[j * 3 + 1];
|
||||
face.c = surface->triIndices[j * 3 + 2];
|
||||
|
||||
faces.push_back(face);
|
||||
}
|
||||
|
||||
return faces;
|
||||
}
|
||||
|
||||
void removeVertex(const int index, std::vector<Face>& faces, std::vector<Vertex>& vertices) const
|
||||
{
|
||||
vertices.erase(vertices.begin() + index);
|
||||
|
||||
for (auto &face : faces)
|
||||
{
|
||||
if (face.a > index) --face.a;
|
||||
if (face.b > index) --face.b;
|
||||
if (face.c > index) --face.c;
|
||||
}
|
||||
}
|
||||
|
||||
void filterSurfaceVertices(std::vector<Face>& faces, std::vector<Vertex>& vertices) const
|
||||
{
|
||||
for (auto i = 0; i < int(vertices.size()); ++i)
|
||||
{
|
||||
auto referenced = false;
|
||||
|
||||
for (const auto &face : faces)
|
||||
{
|
||||
if (face.a == i || face.b == i || face.c == i)
|
||||
{
|
||||
referenced = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!referenced)
|
||||
{
|
||||
this->removeVertex(i--, faces, vertices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseStaticModel(Game::GfxStaticModelDrawInst* model)
|
||||
{
|
||||
for (unsigned char i = 0; i < model->model->numsurfs; ++i)
|
||||
{
|
||||
this->getFaceList(model->model->materialHandles[i]);
|
||||
}
|
||||
|
||||
const auto* lod = &model->model->lodInfo[model->model->numLods - 1];
|
||||
|
||||
const auto baseIndex = this->vertices_.size() + 1;
|
||||
const auto surfIndex = lod->surfIndex;
|
||||
|
||||
assert(lod->modelSurfs->numsurfs <= model->model->numsurfs);
|
||||
|
||||
for (unsigned short i = 0; i < lod->modelSurfs->numsurfs; ++i)
|
||||
{
|
||||
// TODO: Something is still wrong about the models. Probably baseTriIndex and baseVertIndex might help
|
||||
|
||||
const auto* surface = &lod->modelSurfs->surfs[i];
|
||||
auto faces = this->parseSurfaceFaces(surface);
|
||||
auto vertices = this->parseSurfaceVertices(surface, model->placement);
|
||||
this->filterSurfaceVertices(faces, vertices);
|
||||
|
||||
auto& f = this->getFaceList(model->model->materialHandles[i + surfIndex]);
|
||||
|
||||
for (const auto& vertex : vertices)
|
||||
{
|
||||
this->vertices_.push_back(vertex);
|
||||
}
|
||||
|
||||
for (auto face : faces)
|
||||
{
|
||||
face.a += baseIndex;
|
||||
face.b += baseIndex;
|
||||
face.c += baseIndex;
|
||||
f.indices.push_back(std::move(face));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseStaticModels()
|
||||
{
|
||||
Logger::Print("Parsing static models...\n");
|
||||
|
||||
for (unsigned i = 0u; i < this->world_->dpvs.smodelCount; ++i)
|
||||
{
|
||||
this->parseStaticModel(this->world_->dpvs.smodelDrawInsts + i);
|
||||
}
|
||||
}
|
||||
|
||||
void write()
|
||||
{
|
||||
this->object_ = File(Utils::String::VA("raw/mapdump/%s/%s.obj", this->world_->baseName, this->world_->baseName));
|
||||
this->material_ = File(Utils::String::VA("raw/mapdump/%s/%s.mtl", this->world_->baseName, this->world_->baseName));
|
||||
|
||||
this->object_.append("# Generated by IW4x\n");
|
||||
this->object_.append("# Credit to SE2Dev for his D3DBSP Tool\n");
|
||||
this->object_.append(Utils::String::VA("o %s\n", this->world_->baseName));
|
||||
this->object_.append(Utils::String::VA("mtllib %s.mtl\n\n", this->world_->baseName));
|
||||
|
||||
this->material_.append("# IW4x MTL File\n");
|
||||
this->material_.append("# Credit to SE2Dev for his D3DBSP Tool\n");
|
||||
|
||||
this->writeVertices();
|
||||
this->writeFaces();
|
||||
|
||||
Logger::Print("Writing files...\n");
|
||||
|
||||
this->object_ = {};
|
||||
this->material_ = {};
|
||||
}
|
||||
|
||||
void writeVertices()
|
||||
{
|
||||
Logger::Print("Writing vertices...\n");
|
||||
this->object_.append("# Vertices\n");
|
||||
|
||||
for (const auto& vertex : this->vertices_)
|
||||
{
|
||||
this->object_.append(Utils::String::VA("v %.6f %.6f %.6f\n", vertex.coordinate[0], vertex.coordinate[1], vertex.coordinate[2]));
|
||||
}
|
||||
|
||||
Logger::Print("Writing texture coordinates...\n");
|
||||
this->object_.append("\n# Texture coordinates\n");
|
||||
|
||||
for (const auto& vertex : this->vertices_)
|
||||
{
|
||||
this->object_.append(Utils::String::VA("vt %.6f %.6f\n", vertex.texture[0], vertex.texture[1]));
|
||||
}
|
||||
|
||||
Logger::Print("Writing normals...\n");
|
||||
this->object_.append("\n# Normals\n");
|
||||
|
||||
for (const auto& vertex : this->vertices_)
|
||||
{
|
||||
this->object_.append(Utils::String::VA("vn %.6f %.6f %.6f\n", vertex.normal[0], vertex.normal[1], vertex.normal[2]));
|
||||
}
|
||||
|
||||
this->object_.append("\n");
|
||||
}
|
||||
|
||||
void writeMaterial(Game::Material* material)
|
||||
{
|
||||
std::string name = material->info.name;
|
||||
|
||||
const auto pos = name.find_last_of('/');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
name = name.substr(pos + 1);
|
||||
}
|
||||
|
||||
this->object_.append(Utils::String::VA("usemtl %s\n", name.data()));
|
||||
this->object_.append("s off\n");
|
||||
|
||||
Game::GfxImage *image = nullptr;
|
||||
|
||||
for (char l = 0; l < material->textureCount; ++l)
|
||||
{
|
||||
if (material->textureTable[l].nameStart == 'c' && material->textureTable[l].nameEnd == 'p')
|
||||
{
|
||||
image = material->textureTable[l].u.image; // Hopefully our colorMap
|
||||
}
|
||||
}
|
||||
|
||||
if (!image)
|
||||
{
|
||||
Logger::Print("Failed to get color map for material: %s\n", material->info.name);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
this->material_.append(Utils::String::VA("\nnewmtl %s\n", name.data()));
|
||||
this->material_.append("Ka 1.0000 1.0000 1.0000\n");
|
||||
this->material_.append("Kd 1.0000 1.0000 1.0000\n");
|
||||
this->material_.append("illum 1\n");
|
||||
this->material_.append(Utils::String::VA("map_Ka textures/%s.png\n", image->name));
|
||||
this->material_.append(Utils::String::VA("map_Kd textures/%s.png\n", image->name));
|
||||
}
|
||||
|
||||
void writeFaces()
|
||||
{
|
||||
Logger::Print("Writing faces...\n");
|
||||
Utils::IO::CreateDir(Utils::String::VA("raw/mapdump/%s/textures", this->world_->baseName));
|
||||
|
||||
this->material_.append(Utils::String::VA("# Material count: %d\n", this->faces_.size()));
|
||||
|
||||
this->object_.append("# Faces\n");
|
||||
|
||||
for (const auto& material : this->facesOrder_)
|
||||
{
|
||||
this->writeMaterial(material);
|
||||
|
||||
const auto& faces = this->getFaceList(material);
|
||||
for (const auto& index : faces.indices)
|
||||
{
|
||||
const int a = index.a;
|
||||
const int b = index.b;
|
||||
const int c = index.c;
|
||||
|
||||
this->object_.append(Utils::String::VA("f %d/%d/%d %d/%d/%d %d/%d/%d\n", a, a, a, b, b, b, c, c, c));
|
||||
}
|
||||
|
||||
this->object_.append("\n");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MapDump::MapDump()
|
||||
{
|
||||
Command::Add("dumpmap", [](Command::Params*)
|
||||
{
|
||||
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())
|
||||
{
|
||||
Logger::Print("DirectX needs to be enabled, please start a client to use this command!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::GfxWorld* world = nullptr;
|
||||
Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_GFXWORLD, [](Game::XAssetHeader header, void* world)
|
||||
{
|
||||
*reinterpret_cast<Game::GfxWorld**>(world) = header.gfxWorld;
|
||||
}, &world, false);
|
||||
|
||||
if (world)
|
||||
{
|
||||
MapDumper dumper(world);
|
||||
dumper.dump();
|
||||
|
||||
Logger::Print("Map '%s' exported!\n", world->baseName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::Print("No map loaded, unable to dump anything!\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
10
src/Components/Modules/MapDump.hpp
Normal file
10
src/Components/Modules/MapDump.hpp
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class MapDump : public Component
|
||||
{
|
||||
public:
|
||||
MapDump();
|
||||
};
|
||||
}
|
@ -524,140 +524,6 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUG) && defined(ENABLE_DXSDK)
|
||||
// Credit to SE2Dev, as we shouldn't share the code, keep that in debug mode!
|
||||
void Maps::ExportMap(Game::GfxWorld* world)
|
||||
{
|
||||
Utils::Memory::Allocator allocator;
|
||||
if (!world) return;
|
||||
|
||||
Logger::Print("Exporting '%s'...\n", world->baseName);
|
||||
|
||||
std::string mtl;
|
||||
mtl.append("# IW4x MTL File\n");
|
||||
mtl.append("# Credit to SE2Dev for his D3DBSP Tool\n");
|
||||
|
||||
std::string map;
|
||||
map.append("# Generated by IW4x\n");
|
||||
map.append("# Credit to SE2Dev for his D3DBSP Tool\n");
|
||||
map.append(Utils::String::VA("o %s\n", world->baseName));
|
||||
map.append(Utils::String::VA("mtllib %s.mtl\n\n", world->baseName));
|
||||
|
||||
Logger::Print("Writing vertices...\n");
|
||||
for (unsigned int i = 0; i < world->draw.vertexCount; ++i)
|
||||
{
|
||||
float x = world->draw.vd.vertices[i].xyz[1];
|
||||
float y = world->draw.vd.vertices[i].xyz[2];
|
||||
float z = world->draw.vd.vertices[i].xyz[0];
|
||||
|
||||
map.append(Utils::String::VA("v %.6f %.6f %.6f\n", x, y, z));
|
||||
}
|
||||
|
||||
map.append("\n");
|
||||
|
||||
Logger::Print("Writing texture coordinates...\n");
|
||||
for (unsigned int i = 0; i < world->draw.vertexCount; ++i)
|
||||
{
|
||||
map.append(Utils::String::VA("vt %.6f %.6f\n", world->draw.vd.vertices[i].texCoord[0], -world->draw.vd.vertices[i].texCoord[1]));
|
||||
}
|
||||
|
||||
Logger::Print("Writing normals...\n");
|
||||
for (unsigned int i = 0; i < world->draw.vertexCount; ++i)
|
||||
{
|
||||
Game::vec3_t normal;
|
||||
Game::Vec3UnpackUnitVec(world->draw.vd.vertices[i].normal, &normal);
|
||||
map.append(Utils::String::VA("vn %.6f %.6f %.6f\n", normal[0], normal[1], normal[2]));
|
||||
}
|
||||
|
||||
map.append("\n");
|
||||
|
||||
Logger::Print("Searching materials...\n");
|
||||
int materialCount = 0;
|
||||
Game::Material** materials = allocator.allocateArray<Game::Material*>(world->dpvs.staticSurfaceCount);
|
||||
|
||||
for (unsigned int i = 0; i < world->dpvs.staticSurfaceCount; ++i)
|
||||
{
|
||||
bool isNewMat = true;
|
||||
|
||||
for (int j = 0; j < materialCount; ++j)
|
||||
{
|
||||
if (world->dpvs.surfaces[i].material == materials[j])
|
||||
{
|
||||
isNewMat = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNewMat)
|
||||
{
|
||||
materials[materialCount++] = world->dpvs.surfaces[i].material;
|
||||
}
|
||||
}
|
||||
|
||||
Utils::IO::CreateDir(Utils::String::VA("raw/mapdump/%s/textures", world->baseName));
|
||||
mtl.append(Utils::String::VA("# Material Count: %d\n", materialCount));
|
||||
|
||||
Logger::Print("Exporting materials and faces...\n");
|
||||
for (int m = 0; m < materialCount; ++m)
|
||||
{
|
||||
std::string name = materials[m]->info.name;
|
||||
|
||||
auto pos = name.find_last_of("/");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
name = name.substr(pos + 1);
|
||||
}
|
||||
|
||||
map.append(Utils::String::VA("\nusemtl %s\n", name.data()));
|
||||
map.append("s off\n");
|
||||
|
||||
Game::GfxImage* image = materials[m]->textureTable[0].u.image;
|
||||
|
||||
for (char l = 0; l < materials[m]->textureCount; ++l)
|
||||
{
|
||||
if (materials[m]->textureTable[l].nameStart == 'c')
|
||||
{
|
||||
if (materials[m]->textureTable[l].nameEnd == 'p')
|
||||
{
|
||||
image = materials[m]->textureTable[l].u.image; // Hopefully our colorMap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string _name = Utils::String::VA("raw/mapdump/%s/textures/%s.png", world->baseName, image->name);
|
||||
D3DXSaveTextureToFile(std::wstring(_name.begin(), _name.end()).data(), D3DXIFF_PNG, image->texture.map, NULL);
|
||||
|
||||
mtl.append(Utils::String::VA("\nnewmtl %s\n", name.data()));
|
||||
mtl.append("Ka 1.0000 1.0000 1.0000\n");
|
||||
mtl.append("Kd 1.0000 1.0000 1.0000\n");
|
||||
mtl.append("illum 1\n");
|
||||
mtl.append(Utils::String::VA("map_Ka textures/%s.png\n", image->name));
|
||||
mtl.append(Utils::String::VA("map_Kd textures/%s.png\n", image->name));
|
||||
|
||||
for (unsigned int i = 0; i < world->dpvs.staticSurfaceCount; ++i)
|
||||
{
|
||||
if (world->dpvs.surfaces[i].material != materials[m])
|
||||
continue;
|
||||
|
||||
int vertOffset = world->dpvs.surfaces[i].tris.firstVertex + 1;//+1 cus obj starts at 1
|
||||
int indexOffset = world->dpvs.surfaces[i].tris.baseIndex;
|
||||
for (unsigned short j = 0; j < world->dpvs.surfaces[i].tris.triCount; ++j)
|
||||
{
|
||||
int a = world->draw.indices[indexOffset + j * 3 + 0] + vertOffset;
|
||||
int b = world->draw.indices[indexOffset + j * 3 + 1] + vertOffset;
|
||||
int c = world->draw.indices[indexOffset + j * 3 + 2] + vertOffset;
|
||||
|
||||
map.append(Utils::String::VA("f %d/%d/%d %d/%d/%d %d/%d/%d\n", a, a, a, b, b, b, c, c, c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger::Print("Writing final files...\n");
|
||||
Utils::IO::WriteFile(Utils::String::VA("raw/mapdump/%s/%s.mtl", world->baseName, world->baseName), mtl);
|
||||
Utils::IO::WriteFile(Utils::String::VA("raw/mapdump/%s/%s.obj", world->baseName, world->baseName), map);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Maps::AddDlc(Maps::DLC dlc)
|
||||
{
|
||||
for (auto& pack : Maps::DlcPacks)
|
||||
@ -816,12 +682,16 @@ namespace Components
|
||||
Game::dvar_t* Maps::GetSpecularDvar()
|
||||
{
|
||||
Game::dvar_t*& r_specular = *reinterpret_cast<Game::dvar_t**>(0x69F0D94);
|
||||
static Game::dvar_t* r_specularCustomMaps = Game::Dvar_RegisterBool("r_specularCustomMaps", false, Game::DVAR_FLAG_SAVED, "Allows shaders to use phong specular lighting on custom maps");
|
||||
|
||||
if (Maps::IsCustomMap())
|
||||
{
|
||||
static Game::dvar_t noSpecular;
|
||||
ZeroMemory(&noSpecular, sizeof noSpecular);
|
||||
return &noSpecular;
|
||||
if (!r_specularCustomMaps->current.enabled)
|
||||
{
|
||||
static Game::dvar_t noSpecular;
|
||||
ZeroMemory(&noSpecular, sizeof noSpecular);
|
||||
return &noSpecular;
|
||||
}
|
||||
}
|
||||
|
||||
return r_specular;
|
||||
@ -1001,33 +871,6 @@ namespace Components
|
||||
//Maps::AddDependency("co_hunted", "mp_storm");
|
||||
//Maps::AddDependency("mp_shipment", "mp_shipment_long");
|
||||
|
||||
#if defined(DEBUG) && defined(ENABLE_DXSDK)
|
||||
Command::Add("dumpmap", [](Command::Params*)
|
||||
{
|
||||
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())
|
||||
{
|
||||
Logger::Print("DirectX needs to be enabled, please start a client to use this command!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::GfxWorld* world = nullptr;
|
||||
Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_GFXWORLD, [](Game::XAssetHeader header, void* world)
|
||||
{
|
||||
*reinterpret_cast<Game::GfxWorld**>(world) = header.gfxWorld;
|
||||
}, &world, false);
|
||||
|
||||
if (world)
|
||||
{
|
||||
Maps::ExportMap(world);
|
||||
Logger::Print("Map '%s' exported!\n", world->baseName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::Print("No map loaded, unable to dump anything!\n");
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
// Allow hiding specific smodels
|
||||
Utils::Hook(0x50E67C, Maps::HideModelStub, HOOK_CALL).install()->quick();
|
||||
|
||||
|
@ -100,10 +100,6 @@ namespace Components
|
||||
static void AddDlc(DLC dlc);
|
||||
static void UpdateDlcStatus();
|
||||
|
||||
#if defined(DEBUG) && defined(ENABLE_DXSDK)
|
||||
static void ExportMap(Game::GfxWorld* world);
|
||||
#endif
|
||||
|
||||
static void PrepareUsermap(const char* mapname);
|
||||
static void SpawnServerStub();
|
||||
static void LoadMapLoadscreenStub();
|
||||
|
@ -209,7 +209,8 @@ namespace Components
|
||||
|
||||
UIScript::Add("visitWiki", [](UIScript::Token)
|
||||
{
|
||||
Utils::OpenUrl(Utils::Cache::GetStaticUrl("/wiki/"));
|
||||
//Utils::OpenUrl(Utils::Cache::GetStaticUrl("/wiki/"));
|
||||
Utils::OpenUrl("https://github.com/Jawesome99/IW4x/wiki");
|
||||
});
|
||||
|
||||
UIScript::Add("visitDiscord", [](UIScript::Token)
|
||||
|
@ -119,6 +119,26 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) int QuickPatch::SVGameClientNum()
|
||||
{
|
||||
_asm
|
||||
{
|
||||
mov eax, [esp + 4] // index
|
||||
mov ecx, 1A831A8h
|
||||
mov ecx, [ecx]
|
||||
imul eax, 366Ch
|
||||
lea edx, [eax + ecx + 3134h]
|
||||
cmp edx, 0
|
||||
jnz valid_player_state
|
||||
mov eax, 0
|
||||
ret
|
||||
|
||||
valid_player_state:
|
||||
mov eax, [eax + ecx + 3134h]
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
bool QuickPatch::InvalidNameCheck(char *dest, char *source, int size)
|
||||
{
|
||||
strncpy(dest, source, size - 1);
|
||||
@ -248,6 +268,69 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
Game::dvar_t* QuickPatch::g_playerCollision;
|
||||
__declspec(naked) void QuickPatch::PlayerCollisionStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
// check the value of g_playerCollision
|
||||
push eax;
|
||||
mov eax, g_playerCollision;
|
||||
cmp byte ptr[eax + 16], 0;
|
||||
pop eax;
|
||||
|
||||
// dont collide if g_playerCollision is set to 0
|
||||
je dontcollide;
|
||||
|
||||
// original code
|
||||
mov eax, dword ptr[esp + 0xa0];
|
||||
jmp collide;
|
||||
|
||||
collide:
|
||||
push 0x00478376;
|
||||
retn;
|
||||
|
||||
dontcollide:
|
||||
mov eax, dword ptr[esp + 0xa0];
|
||||
mov ecx, dword ptr[esp + 9ch];
|
||||
push eax;
|
||||
push ecx;
|
||||
lea edx, [esp + 48h];
|
||||
push edx;
|
||||
mov eax, esi;
|
||||
push 0x0047838b;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
Game::dvar_t* QuickPatch::g_playerEjection;
|
||||
__declspec(naked) void QuickPatch::PlayerEjectionStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
// check the value of g_playerEjection
|
||||
push eax;
|
||||
mov eax, g_playerEjection;
|
||||
cmp byte ptr[eax + 16], 0;
|
||||
pop eax;
|
||||
|
||||
// dont eject if g_playerEjection is set to 0
|
||||
je donteject;
|
||||
|
||||
// original code
|
||||
cmp dword ptr[ebx + 19ch], edi;
|
||||
jle eject;
|
||||
|
||||
eject:
|
||||
push 0x005d8152;
|
||||
retn;
|
||||
|
||||
donteject:
|
||||
push 0x005d815b;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> std::function < T > ImportFunction(const std::string& dll, const std::string& function)
|
||||
{
|
||||
auto dllHandle = GetModuleHandleA(&dll[0]);
|
||||
@ -306,6 +389,14 @@ namespace Components
|
||||
// Intermission time dvar
|
||||
Game::Dvar_RegisterFloat("scr_intermissionTime", 10, 0, 120, Game::DVAR_FLAG_REPLICATED | Game::DVAR_FLAG_DEDISAVED, "Time in seconds before match server loads the next map");
|
||||
|
||||
// Player Collision dvar
|
||||
g_playerCollision = Game::Dvar_RegisterBool("g_playerCollision", true, Game::DVAR_FLAG_REPLICATED, "Flag whether player collision is on or off");
|
||||
Utils::Hook(0x47836F, QuickPatch::PlayerCollisionStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Player Ejection dvar
|
||||
g_playerEjection = Game::Dvar_RegisterBool("g_playerEjection", true, Game::DVAR_FLAG_REPLICATED, "Flag whether player ejection is on or off");
|
||||
Utils::Hook(0x5D814A, QuickPatch::PlayerEjectionStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Disallow invalid player names
|
||||
Utils::Hook(0x401983, QuickPatch::InvalidNameStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
@ -589,6 +680,12 @@ namespace Components
|
||||
// Patch selectStringTableEntryInDvar
|
||||
Utils::Hook::Set(0x405959, QuickPatch::SelectStringTableEntryInDvarStub);
|
||||
|
||||
// Patch SV_GameClientNum for edge case generating status
|
||||
Utils::Hook(0x624DE2, QuickPatch::SVGameClientNum, HOOK_CALL).install()->quick();
|
||||
|
||||
// Ignore call to print 'Offhand class mismatch when giving weapon...'
|
||||
Utils::Hook(0x5D9047, 0x4BB9B0, HOOK_CALL).install()->quick();
|
||||
|
||||
Command::Add("unlockstats", [](Command::Params*)
|
||||
{
|
||||
QuickPatch::UnlockStats();
|
||||
|
@ -19,6 +19,7 @@ namespace Components
|
||||
static void SelectStringTableEntryInDvarStub();
|
||||
|
||||
static int SVCanReplaceServerCommand(Game::client_t *client, const char *cmd);
|
||||
static int SVGameClientNum();
|
||||
|
||||
static int MsgReadBitsCompressCheckSV(const char *from, char *to, int size);
|
||||
static int MsgReadBitsCompressCheckCL(const char *from, char *to, int size);
|
||||
@ -37,5 +38,10 @@ namespace Components
|
||||
static Game::dvar_t* Dvar_RegisterAspectRatioDvar(const char* name, char** enumValues, int defaultVal, int flags, const char* description);
|
||||
static void SetAspectRatioStub();
|
||||
static void SetAspectRatio();
|
||||
|
||||
static Game::dvar_t* g_playerCollision;
|
||||
static void PlayerCollisionStub();
|
||||
static Game::dvar_t* g_playerEjection;
|
||||
static void PlayerEjectionStub();
|
||||
};
|
||||
}
|
||||
|
@ -247,11 +247,6 @@ namespace Components
|
||||
Script::ScriptFunctions.push_back({ name, function, isDev });
|
||||
}
|
||||
|
||||
void Script::OnVMShutdown(Utils::Slot<Scheduler::Callback> callback)
|
||||
{
|
||||
Script::VMShutdownSignal.connect(callback);
|
||||
}
|
||||
|
||||
Game::scr_function_t Script::GetFunction(void* caller, const char** name, int* isDev)
|
||||
{
|
||||
for (auto& function : Script::ScriptFunctions)
|
||||
@ -293,6 +288,11 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
void Script::OnVMShutdown(Utils::Slot<Scheduler::Callback> callback)
|
||||
{
|
||||
Script::VMShutdownSignal.connect(callback);
|
||||
}
|
||||
|
||||
void Script::ScrShutdownSystemStub(int num)
|
||||
{
|
||||
Script::VMShutdownSignal();
|
||||
@ -316,6 +316,21 @@ namespace Components
|
||||
return Game::Scr_GetNumParam();
|
||||
}
|
||||
|
||||
Game::gentity_t* Script::getEntFromEntRef(Game::scr_entref_t entref)
|
||||
{
|
||||
Game::gentity_t* gentity = &Game::g_entities[entref];
|
||||
return gentity;
|
||||
}
|
||||
|
||||
Game::client_t* Script::getClientFromEnt(Game::gentity_t* gentity)
|
||||
{
|
||||
if (!gentity->client)
|
||||
{
|
||||
Logger::Error(5, "Entity: %i is not a client", gentity);
|
||||
}
|
||||
return &Game::svs_clients[gentity->number];
|
||||
}
|
||||
|
||||
Script::Script()
|
||||
{
|
||||
Utils::Hook(0x612DB0, Script::StoreFunctionNameStub, HOOK_JUMP).install()->quick();
|
||||
@ -330,7 +345,7 @@ namespace Components
|
||||
Utils::Hook(0x45D44A, Script::LoadGameTypeScript, HOOK_CALL).install()->quick();
|
||||
|
||||
Utils::Hook(0x44E736, Script::GetFunctionStub, HOOK_JUMP).install()->quick(); // Scr_GetFunction
|
||||
//Utils::Hook(0x4EC8E5, Script::GetFunctionStub, HOOK_JUMP).install()->quick(); // Scr_GetMethod
|
||||
Utils::Hook(0x4EC8E5, Script::GetFunctionStub, HOOK_JUMP).install()->quick(); // Scr_GetMethod
|
||||
|
||||
Utils::Hook(0x5F41A3, Script::SetExpFogStub, HOOK_CALL).install()->quick();
|
||||
|
||||
|
@ -29,6 +29,9 @@ namespace Components
|
||||
|
||||
static void OnVMShutdown(Utils::Slot<Scheduler::Callback> callback);
|
||||
|
||||
static Game::gentity_t* getEntFromEntRef(Game::scr_entref_t entref);
|
||||
static Game::client_t* getClientFromEnt(Game::gentity_t* gentity);
|
||||
|
||||
private:
|
||||
static std::string ScriptName;
|
||||
static std::vector<int> ScriptHandles;
|
||||
|
@ -66,6 +66,7 @@ namespace Components
|
||||
int Stats::SaveStats(char* dest, const char* folder, const char* buffer, size_t length)
|
||||
{
|
||||
const auto fs_game = Game::Dvar_FindVar("fs_game");
|
||||
|
||||
if (fs_game && fs_game->current.string && strlen(fs_game->current.string) && !strncmp(fs_game->current.string, "mods/", 5))
|
||||
{
|
||||
folder = fs_game->current.string;
|
||||
|
@ -2908,23 +2908,14 @@ namespace Components
|
||||
|
||||
// check if file should be skipped
|
||||
auto skipFile = false;
|
||||
if (!strncmp(&file[strlen(file) - 4], ".iwi", 4))
|
||||
{
|
||||
if (readSize > 3 && !memcmp(&fileBuffer[0], "IWi", 3))
|
||||
{
|
||||
skipFile = true;
|
||||
}
|
||||
}
|
||||
else if (strstr(file, "weapons"))
|
||||
|
||||
if (strlen(file) > 5 && ((strncmp(&file[strlen(file) - 4], ".iwi", 4) != 0)))
|
||||
{
|
||||
skipFile = true;
|
||||
}
|
||||
else
|
||||
else if (readSize >= 3 && (!memcmp(&fileBuffer[0], "IWi", 3)))
|
||||
{
|
||||
if (readSize > 8 && *reinterpret_cast<std::uint32_t*>(&fileBuffer[4]) == 0xe9c9c447)
|
||||
{
|
||||
skipFile = true;
|
||||
}
|
||||
skipFile = true;
|
||||
}
|
||||
|
||||
// if the header seems encrypted...
|
||||
@ -3409,9 +3400,11 @@ namespace Components
|
||||
Utils::Hook(0x45A806, RelocateFileCount, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x45A6A0, RelocateFileCount, HOOK_CALL).install()->quick();
|
||||
|
||||
#ifndef DEBUG
|
||||
// Ignore missing soundaliases for now
|
||||
// TODO: Include them in the dependency zone!
|
||||
Utils::Hook::Nop(0x644207, 5);
|
||||
#endif
|
||||
|
||||
// Block Mark_pathnode_constant_t
|
||||
Utils::Hook::Set<BYTE>(0x4F74B0, 0xC3);
|
||||
|
@ -683,6 +683,43 @@ namespace Game
|
||||
return atoi(StringTable_Lookup(rankTable, 0, maxrank, 7));
|
||||
}
|
||||
|
||||
void Vec3Normalize(vec3_t& vec)
|
||||
{
|
||||
const auto length = std::sqrt(std::pow(vec[0], 2) + std::pow(vec[1], 2) + std::pow(vec[2], 2));
|
||||
vec[0] /= length;
|
||||
vec[1] /= length;
|
||||
vec[2] /= length;
|
||||
}
|
||||
|
||||
void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out)
|
||||
{
|
||||
unsigned int v3; // xmm1_4
|
||||
|
||||
if (LOWORD(in.packed))
|
||||
v3 = ((in.packed & 0x8000) << 16) | (((((in.packed & 0x3FFF) << 14) - (~(LOWORD(in.packed) << 14) & 0x10000000)) ^ 0x80000001) >> 1);
|
||||
else
|
||||
v3 = 0;
|
||||
|
||||
(*out)[0] = *reinterpret_cast<float*>(&v3);
|
||||
|
||||
if (HIWORD(in.packed))
|
||||
v3 = ((HIWORD(in.packed) & 0x8000) << 16) | (((((HIWORD(in.packed) & 0x3FFF) << 14)
|
||||
- (~(HIWORD(in.packed) << 14) & 0x10000000)) ^ 0x80000001) >> 1);
|
||||
else
|
||||
v3 = 0;
|
||||
|
||||
(*out)[1] = *reinterpret_cast<float*>(&v3);
|
||||
}
|
||||
|
||||
void MatrixVecMultiply(const float (& mulMat)[3][3], const vec3_t& mulVec, vec3_t& solution)
|
||||
{
|
||||
vec3_t res;
|
||||
res[0] = mulMat[0][0] * mulVec[0] + mulMat[1][0] * mulVec[1] + mulMat[2][0] * mulVec[2];
|
||||
res[1] = mulMat[0][1] * mulVec[0] + mulMat[1][1] * mulVec[1] + mulMat[2][1] * mulVec[2];
|
||||
res[2] = mulMat[0][2] * mulVec[0] + mulMat[1][2] * mulVec[1] + mulMat[2][2] * mulVec[2];
|
||||
std::memmove(&solution[0], &res[0], sizeof(res));
|
||||
}
|
||||
|
||||
void SortWorldSurfaces(GfxWorld* world)
|
||||
{
|
||||
DWORD* specular1 = reinterpret_cast<DWORD*>(0x69F105C);
|
||||
|
@ -885,6 +885,10 @@ namespace Game
|
||||
|
||||
void Image_Setup(GfxImage* image, unsigned int width, unsigned int height, unsigned int depth, unsigned int flags, _D3DFORMAT format);
|
||||
|
||||
void Vec3Normalize(vec3_t& vec);
|
||||
void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out);
|
||||
void MatrixVecMultiply(const float(&mulMat)[3][3], const vec3_t& mulVec, vec3_t& solution);
|
||||
|
||||
void SortWorldSurfaces(GfxWorld* world);
|
||||
void R_AddDebugLine(float* color, float* v1, float* v2);
|
||||
void R_AddDebugString(float *color, float *pos, float scale, const char *str);
|
||||
|
@ -91,4 +91,25 @@ extern "C"
|
||||
|
||||
// Enable 'High Performance Graphics'
|
||||
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
|
||||
|
||||
// Tommath fixes
|
||||
int s_read_arc4random(void*, size_t)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int s_read_getrandom(void*, size_t)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int s_read_urandom(void*, size_t)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int s_read_ltm_rng(void*, size_t)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
@ -45,10 +45,8 @@
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
#ifdef ENABLE_DXSDK
|
||||
#include <d3dx9tex.h>
|
||||
#pragma comment(lib, "D3dx9.lib")
|
||||
#endif
|
||||
|
||||
// Usefull for debugging
|
||||
template <size_t S> class Sizer { };
|
||||
|
@ -175,11 +175,11 @@ namespace Steam
|
||||
size_t expectedParams = Proxy::ClientUser.paramSize("SpawnProcess");
|
||||
if (expectedParams == 40) // Release
|
||||
{
|
||||
Proxy::ClientUser.invoke<bool>("SpawnProcess", ourPath, cmdline.data(), 0, ourDirectory, gameID.bits, mod.data(), Proxy::AppId, 0, 0);
|
||||
Proxy::ClientUser.invoke<bool>("SpawnProcess", ourPath, cmdline.data(), ourDirectory, gameID.bits, mod.data(), Proxy::AppId, 0, 0);
|
||||
}
|
||||
else if (expectedParams == 36) // Beta
|
||||
{
|
||||
Proxy::ClientUser.invoke<bool>("SpawnProcess", ourPath, cmdline.data(), 0, ourDirectory, gameID.bits, mod.data(), 0, 0);
|
||||
Proxy::ClientUser.invoke<bool>("SpawnProcess", ourPath, cmdline.data(), ourDirectory, gameID.bits, mod.data(), Proxy::AppId, 0, 0);
|
||||
}
|
||||
else if (expectedParams == 48) // Legacy, expects VAC blob
|
||||
{
|
||||
|
@ -4,7 +4,6 @@ namespace Utils
|
||||
{
|
||||
CSV::CSV(const std::string& file, bool isFile, bool allowComments)
|
||||
{
|
||||
this->valid = false;
|
||||
this->parse(file, isFile, allowComments);
|
||||
}
|
||||
|
||||
@ -111,7 +110,7 @@ namespace Utils
|
||||
//++i;
|
||||
continue;
|
||||
}
|
||||
else if (!isString && row[i] == '#' && allowComments) // Skip comments. I know CSVs usually don't have comments, but in this case it's useful
|
||||
else if (!isString && (row[i] == '#' || (row[i] == '/' && (i + 1) < row.size() && row[i + 1] == '/') ) && allowComments) // Skip comments. I know CSVs usually don't have comments, but in this case it's useful
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ namespace Utils
|
||||
bool isValid() { return this->valid; }
|
||||
|
||||
private:
|
||||
bool valid;
|
||||
bool valid = false;
|
||||
std::vector<std::vector<std::string>> dataMap;
|
||||
|
||||
void parse(const std::string& file, bool isFile = true, bool allowComments = true);
|
||||
|
@ -4,6 +4,8 @@ namespace Utils
|
||||
{
|
||||
const char* Cache::Urls[] =
|
||||
{
|
||||
"https://iw4x.org",
|
||||
|
||||
"https://iw4xcachep26muba.onion.to",
|
||||
"https://iw4xcachep26muba.tor2web.xyz",
|
||||
"https://iw4xcachep26muba.onion.ws",
|
||||
|
BIN
tools/protoc.exe
BIN
tools/protoc.exe
Binary file not shown.
Loading…
Reference in New Issue
Block a user