Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.

This commit is contained in:
LFeenanEA 2025-02-27 17:34:39 +00:00
parent 2e338c00cb
commit 3d0ee53a05
No known key found for this signature in database
GPG key ID: C6EBE8C2EA08F7E0
6072 changed files with 2283311 additions and 0 deletions

View file

@ -0,0 +1,428 @@
/*
** 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;
}

View file

@ -0,0 +1,99 @@
/*
** 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.h : Declaration of the CDownload
#ifndef __DOWNLOAD_H_
#define __DOWNLOAD_H_
//#include "../resource.h" // main symbols
#include "WWDownload/ftp.h"
#include "WWDownload/downloaddefs.h"
/////////////////////////////////////////////////////////////////////////////
// CDownload
class IDownload
{
public:
virtual HRESULT OnError( int error )=0;
virtual HRESULT OnEnd()=0;
virtual HRESULT OnQueryResume()=0;
virtual HRESULT OnProgressUpdate(int bytesread, int totalsize, int timetaken, int timeleft)=0;
virtual HRESULT OnStatusUpdate(int status)=0;
};
class CDownload
{
public:
CDownload(IDownload *listener) :
Listener(listener)
{
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';
m_Ftp = new( Cftp );
m_TryResume = false;
m_predictions = 0;
for (int i=0; i<8; ++i)
{
m_predictionTimes[i] = 0;
}
}
~CDownload() { delete m_Ftp; }
public:
virtual HRESULT PumpMessages();
virtual HRESULT Abort();
virtual HRESULT DownloadFile(LPCSTR server, LPCSTR username, LPCSTR password, LPCSTR file, LPCSTR localfile, LPCSTR regkey, bool tryresume=true);
virtual HRESULT GetLastLocalFile(char *local_file, int maxlen);
private:
char m_Server[ 256 ];
char m_Login[ 64 ];
char m_Password[ 64 ];
char m_File[ 256 ];
char m_LocalFile[ 256 ];
char m_LastLocalFile[ 256 ];
char m_RegKey[ 256 ];
int m_Status;
int m_TimeStarted;
int m_StartPosition;
int m_FileSize;
int m_BytesRead;
bool m_TryResume;
int m_predictions;
int m_predictionTimes[8];
Cftp *m_Ftp;
IDownload *Listener;
};
#endif //__DOWNLOAD_H_

View file

@ -0,0 +1,68 @@
/*
** 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/>.
*/
// DownloadDebug.h
#ifndef __DOWNLOADDEBUG_H_
#define __DOWNLOADDEBUG_H_
// BGC 3/27/03 - added this for disabling debug logging for "release" worldbuilder.
// uncomment this line to remove logging from game spy code.
//#define DISABLE_DEBUG_LOGGING
#if defined(NDEBUG) || defined(DISABLE_DEBUG_LOGGING)
#define DEBUG_LOG(exp) ((void)0)
#else
#ifdef __cplusplus
extern "C" {
#endif
#include <stdarg.h>
extern void DebugCrash( const char *fmt, ... );
extern void DebugLog( const char *fmt, ... );
/*
Yeah, it's a sleazy global, since we can't reasonably add
any args to DebugCrash due to the varargs nature of it.
We'll just let it slide in this case...
*/
extern char* TheCurrentIgnoreCrashPtr;
#define DEBUG_CRASH(m) \
do { \
{ \
static char ignoreCrash = 0; \
if (!ignoreCrash) { \
TheCurrentIgnoreCrashPtr = &ignoreCrash; \
DebugCrash m ; \
TheCurrentIgnoreCrashPtr = NULL; \
} \
} \
} while (0)
#define DEBUG_LOG(x) do { DebugLog x; } while (0)
#define DEBUG_ASSERTCRASH(c, m) do { { if (!(c)) DEBUG_CRASH(m); } } while (0)
#ifdef __cplusplus
}
#endif
#endif // NDEBUG
#endif //__DOWNLOADDEBUG_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,50 @@
/*
** 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/>.
*/
// Registry.h
// Simple interface for storing/retreiving registry values
// Author: Matthew D. Campbell, December 2001
#pragma once
#ifndef __WWDOWNLOAD_REGISTRY_H__
#define __WWDOWNLOAD_REGISTRY_H__
#include <string>
/**
* Get a string from the registry
*/
bool GetStringFromRegistry(std::string path, std::string key, std::string& val);
/**
* Get an unsigned int from the registry
*/
bool GetUnsignedIntFromRegistry(std::string path, std::string key, unsigned int& val);
/**
* Store a string in the registry - returns true on success
*/
bool SetStringInRegistry(std::string path, std::string key, std::string val);
/**
* Store an unsigned int in the registry - returns true on success
*/
bool SetUnsignedIntInRegistry(std::string path, std::string key, unsigned int val);
#endif // __WWDOWNLOAD_REGISTRY_H__

