You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
434 lines
15 KiB
434 lines
15 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
Additional permissions under GNU GPL version 3 section 7 |
|
|
|
If you modify this Program, or any covered work, by linking or |
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
|
(or a modified version of those libraries), |
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
|
licensors of this Program grant you additional |
|
permission to convey the resulting work. Corresponding Source for a |
|
non-source form of such a combination shall include the source code for |
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
|
work. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
|
|
#include "plResBrowser.h" |
|
#include "ui_ResBrowser.h" |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
# include "plWinRegistryTools.h" |
|
#endif |
|
|
|
#include "pnAllCreatables.h" |
|
#include "plResMgr/plResMgrCreatable.h" |
|
#include "plResMgr/plResManager.h" |
|
#include "plResMgr/plResMgrSettings.h" |
|
#include "plMessage/plResMgrHelperMsg.h" |
|
REGISTER_CREATABLE(plResMgrHelperMsg); |
|
|
|
#include "plResMgr/plRegistryHelpers.h" |
|
#include "plResMgr/plRegistryNode.h" |
|
#include "plResMgr/plPageInfo.h" |
|
#include "pnKeyedObject/plUoid.h" |
|
#include "pnKeyedObject/plKey.h" |
|
#include "pnKeyedObject/plKeyImp.h" |
|
|
|
#include <QApplication> |
|
#include <QDialog> |
|
#include <QMessageBox> |
|
#include <QFileDialog> |
|
#include <QDragEnterEvent> |
|
#include <QMimeData> |
|
#include <functional> |
|
|
|
static void IAboutDialog(QWidget *parent) |
|
{ |
|
QDialog dlg(parent); |
|
QLabel *image = new QLabel(&dlg); |
|
image->setPixmap(QPixmap(":/icon1.ico")); |
|
QLabel *text = new QLabel(QObject::tr(R"(plResBrowser |
|
A simple Plasma 2.0 packfile browsing utility |
|
Copyright (C) 2002 Cyan Worlds, Inc. |
|
|
|
Who needs log files?)"), &dlg); |
|
QPushButton *ok = new QPushButton(QObject::tr("OK"), &dlg); |
|
ok->setDefault(true); |
|
|
|
QHBoxLayout *layout = new QHBoxLayout(&dlg); |
|
layout->setMargin(8); |
|
layout->setSpacing(10); |
|
layout->addWidget(image); |
|
layout->addWidget(text); |
|
layout->addWidget(ok); |
|
|
|
dlg.connect(ok, &QPushButton::clicked, &dlg, &QDialog::accept); |
|
dlg.exec(); |
|
} |
|
|
|
plResBrowser::plResBrowser() |
|
: QMainWindow() |
|
{ |
|
fUI = new Ui_ResBrowser; |
|
fUI->setupUi(this); |
|
|
|
setAcceptDrops(true); |
|
|
|
// Use window background color for read-only text fields |
|
// Note that we don't set them disabled, so you can still copy their contents |
|
QPalette pal = fUI->fAgeName->palette(); |
|
pal.setColor(QPalette::Text, fUI->label->palette().color(QPalette::Text)); |
|
pal.setColor(QPalette::Base, fUI->label->palette().color(QPalette::Window)); |
|
fUI->fAgeName->setPalette(pal); |
|
fUI->fPageName->setPalette(pal); |
|
fUI->fLocation->setPalette(pal); |
|
fUI->fDataVersion->setPalette(pal); |
|
fUI->fChecksum->setPalette(pal); |
|
fUI->fChecksumType->setPalette(pal); |
|
fUI->fObjectName->setPalette(pal); |
|
fUI->fObjectClass->setPalette(pal); |
|
fUI->fStartPos->setPalette(pal); |
|
fUI->fObjectSize->setPalette(pal); |
|
|
|
connect(fUI->fOpenAction, SIGNAL(triggered()), SLOT(OpenFile())); |
|
connect(fUI->fOpenDirectoryAction, SIGNAL(triggered()), SLOT(OpenDirectory())); |
|
connect(fUI->fSaveSelectedAction, SIGNAL(triggered()), SLOT(SaveSelectedObject())); |
|
connect(fUI->fFindAction, SIGNAL(triggered()), fUI->fTreeView, SLOT(FindObject())); |
|
connect(fUI->fFindNextAction, SIGNAL(triggered()), fUI->fTreeView, SLOT(FindNextObject())); |
|
connect(fUI->fShowOnlyLoadableAction, SIGNAL(triggered()), SLOT(RefreshTree())); |
|
connect(fUI->fExitAction, SIGNAL(triggered()), SLOT(close())); |
|
connect(fUI->fAboutAction, &QAction::triggered, std::bind(&IAboutDialog, this)); |
|
|
|
connect(fUI->fTreeView, &QTreeWidget::currentItemChanged, |
|
std::bind(&plResBrowser::UpdateInfoPage, this)); |
|
connect(fUI->fHexValues, SIGNAL(clicked()), SLOT(UpdateInfoPage())); |
|
connect(fUI->fFindButton, SIGNAL(clicked()), fUI->fTreeView, SLOT(FindObject())); |
|
connect(fUI->fFindNextButton, SIGNAL(clicked()), fUI->fTreeView, SLOT(FindNextObject())); |
|
connect(fUI->fSaveButton, SIGNAL(clicked()), SLOT(SaveSelectedObject())); |
|
|
|
RegisterFileTypes(); |
|
plResMgrSettings::Get().SetLoadPagesOnInit(false); |
|
|
|
QStringList args = qApp->arguments(); |
|
if (args.size() > 1) |
|
{ |
|
plFileName path = args[1].toUtf8().constData(); |
|
if (path.GetFileExt() == "prp") |
|
LoadPrpFile(args[1]); |
|
} |
|
} |
|
|
|
plResBrowser::~plResBrowser() |
|
{ |
|
delete fUI; |
|
} |
|
|
|
void plResBrowser::SetWindowTitle(const QString &title) |
|
{ |
|
setWindowTitle(QString("plResBrowser%1").arg(title.isEmpty() ? "" : " - " + title)); |
|
} |
|
|
|
void plResBrowser::dragEnterEvent(QDragEnterEvent *event) |
|
{ |
|
if (event->mimeData()->hasUrls()) |
|
event->acceptProposedAction(); |
|
} |
|
|
|
void plResBrowser::dropEvent(QDropEvent *event) |
|
{ |
|
QList<QUrl> urls = event->mimeData()->urls(); |
|
if (urls.size() == 0) |
|
return; |
|
|
|
fUI->fTreeView->clear(); |
|
hsgResMgr::Reset(); |
|
plResManager *mgr = static_cast<plResManager *>(hsgResMgr::ResMgr()); |
|
|
|
QString path = urls[0].toLocalFile(); |
|
|
|
if (urls.size() == 1 && urls[0].isLocalFile() && path.lastIndexOf('.') == -1) |
|
{ |
|
// Must be a directory |
|
std::vector<plFileName> prpFiles = plFileSystem::ListDir(path.toUtf8().constData(), "*.prp"); |
|
for (auto iter = prpFiles.begin(); iter != prpFiles.end(); ++iter) |
|
mgr->AddSinglePage(*iter); |
|
} |
|
else |
|
{ |
|
for (const QUrl &url : urls) |
|
{ |
|
if (!url.isLocalFile()) |
|
continue; |
|
|
|
plFileName fileName = url.toLocalFile().toUtf8().constData(); |
|
if (fileName.GetFileExt() == "prp") |
|
{ |
|
mgr->AddSinglePage(fileName); |
|
path = fileName.StripFileName().AsString().c_str(); |
|
} |
|
} |
|
} |
|
|
|
fUI->fTreeView->LoadFromRegistry(fUI->fShowOnlyLoadableAction->isChecked()); |
|
SetWindowTitle(path); |
|
} |
|
|
|
void plResBrowser::OpenFile() |
|
{ |
|
QString fileName = QFileDialog::getOpenFileName(this, tr("Choose a file to browse"), |
|
QString(), "Plasma 2 Pack Files (*.prp);;All files (*.*)"); |
|
|
|
if (!fileName.isEmpty()) |
|
LoadPrpFile(fileName); |
|
} |
|
|
|
void plResBrowser::OpenDirectory() |
|
{ |
|
QString path = QFileDialog::getExistingDirectory(this, |
|
tr("Select a Plasma 2 Data Directory:"), |
|
QDir::current().absolutePath(), |
|
QFileDialog::ShowDirsOnly | QFileDialog::ReadOnly); |
|
|
|
if (!path.isEmpty()) |
|
LoadResourcePath(path); |
|
} |
|
|
|
void plResBrowser::SaveSelectedObject() |
|
{ |
|
plResTreeViewItem *item = static_cast<plResTreeViewItem *>(fUI->fTreeView->currentItem()); |
|
plKey itemKey = item ? item->GetKey() : nullptr; |
|
if (!itemKey) |
|
return; |
|
|
|
QString fileName = QFileDialog::getSaveFileName(this, tr("Export object"), |
|
QString("%1.bin").arg(itemKey->GetName().c_str()), |
|
"Binary Files (*.bin);;All files (*.*)"); |
|
|
|
if (!fileName.isEmpty()) |
|
{ |
|
plKeyImp *keyImp = static_cast<plKeyImp *>(itemKey); |
|
|
|
if (keyImp->GetDataLen() <= 0) |
|
return; |
|
|
|
plResManager *resMgr = static_cast<plResManager *>(hsgResMgr::ResMgr()); |
|
plRegistryPageNode *pageNode = resMgr->FindPage(keyImp->GetUoid().GetLocation()); |
|
|
|
hsStream *stream = pageNode->OpenStream(); |
|
if (!stream) |
|
return; |
|
|
|
uint8_t *buffer = new uint8_t[keyImp->GetDataLen()]; |
|
if (buffer) |
|
{ |
|
stream->SetPosition(keyImp->GetStartPos()); |
|
stream->Read(keyImp->GetDataLen(), buffer); |
|
} |
|
|
|
if (!buffer) |
|
return; |
|
|
|
hsUNIXStream outStream; |
|
outStream.Open(fileName.toUtf8().constData(), "wb"); |
|
outStream.Write(keyImp->GetDataLen(), buffer); |
|
outStream.Close(); |
|
|
|
delete[] buffer; |
|
} |
|
} |
|
|
|
void plResBrowser::RefreshTree() |
|
{ |
|
fUI->fTreeView->clear(); |
|
fUI->fTreeView->LoadFromRegistry(fUI->fShowOnlyLoadableAction->isChecked()); |
|
} |
|
|
|
void plResBrowser::UpdateInfoPage() |
|
{ |
|
const bool showAsHex = fUI->fHexValues->isChecked(); |
|
|
|
fUI->fObjectName->setText(""); |
|
fUI->fObjectClass->setText(""); |
|
fUI->fObjectSize->setText(""); |
|
fUI->fStartPos->setText(""); |
|
fUI->fSaveSelectedAction->setEnabled(false); |
|
fUI->fSaveButton->setEnabled(false); |
|
|
|
plResTreeViewItem *item = static_cast<plResTreeViewItem *>(fUI->fTreeView->currentItem()); |
|
if (item) |
|
{ |
|
if (item->GetPage()) |
|
{ |
|
const plPageInfo &pageInfo = item->GetPage()->GetPageInfo(); |
|
fUI->fAgeName->setText(pageInfo.GetAge().c_str()); |
|
fUI->fPageName->setText(pageInfo.GetPage().c_str()); |
|
fUI->fLocation->setText(pageInfo.GetLocation().StringIze().c_str()); |
|
|
|
fUI->fReserved->setChecked((pageInfo.GetLocation().GetFlags() & plLocation::kReserved) != 0); |
|
fUI->fBuiltIn->setChecked((pageInfo.GetLocation().GetFlags() & plLocation::kBuiltIn) != 0); |
|
fUI->fVolatile->setChecked((pageInfo.GetLocation().GetFlags() & plLocation::kVolatile) != 0); |
|
fUI->fLocalOnly->setChecked((pageInfo.GetLocation().GetFlags() & plLocation::kLocalOnly) != 0); |
|
|
|
fUI->fDataVersion->setText(QString::number(pageInfo.GetMajorVersion())); |
|
|
|
if (showAsHex) |
|
fUI->fChecksum->setText(QString("0x%1").arg(pageInfo.GetChecksum(), 0, 16)); |
|
else |
|
fUI->fChecksum->setText(QString::number(pageInfo.GetChecksum())); |
|
|
|
fUI->fChecksumType->setText("Basic (file size)"); |
|
} |
|
|
|
if (item->GetKey()) |
|
{ |
|
plKey key = item->GetKey(); |
|
fUI->fObjectName->setText(key->GetUoid().GetObjectName().c_str()); |
|
|
|
const char *cname = plFactory::GetNameOfClass(key->GetUoid().GetClassType()); |
|
fUI->fObjectClass->setText(QString("%1 (%2)").arg(cname ? cname : "<unknown>") |
|
.arg(key->GetUoid().GetClassType())); |
|
|
|
plKeyImp *imp = static_cast<plKeyImp *>(key); |
|
if (showAsHex) |
|
fUI->fStartPos->setText(QString("0x%1").arg(imp->GetStartPos(), 0, 16)); |
|
else |
|
fUI->fStartPos->setText(QString::number(imp->GetStartPos())); |
|
|
|
if (showAsHex) |
|
{ |
|
fUI->fObjectSize->setText(QString("0x%1").arg(imp->GetDataLen(), 0, 16)); |
|
} |
|
else |
|
{ |
|
if (imp->GetDataLen() < 1024) |
|
fUI->fObjectSize->setText(QString("%1 bytes").arg(imp->GetDataLen())); |
|
else if (imp->GetDataLen() < 1024 * 1024) |
|
fUI->fObjectSize->setText(QString("%1 kB").arg(imp->GetDataLen() / 1024.f, 0, 'f', 2)); |
|
else |
|
fUI->fObjectSize->setText(QString("%1 MB").arg(imp->GetDataLen() / 1024.f / 1024.f, 0, 'f', 2)); |
|
} |
|
|
|
fUI->fSaveSelectedAction->setEnabled(true); |
|
fUI->fSaveButton->setEnabled(true); |
|
} |
|
} |
|
} |
|
|
|
void plResBrowser::RegisterFileTypes() |
|
{ |
|
// Only meaningful on Windows currently... For Linux, consider creating |
|
// a .desktop file as part of the make install process |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
static bool sFileTypesRegistered = false; |
|
if (sFileTypesRegistered) |
|
return; |
|
|
|
// Make sure our file types are created |
|
QString path = QCoreApplication::applicationFilePath(); |
|
|
|
//plWinRegistryTools::AssociateFileType("PlasmaIdxFile", "Plasma 2 Index File", path, 1); |
|
//plWinRegistryTools::AssociateFileType("PlasmaDatFile", "Plasma 2 Data File", path, 2); |
|
//plWinRegistryTools::AssociateFileType("PlasmaPatchFile", "Plasma 2 Patch File", path, 3); |
|
plWinRegistryTools::AssociateFileType("PlasmaPackFile", "Plasma 2 Packfile", path, 4); |
|
|
|
// Check our file extensions |
|
QString prpAssoc = plWinRegistryTools::GetCurrentFileExtensionAssociation(".prp"); |
|
|
|
if (prpAssoc != "PlasmaPackFile") |
|
{ |
|
int retn = QMessageBox::question(this, tr("plResBrowser File Type Association"), |
|
tr("The Plasma 2 packed data file extension .prp is not currently associated " |
|
"with plResBrowser. Would you like to associate it now?")); |
|
if (retn == QMessageBox::Yes) |
|
{ |
|
// Associate 'em |
|
plWinRegistryTools::AssociateFileExtension(".prp", "PlasmaPackFile"); |
|
} |
|
} |
|
|
|
sFileTypesRegistered = true; |
|
#endif |
|
} |
|
|
|
void plResBrowser::LoadPrpFile(const QString &fileName) |
|
{ |
|
fUI->fTreeView->clear(); |
|
hsgResMgr::Reset(); |
|
|
|
// Load that source |
|
plResManager *mgr = static_cast<plResManager *>(hsgResMgr::ResMgr()); |
|
mgr->AddSinglePage(fileName.toUtf8().constData()); |
|
fUI->fTreeView->LoadFromRegistry(fUI->fShowOnlyLoadableAction->isChecked()); |
|
|
|
SetWindowTitle(fileName); |
|
} |
|
|
|
void plResBrowser::LoadResourcePath(const QString &path) |
|
{ |
|
fUI->fTreeView->clear(); |
|
hsgResMgr::Reset(); |
|
|
|
// Load that source |
|
plResManager *mgr = static_cast<plResManager *>(hsgResMgr::ResMgr()); |
|
|
|
std::vector<plFileName> prpFiles = plFileSystem::ListDir(path.toUtf8().constData(), "*.prp"); |
|
for (auto iter = prpFiles.begin(); iter != prpFiles.end(); ++iter) |
|
mgr->AddSinglePage(*iter); |
|
|
|
fUI->fTreeView->LoadFromRegistry(fUI->fShowOnlyLoadableAction->isChecked()); |
|
|
|
SetWindowTitle(path); |
|
} |
|
|
|
|
|
int main(int argc, char *argv[]) |
|
{ |
|
QApplication app(argc, argv); |
|
app.setApplicationName("plResBrowser"); |
|
app.setWindowIcon(QIcon(":/icon1.ico")); |
|
|
|
plResMgrSettings::Get().SetFilterNewerPageVersions(false); |
|
plResMgrSettings::Get().SetFilterOlderPageVersions(false); |
|
|
|
plResManager *rMgr = new plResManager; |
|
hsgResMgr::Init(rMgr); |
|
|
|
int retn; |
|
{ |
|
plResBrowser mainWindow; |
|
mainWindow.show(); |
|
retn = app.exec(); |
|
} |
|
|
|
hsgResMgr::Shutdown(); |
|
|
|
return retn; |
|
}
|
|
|