2023-08-06 08:48:11 -04:00
|
|
|
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
2023-07-08 09:13:02 -04:00
|
|
|
#include "gearlistmodel.h"
|
|
|
|
|
2024-02-04 15:13:46 -05:00
|
|
|
#include <KLocalizedString>
|
2023-07-09 10:54:27 -04:00
|
|
|
#include <QtConcurrent>
|
2023-07-08 09:13:02 -04:00
|
|
|
#include <magic_enum.hpp>
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
GearListModel::GearListModel(GameData *data, QObject *parent)
|
|
|
|
: QAbstractItemModel(parent)
|
|
|
|
, gameData(data)
|
|
|
|
{
|
2023-07-08 09:13:02 -04:00
|
|
|
// smallclothes body
|
|
|
|
{
|
|
|
|
GearInfo info = {};
|
2023-12-10 12:54:46 -05:00
|
|
|
info.name = "SmallClothes Body";
|
2023-07-08 09:13:02 -04:00
|
|
|
info.slot = Slot::Body;
|
|
|
|
|
|
|
|
gears.push_back(info);
|
|
|
|
}
|
|
|
|
|
|
|
|
// smallclothes legs
|
|
|
|
{
|
|
|
|
GearInfo info = {};
|
2023-12-10 12:54:46 -05:00
|
|
|
info.name = "SmallClothes Legs";
|
2023-07-08 09:13:02 -04:00
|
|
|
info.slot = Slot::Legs;
|
|
|
|
|
|
|
|
gears.push_back(info);
|
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
auto exh = physis_parse_excel_sheet_header(physis_gamedata_extract_file(data, "exd/item.exh"));
|
2023-07-08 09:13:02 -04:00
|
|
|
|
2023-07-09 10:54:27 -04:00
|
|
|
exdFuture = new QFutureWatcher<physis_EXD>(this);
|
|
|
|
connect(exdFuture, &QFutureWatcher<physis_EXD>::resultReadyAt, this, &GearListModel::exdFinished);
|
|
|
|
connect(exdFuture, &QFutureWatcher<physis_EXD>::finished, this, &GearListModel::finished);
|
2023-07-08 09:13:02 -04:00
|
|
|
|
2023-07-09 10:54:27 -04:00
|
|
|
QVector<int> pages;
|
2023-12-09 22:35:59 -05:00
|
|
|
for (uint32_t i = 0; i < exh->page_count; i++) {
|
2023-07-09 10:54:27 -04:00
|
|
|
pages.push_back(i);
|
|
|
|
}
|
2023-07-08 09:13:02 -04:00
|
|
|
|
2023-07-09 10:54:27 -04:00
|
|
|
std::function<physis_EXD(int)> loadEXD = [data, exh](const int page) -> physis_EXD {
|
|
|
|
return physis_gamedata_read_excel_sheet(data, "Item", exh, Language::English, page);
|
|
|
|
};
|
2023-07-08 09:13:02 -04:00
|
|
|
|
2023-07-09 10:54:27 -04:00
|
|
|
exdFuture->setFuture(QtConcurrent::mapped(pages, loadEXD));
|
2023-07-08 09:13:02 -04:00
|
|
|
|
|
|
|
for (auto slotName : magic_enum::enum_names<Slot>()) {
|
2023-09-26 00:37:55 -04:00
|
|
|
slotNames.push_back(QLatin1String(slotName.data()));
|
2023-07-08 09:13:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
rootItem = new TreeInformation();
|
|
|
|
rootItem->type = TreeType::Root;
|
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
int GearListModel::rowCount(const QModelIndex &parent) const
|
|
|
|
{
|
|
|
|
TreeInformation *parentItem;
|
2023-07-08 09:13:02 -04:00
|
|
|
if (parent.column() > 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!parent.isValid())
|
|
|
|
parentItem = rootItem;
|
|
|
|
else
|
2023-10-12 23:44:48 -04:00
|
|
|
parentItem = static_cast<TreeInformation *>(parent.internalPointer());
|
2023-07-08 09:13:02 -04:00
|
|
|
|
|
|
|
return parentItem->children.size();
|
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
int GearListModel::columnCount(const QModelIndex &parent) const
|
|
|
|
{
|
2023-12-09 22:35:59 -05:00
|
|
|
Q_UNUSED(parent)
|
2023-07-08 09:13:02 -04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
QModelIndex GearListModel::index(int row, int column, const QModelIndex &parent) const
|
|
|
|
{
|
2023-07-08 09:13:02 -04:00
|
|
|
if (!hasIndex(row, column, parent))
|
2023-12-09 15:24:54 -05:00
|
|
|
return {};
|
2023-07-08 09:13:02 -04:00
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
TreeInformation *parentItem;
|
2023-07-08 09:13:02 -04:00
|
|
|
|
|
|
|
if (!parent.isValid())
|
|
|
|
parentItem = rootItem;
|
|
|
|
else
|
2023-10-12 23:44:48 -04:00
|
|
|
parentItem = static_cast<TreeInformation *>(parent.internalPointer());
|
2023-07-08 09:13:02 -04:00
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
TreeInformation *childItem = parentItem->children[row];
|
2023-07-08 09:13:02 -04:00
|
|
|
if (childItem)
|
|
|
|
return createIndex(row, column, childItem);
|
2023-12-09 15:24:54 -05:00
|
|
|
return {};
|
2023-07-08 09:13:02 -04:00
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
QModelIndex GearListModel::parent(const QModelIndex &index) const
|
|
|
|
{
|
2023-07-08 09:13:02 -04:00
|
|
|
if (!index.isValid())
|
2023-12-09 15:24:54 -05:00
|
|
|
return {};
|
2023-07-08 09:13:02 -04:00
|
|
|
|
2023-12-09 15:24:54 -05:00
|
|
|
auto childItem = static_cast<TreeInformation *>(index.internalPointer());
|
2023-10-12 23:44:48 -04:00
|
|
|
TreeInformation *parentItem = childItem->parent;
|
2023-07-08 09:13:02 -04:00
|
|
|
|
|
|
|
if (parentItem == rootItem)
|
2023-12-09 15:24:54 -05:00
|
|
|
return {};
|
2023-07-08 09:13:02 -04:00
|
|
|
|
|
|
|
return createIndex(parentItem->row, 0, parentItem);
|
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
QVariant GearListModel::data(const QModelIndex &index, int role) const
|
|
|
|
{
|
2023-07-08 09:13:02 -04:00
|
|
|
if (!index.isValid())
|
|
|
|
return {};
|
|
|
|
|
2023-12-09 15:24:54 -05:00
|
|
|
auto item = static_cast<TreeInformation *>(index.internalPointer());
|
2023-07-08 09:13:02 -04:00
|
|
|
|
|
|
|
if (item->type == TreeType::Category) {
|
2024-04-20 15:11:02 -04:00
|
|
|
if (role == Qt::DisplayRole) {
|
|
|
|
return QLatin1String(magic_enum::enum_name(*item->slotType).data());
|
|
|
|
}
|
2023-07-08 09:13:02 -04:00
|
|
|
} else if (item->type == TreeType::Item) {
|
2024-04-20 15:11:02 -04:00
|
|
|
if (role == Qt::DisplayRole) {
|
|
|
|
return QLatin1String(item->gear->name.data());
|
|
|
|
} else if (role == Qt::DecorationRole) {
|
|
|
|
// TODO: cache these images in memory
|
|
|
|
const QString iconName = QString::number(item->gear->icon);
|
|
|
|
const QString iconBaseNum = QString::number(item->gear->icon).left(2).leftJustified(iconName.length(), QLatin1Char('0'));
|
|
|
|
|
|
|
|
const QString iconFolder = QStringLiteral("ui/icon/%1").arg(iconBaseNum, 6, QLatin1Char('0'));
|
|
|
|
const QString iconFile = QStringLiteral("%1.tex").arg(iconName, 6, QLatin1Char('0'));
|
|
|
|
|
|
|
|
const std::string iconFilename = iconFolder.toStdString() + "/" + iconFile.toStdString();
|
|
|
|
|
|
|
|
auto texFile = physis_gamedata_extract_file(gameData, iconFilename.c_str());
|
|
|
|
if (texFile.data != nullptr) {
|
|
|
|
auto tex = physis_texture_parse(texFile);
|
|
|
|
if (tex.rgba != nullptr) {
|
|
|
|
QImage image(tex.rgba, tex.width, tex.height, QImage::Format_RGBA8888);
|
|
|
|
|
|
|
|
QPixmap pixmap;
|
|
|
|
pixmap.convertFromImage(image);
|
|
|
|
|
|
|
|
return pixmap.scaled(32, 32, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return QIcon::fromTheme(QStringLiteral("unknown"));
|
|
|
|
}
|
2023-07-08 09:13:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
QVariant GearListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
|
|
{
|
2023-07-08 09:13:02 -04:00
|
|
|
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
|
|
|
|
if (section == 0) {
|
2024-02-04 15:13:46 -05:00
|
|
|
return i18nc("@title:column Item name", "Name");
|
2023-07-08 09:13:02 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return QAbstractItemModel::headerData(section, orientation, role);
|
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
std::optional<GearInfo> GearListModel::getGearFromIndex(const QModelIndex &index)
|
|
|
|
{
|
2023-12-09 15:24:54 -05:00
|
|
|
auto item = static_cast<TreeInformation *>(index.internalPointer());
|
2023-07-08 09:13:02 -04:00
|
|
|
if (item->type == TreeType::Item) {
|
|
|
|
return item->gear;
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
void GearListModel::exdFinished(int index)
|
|
|
|
{
|
2023-07-09 10:54:27 -04:00
|
|
|
auto exd = exdFuture->resultAt(index);
|
|
|
|
|
2023-12-09 22:35:59 -05:00
|
|
|
for (unsigned int i = 0; i < exd.row_count; i++) {
|
2023-07-09 10:54:27 -04:00
|
|
|
const auto row = exd.row_data[i];
|
|
|
|
auto primaryModel = row.column_data[47].u_int64._0;
|
2023-12-09 22:35:59 -05:00
|
|
|
// auto secondaryModel = row.column_data[48].u_int64._0;
|
2023-07-09 10:54:27 -04:00
|
|
|
|
|
|
|
int16_t parts[4];
|
|
|
|
memcpy(parts, &primaryModel, sizeof(int16_t) * 4);
|
|
|
|
|
|
|
|
GearInfo info = {};
|
|
|
|
info.name = row.column_data[9].string._0;
|
2024-04-20 15:11:02 -04:00
|
|
|
info.icon = row.column_data[10].u_int16._0;
|
2023-07-09 10:54:27 -04:00
|
|
|
info.slot = physis_slot_from_id(row.column_data[17].u_int8._0);
|
|
|
|
info.modelInfo.primaryID = parts[0];
|
|
|
|
|
|
|
|
gears.push_back(info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
void GearListModel::finished()
|
|
|
|
{
|
2023-07-09 10:54:27 -04:00
|
|
|
beginResetModel();
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
for (auto slot : magic_enum::enum_values<Slot>()) {
|
2023-12-09 15:24:54 -05:00
|
|
|
auto categoryItem = new TreeInformation();
|
2023-07-09 10:54:27 -04:00
|
|
|
categoryItem->type = TreeType::Category;
|
|
|
|
categoryItem->slotType = slot;
|
|
|
|
categoryItem->parent = rootItem;
|
|
|
|
categoryItem->row = i++;
|
|
|
|
rootItem->children.push_back(categoryItem);
|
|
|
|
|
|
|
|
int j = 0;
|
2023-12-09 15:24:54 -05:00
|
|
|
for (const auto &gear : gears) {
|
2023-07-09 10:54:27 -04:00
|
|
|
if (gear.slot == slot) {
|
2023-12-09 15:24:54 -05:00
|
|
|
auto item = new TreeInformation();
|
2023-07-09 10:54:27 -04:00
|
|
|
item->type = TreeType::Item;
|
|
|
|
item->gear = gear;
|
|
|
|
item->parent = categoryItem;
|
|
|
|
item->row = j++;
|
|
|
|
categoryItem->children.push_back(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
endResetModel();
|
|
|
|
}
|
|
|
|
|
2023-07-08 09:13:02 -04:00
|
|
|
#include "moc_gearlistmodel.cpp"
|