Skip to content

Commit

Permalink
feat: display error preview if reading a file fails
Browse files Browse the repository at this point in the history
  • Loading branch information
craftablescience committed Sep 19, 2023
1 parent b05fa5b commit 154892a
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 76 deletions.
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.25 FATAL_ERROR)
project(vpkedit
DESCRIPTION "A tool to read, preview, and write to VPK files."
VERSION 3.1.2
VERSION 3.2.0
HOMEPAGE_URL "https://github.com/craftablescience/VPKEdit")
set(PROJECT_NAME_PRETTY "VPKEdit" CACHE STRING "" FORCE)
set(CMAKE_CXX_STANDARD 17)
Expand Down Expand Up @@ -110,6 +110,8 @@ if(VPKEDIT_BUILD_GUI)
"${CMAKE_CURRENT_SOURCE_DIR}/src/gui/popups/NewVPKDialog.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/gui/previews/DirPreview.h"
"${CMAKE_CURRENT_SOURCE_DIR}/src/gui/previews/DirPreview.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/gui/previews/ErrorPreview.h"
"${CMAKE_CURRENT_SOURCE_DIR}/src/gui/previews/ErrorPreview.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/gui/previews/ImagePreview.h"
"${CMAKE_CURRENT_SOURCE_DIR}/src/gui/previews/ImagePreview.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/gui/previews/TextPreview.h"
Expand Down
1 change: 1 addition & 0 deletions src/gui/EntryTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ void EntryTree::removeEntry(QTreeWidgetItem* item) {
}
}

// NOLINTNEXTLINE(*-no-recursion)
void EntryTree::removeEntryRecurse(QTreeWidgetItem* item) {
if (item->childCount() == 0) {
this->window->removeFile(this->getItemPath(item));
Expand Down
83 changes: 32 additions & 51 deletions src/gui/FileViewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <QMessageBox>

#include "previews/DirPreview.h"
#include "previews/ErrorPreview.h"
#include "previews/ImagePreview.h"
#include "previews/TextPreview.h"
#include "previews/VTFPreview.h"
Expand All @@ -19,20 +20,22 @@ FileViewer::FileViewer(Window* window_, QWidget* parent)
auto* layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);

this->dirPreview = new DirPreview(this);
layout->addWidget(this->dirPreview);
auto* dirPreview = newPreview<DirPreview>(this, this);
layout->addWidget(dirPreview);

this->imagePreview = new ImagePreview(this);
layout->addWidget(this->imagePreview);
auto* errorPreview = newPreview<ErrorPreview>(this);
layout->addWidget(errorPreview);

this->textPreview = new TextPreview(this);
layout->addWidget(this->textPreview);
auto* imagePreview = newPreview<ImagePreview>(this);
layout->addWidget(imagePreview);

this->vtfPreview = new VTFPreview(this);
layout->addWidget(this->vtfPreview);
auto* textPreview = newPreview<TextPreview>(this);
layout->addWidget(textPreview);

auto* vtfPreview = newPreview<VTFPreview>(this);
layout->addWidget(vtfPreview);

this->clearContents();
this->setTextPreviewVisible();
}

