2
0
Panagiotis Christopoulos Charitos 10 жил өмнө
parent
commit
f55f6a09d5

+ 1 - 1
CMakeLists.txt

@@ -139,7 +139,7 @@ endif()
 # Add optimization flags
 if(ANKI_OPTIMIZE)
 	# -flto ?
-	set(COMPILER_FLAGS "${COMPILER_FLAGS} -ffast-math -O3 -flto ")
+	set(COMPILER_FLAGS "${COMPILER_FLAGS} -ffast-math -O3 ")
 
 	# Add this because Android compiler complains
 	if(ANDROID)

+ 72 - 14
include/anki/script/LuaBinder.h

@@ -3,28 +3,89 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#ifndef ANKI_SCRIPT_LUA_BINDER_H
-#define ANKI_SCRIPT_LUA_BINDER_H
+#pragma once
 
 #include "anki/util/Assert.h"
 #include "anki/util/StdTypes.h"
 #include "anki/util/Allocator.h"
 #include "anki/util/String.h"
+#include "anki/util/Functions.h"
 #include <lua.hpp>
 #ifndef ANKI_LUA_HPP
 #	error "Wrong LUA header included"
 #endif
-#include <functional>
 
 namespace anki {
 
-/// LUA userdata
+/// LUA userdata.
 class UserData
 {
 public:
+	/// @note NEVER ADD A DESTRUCTOR. LUA cannot call that.
+	~UserData() = delete;
+
+	I64 getSig() const
+	{
+		return m_sig;
+	}
+
+	void initGarbageCollected(I64 sig)
+	{
+		m_sig = sig;
+		m_addressAndGarbageCollect = GC_MASK;
+	}
+
+	void initPointed(I64 sig, void* ptrToObject)
+	{
+		m_sig = sig;
+		U64 addr = ptrToNumber(ptrToObject);
+		ANKI_ASSERT((addr & GC_MASK) == 0
+			&& "Address too high, cannot encode a flag");
+		m_addressAndGarbageCollect = addr;
+	}
+
+	Bool isGarbageCollected() const
+	{
+		return m_addressAndGarbageCollect == GC_MASK;
+	}
+
+	template<typename T>
+	T* getData()
+	{
+		T* out = nullptr;
+		if(isGarbageCollected())
+		{
+			// Garbage collected
+			PtrSize mem = ptrToNumber(this);
+			mem += getAlignedRoundUp(alignof(T), sizeof(UserData));
+			out = numberToPtr<T*>(mem);
+		}
+		else
+		{
+			// Pointed
+			PtrSize mem = static_cast<PtrSize>(m_addressAndGarbageCollect);
+			out = numberToPtr<T*>(mem);
+		}
+
+		ANKI_ASSERT(out);
+		ANKI_ASSERT(isAligned(alignof(T), out));
+		return out;
+	}
+
+	template<typename T>
+	static PtrSize computeSizeForGarbageCollected()
+	{
+		return getAlignedRoundUp(alignof(T), sizeof(UserData)) + sizeof(T);
+	}
+
+private:
+	static constexpr U64 GC_MASK = 1ul << 63ul;
+
 	I64 m_sig = 0; ///< Signature to identify the user data.
-	void* m_data = nullptr;
-	Bool8 m_gc = false; ///< Garbage collection on?
+
+	/// Encodes an address and a flag for garbage collection.
+	/// High bit is the GC flag and the rest are an address.
+	U64 m_addressAndGarbageCollect = 0;
 };
 
 /// Lua binder class. A wrapper on top of LUA
@@ -92,14 +153,14 @@ public:
 		lua_State* l, I32 stackIdx, const char*& out);
 
 	/// Get some user data from the stack.
-	/// The function uses the type signature to validate the type and not the 
+	/// The function uses the type signature to validate the type and not the
 	/// typeName. That is supposed to be faster.
 	static ANKI_USE_RESULT Error checkUserData(
-		lua_State* l, I32 stackIdx, const char* typeName, I64 typeSignature, 
+		lua_State* l, I32 stackIdx, const char* typeName, I64 typeSignature,
 		UserData*& out);
 
 	/// Allocate memory.
