Index: /trunk/nv/core/array2d.hh
===================================================================
--- /trunk/nv/core/array2d.hh	(revision 364)
+++ /trunk/nv/core/array2d.hh	(revision 365)
@@ -320,5 +320,5 @@
 			if ( dest_start_x + min(size_x, source.m_size.x - source_start_x) > m_size.x || dest_start_y + min(size_y, source.m_size.y - source_start_y) > m_size.y )
 			{
-				NV_LOG( LOG_WARNING, "set_sub_array: Source area does not fit in the destination area.  Data will be truncated." );
+				NV_LOG_WARNING( "set_sub_array: Source area does not fit in the destination area.  Data will be truncated." );
 			}
 		
Index: /trunk/nv/core/common.hh
===================================================================
--- /trunk/nv/core/common.hh	(revision 364)
+++ /trunk/nv/core/common.hh	(revision 365)
@@ -130,5 +130,5 @@
 #include <cstdint>
 #include <cassert>
-#include <nv/core/logging.hh>
+//#include <nv/core/logging.hh>
 
 #define NV_STRINGIZE_DETAIL(x) #x
@@ -146,5 +146,5 @@
 #define NV_ASSERT(cond, msg) assert( (cond) && msg )
 #define NV_THROW(eobj, ...) { \
-	NV_LOG( nv::LOG_ERROR, __FILE__ " line " NV_STRINGIZE(__LINE__) " - exception thrown - " #eobj ); \
+	NV_LOG_ERROR( __FILE__ " line " NV_STRINGIZE(__LINE__) " - exception thrown - " #eobj ); \
 	throw eobj( __VA_ARGS__ ); \
 } 
Index: /trunk/nv/core/logger.hh
===================================================================
--- /trunk/nv/core/logger.hh	(revision 364)
+++ /trunk/nv/core/logger.hh	(revision 365)
@@ -16,6 +16,4 @@
 #include <nv/core/string.hh>
 #include <nv/core/logging.hh>
-#include <iosfwd>
-#include <list>
 
 namespace nv
@@ -35,17 +33,17 @@
 		 * @param message message to be logged.
 		 */
-		virtual void log( log_level level, const std::string& message ) = 0;
+		virtual void log( log_level level, const string_ref& message ) = 0;
 		/**
          * Optional timestamp string
 		 */
-		const char* timestamp() const;
+		string_ref timestamp() const;
 		/**
          * Log level name (unpadded)
 		 */
-		const char* level_name( log_level level ) const;
+		string_ref level_name( log_level level ) const;
 		/**
          * Log level name (padded)
 		 */
-		const char* padded_level_name( log_level level ) const;
+		string_ref padded_level_name( log_level level ) const;
 		/**
 		 * Enforcement of virtual destructor.
@@ -86,5 +84,5 @@
 		 * @param message message to be logged.
 		 */
-		virtual void log( log_level level, const std::string& message );
+		virtual void log( log_level level, const string_ref& message );
 
 		/** 
@@ -118,9 +116,13 @@
 
 	protected:
-		/** Type for the log sink list. */
-		typedef std::list< std::pair< log_level, log_sink* > > log_sink_list;
+		struct log_sink_info
+		{
+			log_sink* sink;
+			uint32    level;
+			log_sink_info() : sink( nullptr ), level(0) {}
+		};
 
 		/** Log sink list. */
-		log_sink_list m_log_sinks;
+		log_sink_info m_log_sinks[16];
 	};
 
@@ -140,5 +142,5 @@
 		 * Logging function.
 		 */
-		virtual void log( log_level level, const std::string& message );
+		virtual void log( log_level level, const string_ref& message );
 
 	private:
