684 lines
23 KiB
C++
684 lines
23 KiB
C++
/*
|
|
* Original work: Copyright (c) 2014, Oculus VR, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* RakNet License.txt file in the licenses directory of this source tree. An additional grant
|
|
* of patent rights can be found in the RakNet Patents.txt file in the same directory.
|
|
*
|
|
*
|
|
* Modified work: Copyright (c) 2017, SLikeSoft UG (haftungsbeschränkt)
|
|
*
|
|
* This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
|
|
* license found in the license.txt file in the root directory of this source tree.
|
|
*/
|
|
|
|
#include "AutopatcherClient.h"
|
|
#include "slikenet/DirectoryDeltaTransfer.h"
|
|
#include "slikenet/FileList.h"
|
|
#include "slikenet/StringCompressor.h"
|
|
#include "slikenet/peerinterface.h"
|
|
#include "slikenet/FileListTransfer.h"
|
|
#include "slikenet/FileListTransferCBInterface.h"
|
|
#include "slikenet/BitStream.h"
|
|
#include "slikenet/MessageIdentifiers.h"
|
|
#include "slikenet/AutopatcherPatchContext.h"
|
|
#include "ApplyPatch.h"
|
|
#include "slikenet/FileOperations.h"
|
|
//#include "slikenet/DR_SHA1.h"
|
|
#include <stdio.h>
|
|
#include "slikenet/FileOperations.h"
|
|
#include "slikenet/assert.h"
|
|
#include "slikenet/ThreadPool.h"
|
|
#include "slikenet/linux_adapter.h"
|
|
#include "slikenet/osx_adapter.h"
|
|
|
|
using namespace SLNet;
|
|
|
|
#include "slikenet/SuperFastHash.h"
|
|
static const unsigned HASH_LENGTH=4;
|
|
|
|
#define COPY_ON_RESTART_EXTENSION ".patched.tmp"
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
PatchContext AutopatcherClientCBInterface::ApplyPatchBase(const char *oldFilePath, char **newFileContents, unsigned int *newFileSize, char *patchContents, unsigned int patchSize, uint32_t patchAlgorithm)
|
|
{
|
|
// unused parameters
|
|
(void)patchAlgorithm;
|
|
|
|
return ApplyPatchBSDiff(oldFilePath, newFileContents, newFileSize, patchContents, patchSize);
|
|
}
|
|
|
|
PatchContext AutopatcherClientCBInterface::ApplyPatchBSDiff(const char *oldFilePath, char **newFileContents, unsigned int *newFileSize, char *patchContents, unsigned int patchSize)
|
|
{
|
|
FILE *fp;
|
|
if (fopen_s(&fp, oldFilePath, "rb")!=0)
|
|
return PC_ERROR_PATCH_TARGET_MISSING;
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
unsigned int prePatchLength = ftell(fp);
|
|
fseek(fp, 0, SEEK_SET);
|
|
char *prePatchFile = (char*) rakMalloc_Ex(prePatchLength, _FILE_AND_LINE_);
|
|
fread(prePatchFile, prePatchLength, 1, fp);
|
|
fclose(fp);
|
|
|
|
bool result = ApplyPatch(prePatchFile, prePatchLength, newFileContents, newFileSize, patchContents, patchSize);
|
|
rakFree_Ex(prePatchFile, _FILE_AND_LINE_);
|
|
|
|
if (result==false)
|
|
return PC_ERROR_PATCH_APPLICATION_FAILURE;
|
|
|
|
return PC_WRITE_FILE;
|
|
}
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
struct AutopatcherClientThreadInfo
|
|
{
|
|
FileListTransferCBInterface::OnFileStruct onFileStruct;
|
|
char applicationDirectory[512];
|
|
PatchContext result;
|
|
// unsigned prePatchLength;
|
|
// char *prePatchFile;
|
|
|
|
// postPatchFile is passed in PC_NOTICE_WILL_COPY_ON_RESTART
|
|
char *postPatchFile;
|
|
unsigned postPatchLength;
|
|
AutopatcherClientCBInterface *cbInterface;
|
|
};
|
|
// -----------------------------------------------------------------
|
|
AutopatcherClientThreadInfo* AutopatcherClientWorkerThread(AutopatcherClientThreadInfo* input, bool *returnOutput, void* perThreadData)
|
|
{
|
|
// unused parameters
|
|
(void)perThreadData;
|
|
|
|
char fullPathToDir[1024];
|
|
*returnOutput=true;
|
|
|
|
strcpy_s(fullPathToDir, input->applicationDirectory);
|
|
strcat_s(fullPathToDir, input->onFileStruct.fileName);
|
|
if (input->onFileStruct.context.op==PC_WRITE_FILE)
|
|
{
|
|
if (WriteFileWithDirectories(fullPathToDir, (char*)input->onFileStruct.fileData, input->onFileStruct.byteLengthOfThisFile)==false)
|
|
{
|
|
char newDir[1024];
|
|
strcpy_s(newDir, fullPathToDir);
|
|
strcat_s(newDir, COPY_ON_RESTART_EXTENSION);
|
|
if (WriteFileWithDirectories(newDir, (char*)input->onFileStruct.fileData, input->onFileStruct.byteLengthOfThisFile))
|
|
{
|
|
input->result=PC_NOTICE_WILL_COPY_ON_RESTART;
|
|
}
|
|
else
|
|
{
|
|
input->result=PC_ERROR_FILE_WRITE_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
input->result=(PatchContext) input->onFileStruct.context.op;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RakAssert(input->onFileStruct.context.op==PC_HASH_1_WITH_PATCH || input->onFileStruct.context.op==PC_HASH_2_WITH_PATCH);
|
|
|
|
// CSHA1 sha1;
|
|
|
|
|
|
// printf("apply patch %i bytes\n", byteLengthOfThisFile-SHA1_LENGTH);
|
|
// for (int i=0; i < byteLengthOfThisFile-SHA1_LENGTH; i++)
|
|
// printf("%i ", fileData[SHA1_LENGTH+i]);
|
|
// printf("\n");
|
|
int hashMultiplier;
|
|
if (input->onFileStruct.context.op==PC_HASH_1_WITH_PATCH)
|
|
hashMultiplier=1;
|
|
else
|
|
hashMultiplier=2; // else op==PC_HASH_2_WITH_PATCH
|
|
|
|
PatchContext result = input->cbInterface->ApplyPatchBase(fullPathToDir, &input->postPatchFile, &input->postPatchLength, (char*)input->onFileStruct.fileData+HASH_LENGTH*hashMultiplier, input->onFileStruct.byteLengthOfThisFile-HASH_LENGTH*hashMultiplier, input->onFileStruct.context.flnc_extraData2);
|
|
if (result == PC_ERROR_PATCH_APPLICATION_FAILURE || input->result==PC_ERROR_PATCH_TARGET_MISSING)
|
|
{
|
|
input->result=result;
|
|
return input;
|
|
}
|
|
|
|
unsigned int hash = SuperFastHash(input->postPatchFile, input->postPatchLength);
|
|
if (SLNet::BitStream::DoEndianSwap())
|
|
SLNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash));
|
|
|
|
//if (memcmp(sha1.GetHash(), input->onFileStruct.fileData, HASH_LENGTH)!=0)
|
|
|
|
if (memcmp(&hash, input->onFileStruct.fileData+HASH_LENGTH*(hashMultiplier-1), HASH_LENGTH)!=0)
|
|
{
|
|
input->result=PC_ERROR_PATCH_RESULT_CHECKSUM_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
// Write postPatchFile over the existing file
|
|
if (WriteFileWithDirectories(fullPathToDir, (char*)input->postPatchFile, input->postPatchLength)==false)
|
|
{
|
|
char newDir[1024];
|
|
strcpy_s(newDir, fullPathToDir);
|
|
strcat_s(newDir, COPY_ON_RESTART_EXTENSION);
|
|
if (WriteFileWithDirectories(newDir, (char*)input->postPatchFile, input->postPatchLength))
|
|
{
|
|
input->result=PC_NOTICE_WILL_COPY_ON_RESTART;
|
|
}
|
|
else
|
|
{
|
|
input->result=PC_ERROR_FILE_WRITE_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
input->result=(PatchContext)input->onFileStruct.context.op;
|
|
}
|
|
}
|
|
}
|
|
|
|
return input;
|
|
}
|
|
// -----------------------------------------------------------------
|
|
namespace SLNet
|
|
{
|
|
class AutopatcherClientCallback : public FileListTransferCBInterface
|
|
{
|
|
public:
|
|
ThreadPool<AutopatcherClientThreadInfo*,AutopatcherClientThreadInfo*> threadPool;
|
|
char applicationDirectory[512];
|
|
AutopatcherClientCBInterface *onFileCallback;
|
|
AutopatcherClient *client;
|
|
bool downloadComplete;
|
|
bool canDeleteUser;
|
|
|
|
AutopatcherClientCallback(void)
|
|
{
|
|
threadPool.StartThreads(1,0);
|
|
canDeleteUser=false;
|
|
downloadComplete=false;
|
|
}
|
|
virtual ~AutopatcherClientCallback(void)
|
|
{
|
|
StopThreads();
|
|
}
|
|
void StopThreads(void)
|
|
{
|
|
threadPool.StopThreads();
|
|
RakAssert(threadPool.NumThreadsWorking()==0);
|
|
|
|
unsigned i;
|
|
AutopatcherClientThreadInfo* info;
|
|
for (i=0; i < threadPool.InputSize(); i++)
|
|
{
|
|
info = threadPool.GetInputAtIndex(i);
|
|
// if (info->prePatchFile)
|
|
// rakFree_Ex(info->prePatchFile, _FILE_AND_LINE_ );
|
|
if (info->postPatchFile)
|
|
rakFree_Ex(info->postPatchFile, _FILE_AND_LINE_ );
|
|
if (info->onFileStruct.fileData)
|
|
rakFree_Ex(info->onFileStruct.fileData, _FILE_AND_LINE_ );
|
|
SLNet::OP_DELETE(info, _FILE_AND_LINE_);
|
|
}
|
|
threadPool.ClearInput();
|
|
for (i=0; i < threadPool.OutputSize(); i++)
|
|
{
|
|
info = threadPool.GetOutputAtIndex(i);
|
|
// if (info->prePatchFile)
|
|
// rakFree_Ex(info->prePatchFile, _FILE_AND_LINE_ );
|
|
if (info->postPatchFile)
|
|
rakFree_Ex(info->postPatchFile, _FILE_AND_LINE_ );
|
|
if (info->onFileStruct.fileData)
|
|
rakFree_Ex(info->onFileStruct.fileData, _FILE_AND_LINE_ );
|
|
SLNet::OP_DELETE(info, _FILE_AND_LINE_);
|
|
}
|
|
threadPool.ClearOutput();
|
|
}
|
|
// Update is run in the user thread
|
|
virtual bool Update(void)
|
|
{
|
|
if (threadPool.HasOutputFast() && threadPool.HasOutput())
|
|
{
|
|
AutopatcherClientThreadInfo *threadInfo = threadPool.GetOutput();
|
|
// #med - add proper range-check for threadInfo->result
|
|
threadInfo->onFileStruct.context.op=static_cast<unsigned char>(threadInfo->result);
|
|
switch (threadInfo->result)
|
|
{
|
|
case PC_NOTICE_WILL_COPY_ON_RESTART:
|
|
{
|
|
client->CopyAndRestart(threadInfo->onFileStruct.fileName);
|
|
if (threadInfo->onFileStruct.context.op==PC_WRITE_FILE)
|
|
{
|
|
// Regular file in use but we can write the temporary file. Restart and copy it over the existing
|
|
onFileCallback->OnFile(&threadInfo->onFileStruct);
|
|
}
|
|
else
|
|
{
|
|
// Regular file in use but we can write the temporary file. Restart and copy it over the existing
|
|
rakFree_Ex(threadInfo->onFileStruct.fileData, _FILE_AND_LINE_ );
|
|
threadInfo->onFileStruct.fileData=threadInfo->postPatchFile;
|
|
onFileCallback->OnFile(&threadInfo->onFileStruct);
|
|
threadInfo->onFileStruct.fileData=0;
|
|
}
|
|
}
|
|
break;
|
|
case PC_ERROR_FILE_WRITE_FAILURE:
|
|
{
|
|
if (threadInfo->onFileStruct.context.op==PC_WRITE_FILE)
|
|
{
|
|
onFileCallback->OnFile(&threadInfo->onFileStruct);
|
|
}
|
|
else
|
|
{
|
|
rakFree_Ex(threadInfo->onFileStruct.fileData, _FILE_AND_LINE_ );
|
|
threadInfo->onFileStruct.fileData=threadInfo->postPatchFile;
|
|
threadInfo->onFileStruct.byteLengthOfThisFile=threadInfo->postPatchLength;
|
|
onFileCallback->OnFile(&threadInfo->onFileStruct);
|
|
threadInfo->onFileStruct.fileData=0;
|
|
}
|
|
}
|
|
break;
|
|
case PC_ERROR_PATCH_TARGET_MISSING:
|
|
{
|
|
onFileCallback->OnFile(&threadInfo->onFileStruct);
|
|
client->Redownload(threadInfo->onFileStruct.fileName);
|
|
}
|
|
break;
|
|
case PC_ERROR_PATCH_APPLICATION_FAILURE:
|
|
{
|
|
// Failure - signal class and download this file.
|
|
onFileCallback->OnFile(&threadInfo->onFileStruct);
|
|
client->Redownload(threadInfo->onFileStruct.fileName);
|
|
}
|
|
break;
|
|
case PC_ERROR_PATCH_RESULT_CHECKSUM_FAILURE:
|
|
{
|
|
// Failure - signal class and download this file.
|
|
onFileCallback->OnFile(&threadInfo->onFileStruct);
|
|
client->Redownload(threadInfo->onFileStruct.fileName);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
if (threadInfo->onFileStruct.context.op==PC_WRITE_FILE)
|
|
{
|
|
onFileCallback->OnFile(&threadInfo->onFileStruct);
|
|
}
|
|
else
|
|
{
|
|
rakFree_Ex(threadInfo->onFileStruct.fileData, _FILE_AND_LINE_ );
|
|
threadInfo->onFileStruct.fileData=threadInfo->postPatchFile;
|
|
onFileCallback->OnFile(&threadInfo->onFileStruct);
|
|
threadInfo->onFileStruct.fileData=0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// if (threadInfo->prePatchFile)
|
|
// rakFree_Ex(threadInfo->prePatchFile, _FILE_AND_LINE_ );
|
|
if (threadInfo->postPatchFile)
|
|
rakFree_Ex(threadInfo->postPatchFile, _FILE_AND_LINE_ );
|
|
if (threadInfo->onFileStruct.fileData)
|
|
rakFree_Ex(threadInfo->onFileStruct.fileData, _FILE_AND_LINE_ );
|
|
SLNet::OP_DELETE(threadInfo, _FILE_AND_LINE_);
|
|
}
|
|
|
|
// If both input and output are empty, we are done.
|
|
if (onFileCallback->Update()==false)
|
|
canDeleteUser=true;
|
|
|
|
if ( downloadComplete &&
|
|
canDeleteUser &&
|
|
threadPool.IsWorking()==false)
|
|
{
|
|
// Stop threads before calling OnThreadCompletion, in case the other thread starts a new instance of this thread.
|
|
StopThreads();
|
|
client->OnThreadCompletion();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
virtual bool OnDownloadComplete(DownloadCompleteStruct *dcs)
|
|
{
|
|
downloadComplete=true;
|
|
if (onFileCallback->OnDownloadComplete(dcs)==false)
|
|
{
|
|
canDeleteUser=true;
|
|
}
|
|
return true;
|
|
};
|
|
virtual void OnDereference(void)
|
|
{
|
|
onFileCallback->OnDereference();
|
|
StopThreads();
|
|
}
|
|
virtual bool OnFile(OnFileStruct *onFileStruct)
|
|
{
|
|
AutopatcherClientThreadInfo *inStruct = SLNet::OP_NEW<AutopatcherClientThreadInfo>( _FILE_AND_LINE_ );
|
|
memset(inStruct,0,sizeof(AutopatcherClientThreadInfo));
|
|
// inStruct->prePatchFile=0;
|
|
inStruct->postPatchFile=0;
|
|
inStruct->cbInterface=onFileCallback;
|
|
memcpy(&(inStruct->onFileStruct), onFileStruct, sizeof(OnFileStruct));
|
|
strcpy_s(inStruct->applicationDirectory,applicationDirectory);
|
|
if (onFileStruct->context.op==PC_HASH_1_WITH_PATCH || onFileStruct->context.op==PC_HASH_2_WITH_PATCH)
|
|
onFileStruct->context.op=PC_NOTICE_FILE_DOWNLOADED_PATCH;
|
|
else
|
|
onFileStruct->context.op=PC_NOTICE_FILE_DOWNLOADED;
|
|
onFileCallback->OnFile(onFileStruct);
|
|
threadPool.AddInput(AutopatcherClientWorkerThread, inStruct);
|
|
|
|
// Return false means don't delete OnFileStruct::data
|
|
return false;
|
|
}
|
|
virtual void OnFileProgress(FileProgressStruct *fps)
|
|
{
|
|
char fullPathToDir[1024];
|
|
|
|
if (fps->onFileStruct->fileName)
|
|
{
|
|
strcpy_s(fullPathToDir, applicationDirectory);
|
|
strcat_s(fullPathToDir, fps->onFileStruct->fileName);
|
|
onFileCallback->OnFileProgress(fps);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
AutopatcherClient::AutopatcherClient()
|
|
{
|
|
serverId=UNASSIGNED_SYSTEM_ADDRESS;
|
|
// #med - use unsigned short max?
|
|
serverIdIndex=static_cast<unsigned short>(-1);
|
|
applicationDirectory[0]=0;
|
|
fileListTransfer=0;
|
|
priority=HIGH_PRIORITY;
|
|
orderingChannel=0;
|
|
serverDate=0;
|
|
userCB=0;
|
|
processThreadCompletion=false;
|
|
}
|
|
AutopatcherClient::~AutopatcherClient()
|
|
{
|
|
Clear();
|
|
}
|
|
void AutopatcherClient::Clear(void)
|
|
{
|
|
if (fileListTransfer)
|
|
fileListTransfer->RemoveReceiver(serverId);
|
|
serverId=UNASSIGNED_SYSTEM_ADDRESS;
|
|
setId=(unsigned short)-1;
|
|
redownloadList.Clear();
|
|
copyAndRestartList.Clear();
|
|
}
|
|
void AutopatcherClient::SetUploadSendParameters(PacketPriority _priority, char _orderingChannel)
|
|
{
|
|
priority=_priority;
|
|
orderingChannel=_orderingChannel;
|
|
}
|
|
void AutopatcherClient::SetFileListTransferPlugin(FileListTransfer *flt)
|
|
{
|
|
fileListTransfer=flt;
|
|
}
|
|
double AutopatcherClient::GetServerDate(void) const
|
|
{
|
|
return serverDate;
|
|
}
|
|
void AutopatcherClient::CancelDownload(void)
|
|
{
|
|
fileListTransfer->CancelReceive(setId);
|
|
Clear();
|
|
}
|
|
void AutopatcherClient::OnThreadCompletion(void)
|
|
{
|
|
processThreadCompletionMutex.Lock();
|
|
processThreadCompletion=true;
|
|
processThreadCompletionMutex.Unlock();
|
|
}
|
|
bool AutopatcherClient::IsPatching(void) const
|
|
{
|
|
return fileListTransfer->IsHandlerActive(setId);
|
|
}
|
|
bool AutopatcherClient::PatchApplication(const char *_applicationName, const char *_applicationDirectory, double lastUpdateDate, SystemAddress host, AutopatcherClientCBInterface *onFileCallback, const char *restartOutputFilename, const char *pathToRestartExe)
|
|
{
|
|
RakAssert(applicationName);
|
|
RakAssert(applicationDirectory);
|
|
RakAssert(pathToRestartExe);
|
|
RakAssert(restartOutputFilename);
|
|
|
|
// if (rakPeerInterface->GetIndexFromSystemAddress(host)==-1)
|
|
// return false;
|
|
if (IsPatching())
|
|
return false; // Already in the middle of patching.
|
|
|
|
strcpy_s(applicationDirectory, _applicationDirectory);
|
|
FileList::FixEndingSlash(applicationDirectory, 512);
|
|
strcpy_s(applicationName, _applicationName);
|
|
serverId=host;
|
|
patchComplete=false;
|
|
userCB=onFileCallback;
|
|
strcpy_s(copyOnRestartOut, restartOutputFilename);
|
|
strcpy_s(restartExe, pathToRestartExe);
|
|
processThreadCompletionMutex.Lock();
|
|
processThreadCompletion=false;
|
|
processThreadCompletionMutex.Unlock();
|
|
|
|
SLNet::BitStream outBitStream;
|
|
outBitStream.Write((unsigned char)ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE);
|
|
StringCompressor::Instance()->EncodeString(applicationName, 512, &outBitStream);
|
|
outBitStream.Write(lastUpdateDate);
|
|
SendUnified(&outBitStream, priority, RELIABLE_ORDERED, orderingChannel, host, false);
|
|
return true;
|
|
}
|
|
void AutopatcherClient::Update(void)
|
|
{
|
|
processThreadCompletionMutex.Lock();
|
|
if (processThreadCompletion)
|
|
{
|
|
processThreadCompletion=false;
|
|
processThreadCompletionMutex.Unlock();
|
|
|
|
fileListTransfer->RemoveReceiver(serverId);
|
|
|
|
// If redownload list, process it
|
|
if (redownloadList.fileList.Size())
|
|
{
|
|
SLNet::BitStream outBitStream;
|
|
AutopatcherClientCallback *transferCallback;
|
|
transferCallback = SLNet::OP_NEW<AutopatcherClientCallback>( _FILE_AND_LINE_ );
|
|
strcpy_s(transferCallback->applicationDirectory, applicationDirectory);
|
|
transferCallback->onFileCallback=userCB;
|
|
transferCallback->client=this;
|
|
setId = fileListTransfer->SetupReceive(transferCallback, true, serverId);
|
|
|
|
// Ask for patches for the files in the list that are different from what we have.
|
|
outBitStream.Write((unsigned char)ID_AUTOPATCHER_GET_PATCH);
|
|
outBitStream.Write(setId);
|
|
double lastUpdateData=0;
|
|
outBitStream.Write(lastUpdateData);
|
|
StringCompressor::Instance()->EncodeString(applicationName, 512, &outBitStream);
|
|
redownloadList.Serialize(&outBitStream);
|
|
SendUnified(&outBitStream, priority, RELIABLE_ORDERED, orderingChannel, serverId, false);
|
|
redownloadList.Clear();
|
|
}
|
|
else if (copyAndRestartList.fileList.Size())
|
|
{
|
|
Packet *p = AllocatePacketUnified(1);
|
|
p->bitSize=p->length*8;
|
|
p->data[0]=ID_AUTOPATCHER_RESTART_APPLICATION;
|
|
p->systemAddress=serverId;
|
|
p->systemAddress.systemIndex=serverIdIndex;
|
|
PushBackPacketUnified(p,false);
|
|
|
|
FILE *fp;
|
|
errno_t error = fopen_s(&fp, copyOnRestartOut, "wt");
|
|
RakAssert(error == 0);
|
|
if (error == 0)
|
|
{
|
|
fprintf(fp, "#Sleep 1000\n");
|
|
unsigned i;
|
|
for (i=0; i < copyAndRestartList.fileList.Size(); i++)
|
|
{
|
|
#ifdef _WIN32
|
|
fprintf(fp, "del /q \"%s%s\"\n", applicationDirectory, copyAndRestartList.fileList[i].filename.C_String());
|
|
RakString sourceFn = copyAndRestartList.fileList[i].filename;
|
|
RakString bareFilename = sourceFn;
|
|
bareFilename.StartAfterLastCharacter('/');
|
|
fprintf(fp, "rename \"%s%s%s\" \"%s\"\n", applicationDirectory, bareFilename.C_String(), COPY_ON_RESTART_EXTENSION, copyAndRestartList.fileList[i].filename.C_String());
|
|
#else
|
|
fprintf(fp, "rm -f \"%s%s\"\n", applicationDirectory, copyAndRestartList.fileList[i].filename.C_String());
|
|
fprintf(fp, "mv \"%s%s%s\" \"%s\"\n", applicationDirectory, copyAndRestartList.fileList[i].filename.C_String(), COPY_ON_RESTART_EXTENSION, copyAndRestartList.fileList[i].filename.C_String());
|
|
#endif
|
|
}
|
|
#ifdef _WIN32
|
|
fprintf(fp, "#CreateProcess \"%s\"\n", restartExe);
|
|
#else
|
|
fprintf(fp, "chmod +x \"%s\"\n", restartExe);
|
|
fprintf(fp, "#CreateProcess \"%s\" &\n", restartExe);
|
|
#endif
|
|
fprintf(fp, "#DeleteThisFile\n");
|
|
fclose(fp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Packet *p = AllocatePacketUnified(1);
|
|
p->bitSize=p->length*8;
|
|
p->data[0]=ID_AUTOPATCHER_FINISHED;
|
|
p->systemAddress=serverId;
|
|
p->systemAddress.systemIndex=serverIdIndex;
|
|
PushBackPacketUnified(p,false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
processThreadCompletionMutex.Unlock();
|
|
}
|
|
}
|
|
void AutopatcherClient::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
|
|
{
|
|
// unused parameters
|
|
(void)rakNetGUID;
|
|
(void)lostConnectionReason;
|
|
|
|
if (systemAddress==serverId)
|
|
Clear();
|
|
}
|
|
PluginReceiveResult AutopatcherClient::OnReceive(Packet *packet)
|
|
{
|
|
switch (packet->data[0])
|
|
{
|
|
case ID_AUTOPATCHER_CREATION_LIST:
|
|
return OnCreationList(packet);
|
|
case ID_AUTOPATCHER_DELETION_LIST:
|
|
OnDeletionList(packet);
|
|
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
|
case ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR:
|
|
case ID_AUTOPATCHER_CANNOT_DOWNLOAD_ORIGINAL_UNMODIFIED_FILES:
|
|
fileListTransfer->RemoveReceiver(serverId);
|
|
Clear();
|
|
return RR_CONTINUE_PROCESSING;
|
|
case ID_AUTOPATCHER_FINISHED_INTERNAL:
|
|
return OnDownloadFinishedInternal(packet);
|
|
case ID_AUTOPATCHER_FINISHED:
|
|
return OnDownloadFinished(packet);
|
|
}
|
|
return RR_CONTINUE_PROCESSING;
|
|
}
|
|
void AutopatcherClient::OnShutdown(void)
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
PluginReceiveResult AutopatcherClient::OnCreationList(Packet *packet)
|
|
{
|
|
RakAssert(fileListTransfer);
|
|
if (packet->systemAddress!=serverId)
|
|
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
|
|
|
SLNet::BitStream inBitStream(packet->data, packet->length, false);
|
|
SLNet::BitStream outBitStream;
|
|
FileList remoteFileList, missingOrChanged;
|
|
inBitStream.IgnoreBits(8);
|
|
if (remoteFileList.Deserialize(&inBitStream)==false)
|
|
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
|
|
|
inBitStream.Read(serverDate);
|
|
double patchApplicationLastUpdateDate;
|
|
inBitStream.Read(patchApplicationLastUpdateDate);
|
|
|
|
// Go through the list of hashes. For each file we already have, remove it from the list.
|
|
remoteFileList.ListMissingOrChangedFiles(applicationDirectory, &missingOrChanged, true, false);
|
|
|
|
if (missingOrChanged.fileList.Size()==0)
|
|
{
|
|
packet->data[0]=ID_AUTOPATCHER_FINISHED;
|
|
return RR_CONTINUE_PROCESSING; // Pass to user
|
|
}
|
|
|
|
// Prepare the transfer plugin to get a file list.
|
|
AutopatcherClientCallback *transferCallback;
|
|
transferCallback = SLNet::OP_NEW<AutopatcherClientCallback>( _FILE_AND_LINE_ );
|
|
strcpy_s(transferCallback->applicationDirectory, applicationDirectory);
|
|
transferCallback->onFileCallback=userCB;
|
|
transferCallback->client=this;
|
|
setId = fileListTransfer->SetupReceive(transferCallback, true, packet->systemAddress);
|
|
|
|
// Ask for patches for the files in the list that are different from what we have.
|
|
outBitStream.Write((unsigned char)ID_AUTOPATCHER_GET_PATCH);
|
|
outBitStream.Write(setId);
|
|
outBitStream.Write(patchApplicationLastUpdateDate);
|
|
StringCompressor::Instance()->EncodeString(applicationName, 512, &outBitStream);
|
|
missingOrChanged.Serialize(&outBitStream);
|
|
SendUnified(&outBitStream, priority, RELIABLE_ORDERED, orderingChannel, packet->systemAddress, false);
|
|
|
|
return RR_STOP_PROCESSING_AND_DEALLOCATE; // Absorb this message
|
|
}
|
|
void AutopatcherClient::OnDeletionList(Packet *packet)
|
|
{
|
|
if (packet->systemAddress!=serverId)
|
|
return;
|
|
|
|
SLNet::BitStream inBitStream(packet->data, packet->length, false);
|
|
SLNet::BitStream outBitStream;
|
|
inBitStream.IgnoreBits(8);
|
|
FileList fileList;
|
|
if (fileList.Deserialize(&inBitStream)==false)
|
|
return;
|
|
fileList.DeleteFiles(applicationDirectory);
|
|
}
|
|
PluginReceiveResult AutopatcherClient::OnDownloadFinished(Packet *packet)
|
|
{
|
|
SLNet::BitStream inBitStream(packet->data, packet->length, false);
|
|
inBitStream.IgnoreBits(8);
|
|
// This may have been created internally, with no serverDate written (line 469 or so)
|
|
if (inBitStream.GetNumberOfUnreadBits()>7)
|
|
{
|
|
inBitStream.Read(serverDate);
|
|
}
|
|
serverId=packet->systemAddress;
|
|
serverIdIndex=packet->systemAddress.systemIndex;
|
|
|
|
return RR_CONTINUE_PROCESSING;
|
|
}
|
|
PluginReceiveResult AutopatcherClient::OnDownloadFinishedInternal(Packet *packet)
|
|
{
|
|
SLNet::BitStream inBitStream(packet->data, packet->length, false);
|
|
inBitStream.IgnoreBits(8);
|
|
serverId=packet->systemAddress;
|
|
serverIdIndex=packet->systemAddress.systemIndex;
|
|
inBitStream.Read(serverDate);
|
|
|
|
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
|
}
|
|
void AutopatcherClient::CopyAndRestart(const char *filePath)
|
|
{
|
|
// We weren't able to write applicationDirectory + filePath so we wrote applicationDirectory + filePath + COPY_ON_RESTART_EXTENSION instead
|
|
copyAndRestartList.AddFile(filePath,filePath, 0, 0, 0, FileListNodeContext(0,0,0,0));
|
|
}
|
|
void AutopatcherClient::Redownload(const char *filePath)
|
|
{
|
|
redownloadList.AddFile(filePath,filePath, 0, 0, 0, FileListNodeContext(0,0,0,0));
|
|
}
|