Merge branch 'develop'

This commit is contained in:
/dev/urandom 2016-09-19 18:53:51 +02:00
commit 04b8a1e156
No known key found for this signature in database
GPG Key ID: 41322B973E0F295E
94 changed files with 1334 additions and 292 deletions

288
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,288 @@
#!groovy
/*
This is our new pipeline script to do all of the building in, of and around IW4x.
Here's what it is supposed to do:
- Make sure Modern Warfare 2 is installed (CI should provide the folder like a custom tool)
- Check out code from iw4x-data
- Build the IW4x client library (this code repository)
- Use iw4x.exe from the iw4x-data repository in order to build the zone files in iw4x-data
- Package the IW4x client with the newly built data files
At this point it is done building everything, however afterwards we want the build server to
also push the newly built files to an update repository, depending on the branch we're on.
- For "develop", release to the "iw4x-dev" branch on the repository server.
- For "master", release to the "iw4x" branch on the repository server.
I'm looking into how the logic of pipelining works in detail before deciding on whether to
throw in the IW4x Updater and the IW4x Node binaries in as well or not.
*/
/*
Note that this is just a rewrite of the jobs as they are currently set up on the production
Jenkins server. This will allow every developer to tinker around with how the build process
is set up. For those who want to play around with this, here's a bit of information:
- This is a Groovy script. Essentially Java but with less bullshit (like brackets and verbose writing).
- This gets directly translated into a Jenkins pipeline.
- If you have no idea how to handle scripts, get your hands off this file.
- If you do not use Jenkins, get your hands off this file.
- If you fuck this script up, I will kill you.
*/
import groovy.transform.Field
@Field def configurations = [
"Debug": [
WorkspaceID: "build@debug",
StashName: "iw4x-debug",
MSBuildConfiguration: "Debug",
PremakeArgs: "",
Archive: true,
],
"Release": [
WorkspaceID: "build@release",
StashName: "iw4x-release",
MSBuildConfiguration: "Release",
PremakeArgs: "",
Archive: true,
],
"Release with unit tests": [
WorkspaceID: "build@release+unittests",
StashName: "iw4x-release-unittests",
MSBuildConfiguration: "Release",
PremakeArgs: "--force-unit-tests",
Archive: false,
],
].collect {k, v -> [k, v]}
@Field def testing = [
"Debug": [
WorkspaceID: "testing@debug",
StashName: "iw4x-debug",
],
"Release": [
WorkspaceID: "testing@release",
StashName: "iw4x-release-unittests",
],
].collect {k, v -> [k, v]}
def jobWorkspace(id, f) {
ws("workspace/${env.JOB_NAME}@$id", f)
}
def useShippedPremake(f) {
def premakeHome = "${pwd()}\\tools"
withEnv(["PATH+=${premakeHome}"], f)
}
def getIW4xExecutable() {
step([
$class: 'CopyArtifact',
filter: '*',
fingerprintArtifacts: true,
projectName: 'iw4x/iw4x-executable/master',
selector: [
$class: 'TriggeredBuildSelector',
allowUpstreamDependencies: false,
fallbackToLastSuccessful: true,
upstreamFilterStrategy: 'UseGlobalSetting'
]
])
}
// This will build the IW4x client.
// We need a Windows Server with Visual Studio 2015, Premake5 and Git on it.
def doBuild(cfg) {
node("windows") {
jobWorkspace(cfg.WorkspaceID) {
checkout scm
useShippedPremake {
def outputDir = pwd()
def msbuild = tool "Microsoft.NET MSBuild 14.0"
bat "premake5 vs2015 ${cfg.PremakeArgs}"
bat "\"${msbuild}\" build\\iw4x.sln \"/p:OutDir=$outputDir\\\\\" \"/p:Configuration=${cfg.MSBuildConfiguration}\""
}
stash name: "${cfg.StashName}", includes: "*.dll,*.pdb"
}
}
}
// This will run the unit tests for IW4x.
// We need a Windows Server with MW2 on it.
def doUnitTests(name) {
mw2dir = tool "Modern Warfare 2"
unstash "$name"
// Get installed localization for correct zonefiles directory junction
def localization = readFile("$mw2dir/localization.txt").split("\r?\n")[0]
try {
timeout(time: 180, unit: "MINUTES") {
// Set up environment
if (isUnix()) {
sh """
mkdir -p zone
for f in main zone/dlc \"zone/$localization\"; do
ln -sfv \"$mw2dir/\$f\" \"\$f\"
done
for f in \"$mw2dir\"/*.dll \"$mw2dir\"/*.txt \"$mw2dir\"/*.bmp; do
ln -sfv \"\$f\" \"\$(basename \"\$f\")\"
done
"""
} else {
bat """
mklink /J \"main\" \"$mw2dir\\main\"
mkdir \"zone\"
mklink /J \"zone\\dlc\" \"$mw2dir\\zone\\dlc\"
mklink /J \"zone\\$localization\" \"$mw2dir\\zone\\$localization\"
copy /y \"$mw2dir\\*.dll\"
copy /y \"$mw2dir\\*.txt\"
copy /y \"$mw2dir\\*.bmp\"
"""
}
// Run tests
getIW4xExecutable()
if (isUnix()) {
sh "wine-wrapper iw4x.exe -tests"
} else {
bat "iw4x.exe -tests"
}
}
} finally {
// In all cases make sure to at least remove the directory junctions!
if (!isUnix()) {
bat """
rmdir \"main\"
rmdir \"zone\\dlc\"
rmdir \"zone\\$localization\"
"""
}
deleteDir()
}
}
// Job properties
properties([
[$class: "GitLabConnectionProperty", gitLabConnection: "sr0"]
])
gitlabBuilds(builds: ["Checkout & Versioning", "Build", "Testing", "Archiving"]) {
// First though let's give this build a proper name
stage("Checkout & Versioning") {
gitlabCommitStatus("Checkout & Versioning") {
node("windows") {
jobWorkspace("versioning") {
checkout scm
useShippedPremake {
def version = bat(returnStdout: true, script: '@premake5 version').split("\r?\n")[1]
currentBuild.setDisplayName "$version (#${env.BUILD_NUMBER})"
}
}
}
}
}
// For each available configuration generate a normal build and a unit test build.
stage("Build") {
gitlabCommitStatus("Build") {
def executions = [:]
for (int i = 0; i < configurations.size(); i++) {
def entry = configurations[i]
def configName = entry[0]
def config = entry[1]
executions[configName] = {
node("windows") {
jobWorkspace(config.WorkspaceID) {
doBuild(config)
}
}
}
}
parallel executions
}
}
// Run unit tests on each configuration.
stage("Testing") {
gitlabCommitStatus("Testing") {
executions = [:]
for (int i = 0; i < testing.size(); i++) {
def entry = testing.get(i)
def testName = entry[0]
def test = entry[1]
executions["$testName on Windows"] = {
node("windows") {
jobWorkspace(test.WorkspaceID) {
doUnitTests(test.StashName)
}
}
}
executions["$testName on Linux"] = {
node("docker && linux && amd64") {
try {
def image = null
dir("src") {
checkout scm
image = docker.build("github.com/IW4x/iw4x-client-testing-wine32", "--rm --force-rm -f jenkins/wine32.Dockerfile jenkins")
deleteDir()
}
image.inside {
doUnitTests(test.StashName)
}
} catch (Exception e) {
if (isUnix()) {
manager.buildUnstable()
manager.addWarningBadge "$testName unit test failed on Linux"
} else {
throw e
}
}
}
}
}
parallel executions
}
}
// Collect all the binaries and give each configuration its own subfolder
stage("Archiving") {
gitlabCommitStatus("Archiving") {
node("windows") { // any node will do
jobWorkspace("archiving") {
try {
for (int i = 0; i < configurations.size(); i++) {
def entry = configurations[i]
def configName = entry[0]
def config = entry[1]
if (config.Archive) {
dir(configName) {
unstash config.StashName
}
}
}
archiveArtifacts artifacts: "**/*.dll,**/*.pdb", fingerprint: true
} finally {
deleteDir()
}
}
}
}
}
}

View File

