9#define WIN32_LEAN_AND_MEAN
16#pragma comment(lib, "Shlwapi.lib")
22 UnmapViewOfFile(content.
data);
23 CloseHandle(mapping_handle);
25 if(file_handle != INVALID_HANDLE_VALUE) {
26 CloseHandle(file_handle);
30file::file(file&& other) noexcept : absolute_path(std::move(other.absolute_path)) {
31 mapping_handle = other.mapping_handle;
32 file_handle = other.file_handle;
33 other.mapping_handle =
nullptr;
34 other.file_handle = INVALID_HANDLE_VALUE;
35 content = other.content;
38 mapping_handle = other.mapping_handle;
39 file_handle = other.file_handle;
40 other.mapping_handle =
nullptr;
41 other.file_handle = INVALID_HANDLE_VALUE;
42 content = other.content;
43 absolute_path = std::move(other.absolute_path);
47 file_handle = CreateFileW(full_path.c_str(), GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING,
48 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
nullptr);
49 if(file_handle != INVALID_HANDLE_VALUE) {
50 absolute_path = full_path;
51 mapping_handle = CreateFileMappingW(file_handle,
nullptr, PAGE_READONLY, 0, 0,
nullptr);
53 content.
data = (
char const*)MapViewOfFile(mapping_handle, FILE_MAP_READ, 0, 0, 0);
55 _LARGE_INTEGER pvalue;
56 GetFileSizeEx(file_handle, &pvalue);
62file::file(HANDLE file_handle,
native_string const& full_path) : file_handle(file_handle) {
63 absolute_path = full_path;
64 mapping_handle = CreateFileMappingW(file_handle,
nullptr, PAGE_READONLY, 0, 0,
nullptr);
66 content.data = (
char const*)MapViewOfFile(mapping_handle, FILE_MAP_READ, 0, 0, 0);
68 _LARGE_INTEGER pvalue;
69 GetFileSizeEx(file_handle, &pvalue);
70 content.file_size =
uint32_t(pvalue.QuadPart);
75std::optional<file>
open_file(unopened_file
const& f) {
76 std::optional<file> result(file{f.absolute_path});
77 if(!result->content.data) {
78 result = std::optional<file>{};
83void reset(file_system& fs) {
84 fs.ordered_roots.clear();
85 fs.ignored_paths.clear();
89 fs.ordered_roots.emplace_back(root_path);
93 WCHAR module_name[MAX_PATH] = {};
94 int32_t path_used = GetModuleFileNameW(
nullptr, module_name, MAX_PATH);
95 while(path_used >= 0 && module_name[path_used] != L
'\\') {
96 module_name[path_used] = 0;
103directory
get_root(file_system
const& fs) {
104 return directory(&fs,
NATIVE(
""));
109 for(
auto const& str : fs.ordered_roots) {
110 result +=
NATIVE(
";") + str;
113 for(
auto const& replace_path : fs.ignored_paths) {
114 result += replace_path +
NATIVE(
";");
121 auto break_position = std::find(data.data(), data.data() + data.length(),
NATIVE(
'?'));
124 auto position = data.data() + 1;
125 auto end = break_position;
126 while(position < end) {
127 auto next_semicolon = std::find(position, end,
NATIVE(
';'));
128 fs.ordered_roots.emplace_back(position, next_semicolon);
129 position = next_semicolon + 1;
134 auto position = break_position + 1;
135 auto end = data.data() + data.length();
136 while(position < end) {
137 auto next_semicolon = std::find(position, end,
NATIVE(
';'));
138 fs.ignored_paths.emplace_back(position, next_semicolon);
139 position = next_semicolon + 1;
146 for(
auto c = str; *c != 0; ++c) {
147 if(int32_t(*c) > 127 || int32_t(*c) < 0)
155 std::vector<unopened_file> accumulated_results;
156 if(dir.parent_system) {
157 for(
size_t i = dir.parent_system->ordered_roots.size(); i-- > 0;) {
158 auto const dir_path = dir.parent_system->ordered_roots[i] + dir.relative_path;
159 auto const appended_path = dir_path +
NATIVE(
"\\*") + extension;
165 WIN32_FIND_DATAW find_result;
166 auto find_handle = FindFirstFileExW(appended_path.c_str(), FindExInfoBasic, &find_result, FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH);
167 if(find_handle != INVALID_HANDLE_VALUE) {
169 if(!(find_result.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !
impl::contains_non_ascii(find_result.cFileName)) {
170 if(
auto search_result = std::find_if(accumulated_results.begin(), accumulated_results.end(),
171 [n = find_result.cFileName](
auto const& f) { return f.file_name.compare(n) == 0; });
172 search_result == accumulated_results.end()) {
174 accumulated_results.emplace_back(dir.parent_system->ordered_roots[i] + dir.relative_path +
NATIVE(
"\\") +
175 find_result.cFileName,
176 find_result.cFileName);
179 }
while(FindNextFileW(find_handle, &find_result) != 0);
180 FindClose(find_handle);
184 auto const appended_path = dir.relative_path +
NATIVE(
"\\*") + extension;
185 WIN32_FIND_DATAW find_result;
186 auto find_handle = FindFirstFileExW(appended_path.c_str(), FindExInfoBasic, &find_result, FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH);
187 if(find_handle != INVALID_HANDLE_VALUE) {
189 if(!(find_result.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !
impl::contains_non_ascii(find_result.cFileName)) {
190 accumulated_results.emplace_back(dir.relative_path +
NATIVE(
"\\") + find_result.cFileName, find_result.cFileName);
192 }
while(FindNextFileW(find_handle, &find_result) != 0);
193 FindClose(find_handle);
196 std::sort(accumulated_results.begin(), accumulated_results.end(), [](unopened_file
const& a, unopened_file
const& b) {
197 return std::lexicographical_compare(std::begin(a.file_name), std::end(a.file_name), std::begin(b.file_name),
198 std::end(b.file_name),
199 [](native_char const& char1, native_char const& char2) { return tolower(char1) < tolower(char2); });
201 return accumulated_results;
204 std::vector<directory> accumulated_results;
205 if(dir.parent_system) {
206 for(
size_t i = dir.parent_system->ordered_roots.size(); i-- > 0;) {
207 auto const dir_path = dir.parent_system->ordered_roots[i] + dir.relative_path;
208 auto const appended_path = dir_path +
NATIVE(
"\\*");
212 WIN32_FIND_DATAW find_result;
213 auto find_handle = FindFirstFileExW(appended_path.c_str(), FindExInfoBasic, &find_result, FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH);
214 if(find_handle != INVALID_HANDLE_VALUE) {
216 if((find_result.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !impl::contains_non_ascii(find_result.cFileName)) {
218 if(find_result.cFileName[0] !=
NATIVE(
'.') &&
219 std::find_if(accumulated_results.begin(), accumulated_results.end(),
220 [&rel_name](
auto const& s) { return s.relative_path.compare(rel_name) == 0; }) == accumulated_results.end()) {
221 accumulated_results.emplace_back(dir.parent_system, rel_name);
224 }
while(FindNextFileW(find_handle, &find_result) != 0);
225 FindClose(find_handle);
229 auto const appended_path = dir.relative_path +
NATIVE(
"\\*");
230 WIN32_FIND_DATAW find_result;
231 auto find_handle = FindFirstFileExW(appended_path.c_str(), FindExInfoBasic, &find_result, FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH);
232 if(find_handle != INVALID_HANDLE_VALUE) {
234 if((find_result.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !impl::contains_non_ascii(find_result.cFileName)) {
236 if(find_result.cFileName[0] !=
NATIVE(
'.')) {
237 accumulated_results.emplace_back(
nullptr, rel_name);
240 }
while(FindNextFileW(find_handle, &find_result) != 0);
241 FindClose(find_handle);
244 std::sort(accumulated_results.begin(), accumulated_results.end(), [](directory
const& a, directory
const& b) {
245 return std::lexicographical_compare(std::begin(a.relative_path), std::end(a.relative_path), std::begin(b.relative_path),
246 std::end(b.relative_path),
247 [](native_char const& char1, native_char const& char2) { return tolower(char1) < tolower(char2); });
249 return accumulated_results;
253 return directory(dir.parent_system, dir.relative_path +
NATIVE(
'\\') +
native_string(directory_name));
257 return dir.relative_path;
261 if(dir.parent_system) {
262 for(
size_t i = dir.parent_system->ordered_roots.size(); i-- > 0;) {
263 native_string dir_path = dir.parent_system->ordered_roots[i] + dir.relative_path;
271 return std::optional<native_string>{};
275 if(dir.parent_system) {
276 for(
size_t i = dir.parent_system->ordered_roots.size(); i-- > 0;) {
277 native_string dir_path = dir.parent_system->ordered_roots[i] + dir.relative_path;
282 HANDLE file_handle = CreateFileW(full_path.c_str(), GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING,
283 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
nullptr);
284 if(file_handle != INVALID_HANDLE_VALUE) {
285 return std::optional<file>(file(file_handle, full_path));
290 HANDLE file_handle = CreateFileW(full_path.c_str(), GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING,
291 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
nullptr);
292 if(file_handle != INVALID_HANDLE_VALUE) {
293 return std::optional<file>(file(file_handle, full_path));
296 return std::optional<file>{};
300 if(dir.parent_system) {
301 for(
size_t i = dir.parent_system->ordered_roots.size(); i-- > 0;) {
302 native_string dir_path = dir.parent_system->ordered_roots[i] + dir.relative_path;
307 DWORD dwAttrib = GetFileAttributesW(full_path.c_str());
308 if(dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) {
309 return std::optional<unopened_file>(unopened_file(full_path, file_name));
314 DWORD dwAttrib = GetFileAttributesW(full_path.c_str());
315 if(dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) {
316 return std::optional<unopened_file>(unopened_file(full_path, file_name));
319 return std::optional<unopened_file>{};
326std::vector<native_string>
list_roots(file_system
const& fs) {
327 return fs.ordered_roots;
331 for(
auto const& replace_path : fs.ignored_paths) {
332 if(path.starts_with(replace_path))
339 return f.absolute_path;
347 return f.absolute_path;
351 if(dir.parent_system)
355 HANDLE file_handle = CreateFileW(full_path.c_str(), GENERIC_READ | GENERIC_WRITE, 0,
nullptr, CREATE_ALWAYS,
356 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
nullptr);
357 if(file_handle != INVALID_HANDLE_VALUE) {
358 DWORD written_bytes = 0;
359 WriteFile(file_handle, file_data, DWORD(file_size), &written_bytes,
nullptr);
361 SetEndOfFile(file_handle);
362 CloseHandle(file_handle);
371 wchar_t* local_path_out =
nullptr;
373 if(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0,
nullptr, &local_path_out) == S_OK) {
376 CoTaskMemFree(local_path_out);
377 if(base_path.length() > 0) {
378 CreateDirectoryW(base_path.c_str(),
nullptr);
380 return directory(
nullptr, base_path);
384 wchar_t* local_path_out =
nullptr;
386 if(SHGetKnownFolderPath(FOLDERID_Documents, 0,
nullptr, &local_path_out) == S_OK) {
389 CoTaskMemFree(local_path_out);
390 if(base_path.length() > 0) {
391 CreateDirectoryW(base_path.c_str(),
nullptr);
392 base_path +=
NATIVE(
"\\saved games");
393 CreateDirectoryW(base_path.c_str(),
nullptr);
395 return directory(
nullptr, base_path);
399 wchar_t* local_path_out =
nullptr;
401 if(SHGetKnownFolderPath(FOLDERID_Documents, 0,
nullptr, &local_path_out) == S_OK) {
404 CoTaskMemFree(local_path_out);
405 if(base_path.length() > 0) {
406 CreateDirectoryW(base_path.c_str(),
nullptr);
408 return directory(
nullptr, base_path);
414 if(SHGetKnownFolderPath(FOLDERID_Documents, 0,
nullptr, &local_path_out) == S_OK) {
417 CoTaskMemFree(local_path_out);
418 if(base_path.length() > 0) {
419 CreateDirectoryW(base_path.c_str(),
nullptr);
420 base_path +=
NATIVE(
"\\templates");
421 CreateDirectoryW(base_path.c_str(),
nullptr);
423 return directory(
nullptr, base_path);
429 if(SHGetKnownFolderPath(FOLDERID_Documents, 0,
nullptr, &local_path_out) == S_OK) {
432 CoTaskMemFree(local_path_out);
433 if(base_path.length() > 0) {
434 CreateDirectoryW(base_path.c_str(),
nullptr);
435 base_path +=
NATIVE(
"\\oos");
436 CreateDirectoryW(base_path.c_str(),
nullptr);
438 return directory(
nullptr, base_path);
444 if(SHGetKnownFolderPath(FOLDERID_Documents, 0,
nullptr, &local_path_out) == S_OK) {
447 CoTaskMemFree(local_path_out);
448 if(base_path.length() > 0) {
449 CreateDirectoryW(base_path.c_str(),
nullptr);
450 base_path +=
NATIVE(
"\\scenarios");
451 CreateDirectoryW(base_path.c_str(),
nullptr);
453 return directory(
nullptr, base_path);
459 if(SHGetKnownFolderPath(FOLDERID_Documents, 0,
nullptr, &local_path_out) == S_OK) {
462 CoTaskMemFree(local_path_out);
463 if(base_path.length() > 0) {
464 CreateDirectoryW(base_path.c_str(),
nullptr);
465 base_path +=
NATIVE(
"\\data_dumps");
466 CreateDirectoryW(base_path.c_str(),
nullptr);
468 return directory(
nullptr, base_path);
473 for(
auto ch : data_in) {
481 auto buffer = std::unique_ptr<WCHAR[]>(
new WCHAR[
str.length() * 2]);
482 auto chars_written = MultiByteToWideChar(CP_UTF8, 0,
str.data(), int32_t(
str.length()), buffer.get(), int32_t(
str.length() * 2));
490 auto buffer = std::unique_ptr<char[]>(
new char[
str.length() * 4]);
491 auto chars_written = WideCharToMultiByte(CP_UTF8, 0,
str.data(), int32_t(
str.length()), buffer.get(), int32_t(
str.length() * 4), NULL, NULL);
492 return std::string(buffer.get(),
size_t(chars_written));
494 return std::string(
"");
499 res.reserve(data_in.size());
500 for(
uint32_t i = 0; i < data_in.size(); ++i) {
501 if(data_in[i] ==
'\\') {
503 if(i + 1 < data_in.size() && data_in[i + 1] ==
'\\')
514 res.reserve(path.size());
515 for(
size_t i = 0; i < path.size(); i++) {
516 res += path[i] ==
'/' ?
'\\' : path[i];
void operator=(file const &other)=delete
bool contains_non_ascii(native_char const *str)
std::vector< unopened_file > list_files(directory const &dir, native_char const *extension)
void add_root(file_system &fs, native_string_view root_path)
void reset(file_system &fs)
native_string win1250_to_native(std::string_view data_in)
void add_relative_root(file_system &fs, native_string_view root_path)
std::vector< directory > list_subdirectories(directory const &dir)
directory open_directory(directory const &dir, native_string_view directory_name)
void add_ignore_path(file_system &fs, native_string_view replaced_path)
native_string utf8_to_native(std::string_view data_in)
native_string extract_state(file_system const &fs)
directory get_root(file_system const &fs)
void restore_state(file_system &fs, native_string_view data)
void write_file(directory const &dir, native_string_view file_name, char const *file_data, uint32_t file_size)
directory get_or_create_oos_directory()
std::vector< native_string > list_roots(file_system const &fs)
directory get_or_create_templates_directory()
std::string remove_double_backslashes(std::string_view data_in)
std::optional< native_string > get_path_to_file(directory const &dir, native_string_view file_name)
directory get_or_create_settings_directory()
native_string get_full_name(directory const &f)
native_string correct_slashes(native_string_view path)
directory get_or_create_save_game_directory()
std::optional< file > open_file(directory const &dir, native_string_view file_name)
directory get_or_create_root_documents()
directory get_or_create_data_dumps_directory()
bool is_ignored_path(file_system const &fs, native_string_view path)
std::optional< unopened_file > peek_file(directory const &dir, native_string_view file_name)
std::string native_to_utf8(native_string_view data_in)
file_contents view_contents(file const &f)
native_string get_file_name(unopened_file const &f)
directory get_or_create_scenario_directory()
char16_t win1250toUTF16(char in)
std::string_view native_string_view
std::string native_string