void FileViewer::displayEntry(const QString& path) {
Expand All @@ -41,67 +44,45 @@ void FileViewer::displayEntry(const QString& path) {
if (ImagePreview::EXTENSIONS.contains(extension)) {
// Image
auto binary = this->window->readBinaryEntry(path);
if (binary.empty()) {
QMessageBox::critical(this->window, tr("Error"), tr("Failed to open file! Please ensure that a game or another application is not using the VPK."));
if (!binary) {
this->showPreview<ErrorPreview>();
return;
}
this->imagePreview->setImage(binary);
this->setImagePreviewVisible();
this->getPreview<ImagePreview>()->setImage(*binary);
this->showPreview<ImagePreview>();
} else if (TextPreview::EXTENSIONS.contains(extension)) {
// Text
this->textPreview->setText(this->window->readTextEntry(path));
this->setTextPreviewVisible();
auto text = this->window->readTextEntry(path);
if (!text) {
this->showPreview<ErrorPreview>();
return;
}
this->getPreview<TextPreview>()->setText(*text);
this->showPreview<TextPreview>();
} else if (VTFPreview::EXTENSIONS.contains(extension)) {
// VTF (texture)
auto binary = this->window->readBinaryEntry(path);
if (binary.empty()) {
QMessageBox::critical(this->window, tr("Error"), tr("Failed to open file! Please ensure that a game or another application is not using the VPK."));
if (!binary) {
this->showPreview<ErrorPreview>();
return;
}
this->vtfPreview->setImage(binary);
this->setVTFPreviewVisible();
this->getPreview<VTFPreview>()->setImage(*binary);
this->showPreview<VTFPreview>();
}
}

void FileViewer::displayDir(const QString& /*path*/, const QList<QString>& subfolders, const QList<QString>& entryPaths, const VPK& vpk) {
this->clearContents();
this->dirPreview->setPath(subfolders, entryPaths, vpk);
this->setDirPreviewVisible();
this->getPreview<DirPreview>()->setPath(subfolders, entryPaths, vpk);
this->showPreview<DirPreview>();
this->showPreview<ErrorPreview>();
}

void FileViewer::selectSubItemInDir(const QString& name) {
this->window->selectSubItemInDir(name);
}

void FileViewer::clearContents() {
this->textPreview->setText("");
this->setTextPreviewVisible();
}

void FileViewer::setDirPreviewVisible() {
this->dirPreview->show();
this->imagePreview->hide();
this->textPreview->hide();
this->vtfPreview->hide();
}

void FileViewer::setImagePreviewVisible() {
this->dirPreview->hide();
this->imagePreview->show();
this->textPreview->hide();
this->vtfPreview->hide();
}

void FileViewer::setTextPreviewVisible() {
this->dirPreview->hide();
this->imagePreview->hide();
this->textPreview->show();
this->vtfPreview->hide();
}

void FileViewer::setVTFPreviewVisible() {
this->dirPreview->hide();
this->imagePreview->hide();
this->textPreview->hide();
this->vtfPreview->show();
this->getPreview<TextPreview>()->setText("");
this->showPreview<TextPreview>();
}
37 changes: 24 additions & 13 deletions src/gui/FileViewer.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#pragma once

#include <typeinfo>
#include <typeindex>

#include <QWidget>

class QTextEdit;
Expand All @@ -10,10 +13,6 @@ class VPK;

} // namespace vpkedit

class DirPreview;
class ImagePreview;
class TextPreview;
class VTFPreview;
class Window;

class FileViewer : public QWidget {
Expand All @@ -33,13 +32,25 @@ class FileViewer : public QWidget {
private:
Window* window;

DirPreview* dirPreview;
ImagePreview* imagePreview;
TextPreview* textPreview;
VTFPreview* vtfPreview;

void setDirPreviewVisible();
void setImagePreviewVisible();
void setTextPreviewVisible();
void setVTFPreviewVisible();
std::unordered_map<std::type_index, QWidget*> previews;

template<typename T, typename... Args>
T* newPreview(Args... args) {
auto* preview = new T(std::forward<Args>(args)...);
this->previews[std::type_index(typeid(T))] = preview;
return preview;
}

template<typename T>
inline T* getPreview() {
return dynamic_cast<T*>(this->previews.at(std::type_index(typeid(T))));
}

template<typename T>
void showPreview() {
for (const auto [index, widget] : this->previews) {
widget->hide();
}
this->previews.at(std::type_index(typeid(T)))->show();
}
};
26 changes: 17 additions & 9 deletions src/gui/Window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,20 +349,24 @@ void Window::aboutQt() {
QMessageBox::aboutQt(this);
}

std::vector<std::byte> Window::readBinaryEntry(const QString& path) {
std::optional<std::vector<std::byte>> Window::readBinaryEntry(const QString& path) {
auto entry = (*this->vpk).findEntry(path.toStdString());
if (!entry) {
return {};
return std::nullopt;
}
return (*this->vpk).readBinaryEntry(*entry);
}

QString Window::readTextEntry(const QString& path) {
std::optional<QString> Window::readTextEntry(const QString& path) {
auto entry = (*this->vpk).findEntry(path.toStdString());
if (!entry) {
return {};
return std::nullopt;
}
return {(*this->vpk).readTextEntry(*entry).c_str()};
auto textData = (*this->vpk).readTextEntry(*entry);
if (!textData) {
return std::nullopt;
}
return QString(textData->c_str());
}

void Window::selectEntry(const QString& path) {
Expand Down Expand Up @@ -512,7 +516,8 @@ bool Window::loadVPK(const QString& path) {
QMessageBox::critical(this, tr("Error"), "Unable to load given VPK. Please ensure you are loading a "
"\"directory\" VPK (typically ending in _dir), not a VPK that "
"ends with 3 numbers. Loading a directory VPK will allow you "
"to browse the contents of the numbered archives next to it.");
"to browse the contents of the numbered archives next to it.\n"
"Also, please ensure that a game or another application is not using the VPK.");
return false;
}

Expand All @@ -536,16 +541,19 @@ bool Window::loadVPK(const QString& path) {
}

void Window::writeEntryToFile(const QString& path, const VPKEntry& entry) {
auto data = (*this->vpk).readBinaryEntry(entry);
if (!data) {
QMessageBox::critical(this, tr("Error"), QString("Failed to read data from the VPK for \"") + entry.filename.c_str() + "\". Please ensure that a game or another application is not using the VPK.");
return;
}
QFile file(path);
if (!file.open(QIODevice::WriteOnly)) {
QMessageBox::critical(this, tr("Error"), QString("Failed to write to file at \"") + path + "\".");
return;
}
auto data = (*this->vpk).readBinaryEntry(entry);
auto bytesWritten = file.write(reinterpret_cast<const char*>(data.data()), entry.length);
auto bytesWritten = file.write(reinterpret_cast<const char*>(data->data()), entry.length);
if (bytesWritten != entry.length) {
QMessageBox::critical(this, tr("Error"), QString("Failed to write to file at \"") + path + "\".");
return;
}
file.close();
}
4 changes: 2 additions & 2 deletions src/gui/Window.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ class Window : public QMainWindow {

void aboutQt();

[[nodiscard]] std::vector<std::byte> readBinaryEntry(const QString& path);
[[nodiscard]] std::optional<std::vector<std::byte>> readBinaryEntry(const QString& path);

[[nodiscard]] QString readTextEntry(const QString& path);
[[nodiscard]] std::optional<QString> readTextEntry(const QString& path);

void selectEntry(const QString& path);

Expand Down
21 changes: 21 additions & 0 deletions src/gui/previews/ErrorPreview.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "ErrorPreview.h"

#include <QHBoxLayout>
#include <QLabel>
#include <QPixmap>

ErrorPreview::ErrorPreview(QWidget* parent)
: QWidget(parent) {
this->setSizePolicy(QSizePolicy::Policy::Maximum, QSizePolicy::Policy::Minimum);

auto* layout = new QHBoxLayout(this);
layout->setSpacing(16);

auto* warningImage = new QLabel(this);
warningImage->setPixmap(QPixmap(":/error.png"));
layout->addWidget(warningImage, Qt::AlignCenter);

auto* warningLabel = new QLabel(this);
warningLabel->setText(tr("Failed to read file contents!\nPlease ensure that a game or another application is not using the VPK."));
layout->addWidget(warningLabel, Qt::AlignLeft);
}
14 changes: 14 additions & 0 deletions src/gui/previews/ErrorPreview.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <QWidget>

class ErrorPreview : public QWidget {
Q_OBJECT;

public:
static inline const QStringList EXTENSIONS {
// None, this is displayed when an entry cannot be read, maybe due to another process using it
};

explicit ErrorPreview(QWidget* parent = nullptr);
};
Binary file added src/gui/res/error.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/gui/res/res.qrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/">
<file>icon.png</file>
<file>error.png</file>
</qresource>
</RCC>

0 comments on commit 154892a

Please sign in to comment.