@@ -148,9 +150,9 @@
 
 	/**
-	 * General stream sink.
-	 *
-	 * Logs to passed stream.
-	 */
-	class log_stream_sink : public log_sink
+	* General handle sink.
+	*
+	* Logs to passed handle.
+	*/
+	class log_handle_sink : public log_sink
 	{
 	public:
@@ -161,18 +163,20 @@
 		 * Flushing can be controlled by optional flush parameter.
 		 *
-		 * @param stream stream to be logged to.
+		 * @param sys_handle system dependent handle to be logged to.
 		 * @param flush_always if set to false, wont flush after each line.
 		 */
-		log_stream_sink( std::ostream* stream, bool flush_always ) 
-			: m_stream(stream), m_flush(flush_always) {}
-
-		/**
-		 * Logging function.
-		 */
-		virtual void log( log_level level, const std::string& message );
+		log_handle_sink( void* sys_handle, bool flush_always )
+			: m_handle( sys_handle ), m_flush( flush_always )
+		{
+		}
+
+		/**
+		 * Logging function.
+		 */
+		virtual void log( log_level level, const string_ref& message );
 
 	protected:
-		/** Stored stream. */
-		std::ostream* m_stream;
+		/** Stored handle. */
+		void* m_handle;
 		/** Controls flushing. */
 		bool m_flush;
@@ -181,28 +185,25 @@
 	/**
 	 * File logger sink.
-	 *
-	 * Logs to std::out -- be sure a console window is open, or std::out redirected!
-	 */
-	class log_file_sink : public log_stream_sink
-	{
-	public:
-		/**
-		 * Constructor.
-		 *
-		 * Logs to the file passed. File is closed and disposed of after ending.
-		 * File is not appended, it's overwritten. If file is not creatable,
-		 * the constructor will throw!
-		 *
-		 * @param file_name file to be logged to.
-		 * @param flush_always if set to false, wont flush after each line.
-		 * @throws runtime_error if file_name cannot be opened.
-		 */
-		log_file_sink( const std::string file_name, bool flush_always = true );
-
-		/**
-		 * Destructor.
-		 *
-		 * Flushes, closes file and disposes of the stream.
-		 */
+	 */
+	class log_file_sink : public log_handle_sink
+	{
+	public:
+		/**
+		* Constructor.
+		*
+		* Logs to the file passed. File is closed and disposed of after ending.
+		* File is not appended, it's overwritten. If file is not createable,
+		* the constructor will throw!
+		*
+		* @param file_name file to be logged to.
+		* @param flush_always if set to false, wont flush after each line.
+		*/
+		log_file_sink( const string_ref& file_name, bool flush_always = true );
+
+		/**
+		* Destructor.
+		*
+		* Flushes, closes file and disposes of the stream.
+		*/
 		virtual ~log_file_sink();
 	};
Index: /trunk/nv/core/logging.hh
===================================================================
--- /trunk/nv/core/logging.hh	(revision 364)
+++ /trunk/nv/core/logging.hh	(revision 365)
@@ -15,7 +15,11 @@
 
 #include <nv/core/common.hh>
+#include <nv/core/string.hh>
 #include <nv/core/singleton.hh>
-#include <string>
 #include <sstream>
+
+#include <nv/core/profiler.hh>
+
+
 
 namespace nv
@@ -38,4 +42,8 @@
 	{
 	public:
+		logger_base()
+		{
+			m_pos = m_message;
+		}
 		unsigned int get_level()
 		{
@@ -46,33 +54,100 @@
 			m_level = level;
 		}
-		virtual void log( log_level level, const std::string& message ) = 0;
+		virtual void log( log_level level, const string_ref& message ) = 0;
+		void log_append( string_ref ref )
+		{
+			memcpy( m_pos, ref.data(), ref.size() );
+			m_pos += ref.size();
+		}
+		void log_append( uint32 u )
+		{
+			m_pos += uint32_to_buffer( u, m_pos );
+		}
+		void log_append( sint32 s )
+		{
+			m_pos += sint32_to_buffer( s, m_pos );
+		}
+		void log_append( f32 f )
+		{
+			m_pos += f32_to_buffer( f, m_pos );
+		}
+		void log( log_level level )
+		{
+			*m_pos = '\0';
+			log( level, string_ref( m_message, (size_t) ( m_pos - m_message ) ) );
+			m_pos = m_message;
+		}
+		static bool can_log( log_level level )
+		{
+			return logger_base::is_valid() && (unsigned int)reference().get_level() >= (unsigned int)level;
+		}
 		virtual ~logger_base() {}
 	protected:
+		char m_message[256];
+		char* m_pos;
 		unsigned int m_level;
 	};
 
+	namespace detail
+	{
+		inline void streamer( std::stringstream& )
+		{
+			// noop;
+		}
+
+		template < typename T, typename... Args >
+		inline void streamer( std::stringstream& ss, T&& t, Args&&... args )
+		{
+			ss << t;
+			streamer( ss, std::forward<Args>(args)... );
+		}
+		inline void logger( log_level level, logger_base& base )
+		{
+			base.log( level );
+		}
+
+		template < typename T, typename... Args >
+		inline void logger( log_level level, logger_base& base, T&& t, Args&&... args )
+		{
+			base.log_append( t );
+			logger( level, base, std::forward<Args>( args )... );
+		}
+	}
+
 } // namespace nv
 
-#define NV_LOG(level, message_stream) \
-	if ( nv::logger_base::is_valid() && \
-		(unsigned int)nv::logger_base::reference().get_level() >= (unsigned int)level ) \
+#define NV_LOG_STREAM(level, message_stream) \
+	if ( nv::logger_base::can_log(level) ) \
 	{       \
 		std::stringstream ss; \
-		ss << message_stream; \
-		nv::logger_base::reference().log( level, ss.str() ); \
+		ss << message_stream;\
+		nv::logger_base::reference().log( level, string_ref( ss.str() ) ); \
 	}
 
+#define NV_LOG(level, ...) \
+	if ( nv::logger_base::can_log(level) ) \
+										{       \
+		NV_PROFILE( "logging" );\
+		nv::detail::logger( level, nv::logger_base::reference(), __VA_ARGS__ ); \
+				}
+
 #if NV_DEBUG == 1
-#define NV_DEBUG_LOG(level, message_stream) \
-	if ( nv::logger_base::is_valid() && \
-		(unsigned int)nv::logger_base::reference().get_level() >= (unsigned int)level ) \
+#define NV_DEBUG_LOG(level, ...) \
+	if ( nv::logger_base::can_log(level) ) \
 	{       \
-		std::stringstream ss; \
-		ss << message_stream; \
-		nv::logger_base::reference().log( level, ss.str() ); \
+		nv::detail::logger( level, nv::logger_base::reference(), __VA_ARGS__ ); \
 	}
 #else
-#define NV_DEBUG_LOG(level, message_stream)
+#define NV_DEBUG_LOG(level, ...)
 #endif
 
+#define NV_LOG_FATAL(...)    NV_LOG( nv::LOG_FATAL, __VA_ARGS__ )
+#define NV_LOG_CRITICAL(...) NV_LOG( nv::LOG_CRITICAL, __VA_ARGS__ )
+#define NV_LOG_ERROR(...)    NV_LOG( nv::LOG_ERROR, __VA_ARGS__ )
+#define NV_LOG_WARNING(...)  NV_LOG( nv::LOG_WARNING, __VA_ARGS__ )
+#define NV_LOG_NOTICE(...)   NV_LOG( nv::LOG_NOTICE, __VA_ARGS__ )
+#define NV_LOG_INFO(...)     NV_LOG( nv::LOG_INFO, __VA_ARGS__ )
+#define NV_LOG_DEBUG(...)    NV_LOG( nv::LOG_DEBUG, __VA_ARGS__ )
+#define NV_LOG_TRACE(...)    NV_LOG( nv::LOG_TRACE, __VA_ARGS__ )
+
 #endif // NV_CORE_LOGGING_HH
Index: /trunk/nv/core/string.hh
===================================================================
--- /trunk/nv/core/string.hh	(revision 364)
+++ /trunk/nv/core/string.hh	(revision 365)
@@ -33,4 +33,6 @@
 	using std::string;
 
+
+
 	/**
 	* Utility function for converting any value to string.
@@ -49,5 +51,5 @@
 		if ( stream.fail() )
 		{
-			NV_THROW( runtime_error, "bad cast" );
+//			NV_THROW( runtime_error, "bad cast" );
 		}
 
@@ -79,5 +81,5 @@
 		if ( value.fail() )
 		{
-			NV_THROW( runtime_error, "bad cast" );
+//			NV_THROW( runtime_error, "bad cast" );
 		}
 	}
@@ -98,5 +100,5 @@
 		if ( value.fail() )
 		{
-			NV_THROW( runtime_error, "bad cast" );
+//			NV_THROW( runtime_error, "bad cast" );
 		}
 		return vout;
@@ -130,5 +132,5 @@
 	{
 		std::ifstream input(filename);
-		if ( !input.is_open() ) NV_THROW( std::runtime_error, "File "+filename+" not found!");
+//		if ( !input.is_open() ) NV_THROW( std::runtime_error, "File "+filename+" not found!");
 		std::stringstream sstr;
 		while(input >> sstr.rdbuf());
@@ -328,6 +330,6 @@
 		}
 
-		inline NV_CONSTEXPR char front()  const { return m_data[0]; }
-		inline NV_CONSTEXPR char back()   const { return m_data[m_size - 1]; }
+		inline NV_CONSTEXPR char front()        const { return m_data[0]; }
+		inline NV_CONSTEXPR char back()         const { return m_data[m_size - 1]; }
 		inline NV_CONSTEXPR const char* data()  const { return m_data; }
 
@@ -472,4 +474,8 @@
 		// Non-literal constructors
 		template< typename U, typename std::enable_if<std::is_same<U, const char*>::value>::type* = nullptr >
+		inline string_ref( U str ) : string_base( str, std::strlen( str ) ) {}
+
+		// Non-literal constructors
+		template< typename U, typename std::enable_if<std::is_same<U, char*>::value>::type* = nullptr >
 		inline string_ref( U str ) : string_base( str, std::strlen( str ) ) {}
 
@@ -625,4 +631,11 @@
 #undef NV_STRING_REF_CAST_OPERATORS
 
+size_t sint32_to_buffer( sint32 n, char* str );
+size_t sint64_to_buffer( sint64 n, char* str );
+size_t uint32_to_buffer( uint32 n, char* str );
+size_t uint64_to_buffer( uint64 n, char* str );
+size_t f32_to_buffer( f32 n, char* str );
+size_t f64_to_buffer( f64 n, char* str );
+
 
 }
Index: /trunk/nv/lib/assimp.hh
===================================================================
--- /trunk/nv/lib/assimp.hh	(revision 364)
+++ /trunk/nv/lib/assimp.hh	(revision 365)
@@ -78,5 +78,5 @@
 	inline void assimp_log_callback( const char* message, char* )
 	{
-		NV_LOG( LOG_DEBUG, rtrimmed( message ) );
+		NV_LOG_DEBUG( rtrimmed( message ) );
 	}
 
Index: /trunk/src/core/io_event.cc
===================================================================
--- /trunk/src/core/io_event.cc	(revision 364)
+++ /trunk/src/core/io_event.cc	(revision 365)
@@ -46,5 +46,5 @@
 void nv::log_event( const io_event& e )
 {
-	NV_LOG( LOG_INFO, "Event: " << get_io_event_name( e.type ) );
+	NV_LOG_INFO( "Event: ", get_io_event_name( e.type ) );
 }
 
Index: /trunk/src/core/library.cc
===================================================================
--- /trunk/src/core/library.cc	(revision 364)
+++ /trunk/src/core/library.cc	(revision 365)
@@ -71,5 +71,5 @@
         return true;
     }
-    NV_LOG( LOG_NOTICE, "library : loading '" + m_name + "'..." );
+    NV_LOG_NOTICE( "library : loading '", m_name, "'..." );
 
     string name = m_name;
@@ -86,8 +86,8 @@
     if ( m_handle == NULL )
     {
-		NV_LOG( LOG_NOTICE, "library : '" + name + "' failed to open." );
+		NV_LOG_NOTICE( "library : '", name, "' failed to open." );
 		return false;
     }
-    NV_LOG( LOG_NOTICE, "library : '" + name + "' loaded." );
+    NV_LOG_NOTICE( "library : '", name, "' loaded." );
 	return true;
 }
@@ -117,5 +117,5 @@
     if ( NV_LIB_CLOSE( (NV_LIB_HANDLE)m_handle ) )
     {
-        NV_LOG( LOG_ERROR, "library : can't close library '" + m_name + "'!" );
+        NV_LOG_ERROR( "library : can't close library '", m_name, "'!" );
     }
     m_handle = NULL;
Index: /trunk/src/core/logger.cc
===================================================================
--- /trunk/src/core/logger.cc	(revision 364)
+++ /trunk/src/core/logger.cc	(revision 365)
@@ -6,11 +6,6 @@
 
 #include "nv/core/common.hh"
-#include <iostream>
-#include <utility>
-#include <algorithm>
-#include <fstream>
-#include <ctime>
+#include "nv/core/time.hh"
 #include <cstdio>
-#include "nv/core/exception.hh"
 #if NV_PLATFORM == NV_WINDOWS
 #define WIN32_LEAN_AND_MEAN
@@ -89,34 +84,30 @@
 
 // log function
-void logger::log( log_level level, const std::string& message )
-{
-	// get the iterator to the beginning of the log_sink list
-	log_sink_list::reverse_iterator it = m_log_sinks.rbegin();
-
-	// iterate
-	while ( it != m_log_sinks.rend() )
-	{
-		// if we have a log sink with high enough level...
-		if ( it->first >= level )
+void logger::log( log_level level, const string_ref& message )
+{
+	for ( auto& sink_info : m_log_sinks )
+	{
+		if ( sink_info.sink && (sink_info.level >= (uint32)level) )
 		{
 			// log and iterate
-			it->second->log( level, message );
+			sink_info.sink->log( level, message );
 		}
-		else 
+	}
+}
+
+// add a new sink
+void logger::add_sink( log_sink* sink, int level )
+{
+	// add a sink
+	for ( auto& sink_info : m_log_sinks )
+	{
+		if ( sink_info.sink == nullptr )
 		{
-			// otherwise return, the list is sorted by log level
+			sink_info.sink  = sink;
+			sink_info.level = (uint32)level;
 			return;
 		}
-		++it;
-	}
-}
-
-// add a new sink
-void logger::add_sink( log_sink* sink, int level )
-{
-	// add a sink
-	m_log_sinks.push_back( std::make_pair( log_level(level), sink ) );
-	// and sort the list (default sort of pairs is by first element)
-	m_log_sinks.sort();
+	}
+	NV_ASSERT( false, "ran out of log sink space!" );
 }
 
@@ -124,20 +115,13 @@
 bool logger::remove_sink( log_sink* sink )
 {
-	// get the iterator to the beginning of the log_sink list
-	log_sink_list::iterator it = m_log_sinks.begin();
-
-	// iterate
-	while ( it != m_log_sinks.end() )
-	{
-		// found?
-		if ( it->second == sink ) 
+	for ( auto& sink_info : m_log_sinks )
+	{
+		if ( sink_info.sink == sink )
 		{
-			// erase and return true to report success
-			m_log_sinks.erase(it);
+			delete sink_info.sink;
+			sink_info.sink = nullptr;
 			return true;
 		}
-		++it;
-	}
-
+	}
 	// not found, return false
 	return false;
@@ -147,11 +131,8 @@
 logger::~logger()
 {
-	// while we have sinks
-	while ( !m_log_sinks.empty() )
-	{
-		// delete the last one
-		delete m_log_sinks.back().second;
-		// and pop it
-		m_log_sinks.pop_back();
+	// delete all sinks
+	for ( auto& sink_info : m_log_sinks )
+	{
+		delete sink_info.sink;
 	}
 }
@@ -159,68 +140,90 @@
 
 // console logging
-void log_console_sink::log( log_level level, const std::string& message )
-{
-	if (m_color) 
-	{
-#if NV_PLATFORM == NV_WINDOWS 
-		SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY );
-		std::cout << timestamp() << " [";
-		SetConsoleTextAttribute( m_handle, log_color[ (level) / 10 ] );
-		std::cout << NV_LOG_LEVEL_NAME_PAD(level);
-		SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY );
-		std::cout << "] ";
-		SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE );
-		std::cout << message << std::endl;
-#else
-		std::cout << "\33[30;1m" << timestamp() << " [" << log_color[ (level) / 10 ] << NV_LOG_LEVEL_NAME_PAD(level) << "\33[30;1m] \33[37;1m" << message << std::endl;
-#endif
-	}
+void log_console_sink::log( log_level level, const string_ref& message )
+{
+#if NV_PLATFORM == NV_WINDOWS 
+	if ( m_color ) SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY );
+	string_ref stamp( timestamp() );
+	WriteConsole( m_handle, stamp.data(), stamp.size(), nullptr, nullptr );
+	WriteConsole( m_handle, " [", 2, nullptr, nullptr );
+	if (m_color) SetConsoleTextAttribute( m_handle, log_color[( level ) / 10] );
+	WriteConsole( m_handle, NV_LOG_LEVEL_NAME_PAD( level ), 8, nullptr, nullptr );
+	if ( m_color ) SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY );
+	WriteConsole( m_handle, "] ", 2, nullptr, nullptr );
+	if ( m_color ) SetConsoleTextAttribute( m_handle, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE );
+	WriteConsole( m_handle, message.data(), message.size(), nullptr, nullptr );
+	WriteConsole( m_handle, "\n", 1, nullptr, nullptr );
+#else
+	if ( m_color ) fwrite( "\33[30;1m", 7, 1, stdout );
+	fwrite( stamp.data(), stamp.size(), 1, stdout );
+	fwrite( " [", 2, 1, stdout );
+	if ( m_color )
+	{
+		const char* lcolor = log_color[( level ) / 10];
+		fwrite( lcolor, strlen(lcolor), 1, stdout );
+	}
+	fwrite( NV_LOG_LEVEL_NAME_PAD( level ), 8, 1, stdout );
+	if ( m_color )
+		fwrite( "\33[30;1m] \33[37;1m", 16, 1, stdout );
 	else
-	{
-	std::cout << timestamp() << " [" << NV_LOG_LEVEL_NAME_PAD(level) << "] " << message << std::endl;
-	}
-}
-
-// stream logging
-void log_stream_sink::log( log_level level, const std::string& message )
-{
-	// if flushing is enabled
-	if ( m_flush )
-	{
-		// write and flush using std::endl
-		*m_stream << timestamp() << " [" << NV_LOG_LEVEL_NAME(level) << "] " << message << std::endl;
-	}
-	else
-	{
-		// write and end with "\n" (no flush)
-		*m_stream << timestamp() << " [" << NV_LOG_LEVEL_NAME(level) << "] " << message << "\n";
-	}
-}
-
-// file logging
-log_file_sink::log_file_sink( const std::string file_name, bool flush_always /*= true */ )
-	: log_stream_sink( nullptr, flush_always )
-{
-	// create the stream manually
-	std::ofstream* fstream = new std::ofstream( file_name );
-
-	// check if it's open
-	if ( !fstream->is_open() )
-	{
-		// throw if not open
-		NV_THROW( runtime_error, "Could not open file \""+file_name+"\" for logging!" );
-	}
-
-	m_stream = fstream;
-}
-
-// file logger destructor
-log_file_sink::~log_file_sink()
-{
-	// close the file
-	dynamic_cast< std::ofstream* >(m_stream)->close();
-	// dispose of the stream
-	delete m_stream;
-}
+		fwrite( "] ", 2, 1, stdout );
+	fwrite( stamp.data(), stamp.size(), 1, stdout );
+	fwrite( "\n", 1, 1, stdout );
+#endif
+}
+
+// handle logging
+void log_handle_sink::log( log_level level, const string_ref& message )
+{
+	string_ref stamp( timestamp() );
+#if 0 // NV_PLATFORM == NV_WINDOWS
+	// Turns out WriteFile on Windows is unbuffered and quite slower than fwrite 
+	// due to this fact -- especially UNUSABLE with manual FlushFileBuffers
+	// If we want to get rid of C runtime, this would need a buffered I/O layer.
+	DWORD unused = 0;
+	WriteFile( m_handle, stamp.data(), stamp.size(), &unused, nullptr );
+	WriteFile( m_handle, " [", 2, &unused, nullptr );
+	WriteFile( m_handle, NV_LOG_LEVEL_NAME_PAD( level ), 8, &unused, nullptr );
+	WriteFile( m_handle, "] ", 2, &unused, nullptr );
+	WriteFile( m_handle, message.data(), message.size(), &unused, nullptr );
+	WriteFile( m_handle, "\n", 1, &unused, nullptr );
+	//if ( m_flush ) FlushFileBuffers( m_handle );
+#else
+	fwrite( stamp.data(), stamp.size(), 1, (FILE*)m_handle );
+	fwrite( " [", 2, 1, (FILE*)m_handle );
+	fwrite( NV_LOG_LEVEL_NAME_PAD( level ), 8, 1, (FILE*)m_handle );
+	fwrite( "] ", 2, 1, (FILE*)m_handle );
+	fwrite( message.data(), message.size(), 1, (FILE*)m_handle );
+	fwrite( "\n", 1, 1, (FILE*)m_handle );
+	if ( m_flush ) fflush( (FILE*)m_handle );
+#endif
+}
+
+nv::log_file_sink::log_file_sink( const string_ref& file_name, bool flush_always /*= true */ )
+	: log_handle_sink( nullptr, flush_always )
+{
+#if 0 // NV_PLATFORM == NV_WINDOWS
+	// See comments in log_handle_sink
+	HANDLE handle = CreateFile( file_name.data(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr );
+	if ( INVALID_HANDLE_VALUE == handle )
+	{
+		NV_ASSERT( false, "invalid log handle" );
+	}
+	m_handle = handle;
+#else
+	m_handle = fopen( file_name.data(), "w" );
+#endif
+}
+
+nv::log_file_sink::~log_file_sink()
+{
+#if 0 // NV_PLATFORM == NV_WINDOWS
+	// See comments in log_handle_sink
+	CloseHandle( m_handle );
+#else
+	fclose( (FILE*) m_handle );
+#endif
+}
+
 
 nv::log_console_sink::log_console_sink( bool coloring )
@@ -234,29 +237,30 @@
 }
 
