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> | #include <charconv> | ||||||
|  |  | ||||||
|  | #pragma warning(push) | ||||||
|  | #pragma warning(disable: 4244) | ||||||
|  |  | ||||||
| NAMESPACE_REDCRAFT_BEGIN | NAMESPACE_REDCRAFT_BEGIN | ||||||
| NAMESPACE_MODULE_BEGIN(Redcraft) | NAMESPACE_MODULE_BEGIN(Redcraft) | ||||||
| NAMESPACE_MODULE_BEGIN(Utility) | NAMESPACE_MODULE_BEGIN(Utility) | ||||||
| @@ -3176,3 +3179,5 @@ private: | |||||||
| NAMESPACE_MODULE_END(Utility) | NAMESPACE_MODULE_END(Utility) | ||||||
| NAMESPACE_MODULE_END(Redcraft) | NAMESPACE_MODULE_END(Redcraft) | ||||||
| NAMESPACE_REDCRAFT_END | NAMESPACE_REDCRAFT_END | ||||||
|  |  | ||||||
|  | #pragma warning(pop) | ||||||
|   | |||||||
| @@ -1124,10 +1124,7 @@ public: | |||||||
| 	/** @return The non-modifiable standard C character string version of the string. */ | 	/** @return The non-modifiable standard C character string version of the string. */ | ||||||
| 	NODISCARD FORCEINLINE auto operator*() && | 	NODISCARD FORCEINLINE auto operator*() && | ||||||
| 	{ | 	{ | ||||||
| 		if (this->Back() != LITERAL(T, '\0')) | 		if (!EndsWith(LITERAL(T, '\0'))) this->PushBack(LITERAL(T, '\0')); | ||||||
| 		{ |  | ||||||
| 			this->PushBack(LITERAL(T, '\0')); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return AsConst(*this).GetData(); | 		return AsConst(*this).GetData(); | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -493,7 +493,7 @@ public: | |||||||
| 	/** @return The non-modifiable standard C character string version of the string view. */ | 	/** @return The non-modifiable standard C character string version of the string view. */ | ||||||
| 	NODISCARD FORCEINLINE auto operator*() const | 	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); | 			return NAMESPACE_PRIVATE::TCStringFromTStringView<FElementType>(this->GetData(), false); | ||||||
| 		} | 		} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user