aboutsummaryrefslogtreecommitdiff
path: root/src/brussel.common/Utils.cpp
blob: f0ff76defc5d05645da9960c7f7e5712cdabadeb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include "Utils.hpp"

#include "Macros.hpp"
#include "ScopeGuard.hpp"

#ifdef _WIN32
#	include <Windows.h>
#endif

namespace fs = std::filesystem;

#ifdef _WIN32
#	define BRUSSEL_MODE_STRING(string) L##string
#else
#	define BRUSSEL_MODE_STRING(string) string
#endif

#if _WIN32
using FopenModeString = const wchar_t*;
#else
using FopenModeString = const char*;
#endif

static FopenModeString GetModeString(Utils::IoMode mode, bool binary) {
	using namespace Utils;
	if (binary) {
		switch (mode) {
			case Read: return BRUSSEL_MODE_STRING("rb");
			case WriteTruncate: return BRUSSEL_MODE_STRING("wb");
			case WriteAppend: return BRUSSEL_MODE_STRING("ab");
		}
	} else {
		switch (mode) {
			case Read: return BRUSSEL_MODE_STRING("r");
			case WriteTruncate: return BRUSSEL_MODE_STRING("w");
			case WriteAppend: return BRUSSEL_MODE_STRING("a");
		}
	}
	return nullptr;
}

FILE* Utils::OpenCstdioFile(const fs::path& path, IoMode mode, bool binary) {
#ifdef _WIN32
	// fs::path::c_str() returns `const wchar_t*` under Windows, because NT uses UTF-16 natively
	// NOTE: _wfopen() only affects the type of path parameter, otherwise the file stream created is identical to the one by fopen()
	return _wfopen(path.c_str(), ::GetModeString(mode, binary));
#else
	return fopen(path.c_str(), ::GetModeString(mode, binary));
#endif
}

FILE* Utils::OpenCstdioFile(const char* path, IoMode mode, bool binary) {
#ifdef _WIN32
	// On Windows, fopen() accepts ANSI codepage encoded path, convert our UTF-8 string to UTF-16 to ensure that no matter what the locale is, the path continues to work
	WCHAR platformPath[MAX_PATH];
	if (MultiByteToWideChar(CP_UTF8, 0, path, -1, platformPath, MAX_PATH) == 0) {
		return nullptr;
	}
	return _wfopen(platformPath, ::GetModeString(mode, binary));
#else
	return fopen(path, ::GetModeString(mode, binary));
#endif
}

std::string Utils::ReadFileAsString(const fs::path& path) {
	auto file = Utils::OpenCstdioFile(path, Utils::Read);
	if (!file) throw std::runtime_error("Failed to open source file.");
	DEFER { fclose(file); };

	fseek(file, 0, SEEK_END);
	auto fileSize = ftell(file);
	rewind(file);

	std::string result(fileSize, '\0');
	fread(result.data(), fileSize, 1, file);

	return result;
}

bool Utils::ReadCstdioLine(FILE* file, std::string& buffer) {
	buffer.clear();
	while (true) {
		int c = fgetc(file);
		if (c == EOF) {
			if (buffer.empty() || buffer.back() != '\n') {
				buffer += '\n';
			}
			return false;
		} else if (c == '\n') {
			buffer += '\n';
			return true;
		} else {
			buffer += c;
		}
	}
}

bool Utils::ReadCstdioLine(FILE* file, char* buffer, size_t bufferSize, size_t* outLineLength) {
	// TODO
	assert(false && "Unimplemented");
}

bool Utils::InRangeInclusive(int n, int lower, int upper) {
	if (lower > upper) {
		std::swap(lower, upper);
	}
	return n >= lower && n <= upper;
}

bool Utils::LineContains(glm::ivec2 p1, glm::ivec2 p2, glm::ivec2 candidate) {
	bool verticalLine = p1.x == p2.x && InRangeInclusive(candidate.x, p1.x, p2.x);
	bool horizontalLine = p1.y == p2.y && InRangeInclusive(candidate.y, p1.y, p2.y);
	return verticalLine && horizontalLine;
}

bool Utils::IsColinear(glm::ivec2 p1, glm::ivec2 p2) {
	return p1.x == p2.x || p1.y == p2.y;
}

std::string Utils::MakeRandomNumberedName(const char* tag) {
	int n = std::rand();
#define RNG_NAME_PATTERN "Unnamed %s #%d", tag, n
	// NOTE: does not include null-terminator
	int size = snprintf(nullptr, 0, RNG_NAME_PATTERN);
	std::string result;
	result.resize(size); // std::string::resize handles storage for null-terminator alreaedy
	snprintf(result.data(), size, RNG_NAME_PATTERN);
#undef RNG_NAME_PATTERN
	return result;
}