diff options
Diffstat (limited to 'Tools/QtBiomeVisualiser')
-rw-r--r-- | Tools/QtBiomeVisualiser/ChunkSource.cpp | 241 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/ChunkSource.h | 34 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/GeneratorSetupDlg.cpp | 125 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/GeneratorSetupDlg.h | 53 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/MainWindow.cpp | 217 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/MainWindow.h | 45 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro | 40 |
7 files changed, 727 insertions, 28 deletions
diff --git a/Tools/QtBiomeVisualiser/ChunkSource.cpp b/Tools/QtBiomeVisualiser/ChunkSource.cpp index 2235816bc..54da2afe5 100644 --- a/Tools/QtBiomeVisualiser/ChunkSource.cpp +++ b/Tools/QtBiomeVisualiser/ChunkSource.cpp @@ -3,6 +3,8 @@ #include <QThread> #include "Generating/BioGen.h" #include "inifile/iniFile.h" +#include "StringCompression.h" +#include "WorldStorage/FastNBT.h" @@ -182,3 +184,242 @@ void BioGenSource::reload() + +//////////////////////////////////////////////////////////////////////////////// +// AnvilSource::AnvilFile + +class AnvilSource::AnvilFile +{ +public: + /** Coordinates of the region file. */ + int m_RegionX, m_RegionZ; + + /** True iff the file contains proper data. */ + bool m_IsValid; + + + + /** Creates a new instance with the specified region coords. Reads the file header. */ + AnvilFile(int a_RegionX, int a_RegionZ, const AString & a_WorldPath) : + m_RegionX(a_RegionX), + m_RegionZ(a_RegionZ), + m_IsValid(false) + { + readFile(Printf("%s/r.%d.%d.mca", a_WorldPath.c_str(), a_RegionX, a_RegionZ)); + } + + + + /** Returns the compressed data of the specified chunk. + Returns an empty string when chunk not present. */ + AString getChunkData(int a_ChunkX, int a_ChunkZ) + { + if (!m_IsValid) + { + return ""; + } + + // Translate to local coords: + int RelChunkX = a_ChunkX - m_RegionX * 32; + int RelChunkZ = a_ChunkZ - m_RegionZ * 32; + ASSERT((RelChunkX >= 0) && (RelChunkX < 32)); + ASSERT((RelChunkZ >= 0) && (RelChunkZ < 32)); + + // Get the chunk data location: + UInt32 chunkOffset = m_Header[RelChunkX + 32 * RelChunkZ] >> 8; + UInt32 numChunkSectors = m_Header[RelChunkX + 32 * RelChunkZ] & 0xff; + if ((chunkOffset < 2) || (numChunkSectors == 0)) + { + return ""; + } + + // Get the real data size: + const char * chunkData = m_FileData.data() + chunkOffset * 4096; + UInt32 chunkSize = GetBEInt(chunkData); + if ((chunkSize < 2) || (chunkSize / 4096 > numChunkSectors)) + { + // Bad data, bail out + return ""; + } + + // Check the compression method: + if (chunkData[4] != 2) + { + // Chunk is in an unknown compression + return ""; + } + chunkSize--; + + // Read the chunk data: + return m_FileData.substr(chunkOffset * 4096 + 5, chunkSize); + } + +protected: + AString m_FileData; + UInt32 m_Header[2048]; + + + /** Reads the whole specified file contents and parses the header. */ + void readFile(const AString & a_FileName) + { + // Read the entire file: + m_FileData = cFile::ReadWholeFile(a_FileName); + if (m_FileData.size() < sizeof(m_Header)) + { + return; + } + + // Parse the header - change endianness: + const char * hdr = m_FileData.data(); + for (size_t i = 0; i < ARRAYCOUNT(m_Header); i++) + { + m_Header[i] = GetBEInt(hdr + 4 * i); + } + m_IsValid = true; + } +}; + + + + + +//////////////////////////////////////////////////////////////////////////////// +// AnvilSource: + +AnvilSource::AnvilSource(QString a_WorldRegionFolder) : + m_WorldRegionFolder(a_WorldRegionFolder) +{ +} + + + + + +void AnvilSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChunk) +{ + // Load the compressed data: + AString compressedChunkData = getCompressedChunkData(a_ChunkX, a_ChunkZ); + if (compressedChunkData.empty()) + { + return; + } + + // Uncompress the chunk data: + AString uncompressed; + int res = InflateString(compressedChunkData.data(), compressedChunkData.size(), uncompressed); + if (res != Z_OK) + { + return; + } + + // Parse the NBT data: + cParsedNBT nbt(uncompressed.data(), uncompressed.size()); + if (!nbt.IsValid()) + { + return; + } + + // Get the biomes out of the NBT: + int Level = nbt.FindChildByName(0, "Level"); + if (Level < 0) + { + return; + } + cChunkDef::BiomeMap biomeMap; + int mcsBiomes = nbt.FindChildByName(Level, "MCSBiomes"); + if ((mcsBiomes >= 0) && (nbt.GetDataLength(mcsBiomes) == sizeof(biomeMap))) + { + // Convert the biomes from BigEndian to platform native numbers: + const char * beBiomes = nbt.GetData(mcsBiomes); + for (size_t i = 0; i < ARRAYCOUNT(biomeMap); i++) + { + biomeMap[i] = (EMCSBiome)GetBEInt(beBiomes + 4 * i); + } + // Render the biomes: + Chunk::Image img; + biomesToImage(biomeMap, img); + a_DestChunk->setImage(img); + return; + } + + // MCS biomes not found, load Vanilla biomes instead: + int biomes = nbt.FindChildByName(Level, "Biomes"); + if ((biomes < 0) || (nbt.GetDataLength(biomes) != ARRAYCOUNT(biomeMap))) + { + return; + } + // Convert the biomes from Vanilla to EMCSBiome: + const char * vanillaBiomes = nbt.GetData(biomes); + for (size_t i = 0; i < ARRAYCOUNT(biomeMap); i++) + { + biomeMap[i] = EMCSBiome(vanillaBiomes[i]); + } + // Render the biomes: + Chunk::Image img; + biomesToImage(biomeMap, img); + a_DestChunk->setImage(img); +} + + + + + +void AnvilSource::reload() +{ + // Remove all files from the cache: + QMutexLocker lock(&m_Mtx); + m_Files.clear(); +} + + + + + +void AnvilSource::chunkToRegion(int a_ChunkX, int a_ChunkZ, int & a_RegionX, int & a_RegionZ) +{ + a_RegionX = a_ChunkX >> 5; + a_RegionZ = a_ChunkZ >> 5; +} + + + + + +AString AnvilSource::getCompressedChunkData(int a_ChunkX, int a_ChunkZ) +{ + return getAnvilFile(a_ChunkX, a_ChunkZ)->getChunkData(a_ChunkX, a_ChunkZ); +} + + + + + +AnvilSource::AnvilFilePtr AnvilSource::getAnvilFile(int a_ChunkX, int a_ChunkZ) +{ + int RegionX, RegionZ; + chunkToRegion(a_ChunkX, a_ChunkZ, RegionX, RegionZ); + + // Search the cache for the file: + QMutexLocker lock(&m_Mtx); + for (auto itr = m_Files.cbegin(), end = m_Files.cend(); itr != end; ++itr) + { + if (((*itr)->m_RegionX == RegionX) && ((*itr)->m_RegionZ == RegionZ)) + { + // Found the file in the cache, move it to front and return it: + AnvilFilePtr file(*itr); + m_Files.erase(itr); + m_Files.push_front(file); + return file; + } + } + + // File not in cache, create it: + AnvilFilePtr file(new AnvilFile(RegionX, RegionZ, m_WorldRegionFolder.toStdString())); + m_Files.push_front(file); + return file; +} + + + + + diff --git a/Tools/QtBiomeVisualiser/ChunkSource.h b/Tools/QtBiomeVisualiser/ChunkSource.h index 868e4a144..a5612da01 100644 --- a/Tools/QtBiomeVisualiser/ChunkSource.h +++ b/Tools/QtBiomeVisualiser/ChunkSource.h @@ -1,4 +1,5 @@ #pragma once +#include "Globals.h" #include <QString> #include <QMutex> #include "Chunk.h" @@ -64,11 +65,40 @@ class AnvilSource : public ChunkSource { public: - // TODO + /** Constructs a new AnvilSource based on the world path. */ + AnvilSource(QString a_WorldRegionFolder); // ChunkSource overrides: virtual void getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChunk) override; - virtual void reload() override {} + virtual void reload() override; + +protected: + class AnvilFile; + typedef std::shared_ptr<AnvilFile> AnvilFilePtr; + + + /** Folder where the individual Anvil Region files are located. */ + QString m_WorldRegionFolder; + + /** List of currently loaded files. Acts as a cache so that a file is not opened and closed over and over again. + Protected against multithreaded access by m_Mtx. */ + std::list<AnvilFilePtr> m_Files; + + /** Guards m_Files agains multithreaded access. */ + QMutex m_Mtx; + + + /** Converts chunk coords to region coords. */ + void chunkToRegion(int a_ChunkX, int a_ChunkZ, int & a_RegionX, int & a_RegionZ); + + /** Returns the compressed data of the specified chunk. + Returns an empty string if the chunk is not available. */ + AString getCompressedChunkData(int a_ChunkX, int a_ChunkZ); + + /** Returns the file object that contains the specified chunk. + The file is taken from the cache if available there, otherwise it is created anew. */ + AnvilFilePtr getAnvilFile(int a_ChunkX, int a_ChunkZ); + }; diff --git a/Tools/QtBiomeVisualiser/GeneratorSetupDlg.cpp b/Tools/QtBiomeVisualiser/GeneratorSetupDlg.cpp new file mode 100644 index 000000000..e6037fa9b --- /dev/null +++ b/Tools/QtBiomeVisualiser/GeneratorSetupDlg.cpp @@ -0,0 +1,125 @@ +#include "Globals.h" +#include "GeneratorSetupDlg.h" +#include <QLabel> +#include <QLineEdit> +#include "Generating/BioGen.h" +#include "inifile/iniFile.h" + + + + + +static const QString s_GeneratorNames[] = +{ + QString("Checkerboard"), + QString("Constant"), + QString("DistortedVoronoi"), + QString("MultiStepMap"), + QString("TwoLevel"), + QString("Voronoi"), +}; + + + + + +GeneratorSetupDlg::GeneratorSetupDlg(const AString & a_IniFileName, QWidget * a_Parent) : + super(a_Parent), + m_IniFile(new cIniFile()) +{ + // The generator name is in a separate form layout at the top, always present: + m_cbGenerator = new QComboBox(); + m_cbGenerator->setMinimumWidth(300); + for (size_t i = 0; i < ARRAYCOUNT(s_GeneratorNames); i++) + { + m_cbGenerator->addItem(s_GeneratorNames[i]); + } + QFormLayout * nameLayout = new QFormLayout(); + nameLayout->addRow(new QLabel(tr("Generator")), m_cbGenerator); + + // The rest of the controls are in a dynamically created form layout: + m_FormLayout = new QFormLayout(); + + // The main layout joins these two vertically: + m_MainLayout = new QVBoxLayout(); + m_MainLayout->addLayout(nameLayout); + m_MainLayout->addLayout(m_FormLayout); + setLayout(m_MainLayout); + + // Load the INI file, if specified, otherwise set defaults: + if (!a_IniFileName.empty() && m_IniFile->ReadFile(a_IniFileName)) + { + m_cbGenerator->setCurrentText(QString::fromStdString(m_IniFile->GetValue("Generator", "BiomeGen"))); + } + else + { + m_IniFile->SetValue("Generator", "Generator", "Composable"); + m_IniFile->SetValue("Generator", "BiomeGen", m_cbGenerator->currentText().toStdString()); + bool dummy; + delete cBiomeGen::CreateBiomeGen(*m_IniFile, m_Seed, dummy); + } + updateFromIni(); + + // Connect the combo change even only after the data has been loaded: + connect(m_cbGenerator, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(generatorChanged(QString))); +} + + + + + +void GeneratorSetupDlg::generatorChanged(const QString & a_NewName) +{ + // Clear the current contents of the form layout by assigning it to a stack temporary: + { + m_MainLayout->takeAt(1); + QWidget().setLayout(m_FormLayout); + } + + // Re-create the layout: + m_FormLayout = new QFormLayout(); + m_MainLayout->addLayout(m_FormLayout); + + // Recreate the INI file: + m_IniFile->Clear(); + m_IniFile->SetValue("Generator", "Generator", "Composable"); + m_IniFile->SetValue("Generator", "BiomeGen", a_NewName.toStdString()); + + // Create a dummy biome gen from the INI file, this will create the defaults in the INI file: + bool dummy; + delete cBiomeGen::CreateBiomeGen(*m_IniFile, m_Seed, dummy); + + // Read all values from the INI file and put them into the form layout: + updateFromIni(); +} + + + + + +void GeneratorSetupDlg::updateFromIni() +{ + int keyID = m_IniFile->FindKey("Generator"); + if (keyID <= -1) + { + return; + } + int numItems = m_IniFile->GetNumValues(keyID); + for (int i = 0; i < numItems; i++) + { + AString itemName = m_IniFile->GetValueName(keyID, i); + AString itemValue = m_IniFile->GetValue(keyID, i); + if ((itemName == "Generator") || (itemName == "BiomeGen")) + { + // These special cases are not to be added + continue; + } + QLineEdit * edit = new QLineEdit(); + edit->setText(QString::fromStdString(itemValue)); + m_FormLayout->addRow(new QLabel(QString::fromStdString(itemName)), edit); + } // for i - INI values[] +} + + + + diff --git a/Tools/QtBiomeVisualiser/GeneratorSetupDlg.h b/Tools/QtBiomeVisualiser/GeneratorSetupDlg.h new file mode 100644 index 000000000..d7070e331 --- /dev/null +++ b/Tools/QtBiomeVisualiser/GeneratorSetupDlg.h @@ -0,0 +1,53 @@ +#pragma once + +#include <memory> +#include <QDialog> +#include <QComboBox> +#include <QVBoxLayout> +#include <QFormLayout> + + + + + +class cIniFile; + + + + + +class GeneratorSetupDlg : + public QDialog +{ + typedef QDialog super; + + Q_OBJECT + +public: + /** Creates the dialog and loads the contents of the INI file, if not empty. */ + explicit GeneratorSetupDlg(const std::string & a_IniFileName, QWidget * parent = nullptr); + +signals: + +public slots: + /** Called when the user selects a different generator from the top combobox. + Re-creates m_IniFile and updates the form layout. */ + void generatorChanged(const QString & a_NewName); + +protected: + QComboBox * m_cbGenerator; + QVBoxLayout * m_MainLayout; + QFormLayout * m_FormLayout; + + std::unique_ptr<cIniFile> m_IniFile; + + int m_Seed; + + + /** Updates the form layout with the values from m_IniFile. */ + void updateFromIni(); +}; + + + + diff --git a/Tools/QtBiomeVisualiser/MainWindow.cpp b/Tools/QtBiomeVisualiser/MainWindow.cpp index 65d0ccf5e..d2c1ae2c1 100644 --- a/Tools/QtBiomeVisualiser/MainWindow.cpp +++ b/Tools/QtBiomeVisualiser/MainWindow.cpp @@ -5,9 +5,14 @@ #include <QMenuBar> #include <QApplication> #include <QFileDialog> +#include <QSettings> +#include <QDirIterator> #include "inifile/iniFile.h" #include "ChunkSource.h" #include "Generating/BioGen.h" +#include "StringCompression.h" +#include "WorldStorage/FastNBT.h" +#include "GeneratorSetupDlg.h" @@ -16,6 +21,8 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent) { + initMinecraftPath(); + m_BiomeView = new BiomeView(this); setCentralWidget(m_BiomeView); @@ -36,9 +43,37 @@ MainWindow::~MainWindow() -void MainWindow::generate() +void MainWindow::newGenerator() +{ + // TODO + + // (Re-)open the generator setup dialog: + m_GeneratorSetupDlg.reset(new GeneratorSetupDlg("")); + m_GeneratorSetupDlg->show(); + m_GeneratorSetupDlg->raise(); + + // TODO +} + + + + + +void MainWindow::openGenerator() { + // Let the user specify the world.ini file: QString worldIni = QFileDialog::getOpenFileName(this, tr("Open world.ini"), QString(), tr("world.ini (world.ini)")); + if (worldIni.isEmpty()) + { + return; + } + + // (Re-)open the generator setup dialog: + m_GeneratorSetupDlg.reset(new GeneratorSetupDlg(worldIni.toStdString())); + m_GeneratorSetupDlg->show(); + m_GeneratorSetupDlg->raise(); + + // Set the chunk source: m_BiomeView->setChunkSource(std::shared_ptr<BioGenSource>(new BioGenSource(worldIni))); m_BiomeView->redraw(); } @@ -47,9 +82,66 @@ void MainWindow::generate() -void MainWindow::open() +void MainWindow::openWorld() { - // TODO + // Let the user specify the world: + QString regionFolder = QFileDialog::getExistingDirectory(this, tr("Select the region folder"), QString()); + if (regionFolder.isEmpty()) + { + return; + } + + // Remove the generator setup dialog, if open: + if (m_GeneratorSetupDlg.get() != nullptr) + { + m_GeneratorSetupDlg->hide(); + m_GeneratorSetupDlg.reset(nullptr); + } + + // Set the chunk source: + m_BiomeView->setChunkSource(std::shared_ptr<AnvilSource>(new AnvilSource(regionFolder))); + m_BiomeView->redraw(); +} + + + + + +void MainWindow::openVanillaWorld() +{ + // The world is stored in the sender action's data, retrieve it: + QAction * action = qobject_cast<QAction *>(sender()); + if (action == nullptr) + { + return; + } + + // Remove the generator setup dialog, if open: + if (m_GeneratorSetupDlg.get() != nullptr) + { + m_GeneratorSetupDlg->hide(); + m_GeneratorSetupDlg.reset(nullptr); + } + + // Set the chunk source: + m_BiomeView->setChunkSource(std::shared_ptr<AnvilSource>(new AnvilSource(action->data().toString()))); + m_BiomeView->redraw(); +} + + + + + +void MainWindow::initMinecraftPath() +{ + #ifdef Q_OS_MAC + m_MinecraftPath = QDir::homePath() + QDir::toNativeSeparators("/Library/Application Support/minecraft"); + #elif defined Q_OS_WIN32 + QSettings ini(QSettings::IniFormat, QSettings::UserScope, ".minecraft", "minecraft1"); + m_MinecraftPath = QFileInfo(ini.fileName()).absolutePath(); + #else + m_MinecraftPath = QDir::homePath() + QDir::toNativeSeparators("/.minecraft"); + #endif } @@ -58,19 +150,26 @@ void MainWindow::open() void MainWindow::createActions() { - m_actGen = new QAction(tr("&Generate..."), this); - m_actGen->setShortcut(tr("Ctrl+N")); - m_actGen->setStatusTip(tr("Open a generator INI file and display the generated biomes")); - connect(m_actGen, SIGNAL(triggered()), this, SLOT(generate())); + createWorldActions(); + + m_actNewGen = new QAction(tr("&New generator"), this); + m_actNewGen->setShortcut(tr("Ctrl+N")); + m_actNewGen->setStatusTip(tr("Open a generator INI file and display the generated biomes")); + connect(m_actNewGen, SIGNAL(triggered()), this, SLOT(newGenerator())); + + m_actOpenGen = new QAction(tr("&Open generator..."), this); + m_actOpenGen->setShortcut(tr("Ctrl+G")); + m_actOpenGen->setStatusTip(tr("Open a generator INI file and display the generated biomes")); + connect(m_actOpenGen, SIGNAL(triggered()), this, SLOT(openGenerator())); - m_actOpen = new QAction(tr("&Open world..."), this); - m_actOpen->setShortcut(tr("Ctrl+O")); - m_actOpen->setStatusTip(tr("Open an existing world and display its biomes")); - connect(m_actOpen, SIGNAL(triggered()), this, SLOT(open())); + m_actOpenWorld = new QAction(tr("&Open world..."), this); + m_actOpenWorld->setShortcut(tr("Ctrl+O")); + m_actOpenWorld->setStatusTip(tr("Open an existing world and display its biomes")); + connect(m_actOpenWorld, SIGNAL(triggered()), this, SLOT(openWorld())); m_actReload = new QAction(tr("&Reload"), this); m_actReload->setShortcut(tr("F5")); - m_actReload->setStatusTip(tr("Open an existing world and display its biomes")); + m_actReload->setStatusTip(tr("Clear the view cache and force a reload of all the data")); connect(m_actReload, SIGNAL(triggered()), m_BiomeView, SLOT(reload())); m_actExit = new QAction(tr("E&xit"), this); @@ -83,15 +182,95 @@ void MainWindow::createActions() +void MainWindow::createWorldActions() +{ + QDir mc(m_MinecraftPath); + if (!mc.cd("saves")) + { + return; + } + + QDirIterator it(mc); + int key = 1; + while (it.hasNext()) + { + it.next(); + if (!it.fileInfo().isDir()) + { + continue; + } + QString name = getWorldName(it.filePath().toStdString()); + if (name.isEmpty()) + { + continue; + } + QAction * w = new QAction(this); + w->setText(name); + w->setData(it.filePath() + "/region"); + if (key < 10) + { + w->setShortcut("Ctrl+" + QString::number(key)); + key++; + } + connect(w, SIGNAL(triggered()), this, SLOT(openVanillaWorld())); + m_WorldActions.append(w); + } +} + + + + + void MainWindow::createMenus() { - QMenu * mFile = menuBar()->addMenu(tr("&World")); - mFile->addAction(m_actGen); - mFile->addAction(m_actOpen); - mFile->addSeparator(); - mFile->addAction(m_actReload); - mFile->addSeparator(); - mFile->addAction(m_actExit); + QMenu * file = menuBar()->addMenu(tr("&Map")); + file->addAction(m_actNewGen); + file->addAction(m_actOpenGen); + file->addSeparator(); + QMenu * worlds = file->addMenu(tr("Open existing")); + worlds->addActions(m_WorldActions); + if (m_WorldActions.empty()) + { + worlds->setEnabled(false); + } + file->addAction(m_actOpenWorld); + file->addSeparator(); + file->addAction(m_actReload); + file->addSeparator(); + file->addAction(m_actExit); +} + + + + + +QString MainWindow::getWorldName(const AString & a_Path) +{ + AString levelData = cFile::ReadWholeFile(a_Path + "/level.dat"); + if (levelData.empty()) + { + // No such file / no data + return QString(); + } + + AString uncompressed; + if (UncompressStringGZIP(levelData.data(), levelData.size(), uncompressed) != Z_OK) + { + return QString(); + } + cParsedNBT nbt(uncompressed.data(), uncompressed.size()); + if (!nbt.IsValid()) + { + return QString(); + } + AString name = nbt.GetName(1); + OutputDebugStringA(name.c_str()); + int levelNameTag = nbt.FindTagByPath(nbt.GetRoot(), "Data\\LevelName"); + if ((levelNameTag <= 0) || (nbt.GetType(levelNameTag) != TAG_String)) + { + return QString(); + } + return QString::fromStdString(nbt.GetString(levelNameTag)); } diff --git a/Tools/QtBiomeVisualiser/MainWindow.h b/Tools/QtBiomeVisualiser/MainWindow.h index b37bf4120..840e01b0f 100644 --- a/Tools/QtBiomeVisualiser/MainWindow.h +++ b/Tools/QtBiomeVisualiser/MainWindow.h @@ -1,5 +1,7 @@ #pragma once +#include <memory> +#include <QList> #include <QMainWindow> #include "BiomeView.h" @@ -7,6 +9,13 @@ +// fwd: +class GeneratorSetupDlg; + + + + + class MainWindow : public QMainWindow { @@ -15,29 +24,55 @@ class MainWindow : BiomeView * m_BiomeView; public: - MainWindow(QWidget *parent = 0); + MainWindow(QWidget * parent = nullptr); ~MainWindow(); private slots: + /** Creates a generator definition from scratch, lets user modify generator params in realtime. */ + void newGenerator(); + /** Opens a generator definition and generates the biomes based on that. */ - void generate(); + void openGenerator(); /** Opens an existing world and displays the loaded biomes. */ - void open(); + void openWorld(); + + /** Opens a vanilla world that is specified by the calling action. */ + void openVanillaWorld(); protected: // Actions: - QAction * m_actGen; - QAction * m_actOpen; + QAction * m_actNewGen; + QAction * m_actOpenGen; + QAction * m_actOpenWorld; QAction * m_actReload; QAction * m_actExit; + /** List of actions that open the specific vanilla world. */ + QList<QAction *> m_WorldActions; + + /** Path to the vanilla folder. */ + QString m_MinecraftPath; + + /** The dialog for setting up the generator. */ + std::unique_ptr<GeneratorSetupDlg> m_GeneratorSetupDlg; + + + /** Initializes the m_MinecraftPath based on the proper MC path */ + void initMinecraftPath(); /** Creates the actions that the UI supports. */ void createActions(); + /** Creates the actions that open a specific vanilla world. Iterates over the minecraft saves folder. */ + void createWorldActions(); + /** Creates the menu bar and connects its events. */ void createMenus(); + + /** Returns the name of the vanilla world in the specified path. + Reads the level.dat file for the name. Returns an empty string on failure. */ + QString getWorldName(const AString & a_Path); }; diff --git a/Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro b/Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro index e6b65e628..7171562bb 100644 --- a/Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro +++ b/Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro @@ -29,7 +29,25 @@ SOURCES += main.cpp\ ChunkCache.cpp \ Chunk.cpp \ ChunkSource.cpp \ - ChunkLoader.cpp + ChunkLoader.cpp \ + ../../src/StringCompression.cpp \ + ../../src/WorldStorage/FastNBT.cpp \ + ../../lib/zlib/adler32.c \ + ../../lib/zlib/compress.c \ + ../../lib/zlib/crc32.c \ + ../../lib/zlib/deflate.c \ + ../../lib/zlib/gzclose.c \ + ../../lib/zlib/gzlib.c \ + ../../lib/zlib/gzread.c \ + ../../lib/zlib/gzwrite.c \ + ../../lib/zlib/infback.c \ + ../../lib/zlib/inffast.c \ + ../../lib/zlib/inflate.c \ + ../../lib/zlib/inftrees.c \ + ../../lib/zlib/trees.c \ + ../../lib/zlib/uncompr.c \ + ../../lib/zlib/zutil.c \ + GeneratorSetupDlg.cpp HEADERS += MainWindow.h \ Globals.h \ @@ -48,7 +66,21 @@ HEADERS += MainWindow.h \ ChunkCache.h \ Chunk.h \ ChunkSource.h \ - ChunkLoader.h + ChunkLoader.h \ + ../../src/StringCompression.h \ + ../../src/WorldStorage/FastNBT.h \ + ../../lib/zlib/crc32.h \ + ../../lib/zlib/deflate.h \ + ../../lib/zlib/gzguts.h \ + ../../lib/zlib/inffast.h \ + ../../lib/zlib/inffixed.h \ + ../../lib/zlib/inflate.h \ + ../../lib/zlib/inftrees.h \ + ../../lib/zlib/trees.h \ + ../../lib/zlib/zconf.h \ + ../../lib/zlib/zlib.h \ + ../../lib/zlib/zutil.h \ + GeneratorSetupDlg.h INCLUDEPATH += $$_PRO_FILE_PWD_ \ $$_PRO_FILE_PWD_/../../src \ @@ -57,4 +89,8 @@ INCLUDEPATH += $$_PRO_FILE_PWD_ \ CONFIG += C++11 +OTHER_FILES += \ + ../../lib/zlib/example.c.txt \ + ../../lib/zlib/minigzip.c.txt + |