From 6fb6a1e977a29a54f10d130f9f1539c15e1fbebf Mon Sep 17 00:00:00 2001
From: momo5502 <mauriceheumann@gmail.com>
Date: Fri, 25 Dec 2015 21:42:35 +0100
Subject: [PATCH] Party and connect.

---
 iw4/Components/Command.cpp         |  14 +++
 iw4/Components/Command.hpp         |   3 +-
 iw4/Components/Dvar.cpp            |   4 +-
 iw4/Components/Dvar.hpp            |   1 +
 iw4/Components/Loader.cpp          |   1 +
 iw4/Components/Loader.hpp          |   1 +
 iw4/Components/Menus.cpp           |  69 ++++++---------
 iw4/Components/Menus.hpp           |   6 +-
 iw4/Components/Network.cpp         |  45 +---------
 iw4/Components/Network.hpp         |   2 +-
 iw4/Components/Party.cpp           | 133 +++++++++++++++++++++++++++++
 iw4/Components/Party.hpp           |  23 +++++
 iw4/Components/QuickPatch.cpp      |  16 ++++
 iw4/Components/QuickPatch.hpp      |   3 +
 iw4/Game/Functions.cpp             |  21 +++++
 iw4/Game/Functions.hpp             |  55 ++++++++++++
 iw4/STDInclude.hpp                 |   5 ++
 iw4/Steam/Interfaces/SteamUser.cpp |  15 ++--
 iw4/Utils/Utils.cpp                |  27 +++---
 iw4/Utils/Utils.hpp                |   4 +-
 iw4/iw4.vcxproj                    |   2 +
 iw4/iw4.vcxproj.filters            |   6 ++
 22 files changed, 345 insertions(+), 111 deletions(-)
 create mode 100644 iw4/Components/Party.cpp
 create mode 100644 iw4/Components/Party.hpp

diff --git a/iw4/Components/Command.cpp b/iw4/Components/Command.cpp
index 18e05658..74b12b43 100644
--- a/iw4/Components/Command.cpp
+++ b/iw4/Components/Command.cpp
@@ -36,6 +36,20 @@ namespace Components
 		Game::Cmd_AddCommand(name, Command::MainCallback, Command::Allocate(), 0);
 	}
 
+	void Command::Execute(std::string command, bool sync)
+	{
+		command.append("\n"); // Make sure it's terminated
+
+		if (sync)
+		{
+			Game::Cmd_ExecuteSingleCommand(0, 0, command.data());
+		}
+		else
+		{
+			Game::Cbuf_AddText(0, command.data());
+		}
+	}
+
 	Game::cmd_function_t* Command::Allocate()
 	{
 		Game::cmd_function_t* cmd = new Game::cmd_function_t;
diff --git a/iw4/Components/Command.hpp b/iw4/Components/Command.hpp
index f1d7c625..1b293e7a 100644
--- a/iw4/Components/Command.hpp
+++ b/iw4/Components/Command.hpp
@@ -23,7 +23,8 @@ namespace Components
 		const char* GetName() { return "Command"; };
 
 		static void Add(const char* name, Callback callback);
-		static int ArgCount();
+
+		static void Execute(std::string command, bool sync = true);
 
 	private:
 		static Game::cmd_function_t* Allocate();
diff --git a/iw4/Components/Dvar.cpp b/iw4/Components/Dvar.cpp
index f9bc396d..7070d98a 100644
--- a/iw4/Components/Dvar.cpp
+++ b/iw4/Components/Dvar.cpp
@@ -8,7 +8,9 @@ namespace Components
 
 		if (!this->dvar)
 		{
-			// Register the dvar?
+			// Quick-register the dvar
+			Game::SetConsole(dvarName.data(), "");
+			this->dvar = Game::Dvar_FindVar(dvarName.data());
 		}
 	}
 
diff --git a/iw4/Components/Dvar.hpp b/iw4/Components/Dvar.hpp
index 65725015..c2c23b85 100644
--- a/iw4/Components/Dvar.hpp
+++ b/iw4/Components/Dvar.hpp
@@ -10,6 +10,7 @@ namespace Components
 			Var(const Var &obj) { this->dvar = obj.dvar; };
 			Var(Game::dvar_t* _dvar) : dvar(_dvar) {};
 			Var(std::string dvarName);
+			Var(std::string dvarName, std::string value);
 
 			template<typename T> T Get();
 
