Browse Source

initial commit

Roberto Parolin 6 years ago
commit
4396316273

+ 83 - 0
doc/UTFDoc.css

@@ -0,0 +1,83 @@
+body 
+{
+    font-family: Palatino Linotype, Book Antiqua, Times New Roman;
+    font-size: 11pt;
+}
+
+h1
+{
+    font-family: Verdana;
+    display: block;
+    background-color: #FFF0B0;
+    border: solid 2px black;
+    font-size: 16pt;
+    font-weight: bold;
+    padding: 6px;
+}
+
+h2 
+{
+    font-size: 14pt;
+    font-family: Verdana;
+    border-bottom: 2px solid black;
+}
+
+h3
+{
+    font-family: Verdana;
+    font-size: 13pt;
+    font-weight: bold;
+}
+
+.code-example 
+{
+    display: block;
+    background-color: #e0e0f0;
+    margin-left: 3em;
+    margin-right: 3em;
+    margin-top: 1em;
+    margin-bottom: 1em;
+    padding: 8px;
+    border: solid 2px #a0a0d0;
+    font-family: monospace;
+    font-size: 10pt;
+    white-space: pre;
+}
+
+.code-example-span 
+{
+    font-family: monospace;
+    font-size: 10pt;
+    white-space: pre;
+}
+
+.code-example-comment
+{
+    background-color: #e0e0f0; 
+    padding: 0px 0px; 
+    font-family: monospace; 
+    font-size: 10pt; 
+    white-space: pre; 
+    color: #999999; 
+    margin: auto auto; 
+}
+
+
+.faq-question
+{
+    background-color: #D0E0D0;
+    font-size: 12pt;
+    font-weight: bold;
+    margin-bottom: 0.5em;
+    margin-top: 0em;
+    padding-left:8px;
+    padding-right:8px;
+    padding-top:2px;
+    padding-bottom:2px
+}
+
+.faq-answer
+{
+    display: block;
+    margin: 4pt 1em 0.5em 1em;
+}

+ 294 - 0
include/EAMain/EAEntryPointMain.inl

@@ -0,0 +1,294 @@
+///////////////////////////////////////////////////////////////////////////////
+// EAEntryPointMain.inl
+//
+// Copyright (c) Electronic Arts. All Rights Reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EAMAIN_EAENTRYPOINTMAIN_INL
+#define EAMAIN_EAENTRYPOINTMAIN_INL
+
+#include <EAMain/internal/Version.h>
+#include <EAMain/EAMain.h>
+#include <EAMain/EAMainExit.h>
+
+#include <EAMain/internal/EAMainStartupShutdown.h>
+
+#if defined(EA_PLATFORM_WINDOWS)
+    #include <EABase/eabase.h>
+
+    EA_DISABLE_ALL_VC_WARNINGS()
+    #include <EAStdC/EAString.h>
+    #include <malloc.h>
+    #define NOMINMAX
+    #include <Windows.h>
+    #undef NOMINMAX
+    EA_RESTORE_ALL_VC_WARNINGS()
+#endif
+
+
+/// EAEntryPointMain
+///
+/// Provides a platform-independent main entry function. The user must implement
+/// this function himself. Different platforms have different variations of
+/// main and some platforms have multiple variations of main within the platform.
+/// EAEnrtyPointMain is a universal entrypoint which you can use to implement a platform-
+/// and string encoding- independent main entrypoint.
+///
+/// You use this function by #including "EATestApp/EAMain.h" and implementing the
+/// function. You don't need to use this function and this code is neither seen
+/// nor compiled if you don't use it. It is merely a convenience to deal with
+/// platform indepdence in a proper way and also a reference for how to do such
+/// a thing manually.
+///
+/// Microsoft-specific:
+/// By default an 8 bit C Standard main is implemented unless EAMAIN_MAIN_16 is defined.
+/// If EAMAIN_MAIN_16 is defined under a supporting Microsoft platform then either
+/// wWinMain or wmain is used. You need to use EAMAIN_MAIN_16 under Microsoft platforms if
+/// you want arguments to be in Unicode, as Windows 8 bit arguments are not UTF8 or any
+/// kind of Unicode.
+///
+/// Example usage:
+///    #include <EAMain/EAEntryPointMain.inl>
+///
+///    int EAMain(int argc, char** argv){
+///        printf("hello world");
+///    }
+///
+extern "C" int EAMain(int argc, char** argv);
+
+namespace EA
+{
+    namespace EAMain
+    {
+        namespace Internal
+        {
+            EAMAIN_API extern EAMainFunction gEAMainFunction;
+
+            EAMAIN_API const char *ExtractPrintServerAddress(int argc, char **argv);
+        }
+    }
+}
+
+
+// Windows compilers #define _CONSOLE when the intended target is a console application instead of
+// a windows application.
+// Note: _CONSOLE doesn't seem to be getting defined automatically (at least for native nant builds).
+//       Temporarily define _CONSOLE ourselves to allow the build to work on windows.
+#ifndef _CONSOLE
+    #define _CONSOLE
+#endif
+
+// Windows RT-based applications need to use Microsoft's Managed C++ extensions with their
+// Windows::ApplicationModel::Core::IFrameworkView class, as opposed to class C main(argc, argv).
+// Additionally, a new kind of main is required with the signature: int main(Platform::Array<Platform::String^>^ args).
+// The code below doesn't call EAEntryPointMain directly, but the WinRTRunner that it calls will call EAEntryPointMain.
+#if (defined(EA_PLATFORM_MICROSOFT) && !defined(CS_UNDEFINED_STRING) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP))
+
+    // Allow the user to define their own WinRT application entry point should
+    // they have a need to create their own IApplicationView.
+    //
+    // In most cases this shouldn't ever be touched. Likely, if you find you're
+    // having to manipulate this then your application is in a state where it
+    // has grown beyond EAMain's scope and you would be better served with a
+    // custom entry point.
+    #ifndef EAMAIN_WINRT_APPLICATION_ENTRY
+        #define EAMAIN_WINRT_APPLICATION_ENTRY() { EA::EAMain::Internal::gEAMainFunction = ::EAMain; EA::EAMain::Internal::StartWinRtApplication(); }
+    #endif
+
+    namespace EA
+    {
+        namespace EAMain
+        {
+            namespace Internal
+            {
+                EAMAIN_API void StartWinRtApplication();
+            }
+        }
+    }
+
+    [Platform::MTAThread]
+    int main(Platform::Array<Platform::String^>^ /*args*/)
+    {
+        #if defined(EAMAIN_USE_INITFINI)
+            EAMainInit();
+        #endif
+
+        EA::EAMain::InitializeSignalHandler();
+
+        EAMAIN_WINRT_APPLICATION_ENTRY();
+
+        #if defined(EAMAIN_USE_INITFINI)
+            EAMainFini();
+        #endif
+        return 0;
+    }
+
+#elif defined(EA_PLATFORM_WINDOWS)
+    #pragma warning(push, 0)
+    #include <Windows.h>
+    #include <shellapi.h>
+    #pragma comment(lib, "shell32.lib")
+    #pragma warning(pop)
+
+    int main(int argc, char** argv)
+    {
+        // To do: Some platforms may require reading the command line from a file.
+        // Do we automatically do this for those platforms? Probably try to open a default file.
+        // Do we support loading from a file from any platform? Probably so.
+    #if defined(EAMAIN_USE_INITFINI)
+        EAMainInit();
+    #endif
+
+        EA::EAMain::Internal::gEAMainFunction = ::EAMain;
+
+        EA::EAMain::CommandLine commandLine(argc, argv);
+
+        const char *printServerAddress = EA::EAMain::Internal::ExtractPrintServerAddress(commandLine.Argc(), commandLine.Argv());
+        EA::EAMain::Internal::EAMainStartup(printServerAddress);
+
+        EA::EAMain::InitializeSignalHandler();
+
+        int returnValue = EA::EAMain::Internal::gEAMainFunction(commandLine.Argc(), commandLine.Argv());
+
+        EA::EAMain::Internal::EAMainShutdown(returnValue);
+
+    #if defined(EAMAIN_USE_INITFINI)
+        EAMainFini();
+    #endif
+        return returnValue;
+    }
+
+    int WINAPI wWinMainShared(HINSTANCE /*instance*/, HINSTANCE /*prevInstance*/, LPWSTR wCmdLine, int /*cmdShow*/)
+    {
+        int       returnValue = 1;
+        int       argc  = 0;
+        wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
+
+        EA::EAMain::Internal::gEAMainFunction = ::EAMain;
+
+        if(wargv)
+        {
+            char8_t** argv = (char8_t**)_malloca(argc * sizeof(char8_t*));
+            __analysis_assume(argv != NULL);
+
+            for(int i = 0; i < argc; i++)
+            {
+                argv[i] = NULL;
+                const int requiredStrlen = EA::StdC::Strlcpy(argv[i], wargv[i], 0);
+                argv[i] = (char8_t *)_malloca(sizeof(char8_t) * (requiredStrlen + 1));
+                EA::StdC::Strlcpy(argv[i], wargv[i], requiredStrlen + 1);
+            }
+
+            #if defined(EAMAIN_USE_INITFINI)
+                EAMainInit();
+            #endif
+
+            const char *printServerAddress = EA::EAMain::Internal::ExtractPrintServerAddress(argc, argv);
+            EA::EAMain::Internal::EAMainStartup(printServerAddress);
+
+            EA::EAMain::InitializeSignalHandler();
+
+            returnValue = EA::EAMain::Internal::gEAMainFunction(argc, argv);
+            EA::EAMain::Internal::EAMainShutdown(returnValue);
+
+            #if defined(EAMAIN_USE_INITFINI)
+                EAMainFini();
+            #endif
+            LocalFree(wargv);
+        }
+
+        return returnValue;
+    }
+
+    #if defined(EAMAIN_MAIN_16)
+        int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR wCmdLine, int cmdShow)
+        {
+            return wWinMainShared(instance, prevInstance, wCmdLine, cmdShow);
+        }
+    #else
+        int WINAPI WinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prevInstance, _In_ LPSTR cmdLine, _In_ int cmdShow)
+        {
+            size_t   wCapacity = strlen(cmdLine) * 3; // UTF8 -> UCS2 requires at most 3 times the bytes.
+            wchar_t* wCmdLine  = (wchar_t*)_malloca(wCapacity);
+            EA::StdC::Strlcpy(wCmdLine, cmdLine, wCapacity);
+
+            return wWinMainShared(instance, prevInstance, wCmdLine, cmdShow);
+        }
+    #endif
+#else
+    #if defined(EA_PLATFORM_WINDOWS) && defined(EA_MAIN_16)
+        int wmain(int argc, wchar_t** wargv)
+        {
+            EA::EAMain::Internal::gEAMainFunction = ::EAMain;
+
+            // Allocate and convert-copy wargv to argv.
+            char8_t** argv = (char8_t**)_malloca(argc * sizeof(char8_t*));
+
+            for(int i = 0; i < argc; i++)
+            {
+                argv[i] = NULL;
+                const int requiredStrlen = EA::StdC::Strlcpy(argv[i], wargv[i], 0);
+                argv[i] = (char8_t)_malloca(sizeof(char8_t) * (requiredStrlen + 1));
+                EA::StdC::Strlcpy(argv[i], wargv[i], requiredStrlen + 1);
+            }
+
+            #if defined(EAMAIN_USE_INITFINI)
+                EAMainInit();
+            #endif
+
+            const char *printServerAddress = EA::EAMain::Internal::ExtractPrintServerAddress(argc, argv);
+            EA::EAMain::Internal::EAMainStartup(printServerAddress);
+
+            EA::EAMain::InitializeSignalHandler();
+
+            int returnValue = EA::EAMain::Internal::gEAMainFunction(argc, argv);
+            EA::EAMain::Internal::EAMainShutdown(returnValue);
+
+            #if defined(EAMAIN_USE_INITFINI)
+                EAMainFini();
+            #endif
+
+            return returnValue;
+        }
+    #else
+        #if !defined(EAMAIN_FORCE_MAIN_USAGE) // If the user defines EAMAIN_FORCE_MAIN_USAGE before #including this file, then main is used even if we don't normally use it for the given platform.
+            #if defined(EA_PLATFORM_IPHONE)
+                #define main iosMain
+                extern "C" int iosMain(int argc, char** argv);
+            #endif
+        #endif
+    #endif
+
+    int main(int argc, char** argv)
+    {
+        // To do: Some platforms may require reading the command line from a file.
+        // Do we automatically do this for those platforms? Probably try to open a default file.
+        // Do we support loading from a file from any platform? Probably so.
+    #if defined(EAMAIN_USE_INITFINI)
+        EAMainInit();
+    #endif
+
+        EA::EAMain::Internal::gEAMainFunction = ::EAMain;
+
+        EA::EAMain::CommandLine commandLine(argc, argv);
+
+        const char *printServerAddress = EA::EAMain::Internal::ExtractPrintServerAddress(commandLine.Argc(), commandLine.Argv());
+        EA::EAMain::Internal::EAMainStartup(printServerAddress);
+
+        EA::EAMain::InitializeSignalHandler();
+
+        int returnValue = EA::EAMain::Internal::gEAMainFunction(commandLine.Argc(), commandLine.Argv());
+
+        EA::EAMain::Internal::EAMainShutdown(returnValue);
+
+    #if defined(EAMAIN_USE_INITFINI)
+        EAMainFini();
+    #endif
+        return returnValue;
+    }
+
+#endif
+
+
+#endif // header include guard

+ 213 - 0
include/EAMain/EAMain.h

@@ -0,0 +1,213 @@
+/////////////////////////////////////////////////////////////////////////////
+// EAMain.h
+//
+// Copyright (c) 2011 Electronic Arts Inc. All Rights Reserved.
+// Created by Paul Pedriana
+//
+// Provides a print server for retrieving console output and a simple
+// command line parser.
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef EAMAIN_H
+#define EAMAIN_H
+
+#ifdef _MSC_VER
+    #pragma warning(push, 0)        // Microsoft headers generate warnings at our higher warning levels.
+    #pragma warning(disable: 4530)  // C++ exception handler used, but unwind semantics are not enabled.
+    #pragma warning(disable: 4548)  // Expression before comma has no effect; expected expression with side-effect.
+    #pragma warning(disable: 4251)  // class (some template) needs to have dll-interface to be used by clients.
+#endif
+
+#include <EABase/eabase.h>
+#include <EAMain/internal/Version.h>
+
+namespace EA
+{
+    namespace EAMain
+    {
+        typedef int (*EAMainFunction)(int, char **);
+    }
+}
+
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <stddef.h>
+#include <limits.h>
+#include <stdarg.h>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+namespace EA
+{
+    namespace EAMain
+    {
+        typedef void (*ReportFunction)(const char8_t*);
+        EAMAIN_API void           SetReportFunction(ReportFunction pReportFunction);
+        EAMAIN_API ReportFunction GetReportFunction();
+        EAMAIN_API ReportFunction GetDefaultReportFunction();
+
+        /// Report
+        /// Provides a way to call Report with sprintf-style arguments.
+        /// This function will call the Report function after formatting the output.
+        /// This function acts just like printf, except that the output goes to the
+        /// given report function.
+        ///
+        /// The user needs to supply a newline if the user wants newlines, as the report
+        /// function will not append one. The user may supply multiple newlines if desired.
+        /// This is a low level function which user code can use to directly write
+        /// information to the debug output. This function is also used by the higher
+        /// level functionality here to write output.
+        ///
+        /// This function is the equivalent of ReportVerbosity(0, pFormat, ...).
+        ///
+        /// Example usage:
+        ///     Report("Time passed: %d\n", timeDelta);
+        ///
+        EAMAIN_API void Report(const char8_t* pFormat, ...);
+
+        /// ReportVerbosity
+        /// Same as Report, but is silent unless GetVerbosity() is >= the value specified as minVerbosity.
+        /// Typically to do a non-error trace print, you would specify a minVerbosity of 1.
+        /// If you are writing an error output, you can specify minVerbosity or 0, which is the same
+        /// as calling Report().
+        ///
+        /// Example usage:
+        ///     ReportVerbosity(1, "Time passed: %d\n", timeDelta);
+        ///
+        EAMAIN_API void ReportVerbosity(unsigned minVerbosity, const char8_t* pFormat, ...);
+
+        /// VReport
+        /// Called by EATest Report Wrapper to preserve the optional variable arguments
+        ///
+        EAMAIN_API void VReport(const char8_t* pFormat, va_list arguments);
+
+        /// VReportVerbosity
+        /// Called by EATest ReportVerbosity Wrapper to preserve the optional variable arguments
+        ///
+        EAMAIN_API void VReportVerbosity(unsigned minVerbosity, const char8_t* pFormat, va_list arguments);
+
+        ///////////////////////////////////////////////////////////////////////
+        /// GetVerbosity / SetVerbosity
+        ///
+        /// A value of 0 means to output just error results.
+        /// A value > 0 means to output more. This Test library doesn't itself
+        /// use this verbosity value; it's intended for the application to use
+        /// it while tracing test output.
+        ///
+        EAMAIN_API unsigned GetVerbosity();
+        EAMAIN_API void     SetVerbosity(unsigned verbosity);
+
+        /// PlatformStartup / PlatformShutdown
+        ///
+        /// Execute any necessary per-platform startup / shutdown code.
+        /// This should be executed once as the first line of main() and
+        /// once as the last line of main().
+        ///
+
+        /// DEPRECATED
+        /// Please use the PlatformStartup(int argc, char **argv) preferably
+        EAMAIN_API void PlatformStartup();
+
+        EAMAIN_API void PlatformStartup(int argc, char **argv);
+
+        EAMAIN_API void PlatformStartup(const char *printServerNetworkAddress);
+
+        EAMAIN_API void PlatformShutdown(int nError);
+
+        /// CommandLine
+        ///
+        /// Implements a small command line parser.
+        ///
+        /// Example usage:
+        ///     int main(int argc, char** argv) {
+        ///         CommandLine cmdLine(argc, argv);
+        ///         printf("%d", cmdLine.Argc());
+        ///     }
+        ///
+        class EAMAIN_API CommandLine
+        {
+        public:
+            enum Flags
+            {
+                FLAG_NONE            = 0,
+                FLAG_NO_PROGRAM_NAME = 1 << 0
+            };
+
+            static const char DEFAULT_DELIMITER = ':';
+            static const int MAX_COMMANDLINE_ARGS = 128;
+
+            CommandLine(int argc, char** argv);
+            explicit CommandLine(const char *commandLineString);
+            CommandLine(const char *commandLineString, unsigned int flags);
+            ~CommandLine();
+
+            int FindSwitch(const char *pSwitch, bool bCaseSensitive = false, const char **pResult = NULL, int nStartingIndex = 0, char delimiter = DEFAULT_DELIMITER) const;
+            bool HasHelpSwitch() const;
+
+            int Argc() const
+            {
+                return mArgc;
+            }
+
+            char** Argv() const
+            {
+                return mArgv;
+            }
+
+            const char *Arg(int pos) const
+            {
+                if (pos <= mArgc)
+                {
+                    return mArgv[pos];
+                }
+
+                return "";
+            }
+
+        private:
+            void ParseCommandLine(const char *args, unsigned int flags);
+
+            int    mArgc;
+            char** mArgv;
+            char*  mCommandLine;
+        };
+
+
+    } //namespace EAMain
+
+#if (defined(EA_PLATFORM_MICROSOFT) && !defined(CS_UNDEFINED_STRING) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP))
+    namespace EAMain
+    {
+        // Helper class for spawning tests in a separate thread on WinRT platforms, works around <future> include issues.
+        class IWinRTRunner
+        {
+        public:
+            virtual ~IWinRTRunner() {}
+            virtual void Run(int argc = 0, char** argv = NULL) = 0;
+            virtual bool IsFinished() = 0;
+            virtual void ReportResult() = 0;
+        };
+
+        EAMAIN_API IWinRTRunner* CreateWinRTRunner();
+    }
+#elif defined(EA_PLATFORM_KETTLE)
+    namespace EAMain
+    {
+        // By default EAMain will spawn a thread on Kettle which calls Gnm::submitDone to prevent
+        // the OS frome terminating the application.
+        //
+        // Applications that want to use the GPU (and will therefore need to call submitDone themselves)
+        // can call DisableSubmitDoneThread() after EAMain() is called.  No calls to submitDone will be made
+        // by this thread once DisableSubmitDoneThread() returns.
+        //
+        // Note: For thread safety, this function must be called from the same thread that EAMain is called from.
+        void DisableSubmitDoneThread();
+    }
+#endif
+
+} // namespace EA
+
+#ifdef _MSC_VER
+    #pragma warning(pop)
+#endif
+
+#endif // Header include guard

