CnC_Remastered_Collection/TIBERIANDAWN/INTERNET.CPP

887 lines
30 KiB
C++

//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding source code 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.
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
/*************************************************************************************
** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S **
*************************************************************************************
* *
* Project Name : Command & Conquer - Red Alert *
* *
* File Name : INTERNET.CPP *
* *
* Programmer : Steve Tall *
* *
* Start Date : March 11th, 1996 *
* *
* Last Update : August 5th, 1996 [ST] *
* *
*-----------------------------------------------------------------------------------*
* Overview: *
* *
* Miscellaneous junk related to H2H internet connection. *
* *
*-----------------------------------------------------------------------------------*
* Functions: *
* Check_From_WChat -- Interprets start game packet from WChat *
* Read_Game_Options -- Read the game setup options from the wchat packet *
* Is_User_WChat_Registered -- retrieve the users wchat entry from registry *
* Spawn_WChat -- spawns or switches focus to wchat *
* Spawn_Registration_App -- spawns the C&C/Planet westwood registration app *
* Do_The_Internet_Menu_Thang -- Handle case where user clicks on 'Internet' button *
* *
* *
* *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
#include "tcpip.h"
#include "ccdde.h"
/***************************************************************************
** Internet specific globals
*/
char PlanetWestwoodHandle[] = {"Handle"}; //Planet WW user name
char PlanetWestwoodPassword[] = {"Password"}; //Planet WW password
char PlanetWestwoodIPAddress[IP_ADDRESS_MAX] = {"206.154.108.87"}; //IP of server or other player
long PlanetWestwoodPortNumber = 1234; //Port number to send to
bool PlanetWestwoodIsHost = false; //Flag true if player has control of game options
unsigned long PlanetWestwoodGameID; //Game ID
unsigned long PlanetWestwoodStartTime; //Time that game was started
HWND WChatHWND = 0; //Handle to Wchat window.
bool UseVirtualSubnetServer;
int InternetMaxPlayers;
int WChatMaxAhead;
int WChatSendRate;
int Read_Game_Options(void);
extern bool SpawnedFromWChat;
/***********************************************************************************************
* Check_From_WChat -- This function reads in C&CSPAWN.INI and interprets it *
* C&CSPAWN.INI is now sent to us by WCHAT via DDE *
* *
* *
* *
* INPUT: Name of C&CSPAWN.INI file. If NULL then get file from DDE Server *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 6/9/96 1:44PM ST : Created *
*=============================================================================================*/
void Check_From_WChat(char *wchat_name)
{
#ifndef DEMO
char default_string[] = {"Error"};
char key_string[256];
char *ini_file;
RawFileClass wchat_file;
/*
** Get a pointer to C&CSPAWN.INI either by reading it from disk or getting it from
** the DDE server.
*/
if (wchat_name){
ini_file = new char [8192];
}else{
ini_file = DDEServer.Get_MPlayer_Game_Info();
#if (0)
/*
** Save it to disk as well so I can see it
*/
RawFileClass anotherfile ("FROMCHAT.TXT");
anotherfile.Write(ini_file, DDEServer.Get_MPlayer_Game_Info_Length());
#endif //(0)
}
if (wchat_name){
wchat_file.Set_Name(wchat_name);
}
if (!wchat_name || wchat_file.Is_Available()){
/*
** Read the ini file from disk if we founf it there
*/
if (wchat_name){
wchat_file.Read(ini_file, wchat_file.Size());
}
/*
** Get the IP address
*/
key_string[0] = 0;
WWGetPrivateProfileString("Internet",
"Address",
default_string,
key_string,
sizeof(key_string),
ini_file);
if (!strcmp (key_string, default_string)) {
if (wchat_name) delete ini_file;
return;
}
strcpy (PlanetWestwoodIPAddress, key_string);
/*
** Get the port number
*/
key_string[0] = 0;
WWGetPrivateProfileString("Internet",
"Port",
default_string,
key_string,
sizeof(key_string),
ini_file);
if (!strcmp (key_string, default_string)) {
if (wchat_name) delete ini_file;
return;
}
PlanetWestwoodPortNumber = atol(key_string);
/*
** Get host or client
*/
key_string[0] = 0;
WWGetPrivateProfileString("Internet",
"Host",
default_string,
key_string,
sizeof(key_string),
ini_file);
if (!strcmp (key_string, default_string)) {
if (wchat_name) delete ini_file;
return;
}
if (strchr (key_string, '1')){
PlanetWestwoodIsHost = true;
}else{
PlanetWestwoodIsHost = false;
}
UseVirtualSubnetServer = WWGetPrivateProfileInt("Internet", "UseVSS", 0, ini_file);
Special.IsFromWChat = true;
}
if (wchat_name) delete ini_file;
#else //DEMO
wchat_name = wchat_name;
#endif //DEMO
}
//EventClass Wibble;
/***************************************************************************
* Read_Game_Options -- reads multiplayer game options from disk *
* *
* This routine is used for multiplayer games which read the game options *
* from disk, rather than through a connection dialog. *
* *
* INPUT: *
* name of C&CSPAWN.INI file. Null if data should be got from DDE server* *
* OUTPUT: *
* 1 = OK, 0 = error *
* *
* WARNINGS: \ *
* none. *
* *
* HISTORY: *
* 01/11/1996 BRR : Created. *
*=========================================================================*/
int Read_Game_Options(char *name)
{
char *buffer;
char filename[256] = {"INVALID.123"};
if (name){
strcpy (filename, name);
}
/*------------------------------------------------------------------------
Create filename and read the file.
------------------------------------------------------------------------*/
CCFileClass file (filename);
if (name && !file.Is_Available()) {
return(0);
} else {
if (name){
buffer = new char [8192]; // INI staging buffer pointer.
memset(buffer, '\0', 8192);
file.Read(buffer, 8192-1);
file.Close();
}else{
buffer = DDEServer.Get_MPlayer_Game_Info();
}
}
/*------------------------------------------------------------------------
Get the player's name
------------------------------------------------------------------------*/
WWGetPrivateProfileString("Options", "Handle", "Noname", MPlayerName,
sizeof(MPlayerName), buffer);
strcpy(MPlayerGameName, MPlayerName);
MPlayerColorIdx = WWGetPrivateProfileInt("Options", "Color", 0, buffer);
MPlayerPrefColor = MPlayerColorIdx;
MPlayerHouse = (HousesType)WWGetPrivateProfileInt("Options", "Side",
HOUSE_GOOD, buffer);
MPlayerCredits = WWGetPrivateProfileInt("Options", "Credits", 0, buffer);
MPlayerBases = WWGetPrivateProfileInt("Options", "Bases", 0, buffer);
MPlayerTiberium = WWGetPrivateProfileInt("Options", "Tiberium", 0, buffer);
MPlayerGoodies = WWGetPrivateProfileInt("Options", "Crates", 0, buffer);
MPlayerGhosts = WWGetPrivateProfileInt("Options", "AI", 0, buffer);
BuildLevel = WWGetPrivateProfileInt("Options", "BuildLevel", 0, buffer);
MPlayerUnitCount = WWGetPrivateProfileInt("Options", "UnitCount", 0, buffer);
Seed = WWGetPrivateProfileInt("Options", "Seed", 0, buffer);
Special.IsCaptureTheFlag = WWGetPrivateProfileInt("Options", "CaptureTheFlag", 0, buffer);
PlanetWestwoodGameID = WWGetPrivateProfileInt("Internet", "GameID", 0, buffer);
PlanetWestwoodStartTime = WWGetPrivateProfileInt ("Internet", "StartTime", 0, buffer);
WChatHWND = (HWND) WWGetPrivateProfileInt("Internet", "HWND", (int)FindWindow("OWL_Window", "Westwood Chat"), buffer);
InternetMaxPlayers = WWGetPrivateProfileInt("Internet", "MaxPlayers", 2, buffer);
if (MPlayerTiberium) {
Special.IsTGrowth = 1;
Special.IsTSpread = 1;
} else {
Special.IsTGrowth = 0;
Special.IsTSpread = 0;
}
ScenarioIdx = WWGetPrivateProfileInt("Options", "Scenario", 0, buffer);
Scenario = ScenarioIdx; //MPlayerFilenum[ScenarioIdx];
Options.GameSpeed = 0;
MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse);
MPlayerMaxAhead = WChatMaxAhead = WWGetPrivateProfileInt("Timing", "MaxAhead", 9, buffer);
FrameSendRate = WChatSendRate = WWGetPrivateProfileInt("Timing", "SendRate", 3, buffer);
if (name) delete buffer;
return (1);
}
/***********************************************************************************************
* Get_Registry_Sub_Key -- search a registry key for a sub-key *
* *
* *
* *
* INPUT: handle of key to search *
* text to search for *
* true if old key should be closed when new key opened *
* *
* OUTPUT: handle to the key we found or 0 *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 1/12/96 2:11PM ST : Created *
*=============================================================================================*/
extern HKEY Get_Registry_Sub_Key (HKEY base_key, char *search_key, BOOL close);
void Just_Path(char *path, char *destpath)
{
char *terminator = NULL; //He'll be back.
strcpy (destpath, path);
terminator = strrchr (destpath, '\\');
if (terminator){
*terminator = 0;
}
}
/***********************************************************************************************
* Is_User_WChat_Registered -- retrieve the users wchat entry from the registry *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: TRUE if users wchat entry was found in the registry *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 1/12/96 2:13PM ST : Created *
*=============================================================================================*/
bool Is_User_WChat_Registered(char *buffer, int buffer_len)
{
buffer;
buffer_len;
return false;
#if (0) //ST - 1/2/2019 5:54PM
HKEY key;
char user_handle[256];
DWORD user_handle_size = sizeof (user_handle);
char user_pword[256];
DWORD user_pword_size = sizeof (user_pword);
/*
** Check HKEY_CLASSES_ROOT first. Old versions of Wchat register there
*/
key = Get_Registry_Sub_Key (HKEY_CLASSES_ROOT, "Wchat", FALSE);
if (key){
key = Get_Registry_Sub_Key (key, "Nick1", TRUE);
if (key){
if (RegQueryValue(key, "Nick", user_handle, (long*)&user_handle_size) == ERROR_SUCCESS){
if (RegQueryValue(key, "Pass", user_pword, (long*)&user_pword_size) == ERROR_SUCCESS){
/*
** If the first char of the users name is non-numberic and there is a password
** then return success
*/
if ((user_handle[0] < '0' || user_handle[0] > '9') && user_pword[0]){
RegCloseKey( key );
return (TRUE);
}
}
}
}
RegCloseKey ( key );
}
/*
** Check HKEY_LOCAL_MACKINE/Software
*/
user_handle_size = sizeof (user_handle);
key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE);
if (!key) return (FALSE);
key = Get_Registry_Sub_Key (key, "Westwood", TRUE);
if (!key) return (FALSE);
key = Get_Registry_Sub_Key (key, "InetReg", TRUE);
if (!key) return (FALSE);
//key = Get_Registry_Sub_Key (key, "UserName", TRUE);
//if (!key) return (FALSE);
//key = Get_Registry_Sub_Key (key, "Nick", TRUE);
//if (!key) return (FALSE);
if (RegQueryValueEx(key, "UserName", NULL, NULL, (unsigned char*)user_handle, &user_handle_size) != ERROR_SUCCESS){
RegCloseKey(key);
return (FALSE);
}
RegCloseKey(key);
memcpy (buffer, user_handle, min((unsigned)buffer_len, user_handle_size));
/*
** If the first char of the users name is non-numeric then return success
*/
if (user_handle[0] < '0' || user_handle[0] > '9'){
return (TRUE);
}else{
return (FALSE);
}
#endif
}
/***********************************************************************************************
* Spawn_WChat -- spawns or switches focus to wchat *
* *
* *
* *
* INPUT: can launch. If set then we are allowed to launch WChat if not already running *
* *
* OUTPUT: True if wchat was spawned *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 6/8/96 12:33PM ST : Created *
*=============================================================================================*/
bool Poke_WChat(void);
bool Spawn_WChat(bool can_launch)
{
can_launch;
return false;
#if (0)
CCDebugString ("C&C95 - In Spawn_WChat.\n");
char packet[10] = {"Hello"};
HWND chat_window = NULL;
/*
** See if WChat is already running...
*/
if (WChatHWND && IsWindow (WChatHWND) ){
chat_window = WChatHWND;
}else{
chat_window = FindWindow ( "OWL_Window", "Westwood Chat" );
}
if (chat_window){
/*
** WChat is already running. Minimize myself then try to give it focus.
*/
Set_Palette(BlackPalette);
VisiblePage.Clear();
ShowWindow (MainWindow, SW_MINIMIZE);
/*
** Give windoze a couple of secs to sort itself out.
*/
CountDownTimerClass wibble_timer;
wibble_timer.Set ( 60 * 3, true);
while (wibble_timer.Time()){
/*
** Call our message loop to make sure we get all the messages that are sent to us
** when we minimise.
*/
Keyboard::Check();
}
/*
** Send chat a tickle message so it knows to send the game stats to the server.
*/
if (GameStatisticsPacketSent && !PlanetWestwoodIsHost) {
Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_TICKLE);
}
/*
** Give the focus to WChat
*/
SetForegroundWindow ( chat_window );
ShowWindow ( chat_window, SW_RESTORE );
return(true);
}
/*
** Fail if we aren't allowed to launch wchat and we couldnt find its window.
*/
if (!can_launch) return (false);
/*
** Find where WChat was installed to
*/
HKEY key;
char wchat_loc[256];
DWORD wchat_loc_size = 256;
key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE);
if (!key) return (FALSE);
key = Get_Registry_Sub_Key (key, "Westwood", TRUE);
if (!key) return (FALSE);
key = Get_Registry_Sub_Key (key, "WChat", TRUE);
if (!key) return (FALSE);
//key = Get_Registry_Sub_Key (key, "UserName", TRUE);
//if (!key) return (FALSE);
//key = Get_Registry_Sub_Key (key, "Nick", TRUE);
//if (!key) return (FALSE);
if (RegQueryValueEx(key, "InstallPath", NULL, NULL, (unsigned char*)wchat_loc, &wchat_loc_size) != ERROR_SUCCESS){
RegCloseKey(key);
return (FALSE);
}
RegCloseKey(key);
PROCESS_INFORMATION process_info;
STARTUPINFO start_info;
memset ((void*)&start_info, 0, sizeof(start_info));
start_info.cb = sizeof(start_info);
char justpath [256];
Just_Path(wchat_loc, justpath);
/*
** We found WChat in the registry. Minimize myself then try to spawn it.
*/
Set_Palette(BlackPalette);
VisiblePage.Clear();
ShowWindow (MainWindow, SW_MINIMIZE);
/*
** Give windoze a couple of secs to sort itself out.
*/
CountDownTimerClass wibble_timer;
wibble_timer.Set ( 60 * 3, true);
while (wibble_timer.Time()){
/*
** Call our message loop to make sure we get all the messages that are sent to us
** when we minimise.
*/
Keyboard::Check();
}
bool success = CreateProcess (wchat_loc, NULL, NULL, NULL, false, 0, NULL, justpath, &start_info, &process_info);
if (success){
return (true);
}else{
ShowWindow (MainWindow, SW_RESTORE);
while ( Keyboard::Check() ) {};
return (false);
}
#endif
}
/***********************************************************************************************
* Spawn_Registration_App -- spawns the C&C/Planet westwood registration app *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: True if app was spawned *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 6/8/96 12:33PM ST : Created *
*=============================================================================================*/
bool Spawn_Registration_App(void)
{
return false;
#if (0) //ST - 1/2/2019 5:53PM
/*
** Find where inetreg was installed to
*/
HKEY key;
char inetreg_loc[256];
DWORD inetreg_loc_size = 256;
key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE);
if (!key) return (FALSE);
key = Get_Registry_Sub_Key (key, "Westwood", TRUE);
if (!key) return (FALSE);
key = Get_Registry_Sub_Key (key, "InetReg", TRUE);
if (!key) return (FALSE);
if (RegQueryValueEx(key, "InstallPath", NULL, NULL, (unsigned char*)inetreg_loc, &inetreg_loc_size) != ERROR_SUCCESS){
RegCloseKey(key);
return (FALSE);
}
RegCloseKey(key);
PROCESS_INFORMATION process_info;
STARTUPINFO start_info;
char justpath [256];
memset ((void*)&start_info, 0, sizeof(start_info));
start_info.cb = sizeof(start_info);
Just_Path(inetreg_loc, justpath);
BOOL success = CreateProcess (inetreg_loc, NULL, NULL, NULL, false, 0, NULL, justpath, &start_info, &process_info);
if (success){
//WaitForSingleObject (process_info.hProcess, 1000*10000);
//SetForegroundWindow ( MainWindow );
//ShowWindow ( MainWindow, SW_RESTORE );
}
return (success);
#endif
}
/***********************************************************************************************
* Do_The_Internet_Menu_Thang -- Handle case where user clicks on 'Internet' button *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 6/7/96 8:30PM ST : Created *
*=============================================================================================*/
bool Do_The_Internet_Menu_Thang(void)
{
#ifndef DEMO
int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2;
/*
** Dialog & button dimensions
*/
int d_dialog_w = 120 *factor; // dialog width
int d_dialog_h = 80*factor; // dialog height
int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord
int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord
int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord
int d_margin1=10;
int d_txt6_h=15;
#if (GERMAN | FRENCH)
int d_cancel_w = 50*factor;
#else
int d_cancel_w = 40*factor;
#endif
int d_cancel_h = 9*factor;
int d_cancel_x = d_dialog_cx - d_cancel_w / 2;
int d_cancel_y = d_dialog_y + d_dialog_h - 20*factor;
char packet[10] = {"Hello"};
#if (GERMAN | FRENCH)
int width=160*factor;
int height=80*factor;
#else
int width=120*factor;
int height=80*factor;
#endif //GERMAN | FRENCH
Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW);
Format_Window_String((char*)Text_String (TXT_CONNECTING), SeenBuff.Get_Height(), width, height);
#if (GERMAN | FRENCH)
d_dialog_w = width + 25*factor;
d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord
d_cancel_x = d_dialog_cx - (d_cancel_w / 2);
#endif
/*
** Button Enumerations
*/
enum {
BUTTON_CANCEL = 100,
};
/*
** Buttons
*/
//TextButtonClass *buttons; // button list
TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL,
TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW,
//#if (GERMAN | FRENCH)
// d_cancel_x, d_cancel_y);
//#else
d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h);
//#endif
//buttons = &cancelbtn;
Fancy_Text_Print(TXT_NONE, 0, 0, CC_GREEN, TBLACK,
TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW);
char users_name[256];
int buffer_len = sizeof (users_name);
bool process;
bool display;
KeyNumType input;
if (!Special.IsFromWChat && !SpawnedFromWChat){
/*
** If the user is registered with Planet Westwood then spawn WChat.
*/
if (Is_User_WChat_Registered(users_name, buffer_len)){
GameStatisticsPacketSent = false;
if (!Spawn_WChat(true)){
Set_Logic_Page(SeenBuff);
Load_Title_Screen("HTITLE.PCX", &HidPage, Palette);
Blit_Hid_Page_To_Seen_Buff();
Set_Palette(Palette);
CCMessageBox().Process(TXT_ERROR_UNABLE_TO_RUN_WCHAT, TXT_OK);
LogicPage->Clear();
return(false);
}
}else{
Set_Logic_Page(SeenBuff);
Load_Title_Screen("HTITLE.PCX", &HidPage, Palette);
Blit_Hid_Page_To_Seen_Buff();
Set_Palette(Palette);
if (CCMessageBox().Process(TXT_EXPLAIN_REGISTRATION, TXT_REGISTER, TXT_CANCEL)){
LogicPage->Clear();
return(false);
}else{
LogicPage->Clear();
Spawn_Registration_App();
return(false);
}
}
}
/*
**
** User is registered and we spawned WChat. Wait for a game start message from WChat.
**
*/
process = true;
display = true;
while (process){
/*
** If we have just received input focus again after running in the background then
** we need to redraw.
*/
if (AllSurfaces.SurfacesRestored){
AllSurfaces.SurfacesRestored = FALSE;
display = true;
}
if (display) {
Set_Logic_Page(SeenBuff);
Hide_Mouse();
/*
** Redraw backgound & dialog box
*/
Load_Title_Screen("HTITLE.PCX", &HidPage, Palette);
Blit_Hid_Page_To_Seen_Buff();
Set_Palette(Palette);
Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h);
/*
** Dialog & Field labels
*/
Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w);
Fancy_Text_Print(TXT_CONNECTING, d_dialog_cx-width/2, d_dialog_y + 25*factor, CC_GREEN, TBLACK,
TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW);
//cancelbtn.Zap();
//buttons = &cancelbtn;
/*
.................... Rebuild the button list ....................
*/
//buttons->Draw_All();
cancelbtn.Draw_Me(true);
Show_Mouse();
display = false;
}
/*
** See if the game start packet has arrived from wchat yet.
*/
if (DDEServer.Get_MPlayer_Game_Info()){
//MessageBox (NULL, "About to restore focus to C&C95", "C&C95", MB_OK);
//SetForegroundWindow ( MainWindow );
//ShowWindow ( MainWindow, SW_SHOWMAXIMIZED );
return(true);
}
//input = buttons->Input();
input = cancelbtn.Input();
/*
---------------------------- Process input ----------------------------
*/
switch (input) {
/*
** Cancel. Just return to the main menu
*/
case (KN_ESC):
case (BUTTON_CANCEL | KN_BUTTON):
process = false;
Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_CONNECTION_FAILED);
GameStatisticsPacketSent = false;
Spawn_WChat(false);
break;
}
}
#endif //DEMO
return (false);
}