Skip to content

Commit 1a82011

Browse files
committed
Allow viewing basic properties of unknown files
Of course, we can only show you the raw hex and other basic information but exploring this can now be done in a useful fashion.
1 parent c95e492 commit 1a82011

File tree

8 files changed

+80
-46
lines changed

8 files changed

+80
-46
lines changed

apps/sagasu/include/filetreemodel.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#pragma once
55

66
#include "hashdatabase.h"
7+
#include "physis.hpp"
8+
79
#include <QAbstractItemModel>
810
#include <QFutureWatcher>
911

@@ -17,6 +19,8 @@ struct TreeInformation {
1719
QString name;
1820
int row = 0;
1921
uint32_t hash = 0;
22+
Hash originalHash;
23+
QString indexPath;
2024

2125
std::vector<TreeInformation *> children;
2226
};
@@ -30,8 +34,10 @@ class FileTreeModel : public QAbstractItemModel
3034

3135
enum CustomRoles {
3236
PathRole = Qt::UserRole,
33-
IsUnknown,
34-
IsFolder,
37+
IsUnknownRole,
38+
IsFolderRole,
39+
HashRole,
40+
IndexPathRole,
3541
};
3642

3743
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@@ -48,7 +54,7 @@ class FileTreeModel : public QAbstractItemModel
4854
TreeInformation *rootItem = nullptr;
4955

5056
void addKnownFolder(const QString &string);
51-
void addFile(TreeInformation *parentItem, uint32_t filenameHash, const QString &name);
57+
void addFile(TreeInformation *parentItem, uint32_t filenameHash, const QString &name, Hash originalHash, const QString &indexPath);
5258
void addFolder(TreeInformation *parentItem, uint32_t filenameHash);
5359

5460
QHash<uint32_t, TreeInformation *> knownDirHashes;

apps/sagasu/include/filetreewindow.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class FileTreeWindow : public QWidget
2121

2222
Q_SIGNALS:
2323
void extractFile(const QString &path);
24-
void pathSelected(const QString &path);
24+
void pathSelected(const QString &indexPath, Hash hash, const QString &path);
2525

2626
private:
2727
SqPackResource *data = nullptr;

apps/sagasu/include/mainwindow.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,5 @@ class MainWindow : public KXmlGuiWindow
3535
QLabel *m_hashLabel = nullptr;
3636
QLabel *m_fileTypeLabel = nullptr;
3737

38-
void refreshParts(const QString &path);
38+
void refreshParts(const QString &indexPath, Hash hash, const QString &path);
3939
};

apps/sagasu/src/filepropertieswindow.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,20 @@ FilePropertiesWindow::FilePropertiesWindow(const QString &path, physis_Buffer bu
1818
auto layout = new QFormLayout();
1919
setLayout(layout);
2020

21-
auto pathLabel = new QLabel(path);
22-
layout->addRow(i18nc("@label", "Path:"), pathLabel);
21+
if (!path.isEmpty()) {
22+
auto pathLabel = new QLabel(path);
23+
layout->addRow(i18nc("@label", "Path:"), pathLabel);
2324

24-
QFileInfo info(path);
25+
QFileInfo info(path);
2526

26-
const FileType type = FileTypes::getFileType(info.completeSuffix());
27+
const FileType type = FileTypes::getFileType(info.completeSuffix());
2728

28-
auto typeLabel = new QLabel(FileTypes::getFiletypeName(type));
29-
layout->addRow(i18nc("@label", "Type:"), typeLabel);
29+
auto typeLabel = new QLabel(FileTypes::getFiletypeName(type));
30+
layout->addRow(i18nc("@label", "Type:"), typeLabel);
31+
}
3032

3133
auto sizeLabel = new QLabel(QString::number(buffer.size));
3234
layout->addRow(i18nc("@label", "Size (in bytes):"), sizeLabel);
3335
}
3436

35-
#include "moc_filepropertieswindow.cpp"
37+
#include "moc_filepropertieswindow.cpp"

apps/sagasu/src/filetreemodel.cpp

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <QIcon>
1010
#include <QtConcurrent>
1111