+ 50 - 0
include/EAMain/EAMainExit.h

@@ -0,0 +1,50 @@
+/////////////////////////////////////////////////////////////////////////////
+// EAMainExit.h
+//
+// Copyright (c) 2015 Electronic Arts Inc. All Rights Reserved.
+// Created by Justin Sharma
+//
+// Provides an exit function for clients to immediately close the application
+// without needing to throw an exception or call abort.
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef EAMAINEXIT_H
+#define EAMAINEXIT_H
+
+#ifdef _MSC_VER
+    #pragma warning(push, 0)        // Microsoft headers generate warnings at our higher warning levels.
+    #pragma warning(disable: 4530)  // C++ exception handler used, but unwind semantics are not enabled.
+    #pragma warning(disable: 4548)  // Expression before comma has no effect; expected expression with side-effect.
+    #pragma warning(disable: 4251)  // class (some template) needs to have dll-interface to be used by clients.
+#endif
+
+#include <EABase/eabase.h>
+#include <EAMain/internal/Version.h>
+
+namespace EA {
+    namespace EAMain {
+        enum ExitCodes {
+            ExitCode_Succeeded,
+            ExitCode_Asserted,
+            ExitCode_SignalAbort,
+            ExitCode_SignalSegmentationViolation,
+            ExitCode_SignalIllegalInstruction,
+            ExitCode_SignalHangup,
+            ExitCode_SignalFloatingPointException,
+            ExitCode_SignalBusError,
+            ExitCode_Unknown,
+            ExitCode_Max
+        };
+
+        EAMAIN_API EA_NO_INLINE void Exit(int exitcode);
+
+        EAMAIN_API EA_NO_INLINE void InitializeSignalHandler();
+    }
+}
+
+#ifdef _MSC_VER
+    #pragma warning(pop)
+#endif
+
+#endif // Header include guard

+ 68 - 0
include/EAMain/EAMainInitFini.inl

@@ -0,0 +1,68 @@
+///////////////////////////////////////////////////////////////////////////////
+// EAMainInitFini.inl
+//
+// Copyright (c) Electronic Arts. All Rights Reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef EAMAIN_EAENTRYPOINTMAIN_INITFINI_H
+#define EAMAIN_EAENTRYPOINTMAIN_INITFINI_H
+
+
+////////////////////////////////////////////////////////////////////////////////
+// This is a file that you can #include before #including <EAMain/EAEntryPointMain.inl>
+// If you #include this file or otherwise #define EAMAIN_USE_INITFINI then you are
+// expected to provide the extern "C" function implementations below in your
+// application. These two functions will be called before EAMain and after EAMain
+// respectively, to give the application a chance to do things like set up and
+// shutdown a custom memory manager.
+//
+// Example usage:
+//     #include <EAMain/EAMainInitFini.inl>
+//     #include <EAMain/EAEntryPointMain.inl>
+//
+//     extern "C" void EAMainInit()
+//     {
+//         using namespace EA::Allocator;
+//
+//         EA::EAMain::PlatformStartup();
+//         SetGeneralAllocator(&gEAGeneralAllocator); // This example assumes you are using a heap named as such.
+//         gEAGeneralAllocator.SetOption(GeneralAllocatorDebug::kOptionEnablePtrValidation, 0);
+//         gEAGeneralAllocator.SetAutoHeapValidation(GeneralAllocator::kHeapValidationLevelBasic, 64);
+//     }
+//
+//     void EAMainFini()
+//     {
+//         if(EA::EAMain::GetVerbosity() > 0)
+//             EA::Allocator::gEAGeneralAllocator.TraceAllocatedMemory();
+//         EA::EAMain::PlatformShutdown(gTestResult);
+//     }
+//
+//     int EAMain(int argc, char** argv)
+//     {
+//         . . .
+//     }
+////////////////////////////////////////////////////////////////////////////////
+
+#ifdef EAMAIN_EAENTRYPOINTMAIN_INL
+    #error EAMainInitFini.inl must be included before EAMainEntryPoint.inl
+#endif
+
+/// EAMainInit / EAMainFini
+///
+/// This functions are declared here, but must be defined in user code.
+///
+extern "C" void EAMainInit();
+extern "C" void EAMainFini();
+
+
+/// EAMAIN_USE_INITFINI
+///
+/// Defined or not defined. When defined it directs EAEntryPointMain.inl to call
+/// EAMainInit before EAMain and call EAMainFini after EAMain returns.
+///
+#if !defined(EAMAIN_USE_INITFINI)
+    #define EAMAIN_USE_INITFINI 1
+#endif
+
+
+#endif // header include guard

+ 24 - 0
include/EAMain/IPhoneEntry.h

@@ -0,0 +1,24 @@
+// Copyright (c), Electronic Arts Inc.
+
+#ifndef IPHONEENTRY_H
+#define IPHONEENTRY_H
+
+#pragma once
+
+namespace EA
+{
+    namespace EAMain
+    {
+        // Allows the user to specify their own custom Application Delegate, to
+        // replace the one defined in IPhoneEntry.mm. Be warned that if you want
+        // to run tests on the EATech Build Farm, you may need to implement some
+        // of the behaviours of the EAMainAppDelegate in your custom delegate,
+        // specifically the "Tests complete" alert.
+        void SetAppDelegateName(const char* delegateName);
+
+        // Accessors for command-line arguments
+        int GetArgC();
+        char** GetArgV();
+    }
+}
+#endif // Header include guard

+ 59 - 0
include/EAMain/internal/EAMainChannels.h

@@ -0,0 +1,59 @@
+///////////////////////////////////////////////////////////////////////////////
+// EAMainChannels.h
+//
+// Copyright (c) 2012 Electronic Arts Inc.
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef EAMAIN_ICHANNEL_H
+#define EAMAIN_ICHANNEL_H
+
+#include <EABase/eabase.h>
+#include <EAMain/internal/Version.h>
+
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <stdio.h>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+namespace EA
+{
+namespace EAMain
+{
+    // -----------------------------------------------------------
+    // Interface for EAMain channels
+    // -----------------------------------------------------------
+    class EAMAIN_API IChannel
+    {
+    public:
+        virtual ~IChannel() {}
+        virtual void Init() {}
+        virtual void Send(const char8_t* pData) {}
+        virtual void Shutdown() {}
+    };
+
+    // -----------------------------------------------------------
+    // Basic channel that echos to stdout.
+    // -----------------------------------------------------------
+    class EAMAIN_API PrintfChannel : public IChannel
+    {
+    public:
+        virtual ~PrintfChannel() {}
+        virtual void Send(const char8_t* pData);
+    };
+
+    // -----------------------------------------------------------
+    // Channel that serializes output data to a file.
+    // -----------------------------------------------------------
+    class EAMAIN_API FileChannel : public IChannel
+    {
+    public:
+        virtual ~FileChannel() {}
+        virtual void Init();
+        virtual void Send(const char8_t* pData);
+        virtual void Shutdown();
+
+    private:
+        FILE* mFileHandle;
+    };
+}}
+
+#endif

+ 49 - 0
include/EAMain/internal/EAMainPrintManager.h

@@ -0,0 +1,49 @@
+///////////////////////////////////////////////////////////////////////////////
+// EAMainNetworkManager.h
+//
+// Copyright (c) 2012 Electronic Arts Inc.
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef EAMAIN_PRINTMANAGER_H
+#define EAMAIN_PRINTMANAGER_H
+
+#include <EABase/eabase.h>
+#include <EAMain/internal/Version.h>
+
+#define TO_STRING(x) #x
+#define MACRO_TO_STRING(x) TO_STRING(x)
+
+namespace EA
+{
+namespace EAMain
+{
+    class IChannel;
+
+    enum EAMainChannel
+    {
+        CHANNEL_PRINTF = 0,
+        CHANNEL_NETWORK,
+        CHANNEL_FILE,
+        CHANNEL_MAX
+    };
+
+    class EAMAIN_API PrintManager
+    {
+    public:
+        PrintManager();
+        static PrintManager& Instance();
+
+        void Startup(const char8_t* ServerIP);
+        void Shutdown();
+
+        void Send(const char8_t* pData);
+        void Add(EAMainChannel channel, IChannel* instance);
+        void Remove(EAMainChannel channel, IChannel* instance);
+        void ClearChannel(EAMainChannel channel);
+
+    private:
+        IChannel* m_Channels[CHANNEL_MAX];
+    };
+}}
+
+#endif  // header include guard

+ 29 - 0
include/EAMain/internal/EAMainStartupShutdown.h

@@ -0,0 +1,29 @@
+///////////////////////////////////////////////////////////////////////////////
+// EAMainStartupShutdown.h
+//
+// Copyright (c) Electronic Arts. All Rights Reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef EAMAIN_EAENTRYPOINTMAIN_INTERNAL_STARTUPSHUTDOWN_H
+#define EAMAIN_EAENTRYPOINTMAIN_INTERNAL_STARTUPSHUTDOWN_H
+
+#include <EABase/eabase.h>
+#include <EAMain/internal/EAMainPrintManager.h>
+
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <stdio.h>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+namespace EA
+{
+    namespace EAMain
+    {
+        namespace Internal
+        {
+            EAMAIN_API void EAMainStartup(const char* printServerAddress = NULL);
+            EAMAIN_API int EAMainShutdown(int errorCount);
+        }
+    }
+}
+
+#endif // header include guard

+ 85 - 0
include/EAMain/internal/EAMainStartupShutdown.inl

@@ -0,0 +1,85 @@
+///////////////////////////////////////////////////////////////////////////////
+// EAMainStartupShutdown.h
+//
+// Copyright (c) Electronic Arts. All Rights Reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef EAMAIN_EAENTRYPOINTMAIN_INTERNAL_STARTUPSHUTDOWN_H
+#define EAMAIN_EAENTRYPOINTMAIN_INTERNAL_STARTUPSHUTDOWN_H
+
+#include <EABase/eabase.h>
+#include <EAMain/internal/EAMainPrintManager.h>
+
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <stdio.h>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+namespace EA
+{
+    namespace EAMain
+    {
+        namespace Internal
+        {
+
+            // Handle the internal EAMain main start-up
+            //
+            inline void EAMainStartup(const char8_t* ServerIP = NULL)
+            {
+                static bool sEAMainShutdown_StartupHandled = false;
+
+                if(!sEAMainShutdown_StartupHandled)
+                {
+                    sEAMainShutdown_StartupHandled = true;
+
+                    // Running under NAnt output only appears when the buffer is filled if we allow the default buffering scheme for printf.
+                    setvbuf(stdout, NULL, _IONBF, BUFSIZ);
+                    setvbuf(stderr, NULL, _IONBF, BUFSIZ);
+
+                    // Startup the print manager
+                    //
+                    EA::EAMain::PrintManager::Instance().Startup(ServerIP);
+                }
+            }
+
+
+            // Handle the internal EAMain main shutdown
+            //
+            inline int EAMainShutdown(int errorCount)
+            {
+               static bool sEAMainShutdown_ShutdownHandled = false;
+
+               if(!sEAMainShutdown_ShutdownHandled)
+               {
+                    sEAMainShutdown_ShutdownHandled = true;
+
+                    // Handle the application specific exit code.
+                    //
+                    #if defined(EA_PLATFORM_IPHONE)
+                       // Get test result. (iOS 5 bug prevents iPhone Runner from getting this from the exit code)
+                       if (errorCount == 0)
+                           Report("\nAll tests completed successfully.\n");
+                       else
+                           Report("\nTests failed. Total error count: %d\n", errorCount);
+                       fflush(stdout);
+                    #endif
+
+                    #if !defined(EA_PLATFORM_DESKTOP) && !defined(EA_PLATFORM_SERVER)  // TODO:  change define to something related to the StdC library used on the system.
+                        fflush(stdout);
+                    #endif
+
+                    // Required so the EAMainPrintServer can terminate with the correct error code.
+                    //
+                    EA::EAMain::Report("RETURNCODE=%d\n", errorCount);
+
+                    // Shutdown the EAMain print manager.
+                    //
+                    EA::EAMain::PrintManager::Instance().Shutdown();
+               }
+
+               return errorCount;
+            }
+        } //namespace Internal
+    } // namespace EAMain
+} // namespace EA
+
+#endif // header include guard

+ 120 - 0
include/EAMain/internal/Version.h

@@ -0,0 +1,120 @@
+///////////////////////////////////////////////////////////////////////////////
+// Version.h
+//
+// Copyright (c) Electronic Arts Inc.
+// Written and maintained by Paul Pedriana.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef EAMAIN_INTERNAL_VERSION_H
+#define EAMAIN_INTERNAL_VERSION_H
+
+
+#include <EABase/eabase.h>
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EAMAIN_VERSION
+//
+// EAMain, at least from version `2.15.0`, adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
+//
+// Example usage:
+//     printf("EAMAIN version: %s", EAMAIN_VERSION);
+//     printf("EAMAIN version: %d.%d.%d", EAMAIN_VERSION_N / 10000 % 100, EAMAIN_VERSION_N / 100 % 100, EAMAIN_VERSION_N % 100);
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef EAMAIN_VERSION
+    #define EAMAIN_VERSION    "3.01.01"
+    #define EAMAIN_VERSION_N  ((EA_MAIN_VERSION_MAJOR * 10000) + (EA_MAIN_VERSION_MINOR * 100) + (EA_MAIN_VERSION_PATCH))
+#endif
+
+#if defined _MSC_VER
+    #pragma once
+#endif
+
+/*
+This file provides a version number of the EAMain package for your own code to check against.
+
+Major, minor and patch versions are defined and updated each release.
+*/
+
+// Define the major, minor and patch versions.
+// This information is updated with each release.
+
+//! This define indicates the major version number for the package.
+//! \sa EA_MAIN_VERSION
+#define EA_MAIN_VERSION_MAJOR   3
+//! This define indicates the minor version number for the package.
+//! \sa EA_MAIN_VERSION
+#define EA_MAIN_VERSION_MINOR   0
+//! This define indicates the patch version number for the package.
+//! \sa EA_MAIN_VERSION
+#define EA_MAIN_VERSION_PATCH   1
+//! This define can be used for convenience when printing the version number
+//! \sa EA_MAIN_VERSION
+#define EA_MAIN_VERSION_STRING EA_MAIN_VERSION
+
+/*!
+ * This is a utility macro that users may use to create a single version number
+ * that can be compared against EA_MAIN_VERSION.
+ *
+ * For example:
+ *
+ * \code
+ *
+ * #if EA_MAIN_VERSION > EA_MAIN_CREATE_VERSION_NUMBER( 1, 1, 0 )
+ * printf("EAMain version is greater than 1.1.0.\n");
+ * #endif
+ *
+ * \endcode
+ */
+#define EA_MAIN_CREATE_VERSION_NUMBER( major_ver, minor_ver, patch_ver ) \
+    ((major_ver) * 1000000 + (minor_ver) * 1000 + (patch_ver))
+
+/*!
+ * This macro is an aggregate of the major, minor and patch version numbers.
+ * \sa EA_MAIN_CREATE_VERSION_NUMBER
+ */
+#define EA_MAIN_VERSION \
+    EA_MAIN_CREATE_VERSION_NUMBER( EA_MAIN_VERSION_MAJOR, EA_MAIN_VERSION_MINOR, EA_MAIN_VERSION_PATCH )
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EAMAIN_DLL
+//
+// Defined as 0 or 1. The default is dependent on the definition of EA_DLL.
+// If EA_DLL is defined, then EAMAIN_DLL is 1, else EAMAIN_DLL is 0.
+// EA_DLL is a define that controls DLL builds within the EAConfig build system.
+//
+#ifndef EAMAIN_DLL
+    #if defined(EA_DLL)
+        #define EAMAIN_DLL 1
+    #else
+        #define EAMAIN_DLL 0
+    #endif
+#endif
+
+#ifndef EAMAIN_API // If the build file hasn't already defined this to be dllexport...
+    #if defined(EAMAIN_DLL) && EAMAIN_DLL
+        #if defined(_MSC_VER)
+            #define EAMAIN_API      __declspec(dllimport)
+            #define EAMAIN_LOCAL
+        #elif defined(__CYGWIN__)
+            #define EAMAIN_API      __attribute__((dllimport))
+            #define EAMAIN_LOCAL
+        #elif (defined(__GNUC__) && (__GNUC__ >= 4))
+            #define EAMAIN_API      __attribute__ ((visibility("default")))
+            #define EAMAIN_LOCAL    __attribute__ ((visibility("hidden")))
+        #else
+            #define EAMAIN_API
+            #define EAMAIN_LOCAL
+        #endif
+
+    #else
+        #define EAMAIN_API
+        #define EAMAIN_LOCAL
+    #endif
+#endif
+
+#endif // Header include guard

