// ---------------------------------------------------------------------------
// Name:        osTCPSocketServer::bind
//
// Description: Associates a local port with this socket.
//              I.E: Any socket client connection to this port address will be mapped
//                   to this socket server.
//
// Arguments:   portAddress - The address of the port to which the socket will be bound.
//
// Return Val:  bool - Success / failure.
// Author:      AMD Developer Tools Team
// Date:        3/1/2004
// ---------------------------------------------------------------------------
bool osTCPSocketServer::bind(const osPortAddress& portAddress)
{
    bool retVal = false;

    // Verify that the socket is open:
    if (isOpen())
    {
        // Translate portAddress into a win32 local port address:
        SOCKADDR_IN win32PortAddress;
        bool rc = portAddress.asSockaddr(win32PortAddress, _blockOnDNS);

        if (rc)
        {
            // Bind this socket to the input local port address:
            osSocketDescriptor socketDescriptor = OSSocketDescriptor();
            int rc1 = ::bind(socketDescriptor, (LPSOCKADDR)&win32PortAddress,
                             sizeof(SOCKADDR_IN));

            if (rc1 != SOCKET_ERROR)
            {
                retVal = true;
                _boundAddress = portAddress;
            }
            else
            {
                // An error occurred:
                gtString errMsg = OS_STR_bindError;
                errMsg += OS_STR_osReportedErrorIs;
                errMsg += OS_STR_host;
                errMsg += portAddress.hostName();
                errMsg += OS_STR_port;
                unsigned short portNum = portAddress.portNumber();
                errMsg.appendFormattedString(L"%u ", portNum);
                gtString systemError;
                osGetLastSystemErrorAsString(systemError);
                errMsg += systemError;
                GT_ASSERT_EX(false, errMsg.asCharArray());
            }
        }
    }

    return retVal;
}
//-----------------------------------------------------------------------------
bool Send(Response& rResponse, const char* mime, const char* pData, unsigned long dwSize)
{
    char sendbuffer[COMM_BUFFER_SIZE];
    sendbuffer[0] = 0;

    // send the http header and the file contents to the browser
    if (rResponse.m_bNeedToSendHeader == true)
    {
        GenerateHeader(rResponse, sendbuffer, COMM_BUFFER_SIZE);
        rResponse.m_bNeedToSendHeader = false;
    }

    if (rResponse.m_bStreamingEnabled == true)
    {
        strncat_s(sendbuffer, COMM_BUFFER_SIZE, "--BoundaryString\r\n", COMM_BUFFER_SIZE);
    }

    DWORD len = (DWORD)strlen(sendbuffer);
    sprintf_s(sendbuffer + len, COMM_BUFFER_SIZE - len, "Content-Type: %s\r\n"
              "Content-Length: %ld\r\n"
              "\r\n",
              mime,
              dwSize);

    // send header
    bool res = rResponse.client_socket->Send(sendbuffer, (DWORD)strlen(sendbuffer));

    if (res == true)
    {
        // if header could be sent
        // send data
        res = rResponse.client_socket->Send(pData, dwSize);
    }
    else
    {
        osSystemErrorCode systemLastError = osGetLastSystemError();

        // If no system error was recorded:
        if (systemLastError != 0)
        {
            gtString systemErrorString;
            osGetLastSystemErrorAsString(systemErrorString);

            Log(logERROR, "Failed to send %s response data due to error %d: %s\n", mime, systemLastError, systemErrorString.asASCIICharArray());
        }

        // there was an error sending
        // so close the connection and return false
        CloseConnection(rResponse);
        return false;
    }

    // if the response is not streaming,
    // the connection should be closed
    // otherwise it should be left open
    if (rResponse.m_bStreamingEnabled == false)
    {
        CloseConnection(rResponse);
    }

    return true;
}
//--------------------------------------------------------------
//  LaunchAppInNewProcess
//--------------------------------------------------------------
PROCESS_INFORMATION ProcessTracker::LaunchAppInNewProcess(gtASCIIString strApp, gtASCIIString strDir, gtASCIIString strArgs, osModuleArchitecture binaryType)
{
#ifdef _LINUX
    PS_UNREFERENCED_PARAMETER(binaryType);
#endif
    LogConsole(logMESSAGE, "About to launch: %s\n", strApp.asCharArray());
    LogConsole(logMESSAGE, "Params: %s\n", strArgs.asCharArray());
    LogConsole(logMESSAGE, "Working Directory: %s\n", strDir.asCharArray());

    // Get app directory and make it default
    if (strDir.isEmpty())
    {
        size_t pos = strApp.find_last_of("\\");

        if (pos != std::string::npos)
        {
            strDir = strApp.substr(0, (int)pos);

            if (strApp[0] == '\"')
            {
                strDir += "\"";
            }
        }
    }

    PROCESS_INFORMATION pi;
    ZeroMemory(&pi, sizeof(pi));

#ifdef _WIN32
    DWORD dwFlags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED;

    SetLastError(0);

    STARTUPINFO si;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
#endif

    // Cmd line has to include the exe name since many apps expect the executable name to be in argv[0]!
    // Note argv[0] on the command line needs to be surrounded with quotes if it contains spaces.
    // The arguments in strArgs have already been "quoted" as they are parsed.

    gtASCIIString strCmdLine = AddQuotesIfStringHasSpaces(strApp.asCharArray());
    strCmdLine += " ";
    strCmdLine += strArgs;

    LogConsole(logMESSAGE, "strApp: %s\n", strApp.asCharArray());
    LogConsole(logMESSAGE, "strCmdLine: %s\n", strCmdLine.asCharArray());

    // Attempt to initialize the environment that the new process will run in. The child process should inherit "this" environment.
    if (!PrelaunchEnvironmentInitialization())
    {
        // Log a warning if this failed- initializing the environment for the new process can fail if Mantle support isn't installed.
        // In these cases, if the user is attempting to debug a Mantle application, they will have bigger problems to deal with.
        // In cases where a DX/GL app is being debugged, this warning can be ignored without any side effects.
        Log(logWARNING, "Environment initialization failed. If using DX/GL, it is safe to ignore this warning.\n");
    }

    BOOL succeeded = FALSE;

#ifdef _WIN32

    char microDLLPath[PS_MAX_PATH];
    const char* strServerPath;
    strServerPath = SG_GET_PATH(ServerPath);

    if (SG_GET_BOOL(OptionDllReplacement) == true)
    {
        DllReplacement::SetDllDirectory(binaryType == OS_X86_64_ARCHITECTURE);
    }

    // if using manual dll replacement or the AppInit_DLLs registry setting, don't use any kind of dll injection
    if (SG_GET_BOOL(OptionManualDllReplacement) == true || SG_GET_BOOL(OptionAppInitDll))
    {
        succeeded = CreateProcess(strApp.asCharArray(), (LPSTR)strCmdLine.asCharArray(), NULL, NULL, TRUE, dwFlags, NULL, strDir.asCharArray(), &si, &pi);
    }
    else
    {
#ifdef X64

        // can only launch 64 bit applications
        if (binaryType != OS_X86_64_ARCHITECTURE)
        {
            sprintf_s(microDLLPath, PS_MAX_PATH, "%s" MICRODLLNAME "%s%s.dll", SG_GET_PATH(ServerPath), GDT_DEBUG_SUFFIX, GDT_BUILD_SUFFIX);
            succeeded = AMDT::CreateProcessAndInjectDll(strApp.asCharArray(), (LPSTR)strCmdLine.asCharArray(),
                                                        NULL, NULL, TRUE, dwFlags, NULL,
                                                        strDir.asCharArray(),
                                                        &si, &pi,
                                                        microDLLPath);
        }

#else

        if (binaryType != OS_I386_ARCHITECTURE)
        {
            sprintf_s(microDLLPath, PS_MAX_PATH, "%s" MICRODLLNAME "-x64%s%s.dll", SG_GET_PATH(ServerPath), GDT_DEBUG_SUFFIX, GDT_BUILD_SUFFIX);
            succeeded = AMDT::CreateProcessAndInjectDll(strApp.asCharArray(), (LPSTR)strCmdLine.asCharArray(),
                                                        NULL, NULL, TRUE, dwFlags, NULL,
                                                        strDir.asCharArray(),
                                                        &si, &pi,
                                                        microDLLPath);
        }

#endif // X64
        else
        {
            succeeded = AMDT::CreateProcessAndInjectDll(strApp.asCharArray(), (LPSTR)strCmdLine.asCharArray(),
                                                        NULL, NULL, TRUE, dwFlags, NULL,
                                                        strDir.asCharArray(),
                                                        &si, &pi,
                                                        SG_GET_PATH(MicroDLLPath));
        }
    }

#else

    // Create the app process
    succeeded = CreateProcess(strApp.asCharArray(), strCmdLine.asCharArray(), strDir.asCharArray(), &pi);
#endif // _WIN32

    if (!succeeded)
    {
        osSystemErrorCode systemLastError = osGetLastSystemError();

        gtString systemErrorString;
        osGetLastSystemErrorAsString(systemErrorString);

        Log(logERROR, "CreateProcessAndInjectDll failed; Error %d: %s\n", systemLastError, systemErrorString.asASCIICharArray());
        pi.dwProcessId = 0;
    }

#ifdef _WIN32
    else
    {
        // Check to see if the Steam.exe has been hooked and if so, set the value in shared memory
        // If Steam.exe was used to launch the target application, then the checks for cmd.exe and fcx.exe
        // need to be ignored.
        if (strApp.length() > 0)
        {
            if (strstr(strApp.toLowerCase().asCharArray(), "steam.exe") != NULL)
            {
                SG_SET_BOOL(SteamInjected, true);
            }

            ShowLauncherReminder(strApp.toLowerCase().asCharArray());
        }
        else
        {
            if (strstr(strCmdLine.toLowerCase().asCharArray(), "steam.exe") != NULL)
            {
                SG_SET_BOOL(SteamInjected, true);
            }

            ShowLauncherReminder(strCmdLine.toLowerCase().asCharArray());
        }
    }

#endif // _WIN32

    return pi;
}
//--------------------------------------------------------------
/// Writes information about the allowed plugins into shared memory
/// and launches the application specified in s_strInjectedAppName
/// \return true if the app is successfully launched; false otherwise
//--------------------------------------------------------------
bool ProcessTracker::WritePluginsToSharedMemoryAndLaunchApp()
{
    bool bGetType = false;

    if (OSDependentModulesInitialization() == false)
    {
        Log(logERROR, "Failed to initialize for plugins\n");
        return false;
    }

    osModuleArchitecture binaryType;

    bGetType = OSWrappers::GetBinaryType(m_injectedAppName.c_str(), &binaryType);

#ifdef _WIN32

    if (!bGetType)
    {
        // try without quotes, should be W8x preference.  This will update m_injectedAppName for later use also.
        m_injectedAppName.erase(0, 1);
        m_injectedAppName.resize(m_injectedAppName.size() - 1);

        bGetType = OSWrappers::GetBinaryType(m_injectedAppName.c_str(), &binaryType);
    }

#endif // def _WIN32

    if (!bGetType)
    {
        // file is not executable
        LogConsole(logERROR, "%s is not an executable file\n", m_injectedAppName.c_str());
        return false;
    }

    Log(logMESSAGE, "WritePluginsToSharedMemoryAndLaunchApp()\n");

    if (false == CreateSharedMemory())
    {
        return false;
    }

    // now actually start the application
    PROCESS_INFORMATION pi = LaunchAppInNewProcess(m_injectedAppName.c_str(), m_injectedAppDir.c_str(), m_injectedAppArgs.c_str(), binaryType);

    if (pi.dwProcessId == 0)
    {
        LogConsole(logWARNING, "Failed to start application: %s %s\n\n", m_injectedAppName.c_str(), m_injectedAppArgs.c_str());
        return false;
    }

    Log(logMESSAGE, "About to resume application thread\n");
#ifdef _WIN32

    if (osResumeSuspendedProcess(0, 0, pi.hThread, false) == false)
    {
        osSystemErrorCode systemLastError = osGetLastSystemError();

        gtString systemErrorString;
        osGetLastSystemErrorAsString(systemErrorString);

        Log(logERROR, "Resuming thread failed; Error %d: %s\n", systemLastError, systemErrorString.asASCIICharArray());
    }

#endif

    m_injectedAppID = pi.dwProcessId;

    return true;
}