// ctcl.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "ctcls.h"
#include "ctcl.h"

#if !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)
#include "recscore.h"
#endif // !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)

#define MAX_TESLAS          16
#define MAX_CAMERAS         4

#define PORT_Launcher       1000
#define PORT_Game           1001
#define PORT_CameraShip     1001

#define TCKS_GameInfo       1000

class CTeslaInfo;

#if !defined(CTCL_LAUNCHER)
extern void _stdcall GetFileName4GUID(char szBuf[256], const char* pcszDrv, const char* pcszPath, const char* pcszExt, const GUID& guid = *(const GUID*)NULL);
extern const char* g_pcszGameDataSavePath;
extern const char* g_pcszGameDataExtPR;
extern const char* g_pcszGameDataExtMR;
extern HWND hWindow;
#endif // !defined(CTCL_LAUNCHER)

int GetCTCLTypePort(int nCTCLType);
CSOCClient* CreateSOCClient(const CSOCListen& SOCListen, DWORD dwCSCParam1, DWORD dwCSCParam2);

class CSOC_Server : public CSOCServer
{
	DECLARE_PACKET_MAP(CSOC_Server);
public:
    CTeslaInfo* m_pTI;
    DWORD m_dwLastGameInfo;
    int m_nGameReturn;
public:
    CSOC_Server(CTeslaInfo& TI);
    virtual ~CSOC_Server();
    virtual void OnConnect();
    BOOL CheckLastGameInfo();
    void OnGameReturn();
#if !defined(CTCL_LAUNCHER)
    void OnSendFile();
    void OnInviteCOOP();
#endif // !defined(CTCL_LAUNCHER)
};

class CSOC_2Launcher : public CSOC_Server
{
	DECLARE_PACKET_MAP(CSOC_2Launcher);
public:
    BOOL m_bMissionReview;
public:
	CSOC_2Launcher(CTeslaInfo& TI);
	virtual ~CSOC_2Launcher();
    virtual void OnConnect();
    void CheckConnection();
    void OnGameInfo();
};

class CSOC_2Game : public CSOC_Server
{
	DECLARE_PACKET_MAP(CSOC_2Game);
public:
public:
	CSOC_2Game(CTeslaInfo& TI);
	virtual ~CSOC_2Game();
};

class CSOC_2CameraShip : public CSOC_Server
{
	DECLARE_PACKET_MAP(CSOC_2CameraShip);
public:
public:
	CSOC_2CameraShip(CTeslaInfo& TI);
	virtual ~CSOC_2CameraShip();
};

class CSOC_Client : public CSOCClient
{
	DECLARE_PACKET_MAP(CSOC_Client);
public:
public:
    CSOC_Client();
    virtual ~CSOC_Client();
    virtual void OnAccept();
    void OnOrderAppl();
    void OnErrorStartGame();
    void OnReadyStartGame();
    void OnBOTS();
    void OnSetMechs();
    void OnDoLaunch();
#if !defined(CTCL_LAUNCHER)
    void OnSendFile();
    void OnInviteCOOP();
#endif // !defined(CTCL_LAUNCHER)
};
#if defined(CTCL_LAUNCHER)
class CSOC_Launcher : public CSOC_Client
{
	DECLARE_PACKET_MAP(CSOC_Launcher);
public:
public:
	CSOC_Launcher();
	virtual ~CSOC_Launcher();
    void OnGameInfo();
    void OnOrderAppl();
};
#else // !defined(CTCL_LAUNCHER)
class CSOC_Game : public CSOC_Client
{
	DECLARE_PACKET_MAP(CSOC_Game);
public:
public:
	CSOC_Game();
	virtual ~CSOC_Game();
};

class CSOC_CameraShip : public CSOC_Client
{
	DECLARE_PACKET_MAP(CSOC_CameraShip);
public:
public:
	CSOC_CameraShip();
	virtual ~CSOC_CameraShip();
};
#endif // defined(CTCL_LAUNCHER)
DECLARE_TARRAYREF(CTeslaInfo);
class CTeslaInfo : public CStr
{
public:
    BOOL m_bCameraShip;
    CStr m_strHost;
    u_long m_ulAddr;
	CSOC_2Launcher m_2Launcher;
    CSOC_2Game* m_p2Game;
    CSOC_2CameraShip* m_p2CameraShip;

    int m_nApplType;
    int m_nApplState;
    int m_nGameState;
    int m_nGameTime;
    int m_nGameIsServer;

    CInviteCOOP m_COOP;
public:
    CTeslaInfo();
    void SetTeslaInfo();
    void CheckConnection();
    CSOC_Server& GetGameSOC() const
    {
        if (m_bCameraShip) {
            return *m_p2CameraShip;
        } else {
            return *m_p2Game;
        }
    }
};

class CTeslas : public TARRAYREF(CTeslaInfo)
{
public:
    int m_nCameraShips;
public:
    CTeslas()
    {
        m_nCameraShips = 0;
    }
    ~CTeslas()
    {
        m_nCameraShips = 0;
    }
    CTeslaInfo& AddNew(int nAllocType);
    CTeslaInfo& GetMissionReview() const;
};

#if !defined(CTCL_LAUNCHER)
class CSendFile : public TDBLNK(CSendFile*)
{
public:
    SYSTEMTIME m_SysTime;
    GUID m_guid;
    int m_nType;
    DWORD m_dwLast;
    bool m_bProcessed;
    CStr m_strHost;
    DWORD m_dwTickCreated;
    DWORD m_dwTickDelay;
public:
    CSendFile(const SYSTEMTIME& SysTime, const GUID& guid, int nType);
    void Check();
};

class CSendFileManager : public TDBLNKDESTROY(CSendFile)
{
public:
public:
    CSendFileManager();
    CSendFile* Find(const GUID& guid, int nType) const;
};
#endif // !defined(CTCL_LAUNCHER)

class CCTCLManager : public CSOCManager
{
public:
    CTeslas m_aTIs;
    CStr m_aCmds[_EAT_MAX][2]; // game & cameraship
#if !defined(CTCLS_EXPORTS)
    // Mech4 only
    TARRAY(BYTE) m_aNMP;
#if !defined(CTCL_LAUNCHER)
    CSendFileManager m_SFM_Send;
    CSendFileManager m_SFM_Recv;
    CSendFileManager m_SFM_Done;
#endif // !defined(CTCL_LAUNCHER)
#endif // !defined(CTCLS_EXPORTS)
public:
    CCTCLManager() : CSOCManager(1000) { }
    BOOL Start(int nCTCLType);
    void Stop();
    void Run();
#if !defined(CTCLS_EXPORTS)
    void DoMech4Comm();
    void SaveNMP(const void* p, int nSize);
    void* LoadNMP(int& nSize);
#if !defined(CTCL_LAUNCHER)
    void SendFile(const SYSTEMTIME& SysTime, const GUID& guid, int nType);
    bool DoReprintOrCheck(bool bRealPrint);
    bool IsPendingMissionReplayRequest() const;
#endif / !defined(CTCL_LAUNCHER)
#endif // !defined(CTCLS_EXPORTS)
};
CCTCLManager* g_pCTCLManager = NULL;

#if !defined(CTCL_LAUNCHER)
// not extern "C"
extern bool g_bCOOP; // COin OPeration
extern int g_nCoinCount;
extern int g_nCoinCountTemp;
extern int g_nCoinPerGame;
#endif // !defined(CTCL_LAUNCHER)

#if defined(CTCL_LAUNCHER)
char g_szTeslaName[128];
#else // !defined(CTCL_LAUNCHER)
extern char g_szTeslaName[128];
extern char g_szTeslaAddr[128];
extern DWORD g_dwTeslaAddr;
extern char g_szTeslaInviter[128];
#endif // !defined(CTCL_LAUNCHER)

extern "C" {
int g_nCTCLType = _ECTCL_None;

int g_nCTCL_DoLaunchGame = 0;
#if !defined(CTCLS_EXPORTS)
CInviteCOOP g_InviteCOOP;
// Mech4 only
int g_nMech4Comm = 0;
BOOL g_bIsServer = FALSE;
SPlayerInfo g_aPlayerInfos[20];
int g_nPlayerInfos;
int g_nServer;
int g_nTeslas;
int g_nPrintOut;
int g_nMissionReview;
SYSTEMTIME g_SysTime;
GUID g_guidGameDatas;
char g_szGameName[128];
int g_nBOTs;

DWORD g_dwJoinStartTicks;
#endif // !defined(CTCLS_EXPORTS)

const char PROF_INI[] = "c:\\ctcl.ini";
const char SECT_TESLAS[] = "teslas";
#ifdef _DEBUG
const char SECT_GAMES[] = "games4dbg";
#else // !_DEBUG
const char SECT_GAMES[] = "games";
#endif // _DEBUG
const char SECT_CONFIG[] = "config";

const char SECT_AUX1TEAM[] = "aux1team";
const char SECT_AUX1FFA[] = "aux1ffa";
const char SECT_AUX1MISC[] = "aux1misc";
const char SECT_SND1TEAM[] = "snd1team";
const char SECT_SND1FFA[] = "snd1ffa";
const char SECT_SND1MISC[] = "snd1misc";
} // end of extern "C"