diff --git a/iw4/Components/Loader.cpp b/iw4/Components/Loader.cpp
index a8e5a1c1..6e7a83af 100644
--- a/iw4/Components/Loader.cpp
+++ b/iw4/Components/Loader.cpp
@@ -8,6 +8,7 @@ namespace Components
 	{
 		Loader::Register(new Dvar());
 		Loader::Register(new Menus());
+		Loader::Register(new Party());
 		Loader::Register(new Colors());
 		Loader::Register(new Logger());
 		Loader::Register(new Window());
diff --git a/iw4/Components/Loader.hpp b/iw4/Components/Loader.hpp
index 8fda62a8..57220501 100644
--- a/iw4/Components/Loader.hpp
+++ b/iw4/Components/Loader.hpp
@@ -28,6 +28,7 @@ namespace Components
 #include "Command.hpp"
 #include "Console.hpp"
 #include "Network.hpp"
+#include "Party.hpp" // Destroys the order, but requires network classes :D
 #include "RawFiles.hpp"
 #include "Renderer.hpp"
 #include "FastFiles.hpp"
diff --git a/iw4/Components/Menus.cpp b/iw4/Components/Menus.cpp
index 868e3ff4..274c85cb 100644
--- a/iw4/Components/Menus.cpp
+++ b/iw4/Components/Menus.cpp
@@ -116,7 +116,7 @@ namespace Components
 		Game::pc_token_t token;
 		Game::keywordHash_t *key;
 
-		if (!Menus::ReadToken(handle, &token) || token.string[0] != '{')
+		if (!Game::PC_ReadTokenHandle(handle, &token) || token.string[0] != '{')
 		{
 			return menu;
 		}
@@ -125,7 +125,7 @@ namespace Components
 		{
 			ZeroMemory(&token, sizeof(token));
 
-			if (!Menus::ReadToken(handle, &token)) 
+			if (!Game::PC_ReadTokenHandle(handle, &token))
 			{
 				Game::PC_SourceError(handle, "end of file inside menu\n");
 				break; // Fail
@@ -153,40 +153,11 @@ namespace Components
 			}
 		}
 
+		OutputDebugStringA(Utils::VA("%X %s", menu->window.name, menu->window.name));
+
 		return menu;
 	}
 