-const char* nv::log_sink::timestamp() const
-{
-	std::clock_t time = std::clock();
-	unsigned int secs = (unsigned int)(time / CLOCKS_PER_SEC);
-	unsigned int mm   = (unsigned int)(time*100 / CLOCKS_PER_SEC) % 100;
+string_ref nv::log_sink::timestamp() const
+{
+	uint32 ms = get_system_ms();
+	unsigned int secs = (unsigned int)(ms / 1000);
+	unsigned int mm   = (unsigned int)( ms * 100 / 1000 ) % 100;
 	unsigned int h    = (unsigned int)(secs / (60*60));
 	unsigned int m    = (unsigned int)(secs / 60) % 60;
 	unsigned int s    = secs % 60;
-	static char buffer[128];
-#if NV_PLATFORM == NV_WINDOWS 
-	sprintf_s( buffer, 128, "%02d:%02d:%02d.%02d", h, m, s, mm );
-#else
-	sprintf( buffer, "%02d:%02d:%02d.%02d", h, m, s, mm );
+	static char buffer[16];
+#if NV_PLATFORM == NV_WINDOWS 
+	sprintf_s( buffer, 16, "%02d:%02d:%02d.%02d", h, m, s, mm );
+#else
+	snprintf( buffer, 16, "%02d:%02d:%02d.%02d", h, m, s, mm );
 #endif
 	buffer[11] = '\0';
-	return buffer;
-}
-
-const char* nv::log_sink::level_name( log_level level ) const
+	return string_ref( buffer, 10 );
+}
+
+string_ref nv::log_sink::level_name( log_level level ) const
 {
 	return NV_LOG_LEVEL_NAME( level );
 }
 