BEGIN_PACKET_MAP(CSOC_Server, CSOCServer)
    PACKET_MAP_ENTRY(CSOC_Server, S_GameReturn, OnGameReturn)
#if !defined(CTCL_LAUNCHER)
    PACKET_MAP_ENTRY(CSOC_Server, S_SendFile, OnSendFile)
    PACKET_MAP_ENTRY(CSOC_Server, S_InviteCOOP, OnInviteCOOP)
#endif // !defined(CTCL_LAUNCHER)
END_PACKET_MAP(CSOC_Server)

BEGIN_PACKET_MAP(CSOC_2Launcher, CSOC_Server)
    PACKET_MAP_ENTRY(CSOC_2Launcher, S_GameInfo, OnGameInfo)
END_PACKET_MAP(CSOC_2Launcher)

BEGIN_PACKET_MAP(CSOC_2Game, CSOC_Server)
END_PACKET_MAP(CSOC_2Game)

BEGIN_PACKET_MAP(CSOC_2CameraShip, CSOC_Server)
END_PACKET_MAP(CSOC_2CameraShip)

BEGIN_PACKET_MAP(CSOC_Client, CSOCClient)
    PACKET_MAP_ENTRY(CSOC_Client, C_OrderAppl, OnOrderAppl)
    PACKET_MAP_ENTRY(CSOC_Client, C_ErrorStartGame, OnErrorStartGame)
    PACKET_MAP_ENTRY(CSOC_Client, C_ReadyStartGame, OnReadyStartGame)
    PACKET_MAP_ENTRY(CSOC_Client, C_BOTS, OnBOTS)
    PACKET_MAP_ENTRY(CSOC_Client, C_SetMechs, OnSetMechs)
    PACKET_MAP_ENTRY(CSOC_Client, C_DoLaunch, OnDoLaunch)
#if !defined(CTCL_LAUNCHER)
    PACKET_MAP_ENTRY(CSOC_Client, C_SendFile, OnSendFile)
    PACKET_MAP_ENTRY(CSOC_Client, C_InviteCOOP, OnInviteCOOP)
#endif // !defined(CTCL_LAUNCHER)
END_PACKET_MAP(CSOC_Client)

#if defined(CTCL_LAUNCHER)
BEGIN_PACKET_MAP(CSOC_Launcher, CSOC_Client)
    PACKET_MAP_ENTRY(CSOC_Launcher, C_GameInfo, OnGameInfo)
    PACKET_MAP_ENTRY(CSOC_Launcher, C_OrderAppl, OnOrderAppl)
END_PACKET_MAP(CSOC_Launcher)
#else // !defined(CTCL_LAUNCHER)
BEGIN_PACKET_MAP(CSOC_Game, CSOC_Client)
END_PACKET_MAP(CSOC_Game)

BEGIN_PACKET_MAP(CSOC_CameraShip, CSOC_Client)
END_PACKET_MAP(CSOC_CameraShip)
#endif // defined(CTCL_LAUNCHER)

#if !(defined(CTCLS_EXPORTS) || defined(CTCL_LAUNCHER))
void LoadAux1Data();
#endif // !(defined(CTCLS_EXPORTS) || defined(CTCL_LAUNCHER))

#if !(defined(CTCLS_EXPORTS) || defined(CTCL_LAUNCHER))
void LoadAux1Data()
{
extern char aux1_messages[14 + 2][60];
extern char aux1_sounds[14 + 2][60];
    char sz[4 + 60 + 10/*safety*/];
    char szName[64];
    char* psz = &sz[4];

    for(int i = 1; i <= 7; i++) {
        sprintf(szName, "%d", i);

        sprintf(sz, "%d - ", i);
        GetPrivateProfileString(SECT_AUX1TEAM, szName, "", psz, 60 - 4, PROF_INI);
        if (psz[0]) {
            strcpy(&aux1_messages[i - 1][0], sz);
        }

        GetPrivateProfileString(SECT_AUX1FFA, szName, "", psz, 60 - 4, PROF_INI);
        if (psz[0]) {
            strcpy(&aux1_messages[i - 1 + 7][0], sz);
        }

        if (i <= 2) { // misc
            GetPrivateProfileString(SECT_AUX1MISC, szName, "", psz, 60 - 4, PROF_INI);
            if (sz[0]) {
                strcpy(&aux1_messages[i - 1 + 14][0], sz);
            }
        }

        GetPrivateProfileString(SECT_SND1TEAM, szName, "", sz, 60, PROF_INI);
        if (sz[0]) {
            strcat(sz, "{handle}");
            strcpy(&aux1_sounds[i - 1][0], sz);
        }

        GetPrivateProfileString(SECT_SND1FFA, szName, "", sz, 60, PROF_INI);
        if (sz[0]) {
            strcat(sz, "{handle}");
            strcpy(&aux1_sounds[i - 1 + 7][0], sz);
        }

        if (i <= 2) { // misc
            GetPrivateProfileString(SECT_SND1MISC, szName, "", sz, 60, PROF_INI);
            if (sz[0]) {
                strcat(sz, "{handle}");
                strcpy(&aux1_sounds[i - 1 + 14][0], sz);
            }
        }
    }
}
#endif // !(defined(CTCLS_EXPORTS) || defined(CTCL_LAUNCHER))

bool _stdcall RunExec(const CStr& strExec)
{
    if (strExec.GetLength() > 0) {
        CStr strDir;
        int nStart = 0, nLen;
        const char* pcszExec = strExec.String();
        if (*pcszExec == '"') {
            nStart++;
            nLen = strExec.Find('"');
            if (nLen != -1) {
                nLen--;
            }
        } else {
            nLen = strExec.Find(' ');
        }
        strDir.Assign(&pcszExec[nStart], nLen);
        strDir.AsDirectory(TRUE);

        char szBuf[512];
        strcpy(szBuf, strExec.String());
        STARTUPINFO si;
        memset(&si, 0, sizeof(si));
        si.cb = sizeof(si);
        si.dwX = si.dwY = si.dwXSize = si.dwYSize = CW_USEDEFAULT;
        PROCESS_INFORMATION pi;
    
        return !!CreateProcess(NULL, szBuf, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_PROCESS_GROUP | HIGH_PRIORITY_CLASS, NULL, strDir.String(), &si, &pi);
    }

    return false;
}

int GetCTCLTypePort(int nCTCLType)
{
    switch(nCTCLType) {
    case _ECTCL_Launcher:
        return PORT_Launcher;
    case _ECTCL_Game:
        return PORT_Game;
    case _ECTCL_CameraShip:
        return PORT_CameraShip;
    default:
        ASSERT(FALSE);
        return 0;
    }
}

CSOCClient* CreateSOCClient(const CSOCListen& SOCListen, DWORD dwCSCParam1, DWORD dwCSCParam2)
{
    &dwCSCParam1, &dwCSCParam2;

    switch(dwCSCParam1) {
#if defined(CTCL_LAUNCHER)
    case _ECTCL_Launcher:
        return new CSOC_Launcher();
#else // !defined(CTCL_LAUNCHER)
    case _ECTCL_Game:
        return new CSOC_Game();
    case _ECTCL_CameraShip:
        return new CSOC_CameraShip();
#endif // defined(CTCL_LAUNCHER)
    default:
        ASSERT(FALSE);
        return NULL;
    }
}
#if !defined(CTCLS_EXPORTS)
CInviteCOOP::CInviteCOOP()
{
    m_pParent = NULL;

    Clear();
}

void CInviteCOOP::Clear()
{
    m_nTimeInvite = 0;
    m_nResult = 0;
    m_nResultAccepted = 0;
    memset(&m_guidGameData, 0, sizeof(m_guidGameData));
}
#endif // !defined(CTCLS_EXPORTS)
CSOC_Server::CSOC_Server(CTeslaInfo& TI)
    : m_pTI(&TI)
{
    m_dwLastGameInfo = 0;
    m_nGameReturn = _EGR_NotConnected;
}

CSOC_Server::~CSOC_Server()
{
}

void CSOC_Server::OnConnect()
{
    m_nGameReturn = _EGR_Connected;
}

BOOL CSOC_Server::CheckLastGameInfo()
{
    DWORD dwTicks = GetTickCount();
    if (m_dwLastGameInfo) {
        if ((m_dwLastGameInfo + TCKS_GameInfo) <= dwTicks) {
            m_dwLastGameInfo = dwTicks;
            return TRUE;
        }
    } else {
        m_dwLastGameInfo = dwTicks - TCKS_GameInfo + (rand() % TCKS_GameInfo);
    }
    return FALSE;
}