-	static void* luaAlloc(lua_State* l, size_t size);
+	static void* luaAlloc(lua_State* l, size_t size, U32 alignment);
 
 	/// Free memory.
 	static void luaFree(lua_State* l, void* ptr);
@@ -142,14 +203,11 @@ template<typename T>
 inline void LuaBinder::exposeVariable(const char* name, T* y)
 {
 	void* ptr = lua_newuserdata(m_l, sizeof(UserData));
-	UserData* ud = reinterpret_cast<UserData*>(ptr);
-	ud->m_data = y;
-	ud->m_gc = false;
-	ud->m_sig = getWrappedTypeSignature<T>();
+	UserData* ud = static_cast<UserData*>(ptr);
+	ud->initPointed(getWrappedTypeSignature<T>(), y);
 	luaL_setmetatable(m_l, getWrappedTypeName<T>());
 	lua_setglobal(m_l, name);
 }
 
 } // end namespace anki
 
-#endif

+ 20 - 0
include/anki/util/Functions.h

@@ -131,6 +131,26 @@ inline void swapValues(T& a, T& b)
 	a = tmp;
 }
 
+/// Convert any pointer to a number.
+template<typename TPtr>
+inline PtrSize ptrToNumber(TPtr ptr)
+{
+	ANKI_ASSERT(ptr);
+	uintptr_t i = reinterpret_cast<uintptr_t>(ptr);
+	PtrSize size = i;
+	return size;
+}
+
+/// Convert a number to a pointer.
+template<typename TPtr>
+inline TPtr numberToPtr(PtrSize num)
+{
+	ANKI_ASSERT(num);
+	uintptr_t i = static_cast<uintptr_t>(num);
+	TPtr ptr = reinterpret_cast<TPtr>(i);
+	return ptr;
+}
+
 /// A simple template trick to remove the pointer from one type
 ///
 /// Example:

+ 8 - 9
src/script/Event.cpp

@@ -95,13 +95,14 @@ static inline int pwrapEventManagernewSceneAmbientColorEvent(lua_State* l)
 	(void)ud;
 	void* voidp;
 	(void)voidp;
+	PtrSize size;
+	(void)size;
 	
 	LuaBinder::checkArgsCount(l, 4);
 	
 	// Get "this" as "self"
 	if(LuaBinder::checkUserData(l, 1, classnameEventManager, -6959305329499243407, ud)) return -1;
-	EventManager* self = static_cast<EventManager*>(ud->m_data);
-	ANKI_ASSERT(self != nullptr);
+	EventManager* self = ud->getData<EventManager>();
 	
 	// Pop arguments
 	F32 arg0;
@@ -111,7 +112,7 @@ static inline int pwrapEventManagernewSceneAmbientColorEvent(lua_State* l)
 	if(LuaBinder::checkNumber(l, 3, arg1)) return -1;
 	
 	if(LuaBinder::checkUserData(l, 4, "Vec4", 6804478823655046386, ud)) return -1;
-	Vec4* iarg2 = static_cast<Vec4*>(ud->m_data);
+	Vec4* iarg2 = ud->getData<Vec4>();
 	const Vec4& arg2(*iarg2);
 	
 	// Call the method
@@ -127,9 +128,7 @@ static inline int pwrapEventManagernewSceneAmbientColorEvent(lua_State* l)
 	voidp = lua_newuserdata(l, sizeof(UserData));
 	ud = static_cast<UserData*>(voidp);
 	luaL_setmetatable(l, "SceneAmbientColorEvent");
-	ud->m_data = static_cast<void*>(ret);
-	ud->m_gc = false;
-	ud->m_sig = -2736282921550252951;
+	ud->initPointed(-2736282921550252951, const_cast<SceneAmbientColorEvent*>(ret));
 	
 	return 1;
 }
@@ -161,6 +160,8 @@ static inline int pwrapgetEventManager(lua_State* l)
 	(void)ud;
 	void* voidp;
 	(void)voidp;
+	PtrSize size;
+	(void)size;
 	
 	LuaBinder::checkArgsCount(l, 0);
 	
@@ -177,9 +178,7 @@ static inline int pwrapgetEventManager(lua_State* l)
 	voidp = lua_newuserdata(l, sizeof(UserData));
 	ud = static_cast<UserData*>(voidp);
 	luaL_setmetatable(l, "EventManager");
