Compare commits
	
		
			7 Commits
		
	
	
		
			d8adf47d10
			...
			0.1.0-expe
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6cc31f660c | |||
| 2d79e18b25 | |||
| 1d5a61f308 | |||
| 38e1c3f8b7 | |||
| 8d3fa1ac98 | |||
| 88e330336a | |||
| eca0d90d98 | 
							
								
								
									
										462
									
								
								Redcraft.Utility/Source/Private/Miscellaneous/Console.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										462
									
								
								Redcraft.Utility/Source/Private/Miscellaneous/Console.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,462 @@ | ||||
| #include "Miscellaneous/Console.h" | ||||
|  | ||||
| #include <cstdio> | ||||
|  | ||||
| #if PLATFORM_WINDOWS | ||||
| #	undef TEXT | ||||
| #	include <windows.h> | ||||
| #elif PLATFORM_LINUX | ||||
| #	include <sys/ioctl.h> | ||||
| #	include <unistd.h> | ||||
| #endif | ||||
|  | ||||
| NAMESPACE_REDCRAFT_BEGIN | ||||
| NAMESPACE_MODULE_BEGIN(Redcraft) | ||||
| NAMESPACE_MODULE_BEGIN(Utility) | ||||
|  | ||||
| #if PLATFORM_WINDOWS | ||||
|  | ||||
| NODISCARD bool InitANSIConsole() | ||||
| { | ||||
| 	static bool bResult = [] | ||||
| 	{ | ||||
| 		HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
|  | ||||
| 		if (Console == INVALID_HANDLE_VALUE) return false; | ||||
|  | ||||
| 		DWORD ConsoleMode = 0; | ||||
|  | ||||
| 		if (!GetConsoleMode(Console, &ConsoleMode)) return false; | ||||
|  | ||||
| 		ConsoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; | ||||
|  | ||||
| 		if (!SetConsoleMode(Console, ConsoleMode)) return false; | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
| 	(); | ||||
|  | ||||
| 	return bResult; | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| EColor GForegroundColor = EColor::Default; | ||||
| EColor GBackgroundColor = EColor::Default; | ||||
|  | ||||
| EColor GetForegroundColor() | ||||
| { | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		if (InitANSIConsole()) return GForegroundColor; | ||||
|  | ||||
| 		const HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
|  | ||||
| 		if (Console == INVALID_HANDLE_VALUE) return EColor::Default; | ||||
|  | ||||
| 		CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo; | ||||
|  | ||||
| 		if (!GetConsoleScreenBufferInfo(Console, &ConsoleInfo)) return EColor::Default; | ||||
|  | ||||
| 		const WORD Color = ConsoleInfo.wAttributes; | ||||
|  | ||||
| 		EColor Result = EColor::Black; | ||||
|  | ||||
| 		if (Color & FOREGROUND_RED)       Result |= EColor::Red; | ||||
| 		if (Color & FOREGROUND_GREEN)     Result |= EColor::Green; | ||||
| 		if (Color & FOREGROUND_BLUE)      Result |= EColor::Blue; | ||||
| 		if (Color & FOREGROUND_INTENSITY) Result |= EColor::Intensity; | ||||
|  | ||||
| 		return Result; | ||||
| 	} | ||||
| #	endif | ||||
|  | ||||
| 	return GForegroundColor; | ||||
| } | ||||
|  | ||||
| EColor GetBackgroundColor() | ||||
| { | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		if (InitANSIConsole()) return GBackgroundColor; | ||||
|  | ||||
| 		const HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
|  | ||||
| 		if (Console == INVALID_HANDLE_VALUE) return EColor::Default; | ||||
|  | ||||
| 		CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo; | ||||
|  | ||||
| 		if (!GetConsoleScreenBufferInfo(Console, &ConsoleInfo)) return EColor::Default; | ||||
|  | ||||
| 		const WORD Color = ConsoleInfo.wAttributes; | ||||
|  | ||||
| 		EColor Result = EColor::Black; | ||||
|  | ||||
| 		if (Color & BACKGROUND_RED)       Result |= EColor::Red; | ||||
| 		if (Color & BACKGROUND_GREEN)     Result |= EColor::Green; | ||||
| 		if (Color & BACKGROUND_BLUE)      Result |= EColor::Blue; | ||||
| 		if (Color & BACKGROUND_INTENSITY) Result |= EColor::Intensity; | ||||
|  | ||||
| 		return Result; | ||||
| 	} | ||||
| #	endif | ||||
|  | ||||
| 	return GBackgroundColor; | ||||
| } | ||||
|  | ||||
| EColor SetForegroundColor(EColor InColor) | ||||
| { | ||||
| 	if (IsOutputRedirected()) return GetForegroundColor(); | ||||
|  | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		if (!InitANSIConsole()) | ||||
| 		{ | ||||
| 			const HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
|  | ||||
| 			if (Console == INVALID_HANDLE_VALUE) return GetForegroundColor(); | ||||
|  | ||||
| 			CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo; | ||||
|  | ||||
| 			if (!GetConsoleScreenBufferInfo(Console, &ConsoleInfo)) return GetForegroundColor(); | ||||
|  | ||||
| 			WORD Color = ConsoleInfo.wAttributes & ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); | ||||
|  | ||||
| 			if (InColor == EColor::Default) InColor = EColor::White; | ||||
|  | ||||
| 			if (!!(InColor & EColor::Red))       Color |= FOREGROUND_RED; | ||||
| 			if (!!(InColor & EColor::Green))     Color |= FOREGROUND_GREEN; | ||||
| 			if (!!(InColor & EColor::Blue))      Color |= FOREGROUND_BLUE; | ||||
| 			if (!!(InColor & EColor::Intensity)) Color |= FOREGROUND_INTENSITY; | ||||
|  | ||||
| 			if (!SetConsoleTextAttribute(Console, Color)) return GetForegroundColor(); | ||||
|  | ||||
| 			return InColor; | ||||
| 		} | ||||
| 	} | ||||
| #	endif | ||||
|  | ||||
| #	if PLATFORM_WINDOWS || PLATFORM_LINUX | ||||
| 	{ | ||||
| 		int Result; | ||||
|  | ||||
| 		switch (InColor) | ||||
| 		{ | ||||
| 		case EColor::Black:         Result = std::fputs("\033[30m", stdout); break; | ||||
| 		case EColor::Red:           Result = std::fputs("\033[31m", stdout); break; | ||||
| 		case EColor::Green:         Result = std::fputs("\033[32m", stdout); break; | ||||
| 		case EColor::Yellow:        Result = std::fputs("\033[33m", stdout); break; | ||||
| 		case EColor::Blue:          Result = std::fputs("\033[34m", stdout); break; | ||||
| 		case EColor::Magenta:       Result = std::fputs("\033[35m", stdout); break; | ||||
| 		case EColor::Cyan:          Result = std::fputs("\033[36m", stdout); break; | ||||
| 		case EColor::White:         Result = std::fputs("\033[37m", stdout); break; | ||||
| 		case EColor::BrightBlack:   Result = std::fputs("\033[90m", stdout); break; | ||||
| 		case EColor::BrightRed:     Result = std::fputs("\033[91m", stdout); break; | ||||
| 		case EColor::BrightGreen:   Result = std::fputs("\033[92m", stdout); break; | ||||
| 		case EColor::BrightYellow:  Result = std::fputs("\033[93m", stdout); break; | ||||
| 		case EColor::BrightBlue:    Result = std::fputs("\033[94m", stdout); break; | ||||
| 		case EColor::BrightMagenta: Result = std::fputs("\033[95m", stdout); break; | ||||
| 		case EColor::BrightCyan:    Result = std::fputs("\033[96m", stdout); break; | ||||
| 		case EColor::BrightWhite:   Result = std::fputs("\033[97m", stdout); break; | ||||
| 		default:                    Result = std::fputs("\033[39m", stdout); break; | ||||
| 		} | ||||
|  | ||||
| 		if (Result == EOF) return GetForegroundColor(); | ||||
|  | ||||
| 		return GForegroundColor = InColor; | ||||
| 	} | ||||
| #	endif | ||||
|  | ||||
| 	return GetForegroundColor(); | ||||
| } | ||||
|  | ||||
| EColor SetBackgroundColor(EColor InColor) | ||||
| { | ||||
| 	if (IsOutputRedirected()) return GetBackgroundColor(); | ||||
|  | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		if (!InitANSIConsole()) | ||||
| 		{ | ||||
| 			const HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
|  | ||||
| 			if (Console == INVALID_HANDLE_VALUE) return GetBackgroundColor(); | ||||
|  | ||||
| 			CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo; | ||||
|  | ||||
| 			if (!GetConsoleScreenBufferInfo(Console, &ConsoleInfo)) return GetBackgroundColor(); | ||||
|  | ||||
| 			WORD Color = ConsoleInfo.wAttributes & ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY); | ||||
|  | ||||
| 			if (InColor == EColor::Default) InColor = EColor::Black; | ||||
|  | ||||
| 			if (!!(InColor & EColor::Red))       Color |= BACKGROUND_RED; | ||||
| 			if (!!(InColor & EColor::Green))     Color |= BACKGROUND_GREEN; | ||||
| 			if (!!(InColor & EColor::Blue))      Color |= BACKGROUND_BLUE; | ||||
| 			if (!!(InColor & EColor::Intensity)) Color |= BACKGROUND_INTENSITY; | ||||
|  | ||||
| 			if (!SetConsoleTextAttribute(Console, Color)) return GetBackgroundColor(); | ||||
|  | ||||
| 			return InColor; | ||||
| 		} | ||||
| 	} | ||||
| #	endif | ||||
|  | ||||
| #	if PLATFORM_WINDOWS || PLATFORM_LINUX | ||||
| 	{ | ||||
| 		int Result; | ||||
|  | ||||
| 		switch (InColor) | ||||
| 		{ | ||||
| 		case EColor::Black:         Result = std::fputs("\033[40m",  stdout); break; | ||||
| 		case EColor::Red:           Result = std::fputs("\033[41m",  stdout); break; | ||||
| 		case EColor::Green:         Result = std::fputs("\033[42m",  stdout); break; | ||||
| 		case EColor::Yellow:        Result = std::fputs("\033[43m",  stdout); break; | ||||
| 		case EColor::Blue:          Result = std::fputs("\033[44m",  stdout); break; | ||||
| 		case EColor::Magenta:       Result = std::fputs("\033[45m",  stdout); break; | ||||
| 		case EColor::Cyan:          Result = std::fputs("\033[46m",  stdout); break; | ||||
| 		case EColor::White:         Result = std::fputs("\033[47m",  stdout); break; | ||||
| 		case EColor::BrightBlack:   Result = std::fputs("\033[100m", stdout); break; | ||||
| 		case EColor::BrightRed:     Result = std::fputs("\033[101m", stdout); break; | ||||
| 		case EColor::BrightGreen:   Result = std::fputs("\033[102m", stdout); break; | ||||
| 		case EColor::BrightYellow:  Result = std::fputs("\033[103m", stdout); break; | ||||
| 		case EColor::BrightBlue:    Result = std::fputs("\033[104m", stdout); break; | ||||
| 		case EColor::BrightMagenta: Result = std::fputs("\033[105m", stdout); break; | ||||
| 		case EColor::BrightCyan:    Result = std::fputs("\033[106m", stdout); break; | ||||
| 		case EColor::BrightWhite:   Result = std::fputs("\033[107m", stdout); break; | ||||
| 		default:                    Result = std::fputs("\033[49m",  stdout); break; | ||||
| 		} | ||||
|  | ||||
| 		if (Result == EOF) return GetBackgroundColor(); | ||||
|  | ||||
| 		return GBackgroundColor = InColor; | ||||
| 	} | ||||
| #	endif | ||||
|  | ||||
| 	return GetBackgroundColor(); | ||||
| } | ||||
|  | ||||
| uint GetWindowWidth() | ||||
| { | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		const HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
|  | ||||
| 		if (Console == INVALID_HANDLE_VALUE) return static_cast<uint>(-1); | ||||
|  | ||||
| 		CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo; | ||||
|  | ||||
| 		if (!GetConsoleScreenBufferInfo(Console, &ConsoleInfo)) return static_cast<uint>(-1); | ||||
|  | ||||
| 		return static_cast<uint>(ConsoleInfo.srWindow.Right - ConsoleInfo.srWindow.Left + 1); | ||||
| 	} | ||||
| #	elif PLATFORM_LINUX | ||||
| 	{ | ||||
| 		winsize Size; | ||||
|  | ||||
| 		if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &Size) == -1) return static_cast<uint>(-1); | ||||
|  | ||||
| 		return static_cast<uint>(Size.ws_col); | ||||
| 	} | ||||
| #	endif | ||||
|  | ||||
| 	return static_cast<uint>(-1); | ||||
| } | ||||
|  | ||||
| uint GetWindowHeight() | ||||
| { | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		const HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
|  | ||||
| 		if (Console == INVALID_HANDLE_VALUE) return static_cast<uint>(-1); | ||||
|  | ||||
| 		CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo; | ||||
|  | ||||
| 		if (!GetConsoleScreenBufferInfo(Console, &ConsoleInfo)) return static_cast<uint>(-1); | ||||
|  | ||||
| 		return static_cast<uint>(ConsoleInfo.srWindow.Bottom - ConsoleInfo.srWindow.Top + 1); | ||||
| 	} | ||||
| #	elif PLATFORM_LINUX | ||||
| 	{ | ||||
| 		winsize Size; | ||||
|  | ||||
| 		if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &Size) == -1) return static_cast<uint>(-1); | ||||
|  | ||||
| 		return static_cast<uint>(Size.ws_row); | ||||
| 	} | ||||
| #	endif | ||||
|  | ||||
| 	return static_cast<uint>(-1); | ||||
| } | ||||
|  | ||||
| bool IsInputRedirected() | ||||
| { | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		const HANDLE StandardInput = GetStdHandle(STD_INPUT_HANDLE); | ||||
|  | ||||
| 		if (StandardInput == INVALID_HANDLE_VALUE) return false; | ||||
|  | ||||
| 		DWORD FileType = GetFileType(StandardInput); | ||||
|  | ||||
| 		return FileType != FILE_TYPE_CHAR; | ||||
| 	} | ||||
| #	elif PLATFORM_LINUX | ||||
| 	{ | ||||
| 		return isatty(fileno(stdin)) == 0; | ||||
| 	} | ||||
| #	endif | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool IsOutputRedirected() | ||||
| { | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		const HANDLE StandardOutput = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
|  | ||||
| 		if (StandardOutput == INVALID_HANDLE_VALUE) return false; | ||||
|  | ||||
| 		DWORD FileType = GetFileType(StandardOutput); | ||||
|  | ||||
| 		return FileType != FILE_TYPE_CHAR; | ||||
| 	} | ||||
| #	elif PLATFORM_LINUX | ||||
| 	{ | ||||
| 		return isatty(fileno(stdout)) == 0; | ||||
| 	} | ||||
| #	endif | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool IsErrorRedirected() | ||||
| { | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		const HANDLE StandardError = GetStdHandle(STD_ERROR_HANDLE); | ||||
|  | ||||
| 		if (StandardError == INVALID_HANDLE_VALUE) return false; | ||||
|  | ||||
| 		DWORD FileType = GetFileType(StandardError); | ||||
|  | ||||
| 		return FileType != FILE_TYPE_CHAR; | ||||
| 	} | ||||
| #	elif PLATFORM_LINUX | ||||
| 	{ | ||||
| 		return isatty(fileno(stderr)) == 0; | ||||
| 	} | ||||
| #	endif | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| void Clear() | ||||
| { | ||||
| 	if (IsOutputRedirected()) return; | ||||
|  | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		std::system("cls"); | ||||
| 	} | ||||
| #	elif PLATFORM_LINUX | ||||
| 	{ | ||||
| 		Ignore = std::fputs("\033[2J\033[1;1H", stdout); | ||||
| 	} | ||||
| #	endif | ||||
| } | ||||
|  | ||||
| char Input(bool bEcho) | ||||
| { | ||||
| 	if (bEcho || IsOutputRedirected()) | ||||
| 	{ | ||||
| 		const int Result = std::getchar(); | ||||
|  | ||||
| 		if (Result == EOF) return static_cast<char>(-1); | ||||
|  | ||||
| 		return static_cast<char>(Result); | ||||
| 	} | ||||
|  | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		const HANDLE Console = GetStdHandle(STD_INPUT_HANDLE); | ||||
|  | ||||
| 		if (Console == INVALID_HANDLE_VALUE) return static_cast<char>(-1); | ||||
|  | ||||
| 		DWORD ConsoleMode = 0; | ||||
|  | ||||
| 		GetConsoleMode(Console, &ConsoleMode); | ||||
|  | ||||
| 		SetConsoleMode(Console, ConsoleMode & ~ENABLE_ECHO_INPUT); | ||||
|  | ||||
| 		const char Result = Input(); | ||||
|  | ||||
| 		SetConsoleMode(Console, ConsoleMode); | ||||
|  | ||||
| 		return Result; | ||||
| 	} | ||||
| #	elif PLATFORM_LINUX | ||||
| 	{ } | ||||
| #	endif | ||||
|  | ||||
| 	return static_cast<char>(-1); | ||||
| } | ||||
|  | ||||
| FString InputLn(bool bEcho) | ||||
| { | ||||
| 	if (bEcho || IsOutputRedirected()) | ||||
| 	{ | ||||
| 		FString Result; | ||||
|  | ||||
| 		while (true) | ||||
| 		{ | ||||
| 			const int Char = std::getchar(); | ||||
|  | ||||
| 			if (Char == EOF || Char == '\n') break; | ||||
|  | ||||
| 			Result.PushBack(static_cast<char>(Char)); | ||||
| 		} | ||||
|  | ||||
| 		return Result; | ||||
| 	} | ||||
|  | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		const HANDLE Console = GetStdHandle(STD_INPUT_HANDLE); | ||||
|  | ||||
| 		if (Console == INVALID_HANDLE_VALUE) return ""; | ||||
|  | ||||
| 		DWORD ConsoleMode = 0; | ||||
|  | ||||
| 		GetConsoleMode(Console, &ConsoleMode); | ||||
|  | ||||
| 		SetConsoleMode(Console, ConsoleMode & ~ENABLE_ECHO_INPUT); | ||||
|  | ||||
| 		const FString Result = InputLn(); | ||||
|  | ||||
| 		SetConsoleMode(Console, ConsoleMode); | ||||
|  | ||||
| 		return Result; | ||||
| 	} | ||||
| #	elif PLATFORM_LINUX | ||||
| 	{ } | ||||
| #	endif | ||||
|  | ||||
| 	return ""; | ||||
| } | ||||
|  | ||||
| bool Print(char Char) | ||||
| { | ||||
| 	return std::putchar(Char) != EOF; | ||||
| } | ||||
|  | ||||
| bool Error(char Char) | ||||
| { | ||||
| 	return std::fputc(Char, stderr) != EOF; | ||||
| } | ||||
|  | ||||
| NAMESPACE_MODULE_END(Utility) | ||||
| NAMESPACE_MODULE_END(Redcraft) | ||||
| NAMESPACE_REDCRAFT_END | ||||
							
								
								
									
										596
									
								
								Redcraft.Utility/Source/Private/Miscellaneous/FileSystem.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										596
									
								
								Redcraft.Utility/Source/Private/Miscellaneous/FileSystem.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,596 @@ | ||||
