And the most important piece: Import of experimental gamedata autoupdater. Also new debug command "sm_gamedata_md5 <filename>"
--HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%402323
This commit is contained in:
parent
f3f46fc126
commit
a513a2a6f8
@ -88,4 +88,18 @@
|
||||
* "off" - Translate using default server's language
|
||||
*/
|
||||
"AllowClLanguageVar" "On"
|
||||
|
||||
/**
|
||||
* Enables or Disables SourceMod's automatic gamedata updating.
|
||||
*
|
||||
* The default value is "no". A value of "yes" will block the Auto Updater.
|
||||
*/
|
||||
"DisableAutoUpdate" "no"
|
||||
|
||||
/**
|
||||
* Enables or disables automatic restarting of the server after a successful update.
|
||||
*
|
||||
* The default value is "no". A value of "yes" will let the server automatically restart.
|
||||
*/
|
||||
"ForceRestartAfterUpdate" "no"
|
||||
}
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "LibrarySys.h"
|
||||
#include "HandleSys.h"
|
||||
#include "sm_crc32.h"
|
||||
#include "GameDataFetcher.h"
|
||||
|
||||
#if defined PLATFORM_LINUX
|
||||
#include <dlfcn.h>
|
||||
@ -630,6 +631,13 @@ void GameConfigManager::OnSourceModAllShutdown()
|
||||
|
||||
bool GameConfigManager::LoadGameConfigFile(const char *file, IGameConfig **_pConfig, char *error, size_t maxlength)
|
||||
{
|
||||
/* A crash was detected during last load - We block the gamedata loading so it hopefully won't happen again */
|
||||
if (g_blockGameDataLoad)
|
||||
{
|
||||
UTIL_Format(error, maxlength, "GameData loaded blocked due to detected crash");
|
||||
return false;
|
||||
}
|
||||
|
||||
CGameConfig *pConfig;
|
||||
|
||||
if (sm_trie_retrieve(m_pLookup, file, (void **)&pConfig))
|
||||
|
682
core/GameDataFetcher.cpp
Normal file
682
core/GameDataFetcher.cpp
Normal file
@ -0,0 +1,682 @@
|
||||
/**
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#include "GameDataFetcher.h"
|
||||
#include "bitbuf.h"
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define INVALID_SOCKET -1
|
||||
#define closesocket close
|
||||
#endif
|
||||
|
||||
#include "sh_vector.h"
|
||||
#include "sh_string.h"
|
||||
#include "sm_version.h"
|
||||
|
||||
#ifdef ORANGEBOX_BUILD
|
||||
#include "convar_sm_ob.h"
|
||||
#else
|
||||
#include "convar_sm.h"
|
||||
#endif
|
||||
|
||||
#include "sourcemm_api.h"
|
||||
#include "time.h"
|
||||
#include "TimerSys.h"
|
||||
|
||||
#define QUERY_MAX_LENGTH 1024
|
||||
|
||||
BuildMD5ableBuffer g_MD5Builder;
|
||||
FetcherThread g_FetchThread;
|
||||
|
||||
FILE *logfile = NULL;
|
||||
|
||||
bool g_disableGameDataUpdate = false;
|
||||
bool g_restartAfterUpdate = false;
|
||||
|
||||
void FetcherThread::RunThread( IThreadHandle *pHandle )
|
||||
{
|
||||
char lock_path[PLATFORM_MAX_PATH];
|
||||
g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp");
|
||||
g_LibSys.CreateFolder(lock_path);
|
||||
|
||||
g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp/gamedata.lock");
|
||||
|
||||
g_Logger.LogMessage("Starting experimental 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");
|
||||
|
||||
g_LibSys.CreateFolder(log_path);
|
||||
|
||||
time_t t;
|
||||
GetAdjustedTime(&t);
|
||||
tm *curtime = localtime(&t);
|
||||
|
||||
g_SourceMod.BuildPath(Path_SM, log_path, sizeof(log_path), "logs/gamedata/L%02d%02d.log", curtime->tm_mon + 1, curtime->tm_mday);
|
||||
|
||||
logfile = fopen(log_path, "a");
|
||||
|
||||
if (!logfile)
|
||||
{
|
||||
g_Logger.LogError("Failed to create GameData log file");
|
||||
return;
|
||||
}
|
||||
|
||||
//Create a blank lock file
|
||||
FILE *fp = fopen(lock_path, "w");
|
||||
if (fp)
|
||||
{
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/* Create a new socket for this connection */
|
||||
int socketDescriptor = ConnectSocket();
|
||||
|
||||
if (socketDescriptor == INVALID_SOCKET)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char query[QUERY_MAX_LENGTH];
|
||||
|
||||
/* Check for updated gamedata files */
|
||||
int len = BuildGameDataQuery(query, QUERY_MAX_LENGTH);
|
||||
|
||||
if (len == 0)
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "Query Writing failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_disableGameDataUpdate)
|
||||
{
|
||||
g_Logger.LogMessage("Skipping GameData Query due to DisableAutoUpdate being set to true");
|
||||
return;
|
||||
}
|
||||
|
||||
int sent = NonBlockingSend(socketDescriptor, query, len);
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Sent Query!");
|
||||
|
||||
if (sent == 0)
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "Failed to send data");
|
||||
closesocket(socketDescriptor);
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessGameDataQuery(socketDescriptor);
|
||||
|
||||
/* And we're done! */
|
||||
closesocket(socketDescriptor);
|
||||
|
||||
fclose(logfile);
|
||||
|
||||
// Delete our lock
|
||||
unlink(lock_path);
|
||||
}
|
||||
|
||||
void FetcherThread::OnTerminate( IThreadHandle *pHandle, bool cancel )
|
||||
{
|
||||
//delete this;
|
||||
}
|
||||
|
||||
int FetcherThread::BuildGameDataQuery( char *buffer, int maxlen )
|
||||
{
|
||||
char gamedata_path[PLATFORM_MAX_PATH];
|
||||
g_SourceMod.BuildPath(Path_SM, gamedata_path, sizeof(gamedata_path), "gamedata");
|
||||
|
||||
IDirectory *dir = g_LibSys.OpenDirectory(gamedata_path);
|
||||
|
||||
if (dir == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bf_write Writer = bf_write("GameDataQuery", buffer, maxlen);
|
||||
|
||||
Writer.WriteByte('A'); //Generic Header char
|
||||
Writer.WriteByte('G'); //G for gamedata query, or green, like my hat.
|
||||
|
||||
short build[4] = { SVN_FILE_VERSION };
|
||||
|
||||
Writer.WriteBytes(&build[0], 8);
|
||||
|
||||
Writer.WriteByte(0); // Initialize the file counter - Index 10
|
||||
|
||||
while (dir->MoreFiles())
|
||||
{
|
||||
if (dir->IsEntryFile())
|
||||
{
|
||||
const char *name = dir->GetEntryName();
|
||||
size_t len = strlen(name);
|
||||
if (len >= 4
|
||||
&& strcmp(&name[len-4], ".txt") == 0)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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());
|
||||
|
||||
FileData *data = new FileData();
|
||||
data->filename = new SourceHook::String(file);
|
||||
memcpy(data->checksum, g_MD5Builder.GetMD5String(), 33);
|
||||
filenames.push_back(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "%s no md5?", file);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "%s failed!", file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dir->NextEntry();
|
||||
}
|
||||
|
||||
return Writer.GetNumBytesWritten();
|
||||
}
|
||||
|
||||
int FetcherThread::ConnectSocket()
|
||||
{
|
||||
struct protoent *ptrp;
|
||||
ptrp = getprotobyname("tcp");
|
||||
|
||||
#ifdef WIN32
|
||||
WSADATA wsaData;
|
||||
WSAStartup(0x0101, &wsaData);
|
||||
#endif
|
||||
|
||||
if (ptrp == NULL)
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "Failed to find TCP");
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
int socketDescriptor = socket(AF_INET, SOCK_STREAM, ptrp->p_proto);
|
||||
|
||||
if (socketDescriptor == INVALID_SOCKET)
|
||||
{
|
||||
//bugger aye?
|
||||
g_Logger.LogToOpenFile(logfile, "Failed to create a new socket");
|
||||
closesocket(socketDescriptor);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
struct hostent *he;
|
||||
struct sockaddr_in local_addr;
|
||||
|
||||
local_addr.sin_family = AF_INET;
|
||||
local_addr.sin_port = htons((u_short)6500);
|
||||
|
||||
he = gethostbyname("hayate.alliedmods.net");
|
||||
|
||||
if (!he)
|
||||
{
|
||||
if ((local_addr.sin_addr.s_addr = inet_addr("hayate.alliedmods.net")) == INADDR_NONE)
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "Couldnt locate address");
|
||||
closesocket(socketDescriptor);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(&local_addr.sin_addr, (struct in_addr *)he->h_addr, he->h_length);
|
||||
}
|
||||
|
||||
if (connect(socketDescriptor, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0)
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "Couldn't connect");
|
||||
closesocket(socketDescriptor);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
return socketDescriptor;
|
||||
}
|
||||
|
||||
void FetcherThread::ProcessGameDataQuery(int socketDescriptor)
|
||||
{
|
||||
char buffer[50];
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Waiting for reply!");
|
||||
|
||||
//Read in the header bytes
|
||||
int returnLen = NonBlockingRecv(socketDescriptor, buffer, 12);
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Recv Completed");
|
||||
|
||||
if (returnLen == 0)
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, ",but it failed.");
|
||||
/* Timeout or fail? */
|
||||
return;
|
||||
}
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Received Header!");
|
||||
|
||||
bf_read Reader = bf_read("GameDataQuery", buffer, 12);
|
||||
|
||||
if (Reader.ReadByte() != 'A' || Reader.ReadByte() != 'G')
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "Invalid Query to handle");
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateStatus updateStatus = (UpdateStatus)Reader.ReadByte();
|
||||
|
||||
short build[4] = {0,0,0,0};
|
||||
|
||||
build[0] = Reader.ReadShort();
|
||||
build[1] = Reader.ReadShort();
|
||||
build[2] = Reader.ReadShort();
|
||||
build[3] = Reader.ReadShort();
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Update Status: %i - Latest %i.%i.%i.%i", updateStatus, build[0], build[1], build[2], build[3]);
|
||||
|
||||
HandleUpdateStatus(updateStatus, build);
|
||||
|
||||
int changedFiles = Reader.ReadByte();
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Files to download: %i", changedFiles);
|
||||
|
||||
for (int i=0; i<changedFiles; i++)
|
||||
{
|
||||
//Read in the file index and byte count
|
||||
returnLen = NonBlockingRecv(socketDescriptor, buffer, 5);
|
||||
|
||||
if (returnLen == 0)
|
||||
{
|
||||
/* Timeout or fail? */
|
||||
return;
|
||||
}
|
||||
|
||||
Reader.StartReading(buffer, 5);
|
||||
|
||||
int index = Reader.ReadByte();
|
||||
int tempLen = Reader.ReadUBitLong(32);
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "File index %i and length %i", index, tempLen);
|
||||
|
||||
void *memPtr;
|
||||
memtable->CreateMem(tempLen+1, &memPtr);
|
||||
|
||||
//Read the contents of our file into the memtable
|
||||
returnLen = NonBlockingRecv(socketDescriptor, (char *)memPtr, tempLen);
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Recieved %i bytes", returnLen);
|
||||
|
||||
if (returnLen == 0)
|
||||
{
|
||||
/* Timeout or fail? */
|
||||
return;
|
||||
}
|
||||
|
||||
((unsigned char *)memPtr)[tempLen] = '\0';
|
||||
|
||||
FileData *data = filenames.at(index);
|
||||
const char* filename;
|
||||
if (data != NULL)
|
||||
{
|
||||
filename = data->filename->c_str();
|
||||
|
||||
FILE *fp = fopen(filename, "w");
|
||||
|
||||
if (fp)
|
||||
{
|
||||
fprintf(fp, (const char *)memPtr);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = "";
|
||||
}
|
||||
|
||||
memtable->Reset();
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Updated File %s", filename);
|
||||
}
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "File Downloads Completed!");
|
||||
|
||||
bool 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
|
||||
returnLen = NonBlockingRecv(socketDescriptor, buffer, 1);
|
||||
|
||||
if (returnLen == 0)
|
||||
{
|
||||
/* Timeout or fail? */
|
||||
g_Logger.LogToOpenFile(logfile, "Failed to receive unknown count");
|
||||
return;
|
||||
}
|
||||
|
||||
Reader.StartReading(buffer, 1);
|
||||
|
||||
changedFiles = Reader.ReadByte();
|
||||
|
||||
if (changedFiles == 0)
|
||||
{
|
||||
g_Logger.LogToOpenFile(logfile, "No unknown files. We're all done");
|
||||
return;
|
||||
}
|
||||
|
||||
char *changedFileIndexes = new char[changedFiles];
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "%i Files were unknown", changedFiles);
|
||||
|
||||
returnLen = NonBlockingRecv(socketDescriptor, changedFileIndexes, changedFiles);
|
||||
|
||||
if (returnLen == 0)
|
||||
{
|
||||
/* Timeout or fail? */
|
||||
g_Logger.LogToOpenFile(logfile, "Failed to receive unknown list");
|
||||
return;
|
||||
}
|
||||
|
||||
Reader.StartReading(changedFileIndexes, changedFiles);
|
||||
|
||||
for (int i=0; i<changedFiles; i++)
|
||||
{
|
||||
int index = Reader.ReadByte();
|
||||
char fileName[30];
|
||||
|
||||
FileData *data = filenames.at(index);
|
||||
const char* pathname;
|
||||
if (data != NULL)
|
||||
{
|
||||
pathname = data->filename->c_str();
|
||||
}
|
||||
else
|
||||
{
|
||||
pathname = "";
|
||||
}
|
||||
|
||||
g_LibSys.GetFileFromPath(fileName, sizeof(fileName), pathname);
|
||||
|
||||
g_Logger.LogToOpenFile(logfile, "Unknown File %i : %s", index, fileName);
|
||||
}
|
||||
|
||||
delete [] changedFileIndexes;
|
||||
|
||||
if (needsRestart && g_restartAfterUpdate)
|
||||
{
|
||||
g_Logger.LogMessage("Automatically restarting server after a successful gamedata update!");
|
||||
engine->ServerCommand("quit\n");
|
||||
}
|
||||
}
|
||||
|
||||
int FetcherThread::NonBlockingRecv( int socketDescriptor, char *buffer, int len )
|
||||
{
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
|
||||
/* Create a 10 Second Timeout */
|
||||
tv.tv_sec = 10;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
/* Add our socket to a socket set */
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(socketDescriptor, &fds);
|
||||
|
||||
/* Wait max of 10 seconds for recv to become available */
|
||||
select(socketDescriptor+1, &fds, NULL, NULL, &tv);
|
||||
|
||||
int bytesReceived = 0;
|
||||
|
||||
/* Is there a limit on how much we can receive? Some site said 1024 bytes, which will be well short of a file */
|
||||
if (FD_ISSET(socketDescriptor, &fds))
|
||||
{
|
||||
bytesReceived = recv(socketDescriptor, buffer, len, 0);
|
||||
}
|
||||
|
||||
if (bytesReceived == 0 || bytesReceived == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bytesReceived < len)
|
||||
{
|
||||
return bytesReceived + NonBlockingRecv(socketDescriptor, buffer+bytesReceived, len-bytesReceived);
|
||||
}
|
||||
|
||||
return bytesReceived;
|
||||
}
|
||||
|
||||
int FetcherThread::NonBlockingSend( int socketDescriptor, char *buffer, int len )
|
||||
{
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = 10;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(socketDescriptor, &fds);
|
||||
|
||||
select(socketDescriptor+1, NULL, &fds, NULL, &tv);
|
||||
|
||||
int sentBytes = 0;
|
||||
|
||||
if (FD_ISSET(socketDescriptor, &fds))
|
||||
{
|
||||
sentBytes = send(socketDescriptor, buffer, len, 0);
|
||||
}
|
||||
|
||||
if (sentBytes == 0 || sentBytes == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sentBytes < len)
|
||||
{
|
||||
return sentBytes + NonBlockingSend(socketDescriptor, buffer+sentBytes, len-sentBytes);
|
||||
}
|
||||
|
||||
return sentBytes;
|
||||
}
|
||||
|
||||
void FetcherThread::HandleUpdateStatus( UpdateStatus status, short version[4] )
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case Update_Unknown:
|
||||
case Update_Current:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case Update_NewBuild:
|
||||
{
|
||||
g_Logger.LogMessage("SourceMod Update: A new SVN 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("Current Version: %i.%i.%i Available: %i.%i.%i", version[0], version[1], version[2], version[0], version[1], version[2]);
|
||||
break;
|
||||
}
|
||||
|
||||
case Update_MajorAvailable:
|
||||
{
|
||||
g_Logger.LogMessage("SourceMod Update: An major 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;
|
||||
}
|
||||
|
||||
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]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool g_blockGameDataLoad = false;
|
||||
|
||||
class InitFetch : public SMGlobalClass
|
||||
{
|
||||
public:
|
||||
void OnSourceModAllInitialized_Post()
|
||||
{
|
||||
char lock_path[PLATFORM_MAX_PATH];
|
||||
g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp/gamedata.lock");
|
||||
|
||||
if (g_LibSys.IsPathFile(lock_path) && g_LibSys.PathExists(lock_path))
|
||||
{
|
||||
g_Logger.LogError("sourcemod/data/temp/gamedata.lock file detected. This is most likely due to a crash during GameData updating - Blocking GameData loading");
|
||||
g_Logger.LogError("If this error persists delete the file manually");
|
||||
g_blockGameDataLoad = true;
|
||||
}
|
||||
|
||||
ThreadParams fetchThreadParams = ThreadParams();
|
||||
fetchThreadParams.prio = ThreadPrio_Low;
|
||||
g_pThreader->MakeThread(&g_FetchThread, &fetchThreadParams);
|
||||
}
|
||||
|
||||
ConfigResult OnSourceModConfigChanged(const char *key,
|
||||
const char *value,
|
||||
ConfigSource source,
|
||||
char *error,
|
||||
size_t maxlength)
|
||||
{
|
||||
if (strcmp(key, "DisableAutoUpdate") == 0)
|
||||
{
|
||||
if (strcmp(value, "yes") == 0)
|
||||
{
|
||||
g_disableGameDataUpdate = true;
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
else if (strcmp(value, "no") == 0)
|
||||
{
|
||||
g_disableGameDataUpdate = false;
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
|
||||
return ConfigResult_Reject;
|
||||
}
|
||||
|
||||
if (strcmp(key, "ForceRestartAfterUpdate") == 0)
|
||||
{
|
||||
if (strcmp(value, "yes") == 0)
|
||||
{
|
||||
g_restartAfterUpdate = true;
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
else if (strcmp(value, "no") == 0)
|
||||
{
|
||||
g_restartAfterUpdate = false;
|
||||
return ConfigResult_Accept;
|
||||
}
|
||||
|
||||
return ConfigResult_Reject;
|
||||
}
|
||||
|
||||
return ConfigResult_Ignore;
|
||||
}
|
||||
} g_InitFetch;
|
||||
|
||||
CON_COMMAND(sm_gamedata_md5, "Checks the MD5 sum for a given gamedata file")
|
||||
{
|
||||
#if !defined ORANGEBOX_BUILD
|
||||
CCommand args;
|
||||
#endif
|
||||
|
||||
if (args.ArgC() < 2)
|
||||
{
|
||||
g_SMAPI->ConPrint("Usage: sm_gamedata_md5 <file>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *file = args.Arg(1);
|
||||
if (!file || file[0] == '\0')
|
||||
{
|
||||
g_SMAPI->ConPrint("Usage: sm_gamedata_md5 <file>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
SourceHook::CVector<FileData *>::iterator iter = g_FetchThread.filenames.begin();
|
||||
|
||||
FileData *curData;
|
||||
|
||||
while (iter != g_FetchThread.filenames.end())
|
||||
{
|
||||
curData = (*iter);
|
||||
|
||||
char fileName[30];
|
||||
|
||||
g_LibSys.GetFileFromPath(fileName, sizeof(fileName), curData->filename->c_str());
|
||||
|
||||
if (strcmpi(fileName, file) == 0)
|
||||
{
|
||||
g_SMAPI->ConPrintf("MD5 Sum: %s\n", curData->checksum);
|
||||
return;
|
||||
}
|
||||
|
||||
iter++;
|
||||
}
|
||||
|
||||
g_SMAPI->ConPrint("File not found!\n");
|
||||
}
|
185
core/GameDataFetcher.h
Normal file
185
core/GameDataFetcher.h
Normal file
@ -0,0 +1,185 @@
|
||||
/**
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 <http://www.sourcemod.net/license.php>.
|
||||
*
|
||||
* Version: $Id$
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SOURCEMOD_GAMEDATAFETCHER_H_
|
||||
#define _INCLUDE_SOURCEMOD_GAMEDATAFETCHER_H_
|
||||
|
||||
#include "sourcemod.h"
|
||||
#include "TextParsers.h"
|
||||
#include "IThreader.h"
|
||||
#include "Logger.h"
|
||||
#include "LibrarySys.h"
|
||||
#include "ThreadSupport.h"
|
||||
#include "md5.h"
|
||||
#include "sm_memtable.h"
|
||||
|
||||
enum UpdateStatus
|
||||
{
|
||||
Update_Unknown = 0, /* Version wasn't recognised or version querying is unsupported */
|
||||
Update_Current = 1, /* Server is running latest version */
|
||||
Update_NewBuild = 2, /* Server is on a svn release and a newer version is available */
|
||||
Update_MinorAvailable = 3, /* Server is on a release and a minor release has superceeded it */
|
||||
Update_MajorAvailable = 4, /* Server is on a release and a major release has superceeded it */
|
||||
Update_CriticalAvailable = 5, /* A critical update has been released (security fixes etc) */
|
||||
};
|
||||
|
||||
class BuildMD5ableBuffer : public ITextListener_SMC
|
||||
{
|
||||
public:
|
||||
|
||||
BuildMD5ableBuffer()
|
||||
{
|
||||
stringTable = new BaseStringTable(2048);
|
||||
md5[0] = 0;
|
||||
md5String[0] = 0;
|
||||
}
|
||||
|
||||
~BuildMD5ableBuffer()
|
||||
{
|
||||
delete stringTable;
|
||||
}
|
||||
|
||||
void ReadSMC_ParseStart()
|
||||
{
|
||||
checksum = MD5();
|
||||
}
|
||||
|
||||
SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
|
||||
{
|
||||
stringTable->AddString(key);
|
||||
stringTable->AddString(value);
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name)
|
||||
{
|
||||
stringTable->AddString(name);
|
||||
|
||||
return SMCResult_Continue;
|
||||
}
|
||||
|
||||
void 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();
|
||||
|
||||
checksum.hex_digest(md5String);
|
||||
checksum.raw_digest(md5);
|
||||
|
||||
stringTable->Reset();
|
||||
}
|
||||
|
||||
unsigned char * GetMD5()
|
||||
{
|
||||
return md5;
|
||||
}
|
||||
|
||||
unsigned char * GetMD5String()
|
||||
{
|
||||
return (unsigned char *)&md5String[0];
|
||||
}
|
||||
|
||||
private:
|
||||
MD5 checksum;
|
||||
unsigned char md5[16];
|
||||
char md5String[33];
|
||||
BaseStringTable *stringTable;
|
||||
};
|
||||
|
||||
struct FileData
|
||||
{
|
||||
SourceHook::String *filename;
|
||||
unsigned char checksum[33];
|
||||
};
|
||||
|
||||
|
||||
class FetcherThread : public IThread
|
||||
{
|
||||
public:
|
||||
FetcherThread()
|
||||
{
|
||||
//filenames = new BaseStringTable(200);
|
||||
memtable = new BaseMemTable(4096);
|
||||
}
|
||||
|
||||
~FetcherThread()
|
||||
{
|
||||
//delete filenames;
|
||||
SourceHook::CVector<FileData *>::iterator iter = filenames.begin();
|
||||
|
||||
FileData *curData;
|
||||
|
||||
while (iter != filenames.end())
|
||||
{
|
||||
curData = (*iter);
|
||||
delete curData->filename;
|
||||
delete curData;
|
||||
iter = filenames.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void RunThread(IThreadHandle *pHandle);
|
||||
void OnTerminate(IThreadHandle *pHandle, bool cancel);
|
||||
|
||||
private:
|
||||
int BuildGameDataQuery(char *buffer, int maxlen);
|
||||
void ProcessGameDataQuery(int SocketDescriptor);
|
||||
|
||||
/* These names are a lie. It's really NoMoreBlockingThan10Seconds */
|
||||
int NonBlockingRecv(int socketDescriptor, char *buffer, int len);
|
||||
int NonBlockingSend(int socketDescriptor, char *buffer, int len);
|
||||
|
||||
int ConnectSocket();
|
||||
|
||||
void HandleUpdateStatus(UpdateStatus status, short version[4]);
|
||||
|
||||
public:
|
||||
SourceHook::CVector<FileData *> filenames;
|
||||
private:
|
||||
BaseMemTable *memtable;
|
||||
};
|
||||
|
||||
extern BuildMD5ableBuffer g_MD5Builder;
|
||||
extern bool g_blockGameDataLoad;
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_GAMEDATAFETCHER_H_
|
@ -19,7 +19,7 @@ OBJECTS = AdminCache.cpp CDataPack.cpp ConCmdManager.cpp ConVarManager.cpp CoreC
|
||||
sourcemm_api.cpp sourcemod.cpp MenuStyle_Base.cpp MenuStyle_Valve.cpp MenuManager.cpp \
|
||||
MenuStyle_Radio.cpp ChatTriggers.cpp ADTFactory.cpp MenuVoting.cpp sm_crc32.cpp \
|
||||
frame_hooks.cpp concmd_cleaner.cpp Profiler.cpp PhraseCollection.cpp NextMap.cpp \
|
||||
NativeOwner.cpp
|
||||
NativeOwner.cpp GameDataFetcher.cpp md5.cpp
|
||||
OBJECTS += smn_admin.cpp smn_bitbuffer.cpp smn_console.cpp smn_core.cpp \
|
||||
smn_datapacks.cpp smn_entities.cpp smn_events.cpp smn_fakenatives.cpp \
|
||||
smn_filesystem.cpp smn_float.cpp smn_functions.cpp smn_gameconfigs.cpp smn_halflife.cpp \
|
||||
|
485
core/md5.cpp
Normal file
485
core/md5.cpp
Normal file
@ -0,0 +1,485 @@
|
||||
// MD5.CC - source code for the C++/object oriented translation and
|
||||
// modification of MD5.
|
||||
|
||||
// Translation and modification (c) 1995 by Mordechai T. Abzug
|
||||
|
||||
// This translation/ modification is provided "as is," without express or
|
||||
// implied warranty of any kind.
|
||||
|
||||
// The translator/ modifier does not claim (1) that MD5 will do what you think
|
||||
// it does; (2) that this translation/ modification is accurate; or (3) that
|
||||
// this software is "merchantible." (Language for this disclaimer partially
|
||||
// copied from the disclaimer below).
|
||||
|
||||
/* based on:
|
||||
|
||||
MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
|
||||
MDDRIVER.C - test driver for MD2, MD4 and MD5
|
||||
|
||||
|
||||
Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
||||
rights reserved.
|
||||
|
||||
License to copy and use this software is granted provided that it
|
||||
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
||||
Algorithm" in all material mentioning or referencing this software
|
||||
or this function.
|
||||
|
||||
License is also granted to make and use derivative works provided
|
||||
that such works are identified as "derived from the RSA Data
|
||||
Security, Inc. MD5 Message-Digest Algorithm" in all material
|
||||
mentioning or referencing the derived work.
|
||||
|
||||
RSA Data Security, Inc. makes no representations concerning either
|
||||
the merchantability of this software or the suitability of this
|
||||
software for any particular purpose. It is provided "as is"
|
||||
without express or implied warranty of any kind.
|
||||
|
||||
These notices must be retained in any copies of any part of this
|
||||
documentation and/or software.
|
||||
|
||||
*/
|
||||
|
||||
#include "md5.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
// MD5 simple initialization method
|
||||
|
||||
MD5::MD5(){
|
||||
|
||||
init();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MD5 block update operation. Continues an MD5 message-digest
|
||||
// operation, processing another message block, and updating the
|
||||
// context.
|
||||
|
||||
void MD5::update (uint1 *input, uint4 input_length) {
|
||||
|
||||
uint4 input_index, buffer_index;
|
||||
uint4 buffer_space; // how much space is left in buffer
|
||||
|
||||
if (finalized){ // so we can't update!
|
||||
/*cerr << "MD5::update: Can't update a finalized digest!" << endl;*/
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute number of bytes mod 64
|
||||
buffer_index = (unsigned int)((count[0] >> 3) & 0x3F);
|
||||
|
||||
// Update number of bits
|
||||
if ( (count[0] += ((uint4) input_length << 3))<((uint4) input_length << 3) )
|
||||
count[1]++;
|
||||
|
||||
count[1] += ((uint4)input_length >> 29);
|
||||
|
||||
|
||||
buffer_space = 64 - buffer_index; // how much space is left in buffer
|
||||
|
||||
// Transform as many times as possible.
|
||||
if (input_length >= buffer_space) { // ie. we have enough to fill the buffer
|
||||
// fill the rest of the buffer and transform
|
||||
memcpy (buffer + buffer_index, input, buffer_space);
|
||||
transform (buffer);
|
||||
|
||||
// now, transform each 64-byte piece of the input, bypassing the buffer
|
||||
for (input_index = buffer_space; input_index + 63 < input_length;
|
||||
input_index += 64)
|
||||
transform (input+input_index);
|
||||
|
||||
buffer_index = 0; // so we can buffer remaining
|
||||
}
|
||||
else
|
||||
input_index=0; // so we can buffer the whole input
|
||||
|
||||
|
||||
// and here we do the buffering:
|
||||
memcpy(buffer+buffer_index, input+input_index, input_length-input_index);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MD5 update for files.
|
||||
// Like above, except that it works on files (and uses above as a primitive.)
|
||||
|
||||
void MD5::update(FILE *file){
|
||||
|
||||
unsigned char buffer[1024];
|
||||
int len;
|
||||
|
||||
while ((len=fread(buffer, 1, 1024, file)))
|
||||
update(buffer, len);
|
||||
|
||||
fclose (file);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MD5 finalization. Ends an MD5 message-digest operation, writing the
|
||||
// the message digest and zeroizing the context.
|
||||
|
||||
|
||||
void MD5::finalize (){
|
||||
|
||||
unsigned char bits[8];
|
||||
unsigned int index, padLen;
|
||||
static uint1 PADDING[64]={
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
if (finalized){
|
||||
/* cerr << "MD5::finalize: Already finalized this digest!" << endl;*/
|
||||
return;
|
||||
}
|
||||
|
||||
// Save number of bits
|
||||
encode (bits, count, 8);
|
||||
|
||||
// Pad out to 56 mod 64.
|
||||
index = (uint4) ((count[0] >> 3) & 0x3f);
|
||||
padLen = (index < 56) ? (56 - index) : (120 - index);
|
||||
update (PADDING, padLen);
|
||||
|
||||
// Append length (before padding)
|
||||
update (bits, 8);
|
||||
|
||||
// Store state in digest
|
||||
encode (digest, state, 16);
|
||||
|
||||
// Zeroize sensitive information
|
||||
memset (buffer, 0, sizeof(*buffer));
|
||||
|
||||
finalized=1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
MD5::MD5(FILE *file){
|
||||
|
||||
init(); // must be called be all constructors
|
||||
update(file);
|
||||
finalize ();
|
||||
}
|
||||
|
||||
unsigned char *MD5::raw_digest(){
|
||||
|
||||
uint1 *s = new uint1[16];
|
||||
|
||||
if (!finalized){
|
||||
/* cerr << "MD5::raw_digest: Can't get digest if you haven't "<<
|
||||
"finalized the digest!" <<endl;*/
|
||||
return ( (unsigned char*) "");
|
||||
}
|
||||
|
||||
memcpy(s, digest, 16);
|
||||
return s;
|
||||
}
|
||||
|
||||
unsigned char *MD5::raw_digest(unsigned char buffer[16])
|
||||
{
|
||||
if (!finalized)
|
||||
{
|
||||
return ( (unsigned char*) "");
|
||||
}
|
||||
|
||||
memcpy(buffer, digest, 16);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
char *MD5::hex_digest(){
|
||||
|
||||
int i;
|
||||
char *s= new char[33];
|
||||
|
||||
if (!finalized){
|
||||
/* cerr << "MD5::hex_digest: Can't get digest if you haven't "<<
|
||||
"finalized the digest!" <<endl;*/
|
||||
return "";
|
||||
}
|
||||
|
||||
for (i=0; i<16; i++)
|
||||
sprintf(s+i*2, "%02x", digest[i]);
|
||||
|
||||
s[32]='\0';
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
char *MD5::hex_digest(char buffer[33]){
|
||||
|
||||
int i;
|
||||
|
||||
if (!finalized)
|
||||
{
|
||||
/* cerr << "MD5::hex_digest: Can't get digest if you haven't "<<
|
||||
"finalized the digest!" <<endl;*/
|
||||
return "";
|
||||
}
|
||||
|
||||
for (i=0; i<16; i++)
|
||||
sprintf(buffer+i*2, "%02x", digest[i]);
|
||||
|
||||
buffer[32]='\0';
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
// PRIVATE METHODS:
|
||||
|
||||
|
||||
|
||||
void MD5::init(){
|
||||
finalized=0; // we just started!
|
||||
|
||||
// Nothing counted, so count=0
|
||||
count[0] = 0;
|
||||
count[1] = 0;
|
||||
|
||||
// Load magic initialization constants.
|
||||
state[0] = 0x67452301;
|
||||
state[1] = 0xefcdab89;
|
||||
state[2] = 0x98badcfe;
|
||||
state[3] = 0x10325476;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Constants for MD5Transform routine.
|
||||
// Although we could use C++ style constants, defines are actually better,
|
||||
// since they let us easily evade scope clashes.
|
||||
|
||||
#define S11 7
|
||||
#define S12 12
|
||||
#define S13 17
|
||||
#define S14 22
|
||||
#define S21 5
|
||||
#define S22 9
|
||||
#define S23 14
|
||||
#define S24 20
|
||||
#define S31 4
|
||||
#define S32 11
|
||||
#define S33 16
|
||||
#define S34 23
|
||||
#define S41 6
|
||||
#define S42 10
|
||||
#define S43 15
|
||||
#define S44 21
|
||||
|
||||
|
||||
|
||||
|
||||
// MD5 basic transformation. Transforms state based on block.
|
||||
void MD5::transform (uint1 block[64]){
|
||||
|
||||
uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
|
||||
|
||||
decode (x, block, 64);
|
||||
|
||||
assert(!finalized); // not just a user error, since the method is private
|
||||
|
||||
/* Round 1 */
|
||||
FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
|
||||
FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
|
||||
FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
|
||||
FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
|
||||
FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
|
||||
FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
|
||||
FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
|
||||
FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
|
||||
FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
|
||||
FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
|
||||
FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
|
||||
FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
|
||||
FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
|
||||
FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
|
||||
FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
|
||||
FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
|
||||
|
||||
/* Round 2 */
|
||||
GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
|
||||
GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
|
||||
GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
|
||||
GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
|
||||
GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
|
||||
GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
|
||||
GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
|
||||
GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
|
||||
GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
|
||||
GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
|
||||
GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
|
||||
GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
|
||||
GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
|
||||
GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
|
||||
GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
|
||||
GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
|
||||
|
||||
/* Round 3 */
|
||||
HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
|
||||
HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
|
||||
HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
|
||||
HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
|
||||
HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
|
||||
HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
|
||||
HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
|
||||
HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
|
||||
HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
|
||||
HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
|
||||
HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
|
||||
HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
|
||||
HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
|
||||
HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
|
||||
HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
|
||||
HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
|
||||
|
||||
/* Round 4 */
|
||||
II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
|
||||
II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
|
||||
II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
|
||||
II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
|
||||
II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
|
||||
II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
|
||||
II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
|
||||
II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
|
||||
II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
|
||||
II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
|
||||
II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
|
||||
II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
|
||||
II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
|
||||
II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
|
||||
II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
|
||||
II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
|
||||
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
|
||||
// Zeroize sensitive information.
|
||||
memset ( (uint1 *) x, 0, sizeof(x));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Encodes input (UINT4) into output (unsigned char). Assumes len is
|
||||
// a multiple of 4.
|
||||
void MD5::encode (uint1 *output, uint4 *input, uint4 len) {
|
||||
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0, j = 0; j < len; i++, j += 4) {
|
||||
output[j] = (uint1) (input[i] & 0xff);
|
||||
output[j+1] = (uint1) ((input[i] >> 8) & 0xff);
|
||||
output[j+2] = (uint1) ((input[i] >> 16) & 0xff);
|
||||
output[j+3] = (uint1) ((input[i] >> 24) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Decodes input (unsigned char) into output (UINT4). Assumes len is
|
||||
// a multiple of 4.
|
||||
void MD5::decode (uint4 *output, uint1 *input, uint4 len){
|
||||
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0, j = 0; j < len; i++, j += 4)
|
||||
output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) |
|
||||
(((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Note: Replace "for loop" with standard memcpy if possible.
|
||||
void MD5::memcpy (uint1 *output, uint1 *input, uint4 len){
|
||||
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
output[i] = input[i];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Note: Replace "for loop" with standard memset if possible.
|
||||
void MD5::memset (uint1 *output, uint1 value, uint4 len){
|
||||
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
output[i] = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ROTATE_LEFT rotates x left n bits.
|
||||
|
||||
inline unsigned int MD5::rotate_left (uint4 x, uint4 n){
|
||||
return (x << n) | (x >> (32-n)) ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// F, G, H and I are basic MD5 functions.
|
||||
|
||||
inline unsigned int MD5::F (uint4 x, uint4 y, uint4 z){
|
||||
return (x & y) | (~x & z);
|
||||
}
|
||||
|
||||
inline unsigned int MD5::G (uint4 x, uint4 y, uint4 z){
|
||||
return (x & z) | (y & ~z);
|
||||
}
|
||||
|
||||
inline unsigned int MD5::H (uint4 x, uint4 y, uint4 z){
|
||||
return x ^ y ^ z;
|
||||
}
|
||||
|
||||
inline unsigned int MD5::I (uint4 x, uint4 y, uint4 z){
|
||||
return y ^ (x | ~z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
|
||||
// Rotation is separate from addition to prevent recomputation.
|
||||
|
||||
|
||||
inline void MD5::FF(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
|
||||
uint4 s, uint4 ac){
|
||||
a += F(b, c, d) + x + ac;
|
||||
a = rotate_left (a, s) +b;
|
||||
}
|
||||
|
||||
inline void MD5::GG(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
|
||||
uint4 s, uint4 ac){
|
||||
a += G(b, c, d) + x + ac;
|
||||
a = rotate_left (a, s) +b;
|
||||
}
|
||||
|
||||
inline void MD5::HH(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
|
||||
uint4 s, uint4 ac){
|
||||
a += H(b, c, d) + x + ac;
|
||||
a = rotate_left (a, s) +b;
|
||||
}
|
||||
|
||||
inline void MD5::II(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
|
||||
uint4 s, uint4 ac){
|
||||
a += I(b, c, d) + x + ac;
|
||||
a = rotate_left (a, s) +b;
|
||||
}
|
106
core/md5.h
Normal file
106
core/md5.h
Normal file
@ -0,0 +1,106 @@
|
||||
// MD5.CC - source code for the C++/object oriented translation and
|
||||
// modification of MD5.
|
||||
|
||||
// Translation and modification (c) 1995 by Mordechai T. Abzug
|
||||
|
||||
// This translation/ modification is provided "as is," without express or
|
||||
// implied warranty of any kind.
|
||||
|
||||
// The translator/ modifier does not claim (1) that MD5 will do what you think
|
||||
// it does; (2) that this translation/ modification is accurate; or (3) that
|
||||
// this software is "merchantible." (Language for this disclaimer partially
|
||||
// copied from the disclaimer below).
|
||||
|
||||
/* based on:
|
||||
|
||||
MD5.H - header file for MD5C.C
|
||||
MDDRIVER.C - test driver for MD2, MD4 and MD5
|
||||
|
||||
Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
||||
rights reserved.
|
||||
|
||||
License to copy and use this software is granted provided that it
|
||||
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
||||
Algorithm" in all material mentioning or referencing this software
|
||||
or this function.
|
||||
|
||||
License is also granted to make and use derivative works provided
|
||||
that such works are identified as "derived from the RSA Data
|
||||
Security, Inc. MD5 Message-Digest Algorithm" in all material
|
||||
mentioning or referencing the derived work.
|
||||
|
||||
RSA Data Security, Inc. makes no representations concerning either
|
||||
the merchantability of this software or the suitability of this
|
||||
software for any particular purpose. It is provided "as is"
|
||||
without express or implied warranty of any kind.
|
||||
|
||||
These notices must be retained in any copies of any part of this
|
||||
documentation and/or software.
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
//#include <fstream.h>
|
||||
//#include <iostream.h>
|
||||
|
||||
class MD5 {
|
||||
|
||||
public:
|
||||
// methods for controlled operation:
|
||||
MD5 (); // simple initializer
|
||||
void update (unsigned char *input, unsigned int input_length);
|
||||
void update (FILE *file);
|
||||
void finalize ();
|
||||
|
||||
// constructors for special circumstances. All these constructors finalize
|
||||
// the MD5 context.
|
||||
MD5 (unsigned char *string); // digest string, finalize
|
||||
MD5 (FILE *file); // digest file, close, finalize
|
||||
|
||||
// methods to acquire finalized result
|
||||
unsigned char *raw_digest (); // digest as a 16-byte binary array
|
||||
unsigned char *raw_digest(unsigned char buffer[16]);
|
||||
char * hex_digest (); // digest as a 33-byte ascii-hex string
|
||||
char * hex_digest (char buffer[33]); //same as above, passing buffer
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// first, some types:
|
||||
typedef unsigned int uint4; // assumes integer is 4 words long
|
||||
typedef unsigned short int uint2; // assumes short integer is 2 words long
|
||||
typedef unsigned char uint1; // assumes char is 1 word long
|
||||
|
||||
// next, the private data:
|
||||
uint4 state[4];
|
||||
uint4 count[2]; // number of *bits*, mod 2^64
|
||||
uint1 buffer[64]; // input buffer
|
||||
uint1 digest[16];
|
||||
uint1 finalized;
|
||||
|
||||
// last, the private methods, mostly static:
|
||||
void init (); // called by all constructors
|
||||
void transform (uint1 *buffer); // does the real update work. Note
|
||||
// that length is implied to be 64.
|
||||
|
||||
static void encode (uint1 *dest, uint4 *src, uint4 length);
|
||||
static void decode (uint4 *dest, uint1 *src, uint4 length);
|
||||
static void memcpy (uint1 *dest, uint1 *src, uint4 length);
|
||||
static void memset (uint1 *start, uint1 val, uint4 length);
|
||||
|
||||
static inline uint4 rotate_left (uint4 x, uint4 n);
|
||||
static inline uint4 F (uint4 x, uint4 y, uint4 z);
|
||||
static inline uint4 G (uint4 x, uint4 y, uint4 z);
|
||||
static inline uint4 H (uint4 x, uint4 y, uint4 z);
|
||||
static inline uint4 I (uint4 x, uint4 y, uint4 z);
|
||||
static inline void FF (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
|
||||
uint4 s, uint4 ac);
|
||||
static inline void GG (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
|
||||
uint4 s, uint4 ac);
|
||||
static inline void HH (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
|
||||
uint4 s, uint4 ac);
|
||||
static inline void II (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
|
||||
uint4 s, uint4 ac);
|
||||
|
||||
};
|
@ -869,6 +869,10 @@
|
||||
RelativePath="..\GameConfigs.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\GameDataFetcher.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\HalfLife2.cpp"
|
||||
>
|
||||
@ -877,6 +881,10 @@
|
||||
RelativePath="..\Logger.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\md5.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\MemoryUtils.cpp"
|
||||
>
|
||||
@ -1043,6 +1051,10 @@
|
||||
RelativePath="..\GameConfigs.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\GameDataFetcher.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\HalfLife2.h"
|
||||
>
|
||||
@ -1051,6 +1063,10 @@
|
||||
RelativePath="..\Logger.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\md5.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\MemoryUtils.h"
|
||||
>
|
||||
|
@ -63,6 +63,11 @@ public:
|
||||
return size;
|
||||
}
|
||||
|
||||
inline unsigned int GetActualMemUsed()
|
||||
{
|
||||
return tail;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned char *membase;
|
||||
unsigned int size;
|
||||
|
@ -186,6 +186,11 @@ void CForwardManager::ReleaseForward(IForward *forward)
|
||||
|
||||
void CForwardManager::ForwardFree(CForward *fwd)
|
||||
{
|
||||
if (fwd == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_FreeForwards.push(fwd);
|
||||
m_managed.remove(fwd);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user