1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/GameDataFetcher.cpp Sun Jul 06 04:49:55 2008 +0000
1.3 @@ -0,0 +1,693 @@
1.4 +/**
1.5 +* vim: set ts=4 :
1.6 +* =============================================================================
1.7 +* SourceMod
1.8 +* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 +* =============================================================================
1.10 +*
1.11 +* This program is free software; you can redistribute it and/or modify it under
1.12 +* the terms of the GNU General Public License, version 3.0, as published by the
1.13 +* Free Software Foundation.
1.14 +*
1.15 +* This program is distributed in the hope that it will be useful, but WITHOUT
1.16 +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 +* details.
1.19 +*
1.20 +* You should have received a copy of the GNU General Public License along with
1.21 +* this program. If not, see <http://www.gnu.org/licenses/>.
1.22 +*
1.23 +* As a special exception, AlliedModders LLC gives you permission to link the
1.24 +* code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 +* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 +* by the Valve Corporation. You must obey the GNU General Public License in
1.27 +* all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 +* this exception to all derivative works. AlliedModders LLC defines further
1.29 +* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 +* or <http://www.sourcemod.net/license.php>.
1.31 +*
1.32 +* Version: $Id$
1.33 +*/
1.34 +
1.35 +#include "GameDataFetcher.h"
1.36 +#include "bitbuf.h"
1.37 +
1.38 +#ifdef PLATFORM_WINDOWS
1.39 +#include <winsock2.h>
1.40 +#else
1.41 +#include <unistd.h>
1.42 +#include <sys/types.h>
1.43 +#include <sys/socket.h>
1.44 +#include <netinet/in.h>
1.45 +#include <netdb.h>
1.46 +#include <arpa/inet.h>
1.47 +
1.48 +#define INVALID_SOCKET -1
1.49 +#define closesocket close
1.50 +#endif
1.51 +
1.52 +#include "sh_vector.h"
1.53 +#include "sh_string.h"
1.54 +#include "sm_version.h"
1.55 +
1.56 +#ifdef ORANGEBOX_BUILD
1.57 +#include "convar_sm_ob.h"
1.58 +#else
1.59 +#include "convar_sm.h"
1.60 +#endif
1.61 +
1.62 +#include "sourcemm_api.h"
1.63 +#include "time.h"
1.64 +#include "TimerSys.h"
1.65 +#include "compat_wrappers.h"
1.66 +
1.67 +#define QUERY_MAX_LENGTH 1024
1.68 +
1.69 +BuildMD5ableBuffer g_MD5Builder;
1.70 +FetcherThread g_FetchThread;
1.71 +
1.72 +FILE *logfile = NULL;
1.73 +
1.74 +bool g_disableGameDataUpdate = false;
1.75 +bool g_restartAfterUpdate = false;
1.76 +
1.77 +void FetcherThread::RunThread( IThreadHandle *pHandle )
1.78 +{
1.79 + char lock_path[PLATFORM_MAX_PATH];
1.80 + g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp");
1.81 + g_LibSys.CreateFolder(lock_path);
1.82 +
1.83 + g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp/gamedata.lock");
1.84 +
1.85 + g_Logger.LogMessage("Starting experimental gamedata update fetcher... please report problems to bugs.alliedmods.net");
1.86 +
1.87 + char log_path[PLATFORM_MAX_PATH];
1.88 + g_SourceMod.BuildPath(Path_SM, log_path, sizeof(log_path), "logs/gamedata");
1.89 +
1.90 + g_LibSys.CreateFolder(log_path);
1.91 +
1.92 + time_t t;
1.93 + GetAdjustedTime(&t);
1.94 + tm *curtime = localtime(&t);
1.95 +
1.96 + g_SourceMod.BuildPath(Path_SM, log_path, sizeof(log_path), "logs/gamedata/L%02d%02d.log", curtime->tm_mon + 1, curtime->tm_mday);
1.97 +
1.98 + logfile = fopen(log_path, "a");
1.99 +
1.100 + if (!logfile)
1.101 + {
1.102 + g_Logger.LogError("Failed to create GameData log file");
1.103 + return;
1.104 + }
1.105 +
1.106 + //Create a blank lock file
1.107 + FILE *fp = fopen(lock_path, "w");
1.108 + if (fp)
1.109 + {
1.110 + fclose(fp);
1.111 + }
1.112 +
1.113 + /* Create a new socket for this connection */
1.114 + int socketDescriptor = ConnectSocket();
1.115 +
1.116 + if (socketDescriptor == INVALID_SOCKET)
1.117 + {
1.118 + fclose(logfile);
1.119 + unlink(lock_path);
1.120 + return;
1.121 + }
1.122 +
1.123 + char query[QUERY_MAX_LENGTH];
1.124 +
1.125 + /* Check for updated gamedata files */
1.126 + int len = BuildGameDataQuery(query, QUERY_MAX_LENGTH);
1.127 +
1.128 + if (len == 0)
1.129 + {
1.130 + g_Logger.LogToOpenFile(logfile, "Query Writing failed");
1.131 +
1.132 + closesocket(socketDescriptor);
1.133 + fclose(logfile);
1.134 + unlink(lock_path);
1.135 + return;
1.136 + }
1.137 +
1.138 + if (g_disableGameDataUpdate)
1.139 + {
1.140 + g_Logger.LogMessage("Skipping GameData Query due to DisableAutoUpdate being set to true");
1.141 +
1.142 + closesocket(socketDescriptor);
1.143 + fclose(logfile);
1.144 + unlink(lock_path);
1.145 + return;
1.146 + }
1.147 +
1.148 + int sent = NonBlockingSend(socketDescriptor, query, len);
1.149 +
1.150 + g_Logger.LogToOpenFile(logfile, "Sent Query!");
1.151 +
1.152 + if (sent == 0)
1.153 + {
1.154 + g_Logger.LogToOpenFile(logfile, "Failed to send data");
1.155 +
1.156 + closesocket(socketDescriptor);
1.157 + fclose(logfile);
1.158 + unlink(lock_path);
1.159 + return;
1.160 + }
1.161 +
1.162 + ProcessGameDataQuery(socketDescriptor);
1.163 +
1.164 + /* And we're done! */
1.165 + closesocket(socketDescriptor);
1.166 + fclose(logfile);
1.167 + unlink(lock_path);
1.168 +}
1.169 +
1.170 +void FetcherThread::OnTerminate( IThreadHandle *pHandle, bool cancel )
1.171 +{
1.172 + //delete this;
1.173 +}
1.174 +
1.175 +int FetcherThread::BuildGameDataQuery( char *buffer, int maxlen )
1.176 +{
1.177 + char gamedata_path[PLATFORM_MAX_PATH];
1.178 + g_SourceMod.BuildPath(Path_SM, gamedata_path, sizeof(gamedata_path), "gamedata");
1.179 +
1.180 + IDirectory *dir = g_LibSys.OpenDirectory(gamedata_path);
1.181 +
1.182 + if (dir == NULL)
1.183 + {
1.184 + return 0;
1.185 + }
1.186 +
1.187 + bf_write Writer = bf_write("GameDataQuery", buffer, maxlen);
1.188 +
1.189 + Writer.WriteByte('A'); //Generic Header char
1.190 + Writer.WriteByte('G'); //G for gamedata query, or green, like my hat.
1.191 +
1.192 + short build[4] = { SVN_FILE_VERSION };
1.193 +
1.194 + Writer.WriteBytes(&build[0], 8);
1.195 +
1.196 + Writer.WriteByte(0); // Initialize the file counter - Index 10
1.197 +
1.198 + while (dir->MoreFiles())
1.199 + {
1.200 + if (dir->IsEntryFile())
1.201 + {
1.202 + const char *name = dir->GetEntryName();
1.203 + size_t len = strlen(name);
1.204 + if (len >= 4
1.205 + && strcmp(&name[len-4], ".txt") == 0)
1.206 + {
1.207 + char file[PLATFORM_MAX_PATH];
1.208 +
1.209 + g_LibSys.PathFormat(file, sizeof(file), "%s/%s", gamedata_path, name);
1.210 +
1.211 + SMCStates states;
1.212 + if (g_TextParser.ParseFile_SMC(file, &g_MD5Builder, &states) == SMCError_Okay)
1.213 + {
1.214 + unsigned char *md5 = g_MD5Builder.GetMD5();
1.215 + if (md5 != NULL)
1.216 + {
1.217 + (uint8_t)buffer[10]++; //Increment the file counter
1.218 + Writer.WriteBytes(md5, 16);
1.219 +
1.220 + g_Logger.LogToOpenFile(logfile, "%s - \"%s\"", file, g_MD5Builder.GetMD5String());
1.221 +
1.222 + FileData *data = new FileData();
1.223 + data->filename = new SourceHook::String(file);
1.224 + memcpy(data->checksum, g_MD5Builder.GetMD5String(), 33);
1.225 + filenames.push_back(data);
1.226 + }
1.227 + else
1.228 + {
1.229 + g_Logger.LogToOpenFile(logfile, "%s no md5?", file);
1.230 + }
1.231 + }
1.232 + else
1.233 + {
1.234 + g_Logger.LogToOpenFile(logfile, "%s failed!", file);
1.235 + }
1.236 + }
1.237 + }
1.238 +
1.239 + dir->NextEntry();
1.240 + }
1.241 +
1.242 + return Writer.GetNumBytesWritten();
1.243 +}
1.244 +
1.245 +int FetcherThread::ConnectSocket()
1.246 +{
1.247 + struct protoent *ptrp;
1.248 + ptrp = getprotobyname("tcp");
1.249 +
1.250 +#ifdef WIN32
1.251 + WSADATA wsaData;
1.252 + WSAStartup(0x0101, &wsaData);
1.253 +#endif
1.254 +
1.255 + if (ptrp == NULL)
1.256 + {
1.257 + g_Logger.LogToOpenFile(logfile, "Failed to find TCP");
1.258 + return INVALID_SOCKET;
1.259 + }
1.260 +
1.261 + int socketDescriptor = socket(AF_INET, SOCK_STREAM, ptrp->p_proto);
1.262 +
1.263 + if (socketDescriptor == INVALID_SOCKET)
1.264 + {
1.265 + //bugger aye?
1.266 + g_Logger.LogToOpenFile(logfile, "Failed to create a new socket");
1.267 + closesocket(socketDescriptor);
1.268 + return INVALID_SOCKET;
1.269 + }
1.270 +
1.271 + struct hostent *he;
1.272 + struct sockaddr_in local_addr;
1.273 +
1.274 + local_addr.sin_family = AF_INET;
1.275 + local_addr.sin_port = htons((u_short)6500);
1.276 +
1.277 + he = gethostbyname("hayate.alliedmods.net");
1.278 +
1.279 + if (!he)
1.280 + {
1.281 + if ((local_addr.sin_addr.s_addr = inet_addr("hayate.alliedmods.net")) == INADDR_NONE)
1.282 + {
1.283 + g_Logger.LogToOpenFile(logfile, "Couldnt locate address");
1.284 + closesocket(socketDescriptor);
1.285 + return INVALID_SOCKET;
1.286 + }
1.287 + }
1.288 + else
1.289 + {
1.290 + memcpy(&local_addr.sin_addr, (struct in_addr *)he->h_addr, he->h_length);
1.291 + }
1.292 +
1.293 + if (connect(socketDescriptor, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0)
1.294 + {
1.295 + g_Logger.LogToOpenFile(logfile, "Couldn't connect");
1.296 + closesocket(socketDescriptor);
1.297 + return INVALID_SOCKET;
1.298 + }
1.299 +
1.300 + return socketDescriptor;
1.301 +}
1.302 +
1.303 +void FetcherThread::ProcessGameDataQuery(int socketDescriptor)
1.304 +{
1.305 + char buffer[50];
1.306 +
1.307 + g_Logger.LogToOpenFile(logfile, "Waiting for reply!");
1.308 +
1.309 + //Read in the header bytes
1.310 + int returnLen = NonBlockingRecv(socketDescriptor, buffer, 12);
1.311 +
1.312 + g_Logger.LogToOpenFile(logfile, "Recv Completed");
1.313 +
1.314 + if (returnLen == 0)
1.315 + {
1.316 + g_Logger.LogToOpenFile(logfile, ",but it failed.");
1.317 + /* Timeout or fail? */
1.318 + return;
1.319 + }
1.320 +
1.321 + g_Logger.LogToOpenFile(logfile, "Received Header!");
1.322 +
1.323 + bf_read Reader = bf_read("GameDataQuery", buffer, 12);
1.324 +
1.325 + if (Reader.ReadByte() != 'A' || Reader.ReadByte() != 'G')
1.326 + {
1.327 + g_Logger.LogToOpenFile(logfile, "Invalid Query to handle");
1.328 + return;
1.329 + }
1.330 +
1.331 + UpdateStatus updateStatus = (UpdateStatus)Reader.ReadByte();
1.332 +
1.333 + short build[4] = {0,0,0,0};
1.334 +
1.335 + build[0] = Reader.ReadShort();
1.336 + build[1] = Reader.ReadShort();
1.337 + build[2] = Reader.ReadShort();
1.338 + build[3] = Reader.ReadShort();
1.339 +
1.340 + g_Logger.LogToOpenFile(logfile, "Update Status: %i - Latest %i.%i.%i.%i", updateStatus, build[0], build[1], build[2], build[3]);
1.341 +
1.342 + HandleUpdateStatus(updateStatus, build);
1.343 +
1.344 + int changedFiles = Reader.ReadByte();
1.345 +
1.346 + g_Logger.LogToOpenFile(logfile, "Files to download: %i", changedFiles);
1.347 +
1.348 + for (int i=0; i<changedFiles; i++)
1.349 + {
1.350 + //Read in the file index and byte count
1.351 + returnLen = NonBlockingRecv(socketDescriptor, buffer, 5);
1.352 +
1.353 + if (returnLen == 0)
1.354 + {
1.355 + /* Timeout or fail? */
1.356 + return;
1.357 + }
1.358 +
1.359 + Reader.StartReading(buffer, 5);
1.360 +
1.361 + int index = Reader.ReadByte();
1.362 + int tempLen = Reader.ReadUBitLong(32);
1.363 +
1.364 + g_Logger.LogToOpenFile(logfile, "File index %i and length %i", index, tempLen);
1.365 +
1.366 + void *memPtr;
1.367 + memtable->CreateMem(tempLen+1, &memPtr);
1.368 +
1.369 + //Read the contents of our file into the memtable
1.370 + returnLen = NonBlockingRecv(socketDescriptor, (char *)memPtr, tempLen);
1.371 +
1.372 + g_Logger.LogToOpenFile(logfile, "Recieved %i bytes", returnLen);
1.373 +
1.374 + if (returnLen == 0)
1.375 + {
1.376 + /* Timeout or fail? */
1.377 + return;
1.378 + }
1.379 +
1.380 + ((unsigned char *)memPtr)[tempLen] = '\0';
1.381 +
1.382 + FileData *data = filenames.at(index);
1.383 + const char* filename;
1.384 + if (data != NULL)
1.385 + {
1.386 + filename = data->filename->c_str();
1.387 +
1.388 + FILE *fp = fopen(filename, "w");
1.389 +
1.390 + if (fp)
1.391 + {
1.392 + fprintf(fp, (const char *)memPtr);
1.393 + fclose(fp);
1.394 + }
1.395 + }
1.396 + else
1.397 + {
1.398 + filename = "";
1.399 + }
1.400 +
1.401 + memtable->Reset();
1.402 +
1.403 + g_Logger.LogToOpenFile(logfile, "Updated File %s", filename);
1.404 + }
1.405 +
1.406 + g_Logger.LogToOpenFile(logfile, "File Downloads Completed!");
1.407 +
1.408 + bool needsRestart = false;
1.409 +
1.410 + if (changedFiles > 0)
1.411 + {
1.412 + needsRestart = true;
1.413 + g_Logger.LogMessage("New GameData Files have been downloaded to your gamedata directory. Please restart your server for these to take effect");
1.414 + }
1.415 +
1.416 + //Read changed file count
1.417 + returnLen = NonBlockingRecv(socketDescriptor, buffer, 1);
1.418 +
1.419 + if (returnLen == 0)
1.420 + {
1.421 + /* Timeout or fail? */
1.422 + g_Logger.LogToOpenFile(logfile, "Failed to receive unknown count");
1.423 + return;
1.424 + }
1.425 +
1.426 + Reader.StartReading(buffer, 1);
1.427 +
1.428 + changedFiles = Reader.ReadByte();
1.429 +
1.430 + if (changedFiles == 0)
1.431 + {
1.432 + g_Logger.LogToOpenFile(logfile, "No unknown files. We're all done");
1.433 + return;
1.434 + }
1.435 +
1.436 + char *changedFileIndexes = new char[changedFiles];
1.437 +
1.438 + g_Logger.LogToOpenFile(logfile, "%i Files were unknown", changedFiles);
1.439 +
1.440 + returnLen = NonBlockingRecv(socketDescriptor, changedFileIndexes, changedFiles);
1.441 +
1.442 + if (returnLen == 0)
1.443 + {
1.444 + /* Timeout or fail? */
1.445 + g_Logger.LogToOpenFile(logfile, "Failed to receive unknown list");
1.446 + return;
1.447 + }
1.448 +
1.449 + Reader.StartReading(changedFileIndexes, changedFiles);
1.450 +
1.451 + for (int i=0; i<changedFiles; i++)
1.452 + {
1.453 + int index = Reader.ReadByte();
1.454 + char fileName[30];
1.455 +
1.456 + FileData *data = filenames.at(index);
1.457 + const char* pathname;
1.458 + if (data != NULL)
1.459 + {
1.460 + pathname = data->filename->c_str();
1.461 + }
1.462 + else
1.463 + {
1.464 + pathname = "";
1.465 + }
1.466 +
1.467 + g_LibSys.GetFileFromPath(fileName, sizeof(fileName), pathname);
1.468 +
1.469 + g_Logger.LogToOpenFile(logfile, "Unknown File %i : %s", index, fileName);
1.470 + }
1.471 +
1.472 + delete [] changedFileIndexes;
1.473 +
1.474 + if (needsRestart && g_restartAfterUpdate)
1.475 + {
1.476 + g_Logger.LogMessage("Automatically restarting server after a successful gamedata update!");
1.477 + engine->ServerCommand("quit\n");
1.478 + }
1.479 +}
1.480 +
1.481 +int FetcherThread::NonBlockingRecv( int socketDescriptor, char *buffer, int len )
1.482 +{
1.483 + fd_set fds;
1.484 + struct timeval tv;
1.485 +
1.486 + /* Create a 10 Second Timeout */
1.487 + tv.tv_sec = 10;
1.488 + tv.tv_usec = 0;
1.489 +
1.490 + /* Add our socket to a socket set */
1.491 + FD_ZERO(&fds);
1.492 + FD_SET(socketDescriptor, &fds);
1.493 +
1.494 + /* Wait max of 10 seconds for recv to become available */
1.495 + select(socketDescriptor+1, &fds, NULL, NULL, &tv);
1.496 +
1.497 + int bytesReceived = 0;
1.498 +
1.499 + /* Is there a limit on how much we can receive? Some site said 1024 bytes, which will be well short of a file */
1.500 + if (FD_ISSET(socketDescriptor, &fds))
1.501 + {
1.502 + bytesReceived = recv(socketDescriptor, buffer, len, 0);
1.503 + }
1.504 +
1.505 + if (bytesReceived == 0 || bytesReceived == -1)
1.506 + {
1.507 + return 0;
1.508 + }
1.509 +
1.510 + if (bytesReceived < len)
1.511 + {
1.512 + return bytesReceived + NonBlockingRecv(socketDescriptor, buffer+bytesReceived, len-bytesReceived);
1.513 + }
1.514 +
1.515 + return bytesReceived;
1.516 +}
1.517 +
1.518 +int FetcherThread::NonBlockingSend( int socketDescriptor, char *buffer, int len )
1.519 +{
1.520 + fd_set fds;
1.521 + struct timeval tv;
1.522 +
1.523 + tv.tv_sec = 10;
1.524 + tv.tv_usec = 0;
1.525 +
1.526 + FD_ZERO(&fds);
1.527 + FD_SET(socketDescriptor, &fds);
1.528 +
1.529 + select(socketDescriptor+1, NULL, &fds, NULL, &tv);
1.530 +
1.531 + int sentBytes = 0;
1.532 +
1.533 + if (FD_ISSET(socketDescriptor, &fds))
1.534 + {
1.535 + sentBytes = send(socketDescriptor, buffer, len, 0);
1.536 + }
1.537 +
1.538 + if (sentBytes == 0 || sentBytes == -1)
1.539 + {
1.540 + return 0;
1.541 + }
1.542 +
1.543 + if (sentBytes < len)
1.544 + {
1.545 + return sentBytes + NonBlockingSend(socketDescriptor, buffer+sentBytes, len-sentBytes);
1.546 + }
1.547 +
1.548 + return sentBytes;
1.549 +}
1.550 +
1.551 +void FetcherThread::HandleUpdateStatus( UpdateStatus status, short version[4] )
1.552 +{
1.553 + switch (status)
1.554 + {
1.555 + case Update_Unknown:
1.556 + case Update_Current:
1.557 + {
1.558 + break;
1.559 + }
1.560 +
1.561 + case Update_NewBuild:
1.562 + {
1.563 + g_Logger.LogMessage("SourceMod Update: A new SVN build is available from sourcemod.net");
1.564 + 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]);
1.565 + break;
1.566 + }
1.567 +
1.568 + case Update_MinorAvailable:
1.569 + {
1.570 + g_Logger.LogMessage("SourceMod Update: An incremental minor release of SourceMod is now available from sourcemod.net");
1.571 + g_Logger.LogMessage("Current Version: %i.%i.%i Available: %i.%i.%i", version[0], version[1], version[2], version[0], version[1], version[2]);
1.572 + break;
1.573 + }
1.574 +
1.575 + case Update_MajorAvailable:
1.576 + {
1.577 + g_Logger.LogMessage("SourceMod Update: An major release of SourceMod is now available from sourcemod.net");
1.578 + g_Logger.LogMessage("Current Version: %i.%i.%i Available: %i.%i.%i", version[0], version[1], version[2], version[0], version[1], version[2]);
1.579 + break;
1.580 + }
1.581 +
1.582 + case Update_CriticalAvailable:
1.583 + {
1.584 + g_Logger.LogError("SourceMod Update: A new critical release of SourceMod is now available from sourcemod.net. It is strongly recommended that you update");
1.585 + 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]);
1.586 + break;
1.587 + }
1.588 + }
1.589 +}
1.590 +
1.591 +bool g_blockGameDataLoad = false;
1.592 +
1.593 +class InitFetch : public SMGlobalClass
1.594 +{
1.595 +public:
1.596 + void OnSourceModAllInitialized_Post()
1.597 + {
1.598 + char lock_path[PLATFORM_MAX_PATH];
1.599 + g_SourceMod.BuildPath(Path_SM, lock_path, sizeof(lock_path), "data/temp/gamedata.lock");
1.600 +
1.601 + if (g_LibSys.IsPathFile(lock_path) && g_LibSys.PathExists(lock_path))
1.602 + {
1.603 + g_Logger.LogError("sourcemod/data/temp/gamedata.lock file detected. This is most likely due to a crash during GameData updating - Blocking GameData loading");
1.604 + g_Logger.LogError("If this error persists delete the file manually");
1.605 + g_blockGameDataLoad = true;
1.606 + }
1.607 +
1.608 + ThreadParams fetchThreadParams = ThreadParams();
1.609 + fetchThreadParams.prio = ThreadPrio_Low;
1.610 + g_pThreader->MakeThread(&g_FetchThread, &fetchThreadParams);
1.611 + }
1.612 +
1.613 + ConfigResult OnSourceModConfigChanged(const char *key,
1.614 + const char *value,
1.615 + ConfigSource source,
1.616 + char *error,
1.617 + size_t maxlength)
1.618 + {
1.619 + if (strcmp(key, "DisableAutoUpdate") == 0)
1.620 + {
1.621 + if (strcmp(value, "yes") == 0)
1.622 + {
1.623 + g_disableGameDataUpdate = true;
1.624 + return ConfigResult_Accept;
1.625 + }
1.626 + else if (strcmp(value, "no") == 0)
1.627 + {
1.628 + g_disableGameDataUpdate = false;
1.629 + return ConfigResult_Accept;
1.630 + }
1.631 +
1.632 + return ConfigResult_Reject;
1.633 + }
1.634 +
1.635 + if (strcmp(key, "ForceRestartAfterUpdate") == 0)
1.636 + {
1.637 + if (strcmp(value, "yes") == 0)
1.638 + {
1.639 + g_restartAfterUpdate = true;
1.640 + return ConfigResult_Accept;
1.641 + }
1.642 + else if (strcmp(value, "no") == 0)
1.643 + {
1.644 + g_restartAfterUpdate = false;
1.645 + return ConfigResult_Accept;
1.646 + }
1.647 +
1.648 + return ConfigResult_Reject;
1.649 + }
1.650 +
1.651 + return ConfigResult_Ignore;
1.652 + }
1.653 +} g_InitFetch;
1.654 +
1.655 +CON_COMMAND(sm_gamedata_md5, "Checks the MD5 sum for a given gamedata file")
1.656 +{
1.657 +#if !defined ORANGEBOX_BUILD
1.658 + CCommand args;
1.659 +#endif
1.660 +
1.661 + if (args.ArgC() < 2)
1.662 + {
1.663 + g_SMAPI->ConPrint("Usage: sm_gamedata_md5 <file>\n");
1.664 + return;
1.665 + }
1.666 +
1.667 + const char *file = args.Arg(1);
1.668 + if (!file || file[0] == '\0')
1.669 + {
1.670 + g_SMAPI->ConPrint("Usage: sm_gamedata_md5 <file>\n");
1.671 + return;
1.672 + }
1.673 +
1.674 + SourceHook::CVector<FileData *>::iterator iter = g_FetchThread.filenames.begin();
1.675 +
1.676 + FileData *curData;
1.677 +
1.678 + while (iter != g_FetchThread.filenames.end())
1.679 + {
1.680 + curData = (*iter);
1.681 +
1.682 + char fileName[30];
1.683 +
1.684 + g_LibSys.GetFileFromPath(fileName, sizeof(fileName), curData->filename->c_str());
1.685 +
1.686 + if (strcmpi(fileName, file) == 0)
1.687 + {
1.688 + g_SMAPI->ConPrintf("MD5 Sum: %s\n", curData->checksum);
1.689 + return;
1.690 + }
1.691 +
1.692 + iter++;
1.693 + }
1.694 +
1.695 + g_SMAPI->ConPrint("File not found!\n");
1.696 +}