+ 8 - 0
include/EAMain/version.h

@@ -0,0 +1,8 @@
+// (c) Electronic Arts. All Rights Reserved.
+
+#ifndef EAMAIN_VERSION_H
+#define EAMAIN_VERSION_H
+
+#include <EAMain/internal/Version.h>
+
+#endif // EA_MAIN_VERSION_H

+ 333 - 0
source/EAEntryPointMain.cpp

@@ -0,0 +1,333 @@
+#include "EABase/eabase.h"
+#if (defined(EA_PLATFORM_MICROSOFT) && !defined(CS_UNDEFINED_STRING) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP))
+#include <EAAssert/eaassert.h>
+#include <EAMain/EAMain.h>
+#include <EAStdC/EASprintf.h>
+#include <EAStdC/EAString.h>
+
+EA_DISABLE_ALL_VC_WARNINGS()
+#pragma warning(disable: 4472 4355)  // additional warnings generated by XDK with VS2015
+#include <memory>
+#ifndef WIN32_LEAN_AND_MEAN
+    #define WIN32_LEAN_AND_MEAN
+#endif
+#include <Windows.h>
+
+#if defined EA_PLATFORM_CAPILANO
+    #if defined EAMAIN_CAPILANO_DX12
+        #include <d3d12_x.h>
+    #else
+        #include <d3d11_x.h>
+    #endif
+#endif
+EA_RESTORE_ALL_VC_WARNINGS()
+
+namespace EA
+{
+namespace EAMain
+{
+
+// Application - implements the required functionality for a application
+ref class ApplicationView sealed : public Windows::ApplicationModel::Core::IFrameworkView
+{
+public:
+    // IFrameworkView Methods
+    virtual void Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView);
+    virtual void SetWindow(Windows::UI::Core::CoreWindow^ window) {}
+    virtual void Load(Platform::String^ entryPoint);
+    virtual void Run();
+    virtual void Uninitialize();
+private:
+    char *mCommandLine;
+#if EA_PLATFORM_CAPILANO
+    #if defined EAMAIN_CAPILANO_DX12
+        ID3D12CommandQueue* mpCommandQueue;
+    #else
+        ID3DXboxPerformanceContext* mpD3DXboxPerfContext;
+    #endif
+#endif
+
+    void CreateDeviceResources();
+    void OnActivated( Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args );
+};
+
+// ApplicationSource - responsible for creating the Application instance and passing it back to the system
+ref class ApplicationViewSource : Windows::ApplicationModel::Core::IFrameworkViewSource
+{
+public:
+    virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView()
+    {
+        return ref new ApplicationView();
+    }
+};
+
+void OutputAppTransition(const char* transition, const wchar_t* sender, const wchar_t* args)
+{
+    char msg[1024];
+
+    EA::StdC::Snprintf(msg, EAArrayCount(msg), "****\n  app is %s.\n  sender: ", transition);
+    EA::StdC::Strlcat(msg, sender, EAArrayCount(msg));
+    EA::StdC::Strlcat(msg, "\n  args: ", EAArrayCount(msg));
+    EA::StdC::Strlcat(msg, args, EAArrayCount(msg));
+    EA::StdC::Strlcat(msg, "\n****\n", EAArrayCount(msg));
+
+    OutputDebugStringA(msg);
+}
+
+void ApplicationView::CreateDeviceResources()
+{
+#if defined EA_PLATFORM_CAPILANO
+    #if defined EAMAIN_CAPILANO_DX12
+        ID3D12Device* pD3DDevice;
+
+        HRESULT hr = D3D12CreateDevice(
+            nullptr,                    // specify null to use the default adapter
+            D3D_FEATURE_LEVEL_11_0,
+            __uuidof(ID3D12Device),
+            (void**)&pD3DDevice
+        );
+
+        if (FAILED(hr))
+        {
+            OutputDebugStringA("Failed to create device. Suspending will not work for this application.");
+            return;
+        }
+
+        D3D12XBOX_COMMAND_QUEUE_DESC queueDesc = {};
+        queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
+        queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
+        queueDesc.EngineOrPipeIndex = 0;
+        queueDesc.QueueIndex = 0;
+
+        hr = pD3DDevice->CreateCommandQueueX(&queueDesc, __uuidof(ID3D12CommandQueue), (void**)&mpCommandQueue);
+        if (FAILED(hr))
+        {
+            OutputDebugStringA("Failed to create command queue. Suspending will not work for this application.");
+        }
+
+        if (pD3DDevice) { pD3DDevice->Release(); pD3DDevice = NULL; }
+    #else
+        // This flag adds support for surfaces with a different color channel ordering than the API default.
+        // It is recommended usage, and is required for compatibility with Direct2D.
+        UINT creationFlags = D3D11_CREATE_DEVICE_INSTRUMENTED;
+
+        // This array defines the set of DirectX hardware feature levels this app will support.
+        // Note the ordering should be preserved.
+        // Don't forget to declare your application's minimum required feature level in its
+        // description.  All applications are assumed to support 9.1 unless otherwise stated.
+        D3D_FEATURE_LEVEL featureLevels[] =
+        {
+            D3D_FEATURE_LEVEL_11_0
+        };
+
+        // Create the DX11 API device object, and get a corresponding context.
+        ID3D11Device* pD3DDevice;
+        ID3D11DeviceContext* pD3DDeviceContext;
+
+        HRESULT hr = D3D11CreateDevice(
+            nullptr,                    // specify null to use the default adapter
+            D3D_DRIVER_TYPE_HARDWARE,
+            nullptr,                    // leave as nullptr unless software device
+            creationFlags,              // optionally set debug and Direct2D compatibility flags
+            featureLevels,              // list of feature levels this app can support
+            ARRAYSIZE(featureLevels), // number of entries in above list
+            D3D11_SDK_VERSION,          // always set this to D3D11_SDK_VERSION
+            &pD3DDevice,                // returns the Direct3D device created
+            NULL,                       // returns feature level of device created
+            &pD3DDeviceContext          // returns the device immediate context
+        );
+
+        if (FAILED(hr))
+        {
+            OutputDebugStringA("Failed to create device. Suspending will not work for this application.");
+            return;
+        }
+
+            hr = pD3DDevice->QueryInterface(__uuidof(mpD3DXboxPerfContext), (void **)&mpD3DXboxPerfContext);
+
+        if (FAILED(hr))
+        {
+            OutputDebugStringA("Failed to get perfcontext. Suspending will not work for this application.");
+        }
+
+        if (pD3DDevice) { pD3DDevice->Release(); pD3DDevice = NULL; }
+        if (pD3DDeviceContext) { pD3DDeviceContext->Release(); pD3DDeviceContext = NULL; }
+    #endif
+#else
+    // Do nothing
+#endif
+}
+
+// Called by the system.  Perform application initialization here,
+// hooking application wide events, etc.
+void ApplicationView::Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView)
+{
+    using namespace Platform;
+    using namespace Windows::Foundation;
+    using namespace Windows::ApplicationModel;
+    using namespace Windows::ApplicationModel::Core;
+    using namespace Windows::ApplicationModel::Activation;
+
+    // Creates any resources required by the platform to run an application / enable a particular feature set (like GPU profiling or suspend/resume)
+    CreateDeviceResources();
+
+#pragma warning(push)
+// Disables warning for MS class 'Windows::Foundation::TypedEventHandler<TSender,TResult>::{ctor}::__abi_PointerToMemberCapture'
+// "layout of class may have changed from a previous version of the compiler due to better packing of member 'Windows::Foundation::TypedEventHandler<TSender,TResult>::{ctor}::__abi_PointerToMemberCapture::member'"
+#pragma warning(disable:4371)
+    applicationView->Activated += ref new Windows::Foundation::TypedEventHandler< CoreApplicationView^, IActivatedEventArgs^ >( this, &ApplicationView::OnActivated );
+#pragma warning(pop)
+    CoreApplication::Suspending += ref new EventHandler<SuspendingEventArgs^>([this](Object^ sender, SuspendingEventArgs^ args) {
+        OutputAppTransition("suspending", sender ? sender->ToString()->Data() : L"NULL", args ? args->ToString()->Data() : L"NULL");
+        #if defined EA_PLATFORM_CAPILANO
+            #if defined EAMAIN_CAPILANO_DX12
+                mpCommandQueue->SuspendX(0);
+            #else
+                mpD3DXboxPerfContext->Suspend(0);
+            #endif
+        #endif
+    });
+    CoreApplication::Resuming += ref new EventHandler<Object^>([this](Object^ sender, Object^ args) {
+        OutputAppTransition("resuming", sender ? sender->ToString()->Data() : L"NULL", args ? args->ToString()->Data() : L"NULL");
+        #if defined EA_PLATFORM_CAPILANO
+            #if defined EAMAIN_CAPILANO_DX12
+                mpCommandQueue->ResumeX();
+            #else
+                mpD3DXboxPerfContext->Resume();
+            #endif
+        #endif
+    });
+    CoreApplication::Exiting += ref new EventHandler<Object^>([](Object^ sender, Object^ args) {
+        OutputAppTransition("exiting", sender ? sender->ToString()->Data() : L"NULL", args ? args->ToString()->Data() : L"NULL");
+    });
+}
+
+static char *ConvertLaunchArgsToMultibyte(LPCWSTR rawArgumentString, int rawArgumentStringLength)
+{
+    int bufferSize = WideCharToMultiByte(
+            CP_UTF8,
+            0,
+            rawArgumentString,
+            rawArgumentStringLength,
+            NULL,
+            0,
+            NULL,
+            NULL);
+
+    char *commandLine = static_cast<char *>(calloc(bufferSize + 1, 1));
+    int rv = WideCharToMultiByte(
+            CP_UTF8,
+            0,
+            rawArgumentString,
+            rawArgumentStringLength,
+            commandLine,
+            bufferSize + 1,
+            NULL,
+            NULL);
+
+    commandLine[bufferSize] = 0;
+    EA_ASSERT(rv == bufferSize);
+    EA_UNUSED(rv); // avoids warnings in opt builds regarding unused variables
+
+    return commandLine;
+}
+
+static char *ReadArgsFromFile()
+{
+    FILE *fp = fopen("EAMainArgsFile.txt", "rb");
+
+    if (fp == NULL)
+    {
+        goto error_return;
+    }
+
+    size_t fileSize;
+    fseek(fp, 0, SEEK_END);
+    fileSize = static_cast<size_t>(ftell(fp));
+    fseek(fp, 0, SEEK_SET);
+
+    char *argsBuffer = static_cast<char *>(calloc(fileSize + 1, 1));
+
+    if (fread(argsBuffer, 1, fileSize, fp) != fileSize)
+    {
+        goto error_return_free_buffer;
+    }
+
+    return argsBuffer;
+
+error_return_free_buffer:
+    free(argsBuffer);
+    fclose(fp);
+
+error_return:
+    return static_cast<char *>(calloc(1, 1));
+}
+
+void ApplicationView::OnActivated( Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args )
+{
+    if (args->Kind == Windows::ApplicationModel::Activation::ActivationKind::Launch)
+    {
+        Windows::ApplicationModel::Activation::LaunchActivatedEventArgs ^launchArgs = (Windows::ApplicationModel::Activation::LaunchActivatedEventArgs ^) args;
+        Platform::String ^argumentString = launchArgs->Arguments;
+        LPCWSTR rawArgumentString = argumentString->Data();
+        int rawArgumentStringLength = argumentString->Length();
+
+        if (rawArgumentString == NULL || wcslen(rawArgumentString) == 0)
+        {
+            mCommandLine = ReadArgsFromFile();
+        }
+        else
+        {
+            mCommandLine = ConvertLaunchArgsToMultibyte(rawArgumentString, rawArgumentStringLength);
+        }
+    }
+    Windows::UI::Core::CoreWindow::GetForCurrentThread()->Activate();
+}
+
+void ApplicationView::Load(Platform::String^ entryPoint)
+{
+
+}
+
+// Called by the system after initialization is complete. This implements the traditional game loop.
+void ApplicationView::Run()
+{
+    using namespace EA::EAMain;
+    std::unique_ptr<IWinRTRunner> winRTRunner(CreateWinRTRunner());
+
+    CommandLine commandline(mCommandLine, CommandLine::FLAG_NO_PROGRAM_NAME);
+
+    winRTRunner->Run(commandline.Argc(),commandline.Argv());
+    do
+    {
+        // ProcessEvents will throw if the process is exiting, allowing us to
+        // break out of the loop.  This will be cleaned up when we get proper
+        // process lifetime management online in a future release.
+        Windows::UI::Core::CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent);
+    } while(!winRTRunner->IsFinished());
+
+    winRTRunner->ReportResult();
+    Windows::ApplicationModel::Core::CoreApplication::Exit();
+
+    free(mCommandLine);
+    mCommandLine = nullptr;
+}
+
+
+void ApplicationView::Uninitialize()
+{
+
+}
+
+namespace Internal
+{
+    EAMAIN_API void StartWinRtApplication()
+    {
+        // To do: store args so they can be passed to EAEntryPointMain.
+        Windows::ApplicationModel::Core::CoreApplication::Run(ref new ApplicationViewSource());
+    }
+} // namespace Internal
+} // namespace EAMain
+} // namespace EA
+
+#endif

+ 753 - 0
source/EAMain.cpp