-const char* nv::log_sink::padded_level_name( log_level level ) const
-{
-	return NV_LOG_LEVEL_NAME_PAD( level );
-}
+string_ref nv::log_sink::padded_level_name( log_level level ) const
+{
+	return string_ref( NV_LOG_LEVEL_NAME_PAD( level ), 8 );
+}
+
Index: /trunk/src/core/profiler.cc
===================================================================
--- /trunk/src/core/profiler.cc	(revision 364)
+++ /trunk/src/core/profiler.cc	(revision 365)
@@ -8,4 +8,5 @@
 #include <ios>
 #include "nv/core/time.hh"
+#include "nv/core/logging.hh"
 
 using namespace nv;
@@ -97,10 +98,9 @@
 }
 
-
 void profiler::log_report()
 {
 	m_root->stop();
-	NV_LOG( LOG_INFO, "-- PROFILER REPORT -----------------------------------------" );
-	NV_LOG( LOG_INFO, std::left << std::setw(24) << "TAG" 
+	NV_LOG_INFO( "-- PROFILER REPORT -----------------------------------------" );
+	NV_LOG_STREAM( LOG_INFO, std::left << std::setw(24) << "TAG" 
 		<< std::setw(7) << "%PARNT" 
 		<< std::setw(7) << "CALLS" 
@@ -108,5 +108,5 @@
 		<< std::setw(10) << "AVG(ms)" );
 	log_node_children( "", m_root );
-	NV_LOG( LOG_INFO, "-- PROFILER REPORT END -------------------------------------" );
+	NV_LOG_INFO( "-- PROFILER REPORT END -------------------------------------" );
 	m_root->start();
 }
@@ -119,5 +119,5 @@
 		if ( c->m_calls > 0 )
 		{
-			NV_LOG( LOG_INFO, std::left << std::setw(24) << ind + c->m_tag 
+			NV_LOG_STREAM( LOG_INFO, std::left << std::setw(24) << ind + c->m_tag 
 				<< std::setw(7) << std::setprecision(2) << std::fixed << ( (double)c->m_total_time_us / (double)c->m_parent->m_total_time_us ) * 100.0
 				<< std::setw(7) << c->m_calls 
Index: /trunk/src/core/string.cc
===================================================================
--- /trunk/src/core/string.cc	(revision 365)
+++ /trunk/src/core/string.cc	(revision 365)
@@ -0,0 +1,184 @@
+// Copyright (C) 2015 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+//
+// TODO: speedup conversion by doing divisions by 100
+
+#include "nv/core/string.hh"
+
+#include <cstdio>
+#include <cstdlib>
+
+using namespace nv;
+
+static const double s_power_10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000,
+10000000, 100000000, 1000000000 };
+
+static inline void string_reverse( char* begin, char* end )
+{
+	char temp;
+	while ( end > begin )
+	{
+		temp = *end;
+		*end-- = *begin;
+		*begin++ = temp;
+	}
+}
+
+size_t nv::sint32_to_buffer( sint32 n, char* str )
+{
+	char* s = str;
+	uint32 abs = ( n < 0 ) ? (uint32)( -n ) : (uint32)( n );
+	do
+	{
+		*s++ = (char)( '0' + ( abs % 10 ) );
+		abs /= 10;
+	} while ( abs > 0 );
+	if ( n < 0 ) *s++ = '-';
+	*s = '\0';
+	string_reverse( str, s - 1 );
+	return (size_t)( s - str );
+}
+
+size_t nv::sint64_to_buffer( sint64 n, char* str )
+{
+	char* s = str;
+	uint64 abs = ( n < 0 ) ? (uint64)( -n ) : (uint64)( n );
+	do
+	{
+		*s++ = (char)( '0' + ( abs % 10 ) );
+		abs /= 10;
+	} while ( abs > 0 );
+	if ( n < 0 ) *s++ = '-';
+	*s = '\0';
+	string_reverse( str, s - 1 );
+	return (size_t)( s - str );
+}
+
+size_t nv::uint32_to_buffer( uint32 n, char* str )
+{
+	char* s = str;
+	do
+	{
+		*s++ = (char)( '0' + ( n % 10 ) );
+		n /= 10;
+	} while ( n > 0 );
+	if ( n < 0 ) *s++ = '-';
+	*s = '\0';
+	string_reverse( str, s - 1 );
+	return (size_t)( s - str );
+}
+
+size_t nv::uint64_to_buffer( uint64 n, char* str )
+{
+	char* s = str;
+	do
+	{
+		*s++ = (char)( '0' + ( n % 10 ) );
+		n /= 10;
+	} while ( n > 0 );
+	if ( n < 0 ) *s++ = '-';
+	*s = '\0';
+	string_reverse( str, s - 1 );
+	return (size_t)( s - str );
+}
+
+size_t nv::f32_to_buffer( f32 n, char* str )
+{
+#if NV_COMPILER == NV_MSVC
+	sprintf_s( str, 64, "%.*g", 6, n );
+#else
+	snprintf( buffer, 64, "%.*g", 6, n );
+#endif
+	sprintf( str, "%g", n );
+	return strlen( str );
+}
+
+size_t nv::f64_to_buffer( f64 n, char* str )
+{
+#if NV_COMPILER == NV_MSVC
+	sprintf_s( str, 64, "%.*g", 6, n );
+#else
+	snprintf( buffer, 64, "%.*g", 6, n );
+#endif
+	return strlen( str );
+}
+
+sint32 buffer_to_sint32( const char* str, char** end )
+{
+	const char* s = str;
+	while ( *s == ' ' ) ++s;
+	sint32 result = 0;
+	bool positive = true;
+	switch ( *s )
+	{
+	case '-': ++s; positive = false; break;
+	case '+': ++s; break;
+	default: break;
+	}
+	while ( *s >= '0' && *s <= '9' )
+	{
+		result = ( result * 10 ) + ( *s - '0' );
+		++s;
+	}
+	if ( end != nullptr ) *end = (char*)s;
+	return positive ? result : -result;
+}
+
+sint64 buffer_to_sint64( const char* s, char** end )
+{
+	while ( *s == ' ' ) ++s;
+	sint64 result = 0;
+	bool positive = true;
+	switch ( *s )
+	{
+	case '-': ++s; positive = false; break;
+	case '+': ++s; break;
+	default: break;
+	}
+	while ( *s >= '0' && *s <= '9' )
+	{
+		result = ( result * 10 ) + ( *s - '0' );
+		++s;
+	}
+	if ( end != nullptr ) *end = (char*)s;
+	return positive ? result : -result;
+}
+
+uint32 buffer_to_uint32( const char* s, char** end )
+{
+	while ( *s == ' ' ) ++s;
+	uint32 result = 0;
+	while ( *s >= '0' && *s <= '9' )
+	{
+		result = ( result * 10 ) + ( *s - '0' );
+		++s;
+	}
+	if ( end != nullptr ) *end = (char*)s;
+	return result;
+}
+
+uint64 buffer_to_uint64( const char* s, char** end )
+{
+	while ( *s == ' ' ) ++s;
+	uint64 result = 0;
+	while ( *s >= '0' && *s <= '9' )
+	{
+		result = ( result * 10 ) + ( *s - '0' );
+		++s;
+	}
+	if ( end != nullptr ) *end = (char*)s;
+	return result;
+}
+
+float buffer_to_f32( const char* s, char** end )
+{
+	return strtof( s, end );
+}
+
+double buffer_to_f64( const char* s, char** end )
+{
+	return strtod( s, end );
+}
Index: /trunk/src/engine/particle_engine.cc
===================================================================
--- /trunk/src/engine/particle_engine.cc	(revision 364)
+++ /trunk/src/engine/particle_engine.cc	(revision 365)
@@ -303,5 +303,5 @@
 	if ( id == "" )
 	{
-		NV_LOG( LOG_ERROR, "Bad table passed to particle_engine!" )
+		NV_LOG_ERROR( "Bad table passed to particle_engine!" )
 	}
 	// TODO : overwrite check
@@ -325,5 +325,5 @@
 	else 
 	{
-		NV_LOG( LOG_ERROR, "Unknown orientation type! (" << orientation << ")!" );
+		NV_LOG_ERROR( "Unknown orientation type! (", orientation, ")!" );
 		data.orientation = particle_orientation::POINT;
 	}
@@ -341,5 +341,5 @@
 	else 
 	{
-		NV_LOG( LOG_ERROR, "Unknown particle origin! (" << origin << ")!" );
+		NV_LOG_ERROR( "Unknown particle origin! (", origin, ")!" );
 		data.origin = particle_origin::CENTER;
 	}
@@ -368,5 +368,5 @@
 				{
 					edata.emmiter_func = nv_particle_emmiter_point;
-					NV_LOG( LOG_WARNING, "Unknown emmiter type in particle system! (" << sub_type << ")" );
+					NV_LOG_WARNING( "Unknown emmiter type in particle system! (", sub_type, ")" );
 				}
 
@@ -417,5 +417,5 @@
 			else
 			{
-				NV_LOG( LOG_ERROR, "Too many emmiters (" << MAX_PARTICLE_EMMITERS << " is MAX)!" );
+				NV_LOG_ERROR( "Too many emmiters (", MAX_PARTICLE_EMMITERS, " is MAX)!" );
 			}
 		}
@@ -433,5 +433,5 @@
 					{
 						data.affector_count--;
-						NV_LOG( LOG_WARNING, "Bad data passed to " << sub_type << " affector in particle system!" );
+						NV_LOG_WARNING( "Bad data passed to ", sub_type, " affector in particle system!" );
 					}
 				}
@@ -439,15 +439,15 @@
 				{
 					data.affector_count--;
-					NV_LOG( LOG_WARNING, "Unknown affector type in particle system! (" << sub_type << ")" );
+					NV_LOG_WARNING( "Unknown affector type in particle system! (", sub_type, ")" );
 				}
 			}
 			else
 			{
-				NV_LOG( LOG_ERROR, "Too many affectors (" << MAX_PARTICLE_AFFECTORS << " is MAX)!" );
+				NV_LOG_ERROR( "Too many affectors (", MAX_PARTICLE_AFFECTORS, " is MAX)!" );
 			}
 		}
 		else 
 		{
-			NV_LOG( LOG_WARNING, "Unknown element in particle system! (" << type << ")" );
+			NV_LOG_WARNING( "Unknown element in particle system! (", type, ")" );
 		}
 	}
Index: /trunk/src/engine/program_manager.cc
===================================================================
--- /trunk/src/engine/program_manager.cc	(revision 364)
+++ /trunk/src/engine/program_manager.cc	(revision 365)
@@ -7,5 +7,7 @@
 #include "nv/engine/program_manager.hh"
 #include "nv/core/range.hh"
+#include "nv/core/logging.hh"
 #include "nv/lua/lua_nova.hh"
+
 
 nv::program_manager::program_manager( context* a_context ) : m_context( a_context )
@@ -17,5 +19,5 @@
 nv::resource_id nv::program_manager::load_resource( lua::table_guard& table )
 {
-	NV_LOG( LOG_DEBUG, table.get_string("id") );
+	NV_LOG_DEBUG( table.get_string("id") );
 	std::string vsource;
 	std::string fsource;
Index: /trunk/src/fmod/fmod_audio.cc
===================================================================
--- /trunk/src/fmod/fmod_audio.cc	(revision 364)
+++ /trunk/src/fmod/fmod_audio.cc	(revision 365)
@@ -24,5 +24,5 @@
 	if ( result != FMOD_OK )
 	{
-		NV_LOG( LOG_CRITICAL, "Failed to create FMOD System -- " << FMOD_ErrorString( result ) );
+		NV_LOG_CRITICAL( "Failed to create FMOD System -- ", FMOD_ErrorString( result ) );
 		return;
 	}
@@ -30,5 +30,5 @@
 	if ( result != FMOD_OK )
 	{
-		NV_LOG( LOG_ERROR, "Failed to initialize FMOD System -- " << FMOD_ErrorString( result ) );
+		NV_LOG_ERROR( "Failed to initialize FMOD System -- ", FMOD_ErrorString( result ) );
 		return;
 	}
@@ -47,5 +47,5 @@
 		if ( result != FMOD_OK )
 		{
-			NV_LOG( LOG_WARNING, "FMOD failed to play sound -- " << FMOD_ErrorString( result ) );
+			NV_LOG_WARNING( "FMOD failed to play sound -- ", FMOD_ErrorString( result ) );
 		}
 		else
@@ -70,5 +70,5 @@
 		if ( result != FMOD_OK )
 		{
-			NV_LOG( LOG_WARNING, "FMOD failed to play sound -- " << FMOD_ErrorString( result ) );
+			NV_LOG_WARNING( "FMOD failed to play sound -- ", FMOD_ErrorString( result ) );
 		}
 		else
@@ -95,5 +95,5 @@
 	if ( fm_result != FMOD_OK )
 	{
-		NV_LOG( LOG_ERROR, "FMOD failed to load sample '" << a_path << "' -- " << FMOD_ErrorString( fm_result ) );
+		NV_LOG_ERROR( "FMOD failed to load sample '", a_path, "' -- ", FMOD_ErrorString( fm_result ) );
 		return sound();
 	}
Index: /trunk/src/formats/assimp_loader.cc
===================================================================
--- /trunk/src/formats/assimp_loader.cc	(revision 364)
+++ /trunk/src/formats/assimp_loader.cc	(revision 365)
@@ -89,5 +89,5 @@
 	m_scene = nullptr;
 	m_mesh_count = 0;
-	NV_LOG( nv::LOG_NOTICE, "AssImp loading file..." );
+	NV_LOG_NOTICE( "AssImp loading file..." );
 	size_t size = source.size();
 	char* data  = new char[ size ];
@@ -97,10 +97,10 @@
 	if( !scene)
 	{
-		NV_LOG( nv::LOG_ERROR, aiGetErrorString() );
+		NV_LOG_ERROR( aiGetErrorString() );
 		return false;
 	}
 	m_scene      = scene;
 	m_mesh_count = scene->mNumMeshes;
-	NV_LOG( nv::LOG_NOTICE, "Loading successfull" );
+	NV_LOG_NOTICE( "Loading successfull" );
 	return true;
 }
@@ -213,23 +213,23 @@
 	if ( scene == nullptr ) return;
 
-	NV_LOG( nv::LOG_NOTICE, "------------------------" );
-	NV_LOG( nv::LOG_NOTICE, "Texture   count - " << scene->mNumTextures );
-	NV_LOG( nv::LOG_NOTICE, "Animation count - " << scene->mNumAnimations );
-	NV_LOG( nv::LOG_NOTICE, "Material  count - " << scene->mNumMaterials );
-	NV_LOG( nv::LOG_NOTICE, "Meshes    count - " << scene->mNumMeshes );
-	NV_LOG( nv::LOG_NOTICE, "------------------------" );
+	NV_LOG_NOTICE( "------------------------" );
+	NV_LOG_NOTICE( "Texture   count - ", scene->mNumTextures );
+	NV_LOG_NOTICE( "Animation count - ", scene->mNumAnimations );
+	NV_LOG_NOTICE( "Material  count - ", scene->mNumMaterials );
+	NV_LOG_NOTICE( "Meshes    count - ", scene->mNumMeshes );
+	NV_LOG_NOTICE( "------------------------" );
 
 	aiNode* root = scene->mRootNode;
 	if (root)
 	{
-		NV_LOG( nv::LOG_NOTICE, "Root node  - " << root->mName.data );
-		NV_LOG( nv::LOG_NOTICE, "  meshes   - " << root->mNumMeshes );
-		NV_LOG( nv::LOG_NOTICE, "  children - " << root->mNumChildren );
+		NV_LOG_NOTICE( "Root node  - ", root->mName.data );
+		NV_LOG_NOTICE( "  meshes   - ", root->mNumMeshes );
+		NV_LOG_NOTICE( "  children - ", root->mNumChildren );
 	}
 	else
 	{
-		NV_LOG( nv::LOG_NOTICE, "No root node!" );
-	}
-	NV_LOG( nv::LOG_NOTICE, "------------------------" );
+		NV_LOG_NOTICE( "No root node!" );
+	}
+	NV_LOG_NOTICE( "------------------------" );
 
 	if ( scene->mNumMeshes > 0 )
@@ -239,15 +239,15 @@
 			aiMesh* mesh = scene->mMeshes[mc];
 
-			NV_LOG( nv::LOG_NOTICE, "Mesh #"<<mc<<"   - " << std::string( mesh->mName.data ) );
-			NV_LOG( nv::LOG_NOTICE, "  bones   - " << mesh->mNumBones );
-			NV_LOG( nv::LOG_NOTICE, "  uvs     - " << mesh->mNumUVComponents[0] );
-			NV_LOG( nv::LOG_NOTICE, "  verts   - " << mesh->mNumVertices );
-			NV_LOG( nv::LOG_NOTICE, "  faces   - " << mesh->mNumFaces );
-
-			// 			NV_LOG( nv::LOG_NOTICE, "Bones:" );
+			NV_LOG_NOTICE( "Mesh #", mc, "   - ", std::string( mesh->mName.data ) );
+			NV_LOG_NOTICE( "  bones   - ", mesh->mNumBones );
+			NV_LOG_NOTICE( "  uvs     - ", mesh->mNumUVComponents[0] );
+			NV_LOG_NOTICE( "  verts   - ", mesh->mNumVertices );
+			NV_LOG_NOTICE( "  faces   - ", mesh->mNumFaces );
+
+			// 			NV_LOG_NOTICE(  "Bones:" );
 			// 			for (unsigned int m=0; m<mesh->mNumBones; m++)
 			// 			{
 			// 				aiBone* bone  = mesh->mBones[m];
