summaryrefslogtreecommitdiff
path: root/app/mac/impl/file-browser-posix.cc
blob: 4c717fd0de620643717434ac798a512488b3353e (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
#include "impl/file-browser-posix.hh"
#include <string>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>

namespace nes::app::mac
{
	file_browser_posix::file_browser_posix()
	{
		reopen_directory(path_.get_path());
	}

	file_browser_posix::~file_browser_posix()
	{
		if (it_) { closedir(it_); }
	}

	auto file_browser_posix::seek(u32 const pos) -> void
	{
		if (it_pos_ > pos)
		{
			reopen_directory(path_.get_path());
		}

		while (it_pos_ < pos)
		{
			if (!read_next(nullptr)) { break; }
		}
	}

	auto file_browser_posix::read_next(item* out_item) -> bool
	{
		if (!it_) { return false; }

		while (true)
		{
			errno = 0;
			auto const entry = readdir(it_);
			if (!entry)
			{
				if (errno) { perror("file_browser_posix: readdir"); }
				return false;
			}

			auto const name = std::string_view{ entry->d_name };
			if (name.length() > max_name_length) { continue; }
			if (name[0] == '.') { continue; }

			auto type = item_type::directory;
			if (entry->d_type == DT_DIR) { type = item_type::directory; }
			else if (entry->d_type == DT_REG) { type = item_type::file; }
			else { continue; }

			it_pos_ += 1;
			if (out_item) { *out_item = item{ type, name }; }
			return true;
		}
	}

	auto file_browser_posix::navigate_up() -> status
	{
		auto new_path = path_;
		if (auto const s = new_path.pop(); s != status::success) { return s; }
		if (auto const s = reopen_directory(new_path.get_path()); s != status::success) { return s; }
		path_ = new_path;
		return status::success;
	}

	auto file_browser_posix::navigate(std::string_view const item) -> status
	{
		auto new_path = path_;
		if (auto const s = new_path.push(item); s != status::success) { return s; }
		if (auto const s = reopen_directory(new_path.get_path()); s != status::success) { return s; }
		path_ = new_path;
		return status::success;
	}

	auto file_browser_posix::load(std::string_view const item, span<u8> const buffer, u32* out_length) -> status
	{
		auto file_path = path_;
		if (auto const s = file_path.push(item); s != status::success) { return s; }

		auto const file_path_str = std::string{ file_path.get_path() };
		auto const fd = open(file_path_str.c_str(), O_RDONLY);
		if (fd == -1)
		{
			perror("file_browser_posix: open");
			return status::error_system_error;
		}

		auto const res = read(fd, buffer.get_data(), buffer.get_length());
		if (res < 0)
		{
			perror("file_browser_posix: read");
			close(fd);
			return status::error_system_error;
		}
		if (out_length) { *out_length = static_cast<u32>(res); }

		auto remaining = char{};
		if (read(fd, &remaining, 1) != 0)
		{
			// File was too long.
			close(fd);
			return status::error_invalid_ines_data;
		}

		close(fd);

		return status::success;
	}

	auto file_browser_posix::reopen_directory(std::string_view const path) -> status
	{
		auto const path_str = std::string{ path };
		auto const new_it = opendir(path_str.c_str());
		if (!new_it)
		{
			perror("file_browser_posix: opendir");
			return status::error_system_error;
		}

		if (it_) { closedir(it_); }
		it_ = new_it;
		it_pos_ = 0;
		return status::success;
	}
} // namespace nes::app::mac