@@ -0,0 +1,753 @@
+/////////////////////////////////////////////////////////////////////////////
+// EAMain.cpp
+//
+// Copyright (c) 2011 Electronic Arts Inc. All Rights Reserved.
+// Created by Paul Pedriana
+/////////////////////////////////////////////////////////////////////////////
+
+
+#ifdef _MSC_VER
+    #pragma warning(disable: 4530 4548 4509)
+    #pragma warning(disable: 6320)  // Exception-filter expression is the constant EXCEPTION_EXECUTE_HANDLER.
+    #pragma warning(disable: 4472 4355)  // additional warnings generated by XDK with VS2015
+#endif
+
+#include <EAAssert/eaassert.h>
+#include <EAMain/EAMain.h>
+#include <eathread/eathread.h>
+#include <eathread/eathread_atomic.h>
+#ifdef EA_PLATFORM_ANDROID
+    #include <eathread/eathread_futex.h>
+#endif
+#include <EAStdC/EAString.h>
+#include <EAStdC/EASprintf.h>
+#include <EAStdC/EADateTime.h>
+#include <EAStdC/EAProcess.h>
+#include <EAMain/internal/EAMainStartupShutdown.h>
+#include <EAMain/internal/EAMainPrintManager.h>
+
+
+#include <EABase/eabase.h>
+
+EA_DISABLE_ALL_VC_WARNINGS()
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <stdarg.h>
+
+#if defined(EA_PLATFORM_MICROSOFT)
+        #ifndef WIN32_LEAN_AND_MEAN
+            #define WIN32_LEAN_AND_MEAN
+        #endif
+        #include <Windows.h>
+#elif defined(__APPLE__)    // OS X, iPhone, iPad, etc.
+    #include <stdbool.h>
+    #include <sys/types.h>
+    #include <unistd.h>
+    #include <sys/sysctl.h>
+    #import <mach/mach.h>
+    #import <mach/mach_host.h>
+#elif defined(EA_PLATFORM_BSD)
+    #include <sys/types.h>
+    #include <sys/ptrace.h>
+#elif defined(EA_HAVE_SYS_PTRACE_H)
+    #include <unistd.h>
+    #include <sys/ptrace.h>
+#elif defined(EA_PLATFORM_ANDROID)
+    #include <unistd.h>
+    #include <android/log.h>
+#endif
+
+EA_RESTORE_ALL_VC_WARNINGS()
+
+///////////////////////////////////////////////////////////////////////////////
+// EA_COMPILER_VA_COPY_REQUIRED
+//
+// This is already present in EABase version >= 2.00.40a report may not cause a flush
+// See EABase for documentation.
+//
+#ifndef EA_COMPILER_VA_COPY_REQUIRED
+    #if (EABASE_VERSION_N < 20040) // If not already handled by EABase...
+        #if   ((defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__)) && (!defined(__i386__) || defined(__x86_64__)) && !defined(__ppc__) && !defined(__PPC__) && !defined(__PPC64__)
+            #define EA_COMPILER_VA_COPY_REQUIRED 1
+        #endif
+    #endif
+#endif
+
+
+namespace EA
+{
+    namespace EAMain
+    {
+        namespace Internal
+        {
+            EAMAIN_API EAMainFunction gEAMainFunction;
+
+            /// ReportDefault
+            ///
+            /// This is the default report function.
+            /// It does not append any newlines to the output nor does it require
+            /// the user to do so. It simply passes on the input to stdout.
+            /// If the user wants the output to have a newline, the user must supply it.
+            /// This allows the user to report multiple text items to the same line if desired.
+            /// It does not send the input to stderr, as the output of a unit test
+            /// is deemed to be test results (success or failure) and not errors.
+            ///
+#ifdef EA_PLATFORM_ANDROID
+            static const size_t ANDROID_REPORT_BUFFER_SIZE = 1023;
+            static char gAndroidReportBuffer[ANDROID_REPORT_BUFFER_SIZE + 1];
+            static char *gAndroidReportBufferWritePtr = gAndroidReportBuffer;
+            static EA::Thread::Futex gBufferFutex;
+            static EA::Thread::ThreadTime gLastThreadTime = EA::Thread::kTimeoutImmediate;
+            static EA::Thread::ThreadTime gMinTimeBetweenPrints = EA::Thread::ThreadTime(1);
+
+            // This function assumes that the buffer futex above is held
+            // prior to entry. Please ensure that the buffer futex is either
+            // held by the calling code.
+            static void FlushAndroidReportBuffer()
+            {
+                if (gAndroidReportBufferWritePtr != gAndroidReportBuffer)
+                {
+                    *gAndroidReportBufferWritePtr = 0;
+                    __android_log_write(ANDROID_LOG_INFO, "EAMain", gAndroidReportBuffer);
+
+                    // We found that if the OS is spammed too quickly with log info, it stops taking output from the app
+                    // because it thinks it's a DDOS attack.  So we sleep to give the OS time in the case the last
+                    // log output was too recent.
+                    EA::Thread::ThreadTime currentTime = EA::Thread::GetThreadTime();
+                    if (gLastThreadTime != EA::Thread::kTimeoutImmediate &&
+                        ((currentTime - gLastThreadTime) < gMinTimeBetweenPrints))
+                    {
+                        EA::Thread::ThreadSleep(gMinTimeBetweenPrints);
+                    }
+                    gLastThreadTime = currentTime;
+
+                    gAndroidReportBufferWritePtr = gAndroidReportBuffer;
+                }
+            }
+
+            static void AppendToReportBuffer(char8_t c)
+            {
+                char *ptr = gAndroidReportBufferWritePtr;
+                char *end = &gAndroidReportBuffer[ANDROID_REPORT_BUFFER_SIZE];
+
+                if (ptr >= end)
+                {
+                    FlushAndroidReportBuffer();
+                }
+
+                *gAndroidReportBufferWritePtr++ = c;
+            }
+
+            static void AndroidReport(const char8_t *pMessage)
+            {
+                using namespace EA::StdC;
+                using namespace EA::Thread;
+
+                AutoFutex autoFutex(gBufferFutex);
+
+                size_t messageLength = Strlen(pMessage);
+                for (size_t i = 0; i < messageLength; ++i)
+                {
+                    char8_t c = pMessage[i];
+
+                    switch (c)
+                    {
+                        case '\n':
+                            FlushAndroidReportBuffer();
+                            break;
+                        default:
+                            AppendToReportBuffer(c);
+                            break;
+                    }
+                }
+            }
+#endif
+
+            static void ReportDefault(const char8_t* pMessage)
+            {
+                if (!pMessage)
+                {
+                    return;
+                }
+
+                // It's possible that the underlying print back end can't handle large
+                // output sizes. For example, the OutputDebugStringA call below drops
+                // chars beyond about 4096.
+                size_t       length = EA::StdC::Strlen(pMessage); // It might be faster to make a custom Strlen which quits after N chars.
+                const size_t kMaxLength = 1024;
+
+                if(length > kMaxLength)
+                {
+                    for(size_t i = 0, copiedLength = 0; i < length; i += copiedLength)
+                    {
+                        char8_t buffer[kMaxLength + 1];
+                        size_t  c;
+
+                        copiedLength = ((length - i) >= kMaxLength) ? kMaxLength : (length - i);
+                        for(c = 0; c < copiedLength; c++)
+                            buffer[c] = pMessage[i + c];
+                        buffer[c] = 0;
+                        ReportDefault(buffer);
+                    }
+                }
+                else
+                {
+                    #if defined(EA_PLATFORM_MICROSOFT) && !defined(CS_UNDEFINED_STRING) // No need to do this for Microsoft console platforms, as the fputs below covers that.
+                        OutputDebugStringA(pMessage);
+                    #endif
+
+                    #if defined(EA_PLATFORM_ANDROID)
+                        // Android doesn't implement stdio (e.g. fputs), though sometimes we use compiler
+                        // linking statements to redirect stdio functions to our own implementations which
+                        // allow it to work.
+                        //
+                        // __android_log_write can write only 512 bytes at a time. Normally we don't write
+                        // so much text in unit test output, but if this becomes a problem then we can loop
+                        // and write blocks of the output. The primary downside to such an approach is that
+                        // __android_log_write appends a \n to your output for each call. See the EAStdC
+                        // EASprintfCore.cpp code for example loop code.
+                        AndroidReport(pMessage);
+                    #else
+                        fputs(pMessage, stdout);
+                        fflush(stdout);
+                    #endif
+                }
+            }
+
+            EAMAIN_API const char *ExtractPrintServerAddress(int argc, char **argv)
+            {
+                CommandLine commandLine(argc, argv);
+                const char *printServerAddress = NULL;
+
+                if (commandLine.FindSwitch("PrintServerIPAddress", false, &printServerAddress, 0, '=') >= 0)
+                {
+                    if (EA::StdC::Strlen(printServerAddress) > 0)
+                    {
+                        return printServerAddress;
+                    }
+                }
+
+                return NULL;
+            }
+        }
+
+        ReportFunction gpReportFunction = EA::EAMain::Internal::ReportDefault;
+        EAMAIN_API void SetReportFunction(ReportFunction pReportFunction)
+        {
+            gpReportFunction = pReportFunction;
+        }
+
+        EAMAIN_API ReportFunction GetReportFunction()
+        {
+            return gpReportFunction;
+        }
+
+        EAMAIN_API ReportFunction GetDefaultReportFunction()
+        {
+            using namespace EA::EAMain::Internal;
+
+            return ReportDefault;
+        }
+        ///////////////////////////////////////////////////////////////////////////////
+        // GetVerbosity / SetVerbosity
+        ///////////////////////////////////////////////////////////////////////////////
+
+        unsigned gVerbosity = 0; // 0 means to display just failures.
+
+        EAMAIN_API unsigned GetVerbosity()
+        {
+            return gVerbosity;
+        }
+
+
+        EAMAIN_API void SetVerbosity(unsigned verbosity)
+        {
+            gVerbosity = verbosity;
+        }
+
+        ///////////////////////////////////////////////////////////////////////////////
+        // ReportVaList
+        //
+        static void ReportVaList(unsigned minVerbosity, ReportFunction pReportFunction, const char8_t* pFormat, va_list arguments)
+        {
+            if(pFormat && (GetVerbosity() >= minVerbosity))
+            {
+                #if defined(EA_PLATFORM_DESKTOP)
+                const int kBufferSize = 2048;
+                #else
+                const int kBufferSize = 512;
+                #endif
+                char buffer[kBufferSize];
+
+                #if defined(EA_COMPILER_VA_COPY_REQUIRED)
+                    va_list argumentsSaved;
+                    va_copy(argumentsSaved, arguments);
+                #endif
+
+                const int nReturnValue = EA::StdC::Vsnprintf(buffer, kBufferSize, pFormat, arguments);
+
+                if(!pReportFunction)
+                    pReportFunction = gpReportFunction;
+
+                if(pReportFunction)
+                {
+                    if((nReturnValue >= 0) && (nReturnValue < (int)kBufferSize))
+                        pReportFunction(buffer);
+                    else if(nReturnValue < 0) // If we simply didn't have enough buffer space.
+                    {
+                        pReportFunction("Invalid format specified.\n    Format: ");
+                        pReportFunction(pFormat);
+                    }
+                    else // Else we simply didn't have enough buffer space.
+                    {
+                        char* pBuffer = static_cast<char *>(calloc(nReturnValue + 1, 1));
+
+                        if(pBuffer)
+                        {
+                            #if defined(EA_COMPILER_VA_COPY_REQUIRED)
+                                va_end(arguments);
+                                va_copy(arguments, argumentsSaved);
+                            #endif
+
+                            EA::StdC::Vsnprintf(pBuffer, nReturnValue + 1, pFormat, arguments);
+                            pReportFunction(pBuffer);
+                            free(pBuffer);
+                        }
+                        else
+                            pReportFunction("Unable to allocate buffer space for large printf.\n");
+                    }
+                }
+
+                #if defined(EA_COMPILER_VA_COPY_REQUIRED)
+                    // The caller will call va_end(arguments)
+                    va_end(argumentsSaved);
+                #endif
+            }
+        }
+
+        ///////////////////////////////////////////////////////////////////////////////
+        // Report
+        //
+        EAMAIN_API void Report(const char8_t* pFormat, ...)
+        {
+            va_list arguments;
+            va_start(arguments, pFormat);
+            ReportVaList(0, gpReportFunction, pFormat, arguments);
+            va_end(arguments);
+        }
+
+        ///////////////////////////////////////////////////////////////////////////////
+        // ReportVerbosity
+        //
+        EAMAIN_API void ReportVerbosity(unsigned minVerbosity, const char8_t* pFormat, ...)
+        {
+            va_list arguments;
+            va_start(arguments, pFormat);
+            ReportVaList(minVerbosity, gpReportFunction, pFormat, arguments);
+            va_end(arguments);
+        }
+
+        ///////////////////////////////////////////////////////////////////////////////
+        // VReport
+        //
+        EAMAIN_API void VReport(const char8_t* pFormat, va_list arguments)
+        {
+            ReportVaList(0, gpReportFunction, pFormat, arguments);
+        }
+
+        ///////////////////////////////////////////////////////////////////////////////
+        // VReportVerbosity
+        //
+        EAMAIN_API void VReportVerbosity(unsigned minVerbosity, const char8_t* pFormat, va_list arguments)
+        {
+            ReportVaList(minVerbosity, gpReportFunction, pFormat, arguments);
+        }
+
+        ///////////////////////////////////////////////////////////////////////////////
+        // PlatformStartup
+        //
+        EAMAIN_API void PlatformStartup()
+        {
+            // Routed to EAMainStartup to centralize
+            // the platform specific startup code.
+            PlatformStartup(NULL);
+        }
+
+        EAMAIN_API void PlatformStartup(int argc, char **argv)
+        {
+            const char *printServerNetworkAddress = Internal::ExtractPrintServerAddress(argc, argv);
+
+            PlatformStartup(printServerNetworkAddress);
+        }
+
+        EAMAIN_API void PlatformStartup(const char *printServerNetworkAddress)
+        {
+            // Routed to EAMainStartup to centralize
+            // the platform specific startup code.
+            EA::EAMain::Internal::EAMainStartup(printServerNetworkAddress);
+        }
+
+        ///////////////////////////////////////////////////////////////////////////////
+        // PlatformShutdown
+        //
+        EAMAIN_API void PlatformShutdown(int errorCount)
+        {
+#ifdef EA_PLATFORM_ANDROID
+            // The Android reporting functions will flush the output buffers
+            // when a newline is encountered. Calling AndroidReport with a
+            // single newline will cause any accumulated output to flush to
+            // the log.
+            //
+            // An alternative would be to call the FlushAndroidReportBuffer
+            // function but doing so would necessitate having separate locks
+            // for both FlushAndroidReportBuffer and AndroidReport, as both
+            // of these could be caused at the same time. To avoid this
+            // complication, FlushAndroidReportBuffer will only be called by
+            // AndroidReport or its children.
+            // -mburke
+            Internal::AndroidReport("\n");
+#endif
+            // Routed to EAMainShutdown to centralize
+            // the platform specific shutdown code.
+            EA::EAMain::Internal::EAMainShutdown(errorCount);
+        }
+
+        ///////////////////////////////////////////////////////////////////////////////
+        // CommandLine
+        ///////////////////////////////////////////////////////////////////////////////
+        CommandLine::CommandLine(int argc, char** argv)
+            : mArgc(argc)
+            , mArgv(NULL)
+            , mCommandLine(NULL)
+        {
+            mArgv = static_cast<char **>(calloc(argc + 1, sizeof(char *)));
+            EA_ASSERT(mArgv != NULL);
+
+            for (int i = 0; i < argc; ++i)
+            {
+                mArgv[i] = argv[i];
+            }
+
+            mArgv[argc] = NULL;
+
+            // Microsoft fails to support argc/argv on Xenon. Sometimes it works; sometimes it doesn't.
+        }
+
+        CommandLine::CommandLine(const char *args)
+            : mArgc(0),
+              mArgv(NULL),
+              mCommandLine(NULL)
+        {
+            ParseCommandLine(args, FLAG_NONE);
+        }
+
+        CommandLine::CommandLine(const char *args, unsigned int flags)
+            : mArgc(0),
+              mArgv(NULL),
+              mCommandLine(NULL)
+        {
+            ParseCommandLine(args, flags);
+        }
+
+
+        CommandLine::~CommandLine()
+        {
+            if (mArgv)
+            {
+                free(mArgv);
+                mArgv = NULL;
+            }
+
+            if (mCommandLine)
+            {
+                free(mCommandLine);
+                mCommandLine = NULL;
+            }
+        }
+
+        /// Stristr
+        /// We implement this here because it isn't consistently present with all compiler-supplied C libraries.
+        static char* Stristr(const char* s1, const char* s2)
+        {
+            const char* cp = s1;
+
+            if(!*s2)
+                return (char*)s1;
+
+            while(*cp)
+            {
+                const char* s = cp;
+                const char* t = s2;
+
+                while(*s && *t && (tolower(*s) == tolower(*t)))
+                    ++s, ++t;
+
+                if(*t == 0)
+                    return (char*)cp;
+                ++cp;
+            }
+
+            return 0;
+        }
+
+        //Returns position switch is found at. Returns -1 if not found
+        int CommandLine::FindSwitch(const char* pSwitch, bool bCaseSensitive, const char** pResult, int nStartingIndex, char delimeter) const
+        {
+            const char8_t kSwitchIDs[]   = { '-', '/' };
+            const int     kSwitchIDCount = sizeof(kSwitchIDs)/sizeof(kSwitchIDs[0]);
+            static const char sEmptyString[] = { 0 };
+
+            if(nStartingIndex < 0)
+            {
+                nStartingIndex = 0;
+            }
+
+            if (pResult)
+            {
+                *pResult = sEmptyString;
+            }
+
+            // Here we move the input pSwitch past any one leading switch indicator such as '-'.
+            for(int i = 0; i < kSwitchIDCount; ++i)
+            {
+                if(*pSwitch == kSwitchIDs[i])
+                {
+                    ++pSwitch;
+                    break;
+                }
+            }
+
+            const size_t nSwitchLength = strlen(pSwitch);
+
+            if (!nSwitchLength || (nStartingIndex >= mArgc))
+                return -1;
+
+            for(int i = nStartingIndex; i < mArgc; ++i)
+            {
+                const char *sCurrent = mArgv[i];
+
+                if(strlen(sCurrent) >= 2) // Enough, for example, for "-x".
+                {
+                    int j;
+
+                    // Make sure the string starts with a switch ID (e.g. '-').
+                    for(j = 0; j < kSwitchIDCount; ++j)
+                    {
+                        if(sCurrent[0] == kSwitchIDs[j])
+                            break;
+                    }
+
+                    if(j < kSwitchIDCount) // If a leading '-' was found...
+                    {
+                        const char* pCurrent = bCaseSensitive ? strstr(sCurrent + 1, pSwitch) : Stristr(sCurrent + 1, pSwitch);
+                        const char* pCStr    = sCurrent;
+
+                        if(pCurrent == (pCStr + 1)) // If the user's input switch matched at least the start of the current argument switch...
+                        {
+                            pCurrent += nSwitchLength; // Move pCurrent past the input switch.
+
+                            // At this point, we require that *pCurrent is either 0 or delimeter.
+                            if((*pCurrent == 0) || (*pCurrent == delimeter))
+                            {
+                                // We have a match. Now possibly return a result string.
+                                if(*pCurrent == delimeter)
+                                {
+                                    if(*++pCurrent)
+                                    {
+                                        if(pResult)
+                                        {
+                                            *pResult = pCurrent;
+                                        }
+                                    }
+                                }
+
+                                return i;
+                            }
+                        }
+                    }
+                }
+            }
+
+            return -1;
+        }
+
+        bool CommandLine::HasHelpSwitch() const
+        {
+            if((FindSwitch("-help", false, NULL, 0) >= 0) ||
+               (FindSwitch("-h", false, NULL, 0) >= 0) ||
+               (FindSwitch("-?", false, NULL, 0) >= 0))
+            {
+                return true;
+            }
+                return false;
+        }
+
+        void CommandLine::ParseCommandLine(const char *inputCommandLine, unsigned int flags)
+        {
+            size_t commandLineLength = strlen(inputCommandLine);
+            size_t allocSize = commandLineLength + 1;
+            size_t startOffset = 0;
+
+            if (flags & FLAG_NO_PROGRAM_NAME)
+            {
+                allocSize += 1;
+                startOffset = 1;
+            }
+
+            char *commandLine = static_cast<char *>(calloc(allocSize, 1));
+            EA_ASSERT(commandLine != NULL);
+
+            memcpy(commandLine + startOffset, inputCommandLine, commandLineLength);
+
+            int argc = 0;
+            char **argv = static_cast<char **>(calloc(MAX_COMMANDLINE_ARGS, sizeof(char *)));
+            EA_ASSERT(argv != NULL);
+
+            char *start = commandLine + startOffset;
+            char *ptr = start;
+            char *end = start + commandLineLength;
+            bool isQuoted = false;
+            const char quote = '"';
+
+            if (flags & FLAG_NO_PROGRAM_NAME)
+            {
+                argv[argc++] = commandLine;
+            }
+
+            while (ptr < end)
+            {
+                // The two cases this parser handles for quotes are:
+                //   "this is a quoted parameter"; and
+                //   -D:"this is a quoted parameter"
+                // The parser does not handle edge cases like
+                //   "this is a quoted parameter"and"this is the same"
+                char *quoteStart = NULL;
+
+                for (;;)
+                {
+                    while ((ptr < end) && !isspace((unsigned char)*ptr))
+                    {
+                        if (*ptr == quote && !isQuoted)
+                        {
+                            isQuoted = true;
+                            quoteStart = ptr;
+                        }
+
+                        ++ptr;
+                    }
+
+                    if (isQuoted)
+                    {
+                        if (*(ptr - 1) == quote)
+                        {
+                            // If we find a quote, shift the whole string back
+                            // by one character, ie:
+                            //   -D:"this is a quoted parameter"
+                            // becomes
+                            //   -D:this is a quoted parameter"
+                            // The trailing quote is removed below when we place
+                            // a null terminator at the end of our argument.
+                            memmove(quoteStart, quoteStart + 1, (end - quoteStart));
+                            --end;
+                            ptr -= 2;
+                            isQuoted = false;
+                            break;
+                        }
+                        ++ptr;
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+
+                if (ptr != start)
+                {
+                    *ptr = 0;
+
+                    argv[argc++] = start;
+                    ++ptr;
+                }
+
+                while ((ptr < end) && isspace((unsigned char)*ptr))
+                {
+                    ++ptr;
+                }
+
+                start = ptr;
+            }
+
+            mArgc = argc;
+            mArgv = argv;
+            mCommandLine = commandLine;
+        }
+    }
+}
+
+// WinRT-based Windows:
+#if (defined(EA_PLATFORM_MICROSOFT) && !defined(CS_UNDEFINED_STRING) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP))
+
+EA_DISABLE_VC_WARNING(4350 4571 4625 4626 4265)
+#include <future>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+extern "C" int EAMain(int argc, char** argv);
+
+namespace EA
+{
+
+namespace EAMain
+{
+
+class WinRTRunner : public IWinRTRunner
+{
+private:
+    // The copy/assignment operator is explicitly inaccessible due to the base class 'IWinRTRunner' containing a member
+    // that is  non-copyable (eg.  std::future).
+    WinRTRunner(const WinRTRunner &);
+    WinRTRunner& operator=(const WinRTRunner &);
+
+public:
+    WinRTRunner() {}
+
+    virtual void Run(int argc, char** argv) override
+    {
+        mResult = std::async(std::launch::async, [=]() {
+
+            const char *printServerAddress = Internal::ExtractPrintServerAddress(argc, argv);
+            EA::EAMain::Internal::EAMainStartup(printServerAddress);
+
+            int result = EA::EAMain::Internal::gEAMainFunction(argc, argv);
+            EA::EAMain::Internal::EAMainShutdown(result);
+            return result;
+        });
+    }
+
+    virtual bool IsFinished() override { return mResult.wait_for(std::chrono::milliseconds(33)) == std::future_status::ready; }
+
+    virtual void ReportResult() override
+    {
+        char output[100];
+        EA::StdC::Snprintf(output, EAArrayCount(output), "EXIT(%d)\n", mResult.get());
+
+        // Using OutputDebugStringA directly here as opposed to Report as someone may overload
+        // the default reporter. And this is what counts for EARunner to know what to do.
+        OutputDebugStringA(output);
+    }
+
+    std::future<int> mResult;
+};
+
+EAMAIN_API IWinRTRunner* CreateWinRTRunner()
+{
+    return new WinRTRunner();
+}
+
+} // namespace EAMain
+
+} // namespace EA
+
+#endif // WinRT-based Windows

+ 159 - 0
source/EAMainExit.cpp

@@ -0,0 +1,159 @@
+/////////////////////////////////////////////////////////////////////////////
+// EAMainExit.cpp
+//
+// Copyright (c) 2015 Electronic Arts Inc. All Rights Reserved.
+// Created by Justin Sharma
+/////////////////////////////////////////////////////////////////////////////
+
+#include <EAMain/EAMainExit.h>
+#include <EAMain/EAMain.h>
+
+#include <EAStdC/EAMemory.h>
+
+#include <EABase/eabase.h>
+
+EA_DISABLE_ALL_VC_WARNINGS()
+
+#include <cstdlib>
+
+#if defined(EA_PLATFORM_SONY) || defined(EA_PLATFORM_ANDROID)
+    // All of these platforms require complex handling of exceptions and signals. No apparent solution yet.
+#elif defined(EA_PLATFORM_LINUX)
+    #include <sys/signal.h>
+#elif defined(EA_PLATFORM_APPLE) || defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_CAPILANO) || defined(CS_UNDEFINED_STRING)
+    #include <csignal>
+#endif
+
+EA_RESTORE_ALL_VC_WARNINGS()
+
+#include <EAMain/internal/EAMainStartupShutdown.h>
+
+namespace EA {
+    namespace EAMain {
+        const char* gExitCodeNames[ExitCode_Max] =
+        {
+            "Succeeded",
+            "Asserted",
+            "Abort Signal",
+            "Segmentation Fault Signal",
+            "Illegal Instruction Signal",
+            "Hangup Signal",
+            "Floating Point Exception Signal",
+            "BusError Signal",
+            "Unkown",
+        };
+
+        void Exit(int exitcode)
+        {
+            int index = (exitcode < 0 || exitcode >= ExitCode_Max) ? exitcode = ExitCode_Unknown : exitcode;
+            Report("======================================================================\nEA::Main::Exit called with exitcode %d (%s)!\nThe caller wanted to immediately end execution!\n======================================================================\n", exitcode, gExitCodeNames[index]);
+#if !defined(EA_PLATFORM_ANDROID)
+            Internal::EAMainShutdown(exitcode);
+            std::exit(exitcode);
+#else
+            PlatformShutdown(exitcode);
+#endif
+        }
+
+#if defined(EA_PLATFORM_SONY) || defined(EA_PLATFORM_ANDROID) || defined(CS_UNDEFINED_STRING)
+        void InitializeSignalHandler() {
+            // Do nothing. No solution for signal/exception handling on this platform yet.
+        }
+
+#elif defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_CAPILANO) || defined(CS_UNDEFINED_STRING)
+        void InitializeSignalHandler()
+        {
+            // Do nothing. Unhandled exception filter will handle creating a minidump on
+            // this platform which will contain more actionable information than a trapped
+            // signal.
+        }
+
+#elif defined(EA_PLATFORM_APPLE) || defined(EA_PLATFORM_IPHONE)
+        int SignalToExitCode(int signal) {
+            switch(signal)
+            {
+            case SIGABRT:
+                return ExitCode_SignalAbort;
+            case SIGSEGV:
+                return ExitCode_SignalSegmentationViolation;
+            case SIGILL:
+                return ExitCode_SignalIllegalInstruction;
+            case SIGFPE:
+                return ExitCode_SignalFloatingPointException;
+            case SIGHUP:
+                return ExitCode_SignalHangup;
+            case SIGBUS:
+                return ExitCode_SignalBusError;
+            default:
+                return ExitCode_Unknown;
+            };
+        }
+
+        void HandleSignal(int signal) {
+            EA::EAMain::Exit(SignalToExitCode(signal));
+        }
+
+        void InitializeSignalHandler() {
+            std::signal(SIGABRT, HandleSignal);
+            std::signal(SIGSEGV, HandleSignal);
+            std::signal(SIGILL, HandleSignal);
+            std::signal(SIGFPE, HandleSignal);
+            std::signal(SIGHUP, HandleSignal);
+            std::signal(SIGBUS, HandleSignal);
+        }
+#elif defined(EA_PLATFORM_LINUX)
+        int SignalToExitCode(int signal) {
+            switch(signal)
+            {
+            case SIGABRT:
+                return ExitCode_SignalAbort;
+            case SIGSEGV:
+                return ExitCode_SignalSegmentationViolation;
+            case SIGILL:
+                return ExitCode_SignalIllegalInstruction;
+            case SIGHUP:
+                return ExitCode_SignalHangup;
+            case SIGFPE:
+                return ExitCode_SignalFloatingPointException;
+            case SIGBUS:
+                return ExitCode_SignalBusError;
+            default:
+                return ExitCode_Unknown;
+            };
+        }
+
+        struct sigaction ABRTAction;
+        struct sigaction SEGVAction;
+        struct sigaction SIGILLAction;
+        struct sigaction SIGHUPAction;
+        struct sigaction SIGFPEAction;
+        struct sigaction SIGBUSAction;
+
+        void HandleSignal(int signal, siginfo_t *sigInfo, void *context) {
+            EA::EAMain::Exit(SignalToExitCode(signal));
+        }
+
+        void InitializeSignalHandler() {
+            ABRTAction.sa_sigaction = HandleSignal;
+            ABRTAction.sa_flags = SA_SIGINFO;
+            SEGVAction.sa_sigaction = HandleSignal;
+            SEGVAction.sa_flags = SA_SIGINFO;
+            SIGILLAction.sa_sigaction = HandleSignal;
+            SIGILLAction.sa_flags = SA_SIGINFO;
+            SIGHUPAction.sa_sigaction = HandleSignal;
+            SIGHUPAction.sa_flags = SA_SIGINFO;
+            SIGFPEAction.sa_sigaction = HandleSignal;
+            SIGFPEAction.sa_flags = SA_SIGINFO;
+            SIGBUSAction.sa_sigaction = HandleSignal;
+            SIGBUSAction.sa_flags = SA_SIGINFO;
+
+            sigaction(SIGABRT, &ABRTAction, NULL);
+            sigaction(SIGSEGV, &SEGVAction, NULL);
+            sigaction(SIGILL, &SIGILLAction, NULL);
+            sigaction(SIGHUP, &SIGHUPAction, NULL);
+            sigaction(SIGFPE, &SIGFPEAction, NULL);
+            sigaction(SIGBUS, &SIGBUSAction, NULL);
+        }
+#endif
+    }
+}

+ 25 - 0
source/EAMainPrint.cpp

@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// EAMainPrintf.h
+//
+// Copyright (c) 2012 Electronic Arts Inc.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EAMainPrintf.h>
+#include <EAMain/internal/EAMainPrintManager.h>
+#include <EAStdC/EASprintf.h>
+
+namespace EA
+{
+namespace EAMain
+{
+    class IChannel;
+
+namespace Messages
+{
+    void Print(const char* pData)
+    {
+        PrintManager::Instance().Send(pData);
+    }
+
+}}}  // namespaces

+ 23 - 0
source/EAMainPrintf.h

@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// EAMainPrintf.h
+//
+// Copyright (c) 2012 Electronic Arts Inc.
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef EAMAIN_PRINTF_H
+#define EAMAIN_PRINTF_H
+
+#include <EAMain/internal/Version.h>
+
+namespace EA
+{
+namespace EAMain
+{
+    class IChannel;
+
+namespace Messages
+{
+    EAMAIN_API void Print(const char* pData);
+}}}
+
+#endif  // header include guard

+ 59 - 0
source/internal/EAMainChannels.cpp

@@ -0,0 +1,59 @@
+///////////////////////////////////////////////////////////////////////////////
+// EAMainPrintManager.cpp
+//
+// Copyright (c) 2012 Electronic Arts Inc.
+///////////////////////////////////////////////////////////////////////////////
+
+#include "EAMain/EAMain.h"
+#include "EABase/eabase.h"
+#include "EAAssert/eaassert.h"
+#include <EAMain/internal/EAMainPrintManager.h>
+#include <EAMain/internal/EAMainChannels.h>
+
+#include <EABase/eabase.h>
+
+EA_DISABLE_ALL_VC_WARNINGS()
+
+#include <stdio.h>
+#include <string.h>
+
+EA_RESTORE_ALL_VC_WARNINGS()
+
+namespace EA {
+namespace EAMain {
+
+//------------------------------------------------------------
+// Printf Channel
+//------------------------------------------------------------
+void PrintfChannel::Send(const char8_t* pData)
+{
+    // Route to default print function
+    EA::EAMain::GetDefaultReportFunction()(pData);
+}
+
+//------------------------------------------------------------
+// File Channel
+//------------------------------------------------------------
+void FileChannel::Init()
+{
+    mFileHandle = fopen("eamain_output.txt", "w");
+    EA_ASSERT_MSG(mFileHandle, "invalid file handle");
+}
+
+//------------------------------------------------------------
+void FileChannel::Send(const char8_t* pData)
+{
+    EA_ASSERT_MSG(mFileHandle, "invalid file handle");
+    fputs(pData, mFileHandle);
+}
+
+//------------------------------------------------------------
+void FileChannel::Shutdown()
+{
+    EA_ASSERT_MSG(mFileHandle, "invalid file handle");
+    fclose(mFileHandle);
+}
+
+//------------------------------------------------------------
+
+}}

+ 156 - 0
source/internal/EAMainPrintManager.cpp

@@ -0,0 +1,156 @@
+///////////////////////////////////////////////////////////////////////////////
+// EAMainPrintManager.cpp
+//
+// Copyright (c) 2012 Electronic Arts Inc.
+///////////////////////////////////////////////////////////////////////////////
+
+#include <EAMain/EAMain.h>
+#include <EAMain/internal/EAMainPrintManager.h>
+#include <EAMain/internal/EAMainChannels.h>
+#include <internal/NetworkChannel.h>
+#include <EAAssert/eaassert.h>
+#include <EAStdC/EAString.h>
+#include <EAMainPrintf.h>
+
+#include <EABase/eabase.h>
+
+EA_DISABLE_ALL_VC_WARNINGS()
+
+#include <string.h>
+
+EA_RESTORE_ALL_VC_WARNINGS()
+
+namespace EA {
+namespace EAMain {
+
+//------------------------------------------------------------
+// STATICS
+//------------------------------------------------------------
+static PrintManager gPrintManager;
+static PrintfChannel gPrintfChannel;
+static FileChannel gFileChannel;
+//------------------------------------------------------------
+
+
+//------------------------------------------------------------
+PrintManager& PrintManager::Instance()
+{
+    return gPrintManager;
+}
+
+//------------------------------------------------------------
+PrintManager::PrintManager()
+{
+    memset(m_Channels, 0, sizeof(m_Channels));
+}
+
+//------------------------------------------------------------
+void PrintManager::Send(const char8_t* pData)
+{
+    // Broadcast the message to all the registered channels.
+    for(int i = 0; i < CHANNEL_MAX; i++)
+    {
+        if(m_Channels[i])
+            m_Channels[i]->Send(pData);
+    }
+}
+
+//------------------------------------------------------------
+void PrintManager::Add(EAMainChannel channel, IChannel* instance)
+{
+    EA_ASSERT_MSG(instance, "invalid channel instance");
+    EA_ASSERT_MSG(m_Channels[channel] == NULL, "channel already added to the list");
+
+    if(instance != NULL && m_Channels[channel] == NULL)
+    {
+        // Initialize the channel, then add it to the channel vector.
+        instance->Init();
+
+        // Add the channel to the array
+        m_Channels[channel] = instance;
+    }
+}
+
+//------------------------------------------------------------
+void PrintManager::ClearChannel(EAMainChannel channel)
+{
+    if (m_Channels[channel] != NULL)
+    {
+        IChannel* instance = m_Channels[channel];
+
+        // Shut down the channel.
+        instance->Shutdown();
+
+        // Remove the channel from the array.
+        m_Channels[channel] = NULL;
+    }
+}
+//------------------------------------------------------------
+void PrintManager::Remove(EAMainChannel channel, IChannel* instance)
+{
+    EA_ASSERT_MSG(instance, "invalid channel instance");
+    EA_ASSERT_MSG(m_Channels[channel] != NULL, "channel not added to list yet");
+
+    if(instance != NULL && m_Channels[channel] != NULL)
+    {
+        // Shut down the channel.
+        instance->Shutdown();
+
+        // Remove the channel from the array.
+        m_Channels[channel] = NULL;
+    }
+}
+
+//------------------------------------------------------------
+void PrintManager::Startup(const char8_t* printServerAddress)
+{
+    // Register PrintManager print function with the reporting module.
+    //
+#if EAMAIN_DISABLE_DEFAULT_NETWORK_CHANNEL
+    if (!printServerAddress)
+    {
+        Add(CHANNEL_PRINTF, &gPrintfChannel);
+        return;
+    }
+#endif
+    EA::EAMain::SetReportFunction(EA::EAMain::Messages::Print);
+    if (!printServerAddress)
+    {
+        printServerAddress = MACRO_TO_STRING(EAMAIN_NETWORK_CHANNEL_IP);
+    }
+    const char8_t* server = printServerAddress;
+    const char8_t* portStr = strchr(printServerAddress, ':');
+
+    int port = EAMAIN_NETWORK_CHANNEL_PORT;
+    char8_t serverBuff[64] = { 0 };
+    if (portStr != NULL)
+    {
+        port = EA::StdC::AtoI32(portStr + 1);
+        server = EA::StdC::Strncpy(serverBuff, server, portStr - server);
+    }
+
+    IChannel *networkChannel = Internal::CreateNetworkChannel(server, port);
+    if (networkChannel)
+    {
+        Add(CHANNEL_NETWORK, networkChannel);
+    }
+    else
+    {
+        Add(CHANNEL_PRINTF, &gPrintfChannel);
+    }
+}
+
+//------------------------------------------------------------
+void PrintManager::Shutdown()
+{
+    for(int i = 0; i < CHANNEL_MAX; i++)
+    {
+        if(m_Channels[i])
+        {
+            m_Channels[i]->Shutdown();
+            m_Channels[i] = NULL;
+        }
+    }
+}
+
+}}

+ 168 - 0
source/internal/EAMainStartupShutdown.cpp

@@ -0,0 +1,168 @@
+///////////////////////////////////////////////////////////////////////////////
+// EAMainStartupShutdown.cpp
+//
+// Copyright (c) 2012 Electronic Arts Inc.
+///////////////////////////////////////////////////////////////////////////////
+
+#include <EAMain/internal/EAMainStartupShutdown.h>
+#include <EAMain/EAMain.h>
+#include <EAMain/EAMainExit.h>
+
+
+#ifdef _MSC_VER
+    #pragma warning(push, 0)        // Microsoft headers generate warnings at our higher warning levels.
+    #pragma warning(disable: 4702)  // Unreachable code detected.
+#endif
+
+#if defined(EA_PLATFORM_MICROSOFT)
+    #if defined(EA_PLATFORM_WINDOWS_PHONE) || defined(EA_PLATFORM_WINRT)
+        #define EAMAIN_HAVE_UNHANDLED_EXCEPTION_FILTER 0
+    #else
+        #if !defined(WIN32_LEAN_AND_MEAN)
+            #define WIN32_LEAN_AND_MEAN
+        #endif
+        #if defined(EA_PLATFORM_CAPILANO)
+            #include <xdk.h>
+        #endif
+        EA_DISABLE_ALL_VC_WARNINGS();
+        #include <Windows.h>
+        #include <DbgHelp.h>
+        EA_RESTORE_ALL_VC_WARNINGS();
+        #include <EAStdC/EASprintf.h>
+        #define EAMAIN_HAVE_UNHANDLED_EXCEPTION_FILTER 1
+    #endif
+#endif
+
+#if !defined(EAMAIN_HAVE_UNHANDLED_EXCEPTION_FILTER)
+    #define EAMAIN_HAVE_UNHANDLED_EXCEPTION_FILTER 0
+#endif
+
+namespace EA
+{
+    namespace EAMain
+    {
+        namespace Internal
+        {
+            #if EAMAIN_HAVE_UNHANDLED_EXCEPTION_FILTER
+                static LONG WINAPI EAMainUnhandledExceptionFilter(LPEXCEPTION_POINTERS exception)
+                {
+                    EA::EAMain::Report("\n");
+                    EA::EAMain::Report("===============================================================================\n");
+                    EA::EAMain::Report("ATTENTION ATTENTION ATTENTION ATTENTION ATTENTION ATTENTION ATTENTION ATTENTION\n");
+                    EA::EAMain::Report("An unhandled exception has been detected. This likely means the application is \n");
+                    EA::EAMain::Report("crashing.\n\n");
+                    EA::EAMain::Report("(This message is courtesy of EAMain but does not mean that EAMain is\n");
+                    EA::EAMain::Report("the cause of the crash.)\n");
+                    EA::EAMain::Report("===============================================================================\n");
+
+                    #if EAMAIN_MINIDUMP_SUPPORTED
+                        char8_t szPath[MAX_PATH];
+                        char8_t szFileName[MAX_PATH];
+                        HANDLE hDumpFile;
+                        SYSTEMTIME stLocalTime;
+                        MINIDUMP_EXCEPTION_INFORMATION ExpParam;
+                        BOOL bMiniDumpSuccessful;
+
+                        #if defined(EA_PLATFORM_CAPILANO)
+                            const char8_t* pszDrive = "G:\\";
+                        #else
+                            const char8_t* pszDrive = "C:\\";
+                        #endif
+
+                        GetLocalTime( &stLocalTime );
+                        EA::StdC::Snprintf(szPath, EAArrayCount(szPath), "%sMiniDumps\\", pszDrive);
+                        CreateDirectoryA(szPath, NULL );
+
+                        EA::StdC::Snprintf(szFileName, EAArrayCount(szFileName), "%sMiniDump-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",
+                                    szPath,
+                                    stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
+                                    stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
+                                    GetCurrentProcessId(), GetCurrentThreadId());
+
+                        EA::EAMain::Report("Creating Dump File: %s - ", szFileName);
+                        hDumpFile = CreateFileA(szFileName, GENERIC_READ|GENERIC_WRITE,
+                                    FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
+                        EA::EAMain::Report("%s (Error: %d) \n", hDumpFile != INVALID_HANDLE_VALUE ? "Success" : "Failure", hDumpFile != INVALID_HANDLE_VALUE ? 0 : HRESULT_FROM_WIN32(GetLastError()));
+
+                        ExpParam.ThreadId = GetCurrentThreadId();
+                        ExpParam.ExceptionPointers = exception;
+                        ExpParam.ClientPointers = TRUE;
+                        bMiniDumpSuccessful = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
+                                        hDumpFile, MiniDumpWithDataSegs, &ExpParam, NULL, NULL);
+
+                        EA::EAMain::Report("Dump %s (Error: %d)\n", bMiniDumpSuccessful == TRUE ? "Successful" : "Failed - so deleted minidump file", bMiniDumpSuccessful == TRUE ? 0 : HRESULT_FROM_WIN32(GetLastError()));
+                        if (bMiniDumpSuccessful == FALSE)
+                        {
+                            DeleteFileA(szFileName);
+                        }
+
+                    #endif
+
+                    EAMainShutdown(1);
+
+                    return EXCEPTION_CONTINUE_SEARCH;
+                }
+            #endif
+
+            void EAMainStartup(const char8_t* printServerAddress)
+            {
+                static bool sEAMainShutdown_StartupHandled = false;
+                if(!sEAMainShutdown_StartupHandled)
+                {
+                    sEAMainShutdown_StartupHandled = true;
+
+                    #if EAMAIN_HAVE_UNHANDLED_EXCEPTION_FILTER
+                        SetUnhandledExceptionFilter(EAMainUnhandledExceptionFilter);
+                    #endif
+
+                    // Running under NAnt output only appears when the buffer is filled if we allow the default buffering scheme for printf.
+                    setvbuf(stdout, NULL, _IONBF, BUFSIZ);
+                    setvbuf(stderr, NULL, _IONBF, BUFSIZ);
+
+                    // Startup the print manager
+                    //
+                    EA::EAMain::PrintManager::Instance().Startup(printServerAddress);
+                }
+            }
+
+            int EAMainShutdown(int errorCount)
+            {
+               static bool sEAMainShutdown_ShutdownHandled = false;
+               if(!sEAMainShutdown_ShutdownHandled)
+               {
+                    sEAMainShutdown_ShutdownHandled = true;
+
+                    // Handle the application specific exit code.
+                    //
+                    #if defined(EA_PLATFORM_IPHONE)
+                       // Get test result. (iOS 5 bug prevents iPhone Runner from getting this from the exit code)
+                       if (errorCount == 0)
+                           Report("\nAll tests completed successfully.\n");
+                       else
+                           Report("\nTests failed. Total error count: %d\n", errorCount);
+                       fflush(stdout);
+                    #endif
+
+                    #if !defined(EA_PLATFORM_DESKTOP) && !defined(EA_PLATFORM_SERVER)  // TODO:  change define to something related to the StdC library used on the system.
+                        fflush(stdout);
+                    #endif
+
+                    // Required so the EAMainPrintServer can terminate with the correct error code.
+                    //
+                    EA::EAMain::Report("\nRETURNCODE=%d\n", errorCount);
+
+                    // Shutdown the EAMain print manager.
+                    //
+                    EA::EAMain::PrintManager::Instance().Shutdown();
+               }
+
+               return errorCount;
+            }
+
+        }
+    }
+}
+
+#ifdef _MSC_VER
+    #pragma warning(pop)
+#endif

+ 605 - 0
source/internal/NetworkChannel.cpp

@@ -0,0 +1,605 @@
+/////////////////////////////////////////////////////////////////////////////
+// NetworkChannel.cpp
+//
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+/////////////////////////////////////////////////////////////////////////////
+
+#include <EABase/eabase.h>
+#include <EAAssert/eaassert.h>
+#include <EAMain/internal/EAMainChannels.h>
+
+#if defined(EA_PLATFORM_IPHONE)
+    #define EAMAIN_HAS_NETWORK_CHANNEL 1
+    #define EAMAIN_FREOPEN_SUPPORTED 1
+#endif
+
+#if defined(EA_PLATFORM_WINRT)
+    #define EAMAIN_HAS_NETWORK_CHANNEL 1
+    #define EAMAIN_FREOPEN_SUPPORTED 0
+#endif
+
+#if defined(EA_PLATFORM_WINDOWS_PHONE) && !defined(EAMAIN_HAS_NETWORK_CHANNEL)
+    #define EAMAIN_HAS_NETWORK_CHANNEL 1
+    #define EAMAIN_FREOPEN_SUPPORTED 0
+#endif
+
+// winrt-arm configurations do not have winsock, so for these configurations
+// we disable the use of the network channel.
+#if defined(_MSC_VER) && !defined(EA_PLATFORM_WINDOWS_PHONE) && defined(EA_PROCESSOR_ARM) && defined(EAMAIN_HAS_NETWORK_CHANNEL)
+    #undef EAMAIN_HAS_NETWORK_CHANNEL
+#endif
+
+#if defined(EA_PLATFORM_CAPILANO) && !defined(EAMAIN_HAS_NETWORK_CHANNEL)
+    #define EAMAIN_HAS_NETWORK_CHANNEL 1
+    #define EAMAIN_FREOPEN_SUPPORTED 1
+#endif
+
+#if !defined(EAMAIN_HAS_NETWORK_CHANNEL)
+    #define EAMAIN_HAS_NETWORK_CHANNEL 0
+#endif
+
+#if !defined(EAMAIN_FREOPEN_SUPPORTED)
+    #define EAMAIN_FREOPEN_SUPPORTED 0
+#endif
+
+#if EAMAIN_HAS_NETWORK_CHANNEL
+
+#if defined(_MSC_VER)
+    EA_DISABLE_ALL_VC_WARNINGS()
+
+    #if defined(WINAPI_FAMILY)
+        #undef WINAPI_FAMILY
+    #endif
+
+    #define WINAPI_FAMILY WINAPI_FAMILY_DESKTOP_APP
+
+    #include <WinSock2.h>
+    #include <ws2tcpip.h>
+
+    #pragma comment(lib, "Ws2_32.lib")
+
+    #if defined(EA_PLATFORM_WINDOWS_PHONE)
+        #pragma warning(disable:4265)
+        #include <thread>
+    #else
+        // Restoring VC warnings on Windows Phone causes some warnings to pop
+        // up down below, emanating from std::thread. In order to have some
+        // coverage of this code, warnings are re-enabled on other platforms.
+        EA_RESTORE_ALL_VC_WARNINGS()
+    #endif
+
+    #define SocketGetLastError() WSAGetLastError()
+    #define snprintf _snprintf
+#else
+    #include <sys/types.h>
+    #include <sys/socket.h>
+    #include <errno.h>
+#if !defined(EA_PLATFORM_SONY)
+    #include <netdb.h>
+#else
+    #include <net.h>
+#endif
+    #include <string.h>
+    #include <unistd.h>
+    typedef int SOCKET;
+    const int INVALID_SOCKET = -1;
+    const int SOCKET_ERROR = -1;
+    #define SD_SEND SHUT_WR
+    #define closesocket close
+    #define WSAECONNREFUSED ECONNREFUSED
+    #define WSAENETUNREACH ENETUNREACH
+    #define SocketGetLastError() errno
+#endif
+
+#include <stdio.h>
+#include "eathread/eathread_thread.h"
+#include "EAStdC/EAMemory.h"
+#include "EAStdC/EAString.h"
+#include "EAStdC/EASprintf.h"
+
+namespace EA
+{
+    namespace EAMain
+    {
+        namespace Internal
+        {
+            class NetworkChannel : public IChannel
+            {
+                SOCKET m_socket;
+                char m_serverAddressStorage[128];
+                char m_port[6];
+                const char *m_serverAddress;
+
+                static void SleepThread(int milliseconds);
+
+            public:
+                NetworkChannel();
+
+                virtual ~NetworkChannel();
+                virtual void Init();
+                virtual void Send(const char8_t *data);
+                virtual void Shutdown();
+
+                void SetServerPort(const char *server, const char *port);
+                bool Connect();
+            };
+
+            static NetworkChannel g_NetworkChannelInstance;
+
+            void NetworkChannel::SleepThread(int milliseconds)
+            {
+            #if defined(_MSC_VER)
+                #if defined(EA_PLATFORM_WINDOWS_PHONE)
+                    std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
+                #else
+                    Sleep(milliseconds);
+                #endif
+            #else
+                usleep(milliseconds * 1000);
+            #endif
+            }
+
+            NetworkChannel::NetworkChannel()
+                : m_socket(INVALID_SOCKET)
+                , m_serverAddress(&m_serverAddressStorage[0])
+            {
+                EA::StdC::Memset8(m_serverAddressStorage, 0, sizeof m_serverAddressStorage);
+                EA::StdC::Memset8(m_port, 0, sizeof m_port);
+
+#if defined(_MSC_VER)
+                WSAData wsaData = {};
+                WSAStartup(MAKEWORD(2, 2), &wsaData);
+#endif
+            }
+
+            static void LogNetworkError(const char *format, ...)
+            {
+                (void)format;
+            }
+
+            NetworkChannel::~NetworkChannel()
+            {
+#if defined(_MSC_VER)
+                WSACleanup();
+#endif
+            }
+
+            void NetworkChannel::Init()
+            {
+            }
+
+            void NetworkChannel::Send(const char8_t *data)
+            {
+                char *buffer = const_cast<char *>(data);
+                ssize_t bufferLength = static_cast<ssize_t>(strlen(buffer));
+                ssize_t bytesSent = 0;
+                while (bytesSent < bufferLength)
+                {
+                    ssize_t result = send(m_socket, buffer + bytesSent, static_cast<int>(bufferLength - bytesSent), 0);
+
+                    if (result == SOCKET_ERROR)
+                    {
+                        bool reconnected = false;
+                        LogNetworkError("[NetworkChannel::Send] Reconnecting...");
+
+                        for (int i = 0; i < 20; ++i)
+                        {
+                            Shutdown();
+
+                            if (!Connect())
+                            {
+                                LogNetworkError("[NetworkChannel::Send] Send failed: %d", SocketGetLastError());
+                            }
+                            else
+                            {
+                                reconnected = true;
+                                break;
+                            }
+
+                            SleepThread(500);
+                        }
+
+                        if (!reconnected)
+                        {
+                            LogNetworkError("[NetworkChannel::Send] Unable to connect, aborting.");
+                            return;
+                        }
+                    }
+                    else
+                    {
+                        bytesSent += result;
+                    }
+                }
+            }
+
+            void NetworkChannel::Shutdown()
+            {
+                // Closing the socket does not wait for pending data to be sent.
+                // To ensure that all data has been sent, the write end of the
+                // socket must first be shut down. This sends a FIN packet to
+                // the receiver after all data has been sent and acknowledged
+                // by the receiver. This must also be paired with a call to
+                // recv to ensure that all pending readable data has been
+                // read.
+                shutdown(m_socket, SD_SEND);
+
+                char buffer[128];
+
+                for (;;)
+                {
+                    ssize_t rv = recv(m_socket, buffer, (int)sizeof buffer, 0);
+
+                    if (rv <= 0)
+                    {
+                        // We can assume that a graceful shutdown has occurred
+                        // when rv == 0. If rv < 0 an error has occurred, but
+                        // we are not at a point where we can easily recover, so
+                        // this code will just shutdown the socket and exit the
+                        // program if an error happens here.
+                        break;
+                    }
+                }
+
+                closesocket(m_socket);
+                m_socket = INVALID_SOCKET;
+            }
+
+            void NetworkChannel::SetServerPort(const char *server, const char *port)
+            {
+                using namespace EA::StdC;
+
+                // If, somehow, the server name string is longer than the storge
+                // allocated, allocate some space for it on the C-heap. Not ideal,
+                // may actually fail some tests, but the alternative would be
+                // no output from EAMain at all.
+                if (Strlcpy(m_serverAddressStorage, server, sizeof m_serverAddressStorage) > (sizeof m_serverAddressStorage))
+                {
+                    size_t serverNameLength = Strlen(server);
+
+                    char *ptr = static_cast<char *>(calloc(serverNameLength + 1, 1));
+                    Strlcpy(ptr, server, serverNameLength + 1);
+
+                    m_serverAddress = ptr;
+                }
+
+                // Valid port numbers are in the range [1, 65535] so will have
+                // always 5 digits max.
+                EA_ASSERT(sizeof m_port == 6);
+                Strlcpy(m_port, port, sizeof m_port);
+            }
+
+            bool NetworkChannel::Connect()
+            {
+                EA_ASSERT(m_serverAddress != NULL);
+                EA_ASSERT(m_port != NULL);
+
+                bool result = false;
+                SOCKET remoteSocket = INVALID_SOCKET;
+                const int MAX_RETRIES = 250;
+
+#if !defined(EA_PLATFORM_SONY)
+                struct addrinfo hints = {};
+                hints.ai_socktype = SOCK_STREAM;
+                addrinfo *p = NULL;
+
+                if (getaddrinfo(m_serverAddress, m_port, &hints, &p) != 0)
+                {
+                    LogNetworkError("[NetworkChannel::Connect] Cannot connect to %s:%s", m_serverAddress, m_port);
+                    goto ErrorReturn;
+                }
+                for (struct addrinfo *endpoint = p; endpoint != NULL; endpoint = endpoint->ai_next)
+#endif
+
+                {
+#if !defined(EA_PLATFORM_SONY)
+                    remoteSocket = socket(endpoint->ai_family, endpoint->ai_socktype, endpoint->ai_protocol);
+#else
+                    remoteSocket = sceNetSocket("EAMain Socket", SCE_NET_AF_INET, SCE_NET_SOCK_STREAM, 0);
+#endif
+                    if (remoteSocket == INVALID_SOCKET)
+                    {
+                        LogNetworkError("[NetworkChannel::Connect] Cannot create socket");
+                        goto ErrorReturn;
+                    }
+
+#if defined(_MSC_VER)
+                    if (endpoint->ai_family == AF_INET6)
+                    {
+                        DWORD v6only = 0;
+                        setsockopt(remoteSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, sizeof v6only);
+                    }
+#endif
+
+                    for (int i = 0; i < MAX_RETRIES; ++i)
+                    {
+#if !defined(EA_PLATFORM_SONY)
+                        int connectResult = connect(remoteSocket, endpoint->ai_addr, (int)endpoint->ai_addrlen);
+#else
+                        char *p;
+                        SceNetInPort_t port = strtol(m_port, &p, 10);
+
+                        SceNetSockaddrIn sin;
+                        EA::StdC::Memset8(&sin, 0, sizeof sin);
+                        sin.sin_len = sizeof(sin);
+                        sin.sin_family = SCE_NET_AF_INET;
+                        if (sceNetInetPton(SCE_NET_AF_INET, m_serverAddress, &sin.sin_addr) <= 0)
+                        {
+                            LogNetworkError("[NetworkChannel::Connect] Cannot connect to %s:%s", m_serverAddress, m_port);
+                            goto ErrorReturn;
+                        }
+
+                        sin.sin_port = sceNetHtons(port);
+                        sin.sin_vport = sceNetHtons(SCE_NET_ADHOC_PORT);
+                        int connectResult = sceNetConnect(remoteSocket, (SceNetSockaddr *)&sin, sizeof(sin));
+#endif
+                        if (connectResult == 0)
+                        {
+                            result = true;
+                            m_socket = remoteSocket;
+                            goto SuccessReturn;
+                        }
+
+                        switch (SocketGetLastError())
+                        {
+                            case WSAENETUNREACH:
+                                SleepThread(20);
+                                continue;
+                            default:
+                                LogNetworkError("[NetworkChannel::Connect] Cannot connect to socket");
+                                goto ErrorReturn;
+                        }
+                    }
+                }
+
+            ErrorReturn:
+                if (remoteSocket != INVALID_SOCKET)
+                {
+                    LogNetworkError("[NetworkChannel::Connect] FAILED");
+                    closesocket(remoteSocket);
+                }
+
+            SuccessReturn:
+#if !defined(EA_PLATFORM_SONY)
+                if (p)
+                {
+                    freeaddrinfo(p);
+                }
+#endif
+                return result;
+            }
+
+            static IChannel *CreateNetworkChannelImpl(const char *server, int port)
+            {
+                char portString[6];
+
+                if (port > 65536 || port < 0)
+                {
+                    return NULL;
+                }
+
+                snprintf(portString, sizeof portString, "%d", port);
+                portString[5] = 0;
+
+                g_NetworkChannelInstance.SetServerPort(server, portString);
+
+                if (g_NetworkChannelInstance.Connect())
+                {
+                    return &g_NetworkChannelInstance;
+                }
+
+                return NULL;
+            }
+
+#if !EAMAIN_FREOPEN_SUPPORTED
+            IChannel *CreateNetworkChannel(const char *server, int port)
+            {
+                // On platforms where we do not support freopen on standard
+                // IO streams, we create a raw network channel.
+                return CreateNetworkChannelImpl(server, port);
+            }
+#else
+            static EA::Thread::Thread g_PrintThread;
+            static volatile bool g_PrintThreadDone;
+            static FILE *g_RedirectedStdoutHandle;
+            static FILE *g_RedirectedStderrHandle;
+
+            class StdoutWrapperChannel : public IChannel
+            {
+                NetworkChannel &m_channel;
+                bool m_shutdown;
+
+                StdoutWrapperChannel& operator=(const StdoutWrapperChannel&);
+                StdoutWrapperChannel(const StdoutWrapperChannel&);
+
+            public:
+                StdoutWrapperChannel(NetworkChannel &channel)
+                    : m_channel(channel)
+                    , m_shutdown(false)
+                {
+                }
+
+                virtual ~StdoutWrapperChannel();
+
+                virtual void Init()
+                {
+                }
+
+                virtual void Send(const char8_t *data)
+                {
+                    fputs(data, stdout);
+                    fflush(stdout);
+                }
+
+                virtual void Shutdown()
+                {
+                    if (g_RedirectedStdoutHandle)
+                    {
+                        fclose(g_RedirectedStdoutHandle);
+                    }
+
+                    if (g_RedirectedStderrHandle)
+                    {
+                        fclose(g_RedirectedStderrHandle);
+                    }
+
+                    g_PrintThreadDone = true;
+
+                    if (g_PrintThread.GetStatus() == EA::Thread::Thread::kStatusRunning)
+                    {
+                        g_PrintThread.WaitForEnd();
+                    }
+
+                    m_channel.Shutdown();
+                    m_shutdown = true;
+                }
+            };
+
+            StdoutWrapperChannel::~StdoutWrapperChannel()
+            {
+                if (!m_shutdown)
+                {
+                    Shutdown();
+                }
+            }
+
+            static bool ReadFromFile(FILE *file)
+            {
+                static const int BUFFER_SIZE = 1024;
+                static char buffer[BUFFER_SIZE + 1];
+                bool haveRead = false;
+
+                // This is a tortuous way of checking to see if there is data
+                // available but the goal here is to prevent this thread from
+                // blocking if nothing is ready. Blocking could be desirable
+                // if we were only reading from one stream but because this
+                // code attempts to read from both stdout and stderr we do not
+                // want it to block on reading one while the other is receiving
+                // important information.
+
+                // Another possibility would be to use non-blocking IO but this
+                // would require different implementations for different
+                // platforms.
+                long currentPosition = ftell(file);
+                fseek(file, 0, SEEK_END);
+                long endPosition = ftell(file);
+
+                if (endPosition > currentPosition)
+                {
+                    size_t bytesAvailable = static_cast<size_t>(endPosition - currentPosition);
+
+                    fseek(file, currentPosition, SEEK_SET);
+                    while (bytesAvailable > 0)
+                    {
+                        size_t bytesToRead = (bytesAvailable > BUFFER_SIZE) ? BUFFER_SIZE : bytesAvailable;
+
+                        size_t bytesRead = fread(buffer, 1, bytesToRead, file);
+                        buffer[bytesRead] = 0;
+
+                        g_NetworkChannelInstance.Send(buffer);
+
+                        bytesAvailable -= bytesRead;
+                    }
+
+                    haveRead = true;
+                }
+
+                return haveRead;
+            }
+
+            static char g_StdoutLogPath[256];
+            static char g_StderrLogPath[256];
+
+            static intptr_t PrintFunction(void *)
+            {
+                FILE *stdoutLog = fopen(g_StdoutLogPath, "rb");
+                FILE *stderrLog = fopen(g_StderrLogPath, "rb");
+
+                while (!g_PrintThreadDone)
+                {
+                    // It might look neater to combine these file reads into
+                    // the if statement but this was not done because the
+                    // shortcircuting of the left hand condition prevented
+                    // the right hand condition from being evaluated, but
+                    // every iteration should read from both sources.
+                    bool haveReadStdout = ReadFromFile(stdoutLog);
+                    bool haveReadStderr = ReadFromFile(stderrLog);
+
+                    if (!haveReadStdout && !haveReadStderr)
+                    {
+                        EA::Thread::ThreadSleep(50);
+                    }
+                }
+
+                fflush(stdout); ReadFromFile(stdoutLog); fclose(stdoutLog);
+                fflush(stderr); ReadFromFile(stderrLog); fclose(stderrLog);
+
+                return 0;
+            }
+
+            IChannel *CreateNetworkChannel(const char *server, int port)
+            {
+#if defined(EA_PLATFORM_CAPILANO)
+                if (IsDebuggerPresent())
+                {
+                    return NULL;
+                }
+#endif
+
+                EA::Thread::ThreadParameters threadParameters;
+
+                char *STDOUT_LOG_NAME = "stdout.log";
+                const char *STDERR_LOG_NAME = "stderr.log";
+
+                threadParameters.mnPriority = EA::Thread::kThreadPriorityMax;
+
+                #if defined EA_PLATFORM_IPHONE
+                    char temporaryDirectory[256];
+                    confstr(_CS_DARWIN_USER_TEMP_DIR, temporaryDirectory, sizeof temporaryDirectory);
+
+                    EA::StdC::Snprintf(g_StdoutLogPath, sizeof g_StdoutLogPath, "%s/%s", temporaryDirectory, STDOUT_LOG_NAME);
+                    EA::StdC::Snprintf(g_StderrLogPath, sizeof g_StderrLogPath, "%s/%s", temporaryDirectory, STDERR_LOG_NAME);
+                #elif defined EA_PLATFORM_CAPILANO
+                    EA::StdC::Snprintf(g_StdoutLogPath, sizeof g_StdoutLogPath, "T:\\%s", STDOUT_LOG_NAME);
+                    EA::StdC::Snprintf(g_StderrLogPath, sizeof g_StderrLogPath, "T:\\%s", STDERR_LOG_NAME);
+                #else
+                    EA::StdC::Strlcpy(g_StdoutLogPath, STDOUT_LOG_NAME, sizeof g_StdoutLogPath);
+                    EA::StdC::Strlcpy(g_StderrLogPath, STDERR_LOG_NAME, sizeof g_StderrLogPath);
+                #endif
+
+                fprintf(stdout, ""); fflush(stdout);
+                fprintf(stderr, ""); fflush(stderr);
+
+                g_RedirectedStdoutHandle = freopen(g_StdoutLogPath, "wb", stdout);
+                setvbuf(stdout, NULL, _IONBF, 0);
+                g_RedirectedStderrHandle = freopen(g_StderrLogPath, "wb", stderr);
+                setvbuf(stderr, NULL, _IONBF, 0);
+
+                if (CreateNetworkChannelImpl(server, port) != NULL)
+                {
+                    g_PrintThread.Begin(PrintFunction, NULL, &threadParameters);
+                }
+
+                return new StdoutWrapperChannel(g_NetworkChannelInstance);
+            }
+#endif
+        }
+    }
+}
+
+#else
+
+namespace EA
+{
+    namespace EAMain
+    {
+        namespace Internal
+        {
+            IChannel *CreateNetworkChannel(const char *server, int port)
+            {
+                return NULL;
+            }
+        }
+    }
+}
+
+#endif

+ 23 - 0
source/internal/NetworkChannel.h

@@ -0,0 +1,23 @@
+/////////////////////////////////////////////////////////////////////////////
+// NetworkChannel.h
+//
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef EAMAIN_INTERNAL_NETWORKCHANNEL_H
+#define EAMAIN_INTERNAL_NETWORKCHANNEL_H
+
+#include <EAMain/internal/EAMainChannels.h>
+
+namespace EA
+{
+    namespace EAMain
+    {
+        namespace Internal
+        {
+            IChannel *CreateNetworkChannel(const char *connection, int port);
+        }
+    }
+}
+
+#endif

+ 110 - 0
source/internal/kettle/EAMainSubmitDone.cpp

@@ -0,0 +1,110 @@
+///////////////////////////////////////////////////////////////////////////////
+// EAMainPrintf.h
+//
+// Copyright (c) Electronic Arts. All Rights Reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+#include <kernel.h>
+#include <sceerror.h>
+#include <gnm.h>
+#include "EAAssert/eaassert.h"
+
+#define SUBMIT_DEBUG_PRINT(arg)
+
+namespace EA
+{
+namespace EAMain
+{
+namespace Internal
+{
+    static ScePthread gSubmitDoneThread;
+    static SceKernelSema gSubmitDoneSema;
+    volatile static bool gShutdownSubmitDoneThread = false;
+
+    void* SubmitDoneThreadFunction(void *)
+    {
+        int result;
+        EA_UNUSED(result);
+
+        SUBMIT_DEBUG_PRINT("Started submit done thread\n");
+        for(;;)
+        {
+            const int microsecondsPerSecond = 1000000;
+            SceKernelUseconds timeout = 2 * microsecondsPerSecond;
+            result = sceKernelWaitSema(gSubmitDoneSema, 1, &timeout);
+            EA_ASSERT(result == SCE_OK || result == SCE_KERNEL_ERROR_ETIMEDOUT);
+
+            if(gShutdownSubmitDoneThread)
+            {
+                // Break out to avoid calling submitDone
+                break;
+            }
+            else
+            {
+               SUBMIT_DEBUG_PRINT("submitDone\n");
+
+#if SCE_ORBIS_SDK_VERSION >= 0x01000051u && SCE_ORBIS_SDK_VERSION != 0x01000071u
+               // Only perform the submitDone call if we are above SDK version 1.00.051
+               // but not 1.00.071 since the requirement was temporarily removed in that release.
+               // This is not required in previous or later SDK versions.
+               sce::Gnm::submitDone();
+#endif
+            }
+        }
+        SUBMIT_DEBUG_PRINT("Ending submit done thread\n");
+        return nullptr;
+    }
+
+    void StartSubmitDoneThread()
+    {
+        int result;
+        EA_UNUSED(result);
+
+        SUBMIT_DEBUG_PRINT("Starting submit done thread\n");
+
+        result = sceKernelCreateSema(&gSubmitDoneSema, "submit done semaphore", 0, 0, 1, nullptr);
+        EA_ASSERT(result == SCE_OK);
+
+        result  = scePthreadCreate(&gSubmitDoneThread, NULL, SubmitDoneThreadFunction, NULL, "submit done thread");
+        EA_ASSERT(result == SCE_OK);
+    }
+
+    void ShutdownSubmitDoneThread()
+    {
+        if(!gShutdownSubmitDoneThread)
+        {
+            SUBMIT_DEBUG_PRINT("Disabling submit done thread\n");
+            int result;
+            EA_UNUSED(result);
+
+            // Indicate that the submit done thread should exit
+            gShutdownSubmitDoneThread = true;
+
+            // Signal semaphore to unblock the submit done thread
+            result = sceKernelSignalSema(gSubmitDoneSema, 1);
+            EA_ASSERT(result == SCE_OK);
+
+            // Wait for the thread to exit
+            result = scePthreadJoin(gSubmitDoneThread, nullptr);
+            EA_ASSERT(result == SCE_OK);
+
+            // Free up kernel resources
+            result = sceKernelDeleteSema(gSubmitDoneSema);
+            EA_ASSERT(result == SCE_OK);
+        }
+    }
+}
+
+
+void DisableSubmitDoneThread()
+{
+    using namespace EA::EAMain::Internal;
+
+    ShutdownSubmitDoneThread();
+}
+
+
+}
+}
+
+#undef SUBMIT_DEBUG_PRINT

+ 6 - 0
test/source/Crash/CrashHelper.cpp

@@ -0,0 +1,6 @@
+// (c) 2014, Electronic Arts Inc
+
+void CrashHelper(int *p)
+{
+    *p = 0;
+}

+ 44 - 0
test/source/Crash/Main.cpp

@@ -0,0 +1,44 @@
+///////////////////////////////////////////////////////////////////////////////
+// crash.cpp
+//
+// Copyright (c) 2014, Electronic Arts Inc. All rights reserved.
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EABase/eabase.h>
+#include <EAMain/EAMain.h>
+#include <EAMain/EAEntryPointMain.inl>
+#include <string.h>
+
+#ifdef _MSC_VER
+    #pragma warning(push, 0)
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#if defined(EA_COMPILER_MSVC) && defined(EA_PLATFORM_MICROSOFT)
+    #include <crtdbg.h>
+#endif
+
+#ifdef _MSC_VER
+    #pragma warning(pop)
+#endif
+
+void* operator new[](size_t size, const char* /*pName*/, int /*flags*/, unsigned /*debugFlags*/, const char* /*file*/, int /*line*/)
+{
+    return operator new[](size);
+}
+
+void* operator new[](size_t size, size_t /*alignment*/, size_t /*alignmentOffset*/, const char* /*pName*/,
+                        int /*flags*/, unsigned /*debugFlags*/, const char* /*file*/, int /*line*/)
+{
+    return operator new[](size);
+}
+
+void CrashHelper(int *ptr);
+
+int EAMain(int argc, char** argv)
+{
+    CrashHelper(NULL);
+    return 0;
+}

+ 286 - 0
test/source/Main/Main.cpp

@@ -0,0 +1,286 @@
+///////////////////////////////////////////////////////////////////////////////
+// main.cpp
+//
+// Copyright (c) 2003, Electronic Arts Inc. All rights reserved.
+// Created by Paul Pedriana
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EABase/eabase.h>
+#include <EAMain/EAMain.h>
+#include <EAMain/EAMainInitFini.inl>
+#include <EAMain/EAEntryPointMain.inl>
+#include <string.h>
+
+#ifdef _MSC_VER
+    #pragma warning(push, 0)
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#if defined(EA_COMPILER_MSVC) && defined(EA_PLATFORM_MICROSOFT)
+    #include <crtdbg.h>
+#endif
+
+#ifdef _MSC_VER
+    #pragma warning(pop)
+#endif
+
+
+
+void* operator new[](size_t size, const char* /*pName*/, int /*flags*/, unsigned /*debugFlags*/, const char* /*file*/, int /*line*/)
+{
+    return operator new[](size);
+}
+
+void* operator new[](size_t size, size_t /*alignment*/, size_t /*alignmentOffset*/, const char* /*pName*/,
+                        int /*flags*/, unsigned /*debugFlags*/, const char* /*file*/, int /*line*/)
+{
+    return operator new[](size);
+}
+
+static int TestCommandLineArgs()
+{
+    using namespace EA::EAMain;
+
+    #define EAMAIN_TEST(x) if (!(x)) { Report("%s(%d): %s\n", __FILE__, __LINE__, #x); ++nErrorCount; } else (void)0
+    #define EAMAIN_TEST_FATAL(x) if (!(x)) { Report("%s(%d): %s\n", __FILE__, __LINE__, #x); ++nErrorCount; return nErrorCount; } else (void)0
+
+    int nErrorCount = 0;
+
+    // This test is disabled on xenon because xenon has implicit command line
+    // construction if argc is zero.
+    {
+        int argc = 0;
+        char *argv[] = { NULL };
+
+        CommandLine commandLine(argc, argv);
+
+        EAMAIN_TEST(commandLine.Argc() == 0);
+        EAMAIN_TEST(commandLine.Argv() != NULL);
+        EAMAIN_TEST(commandLine.FindSwitch("-x") == -1);
+        EAMAIN_TEST(commandLine.Arg(0) == nullptr);
+        EAMAIN_TEST(commandLine.Argv()[0] == nullptr);
+    }
+
+    {
+        int argc = 1;
+        char arg[] = "program.elf";
+        char *argv[] = { arg };
+
+        CommandLine commandLine(argc, argv);
+
+        EAMAIN_TEST(commandLine.Argc() == 1);
+        EAMAIN_TEST_FATAL(commandLine.Argv() != NULL);
+        EAMAIN_TEST(strcmp(commandLine.Argv()[0], "program.elf") == 0);
+        EAMAIN_TEST(strcmp(commandLine.Arg(0), "program.elf") == 0);
+        EAMAIN_TEST(commandLine.FindSwitch("-x") == -1);
+        EAMAIN_TEST(commandLine.Arg(1) == nullptr);
+        EAMAIN_TEST(commandLine.Argv()[1] == nullptr);
+    }
+
+    {
+        const char *commandLineString = "program.elf";
+
+        CommandLine commandLine(commandLineString);
+
+        EAMAIN_TEST(commandLine.Argc() == 1);
+        EAMAIN_TEST_FATAL(commandLine.Argv() != NULL);
+        EAMAIN_TEST(strcmp(commandLine.Argv()[0], "program.elf") == 0);
+        EAMAIN_TEST(strcmp(commandLine.Arg(0), "program.elf") == 0);
+        EAMAIN_TEST(commandLine.FindSwitch("-x") == -1);
+        EAMAIN_TEST(commandLine.Arg(1) == nullptr);
+        EAMAIN_TEST(commandLine.Argv()[1] == nullptr);
+    }
+
+    {
+        const char *commandLineString = "program.elf arg1 \"arg 2\"";
+
+        CommandLine commandLine(commandLineString);
+
+        EAMAIN_TEST(commandLine.Argc() == 3);
+        EAMAIN_TEST_FATAL(commandLine.Argv() != NULL);
+        EAMAIN_TEST(strcmp(commandLine.Argv()[0], "program.elf") == 0);
+        EAMAIN_TEST(strcmp(commandLine.Arg(0), "program.elf") == 0);
+        EAMAIN_TEST(strcmp(commandLine.Arg(1), "arg1") == 0);
+        EAMAIN_TEST(strcmp(commandLine.Arg(2), "arg 2") == 0);
+        EAMAIN_TEST(commandLine.FindSwitch("-x") == -1);
+        EAMAIN_TEST(commandLine.Arg(3) == nullptr);
+        EAMAIN_TEST(commandLine.Argv()[3] == nullptr);
+    }
+
+    {
+        const char *commandLineString = "program.elf -x -y:1";
+        const char *parameter = NULL;
+
+        CommandLine commandLine(commandLineString);
+
+        EAMAIN_TEST(commandLine.Argc() == 3);
+        EAMAIN_TEST_FATAL(commandLine.Argv() != NULL);
+        EAMAIN_TEST(strcmp(commandLine.Arg(0), "program.elf") == 0);
+        EAMAIN_TEST(strcmp(commandLine.Arg(1), "-x") == 0);
+        EAMAIN_TEST(commandLine.FindSwitch("-x") == 1);
+        EAMAIN_TEST(commandLine.FindSwitch("-y", false, &parameter) == 2);
+        EAMAIN_TEST_FATAL(parameter != NULL);
+        EAMAIN_TEST(strcmp(parameter, "1") == 0);
+        EAMAIN_TEST(commandLine.Arg(3) == nullptr);
+        EAMAIN_TEST(commandLine.Argv()[3] == nullptr);
+    }
+
+    {
+        const char *commandLineString = "program.elf -x \"-switch:this switch parameter has spaces\"";
+        const char *parameter = NULL;
+
+        CommandLine commandLine(commandLineString);
+
+        EAMAIN_TEST(commandLine.Argc() == 3);
+        EAMAIN_TEST_FATAL(commandLine.Argv() != NULL);
+        EAMAIN_TEST(strcmp(commandLine.Arg(0), "program.elf") == 0);
+        EAMAIN_TEST(strcmp(commandLine.Arg(1), "-x") == 0);
+        EAMAIN_TEST(commandLine.FindSwitch("-x") == 1);
+        EAMAIN_TEST(commandLine.FindSwitch("-switch", false, &parameter) == 2);
+        EAMAIN_TEST_FATAL(parameter != NULL);
+        EAMAIN_TEST(strcmp(parameter, "this switch parameter has spaces") == 0);
+        EAMAIN_TEST(commandLine.Arg(3) == nullptr);
+        EAMAIN_TEST(commandLine.Argv()[3] == nullptr);
+    }
+
+    {
+        const char *commandLineString = "program.elf -x -switch:\"this switch parameter has spaces\" \"-switch2:as does this one\"";
+        const char *parameter = NULL;
+
+        CommandLine commandLine(commandLineString);
+
+        EAMAIN_TEST(commandLine.Argc() == 4);
+        EAMAIN_TEST_FATAL(commandLine.Argv() != NULL);
+        EAMAIN_TEST(strcmp(commandLine.Arg(0), "program.elf") == 0);
+        EAMAIN_TEST(strcmp(commandLine.Arg(1), "-x") == 0);
+        EAMAIN_TEST(commandLine.FindSwitch("-x") == 1);
+        EAMAIN_TEST(commandLine.FindSwitch("-switch", false, &parameter) == 2);
+        EAMAIN_TEST_FATAL(parameter != NULL);
+        EAMAIN_TEST(strcmp(parameter, "this switch parameter has spaces") == 0);
+        EAMAIN_TEST(commandLine.Arg(4) == nullptr);
+        EAMAIN_TEST(commandLine.Argv()[4] == nullptr);
+    }
+
+    {
+        const char *commandLineString = "-x -switch:\"this switch parameter has spaces\" \"-switch2:as does this one\"";
+        const char *parameter = NULL;
+
+        CommandLine commandLine(commandLineString, CommandLine::FLAG_NO_PROGRAM_NAME);
+
+        EAMAIN_TEST(commandLine.Argc() == 4);
+        EAMAIN_TEST_FATAL(commandLine.Argv() != NULL);
+        EAMAIN_TEST(strcmp(commandLine.Arg(0), "") == 0);
+        EAMAIN_TEST(strcmp(commandLine.Arg(1), "-x") == 0);
+        EAMAIN_TEST(commandLine.FindSwitch("-x") == 1);
+        EAMAIN_TEST(commandLine.FindSwitch("-switch", false, &parameter) == 2);
+        EAMAIN_TEST_FATAL(parameter != NULL);
+        EAMAIN_TEST(strcmp(parameter, "this switch parameter has spaces") == 0);
+        EAMAIN_TEST(commandLine.Arg(4) == nullptr);
+        EAMAIN_TEST(commandLine.Argv()[4] == nullptr);
+    }
+
+    {
+        int argc = 3;
+        char arg0[] = "program.elf";
+        char arg1[] = "-x";
+        char arg2[] = "-switch:this switch parameter has spaces";
+        char *argv[] = { arg0, arg1, arg2 };
+        const char *parameter;
+
+        CommandLine commandLine(argc, argv);
+
+        EAMAIN_TEST(commandLine.Argc() == 3);
+        EAMAIN_TEST_FATAL(commandLine.Argv() != NULL);
+        EAMAIN_TEST(strcmp(commandLine.Arg(0), "program.elf") == 0);
+        EAMAIN_TEST(strcmp(commandLine.Arg(1), "-x") == 0);
+        EAMAIN_TEST(commandLine.FindSwitch("-x") == 1);
+        EAMAIN_TEST(commandLine.FindSwitch("-switch", false, &parameter) == 2);
+        EAMAIN_TEST_FATAL(parameter != NULL);
+        EAMAIN_TEST(strcmp(parameter, "this switch parameter has spaces") == 0);
+        EAMAIN_TEST(commandLine.Arg(3) == nullptr);
+        EAMAIN_TEST(commandLine.Argv()[3] == nullptr);
+    }
+
+    return nErrorCount;
+
+    #undef EAMAIN_TEST
+    #undef EAMAIN_TEST_FATAL
+}
+
+static bool gEAMainInitCalled;
+
+void EAMainInit()
+{
+    gEAMainInitCalled = true;
+}
+
+void EAMainFini()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// EAMain
+//
+int EAMain(int argc, char** argv)
+{
+    using namespace EA::EAMain;
+
+    int nErrorCount(0);
+
+    Report("List of arguments passed:\n");
+    for (int i = 0; i < argc; ++i)
+    {
+        Report("Arg %d: %s\n", i, argv[i]);
+    }
+    Report("\n");
+
+    // Basic test of redirection of stdout/stderr on platforms that support
+    // redirection of these streams.
+    printf("printf(...)\n");
+    fprintf(stdout, "fprintf(stdout, ...)\n");
+    fflush(stdout);
+
+    fprintf(stderr, "fprintf(stderr, ...)\n");
+    fflush(stderr);
+
+    Report("Test of %s.\n", "Report()");
+    Report("Report()\n");
+
+    bool bArgPassed = false;
+
+    for (int i = 0; i < argc; ++i)
+    {
+        if (strcmp(argv[i], "-testargpassing") == 0)
+        {
+            bArgPassed = true;
+        }
+    }
+
+    // Windows Phone does not support passing arguments to app bundles when
+    // launching them.
+#if !defined(EA_PLATFORM_WINDOWS_PHONE)
+    if (!bArgPassed)
+    {
+        Report("Arg not passed!\n");
+        ++nErrorCount;
+    }
+#endif
+
+    if (TestCommandLineArgs() != 0)
+    {
+        Report("Error parsing command line arguments!\n");
+        ++nErrorCount;
+    }
+
+    if (!gEAMainInitCalled)
+    {
+        Report("EAMainInit was not called!\n");
+        ++nErrorCount;
+    }
+
+    Report("This report statement has \nno terminating newline");
+
+    return nErrorCount;
+}

+ 39 - 0
test/source/NoMain/Main.cpp

@@ -0,0 +1,39 @@
+///////////////////////////////////////////////////////////////////////////////
+// main.cpp
+//
+// Copyright (c) 2003, Electronic Arts Inc. All rights reserved.
+// Created by Paul Pedriana
+///////////////////////////////////////////////////////////////////////////////
+
+
+#include <EABase/eabase.h>
+#include <EAMain/EAMain.h>
+
+EA_DISABLE_ALL_VC_WARNINGS()
+#include <new>
+EA_RESTORE_ALL_VC_WARNINGS()
+
+// Array new is a requirement brought in by EAStdC.
+void* operator new[](size_t size, const char* /*pName*/, int /*flags*/, unsigned /*debugFlags*/, const char* /*file*/, int /*line*/)
+{
+    return ::operator new[](size);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// EAMain
+//
+#if (defined(EA_PLATFORM_MICROSOFT) && !defined(CS_UNDEFINED_STRING) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP))
+[Platform::MTAThread]
+int main(Platform::Array<Platform::String^>^)
+#elif defined(EA_PLATFORM_IPHONE)
+extern "C" int iosMain(int, char **)
+#else
+int main(int, char**)
+#endif
+{
+    using namespace EA::EAMain;
+
+    Report("Test of EAMain without an entry point has succeeded.\n");
+
+    return 0;
+}

+ 143 - 0
test/source/WinRT/Main.cpp

@@ -0,0 +1,143 @@
+///////////////////////////////////////////////////////////////////////////////
+// main.cpp
+//
+// Copyright (c) 2003, Electronic Arts Inc. All rights reserved.
+// Created by Paul Pedriana
+///////////////////////////////////////////////////////////////////////////////
+
+void MyCustomWinRtEntry();
+
+#define EAMAIN_WINRT_APPLICATION_ENTRY MyCustomWinRtEntry
+#include <EAAssert/eaassert.h>
+#include <EABase/eabase.h>
+#include <EAMain/EAMain.h>
+#include <EAMain/EAEntryPointMain.inl>
+#include <string.h>
+
+#ifdef _MSC_VER
+    #pragma warning(push, 0)
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#if defined(EA_COMPILER_MSVC) && defined(EA_PLATFORM_MICROSOFT)
+    #include <crtdbg.h>
+    #include <Windows.h>
+#endif
+
+#ifdef _MSC_VER
+    #pragma warning(pop)
+#endif
+
+#include "eathread/eathread.h"
+
+void* operator new[](size_t size, const char* /*pName*/, int /*flags*/, unsigned /*debugFlags*/, const char* /*file*/, int /*line*/)
+{
+    return operator new[](size);
+}
+
+void* operator new[](size_t size, size_t /*alignment*/, size_t /*alignmentOffset*/, const char* /*pName*/, 
+                        int /*flags*/, unsigned /*debugFlags*/, const char* /*file*/, int /*line*/)
+{
+    return operator new[](size);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// EAMain
+//
+
+ref class TestApplicationView sealed : public Windows::ApplicationModel::Core::IFrameworkView
+{
+public:
+    TestApplicationView()
+        : mCommandLine("")
+    {}
+
+    // IFrameworkView Methods
+    virtual void Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView) 
+    {
+        using namespace Windows::ApplicationModel::Activation;
+        using namespace Windows::ApplicationModel::Core;
+        applicationView->Activated += ref new Windows::Foundation::TypedEventHandler< CoreApplicationView^, IActivatedEventArgs^ >( this, &TestApplicationView::OnActivated );
+    }
+    virtual void SetWindow(Windows::UI::Core::CoreWindow^ window) {}
+    virtual void Load(Platform::String^ entryPoint) {}
+    virtual void Run();
+    virtual void Uninitialize() {}
+    void OnActivated( Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args ) {
+        if (args->Kind == Windows::ApplicationModel::Activation::ActivationKind::Launch) 
+        { 
+            Windows::ApplicationModel::Activation::LaunchActivatedEventArgs ^launchArgs = (Windows::ApplicationModel::Activation::LaunchActivatedEventArgs ^) args;
+            Platform::String ^argumentString = launchArgs->Arguments;
+
+            int bufferSize = WideCharToMultiByte(
+                    CP_UTF8,
+                    0,
+                    argumentString->Data(), 
+                    argumentString->Length(),
+                    NULL,
+                    0,
+                    NULL,
+                    NULL);
+
+            mCommandLine = new char[bufferSize + 1];
+            int rv = WideCharToMultiByte(
+                    CP_UTF8,
+                    0,
+                    argumentString->Data(),
+                    argumentString->Length(),
+                    mCommandLine,
+                    bufferSize + 1,
+                    NULL,
+                    NULL);
+
+            mCommandLine[bufferSize] = 0;
+            EA_ASSERT(rv == bufferSize);
+            EA_UNUSED(rv);
+        } 
+        Windows::UI::Core::CoreWindow::GetForCurrentThread()->Activate();
+    }
+
+private:
+    char *mCommandLine;
+};
+
+void TestApplicationView::Run()
+{
+    using namespace EA::EAMain;
+
+    IWinRTRunner *runner = CreateWinRTRunner();
+    CommandLine commandLine(mCommandLine, CommandLine::FLAG_NO_PROGRAM_NAME);
+
+    runner->Run(commandLine.Argc(), commandLine.Argv());
+
+    while (!runner->IsFinished())
+    {
+        EA::Thread::ThreadSleep(1);
+    }
+
+    Windows::ApplicationModel::Core::CoreApplication::Exit();
+}
+
+ref class TestApplicationViewSource : Windows::ApplicationModel::Core::IFrameworkViewSource
+{
+public:
+    virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView() 
+    {
+        return ref new TestApplicationView(); 
+    }
+};
+
+int CustomWinRtEntryEAMain(int argc, char **argv)
+{
+    EA::EAMain::Report("Success!\n");
+
+    return 0;
+}
+
+void MyCustomWinRtEntry()
+{
+    EA::EAMain::Internal::gEAMainFunction = CustomWinRtEntryEAMain;
+    Windows::ApplicationModel::Core::CoreApplication::Run(ref new TestApplicationViewSource);
+}
+