| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** 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/>.
- */
- // Download.cpp : Implementation of CDownload
- #include "DownloadDebug.h"
- #include "download.h"
- #include <mmsystem.h>
- #include <assert.h>
- #include <direct.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <sys/stat.h>
- /////////////////////////////////////////////////////////////////////////////
- // CDownload
- //
- // Kick off a download - copys the parameters for the download to the appropriate
- // data members, then sets the status flag to start the download when PumpMessages()
- // is called.
- //
- HRESULT CDownload::DownloadFile(LPCSTR server, LPCSTR username, LPCSTR password, LPCSTR file, LPCSTR localfile, LPCSTR regkey, bool tryresume)
- {
- // Check we're not in the middle of another download.
- if((m_Status != DOWNLOADSTATUS_DONE ) && (m_Status != DOWNLOADSTATUS_FINDINGFILE))
- {
- return( DOWNLOAD_STATUSERROR );
- }
- // If we're still connected, make sure we're on the right server
- if (m_Status == DOWNLOADSTATUS_FINDINGFILE)
- {
- if ((strcmp(m_Server, server)) || (strcmp(m_Login, username)))
- {
- // Damn, a server switch. Close conn & fix state
- m_Ftp->DisconnectFromServer();
- m_Status=DOWNLOADSTATUS_DONE;
- }
- }
- // Check all parameters are non-null.
- if( ( server == NULL ) || ( username == NULL ) ||
- ( password == NULL ) || ( file == NULL ) ||
- ( localfile == NULL ) || ( regkey == NULL ) )
- {
- //////////DBGMSG("Download Paramerror");
- return( DOWNLOAD_PARAMERROR );
- }
- // Make sure we have a download directory
- _mkdir("download");
- // Copy parameters to member variables.
- strncpy( m_Server, server, sizeof( m_Server ) );
- strncpy( m_Login, username, sizeof( m_Login ) );
- strncpy( m_Password, password, sizeof( m_Password ) );
- strncpy( m_File, file, sizeof( m_File ) );
- strncpy( m_LocalFile, localfile, sizeof( m_LocalFile ) );
- strncpy( m_LastLocalFile, localfile, sizeof( m_LastLocalFile ) );
- strncpy( m_RegKey, regkey, sizeof( m_RegKey ) );
- m_TryResume = tryresume;
- m_StartPosition=0;
- /*********
- // figure out file offset
- if (m_TryResume == false)
- {
- m_StartPosition=0;
- }
- else if(( m_StartPosition = m_Ftp->FileRecoveryPosition( m_LocalFile, m_RegKey ) ) != 0 )
- {
- if( Listener->OnQueryResume() == DOWNLOADEVENT_DONOTRESUME )
- {
- m_Ftp->RestartFrom( 0 );
- m_StartPosition = 0;
- }
- }
- ************/
- // Set status so we start to connect at the next PumpMessages()
- if (m_Status != DOWNLOADSTATUS_FINDINGFILE)
- m_Status = DOWNLOADSTATUS_GO;
- //////////DBGMSG("Ready to download");
- return S_OK;
- }
- //
- // Get the local filename of the last file we requested to download....
- //
- HRESULT CDownload::GetLastLocalFile(char *local_file, int maxlen) {
- if (local_file==0)
- return(E_FAIL);
- strncpy(local_file, m_LastLocalFile, maxlen);
- local_file[maxlen-1]=0;
- return(S_OK);
- }
- //
- // Abort the current download - disconnects from the server and sets the
- // status flag to stop PumpMessages() from doing anything.
- //
- HRESULT CDownload::Abort()
- {
- if( m_Status != DOWNLOADSTATUS_NONE )
- {
- m_Ftp->DisconnectFromServer();
- m_Status = DOWNLOADSTATUS_NONE;
- }
- m_TimeStarted = 0;
- m_StartPosition = 0;
- m_FileSize = 0;
- m_BytesRead = 0;
- m_Server[ 0 ] = '\0';
- m_Login[ 0 ] = '\0';
- m_Password[ 0 ] = '\0';
- m_File[ 0 ] = '\0';
- m_LocalFile[ 0 ] = '\0';
- m_RegKey[ 0 ] = '\0';
- return S_OK;
- }
- //
- // PumpMessages() does all the work - checks for possible resumption of a previous
- // download, connects to the server, finds the file and downloads it.
- //
- HRESULT CDownload::PumpMessages()
- {
- int iResult = 0;
- int timetaken = 0;
- int averagetimepredicted = -1;
- static int reenter = 0;
- /* Avoid reentrancy. */
- ////DBGMSG("Download Pump");
- if( reenter != 0 )
- {
- return( DOWNLOAD_REENTERERROR );
- }
- reenter = 1;
- if( m_Status == DOWNLOADSTATUS_GO )
- {
- // Check to see whether the file already exists locally
- /***********
- if (m_TryResume == false)
- {
- m_StartPosition = 0;
- }
- else if( ( m_StartPosition = m_Ftp->FileRecoveryPosition( m_LocalFile, m_RegKey ) ) != 0 )
- {
- if( Listener->OnQueryResume() == DOWNLOADEVENT_DONOTRESUME )
- {
- m_Ftp->RestartFrom( 0 );
- m_StartPosition = 0;
- }
- }
- ************/
- // Tell the client that we're starting to connect.
- Listener->OnStatusUpdate( DOWNLOADSTATUS_CONNECTING );
-
- m_Status = DOWNLOADSTATUS_CONNECTING;
- }
- if( m_Status == DOWNLOADSTATUS_CONNECTING )
- {
- // Connect to the server
- //////////DBGMSG("Connect to Server");
-
- iResult = m_Ftp->ConnectToServer( m_Server );
- ////////////DBGMSG("out of FTP connect");
- if( iResult == FTP_SUCCEEDED )
- {
- m_Status = DOWNLOADSTATUS_LOGGINGIN;
- }
- else
- {
- if( iResult == FTP_FAILED )
- {
- // Tell the client we couldn't connect
- ///////////DBGMSG("Couldn't connect");
- Listener->OnError( DOWNLOADEVENT_COULDNOTCONNECT );
- reenter = 0;
- return DOWNLOAD_NETWORKERROR;
- }
- }
- reenter = 0;
- return DOWNLOAD_SUCCEEDED;
- }
- if( m_Status == DOWNLOADSTATUS_LOGGINGIN )
- {
- // Login to the server
- ////////////DBGMSG("Login to server");
- iResult = m_Ftp->LoginToServer( m_Login, m_Password );
- if( iResult == FTP_SUCCEEDED )
- {
- Listener->OnStatusUpdate( DOWNLOADSTATUS_FINDINGFILE );
- m_Status = DOWNLOADSTATUS_FINDINGFILE;
- }
- if( iResult == FTP_FAILED )
- {
- // Tell the client we couldn't log in
- Listener->OnError( DOWNLOADEVENT_LOGINFAILED );
- reenter = 0;
- return( DOWNLOAD_NETWORKERROR );
- }
- reenter = 0;
- return DOWNLOAD_SUCCEEDED;
- }
- if(( m_Status == DOWNLOADSTATUS_FINDINGFILE ) && (strlen(m_File)))
- {
-
- // Find the file on the server
- ///////////DBGMSG("Find File");
- if( m_Ftp->FindFile( m_File, &m_FileSize ) == FTP_FAILED )
- {
- // Tell the client we couldn't find the file
- Listener->OnError( DOWNLOADEVENT_NOSUCHFILE );
- reenter = 0;
- return( DOWNLOAD_FILEERROR );
- }
- if( m_FileSize > 0 )
- {
- // First check to see if we already have the complete file in the proper dest location...
- //
- // Note, we will only skip the download if the file is a patch. Patch files should
- // never ever change so this is not a concern.
- //
- // We identify patches because they are written into the patches folder.
- struct _stat statdata;
- if ( (_stat(m_LocalFile, &statdata) == 0) &&
- (statdata.st_size == m_FileSize) &&
- (_strnicmp(m_LocalFile, "patches\\", strlen("patches\\"))==0)) {
- // OK, no need to download this again....
- m_Status = DOWNLOADSTATUS_FINDINGFILE; // ready to find another file
- m_TimeStarted = 0;
- m_StartPosition = 0;
- m_FileSize = 0;
- m_BytesRead = 0;
- m_File[ 0 ] = '\0';
- m_LocalFile[ 0 ] = '\0';
-
- Listener->OnEnd();
- reenter = 0;
- return DOWNLOAD_SUCCEEDED;
- }
- //
- // Check if we can do a file resume
- //
- if (m_TryResume == false)
- {
- m_StartPosition = 0;
- }
- else if( ( m_StartPosition = m_Ftp->FileRecoveryPosition( m_LocalFile, m_RegKey ) ) != 0 )
- {
- if( Listener->OnQueryResume() == DOWNLOADEVENT_DONOTRESUME )
- {
- m_Ftp->RestartFrom( 0 );
- m_StartPosition = 0;
- }
- }
- // end resume check
- m_Status = DOWNLOADSTATUS_DOWNLOADING;
- // Tell the client we're starting to download
- Listener->OnStatusUpdate( DOWNLOADSTATUS_DOWNLOADING );
- }
- reenter = 0;
- return DOWNLOAD_SUCCEEDED;
- }
- if( m_Status == DOWNLOADSTATUS_DOWNLOADING )
- {
- // Get the next chunk of the file
- ///DBGMSG("Get Next File Block");
- iResult = m_Ftp->GetNextFileBlock( m_LocalFile, &m_BytesRead );
- if( m_TimeStarted == 0 )
- {
- // This is the first time through here - record the starting time.
- m_TimeStarted = timeGetTime();
- }
- if( iResult == FTP_SUCCEEDED )
- {
- m_Status = DOWNLOADSTATUS_FINISHING;
- }
- else
- {
- if( iResult == FTP_FAILED )
- {
- Listener->OnError( DOWNLOADEVENT_TCPERROR );
- reenter = 0;
- return( DOWNLOAD_NETWORKERROR );
- }
- }
- // Calculate time taken so far, and predict how long there is left.
- // The prediction returned is the average of the last 8 predictions.
- timetaken = ( timeGetTime() - m_TimeStarted ) / 1000;
- //////////if( m_BytesRead > 0 ) // NAK - RP said this is wrong
- if( ( m_BytesRead - m_StartPosition ) > 0 )
- {
- // Not the first read.
- int predictionIndex = ( m_predictions++ ) & 0x7;
- m_predictionTimes[ predictionIndex ] = MulDiv( timetaken, (m_FileSize - m_BytesRead), (m_BytesRead - m_StartPosition) );
- //__int64 numerator = ( m_FileSize - m_BytesRead ) * timetaken;
- //__int64 denominator = ( m_BytesRead - m_StartPosition );
- //m_predictionTimes[ predictionIndex ] = numerator/denominator;
- //m_predictionTimes[ predictionIndex ] = ( ( m_FileSize - m_BytesRead ) * timetaken ) / ( m_BytesRead - m_StartPosition );
- //m_predictionTimes[ predictionIndex ] = ( ( m_FileSize - m_BytesRead ) / ( m_BytesRead - m_StartPosition ) * timetaken );
- DEBUG_LOG(("About to OnProgressUpdate() - m_FileSize=%d, m_BytesRead=%d, timetaken=%d, numerator=%d",
- m_FileSize, m_BytesRead, timetaken, ( ( m_FileSize - m_BytesRead ) * timetaken )));
- DEBUG_LOG((", m_startPosition=%d, denominator=%d, predictionTime=%d\n",
- m_StartPosition, ( m_BytesRead - m_StartPosition ), predictionIndex));
- DEBUG_LOG(("vals are %d %d %d %d %d %d %d %d\n",
- m_predictionTimes[ 0 ], m_predictionTimes[ 1 ], m_predictionTimes[ 2 ], m_predictionTimes[ 3 ],
- m_predictionTimes[ 4 ], m_predictionTimes[ 5 ], m_predictionTimes[ 6 ], m_predictionTimes[ 7 ]));
- if( m_predictions > 8 )
- {
- // We've had enough time to build up a few predictions, so calculate
- // an average.
- averagetimepredicted = ( m_predictionTimes[ 0 ] + m_predictionTimes[ 1 ] +
- m_predictionTimes[ 2 ] + m_predictionTimes[ 3 ] +
- m_predictionTimes[ 4 ] + m_predictionTimes[ 5 ] +
- m_predictionTimes[ 6 ] + m_predictionTimes[ 7 ] ) / 8;
- }
- else
- {
- // Wait a while longer before passing a "real" prediction
- averagetimepredicted = -1;
- }
- }
- else
- {
- averagetimepredicted = -1;
- }
- // Update the client's progress bar (or whatever)
- Listener->OnProgressUpdate( m_BytesRead, m_FileSize, timetaken, averagetimepredicted + 1);
- }
- if( m_Status == DOWNLOADSTATUS_FINISHING )
- {
- if (m_Ftp->m_iCommandSocket)
- m_Status = DOWNLOADSTATUS_FINDINGFILE; // ready to find another file
- else
- m_Status = DOWNLOADSTATUS_DONE; // command channel closed, connect again...
- m_TimeStarted = 0;
- m_StartPosition = 0;
- m_FileSize = 0;
- m_BytesRead = 0;
- m_File[ 0 ] = '\0';
- m_LocalFile[ 0 ] = '\0';
-
- Listener->OnEnd();
- }
- reenter = 0;
- return DOWNLOAD_SUCCEEDED;
- }
|