void CSOC_Server::OnGameReturn()
{
    Disassemble("n", &m_nGameReturn);
}
#if !defined(CTCL_LAUNCHER)
void CSOC_Server::OnSendFile()
{
    BYTE bCode;
    if (Disassemble("B", &bCode)) {
        ASSERT((bCode == 0) || (bCode == 3));
        // Receiving from Game to console...
        SYSTEMTIME* pSysTime;
        WORD wSysTimeSize;
        GUID* pGUID;
        WORD wSize;
        int nType;
        DWORD dwTickDelay;
        if (Disassemble("xxnD", &pSysTime, &wSysTimeSize, &pGUID, &wSize, &nType, &dwTickDelay)) {
            if (pSysTime && (wSysTimeSize == sizeof(SYSTEMTIME)) && pGUID && (wSize == sizeof(GUID))) {
                CSendFile* pSF;
                pSF = g_pCTCLManager->m_SFM_Done.Find(*pGUID, nType);
                if (!pSF) {
                    // not already processed...
                    pSF = g_pCTCLManager->m_SFM_Recv.Find(*pGUID, nType);
                    if (pSF) {
                        if (bCode == 3) {
                            // Did we receive confirmation the mission review server got the mission review?
                            pSF->m_bProcessed = true;
                            g_pCTCLManager->m_SFM_Recv.Cut(pSF);
                            g_pCTCLManager->m_SFM_Done.AddTail(pSF);
                        }
                    } else {
                        // not already receieved...
                        pSF = new CSendFile(*pSysTime, *pGUID, nType);
                        pSF->m_strHost.Assign(m_pTI->m_strHost);
                        pSF->m_dwTickDelay = dwTickDelay;
                        g_pCTCLManager->m_SFM_Recv.AddTail(pSF);
                    }
                }
                if (bCode == 0)
                    DoSendX("BBxxnD", C_SendFile, 1, pSysTime, wSysTimeSize, pGUID, wSize, nType, 0);
            }
        }
    }
}

void CSOC_Server::OnInviteCOOP()
{
    int nTimeInvite;
    GUID* pGUID;
    WORD wGUID;
    int nResult;

    if (Disassemble("nxn", &nTimeInvite, &pGUID, &wGUID, &nResult)) {
        if (pGUID && (wGUID == sizeof(GUID))) {
            CInviteCOOP& COOP = m_pTI->m_COOP;
            if ((COOP.m_nTimeInvite == nTimeInvite) && (memcmp(&COOP.m_guidGameData, pGUID, sizeof(GUID)) == 0)) {
                DoSendX("Bnxn", S_InviteCOOP, nTimeInvite, pGUID, wGUID, -1); // result accepted...
                COOP.m_nResult = nResult;
            } else {
                DoSendX("Bnxn", S_InviteCOOP, nTimeInvite, pGUID, wGUID, -2); // forget result... I don't know...
            }
        }
    }
}
#endif // !defined(CTCL_LAUNCHER)
CSOC_2Launcher::CSOC_2Launcher(CTeslaInfo& TI)
    : CSOC_Server(TI), m_bMissionReview(FALSE)
{
}

CSOC_2Launcher::~CSOC_2Launcher()
{
}

void CSOC_2Launcher::OnConnect()
{
    CSOC_Server::OnConnect();
    DoSendX("B", C_GameInfo);
}

void CSOC_2Launcher::CheckConnection()
{
#if !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)
    if (g_bCOOP) {
        m_pTI->CheckConnection();
    } else {
#endif // !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)
    if (/*!m_bMissionReview && */!IsValid()) {
        g_pCTCLManager->DoConnect(this);
    } else {
        if (GetConnectionState() == +1) {
            if (CheckLastGameInfo()) {
                DoSendX("B", C_GameInfo);
            }
            m_pTI->CheckConnection();
        }
    }
#if !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)
    }
#endif // !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)
}

void CSOC_2Launcher::OnGameInfo()
{
    BYTE bApplType;
    BYTE bApplState;
    BYTE bGameState;
    int nGameTime;
    BYTE bGameIsServer;

    if (Disassemble("BBBDB", &bApplType, &bApplState, &bGameState, &nGameTime, &bGameIsServer)) {
        m_pTI->m_nApplType = bApplType;
        m_pTI->m_nApplState = bApplState;
        m_pTI->m_nGameState = bGameState;
        m_pTI->m_nGameTime = nGameTime;
        m_pTI->m_nGameIsServer = bGameIsServer;
    }
}

CSOC_2Game::CSOC_2Game(CTeslaInfo& TI)
    : CSOC_Server(TI)
{
    SetTarget(TI.m_ulAddr);
    SetPort(PORT_Game);
    SetSOCPP((CSOC **)&TI.m_p2Game);
}

CSOC_2Game::~CSOC_2Game()
{
}

CSOC_2CameraShip::CSOC_2CameraShip(CTeslaInfo& TI)
    : CSOC_Server(TI)
{
    SetTarget(TI.m_ulAddr);
    SetPort(PORT_CameraShip);
    SetSOCPP((CSOC **)&TI.m_p2CameraShip);
}

CSOC_2CameraShip::~CSOC_2CameraShip()
{
}

CSOC_Client::CSOC_Client()
{
}

CSOC_Client::~CSOC_Client()
{
    if (g_InviteCOOP.m_pParent == this) {
        g_InviteCOOP.m_pParent = NULL;
        //?g_InviteCOOP.Clear();
    }
}

void CSOC_Client::OnOrderAppl()
{
    BYTE bOrder, bCameraShip;
    int nParam;

    if (Disassemble("BDB", &bOrder, &nParam, &bCameraShip)) {
        switch(bOrder) {
        case _CTCL_Order_Terminate: // terminate
            CTCL_DoTerminateAppl();
            break;
        case _CTCL_Order_EndMission: // End Mission
            CTCL_DoEndMission();
            break;
        }
    }
}
#if defined(CTCL_LAUNCHER)
CSOC_Launcher::CSOC_Launcher()
{
}

CSOC_Launcher::~CSOC_Launcher()
{
}

void CSOC_Launcher::OnGameInfo()
{
    int nApplType = CTCL_GetApplType();
    int nApplState = CTCL_GetApplState();
    int nGameState = CTCL_GetGameState();
    int nGameTime = CTCL_GetGameTime();
    int nGameIsServer = CTCL_GetGameIsServer();
    DoSendX("BBBBDB", S_GameInfo, nApplType, nApplState, nGameState, nGameTime, nGameIsServer);
}

void CSOC_Launcher::OnOrderAppl()
{
    BYTE bOrder, bCameraShip;
    int nParam;

    if (Disassemble("BDB", &bOrder, &nParam, &bCameraShip)) {
        switch(bOrder) {
        case _CTCL_Order_Launch: // Launch
            if ((0 < nParam) && (nParam <= _EAT_MAX)) {
                nParam--;
                const CStr& strExec = g_pCTCLManager->m_aCmds[nParam][bCameraShip ? 1: 0];
                if (strExec.GetLength() > 0) {
                    //WinExec(strExec, SW_SHOWNORMAL);
                    RunExec(strExec);
                }
            } else {
                ASSERT(FALSE);
            }
            break;
        case _CTCL_Order_Shutdown:
        case _CTCL_Order_Reboot:
          {
            HANDLE hToken;
            TOKEN_PRIVILEGES TokenPrivileges;
            int nErr = 0;
            if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
                LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME/*SE_REMOTE_SHUTDOWN_NAME*/, &(TokenPrivileges.Privileges[0].Luid));
                TokenPrivileges.PrivilegeCount           = 1;
                TokenPrivileges.Privileges[0].Attributes = 2;
                AdjustTokenPrivileges(hToken, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
                CloseHandle(hToken);
#if 1
                if (!ExitWindowsEx((bOrder == _CTCL_Order_Shutdown) ? EWX_POWEROFF: EWX_REBOOT, 0)) {
                    GetLastError();
                }
#else
                if (!InitiateSystemShutdown(NULL,  // name of computer to shut down 
                    NULL,      // address of message to display
                    10,       // time to display dialog box 
                    TRUE, // force applications with unsaved changes flag 
                    FALSE))
                {
                    GetLastError();
                }
#endif
            }
            break;
           }
		case _CTCL_Order_Unload:
			PostQuitMessage(0);
			break;
        }
    }
}
#else // !defined(CTCL_LAUNCHER)
CSOC_Game::CSOC_Game()
{
}

CSOC_Game::~CSOC_Game()
{
}

CSOC_CameraShip::CSOC_CameraShip()
{
}

CSOC_CameraShip::~CSOC_CameraShip()
{
}
#endif // defined(CTCL_LAUNCHER)
void CSOC_Client::OnAccept()
{
    // include launcher?
    g_pCTCLManager->m_aNMP.Reset();
    g_bIsServer = FALSE;
    g_nPlayerInfos = 0;
    g_nServer = -1;
    g_nTeslas = 0;
    g_nBOTs = 0;
    g_szGameName[0] = '\0';
}

void CSOC_Client::OnErrorStartGame()
{
#if !defined(CTCLS_EXPORTS)
    // to main menu....
	//$$COOP
    if (g_nMech4Comm) {
        if (g_bIsServer) {
        } else {
        }
    }
#endif // !defined(CTCLS_EXPORTS)
}