12+
Q_DECLARE_METATYPE(Hash)
13+
1214
FileTreeModel::FileTreeModel(HashDatabase &database, bool showUnknown, const QString &gamePath, SqPackResource *data, QObject *parent)
1315
: QAbstractItemModel(parent)
1416
, gameData(data)
@@ -28,16 +30,26 @@ FileTreeModel::FileTreeModel(HashDatabase &database, bool showUnknown, const QSt
2830
QFileInfo info = it.fileInfo();
2931
if (info.exists() && (info.completeSuffix() == QStringLiteral("win32.index"))) {
3032
std::string pathStd = info.filePath().toStdString();
31-
auto indexEntries = physis_index_parse(pathStd.c_str());
32-
for (uint32_t i = 0; i < indexEntries.num_entries; i++) {
33-
if (knownDirHashes.contains(indexEntries.dir_entries[i])) {
34-
QString name;
35-
if (m_database.knowsFile(indexEntries.filename_entries[i])) {
36-
name = m_database.getFilename(indexEntries.filename_entries[i]);
33+
const auto indexEntries = physis_index_parse(pathStd.c_str());
34+
for (uint32_t i = 0; i < indexEntries.num_hashes; i++) {
35+
const auto hash = indexEntries.hashes[i];
36+
switch (hash.tag) {
37+
case Hash::Tag::SplitPath: {
38+
const auto completeHash =
39+
static_cast<uint32_t>(static_cast<uint64_t>(hash.split_path.path) << 32 | static_cast<uint64_t>(hash.split_path.name));
40+
if (knownDirHashes.contains(hash.split_path.path)) {
41+
QString name;
42+
if (m_database.knowsFile(completeHash)) {
43+
name = m_database.getFilename(completeHash);
44+
}
45+
addFile(knownDirHashes[hash.split_path.path], completeHash, name, hash, info.filePath());
46+
} else {
47+
addFolder(rootItem, hash.split_path.path);
3748
}
38-
addFile(knownDirHashes[indexEntries.dir_entries[i]], indexEntries.filename_entries[i], name);
39-
} else {
40-
addFolder(rootItem, indexEntries.dir_entries[i]);
49+
} break;
50+
case Hash::Tag::FullPath:
51+
Q_UNREACHABLE(); // shouldn't be in basic index files
52+
break;
4153
}
4254
}
4355
}
@@ -122,10 +134,14 @@ QVariant FileTreeModel::data(const QModelIndex &index, int role) const
122134
}
123135

124136
return path;
125-
} else if (role == IsUnknown) {
137+
} else if (role == IsUnknownRole) {
126138
return item->name.isEmpty(); // unknown files/folders have no name (obviously, we don't know what its named!)
127-
} else if (role == IsFolder) {
139+
} else if (role == IsFolderRole) {
128140
return item->type == TreeType::Folder;
141+
} else if (role == HashRole) {
142+
return QVariant::fromValue(item->originalHash);
143+
} else if (role == IndexPathRole) {
144+
return item->indexPath;
129145
} else if (role == Qt::DisplayRole) {
130146
if (item->type == TreeType::Folder) {
131147
if (item->name.isEmpty()) {
@@ -194,7 +210,7 @@ void FileTreeModel::addKnownFolder(const QString &string)
194210
}
195211
}
196212

197-
void FileTreeModel::addFile(TreeInformation *parentItem, uint32_t name, const QString &realName)
213+
void FileTreeModel::addFile(TreeInformation *parentItem, uint32_t name, const QString &realName, Hash originalHash, const QString &indexPath)
198214
{
199215
if (realName.isEmpty() && !m_showUnknown) {
200216
return;
@@ -206,6 +222,8 @@ void FileTreeModel::addFile(TreeInformation *parentItem, uint32_t name, const QS
206222
fileItem->type = TreeType::File;
207223
fileItem->parent = parentItem;
208224
fileItem->row = parentItem->children.size();
225+
fileItem->originalHash = originalHash;
226+
fileItem->indexPath = indexPath;
209227

210228
parentItem->children.push_back(fileItem);
211229
}

apps/sagasu/src/filetreewindow.cpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,6 @@ FileTreeWindow::FileTreeWindow(HashDatabase &database, const QString &gamePath,
4646
});
4747
layout->addWidget(searchEdit);
4848

49-
// TODO Restore as an action, later. it's currently pretty useless as-is as it's a "please slow down and crash" checkbox
50-
/*m_unknownCheckbox = new QCheckBox();
51-
m_unknownCheckbox->setToolTip(QStringLiteral("Show unknown files and folders."));
52-
connect(m_unknownCheckbox, &QCheckBox::clicked, this, [this] {
53-
refreshModel();
54-
});
55-
searchLayout->addWidget(m_unknownCheckbox);*/
56-
5749
auto treeWidget = new QTreeView();
5850
treeWidget->setModel(m_searchModel);
5951
layout->addWidget(treeWidget);
@@ -64,8 +56,8 @@ FileTreeWindow::FileTreeWindow(HashDatabase &database, const QString &gamePath,
6456

6557
if (index.isValid()) {
6658
const auto path = m_searchModel->data(index, FileTreeModel::CustomRoles::PathRole).toString();
67-
const auto isUnknown = m_searchModel->data(index, FileTreeModel::CustomRoles::IsUnknown).toBool();
68-
const auto isFolder = m_searchModel->data(index, FileTreeModel::CustomRoles::IsFolder).toBool();
59+
const auto isUnknown = m_searchModel->data(index, FileTreeModel::CustomRoles::IsUnknownRole).toBool();
60+
const auto isFolder = m_searchModel->data(index, FileTreeModel::CustomRoles::IsFolderRole).toBool();
6961

7062
auto menu = new QMenu();
7163

@@ -94,8 +86,16 @@ FileTreeWindow::FileTreeWindow(HashDatabase &database, const QString &gamePath,
9486

9587
connect(treeWidget, &QTreeView::clicked, [this, treeWidget](const QModelIndex &item) {
9688
if (item.isValid()) {
97-
auto path = m_searchModel->data(item, FileTreeModel::CustomRoles::PathRole).toString();
98-
Q_EMIT pathSelected(path);
89+
const auto isFolder = m_searchModel->data(item, FileTreeModel::CustomRoles::IsFolderRole).toBool();
90+
if (isFolder) {
91+
return;
92+
}
93+
94+
const auto path = m_searchModel->data(item, FileTreeModel::CustomRoles::PathRole).toString();
95+
const auto indexPath = m_searchModel->data(item, FileTreeModel::CustomRoles::IndexPathRole).toString();
96+
const auto hash = m_searchModel->data(item, FileTreeModel::CustomRoles::HashRole).value<Hash>();
97+
98+
Q_EMIT pathSelected(indexPath, hash, path);
9999
}
100100
});
101101

apps/sagasu/src/mainwindow.cpp

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ MainWindow::MainWindow(const QString &gamePath, SqPackResource *data)
7878
file.write(reinterpret_cast<const char *>(fileData.data), fileData.size);
7979
}
8080
});
81-
connect(m_tree, &FileTreeWindow::pathSelected, this, [this](const QString &path) {
82-
refreshParts(path);
81+
connect(m_tree, &FileTreeWindow::pathSelected, this, [this](const QString &indexPath, Hash hash, const QString &path) {
82+
refreshParts(indexPath, hash, path);
8383
});
8484
m_tree->setMaximumWidth(300);
8585
dummyWidget->addWidget(m_tree);
@@ -89,7 +89,7 @@ MainWindow::MainWindow(const QString &gamePath, SqPackResource *data)
8989
partHolder->setMinimumHeight(720);
9090
dummyWidget->addWidget(partHolder);
9191

92-
refreshParts({});
92+
refreshParts({}, {}, {});
9393

9494
setupActions();
9595
setupGUI(Keys | Save | Create, QStringLiteral("dataexplorer.rc"));
@@ -100,27 +100,35 @@ MainWindow::MainWindow(const QString &gamePath, SqPackResource *data)
100100
actionCollection()->removeAction(actionCollection()->action(KStandardAction::name(KStandardAction::AboutKDE)));
101101
}
102102

103-
void MainWindow::refreshParts(const QString &path)
103+
void MainWindow::refreshParts(const QString &indexPath, Hash hash, const QString &path)
104104
{
105105
partHolder->clear();
106106

107107
std::string pathStd = path.toStdString();
108-
if (path.isEmpty() || !physis_gamedata_exists(data, pathStd.c_str())) {
108+
if (indexPath.isEmpty()) {
109109
return;
110110
}
111111

112112
QFileInfo info(path);
113113
std::string filenameStd = info.fileName().toStdString();
114114

115-
// FIXME: add back once hashes are f igured out
115+
// FIXME: add back once hashes are figured out
116116
// auto crcHash = physis_calculate_hash(filenameStd.c_str());
117117
// m_hashLabel->setText(i18n("Hash: 0x%1", QString::number(crcHash, 16).toUpper().rightJustified(8, QLatin1Char('0'))));
118118

119119
// FIXME: this is terrible, we should not be recalculating this. it isn't a huge deal with the file + index caching, but still
120-
auto datOffset = physis_gamedata_find_offset(data, pathStd.c_str());
121-
m_offsetLabel->setText(i18n("Offset: 0x%1", QString::number(datOffset, 16).toUpper().rightJustified(8, QLatin1Char('0'))));
120+
// TODO: support for unknown files
121+
if (!path.isEmpty()) {
122+
auto datOffset = physis_gamedata_find_offset(data, pathStd.c_str());
123+
m_offsetLabel->setText(i18n("Offset: 0x%1", QString::number(datOffset, 16).toUpper().rightJustified(8, QLatin1Char('0'))));
124+
} else {
125+
m_offsetLabel->setText(i18n("Offset: Unknown"));
126+
}
122127

123-
auto file = physis_gamedata_extract_file(data, path.toStdString().c_str());
128+
auto file = physis_gamedata_extract_file_from_hash(data, indexPath.toStdString().c_str(), hash);
129+
if (file.size == 0) {
130+
return;
131+
}
124132

125133
const FileType type = FileTypes::getFileType(info.completeSuffix());
126134

0 commit comments

Comments
 (0)