-			// 				NV_LOG( nv::LOG_DEBUG, bone->mName.C_Str() );
+			// 				NV_LOG_NOTICE( bone->mName.C_Str() );
 			// 			}
 		}
@@ -255,7 +255,7 @@
 	else
 	{
-		NV_LOG( nv::LOG_NOTICE, "No meshes!" );
-	}
-	NV_LOG( nv::LOG_NOTICE, "------------------------" );
+		NV_LOG_NOTICE( "No meshes!" );
+	}
+	NV_LOG_NOTICE( "------------------------" );
 
 
@@ -270,5 +270,5 @@
 	//  			{
 	// 				texFound = scene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path);
-	// 				NV_LOG( nv::LOG_NOTICE, "  material - " << path.data );
+	// 				NV_LOG_NOTICE( "  material - ", path.data );
 	// 				texIndex++;
 	// 			}
@@ -277,7 +277,7 @@
 	// 	else
 	// 	{
-	// 		NV_LOG( nv::LOG_NOTICE, "No materials" );
+	// 		NV_LOG_NOTICE( "No materials" );
 	// 	}
-	// 	NV_LOG( nv::LOG_NOTICE, "------------------------" );
+	// 	NV_LOG_NOTICE( "------------------------" );
 
 }
Index: /trunk/src/formats/md2_loader.cc
===================================================================
--- /trunk/src/formats/md2_loader.cc	(revision 364)
+++ /trunk/src/formats/md2_loader.cc	(revision 365)
@@ -155,9 +155,9 @@
 	}
 
-	NV_LOG( LOG_INFO, "num_skins    = " << md2->header.num_skins );
-	NV_LOG( LOG_INFO, "num_vertices = " << md2->header.num_vertices );
-	NV_LOG( LOG_INFO, "num_st       = " << md2->header.num_st );
-	NV_LOG( LOG_INFO, "num_tris     = " << md2->header.num_tris );
-	NV_LOG( LOG_INFO, "num_frames   = " << md2->header.num_frames );
+	NV_LOG_INFO( "num_skins    = ", md2->header.num_skins );
+	NV_LOG_INFO( "num_vertices = ", md2->header.num_vertices );
+	NV_LOG_INFO( "num_st       = ", md2->header.num_st );
+	NV_LOG_INFO( "num_tris     = ", md2->header.num_tris );
+	NV_LOG_INFO( "num_frames   = ", md2->header.num_frames );
 
 
@@ -295,7 +295,7 @@
 	}
 
-	NV_LOG( LOG_INFO, "New vertex count = " << m_new_vindexes.size() );
-	NV_LOG( LOG_INFO, "Collisions       = " << stats_collision );
-	NV_LOG( LOG_INFO, "Reuse count      = " << stats_reuse );
+	NV_LOG_INFO( "New vertex count = ", m_new_vindexes.size() );
+	NV_LOG_INFO( "Collisions       = ", stats_collision );
+	NV_LOG_INFO( "Reuse count      = ", stats_reuse );
 }
 
@@ -335,5 +335,5 @@
 	{
 		const md2_frame_t& cframe = md2->frames[current_frame];
-		NV_LOG( LOG_INFO, "FrameID = " << cframe.name );
+		NV_LOG_INFO( "FrameID = ", cframe.name );
 
 		vec3 scale     = md2_vec3( cframe.scale );
Index: /trunk/src/gfx/texture_font.cc
===================================================================
--- /trunk/src/gfx/texture_font.cc	(revision 364)
+++ /trunk/src/gfx/texture_font.cc	(revision 365)
@@ -9,4 +9,5 @@
 #include <stdexcept>
 #include "nv/lib/freetype2.hh"
+#include "nv/core/logging.hh"
 
 using namespace nv;
Index: /trunk/src/gl/gl_context.cc
===================================================================
--- /trunk/src/gl/gl_context.cc	(revision 364)
+++ /trunk/src/gl/gl_context.cc	(revision 365)
@@ -8,4 +8,5 @@
 #include "nv/lib/gl.hh"
 #include "nv/gl/gl_device.hh"
+#include "nv/core/logger.hh"
 
 using namespace nv;
@@ -168,17 +169,17 @@
 		switch ( result )
 		{
-		case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT         : NV_LOG( LOG_ERROR, "gl_context::check : Framebuffer incomplete attachment!" ); break;
-		case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT : NV_LOG( LOG_ERROR, "gl_context::check : Framebuffer missing attachment!" ); break;
-		case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS         : NV_LOG( LOG_ERROR, "gl_context::check : Framebuffer incomplete dimensions!" ); break;
-		case GL_FRAMEBUFFER_INCOMPLETE_FORMATS            : NV_LOG( LOG_ERROR, "gl_context::check : Framebuffer incomplete formats!" ); break;
-		case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER        : NV_LOG( LOG_ERROR, "gl_context::check : Framebuffer incomplete draw buffer!" ); break;
-		case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER        : NV_LOG( LOG_ERROR, "gl_context::check : Framebuffer incomplete read buffer!" ); break;
-		case GL_FRAMEBUFFER_UNSUPPORTED                   : NV_LOG( LOG_ERROR, "gl_context::check : Framebuffer format combination unsupported!" ); break;
-		default : NV_LOG( LOG_ERROR, "gl_context::check : Unknown Framebuffer error! (" << result << ")" ); break;
+		case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT         : NV_LOG_ERROR( "gl_context::check : Framebuffer incomplete attachment!" ); break;
+		case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT : NV_LOG_ERROR( "gl_context::check : Framebuffer missing attachment!" ); break;
+		case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS         : NV_LOG_ERROR( "gl_context::check : Framebuffer incomplete dimensions!" ); break;
+		case GL_FRAMEBUFFER_INCOMPLETE_FORMATS            : NV_LOG_ERROR( "gl_context::check : Framebuffer incomplete formats!" ); break;
+		case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER        : NV_LOG_ERROR( "gl_context::check : Framebuffer incomplete draw buffer!" ); break;
+		case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER        : NV_LOG_ERROR( "gl_context::check : Framebuffer incomplete read buffer!" ); break;
+		case GL_FRAMEBUFFER_UNSUPPORTED                   : NV_LOG_ERROR( "gl_context::check : Framebuffer format combination unsupported!" ); break;
+		default: NV_LOG_ERROR( "gl_context::check : Unknown Framebuffer error! (", result, ")" ); break;
 		}
 	}
 	else
 	{
-		NV_LOG( LOG_ERROR, "gl_context::check : Framebuffer extensions not loaded!" );
+		NV_LOG_ERROR( "gl_context::check : Framebuffer extensions not loaded!" );
 	}
 	return false;
Index: /trunk/src/gl/gl_device.cc
===================================================================
--- /trunk/src/gl/gl_device.cc	(revision 364)
+++ /trunk/src/gl/gl_device.cc	(revision 365)
@@ -41,5 +41,5 @@
 	if (!image)
 	{
-		NV_LOG( LOG_ERROR, "Image file " << filename << " not found!" );
+		NV_LOG_ERROR( "Image file ", filename, " not found!" );
 		return nullptr;
 	}
@@ -59,5 +59,5 @@
 	if ( !image )
 	{
-		NV_LOG( LOG_ERROR, "Image binary data cannot be loaded found!" );
+		NV_LOG_ERROR( "Image binary data cannot be loaded found!" );
 		return nullptr;
 	}
@@ -228,5 +228,5 @@
 		if ( fatal )
 		{
-			NV_LOG( LOG_ERROR, "Uniform '" << name << "' not found in program!" );
+			NV_LOG_ERROR( "Uniform '", name, "' not found in program!" );
 			NV_THROW( runtime_error, ( "Uniform '"+name+"' not found!" ) );
 		}
@@ -247,6 +247,6 @@
 		if ( fatal )
 		{
-			NV_LOG( LOG_ERROR, "Attribute '" << name << "' not found in program!" );
-			NV_THROW( runtime_error, ( "Attribute '"+name+"' not found!" ) );
+			NV_LOG_ERROR( "Attribute '", name, "' not found in program!" );
+			NV_THROW( runtime_error, ( "Attribute '"+ name + "' not found!" ) );
 		}
 	}
@@ -279,9 +279,9 @@
 	glGetProgramInfoLog( p->glid, buffer_size, &length, buffer );
 