@ -1,5 +1,5 @@
# IW4x: Client
# IW4x: Client
## How to compile
@ -8,5 +8,16 @@
## Premake arguments
- `--copy-to=PATH` - Optional, copy the DLL to a custom folder after build, define the path here if wanted.
- `--no-new-structure` - Do not use new virtual path structure (separating headers and source files).
| Argument | Description |
| ------------- | ------------- |
| `--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. |
| `--ac-debug-detections` | Log anticheat detections. |
| `--ac-debug-load-library` | Log libraries that get loaded. |
| `--force-unit-tests` | Always compile unit tests. |
| `--force-exception-handler` | Install custom unhandled exception handler even for Debug builds. |
| `--force-minidump-upload` | Upload minidumps even for Debug builds. |
| `--disable-bitmessage` | Disable use of BitMessage completely. |
| `--disable-node-log` | Disable debugging messages for Nodes in Debug builds. |
| `--disable-base128` | Disable base128 encoding for minidumps. |
| `--no-new-structure` | Do not use new virtual path structure (separating headers and source files). |

View File

@ -18,7 +18,7 @@ exit /B 1
:build
call generate.bat
set PLATFORM=Win32
set CONFIGURATION=ReleaseStatic
set CONFIGURATION=Release
msbuild /nologo /m /v:m %* build\iw4x.sln
endlocal
exit /B %ERRORLEVEL%

2
deps/fmt vendored

@ -1 +1 @@
Subproject commit 0d25f6fcbbf0a867b939a5501965ee4462b21ee6
Subproject commit 1fb0586b065c4202e976528a6bdc6384dc56dc04

2
deps/mongoose vendored

@ -1 +1 @@
Subproject commit 2a541175b56a1aeecd2dc8474f981923ef580af6
Subproject commit ffa981d1728163f8b3d0961c127ef924d68ef84c

2
deps/protobuf vendored

@ -1 +1 @@
Subproject commit 14e74f6a21f2726d25e0e679c59d569f6bc8fe8e
Subproject commit c44ca26fe89ed8a81d3ee475a2ccc1797141dbce

37
jenkins/wine32.Dockerfile Normal file
View File

@ -0,0 +1,37 @@
# Requires a decent modern Docker version (v1.10.x at least ideally)
# Use semi-official Arch Linux image with fixed versioning
FROM pritunl/archlinux:2016-09-10
# Environment variables
ENV WINEPREFIX /wine32
ENV WINEARCH win32
ENV WINEDEBUG -all
# Install Wine (32-bit)
RUN \
echo -e "#!/bin/sh\nwine \$@\nretval=\$?\ntail --pid=\$(pidof wineserver 2>/dev/null||echo 0) -f /dev/null\nexit \$retval" > /usr/local/bin/wine-wrapper &&\
chmod +x /usr/local/bin/wine-wrapper &&\
\
(\
echo '' &&\
echo '[multilib]' &&\
echo 'Include = /etc/pacman.d/mirrorlist'\
) >> /etc/pacman.conf &&\
pacman -Sy --noconfirm wine wget xorg-server-xvfb &&\
\
wine-wrapper wineboot.exe -i &&\
wget -Ovcredist_x86.exe https://download.microsoft.com/download/d/d/9/dd9a82d0-52ef-40db-8dab-795376989c03/vcredist_x86.exe &&\
xvfb-run sh -c 'wine-wrapper vcredist_x86.exe /q' &&\
rm vcredist_x86.exe &&\
\
pacman -Rs --noconfirm xorg-server-xvfb wget &&\
\
find /. -name "*~" -type f -delete &&\
rm -rf /tmp/* /var/tmp/* /usr/share/man/* /usr/share/info/* /usr/share/doc/* &&\
pacman -Scc --noconfirm &&\
paccache -rk0 &&\
pacman-optimize &&\
rm -rf /var/lib/pacman/sync/*
USER 0

View File

@ -46,7 +46,7 @@ function zlib.project()
warnings "Off"
kind "SharedLib"
configuration "*Static"
--configuration "*Static"
defines { "_LIB" }
removedefines { "_USRDLL", "_DLL", "ZLIB_DLL" }
kind "StaticLib"

View File

@ -13,17 +13,17 @@ function cstrquote(value)
return result
end
-- Converts tags in "vX.X.X" format to X,X,X.
-- In the case where the format does not work fall back to old 4,2,REVISION.
function vertonum(value, vernumber)
-- Converts tags in "vX.X.X" format to an array of numbers {X,X,X}.
-- In the case where the format does not work fall back to old {4,2,REVISION}.
function vertonumarr(value, vernumber)
vernum = {}
for num in string.gmatch(value, "%d+") do
table.insert(vernum, num)
table.insert(vernum, tonumber(num))
end
if #vernum < 3 then
return "4,2," .. vernumber
return {4,2,tonumber(vernumber)}
end
return vernum[1] .. "," .. vernum[2] .. "," .. vernum[3]
return vernum
end
-- Option to allow copying the DLL file to a custom folder after build
@ -80,7 +80,7 @@ newoption {
newoption {
trigger = "disable-base128",
description = "Disable debugging messages for Nodes in Debug builds."
description = "Disable base128 encoding for minidumps."
}
newaction {
@ -116,7 +116,7 @@ newaction {
if revDirty then revDirty = 1 else revDirty = 0 end
proc:close()
-- get current tag name (aka milestone for now)
-- get current tag name
proc = assert(io.popen("git describe --tags --abbrev=0"))
local tagName = assert(proc:read('*l'))
@ -153,11 +153,11 @@ newaction {
versionHeader:write("#define REVISION " .. revNumber .. "\n")
versionHeader:write("\n")
versionHeader:write("// Version transformed for RC files\n")
versionHeader:write("#define VERSION_RC " .. vertonum(tagName, revNumber) .. "\n")
versionHeader:write("#define VERSION_RC " .. table.concat(vertonumarr(tagName, revNumber), ",") .. "\n")
versionHeader:write("\n")
versionHeader:write("// Alias definitions\n")
versionHeader:write("#define VERSION GIT_DESCRIBE\n")
versionHeader:write("#define SHORTVERSION GIT_TAG\n")
versionHeader:write("#define SHORTVERSION " .. cstrquote(table.concat(vertonumarr(tagName, revNumber), ".")) .. "\n")
versionHeader:close()
local versionHeader = assert(io.open(wks.location .. "/src/version.hpp", "w"))
versionHeader:write("/*\n")
@ -260,7 +260,8 @@ workspace "iw4x"
location "./build"
objdir "%{wks.location}/obj"
targetdir "%{wks.location}/bin/%{cfg.buildcfg}"
configurations { "Debug", "DebugStatic", "Release", "ReleaseStatic" }
--configurations { "Debug", "DebugStatic", "Release", "ReleaseStatic" }
configurations { "Debug", "Release" }
architecture "x32"
platforms "x86"
@ -280,7 +281,7 @@ workspace "iw4x"
flags { "MultiProcessorCompile", "Symbols", "No64BitChecks" }
optimize "Debug"
configuration "*Static"
--configuration "*Static"
flags { "StaticRuntime" }
project "iw4x"

View File

@ -28,6 +28,7 @@ namespace Components
Loader::Register(new Menus());
Loader::Register(new Toast());
Loader::Register(new Party());
Loader::Register(new Zones());
Loader::Register(new Colors());
Loader::Register(new D3D9Ex());
Loader::Register(new Logger());
@ -56,6 +57,7 @@ namespace Components
Loader::Register(new BitMessage());
#endif
Loader::Register(new FileSystem());
Loader::Register(new ModelSurfs());
Loader::Register(new PlayerName());
Loader::Register(new QuickPatch());
Loader::Register(new ServerInfo());
@ -78,7 +80,10 @@ namespace Components
for (auto component : Loader::Components)
{
#ifdef DEBUG
Logger::Print("Unregistering component: %s\n", component->GetName());
if(!Loader::PerformingUnitTests())
{
Logger::Print("Unregistering component: %s\n", component->GetName());
}
#endif
delete component;
}
@ -94,7 +99,7 @@ namespace Components
for (auto component : Loader::Components)
{
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
Logger::Print("Testing '%s'...\n", component->GetName());
#endif
auto startTime = std::chrono::high_resolution_clock::now();
@ -121,7 +126,10 @@ namespace Components
if (component)
{
#ifdef DEBUG
Logger::Print("Component registered: %s\n", component->GetName());
if(!Loader::PerformingUnitTests())
{
Logger::Print("Component registered: %s\n", component->GetName());
}
#endif
Loader::Components.push_back(component);
}

View File

@ -6,7 +6,7 @@ namespace Components
Component() {};
virtual ~Component() {};
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
virtual const char* GetName() { return "Unknown"; };
#endif
@ -39,6 +39,7 @@ namespace Components
#include "Modules\Flags.hpp"
#include "Modules\Menus.hpp"
#include "Modules\Toast.hpp"
#include "Modules\Zones.hpp"
#include "Modules\Colors.hpp"
#include "Modules\D3D9Ex.hpp"
#include "Modules\Script.hpp"
@ -69,6 +70,7 @@ namespace Components
#include "Modules\Singleton.hpp"
#include "Modules\BitMessage.hpp"
#include "Modules\FileSystem.hpp"
#include "Modules\ModelSurfs.hpp"
#include "Modules\PlayerName.hpp"
#include "Modules\QuickPatch.hpp"
#include "Modules\ServerInfo.hpp"

View File

@ -2,7 +2,7 @@
namespace Components
{
int AntiCheat::LastCheck = 0;
int AntiCheat::LastCheck;
std::string AntiCheat::Hash;
Utils::Hook AntiCheat::LoadLibHook[4];
unsigned long AntiCheat::Flags = NO_FLAG;
@ -92,7 +92,15 @@ namespace Components
MessageBoxA(0, "Check the log for more information!", "AntiCheat triggered", MB_ICONERROR);
ExitProcess(0xFFFFFFFF);
#else
Utils::Hook::Set<BYTE>(0x41BA2C, 0xEB);
static std::thread triggerThread;
if (!triggerThread.joinable())
{
triggerThread = std::thread([] ()
{
std::this_thread::sleep_for(43s);
Utils::Hook::Set<BYTE>(0x41BA2C, 0xEB);
});
}
#endif
}
#endif
@ -112,8 +120,7 @@ namespace Components
Logger::Print(Utils::String::VA("AntiCheat: Callee assertion failed: %X %s", reinterpret_cast<uint32_t>(callee), buffer));
#endif
//AntiCheat::CrashClient();
AntiCheat::Hash.append("\0", 1);
AntiCheat::CrashClient();
}
}
@ -161,8 +168,10 @@ namespace Components
{
static int lastCheck = Game::Sys_Milliseconds();
if ((Game::Sys_Milliseconds() - lastCheck) > 1000 * 70)
if ((Game::Sys_Milliseconds() - lastCheck) > 1000 * 20)
{
lastCheck = Game::Sys_Milliseconds();
if (HANDLE h = OpenProcess(PROCESS_VM_READ, TRUE, GetCurrentProcessId()))
{
#ifdef DEBUG_DETECTIONS
@ -170,7 +179,7 @@ namespace Components
#endif
CloseHandle(h);
AntiCheat::Hash.append("\0", 1);
AntiCheat::CrashClient();
}
}
@ -182,7 +191,7 @@ namespace Components
{
static int lastCheck = Game::Sys_Milliseconds();
if ((Game::Sys_Milliseconds() - lastCheck) > 1000 * 180)
if ((Game::Sys_Milliseconds() - lastCheck) > 1000 * 30)
{
lastCheck = Game::Sys_Milliseconds();
@ -208,10 +217,8 @@ namespace Components
if (lastCheck) count = 0;
else ++count;
if (milliseconds < 1000 * 40) return;
// If there was no check within the last 120 seconds, crash!
if ((lastCheck && (milliseconds - lastCheck) > 1000 * 120) || count > 1)
if ((milliseconds > 1000 * 25) && ((lastCheck && (milliseconds - lastCheck) > 1000 * 40) || count > 1))
{
#ifdef DEBUG_DETECTIONS
Logger::Print("AntiCheat: Integrity check failed");
@ -254,7 +261,7 @@ namespace Components
void AntiCheat::Frame()
{
// Perform check only every 30 seconds
if (AntiCheat::LastCheck && (Game::Sys_Milliseconds() - AntiCheat::LastCheck) < 1000 * 30) return;
if (AntiCheat::LastCheck && (Game::Sys_Milliseconds() - AntiCheat::LastCheck) < 1000 * 10) return;
AntiCheat::LastCheck = Game::Sys_Milliseconds();
AntiCheat::PerformCheck();
@ -627,6 +634,9 @@ namespace Components
AntiCheat::AntiCheat()
{
AntiCheat::Flags = NO_FLAG;
AntiCheat::LastCheck = 0;
AntiCheat::EmptyHash();
#ifdef DEBUG

View File

@ -11,7 +11,7 @@ namespace Components
AntiCheat();
~AntiCheat();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "AntiCheat"; };
#endif

View File

@ -20,7 +20,7 @@ namespace Components
AssetHandler();
~AssetHandler();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "AssetHandler"; };
#endif

View File

@ -34,8 +34,10 @@ namespace Assets
return;
}
const Game::GfxImageFileHeader* iwiHeader = reinterpret_cast<const Game::GfxImageFileHeader*>(iwi.GetBuffer().data());
auto iwiBuffer = iwi.GetBuffer();
const Game::GfxImageFileHeader* iwiHeader = reinterpret_cast<const Game::GfxImageFileHeader*>(iwiBuffer.data());
image->mapType = 3;
image->dataLen1 = iwiHeader->fileSizeForPicmip[0] - 32;
image->dataLen2 = iwiHeader->fileSizeForPicmip[0] - 32;

View File

@ -6,7 +6,7 @@ namespace Assets
{
Game::MaterialTechniqueSet* asset = header.materialTechset;
for (int i = 0; i < ARR_SIZE(Game::MaterialTechniqueSet::techniques); ++i)
for (int i = 0; i < ARRAYSIZE(Game::MaterialTechniqueSet::techniques); ++i)
{
Game::MaterialTechnique* technique = asset->techniques[i];
@ -52,9 +52,9 @@ namespace Assets
}
// Save_MaterialTechniquePtrArray
static_assert(ARR_SIZE(Game::MaterialTechniqueSet::techniques) == 48, "Techniques array invalid!");
static_assert(ARRAYSIZE(Game::MaterialTechniqueSet::techniques) == 48, "Techniques array invalid!");
for (int i = 0; i < ARR_SIZE(Game::MaterialTechniqueSet::techniques); ++i)
for (int i = 0; i < ARRAYSIZE(Game::MaterialTechniqueSet::techniques); ++i)
{
Game::MaterialTechnique* technique = asset->techniques[i];

View File

@ -6,7 +6,7 @@ namespace Components
Auth();
~Auth();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Auth"; };
#endif

View File

@ -8,7 +8,7 @@ namespace Components
Bans();
~Bans();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Bans"; };
#endif

View File

@ -13,11 +13,9 @@ namespace Components
BitMessage::BitMessage()
{
#ifdef DEBUG
Logger::Print("Initializing BitMessage...\n");
Logger::Print("Initializing BitMessage...\n");
#endif // DEBUG
BitMessage::BMClient = new BitMRC(BITMESSAGE_OBJECT_STORAGE_FILENAME, BITMESSAGE_KEYS_FILENAME);
BitMessage::BMClient->init();
BitMessage::BMClient->defaultTTL = 1 * 60 * 60; // 1 hour
@ -36,7 +34,7 @@ Logger::Print("Initializing BitMessage...\n");
BitMessage::BMClient->start();
#ifdef DEBUG
Command::Add("bm_send", [](Command::Params params)
Command::Add("bm_send", [](Command::Params params)
{
if (params.Length() < 3) return;
@ -70,7 +68,7 @@ Logger::Print("Initializing BitMessage...\n");
Logger::Print("Broadcast done.\n");
});
Command::Add("bm_check_messages", [](Command::Params)
Command::Add("bm_check_messages", [](Command::Params)
{
while (BitMessage::BMClient->new_messages.size() > 0)
{
@ -79,7 +77,7 @@ Logger::Print("Initializing BitMessage...\n");
}
});
Command::Add("bm_check_connections", [](Command::Params)
Command::Add("bm_check_connections", [](Command::Params)
{
std::shared_lock<std::shared_timed_mutex> mlock(BitMessage::BMClient->mutex_nodes);
@ -104,7 +102,7 @@ Logger::Print("Initializing BitMessage...\n");
mlock.unlock();
});
Command::Add("bm_check_privatekey", [](Command::Params)
Command::Add("bm_check_privatekey", [](Command::Params)
{
std::shared_lock<std::shared_timed_mutex> mlock(BitMessage::BMClient->mutex_priv);
@ -123,7 +121,7 @@ Logger::Print("Initializing BitMessage...\n");
mlock.unlock();
});
Command::Add("bm_check_publickey", [](Command::Params)
Command::Add("bm_check_publickey", [](Command::Params)
{
std::shared_lock<std::shared_timed_mutex> mlock(BitMessage::BMClient->mutex_pub);
@ -140,12 +138,12 @@ Logger::Print("Initializing BitMessage...\n");
mlock.unlock();
});
Command::Add("bm_save", [](Command::Params)
Command::Add("bm_save", [](Command::Params)
{
BitMessage::Save();
});
Command::Add("bm_address_public", [](Command::Params params)
Command::Add("bm_address_public", [](Command::Params params)
{
if (params.Length() < 2) return;
@ -165,7 +163,7 @@ Logger::Print("Initializing BitMessage...\n");
}
});
Command::Add("bm_address_broadcast", [](Command::Params params)
Command::Add("bm_address_broadcast", [](Command::Params params)
{
if (params.Length() < 2) return;

View File

@ -13,7 +13,7 @@ namespace Components
BitMessage();
~BitMessage();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "BitMessage"; };
#endif

View File

@ -6,7 +6,7 @@ namespace Components
Colors();
~Colors();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Colors"; };
#endif

View File

@ -26,7 +26,7 @@ namespace Components
Command();
~Command();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Command"; };
#endif

View File

@ -5,7 +5,7 @@ namespace Components
public:
ConnectProtocol();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "ConnectProtocol"; };
#endif

View File

@ -9,7 +9,7 @@ namespace Components
Console();
~Console();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Console"; };
#endif

View File

@ -5,7 +5,7 @@ namespace Components
public:
D3D9Ex();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "D3D9Ex"; };
#endif

View File

@ -8,7 +8,7 @@ namespace Components
Dedicated();
~Dedicated();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Dedicated"; };
#endif

View File

@ -6,7 +6,7 @@ namespace Components
Discovery();
~Discovery();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Discovery"; };
#endif

View File

@ -522,7 +522,7 @@ namespace Components
if (file.Exists())
{
std::string& buffer = file.GetBuffer();
std::string buffer = file.GetBuffer();
mg_printf(nc,
"HTTP/1.1 200 OK\r\n"

View File

@ -6,7 +6,7 @@ namespace Components
Download();
~Download();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Download"; };
#endif

View File

@ -42,7 +42,7 @@ namespace Components
Dvar();
~Dvar();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Dvar"; };
#endif

View File

@ -8,7 +8,7 @@ namespace Components
Exception();
~Exception();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Exception"; };
#endif
static LPTOP_LEVEL_EXCEPTION_FILTER Hook();

View File

@ -147,11 +147,13 @@ namespace Components
{
Game::DB_ReadXFileUncompressed(version, size);
// Allow loading out custom version
// Allow loading of custom version
if (*version == XFILE_VERSION_IW4X)
{
*version = XFILE_VERSION;
}
Zones::InstallPatches(*version);
}
FastFiles::FastFiles()

View File

@ -6,7 +6,7 @@ namespace Components
FastFiles();
~FastFiles();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "FastFiles"; };
#endif

View File

@ -2,24 +2,64 @@
namespace Components
{
void FileSystem::File::Read()
FileSystem::File::File(std::string file) : Name(file), Handle(0)
{
this->Size = Game::FS_FOpenFileRead(this->Name.data(), &this->Handle, 0);
}
FileSystem::File::~File()
{
char* buffer = nullptr;
int size = Game::FS_ReadFile(this->FilePath.data(), &buffer);
this->Buffer.clear();
if (size < 0)
if (this->Exists())
{
if (buffer)
{
Game::FS_FreeFile(buffer);
}
Game::FS_FCloseFile(this->Handle);
}
else
}
bool FileSystem::File::Exists()
{
return (this->Size > 0);
}
std::string FileSystem::File::GetName()
{
return this->Name;
}
std::string FileSystem::File::GetBuffer()
{
Utils::Memory::Allocator allocator;
if (!this->Exists()) return std::string();
int position = Game::FS_FTell(this->Handle);
this->Seek(0, FS_SEEK_SET);
char* buffer = allocator.AllocateArray<char>(this->Size);
if (!FileSystem::File::Read(buffer, this->Size))
{
this->Buffer.append(buffer, size);
Game::FS_FreeFile(buffer);
this->Seek(position, FS_SEEK_SET);
return std::string();
}
this->Seek(position, FS_SEEK_SET);
return std::string(buffer, this->Size);
}
bool FileSystem::File::Read(void* buffer, size_t size)
{
if (!this->Exists() || static_cast<size_t>(this->Size) < size || Game::FS_Read(buffer, size, this->Handle) != static_cast<int>(size))
{
return false;
}
return true;
}
void FileSystem::File::Seek(int offset, int origin)
{
if (this->Exists())
{
Game::FS_Seek(this->Handle, offset, origin);
}
}

View File

@ -7,18 +7,20 @@ namespace Components
class File
{
public:
File() {};
File(std::string file) : FilePath(file) { this->Read(); };
File() : Size(-1), Name(), Handle(0) {};
File(std::string file);
~File();
bool Exists() { return !this->Buffer.empty(); };
std::string GetName() { return this->FilePath; };
std::string& GetBuffer() { return this->Buffer; };
bool Exists();
std::string GetName();
std::string GetBuffer();
bool Read(void* buffer, size_t size);
void Seek(int offset, int origin);
private:
std::string FilePath;
std::string Buffer;
void Read();
int Handle;
int Size;
std::string Name;
};
class FileWriter
@ -39,7 +41,7 @@ namespace Components
FileSystem();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "FileSystem"; };
#endif

View File

@ -6,7 +6,7 @@ namespace Components
Flags();
~Flags();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Flags"; };
#endif

View File

@ -63,7 +63,7 @@ namespace Components
IPCPipe();
~IPCPipe();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "IPCPipe"; };
#endif

View File

@ -8,7 +8,7 @@ namespace Components
public:
Lean();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Lean"; };
#endif

View File

@ -6,7 +6,7 @@ namespace Components
Localization();
~Localization();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Localization"; };
#endif

View File

@ -12,6 +12,11 @@ namespace Components
return (IsWindow(*reinterpret_cast<HWND*>(0x64A3288)) != FALSE || (Dedicated::IsEnabled() && !Flags::HasFlag("console")));
}
void Logger::PrintStub(int channel, const char* message, ...)
{
return Logger::MessagePrint(channel, Logger::Format(&message));
}
void Logger::Print(const char* message, ...)
{
return Logger::MessagePrint(0, Logger::Format(&message));
@ -176,6 +181,11 @@ namespace Components
Utils::Hook(0x4B0218, Logger::GameLogStub, HOOK_CALL).Install()->Quick();
Utils::Hook(Game::Com_PrintMessage, Logger::PrintMessageStub, HOOK_JUMP).Install()->Quick();
if (Loader::PerformingUnitTests())
{
Utils::Hook(Game::Com_Printf, Logger::PrintStub, HOOK_JUMP).Install()->Quick();
}
Dvar::OnInit([] ()
{
Command::AddSV("log_add", [] (Command::Params params)

View File

@ -6,7 +6,7 @@ namespace Components
Logger();
~Logger();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Logger"; };
#endif
@ -19,6 +19,8 @@ namespace Components
static void SoftError(const char* message, ...);
static bool IsConsoleReady();
static void PrintStub(int channel, const char* message, ...);
static void PipeOutput(void(*callback)(std::string));
private:

View File

@ -6,7 +6,7 @@ namespace Components
Maps();
~Maps();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Maps"; };
#endif

View File

@ -6,7 +6,7 @@ namespace Components
Materials();
~Materials();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Materials"; };
#endif

View File

@ -25,7 +25,7 @@ namespace Components
return i;
}
Game::script_t* Menus::LoadMenuScript(std::string name, std::string& buffer)
Game::script_t* Menus::LoadMenuScript(std::string name, std::string buffer)
{
Game::script_t* script = Game::Script_Alloc(sizeof(Game::script_t) + 1 + buffer.length());
@ -52,7 +52,7 @@ namespace Components
return script;
}
int Menus::LoadMenuSource(std::string name, std::string& buffer)
int Menus::LoadMenuSource(std::string name, std::string buffer)
{
int handle = Menus::ReserveSourceHandle();
if (!Menus::IsValidSourceHandle(handle)) return 0; // No free source slot!

View File

@ -9,7 +9,7 @@ namespace Components
Menus();
~Menus();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Menus"; };
#endif
@ -30,8 +30,8 @@ namespace Components
static std::vector<Game::menuDef_t*> LoadMenu(Game::menuDef_t* menudef);
static std::vector<Game::menuDef_t*> LoadMenu(std::string file);
static Game::script_t* LoadMenuScript(std::string name, std::string& buffer);
static int LoadMenuSource(std::string name, std::string& buffer);
static Game::script_t* LoadMenuScript(std::string name, std::string buffer);
static int LoadMenuSource(std::string name, std::string buffer);
static int ReserveSourceHandle();
static bool IsValidSourceHandle(int handle);

View File

@ -157,6 +157,7 @@ namespace Components
MinidumpUpload::MinidumpUpload()
{
#if !defined(DEBUG) || defined(FORCE_MINIDUMP_UPLOAD)
if (Loader::PerformingUnitTests() || ZoneBuilder::IsEnabled()) return;
this->uploadThread = std::thread([&]() { this->UploadQueuedMinidumps(); });
#endif
}

View File

@ -36,7 +36,7 @@ namespace Components
class MinidumpUpload : public Component
{
public:
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "MinidumpUpload"; };
#endif
MinidumpUpload();

View File

@ -6,7 +6,7 @@ namespace Components
ModList();
~ModList();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "ModList"; };
#endif

View File

@ -0,0 +1,308 @@
#include "STDInclude.hpp"
namespace Components
{
std::map<void*, IUnknown*> ModelSurfs::BufferMap;
std::map<void*, Game::CModelAllocData*> ModelSurfs::AllocMap;
IUnknown* ModelSurfs::GetBuffer(void* buffer)
{
return ModelSurfs::BufferMap[buffer];
}
void ModelSurfs::SetBuffer(char /*streamHandle*/, void* buffer, IUnknown** bufferOut, int* offsetOut)
{
*offsetOut = 0;
*bufferOut = ModelSurfs::BufferMap[buffer];
}
void ModelSurfs::CreateBuffers(Game::XModelSurfs* surfs)
{
for (int i = 0; i < surfs->numSurfaces; ++i)
{
Game::XSurface* surface = &surfs->surfaces[i];
if (surface->streamHandle == 0xFF)
{
IDirect3DVertexBuffer9* vertexBuffer;
IDirect3DIndexBuffer9* indexBuffer;
Game::Load_VertexBuffer(surface->vertexBuffer, &vertexBuffer, surface->numVertices * 32);
Game::Load_IndexBuffer(surface->indexBuffer, &indexBuffer, surface->numPrimitives * 3);
ModelSurfs::BufferMap[surface->vertexBuffer] = vertexBuffer;
ModelSurfs::BufferMap[surface->indexBuffer] = indexBuffer;
}
}
}
Game::XModelSurfs* ModelSurfs::LoadXModelSurfaces(std::string name)
{
Utils::Memory::Allocator allocator;
FileSystem::File model(fmt::sprintf("models/%s", name.data()));
if (!model.Exists())
{
Logger::Error("Loading model %s failed!", name.data());
}
Game::CModelHeader header;
if (!model.Read(&header, sizeof header))
{
Logger::Error("Reading header for model %s failed!", name.data());
}
if (header.version != 1)
{
Logger::Error("Model %s has an invalid version %d (should be 1)!", name.data(), header.version);
}
// Allocate section buffers
header.sectionHeader[Game::SECTION_MAIN].buffer = Utils::Memory::Allocate(header.sectionHeader[Game::SECTION_MAIN].size);
header.sectionHeader[Game::SECTION_INDEX].buffer = Utils::Memory::AllocateAlign(header.sectionHeader[Game::SECTION_INDEX].size, 16);
header.sectionHeader[Game::SECTION_VERTEX].buffer = Utils::Memory::AllocateAlign(header.sectionHeader[Game::SECTION_VERTEX].size, 16);
header.sectionHeader[Game::SECTION_FIXUP].buffer = allocator.AllocateArray<char>(header.sectionHeader[Game::SECTION_FIXUP].size);
// Load section data
for (int i = 0; i < ARRAY_SIZE(header.sectionHeader); ++i)
{
model.Seek(header.sectionHeader[i].offset, FS_SEEK_SET);
if (!model.Read(header.sectionHeader[i].buffer, header.sectionHeader[i].size))
{
Logger::Error("Reading section %d for model %s failed!", i, name.data());
}
}
// Fixup sections
unsigned int* fixups = reinterpret_cast<unsigned int*>(header.sectionHeader[Game::SECTION_FIXUP].buffer);
for (int i = 0; i < 3; ++i)
{
Game::CModelSectionHeader* section = &header.sectionHeader[i];
for (int j = section->fixupStart; j < section->fixupStart + section->fixupCount; ++j)
{
unsigned int fixup = fixups[i];
*reinterpret_cast<DWORD*>(reinterpret_cast<char*>(section->buffer) + (fixup >> 3)) += reinterpret_cast<DWORD>(header.sectionHeader[fixup & 3].buffer);
}
}
// Store allocation data (not sure if this is correct)
Game::CModelAllocData* allocationData = Utils::Memory::AllocateArray<Game::CModelAllocData>();
allocationData->mainArray = header.sectionHeader[Game::SECTION_MAIN].buffer;
allocationData->indexBuffer = header.sectionHeader[Game::SECTION_INDEX].buffer;
allocationData->vertexBuffer = header.sectionHeader[Game::SECTION_VERTEX].buffer;
ModelSurfs::AllocMap[allocationData->vertexBuffer] = allocationData;
*reinterpret_cast<void**>(reinterpret_cast<char*>(allocationData->mainArray) + 44) = allocationData;
Assert_Size(Game::XSurface, 64);
Game::XModelSurfs* modelSurfs = reinterpret_cast<Game::XModelSurfs*>(allocationData->mainArray);
Game::XSurface* tempSurfaces = allocator.AllocateArray<Game::XSurface>(modelSurfs->numSurfaces);
char* surfaceData = reinterpret_cast<char*>(modelSurfs->surfaces);
for (int i = 0; i < modelSurfs->numSurfaces; ++i)
{
memcpy(&tempSurfaces[i], surfaceData + (i * 84), 12);
memcpy(&tempSurfaces[i].indexBuffer, surfaceData + (i * 84) + 16, 20);
memcpy(&tempSurfaces[i].numCT, surfaceData + (i * 84) + 40, 8);
memcpy(&tempSurfaces[i].pad5, surfaceData + (i * 84) + 52, 24);
tempSurfaces[i].streamHandle = 0xFF; // Fake handle for buffer interception
}
memcpy(surfaceData, tempSurfaces, 64 * modelSurfs->numSurfaces);
ModelSurfs::CreateBuffers(modelSurfs);
return modelSurfs;
}
bool ModelSurfs::LoadSurfaces(Game::XModel* model)
{
if (!model) return false;
bool changed = false;
short surfCount = 0;
for (char i = 0; i < model->numLods; ++i)
{
Game::XModelSurfs* surfs = model->lods[i].surfaces;
if (!surfs->surfaces)
{
Game::XModelSurfs* newSurfs = ModelSurfs::LoadXModelSurfaces(surfs->name);
surfs->surfaces = newSurfs->surfaces;
surfs->numSurfaces = newSurfs->numSurfaces;
model->lods[i].surfaces = newSurfs;
memcpy(model->lods[i].pad3, newSurfs->pad, 24);
short numSurfs = static_cast<short>(newSurfs->numSurfaces);
model->lods[i].someCount = numSurfs;
model->lods[i].someTotalCount = surfCount;
surfCount += numSurfs;
}
}
return changed;
}
void ModelSurfs::ReleaseModelSurf(Game::XAssetHeader header)
{
for (int i = 0; i < header.surfaces->numSurfaces && header.surfaces->surfaces; ++i)
{
Game::XSurface* surface = &header.surfaces->surfaces[i];
if (surface->streamHandle == 0xFF)
{
auto buffer = ModelSurfs::BufferMap.find(surface->indexBuffer);
if (buffer != ModelSurfs::BufferMap.end())
{
buffer->second->Release();
ModelSurfs::BufferMap.erase(buffer);
}
buffer = ModelSurfs::BufferMap.find(surface->vertexBuffer);
if (buffer != ModelSurfs::BufferMap.end())
{
buffer->second->Release();
ModelSurfs::BufferMap.erase(buffer);
}
auto allocData = ModelSurfs::AllocMap.find(surface->vertexBuffer);
if (allocData != ModelSurfs::AllocMap.end())
{
Utils::Memory::Free(allocData->second->indexBuffer);
Utils::Memory::Free(allocData->second->vertexBuffer);
Utils::Memory::Free(allocData->second->mainArray);
Utils::Memory::Free(allocData->second);
ModelSurfs::AllocMap.erase(allocData);
}
}
}
}
void ModelSurfs::BeginRecover()
{
for (auto& buffer : ModelSurfs::BufferMap)
{
buffer.second->Release();
}
ModelSurfs::BufferMap.clear();
}
void ModelSurfs::EndRecover()
{
Game::DB_EnumXAssets_Internal(Game::XAssetType::ASSET_TYPE_XMODELSURFS, [] (Game::XAssetHeader header, void* /*userdata*/)
{
ModelSurfs::CreateBuffers(header.surfaces);
}, nullptr, false);
}
void ModelSurfs::XModelSurfsFixup(Game::XModel* model)
{
if (!ModelSurfs::LoadSurfaces(model))
{
Game::DB_XModelSurfsFixup(model);
}
}
__declspec(naked) void ModelSurfs::GetIndexBufferStub()
{
__asm
{
mov eax, [esp + 4h]
cmp al, 0FFh
jne returnSafe
jmp ModelSurfs::SetBuffer
returnSafe:
movzx eax, [esp + 4h]
mov edx, 4B4DE5h
jmp edx
}
}
__declspec(naked) void ModelSurfs::GetIndexBufferStub2()
{
__asm
{
mov eax, [esp + 4h]
cmp al, 0FFh
jne returnSafe
mov eax, [edi + 0Ch]
push eax
call ModelSurfs::GetBuffer
add esp, 4h
retn
returnSafe:
mov eax, 4FDC20h
jmp eax
}
}
__declspec(naked) void ModelSurfs::GetIndexBaseStub()
{
__asm
{
mov eax, [esp + 4h]
cmp al, 0FFh
jne returnSafe
xor eax, eax
retn
returnSafe:
mov eax, 48C5F0h
jmp eax
}
}
__declspec(naked) void ModelSurfs::GetVertexBufferStub()
{
__asm
{
mov eax, [esp + 4h]
cmp al, 0FFh
jne returnSafe
jmp ModelSurfs::SetBuffer
returnSafe:
movzx eax, [esp + 4h]
mov edx, 5BC055h
jmp edx
}
}
ModelSurfs::ModelSurfs()
{
ModelSurfs::BufferMap.clear();
// Install release handler
Game::DB_ReleaseXAssetHandlers[Game::XAssetType::ASSET_TYPE_XMODELSURFS] = ModelSurfs::ReleaseModelSurf;
// Install device recovery handlers
Renderer::OnDeviceRecoveryBegin(ModelSurfs::BeginRecover);
Renderer::OnDeviceRecoveryEnd(ModelSurfs::EndRecover);
// Install hooks
Utils::Hook(0x47A6BD, ModelSurfs::XModelSurfsFixup, HOOK_CALL).Install()->Quick();
Utils::Hook(0x558F12, ModelSurfs::GetIndexBaseStub, HOOK_CALL).Install()->Quick();
Utils::Hook(0x5BC050, ModelSurfs::GetIndexBufferStub, HOOK_JUMP).Install()->Quick();
Utils::Hook(0x558E70, ModelSurfs::GetIndexBufferStub2, HOOK_CALL).Install()->Quick();
Utils::Hook(0x5BC050, ModelSurfs::GetVertexBufferStub, HOOK_JUMP).Install()->Quick();
}
ModelSurfs::~ModelSurfs()
{
assert(ModelSurfs::BufferMap.empty());
assert(ModelSurfs::AllocMap.empty());
}
}

View File

@ -0,0 +1,35 @@
namespace Components
{
class ModelSurfs : public Component
{
public:
ModelSurfs();
~ModelSurfs();
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "ModelSurfs"; };
#endif
private:
static std::map<void*, IUnknown*> BufferMap;
static std::map<void*, Game::CModelAllocData*> AllocMap;
static void ReleaseModelSurf(Game::XAssetHeader header);
static void BeginRecover();
static void EndRecover();
static IUnknown* GetBuffer(void* buffer);
static void SetBuffer(char streamHandle, void* buffer, IUnknown** bufferOut, int* offsetOut);
static void CreateBuffers(Game::XModelSurfs* surfs);
static Game::XModelSurfs* LoadXModelSurfaces(std::string name);
static bool LoadSurfaces(Game::XModel* model);
static void XModelSurfsFixup(Game::XModel* model);
static void GetIndexBaseStub();
static void GetIndexBufferStub();
static void GetIndexBufferStub2();
static void GetVertexBufferStub();
};
}

View File

@ -6,7 +6,7 @@ namespace Components
MusicalTalent();
~MusicalTalent();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "MusicalTalent"; };
#endif

View File

@ -56,7 +56,7 @@ namespace Components
Network();
~Network();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Network"; };
#endif

View File

@ -6,7 +6,7 @@ namespace Components
News();
~News();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "News"; };
#endif

View File

@ -2,6 +2,8 @@
namespace Components
{
std::mutex Node::NodeMutex;
std::mutex Node::SessionMutex;
Utils::Cryptography::ECC::Key Node::SignatureKey;
std::vector<Node::NodeEntry> Node::Nodes;
std::vector<Node::ClientSession> Node::Sessions;
@ -45,6 +47,7 @@ namespace Components
// However, defining another proto message due to this would be redundant.
//list.set_is_dedi(Dedicated::IsDedicated());
std::lock_guard<std::mutex> _(Node::NodeMutex);
for (auto node : Node::Nodes)
{
if (node.state == Node::STATE_VALID && node.registered)
@ -88,6 +91,7 @@ namespace Components
unsigned int Node::GetValidNodeCount()
{
unsigned int count = 0;
std::lock_guard<std::mutex> _(Node::NodeMutex);
for (auto node : Node::Nodes)
{
@ -108,6 +112,7 @@ namespace Components
if (!address.IsValid() || address.IsLocal() || address.IsSelf()) return;
#endif
std::lock_guard<std::mutex> _(Node::NodeMutex);
Node::NodeEntry* existingEntry = Node::FindNode(address);
if (existingEntry)
{
@ -142,7 +147,9 @@ namespace Components
list.set_protocol(PROTOCOL);
list.set_version(NODE_VERSION);
for (auto node : Node::Nodes)
std::lock_guard<std::mutex> _(Node::NodeMutex);
for (auto& node : Node::Nodes)
{
if (node.state == Node::STATE_VALID && node.registered)
{
@ -163,6 +170,7 @@ namespace Components
void Node::DeleteInvalidSessions()
{
std::lock_guard<std::mutex> _(Node::SessionMutex);
for (auto i = Node::Sessions.begin(); i != Node::Sessions.end();)
{
if (i->lastTime <= 0 || (Game::Sys_Milliseconds() - i->lastTime) > SESSION_TIMEOUT)
@ -178,6 +186,7 @@ namespace Components
void Node::DeleteInvalidNodes()
{
std::lock_guard<std::mutex> _(Node::NodeMutex);
std::vector<Node::NodeEntry> cleanNodes;
for (auto node : Node::Nodes)
@ -204,6 +213,7 @@ namespace Components
void Node::SyncNodeList()
{
std::lock_guard<std::mutex> _(Node::NodeMutex);
for (auto& node : Node::Nodes)
{
if (node.state == Node::STATE_VALID && node.registered)
@ -254,62 +264,65 @@ namespace Components
int registerCount = 0;
int listQueryCount = 0;
for (auto &node : Node::Nodes)
{
// TODO: Decide how to handle nodes that were already registered, but timed out re-registering.
if (node.state == STATE_NEGOTIATING && (Game::Sys_Milliseconds() - node.lastTime) > (NODE_QUERY_TIMEOUT))
std::lock_guard<std::mutex> _(Node::NodeMutex);
for (auto &node : Node::Nodes)
{
node.registered = false; // Definitely unregister here!
node.state = Node::STATE_INVALID;
node.lastHeard = Game::Sys_Milliseconds();
node.lastTime = Game::Sys_Milliseconds();
// TODO: Decide how to handle nodes that were already registered, but timed out re-registering.
if (node.state == STATE_NEGOTIATING && (Game::Sys_Milliseconds() - node.lastTime) > (NODE_QUERY_TIMEOUT))
{
node.registered = false; // Definitely unregister here!
node.state = Node::STATE_INVALID;
node.lastHeard = Game::Sys_Milliseconds();
node.lastTime = Game::Sys_Milliseconds();
#if defined(DEBUG) && !defined(DISABLE_NODE_LOG)
Logger::Print("Node negotiation timed out. Invalidating %s\n", node.address.GetCString());
Logger::Print("Node negotiation timed out. Invalidating %s\n", node.address.GetCString());
#endif
}
if (registerCount < NODE_FRAME_QUERY_LIMIT)
{
// Register when unregistered and in UNKNOWN state (I doubt it's possible to be unregistered and in VALID state)
if (!node.registered && (node.state != Node::STATE_NEGOTIATING && node.state != Node::STATE_INVALID))
{
++registerCount;
node.state = Node::STATE_NEGOTIATING;
Node::PerformRegistration(node.address);
}
// Requery invalid nodes within the NODE_QUERY_INTERVAL
// This is required, as a node might crash, which causes it to be invalid.
// If it's restarted though, we wouldn't query it again.
// But wouldn't it send a registration request to us?
// Not sure if the code below is necessary...
// Well, it might be possible that this node doesn't know use anymore. Anyways, just keep that code here...
// Nvm, this is required for clients, as nodes don't send registration requests to clients.
else if (node.state == STATE_INVALID && (Game::Sys_Milliseconds() - node.lastTime) > NODE_QUERY_INTERVAL)
if (registerCount < NODE_FRAME_QUERY_LIMIT)
{
++registerCount;
Node::PerformRegistration(node.address);
}
}
if (listQueryCount < NODE_FRAME_QUERY_LIMIT)
{
if (node.registered && node.state == Node::STATE_VALID && (!node.lastListQuery || (Game::Sys_Milliseconds() - node.lastListQuery) > NODE_QUERY_INTERVAL))
{
++listQueryCount;
node.state = Node::STATE_NEGOTIATING;
node.lastTime = Game::Sys_Milliseconds();
node.lastListQuery = Game::Sys_Milliseconds();
if (Dedicated::IsEnabled())
// Register when unregistered and in UNKNOWN state (I doubt it's possible to be unregistered and in VALID state)
if (!node.registered && (node.state != Node::STATE_NEGOTIATING && node.state != Node::STATE_INVALID))
{
Network::SendCommand(node.address, "nodeListRequest");
++registerCount;
node.state = Node::STATE_NEGOTIATING;
Node::PerformRegistration(node.address);
}
else
// Requery invalid nodes within the NODE_QUERY_INTERVAL
// This is required, as a node might crash, which causes it to be invalid.
// If it's restarted though, we wouldn't query it again.
// But wouldn't it send a registration request to us?
// Not sure if the code below is necessary...
// Well, it might be possible that this node doesn't know use anymore. Anyways, just keep that code here...
// Nvm, this is required for clients, as nodes don't send registration requests to clients.
else if (node.state == STATE_INVALID && (Game::Sys_Milliseconds() - node.lastTime) > NODE_QUERY_INTERVAL)
{
Network::SendCommand(node.address, "sessionRequest");
++registerCount;
Node::PerformRegistration(node.address);
}
}
if (listQueryCount < NODE_FRAME_QUERY_LIMIT)
{
if (node.registered && node.state == Node::STATE_VALID && (!node.lastListQuery || (Game::Sys_Milliseconds() - node.lastListQuery) > NODE_QUERY_INTERVAL))
{
++listQueryCount;
node.state = Node::STATE_NEGOTIATING;
node.lastTime = Game::Sys_Milliseconds();
node.lastListQuery = Game::Sys_Milliseconds();
if (Dedicated::IsEnabled())
{
Network::SendCommand(node.address, "nodeListRequest");
}
else
{
Network::SendCommand(node.address, "sessionRequest");
}
}
}
}
@ -374,6 +387,7 @@ namespace Components
packet.set_challenge(challenge);
packet.set_signature(Utils::Cryptography::ECC::SignMessage(Node::SignatureKey, challenge));
std::lock_guard<std::mutex> _(Node::NodeMutex);
for (auto node : Node::Nodes)
{
Network::SendCommand(node.address, "nodeDeregister", packet.SerializeAsString());
@ -386,16 +400,16 @@ namespace Components
{
if (Dvar::Var("sv_lanOnly").Get<bool>()) return;
Node::NodeEntry* entry = Node::FindNode(address);
// Create a new entry, if we don't already know it
if (!entry)
if (!Node::FindNode(address))
{
Node::AddNode(address);
entry = Node::FindNode(address);
if (!entry) return;
if (!Node::FindNode(address)) return;
}
std::lock_guard<std::mutex> _(Node::NodeMutex);
Node::NodeEntry* entry = Node::FindNode(address);
#if defined(DEBUG) && !defined(DISABLE_NODE_LOG)
Logger::Print("Received registration request from %s\n", address.GetCString());
#endif
@ -434,6 +448,7 @@ namespace Components
{
if (Dvar::Var("sv_lanOnly").Get<bool>()) return;
std::lock_guard<std::mutex> _(Node::NodeMutex);
Node::NodeEntry* entry = Node::FindNode(address);
if (!entry || entry->state != Node::STATE_NEGOTIATING) return;
@ -485,6 +500,7 @@ namespace Components
if (Dvar::Var("sv_lanOnly").Get<bool>()) return;
// Ignore requests from nodes we don't know
std::lock_guard<std::mutex> _(Node::NodeMutex);
Node::NodeEntry* entry = Node::FindNode(address);
if (!entry || entry->state != Node::STATE_NEGOTIATING) return;
@ -527,21 +543,26 @@ namespace Components
// Check if this is a registered node
bool allowed = false;
Node::NodeEntry* entry = Node::FindNode(address);
if (entry && entry->registered)
{
entry->lastTime = Game::Sys_Milliseconds();
allowed = true;
}
// Check if there is any open session
if (!allowed)
{
Node::ClientSession* session = Node::FindSession(address);
if (session)
std::lock_guard<std::mutex> _(Node::NodeMutex);
Node::NodeEntry* entry = Node::FindNode(address);
if (entry && entry->registered)
{
session->lastTime = Game::Sys_Milliseconds();
allowed = session->valid;
entry->lastTime = Game::Sys_Milliseconds();
allowed = true;
}
// Check if there is any open session
if (!allowed)
{
std::lock_guard<std::mutex> __(Node::SessionMutex);
Node::ClientSession* session = Node::FindSession(address);
if (session)
{
session->lastTime = Game::Sys_Milliseconds();
allowed = session->valid;
}
}
}
@ -563,6 +584,7 @@ namespace Components
{
if (Dvar::Var("sv_lanOnly").Get<bool>()) return;
std::lock_guard<std::mutex> _(Node::NodeMutex);
Node::NodeEntry* entry = Node::FindNode(address);
if (!entry || !entry->registered) return;
@ -598,6 +620,7 @@ namespace Components
if (Dvar::Var("sv_lanOnly").Get<bool>()) return;
// Search an active session, if we haven't found one, register a template
std::lock_guard<std::mutex> _(Node::SessionMutex);
if (!Node::FindSession(address))
{
Node::ClientSession templateSession;
@ -626,6 +649,7 @@ namespace Components
if (Dvar::Var("sv_lanOnly").Get<bool>()) return;
// Return if we don't have a session for this address
std::lock_guard<std::mutex> _(Node::SessionMutex);
Node::ClientSession* session = Node::FindSession(address);
if (!session || session->valid) return;
@ -650,6 +674,7 @@ namespace Components
{
Network::Handle("sessionInitialize", [] (Network::Address address, std::string data)
{
std::lock_guard<std::mutex> _(Node::NodeMutex);
Node::NodeEntry* entry = Node::FindNode(address);
if (!entry) return;
@ -663,12 +688,15 @@ namespace Components
Network::Handle("sessionAcknowledge", [] (Network::Address address, std::string data)
{
Node::NodeEntry* entry = Node::FindNode(address);
if (!entry) return;
{
std::lock_guard<std::mutex> _(Node::NodeMutex);
Node::NodeEntry* entry = Node::FindNode(address);
if (!entry) return;
entry->state = Node::STATE_VALID;
entry->registered = true;
entry->lastTime = Game::Sys_Milliseconds();
entry->state = Node::STATE_VALID;
entry->registered = true;
entry->lastTime = Game::Sys_Milliseconds();
}
#if defined(DEBUG) && !defined(DISABLE_NODE_LOG)
Logger::Print("Session acknowledged by %s, synchronizing node list...\n", address.GetCString());
@ -690,6 +718,7 @@ namespace Components
return;
}
Node::NodeMutex.lock();
Node::NodeEntry* entry = Node::FindNode(address);
if (entry)
{
@ -705,11 +734,21 @@ namespace Components
entry->state = Node::STATE_VALID;
entry->lastTime = Game::Sys_Milliseconds();
// Block old versions
// if (entry->version < NODE_VERSION)
// {
// entry->state = Node::STATE_INVALID;
// Node::NodeMutex.unlock();
// return;
// }
if (!Dedicated::IsEnabled() && entry->isDedi && ServerList::IsOnlineList() && entry->protocol == PROTOCOL)
{
ServerList::InsertRequest(entry->address, true);
}
Node::NodeMutex.unlock();
for (int i = 0; i < list.address_size(); ++i)
{
Network::Address _addr(list.address(i));
@ -730,11 +769,17 @@ namespace Components
Node::AddNode(_addr);
}
}
else
{
Node::NodeMutex.unlock();
}
}
else
{
Node::NodeMutex.unlock();
//Node::AddNode(address);
std::lock_guard<std::mutex> _(Node::SessionMutex);
Node::ClientSession* session = Node::FindSession(address);
if (session && session->valid)
{
@ -754,6 +799,7 @@ namespace Components
{
if (Dedicated::IsEnabled())
{
Node::NodeMutex.lock();
Node::NodeEntry* entry = Node::FindNode(address);
if (entry)
{
@ -761,9 +807,12 @@ namespace Components
entry->lastTime = Game::Sys_Milliseconds();
entry->registered = false;
entry->state = Node::STATE_UNKNOWN;
Node::NodeMutex.unlock();
}
else
{
Node::NodeMutex.unlock();
// Add as new entry to perform registration
Node::AddNode(address);
}
@ -774,6 +823,7 @@ namespace Components
{
Logger::Print("Nodes: %d (%d)\n", Node::Nodes.size(), Node::GetValidNodeCount());
std::lock_guard<std::mutex> _(Node::NodeMutex);
for (auto node : Node::Nodes)
{
Logger::Print("%s\t(%s)\n", node.address.GetCString(), Node::GetStateName(node.state));
@ -787,6 +837,7 @@ namespace Components
Network::Address address(params[1]);
Node::AddNode(address);
std::lock_guard<std::mutex> _(Node::NodeMutex);
Node::NodeEntry* entry = Node::FindNode(address);
if (entry)
{
@ -799,6 +850,7 @@ namespace Components
{
Logger::Print("Re-Synchronizing nodes...\n");
std::lock_guard<std::mutex> _(Node::NodeMutex);
for (auto& node : Node::Nodes)
{
node.state = Node::STATE_UNKNOWN;
@ -817,6 +869,8 @@ namespace Components
Node::SignatureKey.Free();
Node::StoreNodes(true);
std::lock_guard<std::mutex> _(Node::NodeMutex);
std::lock_guard<std::mutex> __(Node::SessionMutex);
Node::Nodes.clear();
Node::Sessions.clear();
}
@ -831,7 +885,7 @@ namespace Components
printf("ECDSA key seems invalid!\n");
return false;
}
printf("Success\n");
printf("Testing 10 valid signatures...");
@ -885,45 +939,6 @@ namespace Components
}
printf("Success\n");
uint32_t randIntCount = 4'000'000;
printf("Generating %d random integers...", randIntCount);
auto startTime = std::chrono::high_resolution_clock::now();
for (uint32_t i = 0; i < randIntCount; ++i)
{
Utils::Cryptography::Rand::GenerateInt();
}
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - startTime).count();
Logger::Print("took %llims\n", duration);
printf("Testing ZLib compression...");
std::string test = fmt::sprintf("%c", Utils::Cryptography::Rand::GenerateInt());
for (int i = 0; i < 21; ++i)
{
std::string compressed = Utils::Compression::ZLib::Compress(test);
std::string decompressed = Utils::Compression::ZLib::Decompress(compressed);
if (test != decompressed)
{
printf("Error\n");
printf("Compressing %d bytes and decompressing failed!\n", test.size());
return false;
}
auto size = test.size();
for (unsigned int j = 0; j < size; ++j)
{
test.append(fmt::sprintf("%c", Utils::Cryptography::Rand::GenerateInt()));
}
}
printf("Success\n");
return true;
}
}

View File

@ -7,7 +7,7 @@
#define NODE_STORE_INTERVAL 1000 * 60* 1 // Store nodes every minute
#define SESSION_TIMEOUT 1000 * 10 // 10 seconds session timeout
#define NODE_VERSION 1
#define NODE_VERSION 2
namespace Components
{
@ -17,7 +17,7 @@ namespace Components
Node();
~Node();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Node"; };
#endif
@ -69,6 +69,8 @@ namespace Components
static Utils::Cryptography::ECC::Key SignatureKey;
static std::mutex NodeMutex;
static std::mutex SessionMutex;
static std::vector<NodeEntry> Nodes;
static std::vector<ClientSession> Sessions;

View File

@ -6,7 +6,7 @@ namespace Components
Party();
~Party();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Party"; };
#endif

View File

@ -6,7 +6,7 @@ namespace Components
PlayerName();
~PlayerName();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "PlayerName"; };
#endif

View File

@ -8,7 +8,7 @@ namespace Components
Playlist();
~Playlist();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Playlist"; };
#endif

View File

@ -424,4 +424,46 @@ namespace Components
{
QuickPatch::ShutdownSignal.clear();
}
bool QuickPatch::UnitTest()
{
uint32_t randIntCount = 4'000'000;
printf("Generating %d random integers...", randIntCount);
auto startTime = std::chrono::high_resolution_clock::now();
for (uint32_t i = 0; i < randIntCount; ++i)
{
Utils::Cryptography::Rand::GenerateInt();
}
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - startTime).count();
Logger::Print("took %llims\n", duration);
printf("Testing ZLib compression...");
std::string test = fmt::sprintf("%c", Utils::Cryptography::Rand::GenerateInt());
for (int i = 0; i < 21; ++i)
{
std::string compressed = Utils::Compression::ZLib::Compress(test);
std::string decompressed = Utils::Compression::ZLib::Decompress(compressed);
if (test != decompressed)
{
printf("Error\n");
printf("Compressing %d bytes and decompressing failed!\n", test.size());
return false;
}
auto size = test.size();
for (unsigned int j = 0; j < size; ++j)
{
test.append(fmt::sprintf("%c", Utils::Cryptography::Rand::GenerateInt()));
}
}
printf("Success\n");
return true;
}
}

View File

@ -8,10 +8,12 @@ namespace Components
QuickPatch();
~QuickPatch();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "QuickPatch"; };
#endif
bool UnitTest();
static void UnlockStats();
static void OnShutdown(Callback* callback);

View File

@ -6,7 +6,7 @@ namespace Components
RCon();
~RCon();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "RCon"; };
#endif

View File

@ -5,7 +5,7 @@ namespace Components
public:
RawFiles();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "RawFiles"; };
#endif

View File

@ -7,6 +7,9 @@ namespace Components
wink::signal<wink::slot<Renderer::Callback>> Renderer::FrameOnceSignal;
wink::signal<wink::slot<Renderer::BackendCallback>> Renderer::BackendFrameSignal;
wink::signal<wink::slot<Renderer::Callback>> Renderer::EndRecoverDeviceSignal;
wink::signal<wink::slot<Renderer::Callback>> Renderer::BeginRecoverDeviceSignal;
__declspec(naked) void Renderer::FrameStub()
{
__asm
@ -62,6 +65,16 @@ namespace Components
Renderer::BackendFrameSignal.connect(callback);
}
void Renderer::OnDeviceRecoveryEnd(Renderer::Callback* callback)
{
Renderer::EndRecoverDeviceSignal.connect(callback);
}
void Renderer::OnDeviceRecoveryBegin(Renderer::Callback* callback)
{
Renderer::BeginRecoverDeviceSignal.connect(callback);
}
int Renderer::Width()
{
return Utils::Hook::Get<int>(0x66E1C68);
@ -106,6 +119,18 @@ namespace Components
Renderer::DrawFrameHook.Initialize(0x5ACB99, Renderer::FrameStub, HOOK_CALL)->Install();
Utils::Hook(0x536A80, Renderer::BackendFrameStub, HOOK_JUMP).Install()->Quick();
Utils::Hook(0x508298, [] ()
{
Game::DB_BeginRecoverLostDevice();
Renderer::BeginRecoverDeviceSignal();
}, HOOK_CALL).Install()->Quick();
Utils::Hook(0x508355, [] ()
{
Renderer::EndRecoverDeviceSignal();
Game::DB_EndRecoverLostDevice();
}, HOOK_CALL).Install()->Quick();
}
Renderer::~Renderer()
@ -114,5 +139,8 @@ namespace Components
Renderer::BackendFrameSignal.clear();
Renderer::FrameOnceSignal.clear();
Renderer::FrameSignal.clear();
Renderer::EndRecoverDeviceSignal.clear();
Renderer::BeginRecoverDeviceSignal.clear();
}
}

View File

@ -9,7 +9,7 @@ namespace Components
Renderer();
~Renderer();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Renderer"; };
#endif
@ -20,6 +20,9 @@ namespace Components
static void OnFrame(Callback* callback);
static void OnBackendFrame(BackendCallback* callback);
static void OnDeviceRecoveryEnd(Callback* callback);
static void OnDeviceRecoveryBegin(Callback* callback);
private:
static void FrameStub();
static void FrameHandler();
@ -29,6 +32,10 @@ namespace Components
static wink::signal<wink::slot<Callback>> FrameSignal;
static wink::signal<wink::slot<Callback>> FrameOnceSignal;
static wink::signal<wink::slot<Callback>> EndRecoverDeviceSignal;
static wink::signal<wink::slot<Callback>> BeginRecoverDeviceSignal;
static wink::signal<wink::slot<BackendCallback>> BackendFrameSignal;
static Utils::Hook DrawFrameHook;
};

View File

@ -6,7 +6,7 @@ namespace Components
Script();
~Script();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Script"; };
#endif

View File

@ -6,7 +6,7 @@ namespace Components
ServerInfo();
~ServerInfo();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "ServerInfo"; };
#endif

View File

@ -27,7 +27,7 @@ namespace Components
ServerList();
~ServerList();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "ServerList"; };
#endif

View File

@ -19,7 +19,7 @@ namespace Components
Console::FreeNativeConsole();
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) return;
if (Loader::PerformingUnitTests() || Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) return;
Singleton::FirstInstance = (CreateMutexA(NULL, FALSE, "iw4x_mutex") && GetLastError() != ERROR_ALREADY_EXISTS);

View File

@ -5,7 +5,7 @@ namespace Components
public:
Singleton();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Singleton"; };
#endif

View File

@ -6,7 +6,7 @@ namespace Components
StringTable();
~StringTable();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "StringTable"; };
#endif

View File

@ -24,7 +24,7 @@ namespace Components
StructuredData();
~StructuredData();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "StructuredData"; };
#endif

View File

@ -5,7 +5,7 @@ namespace Components
public:
Theatre();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Theatre"; };
#endif

View File

@ -6,7 +6,7 @@ namespace Components
Toast();
~Toast();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Toast"; };
#endif

View File

@ -17,7 +17,7 @@ namespace Components
UIFeeder();
~UIFeeder();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "UIFeeder"; };
#endif

View File

@ -6,7 +6,7 @@ namespace Components
UIScript();
~UIScript();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "UIScript"; };
#endif

View File

@ -5,7 +5,7 @@ namespace Components
public:
Weapon();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Weapon"; };
#endif

View File

@ -5,7 +5,7 @@ namespace Components
public:
Window();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Window"; };
#endif

View File

@ -73,7 +73,7 @@ namespace Components
ZoneBuilder();
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "ZoneBuilder"; };
#endif

View File

@ -0,0 +1,21 @@
#include "STDInclude.hpp"
namespace Components
{
int Zones::ZoneVersion;
void Zones::InstallPatches(int version)
{
Zones::ZoneVersion = version;
}
Zones::Zones()
{
}
Zones::~Zones()
{
}
}

View File

@ -0,0 +1,18 @@
namespace Components
{
class Zones : public Component
{
public:
Zones();
~Zones();
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
const char* GetName() { return "Zones"; };
#endif
static void InstallPatches(int version);
private:
static int ZoneVersion;
};
}

View File

@ -22,6 +22,7 @@ namespace Game
Cmd_AddCommand_t Cmd_AddCommand = (Cmd_AddCommand_t)0x470090;
Cmd_AddServerCommand_t Cmd_AddServerCommand = (Cmd_AddServerCommand_t)0x4DCE00;
Cmd_ExecuteSingleCommand_t Cmd_ExecuteSingleCommand = (Cmd_ExecuteSingleCommand_t)0x609540;
Com_ClientPacketEvent_t Com_ClientPacketEvent = (Com_ClientPacketEvent_t)0x49F0B0;
Com_Error_t Com_Error = (Com_Error_t)0x4B22D0;
Com_Printf_t Com_Printf = (Com_Printf_t)0x402500;
@ -31,6 +32,8 @@ namespace Game
Con_DrawMiniConsole_t Con_DrawMiniConsole = (Con_DrawMiniConsole_t)0x464F30;
Con_DrawSolidConsole_t Con_DrawSolidConsole = (Con_DrawSolidConsole_t)0x5A5040;
DB_BeginRecoverLostDevice_t DB_BeginRecoverLostDevice = (DB_BeginRecoverLostDevice_t)0x4BFF90;
DB_EndRecoverLostDevice_t DB_EndRecoverLostDevice = (DB_EndRecoverLostDevice_t)0x46B660;
DB_EnumXAssets_t DB_EnumXAssets = (DB_EnumXAssets_t)0x4B76D0;
DB_EnumXAssets_Internal_t DB_EnumXAssets_Internal = (DB_EnumXAssets_Internal_t)0x5BB0A0;
DB_FindXAssetHeader_t DB_FindXAssetHeader = (DB_FindXAssetHeader_t)0x407930;
@ -40,6 +43,8 @@ namespace Game
DB_IsXAssetDefault_t DB_IsXAssetDefault = (DB_IsXAssetDefault_t)0x48E6A0;
DB_LoadXAssets_t DB_LoadXAssets = (DB_LoadXAssets_t)0x4E5930;
DB_ReadXFileUncompressed_t DB_ReadXFileUncompressed = (DB_ReadXFileUncompressed_t)0x4705E0;
DB_ReleaseXAssetHandler_t* DB_ReleaseXAssetHandlers = (DB_ReleaseXAssetHandler_t*)0x799AB8;
DB_XModelSurfsFixup_t DB_XModelSurfsFixup = (DB_XModelSurfsFixup_t)0x5BAC50;
Dvar_RegisterBool_t Dvar_RegisterBool = (Dvar_RegisterBool_t)0x4CE1A0;
Dvar_RegisterFloat_t Dvar_RegisterFloat = (Dvar_RegisterFloat_t)0x648440;
@ -88,6 +93,8 @@ namespace Game
Image_LoadFromFileWithReader_t Image_LoadFromFileWithReader = (Image_LoadFromFileWithReader_t)0x53ABF0;
Image_Release_t Image_Release = (Image_Release_t)0x51F010;
LargeLocalInit_t LargeLocalInit = (LargeLocalInit_t)0x4A62A0;
Menus_CloseAll_t Menus_CloseAll = (Menus_CloseAll_t)0x4BA5B0;
Menus_OpenByName_t Menus_OpenByName = (Menus_OpenByName_t)0x4CCE60;
Menus_FindByName_t Menus_FindByName = (Menus_FindByName_t)0x487240;
@ -111,6 +118,7 @@ namespace Game
NET_AdrToString_t NET_AdrToString = (NET_AdrToString_t)0x469880;
NET_CompareAdr_t NET_CompareAdr = (NET_CompareAdr_t)0x4D0AA0;
NET_Init_t NET_Init = (NET_Init_t)0x491860;
NET_IsLocalAddress_t NET_IsLocalAddress = (NET_IsLocalAddress_t)0x402BD0;
NET_StringToAdr_t NET_StringToAdr = (NET_StringToAdr_t)0x409010;
NET_OutOfBandPrint_t NET_OutOfBandPrint = (NET_OutOfBandPrint_t)0x4AEF00;
@ -131,6 +139,7 @@ namespace Game
PartyHost_GetMemberName_t PartyHost_GetMemberName = (PartyHost_GetMemberName_t)0x44BE90;
R_AddCmdDrawStretchPic_t R_AddCmdDrawStretchPic = (R_AddCmdDrawStretchPic_t)0x509770;
R_AllocStaticIndexBuffer_t R_AllocStaticIndexBuffer = (R_AllocStaticIndexBuffer_t)0x51E7A0;
R_Cinematic_StartPlayback_Now_t R_Cinematic_StartPlayback_Now = (R_Cinematic_StartPlayback_Now_t)0x51C5B0;
R_RegisterFont_t R_RegisterFont = (R_RegisterFont_t)0x505670;
R_AddCmdDrawText_t R_AddCmdDrawText = (R_AddCmdDrawText_t)0x509D80;
@ -461,4 +470,38 @@ namespace Game
pop esi
}
}
void Load_IndexBuffer(void* data, IDirect3DIndexBuffer9** storeHere, int count)
{
if (Components::Dvar::Var("r_loadForRenderer").Get<bool>())
{
void* buffer = R_AllocStaticIndexBuffer(storeHere, 2 * count);
memcpy(buffer, data, 2 * count);
if (storeHere && *storeHere)
{
(*storeHere)->Unlock();
}
}
}
void Load_VertexBuffer(void* data, IDirect3DVertexBuffer9** where, int len)
{
__asm
{
push edi
push ebx
mov eax, len
mov edi, where
push data
mov ebx, 5112C0h
call ebx
add esp, 4
pop ebx
pop edi
}
}
}

View File

@ -45,6 +45,9 @@ namespace Game
typedef void(__cdecl * Cmd_ExecuteSingleCommand_t)(int localClientNum, int controllerIndex, const char* cmd);
extern Cmd_ExecuteSingleCommand_t Cmd_ExecuteSingleCommand;
typedef void(__cdecl * Com_ClientPacketEvent_t)();
extern Com_ClientPacketEvent_t Com_ClientPacketEvent;
typedef void(__cdecl * Com_Error_t)(int type, char* message, ...);
extern Com_Error_t Com_Error;
@ -63,6 +66,12 @@ namespace Game
typedef void (__cdecl * Con_DrawSolidConsole_t)();
extern Con_DrawSolidConsole_t Con_DrawSolidConsole;
typedef void(__cdecl * DB_BeginRecoverLostDevice_t)();
extern DB_BeginRecoverLostDevice_t DB_BeginRecoverLostDevice;
typedef void(__cdecl * DB_EndRecoverLostDevice_t)();
extern DB_EndRecoverLostDevice_t DB_EndRecoverLostDevice;
typedef void(__cdecl * DB_EnumXAssets_t)(XAssetType type, void(*)(XAssetHeader, void *), void* userdata, bool overrides);
extern DB_EnumXAssets_t DB_EnumXAssets;
@ -90,6 +99,12 @@ namespace Game
typedef void(__cdecl * DB_ReadXFileUncompressed_t)(void* buffer, int size);
extern DB_ReadXFileUncompressed_t DB_ReadXFileUncompressed;
typedef void(__cdecl * DB_ReleaseXAssetHandler_t)(XAssetHeader header);
extern DB_ReleaseXAssetHandler_t* DB_ReleaseXAssetHandlers;
typedef void(__cdecl * DB_XModelSurfsFixup_t)(XModel* model);
extern DB_XModelSurfsFixup_t DB_XModelSurfsFixup;
typedef dvar_t* (__cdecl * Dvar_RegisterBool_t)(const char* name, bool default, int flags, const char* description);
extern Dvar_RegisterBool_t Dvar_RegisterBool;
@ -206,6 +221,9 @@ namespace Game
typedef void(__cdecl * Image_Release_t)(GfxImage* image);
extern Image_Release_t Image_Release;
typedef void(__cdecl * LargeLocalInit_t)();
extern LargeLocalInit_t LargeLocalInit;
typedef void(__cdecl * Menus_CloseAll_t)(UiContext *dc);
extern Menus_CloseAll_t Menus_CloseAll;
@ -266,6 +284,9 @@ namespace Game
typedef bool(__cdecl * NET_CompareAdr_t)(netadr_t a, netadr_t b);
extern NET_CompareAdr_t NET_CompareAdr;
typedef void(__cdecl * NET_Init_t)();
extern NET_Init_t NET_Init;
typedef bool(__cdecl * NET_IsLocalAddress_t)(netadr_t adr);
extern NET_IsLocalAddress_t NET_IsLocalAddress;
@ -317,6 +338,9 @@ namespace Game
typedef void(_cdecl * R_AddCmdDrawStretchPic_t)(float x, float y, float w, float h, float xScale, float yScale, float xay, float yay, const float *color, Game::Material* material);
extern R_AddCmdDrawStretchPic_t R_AddCmdDrawStretchPic;
typedef void* (__cdecl * R_AllocStaticIndexBuffer_t)(IDirect3DIndexBuffer9** store, int length);
extern R_AllocStaticIndexBuffer_t R_AllocStaticIndexBuffer;
typedef bool(__cdecl * R_Cinematic_StartPlayback_Now_t)();
extern R_Cinematic_StartPlayback_Now_t R_Cinematic_StartPlayback_Now;
@ -518,4 +542,7 @@ namespace Game
void IN_KeyUp(kbutton_t* button);
void IN_KeyDown(kbutton_t* button);
void Load_IndexBuffer(void* data, IDirect3DIndexBuffer9** storeHere, int count);
void Load_VertexBuffer(void* data, IDirect3DVertexBuffer9** where, int len);
}

View File

@ -818,6 +818,10 @@ namespace Game
menuDef_t **menus;
};
#define FS_SEEK_CUR 0
#define FS_SEEK_END 1
#define FS_SEEK_SET 2
enum FsListBehavior_e
{
FS_LIST_PURE_ONLY = 0x0,
@ -1394,7 +1398,10 @@ namespace Game
struct XModelLodInfo
{
char pad[4]; // +0
// I'm not sure if this is correct
short someCount;
short someTotalCount;
short numSurfs; // +4
short pad2;// +6
XModelSurfs* surfaces; // +8
@ -1525,6 +1532,39 @@ namespace Game
PhysCollmap* physCollmap;
}; // total size 304
//static_assert(offsetof(XModel, lods) <= 70, "");
struct CModelAllocData
{
void* mainArray;
void* vertexBuffer;
void* indexBuffer;
};
struct CModelSectionHeader
{
int size;
int offset;
int fixupStart;
int fixupCount;
void* buffer;
};
enum CModelSection
{
SECTION_MAIN = 0,
SECTION_INDEX = 1,
SECTION_VERTEX = 2,
SECTION_FIXUP = 3,
};
struct CModelHeader
{
int version;
unsigned int signature;
CModelSectionHeader sectionHeader[4];
};
struct DSkelPartBits
{
int anim[4];

View File

@ -2,13 +2,15 @@
// Rename sections
#ifndef DEBUG
#pragma comment(linker, "/merge:.text=.fucker")
#pragma comment(linker, "/merge:.data=.bastard")
#pragma comment(linker, "/merge:.rdata=.faggot")
#pragma comment(linker, "/merge:.tls=.asshole")
#pragma comment(linker, "/merge:.gfids=.idiot")
#pragma comment(linker, "/merge:.text=.UPX0")
#pragma comment(linker, "/merge:.data=.UPX1")
#pragma comment(linker, "/merge:.rdata=.UPX2")
#pragma comment(linker, "/merge:.tls=.UPX3")
#pragma comment(linker, "/merge:.gfids=.UPX4")
#endif
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
// Do necessary assertions here
// Some compilers treat them differently which causes a size mismatch

View File

@ -91,6 +91,7 @@
#include "Utils\Memory.hpp"
#include "Utils\String.hpp"
#include "Utils\Hooking.hpp"
#include "Utils\Library.hpp"
#include "Utils\InfoString.hpp"
#include "Utils\Compression.hpp"
#include "Utils\Cryptography.hpp"

View File

@ -2,8 +2,8 @@
namespace Steam
{
HMODULE Proxy::Client = nullptr;
HMODULE Proxy::Overlay = nullptr;
::Utils::Library Proxy::Client;
::Utils::Library Proxy::Overlay;
bool Proxy::Inititalize()
{
@ -12,20 +12,16 @@ namespace Steam
SetDllDirectoryA(Proxy::GetSteamDirectory().data());
Proxy::Client = LoadLibraryA(STEAMCLIENT_LIB);
Proxy::Overlay = LoadLibraryA(GAMEOVERLAY_LIB);
Proxy::Client = ::Utils::Library(STEAMCLIENT_LIB, false);
Proxy::Overlay = ::Utils::Library(GAMEOVERLAY_LIB, false);
return (Proxy::Client && Proxy::Overlay);
return (Proxy::Client.Valid() && Proxy::Overlay.Valid());
}
void Proxy::Uninititalize()
{
// Freeing libraries causes crashes
//if (Proxy::Client) FreeLibrary(Proxy::Client);
Proxy::Client = nullptr;
//if (Proxy::Overlay) FreeLibrary(Proxy::Overlay);
Proxy::Overlay = nullptr;
Proxy::Client = ::Utils::Library();
Proxy::Overlay = ::Utils::Library();
}
std::string Proxy::GetSteamDirectory()
@ -46,27 +42,17 @@ namespace Steam
void Proxy::SetOverlayNotificationPosition(uint32_t eNotificationPosition)
{
if (Proxy::Overlay)
if (Proxy::Overlay.Valid())
{
FARPROC SetNotificationPositionFn = GetProcAddress(Proxy::Overlay, "SetNotificationPosition");
if (SetNotificationPositionFn)
{
::Utils::Hook::Call<void(uint32_t)>(SetNotificationPositionFn)(eNotificationPosition);
}
Proxy::Overlay.Get<void(uint32_t)>("SetNotificationPosition")(eNotificationPosition);
}
}
bool Proxy::IsOverlayEnabled()
{
if (Proxy::Overlay)
if (Proxy::Overlay.Valid())
{
FARPROC IsOverlayEnabledFn = GetProcAddress(Proxy::Overlay, "IsOverlayEnabled");
if (IsOverlayEnabledFn)
{
return ::Utils::Hook::Call<bool()>(IsOverlayEnabledFn)();
}
return Proxy::Overlay.Get<bool()>("IsOverlayEnabled")();
}
return false;
@ -74,14 +60,9 @@ namespace Steam
bool Proxy::BOverlayNeedsPresent()
{
if (Proxy::Overlay)
if (Proxy::Overlay.Valid())
{
FARPROC BOverlayNeedsPresentFn = GetProcAddress(Proxy::Overlay, "BOverlayNeedsPresent");
if (BOverlayNeedsPresentFn)
{
return ::Utils::Hook::Call<bool()>(BOverlayNeedsPresentFn)();
}
return Proxy::Overlay.Get<bool()>("BOverlayNeedsPresent")();
}
return false;

View File

@ -22,8 +22,8 @@ namespace Steam
static bool BOverlayNeedsPresent();
private:
static HMODULE Client;
static HMODULE Overlay;
static ::Utils::Library Client;
static ::Utils::Library Overlay;
static std::string GetSteamDirectory();
};

View File

@ -1,5 +1,3 @@
#define ARR_SIZE(x) (sizeof(x) / sizeof(x[0]))
namespace Utils
{
class InfoString

29
src/Utils/Library.cpp Normal file
View File

@ -0,0 +1,29 @@
#include "STDInclude.hpp"
namespace Utils
{
Library::Library(std::string buffer, bool freeOnDestroy) : FreeOnDestroy(freeOnDestroy), Module(nullptr)
{
this->Module = LoadLibraryExA(buffer.data(), NULL, 0);
}
Library::~Library()
{
if (this->FreeOnDestroy && this->Valid())
{
FreeLibrary(this->GetModule());
}
this->Module = nullptr;
}
bool Library::Valid()
{
return (this->GetModule() != nullptr);
}
HMODULE Library::GetModule()
{
return this->Module;
}
}

28
src/Utils/Library.hpp Normal file
View File

@ -0,0 +1,28 @@
namespace Utils
{
class Library
{
public:
Library() : Module(nullptr), FreeOnDestroy(false) {};
Library(std::string buffer, bool freeOnDestroy = true);
~Library();
bool Valid();
HMODULE GetModule();
template <typename T>
std::function<T> Get(std::string process)
{
if (!this->Valid())
{
throw new std::runtime_error("Library not loaded!");
}
return reinterpret_cast<T*>(GetProcAddress(this->GetModule(), process.data()));
}
private:
HMODULE Module;
bool FreeOnDestroy;
};
}

View File

@ -2,17 +2,17 @@
namespace Utils
{
void* Memory::AllocateAlign(size_t length, size_t alignment)
{
void* data = _aligned_malloc(length, alignment);
assert(data != nullptr);
return data;
}
void* Memory::Allocate(size_t length)
{
void* data = new char[length];
void* data = calloc(length, 1);
assert(data != nullptr);
if (data)
{
ZeroMemory(data, length);
}
return data;
}
@ -27,7 +27,7 @@ namespace Utils
{
if (data)
{
delete[] data;
free(data);
}
}

View File

@ -92,6 +92,7 @@ namespace Utils
std::map<void*, FreeCallback> RefMemory;
};
static void* AllocateAlign(size_t length, size_t alignment);
static void* Allocate(size_t length);
template <typename T> static T* Allocate()
{

View File

@ -12,7 +12,7 @@ namespace Utils
class Reader
{
public:
Reader(Utils::Memory::Allocator* allocator, std::string& buffer) : Buffer(buffer), Allocator(allocator), Position(0) {}
Reader(Utils::Memory::Allocator* allocator, std::string buffer) : Buffer(buffer), Allocator(allocator), Position(0) {}
std::string ReadString();
const char* ReadCString();

Binary file not shown.