aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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>();
+}