void CSOC_Client::OnReadyStartGame()
{
#if !defined(CTCLS_EXPORTS)
	//$$COOP
    BYTE bType;
    BYTE* p;
    WORD wLen;
	BYTE bTeam;
    const char* pcszName;
    int nMechIndex;
	short fileID;
	WORD recordID;
    const char* pcszMech;
    int nTeamOrSkin;
    int nDecal;
    DWORD dwAddr;
    BYTE bPrintOut;
    BYTE bMissionReview;
    BYTE* pGUID;
    WORD wGuidSize;
    SYSTEMTIME* pSysTime;
    WORD wSysTime;

    if (Disassemble("BnnxBsDWwsnnDBBxx", &bType, &g_nTeslas, &g_nBOTs, &p, &wLen, &bTeam, &pcszName,
        &nMechIndex, &fileID, &recordID, &pcszMech, &nTeamOrSkin, &nDecal, &dwAddr,
        &bPrintOut, &bMissionReview, &pGUID, &wGuidSize, &pSysTime, &wSysTime)) {
        if (pGUID && (wGuidSize == sizeof(GUID)) && pSysTime && (wSysTime == sizeof(SYSTEMTIME))) {
            g_nPlayerInfos = 1 + g_nBOTs + g_nTeslas;
            g_bIsServer = (bType == 1) ? TRUE: FALSE;
            const GUID& guid = *(const GUID*)pGUID;
            sprintf(g_szGameName, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
                    guid.Data1, guid.Data2, guid.Data3,
                    guid.Data4[0], guid.Data4[1],
                    guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);

            g_pCTCLManager->SaveNMP(p, wLen);

            SPlayerInfo& PI = g_aPlayerInfos[0];
            PI.m_bBot = FALSE;
		    PI.m_bTeam = bTeam;
            PI.m_nLevelOrTesla = 0; // never mind
            strcpy(PI.m_szName, pcszName);
            PI.m_uPlayerID = 0;
            PI.m_nMechIndex = nMechIndex;
	        PI.m_fileID = fileID;
	        PI.m_recordID = recordID;
            strcpy(PI.m_szMech, pcszMech);
            PI.m_nTeamOrSkin = nTeamOrSkin;
            PI.m_nDecal = nDecal;
            PI.m_dwAddr = dwAddr;
            g_nPrintOut = bPrintOut;
            g_nMissionReview = bMissionReview;
            g_guidGameDatas = *(GUID*)pGUID;
            g_SysTime = *pSysTime;

            CTCL_DoSendGameReturn(_EGR_PreparingStarted);
        } else {
            Drop();
        }
    }
#endif // !defined(CTCLS_EXPORTS)
}

void CSOC_Client::OnBOTS()
{
#if !defined(CTCLS_EXPORTS)
	//$$COOP
    ASSERT(g_bIsServer);
    int i;
    BYTE bBOT;
    int nLevelOrTesla;
    const char* pcszName;
    int nMechIndex;
	short fileID;
	WORD recordID;
    const char* pcszMech;
    int nTeamOrSkin;
    int nDecal;

    for(i = 1; i < g_nPlayerInfos; i++) {
        if (Disassemble("BnsDWwsnn", &bBOT, &nLevelOrTesla, &pcszName, &nMechIndex, &fileID, &recordID, &pcszMech, &nTeamOrSkin, &nDecal)) {
            SPlayerInfo& PI = g_aPlayerInfos[i];
            PI.m_bBot = bBOT ? TRUE: FALSE;
            PI.m_nLevelOrTesla = nLevelOrTesla;
            strcpy(PI.m_szName, pcszName);
            PI.m_uPlayerID = 0;
            PI.m_nMechIndex = nMechIndex;
	        PI.m_fileID = fileID;
	        PI.m_recordID = recordID;
            strcpy(PI.m_szMech, pcszMech);
            PI.m_nTeamOrSkin = nTeamOrSkin;
            PI.m_nDecal = nDecal;
        } else {
            return;
        }
    }
    g_nMech4Comm = 1;
#endif // !defined(CTCLS_EXPORTS)
}

void CSOC_Client::OnSetMechs()
{
    ASSERT(!g_bIsServer);
    CTCL_DoJoinGame();
}

void CSOC_Client::OnDoLaunch()
{
	CTCL_StartGame(TRUE);
}
#if !defined(CTCL_LAUNCHER)
void CSOC_Client::OnSendFile()
{
    BYTE bCode;
    if (Disassemble("B", &bCode)) {
        ASSERT((bCode == 1) || (bCode == 2));
        // Received from console to Game...
        SYSTEMTIME* pSysTime;
        WORD wSysTimeSize;
        GUID* pGUID;
        WORD wSize;
        int nType;
        if (Disassemble("xxn", &pSysTime, &wSysTimeSize, &pGUID, &wSize, &nType)) {
            if (pSysTime && (wSysTimeSize == sizeof(SYSTEMTIME)) && pGUID && (wSize == sizeof(GUID))) {
                CSendFile* pSF;

                if (bCode == 1) {
                    pSF = g_pCTCLManager->m_SFM_Send.Find(*pGUID, nType);
                    if (pSF) {
                        // receieved by ...
                        g_pCTCLManager->m_SFM_Send.Cut(pSF);
                        delete pSF;
                    }
                } else {
                    ASSERT(bCode == 2);
                    const char* pcszFile;
                    if (Disassemble("s", &pcszFile)) {
                        pSF = g_pCTCLManager->m_SFM_Done.Find(*pGUID, nType);
                        if (!pSF) {
                            pSF = g_pCTCLManager->m_SFM_Recv.Find(*pGUID, nType);
                            if (!pSF) {
                                CSendFile* pSF = new CSendFile(*pSysTime, *pGUID, nType);
                                pSF->m_strHost.Assign(pcszFile);
                                if ((nType == 1) && (g_nCTCLType == _ECTCL_CameraShip) && (*pSF->m_strHost.String() != '\0')) {
                                    g_pCTCLManager->m_SFM_Recv.AddTail(pSF);
                                    CTCL_SetGameState(_EGS_Preparing);
                                } else {
                                    g_pCTCLManager->m_SFM_Done.AddTail(pSF);
                                }
                            }
                        }
                        DoSendX("BBxxnD", S_SendFile, 3, pSysTime, wSysTimeSize, pGUID, wSize, nType, 0);
                    }
                }
            }
        }
    }
}

void CSOC_Client::OnInviteCOOP()
{
    int nTimeInvite;
    GUID* pGUID;
    WORD wGUID;
    int nResult;

    if (Disassemble("nxn", &nTimeInvite, &pGUID, &wGUID, &nResult)) {
        if (pGUID && (wGUID == sizeof(GUID))) {
            if (nResult == 0) { // requested...
                if (nTimeInvite == -999) {
                    if (g_InviteCOOP.m_pParent == this) {
                        g_InviteCOOP.Clear();
                        g_InviteCOOP.m_pParent = NULL;
                    }
                    DoSendX("Bnxn", C_InviteCOOP, nTimeInvite, pGUID, wGUID, -1);
                    return;
                /*} else if (g_nCoinCount < g_nCoinPerGame) {
                    g_InviteCOOP.Clear();
                    g_InviteCOOP.m_pParent = NULL;
                    DoSendX("Bnxn", C_InviteCOOP, nTimeInvite, pGUID, wGUID, 0);
                    return;*/
                } else if (g_InviteCOOP.m_pParent) {
                    if (g_InviteCOOP.m_pParent == this) {
                        if ((g_InviteCOOP.m_nTimeInvite == nTimeInvite) && (memcmp(&g_InviteCOOP.m_guidGameData, pGUID, sizeof(GUID)) == 0)) {
                            if (g_InviteCOOP.m_nResult == 0) {
                                g_InviteCOOP.m_nResult = 1;
                            }
                            DoSendX("Bnxn", C_InviteCOOP, nTimeInvite, pGUID, wGUID, g_InviteCOOP.m_nResult);
                        } else {
                            goto as_new;
                        }
                    } else {
                        if (g_InviteCOOP.m_nResult == 2) {
                            if (g_InviteCOOP.m_nResultAccepted)
                                goto as_new;
                        }
                    }
                } else {
as_new:
                    g_InviteCOOP.m_pParent = this;

                    g_InviteCOOP.m_nTimeInvite = nTimeInvite;
                    g_InviteCOOP.m_nResult = 0;
                    g_InviteCOOP.m_nResultAccepted = 0;
                    memcpy(&g_InviteCOOP.m_guidGameData, pGUID, sizeof(GUID));

                    const char* pcszTesla = "Unknown";
                    const CTeslas& aTIs = g_pCTCLManager->m_aTIs;
                    int i, nTesla = aTIs.GetSize();

                    for(i = 0; i < nTesla; i++)
                    {
                        CTeslaInfo& ti = aTIs.GetAt(i);
                        if (ti.m_ulAddr == m_Addr.GetTargetAddr()) {
                            pcszTesla = ti.String();
                            break;
                        }
                    }
                    strcpy(&g_szTeslaInviter[0], pcszTesla);
                }
            } else {
                ASSERT((nResult == -1) || (nResult == -2));
                if (g_InviteCOOP.m_pParent == this) {
                    if ((g_InviteCOOP.m_nTimeInvite == nTimeInvite) && (memcmp(&g_InviteCOOP.m_guidGameData, pGUID, sizeof(GUID)) == 0)) {
                        g_InviteCOOP.m_nResultAccepted = 1;
                    }
                }
            }
        }
    }
}
#endif // !defined(CTCL_LAUNCHER)
CTeslaInfo::CTeslaInfo()
    : m_2Launcher(*this)
{
    m_bCameraShip = FALSE;
    m_ulAddr = INADDR_ANY;

    m_2Launcher.SetAutoDelete(FALSE);
    m_p2Game = NULL;
    m_p2CameraShip = NULL;
}