-	int Menus::ReadToken(int handle, Game::pc_token_t *pc_token)
-	{
-		Game::token_t token;
-		int ret;
-
-		if (!Menus::IsValidSourceHandle(handle)) return 0;
-
-		ret = Game::PC_ReadToken(Game::sourceFiles[handle], &token);
-		strcpy(pc_token->string, token.string);
-		pc_token->type = token.type;
-		pc_token->subtype = token.subtype;
-		pc_token->intvalue = token.intvalue;
-		pc_token->floatvalue = (float)token.floatvalue;
-
-		if (pc_token->type == TT_STRING)
-		{
-			// StripDoubleQuotes
-			char *string = pc_token->string;
-			if (*string == '\"')
-			{
-				strcpy(string, string + 1);
-			}
-			if (string[strlen(string) - 1] == '\"')
-			{
-				string[strlen(string) - 1] = '\0';
-			}
-		}
-
-		return ret;
-	}
-
 	std::vector<Game::menuDef_t*> Menus::LoadMenu(Game::menuDef_t* menudef)
 	{
 		std::vector<Game::menuDef_t*> menus;
@@ -206,14 +177,14 @@ namespace Components
 			{
 				ZeroMemory(&token, sizeof(token));
 
-				if (!Menus::ReadToken(handle, &token) || token.string[0] == '}')
+				if (!Game::PC_ReadTokenHandle(handle, &token) || token.string[0] == '}')
 				{
 					break;
 				}
 
 				if (!_stricmp(token.string, "loadmenu"))
 				{
-					Menus::ReadToken(handle, &token);
+					Game::PC_ReadTokenHandle(handle, &token);
 
 					// Ugly, but does the job ;)
 					Game::menuDef_t _temp;
@@ -250,6 +221,11 @@ namespace Components
 
 		for (int i = 0; i < menuList->menuCount; i++)
 		{
+			if (!menuList->menus[i])
+			{
+				continue;
+			}
+
 			std::vector<Game::menuDef_t*> newMenus = Menus::LoadMenu(menuList->menus[i]);
 
 			for (auto newMenu : newMenus)
@@ -409,19 +385,16 @@ namespace Components
 
 	Game::XAssetHeader Menus::MenuFileLoad(Game::XAssetType type, const char* filename)
 	{
-		Game::XAssetHeader header = { 0 };
+ 		Game::XAssetHeader header = { 0 };
 
 		// Check if we already loaded it
 		for (auto menuList : Menus::MenuListList)
 		{
 			if (!_stricmp(menuList->name, filename))
 			{
-				// Free it!
-				// Seems like the game deallocated half of it :P
+				// Free it, seems like the game deallocated it
 				Menus::RemoveMenuList(menuList);
 				break;
-				//header.menuList = menuList;
-				//return header;
 			}
 		}
 
@@ -431,7 +404,7 @@ namespace Components
 		if (menuList)
 		{
 			// Don't parse scriptmenus for now!
-			if (!Utils::EndsWith(filename, ".menu"))
+			if (strcmp(menuList->menus[0]->window.name, "default_menu") && !Utils::EndsWith(filename, ".menu"))
 			{
 				header.menuList = Menus::LoadMenuList(menuList);
 			}
@@ -440,17 +413,31 @@ namespace Components
 		return header;
 	}
 
+	void Menus::AddMenuListHook(int dc, Game::MenuList *menuList, int close)
+	{
+		Game::MenuList* menus = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MENUFILE, "ui_mp/menus.txt").menuList;
+
+		Game::UI_AddMenuList(dc, menus, close);
+		Game::UI_AddMenuList(dc, menuList, close);
+	}
+
 	Menus::Menus()
 	{
 		AssetHandler::On(Game::XAssetType::ASSET_TYPE_MENUFILE, Menus::MenuFileLoad);
 		//Utils::Hook(0x63FE80, Menus::MenuFileLoad, HOOK_JUMP).Install()->Quick();
 
+		// Load menus ingame
+		//Utils::Hook(0x41C178, Menus::AddMenuListHook, HOOK_CALL).Install()->Quick();
+
 		// disable the 2 new tokens in ItemParse_rect
 		Utils::Hook::Set<BYTE>(0x640693, 0xEB);
 
 		// don't load ASSET_TYPE_MENU assets for every menu (might cause patch menus to fail)
 		Utils::Hook::Nop(0x453406, 5);
 
+		//make Com_Error and similar go back to main_text instead of menu_xboxlive.
+		strcpy((char*)0x6FC790, "main_text");
+
 		Command::Add("openmenu", [] (Command::Params params)
 		{
 			if (params.Length() != 2)
diff --git a/iw4/Components/Menus.hpp b/iw4/Components/Menus.hpp
index 4df89f9c..a30457c1 100644
--- a/iw4/Components/Menus.hpp
+++ b/iw4/Components/Menus.hpp
@@ -9,6 +9,8 @@ namespace Components
 		~Menus();
 		const char* GetName() { return "Menus"; };
 
+		static void FreeEverything();
+
 	private:
 		static std::vector<Game::menuDef_t*> MenuList;
 		static std::vector<Game::MenuList*> MenuListList;
@@ -23,8 +25,6 @@ namespace Components
 		static int ReserveSourceHandle();
 		static bool IsValidSourceHandle(int handle);
 
-		static int ReadToken(int handle, Game::pc_token_t *pc_token);
-
 		static Game::menuDef_t* ParseMenu(int handle);
 
 		static void FreeMenuSource(int handle);
@@ -35,7 +35,7 @@ namespace Components
 		static void RemoveMenu(Game::menuDef_t* menudef);
 		static void RemoveMenuList(Game::MenuList* menuList);
 
-		static void FreeEverything();
+		static void AddMenuListHook(int dc, Game::MenuList *menuList, int close);
 
 		// Ugly!
 		static int KeywordHash(char* key);
diff --git a/iw4/Components/Network.cpp b/iw4/Components/Network.cpp
index a76d81a4..d8cd9a4b 100644
--- a/iw4/Components/Network.cpp
+++ b/iw4/Components/Network.cpp
@@ -60,7 +60,8 @@ namespace Components
 	{
 		if (Network::PacketHandlers.find(Network::SelectedPacket) != Network::PacketHandlers.end())
 		{
-			Network::PacketHandlers[Network::SelectedPacket](from, msg);
+			size_t offset = Network::SelectedPacket.size() + 4 + 1;
+			Network::PacketHandlers[Network::SelectedPacket](from, std::string(msg->data + offset, msg->cursize - offset));
 		}
 		else
 		{
@@ -109,48 +110,6 @@ namespace Components
 
 		// Install packet deploy hook
 		Utils::Hook::Set<int>(0x5AA715, (DWORD)Network::DeployPacketStub - 0x5AA713 - 6);
-
-		Network::Handle("infoResponse", [] (Address address, Game::msg_t* message)
-		{
-			OutputDebugStringA(Utils::VA("Inforesponse received: %s %s!", address.GetString(), message->data));
-		});
-
-		Network::Handle("getInfo", [] (Address address, Game::msg_t* message)
-		{
-			OutputDebugStringA(Utils::VA("getinfo received: %s!", address.GetString()));
-
-			int clientCount = 0;
-
-			for (int i = 0; i < *Game::svs_numclients; i++)
-			{
-				if (Game::svs_clients[i].state >= 3)
-				{
-					clientCount++;
-				}
-			}
-
-			auto data = std::string(message->data);
-			auto challenge = data.substr(data.find_first_of(" \n") + 1);
-			challenge = challenge.substr(0, challenge.find_first_of(" \n"));
-
-			Utils::InfoString info;
-			info.Set("challenge",     challenge.data()); // TODO: Fill!
-			info.Set("gamename",      "IW4");
-			info.Set("hostname",      Dvar::Var("sv_hostname").Get<const char*>());
-			info.Set("mapname",       Dvar::Var("mapname").Get<const char*>());
-			info.Set("gametype",      Dvar::Var("g_gametype").Get<const char*>());
-			info.Set("fs_game",       Dvar::Var("fs_game").Get<const char*>());
-			info.Set("xuid",          Utils::VA("%llX", Steam::SteamUser()->GetSteamID().m_Bits));
-			info.Set("clients",       Utils::VA("%i", clientCount));
-			info.Set("sv_maxclients", Utils::VA("%i", *Game::svs_numclients));
-
-			Network::Send(Game::NS_CLIENT, address, Utils::VA("infoResponse\n%s\n", info.Build().data()));
-		});
-
-		Command::Add("zob", [] (Command::Params params)
-		{
-			Network::Send(Game::NS_CLIENT, Network::Address("localhost:28960"), "getinfo xxx\n");
-		});
 	}
 
 	Network::~Network()
diff --git a/iw4/Components/Network.hpp b/iw4/Components/Network.hpp
index fc845545..958f592a 100644
--- a/iw4/Components/Network.hpp
+++ b/iw4/Components/Network.hpp
@@ -24,7 +24,7 @@ namespace Components
 			Game::netadr_t address;
 		};
 
-		typedef void(*Callback)(Address address, Game::msg_t* message);
+		typedef void(*Callback)(Address address, std::string data);
 
 		Network();
 		~Network();
diff --git a/iw4/Components/Party.cpp b/iw4/Components/Party.cpp
new file mode 100644
index 00000000..5f881f2d
--- /dev/null
+++ b/iw4/Components/Party.cpp
@@ -0,0 +1,133 @@
+#include "..\STDInclude.hpp"
+
+namespace Components
+{
+	Party::JoinContainer Party::Container;
+
+	void Party::Connect(Network::Address target)
+	{
+		Party::Container.Valid = true;
+		Party::Container.JoinTime = Game::Com_Milliseconds();
+		Party::Container.Target = target;
+		Party::Container.Challenge = Utils::VA("%X", Party::Container.JoinTime);
+
+		Network::Send(Game::NS_CLIENT, Party::Container.Target, Utils::VA("getinfo %s\n", Party::Container.Challenge.data()));
+
+		Command::Execute("openmenu popup_reconnectingtoparty");
+	}
+
+	Party::Party()
+	{
+		// various changes to SV_DirectConnect-y stuff to allow non-party joinees
+		Utils::Hook::Set<WORD>(0x460D96, 0x90E9);
+		Utils::Hook::Set<BYTE>(0x460F0A, 0xEB);
+		Utils::Hook::Set<BYTE>(0x401CA4, 0xEB);
+		Utils::Hook::Set<BYTE>(0x401C15, 0xEB);
+
+		// disable configstring checksum matching (it's unreliable at most)
+		Utils::Hook::Set<BYTE>(0x4A75A7, 0xEB); // SV_SpawnServer
+		Utils::Hook::Set<BYTE>(0x5AC2CF, 0xEB); // CL_ParseGamestate
+		Utils::Hook::Set<BYTE>(0x5AC2C3, 0xEB); // CL_ParseGamestate
+
+		Command::Add("connect", [] (Command::Params params)
+		{
+			if (params.Length() < 2)
+			{
+				return;
+			}
+
+			Party::Connect(Network::Address(params[1]));
+		});
+
+		Renderer::OnFrame([] ()
+		{
+			if (!Party::Container.Valid) return;
+
+			if ((Game::Com_Milliseconds() - Party::Container.JoinTime) > 5000)
+			{
+				Party::Container.Valid = false;
+
+				Command::Execute("closemenu popup_reconnectingtoparty");
+				Dvar::Var("partyend_reason").Set("Server connection timed out.");
+				Command::Execute("openmenu menu_xboxlive_partyended");
+			}
+		});
+
+		// Basic info handler
+		Network::Handle("getInfo", [] (Network::Address address, std::string data)
+		{
+			int clientCount = 0;
+
+			for (int i = 0; i < *Game::svs_numclients; i++)
+			{
+				if (Game::svs_clients[i].state >= 3)
+				{
+					clientCount++;
+				}
+			}
+
+			Utils::InfoString info;
+			info.Set("challenge", data.substr(0, data.find_first_of("\n")).data());
+			info.Set("gamename", "IW4");
+			info.Set("hostname", Dvar::Var("sv_hostname").Get<const char*>());
+			info.Set("mapname", Dvar::Var("mapname").Get<const char*>());
+			info.Set("gametype", Dvar::Var("g_gametype").Get<const char*>());
+			info.Set("fs_game", Dvar::Var("fs_game").Get<const char*>());
+			info.Set("xuid", Utils::VA("%llX", Steam::SteamUser()->GetSteamID().m_Bits));
+			info.Set("clients", Utils::VA("%i", clientCount));
+			info.Set("sv_maxclients", Utils::VA("%i", *Game::svs_numclients));
+
+			Network::Send(Game::NS_CLIENT, address, Utils::VA("infoResponse\n%s\n", info.Build().data()));
+		});
+
+		Network::Handle("infoResponse", [] (Network::Address address, std::string data)
+		{
+			// Handle connection
+			if (Party::Container.Valid)
+			{
+				if (Party::Container.Target == address)
+				{
+					// Invalidate handler for future packets
+					Party::Container.Valid = false;
+
+					Utils::InfoString info(data);
+
+					OutputDebugStringA(data.data());
+
+					if (info.Get("challenge") != Party::Container.Challenge)
+					{
+						OutputDebugStringA(Utils::VA("\"%s\" vs. \"%s\"", info.Get("challenge").data(), Party::Container.Challenge.data()));
+						Command::Execute("closemenu popup_reconnectingtoparty");
+						Dvar::Var("partyend_reason").Set("Invalid join response: Challenge mismatch.");
+						Command::Execute("openmenu menu_xboxlive_partyended");
+					}
+					else if (atoi(info.Get("clients").data()) >= atoi(info.Get("sv_maxclients").data()))
+					{
+						Command::Execute("closemenu popup_reconnectingtoparty");
+						Dvar::Var("partyend_reason").Set("@EXE_SERVERISFULL");
+						Command::Execute("openmenu menu_xboxlive_partyended");
+					}
+					else if (info.Get("mapname") == "" || info.Get("gametype") == "")
+					{
+						Command::Execute("closemenu popup_reconnectingtoparty");
+						Dvar::Var("partyend_reason").Set("Invalid map or gametype.");
+						Command::Execute("openmenu menu_xboxlive_partyended");
+					}
+					else
+					{
+						Dvar::Var("xblive_privatematch").Set(1);
+						Game::Menus_CloseAll(0x62E2858);
+
+						char xnaddr[32];
+						Game::CL_ConnectFromParty(0, xnaddr, *address.Get(), 0, 0, info.Get("mapname").data(), info.Get("gametype").data());
+					}
+				}
+			}
+		});
+	}
+
+	Party::~Party()
+	{
+
+	}
+}
diff --git a/iw4/Components/Party.hpp b/iw4/Components/Party.hpp
new file mode 100644
index 00000000..9f2d6237
--- /dev/null
+++ b/iw4/Components/Party.hpp
@@ -0,0 +1,23 @@
+namespace Components
+{
+	class Party : public Component
+	{
+	public:
+		Party();
+		~Party();
+		const char* GetName() { return "Party"; };
+
+		static void Connect(Network::Address target);
+
+	private:
+		struct JoinContainer
+		{
+			Network::Address Target;
+			std::string Challenge;
+			DWORD JoinTime;
+			bool Valid;
+		};
+
+		static JoinContainer Container;
+	};
+}
diff --git a/iw4/Components/QuickPatch.cpp b/iw4/Components/QuickPatch.cpp
index e269a21a..5f6d0d33 100644
--- a/iw4/Components/QuickPatch.cpp
+++ b/iw4/Components/QuickPatch.cpp
@@ -2,6 +2,12 @@
 
 namespace Components
 {
+	__int64* QuickPatch::GetStatsID()
+	{
+		static __int64 id = 0x110000100001337;
+		return &id;
+	}
+
 	QuickPatch::QuickPatch()
 	{
 		// remove system pre-init stuff (improper quit, disk full)
@@ -54,6 +60,16 @@ namespace Components
 		// default sv_pure to 0
 		Utils::Hook::Set<BYTE>(0x4D3A74, 0);
 
+		// Force debug logging
+		Utils::Hook::Nop(0x4AA89F, 2);
+		Utils::Hook::Nop(0x4AA8A1, 6);
+
+		// Patch stats steamid
+		Utils::Hook::Nop(0x682EBF, 20);
+		Utils::Hook::Nop(0x6830B1, 20);
+		Utils::Hook(0x682EBF, QuickPatch::GetStatsID, HOOK_CALL).Install()->Quick();
+		Utils::Hook(0x6830B1, QuickPatch::GetStatsID, HOOK_CALL).Install()->Quick();
+
 		// Why?
 		Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_WEAPON, 2400);
 	}
diff --git a/iw4/Components/QuickPatch.hpp b/iw4/Components/QuickPatch.hpp
index 11984c3f..7054ce0b 100644
--- a/iw4/Components/QuickPatch.hpp
+++ b/iw4/Components/QuickPatch.hpp
@@ -5,5 +5,8 @@ namespace Components
 	public:
 		QuickPatch();
 		const char* GetName() { return "QuickPatch"; };
+
+	private:
+		static _int64* GetStatsID();
 	};
 }
diff --git a/iw4/Game/Functions.cpp b/iw4/Game/Functions.cpp
index bc7fde52..654b8d6c 100644
--- a/iw4/Game/Functions.cpp
+++ b/iw4/Game/Functions.cpp
@@ -5,6 +5,7 @@ namespace Game
 	Cbuf_AddText_t Cbuf_AddText = (Cbuf_AddText_t)0x404B20;
 
 	CL_IsCgameInitialized_t CL_IsCgameInitialized = (CL_IsCgameInitialized_t)0x43EB20;
+	CL_ConnectFromParty_t CL_ConnectFromParty = (CL_ConnectFromParty_t)0x433D30;
 
 	Cmd_AddCommand_t Cmd_AddCommand = (Cmd_AddCommand_t)0x470090;
 	Cmd_ExecuteSingleCommand_t Cmd_ExecuteSingleCommand = (Cmd_ExecuteSingleCommand_t)0x609540;
@@ -34,8 +35,23 @@ namespace Game
 
 	FreeMemory_t FreeMemory = (FreeMemory_t)0x4D6640;
 
+	FS_FileExists_t FS_FileExists = (FS_FileExists_t)0x4DEFA0;
 	FS_FreeFile_t FS_FreeFile = (FS_FreeFile_t)0x4416B0;
 	FS_ReadFile_t FS_ReadFile = (FS_ReadFile_t)0x4F4B90;
+	FS_ListFiles_t FS_ListFiles = (FS_ListFiles_t)0x441BB0;
+	FS_FreeFileList_t FS_FreeFileList = (FS_FreeFileList_t)0x4A5DE0;
+	FS_FOpenFileAppend_t FS_FOpenFileAppend = (FS_FOpenFileAppend_t)0x410BB0;
+	FS_FOpenFileAppend_t FS_FOpenFileWrite = (FS_FOpenFileAppend_t)0x4BA530;
+	FS_FOpenFileRead_t FS_FOpenFileRead = (FS_FOpenFileRead_t)0x46CBF0;
+	FS_FCloseFile_t FS_FCloseFile = (FS_FCloseFile_t)0x462000;
+	FS_WriteFile_t FS_WriteFile = (FS_WriteFile_t)0x426450;
+	FS_Write_t FS_Write = (FS_Write_t)0x4C06E0;
+	FS_Read_t FS_Read = (FS_Read_t)0x4A04C0;
+	FS_Seek_t FS_Seek = (FS_Seek_t)0x4A63D0;
+	FS_FTell_t FS_FTell = (FS_FTell_t)0x4E6760;
+	FS_Remove_t FS_Remove = (FS_Remove_t)0x4660F0;
+	FS_Restart_t FS_Restart = (FS_Restart_t)0x461A50;
+	FS_BuildPathToFile_t FS_BuildPathToFile = (FS_BuildPathToFile_t)0x4702C0;
 
 	Menus_CloseAll_t Menus_CloseAll = (Menus_CloseAll_t)0x4BA5B0;
 	Menus_OpenByName_t Menus_OpenByName = (Menus_OpenByName_t)0x4CCE60;
@@ -49,12 +65,17 @@ namespace Game
 	sendOOB_t OOBPrint = (sendOOB_t)0x4AEF00;
 
 	PC_ReadToken_t PC_ReadToken = (PC_ReadToken_t)0x4ACCD0;
+	PC_ReadTokenHandle_t PC_ReadTokenHandle = (PC_ReadTokenHandle_t)0x4D2060;
 	PC_SourceError_t PC_SourceError = (PC_SourceError_t)0x467A00;
 
 	Script_Alloc_t Script_Alloc = (Script_Alloc_t)0x422E70;
 	Script_SetupTokens_t Script_SetupTokens = (Script_SetupTokens_t)0x4E6950;
 	Script_CleanString_t Script_CleanString = (Script_CleanString_t)0x498220;
 
+	SetConsole_t SetConsole = (SetConsole_t)0x44F060;
+
+	UI_AddMenuList_t UI_AddMenuList = (UI_AddMenuList_t)0x4533C0;
+
 	Win_GetLanguage_t Win_GetLanguage = (Win_GetLanguage_t)0x45CBA0;
 
 	void** DB_XAssetPool = (void**)0x7998A8;
diff --git a/iw4/Game/Functions.hpp b/iw4/Game/Functions.hpp
index 70717d5e..b97116bc 100644
--- a/iw4/Game/Functions.hpp
+++ b/iw4/Game/Functions.hpp
@@ -6,6 +6,9 @@ namespace Game
 	typedef int(__cdecl * CL_IsCgameInitialized_t)();
 	extern CL_IsCgameInitialized_t CL_IsCgameInitialized;
 
+	typedef void(__cdecl * CL_ConnectFromParty_t)(int controller, void*, netadr_t adr, int, int, const char*, const char*);
+	extern CL_ConnectFromParty_t CL_ConnectFromParty;
+
 	typedef void(__cdecl * Cmd_AddCommand_t)(const char* name, void(*callback), cmd_function_t* data, char);
 	extern Cmd_AddCommand_t Cmd_AddCommand;
 
@@ -75,6 +78,49 @@ namespace Game
 	typedef int(__cdecl * FS_ReadFile_t)(const char* path, char** buffer);
 	extern FS_ReadFile_t FS_ReadFile;
 
+	typedef char** (__cdecl * FS_ListFiles_t)(char* path, char* extension, int noclue, int* amount);
+	extern FS_ListFiles_t FS_ListFiles;
+
+	typedef void(__cdecl * FS_FreeFileList_t)(char** list);
+	extern FS_FreeFileList_t FS_FreeFileList;
+
+	typedef int(__cdecl * FS_FOpenFileAppend_t)(char* file);
+	extern FS_FOpenFileAppend_t FS_FOpenFileAppend;
+	extern FS_FOpenFileAppend_t FS_FOpenFileWrite;
+
+	typedef int(__cdecl * FS_FOpenFileRead_t)(const char* file, int* fh, int uniqueFile);
+	extern FS_FOpenFileRead_t FS_FOpenFileRead;
+
+	typedef int(__cdecl * FS_FCloseFile_t)(int fh);
+	extern FS_FCloseFile_t FS_FCloseFile;
+
+	typedef bool(__cdecl * FS_FileExists_t)(const char* file);
+	extern FS_FileExists_t FS_FileExists;
+
+	typedef bool(__cdecl * FS_WriteFile_t)(char* filename, char* folder, void* buffer, int size);
+	extern FS_WriteFile_t FS_WriteFile;
+
+	typedef int(__cdecl * FS_Write_t)(void* buffer, size_t size, int file);
+	extern FS_Write_t FS_Write;
+
+	typedef int(__cdecl * FS_Read_t)(void* buffer, size_t size, int file);
+	extern FS_Read_t FS_Read;
+
+	typedef int(__cdecl * FS_Seek_t)(int fileHandle, int seekPosition, int seekOrigin);
+	extern FS_Seek_t FS_Seek;
+
+	typedef int(__cdecl * FS_FTell_t)(int fileHandle);
+	extern FS_FTell_t FS_FTell;
+
+	typedef int(__cdecl * FS_Remove_t)(char *);
+	extern FS_Remove_t FS_Remove;
+
+	typedef int(__cdecl * FS_Restart_t)(int a1, int a2);
+	extern FS_Restart_t FS_Restart;
+
+	typedef int(__cdecl * FS_BuildPathToFile_t)(const char*, const char*, const char*, char**);
+	extern FS_BuildPathToFile_t FS_BuildPathToFile;
+
 	typedef void(__cdecl * Menus_CloseAll_t)(/*UiContext **/int dc);
 	extern Menus_CloseAll_t Menus_CloseAll;
 
@@ -99,6 +145,9 @@ namespace Game
 	typedef int(__cdecl * PC_ReadToken_t)(source_t*, token_t*);
 	extern PC_ReadToken_t PC_ReadToken;
 
+	typedef int(__cdecl * PC_ReadTokenHandle_t)(int handle, pc_token_s *pc_token);
+	extern PC_ReadTokenHandle_t PC_ReadTokenHandle;
+
 	typedef void(__cdecl * PC_SourceError_t)(int, const char*, ...);
 	extern PC_SourceError_t PC_SourceError;
 
@@ -111,6 +160,12 @@ namespace Game
 	typedef int(__cdecl * Script_CleanString_t)(char* buffer);
 	extern Script_CleanString_t Script_CleanString;
 
+	typedef void(__cdecl * SetConsole_t)(const char* cvar, const char* value);
+	extern SetConsole_t SetConsole;
+
+	typedef void(__cdecl * UI_AddMenuList_t)(/*UiContext **/int dc, MenuList *menuList, int close);
+	extern UI_AddMenuList_t UI_AddMenuList;
+
 	typedef const char * (__cdecl * Win_GetLanguage_t)();
 	extern Win_GetLanguage_t Win_GetLanguage;
 
diff --git a/iw4/STDInclude.hpp b/iw4/STDInclude.hpp
index eba6c50f..ae63d275 100644
--- a/iw4/STDInclude.hpp
+++ b/iw4/STDInclude.hpp
@@ -6,7 +6,10 @@
 #include <stdio.h>
 #include <string.h>
 #include <wincrypt.h>
+#include <time.h>
+#include <timeapi.h>
 
+#pragma comment(lib, "Winmm.lib")
 #pragma comment(lib, "Crypt32.lib")
 
 #include <map>
@@ -14,6 +17,8 @@
 #include <string>
 #include <vector>
 #include <algorithm>
+#include <sstream>
+#include <utility>
 
 #include "Game\Structs.hpp"
 #include "Game\Functions.hpp"
diff --git a/iw4/Steam/Interfaces/SteamUser.cpp b/iw4/Steam/Interfaces/SteamUser.cpp
index 71330562..e15d00a6 100644
--- a/iw4/Steam/Interfaces/SteamUser.cpp
+++ b/iw4/Steam/Interfaces/SteamUser.cpp
@@ -14,17 +14,16 @@ namespace Steam
 
 	SteamID User::GetSteamID()
 	{
+		static int subId = 0;
+
 		SteamID id;
 
-		DATA_BLOB DataIn;
-		DATA_BLOB DataOut;
+		if (!subId)
+		{
+			subId = (Game::Com_Milliseconds() + timeGetTime());
+		}
 
-		DataIn.pbData = (BYTE *)"AAAAAAAAAA";
-		DataIn.cbData = 10;
-
-		CryptProtectData(&DataIn, NULL, NULL, NULL, NULL, CRYPTPROTECT_LOCAL_MACHINE, &DataOut);
-
-		id.m_Bits = 0x110000100000000 | ::Utils::OneAtATime((char*)DataOut.pbData, 52);
+		id.m_Bits = 0x110000100000000 | subId;
 		return id;
 	}
 
diff --git a/iw4/Utils/Utils.cpp b/iw4/Utils/Utils.cpp
index a924646a..7178ff7f 100644
--- a/iw4/Utils/Utils.cpp
+++ b/iw4/Utils/Utils.cpp
@@ -30,22 +30,17 @@ namespace Utils
 		return (strstr(heystack, needle) == (heystack + strlen(heystack) - strlen(needle)));
 	}
 
-	std::vector<std::string> Explode(const std::string& str, const std::string& delimiters)
+	std::vector<std::string> Explode(const std::string& str, char delim)
 	{
-		std::vector<std::string> tokens;
+		std::vector<std::string> result;
+		std::istringstream iss(str);
 
-		auto subStrBeginPos = str.find_first_not_of(delimiters, 0);
-		auto subStrEndPos = str.find_first_of(delimiters, subStrBeginPos);
-
-		while (std::string::npos != subStrBeginPos || std::string::npos != subStrEndPos)
+		for (std::string token; std::getline(iss, token, delim);)
 		{
-			tokens.push_back(str.substr(subStrBeginPos, subStrEndPos - subStrBeginPos));
-
-			subStrBeginPos = str.find_first_not_of(delimiters, subStrEndPos);
-			subStrEndPos = str.find_first_of(delimiters, subStrBeginPos);
+			result.push_back(std::move(token));
 		}
 
-		return tokens;
+		return result;
 	}
 
 	void Replace(std::string &string, std::string find, std::string replace)
@@ -109,9 +104,17 @@ namespace Utils
 		return infoString;
 	}
 
+	void InfoString::Dump()
+	{
+		for (auto i = this->KeyValuePairs.begin(); i != this->KeyValuePairs.end(); i++)
+		{
+			OutputDebugStringA(Utils::VA("%s: %s", i->first.data(), i->second.data()));
+		}
+	}
+
 	void InfoString::Parse(std::string buffer)
 	{
-		std::vector<std::string> KeyValues = Utils::Explode(buffer, "\\");
+		std::vector<std::string> KeyValues = Utils::Explode(buffer, '\\');
 
 		for (unsigned int i = 0; i < (KeyValues.size() - 1); i+=2)
 		{
diff --git a/iw4/Utils/Utils.hpp b/iw4/Utils/Utils.hpp
index 8d61dad7..4f13f7e0 100644
--- a/iw4/Utils/Utils.hpp
+++ b/iw4/Utils/Utils.hpp
@@ -3,7 +3,7 @@ namespace Utils
 	const char *VA(const char *fmt, ...);
 	std::string StrToLower(std::string input);
 	bool EndsWith(const char* heystack, const char* needle);
-	std::vector<std::string> Explode(const std::string& str, const std::string& delimiters);
+	std::vector<std::string> Explode(const std::string& str, char delim);
 	void Replace(std::string &string, std::string find, std::string replace);
 	unsigned int OneAtATime(char *key, size_t len);
 
@@ -18,6 +18,8 @@ namespace Utils
 
 		std::string Build();
 
+		void Dump();
+
 	private:
 		std::map<std::string, std::string> KeyValuePairs;
 		void Parse(std::string buffer);
diff --git a/iw4/iw4.vcxproj b/iw4/iw4.vcxproj
index aa2eb0f6..ef23a1de 100644
--- a/iw4/iw4.vcxproj
+++ b/iw4/iw4.vcxproj
@@ -64,6 +64,7 @@
     <ClInclude Include="Components\Menus.hpp" />
     <ClInclude Include="Components\MusicalTalent.hpp" />
     <ClInclude Include="Components\Network.hpp" />
+    <ClInclude Include="Components\Party.hpp" />
     <ClInclude Include="Components\QuickPatch.hpp" />
     <ClInclude Include="Components\RawFiles.hpp" />
     <ClInclude Include="Components\Renderer.hpp" />
@@ -97,6 +98,7 @@
     <ClCompile Include="Components\Menus.cpp" />
     <ClCompile Include="Components\MusicalTalent.cpp" />
     <ClCompile Include="Components\Network.cpp" />
+    <ClCompile Include="Components\Party.cpp" />
     <ClCompile Include="Components\QuickPatch.cpp" />
     <ClCompile Include="Components\RawFiles.cpp" />
     <ClCompile Include="Components\Renderer.cpp" />
diff --git a/iw4/iw4.vcxproj.filters b/iw4/iw4.vcxproj.filters
index 6b74f005..a48432ab 100644
--- a/iw4/iw4.vcxproj.filters
+++ b/iw4/iw4.vcxproj.filters
@@ -119,6 +119,9 @@
     <ClCompile Include="Components\Network.cpp">
       <Filter>Source\Components\Modules</Filter>
     </ClCompile>
+    <ClCompile Include="Components\Party.cpp">
+      <Filter>Source\Components\Modules</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Steam\Interfaces\SteamUser.hpp">
@@ -214,5 +217,8 @@
     <ClInclude Include="Components\Network.hpp">
       <Filter>Source\Components\Modules</Filter>
     </ClInclude>
+    <ClInclude Include="Components\Party.hpp">
+      <Filter>Source\Components\Modules</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>
\ No newline at end of file