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