void CTeslaInfo::SetTeslaInfo()
{
    m_2Launcher.m_pTI = this;
#if !defined(CTCL_LAUNCHER)
    if (g_bCOOP) {
        const char* pcszHost = m_strHost.String();
        ASSERT((pcszHost[0] == '\\') && (pcszHost[1] == '\\'));
        CSockAddr addr(0, &pcszHost[2]);
        u_long ulAddr = addr.GetTargetAddr();
        if ((ulAddr != INADDR_NONE) && (ulAddr != INADDR_ANY)  && (ulAddr != INADDR_LOOPBACK)) {
            m_ulAddr = ulAddr;
        }
    }
#endif // !defined(CTCL_LAUNCHER)
}

void CTeslaInfo::CheckConnection()
{
#if !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)
    if (g_bCOOP || (m_nApplType != _EAT_None))
#else // !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)
    if (m_nApplType != _EAT_None)
#endif // !defined(CTCLS_EXPORTS) && !defined(CTCL_LAUNCHER)
    {
        if (m_bCameraShip) {
            if (m_p2CameraShip)
                return;
            m_p2CameraShip = new CSOC_2CameraShip(*this);
            g_pCTCLManager->DoConnect(m_p2CameraShip);
        } else {
            if (m_p2Game)
                return;
            m_p2Game = new CSOC_2Game(*this);
            g_pCTCLManager->DoConnect(m_p2Game);
        }
    }
}

CTeslaInfo& CTeslas::AddNew(int nAllocType)
{
    if (nAllocType < 0) {
        int nSize = GetSize();
        if (nAllocType == -2) {
            nAllocType = nSize;
        } else {
            ASSERT(nAllocType == -1);
            int nStart = nSize - m_nCameraShips;
            while(nStart < nSize) {
                CTeslaInfo& ti = GetAt(nStart);
                if (ti.m_2Launcher.m_bMissionReview)
                    break;
                nStart++;
            }
            nAllocType = nStart;
        }
    }

    return Ins(nAllocType);
}

CTeslaInfo& CTeslas::GetMissionReview() const
{
    if (m_nCameraShips > 0) {
        CTeslaInfo* pTI = &GetAt(GetSize() - m_nCameraShips);
        CTeslaInfo* pTIEnd = &pTI[m_nCameraShips];
        while(pTI < pTIEnd) {
            if (pTI->m_2Launcher.m_bMissionReview) {
                return *pTI;
            }
            pTI++;
        }
        //return pTIEnd[-1];
    }
    return *(CTeslaInfo*)NULL;
}

BOOL CTCL_API CTCL_Start(int nCTCLType)
{
    ASSERT(g_nCTCLType == _ECTCL_None);
    g_pCTCLManager = new CCTCLManager();
    if (g_pCTCLManager) {
        if (g_pCTCLManager->Start(nCTCLType)) {
            return TRUE;
        }
        delete g_pCTCLManager;
        g_pCTCLManager = NULL;
    }
    return FALSE;
}

void CTCL_API CTCL_Stop()
{
    if (g_pCTCLManager) {
        g_pCTCLManager->Stop();
        delete g_pCTCLManager;
        g_pCTCLManager = NULL;
    }
}

void CTCL_API CTCL_Run()
{
    if (g_pCTCLManager) {
        g_pCTCLManager->Run();
    }
}
#if !defined(CTCLS_EXPORTS)
#if !defined(CTCL_LAUNCHER)
CSendFile::CSendFile(const SYSTEMTIME& SysTime, const GUID& guid, int nType)
    : m_SysTime(SysTime), m_guid(guid), m_nType(nType), m_dwLast(0), m_bProcessed(false), m_dwTickCreated(GetTickCount()), m_dwTickDelay(0)
{
    extern int g_nSecsMissionReplay;
    extern int g_nSecsMissionReport;

    if (m_nType == 1) { // mission replay file...
        m_dwTickDelay = g_nSecsMissionReplay * 1000;
    }
}

void CSendFile::Check()
{
    CSOC& SOC = *g_pCTCLManager->FindSOC(0);

    if (SOC.GetConnectionState() == 1) {
        DWORD dw = GetTickCount();
        if ((m_dwLast == 0) || ((m_dwLast + 500) < dw)) {
            m_dwLast = dw;
            DWORD dwTickDelay = m_dwTickDelay;
            DWORD dwElapsed = GetTickCount() - m_dwTickCreated;
            if (dwElapsed > dwTickDelay)
                dwTickDelay = 0;
            else
                dwTickDelay -= dwElapsed;
            SOC.DoSendX("BBxxnD", S_SendFile, 0, &m_SysTime, sizeof(m_SysTime), &m_guid, sizeof(m_guid), m_nType, dwTickDelay);
        }
    }
}

CSendFileManager::CSendFileManager()
{
}

CSendFile* CSendFileManager::Find(const GUID& guid, int nType) const
{
    CSendFile* pSF = GetHead();
    while(pSF) {
        if ((memcmp(&pSF->m_guid, &guid, sizeof(guid)) == 0) && (pSF->m_nType == nType))
            return pSF;
        pSF = pSF->m_pNext;
    }

    return pSF;
}
#endif // !defined(CTCL_LAUNCHER)
#endif // !defined(CTCLS_EXPORTS)
BOOL CCTCLManager::Start(int nCTCLType)
{
    if (Init()) {
        g_nCTCLType = nCTCLType;

#if !(defined(CTCLS_EXPORTS) || defined(CTCL_LAUNCHER))
        if ((nCTCLType == _ECTCL_Game) || (nCTCLType == _ECTCL_CameraShip)) {
            LoadAux1Data();
        }
#endif // !(defined(CTCLS_EXPORTS) || defined(CTCL_LAUNCHER))
        CSockAddr addrHost(0, GetHostAddress());
#if !defined(CTCL_LAUNCHER)
        strcpy(&g_szTeslaName[0], addrHost.GetTarget());
        //g_szTeslaAddr[0] = '\x01';
        strcpy(&g_szTeslaAddr[0], addrHost.GetTarget());
        g_dwTeslaAddr = addrHost.GetTargetAddr();
#endif // !defined(CTCL_LAUNCHER)

        char sz[1024];
        char szName[256];
        if (CTCL_IsConsoleOrCOOP()) {
            // Load TeslaInfos...
            bool bMissionReviewFound = false;
            sz[0] = '\0';
            GetPrivateProfileString(SECT_TESLAS, NULL, "", sz, sizeof(sz), PROF_INI);
            const char* pcsz = &sz[0];
            int nTIs = 0;
            while(*pcsz)
            {
                char c = pcsz[0];
                BOOL bCameraShip;
                
                if ((c == '#') || (c == '*')) {
                    pcsz++;
                    bCameraShip = TRUE;
                } else {
                    bCameraShip = FALSE;
                }
                CSockAddr addr(0, pcsz);
                u_long ulAddr = addr.GetTargetAddr();
                int i, n = m_aTIs.GetSize();
                bool bOK = false;
                if ((ulAddr != INADDR_NONE) && (ulAddr != INADDR_ANY)  && (ulAddr != INADDR_LOOPBACK)) {
                for(i = 0; i < n; i++) {
                    const CTeslaInfo& TI = m_aTIs.GetAt(i);
                        if (TI.m_ulAddr == ulAddr)
                        break;
                }
                if (i == n) {
                        bOK = true;
                    }
                }
                if (bOK) {
                        GetPrivateProfileString(SECT_TESLAS, bCameraShip ? &pcsz[-1]: pcsz, "", szName, sizeof(szName), PROF_INI);
                        char* pComma = strchr(szName,',');
                        if (pComma) {
                            *pComma++ = '\0';
                            CStr strName(szName), strHost(pComma);
                            strName.TrimAll();
                            strHost.TrimAll();
                            if ((strName.GetLength() > 0) && (strHost.GetLength() > 0)) {
                            BOOL bAdd = FALSE;
                            if (addrHost.GetTargetAddr() == ulAddr) {
                                strcpy(&g_szTeslaName[0], strName);
                            } else {
                                if (bCameraShip) {
                                    if (m_aTIs.m_nCameraShips < MAX_CAMERAS)
                                        bAdd = TRUE;
                                } else {
                                    if (nTIs < MAX_TESLAS)
                                        bAdd = TRUE;
                                }
                            }
                            if (bAdd) {
                                int nAllocType;
                                if (bCameraShip) {
                                    if (bMissionReviewFound || (pcsz[-1] == '*'))
                                        nAllocType = -2;
                                    else
                                        nAllocType = -1;
                                } else {
                                    nAllocType = nTIs;
                                }
                                CTeslaInfo& TI = m_aTIs.AddNew(nAllocType);
                                TI.Assign(strName);
                                TI.m_bCameraShip = bCameraShip;
                                TI.m_strHost.Assign("\\\\");
                                TI.m_strHost += strHost;
                                TI.m_ulAddr = ulAddr;
                                TI.m_2Launcher.SetTarget(ulAddr);
					            TI.m_2Launcher.SetPort(PORT_Launcher);
                                if (bCameraShip) {
                                    m_aTIs.m_nCameraShips++;
                                    if (!bMissionReviewFound && (nAllocType == -2)) {
                                        TI.m_2Launcher.m_bMissionReview = TRUE;
                                        bMissionReviewFound = true;
                                    }
                                } else {
                                    nTIs++;
                                }
                            }
                        }
                    }
                }
                pcsz += strlen(pcsz) + 1;
            }
            for(int i = 0; i < m_aTIs.GetSize(); i++) {
                m_aTIs.GetAt(i).SetTeslaInfo();
            }
            if (
#if !defined(CTCL_LAUNCHER)
                !g_bCOOP && 
#endif // !defined(CTCL_LAUNCHER)
                (nTIs <= 0) && (m_aTIs.m_nCameraShips <= 0)) {
                Exit();
                g_nCTCLType = _ECTCL_None;
                MessageBox(NULL, "no valid tesla infos!!!", "ctcl.ini error", MB_OK);
                return FALSE;
            }
        } else {
#ifdef CTCL_LAUNCHER
            if (nCTCLType == _ECTCL_Launcher) {
                for(int i = 1; i <= _EAT_MAX; i++) {
                    sprintf(szName, "%d", i); // game
                    GetPrivateProfileString(SECT_GAMES, szName, "", sz, sizeof(sz), PROF_INI);
                    CStr& str1 = m_aCmds[i - 1][0];
                    str1.Assign(sz);
                    str1.TrimAll();

                    sprintf(szName, "#%d", i); // camera ship
                    GetPrivateProfileString(SECT_GAMES, szName, "", sz, sizeof(sz), PROF_INI);
                    CStr& str2 = m_aCmds[i - 1][1];
                    str2.Assign(sz);
                    str2.TrimAll();
                }
            }
#endif // CTCL_LAUNCHER
            }
        if (nCTCLType != _ECTCL_Console) {
            if (!DoListen(GetCTCLTypePort(nCTCLType), CreateSOCClient, nCTCLType, 0)) {
                Exit();
                g_nCTCLType = _ECTCL_None;
                return FALSE;
            }
        }

        return TRUE;
    }

    return FALSE;
}