| #include <Miscellaneous/FileSystem.h> | ||||
|  | ||||
| #include "Numerics/Bit.h" | ||||
| #include "Numerics/Math.h" | ||||
| #include "Templates/ScopeHelper.h" | ||||
| #include "Containers/StaticArray.h" | ||||
|  | ||||
| #include <cstdio> | ||||
|  | ||||
| #if PLATFORM_WINDOWS | ||||
| #	undef TEXT | ||||
| #	include <windows.h> | ||||
| #	undef CreateDirectory | ||||
| #elif PLATFORM_LINUX | ||||
| #	include <unistd.h> | ||||
| #	include <dirent.h> | ||||
| #	include <sys/stat.h> | ||||
| #endif | ||||
|  | ||||
| #pragma warning(push) | ||||
| #pragma warning(disable: 4996) | ||||
|  | ||||
| NAMESPACE_REDCRAFT_BEGIN | ||||
| NAMESPACE_MODULE_BEGIN(Redcraft) | ||||
| NAMESPACE_MODULE_BEGIN(Utility) | ||||
|  | ||||
| NAMESPACE_BEGIN(FileSystem) | ||||
|  | ||||
| bool LoadFileToArray(TArray<uint8>& Result, FStringView Path) | ||||
| { | ||||
| 	if (!FileSystem::Exists(Path)) return false; | ||||
|  | ||||
| 	FILE* File = std::fopen(*Path, "rb"); | ||||
|  | ||||
| 	if (File == nullptr) return false; | ||||
|  | ||||
| 	auto FileGuard = TScopeCallback([=] { Ignore = std::fclose(File); }); | ||||
|  | ||||
| 	if (std::fseek(File, 0, SEEK_END) != 0) return false; | ||||
|  | ||||
| 	const long Length = std::ftell(File); | ||||
|  | ||||
| 	if (!Math::IsWithin(Length, 0, TNumericLimits<long>::Max())) return false; | ||||
|  | ||||
| 	if (std::fseek(File, 0, SEEK_SET) != 0) return false; | ||||
|  | ||||
| 	Result.SetNum(Length); | ||||
|  | ||||
| 	if (std::fread(Result.GetData(), sizeof(uint8), Length, File) != static_cast<size_t>(Length)) return false; | ||||
|  | ||||
| 	FileGuard.Release(); | ||||
|  | ||||
| 	if (std::fclose(File) != 0) return false; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool SaveArrayToFile(TArrayView<const uint8> Data, FStringView Path) | ||||
| { | ||||
| 	FILE* File = std::fopen(*Path, "wb"); | ||||
|  | ||||
| 	if (File == nullptr) return false; | ||||
|  | ||||
| 	auto FileGuard = TScopeCallback([=] { Ignore = std::fclose(File); }); | ||||
|  | ||||
| 	if (std::fwrite(Data.GetData(), sizeof(uint8), Data.Num(), File) != Data.Num()) return false; | ||||
|  | ||||
| 	FileGuard.Release(); | ||||
|  | ||||
| 	if (std::fclose(File) != 0) return false; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| template <CCharType T> | ||||
| bool LoadFileToString(TString<T>& Result, FStringView Path, FileSystem::EEncoding Encoding /* = FileSystem::EEncoding::Default */, bool bVerify /* = false */) | ||||
| { | ||||
| 	if (!FileSystem::Exists(Path)) return false; | ||||
|  | ||||
| 	FILE* File = std::fopen(*Path, "rb"); | ||||
|  | ||||
| 	if (File == nullptr) return false; | ||||
|  | ||||
| 	auto FileGuard = TScopeCallback([=] { Ignore = std::fclose(File); }); | ||||
|  | ||||
| 	if (std::fseek(File, 0, SEEK_END) != 0) return false; | ||||
|  | ||||
| 	long Length = std::ftell(File); | ||||
|  | ||||
| 	if (!Math::IsWithin(Length, 0, TNumericLimits<long>::Max())) return false; | ||||
|  | ||||
| 	if (std::fseek(File, 0, SEEK_SET) != 0) return false; | ||||
|  | ||||
| 	TStaticArray<uint8, 4> Buffer = { 0xAA, 0xAA, 0xAA, 0xAA }; | ||||
|  | ||||
| 	Ignore = std::fread(Buffer.GetData(), sizeof(uint8), Buffer.Num(), File); | ||||
|  | ||||
| 	// Auto-detect the encoding if it is not specified. | ||||
| 	if (Encoding == FileSystem::EEncoding::Default) | ||||
| 	{ | ||||
| 		// Check if the file is a UTF-32 encoded file. | ||||
| 		if      (Buffer[0] == 0x00 && Buffer[1] == 0x00 && Buffer[2] == 0xFE && Buffer[3] == 0xFF) Encoding = FileSystem::EEncoding::UTF32BE; | ||||
| 		else if (Buffer[0] == 0xFF && Buffer[1] == 0xFE && Buffer[2] == 0x00 && Buffer[3] == 0x00) Encoding = FileSystem::EEncoding::UTF32LE; | ||||
|  | ||||
| 		// Check if the file is a UTF-16 encoded file. | ||||
| 		else if (Buffer[0] == 0xFF && Buffer[1] == 0xFE) Encoding = FileSystem::EEncoding::UTF16LE; | ||||
| 		else if (Buffer[0] == 0xFE && Buffer[1] == 0xFF) Encoding = FileSystem::EEncoding::UTF16BE; | ||||
|  | ||||
| 		// Check if the file is a UTF-8 encoded file. | ||||
| 		else if (Buffer[0] == 0xEF && Buffer[1] == 0xBB && Buffer[2] == 0xBF) Encoding = FileSystem::EEncoding::UTF8; | ||||
|  | ||||
| 		// Check if the file is a wide character encoded file. | ||||
| 		else if (Buffer[0] == 0x00 || Buffer[1] == 0x00 || Buffer[2] == 0x00 || Buffer[3] == 0x00) Encoding = FileSystem::EEncoding::Wide; | ||||
|  | ||||
| 		// Check if the file is a narrow character encoded file. | ||||
| 		else Encoding = FileSystem::EEncoding::Narrow; | ||||
| 	} | ||||
|  | ||||
| 	// Jump to the BOM character if the file is a UTF-8, UTF-16 or UTF-32 encoded file. | ||||
| 	switch (Encoding) | ||||
| 	{ | ||||
| 	case FileSystem::EEncoding::Narrow: | ||||
| 	case FileSystem::EEncoding::Wide:                                                                                          { Length -= 0; if (std::fseek(File, 0, SEEK_SET) != 0) return false; } break; | ||||
| 	case FileSystem::EEncoding::UTF8:    if (Buffer[0] == 0xEF && Buffer[1] == 0xBB && Buffer[2] == 0xBF)                      { Length -= 3; if (std::fseek(File, 3, SEEK_SET) != 0) return false; } break; | ||||
| 	case FileSystem::EEncoding::UTF16BE: if (Buffer[0] == 0xFE && Buffer[1] == 0xFF)                                           { Length -= 2; if (std::fseek(File, 2, SEEK_SET) != 0) return false; } break; | ||||
| 	case FileSystem::EEncoding::UTF16LE: if (Buffer[0] == 0xFF && Buffer[1] == 0xFE)                                           { Length -= 2; if (std::fseek(File, 2, SEEK_SET) != 0) return false; } break; | ||||
| 	case FileSystem::EEncoding::UTF32BE: if (Buffer[0] == 0x00 && Buffer[1] == 0x00 && Buffer[2] == 0xFE && Buffer[3] == 0xFF) { Length -= 4; if (std::fseek(File, 4, SEEK_SET) != 0) return false; } break; | ||||
| 	case FileSystem::EEncoding::UTF32LE: if (Buffer[0] == 0xFF && Buffer[1] == 0xFE && Buffer[2] == 0x00 && Buffer[3] == 0x00) { Length -= 4; if (std::fseek(File, 4, SEEK_SET) != 0) return false; } break; | ||||
| 	default: check_no_entry(); | ||||
| 	} | ||||
|  | ||||
| 	check(Math::EEndian::Native == Math::EEndian::Big || Math::EEndian::Native == Math::EEndian::Little); | ||||
|  | ||||
| 	const bool bByteSwap = | ||||
| 		Math::EEndian::Native == Math::EEndian::Big    ? Encoding == FileSystem::EEncoding::UTF16LE || Encoding == FileSystem::EEncoding::UTF32LE : | ||||
| 		Math::EEndian::Native == Math::EEndian::Little ? Encoding == FileSystem::EEncoding::UTF16BE || Encoding == FileSystem::EEncoding::UTF32BE : false; | ||||
|  | ||||
| 	const auto LoadImpl = [File, Length, bByteSwap]<typename U>(TString<U>& String) -> bool | ||||
| 	{ | ||||
| 		if (Length % sizeof(U) != 0) return false; | ||||
|  | ||||
| 		String.Reserve(Length / sizeof(U)); | ||||
|  | ||||
| 		while (true) | ||||
| 		{ | ||||
| 			U Char; | ||||
|  | ||||
| 			const size_t ReadNum = std::fread(&Char, 1, sizeof(U), File); | ||||
|  | ||||
| 			if (ReadNum == 0) break; | ||||
|  | ||||
| 			if (ReadNum != sizeof(U)) return false; | ||||
|  | ||||
| 			if (bByteSwap) Char = Math::ByteSwap(static_cast<TMakeUnsigned<U>>(Char)); | ||||
|  | ||||
| #			if PLATFORM_WINDOWS | ||||
| 			{ | ||||
| 				if (!String.IsEmpty() && String.Back() == LITERAL(U, '\r') && Char == LITERAL(U, '\n')) | ||||
| 				{ | ||||
| 					String.PopBack(); | ||||
| 				} | ||||
| 			} | ||||
| #			endif | ||||
|  | ||||
| 			String.PushBack(Char); | ||||
| 		} | ||||
|  | ||||
| 		return true; | ||||
| 	}; | ||||
|  | ||||
| 	bool bCompatible = false; | ||||
|  | ||||
| 	if      constexpr (CSameAs<T, char>)    bCompatible |= Encoding == FileSystem::EEncoding::Narrow; | ||||
| 	else if constexpr (CSameAs<T, wchar>)   bCompatible |= Encoding == FileSystem::EEncoding::Wide; | ||||
| 	else if constexpr (CSameAs<T, u8char>)  bCompatible |= Encoding == FileSystem::EEncoding::UTF8; | ||||
| 	else if constexpr (CSameAs<T, u16char>) bCompatible |= Encoding == FileSystem::EEncoding::UTF16BE || Encoding == FileSystem::EEncoding::UTF16LE; | ||||
| 	else if constexpr (CSameAs<T, u32char>) bCompatible |= Encoding == FileSystem::EEncoding::UTF32BE || Encoding == FileSystem::EEncoding::UTF32LE; | ||||
|  | ||||
| 	else static_assert(sizeof(T) == -1, "Unsupported character type"); | ||||
|  | ||||
| 	if (!bCompatible || bVerify) | ||||
| 	{ | ||||
| 		switch (Encoding) | ||||
| 		{ | ||||
| 		case FileSystem::EEncoding::Narrow:  { FString    Temp; if (!LoadImpl(Temp)) return false; if (!Result.DecodeFrom(Temp)) return false; break; } | ||||
| 		case FileSystem::EEncoding::Wide:    { FWString   Temp; if (!LoadImpl(Temp)) return false; if (!Result.DecodeFrom(Temp)) return false; break; } | ||||
| 		case FileSystem::EEncoding::UTF8:    { FU8String  Temp; if (!LoadImpl(Temp)) return false; if (!Result.DecodeFrom(Temp)) return false; break; } | ||||
| 		case FileSystem::EEncoding::UTF16BE: | ||||
| 		case FileSystem::EEncoding::UTF16LE: { FU16String Temp; if (!LoadImpl(Temp)) return false; if (!Result.DecodeFrom(Temp)) return false; break; } | ||||
| 		case FileSystem::EEncoding::UTF32BE: | ||||
| 		case FileSystem::EEncoding::UTF32LE: { FU32String Temp; if (!LoadImpl(Temp)) return false; if (!Result.DecodeFrom(Temp)) return false; break; } | ||||
| 		default: check_no_entry(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	else if (!LoadImpl(Result)) return false; | ||||
|  | ||||
| 	FileGuard.Release(); | ||||
|  | ||||
| 	if (std::fclose(File) != 0) return false; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| template REDCRAFTUTILITY_API bool LoadFileToString<char>   (FString&,    FStringView, FileSystem::EEncoding, bool); | ||||
| template REDCRAFTUTILITY_API bool LoadFileToString<wchar>  (FWString&,   FStringView, FileSystem::EEncoding, bool); | ||||
| template REDCRAFTUTILITY_API bool LoadFileToString<u8char> (FU8String&,  FStringView, FileSystem::EEncoding, bool); | ||||
| template REDCRAFTUTILITY_API bool LoadFileToString<u16char>(FU16String&, FStringView, FileSystem::EEncoding, bool); | ||||
| template REDCRAFTUTILITY_API bool LoadFileToString<u32char>(FU32String&, FStringView, FileSystem::EEncoding, bool); | ||||
|  | ||||
| template <CCharType T> | ||||
| bool SaveStringToFile(TStringView<T> String, FStringView Path, FileSystem::EEncoding Encoding /* = FileSystem::EEncoding::Default */, bool bWithBOM /* = true */) | ||||
| { | ||||
| 	bool bCompatible = Encoding == FileSystem::EEncoding::Default; | ||||
|  | ||||
| 	if      constexpr (CSameAs<T, char>)    bCompatible |= Encoding == FileSystem::EEncoding::Narrow; | ||||
| 	else if constexpr (CSameAs<T, wchar>)   bCompatible |= Encoding == FileSystem::EEncoding::Wide; | ||||
| 	else if constexpr (CSameAs<T, u8char>)  bCompatible |= Encoding == FileSystem::EEncoding::UTF8; | ||||
| 	else if constexpr (CSameAs<T, u16char>) bCompatible |= Encoding == FileSystem::EEncoding::UTF16BE || Encoding == FileSystem::EEncoding::UTF16LE; | ||||
| 	else if constexpr (CSameAs<T, u32char>) bCompatible |= Encoding == FileSystem::EEncoding::UTF32BE || Encoding == FileSystem::EEncoding::UTF32LE; | ||||
|  | ||||
| 	else static_assert(sizeof(T) == -1, "Unsupported character type"); | ||||
|  | ||||
| 	if (bCompatible) | ||||
| 	{ | ||||
| 		FILE* File = std::fopen(*Path, "wb"); | ||||
|  | ||||
| 		if (File == nullptr) return false; | ||||
|  | ||||
| 		auto FileGuard = TScopeCallback([=] { Ignore = std::fclose(File); }); | ||||
|  | ||||
| 		if (bWithBOM) | ||||
| 		{ | ||||
| 			if constexpr (CSameAs<T, u8char>) | ||||
| 			{ | ||||
| 				if (std::fwrite(U8TEXT("\uFEFF"), 1, 3, File) != 3) return false; | ||||
| 			} | ||||
|  | ||||
| 			else if constexpr (CSameAs<T, u16char>) | ||||
| 			{ | ||||
| 				constexpr TStaticArray<uint8, 2> BufferBE = { 0xFE, 0xFF }; | ||||
| 				constexpr TStaticArray<uint8, 2> BufferLE = { 0xFF, 0xFE }; | ||||
|  | ||||
| 				if      (Encoding == FileSystem::EEncoding::UTF16BE) { if (std::fwrite(BufferBE.GetData(), 1, BufferBE.Num(), File) != BufferBE.Num()) return false; } | ||||
| 				else if (Encoding == FileSystem::EEncoding::UTF16LE) { if (std::fwrite(BufferLE.GetData(), 1, BufferLE.Num(), File) != BufferLE.Num()) return false; } | ||||
|  | ||||
| 				else if (std::fwrite(U16TEXT("\uFEFF"), 1, sizeof(T), File) != sizeof(T)) return false; | ||||
| 			} | ||||
|  | ||||
| 			else if constexpr (CSameAs<T, u32char>) | ||||
| 			{ | ||||
| 				constexpr TStaticArray<uint8, 4> BufferBE = { 0x00, 0x00, 0xFE, 0xFF }; | ||||
| 				constexpr TStaticArray<uint8, 4> BufferLE = { 0xFF, 0xFE, 0x00, 0x00 }; | ||||
|  | ||||
| 				if      (Encoding == FileSystem::EEncoding::UTF32BE) { if (std::fwrite(BufferBE.GetData() , 1, BufferBE.Num(), File) != BufferBE.Num()) return false; } | ||||
| 				else if (Encoding == FileSystem::EEncoding::UTF32LE) { if (std::fwrite(BufferLE.GetData() , 1, BufferLE.Num(), File) != BufferLE.Num()) return false; } | ||||
|  | ||||
| 				else if (std::fwrite(U32TEXT("\uFEFF"), 1, sizeof(T), File) != sizeof(T)) return false; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		check(Math::EEndian::Native == Math::EEndian::Big || Math::EEndian::Native == Math::EEndian::Little); | ||||
|  | ||||
| 		const bool bByteSwap = | ||||
| 			Math::EEndian::Native == Math::EEndian::Big    ? Encoding == FileSystem::EEncoding::UTF16LE || Encoding == FileSystem::EEncoding::UTF32LE : | ||||
| 			Math::EEndian::Native == Math::EEndian::Little ? Encoding == FileSystem::EEncoding::UTF16BE || Encoding == FileSystem::EEncoding::UTF32BE : false; | ||||
|  | ||||
| 		for (T Char : String) | ||||
| 		{ | ||||
| #			if PLATFORM_WINDOWS | ||||
| 			{ | ||||
| 				if (Char == LITERAL(T, '\n')) | ||||
| 				{ | ||||
| 					T Return = LITERAL(T, '\r'); | ||||
|  | ||||
| 					if (bByteSwap) Return = Math::ByteSwap(static_cast<TMakeUnsigned<T>>(Return)); | ||||
|  | ||||
| 					if (std::fwrite(&Return, 1, sizeof(T), File) != sizeof(T)) return false; | ||||
| 				} | ||||
| 			} | ||||
| #			endif | ||||
|  | ||||
| 			if (bByteSwap) Char = Math::ByteSwap(static_cast<TMakeUnsigned<T>>(Char)); | ||||
|  | ||||
| 			if (std::fwrite(&Char, 1, sizeof(T), File) != sizeof(T)) return false; | ||||
| 		} | ||||
|  | ||||
| 		FileGuard.Release(); | ||||
|  | ||||
| 		if (std::fclose(File) != 0) return false; | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	FString PathWithNull; | ||||
|  | ||||
| 	PathWithNull.Reserve(Path.Num() + 1); | ||||
|  | ||||
| 	PathWithNull += Path; | ||||
| 	PathWithNull += '\0'; | ||||
|  | ||||
| 	switch (Encoding) | ||||
| 	{ | ||||
| 	case FileSystem::EEncoding::Narrow:  { FString    Temp; if (!Temp.DecodeFrom(String)) return false; if (!FileSystem::SaveStringToFile(Temp, PathWithNull, FileSystem::EEncoding::Narrow,  bWithBOM)) return false; break; } | ||||
| 	case FileSystem::EEncoding::Wide:    { FWString   Temp; if (!Temp.DecodeFrom(String)) return false; if (!FileSystem::SaveStringToFile(Temp, PathWithNull, FileSystem::EEncoding::Wide,    bWithBOM)) return false; break; } | ||||
| 	case FileSystem::EEncoding::UTF8:    { FU8String  Temp; if (!Temp.DecodeFrom(String)) return false; if (!FileSystem::SaveStringToFile(Temp, PathWithNull, FileSystem::EEncoding::UTF8,    bWithBOM)) return false; break; } | ||||
| 	case FileSystem::EEncoding::UTF16BE: { FU16String Temp; if (!Temp.DecodeFrom(String)) return false; if (!FileSystem::SaveStringToFile(Temp, PathWithNull, FileSystem::EEncoding::UTF16BE, bWithBOM)) return false; break; } | ||||
| 	case FileSystem::EEncoding::UTF16LE: { FU16String Temp; if (!Temp.DecodeFrom(String)) return false; if (!FileSystem::SaveStringToFile(Temp, PathWithNull, FileSystem::EEncoding::UTF16LE, bWithBOM)) return false; break; } | ||||
| 	case FileSystem::EEncoding::UTF32BE: { FU32String Temp; if (!Temp.DecodeFrom(String)) return false; if (!FileSystem::SaveStringToFile(Temp, PathWithNull, FileSystem::EEncoding::UTF32BE, bWithBOM)) return false; break; } | ||||
| 	case FileSystem::EEncoding::UTF32LE: { FU32String Temp; if (!Temp.DecodeFrom(String)) return false; if (!FileSystem::SaveStringToFile(Temp, PathWithNull, FileSystem::EEncoding::UTF32LE, bWithBOM)) return false; break; } | ||||
| 	default: check_no_entry(); return false; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| template REDCRAFTUTILITY_API bool SaveStringToFile<char>   (FStringView,    FStringView, FileSystem::EEncoding, bool); | ||||
| template REDCRAFTUTILITY_API bool SaveStringToFile<wchar>  (FWStringView,   FStringView, FileSystem::EEncoding, bool); | ||||
| template REDCRAFTUTILITY_API bool SaveStringToFile<u8char> (FU8StringView,  FStringView, FileSystem::EEncoding, bool); | ||||
| template REDCRAFTUTILITY_API bool SaveStringToFile<u16char>(FU16StringView, FStringView, FileSystem::EEncoding, bool); | ||||
| template REDCRAFTUTILITY_API bool SaveStringToFile<u32char>(FU32StringView, FStringView, FileSystem::EEncoding, bool); | ||||
|  | ||||
| size_t FileSize(FStringView Path) | ||||
| { | ||||
| 	if (!FileSystem::Exists(Path)) return static_cast<size_t>(-1); | ||||
|  | ||||
| 	FILE* File = std::fopen(*Path, "rb"); | ||||
|  | ||||
| 	if (File == nullptr) return static_cast<size_t>(-1); | ||||
|  | ||||
| 	auto FileGuard = TScopeCallback([=] { Ignore = std::fclose(File); }); | ||||
|  | ||||
| 	if (std::fseek(File, 0, SEEK_END) != 0) return static_cast<size_t>(-1); | ||||
|  | ||||
| 	const long Length = std::ftell(File); | ||||
|  | ||||
| 	if (!Math::IsWithin(Length, 0, TNumericLimits<long>::Max())) return static_cast<size_t>(-1); | ||||
|  | ||||
| 	FileGuard.Release(); | ||||
|  | ||||
| 	if (std::fclose(File) != 0) return static_cast<size_t>(-1); | ||||
|  | ||||
| 	return Length; | ||||
| } | ||||
|  | ||||
| bool Delete(FStringView Path) | ||||
| { | ||||
| 	return std::remove(*Path) == 0; | ||||
| } | ||||
|  | ||||
| bool Exists(FStringView Path) | ||||
| { | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		DWORD Attributes = GetFileAttributesA(*Path); | ||||
|  | ||||
| 		if (Attributes == INVALID_FILE_ATTRIBUTES) return false; | ||||
|  | ||||
| 		return !(Attributes & FILE_ATTRIBUTE_DIRECTORY); | ||||
| 	} | ||||
| #	elif PLATFORM_LINUX | ||||
| 	{ | ||||
| 		struct stat FileInfo; | ||||
|  | ||||
| 		FileInfo.st_size = -1; | ||||
|  | ||||
| 		if (stat(*Path, &FileInfo) != 0) return false; | ||||
|  | ||||
| 		if (!S_ISREG(FileInfo.st_mode)) return false; | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
| #	endif | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool Copy(FStringView Destination, FStringView Source) | ||||
| { | ||||
| 	if (!FileSystem::Exists(Source)) return false; | ||||
|  | ||||
| 	FILE* FileA = std::fopen(*Source, "rb"); | ||||
|  | ||||
| 	if (FileA == nullptr) return false; | ||||
|  | ||||
| 	auto FileGuardA = TScopeCallback([=] { Ignore = std::fclose(FileA); }); | ||||
|  | ||||
| 	FILE* FileB = std::fopen(*Destination, "wb"); | ||||
|  | ||||
| 	if (FileB == nullptr) return false; | ||||
|  | ||||
| 	auto FileGuardB = TScopeCallback([=] { Ignore = std::fclose(FileB); }); | ||||
|  | ||||
| 	size_t ReadSize; | ||||
|  | ||||
| 	constexpr size_t BufferSize = 4096; | ||||
|  | ||||
| 	TStaticArray<uint8, BufferSize> Buffer; | ||||
|  | ||||
| 	do | ||||
| 	{ | ||||
| 		ReadSize = std::fread(Buffer.GetData(), 1, BufferSize, FileA); | ||||
|  | ||||
| 		if (std::fwrite(Buffer.GetData(), 1, ReadSize, FileB) != ReadSize) return false; | ||||
| 	} | ||||
| 	while (ReadSize == BufferSize); | ||||
|  | ||||
| 	FileGuardA.Release(); | ||||
|  | ||||
| 	if (std::fclose(FileA) != 0) return false; | ||||
|  | ||||
| 	FileGuardB.Release(); | ||||
|  | ||||
| 	if (std::fclose(FileB) != 0) return false; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool Rename(FStringView Destination, FStringView Source) | ||||
| { | ||||
| 	return std::rename(*Source, *Destination) == 0; | ||||
| } | ||||
|  | ||||
| bool CreateDirectory(FStringView Path, bool bRecursive /* = false */) | ||||
| { | ||||
| 	if (Path.Num() == 0) return false; | ||||
|  | ||||
| 	if (bRecursive) | ||||
| 	{ | ||||
| 		if (Path.Back() == '/' || Path.Back() == '\\') Path = Path.First(Path.Num() - 1); | ||||
|  | ||||
| 		FStringView Parent = Path.First(Path.FindLastOf("/\\")); | ||||
|  | ||||
| 		if (!FileSystem::ExistsDirectory(Parent) && !FileSystem::CreateDirectory(Parent, true)) return false; | ||||
| 	} | ||||
|  | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		return CreateDirectoryA(*Path, nullptr) != 0; | ||||
| 	} | ||||
| #	elif PLATFORM_LINUX | ||||
| 	{ | ||||
| 		return mkdir(*Path, 0755) == 0; | ||||
| 	} | ||||
| #	endif | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool DeleteDirectory(FStringView Path, bool bRecursive /* = false */) | ||||
| { | ||||
| 	if (bRecursive) | ||||
| 	{ | ||||
| 		FString Temp; | ||||
|  | ||||
| 		bool bSuccessfully = FileSystem::IterateDirectory(Path, [&](FStringView File, bool bIsDirectory) -> bool | ||||
| 		{ | ||||
| 			Temp.Reset(false); | ||||
|  | ||||
| 			Temp += Path; | ||||
| 			Temp += '/'; | ||||
| 			Temp += File; | ||||
| 			Temp += '\0'; | ||||
|  | ||||
| 			if (bIsDirectory) | ||||
| 			{ | ||||
| 				if (!FileSystem::DeleteDirectory(Temp, true)) return false; | ||||
| 			} | ||||
|  | ||||
| 			else | ||||
| 			{ | ||||
| 				if (!FileSystem::Delete(Temp)) return false; | ||||
| 			} | ||||
|  | ||||
| 			return true; | ||||
| 		}); | ||||
|  | ||||
| 		if (!bSuccessfully) return false; | ||||
| 	} | ||||
|  | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		return RemoveDirectoryA(*Path) != 0; | ||||
| 	} | ||||
| #	elif PLATFORM_LINUX | ||||
| 	{ | ||||
| 		return rmdir(*Path) == 0; | ||||
| 	} | ||||
| #	endif | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool ExistsDirectory(FStringView Path) | ||||
| { | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		DWORD Attributes = GetFileAttributesA(*Path); | ||||
|  | ||||
| 		if (Attributes == INVALID_FILE_ATTRIBUTES) return false; | ||||
|  | ||||
| 		return Attributes & FILE_ATTRIBUTE_DIRECTORY; | ||||
| 	} | ||||
| #	elif PLATFORM_LINUX | ||||
| 	{ | ||||
| 		DIR* Directory = opendir(*Path); | ||||
|  | ||||
| 		if (Directory == nullptr) return false; | ||||
|  | ||||
| 		Ignore = closedir(Directory); | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
| #	endif | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool IterateDirectory(FStringView Path, TFunctionRef<bool(FStringView /* Path */, bool /* bIsDirectory */)> Visitor) | ||||
| { | ||||
| #	if PLATFORM_WINDOWS | ||||
| 	{ | ||||
| 		FString FindPath; | ||||
|  | ||||
| 		FindPath.Reserve(Path.Num() + 3); | ||||
|  | ||||
| 		FindPath += Path; | ||||
| 		FindPath += '\\'; | ||||
| 		FindPath += '*'; | ||||
| 		FindPath += '\0'; | ||||
|  | ||||
| 		WIN32_FIND_DATA FindData; | ||||
|  | ||||
| 		HANDLE FindHandle = FindFirstFileA(*FindPath, &FindData); | ||||
|  | ||||
| 		auto FindGuard = TScopeCallback([=] { Ignore = FindClose(FindHandle); }); | ||||
|  | ||||
| 		if (FindHandle == INVALID_HANDLE_VALUE) return false; | ||||
|  | ||||
| 		do | ||||
| 		{ | ||||
| 			const FStringView FilePath = FindData.cFileName; | ||||
|  | ||||
| 			if (FilePath == "." || FilePath == "..") continue; | ||||
|  | ||||
| 			const bool bIsDirectory = (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; | ||||
|  | ||||
| 			if (!Visitor(FilePath, bIsDirectory)) return false; | ||||
| 		} | ||||
| 		while (FindNextFileA(FindHandle, &FindData) != 0); | ||||
|  | ||||
| 		FindGuard.Release(); | ||||
|  | ||||
| 		if (!FindClose(FindHandle)) return false; | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
| #	elif PLATFORM_LINUX | ||||
| 	{ | ||||
| 		DIR* Directory = opendir(*Path); | ||||
|  | ||||
| 		if (Directory == nullptr) return false; | ||||
|  | ||||
| 		auto DirectoryGuard = TScopeCallback([=] { Ignore = closedir(Directory); }); | ||||
|  | ||||
| 		dirent* Entry; | ||||
|  | ||||
| 		while ((Entry = readdir(Directory)) != nullptr) | ||||
| 		{ | ||||
| 			const FStringView FilePath = Entry->d_name; | ||||
|  | ||||
| 			if (FilePath == "." || FilePath == "..") continue; | ||||
|  | ||||
| 			const bool bIsDirectory = Entry->d_type == DT_DIR; | ||||
|  | ||||
| 			if (!Visitor(FilePath, bIsDirectory)) return false; | ||||
| 		} | ||||
|  | ||||
| 		DirectoryGuard.Release(); | ||||
|  | ||||
| 		if (closedir(Directory) != 0) return false; | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
| #	endif | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| NAMESPACE_END(FileSystem) | ||||
|  | ||||
| NAMESPACE_MODULE_END(Utility) | ||||
| NAMESPACE_MODULE_END(Redcraft) | ||||
| NAMESPACE_REDCRAFT_END | ||||
|  | ||||
| #pragma warning(pop) | ||||
							
								
								
									
										32
									
								
								Redcraft.Utility/Source/Public/Miscellaneous/BitwiseEnum.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								Redcraft.Utility/Source/Public/Miscellaneous/BitwiseEnum.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "CoreTypes.h" | ||||
| #include "TypeTraits/TypeTraits.h" | ||||
|  | ||||
| NAMESPACE_REDCRAFT_BEGIN | ||||
| NAMESPACE_MODULE_BEGIN(Redcraft) | ||||
| NAMESPACE_MODULE_BEGIN(Utility) | ||||
|  | ||||
| #define ENABLE_ENUM_CLASS_BITWISE_OPERATIONS(Enum)                                                                                                                                         \ | ||||
| 	NODISCARD FORCEINLINE constexpr Enum  operator| (Enum  LHS, Enum RHS) { return static_cast<Enum>(static_cast<TUnderlyingType<Enum>>(LHS) | static_cast<TUnderlyingType<Enum>>(RHS)); } \ | ||||
| 	NODISCARD FORCEINLINE constexpr Enum  operator& (Enum  LHS, Enum RHS) { return static_cast<Enum>(static_cast<TUnderlyingType<Enum>>(LHS) & static_cast<TUnderlyingType<Enum>>(RHS)); } \ | ||||
| 	NODISCARD FORCEINLINE constexpr Enum  operator^ (Enum  LHS, Enum RHS) { return static_cast<Enum>(static_cast<TUnderlyingType<Enum>>(LHS) ^ static_cast<TUnderlyingType<Enum>>(RHS)); } \ | ||||
| 	          FORCEINLINE constexpr Enum& operator|=(Enum& LHS, Enum RHS) { LHS = LHS | RHS; return LHS; }                                                                                 \ | ||||
| 	          FORCEINLINE constexpr Enum& operator&=(Enum& LHS, Enum RHS) { LHS = LHS & RHS; return LHS; }                                                                                 \ | ||||
| 	          FORCEINLINE constexpr Enum& operator^=(Enum& LHS, Enum RHS) { LHS = LHS ^ RHS; return LHS; }                                                                                 \ | ||||
| 	NODISCARD FORCEINLINE constexpr bool  operator! (Enum  E            ) { return                   !static_cast<TUnderlyingType<Enum>>(E);  }                                            \ | ||||
| 	NODISCARD FORCEINLINE constexpr Enum  operator~ (Enum  E            ) { return static_cast<Enum>(~static_cast<TUnderlyingType<Enum>>(E)); } | ||||
|  | ||||
| #define FRIEND_ENUM_CLASS_BITWISE_OPERATIONS(Enum)  \ | ||||
| 	friend constexpr Enum  operator| (Enum , Enum); \ | ||||
| 	friend constexpr Enum  operator& (Enum , Enum); \ | ||||
| 	friend constexpr Enum  operator^ (Enum , Enum); \ | ||||
| 	friend constexpr Enum& operator|=(Enum&, Enum); \ | ||||
| 	friend constexpr Enum& operator&=(Enum&, Enum); \ | ||||
| 	friend constexpr Enum& operator^=(Enum&, Enum); \ | ||||
| 	friend constexpr bool  operator! (Enum );       \ | ||||
| 	friend constexpr Enum  operator~ (Enum ); | ||||
|  | ||||
| NAMESPACE_MODULE_END(Utility) | ||||
| NAMESPACE_MODULE_END(Redcraft) | ||||
| NAMESPACE_REDCRAFT_END | ||||
							
								
								
									
										217
									
								
								Redcraft.Utility/Source/Public/Miscellaneous/Console.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								Redcraft.Utility/Source/Public/Miscellaneous/Console.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,217 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "CoreTypes.h" | ||||
| #include "TypeTraits/TypeTraits.h" | ||||
| #include "Templates/Utility.h" | ||||
| #include "Iterators/Utility.h" | ||||
| #include "Strings/Formatting.h" | ||||
| #include "Strings/StringView.h" | ||||
| #include "Strings/String.h" | ||||
| #include "Miscellaneous/BitwiseEnum.h" | ||||
|  | ||||
| NAMESPACE_REDCRAFT_BEGIN | ||||
| NAMESPACE_MODULE_BEGIN(Redcraft) | ||||
| NAMESPACE_MODULE_BEGIN(Utility) | ||||
|  | ||||
| /** An enumeration that defines the color of the console. */ | ||||
| enum class EColor : uint8 | ||||
| { | ||||
| 	Default = 0xFF, | ||||
|  | ||||
| 	Black     = 0b0000, | ||||
| 	Red       = 0b0001, | ||||
| 	Green     = 0b0010, | ||||
| 	Blue      = 0b0100, | ||||
| 	Intensity = 0b1000, | ||||
|  | ||||
| 	Cyan    = Green | Blue, | ||||
| 	Magenta = Blue  | Red, | ||||
| 	Yellow  = Red   | Green, | ||||
|  | ||||
| 	White = Red | Green | Blue, | ||||
|  | ||||
| 	BrightBlack   = Intensity | Black, | ||||
| 	BrightRed     = Intensity | Red, | ||||
| 	BrightGreen   = Intensity | Green, | ||||
| 	BrightBlue    = Intensity | Blue, | ||||
| 	BrightYellow  = Intensity | Yellow, | ||||
| 	BrightMagenta = Intensity | Magenta, | ||||
| 	BrightCyan    = Intensity | Cyan, | ||||
| 	BrightWhite   = Intensity | White | ||||
| }; | ||||
|  | ||||
| ENABLE_ENUM_CLASS_BITWISE_OPERATIONS(EColor) | ||||
|  | ||||
| /** @return The color of the console. */ | ||||
| NODISCARD REDCRAFTUTILITY_API EColor GetForegroundColor(); | ||||
| NODISCARD REDCRAFTUTILITY_API EColor GetBackgroundColor(); | ||||
|  | ||||
| /** Set the color of the console. Returns the color that was successfully set. */ | ||||
| REDCRAFTUTILITY_API EColor SetForegroundColor(EColor InColor); | ||||
| REDCRAFTUTILITY_API EColor SetBackgroundColor(EColor InColor); | ||||
|  | ||||
| /** @return The size of the console window. */ | ||||
| NODISCARD REDCRAFTUTILITY_API uint GetWindowWidth(); | ||||
| NODISCARD REDCRAFTUTILITY_API uint GetWindowHeight(); | ||||
|  | ||||
| /** @return true if the standard stream is redirected. */ | ||||
| NODISCARD REDCRAFTUTILITY_API bool IsInputRedirected(); | ||||
| NODISCARD REDCRAFTUTILITY_API bool IsOutputRedirected(); | ||||
| NODISCARD REDCRAFTUTILITY_API bool IsErrorRedirected(); | ||||
|  | ||||
| /** Clear the console screen. */ | ||||
| REDCRAFTUTILITY_API void Clear(); | ||||
|  | ||||
| /** @return The input character from the standard input. */ | ||||
| NODISCARD REDCRAFTUTILITY_API char Input(bool bEcho = true); | ||||
|  | ||||
| /** @return The input line from the standard input. */ | ||||
| NODISCARD REDCRAFTUTILITY_API FString InputLn(bool bEcho = true); | ||||
|  | ||||
| /** Print the character to the standard output. */ | ||||
| REDCRAFTUTILITY_API bool Print(char Char); | ||||
|  | ||||
| /** Print the formatted string to the standard output. */ | ||||
| template <CFormattable... Ts> | ||||
| FORCEINLINE bool Print(FStringView Fmt, Ts&&... Args) | ||||
| { | ||||
| 	struct FStandardOutputIterator | ||||
| 	{ | ||||
| 		FORCEINLINE constexpr FStandardOutputIterator& operator=(char Char) | ||||
| 		{ | ||||
| 			bError |= !Print(Char); | ||||
| 			return *this; | ||||
| 		} | ||||
|  | ||||
| 		FORCEINLINE constexpr bool operator==(FDefaultSentinel) const { return bError; } | ||||
|  | ||||
| 		FORCEINLINE constexpr FStandardOutputIterator& operator*()     { return *this; } | ||||
| 		FORCEINLINE constexpr FStandardOutputIterator& operator++()    { return *this; } | ||||
| 		FORCEINLINE constexpr FStandardOutputIterator& operator++(int) { return *this; } | ||||
|  | ||||
| 		bool bError = false; | ||||
| 	}; | ||||
|  | ||||
| 	static_assert(COutputIterator<FStandardOutputIterator, char>); | ||||
|  | ||||
| 	FStandardOutputIterator Iter; | ||||
|  | ||||
| 	auto Range = Ranges::View(Iter, DefaultSentinel); | ||||
|  | ||||
| 	Iter = Algorithms::Format(Range, Fmt, Forward<Ts>(Args)...); | ||||
|  | ||||
| 	return Iter != DefaultSentinel; | ||||
| } | ||||
|  | ||||
| /** Print the value to the standard output. */ | ||||
| template <CFormattable T> | ||||
| FORCEINLINE bool Print(T&& Value) | ||||
| { | ||||
| 	if constexpr (CSameAs<TRemoveCVRef<T>, char>) | ||||
| 	{ | ||||
| 		return Print(static_cast<char>(Value)); | ||||
| 	} | ||||
|  | ||||
| 	else if constexpr (CConvertibleTo<T, FStringView>) | ||||
| 	{ | ||||
| 		return Print(static_cast<FStringView>(Value)); | ||||
| 	} | ||||
|  | ||||
| 	else return Print(TEXT("{0}"), Forward<T>(Value)); | ||||
| } | ||||
|  | ||||
| /** Print the newline character to the standard output. */ | ||||
| FORCEINLINE bool PrintLn() | ||||
| { | ||||
| 	return Print(TEXT("\n")); | ||||
| } | ||||
|  | ||||
| /** Print the string to the standard output and append the newline character. */ | ||||
| template <CFormattable... Ts> | ||||
| FORCEINLINE bool PrintLn(FStringView Fmt, Ts&&... Args) | ||||
| { | ||||
| 	return Print(Fmt, Forward<Ts>(Args)...) && Print(TEXT("\n")); | ||||
| } | ||||
|  | ||||
| /** Print the value to the standard output and append the newline character. */ | ||||
| template <CFormattable T> | ||||
| FORCEINLINE bool PrintLn(T&& Value) | ||||
| { | ||||
| 	return Print(Forward<T>(Value)) && Print(TEXT("\n")); | ||||
| } | ||||
|  | ||||
| /** Print the character to the standard error. */ | ||||
| REDCRAFTUTILITY_API bool Error(char Char); | ||||
|  | ||||
| /** Print the formatted string to the standard error. */ | ||||
| template <CFormattable... Ts> | ||||
| FORCEINLINE bool Error(FStringView Fmt, Ts&&... Args) | ||||
| { | ||||
| 	struct FStandardOutputIterator | ||||
| 	{ | ||||
| 		FORCEINLINE constexpr FStandardOutputIterator& operator=(char Char) | ||||
| 		{ | ||||
| 			bError |= !Error(Char); | ||||
| 			return *this; | ||||
| 		} | ||||
|  | ||||
| 		FORCEINLINE constexpr bool operator==(FDefaultSentinel) const { return bError; } | ||||
|  | ||||
| 		FORCEINLINE constexpr FStandardOutputIterator& operator*()     { return *this; } | ||||
| 		FORCEINLINE constexpr FStandardOutputIterator& operator++()    { return *this; } | ||||
| 		FORCEINLINE constexpr FStandardOutputIterator& operator++(int) { return *this; } | ||||
|  | ||||
| 		bool bError = false; | ||||
| 	}; | ||||
|  | ||||
| 	static_assert(COutputIterator<FStandardOutputIterator, char>); | ||||
|  | ||||
| 	FStandardOutputIterator Iter; | ||||
|  | ||||
| 	auto Range = Ranges::View(Iter, DefaultSentinel); | ||||
|  | ||||
| 	Iter = Algorithms::Format(Range, Fmt, Forward<Ts>(Args)...); | ||||
|  | ||||
| 	return Iter != DefaultSentinel; | ||||
| } | ||||
|  | ||||
| /** Print the value to the standard error. */ | ||||
| template <CFormattable T> | ||||
| FORCEINLINE bool Error(T&& Value) | ||||
| { | ||||
| 	if constexpr (CSameAs<TRemoveCVRef<T>, char>) | ||||
| 	{ | ||||
| 		return Error(static_cast<char>(Value)); | ||||
| 	} | ||||
|  | ||||
| 	else if constexpr (CConvertibleTo<T, FStringView>) | ||||
| 	{ | ||||
| 		return Error(static_cast<FStringView>(Value)); | ||||
| 	} | ||||
|  | ||||
| 	else return Error(TEXT("{0}"), Forward<T>(Value)); | ||||
| } | ||||
|  | ||||
| /** Print the newline character to the standard error. */ | ||||
| FORCEINLINE bool ErrorLn() | ||||
| { | ||||
| 	return Error(TEXT("\n")); | ||||
| } | ||||
|  | ||||
| /** Print the string to the standard error and append the newline character. */ | ||||
| template <CFormattable... Ts> | ||||
| FORCEINLINE bool ErrorLn(FStringView Fmt, Ts&&... Args) | ||||
| { | ||||
| 	return Error(Fmt, Forward<Ts>(Args)...) && Error(TEXT("\n")); | ||||
| } | ||||
|  | ||||
| /** Print the value to the standard error and append the newline character. */ | ||||
| template <CFormattable T> | ||||
| FORCEINLINE bool ErrorLn(T&& Value) | ||||
| { | ||||
| 	return Error(Forward<T>(Value)) && Error(TEXT("\n")); | ||||
| } | ||||
|  | ||||
| NAMESPACE_MODULE_END(Utility) | ||||
| NAMESPACE_MODULE_END(Redcraft) | ||||
| NAMESPACE_REDCRAFT_END | ||||
							
								
								
									
										116
									
								
								Redcraft.Utility/Source/Public/Miscellaneous/FileSystem.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								Redcraft.Utility/Source/Public/Miscellaneous/FileSystem.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "CoreTypes.h" | ||||
| #include "TypeTraits/TypeTraits.h" | ||||
| #include "Templates/Utility.h" | ||||
| #include "Templates/Function.h" | ||||
| #include "Containers/Array.h" | ||||
| #include "Strings/StringView.h" | ||||
| #include "Strings/String.h" | ||||
|  | ||||
| NAMESPACE_REDCRAFT_BEGIN | ||||
| NAMESPACE_MODULE_BEGIN(Redcraft) | ||||
| NAMESPACE_MODULE_BEGIN(Utility) | ||||
|  | ||||
| NAMESPACE_BEGIN(FileSystem) | ||||
|  | ||||
| /** The encoding of the text file. */ | ||||
| enum class EEncoding : uint8 | ||||
| { | ||||
| 	Default, | ||||
| 	Narrow, | ||||
| 	Wide, | ||||
| 	UTF8, | ||||
| 	UTF16BE, | ||||
| 	UTF16LE, | ||||
| 	UTF32BE, | ||||
| 	UTF32LE, | ||||
| }; | ||||
|  | ||||
| /** Loads the file at the specified path into the byte array. */ | ||||
| REDCRAFTUTILITY_API bool LoadFileToArray(TArray<uint8>& Result, FStringView Path); | ||||
|  | ||||
| /** Saves the byte array to the file at the specified path. */ | ||||
| REDCRAFTUTILITY_API bool SaveArrayToFile(TArrayView<const uint8> Data, FStringView Path); | ||||
|  | ||||
| /** | ||||
|  * Loads the file at the specified path into the string. | ||||
|  * | ||||
|  * @param Result   - The string to load the file into. | ||||
|  * @param Path     - The path to the file to load. | ||||
|  * @param Encoding - The encoding of the file. The default value indicates automatic detection. | ||||
|  * @param bVerify  - Whether to verify the character validity of the file. | ||||
|  * | ||||
|  * @return true if the file was successfully loaded, false otherwise. | ||||
|  */ | ||||
| template <CCharType T> | ||||
| REDCRAFTUTILITY_API bool LoadFileToString(TString<T>& Result, FStringView Path, FileSystem::EEncoding Encoding = FileSystem::EEncoding::Default, bool bVerify = false); | ||||
|  | ||||
| /** | ||||
|  * Saves the string to the file at the specified path. | ||||
|  * | ||||
|  * @param String   - The string to save to the file. | ||||
|  * @param Path     - The path to the file to save. | ||||
|  * @param Encoding - The encoding of the file. The default value indicates the same as the string. | ||||
|  * @param bWithBOM - Whether to write the BOM character at the beginning of the file. Not valid for narrow and wide encoding. | ||||
|  * | ||||
|  * @return true if the file was successfully saved, false otherwise. | ||||
|  */ | ||||
| template <CCharType T> | ||||
| REDCRAFTUTILITY_API bool SaveStringToFile(TStringView<T> String, FStringView Path, FileSystem::EEncoding Encoding = FileSystem::EEncoding::Default, bool bWithBOM = true); | ||||
|  | ||||
| /** | ||||
|  * Saves the string to the file at the specified path. | ||||
|  * | ||||
|  * @param String   - The string to save to the file. | ||||
|  * @param Path     - The path to the file to save. | ||||
|  * @param Encoding - The encoding of the file. The default value indicates the same as the string. | ||||
|  * @param bWithBOM - Whether to write the BOM character at the beginning of the file. Not valid for narrow and wide encoding. | ||||
|  * | ||||
|  * @return true if the file was successfully saved, false otherwise. | ||||
|  */ | ||||
| template <typename T> requires (CConvertibleTo<T&&, FStringView> || CConvertibleTo<T&&, FWStringView> | ||||
| 	|| CConvertibleTo<T&&, FU8StringView> || CConvertibleTo<T&&, FU16StringView> || CConvertibleTo<T&&, FU32StringView>) | ||||
| bool SaveStringToFile(T&& String, FStringView Path, FileSystem::EEncoding Encoding = FileSystem::EEncoding::Default, bool bWithBOM = true) | ||||
| { | ||||
| 	if      constexpr (CConvertibleTo<T&&, FStringView>)    return SaveStringToFile(FStringView   (Forward<T>(String)), Path, Encoding, bWithBOM); | ||||
| 	else if constexpr (CConvertibleTo<T&&, FWStringView>)   return SaveStringToFile(FWStringView  (Forward<T>(String)), Path, Encoding, bWithBOM); | ||||
| 	else if constexpr (CConvertibleTo<T&&, FU8StringView>)  return SaveStringToFile(FU8StringView (Forward<T>(String)), Path, Encoding, bWithBOM); | ||||
| 	else if constexpr (CConvertibleTo<T&&, FU16StringView>) return SaveStringToFile(FU16StringView(Forward<T>(String)), Path, Encoding, bWithBOM); | ||||
| 	else if constexpr (CConvertibleTo<T&&, FU32StringView>) return SaveStringToFile(FU32StringView(Forward<T>(String)), Path, Encoding, bWithBOM); | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| /** @return The size of the file at the specified path. */ | ||||
| REDCRAFTUTILITY_API size_t FileSize(FStringView Path); | ||||
|  | ||||
| /** Deletes the file at the specified path. */ | ||||
| REDCRAFTUTILITY_API bool Delete(FStringView Path); | ||||
|  | ||||
| /** @return true if the regular file at the specified path exists, false otherwise. */ | ||||
| REDCRAFTUTILITY_API bool Exists(FStringView Path); | ||||
|  | ||||
| /** Copies the file from the source path to the destination path. */ | ||||
| REDCRAFTUTILITY_API bool Copy(FStringView Destination, FStringView Source); | ||||
|  | ||||
| /** Renames the file from the source path to the destination path. */ | ||||
| REDCRAFTUTILITY_API bool Rename(FStringView Destination, FStringView Source); | ||||
|  | ||||
| /** Creates the directory at the specified path. If recursive, it will not delete the created items on failure. */ | ||||
| REDCRAFTUTILITY_API bool CreateDirectory(FStringView Path, bool bRecursive = false); | ||||
|  | ||||
| /** Deletes the directory at the specified path. If recursive, it will not recreate the deleted items on failure. */ | ||||
| REDCRAFTUTILITY_API bool DeleteDirectory(FStringView Path, bool bRecursive = false); | ||||
|  | ||||
| /** @return true if the directory at the specified path exists, false otherwise. */ | ||||
| REDCRAFTUTILITY_API bool ExistsDirectory(FStringView Path); | ||||
|  | ||||
| /** Iterates items in the directory at the specified path. */ | ||||
| REDCRAFTUTILITY_API bool IterateDirectory(FStringView Path, TFunctionRef<bool(FStringView /* Path */, bool /* bIsDirectory */)> Visitor); | ||||
|  | ||||
| NAMESPACE_END(FileSystem) | ||||
|  | ||||
| NAMESPACE_MODULE_END(Utility) | ||||
| NAMESPACE_MODULE_END(Redcraft) | ||||
| NAMESPACE_REDCRAFT_END | ||||
| @@ -15,6 +15,9 @@ | ||||
|  | ||||
| #include <charconv> | ||||
|  | ||||
| #pragma warning(push) | ||||
| #pragma warning(disable: 4244) | ||||
|  | ||||
| NAMESPACE_REDCRAFT_BEGIN | ||||
| NAMESPACE_MODULE_BEGIN(Redcraft) | ||||
| NAMESPACE_MODULE_BEGIN(Utility) | ||||
| @@ -3176,3 +3179,5 @@ private: | ||||
| NAMESPACE_MODULE_END(Utility) | ||||
| NAMESPACE_MODULE_END(Redcraft) | ||||
| NAMESPACE_REDCRAFT_END | ||||
|  | ||||
| #pragma warning(pop) | ||||
|   | ||||
| @@ -1124,10 +1124,7 @@ public: | ||||
| 	/** @return The non-modifiable standard C character string version of the string. */ | ||||
| 	NODISCARD FORCEINLINE auto operator*() && | ||||
| 	{ | ||||
| 		if (this->Back() != LITERAL(T, '\0')) | ||||
| 		{ | ||||
| 			this->PushBack(LITERAL(T, '\0')); | ||||
| 		} | ||||
| 		if (!EndsWith(LITERAL(T, '\0'))) this->PushBack(LITERAL(T, '\0')); | ||||
|  | ||||
| 		return AsConst(*this).GetData(); | ||||
| 	} | ||||
|   | ||||
| @@ -493,7 +493,7 @@ public: | ||||
| 	/** @return The non-modifiable standard C character string version of the string view. */ | ||||
| 	NODISCARD FORCEINLINE auto operator*() const | ||||
| 	{ | ||||
| 		if (this->Back() == LITERAL(FElementType, '\0') || Contains(LITERAL(FElementType, '\0'))) | ||||
| 		if (EndsWith(LITERAL(FElementType, '\0')) || Contains(LITERAL(FElementType, '\0'))) | ||||
| 		{ | ||||
| 			return NAMESPACE_PRIVATE::TCStringFromTStringView<FElementType>(this->GetData(), false); | ||||
| 		} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user