View file

@ -0,0 +1,161 @@
# Microsoft Developer Studio Project File - Name="WWDownload" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Static Library" 0x0104
CFG=WWDownload - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "WWDownload.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "WWDownload.mak" CFG="WWDownload - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "WWDownload - Win32 Release" (based on "Win32 (x86) Static Library")
!MESSAGE "WWDownload - Win32 Debug" (based on "Win32 (x86) Static Library")
!MESSAGE "WWDownload - Win32 Internal" (based on "Win32 (x86) Static Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""$/Gamespy/GOA/WWDownload", PHXAAAAA"
# PROP Scc_LocalPath "."
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "WWDownload - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /G6 /MD /W3 /GX /O2 /Ob2 /I ".." /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fr /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo /out:"..\..\..\lib\WWDownload.lib"
!ELSEIF "$(CFG)" == "WWDownload - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
# ADD CPP /nologo /G6 /MDd /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fr /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo /out:"..\..\..\lib\WWDownloadDebug.lib"
!ELSEIF "$(CFG)" == "WWDownload - Win32 Internal"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Internal"
# PROP BASE Intermediate_Dir "Internal"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Internal"
# PROP Intermediate_Dir "Internal"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /G6 /MD /W3 /GX /O2 /I ".." /D "WIN32" /D "DEBUG" /D "_MBCS" /D "_LIB" /Fr /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo /out:"..\..\..\lib\WWDownload.lib"
# ADD LIB32 /nologo /out:"..\..\..\lib\WWDownloadInternal.lib"
!ENDIF
# Begin Target
# Name "WWDownload - Win32 Release"
# Name "WWDownload - Win32 Debug"
# Name "WWDownload - Win32 Internal"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\Download.cpp
# End Source File
# Begin Source File
SOURCE=.\Ftp.cpp
# End Source File
# Begin Source File
SOURCE=.\registry.cpp
# End Source File
# Begin Source File
SOURCE=.\urlBuilder.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\Download.h
# End Source File
# Begin Source File
SOURCE=.\DownloadDebug.h
# End Source File
# Begin Source File
SOURCE=.\DownloadDefs.h
# End Source File
# Begin Source File
SOURCE=.\Ftp.h
# End Source File
# Begin Source File
SOURCE=.\FtpDefs.h
# End Source File
# Begin Source File
SOURCE=.\Registry.h
# End Source File
# Begin Source File
SOURCE=.\urlBuilder.h
# End Source File
# End Group
# End Target
# End Project

View file

@ -0,0 +1,61 @@
/*
** 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/>.
*/
#ifndef _DOWNLOADDEFS_H
#define _DOWNLOADDEFS_H
// CDownload statuses
#define DOWNLOADSTATUS_NONE 0
#define DOWNLOADSTATUS_GO 1
#define DOWNLOADSTATUS_CONNECTING 2
#define DOWNLOADSTATUS_LOGGINGIN 3
#define DOWNLOADSTATUS_FINDINGFILE 4
#define DOWNLOADSTATUS_QUERYINGRESUME 5
#define DOWNLOADSTATUS_DOWNLOADING 6
#define DOWNLOADSTATUS_DISCONNECTING 7
#define DOWNLOADSTATUS_FINISHING 8
#define DOWNLOADSTATUS_DONE 0
// CDownload return codes
#define DOWNLOAD_SUCCEEDED S_OK
#define DOWNLOAD_PARAMERROR MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 1 )
#define DOWNLOAD_STATUSERROR MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 2 )
#define DOWNLOAD_NETWORKERROR MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 3 )
#define DOWNLOAD_FILEERROR MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 4 )
#define DOWNLOAD_REENTERERROR MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 4 )
// CDownloadEvent return codes.
#define DOWNLOADEVENT_SUCCEEDED S_OK
#define DOWNLOADEVENT_FAILED MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 5 )
#define DOWNLOADEVENT_RESUME S_OK
#define DOWNLOADEVENT_DONOTRESUME MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 6 )
// CDownloadEvent error codes for OnError()
#define DOWNLOADEVENT_NOSUCHSERVER 1
#define DOWNLOADEVENT_COULDNOTCONNECT 2
#define DOWNLOADEVENT_LOGINFAILED 3
#define DOWNLOADEVENT_NOSUCHFILE 4
#define DOWNLOADEVENT_LOCALFILEOPENFAILED 5
#define DOWNLOADEVENT_TCPERROR 6
#define DOWNLOADEVENT_DISCONNECTERROR 7
#endif

View file

@ -0,0 +1,117 @@
/*
** 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/>.
*/
// ftp.h : Declaration of the Cftp
#ifndef __FTP_H_
#define __FTP_H_
//#include "../resource.h" // main symbols
#include "winsock.h"
#include "stdio.h"
#include "WWDownload/ftpdefs.h"
// FTP server return codes. See RFC 959
#define FTPREPLY_SERVEROK 220
#define FTPREPLY_PASSWORD 331
#define FTPREPLY_LOGGEDIN 230
#define FTPREPLY_PORTOK 200
#define FTPREPLY_TYPEOK 200
#define FTPREPLY_RESTARTOK 350
#define FTPREPLY_CWDOK 250
#define FTPREPLY_OPENASCII 150
#define FTPREPLY_OPENBINARY 150
#define FTPREPLY_COMPLETE 226
#define FTPREPLY_CONTROLCLOSED 421
// Temporary download file name
#define FTP_TEMPFILENAME "..\\__~DOWN_L~D"
/////////////////////////////////////////////////////////////////////////////
// Cftp
class Cftp
{
private:
friend class CDownload;
int m_iCommandSocket; // Socket for commands
int m_iDataSocket; // Socket for data
struct sockaddr_in m_CommandSockAddr; // Address for commands
struct sockaddr_in m_DataSockAddr; // Address for data
int m_iFilePos; // Byte offset into file
int m_iBytesRead; // Number of bytes downloaded
int m_iFileSize; // Total size of the file
char m_szRemoteFilePath[128];
char m_szRemoteFileName[128];
char m_szLocalFilePath[128];
char m_szLocalFileName[128];
char m_szServerName[128];
char m_szUserName[128];
char m_szPassword[128];
FILE * m_pfLocalFile;
int m_iStatus;
int m_sendNewPortStatus;
int m_findStart;
int SendData( char * pData, int iSize );
int RecvData( char * pData, int iSize );
int SendNewPort();
int OpenDataConnection();
void CloseDataConnection();
int AsyncGetHostByName( char * szName, struct sockaddr_in &address );
// Convert a local filename into a temp filename to download into
void GetDownloadFilename( const char* localname, char* downloadname);
void CloseSockets(void);
void ZeroStuff(void);
public:
Cftp();
virtual ~Cftp();
public:
HRESULT ConnectToServer(LPCSTR szServerName);
HRESULT DisconnectFromServer();
HRESULT LoginToServer( LPCSTR szUserName, LPCSTR szPassword );
HRESULT LogoffFromServer( void );
HRESULT FindFile( LPCSTR szRemoteFileName, int * piSize );
HRESULT FileRecoveryPosition( LPCSTR szLocalFileName, LPCSTR szRegistryRoot );
HRESULT RestartFrom( int i ) { m_iFilePos = i; return FTP_SUCCEEDED; };
HRESULT GetNextFileBlock( LPCSTR szLocalFileName, int * piTotalRead );
HRESULT RecvReply( LPCSTR pReplyBuffer, int iSize, int * piRetCode );
HRESULT SendCommand( LPCSTR pCommand, int iSize );
};
#endif //__FTP_H_

View file

@ -0,0 +1,30 @@
/*
** 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/>.
*/
#ifndef __FTPDEFS_H_INCLUDED__
#define __FTPDEFS_H_INCLUDED__
// CFtp return codes.
#define FTP_SUCCEEDED S_OK
#define FTP_FAILED MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 1 )
#define FTP_TRYING MAKE_HRESULT( SEVERITY_ERROR, FACILITY_ITF, 2 )
#endif

View file

@ -0,0 +1,159 @@
/*
** 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/>.
*/
// Registry.cpp
// Simple interface for storing/retreiving registry values
// Author: Matthew D. Campbell, December 2001
#include <string>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "Registry.h"
bool getStringFromRegistry(HKEY root, std::string path, std::string key, std::string& val)
{
HKEY handle;
unsigned char buffer[256];
unsigned long size = 256;
unsigned long type;
int returnValue;
if ((returnValue = RegOpenKeyEx( root, path.c_str(), 0, KEY_READ, &handle )) == ERROR_SUCCESS)
{
returnValue = RegQueryValueEx(handle, key.c_str(), NULL, &type, (unsigned char *) &buffer, &size);
RegCloseKey( handle );
}
if (returnValue == ERROR_SUCCESS)
{
val = (char *)buffer;
return true;
}
return false;
}
bool getUnsignedIntFromRegistry(HKEY root, std::string path, std::string key, unsigned int& val)
{
HKEY handle;
unsigned long buffer;
unsigned long size = sizeof(buffer);
unsigned long type;
int returnValue;
if ((returnValue = RegOpenKeyEx( root, path.c_str(), 0, KEY_READ, &handle )) == ERROR_SUCCESS)
{
returnValue = RegQueryValueEx(handle, key.c_str(), NULL, &type, (unsigned char *) &buffer, &size);
RegCloseKey( handle );
}
if (returnValue == ERROR_SUCCESS)
{
val = buffer;
return true;
}
return false;
}
bool setStringInRegistry( HKEY root, std::string path, std::string key, std::string val)
{
HKEY handle;
unsigned long type;
unsigned long returnValue;
int size;
if ((returnValue = RegCreateKeyEx( root, path.c_str(), 0, "REG_NONE", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &handle, NULL )) == ERROR_SUCCESS)
{
type = REG_SZ;
size = val.length()+1;
returnValue = RegSetValueEx(handle, key.c_str(), 0, type, (unsigned char *)val.c_str(), size);
RegCloseKey( handle );
}
return (returnValue == ERROR_SUCCESS);
}
bool setUnsignedIntInRegistry( HKEY root, std::string path, std::string key, unsigned int val)
{
HKEY handle;
unsigned long type;
unsigned long returnValue;
int size;
if ((returnValue = RegCreateKeyEx( root, path.c_str(), 0, "REG_NONE", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &handle, NULL )) == ERROR_SUCCESS)
{
type = REG_DWORD;
size = 4;
returnValue = RegSetValueEx(handle, key.c_str(), 0, type, (unsigned char *)&val, size);
RegCloseKey( handle );
}
return (returnValue == ERROR_SUCCESS);
}
bool GetStringFromRegistry(std::string path, std::string key, std::string& val)
{
std::string fullPath = "SOFTWARE\\Electronic Arts\\EA Games\\Generals";
fullPath.append(path);
if (getStringFromRegistry(HKEY_LOCAL_MACHINE, fullPath.c_str(), key.c_str(), val))
{
return true;
}
return getStringFromRegistry(HKEY_CURRENT_USER, fullPath.c_str(), key.c_str(), val);
}
bool GetUnsignedIntFromRegistry(std::string path, std::string key, unsigned int& val)
{
std::string fullPath = "SOFTWARE\\Electronic Arts\\EA Games\\Generals";
fullPath.append(path);
if (getUnsignedIntFromRegistry(HKEY_LOCAL_MACHINE, fullPath.c_str(), key.c_str(), val))
{
return true;
}
return getUnsignedIntFromRegistry(HKEY_CURRENT_USER, fullPath.c_str(), key.c_str(), val);
}
bool SetStringInRegistry( std::string path, std::string key, std::string val)
{
std::string fullPath = "SOFTWARE\\Electronic Arts\\EA Games\\Generals";
fullPath.append(path);
if (setStringInRegistry( HKEY_LOCAL_MACHINE, fullPath, key, val))
return true;
return setStringInRegistry( HKEY_CURRENT_USER, fullPath, key, val );
}
bool SetUnsignedIntInRegistry( std::string path, std::string key, unsigned int val)
{
std::string fullPath = "SOFTWARE\\Electronic Arts\\EA Games\\Generals";
fullPath.append(path);
if (setUnsignedIntInRegistry( HKEY_LOCAL_MACHINE, fullPath, key, val))
return true;
return setUnsignedIntInRegistry( HKEY_CURRENT_USER, fullPath, key, val );
}

View file

@ -0,0 +1,49 @@
/*
** 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/>.
*/
#include <string>
#include <stdio.h>
#include "registry.h"
void FormatURLFromRegistry( std::string& gamePatchURL, std::string& mapPatchURL,
std::string& configURL, std::string& motdURL )
{
std::string sku = "generals";
std::string language = "english";
unsigned int version = 0; // invalid version - can't get on with a corrupt reg.
unsigned int mapVersion = 0; // invalid version - can't get on with a corrupt reg.
std::string baseURL = "http://servserv.generals.ea.com/servserv/";
baseURL.append(sku);
baseURL.append("/");
GetStringFromRegistry("", "BaseURL", baseURL);
GetStringFromRegistry("", "Language", language);
GetUnsignedIntFromRegistry("", "Version", version);
GetUnsignedIntFromRegistry("", "MapPackVersion", mapVersion);
char buf[256];
_snprintf(buf, 256, "%s%s-%d.txt", baseURL.c_str(), language.c_str(), version);
gamePatchURL = buf;
_snprintf(buf, 256, "%smaps-%d.txt", baseURL.c_str(), mapVersion);
mapPatchURL = buf;
_snprintf(buf, 256, "%sconfig.txt", baseURL.c_str());
configURL = buf;
_snprintf(buf, 256, "%sMOTD-%s.txt", baseURL.c_str(), language.c_str());
motdURL = buf;
}

View file

@ -0,0 +1,24 @@
/*
** 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/>.
*/
#ifndef _URLBUILDER_H_
#define _URLBUILDER_H_
void FormatURLFromRegistry( std::string& gamePatchURL, std::string& mapPatchURL,
std::string& configURL, std::string& motdURL );
#endif _URLBUILDER_H_