void CCTCLManager::Stop()
{
    ASSERT(g_nCTCLType != _ECTCL_None);
    Exit();
    if (g_nCTCLType == _ECTCL_Console) {
        m_aTIs.Reset();
    }
    g_nCTCLType = _ECTCL_None;
}

void CCTCLManager::Run()
{
    static BOOL s_bRunning = FALSE;
    int i, nSize;

    if (!s_bRunning) {
        if (CTCL_IsConsoleX()) {
            CTCL_SetApplState(_EAS_Launched);
            CTCL_SetGameState(_EGS_Idle);
        }
        s_bRunning = TRUE;
    }
    if (CTCL_IsConsoleOrCOOP()) {
        static DWORD s_dwLast = 0;
        if ((s_dwLast == 0) || ((s_dwLast + 500) <= GetTickCount())) {
            nSize = m_aTIs.GetSize();
            for(i = 0; i < nSize; i++) {
                m_aTIs.GetAt(i).m_2Launcher.CheckConnection();
            }
            s_dwLast = GetTickCount();
        }
    }
    if (g_nCTCLType == _ECTCL_Console) {
#if !defined(CTCL_LAUNCHER)
        bool bPrinted = false, bMissionReplay = false;
        CSendFile* pSF = m_SFM_Recv.GetHead();
        while(pSF && !(bPrinted && bMissionReplay)) {
            CSendFile* pSFNext = pSF->m_pNext;

            DWORD dw = GetTickCount();
            if ((pSF->m_dwLast == 0) || ((pSF->m_dwLast + 500) < dw)) {
                pSF->m_dwLast = dw;

                MW4PRINT_COPYDATASTRUCT cd;

                if (pSF->m_nType == 0) {
                    if (!bPrinted) {
                        HWND hWndPrint = FindWindow(NULL, "mw4print");
                        if (hWndPrint) {
                            GetFileName4GUID(cd.m_szFile, pSF->m_strHost, g_pcszGameDataSavePath, g_pcszGameDataExtPR, pSF->m_guid);
                            cd.dwData = MW4PRINT_COPYDATA_META;
                            cd.cbData = sizeof(cd) - sizeof(COPYDATASTRUCT);
                            cd.lpData = &cd.m_GUID;
                            cd.m_GUID = GUID_NULL;
                            if (::SendMessage(hWndPrint, WM_COPYDATA, (WPARAM)hWindow, (LPARAM)&cd) == 1) {
                                pSF->m_bProcessed = true;
                                m_SFM_Recv.Cut(pSF);
                                m_SFM_Done.AddTail(pSF);
                            }
                        }
                        bPrinted = true;
                    }
                } else if (pSF->m_nType == 1) {
                    if (!bMissionReplay) {
                        DWORD dwTickDelay = GetTickCount() - pSF->m_dwTickCreated;
                        if (pSF->m_dwTickDelay <= dwTickDelay) {
                            GetFileName4GUID(cd.m_szFile, pSF->m_strHost, g_pcszGameDataSavePath, g_pcszGameDataExtMR, pSF->m_guid);
                            CTeslaInfo& TI = m_aTIs.GetMissionReview();
                            if (&TI) {
                                ASSERT(TI.m_bCameraShip && TI.m_2Launcher.m_bMissionReview);
                                CSOC_Server& SOC = TI.GetGameSOC();
                                if (SOC.GetConnectionState() == +1) {
                                    SOC.DoSendX("BBxxnsD", S_SendFile, 2, &pSF->m_SysTime, sizeof(pSF->m_SysTime), &pSF->m_guid, sizeof(pSF->m_guid), pSF->m_nType, cd.m_szFile, 0);
                                }
                            }
                        }
                        bMissionReplay = true;
                    }
                } else {
                    if (pSF->m_nType == 2) {
                        extern void _stdcall CTCL_SaveLogGameEnd(const SYSTEMTIME& SysTime, const GUID& guid, const char* pcszHost);
                        CTCL_SaveLogGameEnd(pSF->m_SysTime, pSF->m_guid, pSF->m_strHost.String());
                    } else {
                        // error...
                    }
                    pSF->m_bProcessed = true;
                    m_SFM_Recv.Cut(pSF);
                    m_SFM_Done.AddTail(pSF);
                }
            }
            pSF = pSFNext;
        }
#endif // !defined(CTCL_LAUNCHER)
    } else {
		/*
		Code executed for both client and server.

		*Possible states:
			Before server/client role is determined: waiting for commands
				(Commands such as exit/start-server/start-client may arrive.)

			Running as server, waiting for server mech configuration data
				(State after CreateSession has been called.)
			Running as server, waiting for Bot information
				(Client joining happens automatically between client and server.)
			Running as server, waiting for Launch

			==>Client has no waiting state as data is transferred immediately on startup.

		==>Define each state and implement the appropriate handling for each.
		switch(state){
		}
		*/
#if !defined(CTCL_LAUNCHER)
        CSendFile* pSF;
        pSF = m_SFM_Send.GetHead();
        while(pSF) {
            pSF->Check();
            pSF = pSF->m_pNext;
        }
        if (g_nMech4Comm == 0) {
            pSF = m_SFM_Recv.GetHead();
            if (pSF) {
                extern bool _stdcall CTCL_DoMissionReplay(const char* pcszFile);
                if (CTCL_DoMissionReplay(pSF->m_strHost)) {
                    m_SFM_Recv.Cut(pSF);
                    m_SFM_Done.AddTail(pSF);
                }
            }
        }
#endif // !defined(CTCL_LAUNCHER)
    }
#if !defined(CTCLS_EXPORTS)
    // Mech4 only...
    if (g_nMech4Comm) {
        DoMech4Comm();
    }
#endif // !defined(CTCLS_EXPORTS)
    SingleStep();
}
#if !defined(CTCLS_EXPORTS)
void CCTCLManager::DoMech4Comm()
{
	//$$COOP
    ASSERT(g_nMech4Comm);
    if (CTCL_IsConsole()) {
        int i, nMech4CommOld = g_nMech4Comm;
        SPlayerInfo& SVR_PI = g_aPlayerInfos[g_nServer];
        const CTeslaInfo& SVR = m_aTIs.GetAt(SVR_PI.m_nLevelOrTesla);
        CSOC_Server& SOCGame = SVR.GetGameSOC();

        for(i = 0; g_nMech4Comm && (i < g_nPlayerInfos); i++) {
            SPlayerInfo& pi = g_aPlayerInfos[i];
            if (!pi.m_bBot) {
                CTeslaInfo& ti = m_aTIs.GetAt(pi.m_nLevelOrTesla);
                CSOC_Server& soc = ti.GetGameSOC();
                if (soc.GetConnectionState() == 1) {
                    if (g_nMech4Comm == 1) {
                        soc.m_nGameReturn = _EGR_NoError;
                    } else {
                        switch(soc.m_nGameReturn) {
                        case _EGR_Connected: //newly connected
                        case _EGR_ErrCreateSession:
                        case _EGR_ErrJoinSession:
                            g_nMech4Comm = 0;
                            break;
                        }
                    }
                } else {
                    g_nMech4Comm = 0;
                }
            }
        }
        switch(g_nMech4Comm)
        {
        case 0: // all error goes to here!!!
            if (nMech4CommOld != 1) {
                for(i = 0; i < g_nPlayerInfos; i++) {
                    SPlayerInfo& pi = g_aPlayerInfos[i];
                    if (!pi.m_bBot) {
                        CTeslaInfo& ti = m_aTIs.GetAt(pi.m_nLevelOrTesla);
                        CSOC_Server& soc = ti.GetGameSOC();
                        if (soc.GetConnectionState() == 1) {
                            // error to main screen...
                            soc.DoSendCmdWOP(C_ErrorStartGame);
                        }
                    }
                }
            }
            break;
        case 1:
		    //Send command to prepare for execution as server/client.
		    //Also transmit all parameters required to run the server.
            for(i = 0; i < g_nPlayerInfos; i++) {
                SPlayerInfo& pi = g_aPlayerInfos[i];
                if (!pi.m_bBot) {
                    CTeslaInfo& ti = m_aTIs.GetAt(pi.m_nLevelOrTesla);
                    CSOC_Server& soc = ti.GetGameSOC();
                    if (&soc) {
                        soc.DoSendX("BBnnxBsDWwsnnDBBxx", C_ReadyStartGame, (i == g_nServer) ? 1: 0, g_nTeslas, g_nPlayerInfos - g_nTeslas - 1, &m_aNMP.GetAt(0), m_aNMP.GetSize(),
                            pi.m_bTeam, pi.m_szName, pi.m_nMechIndex, pi.m_fileID, pi.m_recordID, pi.m_szMech, pi.m_nTeamOrSkin, pi.m_nDecal, SVR.m_ulAddr,
                            !!g_nPrintOut, !!g_nMissionReview, &g_guidGameDatas, sizeof(g_guidGameDatas), &g_SysTime, sizeof(g_SysTime));
                    }
                }
            }
            g_nMech4Comm = 2;
            break;
        case 2:
            for(i = 0; i < g_nPlayerInfos; i++) {
                SPlayerInfo& pi = g_aPlayerInfos[i];
                if (!pi.m_bBot) {
                    CTeslaInfo& ti = m_aTIs.GetAt(pi.m_nLevelOrTesla);
                    CSOC_Server& soc = ti.GetGameSOC();
                    if (soc.m_nGameReturn != _EGR_PreparingStarted) {
                        break;
                    }
                }
            }
            if (i == g_nPlayerInfos) {
		        // Bundle and send all Bot information together.
                CPacket pak(&SVR.GetGameSOC());

                pak.Assemble("B", C_BOTS);
                for(i = 0; i < g_nPlayerInfos; i++) {
                    if (i != g_nServer) { // Originally bots only; now everyone except the server...
                        SPlayerInfo& pi = g_aPlayerInfos[i];
                        pak.Assemble("BnsDWwsnn", pi.m_bBot, pi.m_nLevelOrTesla, pi.m_szName, pi.m_nMechIndex, pi.m_fileID, pi.m_recordID, pi.m_szMech, pi.m_nTeamOrSkin, pi.m_nDecal);
                    }
                }

                SOCGame.DoSend(pak);
                g_nMech4Comm = 4;
            }
            break;
        case 4:
		    // Once the game is successfully created, set Mech info for server and all clients...
            if (SOCGame.m_nGameReturn == _EGR_OkCreateSession) {
                for(i = 0; i < g_nPlayerInfos; i++) {
                    if (i != g_nServer) { // Server has already entered SetMech...
                        SPlayerInfo& pi = g_aPlayerInfos[i];
                        if (!pi.m_bBot) {
                            CTeslaInfo& ti = m_aTIs.GetAt(pi.m_nLevelOrTesla);
                            CSOC_Server& soc = ti.GetGameSOC();
                            soc.DoSendCmdWOP(C_SetMechs);
                        }
                    }
                }
                g_nMech4Comm = 6;
            }
            break;
        case 6:
		    //Wait for all clients to join the server. <==This response comes from the server.
		    //Once all clients have joined, instruct the server to Launch the game.
            if (SOCGame.m_nGameReturn == _EGR_OkLaunchReady) {
                g_nMech4Comm = 9;
            }
            break;
        case 9:
            // wait...
            break;
        case 10:
            SOCGame.DoSendX("B", C_DoLaunch);
            g_nMech4Comm = 0;
            break;
        }
    } else if (g_bIsServer) { // server
        switch(g_nMech4Comm)
        {
		case -1:
			g_nMech4Comm = +1;
			break;
		case -3:
			g_nMech4Comm = -2;
			break;
		case -2:
			CTCL_StartGame(TRUE);
			break;
        case 1:
            CTCL_DoCreateGame();
            break;
        case 2: // wait SendRequestConnectionMessage
            CTCL_CheckNetConnectedToServer();
            break;
		case 3:
			CTCL_SetMissionParams();
			break;
        case 5:
			CTCL_SetMechs();
			break;
        case 7:
            CTCL_CheckClientReady();
            break;
        case 8:
            CTCL_CheckServerReady();
            break;
        case 9:
            // wait... load
            break;
        case 10:
            // wait... C_DoLaunch
            break;
        case 11:
            // wait... launch
            break;
        }
    } else { // clients
        switch(g_nMech4Comm)
        {
        case 1:
            CTCL_CheckJoinGame();
            break;
        case 2: // wait SendRequestConnectionMessage
            CTCL_CheckNetConnectedToServer();
            break;
		case 4:
			CTCL_CheckMissionParams();
			break;
        case 5:
			CTCL_SetMechs();
			break;
        case 7:
            CTCL_CheckClientReady();
            break;
        }
    }
}