-	NV_LOG( LOG_INFO, "Program #" << p->glid << (status == GL_FALSE ? " failed to compile!" : " compiled successfully.") );
+	NV_LOG_INFO( "Program #", p->glid, (status == GL_FALSE ? " failed to compile!" : " compiled successfully.") );
 
 	if ( length > 0 )
 	{
-		NV_LOG( LOG_INFO, "Program #" << p->glid << " log: " << buffer );
+		NV_LOG_INFO( "Program #", p->glid, " log: ", buffer );
 	}
 
@@ -297,5 +297,5 @@
 	{
 		glGetProgramInfoLog( p->glid, buffer_size, &length, buffer );
-		NV_LOG( LOG_ERROR, "Program #" << p->glid << " validation error : " << buffer );
+		NV_LOG_ERROR( "Program #", p->glid, " validation error : ", buffer );
 		return false;
 	}
@@ -418,14 +418,14 @@
 		if ( compile_ok == 0 )
 		{
-			NV_LOG( LOG_ERROR, "Shader #" << glid << " error: " << buffer );
+			NV_LOG_ERROR( "Shader #", glid, " error: ", buffer );
 		}
 		else
 		{
-			NV_LOG( LOG_INFO, "Shader #" << glid << " compiled successfully: " << buffer );
+			NV_LOG_INFO( "Shader #", glid, " compiled successfully: ", buffer );
 		}
 	}
 	else
 	{
-		NV_LOG( LOG_INFO, "Shader #" << glid << " compiled successfully." );
+		NV_LOG_INFO( "Shader #", glid, " compiled successfully." );
 	}
 	return compile_ok != 0;
Index: /trunk/src/gui/gui_ascii_renderer.cc
===================================================================
--- /trunk/src/gui/gui_ascii_renderer.cc	(revision 364)
+++ /trunk/src/gui/gui_ascii_renderer.cc	(revision 365)
@@ -6,4 +6,6 @@
 
 #include "nv/gui/gui_ascii_renderer.hh"
+
+#include "nv/core/logging.hh"
 
 using namespace nv;
@@ -115,5 +117,5 @@
 	// TODO: FIX
 	int fix_me;
-	NV_LOG( nv::LOG_DEBUG, "on_hover_change" );
+	NV_LOG_DEBUG( "on_hover_change" );
 	e->m_flags[DIRTY] = true;
 }
@@ -123,5 +125,5 @@
 	// TODO: FIX
 	int fix_me;
-	NV_LOG( nv::LOG_DEBUG, "on_select_change" );
+	NV_LOG_DEBUG( "on_select_change" );
 	e->m_flags[DIRTY] = true;
 }
Index: /trunk/src/gui/gui_gfx_renderer.cc
===================================================================
--- /trunk/src/gui/gui_gfx_renderer.cc	(revision 364)
+++ /trunk/src/gui/gui_gfx_renderer.cc	(revision 365)
@@ -143,5 +143,5 @@
 	, m_reupload( true )
 {
-	NV_LOG( LOG_TRACE, "Creating GUI renderer..." );
+	NV_LOG_TRACE( "Creating GUI renderer..." );
 	m_context = w->get_context();
 	m_area.dim( dimension( w->get_width(), w->get_height() ) );
@@ -154,5 +154,5 @@
 	delete[] wfill;
 
-	NV_LOG( LOG_TRACE, "Creating render data..." );
+	NV_LOG_TRACE( "Creating render data..." );
 	screen_render_data* sr = new screen_render_data( w->get_context(), 1024 );
 	m_render_data = sr;
@@ -174,5 +174,5 @@
 	m_render_state.blending.src_alpha_factor = blending::SRC_ALPHA;
 	m_render_state.blending.dst_alpha_factor = blending::ONE_MINUS_SRC_ALPHA;
-	NV_LOG( LOG_TRACE, "GUI Renderer created" );
+	NV_LOG_TRACE( "GUI Renderer created" );
 }
 
@@ -347,5 +347,5 @@
 	// TODO: FIX
 	int fix_me;
-	NV_LOG( nv::LOG_DEBUG, "on_hover_change" );
+	NV_LOG_DEBUG( "on_hover_change" );
 	e->m_flags[DIRTY] = true;
 }
@@ -355,5 +355,5 @@
 	// TODO: FIX
 	int fix_me;
-	NV_LOG( nv::LOG_DEBUG, "on_select_change" );
+	NV_LOG_DEBUG( "on_select_change" );
 	e->m_flags[DIRTY] = true;
 }
Index: /trunk/src/lib/gl.cc
===================================================================
--- /trunk/src/lib/gl.cc	(revision 364)
+++ /trunk/src/lib/gl.cc	(revision 365)
@@ -7,4 +7,5 @@
 #include "nv/core/common.hh"
 #include "nv/core/range.hh"
+#include "nv/core/logging.hh"
 #include "nv/lib/gl.hh"
 
@@ -58,5 +59,5 @@
 {
 	void* result = gl_ext_loader( name );
-	NV_LOG( ( result ? nv::LOG_DEBUG : fail_level ), "load_gl_ext_symbol : " << name << ( result ? " succeded." : "failed." ) );
+	NV_LOG( ( result ? nv::LOG_DEBUG : fail_level ), "load_gl_ext_symbol : ", name, ( result ? " succeded." : "failed." ) );
 	return result;
 }
@@ -237,5 +238,5 @@
 	if ( !gl_library.is_open() ) 
 	{
-		NV_LOG( nv::LOG_ERROR, "load_gl_extension used, while gl_library was closed!" );
+		NV_LOG_ERROR( "load_gl_extension used, while gl_library was closed!" );
 		return false;
 	}
@@ -243,8 +244,8 @@
 	if ( gl_ext_loader == nullptr )
 	{
-		NV_LOG( nv::LOG_ERROR, "load_gl_extension used, while gl_ext_loader was undefined!" );
+		NV_LOG_ERROR( "load_gl_extension used, while gl_ext_loader was undefined!" );
 		return false;
 	}
-	NV_LOG( nv::LOG_DEBUG, "load_gl_extension - loading extension - \"" << name << "\"..." );
+	NV_LOG_DEBUG( "load_gl_extension - loading extension - \"", name, "\"..." );
 
 	uint32 count      = 0; 
@@ -264,5 +265,5 @@
 	} break;
 	default : {
-		NV_LOG( nv::LOG_ERROR, "load_gl_extension - unknown extension \"" << name << "\"!" );
+		NV_LOG_ERROR( "load_gl_extension - unknown extension \"", name, "\"!" );
 		return nullptr;
 	}
@@ -272,9 +273,9 @@
 	if ( fail_count == 0 )
 	{
-		NV_LOG( nv::LOG_NOTICE, "load_gl_extension - extension \"" << name << "\" loaded (" << count << " symbols)" );
+		NV_LOG_NOTICE( "load_gl_extension - extension \"", name, "\" loaded (", count, " symbols)" );
 		gl_loaded_extensions = (gl_extensions)( gl_loaded_extensions | (unsigned)extension );
 		return false;
 	}
-	NV_LOG( nv::LOG_NOTICE, "load_gl_extension - failed to load extension \"" << name << "\" (" << count << "/" << fail_count << " symbols loaded)" );
+	NV_LOG_NOTICE( "load_gl_extension - failed to load extension \"", name, "\" (", count, "/", fail_count, " symbols loaded)" );
 	return true;
 }
Index: /trunk/src/lua/lua_handle.cc
===================================================================
--- /trunk/src/lua/lua_handle.cc	(revision 364)
+++ /trunk/src/lua/lua_handle.cc	(revision 365)
@@ -7,4 +7,5 @@
 #include "nv/lua/lua_handle.hh"
 
+#include "nv/core/logging.hh"
 #include "nv/lua/lua_state.hh"
 #include "nv/lua/lua_raw.hh"
@@ -18,5 +19,5 @@
 	if ( !lua_istable( L, -1 ) )
 	{
-		NV_LOG( nv::LOG_ERROR, "NIL" );
+		NV_LOG_ERROR( "NIL" );
 		lua_pop( L, 2 );
 		lua_pushnil( L );
Index: /trunk/src/lua/lua_state.cc
===================================================================
--- /trunk/src/lua/lua_state.cc	(revision 364)
+++ /trunk/src/lua/lua_state.cc	(revision 365)
@@ -111,5 +111,5 @@
 	if ( !p.resolve( m_state, global ) )
 	{
-		NV_LOG( LOG_ERROR, "Lua error : not a valid path - " + p.to_string() );
+		NV_LOG_ERROR( "Lua error : not a valid path - ", p.to_string() );
 		return false;
 	}
@@ -118,5 +118,5 @@
 	{
 		lua_pop( m_state, 1 );
-		NV_LOG( LOG_ERROR, "Lua error : not a valid function - " + p.to_string() );
+		NV_LOG_ERROR( "Lua error : not a valid function - ", p.to_string() );
 		return false;
 	}
@@ -129,5 +129,5 @@
 	if ( status != 0 )
 	{
-		NV_LOG( LOG_ERROR, "Lua error : " << lua_tostring( m_state, -1 ) );
+		NV_LOG_ERROR( "Lua error : ", lua_tostring( m_state, -1 ) );
 		lua_pop( m_state, 1 );
 	}
@@ -144,5 +144,5 @@
 	if ( !p.resolve( m_state, global ) || lua_type(m_state, -1) != LUA_TTABLE )
 	{
-		NV_LOG( LOG_ERROR, "Could not resolve table!" );
+		NV_LOG_ERROR( "Could not resolve table!" );
 		// TODO : error handling
 	}
@@ -156,5 +156,5 @@
 	if ( !p.resolve( m_state, false ) || lua_type(m_state, -1) != LUA_TTABLE )
 	{
-		NV_LOG( LOG_ERROR, "Could not resolve table!" );
+		NV_LOG_ERROR( "Could not resolve table!" );
 		// TODO : error handling
 	}
@@ -347,10 +347,10 @@
 	}
 
