diff --git a/.gitignore b/.gitignore
index 4b02965..0860212 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
-/build
-/extension/version_auto.h
-/breakpad
+/build
+/extension/version_auto.h
+/breakpad
diff --git a/buildbot/BreakpadSymbols b/buildbot/BreakpadSymbols
index c83ad22..6e1e165 100644
--- a/buildbot/BreakpadSymbols
+++ b/buildbot/BreakpadSymbols
@@ -1,45 +1,45 @@
-# vim: set ts=2 sw=2 tw=99 noet ft=python:
-import os
-from ambuild.command import Command
-from ambuild.command import ShellCommand
-
-try:
- import urllib.request as urllib
-except ImportError:
- import urllib2 as urllib
-
-class IterateDebugInfoCommand(Command):
- def run(self, master, job):
- pdblog = open(os.path.join(AMBuild.outputFolder, 'pdblog.txt'), 'rt')
- for debug_info in pdblog:
- debug_info = os.path.join(AMBuild.outputFolder, debug_info.strip())
- job.AddCommand(SymbolCommand(debug_info, symbolServer))
- pdblog.close()
-
-class SymbolCommand(ShellCommand):
- def __init__(self, debugFile, symbolServer):
- self.serverResponse = None
- self.symbolServer = symbolServer
- if AMBuild.target['platform'] == 'linux':
- cmdstring = "dump_syms {0} {1}".format(debugFile, os.path.dirname(debugFile))
- elif AMBuild.target['platform'] == 'darwin':
- cmdstring = "dump_syms {0}".format(debugFile)
- elif AMBuild.target['platform'] == 'windows':
- cmdstring = "dump_syms.exe {0}".format(debugFile)
- ShellCommand.__init__(self, cmdstring)
- def run(self, master, job):
- ShellCommand.run(self, master, job)
- if self.stdout != None and len(self.stdout) > 0:
- request = urllib.Request(symbolServer, self.stdout.encode('utf-8'))
- request.add_header("Content-Type", "text/plain")
- self.serverResponse = urllib.urlopen(request).read().decode('utf-8')
- def spew(self, runner):
- if self.stderr != None and len(self.stderr) > 0:
- runner.PrintOut(self.stderr)
- if self.serverResponse != None and len(self.serverResponse) > 0:
- runner.PrintOut(self.serverResponse)
-
-if 'BREAKPAD_SYMBOL_SERVER' in os.environ:
- symbolServer = os.environ['BREAKPAD_SYMBOL_SERVER']
- job = AMBuild.AddJob('breakpad-symbols')
- job.AddCommand(IterateDebugInfoCommand())
+# vim: set ts=2 sw=2 tw=99 noet ft=python:
+import os
+from ambuild.command import Command
+from ambuild.command import ShellCommand
+
+try:
+ import urllib.request as urllib
+except ImportError:
+ import urllib2 as urllib
+
+class IterateDebugInfoCommand(Command):
+ def run(self, master, job):
+ pdblog = open(os.path.join(AMBuild.outputFolder, 'pdblog.txt'), 'rt')
+ for debug_info in pdblog:
+ debug_info = os.path.join(AMBuild.outputFolder, debug_info.strip())
+ job.AddCommand(SymbolCommand(debug_info, symbolServer))
+ pdblog.close()
+
+class SymbolCommand(ShellCommand):
+ def __init__(self, debugFile, symbolServer):
+ self.serverResponse = None
+ self.symbolServer = symbolServer
+ if AMBuild.target['platform'] == 'linux':
+ cmdstring = "dump_syms {0} {1}".format(debugFile, os.path.dirname(debugFile))
+ elif AMBuild.target['platform'] == 'darwin':
+ cmdstring = "dump_syms {0}".format(debugFile)
+ elif AMBuild.target['platform'] == 'windows':
+ cmdstring = "dump_syms.exe {0}".format(debugFile)
+ ShellCommand.__init__(self, cmdstring)
+ def run(self, master, job):
+ ShellCommand.run(self, master, job)
+ if self.stdout != None and len(self.stdout) > 0:
+ request = urllib.Request(symbolServer, self.stdout.encode('utf-8'))
+ request.add_header("Content-Type", "text/plain")
+ self.serverResponse = urllib.urlopen(request).read().decode('utf-8')
+ def spew(self, runner):
+ if self.stderr != None and len(self.stderr) > 0:
+ runner.PrintOut(self.stderr)
+ if self.serverResponse != None and len(self.serverResponse) > 0:
+ runner.PrintOut(self.serverResponse)
+
+if 'BREAKPAD_SYMBOL_SERVER' in os.environ:
+ symbolServer = os.environ['BREAKPAD_SYMBOL_SERVER']
+ job = AMBuild.AddJob('breakpad-symbols')
+ job.AddCommand(IterateDebugInfoCommand())
diff --git a/buildbot/PackageScript b/buildbot/PackageScript
index b9257ec..ab241b3 100644
--- a/buildbot/PackageScript
+++ b/buildbot/PackageScript
@@ -1,127 +1,127 @@
-# vim: set ts=2 sw=2 tw=99 noet ft=python:
-import os
-import shutil
-import ambuild.osutil as osutil
-from ambuild.command import Command
-
-job = AMBuild.AddJob('package')
-
-class DestroyPath(Command):
- def __init__(self, folder):
- Command.__init__(self)
- self.folder = folder
-
- def destroy(self, path):
- entries = os.listdir(path)
- for entry in entries:
- newpath = os.path.join(path, entry)
- if os.path.isdir(newpath):
- self.destroy(newpath)
- os.rmdir(newpath)
- elif os.path.isfile(newpath):
- os.remove(newpath)
-
- def run(self, runner, job):
- runner.PrintOut('rm -rf {0}/*'.format(self.folder))
- self.destroy(self.folder)
-
-class CreateFolders(Command):
- def __init__(self, folders):
- Command.__init__(self)
- self.folders = folders
-
- def run(self, runner, job):
- for folder in self.folders:
- path = os.path.join(*folder)
- runner.PrintOut('mkdir {0}'.format(path))
- os.makedirs(path)
-
-#Shallow folder copy
-class CopyFolder(Command):
- def __init__(self, fromList, toList, excludes = []):
- Command.__init__(self)
- self.fromPath = os.path.join(AMBuild.sourceFolder, *fromList)
- self.toPath = os.path.join(*toList)
- self.excludes = excludes
-
- def run(self, runner, job):
- entries = os.listdir(self.fromPath)
- for entry in entries:
- if entry in self.excludes:
- continue
- path = os.path.join(self.fromPath, entry)
- if not os.path.isfile(path):
- continue
- runner.PrintOut('copy {0} to {1}'.format(path, self.toPath))
- shutil.copy(path, self.toPath)
-
-#Single file copy
-class CopyFile(Command):
- def __init__(self, fromFile, toPath):
- Command.__init__(self)
- self.fromFile = fromFile
- self.toPath = toPath
-
- def run(self, runner, job):
- runner.PrintOut('copy {0} to {1}'.format(self.fromFile, self.toPath))
- shutil.copy(self.fromFile, self.toPath)
-
-
-folders = [
- ['addons', 'sourcemod', 'configs'],
- ['addons', 'sourcemod', 'gamedata'],
- ['addons', 'sourcemod', 'extensions'],
-]
-
-#Setup
-job.AddCommand(DestroyPath(os.path.join(AMBuild.outputFolder, 'package')))
-job.AddCommand(CreateFolders(folders))
-
-job.AddCommand(CopyFile(os.path.join(AMBuild.sourceFolder, 'accelerator.games.txt'), os.path.join('addons', 'sourcemod', 'gamedata')))
-job.AddCommand(CopyFile(os.path.join(AMBuild.sourceFolder, 'accelerator.autoload'), os.path.join('addons', 'sourcemod', 'extensions')))
-
-bincopies = []
-
-def AddNormalLibrary(name, dest):
- dest = os.path.join('addons', 'sourcemod', dest)
- bincopies.append(CopyFile(os.path.join('..', name, name + osutil.SharedLibSuffix()), dest))
-
- # Each platform's version of dump_syms needs the path in a different format.
- if AMBuild.target['platform'] == 'linux':
- debug_info.append(name + '/' + name + '.so')
- elif AMBuild.target['platform'] == 'darwin':
- debug_info.append(name + '/' + name + '.dylib.dSYM')
- elif AMBuild.target['platform'] == 'windows':
- debug_info.append(name + '\\' + name + '.pdb')
-
-def AddExecutable(name, dest):
- dest = os.path.join('addons', 'sourcemod', dest)
- bincopies.append(CopyFile(os.path.join('..', name, name + osutil.ExecutableSuffix()), dest))
-
- # Each platform's version of dump_syms needs the path in a different format.
- if AMBuild.target['platform'] == 'linux':
- debug_info.append(name + '/' + name)
- elif AMBuild.target['platform'] == 'darwin':
- debug_info.append(name + '/' + name + '.dSYM')
- elif AMBuild.target['platform'] == 'windows':
- debug_info.append(name + '\\' + name + '.pdb')
-
-def AddHL2Library(name, dest):
- for i in SM.sdkInfo:
- sdk = SM.sdkInfo[i]
- if AMBuild.target['platform'] not in sdk['platform']:
- continue
- AddNormalLibrary(name + '.ext.' + sdk['ext'], dest)
-
-debug_info = []
-
-AddNormalLibrary('accelerator.ext', 'extensions')
-AddExecutable('test-crash-dump-generation', 'configs')
-
-job.AddCommandGroup(bincopies)
-
-pdblog = open(os.path.join(AMBuild.outputFolder, 'pdblog.txt'), 'wt')
-for pdb in debug_info:
- pdblog.write(pdb + '\n')
-pdblog.close()
-
+# vim: set ts=2 sw=2 tw=99 noet ft=python:
+import os
+import shutil
+import ambuild.osutil as osutil
+from ambuild.command import Command
+
+job = AMBuild.AddJob('package')
+
+class DestroyPath(Command):
+ def __init__(self, folder):
+ Command.__init__(self)
+ self.folder = folder
+
+ def destroy(self, path):
+ entries = os.listdir(path)
+ for entry in entries:
+ newpath = os.path.join(path, entry)
+ if os.path.isdir(newpath):
+ self.destroy(newpath)
+ os.rmdir(newpath)
+ elif os.path.isfile(newpath):
+ os.remove(newpath)
+
+ def run(self, runner, job):
+ runner.PrintOut('rm -rf {0}/*'.format(self.folder))
+ self.destroy(self.folder)
+
+class CreateFolders(Command):
+ def __init__(self, folders):
+ Command.__init__(self)
+ self.folders = folders
+
+ def run(self, runner, job):
+ for folder in self.folders:
+ path = os.path.join(*folder)
+ runner.PrintOut('mkdir {0}'.format(path))
+ os.makedirs(path)
+
+#Shallow folder copy
+class CopyFolder(Command):
+ def __init__(self, fromList, toList, excludes = []):
+ Command.__init__(self)
+ self.fromPath = os.path.join(AMBuild.sourceFolder, *fromList)
+ self.toPath = os.path.join(*toList)
+ self.excludes = excludes
+
+ def run(self, runner, job):
+ entries = os.listdir(self.fromPath)
+ for entry in entries:
+ if entry in self.excludes:
+ continue
+ path = os.path.join(self.fromPath, entry)
+ if not os.path.isfile(path):
+ continue
+ runner.PrintOut('copy {0} to {1}'.format(path, self.toPath))
+ shutil.copy(path, self.toPath)
+
+#Single file copy
+class CopyFile(Command):
+ def __init__(self, fromFile, toPath):
+ Command.__init__(self)
+ self.fromFile = fromFile
+ self.toPath = toPath
+
+ def run(self, runner, job):
+ runner.PrintOut('copy {0} to {1}'.format(self.fromFile, self.toPath))
+ shutil.copy(self.fromFile, self.toPath)
+
+
+folders = [
+ ['addons', 'sourcemod', 'configs'],
+ ['addons', 'sourcemod', 'gamedata'],
+ ['addons', 'sourcemod', 'extensions'],
+]
+
+#Setup
+job.AddCommand(DestroyPath(os.path.join(AMBuild.outputFolder, 'package')))
+job.AddCommand(CreateFolders(folders))
+
+job.AddCommand(CopyFile(os.path.join(AMBuild.sourceFolder, 'accelerator.games.txt'), os.path.join('addons', 'sourcemod', 'gamedata')))
+job.AddCommand(CopyFile(os.path.join(AMBuild.sourceFolder, 'accelerator.autoload'), os.path.join('addons', 'sourcemod', 'extensions')))
+
+bincopies = []
+
+def AddNormalLibrary(name, dest):
+ dest = os.path.join('addons', 'sourcemod', dest)
+ bincopies.append(CopyFile(os.path.join('..', name, name + osutil.SharedLibSuffix()), dest))
+
+ # Each platform's version of dump_syms needs the path in a different format.
+ if AMBuild.target['platform'] == 'linux':
+ debug_info.append(name + '/' + name + '.so')
+ elif AMBuild.target['platform'] == 'darwin':
+ debug_info.append(name + '/' + name + '.dylib.dSYM')
+ elif AMBuild.target['platform'] == 'windows':
+ debug_info.append(name + '\\' + name + '.pdb')
+
+def AddExecutable(name, dest):
+ dest = os.path.join('addons', 'sourcemod', dest)
+ bincopies.append(CopyFile(os.path.join('..', name, name + osutil.ExecutableSuffix()), dest))
+
+ # Each platform's version of dump_syms needs the path in a different format.
+ if AMBuild.target['platform'] == 'linux':
+ debug_info.append(name + '/' + name)
+ elif AMBuild.target['platform'] == 'darwin':
+ debug_info.append(name + '/' + name + '.dSYM')
+ elif AMBuild.target['platform'] == 'windows':
+ debug_info.append(name + '\\' + name + '.pdb')
+
+def AddHL2Library(name, dest):
+ for i in SM.sdkInfo:
+ sdk = SM.sdkInfo[i]
+ if AMBuild.target['platform'] not in sdk['platform']:
+ continue
+ AddNormalLibrary(name + '.ext.' + sdk['ext'], dest)
+
+debug_info = []
+
+AddNormalLibrary('accelerator.ext', 'extensions')
+AddExecutable('test-crash-dump-generation', 'configs')
+
+job.AddCommandGroup(bincopies)
+
+pdblog = open(os.path.join(AMBuild.outputFolder, 'pdblog.txt'), 'wt')
+for pdb in debug_info:
+ pdblog.write(pdb + '\n')
+pdblog.close()
+
diff --git a/buildbot/Versioning b/buildbot/Versioning
index 6b9766e..5240987 100644
--- a/buildbot/Versioning
+++ b/buildbot/Versioning
@@ -1,56 +1,56 @@
-# vim: set ts=2 sw=2 tw=99 noet ft=python:
-import os
-import re
-import subprocess
-from ambuild.cache import Cache
-import ambuild.command as command
-
-#Quickly try to ascertain the current repository revision
-def GetVersion():
- rev = command.RunDirectCommand(AMBuild, ['git', 'rev-list', '--count', 'HEAD']).stdoutText.strip()
- cset = command.RunDirectCommand(AMBuild, ['git', 'log', '--pretty=format:%h', '-n', '1']).stdoutText.strip()
-
- if not rev or not cset:
- raise Exception('Could not determine repository version')
-
- return (rev, cset)
-
-def PerformReversioning():
- rev, cset = GetVersion()
- cacheFile = os.path.join(AMBuild.outputFolder, '.ambuild', 'hgcache')
- cache = Cache(cacheFile)
- if os.path.isfile(cacheFile):
- cache.LoadCache()
- if cache.HasVariable('cset') and cache['cset'] == cset:
- return False
- cache.CacheVariable('cset', cset)
-
- productFile = open(os.path.join(AMBuild.sourceFolder, 'product.version'), 'r')
- productContents = productFile.read()
- productFile.close()
- m = re.match('(\d+)\.(\d+)\.(\d+)(.*)', productContents)
- if m == None:
- raise Exception('Could not detremine product version')
- major, minor, release, tag = m.groups()
-
- incFolder = os.path.join(AMBuild.sourceFolder, 'extension')
- incFile = open(os.path.join(incFolder, 'version_auto.h'), 'w')
- incFile.write("""
-#ifndef _AUTO_VERSION_INFORMATION_H_
-#define _AUTO_VERSION_INFORMATION_H_
-
-#define SM_BUILD_TAG \"{0}\"
-#define SM_BUILD_UNIQUEID \"{1}:{2}\" SM_BUILD_TAG
-#define SM_VERSION \"{3}.{4}.{5}\"
-#define SM_FULL_VERSION SM_VERSION SM_BUILD_TAG
-#define SM_FILE_VERSION {6},{7},{8},0
-
-#endif /* _AUTO_VERSION_INFORMATION_H_ */
-
-""".format(tag, rev, cset, major, minor, release, major, minor, release))
- incFile.close()
- cache.WriteCache()
-
-PerformReversioning()
-
-
+# vim: set ts=2 sw=2 tw=99 noet ft=python:
+import os
+import re
+import subprocess
+from ambuild.cache import Cache
+import ambuild.command as command
+
+#Quickly try to ascertain the current repository revision
+def GetVersion():
+ rev = command.RunDirectCommand(AMBuild, ['git', 'rev-list', '--count', 'HEAD']).stdoutText.strip()
+ cset = command.RunDirectCommand(AMBuild, ['git', 'log', '--pretty=format:%h', '-n', '1']).stdoutText.strip()
+
+ if not rev or not cset:
+ raise Exception('Could not determine repository version')
+
+ return (rev, cset)
+
+def PerformReversioning():
+ rev, cset = GetVersion()
+ cacheFile = os.path.join(AMBuild.outputFolder, '.ambuild', 'hgcache')
+ cache = Cache(cacheFile)
+ if os.path.isfile(cacheFile):
+ cache.LoadCache()
+ if cache.HasVariable('cset') and cache['cset'] == cset:
+ return False
+ cache.CacheVariable('cset', cset)
+
+ productFile = open(os.path.join(AMBuild.sourceFolder, 'product.version'), 'r')
+ productContents = productFile.read()
+ productFile.close()
+ m = re.match('(\d+)\.(\d+)\.(\d+)(.*)', productContents)
+ if m == None:
+ raise Exception('Could not detremine product version')
+ major, minor, release, tag = m.groups()
+
+ incFolder = os.path.join(AMBuild.sourceFolder, 'extension')
+ incFile = open(os.path.join(incFolder, 'version_auto.h'), 'w')
+ incFile.write("""
+#ifndef _AUTO_VERSION_INFORMATION_H_
+#define _AUTO_VERSION_INFORMATION_H_
+
+#define SM_BUILD_TAG \"{0}\"
+#define SM_BUILD_UNIQUEID \"{1}:{2}\" SM_BUILD_TAG
+#define SM_VERSION \"{3}.{4}.{5}\"
+#define SM_FULL_VERSION SM_VERSION SM_BUILD_TAG
+#define SM_FILE_VERSION {6},{7},{8},0
+
+#endif /* _AUTO_VERSION_INFORMATION_H_ */
+
+""".format(tag, rev, cset, major, minor, release, major, minor, release))
+ incFile.close()
+ cache.WriteCache()
+
+PerformReversioning()
+
+
diff --git a/extension/MemoryDownloader.cpp b/extension/MemoryDownloader.cpp
index af5b470..73ed3a8 100644
--- a/extension/MemoryDownloader.cpp
+++ b/extension/MemoryDownloader.cpp
@@ -1,85 +1,85 @@
-/**
- * vim: set ts=4 :
- * =============================================================================
- * SourceMod Updater Extension
- * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
- * =============================================================================
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, version 3.0, as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see .
- *
- * As a special exception, AlliedModders LLC gives you permission to link the
- * code of this program (as well as its derivative works) to "Half-Life 2," the
- * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
- * by the Valve Corporation. You must obey the GNU General Public License in
- * all respects for all other code used. Additionally, AlliedModders LLC grants
- * this exception to all derivative works. AlliedModders LLC defines further
- * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
- * or .
- *
- * Version: $Id$
- */
-
-#include
-#include
-#include
-#include "MemoryDownloader.h"
-
-using namespace SourceMod;
-
-MemoryDownloader::MemoryDownloader() : buffer(NULL), bufsize(0), bufpos(0)
-{
-}
-
-MemoryDownloader::~MemoryDownloader()
-{
- free(buffer);
-}
-
-DownloadWriteStatus MemoryDownloader::OnDownloadWrite(IWebTransfer *session,
- void *userdata,
- void *ptr,
- size_t size,
- size_t nmemb)
-{
- size_t total = size * nmemb;
-
- if (bufpos + total > bufsize)
- {
- size_t rem = (bufpos + total) - bufsize;
- bufsize += rem + (rem / 2);
- buffer = (char *)realloc(buffer, bufsize);
- }
-
- assert(bufpos + total <= bufsize);
-
- memcpy(&buffer[bufpos], ptr, total);
- bufpos += total;
-
- return DownloadWrite_Okay;
-}
-
-void MemoryDownloader::Reset()
-{
- bufpos = 0;
-}
-
-char *MemoryDownloader::GetBuffer()
-{
- return buffer;
-}
-
-size_t MemoryDownloader::GetSize()
-{
- return bufpos;
-}
-
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod Updater Extension
+ * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#include
+#include
+#include
+#include "MemoryDownloader.h"
+
+using namespace SourceMod;
+
+MemoryDownloader::MemoryDownloader() : buffer(NULL), bufsize(0), bufpos(0)
+{
+}
+
+MemoryDownloader::~MemoryDownloader()
+{
+ free(buffer);
+}
+
+DownloadWriteStatus MemoryDownloader::OnDownloadWrite(IWebTransfer *session,
+ void *userdata,
+ void *ptr,
+ size_t size,
+ size_t nmemb)
+{
+ size_t total = size * nmemb;
+
+ if (bufpos + total > bufsize)
+ {
+ size_t rem = (bufpos + total) - bufsize;
+ bufsize += rem + (rem / 2);
+ buffer = (char *)realloc(buffer, bufsize);
+ }
+
+ assert(bufpos + total <= bufsize);
+
+ memcpy(&buffer[bufpos], ptr, total);
+ bufpos += total;
+
+ return DownloadWrite_Okay;
+}
+
+void MemoryDownloader::Reset()
+{
+ bufpos = 0;
+}
+
+char *MemoryDownloader::GetBuffer()
+{
+ return buffer;
+}
+
+size_t MemoryDownloader::GetSize()
+{
+ return bufpos;
+}
+
diff --git a/extension/MemoryDownloader.h b/extension/MemoryDownloader.h
index 7ac64e0..8fad5bd 100644
--- a/extension/MemoryDownloader.h
+++ b/extension/MemoryDownloader.h
@@ -1,62 +1,62 @@
-/**
- * vim: set ts=4 :
- * =============================================================================
- * SourceMod Updater Extension
- * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
- * =============================================================================
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, version 3.0, as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see .
- *
- * As a special exception, AlliedModders LLC gives you permission to link the
- * code of this program (as well as its derivative works) to "Half-Life 2," the
- * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
- * by the Valve Corporation. You must obey the GNU General Public License in
- * all respects for all other code used. Additionally, AlliedModders LLC grants
- * this exception to all derivative works. AlliedModders LLC defines further
- * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
- * or .
- *
- * Version: $Id$
- */
-
-#ifndef _INCLUDE_SOURCEMOD_UPDATER_MEMORY_DOWNLOADER_H_
-#define _INCLUDE_SOURCEMOD_UPDATER_MEMORY_DOWNLOADER_H_
-
-#include
-
-namespace SourceMod
-{
- class MemoryDownloader : public ITransferHandler
- {
- public:
- MemoryDownloader();
- ~MemoryDownloader();
- public:
- DownloadWriteStatus OnDownloadWrite(IWebTransfer *session,
- void *userdata,
- void *ptr,
- size_t size,
- size_t nmemb);
- public:
- void Reset();
- char *GetBuffer();
- size_t GetSize();
- private:
- char *buffer;
- size_t bufsize;
- size_t bufpos;
- };
-}
-
-#endif /* _INCLUDE_SOURCEMOD_UPDATER_MEMORY_DOWNLOADER_H_ */
-
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod Updater Extension
+ * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#ifndef _INCLUDE_SOURCEMOD_UPDATER_MEMORY_DOWNLOADER_H_
+#define _INCLUDE_SOURCEMOD_UPDATER_MEMORY_DOWNLOADER_H_
+
+#include
+
+namespace SourceMod
+{
+ class MemoryDownloader : public ITransferHandler
+ {
+ public:
+ MemoryDownloader();
+ ~MemoryDownloader();
+ public:
+ DownloadWriteStatus OnDownloadWrite(IWebTransfer *session,
+ void *userdata,
+ void *ptr,
+ size_t size,
+ size_t nmemb);
+ public:
+ void Reset();
+ char *GetBuffer();
+ size_t GetSize();
+ private:
+ char *buffer;
+ size_t bufsize;
+ size_t bufpos;
+ };
+}
+
+#endif /* _INCLUDE_SOURCEMOD_UPDATER_MEMORY_DOWNLOADER_H_ */
+
diff --git a/extension/extension.cpp b/extension/extension.cpp
index b72cec2..f95235f 100644
--- a/extension/extension.cpp
+++ b/extension/extension.cpp
@@ -1,895 +1,895 @@
-/*
- * =============================================================================
- * Accelerator Extension
- * Copyright (C) 2011 Asher Baker (asherkin). All rights reserved.
- * =============================================================================
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, version 3.0, as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see .
- */
-
-#include "extension.h"
-
-#include
-#include "MemoryDownloader.h"
-
-#if defined _LINUX
-#include "client/linux/handler/exception_handler.h"
-#include "common/linux/linux_libc_support.h"
-#include "third_party/lss/linux_syscall_support.h"
-#include "common/linux/dump_symbols.h"
-#include "common/path_helper.h"
-
-#include
-#include
-#include
-#include
-
-class StderrInhibitor
-{
- FILE *saved_stderr = nullptr;
-
-public:
- StderrInhibitor() {
- saved_stderr = fdopen(dup(fileno(stderr)), "w");
- if (freopen(_PATH_DEVNULL, "w", stderr)) {
- // If it fails, not a lot we can (or should) do.
- // Add this brace section to silence gcc warnings.
- }
- }
-
- ~StderrInhibitor() {
- fflush(stderr);
- dup2(fileno(saved_stderr), fileno(stderr));
- fclose(saved_stderr);
- }
-};
-
-#elif defined _WINDOWS
-#define _STDINT // ~.~
-#include "client/windows/handler/exception_handler.h"
-
-#else
-#error Bad platform.
-#endif
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-Accelerator g_accelerator;
-SMEXT_LINK(&g_accelerator);
-
-IWebternet *webternet;
-IGameConfig *gameconfig;
-
-typedef void (*GetSpew_t)(char *buffer, unsigned int length);
-GetSpew_t GetSpew;
-#if defined _WINDOWS
-typedef void(__fastcall *GetSpewFastcall_t)(char *buffer, unsigned int length);
-GetSpewFastcall_t GetSpewFastcall;
-#endif
-
-char spewBuffer[65536]; // Hi.
-
-char crashMap[256];
-char crashGamePath[512];
-char crashCommandLine[1024];
-char crashSourceModPath[512];
-char crashGameDirectory[256];
-char steamInf[1024];
-
-char dumpStoragePath[512];
-char logPath[512];
-
-google_breakpad::ExceptionHandler *handler = NULL;
-
-# if 0
-struct PluginInfo {
- unsigned int serial;
- PluginStatus status;
- char filename[256];
- char name[256];
- char author[256];
- char description[256];
- char version[256];
- char url[256];
-};
-
-unsigned int plugin_count;
-PluginInfo plugins[256];
-#endif
-
-#if defined _LINUX
-void (*SignalHandler)(int, siginfo_t *, void *);
-
-const int kExceptionSignals[] = {
- SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS
-};
-
-const int kNumHandledSignals = sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]);
-
-static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded)
-{
- //printf("Wrote minidump to: %s\n", descriptor.path());
-
- if (succeeded) {
- sys_write(STDOUT_FILENO, "Wrote minidump to: ", 19);
- } else {
- sys_write(STDOUT_FILENO, "Failed to write minidump to: ", 29);
- }
-
- sys_write(STDOUT_FILENO, descriptor.path(), my_strlen(descriptor.path()));
- sys_write(STDOUT_FILENO, "\n", 1);
-
- if (!succeeded) {
- return succeeded;
- }
-
- my_strlcpy(dumpStoragePath, descriptor.path(), sizeof(dumpStoragePath));
- my_strlcat(dumpStoragePath, ".txt", sizeof(dumpStoragePath));
-
- int extra = sys_open(dumpStoragePath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
- if (extra == -1) {
- sys_write(STDOUT_FILENO, "Failed to open metadata file!\n", 30);
- return succeeded;
- }
-
- sys_write(extra, "-------- CONFIG BEGIN --------", 30);
- sys_write(extra, "\nMap=", 5);
- sys_write(extra, crashMap, my_strlen(crashMap));
- sys_write(extra, "\nGamePath=", 10);
- sys_write(extra, crashGamePath, my_strlen(crashGamePath));
- sys_write(extra, "\nCommandLine=", 13);
- sys_write(extra, crashCommandLine, my_strlen(crashCommandLine));
- sys_write(extra, "\nSourceModPath=", 15);
- sys_write(extra, crashSourceModPath, my_strlen(crashSourceModPath));
- sys_write(extra, "\nGameDirectory=", 15);
- sys_write(extra, crashGameDirectory, my_strlen(crashGameDirectory));
- sys_write(extra, "\nExtensionVersion=", 18);
- sys_write(extra, SM_VERSION, my_strlen(SM_VERSION));
- sys_write(extra, "\nExtensionBuild=", 16);
- sys_write(extra, SM_BUILD_UNIQUEID, my_strlen(SM_BUILD_UNIQUEID));
- sys_write(extra, steamInf, my_strlen(steamInf));
- sys_write(extra, "\n-------- CONFIG END --------\n", 30);
-
- if (GetSpew) {
- GetSpew(spewBuffer, sizeof(spewBuffer));
-
- if (my_strlen(spewBuffer) > 0) {
- sys_write(extra, "-------- CONSOLE HISTORY BEGIN --------\n", 40);
- sys_write(extra, spewBuffer, my_strlen(spewBuffer));
- sys_write(extra, "-------- CONSOLE HISTORY END --------\n", 38);
- }
- }
-
-#if 0
- char pis[64];
- char pds[32];
- for (unsigned i = 0; i < plugin_count; ++i) {
- PluginInfo *p = &plugins[i];
- if (p->serial == 0) continue;
- my_uitos(pds, i, my_uint_len(i));
- pds[my_uint_len(i)] = '\0';
- my_strlcpy(pis, "plugin[", sizeof(pis));
- my_strlcat(pis, pds, sizeof(pis));
- my_strlcat(pis, "].", sizeof(pis));
- sys_write(extra, pis, my_strlen(pis));
- sys_write(extra, "filename=", 9);
- sys_write(extra, p->filename, my_strlen(p->filename));
- sys_write(extra, "\n", 1);
- sys_write(extra, pis, my_strlen(pis));
- sys_write(extra, "name=", 5);
- sys_write(extra, p->name, my_strlen(p->name));
- sys_write(extra, "\n", 1);
- sys_write(extra, pis, my_strlen(pis));
- sys_write(extra, "author=", 7);
- sys_write(extra, p->author, my_strlen(p->author));
- sys_write(extra, "\n", 1);
- sys_write(extra, pis, my_strlen(pis));
- sys_write(extra, "description=", 12);
- sys_write(extra, p->description, my_strlen(p->description));
- sys_write(extra, "\n", 1);
- sys_write(extra, pis, my_strlen(pis));
- sys_write(extra, "version=", 8);
- sys_write(extra, p->version, my_strlen(p->version));
- sys_write(extra, "\n", 1);
- sys_write(extra, pis, my_strlen(pis));
- sys_write(extra, "url=", 4);
- sys_write(extra, p->url, my_strlen(p->url));
- sys_write(extra, "\n", 1);
- }
-#endif
-
- sys_close(extra);
-
- return succeeded;
-}
-
-void OnGameFrame(bool simulating)
-{
- bool weHaveBeenFuckedOver = false;
- struct sigaction oact;
-
- for (int i = 0; i < kNumHandledSignals; ++i) {
- sigaction(kExceptionSignals[i], NULL, &oact);
-
- if (oact.sa_sigaction != SignalHandler) {
- weHaveBeenFuckedOver = true;
- break;
- }
- }
-
- if (!weHaveBeenFuckedOver) {
- return;
- }
-
- struct sigaction act;
- memset(&act, 0, sizeof(act));
- sigemptyset(&act.sa_mask);
-
- for (int i = 0; i < kNumHandledSignals; ++i) {
- sigaddset(&act.sa_mask, kExceptionSignals[i]);
- }
-
- act.sa_sigaction = SignalHandler;
- act.sa_flags = SA_ONSTACK | SA_SIGINFO;
-
- for (int i = 0; i < kNumHandledSignals; ++i) {
- sigaction(kExceptionSignals[i], &act, NULL);
- }
-}
-
-#elif defined _WINDOWS
-void *vectoredHandler = NULL;
-
-LONG CALLBACK BreakpadVectoredHandler(_In_ PEXCEPTION_POINTERS ExceptionInfo)
-{
- switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
- {
- case EXCEPTION_ACCESS_VIOLATION:
- case EXCEPTION_INVALID_HANDLE:
- case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
- case EXCEPTION_DATATYPE_MISALIGNMENT:
- case EXCEPTION_ILLEGAL_INSTRUCTION:
- case EXCEPTION_INT_DIVIDE_BY_ZERO:
- case EXCEPTION_STACK_OVERFLOW:
- case 0xC0000409: // STATUS_STACK_BUFFER_OVERRUN
- case 0xC0000374: // STATUS_HEAP_CORRUPTION
- break;
- case 0: // Valve use this for Sys_Error.
- if ((ExceptionInfo->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) == 0)
- return EXCEPTION_CONTINUE_SEARCH;
- break;
- default:
- return EXCEPTION_CONTINUE_SEARCH;
- }
-
- if (handler->WriteMinidumpForException(ExceptionInfo))
- {
- // Stop the handler thread from deadlocking us.
- delete handler;
-
- // Stop Valve's handler being called.
- ExceptionInfo->ExceptionRecord->ExceptionCode = EXCEPTION_BREAKPOINT;
-
- return EXCEPTION_EXECUTE_HANDLER;
- } else {
- return EXCEPTION_CONTINUE_SEARCH;
- }
-}
-
-static bool dumpCallback(const wchar_t* dump_path,
- const wchar_t* minidump_id,
- void* context,
- EXCEPTION_POINTERS* exinfo,
- MDRawAssertionInfo* assertion,
- bool succeeded)
-{
- if (!succeeded) {
- printf("Failed to write minidump to: %ls\\%ls.dmp\n", dump_path, minidump_id);
- return succeeded;
- }
-
- printf("Wrote minidump to: %ls\\%ls.dmp\n", dump_path, minidump_id);
-
- sprintf(dumpStoragePath, "%ls\\%ls.dmp.txt", dump_path, minidump_id);
-
- FILE *extra = fopen(dumpStoragePath, "wb");
- if (!extra) {
- printf("Failed to open metadata file!\n");
- return succeeded;
- }
-
- fprintf(extra, "-------- CONFIG BEGIN --------");
- fprintf(extra, "\nMap=%s", crashMap);
- fprintf(extra, "\nGamePath=%s", crashGamePath);
- fprintf(extra, "\nCommandLine=%s", crashCommandLine);
- fprintf(extra, "\nSourceModPath=%s", crashSourceModPath);
- fprintf(extra, "\nGameDirectory=%s", crashGameDirectory);
- fprintf(extra, "\nExtensionVersion=%s", SM_VERSION);
- fprintf(extra, "\nExtensionBuild=%s", SM_BUILD_UNIQUEID);
- fprintf(extra, "%s", steamInf);
- fprintf(extra, "\n-------- CONFIG END --------\n");
-
- if (GetSpew || GetSpewFastcall) {
- if (GetSpew) {
- GetSpew(spewBuffer, sizeof(spewBuffer));
- } else if (GetSpewFastcall) {
- GetSpewFastcall(spewBuffer, sizeof(spewBuffer));
- }
-
- if (strlen(spewBuffer) > 0) {
- fprintf(extra, "-------- CONSOLE HISTORY BEGIN --------\n%s-------- CONSOLE HISTORY END --------\n", spewBuffer);
- }
- }
-
- fclose(extra);
-
- return succeeded;
-}
-
-#else
-#error Bad platform.
-#endif
-
-class ClogInhibitor
-{
- std::streambuf *saved_clog = nullptr;
-
-public:
- ClogInhibitor() {
- saved_clog = std::clog.rdbuf();
- std::clog.rdbuf(nullptr);
- }
-
- ~ClogInhibitor() {
- std::clog.rdbuf(saved_clog);
- }
-};
-
-class UploadThread: public IThread
-{
- void RunThread(IThreadHandle *pHandle) {
- rootconsole->ConsolePrint("Accelerator upload thread started.");
-
- FILE *log = fopen(logPath, "a");
- if (!log) {
- g_pSM->LogError(myself, "Failed to open Accelerator log file: %s", logPath);
- }
-
- IDirectory *dumps = libsys->OpenDirectory(dumpStoragePath);
-
- int count = 0;
- int failed = 0;
- char path[512];
- char response[512];
-
- while (dumps->MoreFiles()) {
- if (!dumps->IsEntryFile()) {
- dumps->NextEntry();
- continue;
- }
-
- const char *name = dumps->GetEntryName();
-
- int namelen = strlen(name);
- if (namelen < 4 || strcmp(&name[namelen-4], ".dmp") != 0) {
- dumps->NextEntry();
- continue;
- }
-
- g_pSM->Format(path, sizeof(path), "%s/%s", dumpStoragePath, name);
-
- // TODO: Check the return value.
- bool wantsUpload = PresubmitCrashDump(path);
-
- bool uploaded = UploadAndDeleteCrashDump(path, response, sizeof(response));
-
- if (uploaded) {
- count++;
- g_pSM->LogError(myself, "Accelerator uploaded crash dump: %s", response);
- if (log) fprintf(log, "Uploaded crash dump: %s\n", response);
- } else {
- failed++;
- g_pSM->LogError(myself, "Accelerator failed to upload crash dump: %s", response);
- if (log) fprintf(log, "Failed to upload crash dump: %s\n", response);
- }
-
- dumps->NextEntry();
- }
-
- libsys->CloseDirectory(dumps);
-
- if (log) {
- fclose(log);
- }
-
- rootconsole->ConsolePrint("Accelerator upload thread finished. (%d uploaded, %d failed)", count, failed);
- }
-
- void OnTerminate(IThreadHandle *pHandle, bool cancel) {
- rootconsole->ConsolePrint("Accelerator upload thread terminated. (canceled = %s)", (cancel ? "true" : "false"));
- }
-
- bool PresubmitCrashDump(const char *path) {
- google_breakpad::ProcessState processState;
- google_breakpad::ProcessResult processResult;
- google_breakpad::MinidumpProcessor minidumpProcessor(nullptr, nullptr);
-
- {
- ClogInhibitor clogInhibitor;
- processResult = minidumpProcessor.Process(path, &processState);
- }
-
- if (processResult != google_breakpad::PROCESS_OK) {
- return false;
- }
-
- int requestingThread = processState.requesting_thread();
- if (requestingThread == -1) {
- requestingThread = 0;
- }
-
- const google_breakpad::CallStack *stack = processState.threads()->at(requestingThread);
- if (!stack) {
- return false;
- }
-
- int frameCount = stack->frames()->size();
- /*if (frameCount > 10) {
- frameCount = 10;
- }*/
-
- std::ostringstream summaryStream;
- summaryStream << 1 << "|" << processState.crashed() << "|" << processState.crash_reason() << "|" << std::hex << processState.crash_address() << std::dec << "|" << requestingThread;
-
- std::map moduleMap;
-
- unsigned int moduleCount = processState.modules()->module_count();
- for (unsigned int moduleIndex = 0; moduleIndex < moduleCount; ++moduleIndex) {
- auto module = processState.modules()->GetModuleAtIndex(moduleIndex);
- moduleMap[module] = moduleIndex;
-
- auto debugFile = google_breakpad::PathnameStripper::File(module->debug_file());
- auto debugIdentifier = module->debug_identifier();
-
- summaryStream << "|M|" << debugFile << "|" << debugIdentifier;
- }
-
- for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
- auto frame = stack->frames()->at(frameIndex);
-
- int moduleIndex = -1;
- auto moduleOffset = frame->ReturnAddress();
- if (frame->module) {
- moduleIndex = moduleMap[frame->module];
- moduleOffset -= frame->module->base_address();
- }
-
- summaryStream << "|F|" << moduleIndex << "|" << std::hex << moduleOffset << std::dec;
- }
-
- auto summaryLine = summaryStream.str();
- // printf("%s\n", summaryLine.c_str());
-
- IWebForm *form = webternet->CreateForm();
-
- const char *minidumpAccount = g_pSM->GetCoreConfigValue("MinidumpAccount");
- if (minidumpAccount) form->AddString("UserID", minidumpAccount);
-
- form->AddString("CrashSignature", summaryLine.c_str());
-
- MemoryDownloader data;
- IWebTransfer *xfer = webternet->CreateSession();
- xfer->SetFailOnHTTPError(true);
-
- const char *minidumpUrl = g_pSM->GetCoreConfigValue("MinidumpUrl");
- if (!minidumpUrl) minidumpUrl = "http://crash.limetech.org/submit";
-
- bool uploaded = xfer->PostAndDownload(minidumpUrl, form, &data, NULL);
-
- if (!uploaded) {
- printf(">>> Presubmit failed: %s (%d)\n", xfer->LastErrorMessage(), xfer->LastErrorCode());
- return false;
- }
-
- int responseSize = data.GetSize();
- char *response = new char[responseSize + 1];
- strncpy(response, data.GetBuffer(), responseSize + 1);
- response[responseSize] = '\0';
- printf(">>> Presubmit complete: %s\n", response);
-
- if (responseSize < 2) {
- printf(">>> Response too short\n");
- delete[] response;
- return false;
- }
-
- if (response[0] == 'E') {
- printf(">>> Presubmit error: %s\n", &response[2]);
- delete[] response;
- return false;
- }
-
- bool submitCrash = (response[0] == 'Y');
-
- if (response[1] != '|') {
- printf(">>> Response delimiter missing\n");
- delete[] response;
- return false;
- }
-
- unsigned int responseCount = responseSize - 2;
- if (responseCount != moduleCount) {
- printf(">>> Response module list doesn't match sent list (%d != %d)\n", responseCount, moduleCount);
- delete[] response;
- return false;
- }
-
-#if defined _LINUX
- for (unsigned int moduleIndex = 0; moduleIndex < moduleCount; ++moduleIndex) {
- bool submitModule = (response[2 + moduleIndex] == 'Y');
- if (!submitModule) {
- continue;
- }
-
- auto module = processState.modules()->GetModuleAtIndex(moduleIndex);
-
- auto debugFile = module->debug_file();
- if (debugFile[0] != '/') {
- continue;
- }
-
- printf(">>> Submitting %s\n", debugFile.c_str());
-
- auto debugFileDir = google_breakpad::DirName(debugFile);
- std::vector debug_dirs{
- debugFileDir,
- };
-
- std::ostringstream outputStream;
- google_breakpad::DumpOptions options(ALL_SYMBOL_DATA, true);
-
- {
- StderrInhibitor stdrrInhibitor;
-
- if (!WriteSymbolFile(debugFile, debug_dirs, options, outputStream)) {
- outputStream.str("");
- outputStream.clear();
-
- // Try again without debug dirs.
- if (!WriteSymbolFile(debugFile, {}, options, outputStream)) {
- // TODO: Something.
- continue;
- }
- }
- }
-
- auto output = outputStream.str();
- // output = output.substr(0, output.find("\n"));
- // printf(">>> %s\n", output.c_str());
-
- IWebForm *symbolForm = webternet->CreateForm();
- symbolForm->AddString("symbol_file", output.c_str());
-
- MemoryDownloader symbolData;
- IWebTransfer *symbolXfer = webternet->CreateSession();
- xfer->SetFailOnHTTPError(true);
-
- const char *symbolUrl = g_pSM->GetCoreConfigValue("MinidumpSymbolUrl");
- if (!symbolUrl) symbolUrl = "http://crash.limetech.org/symbols/submit";
-
- bool symbolUploaded = symbolXfer->PostAndDownload(symbolUrl, symbolForm, &symbolData, NULL);
-
- if (!symbolUploaded) {
- printf(">>> Symbol upload failed: %s (%d)\n", symbolXfer->LastErrorMessage(), symbolXfer->LastErrorCode());
- continue;
- }
-
- int symbolResponseSize = symbolData.GetSize();
- char *symbolResponse = new char[symbolResponseSize + 1];
- strncpy(symbolResponse, symbolData.GetBuffer(), symbolResponseSize + 1);
- do {
- symbolResponse[symbolResponseSize] = '\0';
- } while (symbolResponse[--symbolResponseSize] == '\n');
- printf(">>> Symbol upload complete: %s\n", symbolResponse);
- delete[] symbolResponse;
- }
-#else
- printf(">>> Symbol submission not available on this platform\n");
-#endif
-
- delete[] response;
- return submitCrash;
- }
-
- bool UploadAndDeleteCrashDump(const char *path, char *response, int maxlen) {
- IWebForm *form = webternet->CreateForm();
-
- const char *minidumpAccount = g_pSM->GetCoreConfigValue("MinidumpAccount");
- if (minidumpAccount) form->AddString("UserID", minidumpAccount);
-
- form->AddString("GameDirectory", g_pSM->GetGameFolderName());
- form->AddString("ExtensionVersion", SMEXT_CONF_VERSION);
-
- form->AddFile("upload_file_minidump", path);
-
- char metapath[512];
- g_pSM->Format(metapath, sizeof(metapath), "%s.txt", path);
- if (libsys->PathExists(metapath)) {
- form->AddFile("upload_file_metadata", metapath);
- }
-
- MemoryDownloader data;
- IWebTransfer *xfer = webternet->CreateSession();
- xfer->SetFailOnHTTPError(true);
-
- const char *minidumpUrl = g_pSM->GetCoreConfigValue("MinidumpUrl");
- if (!minidumpUrl) minidumpUrl = "http://crash.limetech.org/submit";
-
- bool uploaded = xfer->PostAndDownload(minidumpUrl, form, &data, NULL);
-
- if (response) {
- if (uploaded) {
- int responseSize = data.GetSize();
- if (responseSize >= maxlen) responseSize = maxlen - 1;
- strncpy(response, data.GetBuffer(), responseSize);
- response[responseSize] = '\0';
- } else {
- g_pSM->Format(response, maxlen, "%s (%d)", xfer->LastErrorMessage(), xfer->LastErrorCode());
- }
- }
-
- if (libsys->PathExists(metapath)) {
- unlink(metapath);
- }
-
- unlink(path);
-
- return uploaded;
- }
-} uploadThread;
-
-class VFuncEmptyClass {};
-
-const char *GetCmdLine()
-{
- static int getCmdLineOffset = 0;
- if (getCmdLineOffset == 0) {
- if (!gameconfig || !gameconfig->GetOffset("GetCmdLine", &getCmdLineOffset)) {
- return "";
- }
- if (getCmdLineOffset == 0) {
- return "";
- }
- }
-
- void *cmdline = gamehelpers->GetValveCommandLine();
- void **vtable = *(void ***)cmdline;
- void *vfunc = vtable[getCmdLineOffset];
-
- union {
- const char *(VFuncEmptyClass::*mfpnew)();
-#ifndef WIN32
- struct {
- void *addr;
- intptr_t adjustor;
- } s;
- } u;
- u.s.addr = vfunc;
- u.s.adjustor = 0;
-#else
- void *addr;
- } u;
- u.addr = vfunc;
-#endif
-
- return (const char *)(reinterpret_cast(cmdline)->*u.mfpnew)();
-}
-
-bool Accelerator::SDK_OnLoad(char *error, size_t maxlength, bool late)
-{
- sharesys->AddDependency(myself, "webternet.ext", true, true);
- SM_GET_IFACE(WEBTERNET, webternet);
-
- g_pSM->BuildPath(Path_SM, dumpStoragePath, sizeof(dumpStoragePath), "data/dumps");
-
- if (!libsys->IsPathDirectory(dumpStoragePath))
- {
- if (!libsys->CreateFolder(dumpStoragePath))
- {
- if (error)
- g_pSM->Format(error, maxlength, "%s didn't exist and we couldn't create it :(", dumpStoragePath);
- return false;
- }
- }
-
- g_pSM->BuildPath(Path_SM, logPath, sizeof(logPath), "logs/accelerator.log");
-
- threader->MakeThread(&uploadThread);
-
- do {
- char gameconfigError[256];
- if (!gameconfs->LoadGameConfigFile("accelerator.games", &gameconfig, gameconfigError, sizeof(gameconfigError))) {
- smutils->LogMessage(myself, "WARNING: Failed to load gamedata file, console output and command line will not be included in crash reports: %s", gameconfigError);
- break;
- }
-
- bool useFastcall = false;
-
-
-#if defined _WINDOWS
- const char *fastcall = gameconfig->GetKeyValue("UseFastcall");
- if (fastcall && strcmp(fastcall, "yes") == 0) {
- useFastcall = true;
- }
-
- if (useFastcall && !gameconfig->GetMemSig("GetSpewFastcall", (void **)&GetSpewFastcall)) {
- smutils->LogMessage(myself, "WARNING: GetSpewFastcall not found in gamedata, console output will not be included in crash reports.");
- break;
- }
-#endif
-
- if (!useFastcall && !gameconfig->GetMemSig("GetSpew", (void **)&GetSpew)) {
- smutils->LogMessage(myself, "WARNING: GetSpew not found in gamedata, console output will not be included in crash reports.");
- break;
- }
-
- if (!GetSpew
-#if defined _WINDOWS
- && !GetSpewFastcall
-#endif
- ) {
- smutils->LogMessage(myself, "WARNING: Sigscan for GetSpew failed, console output will not be included in crash reports.");
- break;
- }
- } while(false);
-
-#if defined _LINUX
- google_breakpad::MinidumpDescriptor descriptor(dumpStoragePath);
- handler = new google_breakpad::ExceptionHandler(descriptor, NULL, dumpCallback, NULL, true, -1);
-
- struct sigaction oact;
- sigaction(SIGSEGV, NULL, &oact);
- SignalHandler = oact.sa_sigaction;
-
- g_pSM->AddGameFrameHook(OnGameFrame);
-#elif defined _WINDOWS
- wchar_t *buf = new wchar_t[sizeof(dumpStoragePath)];
- size_t num_chars = mbstowcs(buf, dumpStoragePath, sizeof(dumpStoragePath));
-
- handler = new google_breakpad::ExceptionHandler(std::wstring(buf, num_chars), NULL, dumpCallback, NULL, google_breakpad::ExceptionHandler::HANDLER_ALL);
-
- vectoredHandler = AddVectoredExceptionHandler(0, BreakpadVectoredHandler);
-
- delete buf;
-#else
-#error Bad platform.
-#endif
-
-#if 0
- IPluginIterator *i = plsys->GetPluginIterator();
- while (i->MorePlugins()) {
- IPlugin *p = i->GetPlugin();
- const sm_plugininfo_t *pmi = p->GetPublicInfo();
- PluginInfo *pi = &plugins[plugin_count++];
-
- pi->serial = p->GetSerial();
- pi->status = p->GetStatus();
-
- strncpy(pi->filename, p->GetFilename(), sizeof(pi->filename) - 1);
-
- strncpy(pi->name, pmi->name, sizeof(pi->name) - 1);
- strncpy(pi->author, pmi->author, sizeof(pi->author) - 1);
- strncpy(pi->description, pmi->description, sizeof(pi->description) - 1);
- strncpy(pi->version, pmi->version, sizeof(pi->version) - 1);
- strncpy(pi->url, pmi->url, sizeof(pi->url) - 1);
-
- i->NextPlugin();
- }
- delete i;
-#endif
-
- strncpy(crashGamePath, g_pSM->GetGamePath(), sizeof(crashGamePath) - 1);
- strncpy(crashCommandLine, GetCmdLine(), sizeof(crashCommandLine) - 1);
- strncpy(crashSourceModPath, g_pSM->GetSourceModPath(), sizeof(crashSourceModPath) - 1);
- strncpy(crashGameDirectory, g_pSM->GetGameFolderName(), sizeof(crashGameDirectory) - 1);
-
- char steamInfPath[512];
- g_pSM->BuildPath(Path_Game, steamInfPath, sizeof(steamInfPath), "steam.inf");
-
- FILE *steamInfFile = fopen(steamInfPath, "rb");
- if (steamInfFile) {
- char steamInfTemp[1024] = {0};
- fread(steamInfTemp, sizeof(char), sizeof(steamInfTemp) - 1, steamInfFile);
-
- fclose(steamInfFile);
-
- unsigned commentChars = 0;
- unsigned valueChars = 0;
- unsigned source = 0;
- strcpy(steamInf, "\nSteam_");
- unsigned target = 7; // strlen("\nSteam_");
- while (true) {
- if (steamInfTemp[source] == '\0') {
- source++;
- break;
- }
- if (steamInfTemp[source] == '/') {
- source++;
- commentChars++;
- continue;
- }
- if (commentChars == 1) {
- commentChars = 0;
- steamInf[target++] = '/';
- valueChars++;
- }
- if (steamInfTemp[source] == '\r') {
- source++;
- continue;
- }
- if (steamInfTemp[source] == '\n') {
- commentChars = 0;
- source++;
- if (steamInfTemp[source] == '\0') {
- break;
- }
- if (valueChars > 0) {
- valueChars = 0;
- strcpy(&steamInf[target], "\nSteam_");
- target += 7;
- }
- continue;
- }
- if (commentChars >= 2) {
- source++;
- continue;
- }
- steamInf[target++] = steamInfTemp[source++];
- valueChars++;
- }
- }
-
- if (late) {
- this->OnCoreMapStart(NULL, 0, 0);
- }
-
- return true;
-}
-
-void Accelerator::SDK_OnUnload()
-{
-#if defined _LINUX
- g_pSM->RemoveGameFrameHook(OnGameFrame);
-#elif defined _WINDOWS
- if (vectoredHandler) {
- RemoveVectoredExceptionHandler(vectoredHandler);
- }
-#else
-#error Bad platform.
-#endif
-
- delete handler;
-}
-
-void Accelerator::OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax)
-{
- strncpy(crashMap, gamehelpers->GetCurrentMap(), sizeof(crashMap) - 1);
-}
+/*
+ * =============================================================================
+ * Accelerator Extension
+ * Copyright (C) 2011 Asher Baker (asherkin). All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+
+#include "extension.h"
+
+#include
+#include "MemoryDownloader.h"
+
+#if defined _LINUX
+#include "client/linux/handler/exception_handler.h"
+#include "common/linux/linux_libc_support.h"
+#include "third_party/lss/linux_syscall_support.h"
+#include "common/linux/dump_symbols.h"
+#include "common/path_helper.h"
+
+#include
+#include
+#include
+#include
+
+class StderrInhibitor
+{
+ FILE *saved_stderr = nullptr;
+
+public:
+ StderrInhibitor() {
+ saved_stderr = fdopen(dup(fileno(stderr)), "w");
+ if (freopen(_PATH_DEVNULL, "w", stderr)) {
+ // If it fails, not a lot we can (or should) do.
+ // Add this brace section to silence gcc warnings.
+ }
+ }
+
+ ~StderrInhibitor() {
+ fflush(stderr);
+ dup2(fileno(saved_stderr), fileno(stderr));
+ fclose(saved_stderr);
+ }
+};
+
+#elif defined _WINDOWS
+#define _STDINT // ~.~
+#include "client/windows/handler/exception_handler.h"
+
+#else
+#error Bad platform.
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+Accelerator g_accelerator;
+SMEXT_LINK(&g_accelerator);
+
+IWebternet *webternet;
+IGameConfig *gameconfig;
+
+typedef void (*GetSpew_t)(char *buffer, unsigned int length);
+GetSpew_t GetSpew;
+#if defined _WINDOWS
+typedef void(__fastcall *GetSpewFastcall_t)(char *buffer, unsigned int length);
+GetSpewFastcall_t GetSpewFastcall;
+#endif
+
+char spewBuffer[65536]; // Hi.
+
+char crashMap[256];
+char crashGamePath[512];
+char crashCommandLine[1024];
+char crashSourceModPath[512];
+char crashGameDirectory[256];
+char steamInf[1024];
+
+char dumpStoragePath[512];
+char logPath[512];
+
+google_breakpad::ExceptionHandler *handler = NULL;
+
+# if 0
+struct PluginInfo {
+ unsigned int serial;
+ PluginStatus status;
+ char filename[256];
+ char name[256];
+ char author[256];
+ char description[256];
+ char version[256];
+ char url[256];
+};
+
+unsigned int plugin_count;
+PluginInfo plugins[256];
+#endif
+
+#if defined _LINUX
+void (*SignalHandler)(int, siginfo_t *, void *);
+
+const int kExceptionSignals[] = {
+ SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS
+};
+
+const int kNumHandledSignals = sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]);
+
+static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded)
+{
+ //printf("Wrote minidump to: %s\n", descriptor.path());
+
+ if (succeeded) {
+ sys_write(STDOUT_FILENO, "Wrote minidump to: ", 19);
+ } else {
+ sys_write(STDOUT_FILENO, "Failed to write minidump to: ", 29);
+ }
+
+ sys_write(STDOUT_FILENO, descriptor.path(), my_strlen(descriptor.path()));
+ sys_write(STDOUT_FILENO, "\n", 1);
+
+ if (!succeeded) {
+ return succeeded;
+ }
+
+ my_strlcpy(dumpStoragePath, descriptor.path(), sizeof(dumpStoragePath));
+ my_strlcat(dumpStoragePath, ".txt", sizeof(dumpStoragePath));
+
+ int extra = sys_open(dumpStoragePath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+ if (extra == -1) {
+ sys_write(STDOUT_FILENO, "Failed to open metadata file!\n", 30);
+ return succeeded;
+ }
+
+ sys_write(extra, "-------- CONFIG BEGIN --------", 30);
+ sys_write(extra, "\nMap=", 5);
+ sys_write(extra, crashMap, my_strlen(crashMap));
+ sys_write(extra, "\nGamePath=", 10);
+ sys_write(extra, crashGamePath, my_strlen(crashGamePath));
+ sys_write(extra, "\nCommandLine=", 13);
+ sys_write(extra, crashCommandLine, my_strlen(crashCommandLine));
+ sys_write(extra, "\nSourceModPath=", 15);
+ sys_write(extra, crashSourceModPath, my_strlen(crashSourceModPath));
+ sys_write(extra, "\nGameDirectory=", 15);
+ sys_write(extra, crashGameDirectory, my_strlen(crashGameDirectory));
+ sys_write(extra, "\nExtensionVersion=", 18);
+ sys_write(extra, SM_VERSION, my_strlen(SM_VERSION));
+ sys_write(extra, "\nExtensionBuild=", 16);
+ sys_write(extra, SM_BUILD_UNIQUEID, my_strlen(SM_BUILD_UNIQUEID));
+ sys_write(extra, steamInf, my_strlen(steamInf));
+ sys_write(extra, "\n-------- CONFIG END --------\n", 30);
+
+ if (GetSpew) {
+ GetSpew(spewBuffer, sizeof(spewBuffer));
+
+ if (my_strlen(spewBuffer) > 0) {
+ sys_write(extra, "-------- CONSOLE HISTORY BEGIN --------\n", 40);
+ sys_write(extra, spewBuffer, my_strlen(spewBuffer));
+ sys_write(extra, "-------- CONSOLE HISTORY END --------\n", 38);
+ }
+ }
+
+#if 0
+ char pis[64];
+ char pds[32];
+ for (unsigned i = 0; i < plugin_count; ++i) {
+ PluginInfo *p = &plugins[i];
+ if (p->serial == 0) continue;
+ my_uitos(pds, i, my_uint_len(i));
+ pds[my_uint_len(i)] = '\0';
+ my_strlcpy(pis, "plugin[", sizeof(pis));
+ my_strlcat(pis, pds, sizeof(pis));
+ my_strlcat(pis, "].", sizeof(pis));
+ sys_write(extra, pis, my_strlen(pis));
+ sys_write(extra, "filename=", 9);
+ sys_write(extra, p->filename, my_strlen(p->filename));
+ sys_write(extra, "\n", 1);
+ sys_write(extra, pis, my_strlen(pis));
+ sys_write(extra, "name=", 5);
+ sys_write(extra, p->name, my_strlen(p->name));
+ sys_write(extra, "\n", 1);
+ sys_write(extra, pis, my_strlen(pis));
+ sys_write(extra, "author=", 7);
+ sys_write(extra, p->author, my_strlen(p->author));
+ sys_write(extra, "\n", 1);
+ sys_write(extra, pis, my_strlen(pis));
+ sys_write(extra, "description=", 12);
+ sys_write(extra, p->description, my_strlen(p->description));
+ sys_write(extra, "\n", 1);
+ sys_write(extra, pis, my_strlen(pis));
+ sys_write(extra, "version=", 8);
+ sys_write(extra, p->version, my_strlen(p->version));
+ sys_write(extra, "\n", 1);
+ sys_write(extra, pis, my_strlen(pis));
+ sys_write(extra, "url=", 4);
+ sys_write(extra, p->url, my_strlen(p->url));
+ sys_write(extra, "\n", 1);
+ }
+#endif
+
+ sys_close(extra);
+
+ return succeeded;
+}
+
+void OnGameFrame(bool simulating)
+{
+ bool weHaveBeenFuckedOver = false;
+ struct sigaction oact;
+
+ for (int i = 0; i < kNumHandledSignals; ++i) {
+ sigaction(kExceptionSignals[i], NULL, &oact);
+
+ if (oact.sa_sigaction != SignalHandler) {
+ weHaveBeenFuckedOver = true;
+ break;
+ }
+ }
+
+ if (!weHaveBeenFuckedOver) {
+ return;
+ }
+
+ struct sigaction act;
+ memset(&act, 0, sizeof(act));
+ sigemptyset(&act.sa_mask);
+
+ for (int i = 0; i < kNumHandledSignals; ++i) {
+ sigaddset(&act.sa_mask, kExceptionSignals[i]);
+ }
+
+ act.sa_sigaction = SignalHandler;
+ act.sa_flags = SA_ONSTACK | SA_SIGINFO;
+
+ for (int i = 0; i < kNumHandledSignals; ++i) {
+ sigaction(kExceptionSignals[i], &act, NULL);
+ }
+}
+
+#elif defined _WINDOWS
+void *vectoredHandler = NULL;
+
+LONG CALLBACK BreakpadVectoredHandler(_In_ PEXCEPTION_POINTERS ExceptionInfo)
+{
+ switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
+ {
+ case EXCEPTION_ACCESS_VIOLATION:
+ case EXCEPTION_INVALID_HANDLE:
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+ case EXCEPTION_DATATYPE_MISALIGNMENT:
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ case EXCEPTION_STACK_OVERFLOW:
+ case 0xC0000409: // STATUS_STACK_BUFFER_OVERRUN
+ case 0xC0000374: // STATUS_HEAP_CORRUPTION
+ break;
+ case 0: // Valve use this for Sys_Error.
+ if ((ExceptionInfo->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) == 0)
+ return EXCEPTION_CONTINUE_SEARCH;
+ break;
+ default:
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ if (handler->WriteMinidumpForException(ExceptionInfo))
+ {
+ // Stop the handler thread from deadlocking us.
+ delete handler;
+
+ // Stop Valve's handler being called.
+ ExceptionInfo->ExceptionRecord->ExceptionCode = EXCEPTION_BREAKPOINT;
+
+ return EXCEPTION_EXECUTE_HANDLER;
+ } else {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+}
+
+static bool dumpCallback(const wchar_t* dump_path,
+ const wchar_t* minidump_id,
+ void* context,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion,
+ bool succeeded)
+{
+ if (!succeeded) {
+ printf("Failed to write minidump to: %ls\\%ls.dmp\n", dump_path, minidump_id);
+ return succeeded;
+ }
+
+ printf("Wrote minidump to: %ls\\%ls.dmp\n", dump_path, minidump_id);
+
+ sprintf(dumpStoragePath, "%ls\\%ls.dmp.txt", dump_path, minidump_id);
+
+ FILE *extra = fopen(dumpStoragePath, "wb");
+ if (!extra) {
+ printf("Failed to open metadata file!\n");
+ return succeeded;
+ }
+
+ fprintf(extra, "-------- CONFIG BEGIN --------");
+ fprintf(extra, "\nMap=%s", crashMap);
+ fprintf(extra, "\nGamePath=%s", crashGamePath);
+ fprintf(extra, "\nCommandLine=%s", crashCommandLine);
+ fprintf(extra, "\nSourceModPath=%s", crashSourceModPath);
+ fprintf(extra, "\nGameDirectory=%s", crashGameDirectory);
+ fprintf(extra, "\nExtensionVersion=%s", SM_VERSION);
+ fprintf(extra, "\nExtensionBuild=%s", SM_BUILD_UNIQUEID);
+ fprintf(extra, "%s", steamInf);
+ fprintf(extra, "\n-------- CONFIG END --------\n");
+
+ if (GetSpew || GetSpewFastcall) {
+ if (GetSpew) {
+ GetSpew(spewBuffer, sizeof(spewBuffer));
+ } else if (GetSpewFastcall) {
+ GetSpewFastcall(spewBuffer, sizeof(spewBuffer));
+ }
+
+ if (strlen(spewBuffer) > 0) {
+ fprintf(extra, "-------- CONSOLE HISTORY BEGIN --------\n%s-------- CONSOLE HISTORY END --------\n", spewBuffer);
+ }
+ }
+
+ fclose(extra);
+
+ return succeeded;
+}
+
+#else
+#error Bad platform.
+#endif
+
+class ClogInhibitor
+{
+ std::streambuf *saved_clog = nullptr;
+
+public:
+ ClogInhibitor() {
+ saved_clog = std::clog.rdbuf();
+ std::clog.rdbuf(nullptr);
+ }
+
+ ~ClogInhibitor() {
+ std::clog.rdbuf(saved_clog);
+ }
+};
+
+class UploadThread: public IThread
+{
+ void RunThread(IThreadHandle *pHandle) {
+ rootconsole->ConsolePrint("Accelerator upload thread started.");
+
+ FILE *log = fopen(logPath, "a");
+ if (!log) {
+ g_pSM->LogError(myself, "Failed to open Accelerator log file: %s", logPath);
+ }
+
+ IDirectory *dumps = libsys->OpenDirectory(dumpStoragePath);
+
+ int count = 0;
+ int failed = 0;
+ char path[512];
+ char response[512];
+
+ while (dumps->MoreFiles()) {
+ if (!dumps->IsEntryFile()) {
+ dumps->NextEntry();
+ continue;
+ }
+
+ const char *name = dumps->GetEntryName();
+
+ int namelen = strlen(name);
+ if (namelen < 4 || strcmp(&name[namelen-4], ".dmp") != 0) {
+ dumps->NextEntry();
+ continue;
+ }
+
+ g_pSM->Format(path, sizeof(path), "%s/%s", dumpStoragePath, name);
+
+ // TODO: Check the return value.
+ bool wantsUpload = PresubmitCrashDump(path);
+
+ bool uploaded = UploadAndDeleteCrashDump(path, response, sizeof(response));
+
+ if (uploaded) {
+ count++;
+ g_pSM->LogError(myself, "Accelerator uploaded crash dump: %s", response);
+ if (log) fprintf(log, "Uploaded crash dump: %s\n", response);
+ } else {
+ failed++;
+ g_pSM->LogError(myself, "Accelerator failed to upload crash dump: %s", response);
+ if (log) fprintf(log, "Failed to upload crash dump: %s\n", response);
+ }
+
+ dumps->NextEntry();
+ }
+
+ libsys->CloseDirectory(dumps);
+
+ if (log) {
+ fclose(log);
+ }
+
+ rootconsole->ConsolePrint("Accelerator upload thread finished. (%d uploaded, %d failed)", count, failed);
+ }
+
+ void OnTerminate(IThreadHandle *pHandle, bool cancel) {
+ rootconsole->ConsolePrint("Accelerator upload thread terminated. (canceled = %s)", (cancel ? "true" : "false"));
+ }
+
+ bool PresubmitCrashDump(const char *path) {
+ google_breakpad::ProcessState processState;
+ google_breakpad::ProcessResult processResult;
+ google_breakpad::MinidumpProcessor minidumpProcessor(nullptr, nullptr);
+
+ {
+ ClogInhibitor clogInhibitor;
+ processResult = minidumpProcessor.Process(path, &processState);
+ }
+
+ if (processResult != google_breakpad::PROCESS_OK) {
+ return false;
+ }
+
+ int requestingThread = processState.requesting_thread();
+ if (requestingThread == -1) {
+ requestingThread = 0;
+ }
+
+ const google_breakpad::CallStack *stack = processState.threads()->at(requestingThread);
+ if (!stack) {
+ return false;
+ }
+
+ int frameCount = stack->frames()->size();
+ /*if (frameCount > 10) {
+ frameCount = 10;
+ }*/
+
+ std::ostringstream summaryStream;
+ summaryStream << 1 << "|" << processState.crashed() << "|" << processState.crash_reason() << "|" << std::hex << processState.crash_address() << std::dec << "|" << requestingThread;
+
+ std::map moduleMap;
+
+ unsigned int moduleCount = processState.modules()->module_count();
+ for (unsigned int moduleIndex = 0; moduleIndex < moduleCount; ++moduleIndex) {
+ auto module = processState.modules()->GetModuleAtIndex(moduleIndex);
+ moduleMap[module] = moduleIndex;
+
+ auto debugFile = google_breakpad::PathnameStripper::File(module->debug_file());
+ auto debugIdentifier = module->debug_identifier();
+
+ summaryStream << "|M|" << debugFile << "|" << debugIdentifier;
+ }
+
+ for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
+ auto frame = stack->frames()->at(frameIndex);
+
+ int moduleIndex = -1;
+ auto moduleOffset = frame->ReturnAddress();
+ if (frame->module) {
+ moduleIndex = moduleMap[frame->module];
+ moduleOffset -= frame->module->base_address();
+ }
+
+ summaryStream << "|F|" << moduleIndex << "|" << std::hex << moduleOffset << std::dec;
+ }
+
+ auto summaryLine = summaryStream.str();
+ // printf("%s\n", summaryLine.c_str());
+
+ IWebForm *form = webternet->CreateForm();
+
+ const char *minidumpAccount = g_pSM->GetCoreConfigValue("MinidumpAccount");
+ if (minidumpAccount) form->AddString("UserID", minidumpAccount);
+
+ form->AddString("CrashSignature", summaryLine.c_str());
+
+ MemoryDownloader data;
+ IWebTransfer *xfer = webternet->CreateSession();
+ xfer->SetFailOnHTTPError(true);
+
+ const char *minidumpUrl = g_pSM->GetCoreConfigValue("MinidumpUrl");
+ if (!minidumpUrl) minidumpUrl = "http://crash.limetech.org/submit";
+
+ bool uploaded = xfer->PostAndDownload(minidumpUrl, form, &data, NULL);
+
+ if (!uploaded) {
+ printf(">>> Presubmit failed: %s (%d)\n", xfer->LastErrorMessage(), xfer->LastErrorCode());
+ return false;
+ }
+
+ int responseSize = data.GetSize();
+ char *response = new char[responseSize + 1];
+ strncpy(response, data.GetBuffer(), responseSize + 1);
+ response[responseSize] = '\0';
+ printf(">>> Presubmit complete: %s\n", response);
+
+ if (responseSize < 2) {
+ printf(">>> Response too short\n");
+ delete[] response;
+ return false;
+ }
+
+ if (response[0] == 'E') {
+ printf(">>> Presubmit error: %s\n", &response[2]);
+ delete[] response;
+ return false;
+ }
+
+ bool submitCrash = (response[0] == 'Y');
+
+ if (response[1] != '|') {
+ printf(">>> Response delimiter missing\n");
+ delete[] response;
+ return false;
+ }
+
+ unsigned int responseCount = responseSize - 2;
+ if (responseCount != moduleCount) {
+ printf(">>> Response module list doesn't match sent list (%d != %d)\n", responseCount, moduleCount);
+ delete[] response;
+ return false;
+ }
+
+#if defined _LINUX
+ for (unsigned int moduleIndex = 0; moduleIndex < moduleCount; ++moduleIndex) {
+ bool submitModule = (response[2 + moduleIndex] == 'Y');
+ if (!submitModule) {
+ continue;
+ }
+
+ auto module = processState.modules()->GetModuleAtIndex(moduleIndex);
+
+ auto debugFile = module->debug_file();
+ if (debugFile[0] != '/') {
+ continue;
+ }
+
+ printf(">>> Submitting %s\n", debugFile.c_str());
+
+ auto debugFileDir = google_breakpad::DirName(debugFile);
+ std::vector debug_dirs{
+ debugFileDir,
+ };
+
+ std::ostringstream outputStream;
+ google_breakpad::DumpOptions options(ALL_SYMBOL_DATA, true);
+
+ {
+ StderrInhibitor stdrrInhibitor;
+
+ if (!WriteSymbolFile(debugFile, debug_dirs, options, outputStream)) {
+ outputStream.str("");
+ outputStream.clear();
+
+ // Try again without debug dirs.
+ if (!WriteSymbolFile(debugFile, {}, options, outputStream)) {
+ // TODO: Something.
+ continue;
+ }
+ }
+ }
+
+ auto output = outputStream.str();
+ // output = output.substr(0, output.find("\n"));
+ // printf(">>> %s\n", output.c_str());
+
+ IWebForm *symbolForm = webternet->CreateForm();
+ symbolForm->AddString("symbol_file", output.c_str());
+
+ MemoryDownloader symbolData;
+ IWebTransfer *symbolXfer = webternet->CreateSession();
+ xfer->SetFailOnHTTPError(true);
+
+ const char *symbolUrl = g_pSM->GetCoreConfigValue("MinidumpSymbolUrl");
+ if (!symbolUrl) symbolUrl = "http://crash.limetech.org/symbols/submit";
+
+ bool symbolUploaded = symbolXfer->PostAndDownload(symbolUrl, symbolForm, &symbolData, NULL);
+
+ if (!symbolUploaded) {
+ printf(">>> Symbol upload failed: %s (%d)\n", symbolXfer->LastErrorMessage(), symbolXfer->LastErrorCode());
+ continue;
+ }
+
+ int symbolResponseSize = symbolData.GetSize();
+ char *symbolResponse = new char[symbolResponseSize + 1];
+ strncpy(symbolResponse, symbolData.GetBuffer(), symbolResponseSize + 1);
+ do {
+ symbolResponse[symbolResponseSize] = '\0';
+ } while (symbolResponse[--symbolResponseSize] == '\n');
+ printf(">>> Symbol upload complete: %s\n", symbolResponse);
+ delete[] symbolResponse;
+ }
+#else
+ printf(">>> Symbol submission not available on this platform\n");
+#endif
+
+ delete[] response;
+ return submitCrash;
+ }
+
+ bool UploadAndDeleteCrashDump(const char *path, char *response, int maxlen) {
+ IWebForm *form = webternet->CreateForm();
+
+ const char *minidumpAccount = g_pSM->GetCoreConfigValue("MinidumpAccount");
+ if (minidumpAccount) form->AddString("UserID", minidumpAccount);
+
+ form->AddString("GameDirectory", g_pSM->GetGameFolderName());
+ form->AddString("ExtensionVersion", SMEXT_CONF_VERSION);
+
+ form->AddFile("upload_file_minidump", path);
+
+ char metapath[512];
+ g_pSM->Format(metapath, sizeof(metapath), "%s.txt", path);
+ if (libsys->PathExists(metapath)) {
+ form->AddFile("upload_file_metadata", metapath);
+ }
+
+ MemoryDownloader data;
+ IWebTransfer *xfer = webternet->CreateSession();
+ xfer->SetFailOnHTTPError(true);
+
+ const char *minidumpUrl = g_pSM->GetCoreConfigValue("MinidumpUrl");
+ if (!minidumpUrl) minidumpUrl = "http://crash.limetech.org/submit";
+
+ bool uploaded = xfer->PostAndDownload(minidumpUrl, form, &data, NULL);
+
+ if (response) {
+ if (uploaded) {
+ int responseSize = data.GetSize();
+ if (responseSize >= maxlen) responseSize = maxlen - 1;
+ strncpy(response, data.GetBuffer(), responseSize);
+ response[responseSize] = '\0';
+ } else {
+ g_pSM->Format(response, maxlen, "%s (%d)", xfer->LastErrorMessage(), xfer->LastErrorCode());
+ }
+ }
+
+ if (libsys->PathExists(metapath)) {
+ unlink(metapath);
+ }
+
+ unlink(path);
+
+ return uploaded;
+ }
+} uploadThread;
+
+class VFuncEmptyClass {};
+
+const char *GetCmdLine()
+{
+ static int getCmdLineOffset = 0;
+ if (getCmdLineOffset == 0) {
+ if (!gameconfig || !gameconfig->GetOffset("GetCmdLine", &getCmdLineOffset)) {
+ return "";
+ }
+ if (getCmdLineOffset == 0) {
+ return "";
+ }
+ }
+
+ void *cmdline = gamehelpers->GetValveCommandLine();
+ void **vtable = *(void ***)cmdline;
+ void *vfunc = vtable[getCmdLineOffset];
+
+ union {
+ const char *(VFuncEmptyClass::*mfpnew)();
+#ifndef WIN32
+ struct {
+ void *addr;
+ intptr_t adjustor;
+ } s;
+ } u;
+ u.s.addr = vfunc;
+ u.s.adjustor = 0;
+#else
+ void *addr;
+ } u;
+ u.addr = vfunc;
+#endif
+
+ return (const char *)(reinterpret_cast(cmdline)->*u.mfpnew)();
+}
+
+bool Accelerator::SDK_OnLoad(char *error, size_t maxlength, bool late)
+{
+ sharesys->AddDependency(myself, "webternet.ext", true, true);
+ SM_GET_IFACE(WEBTERNET, webternet);
+
+ g_pSM->BuildPath(Path_SM, dumpStoragePath, sizeof(dumpStoragePath), "data/dumps");
+
+ if (!libsys->IsPathDirectory(dumpStoragePath))
+ {
+ if (!libsys->CreateFolder(dumpStoragePath))
+ {
+ if (error)
+ g_pSM->Format(error, maxlength, "%s didn't exist and we couldn't create it :(", dumpStoragePath);
+ return false;
+ }
+ }
+
+ g_pSM->BuildPath(Path_SM, logPath, sizeof(logPath), "logs/accelerator.log");
+
+ threader->MakeThread(&uploadThread);
+
+ do {
+ char gameconfigError[256];
+ if (!gameconfs->LoadGameConfigFile("accelerator.games", &gameconfig, gameconfigError, sizeof(gameconfigError))) {
+ smutils->LogMessage(myself, "WARNING: Failed to load gamedata file, console output and command line will not be included in crash reports: %s", gameconfigError);
+ break;
+ }
+
+ bool useFastcall = false;
+
+
+#if defined _WINDOWS
+ const char *fastcall = gameconfig->GetKeyValue("UseFastcall");
+ if (fastcall && strcmp(fastcall, "yes") == 0) {
+ useFastcall = true;
+ }
+
+ if (useFastcall && !gameconfig->GetMemSig("GetSpewFastcall", (void **)&GetSpewFastcall)) {
+ smutils->LogMessage(myself, "WARNING: GetSpewFastcall not found in gamedata, console output will not be included in crash reports.");
+ break;
+ }
+#endif
+
+ if (!useFastcall && !gameconfig->GetMemSig("GetSpew", (void **)&GetSpew)) {
+ smutils->LogMessage(myself, "WARNING: GetSpew not found in gamedata, console output will not be included in crash reports.");
+ break;
+ }
+
+ if (!GetSpew
+#if defined _WINDOWS
+ && !GetSpewFastcall
+#endif
+ ) {
+ smutils->LogMessage(myself, "WARNING: Sigscan for GetSpew failed, console output will not be included in crash reports.");
+ break;
+ }
+ } while(false);
+
+#if defined _LINUX
+ google_breakpad::MinidumpDescriptor descriptor(dumpStoragePath);
+ handler = new google_breakpad::ExceptionHandler(descriptor, NULL, dumpCallback, NULL, true, -1);
+
+ struct sigaction oact;
+ sigaction(SIGSEGV, NULL, &oact);
+ SignalHandler = oact.sa_sigaction;
+
+ g_pSM->AddGameFrameHook(OnGameFrame);
+#elif defined _WINDOWS
+ wchar_t *buf = new wchar_t[sizeof(dumpStoragePath)];
+ size_t num_chars = mbstowcs(buf, dumpStoragePath, sizeof(dumpStoragePath));
+
+ handler = new google_breakpad::ExceptionHandler(std::wstring(buf, num_chars), NULL, dumpCallback, NULL, google_breakpad::ExceptionHandler::HANDLER_ALL);
+
+ vectoredHandler = AddVectoredExceptionHandler(0, BreakpadVectoredHandler);
+
+ delete buf;
+#else
+#error Bad platform.
+#endif
+
+#if 0
+ IPluginIterator *i = plsys->GetPluginIterator();
+ while (i->MorePlugins()) {
+ IPlugin *p = i->GetPlugin();
+ const sm_plugininfo_t *pmi = p->GetPublicInfo();
+ PluginInfo *pi = &plugins[plugin_count++];
+
+ pi->serial = p->GetSerial();
+ pi->status = p->GetStatus();
+
+ strncpy(pi->filename, p->GetFilename(), sizeof(pi->filename) - 1);
+
+ strncpy(pi->name, pmi->name, sizeof(pi->name) - 1);
+ strncpy(pi->author, pmi->author, sizeof(pi->author) - 1);
+ strncpy(pi->description, pmi->description, sizeof(pi->description) - 1);
+ strncpy(pi->version, pmi->version, sizeof(pi->version) - 1);
+ strncpy(pi->url, pmi->url, sizeof(pi->url) - 1);
+
+ i->NextPlugin();
+ }
+ delete i;
+#endif
+
+ strncpy(crashGamePath, g_pSM->GetGamePath(), sizeof(crashGamePath) - 1);
+ strncpy(crashCommandLine, GetCmdLine(), sizeof(crashCommandLine) - 1);
+ strncpy(crashSourceModPath, g_pSM->GetSourceModPath(), sizeof(crashSourceModPath) - 1);
+ strncpy(crashGameDirectory, g_pSM->GetGameFolderName(), sizeof(crashGameDirectory) - 1);
+
+ char steamInfPath[512];
+ g_pSM->BuildPath(Path_Game, steamInfPath, sizeof(steamInfPath), "steam.inf");
+
+ FILE *steamInfFile = fopen(steamInfPath, "rb");
+ if (steamInfFile) {
+ char steamInfTemp[1024] = {0};
+ fread(steamInfTemp, sizeof(char), sizeof(steamInfTemp) - 1, steamInfFile);
+
+ fclose(steamInfFile);
+
+ unsigned commentChars = 0;
+ unsigned valueChars = 0;
+ unsigned source = 0;
+ strcpy(steamInf, "\nSteam_");
+ unsigned target = 7; // strlen("\nSteam_");
+ while (true) {
+ if (steamInfTemp[source] == '\0') {
+ source++;
+ break;
+ }
+ if (steamInfTemp[source] == '/') {
+ source++;
+ commentChars++;
+ continue;
+ }
+ if (commentChars == 1) {
+ commentChars = 0;
+ steamInf[target++] = '/';
+ valueChars++;
+ }
+ if (steamInfTemp[source] == '\r') {
+ source++;
+ continue;
+ }
+ if (steamInfTemp[source] == '\n') {
+ commentChars = 0;
+ source++;
+ if (steamInfTemp[source] == '\0') {
+ break;
+ }
+ if (valueChars > 0) {
+ valueChars = 0;
+ strcpy(&steamInf[target], "\nSteam_");
+ target += 7;
+ }
+ continue;
+ }
+ if (commentChars >= 2) {
+ source++;
+ continue;
+ }
+ steamInf[target++] = steamInfTemp[source++];
+ valueChars++;
+ }
+ }
+
+ if (late) {
+ this->OnCoreMapStart(NULL, 0, 0);
+ }
+
+ return true;
+}
+
+void Accelerator::SDK_OnUnload()
+{
+#if defined _LINUX
+ g_pSM->RemoveGameFrameHook(OnGameFrame);
+#elif defined _WINDOWS
+ if (vectoredHandler) {
+ RemoveVectoredExceptionHandler(vectoredHandler);
+ }
+#else
+#error Bad platform.
+#endif
+
+ delete handler;
+}
+
+void Accelerator::OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax)
+{
+ strncpy(crashMap, gamehelpers->GetCurrentMap(), sizeof(crashMap) - 1);
+}
diff --git a/extension/extension.h b/extension/extension.h
index 1ce9265..e153ff1 100644
--- a/extension/extension.h
+++ b/extension/extension.h
@@ -1,109 +1,109 @@
-/**
- * =============================================================================
- * Accelerator Extension
- * Copyright (C) 2009-2010 Asher Baker (asherkin). All rights reserved.
- * =============================================================================
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, version 3.0, as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see .
- *
- */
-
-#ifndef _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
-#define _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
-
-/**
- * @file extension.hpp
- * @brief Accelerator extension code header.
- */
-
-#include "smsdk_ext.h"
-
-/**
- * @brief Sample implementation of the SDK Extension.
- * Note: Uncomment one of the pre-defined virtual functions in order to use it.
- */
-class Accelerator : public SDKExtension
-{
-public:
- /**
- * @brief This is called after the initial loading sequence has been processed.
- *
- * @param error Error message buffer.
- * @param maxlength Size of error message buffer.
- * @param late Whether or not the module was loaded after map load.
- * @return True to succeed loading, false to fail.
- */
- virtual bool SDK_OnLoad(char *error, size_t maxlen, bool late);
-
- /**
- * @brief This is called right before the extension is unloaded.
- */
- virtual void SDK_OnUnload();
-
- /**
- * @brief Called when the pause state is changed.
- */
- //virtual void SDK_OnPauseChange(bool paused);
-
- /**
- * @brief this is called when Core wants to know if your extension is working.
- *
- * @param error Error message buffer.
- * @param maxlength Size of error message buffer.
- * @return True if working, false otherwise.
- */
- //virtual bool QueryRunning(char *error, size_t maxlen);
-public:
-#if defined SMEXT_CONF_METAMOD
- /**
- * @brief Called when Metamod is attached, before the extension version is called.
- *
- * @param error Error buffer.
- * @param maxlength Maximum size of error buffer.
- * @param late Whether or not Metamod considers this a late load.
- * @return True to succeed, false to fail.
- */
- //virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late);
-
- /**
- * @brief Called when Metamod is detaching, after the extension version is called.
- * NOTE: By default this is blocked unless sent from SourceMod.
- *
- * @param error Error buffer.
- * @param maxlength Maximum size of error buffer.
- * @return True to succeed, false to fail.
- */
- //virtual bool SDK_OnMetamodUnload(char *error, size_t maxlen);
-
- /**
- * @brief Called when Metamod's pause state is changing.
- * NOTE: By default this is blocked unless sent from SourceMod.
- *
- * @param paused Pause state being set.
- * @param error Error buffer.
- * @param maxlength Maximum size of error buffer.
- * @return True to succeed, false to fail.
- */
- //virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlen);
-#endif
- /**
- * @brief Called on server activation before plugins receive the OnServerLoad forward.
- *
- * @param pEdictList Edicts list.
- * @param edictCount Number of edicts in the list.
- * @param clientMax Maximum number of clients allowed in the server.
- */
- virtual void OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax);
-};
-
-#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
+/**
+ * =============================================================================
+ * Accelerator Extension
+ * Copyright (C) 2009-2010 Asher Baker (asherkin). All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ */
+
+#ifndef _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
+#define _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
+
+/**
+ * @file extension.hpp
+ * @brief Accelerator extension code header.
+ */
+
+#include "smsdk_ext.h"
+
+/**
+ * @brief Sample implementation of the SDK Extension.
+ * Note: Uncomment one of the pre-defined virtual functions in order to use it.
+ */
+class Accelerator : public SDKExtension
+{
+public:
+ /**
+ * @brief This is called after the initial loading sequence has been processed.
+ *
+ * @param error Error message buffer.
+ * @param maxlength Size of error message buffer.
+ * @param late Whether or not the module was loaded after map load.
+ * @return True to succeed loading, false to fail.
+ */
+ virtual bool SDK_OnLoad(char *error, size_t maxlen, bool late);
+
+ /**
+ * @brief This is called right before the extension is unloaded.
+ */
+ virtual void SDK_OnUnload();
+
+ /**
+ * @brief Called when the pause state is changed.
+ */
+ //virtual void SDK_OnPauseChange(bool paused);
+
+ /**
+ * @brief this is called when Core wants to know if your extension is working.
+ *
+ * @param error Error message buffer.
+ * @param maxlength Size of error message buffer.
+ * @return True if working, false otherwise.
+ */
+ //virtual bool QueryRunning(char *error, size_t maxlen);
+public:
+#if defined SMEXT_CONF_METAMOD
+ /**
+ * @brief Called when Metamod is attached, before the extension version is called.
+ *
+ * @param error Error buffer.
+ * @param maxlength Maximum size of error buffer.
+ * @param late Whether or not Metamod considers this a late load.
+ * @return True to succeed, false to fail.
+ */
+ //virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late);
+
+ /**
+ * @brief Called when Metamod is detaching, after the extension version is called.
+ * NOTE: By default this is blocked unless sent from SourceMod.
+ *
+ * @param error Error buffer.
+ * @param maxlength Maximum size of error buffer.
+ * @return True to succeed, false to fail.
+ */
+ //virtual bool SDK_OnMetamodUnload(char *error, size_t maxlen);
+
+ /**
+ * @brief Called when Metamod's pause state is changing.
+ * NOTE: By default this is blocked unless sent from SourceMod.
+ *
+ * @param paused Pause state being set.
+ * @param error Error buffer.
+ * @param maxlength Maximum size of error buffer.
+ * @return True to succeed, false to fail.
+ */
+ //virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlen);
+#endif
+ /**
+ * @brief Called on server activation before plugins receive the OnServerLoad forward.
+ *
+ * @param pEdictList Edicts list.
+ * @param edictCount Number of edicts in the list.
+ * @param clientMax Maximum number of clients allowed in the server.
+ */
+ virtual void OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax);
+};
+
+#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
diff --git a/extension/smsdk_config.h b/extension/smsdk_config.h
index bb1ea72..dace891 100644
--- a/extension/smsdk_config.h
+++ b/extension/smsdk_config.h
@@ -1,81 +1,81 @@
-/**
- * =============================================================================
- * Accelerator Extension
- * Copyright (C) 2011 Asher Baker (asherkin). All rights reserved.
- * =============================================================================
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, version 3.0, as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see .
- *
- * As a special exception, AlliedModders LLC gives you permission to link the
- * code of this program (as well as its derivative works) to "Half-Life 2," the
- * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
- * by the Valve Corporation. You must obey the GNU General Public License in
- * all respects for all other code used. Additionally, AlliedModders LLC grants
- * this exception to all derivative works. AlliedModders LLC defines further
- * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
- * or .
- */
-
-#ifndef _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
-#define _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
-
-/**
- * @file smsdk_config.hpp
- * @brief Contains macros for configuring basic extension information.
- */
-
- #include "version.h" // SM_FULL_VERSION
-
-/* Basic information exposed publicly */
-#define SMEXT_CONF_NAME "Accelerator"
-#define SMEXT_CONF_DESCRIPTION "SRCDS Crash Handler"
-#define SMEXT_CONF_VERSION SM_FULL_VERSION
-#define SMEXT_CONF_AUTHOR "Asher \"asherkin\" Baker"
-#define SMEXT_CONF_URL "https://crash.limetech.org/"
-#define SMEXT_CONF_LOGTAG "CRASH"
-#define SMEXT_CONF_LICENSE "GPL"
-#define SMEXT_CONF_DATESTRING __DATE__
-
-/**
- * @brief Exposes plugin's main interface.
- */
-#define SMEXT_LINK(name) SDKExtension *g_pExtensionIface = name;
-
-/**
- * @brief Sets whether or not this plugin required Metamod.
- * NOTE: Uncomment to enable, comment to disable.
- */
-//#define SMEXT_CONF_METAMOD
-
-/** Enable interfaces you want to use here by uncommenting lines */
-//#define SMEXT_ENABLE_FORWARDSYS
-//#define SMEXT_ENABLE_HANDLESYS
-//#define SMEXT_ENABLE_PLAYERHELPERS
-//#define SMEXT_ENABLE_DBMANAGER
-#define SMEXT_ENABLE_GAMECONF
-//#define SMEXT_ENABLE_MEMUTILS
-#define SMEXT_ENABLE_GAMEHELPERS
-//#define SMEXT_ENABLE_TIMERSYS
-#define SMEXT_ENABLE_THREADER
-#define SMEXT_ENABLE_LIBSYS
-//#define SMEXT_ENABLE_MENUS
-//#define SMEXT_ENABLE_ADTFACTORY
-#define SMEXT_ENABLE_PLUGINSYS
-//#define SMEXT_ENABLE_ADMINSYS
-//#define SMEXT_ENABLE_TEXTPARSERS
-//#define SMEXT_ENABLE_USERMSGS
-//#define SMEXT_ENABLE_TRANSLATOR
-//#define SMEXT_ENABLE_NINVOKE
-#define SMEXT_ENABLE_ROOTCONSOLEMENU
-
-#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
+/**
+ * =============================================================================
+ * Accelerator Extension
+ * Copyright (C) 2011 Asher Baker (asherkin). All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ */
+
+#ifndef _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
+#define _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
+
+/**
+ * @file smsdk_config.hpp
+ * @brief Contains macros for configuring basic extension information.
+ */
+
+ #include "version.h" // SM_FULL_VERSION
+
+/* Basic information exposed publicly */
+#define SMEXT_CONF_NAME "Accelerator"
+#define SMEXT_CONF_DESCRIPTION "SRCDS Crash Handler"
+#define SMEXT_CONF_VERSION SM_FULL_VERSION
+#define SMEXT_CONF_AUTHOR "Asher \"asherkin\" Baker"
+#define SMEXT_CONF_URL "https://crash.limetech.org/"
+#define SMEXT_CONF_LOGTAG "CRASH"
+#define SMEXT_CONF_LICENSE "GPL"
+#define SMEXT_CONF_DATESTRING __DATE__
+
+/**
+ * @brief Exposes plugin's main interface.
+ */
+#define SMEXT_LINK(name) SDKExtension *g_pExtensionIface = name;
+
+/**
+ * @brief Sets whether or not this plugin required Metamod.
+ * NOTE: Uncomment to enable, comment to disable.
+ */
+//#define SMEXT_CONF_METAMOD
+
+/** Enable interfaces you want to use here by uncommenting lines */
+//#define SMEXT_ENABLE_FORWARDSYS
+//#define SMEXT_ENABLE_HANDLESYS
+//#define SMEXT_ENABLE_PLAYERHELPERS
+//#define SMEXT_ENABLE_DBMANAGER
+#define SMEXT_ENABLE_GAMECONF
+//#define SMEXT_ENABLE_MEMUTILS
+#define SMEXT_ENABLE_GAMEHELPERS
+//#define SMEXT_ENABLE_TIMERSYS
+#define SMEXT_ENABLE_THREADER
+#define SMEXT_ENABLE_LIBSYS
+//#define SMEXT_ENABLE_MENUS
+//#define SMEXT_ENABLE_ADTFACTORY
+#define SMEXT_ENABLE_PLUGINSYS
+//#define SMEXT_ENABLE_ADMINSYS
+//#define SMEXT_ENABLE_TEXTPARSERS
+//#define SMEXT_ENABLE_USERMSGS
+//#define SMEXT_ENABLE_TRANSLATOR
+//#define SMEXT_ENABLE_NINVOKE
+#define SMEXT_ENABLE_ROOTCONSOLEMENU
+
+#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
diff --git a/extension/version.h b/extension/version.h
index f9a2651..77ddd8d 100644
--- a/extension/version.h
+++ b/extension/version.h
@@ -1,27 +1,27 @@
-#ifndef _INCLUDE_VERSION_INFORMATION_H_
-#define _INCLUDE_VERSION_INFORMATION_H_
-
-/**
- * @file Contains version information.
- * @brief This file will redirect to an autogenerated version if being compiled via
- * the build scripts.
- */
-
-#if defined SM_GENERATED_BUILD
-#include "version_auto.h"
-#else
-
-#ifndef SM_GENERATED_BUILD
-#undef BINARY_NAME
-#define BINARY_NAME "accelerator.ext.dll\0"
-#endif
-
-#define SM_BUILD_TAG "-manual"
-#define SM_BUILD_UNIQUEID "[MANUAL BUILD]"
-#define SM_VERSION "2.3.3"
-#define SM_FULL_VERSION SM_VERSION SM_BUILD_TAG
-#define SM_FILE_VERSION 2,3,3,0
-
-#endif
-
-#endif /* _INCLUDE_VERSION_INFORMATION_H_ */
+#ifndef _INCLUDE_VERSION_INFORMATION_H_
+#define _INCLUDE_VERSION_INFORMATION_H_
+
+/**
+ * @file Contains version information.
+ * @brief This file will redirect to an autogenerated version if being compiled via
+ * the build scripts.
+ */
+
+#if defined SM_GENERATED_BUILD
+#include "version_auto.h"
+#else
+
+#ifndef SM_GENERATED_BUILD
+#undef BINARY_NAME
+#define BINARY_NAME "accelerator.ext.dll\0"
+#endif
+
+#define SM_BUILD_TAG "-manual"
+#define SM_BUILD_UNIQUEID "[MANUAL BUILD]"
+#define SM_VERSION "2.3.3"
+#define SM_FULL_VERSION SM_VERSION SM_BUILD_TAG
+#define SM_FILE_VERSION 2,3,3,0
+
+#endif
+
+#endif /* _INCLUDE_VERSION_INFORMATION_H_ */
diff --git a/extension/version.rc b/extension/version.rc
index 4a8a50e..2f458df 100644
--- a/extension/version.rc
+++ b/extension/version.rc
@@ -1,45 +1,45 @@
-#include "winres.h"
-
-#include
-
-#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
-LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
-#pragma code_page(1252)
-#endif
-
-#ifndef SM_GENERATED_BUILD
-#define BINARY_NAME "accelerator.ext.dll\0"
-#endif
-
-VS_VERSION_INFO VERSIONINFO
- FILEVERSION SM_FILE_VERSION
- PRODUCTVERSION SM_FILE_VERSION
- FILEFLAGSMASK 0x17L
-#ifdef _DEBUG
- FILEFLAGS 0x1L
-#else
- FILEFLAGS 0x0L
-#endif
- FILEOS 0x4L
- FILETYPE 0x2L
- FILESUBTYPE 0x0L
-BEGIN
- BLOCK "StringFileInfo"
- BEGIN
- BLOCK "040904b0"
- BEGIN
- VALUE "Comments", "Accelerator Extension"
- VALUE "FileDescription", "SourceMod Accelerator Extension"
- VALUE "FileVersion", SM_BUILD_UNIQUEID
- VALUE "InternalName", "Accelerator"
- VALUE "LegalCopyright", "Copyright (c) 2012, Asher Baker"
- VALUE "OriginalFilename", BINARY_NAME
- VALUE "ProductName", "Accelerator Extension"
- VALUE "ProductVersion", SM_FULL_VERSION
- END
- END
- BLOCK "VarFileInfo"
- BEGIN
- VALUE "Translation", 0x0409, 0x04B0
- END
-END
+#include "winres.h"
+
+#include
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif
+
+#ifndef SM_GENERATED_BUILD
+#define BINARY_NAME "accelerator.ext.dll\0"
+#endif
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION SM_FILE_VERSION
+ PRODUCTVERSION SM_FILE_VERSION
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", "Accelerator Extension"
+ VALUE "FileDescription", "SourceMod Accelerator Extension"
+ VALUE "FileVersion", SM_BUILD_UNIQUEID
+ VALUE "InternalName", "Accelerator"
+ VALUE "LegalCopyright", "Copyright (c) 2012, Asher Baker"
+ VALUE "OriginalFilename", BINARY_NAME
+ VALUE "ProductName", "Accelerator Extension"
+ VALUE "ProductVersion", SM_FULL_VERSION
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 0x04B0
+ END
+END