Browse Source

Merge branch 'master' of github.com:taylor001/crown

Daniele Bartolini 10 năm trước cách đây
mục cha
commit
8cbebd295c
44 tập tin đã thay đổi với 384 bổ sung424 xóa
  1. 3 4
      README.md
  2. 3 2
      docs/doxygen/Doxyfile.doxygen
  3. 22 0
      docs/doxygen/header.html
  4. 0 5
      docs/lua_api.txt
  5. BIN
      docs/shots/console.png
  6. BIN
      docs/shots/level-editor.png
  7. BIN
      docs/shots/node-editor.png
  8. 1 1
      makefile
  9. 2 2
      src/compilers/bundle_compiler.cpp
  10. 21 2
      src/compilers/compile_options.h
  11. 0 2
      src/config.h
  12. 18 30
      src/core/filesystem/apk_file.cpp
  13. 13 16
      src/core/filesystem/apk_file.h
  14. 12 6
      src/core/filesystem/apk_filesystem.cpp
  15. 14 16
      src/core/filesystem/apk_filesystem.h
  16. 16 86
      src/core/filesystem/disk_file.cpp
  17. 9 17
      src/core/filesystem/disk_file.h
  18. 18 6
      src/core/filesystem/disk_filesystem.cpp
  19. 16 15
      src/core/filesystem/disk_filesystem.h
  20. 11 40
      src/core/filesystem/file.h
  21. 6 3
      src/core/filesystem/filesystem.h
  22. 12 0
      src/core/filesystem/filesystem_types.h
  23. 11 31
      src/core/filesystem/null_file.h
  24. 48 27
      src/core/filesystem/os_file.h
  25. 38 2
      src/core/functional.h
  26. 1 0
      src/core/math/aabb.h
  27. 1 0
      src/core/math/color4.h
  28. 0 2
      src/core/math/math_types.h
  29. 0 16
      src/core/math/matrix3x3.h
  30. 0 17
      src/core/math/matrix4x4.h
  31. 1 0
      src/core/math/quaternion.cpp
  32. 1 0
      src/core/math/sphere.h
  33. 29 25
      src/core/os.h
  34. 1 0
      src/core/profiler.h
  35. 8 0
      src/core/strings/dynamic_string.h
  36. 6 3
      src/core/strings/path.cpp
  37. 2 2
      src/core/strings/path.h
  38. 20 24
      src/device.cpp
  39. 3 0
      src/input/input_manager.h
  40. 5 10
      src/renderers/debug_line.cpp
  41. 2 2
      src/renderers/debug_line.h
  42. 6 6
      src/renderers/shader.cpp
  43. 2 2
      src/resource/resource_loader.cpp
  44. 2 2
      src/resource/texture_resource.cpp

+ 3 - 4
README.md

@@ -4,7 +4,6 @@ Lightweight and flexible cross-platform game engine.
 I'm an independent developer and your contributions are invaluable to me. If you like the work I do, please consider supporting Crown development by means of a small contribution. I'm also available for hire to work on or with Crown or somewhat related technologies/projects.
 
 [![PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6FQMPUQQ8KQKW)
-[![Flattr](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/thing/4945485/Crown)
 
 ##What is it?
 
@@ -22,13 +21,13 @@ It is loosely inspired by Bitsquid (now Stingray) engine and its design principl
 
 Lua API: http://taylor001.github.io/crown/lua_api.html
 
-C++ API: http://taylor001.github.io/crown/doxygen/
+C++ API: http://taylor001.github.io/crown/doxygen/modules
 
 ##Screenshots
 
-TCP/IP console with autocomplete and color-coded output highlighting.
+[WIP] Level editor.
 
-![console](https://raw.githubusercontent.com/taylor001/crown/master/docs/shots/console.png)
+![level-editor](https://raw.githubusercontent.com/taylor001/crown/master/docs/shots/level-editor.png)
 
 [WIP] Node editor.
 

+ 3 - 2
docs/Doxyfile.doxygen → docs/doxygen/Doxyfile.doxygen

@@ -857,7 +857,8 @@ EXCLUDE_SYMLINKS       = YES
 
 EXCLUDE_PATTERNS       = */third/* \
                          */tests/* \
-                         */src/core/compat/*
+                         */src/core/compat/* \
+                         */src/main/*
 
 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
 # (namespaces, classes, functions, etc.) that should be excluded from the
@@ -1098,7 +1099,7 @@ HTML_FILE_EXTENSION    = .html
 # of the possible markers and block names see the documentation.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
-HTML_HEADER            =
+HTML_HEADER            = docs/doxygen/header.html
 
 # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
 # generated HTML page. If the tag is left blank doxygen will generate a standard

+ 22 - 0
docs/doxygen/header.html

@@ -0,0 +1,22 @@
+<!-- HTML header for doxygen 1.8.10-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen $doxygenversion"/>
+<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
+<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
+<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="$relpath^jquery.js"></script>
+<script type="text/javascript" src="$relpath^dynsections.js"></script>
+$treeview
+$search
+$mathjax
+<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
+$extrastylesheet
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+
+<!-- end header part -->

+ 0 - 5
docs/lua_api.txt

@@ -890,8 +890,6 @@ Mouse
 
 	**axis** (id) : Vector3
 		Returns the value of the axis *id*.
-		The returned vector holds x and y coordinates of the pointer
-		in window-space.
 
 	**button_id** (name) : int
 		Returns the *id* of the button *name*.
@@ -940,8 +938,6 @@ Touch
 
 	**axis** (id) : Vector3
 		Returns the value of the axis *id*.
-		The returned vector holds x and y coordinates of the pointer
-		in window-space.
 
 Pad1, Pad2, Pad3, Pad4
 ----------------------
@@ -972,7 +968,6 @@ Pad1, Pad2, Pad3, Pad4
 
 	**axis** (id) : Vector3
 		Returns the value of the axis *id*.
-		The returned vector holds values in the range [-1;+1]
 
 	**button_id** (name) : int
 		Returns the *id* of the button *name*.

BIN
docs/shots/console.png


BIN
docs/shots/level-editor.png


BIN
docs/shots/node-editor.png


+ 1 - 1
makefile

@@ -55,7 +55,7 @@ windows-release64: windows-build
 
 .PHONY: docs
 docs:
-	doxygen docs/Doxyfile.doxygen
+	doxygen docs/doxygen/Doxyfile.doxygen
 	rst2html2 --stylesheet=html4css1.css,docs/style.css docs/lua_api.txt build/docs/lua_api.html
 
 .PHONY: clean

+ 2 - 2
src/compilers/bundle_compiler.cpp

@@ -113,10 +113,10 @@ bool BundleCompiler::compile(const char* type, const char* name, const char* pla
 	{
 		CompileOptions opts(_source_fs, output, platform, &buf);
 		compile(_type, src_path.c_str(), opts);
-		File* outf = _bundle_fs.open(path.c_str(), FOM_WRITE);
+		File* outf = _bundle_fs.open(path.c_str(), FileOpenMode::WRITE);
 		uint32_t size = array::size(output);
 		uint32_t written = outf->write(array::begin(output), size);
-		_bundle_fs.close(outf);
+		_bundle_fs.close(*outf);
 		success = size == written;
 	}
 	else

+ 21 - 2
src/compilers/compile_options.h

@@ -6,7 +6,10 @@
 #pragma once
 
 #include "filesystem.h"
+#include "file.h"
 #include "log.h"
+#include "vector.h"
+#include "temp_allocator.h"
 #include <setjmp.h>
 
 #define RESOURCE_COMPILER_ASSERT(condition, opts, msg, ...) do { if (!(condition))\
@@ -22,6 +25,7 @@ struct CompileOptions
 		, _output(output)
 		, _platform(platform)
 		, _jmpbuf(buf)
+		, _dependencies(default_allocator())
 	{
 	}
 
@@ -41,12 +45,14 @@ struct CompileOptions
 
 	Buffer read(const char* path)
 	{
-		File* file = _fs.open(path, FOM_READ);
+		add_dependency(path);
+
+		File* file = _fs.open(path, FileOpenMode::READ);
 		uint32_t size = file->size();
 		Buffer buf(default_allocator());
 		array::resize(buf, size);
 		file->read(array::begin(buf), size);
-		_fs.close(file);
+		_fs.close(*file);
 		return buf;
 	}
 
@@ -81,10 +87,23 @@ struct CompileOptions
 		return _platform;
 	}
 
+	const Vector<DynamicString>& dependencies() const
+	{
+		return _dependencies;
+	}
+
+	void add_dependency(const char* path)
+	{
+		TempAllocator256 ta;
+		DynamicString dep(path, ta);
+		vector::push_back(_dependencies, dep);
+	}
+
 	Filesystem& _fs;
 	Buffer& _output;
 	const char* _platform;
 	jmp_buf* _jmpbuf;
+	Vector<DynamicString> _dependencies;
 };
 
 } // namespace crown

+ 0 - 2
src/config.h

@@ -3,8 +3,6 @@
  * License: https://github.com/taylor001/crown/blob/master/LICENSE
  */
 
-// Adapted from Branimir Karadžić's config.h (https://github.com/bkaradzic/bx)
-
 #pragma once
 
 #include "platform.h"

+ 18 - 30
src/core/filesystem/apk_file.cpp

@@ -16,17 +16,26 @@
 namespace crown
 {
 
-ApkFile::ApkFile(AAssetManager* asset_manager, const char* path)
-	: File(FOM_READ)
+ApkFile::ApkFile(AAssetManager* asset_manager)
+	: _asset_manager(asset_manager)
 	, _asset(NULL)
 {
-	_asset = AAssetManager_open(asset_manager, path, AASSET_MODE_RANDOM);
-	CE_ASSERT(_asset != NULL, "AAssetManager_open: failed to open %s", path);
 }
 
 ApkFile::~ApkFile()
 {
-	if (_asset != NULL)
+	close();
+}
+
+void ApkFile::open(const char* path, FileOpenMode::Enum /*mode*/)
+{
+	_asset = AAssetManager_open(_asset_manager, path, AASSET_MODE_RANDOM);
+	CE_ASSERT(_asset != NULL, "AAssetManager_open: failed to open %s", path);
+}
+
+void ApkFile::close()
+{
+	if (_asset)
 	{
 		AAsset_close(_asset);
 		_asset = NULL;
@@ -54,24 +63,18 @@ void ApkFile::skip(uint32_t bytes)
 	CE_UNUSED(seek_result);
 }
 
-uint32_t ApkFile::read(void* buffer, uint32_t size)
+uint32_t ApkFile::read(void* data, uint32_t size)
 {
-	CE_ASSERT_NOT_NULL(buffer);
-	return (uint32_t)AAsset_read(_asset, buffer, size);
+	CE_ASSERT_NOT_NULL(data);
+	return (uint32_t)AAsset_read(_asset, data, size);
 }
 
-uint32_t ApkFile::write(const void* /*buffer*/, uint32_t /*size*/)
+uint32_t ApkFile::write(const void* /*data*/, uint32_t /*size*/)
 {
 	CE_ASSERT(false, "Apk files are read only!");
 	return 0;
 }
 
-bool ApkFile::copy_to(File& /*file*/, uint32_t /*size = 0*/)
-{
-	CE_ASSERT(false, "Not implemented");
-	return false;
-}
-
 void ApkFile::flush()
 {
 	// Not needed
@@ -97,21 +100,6 @@ uint32_t ApkFile::position()
 	return (uint32_t)(AAsset_getLength(_asset) - AAsset_getRemainingLength(_asset));
 }
 
-bool ApkFile::can_read() const
-{
-	return true;
-}
-
-bool ApkFile::can_write() const
-{
-	return false;
-}
-
-bool ApkFile::can_seek() const
-{
-	return true;
-}
-
 } // namespace crown
 
 #endif // CROWN_PLATFORM_ANDROID

+ 13 - 16
src/core/filesystem/apk_file.h

@@ -12,14 +12,22 @@
 namespace crown
 {
 
+/// Provides common facilities to access Android APK files.
+///
+/// @ingroup Filesystem
 class ApkFile : public File
 {
 public:
 
-	/// Opens the given @a filename.
-	ApkFile(AAssetManager* asset_manager, const char* filename);
+	ApkFile(AAssetManager* asset_manager);
 	~ApkFile();
 
+	/// @copydoc File::open()
+	void open(const char* path, FileOpenMode::Enum mode);
+
+	/// @copydoc File::close()
+	void close();
+
 	/// @copydoc File::seek()
 	void seek(uint32_t position);
 
@@ -30,13 +38,10 @@ public:
 	void skip(uint32_t bytes);
 
 	/// @copydoc File::read()
-	uint32_t read(void* buffer, uint32_t size);
+	uint32_t read(void* data, uint32_t size);
 
 	/// @copydoc File::write()
-	uint32_t write(const void* buffer, uint32_t size);
-
-	/// @copydoc File::copy_to()
-	bool copy_to(File& file, uint32_t size = 0);
+	uint32_t write(const void* data, uint32_t size);
 
 	/// @copydoc File::flush()
 	void flush();
@@ -53,17 +58,9 @@ public:
 	/// @copydoc File::position()
 	uint32_t position();
 
-	/// @copydoc File::can_read()
-	bool can_read() const;
-
-	/// @copydoc File::can_write()
-	bool can_write() const;
-
-	/// @copydoc File::can_seek()
-	bool can_seek() const;
-
 private:
 
+	AAssetManager* _asset_manager;
 	AAsset* _asset;
 };
 

+ 12 - 6
src/core/filesystem/apk_filesystem.cpp

@@ -20,17 +20,18 @@ ApkFilesystem::ApkFilesystem(AAssetManager* asset_manager)
 {
 }
 
-File* ApkFilesystem::open(const char* path, FileOpenMode mode)
+File* ApkFilesystem::open(const char* path, FileOpenMode::Enum mode)
 {
 	CE_ASSERT_NOT_NULL(path);
-	CE_ASSERT(mode == FOM_READ, "Cannot open for writing in Android assets folder");
-	return CE_NEW(default_allocator(), ApkFile)(_asset_manager, path);
+	CE_ASSERT(mode == FileOpenMode::READ, "Cannot open for writing in Android assets folder");
+	ApkFile* file = CE_NEW(default_allocator(), ApkFile)(_asset_manager);
+	file->open(path, mode);
+	return file;
 }
 
-void ApkFilesystem::close(File* file)
+void ApkFilesystem::close(File& file)
 {
-	CE_ASSERT_NOT_NULL(file);
-	CE_DELETE(default_allocator(), file);
+	CE_DELETE(default_allocator(), &file);
 }
 
 bool ApkFilesystem::exists(const char* path)
@@ -48,6 +49,11 @@ bool ApkFilesystem::is_file(const char* path)
 	return true;
 }
 
+uint64_t ApkFilesystem::last_modified_time(const char* path)
+{
+	return 0;
+}
+
 void ApkFilesystem::create_directory(const char* /*path*/)
 {
 	CE_ASSERT(false, "Attempt to create directory in Android assets folder");

+ 14 - 16
src/core/filesystem/apk_filesystem.h

@@ -25,41 +25,39 @@ public:
 	ApkFilesystem(AAssetManager* asset_manager);
 
 	/// @copydoc Filesystem::open()
-	/// @note
-	/// @a mode can only be FOM_READ
-	File* open(const char* rel_path, FileOpenMode mode);
+	File* open(const char* path, FileOpenMode::Enum mode);
 
 	/// @copydoc Filesystem::close()
-	void close(File* file);
+	void close(File& file);
 
-	/// Returns always false under Android.
+	/// @copydoc Filesystem::exists()
 	bool exists(const char* path);
 
-	/// Returns always false under Android.
+	/// @copydoc Filesystem::is_directory()
 	bool is_directory(const char* path);
 
-	/// Returns always false under Android.
+	/// @copydoc Filesystem::is_file()
 	bool is_file(const char* path);
 
-	/// Stub method, assets folder is read-only.
+	/// @copydoc Filesystem::last_modified_time()
+	uint64_t last_modified_time(const char* path);
+
+	/// @copydoc Filesystem::create_directory()
 	void create_directory(const char* path);
 
-	/// Stub method, assets folder is read-only.
+	/// @copydoc Filesystem::delete_directory()
 	void delete_directory(const char* path);
 
-	/// Stub method, assets folder is read-only.
+	/// @copydoc Filesystem::create_file()
 	void create_file(const char* path);
 
-	/// Stub method, assets folder is read-only.
+	/// @copydoc Filesystem::delete_file()
 	void delete_file(const char* path);
 
-	/// @copydoc Filesystem::list_files().
+	/// @copydoc Filesystem::list_files()
 	void list_files(const char* path, Vector<DynamicString>& files);
 
-	/// Returns the absolute path of the given @a path.
-	/// @note
-	/// Assets folder has no concept of "absolute path", all paths are
-	/// relative to the assets folder itself, so, all paths are returned unchanged.
+	/// @copydoc Filesystem::get_absolute_path()
 	void get_absolute_path(const char* path, DynamicString& os_path);
 
 private:

+ 16 - 86
src/core/filesystem/disk_file.cpp

@@ -11,14 +11,21 @@
 namespace crown
 {
 
-DiskFile::DiskFile(FileOpenMode mode, const char* path)
-	: File(mode)
-	, _last_was_read(true)
+DiskFile::DiskFile()
 {
-	_file.open(path, mode);
 }
 
 DiskFile::~DiskFile()
+{
+	close();
+}
+
+void DiskFile::open(const char* path, FileOpenMode::Enum mode)
+{
+	_file.open(path, mode);
+}
+
+void DiskFile::close()
 {
 	_file.close();
 }
@@ -26,86 +33,31 @@ DiskFile::~DiskFile()
 void DiskFile::seek(uint32_t position)
 {
 	check_valid();
-
 	_file.seek(position);
 }
 
 void DiskFile::seek_to_end()
 {
 	check_valid();
-
 	_file.seek_to_end();
 }
 
 void DiskFile::skip(uint32_t bytes)
 {
 	check_valid();
-
 	_file.skip(bytes);
 }
 
-uint32_t DiskFile::read(void* buffer, uint32_t size)
+uint32_t DiskFile::read(void* data, uint32_t size)
 {
 	check_valid();
-
-	if (!_last_was_read)
-	{
-		_last_was_read = true;
-		_file.seek(0);
-	}
-
-	return _file.read(buffer, size);
+	return _file.read(data, size);
 }
 
-uint32_t DiskFile::write(const void* buffer, uint32_t size)
+uint32_t DiskFile::write(const void* data, uint32_t size)
 {
 	check_valid();
-
-	if (_last_was_read)
-	{
-		_last_was_read = false;
-		_file.seek(0);
-	}
-
-	return _file.write(buffer, size);
-}
-
-bool DiskFile::copy_to(File& file, uint32_t size)
-{
-	check_valid();
-
-	const uint32_t chunksize = 1024*1024;
-
-	char* buff = (char*) default_allocator().allocate(chunksize * sizeof(char));
-
-	uint32_t tot_read_bytes = 0;
-
-	while (tot_read_bytes < size)
-	{
-		uint32_t expected_read_bytes = std::min(size - tot_read_bytes, chunksize);
-		uint32_t read_bytes = _file.read(buff, expected_read_bytes);
-
-		if (read_bytes < expected_read_bytes)
-		{
-			if (_file.eof())
-			{
-				if (read_bytes != 0)
-				{
-					file.write(buff, read_bytes);
-				}
-			}
-
-			default_allocator().deallocate(buff);
-			//Either the file gave an error, or ended before size bytes could be copied
-			return false;
-		}
-
-		file.write(buff, read_bytes);
-		tot_read_bytes += read_bytes;
-	}
-
-	default_allocator().deallocate(buff);
-	return true;
+	return _file.write(data, size);
 }
 
 bool DiskFile::end_of_file()
@@ -121,41 +73,19 @@ bool DiskFile::is_valid()
 void DiskFile::flush()
 {
 	check_valid();
-
-	// FIXME implement flush in File
+	_file.flush();
 }
 
 uint32_t DiskFile::position()
 {
 	check_valid();
-
 	return _file.position();
 }
 
 uint32_t DiskFile::size()
 {
 	check_valid();
-
 	return _file.size();
 }
 
-bool DiskFile::can_read() const
-{
-	check_valid();
-
-	return true;
-}
-
-bool DiskFile::can_write() const
-{
-	check_valid();
-
-	return true;
-}
-
-bool DiskFile::can_seek() const
-{
-	return true;
-}
-
 } // namespace crown

+ 9 - 17
src/core/filesystem/disk_file.h

@@ -19,10 +19,15 @@ class DiskFile: public File
 {
 public:
 
-	/// Opens @a path with specified @a mode
-	DiskFile(FileOpenMode mode, const char* path);
+	DiskFile();
 	virtual ~DiskFile();
 
+	/// @copydoc File::open()
+	void open(const char* path, FileOpenMode::Enum mode);
+
+	/// @copydoc File::close()
+	void close();
+
 	/// @copydoc File::seek()
 	void seek(uint32_t position);
 
@@ -33,13 +38,10 @@ public:
 	void skip(uint32_t bytes);
 
 	/// @copydoc File::read()
-	uint32_t read(void* buffer, uint32_t size);
+	uint32_t read(void* data, uint32_t size);
 
 	/// @copydoc File::write()
-	uint32_t write(const void* buffer, uint32_t size);
-
-	/// @copydoc File::copy_to()
-	bool copy_to(File& file, uint32_t size = 0);
+	uint32_t write(const void* data, uint32_t size);
 
 	/// @copydoc File::flush()
 	void flush();
@@ -56,19 +58,9 @@ public:
 	/// @copydoc File::position()
 	uint32_t position();
 
-	/// @copydoc File::can_read()
-	bool can_read() const;
-
-	/// @copydoc File::can_write()
-	bool can_write() const;
-
-	/// @copydoc File::can_seek()
-	bool can_seek() const;
-
 protected:
 
 	OsFile _file;
-	bool _last_was_read;
 
 protected:
 

+ 18 - 6
src/core/filesystem/disk_filesystem.cpp

@@ -4,6 +4,7 @@
  */
 
 #include "disk_filesystem.h"
+#include "file.h"
 #include "string_utils.h"
 #include "temp_allocator.h"
 #include "disk_file.h"
@@ -27,7 +28,7 @@ DiskFilesystem::DiskFilesystem(const char* prefix)
 {
 }
 
-File* DiskFilesystem::open(const char* path, FileOpenMode mode)
+File* DiskFilesystem::open(const char* path, FileOpenMode::Enum mode)
 {
 	CE_ASSERT_NOT_NULL(path);
 
@@ -35,14 +36,14 @@ File* DiskFilesystem::open(const char* path, FileOpenMode mode)
 	DynamicString abs_path(alloc);
 	get_absolute_path(path, abs_path);
 
-	return CE_NEW(default_allocator(), DiskFile)(mode, abs_path.c_str());
+	DiskFile* file = CE_NEW(default_allocator(), DiskFile)();
+	file->open(abs_path.c_str(), mode);
+	return file;
 }
 
-void DiskFilesystem::close(File* file)
+void DiskFilesystem::close(File& file)
 {
-	CE_ASSERT_NOT_NULL(file);
-
-	CE_DELETE(default_allocator(), file);
+	CE_DELETE(default_allocator(), &file);
 }
 
 bool DiskFilesystem::exists(const char* path)
@@ -78,6 +79,17 @@ bool DiskFilesystem::is_file(const char* path)
 	return os::is_file(abs_path.c_str());
 }
 
+uint64_t DiskFilesystem::last_modified_time(const char* path)
+{
+	CE_ASSERT_NOT_NULL(path);
+
+	TempAllocator256 alloc;
+	DynamicString abs_path(alloc);
+	get_absolute_path(path, abs_path);
+
+	return os::mtime(abs_path.c_str());
+}
+
 void DiskFilesystem::create_directory(const char* path)
 {
 	CE_ASSERT_NOT_NULL(path);

+ 16 - 15
src/core/filesystem/disk_filesystem.h

@@ -31,39 +31,40 @@ public:
 	/// The @a prefix must be absolute.
 	DiskFilesystem(const char* prefix);
 
-	/// Opens the file at the given @a path with the given @a mode.
-	File* open(const char* path, FileOpenMode mode);
+	/// @copydoc Filesystem::open()
+	File* open(const char* path, FileOpenMode::Enum mode);
 
-	/// Closes the given @a file.
-	void close(File* file);
+	/// @copydoc Filesystem::close()
+	void close(File& file);
 
-	/// Returns whether @a path exists.
+	/// @copydoc Filesystem::exists()
 	bool exists(const char* path);
 
-	/// Returns true if @a path is a directory.
+	/// @copydoc Filesystem::is_directory()
 	bool is_directory(const char* path);
 
-	/// Returns true if @a path is a regular file.
+	/// @copydoc Filesystem::is_file()
 	bool is_file(const char* path);
 
-	/// Creates the directory at the given @a path.
+	/// @copydoc Filesystem::last_modified_time()
+	uint64_t last_modified_time(const char* path);
+
+	/// @copydoc Filesystem::create_directory()
 	void create_directory(const char* path);
 
-	/// Deletes the directory at the given @a path.
+	/// @copydoc Filesystem::delete_directory()
 	void delete_directory(const char* path);
 
-	/// Creates the file at the given @a path.
+	/// @copydoc Filesystem::create_file()
 	void create_file(const char* path);
 
-	/// Deletes the file at the given @a path.
+	/// @copydoc Filesystem::delete_file()
 	void delete_file(const char* path);
 
-	/// Returns the relative file names in the given @a path.
+	/// @copydoc Filesystem::list_files()
 	void list_files(const char* path, Vector<DynamicString>& files);
 
-	/// Returns the absolute path of the given @a path based on
-	/// the root path of the file source. If @a path is absolute,
-	/// the given path is returned.
+	/// @copydoc Filesystem::get_absolute_path()
 	void get_absolute_path(const char* path, DynamicString& os_path);
 
 private:

+ 11 - 40
src/core/filesystem/file.h

@@ -6,34 +6,26 @@
 #pragma once
 
 #include "types.h"
+#include "filesystem_types.h"
 
 namespace crown
 {
 
-enum FileOpenMode
-{
-	FOM_READ		= 1,
-	FOM_WRITE		= 2
-};
-
-class Compressor;
-
 /// An abstraction to access data files.
 ///
-/// It represents a flow of data attached to a 'file' which can be an archived file,
-/// a regular file, a location in memory or anything that can be read or wrote.
-/// A File is an abstraction to interact with these in an uniform way; every file
-/// comes with a convenient set of methods to facilitate reading from it, writing to
-/// it and so on.
-///
 /// @ingroup Filesystem
 class File
 {
 public:
 
-	/// Opens the file with the given @a mode
-	File(FileOpenMode mode) : _open_mode(mode) {}
-	virtual ~File() {};
+	File() {}
+	virtual ~File() {}
+
+	/// Opens the file at @a path with specified @a mode
+	virtual void open(const char* path, FileOpenMode::Enum mode) = 0;
+
+	/// Closes the file.
+	virtual void close() = 0;
 
 	/// Sets the position indicator of the file to position.
 	virtual void seek(uint32_t position) = 0;
@@ -45,20 +37,12 @@ public:
 	virtual void skip(uint32_t bytes) = 0;
 
 	/// Reads a block of data from the file.
-	virtual uint32_t read(void* buffer, uint32_t size) = 0;
+	virtual uint32_t read(void* data, uint32_t size) = 0;
 
 	/// Writes a block of data to the file.
-	virtual uint32_t write(const void* buffer, uint32_t size) = 0;
-
-	/// Copies a chunk of 'size' bytes of data from this to another file.
-	virtual bool copy_to(File& file, uint32_t size = 0) = 0;
+	virtual uint32_t write(const void* data, uint32_t size) = 0;
 
 	/// Forces the previouses write operations to complete.
-	/// Generally, when a File is attached to a file,
-	/// write operations are not performed instantly, the output data
-	/// may be stored to a temporary buffer before making its way to
-	/// the file. This method forces all the pending output operations
-	/// to be written to the file.
 	virtual void flush() = 0;
 
 	/// Returns whether the file is valid.
@@ -77,19 +61,6 @@ public:
 	/// Generally, for binary data, it means the number of bytes
 	/// from the beginning of the file.
 	virtual uint32_t position() = 0;
-
-	/// Returns whether the file can be read.
-	virtual bool can_read() const = 0;
-
-	/// Returns whether the file can be wrote.
-	virtual bool can_write() const = 0;
-
-	/// Returns whether the file can be sought.
-	virtual bool can_seek() const = 0;
-
-protected:
-
-	FileOpenMode _open_mode;
 };
 
 } // namespace crown

+ 6 - 3
src/core/filesystem/filesystem.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include "file.h"
+#include "filesystem_types.h"
 #include "container_types.h"
 #include "dynamic_string.h"
 
@@ -26,10 +26,10 @@ public:
 	virtual ~Filesystem() {};
 
 	/// Opens the file at the given @a path with the given @a mode.
-	virtual File* open(const char* path, FileOpenMode mode) = 0;
+	virtual File* open(const char* path, FileOpenMode::Enum mode) = 0;
 
 	/// Closes the given @a file.
-	virtual void close(File* file) = 0;
+	virtual void close(File& file) = 0;
 
 	/// Returns whether @a path exists.
 	virtual bool exists(const char* path) = 0;
@@ -40,6 +40,9 @@ public:
 	/// Returns true if @a path is a regular file.
 	virtual bool is_file(const char* path) = 0;
 
+	/// Returns the time of last modify operaton to @a path.
+	virtual uint64_t last_modified_time(const char* path) = 0;
+
 	/// Creates the directory at the given @a path.
 	virtual void create_directory(const char* path) = 0;
 

+ 12 - 0
src/core/filesystem/filesystem_types.h

@@ -11,4 +11,16 @@ namespace crown
 class Filesystem;
 class File;
 
+/// Enumerates file open modes.
+///
+/// @ingroup Filesystem
+struct FileOpenMode
+{
+	enum Enum
+	{
+		READ,
+		WRITE
+	};
+};
+
 } // namespace crown

+ 11 - 31
src/core/filesystem/null_file.h

@@ -10,7 +10,6 @@
 namespace crown
 {
 
-
 /// Bit bucket file.
 /// Discards all data written to it and provides null data reading from it; plain and simple.
 ///
@@ -20,11 +19,17 @@ class NullFile: public File
 public:
 
 	/// @copydoc File::File()
-	NullFile(FileOpenMode mode) : File(mode) {}
+	NullFile() {}
 
 	/// @copydoc File::~File()
 	virtual ~NullFile() {}
 
+	/// @copydoc File::open()
+	void open(const char* /*path*/, FileOpenMode::Enum /*mode*/) {}
+
+	/// @copydoc File::close()
+	void close() {}
+
 	/// @copydoc File::seek()
 	void seek(uint32_t position) { (void)position; }
 
@@ -37,31 +42,21 @@ public:
 	/// @copydoc File::read()
 	/// @note
 	///	Fills buffer with zeroes
-	uint32_t read(void* buffer, uint32_t size)
+	uint32_t read(void* data, uint32_t size)
 	{
-		for (uint32_t i = 0; i < size; i++)
+		for (uint32_t i = 0; i < size; ++i)
 		{
-			((uint8_t*)buffer)[i] = 0;
+			((uint8_t*)data)[i] = 0;
 		}
 		return size;
 	}
 
 	/// @copydoc File::write()
-	uint32_t write(const void* /*buffer*/, uint32_t size)
+	uint32_t write(const void* /*data*/, uint32_t size)
 	{
 		return size;
 	}
 
-	/// @copydoc File::copy_to()
-	/// @note
-	///	Returns always true
-	bool copy_to(File& file, uint32_t size = 0)
-	{
-		char zero = 0;
-		file.write(&zero, size);
-		return true;
-	}
-
 	/// @copydoc File::flush()
 	void flush() {};
 
@@ -84,21 +79,6 @@ public:
 	/// @note
 	///	Returns always zero
 	uint32_t position() { return 0; }
-
-	/// @copydoc File::can_read()
-	/// @note
-	///	Returns always true
-	bool can_read() { return true; }
-
-	/// @copydoc File::can_write()
-	/// @note
-	///	Returns always true
-	bool can_write() { return true; }
-
-	/// @copydoc File::can_seek()
-	/// @note
-	///	Returns always true
-	bool can_seek() { return true; }
 };
 
 } // namespace crown

+ 48 - 27
src/core/filesystem/os_file.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include "file.h" // FileOpenMode
+#include "filesystem_types.h" // FileOpenMode
 #include "types.h"
 #include "error.h"
 #include "macros.h"
@@ -13,6 +13,7 @@
 
 #if CROWN_PLATFORM_POSIX
 	#include <stdio.h>
+	#include <errno.h>
 #elif CROWN_PLATFORM_WINDOWS
 	#include "tchar.h"
 	#include "win_headers.h"
@@ -42,20 +43,21 @@ public:
 		close();
 	}
 
-	void open(const char* path, FileOpenMode mode)
+	void open(const char* path, FileOpenMode::Enum mode)
 	{
 #if CROWN_PLATFORM_POSIX
-		_file = fopen(path, (mode == FOM_READ) ? "rb" : "wb");
-		CE_ASSERT(_file != NULL, "Unable to open file: %s", path);
+		_file = fopen(path, (mode == FileOpenMode::READ) ? "rb" : "wb");
+		CE_ASSERT(_file != NULL, "fopen: errno = %d", errno);
 #elif CROWN_PLATFORM_WINDOWS
 		_file = CreateFile(path
-			, mode == FOM_READ ? GENERIC_READ : GENERIC_WRITE
+			, (mode == FileOpenMode::READ) ? GENERIC_READ : GENERIC_WRITE
 			, 0
 			, NULL
 			, OPEN_ALWAYS
 			, FILE_ATTRIBUTE_NORMAL
-			, NULL);
-		CE_ASSERT(_file != INVALID_HANDLE_VALUE, "Unable to open file: %s", path);
+			, NULL
+			);
+		CE_ASSERT(_file != INVALID_HANDLE_VALUE, "CreateFile: GetLastError = %d", GetLastError());
 #endif
 	}
 
@@ -93,12 +95,12 @@ public:
 		size_t pos = position();
 
 		int err = fseek(_file, 0, SEEK_END);
-		CE_ASSERT(err == 0, "Failed to seek");
+		CE_ASSERT(err == 0, "fseek: errno = %d", errno);
 
 		size_t size = position();
 
 		err = fseek(_file, (long)pos, SEEK_SET);
-		CE_ASSERT(err == 0, "Failed to seek");
+		CE_ASSERT(err == 0, "fseek: errno = %d", errno);
 		CE_UNUSED(err);
 
 		return (uint32_t)size;
@@ -113,11 +115,13 @@ public:
 	{
 		CE_ASSERT(data != NULL, "Data must be != NULL");
 #if CROWN_PLATFORM_POSIX
-		return fread(data, 1, size, _file);
+		size_t bytes_read = fread(data, 1, size, _file);
+		CE_ASSERT(ferror(_file) == 0, "fread error");
+		return (uint32_t)bytes_read;
 #elif CROWN_PLATFORM_WINDOWS
 		DWORD bytes_read;
 		BOOL result = ReadFile(_file, data, size, &bytes_read, NULL);
-		CE_ASSERT(result == TRUE, "Unable to read from file");
+		CE_ASSERT(result == TRUE, "ReadFile: GetLastError = %d", GetLastError());
 
 		if (result && bytes_read == 0)
 			_eof = true;
@@ -132,24 +136,38 @@ public:
 	{
 		CE_ASSERT(data != NULL, "Data must be != NULL");
 #if CROWN_PLATFORM_POSIX
-		return fwrite(data, 1, size, _file);
+		size_t bytes_written = fwrite(data, 1, size, _file);
+		CE_ASSERT(ferror(_file) == 0, "fwrite error");
+		return (uint32_t)bytes_written;
 #elif CROWN_PLATFORM_WINDOWS
 		DWORD bytes_written;
 		WriteFile(_file, data, size, &bytes_written, NULL);
-		CE_ASSERT(size == bytes_written, "Cannot read from file\n");
-		return size;
+		CE_ASSERT(size == bytes_written, "WriteFile: GetLastError = %d", GetLastError());
+		return bytes_written;
 #endif
 	}
 
+	void flush()
+	{
+#if CROWN_PLATFORM_POSIX
+		int err = fflush(_file);
+		CE_ASSERT(err == 0, "fflush: errno = %d", errno);
+#elif CROWN_PLATFORM_WINDOWS
+		BOOL err = FlushFileBuffers(_file);
+		CE_ASSERT(err != 0, "FlushFileBuffers: GetLastError = %d", GetLastError());
+#endif
+		CE_UNUSED(err);
+	}
+
 	/// Moves the file pointer to the given @a position.
 	void seek(uint32_t position)
 	{
 #if CROWN_PLATFORM_POSIX
-		int err = fseek(_file, (long) position, SEEK_SET);
-		CE_ASSERT(err == 0, "Failed to seek");
+		int err = fseek(_file, (long)position, SEEK_SET);
+		CE_ASSERT(err == 0, "fseek: errno = %d", errno);
 #elif CROWN_PLATFORM_WINDOWS
 		DWORD err = SetFilePointer(_file, position, NULL, FILE_BEGIN);
-		CE_ASSERT(err != INVALID_SET_FILE_POINTER, "Failed to seek");
+		CE_ASSERT(err != INVALID_SET_FILE_POINTER, "SetFilePointer: GetLastError = %d", GetLastError());
 #endif
 		CE_UNUSED(err);
 	}
@@ -158,13 +176,13 @@ public:
 	void seek_to_end()
 	{
 #if CROWN_PLATFORM_POSIX
-		int fseek_result = fseek(_file, 0, SEEK_END);
-		CE_ASSERT(fseek_result == 0, "Failed to seek");
-		CE_UNUSED(fseek_result);
+		int err = fseek(_file, 0, SEEK_END);
+		CE_ASSERT(err == 0, "fseek: errno = %d", errno);
 #elif CROWN_PLATFORM_WINDOWS
-		DWORD seek_result = SetFilePointer(_file, 0, NULL, FILE_END);
-		CE_ASSERT(seek_result != INVALID_SET_FILE_POINTER, "Failed to seek to end");
+		DWORD err = SetFilePointer(_file, 0, NULL, FILE_END);
+		CE_ASSERT(err != INVALID_SET_FILE_POINTER, "SetFilePointer: GetLastError = %d", GetLastError());
 #endif
+		CE_UNUSED(err);
 	}
 
 	/// Moves the file pointer @a bytes bytes ahead the current
@@ -173,12 +191,12 @@ public:
 	{
 #if CROWN_PLATFORM_POSIX
 		int err = fseek(_file, bytes, SEEK_CUR);
-		CE_ASSERT(err == 0, "Failed to seek");
-		CE_UNUSED(err);
+		CE_ASSERT(err == 0, "fseek: errno = %d", errno);
 #elif CROWN_PLATFORM_WINDOWS
 		DWORD err = SetFilePointer(_file, bytes, NULL, FILE_CURRENT);
-		CE_ASSERT(err != INVALID_SET_FILE_POINTER, "Failed to skip");
+		CE_ASSERT(err != INVALID_SET_FILE_POINTER, "SetFilePointer: GetLastError = %d", GetLastError());
 #endif
+		CE_UNUSED(err);
 	}
 
 	/// Returns the position of the file pointer from the
@@ -186,12 +204,15 @@ public:
 	uint32_t position() const
 	{
 #if CROWN_PLATFORM_POSIX
-		return (uint32_t)ftell(_file);
+		long pos = ftell(_file);
+		CE_ASSERT(pos != -1, "ftell: errno = %d", errno);
+		return (uint32_t)pos;
 #elif CROWN_PLATFORM_WINDOWS
 		DWORD pos = SetFilePointer(_file, 0, NULL, FILE_CURRENT);
-		CE_ASSERT(pos != INVALID_SET_FILE_POINTER, "Failed to get position");
+		CE_ASSERT(pos != INVALID_SET_FILE_POINTER, "SetFilePointer: GetLastError = %d", GetLastError());
 		return (uint32_t)pos;
 #endif
+		CE_UNUSED(pos);
 	}
 
 	/// Returns whether the file pointer is at the end of the file.

+ 38 - 2
src/core/functional.h

@@ -9,11 +9,20 @@ namespace crown
 {
 
 template<typename T>
-struct less
+struct equal_to
 {
 	bool operator()(const T& a, const T& b) const
 	{
-		return a < b;
+		return a == b;
+	};
+};
+
+template<typename T>
+struct not_equal_to
+{
+	bool operator()(const T& a, const T& b) const
+	{
+		return a != b;
 	};
 };
 
@@ -26,4 +35,31 @@ struct greater
 	};
 };
 
+template<typename T>
+struct less
+{
+	bool operator()(const T& a, const T& b) const
+	{
+		return a < b;
+	};
+};
+
+template<typename T>
+struct greater_equal
+{
+	bool operator()(const T& a, const T& b) const
+	{
+		return a >= b;
+	};
+};
+
+template<typename T>
+struct less_equal
+{
+	bool operator()(const T& a, const T& b) const
+	{
+		return a <= b;
+	};
+};
+
 } // namespace crown

+ 1 - 0
src/core/math/aabb.h

@@ -5,6 +5,7 @@
 
 #pragma once
 
+#include "types.h"
 #include "math_types.h"
 #include "vector3.h"
 #include "matrix4x4.h"

+ 1 - 0
src/core/math/color4.h

@@ -5,6 +5,7 @@
 
 #pragma once
 
+#include "types.h"
 #include "math_types.h"
 
 namespace crown

+ 0 - 2
src/core/math/math_types.h

@@ -5,8 +5,6 @@
 
 #pragma once
 
-#include "types.h"
-
 namespace crown
 {
 

+ 0 - 16
src/core/math/matrix3x3.h

@@ -63,15 +63,6 @@ inline Matrix3x3& operator*=(Matrix3x3& a, float k)
 	return a;
 }
 
-inline Matrix3x3& operator/=(Matrix3x3& a, float k)
-{
-	const float inv_k = 1.0f / k;
-	a.x *= inv_k;
-	a.y *= inv_k;
-	a.z *= inv_k;
-	return a;
-}
-
 inline Matrix3x3& operator*=(Matrix3x3& a, const Matrix3x3& b)
 {
 	Matrix3x3 tmp;
@@ -119,13 +110,6 @@ inline Matrix3x3 operator*(float k, Matrix3x3 a)
 	return a;
 }
 
-/// Divides the matrix @a a by the scalar @a k.
-inline Matrix3x3 operator/(Matrix3x3 a, float k)
-{
-	a /= k;
-	return a;
-}
-
 /// Multiplies the matrix @a a by the vector @a v and returns the result.
 inline Vector3 operator*(const Vector3& v, const Matrix3x3& a)
 {

+ 0 - 17
src/core/math/matrix4x4.h

@@ -171,16 +171,6 @@ inline Matrix4x4& operator*=(Matrix4x4& a, float k)
 	return a;
 }
 
-inline Matrix4x4& operator/=(Matrix4x4& a, float k)
-{
-	const float inv_k = 1.0f / k;
-	a.x *= inv_k;
-	a.y *= inv_k;
-	a.z *= inv_k;
-	a.t *= inv_k;
-	return a;
-}
-
 inline Matrix4x4& operator*=(Matrix4x4& a, const Matrix4x4& b)
 {
 	Matrix4x4 tmp;
@@ -237,13 +227,6 @@ inline Matrix4x4 operator*(float k, Matrix4x4 a)
 	return a;
 }
 
-/// Divides the matrix @a a by the scalar @a k and returns the result.
-inline Matrix4x4 operator/(Matrix4x4 a, float k)
-{
-	a /= k;
-	return a;
-}
-
 /// Multiplies the matrix @a a by the vector @a v and returns the result.
 inline Vector3 operator*(const Vector3& v, const Matrix4x4& a)
 {

+ 1 - 0
src/core/math/quaternion.cpp

@@ -3,6 +3,7 @@
  * License: https://github.com/taylor001/crown/blob/master/LICENSE
  */
 
+#include "types.h"
 #include "quaternion.h"
 #include "error.h"
 

+ 1 - 0
src/core/math/sphere.h

@@ -5,6 +5,7 @@
 
 #pragma once
 
+#include "types.h"
 #include "math_types.h"
 #include "math_utils.h"
 #include "vector3.h"

+ 29 - 25
src/core/os.h

@@ -45,6 +45,7 @@ namespace os
 #endif
 	}
 
+	/// Returns whether the @a path exists.
 	inline bool exists(const char* path)
 	{
 #if CROWN_PLATFORM_POSIX
@@ -54,7 +55,7 @@ namespace os
 #endif
 	}
 
-	/// Returns whether the path is a directory.
+	/// Returns whether @a path is a directory.
 	inline bool is_directory(const char* path)
 	{
 #if CROWN_PLATFORM_POSIX
@@ -70,7 +71,7 @@ namespace os
 #endif
 	}
 
-	/// Returns whether the path is a regular file.
+	/// Returns whether @a path is a regular file.
 	inline bool is_file(const char* path)
 	{
 #if CROWN_PLATFORM_POSIX
@@ -115,7 +116,7 @@ namespace os
 #endif
 	}
 
-	/// Creates a regular file.
+	/// Creates a regular file named @a path.
 	inline void create_file(const char* path)
 	{
 #if CROWN_PLATFORM_POSIX
@@ -124,13 +125,20 @@ namespace os
 		CE_ASSERT(err == 0, "mknod: errno = %d", errno);
 		CE_UNUSED(err);
 #elif CROWN_PLATFORM_WINDOWS
-		HANDLE hfile = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+		HANDLE hfile = CreateFile(path
+			, GENERIC_READ | GENERIC_WRITE
+			, 0
+			, NULL
+			, CREATE_ALWAYS
+			, FILE_ATTRIBUTE_NORMAL
+			, NULL
+			);
 		CE_ASSERT(hfile != INVALID_HANDLE_VALUE, "CreateFile: GetLastError = %d", GetLastError());
 		CloseHandle(hfile);
 #endif
 	}
 
-	/// Deletes a regular file.
+	/// Deletes the file at @a path.
 	inline void delete_file(const char* path)
 	{
 #if CROWN_PLATFORM_POSIX
@@ -144,7 +152,7 @@ namespace os
 #endif
 	}
 
-	/// Creates a directory.
+	/// Creates a directory named @a path.
 	inline void create_directory(const char* path)
 	{
 #if CROWN_PLATFORM_POSIX
@@ -159,7 +167,7 @@ namespace os
 #endif
 	}
 
-	/// Deletes a directory.
+	/// Deletes the directory at @a path.
 	inline void delete_directory(const char* path)
 	{
 #if CROWN_PLATFORM_POSIX
@@ -173,10 +181,7 @@ namespace os
 #endif
 	}
 
-	/// Returns the list of @a files in the given @a dir directory. Optionally walks into
-	/// subdirectories whether @a recursive is true.
-	/// @note
-	/// Does not follow symbolic links.
+	/// Returns the list of @a files at the given @a path.
 	inline void list_files(const char* path, Vector<DynamicString>& files)
 	{
 #if CROWN_PLATFORM_POSIX
@@ -200,29 +205,25 @@ namespace os
 
 		closedir(dir);
 #elif CROWN_PLATFORM_WINDOWS
-		HANDLE file = INVALID_HANDLE_VALUE;
-		WIN32_FIND_DATA ffd;
-
 		TempAllocator1024 ta;
 		DynamicString cur_path(path, ta);
 		cur_path += "\\*";
 
-		file = FindFirstFile(cur_path.c_str(), &ffd);
+		WIN32_FIND_DATA ffd;
+		HANDLE file = FindFirstFile(cur_path.c_str(), &ffd);
+		if (file == INVALID_HANDLE_VALUE)
+			return;
 
 		do
 		{
-			CE_ASSERT(file != INVALID_HANDLE_VALUE, "Unable to list files. errono %d", GetLastError());
+			const char* fname = ffd.cFileName;
 
-			if (strcmp(ffd.cFileName, ".") == 0
-				|| strcmp(ffd.cFileName, "..") == 0)
-			{
+			if (!strcmp(fname, ".") || !strcmp(fname, ".."))
 				continue;
-			}
 
-			DynamicString filename(default_allocator());
-
-			filename = ffd.cFileName;
-			vector::push_back(files, filename);
+			TempAllocator512 ta;
+			DynamicString filename(fname, ta);
+			vector::push_back(files, fname);
 		}
 		while (FindNextFile(file, &ffd) != 0);
 
@@ -230,6 +231,7 @@ namespace os
 #endif
 	}
 
+	/// Returns the current working directory.
 	inline const char* getcwd(char* buf, uint32_t size)
 	{
 #if CROWN_PLATFORM_POSIX
@@ -240,6 +242,7 @@ namespace os
 #endif
 	}
 
+	/// Returns the value of the environment variable @a name.
 	inline const char* getenv(const char* name)
 	{
 #if CROWN_PLATFORM_POSIX
@@ -306,7 +309,8 @@ namespace os
 #endif
 	}
 
-	/// Executes a process.
+	/// Executes the process @a path with the given @a args and returns its exit code.
+	/// It fills @a output with stdout and stderr.
 	inline int execute_process(const char* path, const char* args, StringStream& output)
 	{
 #if CROWN_PLATFORM_POSIX

+ 1 - 0
src/core/profiler.h

@@ -5,6 +5,7 @@
 
 #pragma once
 
+#include "types.h"
 #include "math_types.h"
 
 namespace crown

+ 8 - 0
src/core/strings/dynamic_string.h

@@ -34,6 +34,9 @@ public:
 	bool operator==(const DynamicString& s) const;
 	bool operator==(const char* s) const;
 
+	/// Reserves space for at least @n characters.
+	void reserve(uint32_t n);
+
 	// Returns the length of the string.
 	uint32_t length() const;
 
@@ -133,6 +136,11 @@ inline bool DynamicString::operator==(const char* s) const
 	return strcmp(c_str(), s) == 0;
 }
 
+inline void DynamicString::reserve(uint32_t n)
+{
+	array::reserve(_data, n);
+}
+
 inline uint32_t DynamicString::length() const
 {
 	return (uint32_t)strlen(this->c_str());

+ 6 - 3
src/core/strings/path.cpp

@@ -31,11 +31,14 @@ namespace path
 #endif
 	}
 
-	void join(const char* p1, const char* p2, DynamicString& path)
+	void join(const char* a, const char* b, DynamicString& path)
 	{
-		path += p1;
+		const uint32_t la = strlen(a);
+		const uint32_t lb = strlen(b);
+		path.reserve(la + lb + 1);
+		path += a;
 		path += SEPARATOR;
-		path += p2;
+		path += b;
 	}
 
 	const char* basename(const char* path)

+ 2 - 2
src/core/strings/path.h

@@ -29,8 +29,8 @@ namespace path
 	/// Returns whether the @a path is the root path.
 	bool is_root_path(const char* path);
 
-	/// Appends path @a p2 to @a p1 and fills @a path with the result.
-	void join(const char* p1, const char* p2, DynamicString& path);
+	/// Appends path @a b to @a a and fills @a path with the result.
+	void join(const char* a, const char* b, DynamicString& path);
 
 	/// Returns the basename of the @a path.
 	/// @note

+ 20 - 24
src/device.cpp

@@ -72,19 +72,21 @@ void Device::init()
 	// Initialize
 	CE_LOGI("Initializing Crown Engine %s...", version());
 
+	profiler_globals::init();
+
 #if CROWN_PLATFORM_ANDROID
 	_bundle_filesystem = CE_NEW(_allocator, ApkFilesystem)(_device_options.asset_manager());
 #else
 	_bundle_filesystem = CE_NEW(_allocator, DiskFilesystem)(_device_options.bundle_dir());
 #endif // CROWN_PLATFORM_ANDROID
 
-	profiler_globals::init();
-
 	_resource_loader = CE_NEW(_allocator, ResourceLoader)(*_bundle_filesystem);
 	_resource_manager = CE_NEW(_allocator, ResourceManager)(*_resource_loader);
 
 	read_config();
 
+	_input_manager = CE_NEW(_allocator, InputManager)();
+
 	audio_globals::init();
 	physics_globals::init();
 
@@ -95,60 +97,54 @@ void Device::init()
 		, &_bgfx_allocator
 		);
 
-	CE_LOGD("Creating material manager...");
 	material_manager::init();
 	debug_line::init();
 
+	_boot_package = create_resource_package(_boot_package_id);
+	_boot_package->load();
+	_boot_package->flush();
+
 	_lua_environment = CE_NEW(_allocator, LuaEnvironment)();
 	_lua_environment->load_libs();
-
-	_input_manager = CE_NEW(_allocator, InputManager)();
-
-	CE_LOGD("Crown Engine initialized.");
-	CE_LOGD("Initializing Game...");
+	_lua_environment->execute((LuaResource*)_resource_manager->get(SCRIPT_TYPE, _boot_script_id));
+	_lua_environment->call_global("init", 0);
 
 	_is_init = true;
 	_is_running = true;
 	_last_time = os::clocktime();
 
-	_boot_package = create_resource_package(_boot_package_id);
-	_boot_package->load();
-	_boot_package->flush();
-
-	_lua_environment->execute((LuaResource*)_resource_manager->get(SCRIPT_TYPE, _boot_script_id));
-	_lua_environment->call_global("init", 0);
+	CE_LOGD("Engine initialized");
 }
 
 void Device::shutdown()
 {
 	CE_ASSERT(_is_init, "Engine is not initialized");
 
-	CE_DELETE(_allocator, _input_manager);
+	_is_running = false;
+	_is_init = false;
 
-	// Shutdowns the game
 	_lua_environment->call_global("shutdown", 0);
+	CE_DELETE(_allocator, _lua_environment);
 
 	_boot_package->unload();
 	destroy_resource_package(*_boot_package);
 
-	CE_DELETE(_allocator, _lua_environment);
-
-	CE_LOGD("Releasing material manager...");
 	debug_line::shutdown();
 	material_manager::shutdown();
 
-	CE_DELETE(_allocator, _resource_manager);
-	CE_DELETE(_allocator, _resource_loader);
-
 	bgfx::shutdown();
+
 	physics_globals::shutdown();
 	audio_globals::shutdown();
-	profiler_globals::shutdown();
 
+	CE_DELETE(_allocator, _input_manager);
+	CE_DELETE(_allocator, _resource_manager);
+	CE_DELETE(_allocator, _resource_loader);
 	CE_DELETE(_allocator, _bundle_filesystem);
 
+	profiler_globals::shutdown();
+
 	_allocator.clear();
-	_is_init = false;
 }
 
 void Device::quit()

+ 3 - 0
src/input/input_manager.h

@@ -12,6 +12,9 @@
 namespace crown
 {
 
+/// Manages input devices.
+///
+/// @ingroup Input
 class InputManager
 {
 public:

+ 5 - 10
src/renderers/debug_line.cpp

@@ -141,17 +141,12 @@ void DebugLine::add_line(const Vector3& start, const Vector3& end, const Color4&
 	if (_num >= CROWN_MAX_DEBUG_LINES)
 		 return;
 
-	_lines[_num].p0[0] = start.x;
-	_lines[_num].p0[1] = start.y;
-	_lines[_num].p0[2] = start.z;
-	_lines[_num].c0    = to_abgr(color);
+	_lines[_num].p0 = start;
+	_lines[_num].c0 = to_abgr(color);
+	_lines[_num].p1 = end;
+	_lines[_num].c1 = to_abgr(color);
 
-	_lines[_num].p1[0] = end.x;
-	_lines[_num].p1[1] = end.y;
-	_lines[_num].p1[2] = end.z;
-	_lines[_num].c1    = to_abgr(color);
-
-	_num++;
+	++_num;
 }
 
 void DebugLine::add_axes(const Matrix4x4& m, float length)

+ 2 - 2
src/renderers/debug_line.h

@@ -61,9 +61,9 @@ private:
 
 	struct Line
 	{
-		float p0[3];
+		Vector3 p0;
 		uint32_t c0;
-		float p1[3];
+		Vector3 p1;
 		uint32_t c1;
 	};
 

+ 6 - 6
src/renderers/shader.cpp

@@ -91,17 +91,17 @@ namespace shader_resource
 		opts.get_absolute_path("tmpvs", tmpvs_path);
 		opts.get_absolute_path("tmpfs", tmpfs_path);
 
-		File* vs_file = opts._fs.open(vs_code_path.c_str(), FOM_WRITE);
+		File* vs_file = opts._fs.open(vs_code_path.c_str(), FileOpenMode::WRITE);
 		vs_file->write(vs_code.c_str(), vs_code.length());
-		opts._fs.close(vs_file);
+		opts._fs.close(*vs_file);
 
-		File* fs_file = opts._fs.open(fs_code_path.c_str(), FOM_WRITE);
+		File* fs_file = opts._fs.open(fs_code_path.c_str(), FileOpenMode::WRITE);
 		fs_file->write(fs_code.c_str(), fs_code.length());
-		opts._fs.close(fs_file);
+		opts._fs.close(*fs_file);
 
-		File* varying_file = opts._fs.open(varying_def_path.c_str(), FOM_WRITE);
+		File* varying_file = opts._fs.open(varying_def_path.c_str(), FileOpenMode::WRITE);
 		varying_file->write(varying_def.c_str(), varying_def.length());
-		opts._fs.close(varying_file);
+		opts._fs.close(*varying_file);
 
 		TempAllocator4096 ta;
 		StringStream output(ta);

+ 2 - 2
src/resource/resource_loader.cpp

@@ -88,9 +88,9 @@ int32_t ResourceLoader::run()
 		DynamicString path(alloc);
 		path::join(CROWN_DATA_DIRECTORY, name, path);
 
-		File* file = _fs.open(path.c_str(), FOM_READ);
+		File* file = _fs.open(path.c_str(), FileOpenMode::READ);
 		rr.data = rr.load_function(*file, *rr.allocator);
-		_fs.close(file);
+		_fs.close(*file);
 
 		add_loaded(rr);
 		_mutex.lock();

+ 2 - 2
src/resource/texture_resource.cpp

@@ -565,7 +565,7 @@ namespace texture_resource
 
 		DynamicString name = root.key("source").to_string();
 
-		File* source = opts._fs.open(name.c_str(), FOM_READ);
+		File* source = opts._fs.open(name.c_str(), FileOpenMode::READ);
 		BinaryReader br(*source);
 		ImageData image;
 
@@ -590,7 +590,7 @@ namespace texture_resource
 			CE_FATAL("Source image not supported");
 		}
 
-		opts._fs.close(source);
+		opts._fs.close(*source);
 
 		// Write DDS
 		opts.write(TEXTURE_VERSION); // Version