194 lines
5.2 KiB
C++
194 lines
5.2 KiB
C++
export module oar.files;
|
|
|
|
import coral;
|
|
import coral.files;
|
|
|
|
import oar;
|
|
|
|
using coral::closure;
|
|
using coral::expected;
|
|
using coral::file_reader;
|
|
using coral::file_system;
|
|
using coral::file_walker;
|
|
using coral::io_error;
|
|
using coral::slice;
|
|
using coral::u64;
|
|
|
|
enum class [[nodiscard]] find_result {
|
|
ok,
|
|
not_found,
|
|
io_unavailable,
|
|
archive_invalid,
|
|
};
|
|
|
|
struct archive_reader : public file_reader {
|
|
archive_reader(file_reader & archive_file_reader) : archive_file_reader{archive_file_reader} {}
|
|
|
|
find_result find(oar::entry_path const & path) {
|
|
this->data_offset = 0;
|
|
this->data_length = 0;
|
|
this->data_cursor = 0;
|
|
|
|
if (!this->archive_file_reader.seek(0).is_ok()) return find_result::io_unavailable;
|
|
|
|
oar::header_block archive_header_block;
|
|
|
|
if (!archive_header_block.read(this->archive_file_reader).ok().or_value(false))
|
|
return find_result::archive_invalid;
|
|
|
|
// Read file table.
|
|
u64 head {0};
|
|
u64 tail {archive_header_block.layout.entry_count - 1};
|
|
oar::entry_block archive_entry_block {};
|
|
|
|
while (head <= tail) {
|
|
u64 const midpoint {head + ((tail - head) / 2)};
|
|
|
|
if (!archive_entry_block.read(this->archive_file_reader).ok().or_value(false))
|
|
return find_result::archive_invalid;
|
|
|
|
coral::size const comparison {path.compare(archive_entry_block.layout.path)};
|
|
|
|
if (comparison == 0) {
|
|
this->data_offset = archive_entry_block.layout.data_offset;
|
|
this->data_length = archive_entry_block.layout.data_length;
|
|
this->data_cursor = archive_entry_block.layout.data_offset;
|
|
|
|
return find_result::ok;
|
|
}
|
|
|
|
if (comparison > 0) {
|
|
head = (midpoint + 1);
|
|
} else {
|
|
tail = (midpoint - 1);
|
|
}
|
|
}
|
|
|
|
return find_result::not_found;
|
|
}
|
|
|
|
/**
|
|
* Attempts to read `data.length` bytes from the file and fill `data` with it, returning the
|
|
* number of bytes actually read or a [io_error] value to indicate an error occured.
|
|
*/
|
|
expected<coral::usize, io_error> read(slice<coral::u8> const & data) override {
|
|
if (this->data_offset < sizeof(oar::header_block)) return io_error::unavailable;
|
|
|
|
coral::usize const data_tail {this->data_offset + this->data_length};
|
|
|
|
if (!this->archive_file_reader.seek(coral::clamp(this->data_offset + this->data_cursor,
|
|
this->data_offset, data_tail)).is_ok()) return io_error::unavailable;
|
|
|
|
expected const data_read {this->archive_file_reader.read(
|
|
data.sliced(0, coral::min(data.length, data_tail - this->data_cursor)))};
|
|
|
|
if (data_read.is_ok()) this->data_cursor += *data_read.ok();
|
|
|
|
return data_read;
|
|
}
|
|
|
|
/**
|
|
* Attempts to seek to `offset` absolute position in the file, returning the new absolute
|
|
* cursor or a [io_error] value to indicate an error occured.
|
|
*/
|
|
expected<u64, io_error> seek(u64 offset) override {
|
|
if (this->data_offset < sizeof(oar::header_block)) return io_error::unavailable;
|
|
|
|
this->data_cursor = offset;
|
|
|
|
return io_error::unavailable;
|
|
}
|
|
|
|
/**
|
|
* Attempts to read to read the absolute file cursor position, returning it or a [io_error]
|
|
* value to indicate an error occured.
|
|
*/
|
|
expected<u64, io_error> tell() override {
|
|
if (this->data_offset < sizeof(oar::header_block)) return io_error::unavailable;
|
|
|
|
return this->data_cursor;
|
|
}
|
|
|
|
private:
|
|
file_reader & archive_file_reader;
|
|
|
|
u64 data_offset {0};
|
|
|
|
u64 data_length {0};
|
|
|
|
u64 data_cursor {0};
|
|
};
|
|
|
|
struct archive_walker : public file_walker {
|
|
archive_walker(file_reader & archive_file_reader) : archive_file_reader{archive_file_reader} {}
|
|
|
|
bool enumerate() override {
|
|
// TODO: implement.
|
|
return false;
|
|
}
|
|
|
|
find_result find(oar::entry_path const & path) {
|
|
// TODO: implement.
|
|
return find_result::not_found;
|
|
}
|
|
|
|
expected<slice<char const>, io_error> value() override {
|
|
// TODO: implement.
|
|
return io_error::unavailable;
|
|
}
|
|
|
|
private:
|
|
file_reader & archive_file_reader;
|
|
|
|
u64 files_enumerated {0};
|
|
};
|
|
|
|
export namespace oar {
|
|
struct archive_file : public file_system {
|
|
static void read(file_system & base_file_system, slice<char const> const & archive_path, closure<void(archive_file &)> const & then) {
|
|
if (base_file_system.query_file(archive_path).is_error()) return;
|
|
|
|
archive_file file {base_file_system, archive_path};
|
|
|
|
then(file);
|
|
}
|
|
|
|
void read_file(slice<char const> const & path, closure<void(file_reader &)> const & then) override {
|
|
this->base_file_system.read_file(this->archive_path, [&](file_reader & archive_file_reader) {
|
|
expected const parsed_entry_path {entry_path::parse(path)};
|
|
|
|
if (parsed_entry_path.is_error()) return;
|
|
|
|
archive_reader reader {archive_file_reader};
|
|
|
|
if (reader.find(*parsed_entry_path.ok()) != find_result::ok) return;
|
|
|
|
then(reader);
|
|
});
|
|
}
|
|
|
|
void walk_files(slice<char const> const & path, closure<void(file_walker &)> const & then) override {
|
|
this->base_file_system.read_file(this->archive_path, [&](file_reader & archive_file_reader) {
|
|
expected const parsed_entry_path {entry_path::parse(path)};
|
|
|
|
if (parsed_entry_path.is_error()) return;
|
|
|
|
archive_walker walker {archive_file_reader};
|
|
|
|
if (walker.find(*parsed_entry_path.ok()) != find_result::ok) return;
|
|
|
|
then(walker);
|
|
});
|
|
}
|
|
|
|
private:
|
|
file_system & base_file_system;
|
|
|
|
slice<char const> archive_path;
|
|
|
|
archive_file(file_system & base_file_system, slice<char const> const & archive_path) : base_file_system{base_file_system} {
|
|
this->archive_path = archive_path;
|
|
}
|
|
};
|
|
}
|