Index: trunk/src/formats/nmd_loader.cc
===================================================================
--- trunk/src/formats/nmd_loader.cc	(revision 441)
+++ trunk/src/formats/nmd_loader.cc	(revision 442)
@@ -6,5 +6,4 @@
 
 #include "nv/formats/nmd_loader.hh"
-#include "nv/io/std_stream.hh"
 #include "nv/stl/string.hh"
 #include "nv/interface/data_channel_access.hh"
Index: trunk/src/formats/obj_loader.cc
===================================================================
--- trunk/src/formats/obj_loader.cc	(revision 441)
+++ trunk/src/formats/obj_loader.cc	(revision 442)
@@ -6,8 +6,7 @@
 
 #include "nv/formats/obj_loader.hh"
-#include "nv/io/std_stream.hh"
 #include "nv/interface/data_channel_access.hh"
 
-#include <sstream>
+#include <cstdio>
 
 using namespace nv;
@@ -50,8 +49,6 @@
 	vector< vec2 > t;
 
-	std::string line;
-	std::string cmd;
-	std::string name;
-	std::string next_name;
+	string32 name;
+	string32 next_name;
 
 	nv::size_t size;
@@ -59,5 +56,5 @@
 
 	obj_reader();
-	bool read_stream( std::istream& stream );
+	bool read_stream( stream& str );
 	virtual nv::size_t add_face( uint32* vi, uint32* ti, uint32* ni, nv::size_t count ) = 0;
 	virtual nv::size_t raw_size() const = 0;
@@ -79,46 +76,45 @@
 }
 
