aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoroldmud0 <oldmud0@users.noreply.github.com>2021-06-05 14:58:40 -0500
committeroldmud0 <oldmud0@users.noreply.github.com>2021-06-05 14:58:40 -0500
commitd27501313cae78b838c1e738ebfaeae4740a23b4 (patch)
tree207740d8a42b84a851303b8f2583c523f1624caa
parenta023657348051cbc7c8ea29e3b37f3e2e3fd16d8 (diff)
Finish mounting feature
To pull this one off, a new class called VPath was created that denotes "virtual" paths that can exist anywhere among the list of mount points. It is functionally identical to QString, except that implicit conversion between QString and VPath is not allowed. This makes it easy to spot errors in path resolution at compile time, since get_real_path must be called to resolve a VPath into an absolute path that can be passed into a Qt function as a QString. Other functions, such as the get_*_suffix functions, also return an absolute path QString for convenience. As for the rest of the functions that return a VPath, you will need to call get_real_path yourself. Finally, a path resolution cache was added to try to avoid blowing up what is already a massive lookup cost for assets. The cache is invalidated when the mount path list is changed. Currently, this cache isn't bounded. Might need to write an LRU cache if issues arise.
-rw-r--r--include/aoapplication.h62
-rw-r--r--include/aolayer.h1
-rw-r--r--include/aooptionsdialog.h3
-rw-r--r--src/aoapplication.cpp1
-rw-r--r--src/aobutton.cpp2
-rw-r--r--src/aoevidencebutton.cpp8
-rw-r--r--src/aoevidencedisplay.cpp3
-rw-r--r--src/aoimage.cpp17
-rw-r--r--src/aolayer.cpp2
-rw-r--r--src/aomusicplayer.cpp2
-rw-r--r--src/aooptionsdialog.cpp73
-rw-r--r--src/charselect.cpp4
-rw-r--r--src/courtroom.cpp44
-rw-r--r--src/file_functions.cpp3
-rw-r--r--src/path_functions.cpp151
-rw-r--r--src/text_file_functions.cpp78
16 files changed, 291 insertions, 163 deletions
diff --git a/include/aoapplication.h b/include/aoapplication.h
index b0a87b53..0c61d7b5 100644
--- a/include/aoapplication.h
+++ b/include/aoapplication.h
@@ -37,6 +37,25 @@ class NetworkManager;
class Lobby;
class Courtroom;
+class VPath : QString {
+ using QString::QString;
+
+public:
+ explicit VPath(const QString &str) : QString(str) {}
+ inline QString const &toQString() const { return *this; }
+ inline bool operator==(const VPath &str) const {
+ return this->toQString() == str.toQString();
+ }
+ inline VPath operator+(const VPath &str) const {
+ return VPath(this->toQString() + str.toQString());
+ }
+};
+
+inline uint qHash(const VPath &key, uint seed)
+{
+ return qHash(key.toQString(), seed);
+}
+
class AOApplication : public QApplication {
Q_OBJECT
@@ -130,23 +149,25 @@ public:
// implementation in path_functions.cpp
QString get_base_path();
- QString get_theme_path(QString p_file, QString p_theme="");
- QString get_character_path(QString p_char, QString p_file);
- QString get_misc_path(QString p_misc, QString p_file);
- QString get_sounds_path(QString p_file);
- QString get_music_path(QString p_song);
- QString get_background_path(QString p_file);
- QString get_default_background_path(QString p_file);
- QString get_evidence_path(QString p_file);
- QStringList get_asset_paths(QString p_element, QString p_theme="", QString p_subtheme="", QString p_default_theme="", QString p_misc="", QString p_character="", QString p_placeholder="");
- QString get_asset_path(QStringList pathlist);
- QString get_image_path(QStringList pathlist, bool static_image=false);
- QString get_sfx_path(QStringList pathlist);
+ VPath get_theme_path(QString p_file, QString p_theme="");
+ VPath get_character_path(QString p_char, QString p_file);
+ VPath get_misc_path(QString p_misc, QString p_file);
+ VPath get_sounds_path(QString p_file);
+ VPath get_music_path(QString p_song);
+ VPath get_background_path(QString p_file);
+ VPath get_default_background_path(QString p_file);
+ VPath get_evidence_path(QString p_file);
+ QVector<VPath> get_asset_paths(QString p_element, QString p_theme="", QString p_subtheme="", QString p_default_theme="", QString p_misc="", QString p_character="", QString p_placeholder="");
+ QString get_asset_path(QVector<VPath> pathlist);
+ QString get_image_path(QVector<VPath> pathlist, bool static_image=false);
+ QString get_sfx_path(QVector<VPath> pathlist);
QString get_config_value(QString p_identifier, QString p_config, QString p_theme="", QString p_subtheme="", QString p_default_theme="", QString p_misc="");
QString get_asset(QString p_element, QString p_theme="", QString p_subtheme="", QString p_default_theme="", QString p_misc="", QString p_character="", QString p_placeholder="");
- QString get_image(QString p_element, QString p_theme="", QString p_subtheme="", QString p_default_theme="", QString p_misc="", QString p_character="", QString p_placeholder="");
+ QString get_image(QString p_element, QString p_theme="", QString p_subtheme="", QString p_default_theme="", QString p_misc="", QString p_character="", QString p_placeholder="", bool static_image=false);
QString get_sfx(QString p_sfx, QString p_misc="", QString p_character="");
QString get_case_sensitive_path(QString p_file);
+ QString get_real_path(const VPath &vpath);
+ void invalidate_lookup_cache();
////// Functions for reading and writing files //////
// Implementations file_functions.cpp
@@ -281,6 +302,7 @@ public:
QStringList get_call_words();
// returns all of the file's lines in a QStringList
+ QStringList get_list_file(VPath path);
QStringList get_list_file(QString p_file);
// Process a file and return its text as a QString
@@ -304,6 +326,7 @@ public:
QVector<server_type> read_serverlist_txt();
// Returns the value of p_identifier in the design.ini file in p_design_path
+ QString read_design_ini(QString p_identifier, VPath p_design_path);
QString read_design_ini(QString p_identifier, QString p_design_path);
// Returns the coordinates of widget with p_identifier from p_file
@@ -332,19 +355,22 @@ public:
// Returns the sfx with p_identifier from courtroom_sounds.ini in the current theme path
QString get_court_sfx(QString p_identifier, QString p_misc="");
+ // Find the correct suffix for a given file
+ QString get_suffix(VPath file_to_check, QStringList suffixes);
+
// Figure out if we can opus this or if we should fall back to wav
- QString get_sfx_suffix(QString sound_to_check);
+ QString get_sfx_suffix(VPath sound_to_check);
// Can we use APNG for this? If not, WEBP? If not, GIF? If not, fall back to
// PNG.
- QString get_image_suffix(QString path_to_check, bool static_image=false);
+ QString get_image_suffix(VPath path_to_check, bool static_image=false);
// Returns the value of p_search_line within target_tag and terminator_tag
QString read_char_ini(QString p_char, QString p_search_line,
QString target_tag);
// Returns a QStringList of all key=value definitions on a given tag.
- QStringList read_ini_tags(QString p_file, QString target_tag = "");
+ QStringList read_ini_tags(VPath p_file, QString target_tag = "");
// Sets the char.ini p_search_line key under tag target_tag to value.
void set_char_ini(QString p_char, QString value, QString p_search_line,
@@ -489,6 +515,9 @@ public:
// Get if the theme is animated
bool get_animated_theme();
+ // Get a list of custom mount paths
+ QStringList get_mount_paths();
+
// Currently defined subtheme
QString subtheme;
@@ -514,6 +543,7 @@ private:
QVector<server_type> server_list;
QVector<server_type> favorite_list;
+ QHash<VPath, QString> asset_lookup_cache;
private slots:
void ms_connect_finished(bool connected, bool will_retry);
diff --git a/include/aolayer.h b/include/aolayer.h
index 1984b77e..90108afc 100644
--- a/include/aolayer.h
+++ b/include/aolayer.h
@@ -9,6 +9,7 @@
#include <QBitmap>
class AOApplication;
+class VPath;
// "Brief" explanation of what the hell this is:
//
diff --git a/include/aooptionsdialog.h b/include/aooptionsdialog.h
index 62ce2f7b..3ea8ccc3 100644
--- a/include/aooptionsdialog.h
+++ b/include/aooptionsdialog.h
@@ -180,6 +180,9 @@ private:
QPushButton *ui_mount_remove;
QPushButton *ui_mount_up;
QPushButton *ui_mount_down;
+ QPushButton *ui_mount_clear_cache;
+
+ bool asset_cache_dirty = false;
bool needs_default_audiodev();
void update_values();
diff --git a/src/aoapplication.cpp b/src/aoapplication.cpp
index dd75b0a7..355db037 100644
--- a/src/aoapplication.cpp
+++ b/src/aoapplication.cpp
@@ -19,6 +19,7 @@ AOApplication::AOApplication(int &argc, char **argv) : QApplication(argc, argv)
QObject::connect(net_manager, SIGNAL(ms_connect_finished(bool, bool)),
SLOT(ms_connect_finished(bool, bool)));
// qApp->setStyleSheet("QFrame {background-color:transparent;} QAbstractItemView {background-color: transparent; color: black;}; QLineEdit {background-color:transparent;}");
+ asset_lookup_cache.reserve(2048);
}
AOApplication::~AOApplication()
diff --git a/src/aobutton.cpp b/src/aobutton.cpp
index 7f8c13fe..fb9da7d3 100644
--- a/src/aobutton.cpp
+++ b/src/aobutton.cpp
@@ -27,7 +27,7 @@ void AOButton::set_image(QString p_path, QString p_misc)
else
// Grab a static variant of the image
p_image = ao_app->get_image_path(ao_app->get_asset_paths(p_path, ao_app->current_theme, ao_app->get_subtheme(), ao_app->default_theme, p_misc), true);
- if (!file_exists(p_image)) {
+ if (p_image.isEmpty()) {
this->setIcon(QIcon());
this->setIconSize(this->size());
this->setStyleSheet("");
diff --git a/src/aoevidencebutton.cpp b/src/aoevidencebutton.cpp
index aea903ad..11fed599 100644
--- a/src/aoevidencebutton.cpp
+++ b/src/aoevidencebutton.cpp
@@ -32,7 +32,7 @@ AOEvidenceButton::AOEvidenceButton(QWidget *p_parent, AOApplication *p_ao_app,
void AOEvidenceButton::set_image(QString p_image)
{
- QString image_path = ao_app->get_evidence_path(p_image);
+ QString image_path = ao_app->get_real_path(ao_app->get_evidence_path(p_image));
if (file_exists(p_image)) {
this->setText("");
this->setStyleSheet(
@@ -57,8 +57,10 @@ void AOEvidenceButton::set_image(QString p_image)
void AOEvidenceButton::set_theme_image(QString p_image)
{
- QString theme_image_path = ao_app->get_theme_path(p_image);
- QString default_image_path = ao_app->get_theme_path(p_image, ao_app->default_theme);
+ QString theme_image_path = ao_app->get_real_path(
+ ao_app->get_theme_path(p_image));
+ QString default_image_path = ao_app->get_real_path(
+ ao_app->get_theme_path(p_image, ao_app->default_theme));
QString final_image_path;
diff --git a/src/aoevidencedisplay.cpp b/src/aoevidencedisplay.cpp
index ba1c170d..c635a02f 100644
--- a/src/aoevidencedisplay.cpp
+++ b/src/aoevidencedisplay.cpp
@@ -35,7 +35,8 @@ void AOEvidenceDisplay::show_evidence(QString p_evidence_image,
gif_name = "evidence_appear_right";
}
- QString f_evidence_path = ao_app->get_evidence_path(p_evidence_image);
+ QString f_evidence_path = ao_app->get_real_path(
+ ao_app->get_evidence_path(p_evidence_image));
QPixmap f_pixmap(f_evidence_path);
pos_size_type icon_dimensions =
diff --git a/src/aoimage.cpp b/src/aoimage.cpp
index e1bd8b8c..fbf8c992 100644
--- a/src/aoimage.cpp
+++ b/src/aoimage.cpp
@@ -20,21 +20,16 @@ AOImage::AOImage(QWidget *parent, AOApplication *p_ao_app) : QLabel(parent)
AOImage::~AOImage() {}
-bool AOImage::set_image(QString p_path, QString p_misc)
+bool AOImage::set_image(QString p_image, QString p_misc)
{
- // Check if the user wants animated themes
- if (ao_app->get_animated_theme())
- // We want an animated image
- p_path = ao_app->get_image(p_path, ao_app->current_theme, ao_app->get_subtheme(), ao_app->default_theme, p_misc);
- else
- // Grab a static variant of the image
- p_path = ao_app->get_image_path(ao_app->get_asset_paths(p_path, ao_app->current_theme, ao_app->get_subtheme(), ao_app->default_theme, p_misc), true);
+ p_image = ao_app->get_image(p_image, ao_app->current_theme, ao_app->get_subtheme(),
+ ao_app->default_theme, p_misc, "", "", !ao_app->get_animated_theme());
- if (!file_exists(p_path)) {
- qDebug() << "Warning: Image" << p_path << "not found! Can't set!";
+ if (!file_exists(p_image)) {
+ qDebug() << "Warning: Image" << p_image << "not found! Can't set!";
return false;
}
- path = p_path;
+ path = p_image;
movie->stop();
movie->setFileName(path);
if (ao_app->get_animated_theme() && movie->frameCount() > 1) {
diff --git a/src/aolayer.cpp b/src/aolayer.cpp
index f0d5779e..27e7a657 100644
--- a/src/aolayer.cpp
+++ b/src/aolayer.cpp
@@ -137,7 +137,7 @@ void BackgroundLayer::load_image(QString p_filename)
{
play_once = false;
cull_image = false;
- QString design_path = ao_app->get_background_path("design.ini");
+ VPath design_path = ao_app->get_background_path("design.ini");
transform_mode =
ao_app->get_scaling(ao_app->read_design_ini("scaling", design_path));
stretch = ao_app->read_design_ini("stretch", design_path).startsWith("true");
diff --git a/src/aomusicplayer.cpp b/src/aomusicplayer.cpp
index 8a3142eb..c4d84964 100644
--- a/src/aomusicplayer.cpp
+++ b/src/aomusicplayer.cpp
@@ -19,7 +19,7 @@ void AOMusicPlayer::play(QString p_song, int channel, bool loop,
channel = channel % m_channelmax;
if (channel < 0) // wtf?
return;
- QString f_path = ao_app->get_music_path(p_song);
+ QString f_path = ao_app->get_real_path(ao_app->get_music_path(p_song));
unsigned int flags = BASS_STREAM_PRESCAN | BASS_STREAM_AUTOFREE |
BASS_UNICODE | BASS_ASYNCFILE;
diff --git a/src/aooptionsdialog.cpp b/src/aooptionsdialog.cpp
index 6f1b3489..a035a74f 100644
--- a/src/aooptionsdialog.cpp
+++ b/src/aooptionsdialog.cpp
@@ -14,7 +14,7 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app)
// Setting up the basics.
setWindowFlag(Qt::WindowCloseButtonHint);
setWindowTitle(tr("Settings"));
- resize(400, 408);
+ resize(450, 408);
ui_settings_buttons = new QDialogButtonBox(this);
@@ -907,7 +907,12 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app)
QFileDialog::ShowDirsOnly);
if (dir.isEmpty())
return;
- ui_mount_list->addItem(dir);
+ QListWidgetItem *dir_item = new QListWidgetItem(dir);
+ ui_mount_list->addItem(dir_item);
+ ui_mount_list->setCurrentItem(dir_item);
+
+ // quick hack to update buttons
+ emit ui_mount_list->itemSelectionChanged();
});
ui_mount_remove = new QPushButton(tr("Remove"), ui_assets_tab);
@@ -918,7 +923,9 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app)
auto selected = ui_mount_list->selectedItems();
if (selected.isEmpty())
return;
- ui_mount_list->removeItemWidget(selected[0]);
+ delete selected[0];
+ emit ui_mount_list->itemSelectionChanged();
+ asset_cache_dirty = true;
});
auto *mount_buttons_spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding,
@@ -934,7 +941,13 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app)
auto selected = ui_mount_list->selectedItems();
if (selected.isEmpty())
return;
- ui_mount_list->setEditTriggers()
+ auto *item = selected[0];
+ int row = ui_mount_list->row(item);
+ ui_mount_list->takeItem(row);
+ int new_row = qMax(1, row - 1);
+ ui_mount_list->insertItem(new_row, item);
+ ui_mount_list->setCurrentRow(new_row);
+ asset_cache_dirty = true;
});
ui_mount_down = new QPushButton(tr("↓"), ui_assets_tab);
@@ -942,6 +955,49 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app)
ui_mount_down->setMaximumWidth(40);
ui_mount_down->setEnabled(false);
ui_mount_buttons_layout->addWidget(ui_mount_down, 0, 4, 1, 1);
+ connect(ui_mount_down, &QPushButton::clicked, this, [=] {
+ auto selected = ui_mount_list->selectedItems();
+ if (selected.isEmpty())
+ return;
+ auto *item = selected[0];
+ int row = ui_mount_list->row(item);
+ ui_mount_list->takeItem(row);
+ int new_row = qMin(ui_mount_list->count() + 1, row + 1);
+ ui_mount_list->insertItem(new_row, item);
+ ui_mount_list->setCurrentRow(new_row);
+ asset_cache_dirty = true;
+ });
+
+ auto *mount_buttons_spacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding,
+ QSizePolicy::Minimum);
+ ui_mount_buttons_layout->addItem(mount_buttons_spacer_2, 0, 5, 1, 1);
+
+ ui_mount_clear_cache = new QPushButton(tr("Clear Cache"), ui_assets_tab);
+ ui_mount_clear_cache->setToolTip(tr("Clears the lookup cache for assets. "
+ "Use this when you have added an asset that takes precedence over another "
+ "existing asset."));
+ ui_mount_buttons_layout->addWidget(ui_mount_clear_cache, 0, 6, 1, 1);
+ connect(ui_mount_clear_cache, &QPushButton::clicked, this, [=] {
+ asset_cache_dirty = true;
+ ui_mount_clear_cache->setEnabled(false);
+ });
+
+ connect(ui_mount_list, &QListWidget::itemSelectionChanged, this, [=] {
+ auto selected_items = ui_mount_list->selectedItems();
+ bool row_selected = !ui_mount_list->selectedItems().isEmpty();
+ ui_mount_remove->setEnabled(row_selected);
+ ui_mount_up->setEnabled(row_selected);
+ ui_mount_down->setEnabled(row_selected);
+
+ if (!row_selected)
+ return;
+
+ int row = ui_mount_list->row(selected_items[0]);
+ if (row <= 1)
+ ui_mount_up->setEnabled(false);
+ if (row >= ui_mount_list->count() - 1)
+ ui_mount_down->setEnabled(false);
+ });
update_values();
@@ -1019,6 +1075,7 @@ void AOOptionsDialog::update_values() {
.arg(ao_app->get_base_path()));
defaultMount->setFlags(Qt::ItemFlag::NoItemFlags);
ui_mount_list->addItem(defaultMount);
+ ui_mount_list->addItems(ao_app->get_mount_paths());
}
void AOOptionsDialog::save_pressed()
@@ -1091,9 +1148,17 @@ void AOOptionsDialog::save_pressed()
configini->setValue("casing_can_host_cases",
ui_casing_cm_cases_textbox->text());
+ QStringList mountPaths;
+ for (int i = 1; i < ui_mount_list->count(); i++)
+ mountPaths.append(ui_mount_list->item(i)->text());
+ configini->setValue("mount_paths", mountPaths);
+
if (audioChanged)
ao_app->initBASS();
+ if (asset_cache_dirty)
+ ao_app->invalidate_lookup_cache();
+
callwordsini->close();
// We most probably pressed "Restore defaults" at some point. Since we're saving our settings, remove the temporary file.
diff --git a/src/charselect.cpp b/src/charselect.cpp
index 510d8c0a..08df96fe 100644
--- a/src/charselect.cpp
+++ b/src/charselect.cpp
@@ -157,8 +157,8 @@ void Courtroom::char_clicked(int n_char)
{
if (n_char != -1)
{
- QString char_ini_path =
- ao_app->get_character_path(char_list.at(n_char).name, "char.ini");
+ QString char_ini_path = ao_app->get_real_path(
+ ao_app->get_character_path(char_list.at(n_char).name, "char.ini"));
qDebug() << "char_ini_path" << char_ini_path;
diff --git a/src/courtroom.cpp b/src/courtroom.cpp
index fe5f74d1..4b9c72c3 100644
--- a/src/courtroom.cpp
+++ b/src/courtroom.cpp
@@ -501,7 +501,7 @@ void Courtroom::set_widgets()
{
QString filename = "courtroom_design.ini";
// Update the default theme from the courtroom_design.ini, if it's not defined it will be 'default'.
- QSettings settings(ao_app->get_theme_path(filename, ao_app->current_theme), QSettings::IniFormat);
+ QSettings settings(ao_app->get_real_path(ao_app->get_theme_path(filename, ao_app->current_theme)), QSettings::IniFormat);
ao_app->default_theme = settings.value("default_theme", "default").toString();
set_fonts();
@@ -1384,11 +1384,11 @@ void Courtroom::update_character(int p_cid)
custom_obj_menu->setDefaultAction(action);
objection_custom = "";
}
- if (dir_exists(
- ao_app->get_character_path(current_char, "custom_objections"))) {
- ui_custom_objection->show();
- QDir directory(
+ QString custom_objection_dir = ao_app->get_real_path(
ao_app->get_character_path(current_char, "custom_objections"));
+ if (dir_exists(custom_objection_dir)) {
+ ui_custom_objection->show();
+ QDir directory(custom_objection_dir);
QStringList custom_obj = directory.entryList(QStringList() << "*.png"
<< "*.gif"
<< "*.apng"
@@ -1523,7 +1523,7 @@ void Courtroom::list_music()
treeItem->setText(0, i_song_listname);
treeItem->setText(1, i_song);
- QString song_path = ao_app->get_music_path(i_song);
+ QString song_path = ao_app->get_real_path(ao_app->get_music_path(i_song));
if (file_exists(song_path))
treeItem->setBackground(0, found_brush);
@@ -4333,8 +4333,9 @@ void Courtroom::set_iniswap_dropdown()
ui_iniswap_remove->hide();
return;
}
- QStringList iniswaps = ao_app->get_list_file(
- ao_app->get_character_path(char_list.at(m_cid).name, "iniswaps.ini")) + ao_app->get_list_file(ao_app->get_base_path() + "iniswaps.ini");
+ QStringList iniswaps =
+ ao_app->get_list_file(ao_app->get_character_path(char_list.at(m_cid).name, "iniswaps.ini")) +
+ ao_app->get_list_file(ao_app->get_base_path() + "iniswaps.ini");
iniswaps.removeDuplicates();
iniswaps.prepend(char_list.at(m_cid).name);
if (iniswaps.size() <= 0) {
@@ -4390,7 +4391,8 @@ void Courtroom::on_iniswap_context_menu_requested(const QPoint &pos)
menu->setAttribute(Qt::WA_DeleteOnClose);
menu->addSeparator();
- if (file_exists(ao_app->get_character_path(current_char, "char.ini")))
+ if (file_exists(ao_app->get_real_path(
+ ao_app->get_character_path(current_char, "char.ini"))))
menu->addAction(QString("Edit " + current_char + "/char.ini"), this,
SLOT(on_iniswap_edit_requested()));
if (ui_iniswap_dropdown->itemText(ui_iniswap_dropdown->currentIndex()) !=
@@ -4401,7 +4403,7 @@ void Courtroom::on_iniswap_context_menu_requested(const QPoint &pos)
}
void Courtroom::on_iniswap_edit_requested()
{
- QString p_path = ao_app->get_character_path(current_char, "char.ini");
+ QString p_path = ao_app->get_real_path(ao_app->get_character_path(current_char, "char.ini"));
if (!file_exists(p_path))
return;
QDesktopServices::openUrl(QUrl::fromLocalFile(p_path));
@@ -4478,7 +4480,8 @@ void Courtroom::on_sfx_context_menu_requested(const QPoint &pos)
menu->setAttribute(Qt::WA_DeleteOnClose);
menu->addSeparator();
- if (file_exists(ao_app->get_character_path(current_char, "soundlist.ini")))
+ if (file_exists(ao_app->get_real_path(
+ ao_app->get_character_path(current_char, "soundlist.ini"))))
menu->addAction(QString("Edit " + current_char + "/soundlist.ini"), this,
SLOT(on_sfx_edit_requested()));
else
@@ -4491,10 +4494,10 @@ void Courtroom::on_sfx_context_menu_requested(const QPoint &pos)
void Courtroom::on_sfx_edit_requested()
{
- QString p_path = ao_app->get_character_path(current_char, "soundlist.ini");
+ QString p_path = ao_app->get_real_path(ao_app->get_character_path(current_char, "soundlist.ini"));
if (!file_exists(p_path)) {
p_path = ao_app->get_base_path() + "soundlist.ini";
- }
+ }
QDesktopServices::openUrl(QUrl::fromLocalFile(p_path));
}
@@ -4527,12 +4530,11 @@ void Courtroom::set_effects_dropdown()
// ICON-MAKING HELL
QString p_effect = ao_app->read_char_ini(current_char, "effects", "Options");
- QString custom_path =
- ao_app->get_base_path() + "misc/" + p_effect + "/icons/";
- QString theme_path = ao_app->get_theme_path("effects/icons/");
- QString default_path = ao_app->get_theme_path("effects/icons/", "default");
+ VPath custom_path("misc/" + p_effect + "/icons/");
+ VPath theme_path = ao_app->get_theme_path("effects/icons/");
+ VPath default_path = ao_app->get_theme_path("effects/icons/", "default");
for (int i = 0; i < ui_effects_dropdown->count(); ++i) {
- QString entry = ui_effects_dropdown->itemText(i);
+ VPath entry = VPath(ui_effects_dropdown->itemText(i));
QString iconpath = ao_app->get_image_suffix(custom_path + entry);
if (!file_exists(iconpath)) {
iconpath = ao_app->get_image_suffix(theme_path + entry);
@@ -4566,9 +4568,9 @@ void Courtroom::on_effects_context_menu_requested(const QPoint &pos)
}
void Courtroom::on_effects_edit_requested()
{
- QString p_path = ao_app->get_theme_path("effects/");
+ QString p_path = ao_app->get_real_path(ao_app->get_theme_path("effects/"));
if (!dir_exists(p_path)) {
- p_path = ao_app->get_theme_path("effects/", "default");
+ p_path = ao_app->get_real_path(ao_app->get_theme_path("effects/", "default"));
if (!dir_exists(p_path)) {
return;
}
@@ -4578,7 +4580,7 @@ void Courtroom::on_effects_edit_requested()
void Courtroom::on_character_effects_edit_requested()
{
QString p_effect = ao_app->read_char_ini(current_char, "effects", "Options");
- QString p_path = ao_app->get_base_path() + "misc/" + p_effect + "/";
+ QString p_path = ao_app->get_real_path(VPath("misc/" + p_effect + "/"));
if (!dir_exists(p_path))
return;
diff --git a/src/file_functions.cpp b/src/file_functions.cpp
index e64a46bf..37cdc32c 100644
--- a/src/file_functions.cpp
+++ b/src/file_functions.cpp
@@ -2,6 +2,9 @@
bool file_exists(QString file_path)
{
+ if (file_path.isEmpty())
+ return false;
+
QFileInfo check_file(file_path);
return check_file.exists() && check_file.isFile();
diff --git a/src/path_functions.cpp b/src/path_functions.cpp
index 97005295..b1a5e48e 100644
--- a/src/path_functions.cpp
+++ b/src/path_functions.cpp
@@ -39,71 +39,58 @@ QString AOApplication::get_base_path()
return base_path;
}
-QString AOApplication::get_theme_path(QString p_file, QString p_theme)
+VPath AOApplication::get_theme_path(QString p_file, QString p_theme)
{
if (p_theme == "")
p_theme = current_theme;
- QString path = get_base_path() + "themes/" + p_theme + "/" + p_file;
- return get_case_sensitive_path(path);
+ return VPath("themes/" + p_theme + "/" + p_file);
}
-QString AOApplication::get_character_path(QString p_char, QString p_file)
+VPath AOApplication::get_character_path(QString p_char, QString p_file)
{
- QString path = get_base_path() + "characters/" + p_char + "/" + p_file;
- return get_case_sensitive_path(path);
+ return VPath("characters/" + p_char + "/" + p_file);
}
-QString AOApplication::get_misc_path(QString p_misc, QString p_file)
+VPath AOApplication::get_misc_path(QString p_misc, QString p_file)
{
- QString path = get_base_path() + "misc/" + p_misc + "/" + p_file;
-#ifndef CASE_SENSITIVE_FILESYSTEM
- return path;
-#else
- return get_case_sensitive_path(path);
-#endif
+ return VPath("misc/" + p_misc + "/" + p_file);
}
-QString AOApplication::get_sounds_path(QString p_file)
+VPath AOApplication::get_sounds_path(QString p_file)
{
- QString path = get_base_path() + "sounds/general/" + p_file;
- return get_case_sensitive_path(path);
+ return VPath("sounds/general/" + p_file);
}
-QString AOApplication::get_music_path(QString p_song)
+VPath AOApplication::get_music_path(QString p_song)
{
if (p_song.startsWith("http")) {
- return p_song; // url
+ return VPath(p_song); // url
}
- QString path = get_base_path() + "sounds/music/" + p_song;
- return get_case_sensitive_path(path);
+ return VPath("sounds/music/" + p_song);
}
-QString AOApplication::get_background_path(QString p_file)
+VPath AOApplication::get_background_path(QString p_file)
{
- QString path = get_base_path() + "background/" +
- w_courtroom->get_current_background() + "/" + p_file;
if (courtroom_constructed) {
- return get_case_sensitive_path(path);
+ return VPath("background/" + w_courtroom->get_current_background() + "/" + p_file);
}
return get_default_background_path(p_file);
}
-QString AOApplication::get_default_background_path(QString p_file)
+VPath AOApplication::get_default_background_path(QString p_file)
{
- QString path = get_base_path() + "background/default/" + p_file;
- return get_case_sensitive_path(path);
+ return VPath("background/default/" + p_file);
}
-QString AOApplication::get_evidence_path(QString p_file)
+VPath AOApplication::get_evidence_path(QString p_file)
{
- QString path = get_base_path() + "evidence/" + p_file;
- return get_case_sensitive_path(path);
+ return VPath("evidence/" + p_file);
}
-QStringList AOApplication::get_asset_paths(QString p_element, QString p_theme, QString p_subtheme, QString p_default_theme, QString p_misc, QString p_character, QString p_placeholder)
+QVector<VPath> AOApplication::get_asset_paths(QString p_element, QString p_theme, QString p_subtheme, QString p_default_theme, QString p_misc, QString p_character, QString p_placeholder)
{
- QStringList pathlist;
- pathlist += p_element; // The path by itself
+ QVector<VPath> pathlist;
+ pathlist += VPath(p_element); // The path by itself
if (p_character != "")
pathlist += get_character_path(p_character, p_element); // Character folder
if (p_misc != "" && p_theme != "" && p_subtheme != "")
@@ -125,52 +112,47 @@ QStringList AOApplication::get_asset_paths(QString p_element, QString p_theme, Q
return pathlist;
}
-QString AOApplication::get_asset_path(QStringList pathlist)
+QString AOApplication::get_asset_path(QVector<VPath> pathlist)
{
- QString path;
- for (QString p : pathlist) {
- p = get_case_sensitive_path(p);
- if (file_exists(p)) {
- path = p;
- break;
+ for (const VPath &p : pathlist) {
+ QString path = get_real_path(p);
+ if (!path.isEmpty()) {
+ return path;
}
}
- return path;
+ return QString();
}
-QString AOApplication::get_image_path(QStringList pathlist, bool static_image)
+QString AOApplication::get_image_path(QVector<VPath> pathlist, bool static_image)
{
- QString path;
- for (QString p : pathlist) {
- p = get_case_sensitive_path(get_image_suffix(p, static_image));
- if (file_exists(p)) {
- path = p;
- break;
- }
+ for (const VPath &p : pathlist) {
+ QString path = get_image_suffix(p, static_image);
+ if (!path.isEmpty()) {
+ return path;
+ }
}
- return path;
+ return QString();
}
-QString AOApplication::get_sfx_path(QStringList pathlist)
+QString AOApplication::get_sfx_path(QVector<VPath> pathlist)
{
- QString path;
- for (QString p : pathlist) {
- p = get_case_sensitive_path(get_sfx_suffix(p));
- if (file_exists(p)) {
- path = p;
- break;
+ for (const VPath &p : pathlist) {
+ QString path = get_sfx_suffix(p);
+ if (!path.isEmpty()) {
+ return path;
}
}
- return path;
+ return QString();
}
+
QString AOApplication::get_config_value(QString p_identifier, QString p_config, QString p_theme, QString p_subtheme, QString p_default_theme, QString p_misc)
{
QString path;
// qDebug() << "got request for" << p_identifier << "in" << p_config;
- for (QString p : get_asset_paths(p_config, p_theme, p_subtheme, p_default_theme, p_misc)) {
- p = get_case_sensitive_path(p);
- if (file_exists(p)) {
- QSettings settings(p, QSettings::IniFormat);
+ for (const VPath &p : get_asset_paths(p_config, p_theme, p_subtheme, p_default_theme, p_misc)) {
+ path = get_real_path(p);
+ if (!path.isEmpty()) {
+ QSettings settings(path, QSettings::IniFormat);
QVariant value = settings.value(p_identifier);
if (value.type() == QVariant::StringList) {
// qDebug() << "got" << p << "is a string list, returning" << value.toStringList().join(",");
@@ -190,25 +172,22 @@ QString AOApplication::get_asset(QString p_element, QString p_theme, QString p_s
return get_asset_path(get_asset_paths(p_element, p_theme, p_subtheme, p_default_theme, p_misc, p_character, p_placeholder));
}
-QString AOApplication::get_image(QString p_element, QString p_theme, QString p_subtheme, QString p_default_theme, QString p_misc, QString p_character, QString p_placeholder)
+QString AOApplication::get_image(QString p_element, QString p_theme, QString p_subtheme, QString p_default_theme, QString p_misc, QString p_character, QString p_placeholder,
+ bool static_image)
{
- return get_image_path(get_asset_paths(p_element, p_theme, p_subtheme, p_default_theme, p_misc, p_character, p_placeholder));
+ return get_image_path(get_asset_paths(p_element, p_theme, p_subtheme, p_default_theme, p_misc, p_character, p_placeholder), static_image);
}
QString AOApplication::get_sfx(QString p_sfx, QString p_misc, QString p_character)
{
- QStringList pathlist = get_asset_paths(p_sfx, current_theme, get_subtheme(), default_theme, p_misc, p_character);
+ QVector<VPath> pathlist = get_asset_paths(p_sfx, current_theme, get_subtheme(), default_theme, p_misc, p_character);
pathlist += get_sounds_path(p_sfx); // Sounds folder path
return get_sfx_path(pathlist);
}
QString AOApplication::get_case_sensitive_path(QString p_file)
{
- // no path traversal above base folder
- if (!(p_file.startsWith(get_base_path())))
- return get_base_path() + p_file;
-
- #ifdef CASE_SENSITIVE_FILESYSTEM
+#ifdef CASE_SENSITIVE_FILESYSTEM
// first, check to see if it's actually there (also serves as base case for
// recursion)
QFileInfo file(p_file);
@@ -238,3 +217,35 @@ QString AOApplication::get_case_sensitive_path(QString p_file)
return p_file;
#endif
}
+
+QString AOApplication::get_real_path(const VPath &vpath) {
+ // Try cache first
+ QString phys_path = asset_lookup_cache.value(vpath);
+ if (!phys_path.isEmpty() && exists(phys_path)) {
+ return phys_path;
+ }
+
+ // Cache miss; try all known mount paths
+ QStringList bases = get_mount_paths();
+ bases.push_front(get_base_path());
+
+ for (const QString &base : bases) {
+ QDir baseDir(base);
+ const QString path = baseDir.absoluteFilePath(vpath.toQString());
+ if (!path.startsWith(baseDir.absolutePath())) {
+ qWarning() << "invalid path" << path << "(path is outside vfs)";
+ break;
+ }
+ if (exists(get_case_sensitive_path(path))) {
+ asset_lookup_cache.insert(vpath, path);
+ return path;
+ }
+ }
+
+ // File or directory not found
+ return QString();
+}
+
+void AOApplication::invalidate_lookup_cache() {
+ asset_lookup_cache.clear();
+}
diff --git a/src/text_file_functions.cpp b/src/text_file_functions.cpp
index f6a5d6ce..c92287fb 100644
--- a/src/text_file_functions.cpp
+++ b/src/text_file_functions.cpp
@@ -124,6 +124,11 @@ QStringList AOApplication::get_call_words()
return get_list_file(get_base_path() + "callwords.ini");
}
+QStringList AOApplication::get_list_file(VPath path)
+{
+ return get_list_file(get_real_path(path));
+}
+
QStringList AOApplication::get_list_file(QString p_file)
{
QStringList return_value;
@@ -276,6 +281,12 @@ QVector<server_type> AOApplication::read_serverlist_txt()
}
QString AOApplication::read_design_ini(QString p_identifier,
+ VPath p_design_path)
+{
+ return read_design_ini(p_identifier, get_real_path(p_design_path));
+}
+
+QString AOApplication::read_design_ini(QString p_identifier,
QString p_design_path)
{
QSettings settings(p_design_path, QSettings::IniFormat);
@@ -440,12 +451,14 @@ QString AOApplication::get_chat_markup(QString p_identifier, QString p_chat)
return value.toLatin1();
// Backwards ass compatibility
- QStringList backwards_paths{get_theme_path("misc/" + p_chat + "/config.ini"),
- get_base_path() + "misc/" + p_chat +
- "/config.ini",
- get_base_path() + "misc/default/config.ini",
- get_theme_path("misc/default/config.ini")};
- for (const QString &p : backwards_paths) {
+ QVector<VPath> backwards_paths {
+ get_theme_path("misc/" + p_chat + "/config.ini"),
+ VPath("misc/" + p_chat + "/config.ini"),
+ VPath("misc/default/config.ini"),
+ get_theme_path("misc/default/config.ini")
+ };
+
+ for (const VPath &p : backwards_paths) {
QString value = read_design_ini(p_identifier, p);
if (!value.isEmpty()) {
return value.toLatin1();
@@ -482,36 +495,32 @@ QString AOApplication::get_court_sfx(QString p_identifier, QString p_misc)
return "";
}
-QString AOApplication::get_sfx_suffix(QString sound_to_check)
+QString AOApplication::get_suffix(VPath path_to_check, QStringList suffixes) {
+ for (const QString &suffix : suffixes) {
+ QString path = get_real_path(VPath(path_to_check.toQString() + suffix));
+ if (!path.isEmpty())
+ return path;
+ }
+
+ return QString();
+}
+
+QString AOApplication::get_sfx_suffix(VPath sound_to_check)
{
- if (file_exists(sound_to_check))
- return sound_to_check;
- if (file_exists(sound_to_check + ".opus"))
- return sound_to_check + ".opus";
- if (file_exists(sound_to_check + ".ogg"))
- return sound_to_check + ".ogg";
- if (file_exists(sound_to_check + ".mp3"))
- return sound_to_check + ".mp3";
- if (file_exists(sound_to_check + ".mp4"))
- return sound_to_check + ".mp4";
- return sound_to_check + ".wav";
+ return get_suffix(sound_to_check, { "", ".opus", ".ogg", ".mp3", ".wav" });
}
-QString AOApplication::get_image_suffix(QString path_to_check, bool static_image)
+QString AOApplication::get_image_suffix(VPath path_to_check, bool static_image)
{
- if (file_exists(path_to_check))
- return path_to_check;
+ QStringList suffixes { "" };
// A better method would to actually use AOImageReader and see if these images have more than 1 frame.
// However, that might not be performant.
if (!static_image) {
- if (file_exists(path_to_check + ".webp"))
- return path_to_check + ".webp";
- if (file_exists(path_to_check + ".apng"))
- return path_to_check + ".apng";
- if (file_exists(path_to_check + ".gif"))
- return path_to_check + ".gif";
+ suffixes.append({ ".webp", ".apng", ".gif" });
}
- return path_to_check + ".png";
+ suffixes.append(".png");
+
+ return get_suffix(path_to_check, suffixes);
}
// returns whatever is to the right of "search_line =" within target_tag and
@@ -520,7 +529,7 @@ QString AOApplication::get_image_suffix(QString path_to_check, bool static_image
QString AOApplication::read_char_ini(QString p_char, QString p_search_line,
QString target_tag)
{
- QSettings settings(get_character_path(p_char, "char.ini"),
+ QSettings settings(get_real_path(get_character_path(p_char, "char.ini")),
QSettings::IniFormat);
settings.beginGroup(target_tag);
QString value = settings.value(p_search_line).value<QString>();
@@ -531,7 +540,7 @@ QString AOApplication::read_char_ini(QString p_char, QString p_search_line,
void AOApplication::set_char_ini(QString p_char, QString value,
QString p_search_line, QString target_tag)
{
- QSettings settings(get_character_path(p_char, "char.ini"),
+ QSettings settings(get_real_path(get_character_path(p_char, "char.ini")),
QSettings::IniFormat);
settings.beginGroup(target_tag);
settings.setValue(p_search_line, value);
@@ -539,10 +548,10 @@ void AOApplication::set_char_ini(QString p_char, QString value,
}
// returns all the values of target_tag
-QStringList AOApplication::read_ini_tags(QString p_path, QString target_tag)
+QStringList AOApplication::read_ini_tags(VPath p_path, QString target_tag)
{
QStringList r_values;
- QSettings settings(p_path, QSettings::IniFormat);
+ QSettings settings(get_real_path(p_path), QSettings::IniFormat);
if (!target_tag.isEmpty())
settings.beginGroup(target_tag);
QStringList keys = settings.allKeys();
@@ -1084,3 +1093,8 @@ bool AOApplication::get_animated_theme()
configini->value("animated_theme", "true").value<QString>();
return result.startsWith("true");
}
+
+QStringList AOApplication::get_mount_paths()
+{
+ return configini->value("mount_paths").value<QStringList>();
+}