blob: 500ce8316ca6013b7a1420009fc9e60200b56802 (
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
131
132
133
134
135
136
137
138
139
|
#include "impl/file-browser-posix.hh"
#include <string>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
namespace nes::app::mac
{
file_browser_posix::file_browser_posix()
{
auto const initial_path = getenv("HOME");
if (initial_path)
{
for (auto const component : path_view{ initial_path })
{
if (path_.push(component) != status::success) { break; }
}
}
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(entry* out_entry) -> bool
{
if (!it_) { return false; }
while (true)
{
errno = 0;
auto const ent = readdir(it_);
if (!ent)
{
if (errno) { perror("file_browser_posix: readdir"); }
return false;
}
auto const name = std::string_view{ ent->d_name };
if (name.length() > max_name_length) { continue; }
if (name[0] == '.') { continue; }
auto type = entry_type{};
if (ent->d_type == DT_DIR) { type = entry_type::directory; }
else if (ent->d_type == DT_REG) { type = entry_type::file; }
else { continue; }
it_pos_ += 1;
if (out_entry) { *out_entry = entry{ 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
|