-bool obj_reader::read_stream( std::istream& stream )
-{
-	name = next_name;
+bool obj_reader::read_stream( stream& str )
+{
+	name.assign( next_name );
 	bool added_faces = false;
 	f32 x, y, z;
 	if ( eof ) return false;
-
-	while ( std::getline( stream, line ) )
-	{
-		if ( line.length() < 3 || line[0] == '#' )
-		{
-			continue;
-		}
-
-		std::istringstream ss(line);
-		ss >> cmd;
-
-		if ( cmd == "v" )
-		{
-			ss >> x >> y >> z;
+	char buffer[256];
+	while ( str.gets( buffer, 256 ) )
+	{
+		string_view cline( static_cast<const char*>( buffer ) );
+		cline = nv::trimmed( cline );
+
+		if ( cline.length() < 3 || cline[0] == '#' )
+		{
+			continue;
+		}
+
+		if ( cline.starts_with( "vn " ) )
+		{
+			sscanf( cline.data(), "vn %f %f %f", &x, &y, &z );
+			n.push_back( vec3( x, y, z ) );
+			continue;
+		}
+
+		if ( cline.starts_with( "vt " ) )
+		{
+			sscanf( cline.data(), "vt %f %f", &x, &y );
+			t.push_back( vec2( x, 1.0f - y ) );
+			continue;
+		}
+
+		if ( cline.starts_with( "v " ) )
+		{
+			sscanf( cline.data(), "v %f %f %f", &x, &y, &z );
 			v.push_back( vec3( x, y, z ) );
 			continue;
 		}
 
-		if ( cmd == "vn" )
-		{
-			ss >> x >> y >> z;
-			n.push_back( vec3( x, y, z ) );
-			continue;
-		}
-
-		if ( cmd == "vt" )
-		{
-			ss >> x >> y;
-			t.push_back( vec2( x, 1.0f - y ) );
-			continue;
-		}
-
-		if ( cmd == "f" )
+		if ( cline.starts_with( "f " ) )
 		{
 			added_faces = true;
-			ss >> cmd;
 
 			uint32 vis[8];
@@ -128,19 +124,25 @@
 			uint32 count = 0;
 
-			while ( !ss.fail() )
+			string_view scan( cline );
+			scan.remove_prefix( 2 );
+			size_t pos = 0;
+			while ( pos != string_view::npos )
 			{
-				char ch;
-
-				std::istringstream ss2( cmd );
-				ss2 >> vis[count] >> ch;
-				ss2 >> tis[count] >> ch;
-				if ( ch == '/')
+				scan.remove_prefix( pos );
+				scan = nv::trimmed( scan );
+				if ( sscanf( scan.data(), "%u/%u/%u", &vis[count], &tis[count], &nis[count] ) == 3 )
 				{
 					normals = true;
-					ss2 >> nis[count];
 				}
-
-				ss >> cmd;
+				else if ( !normals && sscanf( scan.data(), "%u/%u", &vis[count], &tis[count] ) == 2 )
+				{
+
+				}
+				else
+				{
+					break;
+				}
 				count++;
+				pos = scan.find_first_of( "\t " );
 			}
 
@@ -149,14 +151,14 @@
 		}
 
-		if ( cmd == "g" )
-		{
-			ss >> next_name;
+		if ( cline.starts_with( "g " ) )
+		{
+			next_name.assign( nv::trimmed( cline.without_prefix( 2 ) ) );
 			if (added_faces) 
 				return true;
-			name = next_name;
-			continue;
-		}
-
-		if ( cmd == "s" )
+			name.assign( next_name );
+			continue;
+		}
+
+		if ( cline.starts_with( "s " ) )
 		{
 			continue;
@@ -317,7 +319,5 @@
 	else
 		reader = new mesh_data_reader_vt();
-	std_stream sstream( &source );
-
-	while ( reader->read_stream( sstream ) )
+	while ( reader->read_stream( source ) )
 	{
 		if ( m_tangents )
@@ -328,5 +328,5 @@
 		data_channel_set* result = data_channel_set_creator::create_set( 1 );
 		data_channel_set_creator raccess( result );
-		raccess.set_name( make_name( reader->name.c_str() ) );
+		raccess.set_name( make_name( reader->name ) );
 		uint8* rdata = raccess.add_channel( m_descriptor, reader->size * 3 ).raw_data();
 
Index: trunk/src/io/c_stream.cc
===================================================================
--- trunk/src/io/c_stream.cc	(revision 441)
+++ trunk/src/io/c_stream.cc	(revision 442)
@@ -47,4 +47,12 @@
 }
 
+bool c_stream::gets( char* buffer, size_t max_count )
+{
+	NV_ASSERT( buffer != nullptr && max_count != 0, "Bad parameter passed to write!" );
+	char* result = ::fgets( buffer, max_count, reinterpret_cast<FILE*>( m_file ) );
+	if ( !result ) return false;
+	return true;
+}
+
 bool c_stream::seek( long offset, origin orig )
 {
Index: trunk/src/io/std_stream.cc
===================================================================
--- trunk/src/io/std_stream.cc	(revision 441)
+++ 	(revision )
@@ -1,62 +1,0 @@
-// Copyright (C) 2012-2015 ChaosForge Ltd
-// http://chaosforge.org/
-//
-// This file is part of Nova libraries. 
-// For conditions of distribution and use, see copying.txt file in root folder.
-//
-// TODO: support for write operations, see http://www.mr-edd.co.uk/blog/beginners_guide_streambuf
-
-#include "nv/io/std_stream.hh"
-#include "nv/stl/math.hh"
-#include "nv/stl/utility.hh"
-
-using namespace nv;
-
-std_streambuf::std_streambuf( stream* source, bool owner /*= false*/, std::size_t bsize /*= 256*/, std::size_t put_back /*= 8 */ )
-	: m_stream( source )
-	, m_owner( owner )
-	, m_buffer( nv::max(bsize, put_back) + put_back )
-	, m_put_back( nv::max( put_back, std::size_t( 1 ) ) )
-{
-	char *end = &m_buffer.front() + m_buffer.size();
-	setg( end, end, end );
-}
-
-std_streambuf::~std_streambuf()
-{
-	if ( m_owner )
-	{
-		delete m_stream;
-	}
-}
-
-std_streambuf::int_type std_streambuf::underflow()
-{
-	if (gptr() < egptr())
-	{
-		// buffer not exhausted
-		return traits_type::to_int_type(*gptr());
-	}
-
-	char *base = &m_buffer.front();
-	char *start = base;
-
-	if (eback() == base) // true when this isn't the first fill
-	{
-		// Make arrangements for putback characters
-		std::copy( egptr() - m_put_back, egptr(), base ); 
-		start += m_put_back;
-	}
-
-	// start is now the start of the buffer, proper.
-	size_t n = m_stream->read( start, 1, m_buffer.size() - static_cast<size_t>(start - base) );
-	if (n == 0)
-	{
-		return traits_type::eof();
-	}
-
-	// Set buffer pointers
-	setg(base, start, start + n);
-
-	return traits_type::to_int_type(*gptr());
-}
Index: trunk/src/stl/string.cc
===================================================================
--- trunk/src/stl/string.cc	(revision 441)
+++ trunk/src/stl/string.cc	(revision 442)
@@ -11,6 +11,4 @@
 #include <cstdio>
 #include <cstdlib>
-#include <fstream> // for slurp only
-#include <sstream> // for slurp only
 
 using namespace nv;