-	NV_LOG( nv::LOG_TRACE, "Lua state created" );
+	NV_LOG_TRACE( "Lua state created" );
 }
 
 int lua::state::load_string( string_ref code, string_ref name )
 {
-	NV_LOG( nv::LOG_TRACE, "Loading Lua string '" << name << "'");
+	NV_LOG_TRACE( "Loading Lua string '", name, "'");
 	return luaL_loadbuffer( m_state, code.data(), code.length(), name.data() );
 }
@@ -358,5 +358,5 @@
 int lua::state::load_stream( std::istream& stream, string_ref name )
 {
-	NV_LOG( nv::LOG_NOTICE, "Loading Lua stream '" << name << "'");
+	NV_LOG_NOTICE( "Loading Lua stream '", name, "'");
 	return load_string( std::string(
 		(std::istreambuf_iterator<char>(stream)),
@@ -366,5 +366,5 @@
 int lua::state::load_file( string_ref filename )
 {
-	NV_LOG( nv::LOG_NOTICE, "Loading Lua file '" << filename << "'");
+	NV_LOG_NOTICE( "Loading Lua file '", filename, "'");
 	return luaL_loadfile( m_state, filename.data() );
 }
@@ -376,5 +376,5 @@
 	if (result)
 	{
-		NV_LOG( nv::LOG_WARNING, "Failed to load string " << name << ": " << lua_tostring(m_state, -1));
+		NV_LOG_WARNING( "Failed to load string ", name, ": ", lua_tostring(m_state, -1));
 		return false;
 	}
@@ -388,5 +388,5 @@
 	if (result)
 	{
-		NV_LOG( nv::LOG_WARNING, "Failed to open stream " << name << ": " << lua_tostring(m_state, -1));
+		NV_LOG_WARNING( "Failed to open stream ", name, ": ", lua_tostring( m_state, -1 ) );
 		return false;
 	}
@@ -400,5 +400,5 @@
 	if (result) 
 	{
-		NV_LOG( nv::LOG_WARNING, "Failed to open file " << filename << ": " << lua_tostring(m_state, -1));
+		NV_LOG_WARNING( "Failed to open file ", filename, ": ", lua_tostring( m_state, -1 ) );
 		return false;
 	}
@@ -411,5 +411,5 @@
 	if (result) 
 	{
-		NV_LOG( nv::LOG_WARNING, "Failed to run script " << name << ": " << lua_tostring(m_state, -1));
+		NV_LOG_WARNING( "Failed to run script ", name, ": ", lua_tostring( m_state, -1 ) );
 		lua_pop( m_state, 1 );
 	}
@@ -425,8 +425,8 @@
 {
 	int top = lua_gettop(m_state);
-	NV_LOG( LOG_DEBUG, "Stack dump (" << top << ")");
+	NV_LOG_DEBUG( "Stack dump (", top, ")");
 	for ( int i = 0; i < top; ++i )
 	{
-		NV_LOG( LOG_DEBUG, "#" << i+1 << " - " << lua_typename(m_state, lua_type(m_state, i+1) ) << " = " << nlua_typecontent(m_state, i+1) );
+		NV_LOG_DEBUG( "#", i+1, " - ", lua_typename(m_state, lua_type(m_state, i+1) ), " = ", nlua_typecontent(m_state, i+1) );
 	}
 }
Index: /trunk/src/sdl/sdl_audio.cc
===================================================================
--- /trunk/src/sdl/sdl_audio.cc	(revision 364)
+++ /trunk/src/sdl/sdl_audio.cc	(revision 365)
@@ -21,5 +21,5 @@
 	if ( SDL_Init( SDL_INIT_AUDIO ) == -1 ) 
 	{
-		NV_LOG( LOG_CRITICAL, "SDL_AUDIO failed to load -- " << SDL_GetError() );
+		NV_LOG_CRITICAL( "SDL_AUDIO failed to load -- ", SDL_GetError() );
 		return;
 	}
@@ -27,5 +27,5 @@
 	if( Mix_OpenAudio( 44100, MIX_DEFAULT_FORMAT, 2, 1024 ) == -1 )
 	{
-		NV_LOG( LOG_CRITICAL, "SDL_mixer failed to load -- " << Mix_GetError() );
+		NV_LOG_CRITICAL( "SDL_mixer failed to load -- ", Mix_GetError() );
 		return;
 	}
@@ -40,5 +40,5 @@
 		if ( channel == -1 )
 		{
-			NV_LOG( LOG_WARNING, "SDL_mixer failed to play -- " << Mix_GetError() );
+			NV_LOG_WARNING( "SDL_mixer failed to play -- ", Mix_GetError() );
 		}
 		else
@@ -67,5 +67,5 @@
 		if ( channel == -1 )
 		{
-			NV_LOG( LOG_WARNING, "SDL_mixer failed to play -- " << Mix_GetError() );
+			NV_LOG_WARNING( "SDL_mixer failed to play -- ", Mix_GetError() );
 		}
 		else
@@ -91,10 +91,10 @@
 	if ( Mix_LoadWAV_RW == nullptr || SDL_RWFromFile == nullptr ) 
 	{
-		NV_LOG( LOG_ERROR, "SDL_mixer not loaded!" );
+		NV_LOG_ERROR( "SDL_mixer not loaded!" );
 	}
 	Mix_Chunk *sample = Mix_LoadWAV_RW(SDL_RWFromFile(a_path.c_str(), "rb"), 1);
 	if ( sample == nullptr )
 	{
-		NV_LOG( LOG_ERROR, "SDL_mixer failed to load sample '" << a_path << "' -- " << Mix_GetError() );
+		NV_LOG_ERROR( "SDL_mixer failed to load sample '", a_path, "' -- ", Mix_GetError() );
 		return sound();
 	}
Index: /trunk/src/sdl/sdl_window.cc
===================================================================
--- /trunk/src/sdl/sdl_window.cc	(revision 364)
+++ /trunk/src/sdl/sdl_window.cc	(revision 365)
@@ -38,5 +38,5 @@
 	if ( m_handle == 0 )
 	{
-		NV_LOG( LOG_CRITICAL, "Video mode set failed: " << SDL_GetError( ) );
+		NV_LOG_CRITICAL( "Video mode set failed: ", SDL_GetError( ) );
 		return; // TODO: Error report
 	}
@@ -56,13 +56,13 @@
 	if ( ctx_handle == 0 )
 	{
-		NV_LOG( LOG_CRITICAL, "GL Context creation failed: " << SDL_GetError( ) );
+		NV_LOG_CRITICAL( "GL Context creation failed: ", SDL_GetError( ) );
 		return; // TODO: Error report
 	}
 
 	nv::load_gl_library();
-	NV_LOG( LOG_INFO, "OpenGL Vendor       : " << glGetString(GL_VENDOR) );
-	NV_LOG( LOG_INFO, "OpenGL Renderer     : " << glGetString(GL_RENDERER) );
-	NV_LOG( LOG_INFO, "OpenGL Version      : " << glGetString(GL_VERSION) );
-	NV_LOG( LOG_INFO, "OpenGL GLSL Version : " << glGetString(GL_SHADING_LANGUAGE_VERSION) );
+	NV_LOG_INFO( "OpenGL Vendor       : ", (const char*)glGetString(GL_VENDOR) );
+	NV_LOG_INFO( "OpenGL Renderer     : ", (const char*)glGetString( GL_RENDERER ) );
+	NV_LOG_INFO( "OpenGL Version      : ", (const char*)glGetString( GL_VERSION ) );
+	NV_LOG_INFO( "OpenGL GLSL Version : ", (const char*)glGetString( GL_SHADING_LANGUAGE_VERSION ) );
 	SDL_GL_SetSwapInterval(1);
 
Index: /trunk/src/sdl/sdl_window_manager.cc
===================================================================
--- /trunk/src/sdl/sdl_window_manager.cc	(revision 364)
+++ /trunk/src/sdl/sdl_window_manager.cc	(revision 365)
@@ -19,5 +19,5 @@
 	if ( SDL_Init(0) < 0 )
 	{
-		NV_LOG( LOG_CRITICAL, "SDL initialization failed: " << SDL_GetError( ) );
+		NV_LOG_CRITICAL( "SDL initialization failed: ", SDL_GetError( ) );
 		return; // TODO: Error report
 	}
