|
23 | 23 | #include <QJsonObject> |
24 | 24 | #include <QByteArray> |
25 | 25 | #include <QJsonArray> |
| 26 | +#include <QEventLoop> |
26 | 27 |
|
27 | | -QString activeSubDirName = "Mods"; // Change this to your desired subdirectory |
| 28 | +QString activeSubDirName = "Mods"; // This is where the active mods are stored |
28 | 29 | QString disabledSubDirName = "(d)Mods"; // Change this to your desired subdirectory |
29 | 30 | QString csvFilePath = "inc/packsDil.csv"; |
30 | | -QString version = "1.0.0"; // Version of the application |
| 31 | +QString csvCloudPath = "https://wrenswift.com/packsDil.csv"; |
| 32 | +QString version = "1.1.0"; // Version of the application |
31 | 33 |
|
32 | 34 | MainWindow::MainWindow(QWidget *parent) |
33 | 35 | : QMainWindow(parent) |
@@ -72,7 +74,7 @@ MainWindow::MainWindow(QWidget *parent) |
72 | 74 | } |
73 | 75 | if (!cachedGameSource.isEmpty()) { |
74 | 76 | ui->gameLineEdit->setText(cachedGameSource); |
75 | | - populatePacksListWidget(cachedGameSource,csvFilePath); |
| 77 | + loadPacksCsv(csvCloudPath, csvFilePath); |
76 | 78 | } |
77 | 79 |
|
78 | 80 | qDebug() << "Resource exists:" << QFile::exists(":/inc/packsDil.csv"); |
@@ -156,6 +158,65 @@ void MainWindow::doVersionCheck() { |
156 | 158 | reply->deleteLater(); |
157 | 159 | } |
158 | 160 |
|
| 161 | +void MainWindow::loadPacksCsv(const QString &url, const QString &localPath) |
| 162 | +{ |
| 163 | + QNetworkAccessManager manager; |
| 164 | + QEventLoop loop; |
| 165 | + QNetworkReply *reply = manager.get(QNetworkRequest(QUrl(url))); |
| 166 | + QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); |
| 167 | + loop.exec(); |
| 168 | + |
| 169 | + QByteArray csvData; |
| 170 | + bool usedNetwork = false; |
| 171 | + |
| 172 | + if (reply->error() == QNetworkReply::NoError) { |
| 173 | + csvData = reply->readAll(); |
| 174 | + usedNetwork = true; |
| 175 | + // Save the latest network version locally for future offline use |
| 176 | + QFile localFile(localPath); |
| 177 | + if (localFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { |
| 178 | + localFile.write(csvData); |
| 179 | + localFile.close(); |
| 180 | + } |
| 181 | + } else { |
| 182 | + // Network failed, try to load from local file |
| 183 | + QFile localFile(localPath); |
| 184 | + if (localFile.open(QIODevice::ReadOnly)) { |
| 185 | + csvData = localFile.readAll(); |
| 186 | + localFile.close(); |
| 187 | + QMessageBox::warning(this, tr("Network Error"), |
| 188 | + tr("Failed to download packs CSV from the cloud. Using local backup copy instead.")); |
| 189 | + } else { |
| 190 | + QMessageBox::warning(this, tr("Network Error"), |
| 191 | + tr("Failed to download packs CSV from the cloud and no local backup is available: %1").arg(reply->errorString())); |
| 192 | + reply->deleteLater(); |
| 193 | + return; |
| 194 | + } |
| 195 | + } |
| 196 | + reply->deleteLater(); |
| 197 | + |
| 198 | + // Parse CSV data (same as before) |
| 199 | + QHash<QString, QString> folderMapping; |
| 200 | + QTextStream in(csvData); |
| 201 | + bool firstLine = true; |
| 202 | + while (!in.atEnd()) { |
| 203 | + QString line = in.readLine().trimmed(); |
| 204 | + if (line.isEmpty()) |
| 205 | + continue; |
| 206 | + if (firstLine) { |
| 207 | + firstLine = false; |
| 208 | + // continue; // Uncomment if your CSV has a header |
| 209 | + } |
| 210 | + QStringList parts = line.split(','); |
| 211 | + if (parts.size() >= 2) { |
| 212 | + QString originalName = parts.at(0).trimmed(); |
| 213 | + QString friendlyName = parts.at(1).trimmed(); |
| 214 | + folderMapping.insert(originalName, friendlyName); |
| 215 | + } |
| 216 | + } |
| 217 | + populatePacksListWidgetWithMapping(ui->gameLineEdit->text(), folderMapping); |
| 218 | +} |
| 219 | + |
159 | 220 | void MainWindow::on_menuMods_clicked(){ |
160 | 221 | ui->mainStackedWidget->setCurrentIndex(0); |
161 | 222 | } |
@@ -614,8 +675,8 @@ void MainWindow::do_S4MPCheck() { |
614 | 675 |
|
615 | 676 | if (foundS4MP) { |
616 | 677 | QMessageBox::warning(this, tr("S4MP Detected"), |
617 | | - tr("\"S4MP Launcher Windows.exe\" is present in your active mods. " |
618 | | - "If you plan to use S4MP please use their launcher for pack selection after desired mods are selected.")); |
| 678 | + tr("'%1' is present in your active mods. " |
| 679 | + "If you plan to use S4MP please use their launcher for pack selection after desired mods are selected.").arg(s4mpExeName)); |
619 | 680 | } |
620 | 681 | } |
621 | 682 |
|
@@ -742,6 +803,57 @@ void MainWindow::populatePacksListWidget(const QString &folderPath, const QStrin |
742 | 803 | sortPacksListByCategory(); |
743 | 804 | } |
744 | 805 |
|
| 806 | +void MainWindow::populatePacksListWidgetWithMapping(const QString &folderPath, const QHash<QString, QString> &folderMapping) |
| 807 | +{ |
| 808 | + ui->packsListWidget->clear(); |
| 809 | + |
| 810 | + QSettings settings("Falcon", "SimsSwitcher"); |
| 811 | + QStringList checkedPacks = settings.value("packsSelection").toStringList(); |
| 812 | + |
| 813 | + QDir dir(folderPath); |
| 814 | + QFileInfoList folderList = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); |
| 815 | + |
| 816 | + for (const QFileInfo &info : folderList) { |
| 817 | + QString originalName = info.fileName(); |
| 818 | + if (!folderMapping.contains(originalName)) |
| 819 | + continue; |
| 820 | + QString displayName = folderMapping.value(originalName); |
| 821 | + QListWidgetItem *item = new QListWidgetItem(displayName); |
| 822 | + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); |
| 823 | + if (checkedPacks.contains(originalName)) |
| 824 | + item->setCheckState(Qt::Checked); |
| 825 | + else |
| 826 | + item->setCheckState(Qt::Unchecked); |
| 827 | + item->setData(Qt::UserRole, info.absoluteFilePath()); |
| 828 | + item->setData(Qt::UserRole + 1, originalName); |
| 829 | + ui->packsListWidget->addItem(item); |
| 830 | + qDebug() << "Added folder:" << originalName << "as" << displayName |
| 831 | + << "Full path:" << info.absoluteFilePath(); |
| 832 | + } |
| 833 | + // If any packs are present in the source directory that are not in the mapping, display a warning. |
| 834 | + QStringList mappedNames = folderMapping.keys(); |
| 835 | + // Check for unmapped packs in the directory. |
| 836 | + for (const QFileInfo &info : folderList) { |
| 837 | + // Get the original name from the QFileInfo. |
| 838 | + QString originalName = info.fileName(); |
| 839 | + // Sort out original names that are not in the format of "EP01", "GP02", etc. |
| 840 | + if (originalName.length() < 4 || !originalName.startsWith("EP") && !originalName.startsWith("GP") && |
| 841 | + !originalName.startsWith("SP") && !originalName.startsWith("FP")) { |
| 842 | + continue; // Skip names that do not match the expected format. |
| 843 | + } |
| 844 | + if (!mappedNames.contains(originalName)) { |
| 845 | + // If the original name is not in the mapping, it means it's an unmapped pack. |
| 846 | + qDebug() << "Unmapped pack found:" << originalName; |
| 847 | + QMessageBox::warning(this, tr("Unmapped Pack Warning"), |
| 848 | + tr("The pack '%1' is not mapped in the CSV file. " |
| 849 | + "This could mean the packs data is out of date or another error is occuring. " |
| 850 | + "If you have not recieved a Network Error please report the issue.").arg(originalName)); |
| 851 | + } |
| 852 | + } |
| 853 | + // Automatically call the sorting function to group items into categories. |
| 854 | + sortPacksListByCategory(); |
| 855 | +} |
| 856 | + |
745 | 857 | void MainWindow::sortPacksListByCategory() |
746 | 858 | { |
747 | 859 | // Define alias-to-description mappings |
@@ -825,7 +937,7 @@ void MainWindow::on_browseGameButton_clicked() { |
825 | 937 | ui->gameLineEdit->setText(gameDir); |
826 | 938 |
|
827 | 939 | // Populate the file list using the full path (Sims 4 Root + Subdirectory). |
828 | | - populatePacksListWidget(gameDir,csvFilePath); |
| 940 | + loadPacksCsv(csvCloudPath, csvFilePath); |
829 | 941 |
|
830 | 942 | // Use QSettings to store the full path including the subdirectory. |
831 | 943 | QSettings settings("Falcon", "SimsSwitcher"); |
|
0 commit comments