This commit is contained in:
jeanlemotan
2024-07-02 18:10:39 +02:00
commit 48ab06b1d9
733 changed files with 321088 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
tags
/build/
build.bat
+18
View File
@@ -0,0 +1,18 @@
[submodule "test/packages/EATest"]
path = test/packages/EATest
url = git@github.com:electronicarts/EATest.git
[submodule "test/packages/EAStdC"]
path = test/packages/EAStdC
url = git@github.com:electronicarts/EAStdC.git
[submodule "test/packages/EAAssert"]
path = test/packages/EAAssert
url = git@github.com:electronicarts/EAAssert.git
[submodule "test/packages/EAThread"]
path = test/packages/EAThread
url = git@github.com:electronicarts/EAThread.git
[submodule "test/packages/EASTL"]
path = test/packages/EASTL
url = git@github.com:electronicarts/EASTL.git
[submodule "test/packages/EABase"]
path = test/packages/EABase
url = git@github.com:electronicarts/EABase.git
+71
View File
@@ -0,0 +1,71 @@
language: cpp
cache:
- ccache: true
os:
- linux
- osx
- windows
compiler:
- gcc
- clang
- msvc
env:
- EA_CONFIG=Debug
- EA_CONFIG=Release
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- george-edison55-precise-backports
- llvm-toolchain-trusty-7
packages:
- cmake
- cmake-data
- g++-7
- clang-7
matrix:
exclude:
- os: osx
compiler: gcc
- os: osx
compiler: msvc
- os: linux
compiler: msvc
- os: windows
compiler: clang
- os: windows
compiler: gcc
# Handle git submodules yourself
git:
submodules: false
# Use sed to replace the SSH URL with the public URL, then initialize submodules
before_install:
- sed --version >/dev/null 2>&1 && sed -i 's/git@github.com:/https:\/\/github.com\//' .gitmodules || sed -i "" 's/git@github.com:/https:\/\/github.com\//' .gitmodules
- git submodule update --init
install:
- if [[ "$CXX" == "g++" ]]; then export CC="gcc-7" ;fi
- if [[ "$CXX" == "g++" ]]; then export CXX="g++-7" ;fi
- if [[ "$CXX" == "clang++" && "${TRAVIS_OS_NAME}" != "osx" ]]; then export CC="clang-7" ;fi
- if [[ "$CXX" == "clang++" && "${TRAVIS_OS_NAME}" != "osx" ]]; then export CXX="clang++-7" ;fi
# Universal Setup
before_script:
- mkdir build_$EA_CONFIG
- cd build_$EA_CONFIG
- cmake .. -DEAMAIN_BUILD_TESTS:BOOL=ON
- cmake --build . --config $EA_CONFIG
script:
# Run Tests
- cd $TRAVIS_BUILD_DIR/build_$EA_CONFIG/test
- ctest -C $EA_CONFIG -V || exit 1
+50
View File
@@ -0,0 +1,50 @@
#-------------------------------------------------------------------------------------------
# Copyright (C) Electronic Arts Inc. All rights reserved.
#-------------------------------------------------------------------------------------------
cmake_minimum_required(VERSION 3.1)
project(EAMain CXX)
#-------------------------------------------------------------------------------------------
# Options
#-------------------------------------------------------------------------------------------
option(EAMAIN_BUILD_TESTS "Enable generation of build files for tests" OFF)
#-------------------------------------------------------------------------------------------
# Compiler Flags
#-------------------------------------------------------------------------------------------
set (CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/test/packages/EASTL/scripts/CMake")
include(CommonCppFlags)
#-------------------------------------------------------------------------------------------
# Library definition
#-------------------------------------------------------------------------------------------
file(GLOB EAMAIN_SOURCES "source/*.cpp" "source/internal/*.cpp")
add_library(EAMain ${EAMAIN_SOURCES})
#-------------------------------------------------------------------------------------------
# Package Tests
#-------------------------------------------------------------------------------------------
if(EAMAIN_BUILD_TESTS)
add_subdirectory(test)
endif()
#-------------------------------------------------------------------------------------------
# Defines
#-------------------------------------------------------------------------------------------
add_definitions(-D_CHAR16T)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_definitions(-DEAMAIN_NETWORK_CHANNEL_PORT=8080)
#-------------------------------------------------------------------------------------------
# Export Include Directories
#-------------------------------------------------------------------------------------------
target_include_directories(EAMain PUBLIC include)
target_include_directories(EAMain PUBLIC source)
#-------------------------------------------------------------------------------------------
# Package Dependencies
#-------------------------------------------------------------------------------------------
target_link_libraries(EAMain EABase)
target_link_libraries(EAMain EAAssert)
target_link_libraries(EAMain EAStdC)
+72
View File
@@ -0,0 +1,72 @@
## Contributing
Before you can contribute, EA must have a Contributor License Agreement (CLA) on file that has been signed by each contributor.
You can sign here: [Go to CLA](https://electronicarts.na1.echosign.com/public/esignWidget?wid=CBFCIBAA3AAABLblqZhByHRvZqmltGtliuExmuV-WNzlaJGPhbSRg2ufuPsM3P0QmILZjLpkGslg24-UJtek*)
### Pull Request Policy
All code contributions are submitted as [Github pull requests](https://help.github.com/articles/using-pull-requests/). All pull requests will be reviewed by a maintainer according to the guidelines found in the next section.
Your pull request should:
* merge cleanly
* come with tests
* tests should be minimal and stable
* fail before your fix is applied
* pass the test suite
* code formatting is encoded in clang format
* limit using clang format on new code
* do not deviate from style already established in the files
### Running the Unit Tests
EAAssert uses CMake as its build system.
* Create and navigate to "your_build_folder":
* mkdir your_build_folder && cd your_build_folder
* Generate build scripts:
* cmake source_folder -DEAMAIN_BUILD_TESTS:BOOL=ON
* Build unit tests for "your_config":
* cmake --build . --config your_config
* Run the unit tests for "your_config" from the test folder:
* cd test && ctest -C your_config
Here is an example batch file.
```batch
set build_folder=out
mkdir %build_folder%
pushd %build_folder%
call cmake .. -DEAMAIN_BUILD_TESTS:BOOL=ON
call cmake --build . --config Release
call cmake --build . --config Debug
call cmake --build . --config RelWithDebInfo
call cmake --build . --config MinSizeRel
pushd test
call ctest -C Release
call ctest -C Debug
call ctest -C RelWithDebInfo
call ctest -C MinSizeRel
popd
popd
```
Here is an example bash file
```bash
build_folder=out
mkdir $build_folder
pushd $build_folder
cmake .. -DEAMAIN_BUILD_TESTS:BOOL=ON
cmake --build . --config Release
cmake --build . --config Debug
cmake --build . --config RelWithDebInfo
cmake --build . --config MinSizeRel
pushd test
ctest -C Release
ctest -C Debug
ctest -C RelWithDebInfo
ctest -C MinSizeRel
popd
popd
```
+27
View File
@@ -0,0 +1,27 @@
/*
Copyright (C) 2017 Electronic Arts Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of Electronic Arts, Inc. ("EA") nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY ELECTRONIC ARTS AND ITS CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ELECTRONIC ARTS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+26
View File
@@ -0,0 +1,26 @@
# EAMain
[![Build Status](https://travis-ci.org/electronicarts/EAMain.svg?branch=master)](https://travis-ci.org/electronicarts/EAMain)
EAMain provides a multi-platform entry point used for platforms that don't support console output, return codes and command-line arguments.
## Documentation
Please see [Introduction](doc/EAMain.html).
## Compiling sources
Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on compiling and testing the source.
## Credits
Roberto Parolin is the current EAMain owner within EA and is responsible for the open source repository.
## License
Modified BSD License (3-Clause BSD license) see the file LICENSE in the project root.
+83
View File
@@ -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;
}
@@ -0,0 +1,291 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. 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
@@ -0,0 +1,211 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// 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
@@ -0,0 +1,48 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// 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
@@ -0,0 +1,66 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. 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
@@ -0,0 +1,26 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#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
@@ -0,0 +1,57 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#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
@@ -0,0 +1,47 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#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
@@ -0,0 +1,27 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. 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
@@ -0,0 +1,83 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. 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
@@ -0,0 +1,116 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#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
@@ -0,0 +1,10 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#ifndef EAMAIN_VERSION_H
#define EAMAIN_VERSION_H
#include <EAMain/internal/Version.h>
#endif // EA_MAIN_VERSION_H
@@ -0,0 +1,337 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#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
+749
View File
@@ -0,0 +1,749 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#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
+156
View File
@@ -0,0 +1,156 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#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
}
}
@@ -0,0 +1,23 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#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
@@ -0,0 +1,21 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#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
@@ -0,0 +1,57 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#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);
}
//------------------------------------------------------------
}}
@@ -0,0 +1,154 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#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;
}
}
}
}}
@@ -0,0 +1,166 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#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
@@ -0,0 +1,603 @@
///////////////////////////////////////////////////////////////////////////////
// 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
@@ -0,0 +1,21 @@
///////////////////////////////////////////////////////////////////////////////
// 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
@@ -0,0 +1,108 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. 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
+59
View File
@@ -0,0 +1,59 @@
#-------------------------------------------------------------------------------------------
# Copyright (C) Electronic Arts Inc. All rights reserved.
#-------------------------------------------------------------------------------------------
#-------------------------------------------------------------------------------------------
# CMake info
#-------------------------------------------------------------------------------------------
cmake_minimum_required(VERSION 3.1)
project(EAMainTest CXX)
include(CTest)
#-------------------------------------------------------------------------------------------
# Defines
#-------------------------------------------------------------------------------------------
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_definitions(-D_SCL_SECURE_NO_WARNINGS)
add_definitions(-D_CHAR16T)
#-------------------------------------------------------------------------------------------
# Compiler Flags
#-------------------------------------------------------------------------------------------
set (CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/packages/EASTL/scripts/CMake")
include(CommonCppFlags)
#-------------------------------------------------------------------------------------------
# Source files
#-------------------------------------------------------------------------------------------
file(GLOB EAMAINTEST_SOURCES "source/Main/Main.cpp")
set(SOURCES ${EAMAINTEST_SOURCES})
#-------------------------------------------------------------------------------------------
# Executable definition
#-------------------------------------------------------------------------------------------
add_executable(EAMainTest ${SOURCES})
#-------------------------------------------------------------------------------------------
# Dependencies
#-------------------------------------------------------------------------------------------
add_subdirectory(packages/EAAssert)
add_subdirectory(packages/EABase)
add_subdirectory(packages/EASTL)
add_subdirectory(packages/EAStdC)
add_subdirectory(packages/EATest)
add_subdirectory(packages/EAThread)
target_link_libraries(EAMainTest EABase)
target_link_libraries(EAMainTest EAAssert)
target_link_libraries(EAMainTest EAMain)
target_link_libraries(EAMainTest EASTL)
target_link_libraries(EAMainTest EAStdC)
target_link_libraries(EAMainTest EATest)
target_link_libraries(EAMainTest EAThread)
#-------------------------------------------------------------------------------------------
# Run Unit tests and verify the results.
#-------------------------------------------------------------------------------------------
add_test(EAMainTestRuns EAMainTest -testargpassing)
set_tests_properties (EAMainTestRuns PROPERTIES PASS_REGULAR_EXPRESSION "RETURNCODE=0")
@@ -0,0 +1,8 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
void CrashHelper(int *p)
{
*p = 0;
}
@@ -0,0 +1,41 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 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;
}
@@ -0,0 +1,283 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#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;
}
@@ -0,0 +1,35 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#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;
}
@@ -0,0 +1,140 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
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);
}