void CCTCLManager::SaveNMP(const void* p, int nSize)
{
    m_aNMP.SetSize(nSize);
    BYTE* p2 = &m_aNMP.GetAt(0);
    if (nSize > 0) {
        memcpy(p2, p, nSize);
    }
    m_aNMP.FreeExtra();
}

void* CCTCLManager::LoadNMP(int& nSize)
{
    //if (g_bIsServer) {
        nSize = m_aNMP.GetSize();
        if (nSize > 0) {
            return &m_aNMP.GetAt(0);
        }
    //}

    return NULL;
}
#if !defined(CTCL_LAUNCHER)
void CCTCLManager::SendFile(const SYSTEMTIME& SysTime, const GUID& guid, int nType)
{
    CSendFile* pSF = new CSendFile(SysTime, guid, nType);
    m_SFM_Send.AddTail(pSF);
    pSF->Check();
}

bool CCTCLManager::DoReprintOrCheck(bool bRealPrint)
{
    if (this) {
        CSendFile* pSFFound = NULL;
        CSendFile* pSF = m_SFM_Done.GetHead();
        while(pSF) {
            if (pSF->m_nType == 0) {
                pSFFound = pSF;
                if (!bRealPrint)
                    break;
            }
            pSF = pSF->m_pNext;
        }
        if (pSFFound) {
            if (bRealPrint) {
                pSF = pSFFound;

                HWND hWndPrint = FindWindow(NULL, "mw4print");
                if (hWndPrint) {
                    MW4PRINT_COPYDATASTRUCT cd;

                    GetFileName4GUID(cd.m_szFile, pSF->m_strHost, g_pcszGameDataSavePath, g_pcszGameDataExtPR, pSF->m_guid);
                    cd.dwData = MW4PRINT_COPYDATA_META;
                    cd.cbData = sizeof(cd) - sizeof(COPYDATASTRUCT);
                    cd.lpData = &cd.m_GUID;
                    cd.m_GUID = GUID_NULL;
                    if (::SendMessage(hWndPrint, WM_COPYDATA, (WPARAM)hWindow, (LPARAM)&cd) == 1) {
                        return true;
                    }
                }
                return false;
            } else {
                return true;
            }
        }
    }

    return false;
}

