diff --git a/binaries/data/config/system.cfg b/binaries/data/config/system.cfg
index 620a3ede24..4b2e653b32 100644
--- a/binaries/data/config/system.cfg
+++ b/binaries/data/config/system.cfg
@@ -1,22 +1,3 @@
-; COMMAND LINE OPTIONS:
-;
-; (note exact spelling of the actual switches, e.g. -nopbuffer, not -NOPBUFFER)
-;
-; -m=MAP load the file
\n";
*m_InterestingLog << html_footer;
- delete m_InterestingLog;
- delete m_MainLog;
+ if (m_OwnsStreams)
+ {
+ delete m_InterestingLog;
+ delete m_MainLog;
+ }
}
void CLogger::WriteMessage(const char *message, int interestedness)
diff --git a/source/ps/CLogger.h b/source/ps/CLogger.h
index 28d36bfe00..06480000a4 100644
--- a/source/ps/CLogger.h
+++ b/source/ps/CLogger.h
@@ -27,19 +27,19 @@ public:
CLogger();
// Special constructor (mostly for testing) - outputs to provided streams.
- // Takes ownership of streams and will delete them in the destructor.
- CLogger(std::ostream* mainLog, std::ostream* interestingLog);
+ // Can take ownership of streams and delete them in the destructor.
+ CLogger(std::ostream* mainLog, std::ostream* interestingLog, bool takeOwnership);
~CLogger();
- //Functions to write different message types
+ // Functions to write different message types
void WriteMessage(const char *message, int interestedness);
void WriteError (const char *message, int interestedness);
void WriteWarning(const char *message, int interestedness);
- //Function to log stuff to file
+ // Function to log stuff to file
void Log(ELogMethod method, const char* category, const char *fmt, ...);
- //Similar to Log, but only outputs each message once no matter how many times it's called
+ // Similar to Log, but only outputs each message once no matter how many times it's called
void LogOnce(ELogMethod method, const char* category, const char *fmt, ...);
private:
@@ -47,11 +47,12 @@ private:
void LogUsingMethod(ELogMethod method, const char* category, const char* message);
- //the output streams
+ // the output streams
std::ostream* m_MainLog;
std::ostream* m_InterestingLog;
+ bool m_OwnsStreams;
- //vars to hold message counts
+ // vars to hold message counts
int m_NumberOfMessages;
int m_NumberOfErrors;
int m_NumberOfWarnings;
diff --git a/source/ps/DllLoader.cpp b/source/ps/DllLoader.cpp
new file mode 100644
index 0000000000..ed8d5ffa8c
--- /dev/null
+++ b/source/ps/DllLoader.cpp
@@ -0,0 +1,87 @@
+#include "precompiled.h"
+
+#include "DllLoader.h"
+
+#include "lib/posix.h"
+#include "lib/lib.h"
+
+void* const HANDLE_UNAVAILABLE = (void*)-1;
+
+
+// TODO Use path_util instead, get the actual path to the ps_dbg exe and append
+// the library name.
+
+// note: on Linux, lib is prepended to the SO file name and we need to add ./
+// to make dlopen look in the current working directory
+#if OS_UNIX
+static const char* prefix = "./lib"
+#else
+static const char* prefix = "";
+#endif
+// since some of our SOs export a C++ interface, it is critical that
+// compiler options are the same between app and SO; therefore,
+// we need to go with the debug version in debug builds.
+// note: on Windows, the extension is replaced with .dll by dlopen.
+#ifndef NDEBUG
+static const char* suffix = "_dbg.so";
+#else
+static const char* suffix = ".so";
+#endif
+
+// (This class is currently only used by 'Collada' and 'AtlasUI' which follow
+// the naming/location convention above - it'll need to be changed if we want
+// to support other DLLs.)
+
+DllLoader::DllLoader(const char* name)
+: m_Name(name), m_Handle(0)
+{
+}
+
+DllLoader::~DllLoader()
+{
+ if (IsLoaded())
+ dlclose(m_Handle);
+}
+
+bool DllLoader::IsLoaded() const
+{
+ return (m_Handle != 0 && m_Handle != HANDLE_UNAVAILABLE);
+}
+
+bool DllLoader::LoadDLL()
+{
+ // first time: try to open the shared object
+ // postcondition: m_Handle valid or == HANDLE_UNAVAILABLE.
+ if (m_Handle == 0)
+ {
+ CStr filename = CStr(prefix) + m_Name + suffix;
+
+ // we don't really care when relocations take place, but one of
+ // {RTLD_NOW, RTLD_LAZY} must be passed. go with the former because
+ // it is safer and matches the Windows load behavior.
+ const int flags = RTLD_LOCAL|RTLD_NOW;
+
+ m_Handle = dlopen(filename, flags);
+
+ // open failed (mostly likely SO not found)
+ if (! m_Handle)
+ m_Handle = HANDLE_UNAVAILABLE;
+ }
+
+ return (m_Handle != HANDLE_UNAVAILABLE);
+}
+
+void DllLoader::LoadSymbolInternal(const char* name, void** fptr) const
+{
+ if (! IsLoaded())
+ {
+ debug_warn("Loading symbol from invalid DLL");
+ *fptr = NULL;
+ throw PSERROR_DllLoader_DllNotLoaded();
+ }
+
+ *fptr = dlsym(m_Handle, name);
+
+ if (*fptr == NULL)
+ throw PSERROR_DllLoader_SymbolNotFound();
+}
diff --git a/source/ps/DllLoader.h b/source/ps/DllLoader.h
new file mode 100644
index 0000000000..6050c19896
--- /dev/null
+++ b/source/ps/DllLoader.h
@@ -0,0 +1,63 @@
+#ifndef DLLLOADER_H__
+#define DLLLOADER_H__
+
+#include "ps/Errors.h"
+
+ERROR_GROUP(DllLoader);
+ERROR_TYPE(DllLoader, DllNotLoaded);
+ERROR_TYPE(DllLoader, SymbolNotFound);
+
+class DllLoader
+{
+public:
+ /**
+ * Prepare the DLL loader. Does no actual work.
+ *
+ * @param name base name of the library (from which we'll derive
+ * "name.dll", "libname_dbg.so", etc). Pointer must remain valid for
+ * this object's lifetime (which is fine if you just use a string literal).
+ */
+ DllLoader(const char* name);
+
+ ~DllLoader();
+
+ /**
+ * Attempt to load and initialise the library, if not already. Can be harmlessly
+ * called multiple times. Returns false if unsuccessful.
+ */
+ bool LoadDLL();
+
+ /**
+ * Check whether the library has been loaded successfully. Returns false
+ * before {@link #LoadDLL} has been called; otherwise returns the same as
+ * LoadDLL did.
+ */
+ bool IsLoaded() const;
+
+ /**
+ * Attempt to load a named symbol from the library. If {@link #IsLoaded} is
+ * false, throws PSERROR_DllLoader_DllNotLoaded. If it cannot load the
+ * symbol, throws PSERROR_DllLoader_SymbolNotFound. In both cases, sets fptr
+ * to NULL. Otherwise, fptr is set to point to the loaded function.
+ *
+ * @throws PSERROR_DllLoader
+ */
+ template
+ void LoadSymbol(const char* name, T& fptr) const;
+
+private:
+ // Typeless version - the public LoadSymbol hides the slightly ugly
+ // casting from users.
+ void LoadSymbolInternal(const char* name, void** fptr) const;
+
+ const char* m_Name;
+ void* m_Handle;
+};
+
+template
+void DllLoader::LoadSymbol(const char* name, T& fptr) const
+{
+ LoadSymbolInternal(name, (void**)&fptr);
+}
+
+#endif // DLLLOADER_H__
diff --git a/source/ps/GameSetup/Atlas.cpp b/source/ps/GameSetup/Atlas.cpp
index 8cebe8c71a..9da794ea52 100644
--- a/source/ps/GameSetup/Atlas.cpp
+++ b/source/ps/GameSetup/Atlas.cpp
@@ -1,70 +1,15 @@
#include "precompiled.h"
-#include "lib/posix.h"
-#include "lib/lib.h"
-#include "ps/GameSetup/CmdLineArgs.h"
#include "Atlas.h"
+#include "ps/GameSetup/CmdLineArgs.h"
+#include "ps/DllLoader.h"
+
//----------------------------------------------------------------------------
// Atlas (map editor) integration
//----------------------------------------------------------------------------
-static void* const ATLAS_SO_UNAVAILABLE = (void*)-1;
-static void* atlas_so_handle = NULL;
-
-// Calculate the correct AtlasUI DLL
-// TODO Use path_util instead, get the actual path to the ps_dbg exe and append
-// the library name.
-
-// note: on Linux, lib is prepended to the SO file name and we need to add ./
-// to make dlopen look in the current working directory
-#if OS_UNIX
-# define PREFIX "./lib"
-#else
-# define PREFIX ""
-#endif
-// since this SO exports a C++ interface, it is critical that
-// compiler options are the same between app and SO; therefore,
-// we need to go with the debug version in debug builds.
-// note: on Windows, the extension is replaced with .dll by dlopen.
-#ifndef NDEBUG
-static const char* so_name = PREFIX "AtlasUI_dbg.so";
-#else
-static const char* so_name = PREFIX "AtlasUI.so";
-#endif
-
-// free reference to Atlas UI SO (avoids resource leak report)
-void ATLAS_Shutdown()
-{
- // (avoid dlclose warnings)
- if(atlas_so_handle != 0 && atlas_so_handle != ATLAS_SO_UNAVAILABLE)
- dlclose(atlas_so_handle);
-}
-
-
-// return true if the Atlas UI shared object is available;
-// used to disable the main menu editor button if not.
-// note: this actually loads the SO, but that isn't expected to be slow.
-// call ATLAS_Shutdown at exit to avoid leaking it.
-static bool ATLAS_IsAvailable()
-{
- // first time: try to open Atlas UI shared object
- // postcondition: atlas_so_handle valid or == ATLAS_SO_UNAVAILABLE.
- if(atlas_so_handle == 0)
- {
- // we don't really care when relocations take place, but one of
- // {RTLD_NOW, RTLD_LAZY} must be passed. go with the former because
- // it is safer and matches the Windows load behavior.
- const int flags = RTLD_LOCAL|RTLD_NOW;
- atlas_so_handle = dlopen(so_name, flags);
- // open failed (mostly likely SO not found)
- if(!atlas_so_handle)
- atlas_so_handle = ATLAS_SO_UNAVAILABLE;
- }
-
- return atlas_so_handle != ATLAS_SO_UNAVAILABLE;
-}
-
+DllLoader atlas_dll("AtlasUI");
enum AtlasRunFlags
{
@@ -78,7 +23,7 @@ enum AtlasRunFlags
static void ATLAS_Run(const CmdLineArgs& args, int flags = 0)
{
// first check if we can run at all
- if(!ATLAS_IsAvailable())
+ if(!atlas_dll.LoadDLL())
{
if(flags & ATLAS_NO_GUI)
DISPLAY_ERROR(L"The Atlas UI was not successfully loaded and therefore cannot be started as requested.");
@@ -88,8 +33,8 @@ static void ATLAS_Run(const CmdLineArgs& args, int flags = 0)
}
// TODO (make nicer)
- extern bool BeginAtlas(const CmdLineArgs& args, void* dll);
- if (!BeginAtlas(args, atlas_so_handle))
+ extern bool BeginAtlas(const CmdLineArgs& args, const DllLoader& dll);
+ if (!BeginAtlas(args, atlas_dll))
{
debug_warn("Atlas loading failed");
return;
@@ -114,13 +59,3 @@ bool ATLAS_RunIfOnCmdLine(const CmdLineArgs& args)
return false;
}
-
-
-
-
-
-
-
-
-
-
diff --git a/source/ps/GameSetup/Config.cpp b/source/ps/GameSetup/Config.cpp
index 6005f60c6f..56df816cef 100644
--- a/source/ps/GameSetup/Config.cpp
+++ b/source/ps/GameSetup/Config.cpp
@@ -135,9 +135,6 @@ static void ProcessCommandLineArgs(const CmdLineArgs& args)
if (args.Has("listfiles"))
trace_enable(true);
- 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");
diff --git a/source/ps/XML/tests/test_Xeromyces.h b/source/ps/XML/tests/test_Xeromyces.h
index 4f70e4c206..4b8c783363 100644
--- a/source/ps/XML/tests/test_Xeromyces.h
+++ b/source/ps/XML/tests/test_Xeromyces.h
@@ -4,29 +4,31 @@
#include "lib/res/file/vfs.h"
#include "lib/res/file/path.h"
#include "lib/res/file/trace.h"
+#include "lib/res/h_mgr.h"
class TestXeromyces : public CxxTest::TestSuite
{
public:
void test_paths()
{
- file_init();
- path_init();
- file_set_root_dir(0, "../data");
+ TS_ASSERT_OK(file_init());
+ TS_ASSERT_OK(file_set_root_dir(0, "../data"));
vfs_init();
- vfs_mount("", "mods/_tests", VFS_MOUNT_RECURSIVE);
- vfs_set_write_target("mods/_tests");
+ TS_ASSERT_OK(vfs_mount("", "mods/_test.xero", VFS_MOUNT_RECURSIVE));
+ TS_ASSERT_OK(vfs_set_write_target("mods/_test.xero"));
char xmbPath[PATH_MAX];
CXeromyces::GetXMBPath("test1.xml", "test1.xmb", xmbPath);
- TS_ASSERT_STR_EQUALS(xmbPath, "cache/mods/_tests/xmb/test1.xmb");
+ TS_ASSERT_STR_EQUALS(xmbPath, "cache/mods/_test.xero/xmb/test1.xmb");
CXeromyces::GetXMBPath("a/b/test1.xml", "a/b/test1.xmb", xmbPath);
- TS_ASSERT_STR_EQUALS(xmbPath, "cache/mods/_tests/xmb/a/b/test1.xmb");
+ TS_ASSERT_STR_EQUALS(xmbPath, "cache/mods/_test.xero/xmb/a/b/test1.xmb");
vfs_shutdown();
+ h_mgr_shutdown();
path_reset_root_dir();
+ TS_ASSERT_OK(file_shutdown());
}
};
diff --git a/source/ps/tests/test_CLogger.h b/source/ps/tests/test_CLogger.h
index 2a4d450188..601dd8f121 100644
--- a/source/ps/tests/test_CLogger.h
+++ b/source/ps/tests/test_CLogger.h
@@ -63,7 +63,7 @@ public:
mainlog = new std::stringstream();
interestinglog = new std::stringstream();
- logger = new CLogger(mainlog, interestinglog);
+ logger = new CLogger(mainlog, interestinglog, true);
lines.clear();
}
diff --git a/source/test_setup.cpp b/source/test_setup.cpp
new file mode 100644
index 0000000000..320a7562b7
--- /dev/null
+++ b/source/test_setup.cpp
@@ -0,0 +1,40 @@
+#include "precompiled.h"
+
+#include
+
+class LeakReporter : public CxxTest::GlobalFixture
+{
+ virtual bool tearDownWorld()
+ {
+ // Enable leak reporting on exit.
+ // (This is done in tearDownWorld so that it doesn't report 'leaks'
+ // if the program is aborted before finishing cleanly.)
+
+#ifdef _MSC_VER
+ int flags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+ flags |= _CRTDBG_LEAK_CHECK_DF; // check for memory leaks
+ flags |= _CRTDBG_ALLOC_MEM_DF; // also check allocs using the non-debug version of new
+ _CrtSetDbgFlag(flags);
+
+ // Send output to stdout as well as the debug window, so it works during
+ // the normal build process as well as when debugging the test .exe
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE|_CRTDBG_MODE_DEBUG);
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
+#endif
+
+ return true;
+ }
+
+ virtual bool setUpWorld()
+ {
+#ifdef _MSC_VER
+ // (Warning: the allocation numbers seem to differ by 3 when you
+ // run in the build process vs the debugger)
+ // _CrtSetBreakAlloc(1952);
+#endif
+ return true;
+ }
+
+};
+
+static LeakReporter leakReporter;
diff --git a/source/tools/atlas/DatafileIO/XMB/XMB.cpp b/source/tools/atlas/DatafileIO/XMB/XMB.cpp
index 00c1aa8142..7eecdc1b68 100644
--- a/source/tools/atlas/DatafileIO/XMB/XMB.cpp
+++ b/source/tools/atlas/DatafileIO/XMB/XMB.cpp
@@ -488,9 +488,12 @@ private:
void complain(const SAXParseException& err, const wchar_t* severity) {
sawErrors = true;
C_ASSERT(sizeof(utf16_t) == sizeof(XMLCh));
- errorText += wtoutf16(L"XML ")+wtoutf16(severity)
- +wtoutf16(L": ")+ (utf16_t*)err.getSystemId()
- +wtoutf16(L" / ") + (utf16_t*)err.getMessage();
+ errorText += wtoutf16(L"XML ");
+ errorText += wtoutf16(severity);
+ errorText += wtoutf16(L": ");
+ errorText += (utf16_t*)err.getSystemId();
+ errorText += wtoutf16(L" / ");
+ errorText += (utf16_t*)err.getMessage();
}
};
diff --git a/source/tools/atlas/GameInterface/GameLoop.cpp b/source/tools/atlas/GameInterface/GameLoop.cpp
index aeb1bf6408..13ff60f387 100644
--- a/source/tools/atlas/GameInterface/GameLoop.cpp
+++ b/source/tools/atlas/GameInterface/GameLoop.cpp
@@ -16,6 +16,7 @@
#include "lib/timer.h"
#include "lib/res/file/vfs.h"
#include "ps/CLogger.h"
+#include "ps/DllLoader.h"
using namespace AtlasMessage;
@@ -74,21 +75,25 @@ static ErrorReaction AtlasDisplayError(const wchar_t* text, uint flags)
return ER_CONTINUE;
}
-bool BeginAtlas(const CmdLineArgs& args, void* dll)
+bool BeginAtlas(const CmdLineArgs& args, const DllLoader& dll)
{
// Load required symbols from the DLL
-#define GET(x) *(void**)&x = dlsym(dll, #x); debug_assert(x); if (! x) return false;
- GET(Atlas_StartWindow);
- GET(Atlas_SetMessagePasser);
- GET(Atlas_GLSetCurrent);
- GET(Atlas_GLSwapBuffers);
- GET(Atlas_NotifyEndOfFrame);
- GET(Atlas_DisplayError);
-#undef GET
-#define GET(x) *(void**)&x##Fptr = dlsym(dll, #x); debug_assert(x##Fptr); if (! x##Fptr) return false;
- GET(ShareableMalloc);
- GET(ShareableFree);
-#undef GET
+ try
+ {
+ dll.LoadSymbol("Atlas_StartWindow", Atlas_StartWindow);
+ dll.LoadSymbol("Atlas_SetMessagePasser", Atlas_SetMessagePasser);
+ dll.LoadSymbol("Atlas_GLSetCurrent", Atlas_GLSetCurrent);
+ dll.LoadSymbol("Atlas_GLSwapBuffers", Atlas_GLSwapBuffers);
+ dll.LoadSymbol("Atlas_NotifyEndOfFrame", Atlas_NotifyEndOfFrame);
+ dll.LoadSymbol("Atlas_DisplayError", Atlas_DisplayError);
+ dll.LoadSymbol("ShareableMalloc", ShareableMallocFptr);
+ dll.LoadSymbol("ShareableFree", ShareableFreeFptr);
+ }
+ catch (PSERROR_DllLoader&)
+ {
+ debug_warn("Failed to initialise DLL");
+ return false;
+ }
// Pass our message handler to Atlas
Atlas_SetMessagePasser(&msgPasser);