Procházet zdrojové kódy

Add a stripped-down version of a bunch of core files from crown engine

Daniele Bartolini před 12 roky
rodič
revize
67793fa5da

+ 212 - 0
tools/core/Args.cpp

@@ -0,0 +1,212 @@
+/*
+Copyright (c) 2013 Daniele Bartolini, Michele Rossi
+Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <cstdio>
+
+#include "Args.h"
+
+namespace crown
+{
+
+//-----------------------------------------------------------------------------
+Args::Args(int argc, char** argv, const char* shortopts, const ArgsOption* longopts) :
+	m_argc(argc),
+	m_argv(argv),
+	m_shortopts(shortopts),
+	m_longopts(longopts),
+	m_optind(1),				// Do not consider argv[0]
+	m_scope(argc),
+	m_optarg(NULL)
+{
+	CE_ASSERT(argv != NULL, "Argument vector must be != NULL");
+	CE_ASSERT(shortopts != NULL, "Short argument list must be != NULL");
+	// longopts could be NULL
+}
+
+//-----------------------------------------------------------------------------
+Args::~Args()
+{
+}
+
+//-----------------------------------------------------------------------------
+int32_t Args::getopt()
+{
+	// Always reset optarg
+	m_optarg = NULL;
+
+	// End of arguments
+	if (m_optind >= m_scope)
+	{
+		return -1;
+	}
+
+	switch (option_type(m_argv[m_optind]))
+	{
+		case AOT_SHORT:
+		{
+			return short_option(m_argv[m_optind]);
+		}
+		case AOT_LONG:
+		{
+			return long_option(m_argv[m_optind]);
+		}
+		case AOT_NOT_OPTION:
+		{
+			not_option();
+			return getopt();
+		}
+		default:
+		{
+			return '?';
+		}
+	}
+}
+
+//-----------------------------------------------------------------------------
+int32_t Args::optind() const
+{
+	return m_optind;
+}
+
+//-----------------------------------------------------------------------------
+const char* Args::optarg() const
+{
+	return m_optarg;
+}
+
+//-----------------------------------------------------------------------------
+void Args::set_optind(int32_t index)
+{
+	m_optind = index;
+}
+
+//-----------------------------------------------------------------------------
+ArgsOptionType Args::option_type(const char* option)
+{
+	const size_t option_len = string::strlen(option);
+
+	if (option_len == 2 && option[0] == '-' && option[1] != '-')
+	{
+		return AOT_SHORT;
+	}
+	else if (option_len > 2 && option[0] == '-' && option[1] == '-')
+	{
+		return AOT_LONG;
+	}
+
+	return AOT_NOT_OPTION;
+}
+
+//-----------------------------------------------------------------------------
+int32_t Args::long_option(const char* option)
+{
+	const ArgsOption* current_option = m_longopts;
+
+	// Loop through all the long options
+	while (!end_of_longopts(current_option))
+	{
+		if (string::strcmp(current_option->name, &option[2]) == 0)
+		{
+			// If the option requires an argument
+			if (current_option->has_arg == AOA_REQUIRED_ARGUMENT)
+			{
+				// Read the argument if it exists
+				if ((m_optind + 1) < m_scope)
+				{
+					// Read the argument and skip the following parameter
+					m_optarg = m_argv[m_optind + 1];
+					m_optind += 2;
+				}
+				else
+				{
+					printf("%s: option requires an argument -- '%s'", m_argv[0], current_option->name);
+
+					// Missing option
+					m_optind += 1;
+					return '?';
+				}
+			}
+			// If the option does not require an argument
+			else
+			{
+				m_optind++;
+			}
+
+			if (current_option->flag == NULL)
+			{
+				return current_option->val;
+			}
+			else
+			{
+				(*current_option->flag) = current_option->val;
+
+				return 0;
+			}
+		}
+
+		current_option++;
+	}
+
+	// Found a long option but was not included in longopts
+	printf("%s: invalid option -- '%s'", m_argv[0], &option[2]);
+	m_optind++;
+	return '?';
+}
+
+//-----------------------------------------------------------------------------
+int32_t Args::short_option(const char* option)
+{
+	(void)option;
+
+	printf("%s: invalid option -- '%s'", m_argv[0], &option[1]);
+	m_optind++;
+	return '?';
+}
+
+//-----------------------------------------------------------------------------
+void Args::not_option()
+{
+	char* current_option = m_argv[m_optind];
+
+	for (int32_t i = m_optind; i < (m_argc - 1); i++)
+	{
+		m_argv[i] = m_argv[i + 1];
+	}
+
+	m_argv[m_argc - 1] = current_option;
+
+	// Reduce the number of true arguments
+	m_scope--;
+}
+
+//-----------------------------------------------------------------------------
+bool Args::end_of_longopts(const ArgsOption* option) const
+{
+	return (option->name == NULL && option->has_arg == 0 && option->flag == NULL && option->val == 0);
+}
+
+
+} // namespace crown

+ 123 - 0
tools/core/Args.h

@@ -0,0 +1,123 @@
+/*
+Copyright (c) 2013 Daniele Bartolini, Michele Rossi
+Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#pragma once
+
+#include "Assert.h"
+#include "Types.h"
+#include "String.h"
+
+namespace crown
+{
+
+enum ArgsOptionType
+{
+	AOT_SHORT,
+	AOT_LONG,
+	AOT_NOT_OPTION
+};
+
+enum ArgsOptionArgument
+{
+	AOA_NO_ARGUMENT,
+	AOA_REQUIRED_ARGUMENT
+};
+
+struct ArgsOption
+{
+	const char*				name;
+	int32_t					has_arg;
+	int32_t*				flag;
+	int32_t					val;
+};
+
+/// Parses the command line arguments in a way very similar to GNU getopt.
+class Args
+{
+public:
+
+						Args(int argc, char** argv, const char* shortopts, const ArgsOption* longopts);
+						~Args();
+
+	/// Finds the next option character and returns it.
+	/// If there are no more option characters, it returns -1 and optind()
+	/// returns the index in argv[] of the first argv-element that is not
+	/// an option.
+	/// If it finds an option that was not included in shortopts or longopts,
+	/// or if it finds a missing option argument, it returns '?' character.
+	int32_t				getopt();
+
+	/// Returns the index of the next argument to be processed.
+	int32_t				optind() const;
+
+	/// Returns the text of the following argv-element in respect
+	/// to the current optind().
+	const char*			optarg() const;
+
+
+	/// Sets the @a index into argv[] from where to start option scanning.
+	/// If @a index >= argc nothing will be scanned.
+	void				set_optind(int32_t index);
+
+private:
+
+	// Returns the @a option type
+	// Returns AOT_SHORT if option is of the form "-x" where 'x' is the option.
+	// Returns AOT_LONG if option is of the form "--option" where "option" is the option.
+	// Returns AOT_NOT_OPTION in all other cases.
+	ArgsOptionType		option_type(const char* option);
+
+	// Parses a long option
+	int32_t				long_option(const char* option);
+
+	// Parses a short option
+	int32_t				short_option(const char* option);
+
+	void				not_option();
+
+	// Returns whether the given option is the last one
+	bool				end_of_longopts(const ArgsOption* option) const;
+
+private:
+
+	int					m_argc;
+	char**				m_argv;
+
+	const char*			m_shortopts;
+	const ArgsOption*	m_longopts;
+
+	// Index of the next argument to be processed
+	int32_t				m_optind;
+
+	// Number of "true" arguments
+	int32_t				m_scope;
+
+	// The text of the following argv-element to argv[optind]
+	char*				m_optarg;
+};
+
+} // namespace crown
+

+ 43 - 0
tools/core/Assert.h

@@ -0,0 +1,43 @@
+/*
+Copyright (c) 2013 Daniele Bartolini, Michele Rossi
+Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <cstdlib>
+#include <cstdio>
+#include "Config.h"
+
+#pragma once
+
+#ifdef CROWN_DEBUG
+	#define CE_ERROR(file, line, message, ...) do { printf(message, __VA_ARGS__);\
+				printf("\n\tIn %s:%d\n\n", file, line); abort(); } while(0)
+	#define CE_ASSERT(condition, message, ...) do { if (!(condition)) { CE_ERROR(__FILE__, __LINE__,\
+				"Assertion failed: %s\n\t" message, #condition, ##__VA_ARGS__); } } while(0)
+#else
+	#define CE_ASSERT(condition, message, ...) ((void)0)
+#endif
+
+#define CE_ASSERT_NOT_NULL(x) CE_ASSERT(x != NULL, "Parameter must be not null")
+

+ 30 - 0
tools/core/Types.h

@@ -0,0 +1,30 @@
+/*
+Copyright (c) 2013 Daniele Bartolini, Michele Rossi
+Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#pragma once
+
+#include <cstddef>
+#include <cstdint>

+ 102 - 0
tools/core/strings/Hash.h

@@ -0,0 +1,102 @@
+/*
+Copyright (c) 2013 Daniele Bartolini, Michele Rossi
+Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#pragma once
+
+#include "Assert.h"
+#include "Types.h"
+
+namespace crown
+{
+
+/// String hashing.
+namespace hash
+{
+
+//-----------------------------------------------------------------------------
+/// MurmurHash2, by Austin Appleby
+///
+/// @note
+/// This code makes a few assumptions about how your machine behaves
+///
+/// 1. We can read a 4-byte value from any address without crashing
+/// 2. sizeof(int) == 4
+///
+/// And it has a few limitations -
+///
+/// 1. It will not work incrementally.
+/// 2. It will not produce the same results on little-endian and big-endian
+///    machines.
+inline uint32_t murmur2_32(const void* key, size_t len, uint32_t seed)
+{
+	CE_ASSERT(key != NULL, "Key must be != NULL");
+
+	// 'm' and 'r' are mixing constants generated offline.
+	// They're not really 'magic', they just happen to work well.
+	const unsigned int m = 0x5bd1e995;
+	const int r = 24;
+
+	// Initialize the hash to a 'random' value
+	unsigned int h = seed ^ len;
+
+	// Mix 4 bytes at a time into the hash
+	const unsigned char * data = (const unsigned char *)key;
+
+	while(len >= 4)
+	{
+		unsigned int k = *(unsigned int *)data;
+
+		k *= m; 
+		k ^= k >> r; 
+		k *= m; 
+		
+		h *= m; 
+		h ^= k;
+
+		data += 4;
+		len -= 4;
+	}
+	
+	// Handle the last few bytes of the input array
+	switch(len)
+	{
+		case 3: h ^= data[2] << 16;
+		case 2: h ^= data[1] << 8;
+		case 1: h ^= data[0];
+	        h *= m;
+	};
+
+	// Do a few final mixes of the hash to ensure the last few
+	// bytes are well-incorporated.
+	h ^= h >> 13;
+	h *= m;
+	h ^= h >> 15;
+
+	return h;
+}
+
+} // namespace hash
+} // namespace crown

+ 300 - 0
tools/core/strings/Path.h

@@ -0,0 +1,300 @@
+/*
+Copyright (c) 2013 Daniele Bartolini, Michele Rossi
+Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#pragma once
+
+#include "String.h"
+
+namespace crown
+{
+namespace path
+{
+
+bool is_valid_segment(const char* segment);
+bool is_valid_path(const char* path);
+bool is_absolute_path(const char* path);
+
+void pathname(const char* path, char* str, size_t len);
+void filename(const char* path, char* str, size_t len);
+void basename(const char* path, char* str, size_t len);
+void extension(const char* path, char* str, size_t len);
+void filename_without_extension(const char* path, char* str, size_t len);
+
+//bool segments(const char* path, List<Str>& ret);
+inline void strip_trailing_separator(const char* path, char* ret, size_t len);
+
+/// Returns whether the segment is valid.
+/// @note
+/// The rules for valid segments are as follows:
+/// a) The empty string is not valid.
+/// b) Any string containing the slash character ('/') is not valid.
+/// c) Common notations for current ('.') and parent ('..') directory are forbidden.
+/// d) Any string containing segment or device separator characters on the local file system,
+/// such as the backslash ('\') and colon (':') on some file systems.
+/// (Thanks org.eclipse.core.runtime for the documentation ;D).
+inline bool is_valid_segment(const char* segment)
+{
+	CE_ASSERT(segment != NULL, "Segment must be != NULL");
+	
+	size_t segment_len = string::strlen(segment);
+
+	// Empty segment is not valid
+	if (segment_len == 0)
+	{
+		return false;
+	}
+
+	// Segments containing only '.' are non valid
+	if (segment_len == 1 && segment[0] == '.')
+	{
+		return false;
+	}
+
+	// Segments containing only ".." are not valid
+	if (segment_len == 2 && segment[0] == '.' && segment[1] == '.')
+	{
+		return false;
+	}
+
+	// The segment does not have to contain any forward slashes ('/')
+	// nor back slashes ('\'), nor colon signs (':')
+	for (size_t i = 0; i < segment_len; i++)
+	{
+		if (segment[i] == '/' ||
+			segment[i] == '\\' ||
+			segment[i] == ':')
+		{
+			return false;
+		}
+	}
+
+	return true;
+}
+
+/// Returns whether the path is valid.
+/// @note
+/// The rules for valid paths are as follows:
+/// a) The empty string is not valid.
+/// b) If the path is absolute, it mustn't contain any leading character.
+inline bool is_valid_path(const char* path)
+{
+	(void)path;
+//	size_t path_len = string::strlen(path);
+
+//	if (pathLen == 0)
+//	{
+//		return false;
+//	}
+
+//	if (is_root_path(path))
+//	{
+//		return true;
+//	}
+
+//	List<Str> segmentList;
+//	if (!get_segments(Str(path), segmentList))
+//	{
+//		return false;
+//	}
+
+//	size_t i = 0;
+//	if (IsAbsolutePath(path) && path[0] != '/')
+//	{
+//		i = 1;
+//	}
+
+//	for (; i < segmentList.GetSize(); i++)
+//	{
+//		if (!IsValidSegment(segmentList[i].c_str()))
+//		{
+//			return false;
+//		}
+//	}
+
+	return true;
+}
+
+/// Returns the pathname of the path.
+/// @note
+/// e.g. "/home/project/texture.tga" -> "/home/project"
+/// e.g. "/home/project" -> "/home"
+/// e.g. "/home" -> "/"
+/// e.g. "home" -> ""
+/// 
+/// The @a path must be valid.
+inline void pathname(const char* path, char* str, size_t len)
+{
+	CE_ASSERT(path != NULL, "Path must be != NULL");
+	CE_ASSERT(str != NULL, "Str must be != NULL");
+
+	const char* last_separator = string::find_last(path, '/');
+
+	if (last_separator == string::end(path))
+	{
+		string::strncpy(str, "", len);
+	}
+	else
+	{
+		string::substring(string::begin(path), last_separator, str, len);
+	}
+}
+
+/// Returns the filename of the path.
+/// @note
+/// e.g. "/home/project/texture.tga" -> "texture.tga"
+/// e.g. "/home/project/texture" -> "texture"
+/// e.g. "/home -> "home"
+/// e.g. "/" -> ""
+///
+/// The @a path must be valid.
+inline void filename(const char* path, char* str, size_t len)
+{
+	CE_ASSERT(path != NULL, "Path must be != NULL");
+	CE_ASSERT(str != NULL, "Str must be != NULL");
+
+	const char* last_separator = string::find_last(path, '/');
+	
+	if (last_separator == string::end(path))
+	{
+		string::strncpy(str, "", len);
+	}
+	else
+	{
+		string::substring(last_separator + 1, string::end(path), str, len);
+	}
+}
+
+/// Returns the basename of the path.
+/// @note
+/// e.g. "/home/project/texture.tga" -> "texture"
+/// e.g. "/home/project" -> "project"
+/// e.g. "/" -> ""
+///
+/// The @a path must be valid.
+inline void basename(const char* path, char* str, size_t len)
+{
+	CE_ASSERT(path != NULL, "Path must be != NULL");
+	CE_ASSERT(str != NULL, "Str must be != NULL");
+
+	const char* last_separator = string::find_last(path, '/');
+	const char* last_dot = string::find_last(path, '.');
+
+	if (last_separator == string::end(path) && last_dot != string::end(path))
+	{
+		string::substring(string::begin(path), last_dot, str, len);
+	}
+	else if (last_separator != string::end(path) && last_dot == string::end(path))
+	{
+		string::substring(last_separator + 1, string::end(path), str, len);
+	}
+	else if (last_separator == string::end(path) && last_dot == string::end(path))
+	{
+		string::strncpy(str, path, len);
+	}
+	else
+	{
+		string::substring(last_separator + 1, last_dot, str, len);
+	}
+}
+
+/// Returns the extension of the path.
+/// @note
+/// e.g. "/home/project/texture.tga" -> "tga"
+/// e.g. "/home/project.x/texture" -> ""
+///
+/// The @a path must be valid.
+inline void extension(const char* path, char* str, size_t len)
+{
+	CE_ASSERT(path != NULL, "Path must be != NULL");
+	CE_ASSERT(str != NULL, "Str must be != NULL");
+	
+	const char* last_dot = string::find_last(path, '.');
+	
+	if (last_dot == string::end(path))
+	{
+		string::strncpy(str, "", len);
+	}
+	else
+	{
+		string::substring(last_dot + 1, string::end(path), str, len);
+	}
+}
+
+/// Returns the filename without the extension.
+/// @note
+/// e.g. "/home/project/texture.tga" -> "/home/project/texture"
+/// e.g. "/home/project/texture" -> "/home/project/texture"
+///
+/// The @a path must be valid.
+inline void filename_without_extension(const char* path, char* str, size_t len)
+{
+	CE_ASSERT(path != NULL, "Path must be != NULL");
+	CE_ASSERT(str != NULL, "Str must be != NULL");
+	
+	const char* last_dot = string::find_last(path, '.');
+	
+	string::substring(string::begin(path), last_dot, str, len);
+}
+
+/// Returns the segments contained in path.
+//bool segments(const char* path, List<Str>& ret)
+//{
+//	path.Split(os::PATH_SEPARATOR, ret);
+
+//	if (ret.GetSize() > 0)
+//	{
+//		return true;
+//	}
+
+//	return false;
+//}
+
+/// Fills 'ret' with the same path but without the trailing directory separator.
+/// @note
+/// e.g. "/home/project/texture.tga/" -> "/home/project/texture.tga"
+/// e.g. "/home/project/texture.tga" -> "/home/project/texture.tga"
+///
+/// The @a path must be valid.
+inline void strip_trailing_separator(const char* path, char* str, size_t len)
+{
+	CE_ASSERT(path != NULL, "Path must be != NULL");
+	CE_ASSERT(str != NULL, "Str must be != NULL");
+	
+	size_t path_len = string::strlen(path);
+	
+	if (path[path_len - 1] == '/')
+	{
+		string::substring(string::begin(path), string::end(path) - 2, str, len);
+	}
+	else
+	{
+		string::substring(string::begin(path), string::end(path), str, len);
+	}
+}
+
+} // namespace path
+} // namespace crown
+

+ 227 - 0
tools/core/strings/String.h

@@ -0,0 +1,227 @@
+/*
+Copyright (c) 2013 Daniele Bartolini, Michele Rossi
+Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#pragma once
+
+#include <cstdio>
+#include <cstring>
+
+#include "Assert.h"
+#include "Types.h"
+
+namespace crown
+{
+namespace string
+{
+
+const char* const	EMPTY = "";
+
+bool				is_alpha(char c);
+bool				is_digit(char c);
+bool				is_upper(char c);
+bool				is_lower(char c);
+bool				is_whitespace(char c);
+
+size_t				strlen(const char* str);
+const char*			strstr(const char* str1, const char* str2);
+int32_t				strcmp(const char* str1, const char* str2);
+char*				strcpy(char* dest, const char* src);
+char*				strncpy(char* dest, const char* src, size_t len);
+char* 				strcat(char* dest, const char* src);
+char*				strncat(char* dest, const char* src, size_t len);
+
+const char*			begin(const char* str);
+const char*			end(const char* str);
+
+const char*			find_first(const char* str, char c);
+const char*			find_last(const char* str, char c);
+void				substring(const char* begin, const char* end, char* out, size_t len);
+
+//-----------------------------------------------------------------------------
+inline bool is_alpha(char c)
+{
+	return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
+}
+
+//-----------------------------------------------------------------------------
+inline bool is_digit(char c)
+{
+	return !(c < '0' || c > '9');
+}
+
+//-----------------------------------------------------------------------------
+inline bool is_upper(char c)
+{
+	return (c >= 'A' && c <= 'Z');
+}
+
+//-----------------------------------------------------------------------------
+inline bool is_lower(char c)
+{
+	return (c >= 'a' && c <= 'z');
+}
+
+//-----------------------------------------------------------------------------
+inline bool is_whitespace(char c)
+{
+	return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
+}
+
+//-----------------------------------------------------------------------------
+inline size_t strlen(const char* str)
+{
+	size_t chars = 0;
+
+	while(*str)
+	{
+		if ((*str & 0xC0) != 0x80)
+		{
+			chars++;
+		}
+		str++;
+	}
+
+	return chars;
+}
+
+//-----------------------------------------------------------------------------
+inline const char* strstr(const char* str1, const char* str2)
+{
+	return ::strstr(str1, str2);
+}
+
+//-----------------------------------------------------------------------------
+inline int32_t strcmp(const char* str1, const char* str2)
+{
+	return ::strcmp(str1, str2);
+}
+
+//-----------------------------------------------------------------------------
+inline char* strcpy(char* dest, const char* src)
+{
+	return ::strcpy(dest, src);
+}
+
+//-----------------------------------------------------------------------------
+inline char* strncpy(char* dest, const char* src, size_t len)
+{
+	return ::strncpy(dest, src, len);
+}
+
+//-----------------------------------------------------------------------------
+inline char* strcat(char* dest, const char* src)
+{
+	return ::strcat(dest, src);
+}
+
+//-----------------------------------------------------------------------------
+inline char* strncat(char* dest, const char* src, size_t len)
+{
+	return ::strncat(dest, src, len);
+}
+
+//-----------------------------------------------------------------------------
+inline const char* begin(const char* str)
+{
+	CE_ASSERT(str != NULL, "Str must be != NULL");
+	
+	return str;
+}
+
+//-----------------------------------------------------------------------------
+inline const char* end(const char* str)
+{
+	CE_ASSERT(str != NULL, "Str must be != NULL");
+	
+	return str + string::strlen(str) + 1;
+}
+
+//-----------------------------------------------------------------------------
+inline const char* find_first(const char* str, char c)
+{
+	CE_ASSERT(str != NULL, "Str must be != NULL");
+
+	const char* str_begin = string::begin(str);
+	
+	while (str_begin != string::end(str))
+	{
+		if ((*str_begin) == c)
+		{
+			return str_begin;
+		}
+		
+		str_begin++;
+	}
+	
+	return string::end(str);
+}
+
+//-----------------------------------------------------------------------------
+inline const char* find_last(const char* str, char c)
+{
+	CE_ASSERT(str != NULL, "Str must be != NULL");
+	
+	const char* str_end = string::end(str) - 1;
+	
+	while (str_end != string::begin(str) - 1)
+	{
+		if ((*str_end) == c)
+		{
+			return str_end;
+		}
+		
+		str_end--;
+	}
+	
+	return string::end(str);
+}
+
+//-----------------------------------------------------------------------------
+inline void substring(const char* begin, const char* end, char* out, size_t len)
+{
+	CE_ASSERT(begin != NULL, "Begin must be != NULL");
+	CE_ASSERT(end != NULL, "End must be != NULL");
+	CE_ASSERT(out != NULL, "Out must be != NULL");
+	
+	size_t i = 0;
+	
+	char* out_iterator = out;
+
+	while (begin != end && i < len)
+	{
+		(*out_iterator) = (*begin);
+		
+		begin++;
+		out_iterator++;
+		i++;
+	}
+
+	out[i] = '\0';
+}
+
+} // namespace string
+} // namespace crown
+