bool CCTCLManager::IsPendingMissionReplayRequest() const
{
    CSendFile* pSF = m_SFM_Recv.GetHead();
    while(pSF) {
        if (pSF->m_nType == 1)
            return true;
        pSF = pSF->m_pNext;
    }

    return false;
}
#endif // !defined(CTCL_LAUNCHER)
#endif // !defined(CTCLS_EXPORTS)
int CTCL_API CTCL_GetType()
{
    return g_nCTCLType;
}

BOOL CTCL_API CTCL_IsNone()
{
    return g_nCTCLType == _ECTCL_None;
}

BOOL CTCL_API CTCL_IsConsole()
{
    return g_nCTCLType == _ECTCL_Console;
}

bool CTCL_API CTCL_IsCOOP()
{
#if defined(CTCL_LAUNCHER)
    return false;
#else // !defined(CTCL_LAUNCHER)
    return g_bCOOP;
#endif // defined(CTCL_LAUNCHER)
}

BOOL CTCL_API CTCL_IsConsoleOrCOOP()
{
#if defined(CTCL_LAUNCHER)
    return g_nCTCLType == _ECTCL_Console;
#else // !defined(CTCL_LAUNCHER)
    return (g_nCTCLType == _ECTCL_Console) || g_bCOOP;
#endif // defined(CTCL_LAUNCHER)
}

BOOL CTCL_API CTCL_IsConsoleX()
{
    return (g_nCTCLType == _ECTCL_Game) || (g_nCTCLType == _ECTCL_CameraShip);
}

int CTCL_API CTCL_GetTeslaCountAll()
{
    ASSERT(CTCL_IsConsoleOrCOOP());
    if (g_pCTCLManager)
    return g_pCTCLManager->m_aTIs.GetSize();
    return 0;
}

int CTCL_API CTCL_GetTeslaCount()
{
    ASSERT(CTCL_IsConsoleOrCOOP());
    if (g_pCTCLManager)
    return g_pCTCLManager->m_aTIs.GetSize() - g_pCTCLManager->m_aTIs.m_nCameraShips;
    return 0;
}
#if !defined(CTCL_LAUNCHER)
TESLA_HANDLE CTCL_API CTCL_GetTeslaInfo(int nIndex, STeslaInfo& TI)
{
    ASSERT(CTCL_IsConsole());
    if ((0 <= nIndex) && (nIndex < g_pCTCLManager->m_aTIs.GetSize())) {
        CTeslaInfo& ti = g_pCTCLManager->m_aTIs.GetAt(nIndex);
        TI.m_bCameraShip = ti.m_bCameraShip;
        TI.m_ulAddr = ti.m_ulAddr;
        TI.m_pcsz = ti;
        TI.m_nLauncherConnection = ti.m_2Launcher.GetConnectionState();
        TI.m_nConnection2 = ti.GetGameSOC().GetConnectionState();

        TI.m_nApplType = ti.m_nApplType;
        TI.m_nApplState = ti.m_nApplState;
        TI.m_nGameState = ti.m_nGameState;
        TI.m_nGameTime = ti.m_nGameTime;
        TI.m_nGameIsServer = ti.m_nGameIsServer;
        if (ti.m_2Launcher.m_bMissionReview) {
            if (g_pCTCLManager->IsPendingMissionReplayRequest()) {
                if ((ti.m_nApplType != _EAT_None) && (ti.m_nApplState == _EAS_Launched)) {
                    if (TI.m_nGameState == _EGS_Idle) {
                        TI.m_nGameState = _EGS_Preparing;
                    } else {
                    }
                } else {
                }
            }
        }

        return (TESLA_HANDLE)&ti;
    }

    return (TESLA_HANDLE)NULL;
}

int CTCL_API CTCL_InviteCOOP(int nTimeInvite, int& nYes, int& nNo, int& nProcessings)
{
    ASSERT(CTCL_IsConsoleOrCOOP());

    int i, nTesla = CTCL_GetTeslaCountAll();

    int nTotal = 0;
    nYes = nNo = nProcessings = 0;

    for(i = 0; i < nTesla; i++)
    {
        CTeslaInfo& ti = g_pCTCLManager->m_aTIs.GetAt(i);
        CSOC_Server& SOC = ti.GetGameSOC();
        CInviteCOOP& COOP = ti.m_COOP;
        if (SOC.GetConnectionState() == +1) {
            if (nTimeInvite == -999) {
                if ((COOP.m_nTimeInvite != -999) || (COOP.m_nResult != -1)) {
                    if (COOP.m_nTimeInvite != -999) {
                        COOP.Clear();
                        COOP.m_nTimeInvite = -999;
                    }
                    SOC.DoSendX("Bnxn", C_InviteCOOP, nTimeInvite, &COOP.m_guidGameData, sizeof(GUID), 0);
                }
                nTotal++;
            } else if (nTimeInvite) {
                bool bInvite = false;

                if (nTimeInvite < 0) {
                    // invite cameraship : -1 1st-camera, -2 2nd camera...
                    if (ti.m_bCameraShip) {
                        if (nTimeInvite == -(i - CTCL_GetTeslaCount()+ 1)) {
                            bInvite = true;
                        }
                    }
                } else {
                    // invite players
                    if (!ti.m_bCameraShip)
                        bInvite = true;
                }
                if (bInvite) {
                    if ((COOP.m_nTimeInvite == nTimeInvite) && (memcmp(&COOP.m_guidGameData, &g_guidGameDatas, sizeof(GUID)) == 0)) {
                        if ((COOP.m_nResult == 0) || (COOP.m_nResult == 1)) {
                            SOC.DoSendX("Bnxn", C_InviteCOOP, nTimeInvite, &COOP.m_guidGameData, sizeof(GUID), 0);
                            if (COOP.m_nResult == 1)
                                nProcessings++;
                        } else {
                            ASSERT((COOP.m_nResult == 2) || (COOP.m_nResult == 3));
                            if (COOP.m_nResult == 2)
                                nNo++;
                            else
                                nYes++;
                        }
                    } else {
                        COOP.Clear();
                        COOP.m_nTimeInvite = nTimeInvite;
                        //COOP.m_nResult = 0;
                        //COOP.m_nResultAccepted = 0;
                        memcpy(&COOP.m_guidGameData, &g_guidGameDatas, sizeof(GUID));
                    }
                    nTotal++;
                }
            } else {
                COOP.Clear();
                nTotal++;
            }
        } else {
            COOP.Clear();
        }
    }

    return nTotal; // nTesla;
}
#endif // !defined(CTCL_LAUNCHER)
const char* CTCL_API CTCL_GetTeslaName(int nIndex)
{
    ASSERT(CTCL_IsConsole());
    return g_pCTCLManager->m_aTIs.GetAt(nIndex).String();
}

int CTCL_API CTCL_OrderAppl(int nIndex, int nOrder, int nParam)
{
    int nRet = 0;

    if ((0 <= nIndex) && (nIndex < g_pCTCLManager->m_aTIs.GetSize())) {
        CTeslaInfo& ti = g_pCTCLManager->m_aTIs.GetAt(nIndex);
        if (100 <= nOrder) {
            if (ti.m_2Launcher.GetConnectionState() == +1) {
                ti.m_2Launcher.DoSendX("BBDB", C_OrderAppl, nOrder, nParam, ti.m_bCameraShip);
                nRet = 1;
            }
        } else {
            CSOC_Server& SOC = ti.GetGameSOC();
            if (SOC.GetConnectionState() == +1) {
                SOC.DoSendX("BBDB", C_OrderAppl, nOrder, nParam, ti.m_bCameraShip);
                nRet = 1;
            }
        }
    }

    return nRet;
}
#if !defined(CTCLS_EXPORTS)
void CTCL_API CTCL_DoSendGameReturn(int nValue)
{
    CSOC& SOC = *g_pCTCLManager->FindSOC(0);

    if (SOC.GetConnectionState() == 1)
        SOC.DoSendX("Bn", S_GameReturn, nValue);
}

void CTCL_API CTCL_SaveNMP(const void* p, int nSize)
{
    g_pCTCLManager->SaveNMP(p, nSize);
}

void* CTCL_API CTCL_LoadNMP(int& nSize)
{
    return g_pCTCLManager->LoadNMP(nSize);
}
#if !defined(CTCL_LAUNCHER)
void CTCL_API CTCL_SendFile(const SYSTEMTIME& SysTime, const GUID& guid, int nType)
{
    g_pCTCLManager->SendFile(SysTime, guid, nType);
}

bool CTCL_API CTCL_DoReprintOrCheck(bool bRealPrint)
{
    return g_pCTCLManager->DoReprintOrCheck(bRealPrint);
}

void CTCL_API CTCL_SetGameStateAsIdle()
{
    CSendFile* pSF;

    pSF = g_pCTCLManager->m_SFM_Recv.GetHead();
    while(pSF) {
        if (pSF->m_nType == 1) {
            CTCL_SetGameState(_EGS_Preparing);
            return;
        }
        pSF = pSF->m_pNext;
    }

    CTCL_SetGameState(_EGS_Idle);
}
#endif // !defined(CTCL_LAUNCHER)
#endif // !defined(CTCLS_EXPORTS)
