diff --git a/build/premake/premake.lua b/build/premake/premake.lua index 4998236fa2..2da871aa65 100755 --- a/build/premake/premake.lua +++ b/build/premake/premake.lua @@ -179,7 +179,7 @@ function package_setup_pch(pch_dir, header, source) }) end for i,v in project.configs do - tinsert(package.config[v].defines, { "USING_PCH" }) + tinsert(package.config[v].defines, "USING_PCH") end end end @@ -732,8 +732,8 @@ function setup_tests() package_add_extern_libs(used_extern_libs) if OS == "windows" then -- required for win.cpp's init mechanism - tinsert(package.linkoptions, "/ENTRY:entry") - + tinsert(package.linkoptions, "/ENTRY:entry_noSEH") + -- from "lowlevel" static lib; must be added here to be linked in tinsert(package.files, source_root.."lib/sysdep/win/error_dialog.rc") diff --git a/source/lib/self_test.h b/source/lib/self_test.h index 83949d9fcb..b33ad6527e 100644 --- a/source/lib/self_test.h +++ b/source/lib/self_test.h @@ -190,8 +190,38 @@ extern bool self_test_active; #include #include +// Perform nice printing of CStr, based on std::string +#include "ps/CStr.h" +namespace CxxTest +{ + CXXTEST_TEMPLATE_INSTANTIATION + class ValueTraits : public ValueTraits + { + public: + ValueTraits( const CStr8 &s ) : ValueTraits( s.c_str() ) {} + }; + + CXXTEST_COPY_CONST_TRAITS( CStr8 ); + + CXXTEST_TEMPLATE_INSTANTIATION + class ValueTraits : public ValueTraits + { + public: + ValueTraits( const CStrW &s ) : ValueTraits( s.c_str() ) {} + }; + + CXXTEST_COPY_CONST_TRAITS( CStrW ); +} + #define TS_ASSERT_OK(expr) TS_ASSERT_EQUALS((expr), INFO::OK) #define TS_ASSERT_STR_EQUALS(str1, str2) TS_ASSERT_EQUALS(std::string(str1), std::string(str2)) #define TS_ASSERT_WSTR_EQUALS(str1, str2) TS_ASSERT_EQUALS(std::wstring(str1), std::wstring(str2)) +template +std::vector ts_make_vector(T* start, size_t size_bytes) +{ + return std::vector(start, start+(size_bytes/sizeof(T))); +} +#define TS_ASSERT_VECTOR_EQUALS_ARRAY(vec1, array) TS_ASSERT_EQUALS(vec1, ts_make_vector((array), sizeof(array))) + #endif // #ifndef SELF_TEST_H__ diff --git a/source/lib/sysdep/win/win.cpp b/source/lib/sysdep/win/win.cpp index 0aa8e20814..23a5972cbe 100644 --- a/source/lib/sysdep/win/win.cpp +++ b/source/lib/sysdep/win/win.cpp @@ -365,3 +365,18 @@ int entry() #endif return SEH_wrapped_entry(); } + + +// Alternative entry point, for programs that don't want the SEH handler +// (e.g. unit tests, where it's better to let the debugger handle any errors) +int entry_noSEH() +{ + int ret; + pre_libc_init(); +#ifdef USE_WINMAIN + ret = WinMainCRTStartup(); +#else + ret = mainCRTStartup(); +#endif + return ret; +} diff --git a/source/main.cpp b/source/main.cpp index eea3be632c..b04b619abe 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -23,6 +23,7 @@ that of Atlas depending on commandline parameters. #include "ps/GameSetup/GameSetup.h" #include "ps/GameSetup/Atlas.h" #include "ps/GameSetup/Config.h" +#include "ps/GameSetup/CmdLineArgs.h" #include "ps/Loader.h" #include "ps/CConsole.h" #include "ps/Profile.h" @@ -365,32 +366,35 @@ void kill_mainloop() int main(int argc, char* argv[]) { // If you ever want to catch a particular allocation: - //_CrtSetBreakAlloc(7864); + //_CrtSetBreakAlloc(321); // see discussion at declaration of win_pre_main_init. #if OS_WIN win_pre_main_init(); #endif - ATLAS_RunIfOnCmdLine(argc, argv); + { // scope for args - Init(argc, argv, 0); - MainControllerInit(); + CmdLineArgs args(argc, argv); -debug_filter_add("LOADER"); + bool ran_atlas = ATLAS_RunIfOnCmdLine(args); -//trace_gen_random(5000); -//trace_write_to_file("../logs/trace.txt"); + // Atlas handles the whole init/shutdown/etc sequence by itself, + // so we skip all this and just exit if Atlas was run + if (! ran_atlas) + { + Init(args, 0); + MainControllerInit(); -#if 0 -trace_run("../logs/trace.txt"); -#else - while(!quit) - Frame(); -#endif + debug_filter_add("LOADER"); // TODO: remove this? - Shutdown(0); - MainControllerShutdown(); + while(!quit) + Frame(); + + Shutdown(0); + MainControllerShutdown(); + } + } debug_printf("Shutdown complete, calling exit() now\n"); diff --git a/source/ps/GameSetup/Atlas.cpp b/source/ps/GameSetup/Atlas.cpp index 8e2a4223fe..8cebe8c71a 100644 --- a/source/ps/GameSetup/Atlas.cpp +++ b/source/ps/GameSetup/Atlas.cpp @@ -2,6 +2,7 @@ #include "lib/posix.h" #include "lib/lib.h" +#include "ps/GameSetup/CmdLineArgs.h" #include "Atlas.h" //---------------------------------------------------------------------------- @@ -74,7 +75,7 @@ enum AtlasRunFlags }; // starts the Atlas UI. -static void ATLAS_Run(int argc, char* argv[], int flags = 0) +static void ATLAS_Run(const CmdLineArgs& args, int flags = 0) { // first check if we can run at all if(!ATLAS_IsAvailable()) @@ -87,8 +88,8 @@ static void ATLAS_Run(int argc, char* argv[], int flags = 0) } // TODO (make nicer) - extern bool BeginAtlas(int argc, char* argv[], void* dll); - if (!BeginAtlas(argc, argv, atlas_so_handle)) + extern bool BeginAtlas(const CmdLineArgs& args, void* dll); + if (!BeginAtlas(args, atlas_so_handle)) { debug_warn("Atlas loading failed"); return; @@ -103,21 +104,15 @@ static void ATLAS_Run(int argc, char* argv[], int flags = 0) // notes: // - GUI init still runs, but some GUI setup will be skipped since // ATLAS_IsRunning() will return true. -// - could be merged into CFG_ParseCommandLineArgs, because that appears -// to be called early enough. it's not really worth it because this -// code is quite simple and we thus avoid startup order dependency. -void ATLAS_RunIfOnCmdLine(int argc, char* argv[]) +bool ATLAS_RunIfOnCmdLine(const CmdLineArgs& args) { - for(int i = 1; i < argc; i++) // skip program name argument + if (args.Has("editor")) { - if(!strcmp(argv[i], "-editor")) - { - // don't bother removing this param (unnecessary) - - ATLAS_Run(argc, argv, ATLAS_NO_GUI); - break; - } + ATLAS_Run(args, ATLAS_NO_GUI); + return true; } + + return false; } diff --git a/source/ps/GameSetup/Atlas.h b/source/ps/GameSetup/Atlas.h index 34c7d6d74f..7802917911 100644 --- a/source/ps/GameSetup/Atlas.h +++ b/source/ps/GameSetup/Atlas.h @@ -1,3 +1,5 @@ +class CmdLineArgs; + // free reference to Atlas UI SO (avoids resource leak report) extern void ATLAS_Shutdown(); @@ -5,4 +7,4 @@ extern void ATLAS_Shutdown(); // this is the alternative to starting the main menu and clicking on // the editor button; it is much faster because it's called during early // init and therefore skips GUI setup. -extern void ATLAS_RunIfOnCmdLine(int argc, char* argv[]); +extern bool ATLAS_RunIfOnCmdLine(const CmdLineArgs& args); diff --git a/source/ps/GameSetup/CmdLineArgs.cpp b/source/ps/GameSetup/CmdLineArgs.cpp new file mode 100644 index 0000000000..51abd5f8f6 --- /dev/null +++ b/source/ps/GameSetup/CmdLineArgs.cpp @@ -0,0 +1,74 @@ +#include "precompiled.h" + +#include "CmdLineArgs.h" + +CmdLineArgs::CmdLineArgs(int argc, char* argv[]) +{ + if (argc >= 1) + m_Arg0 = argv[0]; + + for (int i = 1; i < argc; ++i) + { + // Only accept arguments that start with '-' + if (argv[i][0] != '-') + continue; + + CStr name, value; + + // Check for "-arg=value" + const char* eq = strchr(argv[i], '='); + if (eq) + { + name = CStr(argv[i]+1, eq-argv[i]-1); + value = CStr(eq+1); + } + else + { + name = CStr(argv[i]+1); + } + + m_Args.push_back(make_pair(name, value)); + } +} + +template +struct first_equals +{ + T x; + first_equals(const T& x) : x(x) {} + template bool operator()(const S& v) { return v.first == x; } +}; + +bool CmdLineArgs::Has(const char* name) const +{ + return find_if(m_Args.begin(), m_Args.end(), first_equals(name)) != m_Args.end(); +} + +CStr CmdLineArgs::Get(const char* name) const +{ + ArgsT::const_iterator it = find_if(m_Args.begin(), m_Args.end(), first_equals(name)); + if (it != m_Args.end()) + return it->second; + else + return ""; +} + +std::vector CmdLineArgs::GetMultiple(const char* name) const +{ + std::vector values; + ArgsT::const_iterator it = m_Args.begin(); + while (1) + { + it = find_if(it, m_Args.end(), first_equals(name)); + if (it == m_Args.end()) + break; + values.push_back(it->second); + ++it; // start searching from the next one in the next iteration + } + return values; +} + +CStr CmdLineArgs::GetArg0() const +{ + return m_Arg0; +} diff --git a/source/ps/GameSetup/CmdLineArgs.h b/source/ps/GameSetup/CmdLineArgs.h new file mode 100644 index 0000000000..eee873f693 --- /dev/null +++ b/source/ps/GameSetup/CmdLineArgs.h @@ -0,0 +1,52 @@ +#ifndef CMDLINEARGS_H__ +#define CMDLINEARGS_H__ + +#include "ps/CStr.h" + +class CmdLineArgs +{ +public: + CmdLineArgs() {} + + /** + * Parse the command-line options, for future processing. + * All arguments are required to be of the form -name or + * -name=value - anything else is ignored. + * + * @param argc size of argv array + * @param argv array of arguments; argv[0] should be the program's name + */ + CmdLineArgs(int argc, char* argv[]); + + /** + * Test whether the given name was specified, as either -name or + * -name=value + */ + bool Has(const char* name) const; + + /** + * Get the value of the named parameter. If it was not specified, returns + * the empty string. If it was specified multiple times, returns the value + * from the first occurrence. + */ + CStr Get(const char* name) const; + + /** + * Get all the values given to the named parameter. Returns values in the + * same order as they were given in argv. + */ + std::vector GetMultiple(const char* name) const; + + /** + * Get the value of argv[0], which is typically meant to be the name/path of + * the program (but the actual value is up to whoever executed the program). + */ + CStr GetArg0() const; + +private: + typedef std::vector > ArgsT; + ArgsT m_Args; + CStr m_Arg0; +}; + +#endif // CMDLINEARGS_H__ diff --git a/source/ps/GameSetup/Config.cpp b/source/ps/GameSetup/Config.cpp index 433c118585..7aa3cfaf35 100644 --- a/source/ps/GameSetup/Config.cpp +++ b/source/ps/GameSetup/Config.cpp @@ -3,6 +3,7 @@ #include "ps/CLogger.h" #include "ps/ConfigDB.h" #include "ps/CConsole.h" +#include "ps/GameSetup/CmdLineArgs.h" #include "lib/timer.h" #include "lib/res/sound/snd_mgr.h" #include "lib/res/file/trace.h" @@ -89,120 +90,94 @@ static void LoadGlobals() } -static void ParseCommandLineArgs(int argc, char* argv[]) +static void ProcessCommandLineArgs(const CmdLineArgs& args) { - for(int i = 1; i < argc; i++) + // TODO: all these options (and the ones processed elsewhere) should + // be documented somewhere for users. + + if (args.Has("autostart")) + g_AutostartMap = args.Get("autostart"); + + if (args.Has("buildarchive")) { - // this arg isn't an option; skip - if(argv[i][0] != '-') - continue; - - char* name = argv[i]+1; // no leading '-' - - // switch first letter of option name - switch(argv[i][1]) - { - case 'a': - if(strncmp(name, "autostart=", 10) == 0) - g_AutostartMap = argv[i]+11; - break; - case 'b': - if(!strcmp(name, "buildarchive")) - // note: VFS init is sure to have been completed by now - // (since CONFIG_Init reads from file); therefore, - // it is safe to call this from here directly. - vfs_opt_rebuild_main_archive("mods/official/official1.zip", "../logs/trace.txt"); - break; - case 'c': - if(strcmp(name, "conf") == 0) - { - if(argc-i >= 1) // at least one arg left - { - i++; - char* arg = argv[i]; - char* equ = strchr(arg, '='); - if(equ) - { - *equ = 0; - g_ConfigDB.CreateValue(CFG_COMMAND, arg) - ->m_String = (equ+1); - } - } - } - break; - case 'e': - if(strncmp(name, "entgraph", 8) == 0) - g_EntGraph = true; - break; - case 'f': - if(strncmp(name, "fixedframe", 10) == 0) - g_FixedFrameTiming = true; - break; - case 'g': - if(strncmp(name, "g=", 2) == 0) - { - g_Gamma = (float)atof(argv[i] + 3); - if(g_Gamma == 0.0f) - g_Gamma = 1.0f; - } - break; - case 'l': - if(strncmp(name, "listfiles", 9) == 0) - trace_enable(true); - break; - case 'm': - if(strncmp(name, "mod=", 4) == 0) - { - const char* mod_name = name+4; - char path[PATH_MAX]; - snprintf(path, ARRAY_SIZE(path), "mods/%s", mod_name); - path[PATH_MAX-1] = '\0'; - // note: default is 0. we should set this higher in case the - // mod file mtimes are actually older than the official - // version (*could* happen), otherwise the mod might not - // actually override as intended. - // - // HACK: since this is the only place where mods are added, - // we can get away with just setting it to 1. - // otherwise, add a static counter. - uint pri = 1; - (void)vfs_mount("", path, VFS_MOUNT_RECURSIVE|VFS_MOUNT_ARCHIVES|VFS_MOUNT_WATCH, pri); - } - break; - case 'n': - if(strncmp(name, "novbo", 5) == 0) - g_ConfigDB.CreateValue(CFG_COMMAND, "novbo")->m_String="true"; - break; - case 'p': - if(strncmp(name, "profile=", 8) == 0) - g_ConfigDB.CreateValue(CFG_COMMAND, "profile")->m_String = argv[i]+9; - break; - case 'q': - if(strncmp(name, "quickstart", 10) == 0) - g_Quickstart = true; - break; - case 's': - if(strncmp(name, "shadows", 7) == 0) - g_ConfigDB.CreateValue(CFG_COMMAND, "shadows")->m_String="true"; - break; - case 'v': - if(strncmp(name, "vsync", 5) == 0) - g_ConfigDB.CreateValue(CFG_COMMAND, "vsync")->m_String="true"; - break; - case 'x': - if(strncmp(name, "xres=", 5) == 0) - g_ConfigDB.CreateValue(CFG_COMMAND, "xres")->m_String=argv[i]+6; - break; - case 'y': - if(strncmp(name, "yres=", 5) == 0) - g_ConfigDB.CreateValue(CFG_COMMAND, "yres")->m_String=argv[i]+6; - break; - } // switch + // note: VFS init is sure to have been completed by now + // (since CONFIG_Init reads from file); therefore, + // it is safe to call this from here directly. + vfs_opt_rebuild_main_archive("mods/official/official1.zip", "../logs/trace.txt"); } + + // Handle "-conf=key:value" (potentially multiple times) + std::vector conf = args.GetMultiple("conf"); + for (size_t i = 0; i < conf.size(); ++i) + { + CStr name_value = conf[i]; + if (name_value.Find(':') != -1) + { + CStr name = name_value.BeforeFirst(":"); + CStr value = name_value.AfterFirst(":"); + g_ConfigDB.CreateValue(CFG_COMMAND, name)->m_String = value; + } + } + + if (args.Has("entgraph")) + g_EntGraph = true; + + if (args.Has("fixedframe")) + g_FixedFrameTiming = true; + + if (args.Has("g")) + { + g_Gamma = (float)atof(args.Get("g")); + if (g_Gamma == 0.0f) + g_Gamma = 1.0f; + } + + if (args.Has("listfiles")) + trace_enable(true); + + std::vector mods = args.GetMultiple("mod"); + for (size_t i = 0; i < mods.size(); ++i) + { + const char* mod_name = mods[i]; + char path[PATH_MAX]; + snprintf(path, ARRAY_SIZE(path), "mods/%s", mod_name); + path[PATH_MAX-1] = '\0'; + // note: default is 0. we should set this higher in case the + // mod file mtimes are actually older than the official + // version (*could* happen), otherwise the mod might not + // actually override as intended. + // + // HACK: since this is the only place where mods are added, + // we can get away with just setting it to 1. + // otherwise, add a static counter. + uint pri = 1; + (void)vfs_mount("", path, VFS_MOUNT_RECURSIVE|VFS_MOUNT_ARCHIVES|VFS_MOUNT_WATCH, pri); + } + + if (args.Has("novbo")) + g_ConfigDB.CreateValue(CFG_COMMAND, "novbo")->m_String="true"; + + if (args.Has("profile")) + g_ConfigDB.CreateValue(CFG_COMMAND, "profile")->m_String = args.Get("profile"); + + if (args.Has("quickstart")) + g_Quickstart = true; + + if (args.Has("shadows")) + g_ConfigDB.CreateValue(CFG_COMMAND, "shadows")->m_String = "true"; + + if (args.Has("vsync")) + g_ConfigDB.CreateValue(CFG_COMMAND, "vsync")->m_String = "true"; + + if (args.Has("xres")) + g_ConfigDB.CreateValue(CFG_COMMAND, "xres")->m_String = args.Get("xres"); + + if (args.Has("yres")) + g_ConfigDB.CreateValue(CFG_COMMAND, "yres")->m_String = args.Get("yres"); } -void CONFIG_Init(int argc, char* argv[]) +void CONFIG_Init(const CmdLineArgs& args) { TIMER("CONFIG_Init"); MICROLOG(L"init config"); @@ -215,7 +190,7 @@ void CONFIG_Init(int argc, char* argv[]) g_ConfigDB.SetConfigFile(CFG_MOD, true, "config/mod.cfg"); // No point in reloading mod.cfg here - we haven't mounted mods yet - ParseCommandLineArgs(argc, argv); + ProcessCommandLineArgs(args); // Collect information from system.cfg, the profile file, // and any command-line overrides to fill in the globals. diff --git a/source/ps/GameSetup/Config.h b/source/ps/GameSetup/Config.h index 1401270af4..b7b28d044e 100644 --- a/source/ps/GameSetup/Config.h +++ b/source/ps/GameSetup/Config.h @@ -28,5 +28,5 @@ extern CStr g_AutostartMap; extern CStr g_CursorName; - -extern void CONFIG_Init(int argc, char* argv[]); +class CmdLineArgs; +extern void CONFIG_Init(const CmdLineArgs& args); diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index 80ae2e1357..01dbfaa9ef 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -88,9 +88,10 @@ #include "network/Server.h" #include "network/Client.h" -#include "Atlas.h" -#include "GameSetup.h" -#include "Config.h" +#include "ps/GameSetup/Atlas.h" +#include "ps/GameSetup/GameSetup.h" +#include "ps/GameSetup/Config.h" +#include "ps/GameSetup/CmdLineArgs.h" ERROR_GROUP(System); ERROR_TYPE(System, SDLInitFailed); @@ -875,7 +876,7 @@ void Shutdown(uint flags) } -void Init(int argc, char* argv[], uint flags) +void Init(const CmdLineArgs& args, uint flags) { const bool setup_vmode = (flags & INIT_HAVE_VMODE) == 0; @@ -892,11 +893,7 @@ void Init(int argc, char* argv[], uint flags) // and will mess up the error reporting if anything // crashes before the working directory is set. MICROLOG(L"init vfs"); - const char* argv0 = argc? argv[0] : NULL; - // ScEd doesn't have a main(argc, argv), and so it has no argv. In that - // case, just pass NULL to InitVfs, which will work out the current - // directory for itself. - InitVfs(argv0); + InitVfs(args.GetArg0().c_str()); // This must come after VFS init, which sets the current directory // (required for finding our output log files). @@ -938,7 +935,7 @@ void Init(int argc, char* argv[], uint flags) InitScripting(); // before GUI // g_ConfigDB, command line args, globals - CONFIG_Init(argc, argv); + CONFIG_Init(args); // setup_gui must be set after CONFIG_Init, so command-line parameters can disable it const bool setup_gui = ((flags & INIT_NO_GUI) == 0 && g_AutostartMap.Length() == 0); diff --git a/source/ps/GameSetup/GameSetup.h b/source/ps/GameSetup/GameSetup.h index 4bf6e2deb7..5d5a9a1f81 100644 --- a/source/ps/GameSetup/GameSetup.h +++ b/source/ps/GameSetup/GameSetup.h @@ -34,5 +34,6 @@ enum InitFlags INIT_NO_SIM = 4, }; -extern void Init(int argc, char* argv[], uint flags); +class CmdLineArgs; +extern void Init(const CmdLineArgs& args, uint flags); extern void Shutdown(uint flags); diff --git a/source/ps/GameSetup/tests/test_CmdLineArgs.h b/source/ps/GameSetup/tests/test_CmdLineArgs.h new file mode 100644 index 0000000000..5f3a58bc9c --- /dev/null +++ b/source/ps/GameSetup/tests/test_CmdLineArgs.h @@ -0,0 +1,69 @@ +#include "lib/self_test.h" + +#include "ps/GameSetup/CmdLineArgs.h" + +class TestCmdLineArgs : public CxxTest::TestSuite +{ +public: + void test_has() + { + char* argv[] = { "program", "-test2" }; + CmdLineArgs c(ARRAY_SIZE(argv), argv); + TS_ASSERT(!c.Has("test1")); + TS_ASSERT(c.Has("test2")); + TS_ASSERT(!c.Has("test3")); + TS_ASSERT(!c.Has("")); + } + + void test_get() + { + char* argv[] = { "program", "-test1=", "-test2=x", "-test3=-y=y-", "-=z" }; + CmdLineArgs c(ARRAY_SIZE(argv), argv); + TS_ASSERT_STR_EQUALS(c.Get("test0"), ""); + TS_ASSERT_STR_EQUALS(c.Get("test1"), ""); + TS_ASSERT_STR_EQUALS(c.Get("test2"), "x"); + TS_ASSERT_STR_EQUALS(c.Get("test3"), "-y=y-"); + TS_ASSERT_STR_EQUALS(c.Get(""), "z"); + } + + void test_multiple() + { + char* argv[] = { "program", "-test1=one", "-test1=two", "-test2=none", "-test1=three" }; + CmdLineArgs c(ARRAY_SIZE(argv), argv); + + TS_ASSERT_STR_EQUALS(c.Get("test1"), "one"); + TS_ASSERT_STR_EQUALS(c.Get("test2"), "none"); + + std::vector test1 = c.GetMultiple("test1"); + CStr expected1[] = { "one", "two", "three" }; + TS_ASSERT_VECTOR_EQUALS_ARRAY(test1, expected1); + + std::vector test2 = c.GetMultiple("test2"); + CStr expected2[] = { "none" }; + TS_ASSERT_VECTOR_EQUALS_ARRAY(test2, expected2); + + std::vector test3 = c.GetMultiple("test3"); + TS_ASSERT_EQUALS(test3.size(), (size_t)0); + } + + void test_get_invalid() + { + char* argv[] = { "-test1", "-test2", "test3", " -test4" }; + CmdLineArgs c(ARRAY_SIZE(argv), argv); + + TS_ASSERT(!c.Has("test1")); + TS_ASSERT(c.Has("test2")); + TS_ASSERT(!c.Has("test3")); + TS_ASSERT(!c.Has("test4")); + } + + void test_arg0() + { + char* argv[] = { "program" }; + CmdLineArgs c(ARRAY_SIZE(argv), argv); + TS_ASSERT_STR_EQUALS(c.GetArg0(), "program"); + + CmdLineArgs c2(0, NULL); + TS_ASSERT_STR_EQUALS(c2.GetArg0(), ""); + } +}; diff --git a/source/ps/tests/test_test.h b/source/ps/tests/test_test.h index 9c6aaf831b..81c63d4215 100644 --- a/source/ps/tests/test_test.h +++ b/source/ps/tests/test_test.h @@ -1,5 +1,7 @@ #include "lib/self_test.h" +#include "ps/CStr.h" + class TestTest : public CxxTest::TestSuite { public: @@ -27,4 +29,13 @@ public: TS_ASSERT_STR_EQUALS(TS_AS_STRING((ssize_t)0), "0"); TS_ASSERT_STR_EQUALS(TS_AS_STRING((unsigned int)0), "0"); } + + void test_cstr() + { + TS_ASSERT_STR_EQUALS(TS_AS_STRING(CStr("test")), "\"test\""); + TS_ASSERT_STR_EQUALS(TS_AS_STRING(std::string("test")), "\"test\""); + + TS_ASSERT_STR_EQUALS(TS_AS_STRING(CStrW(L"test")), "L\"test\""); + TS_ASSERT_STR_EQUALS(TS_AS_STRING(std::wstring(L"test")), "L\"test\""); + } }; diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp index 0b3b172972..e7160461bf 100644 --- a/source/renderer/Renderer.cpp +++ b/source/renderer/Renderer.cpp @@ -1437,7 +1437,7 @@ bool CRenderer::LoadTexture(CTexture* texture,u32 wrapflags) { const Handle errorhandle = -1; - Handle h=texture->GetHandle(); + Handle h = texture->GetHandle(); // already tried to load this texture if (h) { @@ -1446,7 +1446,7 @@ bool CRenderer::LoadTexture(CTexture* texture,u32 wrapflags) return h==errorhandle ? true : false; } - h=ogl_tex_load(texture->GetName()); + h = ogl_tex_load(texture->GetName()); if (h <= 0) { LOG(ERROR, LOG_CATEGORY, "LoadTexture failed on \"%s\"",(const char*) texture->GetName()); diff --git a/source/tools/atlas/AtlasUI/ActorEditor/ActorEditor.cpp b/source/tools/atlas/AtlasUI/ActorEditor/ActorEditor.cpp index 711f4916c5..e07cd5e384 100644 --- a/source/tools/atlas/AtlasUI/ActorEditor/ActorEditor.cpp +++ b/source/tools/atlas/AtlasUI/ActorEditor/ActorEditor.cpp @@ -332,7 +332,7 @@ void ActorEditor::OnCreateEntity(wxCommandEvent& WXUNUSED(event)) // The output should be an XML file like: // - // + // // // units/celt_csw_a.xml // @@ -391,7 +391,7 @@ void ActorEditor::OnCreateEntity(wxCommandEvent& WXUNUSED(event)) // Create the XML data to be written // TODO: Native line endings wxString xml = - _T("\r\n") + _T("\r\n") _T("\r\n") _T("\r\n") _T("\t") + actorFilename.GetFullPath(wxPATH_UNIX) + _T("\r\n") diff --git a/source/tools/atlas/GameInterface/GameLoop.cpp b/source/tools/atlas/GameInterface/GameLoop.cpp index f07e5bc25a..aeb1bf6408 100644 --- a/source/tools/atlas/GameInterface/GameLoop.cpp +++ b/source/tools/atlas/GameInterface/GameLoop.cpp @@ -58,15 +58,12 @@ static void* LaunchWindow(void* data) } // Work out which Atlas window to launch, given the command-line arguments -static const wchar_t* FindWindowName(int argc, char* argv[]) +static const wchar_t* FindWindowName(const CmdLineArgs& args) { - for (int i = 1; i < argc; i++) - { - if (strcmp(argv[i], "-actorviewer") == 0) - return L"ActorViewer"; - } - - return L"ScenarioEditor"; + if (args.Has("actorviewer")) + return L"ActorViewer"; + else + return L"ScenarioEditor"; } static ErrorReaction AtlasDisplayError(const wchar_t* text, uint flags) @@ -77,7 +74,7 @@ static ErrorReaction AtlasDisplayError(const wchar_t* text, uint flags) return ER_CONTINUE; } -bool BeginAtlas(int argc, char* argv[], void* dll) +bool BeginAtlas(const CmdLineArgs& args, void* dll) { // Load required symbols from the DLL #define GET(x) *(void**)&x = dlsym(dll, #x); debug_assert(x); if (! x) return false; @@ -100,7 +97,7 @@ bool BeginAtlas(int argc, char* argv[], void* dll) RegisterHandlers(); // Create a new thread, and launch the Atlas window inside that thread - const wchar_t* windowName = FindWindowName(argc, argv); + const wchar_t* windowName = FindWindowName(args); pthread_t uiThread; pthread_create(&uiThread, NULL, LaunchWindow, reinterpret_cast(const_cast(windowName))); @@ -109,8 +106,7 @@ bool BeginAtlas(int argc, char* argv[], void* dll) hooks.display_error = AtlasDisplayError; app_hooks_update(&hooks); - state.argc = argc; - state.argv = argv; + state.args = args; state.running = true; state.view = View::GetView_None(); state.glContext = NULL; @@ -245,5 +241,5 @@ bool BeginAtlas(int argc, char* argv[], void* dll) // Clean up View::DestroyViews(); - exit(0); + return true; } diff --git a/source/tools/atlas/GameInterface/GameLoop.h b/source/tools/atlas/GameInterface/GameLoop.h index 32c5dacb8a..829ee543f0 100644 --- a/source/tools/atlas/GameInterface/GameLoop.h +++ b/source/tools/atlas/GameInterface/GameLoop.h @@ -1,14 +1,15 @@ #ifndef GAMELOOP_H__ #define GAMELOOP_H__ +#include "ps/GameSetup/CmdLineArgs.h" + extern void (*Atlas_GLSetCurrent)(void* context); class View; struct GameLoopState { - int argc; - char** argv; + CmdLineArgs args; bool running; // whether the Atlas game loop is still running View* view; // current 'view' (controls updates, rendering, etc) diff --git a/source/tools/atlas/GameInterface/Handlers/GraphicsSetupHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/GraphicsSetupHandlers.cpp index edb13c8e0b..98652bf677 100644 --- a/source/tools/atlas/GameInterface/Handlers/GraphicsSetupHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/GraphicsSetupHandlers.cpp @@ -34,7 +34,7 @@ MESSAGEHANDLER(Init) if (! msg->initsimulation) flags |= INIT_NO_SIM; - Init(g_GameLoop->argc, g_GameLoop->argv, flags); + Init(g_GameLoop->args, flags); g_DidInitSim = msg->initsimulation; // so we can shut down the right things later