-	ud->m_data = static_cast<void*>(ret);
-	ud->m_gc = false;
-	ud->m_sig = -6959305329499243407;
+	ud->initPointed(-6959305329499243407, const_cast<EventManager*>(ret));
 	
 	return 1;
 }

+ 12 - 11
src/script/LuaBinder.cpp

@@ -66,7 +66,7 @@ void* LuaBinder::luaAllocCallback(
 
 		if(ptr == nullptr)
 		{
-			out = binder.m_alloc.allocate(nsize);
+			out = binder.m_alloc.getMemoryPool().allocate(nsize, 16);
 		}
 		else if(nsize <= osize)
 		{
@@ -76,7 +76,7 @@ void* LuaBinder::luaAllocCallback(
 		{
 			// realloc
 
-			out = binder.m_alloc.allocate(nsize);
+			out = binder.m_alloc.getMemoryPool().allocate(nsize, 16);
 			std::memcpy(out, ptr, osize);
 			binder.m_alloc.getMemoryPool().free(ptr);
 		}
@@ -216,11 +216,8 @@ Error LuaBinder::checkUserData(
 	if(p != nullptr)
 	{
 		out = reinterpret_cast<UserData*>(p);
-		if(out->m_sig == typeSignature)
+		if(out->getSig() == typeSignature)
 		{
-			// All done!
-			ANKI_ASSERT(out->m_data);
-
 			// Check using a LUA method again
 			ANKI_ASSERT(luaL_testudata(l, stackIdx, typeName) != nullptr
 				&& "ANKI type check passes but LUA's type check failed");
@@ -247,21 +244,25 @@ Error LuaBinder::checkUserData(
 }
 
 //==============================================================================
-void* LuaBinder::luaAlloc(lua_State* l, size_t size)
+void* LuaBinder::luaAlloc(lua_State* l, size_t size, U32 alignment)
 {
 	void* ud;
-	lua_Alloc alloc = lua_getallocf(l, &ud);
+	lua_getallocf(l, &ud);
+	ANKI_ASSERT(ud);
+	LuaBinder* binder = static_cast<LuaBinder*>(ud);
 
-	return alloc(ud, nullptr, 0, size);
+	return binder->m_alloc.getMemoryPool().allocate(size, alignment);
 }
 
 //==============================================================================
 void LuaBinder::luaFree(lua_State* l, void* ptr)
 {
 	void* ud;
-	lua_Alloc alloc = lua_getallocf(l, &ud);
+	lua_getallocf(l, &ud);
+	ANKI_ASSERT(ud);
+	LuaBinder* binder = static_cast<LuaBinder*>(ud);
 
-	alloc(ud, ptr, 0, 0);
+	binder->m_alloc.getMemoryPool().free(ptr);
 }
 
 } // end namespace anki

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 187 - 245
src/script/Math.cpp


+ 6 - 4
src/script/Renderer.cpp

@@ -37,13 +37,14 @@ static inline int pwrapDbggetEnabled(lua_State* l)
 	(void)ud;
 	void* voidp;
 	(void)voidp;
+	PtrSize size;
+	(void)size;
 	
 	LuaBinder::checkArgsCount(l, 1);
 	
 	// Get "this" as "self"
 	if(LuaBinder::checkUserData(l, 1, classnameDbg, -2784798555522127122, ud)) return -1;
-	Dbg* self = static_cast<Dbg*>(ud->m_data);
-	ANKI_ASSERT(self != nullptr);
+	Dbg* self = ud->getData<Dbg>();
 	
 	// Call the method
 	Bool ret = self->getEnabled();
@@ -72,13 +73,14 @@ static inline int pwrapDbgsetEnabled(lua_State* l)
 	(void)ud;
 	void* voidp;
 	(void)voidp;
+	PtrSize size;
+	(void)size;
 	
 	LuaBinder::checkArgsCount(l, 2);
 	
 	// Get "this" as "self"
 	if(LuaBinder::checkUserData(l, 1, classnameDbg, -2784798555522127122, ud)) return -1;
-	Dbg* self = static_cast<Dbg*>(ud->m_data);
-	ANKI_ASSERT(self != nullptr);
+	Dbg* self = ud->getData<Dbg>();
 	
 	// Pop arguments
 	Bool arg0;

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 159 - 158
src/script/Scene.cpp


+ 40 - 65
src/script/lua_glue_gen.py

@@ -15,7 +15,7 @@ def parse_commandline():
 	""" Parse the command line arguments """
 
 	parser = optparse.OptionParser(usage = "usage: %prog [options]", \
-		description = "Create LUA bindings using XML")	
+		description = "Create LUA bindings using XML")
 
 	parser.add_option("-i", "--input", dest = "inp",
 		type = "string", help = "specify the XML files to parse. " \
@@ -59,7 +59,7 @@ def type_is_number(type):
 		"U", "I", "PtrSize", "F32", "F64", \
 		"int", "unsigned", "unsigned int", "short", "unsigned short", "uint", \
 		"float", "double"]
-		
+
 	it_is = False
 	for num in numbers:
 		if num == type:
@@ -113,7 +113,7 @@ def ret(ret_el):
 		ident(-1)
 		wglue("}")
 		wglue("")
-	
+
 	if type_is_bool(type):
 		wglue("lua_pushboolean(l, ret);")
 	elif type_is_number(type):
@@ -131,40 +131,27 @@ def ret(ret_el):
 		wglue("")
 		wglue("lua_pushnumber(l, ret);")
 	else:
-	 	wglue("voidp = lua_newuserdata(l, sizeof(UserData));")
-		wglue("ud = static_cast<UserData*>(voidp);")
-		wglue("luaL_setmetatable(l, \"%s\");" % type)
-
-		if is_ptr:
-			if is_const:
-				wglue("ud->m_data = const_cast<void*>(" \
-					"static_cast<const void*>(ret));")
-			else:
-				wglue("ud->m_data = static_cast<void*>(ret);")
-			wglue("ud->m_gc = false;")
-		elif is_ref:
-			if is_const:
-				wglue("ud->m_data = const_cast<void*>(" \
-					"static_cast<const void*>(&ret));")
-			else:
-				wglue("ud->m_data = static_cast<void*>(&ret);")
-			wglue("ud->m_gc = false;")
+		if is_ptr or is_ref:
+		 	wglue("voidp = lua_newuserdata(l, sizeof(UserData));")
+			wglue("ud = static_cast<UserData*>(voidp);")
+			wglue("luaL_setmetatable(l, \"%s\");" % type)
+
+			if is_ptr:
+				wglue("ud->initPointed(%d, const_cast<%s*>(ret));"
+						% (type_sig(type), type))
+			elif is_ref:
+				wglue("ud->initPointed(%d, const_cast<%s*>(&ret));"
+						% (type_sig(type), type))
 		else:
-			wglue("ud->m_data = LuaBinder::luaAlloc(l, sizeof(%s));" % type)
-	
-			wglue("if(ANKI_UNLIKELY(ud->m_data == nullptr))")
-			wglue("{")
-			ident(1)
-			wglue("ud->m_gc = false;")
-			wglue("lua_pushstring(l, \"Out of memory\");")
-			wglue("return -1;")
-			ident(-1)
-			wglue("}")
-			wglue("")
+			wglue("size = UserData::computeSizeForGarbageCollected<%s>();" \
+					% type)
+			wglue("voidp = lua_newuserdata(l, size);")
+			wglue("luaL_setmetatable(l, \"%s\");" % type)
+
+			wglue("ud = static_cast<UserData*>(voidp);")
+			wglue("ud->initGarbageCollected(%d);" % type_sig(type))
 
-			wglue("::new(ud->m_data) %s(std::move(ret));" % type)
-			wglue("ud->m_gc = true;")
-		wglue("ud->m_sig = %d;" % type_sig(type))
+			wglue("::new(ud->getData<%s>()) %s(std::move(ret));" % (type, type))
 
 	wglue("")
 	wglue("return 1;")
@@ -185,7 +172,7 @@ def arg(arg_txt, stack_index, index):
 	else:
 		wglue("if(LuaBinder::checkUserData(l, %d, \"%s\", %d, ud)) return -1;" \
 			% (stack_index, type, type_sig(type)))
-		wglue("%s* iarg%d = static_cast<%s*>(ud->m_data);" \
+		wglue("%s* iarg%d = ud->getData<%s>();" \
 			% (type, index, type))
 
 		if is_ptr:
@@ -271,6 +258,8 @@ def write_local_vars():
 	wglue("(void)ud;")
 	wglue("void* voidp;")
 	wglue("(void)voidp;")
+	wglue("PtrSize size;")
+	wglue("(void)size;")
 	wglue("")
 
 def method(class_name, meth_el):
@@ -295,9 +284,7 @@ def method(class_name, meth_el):
 	wglue("// Get \"this\" as \"self\"")
 	wglue("if(LuaBinder::checkUserData(l, 1, classname%s, %d, ud)) return -1;" \
 		% (class_name, type_sig(class_name)))
-	wglue("%s* self = static_cast<%s*>(ud->m_data);" \
-		% (class_name, class_name))
-	wglue("ANKI_ASSERT(self != nullptr);")
+	wglue("%s* self = ud->getData<%s>();" % (class_name, class_name))
 	wglue("")
 
 	args_str = args(meth_el.find("args"), 2)
@@ -363,7 +350,7 @@ def static_method(class_name, meth_el):
 
 	# Args
 	args_str = args(meth_el.find("args"), 1)
-	
+
 	# Return value
 	ret_txt = None
 	ret_el = meth_el.find("return")
@@ -401,7 +388,7 @@ def static_method(class_name, meth_el):
 
 def constructor(constr_el, class_name):
 	""" Handle constructor """
-	
+
 	global separator
 
 	wglue(separator)
@@ -419,28 +406,17 @@ def constructor(constr_el, class_name):
 	# Create new userdata
 	wglue("// Create user data")
 
-	wglue("void* inst = LuaBinder::luaAlloc(l, sizeof(%s));" % class_name)
-	wglue("if(ANKI_UNLIKELY(inst == nullptr))")
-	wglue("{")
-	ident(1)
-	wglue("lua_pushstring(l, \"Out of memory\");")
-	wglue("return -1;")
-	ident(-1)
-	wglue("}")
-	wglue("")
-	wglue("::new(inst) %s(%s);" % (class_name, args_str))
-	wglue("")
-
-	wglue("voidp = lua_newuserdata(l, sizeof(UserData));")
-	wglue("ud = static_cast<UserData*>(voidp);")
-	wglue("ud->m_data = inst;")
-	wglue("ud->m_gc = true;")
-	wglue("ud->m_sig = %d;" % type_sig(class_name))
+	wglue("size = UserData::computeSizeForGarbageCollected<%s>();" % class_name)
+	wglue("voidp = lua_newuserdata(l, size);")
 	wglue("luaL_setmetatable(l, classname%s);" % class_name)
+	wglue("ud = static_cast<UserData*>(voidp);")
+	wglue("ud->initGarbageCollected(%d);" % type_sig(class_name))
+	wglue("::new(ud->getData<%s>()) %s(%s);" \
+			% (class_name, class_name, args_str))
 	wglue("")
 
 	wglue("return 1;")
-	
+
 	ident(-1)
 	wglue("}")
 	wglue("")
@@ -461,7 +437,7 @@ def constructor(constr_el, class_name):
 
 def destructor(class_name):
 	""" Create a destroctor """
-	
+
 	global separator
 
 	wglue(separator)
@@ -475,13 +451,12 @@ def destructor(class_name):
 	wglue("if(LuaBinder::checkUserData(l, 1, classname%s, %d, ud)) return -1;" \
 		% (class_name, type_sig(class_name)))
 
-	wglue("if(ud->m_gc)")
+	wglue("if(ud->isGarbageCollected())")
 	wglue("{")
 	ident(1)
-	wglue("%s* inst = static_cast<%s*>(ud->m_data);" \
+	wglue("%s* inst = ud->getData<%s>();" \
 		% (class_name, class_name))
 	wglue("inst->~%s();" % class_name)
-	wglue("LuaBinder::luaFree(l, inst);")
 	ident(-1)
 	wglue("}")
 	wglue("")
@@ -494,7 +469,7 @@ def destructor(class_name):
 
 def class_(class_el):
 	""" Create a class """
-	
+
 	global separator
 	class_name = class_el.get("name")
 
@@ -612,7 +587,7 @@ def function(func_el):
 
 	# Args
 	args_str = args(func_el.find("args"), 1)
-	
+
 	# Return value
 	ret_txt = None
 	ret_el = func_el.find("return")

+ 1 - 1
tests/resource/ResourceFilesystem.cpp

@@ -3,8 +3,8 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#define private public
 #include "tests/framework/Framework.h"
+#define private public
 #include "anki/resource/ResourceFilesystem.h"
 
 namespace anki {

+ 1 - 1
tests/resource/ResourceManager.cpp

@@ -57,7 +57,7 @@ ANKI_TEST(Resource, ResourceManager)
 		ANKI_TEST_EXPECT_EQ(b->getRefcount().load(), a->getRefcount().load());
 		ANKI_TEST_EXPECT_EQ(a->getRefcount().load(), refcount + 1);
 
-		ANKI_TEST_EXPECT_EQ(&b.get(), &a.get());
+		ANKI_TEST_EXPECT_EQ(b.get(), a.get());
 
 		// Again
 		DummyResourcePtr c;

+ 10 - 5
tests/script/LuaBinder.cpp

@@ -8,11 +8,16 @@
 #include "anki/Math.h"
 
 static const char* script = R"(
-b = Vec2.new()
+b = Vec4.new(0, 0, 0, 1.1)
+if math.abs(b:getW() - 1.1) > 0.00001 then
+	print(b:getW())
+	error("oh no!!")
+end
+
 b:setX(3.0)
 b:setY(4.0)
 
-v2:copy(v2 * b)
+v4:copy(v4 * b)
 
 v3:setZ(0.1)
 )";
@@ -22,14 +27,14 @@ ANKI_TEST(Script, LuaBinder)
 	ScriptManager sm;
 
 	ANKI_TEST_EXPECT_NO_ERR(sm.create(allocAligned, nullptr, nullptr));
-	Vec2 v2(2.0, 3.0);
+	Vec4 v4(2.0, 3.0, 4.0, 5.0);
 	Vec3 v3(1.1, 2.2, 3.3);
 
-	sm.exposeVariable("v2", &v2);
+	sm.exposeVariable("v4", &v4);
 	sm.exposeVariable("v3", &v3);
 
 	ANKI_TEST_EXPECT_NO_ERR(sm.evalString(script));
 
-	ANKI_TEST_EXPECT_EQ(v2, Vec2(6, 12));
+	ANKI_TEST_EXPECT_EQ(v4, Vec4(6, 12, 0, 5.5));
 	ANKI_TEST_EXPECT_EQ(v3, Vec3(1.1, 2.2, 0.1));
 }

+ 6 - 6
tests/util/Thread.cpp

@@ -22,8 +22,8 @@ ANKI_TEST(Util, Thread)
 
 	t.start(&u, [](Thread::Info& info) -> Error
 	{
-		Bool check = true; 
-		
+		Bool check = true;
+
 		// Check name
 		check = check && (std::strcmp(info.m_threadName, THREAD_NAME) == 0);
 
@@ -106,14 +106,14 @@ ANKI_TEST(Util, Mutex)
 //==============================================================================
 
 /// Struct for our tests
-struct TestJobTP: Threadpool::Task
+struct TestJobTP: ThreadPool::Task
 {
 	U32 in = 0;
 	U32 iterations = 0;
 
 	Error operator()(U32 /*threadId*/, PtrSize /*threadsCount*/)
 	{
-		for(U32 i = 0; i < iterations; i++)	
+		for(U32 i = 0; i < iterations; i++)
 		{
 			++in;
 		}
@@ -124,11 +124,11 @@ struct TestJobTP: Threadpool::Task
 
 } // end namespace anki
 
-ANKI_TEST(Util, Threadpool)
+ANKI_TEST(Util, ThreadPool)
 {
 	const U32 threadsCount = 4;
 	const U32 repeat = 5;
-	Threadpool* tp = new Threadpool(threadsCount);
+	ThreadPool* tp = new ThreadPool(threadsCount);
 
 	TestJobTP jobs[threadsCount];
 

+ 1 - 1
thirdparty

@@ -1 +1 @@
-Subproject commit 0695eec69e4d6d785fc246ff35b074c7a693675e
+Subproject commit 7ff196d28a76c53e49f387a60f8ebaac568ddee7

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно