2021-12-07 22:47:41 +01:00
|
|
|
#include "content/providers/file_provider.hpp"
|
2023-08-09 20:04:12 +02:00
|
|
|
#include "content/providers/memory_file_provider.hpp"
|
2020-11-11 09:18:35 +01:00
|
|
|
|
2023-11-30 11:23:12 +01:00
|
|
|
#include <hex/api/imhex_api.hpp>
|
2023-11-21 14:38:01 +01:00
|
|
|
#include <hex/api/localization_manager.hpp>
|
2023-01-11 23:31:25 +01:00
|
|
|
#include <hex/api/project_file_manager.hpp>
|
2023-11-18 14:50:43 +01:00
|
|
|
#include <hex/api/task_manager.hpp>
|
2023-08-09 20:04:12 +02:00
|
|
|
|
2024-02-27 20:20:14 +01:00
|
|
|
#include <toasts/toast_notification.hpp>
|
|
|
|
|
2021-08-29 22:15:18 +02:00
|
|
|
#include <hex/helpers/utils.hpp>
|
2022-02-01 18:09:40 +01:00
|
|
|
#include <hex/helpers/fmt.hpp>
|
2023-11-30 14:40:07 +01:00
|
|
|
#include <fmt/chrono.h>
|
2022-08-08 21:23:52 +02:00
|
|
|
|
2023-03-12 18:43:05 +01:00
|
|
|
#include <wolv/utils/string.hpp>
|
|
|
|
|
2022-08-08 21:23:52 +02:00
|
|
|
#include <nlohmann/json.hpp>
|
2024-02-27 20:20:14 +01:00
|
|
|
#include <cstring>
|
2020-11-15 16:06:10 +01:00
|
|
|
|
2023-08-16 23:18:16 +02:00
|
|
|
#if defined(OS_WINDOWS)
|
|
|
|
#include <windows.h>
|
|
|
|
#endif
|
|
|
|
|
2022-11-07 00:04:47 +01:00
|
|
|
namespace hex::plugin::builtin {
|
2021-12-07 22:47:41 +01:00
|
|
|
|
2024-02-24 12:06:06 +01:00
|
|
|
std::set<FileProvider*> FileProvider::s_openedFiles;
|
|
|
|
|
2021-09-21 02:29:54 +02:00
|
|
|
bool FileProvider::isAvailable() const {
|
2023-03-17 11:31:50 +01:00
|
|
|
return true;
|
2020-11-11 09:18:35 +01:00
|
|
|
}
|
|
|
|
|
2021-09-21 02:29:54 +02:00
|
|
|
bool FileProvider::isReadable() const {
|
2023-12-19 13:10:25 +01:00
|
|
|
return isAvailable() && m_readable;
|
2020-11-11 09:18:35 +01:00
|
|
|
}
|
|
|
|
|
2021-09-21 02:29:54 +02:00
|
|
|
bool FileProvider::isWritable() const {
|
2023-12-19 13:10:25 +01:00
|
|
|
return isAvailable() && m_writable;
|
2020-11-11 09:18:35 +01:00
|
|
|
}
|
|
|
|
|
2021-09-21 02:29:54 +02:00
|
|
|
bool FileProvider::isResizable() const {
|
2022-09-03 23:08:40 +02:00
|
|
|
return isAvailable() && isWritable();
|
2021-07-27 21:07:36 +02:00
|
|
|
}
|
|
|
|
|
2021-09-21 02:29:54 +02:00
|
|
|
bool FileProvider::isSavable() const {
|
2023-12-19 13:10:25 +01:00
|
|
|
return m_undoRedoStack.canUndo();
|
2020-11-27 09:09:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void FileProvider::readRaw(u64 offset, void *buffer, size_t size) {
|
2023-12-26 23:42:22 +01:00
|
|
|
if (m_fileSize == 0 || (offset + size) > m_fileSize || buffer == nullptr || size == 0)
|
2020-11-27 09:09:48 +01:00
|
|
|
return;
|
|
|
|
|
2024-01-06 16:09:28 +01:00
|
|
|
m_file.readBufferAtomic(offset, static_cast<u8*>(buffer), size);
|
2020-11-11 09:18:35 +01:00
|
|
|
}
|
|
|
|
|
2020-11-27 13:44:52 +01:00
|
|
|
void FileProvider::writeRaw(u64 offset, const void *buffer, size_t size) {
|
2021-12-09 21:10:24 +01:00
|
|
|
if ((offset + size) > this->getActualSize() || buffer == nullptr || size == 0)
|
2020-11-27 09:09:48 +01:00
|
|
|
return;
|
|
|
|
|
2024-01-06 16:09:28 +01:00
|
|
|
m_file.writeBufferAtomic(offset, static_cast<const u8*>(buffer), size);
|
2020-11-27 09:09:48 +01:00
|
|
|
}
|
2021-07-27 21:07:36 +02:00
|
|
|
|
2021-08-21 13:53:50 +02:00
|
|
|
void FileProvider::save() {
|
2024-02-15 21:54:41 +01:00
|
|
|
m_file.flush();
|
|
|
|
|
2023-08-16 23:18:16 +02:00
|
|
|
#if defined(OS_WINDOWS)
|
|
|
|
FILETIME ft;
|
|
|
|
SYSTEMTIME st;
|
|
|
|
|
2024-02-15 21:54:41 +01:00
|
|
|
if (m_file.isValid()) {
|
2023-08-16 23:18:16 +02:00
|
|
|
GetSystemTime(&st);
|
|
|
|
if (SystemTimeToFileTime(&st, &ft)) {
|
2024-02-15 21:54:41 +01:00
|
|
|
auto fileHandle = HANDLE(_get_osfhandle(_fileno(m_file.getHandle())));
|
2023-11-10 20:47:08 +01:00
|
|
|
SetFileTime(fileHandle, nullptr, nullptr, &ft);
|
2023-08-16 23:18:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2023-04-06 17:36:28 +02:00
|
|
|
Provider::save();
|
2021-08-21 13:53:50 +02:00
|
|
|
}
|
|
|
|
|
2023-03-21 15:42:10 +01:00
|
|
|
void FileProvider::saveAs(const std::fs::path &path) {
|
2023-12-19 13:10:25 +01:00
|
|
|
if (path == m_path)
|
2023-03-21 15:42:10 +01:00
|
|
|
this->save();
|
|
|
|
else
|
|
|
|
Provider::saveAs(path);
|
|
|
|
}
|
|
|
|
|
2023-12-08 16:22:36 +01:00
|
|
|
void FileProvider::resizeRaw(u64 newSize) {
|
2024-02-19 22:22:59 +01:00
|
|
|
m_file.setSize(newSize);
|
2024-02-28 22:21:14 +01:00
|
|
|
m_fileSize = newSize;
|
2021-07-27 21:07:36 +02:00
|
|
|
}
|
|
|
|
|
2023-12-08 16:22:36 +01:00
|
|
|
void FileProvider::insertRaw(u64 offset, u64 size) {
|
2022-01-20 23:24:26 +01:00
|
|
|
auto oldSize = this->getActualSize();
|
2023-11-25 12:43:48 +01:00
|
|
|
this->resizeRaw(oldSize + size);
|
2022-01-20 23:24:26 +01:00
|
|
|
|
|
|
|
std::vector<u8> buffer(0x1000);
|
|
|
|
const std::vector<u8> zeroBuffer(0x1000);
|
|
|
|
|
|
|
|
auto position = oldSize;
|
|
|
|
while (position > offset) {
|
2022-06-17 14:21:56 +02:00
|
|
|
const auto readSize = std::min<size_t>(position - offset, buffer.size());
|
2022-01-20 23:24:26 +01:00
|
|
|
|
|
|
|
position -= readSize;
|
|
|
|
|
|
|
|
this->readRaw(position, buffer.data(), readSize);
|
|
|
|
this->writeRaw(position, zeroBuffer.data(), readSize);
|
|
|
|
this->writeRaw(position + size, buffer.data(), readSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-08 16:22:36 +01:00
|
|
|
void FileProvider::removeRaw(u64 offset, u64 size) {
|
2023-07-06 21:14:50 +02:00
|
|
|
if (offset > this->getActualSize() || size == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ((offset + size) > this->getActualSize())
|
|
|
|
size = this->getActualSize() - offset;
|
|
|
|
|
2022-06-17 14:21:56 +02:00
|
|
|
auto oldSize = this->getActualSize();
|
|
|
|
|
|
|
|
std::vector<u8> buffer(0x1000);
|
|
|
|
|
|
|
|
const auto newSize = oldSize - size;
|
|
|
|
auto position = offset;
|
|
|
|
while (position < newSize) {
|
|
|
|
const auto readSize = std::min<size_t>(newSize - position, buffer.size());
|
|
|
|
|
|
|
|
this->readRaw(position + size, buffer.data(), readSize);
|
|
|
|
this->writeRaw(position, buffer.data(), readSize);
|
|
|
|
|
|
|
|
position += readSize;
|
|
|
|
}
|
|
|
|
|
2023-11-25 12:43:48 +01:00
|
|
|
this->resizeRaw(newSize);
|
2022-06-17 14:21:56 +02:00
|
|
|
}
|
|
|
|
|
2023-12-07 12:06:26 +01:00
|
|
|
u64 FileProvider::getActualSize() const {
|
2023-12-19 13:10:25 +01:00
|
|
|
return m_fileSize;
|
2020-11-11 09:18:35 +01:00
|
|
|
}
|
|
|
|
|
2021-09-21 02:29:54 +02:00
|
|
|
std::string FileProvider::getName() const {
|
2023-12-19 13:10:25 +01:00
|
|
|
return wolv::util::toUTF8String(m_path.filename());
|
2021-09-21 02:29:54 +02:00
|
|
|
}
|
|
|
|
|
2023-06-04 10:42:11 +02:00
|
|
|
std::vector<FileProvider::Description> FileProvider::getDataDescription() const {
|
|
|
|
std::vector<Description> result;
|
2020-11-15 16:06:10 +01:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
result.emplace_back("hex.builtin.provider.file.path"_lang, wolv::util::toUTF8String(m_path));
|
2021-02-22 13:07:25 +01:00
|
|
|
result.emplace_back("hex.builtin.provider.file.size"_lang, hex::toByteString(this->getActualSize()));
|
2020-11-15 16:06:10 +01:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
if (m_fileStats.has_value()) {
|
2023-08-02 12:52:10 +02:00
|
|
|
std::string creationTime, accessTime, modificationTime;
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
try { creationTime = hex::format("{:%Y-%m-%d %H:%M:%S}", fmt::localtime(m_fileStats->st_ctime)); }
|
2023-08-02 12:52:10 +02:00
|
|
|
catch (const std::exception&) { creationTime = "???"; }
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
try { accessTime = hex::format("{:%Y-%m-%d %H:%M:%S}", fmt::localtime(m_fileStats->st_atime)); }
|
2023-08-02 12:52:10 +02:00
|
|
|
catch (const std::exception&) { accessTime = "???"; }
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
try { modificationTime = hex::format("{:%Y-%m-%d %H:%M:%S}", fmt::localtime(m_fileStats->st_mtime)); }
|
2023-08-02 12:52:10 +02:00
|
|
|
catch (const std::exception&) { modificationTime = "???"; }
|
|
|
|
|
|
|
|
result.emplace_back("hex.builtin.provider.file.creation"_lang, creationTime);
|
|
|
|
result.emplace_back("hex.builtin.provider.file.access"_lang, accessTime);
|
|
|
|
result.emplace_back("hex.builtin.provider.file.modification"_lang, modificationTime);
|
2020-11-15 16:06:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-01-24 09:07:11 +01:00
|
|
|
std::variant<std::string, i128> FileProvider::queryInformation(const std::string &category, const std::string &argument) {
|
|
|
|
if (category == "file_path")
|
2023-12-19 13:10:25 +01:00
|
|
|
return wolv::util::toUTF8String(m_path);
|
2023-01-24 09:07:11 +01:00
|
|
|
else if (category == "file_name")
|
2023-12-19 13:10:25 +01:00
|
|
|
return wolv::util::toUTF8String(m_path.filename());
|
2023-01-24 09:07:11 +01:00
|
|
|
else if (category == "file_extension")
|
2023-12-19 13:10:25 +01:00
|
|
|
return wolv::util::toUTF8String(m_path.extension());
|
2023-01-24 09:07:11 +01:00
|
|
|
else if (category == "creation_time")
|
2023-12-19 13:10:25 +01:00
|
|
|
return m_fileStats->st_ctime;
|
2023-01-24 09:07:11 +01:00
|
|
|
else if (category == "access_time")
|
2023-12-19 13:10:25 +01:00
|
|
|
return m_fileStats->st_atime;
|
2023-01-24 09:07:11 +01:00
|
|
|
else if (category == "modification_time")
|
2023-12-19 13:10:25 +01:00
|
|
|
return m_fileStats->st_mtime;
|
2023-01-24 09:07:11 +01:00
|
|
|
else if (category == "permissions")
|
2023-12-19 13:10:25 +01:00
|
|
|
return m_fileStats->st_mode & 0777;
|
2023-01-24 09:07:11 +01:00
|
|
|
else
|
|
|
|
return Provider::queryInformation(category, argument);
|
|
|
|
}
|
|
|
|
|
2022-08-12 15:11:27 +02:00
|
|
|
bool FileProvider::handleFilePicker() {
|
|
|
|
return fs::openFileBrowser(fs::DialogMode::Open, {}, [this](const auto &path) {
|
|
|
|
this->setPath(path);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-06-04 10:42:11 +02:00
|
|
|
std::vector<FileProvider::MenuEntry> FileProvider::getMenuEntries(){
|
2023-05-21 13:21:53 +02:00
|
|
|
return {
|
2023-12-19 13:10:25 +01:00
|
|
|
{ "hex.builtin.provider.file.menu.open_folder"_lang, [this] { fs::openFolderWithSelectionExternal(m_path); } },
|
|
|
|
{ "hex.builtin.provider.file.menu.open_file"_lang, [this] { fs::openFileExternal(m_path); } },
|
2023-11-27 15:48:46 +01:00
|
|
|
{ "hex.builtin.provider.file.menu.into_memory"_lang, [this] { this->convertToMemoryFile(); } }
|
2023-05-21 13:21:53 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-03-04 11:36:37 +01:00
|
|
|
void FileProvider::setPath(const std::fs::path &path) {
|
2023-12-19 13:10:25 +01:00
|
|
|
m_path = path;
|
2021-12-12 00:41:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool FileProvider::open() {
|
2023-12-19 13:10:25 +01:00
|
|
|
m_readable = true;
|
|
|
|
m_writable = true;
|
2021-07-27 21:07:36 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
if (!std::fs::exists(m_path)) {
|
|
|
|
this->setErrorMessage(hex::format("hex.builtin.provider.file.error.open"_lang, m_path.string(), ::strerror(ENOENT)));
|
2023-06-21 20:07:36 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
wolv::io::File file(m_path, wolv::io::File::Mode::Write);
|
2023-02-17 10:26:09 +01:00
|
|
|
if (!file.isValid()) {
|
2023-12-19 13:10:25 +01:00
|
|
|
m_writable = false;
|
2023-05-19 20:01:42 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
file = wolv::io::File(m_path, wolv::io::File::Mode::Read);
|
2023-05-19 20:01:42 +02:00
|
|
|
if (!file.isValid()) {
|
2023-12-19 13:10:25 +01:00
|
|
|
m_readable = false;
|
|
|
|
this->setErrorMessage(hex::format("hex.builtin.provider.file.error.open"_lang, m_path.string(), ::strerror(errno)));
|
2023-05-19 20:01:42 +02:00
|
|
|
return false;
|
|
|
|
}
|
2024-02-27 20:20:14 +01:00
|
|
|
|
|
|
|
ui::ToastInfo::open("hex.builtin.popup.error.read_only"_lang);
|
2023-02-17 10:26:09 +01:00
|
|
|
}
|
2022-08-08 21:23:52 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
m_file = std::move(file);
|
2024-01-06 16:09:28 +01:00
|
|
|
m_fileStats = m_file.getFileInfo();
|
|
|
|
m_fileSize = m_file.getSize();
|
2023-05-20 18:07:15 +02:00
|
|
|
|
2024-02-24 12:06:06 +01:00
|
|
|
// Make sure the current file is not already opened
|
|
|
|
{
|
|
|
|
auto alreadyOpenedFileProvider = std::ranges::find_if(s_openedFiles, [this](const FileProvider *provider) {
|
|
|
|
return provider->m_path == m_path;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (alreadyOpenedFileProvider != s_openedFiles.end()) {
|
|
|
|
ImHexApi::Provider::setCurrentProvider(*alreadyOpenedFileProvider);
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
s_openedFiles.insert(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-04 11:16:20 +02:00
|
|
|
return true;
|
2022-08-08 21:23:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void FileProvider::close() {
|
2024-01-06 16:09:28 +01:00
|
|
|
m_file.close();
|
2024-02-24 12:06:06 +01:00
|
|
|
s_openedFiles.erase(this);
|
2022-08-08 21:23:52 +02:00
|
|
|
}
|
2021-12-12 00:41:44 +01:00
|
|
|
|
2022-08-08 21:23:52 +02:00
|
|
|
void FileProvider::loadSettings(const nlohmann::json &settings) {
|
|
|
|
Provider::loadSettings(settings);
|
2022-04-17 16:57:30 +02:00
|
|
|
|
2023-05-16 11:33:00 +02:00
|
|
|
auto pathString = settings.at("path").get<std::string>();
|
2023-01-11 23:31:25 +01:00
|
|
|
std::fs::path path = std::u8string(pathString.begin(), pathString.end());
|
|
|
|
|
2023-07-19 22:35:55 +02:00
|
|
|
if (auto projectPath = ProjectFile::getPath(); !projectPath.empty()) {
|
|
|
|
try {
|
|
|
|
this->setPath(std::fs::weakly_canonical(projectPath.parent_path() / path));
|
|
|
|
} catch (const std::fs::filesystem_error &) {
|
|
|
|
try {
|
|
|
|
this->setPath(projectPath.parent_path() / path);
|
|
|
|
} catch (const std::fs::filesystem_error &e) {
|
2023-12-19 13:10:25 +01:00
|
|
|
this->setErrorMessage(hex::format("hex.builtin.provider.file.error.open"_lang, m_path.string(), e.what()));
|
2023-07-19 22:35:55 +02:00
|
|
|
}
|
|
|
|
}
|
2023-12-27 16:33:49 +01:00
|
|
|
} else {
|
2023-01-11 23:31:25 +01:00
|
|
|
this->setPath(path);
|
2023-12-27 16:33:49 +01:00
|
|
|
}
|
2021-07-27 21:07:36 +02:00
|
|
|
}
|
|
|
|
|
2022-08-08 21:23:52 +02:00
|
|
|
nlohmann::json FileProvider::storeSettings(nlohmann::json settings) const {
|
2023-01-19 11:09:24 +01:00
|
|
|
std::string path;
|
2023-01-11 23:31:25 +01:00
|
|
|
if (auto projectPath = ProjectFile::getPath(); !projectPath.empty())
|
2023-12-19 13:10:25 +01:00
|
|
|
path = wolv::util::toUTF8String(std::fs::proximate(m_path, projectPath.parent_path()));
|
2023-01-19 11:09:24 +01:00
|
|
|
if (path.empty())
|
2023-12-19 13:10:25 +01:00
|
|
|
path = wolv::util::toUTF8String(m_path);
|
2023-01-19 11:09:24 +01:00
|
|
|
|
|
|
|
settings["path"] = path;
|
2022-08-08 21:23:52 +02:00
|
|
|
|
|
|
|
return Provider::storeSettings(settings);
|
2021-07-27 21:07:36 +02:00
|
|
|
}
|
|
|
|
|
2022-08-14 09:38:38 +02:00
|
|
|
std::pair<Region, bool> FileProvider::getRegionValidity(u64 address) const {
|
2022-08-17 16:28:44 +02:00
|
|
|
address -= this->getBaseAddress();
|
|
|
|
|
2022-08-14 09:38:38 +02:00
|
|
|
if (address < this->getActualSize())
|
2022-08-17 16:28:44 +02:00
|
|
|
return { Region { this->getBaseAddress() + address, this->getActualSize() - address }, true };
|
2022-08-14 09:38:38 +02:00
|
|
|
else
|
|
|
|
return { Region::Invalid(), false };
|
|
|
|
}
|
|
|
|
|
2023-08-09 20:04:12 +02:00
|
|
|
void FileProvider::convertToMemoryFile() {
|
|
|
|
auto newProvider = hex::ImHexApi::Provider::createProvider("hex.builtin.provider.mem_file", true);
|
|
|
|
|
|
|
|
if (auto memoryProvider = dynamic_cast<MemoryFileProvider*>(newProvider); memoryProvider != nullptr) {
|
2023-12-27 16:33:49 +01:00
|
|
|
if (!memoryProvider->open()) {
|
2023-08-09 20:04:12 +02:00
|
|
|
ImHexApi::Provider::remove(newProvider);
|
2023-12-27 16:33:49 +01:00
|
|
|
} else {
|
2023-08-09 20:04:12 +02:00
|
|
|
const auto size = this->getActualSize();
|
|
|
|
TaskManager::createTask("Loading into memory", size, [this, size, memoryProvider](Task &task) {
|
|
|
|
task.setInterruptCallback([memoryProvider]{
|
|
|
|
ImHexApi::Provider::remove(memoryProvider);
|
|
|
|
});
|
|
|
|
|
|
|
|
constexpr static size_t BufferSize = 0x10000;
|
|
|
|
std::vector<u8> buffer(BufferSize);
|
|
|
|
|
|
|
|
memoryProvider->resize(size);
|
|
|
|
|
|
|
|
for (u64 i = 0; i < size; i += BufferSize) {
|
|
|
|
auto copySize = std::min<size_t>(BufferSize, size - i);
|
|
|
|
this->read(i, buffer.data(), copySize, true);
|
|
|
|
memoryProvider->writeRaw(i, buffer.data(), copySize);
|
|
|
|
task.update(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
memoryProvider->markDirty(true);
|
2023-11-27 15:48:46 +01:00
|
|
|
memoryProvider->getUndoStack().reset();
|
|
|
|
|
|
|
|
TaskManager::runWhenTasksFinished([this]{
|
|
|
|
ImHexApi::Provider::remove(this, false);
|
|
|
|
});
|
2023-08-09 20:04:12 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-22 13:36:26 +01:00
|
|
|
}
|