Files
Xenia-Canary/src/xenia/vfs/devices/host_path_entry.cc
2020-11-24 00:23:38 -06:00

125 lines
4.5 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/vfs/devices/host_path_entry.h"
#include "xenia/base/filesystem.h"
#include "xenia/base/logging.h"
#include "xenia/base/mapped_memory.h"
#include "xenia/base/math.h"
#include "xenia/base/string.h"
#include "xenia/vfs/device.h"
#include "xenia/vfs/devices/host_path_file.h"
namespace xe {
namespace vfs {
HostPathEntry::HostPathEntry(Device* device, Entry* parent,
const std::string_view path,
const std::filesystem::path& host_path)
: Entry(device, parent, path), host_path_(host_path) {}
HostPathEntry::~HostPathEntry() = default;
HostPathEntry* HostPathEntry::Create(Device* device, Entry* parent,
const std::filesystem::path& full_path,
xe::filesystem::FileInfo file_info) {
auto path = xe::utf8::join_guest_paths(parent->path(),
xe::path_to_utf8(file_info.name));
auto entry = new HostPathEntry(device, parent, path, full_path);
entry->create_timestamp_ = file_info.create_timestamp;
entry->access_timestamp_ = file_info.access_timestamp;
entry->write_timestamp_ = file_info.write_timestamp;
if (file_info.type == xe::filesystem::FileInfo::Type::kDirectory) {
entry->attributes_ = kFileAttributeDirectory;
} else {
entry->attributes_ = kFileAttributeNormal;
if (device->is_read_only()) {
entry->attributes_ |= kFileAttributeReadOnly;
}
entry->size_ = file_info.total_size;
entry->allocation_size_ =
xe::round_up(file_info.total_size, device->bytes_per_sector());
}
return entry;
}
X_STATUS HostPathEntry::Open(uint32_t desired_access, File** out_file) {
if (is_read_only() && (desired_access & (FileAccess::kFileWriteData |
FileAccess::kFileAppendData))) {
XELOGE("Attempting to open file for write access on read-only device");
return X_STATUS_ACCESS_DENIED;
}
auto file_handle =
xe::filesystem::FileHandle::OpenExisting(host_path_, desired_access);
if (!file_handle) {
// TODO(benvanik): pick correct response.
return X_STATUS_NO_SUCH_FILE;
}
*out_file = new HostPathFile(desired_access, this, std::move(file_handle));
return X_STATUS_SUCCESS;
}
std::unique_ptr<MappedMemory> HostPathEntry::OpenMapped(MappedMemory::Mode mode,
size_t offset,
size_t length) {
return MappedMemory::Open(host_path_, mode, offset, length);
}
std::unique_ptr<Entry> HostPathEntry::CreateEntryInternal(
const std::string_view name, uint32_t attributes) {
auto full_path = host_path_ / xe::to_path(name);
if (attributes & kFileAttributeDirectory) {
if (!std::filesystem::create_directories(full_path)) {
return nullptr;
}
} else {
auto file = xe::filesystem::OpenFile(full_path, "wb");
if (!file) {
return nullptr;
}
fclose(file);
}
xe::filesystem::FileInfo file_info;
if (!xe::filesystem::GetInfo(full_path, &file_info)) {
return nullptr;
}
return std::unique_ptr<Entry>(
HostPathEntry::Create(device_, this, full_path, file_info));
}
bool HostPathEntry::DeleteEntryInternal(Entry* entry) {
auto full_path = host_path_ / xe::to_path(entry->name());
std::error_code ec; // avoid exception on remove/remove_all failure
if (entry->attributes() & kFileAttributeDirectory) {
// Delete entire directory and contents.
return std::filesystem::remove_all(full_path, ec);
} else {
// Delete file.
return !std::filesystem::is_directory(full_path) &&
std::filesystem::remove(full_path, ec);
}
}
void HostPathEntry::update() {
xe::filesystem::FileInfo file_info;
if (!xe::filesystem::GetInfo(host_path_, &file_info)) {
return;
}
if (file_info.type == xe::filesystem::FileInfo::Type::kFile) {
size_ = file_info.total_size;
allocation_size_ =
xe::round_up(file_info.total_size, device()->bytes_per_sector());
}
}
} // namespace vfs
} // namespace xe