diff -r 0ff02108615a -r 44e8e76d4e53 core/GameDataFetcher.cpp --- a/core/GameDataFetcher.cpp Sun Dec 28 03:15:15 2008 -0500 +++ b/core/GameDataFetcher.cpp Sun Dec 28 03:23:45 2008 -0500 @@ -1,33 +1,33 @@ /** -* vim: set ts=4 : -* ============================================================================= -* SourceMod -* Copyright (C) 2004-2008 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$ -*/ + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 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 "GameDataFetcher.h" #include "bitbuf.h" @@ -42,8 +42,9 @@ #include #include -#define INVALID_SOCKET -1 -#define closesocket close +#define INVALID_SOCKET -1 +#define closesocket close +#define WSAGetLastError() errno #endif #include "sh_vector.h" @@ -63,21 +64,52 @@ #include "TimerSys.h" #include "compat_wrappers.h" #include "sm_stringutil.h" +#include "md5.h" +#include "frame_hooks.h" #define QUERY_MAX_LENGTH 1024 -BuildMD5ableBuffer g_MD5Builder; -FetcherThread g_FetchThread; +static BuildMD5ableBuffer g_MD5Builder; +static FetcherThread g_FetchThread; -FILE *logfile = NULL; +static FILE *logfile = NULL; bool g_disableGameDataUpdate = false; + +/** + * Note on this. If we issue a reload and changelevel, my srcds.exe will emit + * Assertion Failed: !m_bServiceStarted + * on quit. This seems like a non-issue, because before we just terminated the + * server anyway. If anyone notices and files a bug, we can look into it further. + */ bool g_restartAfterUpdate = false; -int g_serverPort = 6500; -char g_serverAddress[100] = "hayate.alliedmods.net"; +static bool was_level_started = false; +static int g_serverPort = 6500; +static char g_serverAddress[100] = "smupdate.alliedmods.net"; -void FetcherThread::RunThread( IThreadHandle *pHandle ) +static void _ForceRestart(void *data) +{ + char cmd[300]; + g_Logger.LogMessage("Automatically restarting SourceMod after a successful gamedata update."); + UTIL_Format(cmd, sizeof(cmd), "meta unload %d\n", g_PLID); + engine->ServerCommand(cmd); + UTIL_Format(cmd, sizeof(cmd), "changelevel \"%s\"\n", STRING(gpGlobals->mapname)); + engine->ServerCommand(cmd); + UTIL_Format(cmd, sizeof(cmd), "echo SourceMod restarted after gamedata update.\n"); + engine->ServerCommand(cmd); +} + +static void ForceRestart() +{ + FrameAction action; + + action.action = _ForceRestart; + action.data = NULL; + AddFrameAction(action); +} + +void FetcherThread::RunThread(IThreadHandle *pHandle) { char lock_path[PLATFORM_MAX_PATH]; g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp"); @@ -85,8 +117,6 @@ g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp/gamedata.lock"); - g_Logger.LogMessage("Starting Gamedata update fetcher... please report problems to bugs.alliedmods.net"); - char log_path[PLATFORM_MAX_PATH]; g_SourceMod.BuildPath(Path_SM, log_path, sizeof(log_path), "logs/gamedata"); @@ -102,7 +132,7 @@ if (!logfile) { - g_Logger.LogError("Failed to create GameData log file"); + /* :( */ return; } @@ -120,19 +150,16 @@ if (len == 0) { - g_Logger.LogToOpenFile(logfile, "Query Writing failed"); - + g_Logger.LogToFileOnly(logfile, "Could not build gamedata query!"); fclose(logfile); unlink(lock_path); return; } + /* We check this late so we have the MD5 sums available. This may change in the future. */ if (g_disableGameDataUpdate) { -#ifdef DEBUG - g_Logger.LogMessage("Skipping GameData Query due to DisableAutoUpdate being set to true"); -#endif - + g_Logger.LogToFileOnly(logfile, "Skipping gamedata fetcher (DisableAutoUpdate set)"); fclose(logfile); unlink(lock_path); return; @@ -150,13 +177,15 @@ int sent = SendData(socketDescriptor, query, len); -#ifdef DEBUG - g_Logger.LogToOpenFile(logfile, "Sent Query!"); -#endif +IF_DEBUG_SPEW + g_Logger.LogToFileOnly(logfile, "Sent gamedata query"); +ENDIF_DEBUG_SPEW if (sent == 0) { - g_Logger.LogToOpenFile(logfile, "Failed to send gamedata query data to remote host"); +IF_DEBUG_SPEW + g_Logger.LogToFileOnly(logfile, "Failed to send gamedata query data to remote host"); +ENDIF_DEBUG_SPEW closesocket(socketDescriptor); fclose(logfile); @@ -172,12 +201,41 @@ unlink(lock_path); } -void FetcherThread::OnTerminate( IThreadHandle *pHandle, bool cancel ) +void FetcherThread::OnTerminate(IThreadHandle *pHandle, bool cancel) { g_blockGameDataLoad = false; + + if (cancel) + { + return; + } + + if (wasSuccess) + { + HandleUpdateStatus(updateStatus, build); + + if (needsRestart) + { + if (g_restartAfterUpdate) + { + if (was_level_started) + { + ForceRestart(); + } + } + else + { + g_Logger.LogMessage("Your gamedata files have been updated, please restart your server."); + } + } + } + else if (!g_disableGameDataUpdate) + { + g_Logger.LogError("An error occurred in the gamedata fetcher, see your gamedata log files for more information."); + } } -int FetcherThread::BuildGameDataQuery( char *buffer, int maxlen ) +int FetcherThread::BuildGameDataQuery(char *buffer, int maxlen) { char gamedata_path[PLATFORM_MAX_PATH]; g_SourceMod.BuildPath(Path_SM, gamedata_path, sizeof(gamedata_path), "gamedata"); @@ -206,45 +264,42 @@ { const char *name = dir->GetEntryName(); size_t len = strlen(name); - if (len >= 4 - && strcmp(&name[len-4], ".txt") == 0) + if (len >= 4 && strcmp(&name[len-4], ".txt") == 0) { + MD5 md5; + SMCError err; + SMCStates states; + unsigned char raw[16]; char file[PLATFORM_MAX_PATH]; g_LibSys.PathFormat(file, sizeof(file), "%s/%s", gamedata_path, name); - SMCStates states; - if (g_TextParser.ParseFile_SMC(file, &g_MD5Builder, &states) == SMCError_Okay) + g_MD5Builder.checksum = &md5; + if ((err = g_TextParser.ParseFile_SMC(file, &g_MD5Builder, &states)) == SMCError_Okay) { - unsigned char *md5 = g_MD5Builder.GetMD5(); - if (md5 != NULL) - { - (uint8_t)buffer[10]++; //Increment the file counter - Writer.WriteBytes(md5, 16); - - g_Logger.LogToOpenFile(logfile, "%s - \"%s\"", file, g_MD5Builder.GetMD5String()); + md5.raw_digest(raw); + (uint8_t)buffer[10]++; //Increment the file counter + Writer.WriteBytes(raw, 16); - FileData *data = new FileData(); - data->filename = new SourceHook::String(file); - memcpy(data->checksum, g_MD5Builder.GetMD5String(), 33); - filenames.push_back(data); - } - else - { -#ifdef DEBUG - g_Logger.LogToOpenFile(logfile, "%s no md5?", file); -#endif - } + FileData *data = new FileData(); + data->filename = new SourceHook::String(file); + md5.hex_digest(data->checksum); + filenames.push_back(data); + +IF_DEBUG_SPEW + g_Logger.LogToFileOnly(logfile, "Parsed file: %s as %s", file, data->checksum); +ENDIF_DEBUG_SPEW + } else { -#ifdef DEBUG - g_Logger.LogToOpenFile(logfile, "%s failed!", file); -#endif +IF_DEBUG_SPEW + const char *error = g_TextParser.GetSMCErrorString(err); + g_Logger.LogToFileOnly(logfile, "Parsing of file %s failed: %s", file, error); +ENDIF_DEBUG_SPEW } } } - dir->NextEntry(); } @@ -253,17 +308,16 @@ int FetcherThread::ConnectSocket() { - struct protoent *ptrp; - ptrp = getprotobyname("tcp"); - -#ifdef WIN32 +#if defined PLATFORM_WINDOWS WSADATA wsaData; WSAStartup(0x0101, &wsaData); #endif - if (ptrp == NULL) + struct protoent *ptrp; + + if ((ptrp = getprotobyname("tcp")) == NULL) { - g_Logger.LogToOpenFile(logfile, "Failed to find TCP"); + g_Logger.LogToFileOnly(logfile, "Error: Failed to find TCP protocol"); return INVALID_SOCKET; } @@ -271,12 +325,9 @@ if (socketDescriptor == INVALID_SOCKET) { - //bugger aye? -#ifdef WIN32 - g_Logger.LogToOpenFile(logfile, "Failed to create a new socket - Error %i", WSAGetLastError()); -#else - g_Logger.LogToOpenFile(logfile, "Failed to create a new socket - Error %i", errno); -#endif + char error[255]; + g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error)); + g_Logger.LogToFileOnly(logfile, "Error: Failed to create socket: %s", error); closesocket(socketDescriptor); return INVALID_SOCKET; } @@ -293,7 +344,7 @@ { if ((local_addr.sin_addr.s_addr = inet_addr(g_serverAddress)) == INADDR_NONE) { - g_Logger.LogToOpenFile(logfile, "Couldn't locate address"); + g_Logger.LogToFileOnly(logfile, "Couldn't locate address: %s", g_serverAddress); closesocket(socketDescriptor); return INVALID_SOCKET; } @@ -305,11 +356,9 @@ if (connect(socketDescriptor, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0) { -#ifdef WIN32 - g_Logger.LogToOpenFile(logfile, "Couldn't connect - Error %i", WSAGetLastError()); -#else - g_Logger.LogToOpenFile(logfile, "Couldn't connect - Error %i", errno); -#endif + char error[255]; + g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error)); + g_Logger.LogToFileOnly(logfile, "Couldn't connect to %s: %s", g_serverAddress, error); closesocket(socketDescriptor); return INVALID_SOCKET; } @@ -320,58 +369,59 @@ void FetcherThread::ProcessGameDataQuery(int socketDescriptor) { char buffer[50]; -#ifdef DEBUG - g_Logger.LogToOpenFile(logfile, "Waiting for reply!"); -#endif + +IF_DEBUG_SPEW + g_Logger.LogToFileOnly(logfile, "Waiting for reply!"); +ENDIF_DEBUG_SPEW //Read in the header bytes int returnLen = RecvData(socketDescriptor, buffer, 12); -#ifdef DEBUG - g_Logger.LogToOpenFile(logfile, "Recv Completed"); -#endif if (returnLen == 0) { -#ifdef DEBUG - g_Logger.LogToOpenFile(logfile, ",but it failed."); -#endif - /* Timeout or fail? */ + char error[255]; + g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error)); + g_Logger.LogToFileOnly(logfile, "Did not receive reply: %s", error); return; } -#ifdef DEBUG - g_Logger.LogToOpenFile(logfile, "Received Header!"); -#endif +IF_DEBUG_SPEW + g_Logger.LogToFileOnly(logfile, "Received Header!"); +ENDIF_DEBUG_SPEW bf_read Reader = bf_read("GameDataQuery", buffer, 12); if (Reader.ReadByte() != 'A' || Reader.ReadByte() != 'G') { - g_Logger.LogToOpenFile(logfile, "Unknown Query Response"); +IF_DEBUG_SPEW + g_Logger.LogToFileOnly(logfile, "Unknown Query Response"); +ENDIF_DEBUG_SPEW return; } - UpdateStatus updateStatus = (UpdateStatus)Reader.ReadByte(); - - short build[4] = {0,0,0,0}; + updateStatus = (UpdateStatus)Reader.ReadByte(); build[0] = Reader.ReadShort(); build[1] = Reader.ReadShort(); build[2] = Reader.ReadShort(); build[3] = Reader.ReadShort(); -#ifdef DEBUG - g_Logger.LogToOpenFile(logfile, "Update Status: %i - Latest %i.%i.%i.%i", updateStatus, build[0], build[1], build[2], build[3]); -#endif - - HandleUpdateStatus(updateStatus, build); +IF_DEBUG_SPEW + g_Logger.LogToFileOnly(logfile, + "Update Status: %i - Latest %i.%i.%i.%i", + updateStatus, + build[0], + build[1], + build[2], + build[3]); +ENDIF_DEBUG_SPEW int changedFiles = Reader.ReadByte(); -#ifdef DEBUG - g_Logger.LogToOpenFile(logfile, "Files to download: %i", changedFiles); -#endif +IF_DEBUG_SPEW + g_Logger.LogToFileOnly(logfile, "Files to download: %i", changedFiles); +ENDIF_DEBUG_SPEW for (int i=0; iCreateMem(tempLen+1, &memPtr); @@ -399,9 +449,9 @@ //Read the contents of our file into the memtable returnLen = RecvData(socketDescriptor, (char *)memPtr, tempLen); -#ifdef DEBUG - g_Logger.LogToOpenFile(logfile, "Recieved %i bytes", returnLen); -#endif +IF_DEBUG_SPEW + g_Logger.LogToFileOnly(logfile, "Received %i bytes", returnLen); +ENDIF_DEBUG_SPEW if (returnLen == 0) { @@ -412,7 +462,7 @@ ((unsigned char *)memPtr)[tempLen] = '\0'; FileData *data = filenames.at(index); - const char* filename; + const char *filename; if (data != NULL) { filename = data->filename->c_str(); @@ -421,12 +471,12 @@ if (fp) { - fprintf(fp, (const char *)memPtr); + fprintf(fp, "%s", (const char *)memPtr); fclose(fp); } else { - g_Logger.LogToOpenFile(logfile, "Failed to open file \"%s\"", filename); + g_Logger.LogToFileOnly(logfile, "Failed to open file \"%s\" for writing", filename); } } else @@ -436,21 +486,18 @@ memtable->Reset(); -#ifdef DEBUG - g_Logger.LogToOpenFile(logfile, "Updated File %s", filename); -#endif + g_Logger.LogToFileOnly(logfile, "Updated file: %s", filename); } -#ifdef DEBUG - g_Logger.LogToOpenFile(logfile, "File Downloads Completed!"); -#endif +IF_DEBUG_SPEW + g_Logger.LogToFileOnly(logfile, "File Downloads Completed!"); +ENDIF_DEBUG_SPEW - bool needsRestart = false; + needsRestart = false; if (changedFiles > 0) { needsRestart = true; - g_Logger.LogMessage("New GameData Files have been downloaded to your gamedata directory. Please restart your server for these to take effect"); } //Read changed file count @@ -458,8 +505,9 @@ if (returnLen == 0) { - /* Timeout or fail? */ - g_Logger.LogToOpenFile(logfile, "Failed to receive unknown count"); + char error[255]; + g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error)); + g_Logger.LogToFileOnly(logfile, "Did not receive count reply: %s", error); return; } @@ -467,28 +515,27 @@ changedFiles = Reader.ReadByte(); - if (changedFiles == 0) + if (changedFiles < 1) { -#ifdef DEBUG - g_Logger.LogToOpenFile(logfile, "No unknown files. We're all done"); -#endif +IF_DEBUG_SPEW + g_Logger.LogToFileOnly(logfile, "No unknown files. We're all done"); +ENDIF_DEBUG_SPEW return; } char *changedFileIndexes = new char[changedFiles]; -#ifdef DEBUG - g_Logger.LogToOpenFile(logfile, "%i Files were unknown", changedFiles); -#endif +IF_DEBUG_SPEW + g_Logger.LogToFileOnly(logfile, "%i files were unknown", changedFiles); +ENDIF_DEBUG_SPEW returnLen = RecvData(socketDescriptor, changedFileIndexes, changedFiles); if (returnLen == 0) { - /* Timeout or fail? */ -#ifdef DEBUG - g_Logger.LogToOpenFile(logfile, "Failed to receive unknown list"); -#endif + char error[255]; + g_LibSys.GetPlatformErrorEx(WSAGetLastError(), error, sizeof(error)); + g_Logger.LogToFileOnly(logfile, "Did not receive list reply: %s", error); return; } @@ -511,21 +558,17 @@ } g_LibSys.GetFileFromPath(fileName, sizeof(fileName), pathname); -#ifdef DEBUG - g_Logger.LogToOpenFile(logfile, "Unknown File %i : %s", index, fileName); -#endif +IF_DEBUG_SPEW + g_Logger.LogToFileOnly(logfile, "Unknown File %i : %s", index, fileName); +ENDIF_DEBUG_SPEW } delete [] changedFileIndexes; - if (needsRestart && g_restartAfterUpdate) - { - g_Logger.LogMessage("Automatically restarting server after a successful gamedata update!"); - engine->ServerCommand("quit\n"); - } + wasSuccess = true; } -int FetcherThread::RecvData( int socketDescriptor, char *buffer, int len ) +int FetcherThread::RecvData(int socketDescriptor, char *buffer, int len) { fd_set fds; struct timeval tv; @@ -564,7 +607,7 @@ return bytesReceivedTotal; } -int FetcherThread::SendData( int socketDescriptor, char *buffer, int len ) +int FetcherThread::SendData(int socketDescriptor, char *buffer, int len) { fd_set fds; struct timeval tv; @@ -599,7 +642,7 @@ return sentBytesTotal; } -void FetcherThread::HandleUpdateStatus( UpdateStatus status, short version[4] ) +void FetcherThread::HandleUpdateStatus(UpdateStatus status, short version[4]) { switch (status) { @@ -611,14 +654,12 @@ case Update_NewBuild: { - g_Logger.LogMessage("SourceMod Update: A new Mercurial build is available from sourcemod.net"); - g_Logger.LogMessage("Current Version: %i.%i.%i.%i Available: %i.%i.%i.%i", version[0], version[1], version[2], version[3], version[0], version[1], version[2], version[3]); break; } case Update_MinorAvailable: { - g_Logger.LogMessage("SourceMod Update: An incremental minor release of SourceMod is now available from sourcemod.net"); + g_Logger.LogMessage("SourceMod Update: A new release of SourceMod is now available from sourcemod.net"); g_Logger.LogMessage("Current Version: %i.%i.%i Available: %i.%i.%i", version[0], version[1], version[2], version[0], version[1], version[2]); break; } @@ -632,14 +673,15 @@ case Update_CriticalAvailable: { - g_Logger.LogError("SourceMod Update: A new critical release of SourceMod is now available from sourcemod.net. It is strongly recommended that you update"); - g_Logger.LogMessage("Current Version: %i.%i.%i.%i Available: %i.%i.%i.%i", version[0], version[1], version[2], version[3], version[0], version[1], version[2], version[3]); + g_Logger.LogError("SourceMod Update: A critical SourceMod release is available from sourcemod.net. It is strongly recommended that you update!"); + g_Logger.LogMessage("Current Version: %i.%i.%i Available: %i.%i.%i", version[0], version[1], version[2], version[0], version[1], version[2]); break; } } } bool g_blockGameDataLoad = false; +static IThreadHandle *fetch_thread_hndl; class InitFetch : public SMGlobalClass { @@ -658,7 +700,30 @@ ThreadParams fetchThreadParams = ThreadParams(); fetchThreadParams.prio = ThreadPrio_Low; - g_pThreader->MakeThread(&g_FetchThread, &fetchThreadParams); + fetch_thread_hndl = g_pThreader->MakeThread(&g_FetchThread, &fetchThreadParams); + } + + void OnSourceModShutdown() + { + fetch_thread_hndl->WaitForThread(); + fetch_thread_hndl->DestroyThis(); + } + + void OnSourceModLevelActivated() + { + was_level_started = true; + + if (g_restartAfterUpdate && + g_FetchThread.wasSuccess && + g_FetchThread.needsRestart) + { + ForceRestart(); + } + } + + void OnSourceModLevelEnd() + { + was_level_started = false; } ConfigResult OnSourceModConfigChanged(const char *key, @@ -667,14 +732,14 @@ char *error, size_t maxlength) { - if (strcmp(key, "DisableAutoUpdate") == 0) + if (strcasecmp(key, "DisableAutoUpdate") == 0) { - if (strcmp(value, "yes") == 0) + if (strcasecmp(value, "yes") == 0) { g_disableGameDataUpdate = true; return ConfigResult_Accept; } - else if (strcmp(value, "no") == 0) + else if (strcasecmp(value, "no") == 0) { g_disableGameDataUpdate = false; return ConfigResult_Accept; @@ -683,14 +748,14 @@ return ConfigResult_Reject; } - if (strcmp(key, "ForceRestartAfterUpdate") == 0) + if (strcasecmp(key, "ForceRestartAfterUpdate") == 0) { - if (strcmp(value, "yes") == 0) + if (strcasecmp(value, "yes") == 0) { g_restartAfterUpdate = true; return ConfigResult_Accept; } - else if (strcmp(value, "no") == 0) + else if (strcasecmp(value, "no") == 0) { g_restartAfterUpdate = false; return ConfigResult_Accept; @@ -699,14 +764,14 @@ return ConfigResult_Reject; } - if (strcmp(key, "AutoUpdateServer") == 0) + if (strcasecmp(key, "AutoUpdateServer") == 0) { UTIL_Format(g_serverAddress, sizeof(g_serverAddress), "%s", value); return ConfigResult_Accept; } - if (strcmp(key, "AutoUpdatePort") == 0) + if (strcasecmp(key, "AutoUpdatePort") == 0) { int port = atoi(value); @@ -724,6 +789,55 @@ } } g_InitFetch; +BuildMD5ableBuffer::BuildMD5ableBuffer() +{ + stringTable = new BaseStringTable(2048); +} + +BuildMD5ableBuffer::~BuildMD5ableBuffer() +{ + delete stringTable; +} + +void BuildMD5ableBuffer::ReadSMC_ParseStart() +{ + stringTable->Reset(); +} + +SMCResult BuildMD5ableBuffer::ReadSMC_KeyValue(const SMCStates *states, + const char *key, + const char *value) +{ + stringTable->AddString(key); + stringTable->AddString(value); + + return SMCResult_Continue; +} + +SMCResult BuildMD5ableBuffer::ReadSMC_NewSection(const SMCStates *states, const char *name) +{ + stringTable->AddString(name); + + return SMCResult_Continue; +} + +void BuildMD5ableBuffer::ReadSMC_ParseEnd(bool halted, bool failed) +{ + if (halted || failed) + { + return; + } + + void *data = stringTable->GetMemTable()->GetAddress(0); + + if (data != NULL) + { + checksum->update((unsigned char *)data, stringTable->GetMemTable()->GetActualMemUsed()); + } + + checksum->finalize(); +} + CON_COMMAND(sm_gamedata_md5, "Checks the MD5 sum for a given gamedata file") { #if SOURCE_ENGINE == SE_EPISODEONE @@ -766,3 +880,26 @@ g_SMAPI->ConPrint("File not found!\n"); } + +FetcherThread::~FetcherThread() +{ + SourceHook::CVector::iterator iter = filenames.begin(); + + FileData *curData; + + while (iter != filenames.end()) + { + curData = (*iter); + delete curData->filename; + delete curData; + iter = filenames.erase(iter); + } +} + +FetcherThread::FetcherThread() +{ + memtable = new BaseMemTable(4096); + wasSuccess = false; + needsRestart = false; +} +