diff --git a/update_tool/api_link.asm b/update_tool/api_link.asm
new file mode 100644
index 0000000..f57c629
--- /dev/null
+++ b/update_tool/api_link.asm
@@ -0,0 +1,92 @@
+;;;;
+;; (C)2005-2007 AlliedModders LLC
+;; By the Metamod:Source Development Team
+;; This software is licensed under the zlib/libpng free software license.
+;;
+;; This assembly file is a short thunk wrapper to let us load as a VSP and exit quietly,
+;; without any overhead of the rest of the interface, and also to prevent linking against
+;; tierX or vstdlib
+;;;;
+
+;;Exports:
+;; void GetBaseDir(char *buffer, maxlength);
+;; void *GetThisPointer();
+;;Imports:
+;; void LoadFunction();
+
+section .text
+
+global GetThisPointer, GetGameDir
+global _GetThisPointer, _GetGameDir
+extern _LoadFunction
+
+GetThisPointer:
+_GetThisPointer:
+ mov eax, GLOBAL_POINTER
+ ret
+
+GetGameDir:
+_GetGameDir:
+ push ebp
+ mov ebp, esp
+
+ mov ecx, [engine] ;get this pointer
+ mov edx, [ecx] ;get the vtable
+ %ifdef LINUX
+ push ecx ;push this pointer
+ %endif
+ push dword [ebp+12] ;push maxlenth
+ push dword [ebp+8] ;push buffer
+ call dword [edx+216] ;call IVEngineServer::GetGameDir
+ %ifdef LINUX
+ add esp, 8 ;correct stack
+ %endif
+
+ pop ebp
+ ret
+
+thisLoadFunction:
+ push ebp
+ mov ebp, esp
+
+ ;get factory
+ mov eax, [ebp+8]
+
+ push dword 0 ;NULL
+ push dword VENGINESERVER ;iface name
+ call eax ;call factory
+ add esp, 8 ;correct stack
+
+ test eax, eax ;do we have a valid pointer?
+ jz .end ;no, bail out
+
+ mov [engine], eax ;store the engine pointer
+
+ call _LoadFunction
+
+.end:
+ ;We never load, never ever ever!
+ xor eax, eax
+ pop ebp
+ %ifdef LINUX
+ ret
+ %else
+ retn 8
+ %endif
+
+thisUnloadFunction:
+ ret
+
+section .data
+ INTERFACE_NAME DB "ISERVERPLUGINCALLBACKS001", 0
+ VENGINESERVER DB "VEngineServer021", 0
+
+ VIRTUAL_TABLE DD thisLoadFunction
+ DD thisUnloadFunction
+ ;We don't need any more of the vtable here
+
+ GLOBAL_POINTER DD VIRTUAL_TABLE
+
+ temp_ret DD 0
+ temp_ptr DD temp_ret
+ engine DD 0
diff --git a/update_tool/msvc8/update_tool.sln b/update_tool/msvc8/update_tool.sln
new file mode 100644
index 0000000..5250f8a
--- /dev/null
+++ b/update_tool/msvc8/update_tool.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "update_tool", "update_tool.vcproj", "{DDD1563F-7EE2-4E76-BE57-ED84A2664A51}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {DDD1563F-7EE2-4E76-BE57-ED84A2664A51}.Debug|Win32.ActiveCfg = Debug|Win32
+ {DDD1563F-7EE2-4E76-BE57-ED84A2664A51}.Debug|Win32.Build.0 = Debug|Win32
+ {DDD1563F-7EE2-4E76-BE57-ED84A2664A51}.Release|Win32.ActiveCfg = Release|Win32
+ {DDD1563F-7EE2-4E76-BE57-ED84A2664A51}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/update_tool/msvc8/update_tool.vcproj b/update_tool/msvc8/update_tool.vcproj
new file mode 100644
index 0000000..690bac3
--- /dev/null
+++ b/update_tool/msvc8/update_tool.vcproj
@@ -0,0 +1,203 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/update_tool/update_tool.cpp b/update_tool/update_tool.cpp
new file mode 100644
index 0000000..799fde3
--- /dev/null
+++ b/update_tool/update_tool.cpp
@@ -0,0 +1,230 @@
+#include
+#include
+#include
+#include
+
+extern "C" void GetGameDir(char *buffer, int maxlength);
+extern "C" void *GetThisPointer();
+
+#if defined _MSC_VER
+#define SEPCHAR "\\"
+#define MMPATH "|gameinfo_path|addons\\metamod\\bin"
+#define WINDOWS_LEAN_AND_MEAN
+#include
+#elif defined __linux__
+#define SEPCHAR "/"
+#define MMPATH "|gameinfo_path|addons/metamod/bin"
+#include
+#endif
+
+size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...);
+bool s_isspace(char c);
+bool RenameFile(const char *old, const char *newf);
+
+/* This will be called by the thunk */
+#if defined _MSC_VER
+extern "C" void LoadFunction()
+#elif defined __linux__
+extern "C" void _LoadFunction()
+#endif
+{
+ char gamedir[260];
+
+ GetGameDir(gamedir, sizeof(gamedir));
+
+ char old_path[260];
+ char new_path[260];
+
+ UTIL_Format(old_path, sizeof(old_path), "%s" SEPCHAR "gameinfo.txt", gamedir);
+ UTIL_Format(new_path, sizeof(new_path), "%s" SEPCHAR "gameinfo.new.txt", gamedir);
+
+ FILE *fp = fopen(old_path, "rt");
+
+ if (!fp)
+ {
+ return;
+ }
+
+ FILE *op = fopen(new_path, "wt");
+ if (!op)
+ {
+ fclose(fp);
+ return;
+ }
+
+ enum ParseState
+ {
+ Parse_None,
+ Parse_Root,
+ Parse_GameInfo,
+ Parse_FileSystem,
+ Parse_SearchPaths,
+ };
+
+ ParseState ps = Parse_Root;
+
+ char input[1024];
+ char backup[1024];
+
+ while (!feof(fp) && fgets(input, sizeof(input), fp) != NULL)
+ {
+ UTIL_Format(backup, sizeof(backup), "%s", input);
+
+ if (ps != Parse_None)
+ {
+ char *inbuf = input;
+
+ /* Strip beginning whitespace */
+ while (*inbuf != '\0' && s_isspace(*inbuf))
+ {
+ inbuf++;
+ }
+
+ /* Strip ending whitespace */
+ size_t len = strlen(inbuf);
+ for (size_t i = len - 1;
+ i >= 0 && i < len;
+ i--)
+ {
+ if (s_isspace(inbuf[i]))
+ {
+ inbuf[i] = '\0';
+ len--;
+ } else {
+ break;
+ }
+ }
+
+ /* Strip quotation marks */
+ if (inbuf[0] == '"'
+ && inbuf[len-1] == '"')
+ {
+ inbuf[len - 1] = '\0';
+ inbuf = &inbuf[1];
+ len -= 2;
+ }
+
+ /* Do tests */
+ if (ps == Parse_Root && strcmp(inbuf, "GameInfo") == 0)
+ {
+ ps = Parse_GameInfo;
+ } else if (ps == Parse_GameInfo && strcmp(inbuf, "FileSystem") == 0) {
+ ps = Parse_FileSystem;
+ } else if (ps == Parse_FileSystem && strcmp(inbuf, "SearchPaths") == 0) {
+ ps = Parse_SearchPaths;
+ } else if (ps == Parse_SearchPaths) {
+ const char *game = strstr(inbuf, "Game");
+ if (game)
+ {
+ if (strstr(game, "GameBin") != NULL
+ && strstr(game, MMPATH) != NULL)
+ {
+ fclose(op);
+ op = NULL;
+ break; /* Nothing more to do! */
+ } else {
+ fputs("\t\t\t\tGameBin\t\t" MMPATH "\n", op);
+ ps = Parse_None;
+ }
+ }
+ }
+ }
+
+ fputs(backup, op);
+ }
+
+ if (!op)
+ {
+ /* Well, we can't really do anything else. Give up. */
+ fclose(fp);
+ return;
+ }
+
+ /* Close all streams */
+ fclose(op);
+ fclose(fp);
+
+ /* Move the old file to a backup name */
+ char backup_name[260];
+ UTIL_Format(backup_name, sizeof(backup_name), "%s" SEPCHAR "gameinfo.backup.txt", gamedir);
+
+ if (!RenameFile(old_path, backup_name))
+ {
+ /* If we can't rename, just bail out.
+ * We don't want to overwrite the client's default
+ * without backing it up first!
+ */
+ return;
+ }
+ if (!RenameFile(new_path, old_path))
+ {
+ /* Since this failed, we really have no choice.
+ * Try and rename the old back.
+ */
+ RenameFile(backup_name, old_path);
+ return;
+ }
+#if defined _MSC_VER
+ _unlink(new_path);
+#else
+ unlink(new_path);
+#endif
+}
+
+bool s_isspace(char c)
+{
+ if ((unsigned)c & 0x80)
+ {
+ return false;
+ } else {
+ return isspace(c) ? true : false;
+ }
+}
+
+size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ size_t len = vsnprintf(buffer, maxlength, fmt, ap);
+ va_end(ap);
+
+ if (len >= maxlength)
+ {
+ len = maxlength - 1;
+ buffer[len] = '\0';
+ }
+
+ return len;
+}
+
+bool RenameFile(const char *old, const char *newf)
+{
+#if defined __linux__
+ return (rename(old, newf) == 0);
+#elif defined WIN32
+ return (MoveFileA(old, newf) != 0);
+#endif
+}
+
+#if defined _MSC_VER
+extern "C" __declspec(dllexport) void *CreateInterface(const char *iface, int *ret)
+#elif defined __linux__
+extern "C" __attribute__((visibility("default"))) void *CreateInterface(const char *iface, int *ret)
+#endif
+{
+ if (strcmp(iface, "ISERVERPLUGINCALLBACKS001") == 0)
+ {
+ if (ret)
+ {
+ *ret = 0;
+ }
+ return GetThisPointer();
+ }
+
+ if (ret)
+ {
+ *ret = 1;
+ }
+ return NULL;
+}