mirror of
https://github.com/rwengine/openrw.git
synced 2024-10-06 09:07:19 +02:00
rwviewer: view stored texts + fonts
This commit is contained in:
parent
ee455bb157
commit
6d4b69b742
@ -68,9 +68,9 @@ RWGame::RWGame(Logger& log, int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set up text renderer
|
// Set up text renderer
|
||||||
renderer.text.setFontTexture(0, "pager");
|
renderer.text.setFontTexture(FONT_PAGER, "pager");
|
||||||
renderer.text.setFontTexture(1, "font1");
|
renderer.text.setFontTexture(FONT_PRICEDOWN, "font1");
|
||||||
renderer.text.setFontTexture(2, "font2");
|
renderer.text.setFontTexture(FONT_ARIAL, "font2");
|
||||||
|
|
||||||
debug.setDebugMode(btIDebugDraw::DBG_DrawWireframe |
|
debug.setDebugMode(btIDebugDraw::DBG_DrawWireframe |
|
||||||
btIDebugDraw::DBG_DrawConstraints |
|
btIDebugDraw::DBG_DrawConstraints |
|
||||||
|
@ -6,23 +6,43 @@ find_package(Qt5Widgets REQUIRED)
|
|||||||
|
|
||||||
add_executable(rwviewer WIN32
|
add_executable(rwviewer WIN32
|
||||||
main.cpp
|
main.cpp
|
||||||
|
OpenGLCompat.h
|
||||||
|
|
||||||
|
ViewerWindow.hpp
|
||||||
ViewerWindow.cpp
|
ViewerWindow.cpp
|
||||||
|
|
||||||
|
models/ObjectListModel.hpp
|
||||||
models/ObjectListModel.cpp
|
models/ObjectListModel.cpp
|
||||||
|
models/DFFFramesTreeModel.hpp
|
||||||
models/DFFFramesTreeModel.cpp
|
models/DFFFramesTreeModel.cpp
|
||||||
|
models/TextModel.hpp
|
||||||
|
models/TextModel.cpp
|
||||||
|
|
||||||
views/ViewerInterface.hpp
|
views/ViewerInterface.hpp
|
||||||
|
views/ObjectViewer.hpp
|
||||||
views/ObjectViewer.cpp
|
views/ObjectViewer.cpp
|
||||||
|
views/ModelViewer.hpp
|
||||||
views/ModelViewer.cpp
|
views/ModelViewer.cpp
|
||||||
|
views/TextViewer.hpp
|
||||||
|
views/TextViewer.cpp
|
||||||
|
views/WorldViewer.hpp
|
||||||
views/WorldViewer.cpp
|
views/WorldViewer.cpp
|
||||||
|
views/ViewerInterface.hpp
|
||||||
views/ViewerInterface.cpp
|
views/ViewerInterface.cpp
|
||||||
|
|
||||||
ViewerWidget.cpp
|
ViewerWidget.cpp
|
||||||
|
ViewerWidget.hpp
|
||||||
|
ItemListModel.hpp
|
||||||
ItemListModel.cpp
|
ItemListModel.cpp
|
||||||
|
ItemListWidget.hpp
|
||||||
ItemListWidget.cpp
|
ItemListWidget.cpp
|
||||||
|
IMGArchiveModel.hpp
|
||||||
IMGArchiveModel.cpp
|
IMGArchiveModel.cpp
|
||||||
|
widgets/ModelFramesWidget.hpp
|
||||||
widgets/ModelFramesWidget.cpp
|
widgets/ModelFramesWidget.cpp
|
||||||
|
AnimationListModel.hpp
|
||||||
AnimationListModel.cpp
|
AnimationListModel.cpp
|
||||||
|
AnimationListWidget.hpp
|
||||||
AnimationListWidget.cpp
|
AnimationListWidget.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2,12 +2,15 @@
|
|||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <engine/Animator.hpp>
|
#include <engine/Animator.hpp>
|
||||||
|
#include <engine/GameData.hpp>
|
||||||
|
#include <engine/GameWorld.hpp>
|
||||||
#include <glm/gtc/type_ptr.hpp>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
#include <objects/CharacterObject.hpp>
|
#include <objects/CharacterObject.hpp>
|
||||||
#include <objects/InstanceObject.hpp>
|
#include <objects/InstanceObject.hpp>
|
||||||
#include <objects/VehicleObject.hpp>
|
#include <objects/VehicleObject.hpp>
|
||||||
#include <render/GameRenderer.hpp>
|
#include <render/GameRenderer.hpp>
|
||||||
#include <render/ObjectRenderer.hpp>
|
#include <render/ObjectRenderer.hpp>
|
||||||
|
#include <render/TextRenderer.hpp>
|
||||||
|
|
||||||
constexpr float kViewFov = glm::radians(90.0f);
|
constexpr float kViewFov = glm::radians(90.0f);
|
||||||
|
|
||||||
@ -32,6 +35,7 @@ ViewCamera OrbitCamera (const glm::vec2& viewPort, const glm::vec2& viewAngles,
|
|||||||
ViewerWidget::ViewerWidget(QOpenGLContext* context, QWindow* parent)
|
ViewerWidget::ViewerWidget(QOpenGLContext* context, QWindow* parent)
|
||||||
: QWindow(parent)
|
: QWindow(parent)
|
||||||
, context(context)
|
, context(context)
|
||||||
|
, textInfos()
|
||||||
, selectedFrame(nullptr)
|
, selectedFrame(nullptr)
|
||||||
, viewDistance(1.f)
|
, viewDistance(1.f)
|
||||||
, dragging(false)
|
, dragging(false)
|
||||||
@ -127,6 +131,13 @@ void ViewerWidget::drawWorld(GameRenderer& r) {
|
|||||||
r.renderWorld(world(), vc, 0.f);
|
r.renderWorld(world(), vc, 0.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ViewerWidget::drawText(GameRenderer& r) {
|
||||||
|
for(auto &textInfo : textInfos) {
|
||||||
|
_renderer->text.renderText(textInfo, false);
|
||||||
|
}
|
||||||
|
r.renderPostProcess();
|
||||||
|
}
|
||||||
|
|
||||||
void ViewerWidget::paintGL() {
|
void ViewerWidget::paintGL() {
|
||||||
glViewport(0, 0, width() * devicePixelRatio(), height() * devicePixelRatio());
|
glViewport(0, 0, width() * devicePixelRatio(), height() * devicePixelRatio());
|
||||||
glClearColor(0.3f, 0.3f, 0.3f, 1.f);
|
glClearColor(0.3f, 0.3f, 0.3f, 1.f);
|
||||||
@ -141,7 +152,6 @@ void ViewerWidget::paintGL() {
|
|||||||
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
|
||||||
r.getRenderer()->invalidate();
|
r.getRenderer()->invalidate();
|
||||||
r.setupRender();
|
r.setupRender();
|
||||||
|
|
||||||
@ -155,6 +165,9 @@ void ViewerWidget::paintGL() {
|
|||||||
case Mode::World:
|
case Mode::World:
|
||||||
drawWorld(r);
|
drawWorld(r);
|
||||||
break;
|
break;
|
||||||
|
case Mode::Text:
|
||||||
|
drawText(r);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,9 +241,18 @@ void ViewerWidget::showObject(quint16 item) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ViewerWidget::clearText() {
|
||||||
|
textInfos.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewerWidget::showText(const TextRenderer::TextInfo &ti) {
|
||||||
|
textInfos.push_back(ti);
|
||||||
|
}
|
||||||
|
|
||||||
void ViewerWidget::showModel(ClumpPtr model) {
|
void ViewerWidget::showModel(ClumpPtr model) {
|
||||||
_viewMode = Mode::Model;
|
_viewMode = Mode::Model;
|
||||||
_model = model;
|
_model = model;
|
||||||
|
textInfos.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewerWidget::selectFrame(ModelFrame* frame) {
|
void ViewerWidget::selectFrame(ModelFrame* frame) {
|
||||||
@ -248,7 +270,7 @@ void ViewerWidget::exportModel() {
|
|||||||
if( it != world()->objectTypes.end() ) {
|
if( it != world()->objectTypes.end() ) {
|
||||||
for( auto& archive : world()->data.archives ) {
|
for( auto& archive : world()->data.archives ) {
|
||||||
for(size_t i = 0; i < archive.second.getAssetCount(); ++i) {
|
for(size_t i = 0; i < archive.second.getAssetCount(); ++i) {
|
||||||
auto& assetI = archive.second.getAssetInfoByIndex(i);
|
auto& assetI = archive.second.getAssetInfoByIndex(i);;
|
||||||
std::string q(assetI.name);
|
std::string q(assetI.name);
|
||||||
std::transform(q.begin(), q.end(), q.begin(), ::tolower);
|
std::transform(q.begin(), q.end(), q.begin(), ::tolower);
|
||||||
if( q.find(it->second->modelName) != q.npos ) {
|
if( q.find(it->second->modelName) != q.npos ) {
|
||||||
|
@ -6,8 +6,9 @@
|
|||||||
#include <engine/GameWorld.hpp>
|
#include <engine/GameWorld.hpp>
|
||||||
#include <gl/DrawBuffer.hpp>
|
#include <gl/DrawBuffer.hpp>
|
||||||
#include <gl/GeometryBuffer.hpp>
|
#include <gl/GeometryBuffer.hpp>
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <loaders/LoaderIFP.hpp>
|
#include <loaders/LoaderIFP.hpp>
|
||||||
|
#include <render/TextRenderer.hpp>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
// Prevent Qt from conflicting with glLoadGen on macOS
|
// Prevent Qt from conflicting with glLoadGen on macOS
|
||||||
#include "OpenGLCompat.h"
|
#include "OpenGLCompat.h"
|
||||||
@ -16,6 +17,7 @@
|
|||||||
|
|
||||||
class GameRenderer;
|
class GameRenderer;
|
||||||
class Clump;
|
class Clump;
|
||||||
|
|
||||||
class ViewerWidget : public QWindow {
|
class ViewerWidget : public QWindow {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
@ -26,6 +28,8 @@ public:
|
|||||||
Model,
|
Model,
|
||||||
//! View loaded instances, \see showWorld();
|
//! View loaded instances, \see showWorld();
|
||||||
World,
|
World,
|
||||||
|
//! View text strings, \see showText
|
||||||
|
Text,
|
||||||
};
|
};
|
||||||
|
|
||||||
ViewerWidget(QOpenGLContext* context, QWindow* parent);
|
ViewerWidget(QOpenGLContext* context, QWindow* parent);
|
||||||
@ -54,6 +58,8 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void showObject(quint16 item);
|
void showObject(quint16 item);
|
||||||
void showModel(ClumpPtr model);
|
void showModel(ClumpPtr model);
|
||||||
|
void clearText();
|
||||||
|
void showText(const TextRenderer::TextInfo &ti);
|
||||||
void selectFrame(ModelFrame* frame);
|
void selectFrame(ModelFrame* frame);
|
||||||
void exportModel();
|
void exportModel();
|
||||||
|
|
||||||
@ -78,6 +84,7 @@ protected:
|
|||||||
GameWorld* _world = nullptr;
|
GameWorld* _world = nullptr;
|
||||||
GameRenderer* _renderer = nullptr;
|
GameRenderer* _renderer = nullptr;
|
||||||
|
|
||||||
|
std::vector<TextRenderer::TextInfo> textInfos;
|
||||||
ClumpPtr _model;
|
ClumpPtr _model;
|
||||||
ModelFrame* selectedFrame = nullptr;
|
ModelFrame* selectedFrame = nullptr;
|
||||||
GameObject* _object = nullptr;
|
GameObject* _object = nullptr;
|
||||||
@ -103,6 +110,7 @@ protected:
|
|||||||
void drawModel(GameRenderer& r, ClumpPtr& model);
|
void drawModel(GameRenderer& r, ClumpPtr& model);
|
||||||
void drawObject(GameRenderer& r, GameObject* object);
|
void drawObject(GameRenderer& r, GameObject* object);
|
||||||
void drawWorld(GameRenderer& r);
|
void drawWorld(GameRenderer& r);
|
||||||
|
void drawText(GameRenderer& r);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "views/ModelViewer.hpp"
|
#include "views/ModelViewer.hpp"
|
||||||
#include "views/ObjectViewer.hpp"
|
#include "views/ObjectViewer.hpp"
|
||||||
#include "views/WorldViewer.hpp"
|
#include "views/WorldViewer.hpp"
|
||||||
|
#include "views/TextViewer.hpp"
|
||||||
|
|
||||||
#include <engine/GameState.hpp>
|
#include <engine/GameState.hpp>
|
||||||
#include <engine/GameWorld.hpp>
|
#include <engine/GameWorld.hpp>
|
||||||
@ -42,14 +43,17 @@ void ViewerWindow::createMenus() {
|
|||||||
for (int i = 0; i < MaxRecentGames; ++i) {
|
for (int i = 0; i < MaxRecentGames; ++i) {
|
||||||
QAction* r = file->addAction("");
|
QAction* r = file->addAction("");
|
||||||
recentGames.append(r);
|
recentGames.append(r);
|
||||||
connect(r, SIGNAL(triggered()), SLOT(openRecent()));
|
connect(r, &QAction::triggered, this, [r, this]() {
|
||||||
|
QString recentGame = r->data().toString();
|
||||||
|
loadGame(recentGame);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
recentSep = file->addSeparator();
|
recentSep = file->addSeparator();
|
||||||
auto ex = file->addAction("E&xit");
|
auto ex = file->addAction("E&xit");
|
||||||
ex->setShortcut(QKeySequence::Quit);
|
ex->setShortcut(QKeySequence::Quit);
|
||||||
connect(ex, SIGNAL(triggered()), QApplication::instance(),
|
connect(ex, &QAction::triggered,
|
||||||
SLOT(closeAllWindows()));
|
QApplication::instance(), &QApplication::closeAllWindows);
|
||||||
|
|
||||||
mb->addMenu("&Data");
|
mb->addMenu("&Data");
|
||||||
|
|
||||||
@ -94,6 +98,10 @@ void ViewerWindow::createDefaultViews() {
|
|||||||
views->addTab(worldView, "World");
|
views->addTab(worldView, "World");
|
||||||
connect(this, &ViewerWindow::gameLoaded, worldView, &WorldViewer::showData);
|
connect(this, &ViewerWindow::gameLoaded, worldView, &WorldViewer::showData);
|
||||||
|
|
||||||
|
auto textView = new TextViewer(this);
|
||||||
|
views->addTab(textView, "Texts");
|
||||||
|
connect(this, &ViewerWindow::gameLoaded, textView, &TextViewer::showData);
|
||||||
|
|
||||||
setCentralWidget(views);
|
setCentralWidget(views);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,6 +151,10 @@ void ViewerWindow::loadGame(const QString& path) {
|
|||||||
|
|
||||||
gameWorld->data->load();
|
gameWorld->data->load();
|
||||||
|
|
||||||
|
renderer->text.setFontTexture(FONT_PAGER, "pager");
|
||||||
|
renderer->text.setFontTexture(FONT_PRICEDOWN, "font1");
|
||||||
|
renderer->text.setFontTexture(FONT_ARIAL, "font2");
|
||||||
|
|
||||||
gameLoaded(gameWorld.get(), renderer.get());
|
gameLoaded(gameWorld.get(), renderer.get());
|
||||||
|
|
||||||
QSettings settings("OpenRW", "rwviewer");
|
QSettings settings("OpenRW", "rwviewer");
|
||||||
@ -155,13 +167,6 @@ void ViewerWindow::loadGame(const QString& path) {
|
|||||||
updateRecentGames();
|
updateRecentGames();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewerWindow::openRecent() {
|
|
||||||
QAction* r = qobject_cast<QAction*>(sender());
|
|
||||||
if (r) {
|
|
||||||
loadGame(r->data().toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ViewerWindow::showObjectModel(uint16_t) {
|
void ViewerWindow::showObjectModel(uint16_t) {
|
||||||
#pragma message("implement me")
|
#pragma message("implement me")
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,6 @@ signals:
|
|||||||
void gameLoaded(GameWorld*, GameRenderer*);
|
void gameLoaded(GameWorld*, GameRenderer*);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void openRecent();
|
|
||||||
void showObjectModel(uint16_t object);
|
void showObjectModel(uint16_t object);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
76
rwviewer/models/TextModel.cpp
Normal file
76
rwviewer/models/TextModel.cpp
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#include "TextModel.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <QBrush>
|
||||||
|
|
||||||
|
TextModel::TextModel(QObject *parent)
|
||||||
|
: QAbstractTableModel(parent), m_font(FONT_PAGER) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextModel::setData(const TextMapType &textMap) {
|
||||||
|
beginResetModel();
|
||||||
|
m_textMap = textMap;
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
int TextModel::rowCount(const QModelIndex &) const {
|
||||||
|
return m_textMap.keys.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int TextModel::columnCount(const QModelIndex &) const {
|
||||||
|
return m_textMap.languages.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const GameString &TextModel::lookupIndex(const QModelIndex &index) const {
|
||||||
|
const auto &language = m_textMap.languages.at(index.column());
|
||||||
|
const auto &key = m_textMap.keys.at(index.row());
|
||||||
|
return m_textMap.map_lang_key_tran.at(language).at(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant TextModel::data(const QModelIndex &index, int role) const {
|
||||||
|
switch (role) {
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
try {
|
||||||
|
const auto &gameText = this->lookupIndex(index);
|
||||||
|
auto gameString = GameStringUtil::toString(gameText, m_font);
|
||||||
|
return QString::fromStdString(gameString);
|
||||||
|
} catch (const std::out_of_range &) {
|
||||||
|
return QVariant::Invalid;
|
||||||
|
} catch (...) {
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
case Qt::BackgroundRole:
|
||||||
|
try {
|
||||||
|
this->lookupIndex(index);
|
||||||
|
return QVariant::Invalid;
|
||||||
|
} catch (const std::out_of_range &) {
|
||||||
|
return QBrush(Qt::red);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return QVariant::Invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant TextModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
||||||
|
switch (role) {
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
switch (orientation) {
|
||||||
|
case Qt::Horizontal:
|
||||||
|
return QString::fromStdString(m_textMap.languages[section]);
|
||||||
|
case Qt::Vertical:
|
||||||
|
return QString::fromStdString(m_textMap.keys[section]);
|
||||||
|
default:
|
||||||
|
return QVariant::Invalid;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return QVariant::Invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextModel::fontChanged(font_t font) {
|
||||||
|
beginResetModel();
|
||||||
|
m_font = font;
|
||||||
|
endResetModel();
|
||||||
|
}
|
35
rwviewer/models/TextModel.hpp
Normal file
35
rwviewer/models/TextModel.hpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef _TEXTMODEL_HPP_
|
||||||
|
#define _TEXTMODEL_HPP_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
|
||||||
|
#include <fonts/GameTexts.hpp>
|
||||||
|
|
||||||
|
struct TextMapType {
|
||||||
|
std::vector<std::string> languages;
|
||||||
|
std::vector<std::string> keys;
|
||||||
|
std::map<std::string, std::map<std::string, GameString>> map_lang_key_tran;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TextModel : public QAbstractTableModel {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
TextModel(QObject *parent = nullptr);
|
||||||
|
void setData(const TextMapType &textMap);
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||||
|
const GameString &lookupIndex(const QModelIndex &index) const;
|
||||||
|
public slots:
|
||||||
|
void fontChanged(font_t font);
|
||||||
|
private:
|
||||||
|
font_t m_font;
|
||||||
|
TextMapType m_textMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
251
rwviewer/views/TextViewer.cpp
Normal file
251
rwviewer/views/TextViewer.cpp
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
#include "TextViewer.hpp"
|
||||||
|
|
||||||
|
#include <render/TextRenderer.hpp>
|
||||||
|
#include <rw/filesystem.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <fonts/GameTexts.hpp>
|
||||||
|
#include <loaders/LoaderGXT.hpp>
|
||||||
|
#include <models/TextModel.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <render/GameRenderer.hpp>
|
||||||
|
|
||||||
|
#include <QGroupBox>
|
||||||
|
#include <QFormLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QItemSelection>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QModelIndex>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QRadioButton>
|
||||||
|
#include <QRegExp>
|
||||||
|
#include <QRegExpValidator>
|
||||||
|
#include <QSpinBox>
|
||||||
|
#include <QSplitter>
|
||||||
|
#include <QTableView>
|
||||||
|
#include <QTextEdit>
|
||||||
|
|
||||||
|
void TextTableView::selectionChanged(const QItemSelection &selected, const QItemSelection &) {
|
||||||
|
if (!selected.size())
|
||||||
|
return;
|
||||||
|
auto index = selected.indexes()[0];
|
||||||
|
if (!index.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto *textModel = dynamic_cast<TextModel *>(this->model());
|
||||||
|
if (!textModel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
auto gameString = textModel->lookupIndex(index);
|
||||||
|
emit gameStringChanged(gameString);
|
||||||
|
} catch (std::out_of_range &) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextViewer::TextViewer(QWidget* parent, Qt::WindowFlags f)
|
||||||
|
: ViewerInterface(parent, f) {
|
||||||
|
|
||||||
|
auto dataLayout = new QVBoxLayout;
|
||||||
|
|
||||||
|
auto splitter = new QSplitter;
|
||||||
|
splitter->setChildrenCollapsible(false);
|
||||||
|
splitter->setOrientation(Qt::Horizontal);
|
||||||
|
|
||||||
|
viewerWidget = createViewer();
|
||||||
|
|
||||||
|
textModel = new TextModel;
|
||||||
|
textTable = new TextTableView;
|
||||||
|
textTable->setModel(textModel);
|
||||||
|
textTable->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
|
connect(textTable, &TextTableView::gameStringChanged, this, &TextViewer::onGameStringChange);
|
||||||
|
dataLayout->addWidget(textTable);
|
||||||
|
|
||||||
|
auto propLayout = new QHBoxLayout;
|
||||||
|
|
||||||
|
connect(this, &TextViewer::fontChanged, this, &TextViewer::onFontChange);
|
||||||
|
auto groupBox = new QGroupBox;
|
||||||
|
auto *groupBoxLayout = new QHBoxLayout;
|
||||||
|
auto *radioFont1 = new QRadioButton(tr("Pager"));
|
||||||
|
connect(radioFont1, &QRadioButton::clicked, [this]() {emit fontChanged(FONT_PAGER);});
|
||||||
|
groupBoxLayout->addWidget(radioFont1);
|
||||||
|
auto *radioFont2 = new QRadioButton(tr("Pricedown"));
|
||||||
|
connect(radioFont2, &QRadioButton::clicked, [this]() {emit fontChanged(FONT_PRICEDOWN);});
|
||||||
|
groupBoxLayout->addWidget(radioFont2);
|
||||||
|
auto *radioFont3 = new QRadioButton(tr("Arial"));
|
||||||
|
connect(radioFont3, &QRadioButton::clicked, [this]() {emit fontChanged(FONT_ARIAL);});
|
||||||
|
groupBoxLayout->addWidget(radioFont3);
|
||||||
|
groupBox->setLayout(groupBoxLayout);
|
||||||
|
groupBox->setProperty("border", "2px solid gray");
|
||||||
|
propLayout->addWidget(groupBox);
|
||||||
|
|
||||||
|
radioFont1->click();
|
||||||
|
|
||||||
|
auto textSizeLayout = new QFormLayout;
|
||||||
|
auto textSizeSpinBox = new QSpinBox;
|
||||||
|
textSizeSpinBox->setMinimum(1);
|
||||||
|
textSizeSpinBox->setMaximum(50);
|
||||||
|
connect(textSizeSpinBox, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TextViewer::onFontSizeChange);
|
||||||
|
textSizeSpinBox->setValue(20);
|
||||||
|
textSizeLayout->addRow(tr("Font size"), textSizeSpinBox);
|
||||||
|
propLayout->addLayout(textSizeLayout);
|
||||||
|
|
||||||
|
propLayout->addStretch();
|
||||||
|
dataLayout->addLayout(propLayout);
|
||||||
|
|
||||||
|
hexLineEdit = new QLineEdit;
|
||||||
|
hexLineEdit->setReadOnly(true);
|
||||||
|
hexLineEdit->setValidator(new QRegExpValidator(QRegExp("[0-9A-F]*")));
|
||||||
|
dataLayout->addWidget(hexLineEdit);
|
||||||
|
|
||||||
|
textEdit = new QTextEdit;
|
||||||
|
dataLayout->addWidget(textEdit);
|
||||||
|
|
||||||
|
auto dataWidget = new QWidget;
|
||||||
|
dataWidget->setLayout(dataLayout);
|
||||||
|
splitter->addWidget(dataWidget);
|
||||||
|
|
||||||
|
viewerWidget->setMode(ViewerWidget::Mode::Text);
|
||||||
|
splitter->addWidget(QWidget::createWindowContainer(viewerWidget));
|
||||||
|
|
||||||
|
auto mainLayout = new QHBoxLayout;
|
||||||
|
mainLayout->addWidget(splitter);
|
||||||
|
setLayout(mainLayout);
|
||||||
|
|
||||||
|
connect(textSizeSpinBox, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TextViewer::onFontSizeChange);
|
||||||
|
connect(textEdit, &QTextEdit::textChanged, this, &TextViewer::onStringChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextViewer::onFontChange(font_t font) {
|
||||||
|
currentFont = font;
|
||||||
|
emit textModel->fontChanged(font);
|
||||||
|
updateRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextViewer::onFontSizeChange(int size) {
|
||||||
|
currentFontSize = size;
|
||||||
|
updateRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextViewer::onStringChange() {
|
||||||
|
auto string = textEdit->toPlainText();
|
||||||
|
auto newGameString = GameStringUtil::fromString(string.toStdString(), currentFont);
|
||||||
|
onGameStringChange(newGameString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextViewer::onGameStringChange(const GameString &gameString) {
|
||||||
|
if (!currentGameString.compare(gameString)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentGameString = gameString;
|
||||||
|
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << std::hex;
|
||||||
|
for (auto c : gameString) {
|
||||||
|
oss << std::setw(sizeof(gameString[0])) << std::setfill('0')
|
||||||
|
<< int(c) << " ";
|
||||||
|
}
|
||||||
|
auto newHexText = QString::fromStdString(oss.str());
|
||||||
|
if (hexLineEdit->text().compare(newHexText)) {
|
||||||
|
hexLineEdit->setText(newHexText);
|
||||||
|
}
|
||||||
|
auto newText = QString::fromStdString(GameStringUtil::toString(gameString, currentFont));
|
||||||
|
if (textEdit->toPlainText().compare(newText)) {
|
||||||
|
textEdit->setText(newText);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextViewer::updateRender() {
|
||||||
|
viewerWidget->clearText();
|
||||||
|
const int ROW_STRIDE = currentFontSize * 1.2;
|
||||||
|
const int COL_STRIDE = currentFontSize * 1.2;
|
||||||
|
{
|
||||||
|
TextRenderer::TextInfo ti;
|
||||||
|
ti.font = currentFont;
|
||||||
|
ti.size = currentFontSize;
|
||||||
|
ti.baseColour = glm::u8vec3(255);
|
||||||
|
ti.backgroundColour = glm::u8vec4(0, 0, 0, 0);
|
||||||
|
ti.align = TextRenderer::TextInfo::TextAlignment::Left;
|
||||||
|
ti.wrapX = 0;
|
||||||
|
|
||||||
|
ti.text = currentGameString;
|
||||||
|
ti.screenPosition = glm::vec2(10, 10);
|
||||||
|
viewerWidget->showText(ti);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
TextRenderer::TextInfo ti;
|
||||||
|
ti.font = currentFont;
|
||||||
|
ti.size = currentFontSize;
|
||||||
|
ti.baseColour = glm::u8vec3(255);
|
||||||
|
ti.backgroundColour = glm::u8vec4(0, 0, 0, 0);
|
||||||
|
ti.align = TextRenderer::TextInfo::TextAlignment::Left;
|
||||||
|
ti.wrapX = 0;
|
||||||
|
|
||||||
|
for(GameStringChar c=0x20; c<0xb2; ++c) {
|
||||||
|
unsigned column = c % 0x10;
|
||||||
|
unsigned row = (c / 0x10) - 2 + 3; /* +3 to offset first line*/
|
||||||
|
ti.text = c;
|
||||||
|
ti.screenPosition = glm::vec2(10 + (column * COL_STRIDE), 10 + (row * ROW_STRIDE));
|
||||||
|
viewerWidget->showText(ti);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextViewer::worldChanged() {
|
||||||
|
auto textNames = getFontTextureNames();
|
||||||
|
TextMapType textMap;
|
||||||
|
LoaderGXT loader;
|
||||||
|
std::set<std::string> keys;
|
||||||
|
for(const auto &textName : textNames) {
|
||||||
|
GameTexts texts;
|
||||||
|
auto handle = world()->data->index.openFile(textName);
|
||||||
|
loader.load(texts, handle);
|
||||||
|
const auto &language = textName;
|
||||||
|
textMap.languages.push_back(language);
|
||||||
|
const auto &stringTable = texts.getStringTable();
|
||||||
|
for (const auto &tableItem : stringTable) {
|
||||||
|
keys.insert(tableItem.first);
|
||||||
|
textMap.map_lang_key_tran[language][tableItem.first] = tableItem.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
textMap.keys.resize(keys.size());
|
||||||
|
std::move(keys.begin(), keys.end(), textMap.keys.begin());
|
||||||
|
|
||||||
|
textModel->setData(textMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> TextViewer::getFontTextureNames() {
|
||||||
|
const auto &gameDataPath = rwfs::path(world()->data->getDataPath());
|
||||||
|
rwfs::path textPath;
|
||||||
|
for (const rwfs::path &p : rwfs::directory_iterator(gameDataPath)) {
|
||||||
|
if (!rwfs::is_directory(p)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::string filename = p.filename().string();
|
||||||
|
std::transform(filename.begin(), filename.end(), filename.begin(), ::tolower);
|
||||||
|
if (!filename.compare("text")) {
|
||||||
|
textPath = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!textPath.string().length()) {
|
||||||
|
throw std::runtime_error("text directory not found in gamedata path");
|
||||||
|
}
|
||||||
|
std::vector<std::string> names;
|
||||||
|
for (const rwfs::path &p : rwfs::directory_iterator(textPath)) {
|
||||||
|
// auto langName = p.lexically_relative(gameDataPath).string();
|
||||||
|
auto langName = p.filename().string();
|
||||||
|
std::transform(langName.begin(), langName.end(), langName.begin(), ::tolower);
|
||||||
|
names.push_back(langName);
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
64
rwviewer/views/TextViewer.hpp
Normal file
64
rwviewer/views/TextViewer.hpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#ifndef _TEXTVIEWER_HPP_
|
||||||
|
#define _TEXTVIEWER_HPP_
|
||||||
|
|
||||||
|
#include "ViewerInterface.hpp"
|
||||||
|
|
||||||
|
#include <fonts/GameTexts.hpp>
|
||||||
|
#include <rw/filesystem.hpp>
|
||||||
|
|
||||||
|
#include <QTableView>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class TextModel;
|
||||||
|
class ViewerWidget;
|
||||||
|
|
||||||
|
class QLayout;
|
||||||
|
class QItemSelection;
|
||||||
|
class QLineEdit;
|
||||||
|
class QTextEdit;
|
||||||
|
class QModelIndex;
|
||||||
|
class QString;
|
||||||
|
class QWidget;
|
||||||
|
|
||||||
|
class TextTableView : public QTableView {
|
||||||
|
Q_OBJECT
|
||||||
|
protected slots:
|
||||||
|
virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;
|
||||||
|
signals:
|
||||||
|
void gameStringChanged(const GameString &string);
|
||||||
|
};
|
||||||
|
|
||||||
|
class TextViewer : public ViewerInterface {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
TextModel *textModel;
|
||||||
|
TextTableView *textTable;
|
||||||
|
ViewerWidget *viewerWidget;
|
||||||
|
|
||||||
|
QLineEdit *hexLineEdit;
|
||||||
|
QTextEdit *textEdit;
|
||||||
|
|
||||||
|
virtual void worldChanged() override;
|
||||||
|
|
||||||
|
GameString currentGameString;
|
||||||
|
font_t currentFont;
|
||||||
|
int currentFontSize;
|
||||||
|
|
||||||
|
void updateRender();
|
||||||
|
|
||||||
|
void setGameString(const GameString &gameString);
|
||||||
|
std::vector<std::string> getFontTextureNames();
|
||||||
|
public:
|
||||||
|
TextViewer(QWidget* parent = 0, Qt::WindowFlags f = 0);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void fontChanged(font_t font);
|
||||||
|
private slots:
|
||||||
|
void onGameStringChange(const GameString &gameString);
|
||||||
|
void onStringChange();
|
||||||
|
void onFontChange(size_t font);
|
||||||
|
void onFontSizeChange(int font);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user