diff --git a/.gitignore b/.gitignore index 774478e2b3..b17b5a5848 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ build *~ Doxygen -prebuilt \ No newline at end of file +prebuilt +apps/openmw/config.hpp +Docs/mainpage.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c5001a8946..91d3f7dd37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,23 @@ project(OpenMW) IF (APPLE) set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/OpenMW.app") + + # using 10.6 sdk + set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.6.sdk") ENDIF (APPLE) +# Version + +set (OPENMW_VERSION_MAJOR 0) +set (OPENMW_VERSION_MINOR 11) +set (OPENMW_VERSION_RELEASE 0) + +set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") + +# doxygen main page + +configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp") + # Sound source selection option(USE_AUDIERE "use Audiere for sound" OFF) option(USE_FFMPEG "use ffmpeg for sound" OFF) @@ -17,9 +32,8 @@ if(DPKG_PROGRAM) set(MORROWIND_RESOURCE_FILES "/usr/share/games/openmw/resources/" CACHE PATH "location of OpenMW resources files") else() if (APPLE) - # set path inside bundle - set(MORROWIND_DATA_FILES "../data" CACHE PATH "location of Morrowind data files") - set(MORROWIND_RESOURCE_FILES "Contents/Resources/resources" CACHE PATH "location of OpenMW resources files") + set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files") + set(MORROWIND_RESOURCE_FILES "./resources" CACHE PATH "location of OpenMW resources files") else() set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data files") set(MORROWIND_RESOURCE_FILES "resources" CACHE PATH "location of OpenMW resources files") @@ -57,6 +71,7 @@ IF(EXISTS "${CMAKE_SOURCE_DIR}/prebuilt/vc100-mt-gd/ogre_1_7_1") set(ENV{OPENALDIR} "${PREBUILT_DIR}/OpenAL 1.1 SDK") + set(BULLET_ROOT "${PREBUILT_DIR}/bullet") ELSE() message (STATUS "OpenMW pre-built binaries not found. Using standard locations.") ENDIF() @@ -365,7 +380,7 @@ endif() if (APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.mac - "${APP_BUNDLE_DIR}/Contents/MacOS/plugins.cfg") + "${OpenMW_BINARY_DIR}/plugins.cfg") configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist "${APP_BUNDLE_DIR}/Contents/Info.plist" COPYONLY) @@ -384,9 +399,6 @@ if (APPLE) configure_file(${OGRE_PLUGIN_DIR}/Plugin_ParticleFX.dylib "${APP_BUNDLE_DIR}/Contents/Plugins/Plugin_ParticleFX.dylib" COPYONLY) - configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg - "${APP_BUNDLE_DIR}/Contents/MacOS/openmw.cfg") - endif (APPLE) @@ -397,8 +409,9 @@ if (CMAKE_COMPILER_IS_GNUCC) endif (CMAKE_COMPILER_IS_GNUCC) # Apple bundling +# TODO REWRITE! if (APPLE) - set(MISC_FILES + set(MISC_FILES ${APP_BUNDLE_DIR}/Contents/MacOS/openmw.cfg ${APP_BUNDLE_DIR}/Contents/MacOS/plugins.cfg) @@ -435,8 +448,7 @@ if(DPKG_PROGRAM) exec_program("git" ARGS "config --get user.email" OUTPUT_VARIABLE GIT_EMAIL) set(PACKAGE_MAINTAINER "${GIT_NAME} <${GIT_EMAIL}>") else() - #FIXME this should probably be read from some file like ${CMAKE_CURRENT_SOURCE_DIR}/VERSION or something that gets updated when changing version - set(VERSION_STRING "0.10.0") + set(VERSION_STRING "${OPENMW_VERSION}") set(PACKAGE_MAINTAINER "unknown") endif() @@ -483,6 +495,8 @@ if (BUILD_ESMTOOL) add_subdirectory( apps/esmtool ) endif() +add_subdirectory( apps/launcher ) + if (WIN32) if (MSVC) if (USE_DEBUG_CONSOLE) diff --git a/Docs/Doxyfile b/Docs/Doxyfile index 0a1e0eae8d..43c3312ad3 100644 --- a/Docs/Doxyfile +++ b/Docs/Doxyfile @@ -576,6 +576,7 @@ WARN_LOGFILE = INPUT = apps \ components \ libs \ + Docs # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is diff --git a/Docs/DoxyfilePages b/Docs/DoxyfilePages index 943e2eef09..5ce82a7c29 100644 --- a/Docs/DoxyfilePages +++ b/Docs/DoxyfilePages @@ -576,6 +576,7 @@ WARN_LOGFILE = INPUT = apps \ components \ libs \ + Docs # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is diff --git a/Docs/mainpage.hpp.cmake b/Docs/mainpage.hpp.cmake new file mode 100644 index 0000000000..f8cdf8a82a --- /dev/null +++ b/Docs/mainpage.hpp.cmake @@ -0,0 +1,5 @@ +/// \mainpage +/// +/// This is the source documentation for: +/// +/// OpenMW @OPENMW_VERSION@ diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt new file mode 100644 index 0000000000..5090c167d4 --- /dev/null +++ b/apps/launcher/CMakeLists.txt @@ -0,0 +1,79 @@ +set(LAUNCHER + datafilespage.cpp + graphicspage.cpp + lineedit.cpp + main.cpp + maindialog.cpp + naturalsort.cpp + playpage.cpp + pluginsmodel.cpp + pluginsview.cpp +) + +set(LAUNCHER_HEADER + combobox.hpp + datafilespage.hpp + graphicspage.hpp + lineedit.hpp + maindialog.hpp + naturalsort.hpp + playpage.hpp + pluginsmodel.hpp + pluginsview.hpp +) + +# Headers that must be pre-processed +set(LAUNCHER_HEADER_MOC + combobox.hpp + datafilespage.hpp + graphicspage.hpp + lineedit.hpp + maindialog.hpp + playpage.hpp + pluginsmodel.hpp + pluginsview.hpp +) + +source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER} ${LAUNCHER_HEADER_MOC}) + +find_package(Qt4 REQUIRED) +set(QT_USE_QTGUI 1) + +find_package(PNG REQUIRED) +include_directories(${PNG_INCLUDE_DIR}) + +QT4_ADD_RESOURCES(RCC_SRCS resources.qrc) +QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) + +include(${QT_USE_FILE}) + +# Main executable +add_executable(omwlauncher + ${LAUNCHER} + ${MISC} ${MISC_HEADER} + ${FILES} ${FILES_HEADER} + ${TO_UTF8} + ${ESM} + ${RCC_SRCS} + ${MOC_SRCS} +) + +target_link_libraries(omwlauncher + ${Boost_LIBRARIES} + ${OGRE_LIBRARIES} + ${QT_LIBRARIES} + ${PNG_LIBRARY} +) + +if (APPLE) + configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss + "${APP_BUNDLE_DIR}/../launcher.qss") + configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss + "${APP_BUNDLE_DIR}/../launcher.cfg") +else() + configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss") + + configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.cfg") +endif() diff --git a/apps/launcher/combobox.hpp b/apps/launcher/combobox.hpp new file mode 100644 index 0000000000..bc99575d74 --- /dev/null +++ b/apps/launcher/combobox.hpp @@ -0,0 +1,28 @@ +#ifndef COMBOBOX_H +#define COMBOBOX_H + +#include + +class ComboBox : public QComboBox +{ + Q_OBJECT +private: + QString oldText; +public: + ComboBox(QWidget *parent=0) : QComboBox(parent), oldText() + { + connect(this,SIGNAL(editTextChanged(const QString&)), this, + SLOT(textChangedSlot(const QString&))); + connect(this,SIGNAL(currentIndexChanged(const QString&)), this, + SLOT(textChangedSlot(const QString&))); + } +private slots: + void textChangedSlot(const QString &newText) + { + emit textChanged(oldText, newText); + oldText = newText; + } +signals: + void textChanged(const QString &oldText, const QString &newText); +}; +#endif \ No newline at end of file diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp new file mode 100644 index 0000000000..2e8cdfbd20 --- /dev/null +++ b/apps/launcher/datafilespage.cpp @@ -0,0 +1,979 @@ +#include + +#include +#include +#include +#include + +#include "datafilespage.hpp" +#include "lineedit.hpp" +#include "naturalsort.hpp" +#include "pluginsmodel.hpp" +#include "pluginsview.hpp" + +using namespace ESM; +using namespace std; + +//sort QModelIndexList ascending +bool rowGreaterThan(const QModelIndex &index1, const QModelIndex &index2) +{ + return index1.row() >= index2.row(); +} + +//sort QModelIndexList descending +bool rowSmallerThan(const QModelIndex &index1, const QModelIndex &index2) +{ + return index1.row() <= index2.row(); +} + +DataFilesPage::DataFilesPage(QWidget *parent) : QWidget(parent) +{ + mDataFilesModel = new QStandardItemModel(); // Contains all plugins with masters + mPluginsModel = new PluginsModel(); // Contains selectable plugins + + mPluginsProxyModel = new QSortFilterProxyModel(); + mPluginsProxyModel->setDynamicSortFilter(true); + mPluginsProxyModel->setSourceModel(mPluginsModel); + + QLabel *filterLabel = new QLabel(tr("&Filter:"), this); + LineEdit *filterLineEdit = new LineEdit(this); + filterLabel->setBuddy(filterLineEdit); + + QToolBar *filterToolBar = new QToolBar(this); + filterToolBar->setMovable(false); + + // Create a container widget and a layout to get the spacer to work + QWidget *filterWidget = new QWidget(this); + QHBoxLayout *filterLayout = new QHBoxLayout(filterWidget); + QSpacerItem *hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + filterLayout->addItem(hSpacer1); + filterLayout->addWidget(filterLabel); + filterLayout->addWidget(filterLineEdit); + + filterToolBar->addWidget(filterWidget); + + mMastersWidget = new QTableWidget(this); // Contains the available masters + mMastersWidget->setObjectName("MastersWidget"); + mMastersWidget->setSelectionBehavior(QAbstractItemView::SelectRows); + mMastersWidget->setSelectionMode(QAbstractItemView::MultiSelection); + mMastersWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); + mMastersWidget->setAlternatingRowColors(true); + mMastersWidget->horizontalHeader()->setStretchLastSection(true); + mMastersWidget->horizontalHeader()->hide(); + mMastersWidget->verticalHeader()->hide(); + mMastersWidget->insertColumn(0); + + mPluginsTable = new PluginsView(this); + mPluginsTable->setModel(mPluginsProxyModel); + mPluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem); + + mPluginsTable->horizontalHeader()->setStretchLastSection(true); + mPluginsTable->horizontalHeader()->hide(); + + // Set the row height to the size of the checkboxes + QCheckBox checkBox; + unsigned int height = checkBox.sizeHint().height() + 4; + + mPluginsTable->verticalHeader()->setDefaultSectionSize(height); + + // Add both tables to a splitter + QSplitter *splitter = new QSplitter(this); + splitter->setOrientation(Qt::Horizontal); + + splitter->addWidget(mMastersWidget); + splitter->addWidget(mPluginsTable); + + // Adjust the default widget widths inside the splitter + QList sizeList; + sizeList << 100 << 300; + splitter->setSizes(sizeList); + + // Bottom part with profile options + QLabel *profileLabel = new QLabel(tr("Current Profile: "), this); + + mProfilesComboBox = new ComboBox(this); + mProfilesComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); + mProfilesComboBox->setInsertPolicy(QComboBox::InsertAtBottom); + + mProfileToolBar = new QToolBar(this); + mProfileToolBar->setMovable(false); + mProfileToolBar->setIconSize(QSize(16, 16)); + + mProfileToolBar->addWidget(profileLabel); + mProfileToolBar->addWidget(mProfilesComboBox); + + QVBoxLayout *pageLayout = new QVBoxLayout(this); + + pageLayout->addWidget(filterToolBar); + pageLayout->addWidget(splitter); + pageLayout->addWidget(mProfileToolBar); + + connect(mMastersWidget->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, SLOT(masterSelectionChanged(const QItemSelection&, const QItemSelection&))); + + connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(const QString))); + + connect(mPluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); + connect(mPluginsTable, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&))); + + + + setupConfig(); + createActions(); +} + +void DataFilesPage::setupDataFiles(const QStringList &paths, bool strict) +{ + // Put the paths in a boost::filesystem vector to use with Files::Collections + std::vector dataDirs; + + foreach (const QString ¤tPath, paths) { + dataDirs.push_back(boost::filesystem::path(currentPath.toStdString())); + } + + // Create a file collection for the dataDirs + Files::Collections mFileCollections(dataDirs, strict); + + // First we add all the master files + const Files::MultiDirCollection &esm = mFileCollections.getCollection(".esm"); + unsigned int i = 0; // Row number + + for (Files::MultiDirCollection::TIter iter(esm.begin()); iter!=esm.end(); ++iter) + { + std::string filename = boost::filesystem::path (iter->second.filename()).string(); + QString currentMaster = QString::fromStdString(filename); + + const QList itemList = mMastersWidget->findItems(currentMaster, Qt::MatchExactly); + + if (itemList.isEmpty()) // Master is not yet in the widget + { + mMastersWidget->insertRow(i); + QTableWidgetItem *item = new QTableWidgetItem(currentMaster); + mMastersWidget->setItem(i, 0, item); + ++i; + } + } + + // Now on to the plugins + const Files::MultiDirCollection &esp = mFileCollections.getCollection(".esp"); + + for (Files::MultiDirCollection::TIter iter(esp.begin()); iter!=esp.end(); ++iter) + { + ESMReader fileReader; + QStringList availableMasters; // Will contain all found masters + + fileReader.setEncoding("win1252"); + fileReader.open(iter->second.string()); + + // First we fill the availableMasters and the mMastersWidget + ESMReader::MasterList mlist = fileReader.getMasters(); + + for (unsigned int i = 0; i < mlist.size(); ++i) { + const QString currentMaster = QString::fromStdString(mlist[i].name); + availableMasters.append(currentMaster); + + const QList itemList = mMastersWidget->findItems(currentMaster, Qt::MatchExactly); + + if (itemList.isEmpty()) // Master is not yet in the widget + { + // TODO: Show warning, missing master + mMastersWidget->insertRow(i); + QTableWidgetItem *item = new QTableWidgetItem(currentMaster); + mMastersWidget->setItem(i, 0, item); + } + } + + availableMasters.sort(); // Sort the masters alphabetically + + // Now we put the current plugin in the mDataFilesModel under its masters + QStandardItem *parent = new QStandardItem(availableMasters.join(",")); + + std::string filename = boost::filesystem::path (iter->second.filename()).string(); + QStandardItem *child = new QStandardItem(QString::fromStdString(std::string(filename))); + + const QList masterList = mDataFilesModel->findItems(availableMasters.join(",")); + + if (masterList.isEmpty()) { // Masters node not yet in the mDataFilesModel + parent->appendRow(child); + mDataFilesModel->appendRow(parent); + } else { + // Masters node exists, append current plugin + foreach (QStandardItem *currentItem, masterList) { + currentItem->appendRow(child); + } + } + } + + readConfig(); +} + +void DataFilesPage::setupConfig() +{ + QString config = "./launcher.cfg"; + QFile file(config); + + if (!file.exists()) { + config = QString::fromStdString(Files::getPath(Files::Path_ConfigUser, + "openmw", "launcher.cfg")); + } + + file.setFileName(config); // Just for displaying information + + // Open our config file + mLauncherConfig = new QSettings(config, QSettings::IniFormat); + mLauncherConfig->sync(); + + + // Make sure we have no groups open + while (!mLauncherConfig->group().isEmpty()) { + mLauncherConfig->endGroup(); + } + + mLauncherConfig->beginGroup("Profiles"); + QStringList profiles = mLauncherConfig->childGroups(); + + if (profiles.isEmpty()) { + // Add a default profile + profiles.append("Default"); + } + + mProfilesComboBox->addItems(profiles); + + QString currentProfile = mLauncherConfig->value("CurrentProfile").toString(); + + if (currentProfile.isEmpty()) { + // No current profile selected + currentProfile = "Default"; + } + mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(currentProfile)); + + mLauncherConfig->endGroup(); + + // Now we connect the combobox to do something if the profile changes + // This prevents strange behaviour while reading and appending the profiles + connect(mProfilesComboBox, SIGNAL(textChanged(const QString&, const QString&)), this, SLOT(profileChanged(const QString&, const QString&))); +} + +void DataFilesPage::createActions() +{ + // Refresh the plugins + QAction *refreshAction = new QAction(tr("Refresh"), this); + refreshAction->setShortcut(QKeySequence(tr("F5"))); + connect(refreshAction, SIGNAL(triggered()), this, SLOT(refresh())); + + // Profile actions + mNewProfileAction = new QAction(QIcon::fromTheme("document-new"), tr("&New Profile"), this); + mNewProfileAction->setToolTip(tr("New Profile")); + mNewProfileAction->setShortcut(QKeySequence(tr("Ctrl+N"))); + connect(mNewProfileAction, SIGNAL(triggered()), this, SLOT(newProfile())); + + + mCopyProfileAction = new QAction(QIcon::fromTheme("edit-copy"), tr("&Copy Profile"), this); + mCopyProfileAction->setToolTip(tr("Copy Profile")); + mCopyProfileAction->setShortcut(QKeySequence(tr("Ctrl+C"))); + connect(mCopyProfileAction, SIGNAL(triggered()), this, SLOT(copyProfile())); + + mDeleteProfileAction = new QAction(QIcon::fromTheme("edit-delete"), tr("Delete Profile"), this); + mDeleteProfileAction->setToolTip(tr("Delete Profile")); + mDeleteProfileAction->setShortcut(QKeySequence(tr("Delete"))); + connect(mDeleteProfileAction, SIGNAL(triggered()), this, SLOT(deleteProfile())); + + // Add the newly created actions to the toolbar + mProfileToolBar->addSeparator(); + mProfileToolBar->addAction(mNewProfileAction); + mProfileToolBar->addAction(mCopyProfileAction); + mProfileToolBar->addAction(mDeleteProfileAction); + + // Context menu actions + mMoveUpAction = new QAction(QIcon::fromTheme("go-up"), tr("Move &Up"), this); + mMoveUpAction->setShortcut(QKeySequence(tr("Ctrl+U"))); + connect(mMoveUpAction, SIGNAL(triggered()), this, SLOT(moveUp())); + + mMoveDownAction = new QAction(QIcon::fromTheme("go-down"), tr("&Move Down"), this); + mMoveDownAction->setShortcut(QKeySequence(tr("Ctrl+M"))); + connect(mMoveDownAction, SIGNAL(triggered()), this, SLOT(moveDown())); + + mMoveTopAction = new QAction(QIcon::fromTheme("go-top"), tr("Move to Top"), this); + mMoveTopAction->setShortcut(QKeySequence(tr("Ctrl+Shift+U"))); + connect(mMoveTopAction, SIGNAL(triggered()), this, SLOT(moveTop())); + + mMoveBottomAction = new QAction(QIcon::fromTheme("go-bottom"), tr("Move to Bottom"), this); + mMoveBottomAction->setShortcut(QKeySequence(tr("Ctrl+Shift+M"))); + connect(mMoveBottomAction, SIGNAL(triggered()), this, SLOT(moveBottom())); + + mCheckAction = new QAction(tr("Check selected"), this); + connect(mCheckAction, SIGNAL(triggered()), this, SLOT(check())); + + mUncheckAction = new QAction(tr("Uncheck selected"), this); + connect(mUncheckAction, SIGNAL(triggered()), this, SLOT(uncheck())); + + // Makes shortcuts work even if the context menu is hidden + this->addAction(refreshAction); + this->addAction(mMoveUpAction); + this->addAction(mMoveDownAction); + this->addAction(mMoveTopAction); + this->addAction(mMoveBottomAction); + + // Context menu for the plugins table + mContextMenu = new QMenu(this); + + mContextMenu->addAction(mMoveUpAction); + mContextMenu->addAction(mMoveDownAction); + mContextMenu->addSeparator(); + mContextMenu->addAction(mMoveTopAction); + mContextMenu->addAction(mMoveBottomAction); + mContextMenu->addSeparator(); + mContextMenu->addAction(mCheckAction); + mContextMenu->addAction(mUncheckAction); + +} + +void DataFilesPage::newProfile() +{ + bool ok; + QString text = QInputDialog::getText(this, tr("New Profile"), + tr("Profile Name:"), QLineEdit::Normal, + tr("New Profile"), &ok); + if (ok && !text.isEmpty()) { + if (mProfilesComboBox->findText(text) != -1) { + QMessageBox::warning(this, tr("Profile already exists"), + tr("the profile %0 already exists.").arg(text), + QMessageBox::Ok); + } else { + // Add the new profile to the combobox + mProfilesComboBox->addItem(text); + mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(text)); + + } + + } + +} + +void DataFilesPage::copyProfile() +{ + QString profile = mProfilesComboBox->currentText(); + bool ok; + + QString text = QInputDialog::getText(this, tr("Copy Profile"), + tr("Profile Name:"), QLineEdit::Normal, + tr("%0 Copy").arg(profile), &ok); + if (ok && !text.isEmpty()) { + if (mProfilesComboBox->findText(text) != -1) { + QMessageBox::warning(this, tr("Profile already exists"), + tr("the profile %0 already exists.").arg(text), + QMessageBox::Ok); + } else { + // Add the new profile to the combobox + mProfilesComboBox->addItem(text); + + // First write the current profile as the new profile + writeConfig(text); + mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(text)); + + } + + } + +} + +void DataFilesPage::deleteProfile() +{ + QString profile = mProfilesComboBox->currentText(); + + + if (profile.isEmpty()) { + return; + } + + QMessageBox deleteMessageBox(this); + deleteMessageBox.setWindowTitle(tr("Delete Profile")); + deleteMessageBox.setText(tr("Are you sure you want to delete %0?").arg(profile)); + deleteMessageBox.setIcon(QMessageBox::Warning); + QAbstractButton *deleteButton = + deleteMessageBox.addButton(tr("Delete"), QMessageBox::ActionRole); + + deleteMessageBox.addButton(QMessageBox::Cancel); + + deleteMessageBox.exec(); + + if (deleteMessageBox.clickedButton() == deleteButton) { + // Make sure we have no groups open + while (!mLauncherConfig->group().isEmpty()) { + mLauncherConfig->endGroup(); + } + + mLauncherConfig->beginGroup("Profiles"); + + // Open the profile-name subgroup + mLauncherConfig->beginGroup(profile); + mLauncherConfig->remove(""); // Clear the subgroup + mLauncherConfig->endGroup(); + mLauncherConfig->endGroup(); + + // Remove the profile from the combobox + mProfilesComboBox->removeItem(mProfilesComboBox->findText(profile)); + } +} + +void DataFilesPage::moveUp() +{ + // Shift the selected plugins up one row + + if (!mPluginsTable->selectionModel()->hasSelection()) { + return; + } + + QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes(); + + //sort selection ascending because selectedIndexes returns an unsorted list + qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan); + + QModelIndex firstIndex = mPluginsProxyModel->mapToSource(selectedIndexes.first()); + + // Check if the first selected plugin is the top one + if (firstIndex.row() == 0) { + return; + } + + foreach (const QModelIndex ¤tIndex, selectedIndexes) { + const QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(currentIndex); + int currentRow = sourceModelIndex.row(); + + if (sourceModelIndex.isValid() && currentRow > 0) { + mPluginsModel->insertRow((currentRow - 1), mPluginsModel->takeRow(currentRow)); + + const QModelIndex targetIndex = mPluginsModel->index((currentRow - 1), 0, QModelIndex()); + + mPluginsTable->selectionModel()->select(targetIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows); + scrollToSelection(); + } + } +} + +void DataFilesPage::moveDown() +{ + // Shift the selected plugins down one row + + if (!mPluginsTable->selectionModel()->hasSelection()) { + return; + } + + QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes(); + + //sort selection descending because selectedIndexes returns an unsorted list + qSort(selectedIndexes.begin(), selectedIndexes.end(), rowGreaterThan); + + const QModelIndex lastIndex = mPluginsProxyModel->mapToSource(selectedIndexes.first()); + + // Check if last selected plugin is bottom one + if ((lastIndex.row() + 1) == mPluginsModel->rowCount()) { + return; + } + + foreach (const QModelIndex ¤tIndex, selectedIndexes) { + const QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(currentIndex); + int currentRow = sourceModelIndex.row(); + + if (sourceModelIndex.isValid() && (currentRow + 1) < mPluginsModel->rowCount()) { + mPluginsModel->insertRow((currentRow + 1), mPluginsModel->takeRow(currentRow)); + + const QModelIndex targetIndex = mPluginsModel->index((currentRow + 1), 0, QModelIndex()); + + mPluginsTable->selectionModel()->select(targetIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows); + scrollToSelection(); + } + } +} + +void DataFilesPage::moveTop() +{ + // Move the selection to the top of the table + + if (!mPluginsTable->selectionModel()->hasSelection()) { + return; + } + + QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes(); + + //sort selection ascending because selectedIndexes returns an unsorted list + qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan); + + QModelIndex firstIndex = mPluginsProxyModel->mapToSource(selectedIndexes.first()); + + // Check if the first selected plugin is the top one + if (firstIndex.row() == 0) { + return; + } + + for (int i=0; i < selectedIndexes.count(); ++i) { + + const QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(selectedIndexes.at(i)); + + int currentRow = sourceModelIndex.row(); + + if (sourceModelIndex.isValid() && currentRow > 0) { + + mPluginsModel->insertRow(i, mPluginsModel->takeRow(currentRow)); + mPluginsTable->selectionModel()->select(mPluginsModel->index(i, 0, QModelIndex()), QItemSelectionModel::Select | QItemSelectionModel::Rows); + mPluginsTable->scrollToTop(); + } + } +} + +void DataFilesPage::moveBottom() +{ + // Move the selection to the bottom of the table + + if (!mPluginsTable->selectionModel()->hasSelection()) { + return; + } + + QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes(); + + //sort selection descending because selectedIndexes returns an unsorted list + qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan); + + const QModelIndex lastIndex = mPluginsProxyModel->mapToSource(selectedIndexes.last()); + + // Check if last selected plugin is bottom one + if ((lastIndex.row() + 1) == mPluginsModel->rowCount()) { + return; + } + + for (int i=0; i < selectedIndexes.count(); ++i) { + + const QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(selectedIndexes.at(i)); + + // Subtract iterations because takeRow shifts the rows below the taken row up + int currentRow = sourceModelIndex.row() - i; + + if (sourceModelIndex.isValid() && (currentRow + 1) < mPluginsModel->rowCount()) { + mPluginsModel->appendRow(mPluginsModel->takeRow(currentRow)); + + // Rowcount starts with 1, row numbers start with 0 + const QModelIndex lastRow = mPluginsModel->index((mPluginsModel->rowCount() -1), 0, QModelIndex()); + + mPluginsTable->selectionModel()->select(lastRow, QItemSelectionModel::Select | QItemSelectionModel::Rows); + mPluginsTable->scrollToBottom(); + } + } +} + +void DataFilesPage::check() +{ + // Check the current selection + + if (!mPluginsTable->selectionModel()->hasSelection()) { + return; + } + + QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes(); + + //sort selection ascending because selectedIndexes returns an unsorted list + qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan); + + foreach (const QModelIndex ¤tIndex, selectedIndexes) { + QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(currentIndex); + + if (sourceModelIndex.isValid()) { + mPluginsModel->setData(sourceModelIndex, Qt::Checked, Qt::CheckStateRole); + } + } +} + +void DataFilesPage::uncheck() +{ + // Uncheck the current selection + + if (!mPluginsTable->selectionModel()->hasSelection()) { + return; + } + + QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes(); + + //sort selection ascending because selectedIndexes returns an unsorted list + qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan); + + foreach (const QModelIndex ¤tIndex, selectedIndexes) { + QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(currentIndex); + + if (sourceModelIndex.isValid()) { + mPluginsModel->setData(sourceModelIndex, Qt::Unchecked, Qt::CheckStateRole); + } + } +} + +void DataFilesPage::refresh() +{ + // Refresh the plugins table + + writeConfig(); + readConfig(); +} + +void DataFilesPage::scrollToSelection() +{ + // Scroll to the selected plugins + + if (!mPluginsTable->selectionModel()->hasSelection()) { + return; + } + + // Get the selected indexes visible by determining the middle index + QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes(); + qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan); + + // The selected rows including non-selected inside selection + unsigned int selectedRows = selectedIndexes.last().row() - selectedIndexes.first().row(); + + // Determine the row which is roughly in the middle of the selection + unsigned int middleRow = selectedIndexes.first().row() + (int)(selectedRows / 2) + 1; + + + const QModelIndex middleIndex = mPluginsProxyModel->mapFromSource(mPluginsModel->index(middleRow, 0, QModelIndex())); + + // Make sure the whole selection is visible + mPluginsTable->scrollTo(selectedIndexes.first()); + mPluginsTable->scrollTo(selectedIndexes.last()); + mPluginsTable->scrollTo(middleIndex); +} + +void DataFilesPage::showContextMenu(const QPoint &point) +{ + + QPoint globalPos = mPluginsTable->mapToGlobal(point); + + QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes(); + + // Show the check/uncheck actions depending on the state of the selected items + mUncheckAction->setEnabled(false); + mCheckAction->setEnabled(false); + + foreach (const QModelIndex ¤tIndex, selectedIndexes) { + if (currentIndex.isValid()) { + + const QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(currentIndex); + + if (!sourceIndex.isValid()) { + return; + } + + const QStandardItem *currentItem = mPluginsModel->itemFromIndex(sourceIndex); + + if (currentItem->checkState() == Qt::Checked) { + mUncheckAction->setEnabled(true); + } else { + mCheckAction->setEnabled(true); + } + } + + } + + // Make sure these are enabled because they might still be disabled + mMoveUpAction->setEnabled(true); + mMoveTopAction->setEnabled(true); + mMoveDownAction->setEnabled(true); + mMoveBottomAction->setEnabled(true); + + QModelIndex firstIndex = mPluginsProxyModel->mapToSource(selectedIndexes.first()); + QModelIndex lastIndex = mPluginsProxyModel->mapToSource(selectedIndexes.last()); + + // Check if selected first item is top row in model + if (firstIndex.row() == 0) { + mMoveUpAction->setEnabled(false); + mMoveTopAction->setEnabled(false); + } + + // Check if last row is bottom row in model + if ((lastIndex.row() + 1) == mPluginsModel->rowCount()) { + mMoveDownAction->setEnabled(false); + mMoveBottomAction->setEnabled(false); + } + + // Show menu + mContextMenu->exec(globalPos); +} + +void DataFilesPage::masterSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + if (mMastersWidget->selectionModel()->hasSelection()) { + + QStringList masters; + QString masterstr; + + // Create a QStringList containing all the masters + const QStringList masterList = selectedMasters(); + + foreach (const QString ¤tMaster, masterList) { + masters.append(currentMaster); + } + + masters.sort(); + masterstr = masters.join(","); // Make a comma-separated QString + + // Iterate over all masters in the datafilesmodel to see if they are selected + for (int r=0; rrowCount(); ++r) { + QModelIndex currentIndex = mDataFilesModel->index(r, 0); + QString master = currentIndex.data().toString(); + + if (currentIndex.isValid()) { + // See if the current master is in the string with selected masters + if (masterstr.contains(master)) + { + // Append the plugins from the current master to pluginsmodel + addPlugins(currentIndex); + } + } + } + } + + // See what plugins to remove + QModelIndexList deselectedIndexes = deselected.indexes(); + + if (!deselectedIndexes.isEmpty()) { + foreach (const QModelIndex ¤tIndex, deselectedIndexes) { + + QString master = currentIndex.data().toString(); + master.prepend("*"); + master.append("*"); + const QList itemList = mDataFilesModel->findItems(master, Qt::MatchWildcard); + + foreach (const QStandardItem *currentItem, itemList) { + QModelIndex index = currentItem->index(); + removePlugins(index); + } + } + } + +} + +void DataFilesPage::addPlugins(const QModelIndex &index) +{ + // Find the plugins in the datafilesmodel and append them to the pluginsmodel + if (!index.isValid()) + return; + + for (int r=0; rrowCount(index); ++r ) { + QModelIndex childIndex = index.child(r, 0); + + if (childIndex.isValid()) { + // Now we see if the pluginsmodel already contains this plugin + const QString childIndexData = QVariant(mDataFilesModel->data(childIndex)).toString(); + + const QList itemList = mPluginsModel->findItems(childIndexData); + + if (itemList.isEmpty()) + { + // Plugin not yet in the pluginsmodel, add it + QStandardItem *item = new QStandardItem(childIndexData); + item->setFlags(item->flags() & ~(Qt::ItemIsDropEnabled)); + item->setCheckable(true); + + mPluginsModel->appendRow(item); + } + } + + } + +} + +void DataFilesPage::removePlugins(const QModelIndex &index) +{ + + if (!index.isValid()) + return; + + for (int r=0; rrowCount(index); ++r) { + QModelIndex childIndex = index.child(r, 0); + + const QList itemList = mPluginsModel->findItems(QVariant(childIndex.data()).toString()); + + if (!itemList.isEmpty()) { + foreach (const QStandardItem *currentItem, itemList) { + mPluginsModel->removeRow(currentItem->row()); + } + } + } + +} + +void DataFilesPage::setCheckState(QModelIndex index) +{ + if (!index.isValid()) + return; + + QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(index); + + if (mPluginsModel->data(sourceModelIndex, Qt::CheckStateRole) == Qt::Checked) { + // Selected row is checked, uncheck it + mPluginsModel->setData(sourceModelIndex, Qt::Unchecked, Qt::CheckStateRole); + } else { + mPluginsModel->setData(sourceModelIndex, Qt::Checked, Qt::CheckStateRole); + } +} + +const QStringList DataFilesPage::selectedMasters() +{ + QStringList masters; + const QList selectedMasters = mMastersWidget->selectedItems(); + + foreach (const QTableWidgetItem *item, selectedMasters) { + masters.append(item->data(Qt::DisplayRole).toString()); + } + + return masters; +} + +const QStringList DataFilesPage::checkedPlugins() +{ + QStringList checkedItems; + + for (int r=0; rrowCount(); ++r ) { + QModelIndex index = mPluginsModel->index(r, 0); + + if (index.isValid()) { + // See if the current item is checked + if (mPluginsModel->data(index, Qt::CheckStateRole) == Qt::Checked) { + checkedItems.append(index.data().toString()); + } + } + } + return checkedItems; +} + +void DataFilesPage::uncheckPlugins() +{ + for (int r=0; rrowCount(); ++r ) { + QModelIndex index = mPluginsModel->index(r, 0); + + if (index.isValid()) { + // See if the current item is checked + if (mPluginsModel->data(index, Qt::CheckStateRole) == Qt::Checked) { + mPluginsModel->setData(index, Qt::Unchecked, Qt::CheckStateRole); + } + } + } +} + +void DataFilesPage::filterChanged(const QString filter) +{ + QRegExp regExp(filter, Qt::CaseInsensitive, QRegExp::FixedString); + mPluginsProxyModel->setFilterRegExp(regExp); +} + +void DataFilesPage::profileChanged(const QString &previous, const QString ¤t) +{ + if (!previous.isEmpty()) { + writeConfig(previous); + mLauncherConfig->sync(); + } + + uncheckPlugins(); + // Deselect the masters + mMastersWidget->selectionModel()->clearSelection(); + readConfig(); +} + +void DataFilesPage::readConfig() +{ + QString profile = mProfilesComboBox->currentText(); + + // Make sure we have no groups open + while (!mLauncherConfig->group().isEmpty()) { + mLauncherConfig->endGroup(); + } + + mLauncherConfig->beginGroup("Profiles"); + mLauncherConfig->beginGroup(profile); + + QStringList childKeys = mLauncherConfig->childKeys(); + QStringList plugins; + + // Sort the child keys numerical instead of alphabetically + // i.e. Plugin1, Plugin2 instead of Plugin1, Plugin10 + qSort(childKeys.begin(), childKeys.end(), naturalSortLessThanCI); + + foreach (const QString &key, childKeys) { + const QString keyValue = mLauncherConfig->value(key).toString(); + + if (key.startsWith("Plugin")) { + plugins.append(keyValue); + continue; + } + + if (key.startsWith("Master")) { + const QList masterList = mMastersWidget->findItems(keyValue, Qt::MatchFixedString); + + if (!masterList.isEmpty()) { + foreach (QTableWidgetItem *currentMaster, masterList) { + mMastersWidget->selectionModel()->select(mMastersWidget->model()->index(currentMaster->row(), 0), QItemSelectionModel::Select); + } + } + } + } + + // Iterate over the plugins to set their checkstate and position + for (int i = 0; i < plugins.size(); ++i) { + const QString plugin = plugins.at(i); + + const QList pluginList = mPluginsModel->findItems(plugin); + + if (!pluginList.isEmpty()) + { + foreach (const QStandardItem *currentPlugin, pluginList) { + mPluginsModel->setData(currentPlugin->index(), Qt::Checked, Qt::CheckStateRole); + + // Move the plugin to the position specified in the config file + mPluginsModel->insertRow(i, mPluginsModel->takeRow(currentPlugin->row())); + } + } + } + +} + +void DataFilesPage::writeConfig(QString profile) +{ + if (profile.isEmpty()) { + profile = mProfilesComboBox->currentText(); + } + + if (profile.isEmpty()) { + return; + } + + // Make sure we have no groups open + while (!mLauncherConfig->group().isEmpty()) { + mLauncherConfig->endGroup(); + } + + mLauncherConfig->beginGroup("Profiles"); + mLauncherConfig->setValue("CurrentProfile", profile); + + // Open the profile-name subgroup + mLauncherConfig->beginGroup(profile); + mLauncherConfig->remove(""); // Clear the subgroup + + // First write the masters to the config + const QStringList masterList = selectedMasters(); + + // We don't use foreach because we need i + for (int i = 0; i < masterList.size(); ++i) { + const QString master = masterList.at(i); + mLauncherConfig->setValue(QString("Master%0").arg(i), master); + } + + // Now write all checked plugins + const QStringList plugins = checkedPlugins(); + + for (int i = 0; i < plugins.size(); ++i) + { + mLauncherConfig->setValue(QString("Plugin%1").arg(i), plugins.at(i)); + } + + mLauncherConfig->endGroup(); + mLauncherConfig->endGroup(); + +} diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp new file mode 100644 index 0000000000..2d0a385a79 --- /dev/null +++ b/apps/launcher/datafilespage.hpp @@ -0,0 +1,93 @@ +#ifndef DATAFILESPAGE_H +#define DATAFILESPAGE_H + +#include +#include +#include "combobox.hpp" + +class QTableWidget; +class QStandardItemModel; +class QItemSelection; +class QItemSelectionModel; +class QSortFilterProxyModel; +class QStringListModel; +class QSettings; +class QAction; +class QToolBar; +class QMenu; +class PluginsModel; +class PluginsView; +class ComboBox; + +class DataFilesPage : public QWidget +{ + Q_OBJECT + +public: + DataFilesPage(QWidget *parent = 0); + + ComboBox *mProfilesComboBox; + QSettings *mLauncherConfig; + + const QStringList checkedPlugins(); + const QStringList selectedMasters(); + void setupConfig(); + void readConfig(); + void writeConfig(QString profile = QString()); + + void setupDataFiles(const QStringList &paths, bool strict); + +public slots: + void masterSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void setCheckState(QModelIndex index); + + void filterChanged(const QString filter); + void showContextMenu(const QPoint &point); + void profileChanged(const QString &previous, const QString ¤t); + + // Action slots + void newProfile(); + void copyProfile(); + void deleteProfile(); + void moveUp(); + void moveDown(); + void moveTop(); + void moveBottom(); + void check(); + void uncheck(); + void refresh(); + +private: + QTableWidget *mMastersWidget; + PluginsView *mPluginsTable; + + QStandardItemModel *mDataFilesModel; + PluginsModel *mPluginsModel; + + QSortFilterProxyModel *mPluginsProxyModel; + QItemSelectionModel *mPluginsSelectModel; + + QToolBar *mProfileToolBar; + QMenu *mContextMenu; + + QAction *mNewProfileAction; + QAction *mCopyProfileAction; + QAction *mDeleteProfileAction; + + QAction *mMoveUpAction; + QAction *mMoveDownAction; + QAction *mMoveTopAction; + QAction *mMoveBottomAction; + QAction *mCheckAction; + QAction *mUncheckAction; + + void addPlugins(const QModelIndex &index); + void removePlugins(const QModelIndex &index); + void uncheckPlugins(); + void createActions(); + + void scrollToSelection(); + +}; + +#endif diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp new file mode 100644 index 0000000000..d97a915c9e --- /dev/null +++ b/apps/launcher/graphicspage.cpp @@ -0,0 +1,484 @@ +#include + +#include + +#include "graphicspage.hpp" + +GraphicsPage::GraphicsPage(QWidget *parent) : QWidget(parent) +{ + QGroupBox *rendererGroup = new QGroupBox(tr("Renderer"), this); + + QLabel *rendererLabel = new QLabel(tr("Rendering Subsystem:"), rendererGroup); + mRendererComboBox = new QComboBox(rendererGroup); + + // Layout for the combobox and label + QGridLayout *renderSystemLayout = new QGridLayout(); + renderSystemLayout->addWidget(rendererLabel, 0, 0, 1, 1); + renderSystemLayout->addWidget(mRendererComboBox, 0, 1, 1, 1); + + mRendererStackedWidget = new QStackedWidget(rendererGroup); + + QVBoxLayout *rendererGroupLayout = new QVBoxLayout(rendererGroup); + + rendererGroupLayout->addLayout(renderSystemLayout); + rendererGroupLayout->addWidget(mRendererStackedWidget); + + // Display + QGroupBox *displayGroup = new QGroupBox(tr("Display"), this); + + mDisplayStackedWidget = new QStackedWidget(displayGroup); + + QVBoxLayout *displayGroupLayout = new QVBoxLayout(displayGroup); + QSpacerItem *vSpacer3 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Expanding); + + displayGroupLayout->addWidget(mDisplayStackedWidget); + displayGroupLayout->addItem(vSpacer3); + + // Layout for the whole page + QVBoxLayout *pageLayout = new QVBoxLayout(this); + + pageLayout->addWidget(rendererGroup); + pageLayout->addWidget(displayGroup); + + connect(mRendererComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(rendererChanged(const QString&))); + + createPages(); + setupConfig(); + setupOgre(); + + readConfig(); +} + +void GraphicsPage::createPages() +{ + // OpenGL rendering settings + QWidget *mOGLRendererPage = new QWidget(); + + QLabel *OGLRTTLabel = new QLabel(tr("Preferred RTT Mode:"), mOGLRendererPage); + mOGLRTTComboBox = new QComboBox(mOGLRendererPage); + + QLabel *OGLAntiAliasingLabel = new QLabel(tr("Antialiasing:"), mOGLRendererPage); + mOGLAntiAliasingComboBox = new QComboBox(mOGLRendererPage); + + QGridLayout *OGLRendererLayout = new QGridLayout(mOGLRendererPage); + QSpacerItem *vSpacer1 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Expanding); + + OGLRendererLayout->addWidget(OGLRTTLabel, 0, 0, 1, 1); + OGLRendererLayout->addWidget(mOGLRTTComboBox, 0, 1, 1, 1); + OGLRendererLayout->addWidget(OGLAntiAliasingLabel, 1, 0, 1, 1); + OGLRendererLayout->addWidget(mOGLAntiAliasingComboBox, 1, 1, 1, 1); + OGLRendererLayout->addItem(vSpacer1, 2, 1, 1, 1); + + // OpenGL display settings + QWidget *mOGLDisplayPage = new QWidget(); + + QLabel *OGLResolutionLabel = new QLabel(tr("Resolution:"), mOGLDisplayPage); + mOGLResolutionComboBox = new QComboBox(mOGLDisplayPage); + + QLabel *OGLFrequencyLabel = new QLabel(tr("Display Frequency:"), mOGLDisplayPage); + mOGLFrequencyComboBox = new QComboBox(mOGLDisplayPage); + + mOGLVSyncCheckBox = new QCheckBox(tr("Vertical Sync"), mOGLDisplayPage); + mOGLFullScreenCheckBox = new QCheckBox(tr("Full Screen"), mOGLDisplayPage); + + QGridLayout *OGLDisplayLayout = new QGridLayout(mOGLDisplayPage); + QSpacerItem *vSpacer2 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Minimum); + + OGLDisplayLayout->addWidget(OGLResolutionLabel, 0, 0, 1, 1); + OGLDisplayLayout->addWidget(mOGLResolutionComboBox, 0, 1, 1, 1); + OGLDisplayLayout->addWidget(OGLFrequencyLabel, 1, 0, 1, 1); + OGLDisplayLayout->addWidget(mOGLFrequencyComboBox, 1, 1, 1, 1); + + OGLDisplayLayout->addItem(vSpacer2, 2, 1, 1, 1); + OGLDisplayLayout->addWidget(mOGLVSyncCheckBox, 3, 0, 1, 1); + OGLDisplayLayout->addWidget(mOGLFullScreenCheckBox, 6, 0, 1, 1); + + // Direct3D rendering settings + QWidget *mD3DRendererPage = new QWidget(); + + QLabel *D3DRenderDeviceLabel = new QLabel(tr("Rendering Device:"), mD3DRendererPage); + mD3DRenderDeviceComboBox = new QComboBox(mD3DRendererPage); + + QLabel *D3DAntiAliasingLabel = new QLabel(tr("Antialiasing:"), mD3DRendererPage); + mD3DAntiAliasingComboBox = new QComboBox(mD3DRendererPage); + + QLabel *D3DFloatingPointLabel = new QLabel(tr("Floating-point Mode:"), mD3DRendererPage); + mD3DFloatingPointComboBox = new QComboBox(mD3DRendererPage); + + mD3DNvPerfCheckBox = new QCheckBox(tr("Allow NVPerfHUD"), mD3DRendererPage); + + QGridLayout *D3DRendererLayout = new QGridLayout(mD3DRendererPage); + QSpacerItem *vSpacer3 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Minimum); + QSpacerItem *vSpacer4 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Expanding); + + D3DRendererLayout->addWidget(D3DRenderDeviceLabel, 0, 0, 1, 1); + D3DRendererLayout->addWidget(mD3DRenderDeviceComboBox, 0, 1, 1, 1); + D3DRendererLayout->addWidget(D3DAntiAliasingLabel, 1, 0, 1, 1); + D3DRendererLayout->addWidget(mD3DAntiAliasingComboBox, 1, 1, 1, 1); + D3DRendererLayout->addWidget(D3DFloatingPointLabel, 2, 0, 1, 1); + D3DRendererLayout->addWidget(mD3DFloatingPointComboBox, 2, 1, 1, 1); + D3DRendererLayout->addItem(vSpacer3, 3, 1, 1, 1); + D3DRendererLayout->addWidget(mD3DNvPerfCheckBox, 4, 0, 1, 1); + D3DRendererLayout->addItem(vSpacer4, 5, 1, 1, 1); + + // Direct3D display settings + QWidget *mD3DDisplayPage = new QWidget(); + + QLabel *D3DResolutionLabel = new QLabel(tr("Resolution:"), mD3DDisplayPage); + mD3DResolutionComboBox = new QComboBox(mD3DDisplayPage); + + mD3DVSyncCheckBox = new QCheckBox(tr("Vertical Sync"), mD3DDisplayPage); + mD3DFullScreenCheckBox = new QCheckBox(tr("Full Screen"), mD3DDisplayPage); + + QGridLayout *mD3DDisplayLayout = new QGridLayout(mD3DDisplayPage); + QSpacerItem *vSpacer5 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Minimum); + + mD3DDisplayLayout->addWidget(D3DResolutionLabel, 0, 0, 1, 1); + mD3DDisplayLayout->addWidget(mD3DResolutionComboBox, 0, 1, 1, 1); + mD3DDisplayLayout->addItem(vSpacer5, 1, 1, 1, 1); + mD3DDisplayLayout->addWidget(mD3DVSyncCheckBox, 2, 0, 1, 1); + mD3DDisplayLayout->addWidget(mD3DFullScreenCheckBox, 5, 0, 1, 1); + + // Add the created pages + mRendererStackedWidget->addWidget(mOGLRendererPage); + mRendererStackedWidget->addWidget(mD3DRendererPage); + + mDisplayStackedWidget->addWidget(mOGLDisplayPage); + mDisplayStackedWidget->addWidget(mD3DDisplayPage); +} + +void GraphicsPage::setupConfig() +{ + QString ogreCfg = "./ogre.cfg"; + + QFile file(ogreCfg); + + if (!file.exists()) { + ogreCfg = QString::fromStdString(Files::getPath(Files::Path_ConfigUser, + "openmw", "ogre.cfg")); + } + + mOgreConfig = new QSettings(ogreCfg, QSettings::IniFormat); + +} + +void GraphicsPage::setupOgre() +{ + QString pluginCfg = "./plugins.cfg"; + QFile file(pluginCfg); + + if (!file.exists()) { + pluginCfg = QString::fromStdString(Files::getPath(Files::Path_ConfigUser, + "openmw", "plugins.cfg")); + } + + // Reopen the file from user directory + file.setFileName(pluginCfg); + + if (!file.exists()) { + // There's no plugins.cfg in the user directory, use global directory + pluginCfg = QString::fromStdString(Files::getPath(Files::Path_ConfigGlobal, + "openmw", "plugins.cfg")); + } + + // Create a log manager so we can surpress debug text to stdout/stderr + Ogre::LogManager* logMgr = OGRE_NEW Ogre::LogManager; + logMgr->createLog("launcherOgre.log", true, false, false); + + try + { + mOgre = new Ogre::Root(pluginCfg.toStdString()); + } + catch(Ogre::Exception &ex) + { + QString ogreError = QString::fromStdString(ex.getFullDescription().c_str()); + QMessageBox msgBox; + msgBox.setWindowTitle("Error creating Ogre::Root"); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Failed to create the Ogre::Root object

\ + Make sure the plugins.cfg is present and valid.

\ + Press \"Show Details...\" for more information.
")); + msgBox.setDetailedText(ogreError); + msgBox.exec(); + + qCritical("Error creating Ogre::Root, the error reported was:\n %s", qPrintable(ogreError)); + + std::exit(1); + } + + // Get the available renderers and put them in the combobox + const Ogre::RenderSystemList &renderers = mOgre->getAvailableRenderers(); + + for (Ogre::RenderSystemList::const_iterator r = renderers.begin(); r != renderers.end(); ++r) { + mSelectedRenderSystem = *r; + mRendererComboBox->addItem((*r)->getName().c_str()); + } + + int index = mRendererComboBox->findText(mOgreConfig->value("Render System").toString()); + + if ( index != -1) { + mRendererComboBox->setCurrentIndex(index); + } + + // Create separate rendersystems + QString openGLName = mRendererComboBox->itemText(mRendererComboBox->findText(QString("OpenGL"), Qt::MatchStartsWith)); + QString direct3DName = mRendererComboBox->itemText(mRendererComboBox->findText(QString("Direct3D"), Qt::MatchStartsWith)); + + mOpenGLRenderSystem = mOgre->getRenderSystemByName(openGLName.toStdString()); + mDirect3DRenderSystem = mOgre->getRenderSystemByName(direct3DName.toStdString()); + + if (!mOpenGLRenderSystem && !mDirect3DRenderSystem) { + QMessageBox msgBox; + msgBox.setWindowTitle("Error creating renderer"); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not select a valid render system

\ + Please make sure the plugins.cfg file exists and contains a valid rendering plugin.
")); + msgBox.exec(); + + std::exit(1); + } + + // Now fill the GUI elements + // OpenGL + if (mOpenGLRenderSystem) { + mOGLRTTComboBox->addItems(getAvailableOptions(QString("RTT Preferred Mode"), mOpenGLRenderSystem)); + mOGLAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mOpenGLRenderSystem)); + + QStringList videoModes = getAvailableOptions(QString("Video Mode"), mOpenGLRenderSystem); + // Remove extraneous spaces + videoModes.replaceInStrings(QRegExp("\\s{2,}"), QString(" ")); + videoModes.replaceInStrings(QRegExp("^\\s"), QString()); + + mOGLResolutionComboBox->addItems(videoModes); + mOGLFrequencyComboBox->addItems(getAvailableOptions(QString("Display Frequency"), mOpenGLRenderSystem)); + } + + // Direct3D + if (mDirect3DRenderSystem) { + mD3DRenderDeviceComboBox->addItems(getAvailableOptions(QString("Rendering Device"), mDirect3DRenderSystem)); + mD3DAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mDirect3DRenderSystem)); + mD3DFloatingPointComboBox->addItems(getAvailableOptions(QString("Floating-point mode"), mDirect3DRenderSystem)); + + QStringList videoModes = getAvailableOptions(QString("Video Mode"), mDirect3DRenderSystem); + // Remove extraneous spaces + videoModes.replaceInStrings(QRegExp("\\s{2,}"), QString(" ")); + videoModes.replaceInStrings(QRegExp("^\\s"), QString()); + mD3DResolutionComboBox->addItems(videoModes); + } +} + +void GraphicsPage::readConfig() +{ + // Read the config file settings + if (mOpenGLRenderSystem) { + + int index = mOGLRTTComboBox->findText(getConfigValue("RTT Preferred Mode", mOpenGLRenderSystem)); + if ( index != -1) { + mOGLRTTComboBox->setCurrentIndex(index); + } + + index = mOGLAntiAliasingComboBox->findText(getConfigValue("FSAA", mOpenGLRenderSystem)); + if ( index != -1){ + mOGLAntiAliasingComboBox->setCurrentIndex(index); + } + + index = mOGLResolutionComboBox->findText(getConfigValue("Video Mode", mOpenGLRenderSystem)); + if ( index != -1) { + mOGLResolutionComboBox->setCurrentIndex(index); + } + + index = mOGLFrequencyComboBox->findText(getConfigValue("Display Frequency", mOpenGLRenderSystem)); + if ( index != -1) { + mOGLFrequencyComboBox->setCurrentIndex(index); + } + + // Now we do the same for the checkboxes + if (getConfigValue("VSync", mOpenGLRenderSystem) == QLatin1String("Yes")) { + mOGLVSyncCheckBox->setCheckState(Qt::Checked); + } + + if (getConfigValue("Full Screen", mOpenGLRenderSystem) == QLatin1String("Yes")) { + mOGLFullScreenCheckBox->setCheckState(Qt::Checked); + } + } + + if (mDirect3DRenderSystem) { + + int index = mD3DRenderDeviceComboBox->findText(getConfigValue("Rendering Device", mDirect3DRenderSystem)); + if ( index != -1) { + mD3DRenderDeviceComboBox->setCurrentIndex(index); + } + + index = mD3DAntiAliasingComboBox->findText(getConfigValue("FSAA", mDirect3DRenderSystem)); + if ( index != -1) { + mD3DAntiAliasingComboBox->setCurrentIndex(index); + } + + index = mD3DFloatingPointComboBox->findText(getConfigValue("Floating-point mode", mDirect3DRenderSystem)); + if ( index != -1) { + mD3DFloatingPointComboBox->setCurrentIndex(index); + } + + index = mD3DResolutionComboBox->findText(getConfigValue("Video Mode", mDirect3DRenderSystem)); + if ( index != -1) { + mD3DResolutionComboBox->setCurrentIndex(index); + } + + if (getConfigValue("Allow NVPerfHUD", mDirect3DRenderSystem) == QLatin1String("Yes")) { + mD3DNvPerfCheckBox->setCheckState(Qt::Checked); + } + + if (getConfigValue("VSync", mDirect3DRenderSystem) == QLatin1String("Yes")) { + mD3DVSyncCheckBox->setCheckState(Qt::Checked); + } + + if (getConfigValue("Full Screen", mDirect3DRenderSystem) == QLatin1String("Yes")) { + mD3DFullScreenCheckBox->setCheckState(Qt::Checked); + } + } +} + +void GraphicsPage::writeConfig() +{ + // Write the config file settings + + // Custom write method: We cannot use QSettings because it does not accept spaces + QFile file(mOgreConfig->fileName()); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { + // File could not be opened, + QMessageBox msgBox; + msgBox.setWindowTitle("Error opening Ogre configuration file"); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not open %0

\ + Please make sure you have the right permissions and try again.
").arg(file.fileName())); + msgBox.exec(); + return; + } + + QTextStream out(&file); + + out << "Render System=" << mSelectedRenderSystem->getName().c_str() << endl << endl; + + if (mDirect3DRenderSystem) { + QString direct3DName = mDirect3DRenderSystem->getName().c_str(); + direct3DName.prepend("["); + direct3DName.append("]"); + out << direct3DName << endl; + + if (mD3DNvPerfCheckBox->checkState() == Qt::Checked) { + out << "Allow NVPerfHUD=Yes" << endl; + } else { + out << "Allow NVPerfHUD=No" << endl; + } + + out << "FSAA=" << mD3DAntiAliasingComboBox->currentText() << endl; + out << "Floating-point mode=" << mD3DFloatingPointComboBox->currentText() << endl; + + if (mD3DFullScreenCheckBox->checkState() == Qt::Checked) { + out << "Full Screen=Yes" << endl; + } else { + out << "Full Screen=No" << endl; + } + + out << "Rendering Device=" << mD3DRenderDeviceComboBox->currentText() << endl; + out << "Resource Creation Policy=Create on all devices" << endl; + + if (mD3DVSyncCheckBox->checkState() == Qt::Checked) { + out << "VSync=Yes" << endl; + } else { + out << "VSync=No" << endl; + } + + out << "VSync Interval=1" << endl; + out << "Video Mode=" << mD3DResolutionComboBox->currentText() << endl; + out << "sRGB Gamma Conversion=No" << endl; + } + + out << endl; + + if (mOpenGLRenderSystem) { + QString openGLName = mOpenGLRenderSystem->getName().c_str(); + openGLName.prepend("["); + openGLName.append("]"); + out << openGLName << endl; + + out << "Colour Depth=32" << endl; + out << "Display Frequency=" << mOGLFrequencyComboBox->currentText() << endl; + out << "FSAA=" << mOGLAntiAliasingComboBox->currentText() << endl; + + if (mOGLFullScreenCheckBox->checkState() == Qt::Checked) { + out << "Full Screen=Yes" << endl; + } else { + out << "Full Screen=No" << endl; + } + + out << "RTT Preferred Mode=" << mOGLRTTComboBox->currentText() << endl; + + if (mOGLVSyncCheckBox->checkState() == Qt::Checked) { + out << "VSync=Yes" << endl; + } else { + out << "VSync=No" << endl; + } + + out << "VSync Interval=1" << endl; + out << "Video Mode=" << mOGLResolutionComboBox->currentText() << endl; + out << "sRGB Gamma Conversion=No" << endl; + } + + file.close(); + +} + +QString GraphicsPage::getConfigValue(const QString &key, Ogre::RenderSystem *renderer) +{ + QString result; + + mOgreConfig->beginGroup(renderer->getName().c_str()); + result = mOgreConfig->value(key).toString(); + mOgreConfig->endGroup(); + + return result; +} + +QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer) +{ + QStringList result; + + uint row = 0; + Ogre::ConfigOptionMap options = renderer->getConfigOptions(); + + for (Ogre::ConfigOptionMap::iterator i = options.begin (); i != options.end (); i++, row++) + { + Ogre::StringVector::iterator opt_it; + uint idx = 0; + for (opt_it = i->second.possibleValues.begin (); + opt_it != i->second.possibleValues.end (); opt_it++, idx++) + { + + if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0) + result << (*opt_it).c_str(); + } + + } + + return result; +} + +void GraphicsPage::rendererChanged(const QString &renderer) +{ + if (renderer.contains("Direct3D")) { + mRendererStackedWidget->setCurrentIndex(1); + mDisplayStackedWidget->setCurrentIndex(1); + } + + if (renderer.contains("OpenGL")) { + mRendererStackedWidget->setCurrentIndex(0); + mDisplayStackedWidget->setCurrentIndex(0); + } + + mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString()); +} \ No newline at end of file diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp new file mode 100644 index 0000000000..49ed92ac0b --- /dev/null +++ b/apps/launcher/graphicspage.hpp @@ -0,0 +1,69 @@ +#ifndef GRAPHICSPAGE_H +#define GRAPHICSPAGE_H + +#include + +#include +#include +#include +#include + +class QComboBox; +class QCheckBox; +class QStackedWidget; +class QSettings; + +class GraphicsPage : public QWidget +{ + Q_OBJECT + +public: + GraphicsPage(QWidget *parent = 0); + + QSettings *mOgreConfig; + + void writeConfig(); + +public slots: + void rendererChanged(const QString &renderer); + +private: + Ogre::Root *mOgre; + Ogre::RenderSystem *mSelectedRenderSystem; + Ogre::RenderSystem *mOpenGLRenderSystem; + Ogre::RenderSystem *mDirect3DRenderSystem; + + QComboBox *mRendererComboBox; + + QStackedWidget *mRendererStackedWidget; + QStackedWidget *mDisplayStackedWidget; + + // OpenGL + QComboBox *mOGLRTTComboBox; + QComboBox *mOGLAntiAliasingComboBox; + QComboBox *mOGLResolutionComboBox; + QComboBox *mOGLFrequencyComboBox; + + QCheckBox *mOGLVSyncCheckBox; + QCheckBox *mOGLFullScreenCheckBox; + + // Direct3D + QComboBox *mD3DRenderDeviceComboBox; + QComboBox *mD3DAntiAliasingComboBox; + QComboBox *mD3DFloatingPointComboBox; + QComboBox *mD3DResolutionComboBox; + + QCheckBox *mD3DNvPerfCheckBox; + QCheckBox *mD3DVSyncCheckBox; + QCheckBox *mD3DFullScreenCheckBox; + + QString getConfigValue(const QString &key, Ogre::RenderSystem *renderer); + QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer); + + void createPages(); + void setupConfig(); + void setupOgre(); + void readConfig(); +}; + +#endif diff --git a/apps/launcher/launcher.pro b/apps/launcher/launcher.pro new file mode 100644 index 0000000000..0b4e4618d8 --- /dev/null +++ b/apps/launcher/launcher.pro @@ -0,0 +1,30 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Fri Jun 24 21:14:15 2011 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +HEADERS += combobox.hpp \ + datafilespage.hpp \ + graphicspage.hpp \ + lineedit.hpp \ + maindialog.hpp \ + naturalsort.hpp \ + playpage.hpp \ + pluginsmodel.hpp \ + pluginsview.hpp +SOURCES += datafilespage.cpp \ + graphicspage.cpp \ + lineedit.cpp \ + main.cpp \ + maindialog.cpp \ + naturalsort.cpp \ + playpage.cpp \ + pluginsmodel.cpp \ + pluginsview.cpp +RESOURCES += resources.qrc +win32:RC_FILE = launcher.rc diff --git a/apps/launcher/launcher.rc b/apps/launcher/launcher.rc new file mode 100644 index 0000000000..efe86e4da4 --- /dev/null +++ b/apps/launcher/launcher.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "resources/images/openmw.ico" diff --git a/apps/launcher/lineedit.cpp b/apps/launcher/lineedit.cpp new file mode 100644 index 0000000000..254c09fce3 --- /dev/null +++ b/apps/launcher/lineedit.cpp @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (c) 2007 Trolltech ASA +** +** Use, modification and distribution is allowed without limitation, +** warranty, liability or support of any kind. +** +****************************************************************************/ + +#include "lineedit.hpp" +#include +#include + +LineEdit::LineEdit(QWidget *parent) + : QLineEdit(parent) +{ + clearButton = new QToolButton(this); + QPixmap pixmap(":images/clear.png"); + clearButton->setIcon(QIcon(pixmap)); + clearButton->setIconSize(pixmap.size()); + clearButton->setCursor(Qt::ArrowCursor); + clearButton->setStyleSheet("QToolButton { border: none; padding: 0px; }"); + clearButton->hide(); + connect(clearButton, SIGNAL(clicked()), this, SLOT(clear())); + connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateCloseButton(const QString&))); + int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + setStyleSheet(QString("QLineEdit { padding-right: %1px; } ").arg(clearButton->sizeHint().width() + frameWidth + 1)); + QSize msz = minimumSizeHint(); + setMinimumSize(qMax(msz.width(), clearButton->sizeHint().height() + frameWidth * 2 + 2), + qMax(msz.height(), clearButton->sizeHint().height() + frameWidth * 2 + 2)); +} + +void LineEdit::resizeEvent(QResizeEvent *) +{ + QSize sz = clearButton->sizeHint(); + int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + clearButton->move(rect().right() - frameWidth - sz.width(), + (rect().bottom() + 1 - sz.height())/2); +} + +void LineEdit::updateCloseButton(const QString& text) +{ + clearButton->setVisible(!text.isEmpty()); +} + + diff --git a/apps/launcher/lineedit.hpp b/apps/launcher/lineedit.hpp new file mode 100644 index 0000000000..7a96a523c8 --- /dev/null +++ b/apps/launcher/lineedit.hpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (c) 2007 Trolltech ASA +** +** Use, modification and distribution is allowed without limitation, +** warranty, liability or support of any kind. +** +****************************************************************************/ + +#ifndef LINEEDIT_H +#define LINEEDIT_H + +#include + +class QToolButton; + +class LineEdit : public QLineEdit +{ + Q_OBJECT + +public: + LineEdit(QWidget *parent = 0); + +protected: + void resizeEvent(QResizeEvent *); + +private slots: + void updateCloseButton(const QString &text); + +private: + QToolButton *clearButton; +}; + +#endif // LIENEDIT_H + diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp new file mode 100644 index 0000000000..4e438a4db1 --- /dev/null +++ b/apps/launcher/main.cpp @@ -0,0 +1,35 @@ +#include +#include +#include + +#include "maindialog.hpp" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + // Now we make sure the current dir is set to application path + QDir dir(QCoreApplication::applicationDirPath()); + + #if defined(Q_OS_MAC) + if (dir.dirName() == "MacOS") { + dir.cdUp(); + dir.cdUp(); + dir.cdUp(); + } + #endif + + QDir::setCurrent(dir.absolutePath()); + + // Load the stylesheet + QFile file("./launcher.qss"); + + file.open(QFile::ReadOnly); + QString styleSheet = QLatin1String(file.readAll()); + app.setStyleSheet(styleSheet); + + MainDialog dialog; + return dialog.exec(); + +} + diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp new file mode 100644 index 0000000000..f51b3b5497 --- /dev/null +++ b/apps/launcher/maindialog.cpp @@ -0,0 +1,355 @@ +#include + +#include + +#include "maindialog.hpp" +#include "playpage.hpp" +#include "graphicspage.hpp" +#include "datafilespage.hpp" + +MainDialog::MainDialog() +{ + mIconWidget = new QListWidget; + mIconWidget->setObjectName("IconWidget"); + mIconWidget->setViewMode(QListView::IconMode); + mIconWidget->setWrapping(false); + mIconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure + mIconWidget->setIconSize(QSize(48, 48)); + mIconWidget->setMovement(QListView::Static); + + mIconWidget->setMinimumWidth(400); + mIconWidget->setFixedHeight(80); + mIconWidget->setSpacing(4); + mIconWidget->setCurrentRow(0); + mIconWidget->setFlow(QListView::LeftToRight); + + QGroupBox *groupBox = new QGroupBox(this); + QVBoxLayout *groupLayout = new QVBoxLayout(groupBox); + + mPagesWidget = new QStackedWidget(groupBox); + groupLayout->addWidget(mPagesWidget); + + QPushButton *playButton = new QPushButton(tr("Play")); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(this); + buttonBox->setStandardButtons(QDialogButtonBox::Close); + buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole); + + QVBoxLayout *dialogLayout = new QVBoxLayout(this); + dialogLayout->addWidget(mIconWidget); + dialogLayout->addWidget(groupBox); + dialogLayout->addWidget(buttonBox); + + + setWindowTitle(tr("OpenMW Launcher")); + setWindowIcon(QIcon(":/images/openmw.png")); + // Remove what's this? button + setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); + setMinimumSize(QSize(575, 575)); + + connect(buttonBox, SIGNAL(rejected()), this, SLOT(close())); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(play())); + + setupConfig(); + createIcons(); + createPages(); +} + +void MainDialog::createIcons() +{ + if (!QIcon::hasThemeIcon("document-new")) { + QIcon::setThemeName("tango"); + } + + // We create a fallback icon because the default fallback doesn't work + QIcon graphicsIcon = QIcon(":/icons/tango/video-display.png"); + + QListWidgetItem *playButton = new QListWidgetItem(mIconWidget); + playButton->setIcon(QIcon(":/images/openmw.png")); + playButton->setText(tr("Play")); + playButton->setTextAlignment(Qt::AlignCenter); + playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + + QListWidgetItem *graphicsButton = new QListWidgetItem(mIconWidget); + graphicsButton->setIcon(QIcon::fromTheme("video-display", graphicsIcon)); + graphicsButton->setText(tr("Graphics")); + graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute); + graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + + QListWidgetItem *dataFilesButton = new QListWidgetItem(mIconWidget); + dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png")); + dataFilesButton->setText(tr("Data Files")); + dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom); + dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + + connect(mIconWidget, + SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), + this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*))); + +} + +void MainDialog::createPages() +{ + // Various pages + mPlayPage = new PlayPage(this); + mGraphicsPage = new GraphicsPage(this); + mDataFilesPage = new DataFilesPage(this); + + // First we retrieve all data= keys from the config + // We can't use QSettings directly because it + // does not support multiple keys with the same name + QFile file(mGameConfig->fileName()); + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QMessageBox msgBox; + msgBox.setWindowTitle("Error opening OpenMW configuration file"); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not open %0

\ + Please make sure you have the right permissions and try again.
").arg(file.fileName())); + msgBox.exec(); + + QApplication::exit(); // File cannot be opened or created + } + + QTextStream in(&file); + + QStringList dataDirs; + + // Add each data= value + while (!in.atEnd()) { + QString line = in.readLine(); + + if (line.startsWith("data=")) { + dataDirs.append(line.remove("data=")); + } + } + + // Add the data-local= key + QString dataLocal = mGameConfig->value("data-local").toString(); + if (!dataLocal.isEmpty()) { + dataDirs.append(dataLocal); + } + + if (!dataDirs.isEmpty()) { + // Now pass the datadirs on to the DataFilesPage + mDataFilesPage->setupDataFiles(dataDirs, mGameConfig->value("fs-strict").toBool()); + } else { + QMessageBox msgBox; + msgBox.setWindowTitle("Error reading OpenMW configuration file"); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not read the location of the data files

\ + Please make sure OpenMW is correctly configured and try again.
")); + msgBox.exec(); + + QApplication::exit(); // No data or data-local entries in openmw.cfg + } + + // Set the combobox of the play page to imitate the comobox on the datafilespage + mPlayPage->mProfilesComboBox->setModel(mDataFilesPage->mProfilesComboBox->model()); + mPlayPage->mProfilesComboBox->setCurrentIndex(mDataFilesPage->mProfilesComboBox->currentIndex()); + + // Add the pages to the stacked widget + mPagesWidget->addWidget(mPlayPage); + mPagesWidget->addWidget(mGraphicsPage); + mPagesWidget->addWidget(mDataFilesPage); + + // Select the first page + mIconWidget->setCurrentItem(mIconWidget->item(0), QItemSelectionModel::Select); + + connect(mPlayPage->mPlayButton, SIGNAL(clicked()), this, SLOT(play())); + + connect(mPlayPage->mProfilesComboBox, + SIGNAL(currentIndexChanged(int)), + this, SLOT(profileChanged(int))); + + connect(mDataFilesPage->mProfilesComboBox, + SIGNAL(currentIndexChanged(int)), + this, SLOT(profileChanged(int))); + +} + +void MainDialog::profileChanged(int index) +{ + // Just to be sure, should always have a selection + if (!mIconWidget->selectionModel()->hasSelection()) { + return; + } + + QString currentPage = mIconWidget->currentItem()->data(Qt::DisplayRole).toString(); + if (currentPage == QLatin1String("Play")) { + mDataFilesPage->mProfilesComboBox->setCurrentIndex(index); + } + + if (currentPage == QLatin1String("Data Files")) { + mPlayPage->mProfilesComboBox->setCurrentIndex(index); + } +} + +void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) +{ + if (!current) + current = previous; + + mPagesWidget->setCurrentIndex(mIconWidget->row(current)); +} + +void MainDialog::closeEvent(QCloseEvent *event) +{ + // Now write all config files + writeConfig(); + event->accept(); +} + +void MainDialog::play() +{ + // First do a write of all the configs, just to be sure + writeConfig(); + +#ifdef Q_WS_WIN + QString game = "./openmw.exe"; + QFile file(game); +#elif defined(Q_WS_MAC) + QDir dir(QCoreApplication::applicationDirPath()); + QString game = dir.absoluteFilePath("openmw"); + QFile file(game); +#else + QString game = "./openmw"; + QFile file(game); +#endif + + QProcess process; + QFileInfo info(file); + + if (!file.exists()) { + QMessageBox msgBox; + msgBox.setWindowTitle("Error starting OpenMW"); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not find OpenMW

\ + The OpenMW application is not found.
\ + Please make sure OpenMW is installed correctly and try again.
")); + msgBox.exec(); + + return; + } + + if (!info.isExecutable()) { + QMessageBox msgBox; + msgBox.setWindowTitle("Error starting OpenMW"); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not start OpenMW

\ + The OpenMW application is not executable.
\ + Please make sure you have the right permissions and try again.
")); + msgBox.exec(); + + return; + } + + // Start the game + if (!process.startDetached(game)) { + QMessageBox msgBox; + msgBox.setWindowTitle("Error starting OpenMW"); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not start OpenMW

\ + An error occurred while starting OpenMW.

\ + Press \"Show Details...\" for more information.
")); + msgBox.setDetailedText(process.errorString()); + msgBox.exec(); + + return; + } else { + close(); + } +} + +void MainDialog::setupConfig() +{ + // First we read the OpenMW config + QString config = "./openmw.cfg"; + QFile file(config); + + if (!file.exists()) { + config = QString::fromStdString(Files::getPath(Files::Path_ConfigUser, + "openmw", "openmw.cfg")); + } + + file.close(); + + // Open our config file + mGameConfig = new QSettings(config, QSettings::IniFormat); +} + +void MainDialog::writeConfig() +{ + // Write the profiles + mDataFilesPage->writeConfig(); + mDataFilesPage->mLauncherConfig->sync(); + + // Write the graphics settings + mGraphicsPage->writeConfig(); + mGraphicsPage->mOgreConfig->sync(); + + QStringList dataFiles = mDataFilesPage->selectedMasters(); + dataFiles.append(mDataFilesPage->checkedPlugins()); + + // Open the config as a QFile + QFile file(mGameConfig->fileName()); + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + // File cannot be opened or created + QMessageBox msgBox; + msgBox.setWindowTitle("Error opening OpenMW configuration file"); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not open %0

\ + Please make sure you have the right permissions and try again.
").arg(file.fileName())); + msgBox.exec(); + + return; + } + + QTextStream in(&file); + QByteArray buffer; + + // Remove all previous master/plugin entries from config + while (!in.atEnd()) { + QString line = in.readLine(); + if (!line.contains("master") && !line.contains("plugin")) { + buffer += line += "\n"; + } + } + + file.close(); + + // Now we write back the other config entries + if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { + QMessageBox msgBox; + msgBox.setWindowTitle("Error writing OpenMW configuration file"); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not write to %0

\ + Please make sure you have the right permissions and try again.
").arg(file.fileName())); + msgBox.exec(); + + return; + } + + file.write(buffer); + QTextStream out(&file); + + // Write the list of game files to the config + foreach (const QString ¤tFile, dataFiles) { + + if (currentFile.endsWith(QString(".esm"), Qt::CaseInsensitive)) { + out << "master=" << currentFile << endl; + } else if (currentFile.endsWith(QString(".esp"), Qt::CaseInsensitive)) { + out << "plugin=" << currentFile << endl; + } + } + + file.close(); +} diff --git a/apps/launcher/maindialog.hpp b/apps/launcher/maindialog.hpp new file mode 100644 index 0000000000..c6f22e3361 --- /dev/null +++ b/apps/launcher/maindialog.hpp @@ -0,0 +1,46 @@ +#ifndef MAINDIALOG_H +#define MAINDIALOG_H + +#include + +class QListWidget; +class QListWidgetItem; +class QStackedWidget; +class QStringListModel; +class QSettings; + +class PlayPage; +class GraphicsPage; +class DataFilesPage; + +class MainDialog : public QDialog +{ + Q_OBJECT + +public: + MainDialog(); + +public slots: + void changePage(QListWidgetItem *current, QListWidgetItem *previous); + void play(); + void profileChanged(int index); + + +private: + void createIcons(); + void createPages(); + void setupConfig(); + void writeConfig(); + void closeEvent(QCloseEvent *event); + + QListWidget *mIconWidget; + QStackedWidget *mPagesWidget; + + PlayPage *mPlayPage; + GraphicsPage *mGraphicsPage; + DataFilesPage *mDataFilesPage; + + QSettings *mGameConfig; +}; + +#endif diff --git a/apps/launcher/naturalsort.cpp b/apps/launcher/naturalsort.cpp new file mode 100644 index 0000000000..648038ed7f --- /dev/null +++ b/apps/launcher/naturalsort.cpp @@ -0,0 +1,95 @@ +/* + * This file contains code found in the QtGui module of the Qt Toolkit. + * See Qt's qfilesystemmodel source files for more information + */ + +#include "naturalsort.hpp" + +static inline QChar getNextChar(const QString &s, int location) +{ + return (location < s.length()) ? s.at(location) : QChar(); +} + +/*! + * Natural number sort, skips spaces. + * + * Examples: + * 1, 2, 10, 55, 100 + * 01.jpg, 2.jpg, 10.jpg + * + * Note on the algorithm: + * Only as many characters as necessary are looked at and at most they all + * are looked at once. + * + * Slower then QString::compare() (of course) + */ +int naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs) +{ + for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2) { + // skip spaces, tabs and 0's + QChar c1 = getNextChar(s1, l1); + while (c1.isSpace()) + c1 = getNextChar(s1, ++l1); + QChar c2 = getNextChar(s2, l2); + while (c2.isSpace()) + c2 = getNextChar(s2, ++l2); + + if (c1.isDigit() && c2.isDigit()) { + while (c1.digitValue() == 0) + c1 = getNextChar(s1, ++l1); + while (c2.digitValue() == 0) + c2 = getNextChar(s2, ++l2); + + int lookAheadLocation1 = l1; + int lookAheadLocation2 = l2; + int currentReturnValue = 0; + // find the last digit, setting currentReturnValue as we go if it isn't equal + for ( + QChar lookAhead1 = c1, lookAhead2 = c2; + (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length()); + lookAhead1 = getNextChar(s1, ++lookAheadLocation1), + lookAhead2 = getNextChar(s2, ++lookAheadLocation2) + ) { + bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit(); + bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit(); + if (!is1ADigit && !is2ADigit) + break; + if (!is1ADigit) + return -1; + if (!is2ADigit) + return 1; + if (currentReturnValue == 0) { + if (lookAhead1 < lookAhead2) { + currentReturnValue = -1; + } else if (lookAhead1 > lookAhead2) { + currentReturnValue = 1; + } + } + } + if (currentReturnValue != 0) + return currentReturnValue; + } + + if (cs == Qt::CaseInsensitive) { + if (!c1.isLower()) c1 = c1.toLower(); + if (!c2.isLower()) c2 = c2.toLower(); + } + int r = QString::localeAwareCompare(c1, c2); + if (r < 0) + return -1; + if (r > 0) + return 1; + } + // The two strings are the same (02 == 2) so fall back to the normal sort + return QString::compare(s1, s2, cs); +} + +bool naturalSortLessThanCS( const QString &left, const QString &right ) +{ + return (naturalCompare( left, right, Qt::CaseSensitive ) < 0); +} + +bool naturalSortLessThanCI( const QString &left, const QString &right ) +{ + return (naturalCompare( left, right, Qt::CaseInsensitive ) < 0); +} diff --git a/apps/launcher/naturalsort.hpp b/apps/launcher/naturalsort.hpp new file mode 100644 index 0000000000..2d314396f9 --- /dev/null +++ b/apps/launcher/naturalsort.hpp @@ -0,0 +1,9 @@ +#ifndef NATURALSORT_H +#define NATURALSORT_H + +#include + +bool naturalSortLessThanCS( const QString &left, const QString &right ); +bool naturalSortLessThanCI( const QString &left, const QString &right ); + +#endif \ No newline at end of file diff --git a/apps/launcher/playpage.cpp b/apps/launcher/playpage.cpp new file mode 100644 index 0000000000..cb993a8fa0 --- /dev/null +++ b/apps/launcher/playpage.cpp @@ -0,0 +1,43 @@ +#include + +#include "playpage.hpp" + +PlayPage::PlayPage(QWidget *parent) : QWidget(parent) +{ + QWidget *playWidget = new QWidget(this); + playWidget->setObjectName("PlayGroup"); + playWidget->setFixedSize(QSize(425, 375)); + + mPlayButton = new QPushButton(tr("Play"), playWidget); + mPlayButton->setObjectName("PlayButton"); + mPlayButton->setMinimumSize(QSize(200, 50)); + + QLabel *profileLabel = new QLabel(tr("Current Profile:"), playWidget); + profileLabel->setObjectName("ProfileLabel"); + + QPlastiqueStyle *style = new QPlastiqueStyle; + mProfilesComboBox = new QComboBox(playWidget); + mProfilesComboBox->setObjectName("ProfilesComboBox"); + mProfilesComboBox->setStyle(style); + + QGridLayout *playLayout = new QGridLayout(playWidget); + + QSpacerItem *hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + QSpacerItem *hSpacer2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + QSpacerItem *vSpacer1 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + QSpacerItem *vSpacer2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + playLayout->addWidget(mPlayButton, 1, 1, 1, 1); + playLayout->addWidget(profileLabel, 2, 1, 1, 1); + playLayout->addWidget(mProfilesComboBox, 3, 1, 1, 1); + playLayout->addItem(hSpacer1, 2, 0, 1, 1); + playLayout->addItem(hSpacer2, 2, 2, 1, 1); + playLayout->addItem(vSpacer1, 0, 1, 1, 1); + playLayout->addItem(vSpacer2, 4, 1, 1, 1); + + QHBoxLayout *pageLayout = new QHBoxLayout(this); + + pageLayout->addWidget(playWidget); + +} \ No newline at end of file diff --git a/apps/launcher/playpage.hpp b/apps/launcher/playpage.hpp new file mode 100644 index 0000000000..efec6f2b38 --- /dev/null +++ b/apps/launcher/playpage.hpp @@ -0,0 +1,21 @@ +#ifndef PLAYPAGE_H +#define PLAYPAGE_H + +#include + +class QComboBox; +class QPushButton; + +class PlayPage : public QWidget +{ + Q_OBJECT + +public: + PlayPage(QWidget *parent = 0); + + QComboBox *mProfilesComboBox; + QPushButton *mPlayButton; + +}; + +#endif \ No newline at end of file diff --git a/apps/launcher/pluginsmodel.cpp b/apps/launcher/pluginsmodel.cpp new file mode 100644 index 0000000000..86bd53027f --- /dev/null +++ b/apps/launcher/pluginsmodel.cpp @@ -0,0 +1,149 @@ +#include +#include + +#include + +#include "pluginsmodel.hpp" + +PluginsModel::PluginsModel(QObject *parent) : QStandardItemModel(parent) +{ + +} + +void decodeDataRecursive(QDataStream &stream, QStandardItem *item) +{ + int colCount, childCount; + stream >> *item; + stream >> colCount >> childCount; + item->setColumnCount(colCount); + + int childPos = childCount; + + while(childPos > 0) { + childPos--; + QStandardItem *child = new QStandardItem(); + decodeDataRecursive(stream, child); + item->setChild( childPos / colCount, childPos % colCount, child); + } +} + +bool PluginsModel::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) +{ + // Code largely based on QStandardItemModel::dropMimeData + + // check if the action is supported + if (!data || !(action == Qt::CopyAction || action == Qt::MoveAction)) + return false; + + // check if the format is supported + QString format = QLatin1String("application/x-qstandarditemmodeldatalist"); + if (!data->hasFormat(format)) + return QAbstractItemModel::dropMimeData(data, action, row, column, parent); + + if (row > rowCount(parent)) + row = rowCount(parent); + if (row == -1) + row = rowCount(parent); + if (column == -1) + column = 0; + + // decode and insert + QByteArray encoded = data->data(format); + QDataStream stream(&encoded, QIODevice::ReadOnly); + + + //code based on QAbstractItemModel::decodeData + // adapted to work with QStandardItem + int top = std::numeric_limits::max(); + int left = std::numeric_limits::max(); + int bottom = 0; + int right = 0; + QVector rows, columns; + QVector items; + + while (!stream.atEnd()) { + int r, c; + QStandardItem *item = new QStandardItem(); + stream >> r >> c; + decodeDataRecursive(stream, item); + + rows.append(r); + columns.append(c); + items.append(item); + top = qMin(r, top); + left = qMin(c, left); + bottom = qMax(r, bottom); + right = qMax(c, right); + } + + // insert the dragged items into the table, use a bit array to avoid overwriting items, + // since items from different tables can have the same row and column + int dragRowCount = 0; + int dragColumnCount = right - left + 1; + + // Compute the number of continuous rows upon insertion and modify the rows to match + QVector rowsToInsert(bottom + 1); + for (int i = 0; i < rows.count(); ++i) + rowsToInsert[rows.at(i)] = 1; + for (int i = 0; i < rowsToInsert.count(); ++i) { + if (rowsToInsert[i] == 1){ + rowsToInsert[i] = dragRowCount; + ++dragRowCount; + } + } + for (int i = 0; i < rows.count(); ++i) + rows[i] = top + rowsToInsert[rows[i]]; + + QBitArray isWrittenTo(dragRowCount * dragColumnCount); + + // make space in the table for the dropped data + int colCount = columnCount(parent); + if (colCount < dragColumnCount + column) { + insertColumns(colCount, dragColumnCount + column - colCount, parent); + colCount = columnCount(parent); + } + insertRows(row, dragRowCount, parent); + + row = qMax(0, row); + column = qMax(0, column); + + QStandardItem *parentItem = itemFromIndex (parent); + if (!parentItem) + parentItem = invisibleRootItem(); + + QVector newIndexes(items.size()); + // set the data in the table + for (int j = 0; j < items.size(); ++j) { + int relativeRow = rows.at(j) - top; + int relativeColumn = columns.at(j) - left; + int destinationRow = relativeRow + row; + int destinationColumn = relativeColumn + column; + int flat = (relativeRow * dragColumnCount) + relativeColumn; + // if the item was already written to, or we just can't fit it in the table, create a new row + if (destinationColumn >= colCount || isWrittenTo.testBit(flat)) { + destinationColumn = qBound(column, destinationColumn, colCount - 1); + destinationRow = row + dragRowCount; + insertRows(row + dragRowCount, 1, parent); + flat = (dragRowCount * dragColumnCount) + relativeColumn; + isWrittenTo.resize(++dragRowCount * dragColumnCount); + } + if (!isWrittenTo.testBit(flat)) { + newIndexes[j] = index(destinationRow, destinationColumn, parentItem->index()); + isWrittenTo.setBit(flat); + } + } + + for(int k = 0; k < newIndexes.size(); k++) { + if (newIndexes.at(k).isValid()) { + parentItem->setChild(newIndexes.at(k).row(), newIndexes.at(k).column(), items.at(k)); + } else { + delete items.at(k); + } + } + + // The important part, tell the view what is dropped + emit indexesDropped(newIndexes); + + return true; +} diff --git a/apps/launcher/pluginsmodel.hpp b/apps/launcher/pluginsmodel.hpp new file mode 100644 index 0000000000..41df499b53 --- /dev/null +++ b/apps/launcher/pluginsmodel.hpp @@ -0,0 +1,21 @@ +#ifndef PLUGINSMODEL_H +#define PLUGINSMODEL_H + +#include + +class PluginsModel : public QStandardItemModel +{ + Q_OBJECT + +public: + PluginsModel(QObject *parent = 0); + ~PluginsModel() {}; + + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + +signals: + void indexesDropped(QVector indexes); + +}; + +#endif \ No newline at end of file diff --git a/apps/launcher/pluginsview.cpp b/apps/launcher/pluginsview.cpp new file mode 100644 index 0000000000..27af45c569 --- /dev/null +++ b/apps/launcher/pluginsview.cpp @@ -0,0 +1,42 @@ +#include +#include + +#include "pluginsview.hpp" + +PluginsView::PluginsView(QWidget *parent) : QTableView(parent) +{ + setSelectionBehavior(QAbstractItemView::SelectRows); + setSelectionMode(QAbstractItemView::ExtendedSelection); + setEditTriggers(QAbstractItemView::NoEditTriggers); + setAlternatingRowColors(true); + setDragEnabled(true); + setDragDropMode(QAbstractItemView::InternalMove); + setDropIndicatorShown(true); + setDragDropOverwriteMode(false); + setContextMenuPolicy(Qt::CustomContextMenu); + +} + +void PluginsView::startDrag(Qt::DropActions supportedActions) +{ + selectionModel()->select( selectionModel()->selection(), + QItemSelectionModel::Select | QItemSelectionModel::Rows ); + QAbstractItemView::startDrag( supportedActions ); +} + +void PluginsView::setModel(QSortFilterProxyModel *model) +{ + QTableView::setModel(model); + + qRegisterMetaType< QVector >(); + + connect(model->sourceModel(), SIGNAL(indexesDropped(QVector)), + this, SLOT(selectIndexes(QVector)), Qt::QueuedConnection); +} + +void PluginsView::selectIndexes( QVector aIndexes ) +{ + selectionModel()->clearSelection(); + foreach( QPersistentModelIndex pIndex, aIndexes ) + selectionModel()->select( pIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows ); +} diff --git a/apps/launcher/pluginsview.hpp b/apps/launcher/pluginsview.hpp new file mode 100644 index 0000000000..b3dfb79ffb --- /dev/null +++ b/apps/launcher/pluginsview.hpp @@ -0,0 +1,29 @@ +#ifndef PLUGINSVIEW_H +#define PLUGINSVIEW_H + +#include + +#include "pluginsmodel.hpp" + +class QSortFilterProxyModel; + +class PluginsView : public QTableView +{ + Q_OBJECT +public: + PluginsView(QWidget *parent = 0); + + PluginsModel* model() const + { return qobject_cast(QAbstractItemView::model()); } + + void startDrag(Qt::DropActions supportedActions); + void setModel(QSortFilterProxyModel *model); + +public slots: + void selectIndexes(QVector aIndexes); + +}; + +Q_DECLARE_METATYPE(QVector); + +#endif \ No newline at end of file diff --git a/apps/launcher/resources.qrc b/apps/launcher/resources.qrc new file mode 100644 index 0000000000..2b56f80fd6 --- /dev/null +++ b/apps/launcher/resources.qrc @@ -0,0 +1,21 @@ + + + resources/images/clear.png + resources/images/down.png + resources/images/openmw.png + resources/images/openmw-plugin.png + resources/images/openmw-header.png + resources/images/playpage-background.png + + + resources/icons/tango/index.theme + resources/icons/tango/video-display.png + resources/icons/tango/document-new.png + resources/icons/tango/edit-copy.png + resources/icons/tango/edit-delete.png + resources/icons/tango/go-bottom.png + resources/icons/tango/go-down.png + resources/icons/tango/go-top.png + resources/icons/tango/go-up.png + + diff --git a/apps/launcher/resources/icons/tango/document-new.png b/apps/launcher/resources/icons/tango/document-new.png new file mode 100644 index 0000000000..4c3efdd6fa Binary files /dev/null and b/apps/launcher/resources/icons/tango/document-new.png differ diff --git a/apps/launcher/resources/icons/tango/edit-copy.png b/apps/launcher/resources/icons/tango/edit-copy.png new file mode 100644 index 0000000000..8dd48c4949 Binary files /dev/null and b/apps/launcher/resources/icons/tango/edit-copy.png differ diff --git a/apps/launcher/resources/icons/tango/edit-delete.png b/apps/launcher/resources/icons/tango/edit-delete.png new file mode 100644 index 0000000000..d7667c36b4 Binary files /dev/null and b/apps/launcher/resources/icons/tango/edit-delete.png differ diff --git a/apps/launcher/resources/icons/tango/go-bottom.png b/apps/launcher/resources/icons/tango/go-bottom.png new file mode 100644 index 0000000000..2c5a80385c Binary files /dev/null and b/apps/launcher/resources/icons/tango/go-bottom.png differ diff --git a/apps/launcher/resources/icons/tango/go-down.png b/apps/launcher/resources/icons/tango/go-down.png new file mode 100644 index 0000000000..3dd7fccdf0 Binary files /dev/null and b/apps/launcher/resources/icons/tango/go-down.png differ diff --git a/apps/launcher/resources/icons/tango/go-top.png b/apps/launcher/resources/icons/tango/go-top.png new file mode 100644 index 0000000000..70f2c996cd Binary files /dev/null and b/apps/launcher/resources/icons/tango/go-top.png differ diff --git a/apps/launcher/resources/icons/tango/go-up.png b/apps/launcher/resources/icons/tango/go-up.png new file mode 100644 index 0000000000..fa9a7d71b5 Binary files /dev/null and b/apps/launcher/resources/icons/tango/go-up.png differ diff --git a/apps/launcher/resources/icons/tango/index.theme b/apps/launcher/resources/icons/tango/index.theme new file mode 100644 index 0000000000..1f54489ebb --- /dev/null +++ b/apps/launcher/resources/icons/tango/index.theme @@ -0,0 +1,8 @@ +[Icon Theme] +Name=Tango +Comment=Tango Theme +Inherits=default +Directories=16x16 + +[16x16] +Size=16 \ No newline at end of file diff --git a/apps/launcher/resources/icons/tango/video-display.png b/apps/launcher/resources/icons/tango/video-display.png new file mode 100644 index 0000000000..1331436846 Binary files /dev/null and b/apps/launcher/resources/icons/tango/video-display.png differ diff --git a/apps/launcher/resources/images/clear.png b/apps/launcher/resources/images/clear.png new file mode 100644 index 0000000000..6c4b83b7ac Binary files /dev/null and b/apps/launcher/resources/images/clear.png differ diff --git a/apps/launcher/resources/images/down.png b/apps/launcher/resources/images/down.png new file mode 100644 index 0000000000..f529cda7de Binary files /dev/null and b/apps/launcher/resources/images/down.png differ diff --git a/apps/launcher/resources/images/openmw-header.png b/apps/launcher/resources/images/openmw-header.png new file mode 100644 index 0000000000..a168d4d2a8 Binary files /dev/null and b/apps/launcher/resources/images/openmw-header.png differ diff --git a/apps/launcher/resources/images/openmw-plugin.png b/apps/launcher/resources/images/openmw-plugin.png new file mode 100644 index 0000000000..c34cba5543 Binary files /dev/null and b/apps/launcher/resources/images/openmw-plugin.png differ diff --git a/apps/launcher/resources/images/openmw.ico b/apps/launcher/resources/images/openmw.ico new file mode 100644 index 0000000000..c04fc3d9ce Binary files /dev/null and b/apps/launcher/resources/images/openmw.ico differ diff --git a/apps/launcher/resources/images/openmw.png b/apps/launcher/resources/images/openmw.png new file mode 100644 index 0000000000..7a5393821f Binary files /dev/null and b/apps/launcher/resources/images/openmw.png differ diff --git a/apps/launcher/resources/images/playpage-background.png b/apps/launcher/resources/images/playpage-background.png new file mode 100644 index 0000000000..0116adc0fc Binary files /dev/null and b/apps/launcher/resources/images/playpage-background.png differ diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index b206934179..f8b4d7a59f 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -1,5 +1,9 @@ project(OpenMW) +# config file + +configure_file ("${OpenMW_SOURCE_DIR}/config.hpp.cmake" "${OpenMW_SOURCE_DIR}/config.hpp") + # local files set(GAME @@ -7,7 +11,8 @@ set(GAME engine.cpp ) set(GAME_HEADER - engine.hpp) + engine.hpp + config.hpp) source_group(game FILES ${GAME} ${GAME_HEADER}) set(GAMEREND @@ -50,6 +55,7 @@ set(GAMEGUI_HEADER mwgui/dialogue_history.hpp mwgui/window_base.hpp mwgui/stats_window.hpp + mwgui/messagebox.hpp ) set(GAMEGUI mwgui/window_manager.cpp @@ -65,6 +71,7 @@ set(GAMEGUI mwgui/dialogue_history.cpp mwgui/window_base.cpp mwgui/stats_window.cpp + mwgui/messagebox.cpp ) source_group(apps\\openmw\\mwgui FILES ${GAMEGUI_HEADER} ${GAMEGUI}) diff --git a/apps/openmw/config.hpp.cmake b/apps/openmw/config.hpp.cmake new file mode 100644 index 0000000000..848fbe0eb1 --- /dev/null +++ b/apps/openmw/config.hpp.cmake @@ -0,0 +1,9 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#define OPENMW_VERSION_MAJOR @OPENMW_VERSION_MAJOR@ +#define OPENMW_VERSION_MINOR @OPENMW_VERSION_MINOR@ +#define OPENMW_VERSION_RELEASE @OPENMW_VERSION_RELEASE@ +#define OPENMW_VERSION "@OPENMW_VERSION@" + +#endif diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index e1ce97bfa5..d70631cf8f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "components/esm/records.hpp" #include @@ -44,10 +45,6 @@ #include -#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE -#include -#endif - #include #include "mwgui/class.hpp" @@ -76,7 +73,7 @@ void OMW::Engine::executeLocalScripts() } -bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt) +bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) { if(mShowFPS) { @@ -92,6 +89,8 @@ bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt) std::string effect; MWWorld::Ptr::CellStore *current = mEnvironment.mWorld->getPlayer().getPlayer().getCell(); + + //If the region has changed if(!(current->cell->data.flags & current->cell->Interior) && timer.elapsed() >= 10){ timer.restart(); @@ -153,6 +152,9 @@ bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt) { mEnvironment.mFrameDuration = evt.timeSinceLastFrame; + // + mEnvironment.mWindowManager->onFrame(mEnvironment.mFrameDuration); + // global scripts mEnvironment.mGlobalScripts->run (mEnvironment); @@ -246,8 +248,12 @@ void OMW::Engine::loadBSA() for (Files::MultiDirCollection::TIter iter (bsa.begin()); iter!=bsa.end(); ++iter) { std::cout << "Adding " << iter->second.string() << std::endl; - addBSA (iter->second.string()); + Bsa::addBSA (iter->second.string()); } + + std::string m = mDataDir.string(); + std::cout << "Data dir" << m << "\n"; + Bsa::addDir(m, mFSStrict); } // add resources directory @@ -338,15 +344,12 @@ void OMW::Engine::go() ogreCfg.insert(0, cfgUserDir); //A local plugins.cfg will be used if it exist, otherwise look in the default path - if(!isFile(plugCfg.c_str())) + if(!Misc::isFile(plugCfg.c_str())) { plugCfg.insert(0, cfgDir); } - mOgre.configure(!isFile(ogreCfg.c_str()), cfgUserDir, plugCfg, false); - - addResourcesDirectory (mDataDir / "Meshes"); - addResourcesDirectory (mDataDir / "Textures"); + mOgre.configure(!Misc::isFile(ogreCfg.c_str()), cfgUserDir, plugCfg, false); // This has to be added BEFORE MyGUI is initialized, as it needs // to find core.xml here. @@ -358,12 +361,12 @@ void OMW::Engine::go() loadBSA(); // Create physics. shapeLoader is deleted by the physic engine - ManualBulletShapeLoader* shapeLoader = new ManualBulletShapeLoader(); + NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(); mPhysicEngine = new OEngine::Physic::PhysicEngine(shapeLoader); // Create the world mEnvironment.mWorld = new MWWorld::World (mOgre, mPhysicEngine, mFileCollections, mMaster, - mResDir, mNewGame, mEnvironment); + mResDir, mNewGame, mEnvironment, mEncoding); // Set up the GUI system mGuiManager = new OEngine::GUI::MyGUIManager(mOgre.getWindow(), mOgre.getScene(), false, cfgDir); @@ -384,7 +387,7 @@ void OMW::Engine::go() mOgre.getCamera(), mEnvironment.mWorld->getStore(), (mDataDir), - mUseSound); + mUseSound, mFSStrict); // Create script system mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full, @@ -512,3 +515,8 @@ void OMW::Engine::setCompileAll (bool all) { mCompileAll = all; } + +void OMW::Engine::setEncoding(const std::string& encoding) +{ + mEncoding = encoding; +} \ No newline at end of file diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index e49ddfc060..9d14666558 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -56,6 +56,7 @@ namespace OMW class Engine : private Ogre::FrameListener { + std::string mEncoding; boost::filesystem::path mDataDir; boost::filesystem::path mResDir; OEngine::Render::OgreRenderer mOgre; @@ -100,7 +101,7 @@ namespace OMW void executeLocalScripts(); - virtual bool frameStarted(const Ogre::FrameEvent& evt); + virtual bool frameRenderingQueued (const Ogre::FrameEvent& evt); /// Process pending commands @@ -157,6 +158,9 @@ namespace OMW /// Compile all scripts (excludign dialogue scripts) at startup? void setCompileAll (bool all); + + /// Font encoding + void setEncoding(const std::string& encoding); }; } diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index e117a0688d..ed85f4beb7 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -26,6 +26,13 @@ #endif +// for Ogre::macBundlePath +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE +#include +#endif + +#include "config.hpp" + using namespace std; /// Parse command line options and openmw.cfg file (if one exists). Results are directly @@ -41,7 +48,8 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine) "Syntax: openmw \nAllowed options"); desc.add_options() - ("help", "print help message") + ("help", "print help message and quit") + ("version", "print version information and quit") ("data", bpo::value >() ->default_value (std::vector(), "data") ->multitoken(), @@ -77,6 +85,13 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine) ( "fs-strict", boost::program_options::value()-> implicit_value (true)->default_value (false), "strict file system handling (no case folding)") + + ( "encoding", boost::program_options::value()-> + default_value("win1252"), + "Character encoding used in OpenMW game messages:\n" + "\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n" + "\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n" + "\n\twin1252 - Western European (Latin) alphabet, used by default") ; bpo::variables_map variables; @@ -84,7 +99,7 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine) //If there is an openmw.cfg in the current path use that as global config //Otherwise try getPath std::string cfgFile = "openmw.cfg"; - if(!isFile(cfgFile.c_str())) + if(!Misc::isFile(cfgFile.c_str())) { cfgFile = Files::getPath (Files::Path_ConfigGlobal, "openmw", "openmw.cfg"); } @@ -105,10 +120,39 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine) if (globalConfigFile.is_open()) bpo::store ( bpo::parse_config_file (globalConfigFile, desc), variables); + bool run = true; + if (variables.count ("help")) { std::cout << desc << std::endl; + run = false; + } + + if (variables.count ("version")) + { + std::cout << "OpenMW version " << OPENMW_VERSION << std::endl; + run = false; + } + + if (!run) return false; + + // Font encoding settings + std::string encoding(variables["encoding"].as()); + if (encoding == "win1250") + { + std::cout << "Using Central and Eastern European font encoding." << std::endl; + engine.setEncoding(encoding); + } + else if (encoding == "win1251") + { + std::cout << "Using Cyrillic font encoding." << std::endl; + engine.setEncoding(encoding); + } + else + { + std::cout << "Using default (English) font encoding." << std::endl; + engine.setEncoding("win1252"); } // directory settings @@ -177,7 +221,7 @@ int main(int argc, char**argv) { #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE // set current dir to bundle path - boost::filesystem::path bundlePath = boost::filesystem::path(Ogre::macBundlePath()); + boost::filesystem::path bundlePath = boost::filesystem::path(Ogre::macBundlePath()).parent_path(); boost::filesystem::current_path(bundlePath); #endif diff --git a/apps/openmw/mwclass/classes.cpp b/apps/openmw/mwclass/classes.cpp index 7456148527..e9538a6cb4 100644 --- a/apps/openmw/mwclass/classes.cpp +++ b/apps/openmw/mwclass/classes.cpp @@ -42,7 +42,7 @@ namespace MWClass ItemLevList::registerSelf(); Light::registerSelf(); Lockpick::registerSelf(); - Misc::registerSelf(); + Miscellaneous::registerSelf(); Probe::registerSelf(); Repair::registerSelf(); Static::registerSelf(); diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 8cde67671b..0bd133f21c 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -34,7 +34,7 @@ namespace MWClass if (!model.empty()) { MWRender::Rendering rendering (cellRender, ref->ref); - cellRender.insertMesh ("meshes\\" + model); + cellRender.insertMesh("meshes\\" + model); cellRender.insertActorPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index f9995b77f8..2986c812d9 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -14,11 +14,11 @@ namespace MWClass { - void Misc::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender, + void Miscellaneous::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender, MWWorld::Environment& environment) const { - ESMS::LiveCellRef *ref = - ptr.get(); + ESMS::LiveCellRef *ref = + ptr.get(); assert (ref->base != NULL); const std::string &model = ref->base->model; @@ -31,39 +31,39 @@ namespace MWClass } } - std::string Misc::getName (const MWWorld::Ptr& ptr) const + std::string Miscellaneous::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = - ptr.get(); + ESMS::LiveCellRef *ref = + ptr.get(); return ref->base->name; } - boost::shared_ptr Misc::activate (const MWWorld::Ptr& ptr, + boost::shared_ptr Miscellaneous::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const { return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); } - void Misc::insertIntoContainer (const MWWorld::Ptr& ptr, + void Miscellaneous::insertIntoContainer (const MWWorld::Ptr& ptr, MWWorld::ContainerStore& containerStore) const { insertIntoContainerStore (ptr, containerStore.miscItems); } - std::string Misc::getScript (const MWWorld::Ptr& ptr) const + std::string Miscellaneous::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = - ptr.get(); + ESMS::LiveCellRef *ref = + ptr.get(); return ref->base->script; } - void Misc::registerSelf() + void Miscellaneous::registerSelf() { - boost::shared_ptr instance (new Misc); + boost::shared_ptr instance (new Miscellaneous); - registerClass (typeid (ESM::Misc).name(), instance); + registerClass (typeid (ESM::Miscellaneous).name(), instance); } } diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index 0da1f0d3a0..81262cbee8 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -5,7 +5,7 @@ namespace MWClass { - class Misc : public MWWorld::Class + class Miscellaneous : public MWWorld::Class { public: diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index bb5578f6dc..9ef5e6f406 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -95,8 +95,7 @@ namespace MWClass upperright[uppernumbers++] = npcName + "chest"; neckandup[neckNumbers++] = npcName + "chest"; } - //std::cout << "GETTING NPC PART"; - //Orgre::SceneNode test = cellRender.getNpcPart(); + const ESM::BodyPart *upperleg = environment.mWorld->getStore().bodyParts.search (bodyRaceID + "upper leg"); const ESM::BodyPart *groin = environment.mWorld->getStore().bodyParts.search (bodyRaceID + "groin"); @@ -113,8 +112,6 @@ namespace MWClass const ESM::BodyPart *hands = environment.mWorld->getStore().bodyParts.search (bodyRaceID + "hands.1st"); - //std::cout << "RACE" << bodyRaceID << "\n"; - Ogre::Vector3 pos2 = Ogre::Vector3( 0, .5, 75); if (groin){ @@ -124,10 +121,9 @@ namespace MWClass } if (tail) { cellRender.insertMesh("tail\\" + tail->model, Ogre::Vector3(0 , 0, -76), axis, kOgrePi, npcName + "tail", addresses, numbers, "tail"); - //std::cout << "TAIL\n"; } - //addresses[1] = npcName + "groin"; + if(upperleg){ cellRender.insertMesh ("meshes\\" + upperleg->model, Ogre::Vector3( 6, 0, -16), axis, kOgrePi, npcName + "upper leg", addresses, numbers); //-18 cellRender.insertMesh ("meshes\\" + upperleg->model, Ogre::Vector3( -6, 0, -16), axis, Ogre::Radian(0), npcName + "upper leg2", addresses2, numbers); @@ -218,9 +214,6 @@ namespace MWClass if(hand) { - //std::cout << "WE FOUND A HAND\n"; - //-50, 0, -120 - //std::cout << "WE FOUND HANDS\n"; std::string pass; if(hand->model.compare("b\\B_N_Dark Elf_F_Hands.1st.NIF")==0 && bodyRaceID.compare("b_n_dark elf_m_") == 0) pass = "b\\B_N_Dark Elf_M_Hands.1st.NIF"; @@ -230,7 +223,6 @@ namespace MWClass cellRender.insertMesh("meshes\\" + pass, Ogre::Vector3(42, 1, -110), Ogre::Vector3(0, 0,0), kOgrePi, npcName + "hand2", upperright, uppernumbers, false); //0, 100, -100 0,0,120 upperleft[uppernumbers] = npcName + "hand"; upperright[uppernumbers++] = npcName + "hand2"; - //cellRender.rotateMesh(Ogre::Vector3(0, 0,0), kOgrePi, upperleft, uppernumbers); cellRender.scaleMesh(Ogre::Vector3(1, -1, 1), upperleft, uppernumbers); cellRender.scaleMesh(Ogre::Vector3(1, -1, 1), upperright, uppernumbers); } @@ -244,7 +236,6 @@ namespace MWClass else pass =hands->model; //-50, 0, -120 cellRender.insertMesh("meshes\\" + pass, Ogre::Vector3(42, 1,-110), Ogre::Vector3(0, 0, 0), kOgrePi, npcName + "hand", upperleft, uppernumbers, false); //0, 100, -100 42, 0, -110 - //cellRender.insertMesh("meshes\\" + hands->model, Ogre::Vector3(42, 0,110), Ogre::Vector3(1, 0, 0), kOgrePi, npcName + "hand", upperleft, uppernumbers, false); //0, 100, -100 42, 0, -110 cellRender.insertMesh("meshes\\" + pass, Ogre::Vector3(42, 1, -110), Ogre::Vector3(0, 0, 0), kOgrePi, npcName + "hand2", upperright, uppernumbers, false); //0, 100, -100 0,0,120 upperleft[uppernumbers] = npcName + "hand"; upperright[uppernumbers++] = npcName + "hand2"; diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index 851b4d7bf9..cf0be94992 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -19,7 +19,7 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - MWRender::Rendering rendering (cellRender, ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref, true); cellRender.insertMesh ("meshes\\" + model); cellRender.insertObjectPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 0421dc370f..0e2e692d3f 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -171,6 +171,22 @@ namespace MWGui MyGUI::KeyCode key, MyGUI::Char _char) { + if( key == MyGUI::KeyCode::Tab) + { + std::vector matches; + listNames(); + command->setCaption(complete( command->getCaption(), matches )); +#if 0 + int i = 0; + for(std::vector::iterator it=matches.begin(); it < matches.end(); it++,i++ ) + { + printOK( *it ); + if( i == 50 ) + break; + } +#endif + } + if(command_history.empty()) return; // Traverse history with up and down arrows @@ -223,11 +239,11 @@ namespace MWGui try { ConsoleInterpreterContext interpreterContext (*this, mEnvironment, MWWorld::Ptr()); - Interpreter::Interpreter interpreter (interpreterContext); + Interpreter::Interpreter interpreter; MWScript::installOpcodes (interpreter); std::vector code; output.getCode (code); - interpreter.run (&code[0], code.size()); + interpreter.run (&code[0], code.size(), interpreterContext); } catch (const std::exception& error) { @@ -237,4 +253,119 @@ namespace MWGui command->setCaption(""); } + + std::string Console::complete( std::string input, std::vector &matches ) + { + using namespace std; + string output=input; + string tmp=input; + bool has_front_quote = false; + + /* Does the input string contain things that don't have to be completed? If yes erase them. */ + /* Are there quotation marks? */ + if( tmp.find('"') != string::npos ) { + int numquotes=0; + for(string::iterator it=tmp.begin(); it < tmp.end(); it++) { + if( *it == '"' ) + numquotes++; + } + + /* Is it terminated?*/ + if( numquotes % 2 ) { + tmp.erase( 0, tmp.rfind('"')+1 ); + has_front_quote = true; + } + else { + size_t pos; + if( ( ((pos = tmp.rfind(' ')) != string::npos ) ) && ( pos > tmp.rfind('"') ) ) { + tmp.erase( 0, tmp.rfind(' ')+1); + } + else { + tmp.clear(); + } + has_front_quote = false; + } + } + /* No quotation marks. Are there spaces?*/ + else { + size_t rpos; + if( (rpos=tmp.rfind(' ')) != string::npos ) { + if( rpos == 0 ) { + tmp.clear(); + } + else { + tmp.erase(0, rpos+1); + } + } + } + /* Erase the input from the output string so we can easily append the completed form later. */ + output.erase(output.end()-tmp.length(), output.end()); + + /* Is there still something in the input string? If not just display all commands and return the unchanged input. */ + if( tmp.length() == 0 ) { + matches=mNames; + return input; + } + + /* Iterate through the vector. */ + for(vector::iterator it=mNames.begin(); it < mNames.end();it++) { + bool string_different=false; + + /* Is the string shorter than the input string? If yes skip it. */ + if( (*it).length() < tmp.length() ) + continue; + + /* Is the beginning of the string different from the input string? If yes skip it. */ + for( string::iterator iter=tmp.begin(), iter2=(*it).begin(); iter < tmp.end();iter++, iter2++) { + if( tolower(*iter) != tolower(*iter2) ) { + string_different=true; + break; + } + } + + if( string_different ) + continue; + + /* The beginning of the string matches the input string, save it for the next test. */ + matches.push_back(*it); + } + + /* There are no matches. Return the unchanged input. */ + if( matches.empty() ) + { + return input; + } + + /* Only one match. We're done. */ + if( matches.size() == 1 ) { + /* Adding quotation marks when the input string started with a quotation mark or has spaces in it*/ + if( ( matches.front().find(' ') != string::npos ) ) { + if( !has_front_quote ) + output.append(string("\"")); + return output.append(matches.front() + string("\" ")); + } + else if( has_front_quote ) { + return output.append(matches.front() + string("\" ")); + } + else { + return output.append(matches.front() + string(" ")); + } + } + + /* Check if all matching strings match further than input. If yes complete to this match. */ + int i = tmp.length(); + + for(string::iterator iter=matches.front().begin()+tmp.length(); iter < matches.front().end(); iter++, i++) { + for(vector::iterator it=matches.begin(); it < matches.end();it++) { + if( tolower((*it)[i]) != tolower(*iter) ) { + /* Append the longest match to the end of the output string*/ + output.append(matches.front().substr( 0, i)); + return output; + } + } + } + + /* All keywords match with the shortest. Append it to the output string and return it. */ + return output.append(matches.front()); + } } diff --git a/apps/openmw/mwgui/console.hpp b/apps/openmw/mwgui/console.hpp index d08ac58873..eaf4299be0 100644 --- a/apps/openmw/mwgui/console.hpp +++ b/apps/openmw/mwgui/console.hpp @@ -80,6 +80,8 @@ namespace MWGui MyGUI::Char _char); void acceptCommand(MyGUI::EditPtr _sender); + + std::string complete( std::string input, std::vector &matches ); }; } #endif diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp new file mode 100644 index 0000000000..f0745bbbec --- /dev/null +++ b/apps/openmw/mwgui/messagebox.cpp @@ -0,0 +1,394 @@ +#include "messagebox.hpp" + +using namespace MWGui; + +MessageBoxManager::MessageBoxManager (WindowManager *windowManager) +{ + mWindowManager = windowManager; + // defines + mMessageBoxSpeed = 0.1; + mInterMessageBoxe = NULL; +} + +void MessageBoxManager::onFrame (float frameDuration) +{ + std::vector::iterator it; + for(it = mTimers.begin(); it != mTimers.end();) + { + it->current += frameDuration; + if(it->current >= it->max) + { + it->messageBox->mMarkedToDelete = true; + + if(*mMessageBoxes.begin() == it->messageBox) // if this box is the last one + { + // collect all with mMarkedToDelete and delete them. + // and place the other messageboxes on the right position + int height = 0; + std::vector::iterator it2 = mMessageBoxes.begin(); + while(it2 != mMessageBoxes.end()) + { + if((*it2)->mMarkedToDelete) + { + delete (*it2); + it2 = mMessageBoxes.erase(it2); + } + else { + (*it2)->update(height); + height += (*it2)->getHeight(); + it2++; + } + } + } + it = mTimers.erase(it); + } + else + { + it++; + } + } + + if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) { + delete mInterMessageBoxe; + mInterMessageBoxe = NULL; + mWindowManager->setNextMode(GM_Game); + } +} + +void MessageBoxManager::createMessageBox (const std::string& message) +{ + std::cout << "MessageBox: " << message << std::endl; + + MessageBox *box = new MessageBox(*this, message); + + removeMessageBox(message.length()*mMessageBoxSpeed, box); + + mMessageBoxes.push_back(box); + std::vector::iterator it; + + if(mMessageBoxes.size() > 3) { + delete *mMessageBoxes.begin(); + mMessageBoxes.erase(mMessageBoxes.begin()); + } + + int height = 0; + for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it) + { + (*it)->update(height); + height += (*it)->getHeight(); + } +} + +bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector& buttons) +{ + if(mInterMessageBoxe != NULL) { + std::cout << "there is a MessageBox already" << std::endl; + return false; + } + std::cout << "interactive MessageBox: " << message << " - "; + std::copy (buttons.begin(), buttons.end(), std::ostream_iterator (std::cout, ", ")); + std::cout << std::endl; + + mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons); + + return true; +} + +bool MessageBoxManager::isInteractiveMessageBox () +{ + return mInterMessageBoxe != NULL; +} + +void MessageBoxManager::removeMessageBox (float time, MessageBox *msgbox) +{ + MessageBoxManagerTimer timer; + timer.current = 0; + timer.max = time; + timer.messageBox = msgbox; + + mTimers.insert(mTimers.end(), timer); +} + +bool MessageBoxManager::removeMessageBox (MessageBox *msgbox) +{ + std::vector::iterator it; + for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it) + { + if((*it) == msgbox) + { + delete (*it); + mMessageBoxes.erase(it); + return true; + } + } + return false; +} + +void MessageBoxManager::setMessageBoxSpeed (int speed) +{ + mMessageBoxSpeed = speed; +} + + +int MessageBoxManager::readPressedButton () +{ + if(mInterMessageBoxe != NULL) + { + return mInterMessageBoxe->readPressedButton(); + } + return -1; +} + + + + +MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message) + : Layout("openmw_messagebox_layout.xml") + , mMessageBoxManager(parMessageBoxManager) + , cMessage(message) +{ + // defines + mFixedWidth = 300; + mBottomPadding = 20; + mNextBoxPadding = 20; + mMarkedToDelete = false; + + getWidget(mMessageWidget, "message"); + + mMessageWidget->setOverflowToTheLeft(true); + mMessageWidget->addText(cMessage); + + MyGUI::IntSize size; + size.width = mFixedWidth; + size.height = 100; // dummy + + MyGUI::IntCoord coord; + coord.left = 10; // dummy + coord.top = 10; // dummy + + mMessageWidget->setSize(size); + + MyGUI::IntSize textSize = mMessageWidget->_getTextSize(); + size.height = mHeight = textSize.height + 20; // this is the padding between the text and the box + + mMainWidget->setSize(size); + size.width -= 15; // this is to center the text (see messagebox_layout.xml, Widget type="Edit" position="-2 -3 0 0") + mMessageWidget->setSize(size); +} + +void MessageBox::update (int height) +{ + MyGUI::IntSize gameWindowSize = mMessageBoxManager.mWindowManager->getGui()->getViewSize(); + MyGUI::IntCoord coord; + coord.left = (gameWindowSize.width - mFixedWidth)/2; + coord.top = (gameWindowSize.height - mHeight - height - mBottomPadding); + + MyGUI::IntSize size; + size.width = mFixedWidth; + size.height = mHeight; + + mMainWidget->setCoord(coord); + mMainWidget->setSize(size); + mMainWidget->setVisible(true); +} + +int MessageBox::getHeight () +{ + return mHeight+mNextBoxPadding; // 20 is the padding between this and the next MessageBox +} + + + +InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons) + : Layout("openmw_interactive_messagebox_layout.xml") + , mMessageBoxManager(parMessageBoxManager) + , mButtonPressed(-1) +{ + int fixedWidth = 500; + int textPadding = 10; // padding between text-widget and main-widget + int textButtonPadding = 20; // padding between the text-widget und the button-widget + int buttonLeftPadding = 10; // padding between the buttons if horizontal + int buttonTopPadding = 5; // ^-- if vertical + int buttonPadding = 5; // padding between button label and button itself + int buttonMainPadding = 10; // padding between buttons and bottom of the main widget + + mMarkedToDelete = false; + + + getWidget(mMessageWidget, "message"); + getWidget(mButtonsWidget, "buttons"); + + mMessageWidget->setOverflowToTheLeft(true); + mMessageWidget->addText(message); + + MyGUI::IntSize textSize = mMessageWidget->_getTextSize(); + + MyGUI::IntSize gameWindowSize = mMessageBoxManager.mWindowManager->getGui()->getViewSize(); + + int biggestButtonWidth = 0; + int buttonWidth = 0; + int buttonsWidth = 0; + int buttonHeight = 0; + MyGUI::IntCoord dummyCoord(0, 0, 0, 0); + + std::vector::const_iterator it; + for(it = buttons.begin(); it != buttons.end(); ++it) + { + MyGUI::ButtonPtr button = mButtonsWidget->createWidget( + MyGUI::WidgetStyle::Child, + std::string("MW_Button"), + dummyCoord, + MyGUI::Align::Default); + button->setCaption(*it); + + button->eventMouseButtonClick = MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed); + + mButtons.push_back(button); + + buttonWidth = button->_getTextSize().width + 2*buttonPadding + buttonLeftPadding; + buttonsWidth += buttonWidth; + buttonHeight = button->_getTextSize().height + 2*buttonPadding + buttonTopPadding; + + if(buttonWidth > biggestButtonWidth) + { + biggestButtonWidth = buttonWidth; + } + } + buttonsWidth += buttonLeftPadding; + + MyGUI::IntSize mainWidgetSize; + if(buttonsWidth < fixedWidth) + { + // on one line + std::cout << "on one line" << std::endl; + + if(textSize.width + 2*textPadding < buttonsWidth) + { + std::cout << "width = buttonsWidth" << std::endl; + mainWidgetSize.width = buttonsWidth; + } + else + { + mainWidgetSize.width = textSize.width + 3*textPadding; + } + mainWidgetSize.height = textSize.height + textButtonPadding + buttonHeight + buttonMainPadding; + + MyGUI::IntCoord absCoord; + absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; + absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; + + std::cout << "width " << mainWidgetSize.width << " height " << mainWidgetSize.height << std::endl; + std::cout << "left " << absCoord.left << " top " << absCoord.top << std::endl; + + mMainWidget->setCoord(absCoord); + mMainWidget->setSize(mainWidgetSize); + + + MyGUI::IntCoord messageWidgetCoord; + messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; + messageWidgetCoord.top = textPadding; + mMessageWidget->setCoord(messageWidgetCoord); + + mMessageWidget->setSize(textSize); + + MyGUI::IntCoord buttonCord; + MyGUI::IntSize buttonSize(0, buttonHeight); + int left = (mainWidgetSize.width - buttonsWidth)/2 + buttonPadding; + + std::vector::const_iterator button; + for(button = mButtons.begin(); button != mButtons.end(); ++button) + { + buttonCord.left = left; + buttonCord.top = textSize.height + textButtonPadding; + + buttonSize.width = (*button)->_getTextSize().width + 2*buttonPadding; + buttonSize.height = (*button)->_getTextSize().height + 2*buttonPadding; + + (*button)->setCoord(buttonCord); + (*button)->setSize(buttonSize); + + left += buttonSize.width + buttonLeftPadding; + } + } + else + { + // among each other + + if(biggestButtonWidth > textSize.width) { + mainWidgetSize.width = biggestButtonWidth + buttonTopPadding; + } + else { + mainWidgetSize.width = textSize.width + 3*textPadding; + } + mainWidgetSize.height = textSize.height + 2*textPadding + textButtonPadding + buttonHeight * buttons.size() + buttonMainPadding; + + std::cout << "biggestButtonWidth " << biggestButtonWidth << " textSize.width " << textSize.width << std::endl; + std::cout << "width " << mainWidgetSize.width << " height " << mainWidgetSize.height << std::endl; + mMainWidget->setSize(mainWidgetSize); + + MyGUI::IntCoord absCoord; + absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; + absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; + + mMainWidget->setCoord(absCoord); + mMainWidget->setSize(mainWidgetSize); + + + MyGUI::IntCoord messageWidgetCoord; + messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; + messageWidgetCoord.top = textPadding; + mMessageWidget->setCoord(messageWidgetCoord); + + mMessageWidget->setSize(textSize); + + MyGUI::IntCoord buttonCord; + MyGUI::IntSize buttonSize(0, buttonHeight); + + int top = textButtonPadding + buttonTopPadding + textSize.height; + + std::vector::const_iterator button; + for(button = mButtons.begin(); button != mButtons.end(); ++button) + { + buttonSize.width = (*button)->_getTextSize().width + buttonPadding*2; + buttonSize.height = (*button)->_getTextSize().height + buttonPadding*2; + + buttonCord.top = top; + buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2 - 5; // FIXME: -5 is not so nice :/ + + (*button)->setCoord(buttonCord); + (*button)->setSize(buttonSize); + + top += buttonSize.height + 2*buttonTopPadding; + } + + } +} + +void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed) +{ + mMarkedToDelete = true; + int index = 0; + std::vector::const_iterator button; + for(button = mButtons.begin(); button != mButtons.end(); ++button) + { + if(*button == pressed) + { + mButtonPressed = index; + return; + } + index++; + } + std::cout << "Cant be possible :/" << std::endl; +} + +int InteractiveMessageBox::readPressedButton () +{ + int pressed = mButtonPressed; + mButtonPressed = -1; + return pressed; +} + + + + + diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp new file mode 100644 index 0000000000..bf3307accf --- /dev/null +++ b/apps/openmw/mwgui/messagebox.hpp @@ -0,0 +1,89 @@ +#ifndef MWGUI_MESSAGE_BOX_H +#define MWGUI_MESSAGE_BOX_H + +#include +#include + +#include "window_base.hpp" +#include "window_manager.hpp" + + +namespace MWGui +{ + + class InteractiveMessageBox; + class MessageBoxManager; + class MessageBox; + + struct MessageBoxManagerTimer { + float current; + float max; + MessageBox *messageBox; + }; + + class MessageBoxManager + { + public: + MessageBoxManager (WindowManager* windowManager); + void onFrame (float frameDuration); + void createMessageBox (const std::string& message); + bool createInteractiveMessageBox (const std::string& message, const std::vector& buttons); + bool isInteractiveMessageBox (); + + void removeMessageBox (float time, MessageBox *msgbox); + bool removeMessageBox (MessageBox *msgbox); + void setMessageBoxSpeed (int speed); + + int readPressedButton (); + + WindowManager *mWindowManager; + + private: + std::vector mMessageBoxes; + InteractiveMessageBox* mInterMessageBoxe; + std::vector mTimers; + float mMessageBoxSpeed; + }; + + class MessageBox : public OEngine::GUI::Layout + { + public: + MessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message); + void setMessage (const std::string& message); + int getHeight (); + void update (int height); + + bool mMarkedToDelete; + + protected: + MessageBoxManager& mMessageBoxManager; + int mHeight; + const std::string& cMessage; + MyGUI::EditPtr mMessageWidget; + int mFixedWidth; + int mBottomPadding; + int mNextBoxPadding; + }; + + class InteractiveMessageBox : public OEngine::GUI::Layout + { + public: + InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons); + void mousePressed (MyGUI::Widget* _widget); + int readPressedButton (); + + bool mMarkedToDelete; + + private: + MessageBoxManager& mMessageBoxManager; + MyGUI::EditPtr mMessageWidget; + MyGUI::WidgetPtr mButtonsWidget; + std::vector mButtons; + + int mTextButtonPadding; + int mButtonPressed; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index 313b097cf3..b0dc140297 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -26,7 +26,10 @@ namespace MWGui GM_ClassGenerate, GM_ClassPick, GM_ClassCreate, - GM_Review + GM_Review, + + // interactive MessageBox + GM_InterMessageBox }; // Windows shown in inventory mode diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 2d7f70ef86..84e45859fb 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -8,6 +8,7 @@ #include "dialogue.hpp" #include "dialogue_history.hpp" #include "stats_window.hpp" +#include "messagebox.hpp" #include "../mwmechanics/mechanicsmanager.hpp" #include "../mwinput/inputmanager.hpp" @@ -60,6 +61,7 @@ WindowManager::WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment inventory = new InventoryWindow (); #endif console = new Console(w,h, environment, extensions); + mMessageBoxManager = new MessageBoxManager(this); // The HUD is always on hud->setVisible(true); @@ -82,6 +84,7 @@ WindowManager::WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment WindowManager::~WindowManager() { delete console; + delete mMessageBoxManager; delete hud; delete map; delete menu; @@ -326,6 +329,14 @@ void WindowManager::updateVisible() dialogueWindow->open(); return; } + + if(mode == GM_InterMessageBox) + { + if(!mMessageBoxManager->isInteractiveMessageBox()) { + setGuiMode(GM_Game); + } + return; + } // Unsupported mode, switch back to game @@ -446,14 +457,20 @@ void WindowManager::removeDialog(OEngine::GUI::Layout*dialog) void WindowManager::messageBox (const std::string& message, const std::vector& buttons) { - std::cout << "message box: " << message << std::endl; - - if (!buttons.empty()) + if (buttons.empty()) { - std::cout << "buttons: "; - std::copy (buttons.begin(), buttons.end(), std::ostream_iterator (std::cout, ", ")); - std::cout << std::endl; + mMessageBoxManager->createMessageBox(message); } + else + { + mMessageBoxManager->createInteractiveMessageBox(message, buttons); + setGuiMode(GM_InterMessageBox); + } +} + +int WindowManager::readPressedButton () +{ + return mMessageBoxManager->readPressedButton(); } const std::string &WindowManager::getGameSettingString(const std::string &id, const std::string &default_) @@ -555,6 +572,11 @@ void WindowManager::onClassChoice(int _index) }; } +void WindowManager::onFrame (float frameDuration) +{ + mMessageBoxManager->onFrame(frameDuration); +} + namespace MWGui { diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index d3fbf3ea31..293bac6013 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -62,6 +62,7 @@ namespace MWGui class CreateClassDialog; class BirthDialog; class ReviewDialog; + class MessageBoxManager; struct ClassPoint { @@ -84,6 +85,7 @@ namespace MWGui MapWindow *map; MainMenu *menu; StatsWindow *stats; + MessageBoxManager *mMessageBoxManager; #if 0 InventoryWindow *inventory; #endif @@ -245,8 +247,13 @@ namespace MWGui void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. - + void messageBox (const std::string& message, const std::vector& buttons); + + int readPressedButton (); + ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) + + void onFrame (float frameDuration); /** * Fetches a GMST string from the store, if there is no setting with the given diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index dc21680af4..eb6c59963b 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -258,7 +258,7 @@ namespace MWInput } //NOTE: Used to check for movement keys - bool frameStarted(const Ogre::FrameEvent &evt) + bool frameRenderingQueued (const Ogre::FrameEvent &evt) { // Tell OIS to handle all input events input.capture(); diff --git a/apps/openmw/mwrender/cellimp.hpp b/apps/openmw/mwrender/cellimp.hpp index 272bfeeab4..664fed64f0 100644 --- a/apps/openmw/mwrender/cellimp.hpp +++ b/apps/openmw/mwrender/cellimp.hpp @@ -35,7 +35,7 @@ namespace MWRender virtual ~CellRenderImp() {} /// start inserting a new reference. - virtual void insertBegin (ESM::CellRef &ref) = 0; + virtual void insertBegin (ESM::CellRef &ref, bool static_ = false) = 0; virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements) = 0; /// insert a mesh related to the most recent insertBegin call. @@ -71,10 +71,10 @@ namespace MWRender public: - Rendering (CellRenderImp& cellRender, ESM::CellRef &ref) + Rendering (CellRenderImp& cellRender, ESM::CellRef &ref, bool static_ = false) : mCellRender (cellRender), mEnd (false) { - mCellRender.insertBegin (ref); + mCellRender.insertBegin (ref, static_); } ~Rendering() diff --git a/apps/openmw/mwrender/exterior.cpp b/apps/openmw/mwrender/exterior.cpp index fe54af84ac..513fdac2a8 100644 --- a/apps/openmw/mwrender/exterior.cpp +++ b/apps/openmw/mwrender/exterior.cpp @@ -31,12 +31,24 @@ float ExteriorCellRender::lightQuadraticRadiusMult = 1; bool ExteriorCellRender::lightOutQuadInLin = false; -// start inserting a new reference. +int ExteriorCellRender::uniqueID = 0; -void ExteriorCellRender::insertBegin (ESM::CellRef &ref) +ExteriorCellRender::ExteriorCellRender(ESMS::CellStore &_cell, MWWorld::Environment& environment, + MWScene &_scene) + : mCell(_cell), mEnvironment (environment), mScene(_scene), mBase(NULL), mInsert(NULL), mAmbientMode (0) +{ + uniqueID = uniqueID +1; + sg = mScene.getMgr()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID)); +} + + + +void ExteriorCellRender::insertBegin (ESM::CellRef &ref, bool static_) { assert (!mInsert); + isStatic = static_; + // Create and place scene node for this object mInsert = mBase->createChildSceneNode(); @@ -59,7 +71,7 @@ void ExteriorCellRender::insertBegin (ESM::CellRef &ref) // Rotates first around z, then y, then x mInsert->setOrientation(xr*yr*zr); - mInsertMesh.clear(); + mInsertMesh.clear(); } @@ -112,7 +124,7 @@ void ExteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec, } mNpcPart = parent->createChildSceneNode(sceneNodeName); - MeshPtr good2 = NIFLoader::load(mesh); + MeshPtr good2 = NifOgre::NIFLoader::load(mesh); MovableObject *ent = mScene.getMgr()->createEntity(mesh); @@ -201,12 +213,21 @@ void ExteriorCellRender::insertMesh(const std::string &mesh) { assert (mInsert); - NIFLoader::load(mesh); - MovableObject *ent = mScene.getMgr()->createEntity(mesh); - mInsert->attachObject(ent); + NifOgre::NIFLoader::load(mesh); + Entity *ent = mScene.getMgr()->createEntity(mesh); - if (mInsertMesh.empty()) - mInsertMesh = mesh; + if(!isStatic) + { + mInsert->attachObject(ent); + } + else + { + sg->addEntity(ent,mInsert->_getDerivedPosition(),mInsert->_getDerivedOrientation(),mInsert->_getDerivedScale()); + sg->setRegionDimensions(Ogre::Vector3(100000,10000,100000)); + mScene.getMgr()->destroyEntity(ent); + } + if (mInsertMesh.empty()) + mInsertMesh = mesh; } void ExteriorCellRender::insertObjectPhysics() @@ -333,6 +354,8 @@ void ExteriorCellRender::show() configureFog(); insertCell(mCell, mEnvironment); + + sg->build(); } void ExteriorCellRender::hide() @@ -341,15 +364,49 @@ void ExteriorCellRender::hide() mBase->setVisible(false); } +void ExteriorCellRender::destroyAllAttachedMovableObjects(Ogre::SceneNode* i_pSceneNode) +{ + if ( !i_pSceneNode ) + { + assert( false ); + return; + } + + // Destroy all the attached objects + SceneNode::ObjectIterator itObject = i_pSceneNode->getAttachedObjectIterator(); + + while ( itObject.hasMoreElements() ) + { + MovableObject* pObject = static_cast(itObject.getNext()); + i_pSceneNode->getCreator()->destroyMovableObject( pObject ); + } + + // Recurse to child SceneNodes + SceneNode::ChildNodeIterator itChild = i_pSceneNode->getChildIterator(); + + while ( itChild.hasMoreElements() ) + { + SceneNode* pChildNode = static_cast(itChild.getNext()); + destroyAllAttachedMovableObjects( pChildNode ); + } +} + void ExteriorCellRender::destroy() { if(mBase) { + destroyAllAttachedMovableObjects(mBase); mBase->removeAndDestroyAllChildren(); mScene.getMgr()->destroySceneNode(mBase); } - mBase = NULL; + mBase = 0; + + if (sg) + { + mScene.getMgr()->destroyStaticGeometry (sg); + sg = 0; + } } // Switch through lighting modes. diff --git a/apps/openmw/mwrender/exterior.hpp b/apps/openmw/mwrender/exterior.hpp index 93491f263e..6c7f6effc2 100644 --- a/apps/openmw/mwrender/exterior.hpp +++ b/apps/openmw/mwrender/exterior.hpp @@ -6,6 +6,7 @@ #include "OgreColourValue.h" #include +#include namespace Ogre { @@ -57,19 +58,24 @@ namespace MWRender std::string mInsertMesh; Ogre::SceneNode *mNpcPart; + //the static geometry + Ogre::StaticGeometry *sg; + bool isStatic; + // 0 normal, 1 more bright, 2 max int mAmbientMode; Ogre::ColourValue mAmbientColor; /// start inserting a new reference. - virtual void insertBegin (ESM::CellRef &ref); + virtual void insertBegin (ESM::CellRef &ref, bool static_ = false); /// insert a mesh related to the most recent insertBegin call. virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements); virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements, bool translateFirst); virtual void insertMesh(const std::string &mesh); + virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements); virtual void scaleMesh(Ogre::Vector3 axis, std::string sceneNodeName[], int elements); @@ -95,8 +101,7 @@ namespace MWRender public: ExteriorCellRender(ESMS::CellStore &_cell, MWWorld::Environment& environment, - MWScene &_scene) - : mCell(_cell), mEnvironment (environment), mScene(_scene), mBase(NULL), mInsert(NULL), mAmbientMode (0) {} + MWScene &_scene); virtual ~ExteriorCellRender() { destroy(); } @@ -121,6 +126,10 @@ namespace MWRender /// Remove the reference with the given handle permanently from the scene. virtual void deleteObject (const std::string& handle); + + void destroyAllAttachedMovableObjects(Ogre::SceneNode* i_pSceneNode); + + static int uniqueID; }; } diff --git a/apps/openmw/mwrender/interior.cpp b/apps/openmw/mwrender/interior.cpp index dbacaacf95..02bed0898d 100644 --- a/apps/openmw/mwrender/interior.cpp +++ b/apps/openmw/mwrender/interior.cpp @@ -35,7 +35,7 @@ bool InteriorCellRender::lightOutQuadInLin = false; // start inserting a new reference. -void InteriorCellRender::insertBegin (ESM::CellRef &ref) +void InteriorCellRender::insertBegin (ESM::CellRef &ref, bool static_) { assert (!insert); @@ -106,7 +106,7 @@ void InteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec, npcPart = parent->createChildSceneNode(sceneNodeName); //npcPart->showBoundingBox(true); - MeshPtr good2 = NIFLoader::load(mesh); + MeshPtr good2 = NifOgre::NIFLoader::load(mesh); MovableObject *ent = scene.getMgr()->createEntity(mesh); //ent->extr @@ -182,11 +182,11 @@ void InteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec, void InteriorCellRender::insertMesh(const std::string &mesh) { - assert (insert); + assert (insert); - NIFLoader::load(mesh); - MovableObject *ent = scene.getMgr()->createEntity(mesh); - insert->attachObject(ent); + NifOgre::NIFLoader::load(mesh); + MovableObject *ent = scene.getMgr()->createEntity(mesh); + insert->attachObject(ent); if (mInsertMesh.empty()) mInsertMesh = mesh; diff --git a/apps/openmw/mwrender/interior.hpp b/apps/openmw/mwrender/interior.hpp index 7c4b2aaf11..295eaa4755 100644 --- a/apps/openmw/mwrender/interior.hpp +++ b/apps/openmw/mwrender/interior.hpp @@ -63,7 +63,7 @@ namespace MWRender Ogre::ColourValue ambientColor; /// start inserting a new reference. - virtual void insertBegin (ESM::CellRef &ref); + virtual void insertBegin (ESM::CellRef &ref, bool static_ = false); virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements); virtual void scaleMesh(Ogre::Vector3 axis, std::string sceneNodeName[], int elements); /// insert a mesh related to the most recent insertBegin call. diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index fcebaae2be..33ce38cdd7 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -108,4 +108,5 @@ op 0x2000133: Journal op 0x2000134: SetJournalIndex op 0x2000135: GetJournalIndex op 0x2000136: GetPCCell -opcodes 0x2000137-0x3ffffff unused +op 0x2000137: GetButtonPressed +opcodes 0x2000138-0x3ffffff unused diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 0339a11f50..484c0d3ab5 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -15,42 +15,57 @@ namespace MWScript { namespace Gui - { + { class OpEnableWindow : public Interpreter::Opcode0 { MWGui::GuiWindow mWindow; - + public: - + OpEnableWindow (MWGui::GuiWindow window) : mWindow (window) {} - + virtual void execute (Interpreter::Runtime& runtime) { InterpreterContext& context = static_cast (runtime.getContext()); - + context.getWindowManager().allow (mWindow); - } - }; - + } + }; + class OpShowDialogue : public Interpreter::Opcode0 { MWGui::GuiMode mDialogue; - + public: - + OpShowDialogue (MWGui::GuiMode dialogue) : mDialogue (dialogue) {} - + virtual void execute (Interpreter::Runtime& runtime) { InterpreterContext& context = static_cast (runtime.getContext()); - + context.getInputManager().setGuiMode(mDialogue); - } - }; + } + }; + + class OpGetButtonPressed : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + InterpreterContext& context = + static_cast (runtime.getContext()); + + MWWorld::Ptr ptr = context.getReference(); + + runtime.push (context.getWindowManager().readPressedButton()); + } + }; const int opcodeEnableBirthMenu = 0x200000e; const int opcodeEnableClassMenu = 0x200000f; @@ -63,7 +78,8 @@ namespace MWScript const int opcodeEnableStatsMenu = 0x2000016; const int opcodeEnableRest = 0x2000017; const int opcodeShowRestMenu = 0x2000018; - + const int opcodeGetButtonPressed = 0x2000137; + void registerExtensions (Compiler::Extensions& extensions) { extensions.registerInstruction ("enablebirthmenu", "", opcodeEnableBirthMenu); @@ -80,10 +96,12 @@ namespace MWScript extensions.registerInstruction ("enablerestmenu", "", opcodeEnableRest); extensions.registerInstruction ("enablelevelupmenu", "", opcodeEnableRest); - + extensions.registerInstruction ("showrestmenu", "", opcodeShowRestMenu); + + extensions.registerFunction ("getbuttonpressed", 'l', "", opcodeGetButtonPressed); } - + void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (opcodeEnableBirthMenu, @@ -115,6 +133,8 @@ namespace MWScript interpreter.installSegment5 (opcodeShowRestMenu, new OpShowDialogue (MWGui::GM_Rest)); + + interpreter.installSegment5 (opcodeGetButtonPressed, new OpGetButtonPressed); } } } diff --git a/apps/openmw/mwscript/scriptmanager.cpp b/apps/openmw/mwscript/scriptmanager.cpp index 727297a989..07fa934547 100644 --- a/apps/openmw/mwscript/scriptmanager.cpp +++ b/apps/openmw/mwscript/scriptmanager.cpp @@ -12,8 +12,6 @@ #include #include -#include - #include "extensions.hpp" namespace MWScript @@ -21,7 +19,8 @@ namespace MWScript ScriptManager::ScriptManager (const ESMS::ESMStore& store, bool verbose, Compiler::Context& compilerContext) : mErrorHandler (std::cerr), mStore (store), mVerbose (verbose), - mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext) + mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext), + mOpcodesInstalled (false) {} bool ScriptManager::compile (const std::string& name) @@ -99,9 +98,13 @@ namespace MWScript if (!iter->second.empty()) try { - Interpreter::Interpreter interpreter (interpreterContext); - installOpcodes (interpreter); - interpreter.run (&iter->second[0], iter->second.size()); + if (!mOpcodesInstalled) + { + installOpcodes (mInterpreter); + mOpcodesInstalled = true; + } + + mInterpreter.run (&iter->second[0], iter->second.size(), interpreterContext); } catch (const std::exception& e) { diff --git a/apps/openmw/mwscript/scriptmanager.hpp b/apps/openmw/mwscript/scriptmanager.hpp index 639fc59bf7..eab9bdcc08 100644 --- a/apps/openmw/mwscript/scriptmanager.hpp +++ b/apps/openmw/mwscript/scriptmanager.hpp @@ -8,6 +8,7 @@ #include #include +#include #include namespace ESMS @@ -35,6 +36,8 @@ namespace MWScript bool mVerbose; Compiler::Context& mCompilerContext; Compiler::FileParser mParser; + Interpreter::Interpreter mInterpreter; + bool mOpcodesInstalled; std::map > mScripts; diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 564ebe9413..970118a13b 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -84,20 +84,25 @@ namespace MWSound // finding. It takes DOS paths (any case, \\ slashes or / slashes) // relative to the sound dir, and translates them into full paths // of existing files in the filesystem, if they exist. + bool FSstrict; FileFinder::FileFinder files; + FileFinder::FileFinderStrict strict; + FileFinder::FileFinder musicpath; + FileFinder::FileFinderStrict musicpathStrict; SoundImpl(Ogre::Root *root, Ogre::Camera *camera, const ESMS::ESMStore &str, - const std::string &soundDir) + const std::string &soundDir, const std::string &musicDir, bool fsstrict) : mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY))) , updater(mgr) , cameraTracker(mgr) , store(str) - , files(soundDir) + , files(soundDir), strict(soundDir) + ,musicpath(musicDir), musicpathStrict(musicDir) { + FSstrict = fsstrict; cout << "Sound output: " << SOUND_OUT << endl; cout << "Sound decoder: " << SOUND_IN << endl; - // Attach the camera to the camera tracker cameraTracker.followCamera(camera); @@ -111,6 +116,8 @@ namespace MWSound cameraTracker.unfollowCamera(); } + + static std::string toMp3(std::string str) { std::string::size_type i = str.rfind('.'); @@ -122,28 +129,76 @@ namespace MWSound return str; } - bool hasFile(const std::string &str) + bool hasFile(const std::string &str, bool music = false) { - if(files.has(str)) return true; - // Not found? Try with .mp3 - return files.has(toMp3(str)); + if(FSstrict == false) + { + if(music) + { + if(musicpath.has(str)) return true; + + // Not found? Try with .mp3 + return musicpath.has(toMp3(str)); + } + else + { + if(files.has(str)) return true; + return files.has(toMp3(str)); + } + } + else + { + if(music) + { + if(musicpathStrict.has(str)) return true; + + // Not found? Try with .mp3 + return musicpathStrict.has(toMp3(str)); + } + else + { + if(strict.has(str)) return true; + return strict.has(toMp3(str)); + } + } } // Convert a Morrowind sound path (eg. Fx\funny.wav) to full path // with proper slash conversion (eg. datadir/Sound/Fx/funny.wav) - std::string convertPath(const std::string &str) + std::string convertPath(const std::string &str, bool music = false) { - // Search and return - if(files.has(str)) - return files.lookup(str); + if(FSstrict == false) + { + // Search and return + if(music && musicpath.has(str)) + return musicpath.lookup(str); + else if(files.has(str)) + return files.lookup(str); - // Try mp3 if the wav wasn't found - std::string mp3 = toMp3(str); - if(files.has(mp3)) - return files.lookup(mp3); + // Try mp3 if the wav wasn't found + std::string mp3 = toMp3(str); + if(music && musicpath.has(mp3)) + return musicpath.lookup(mp3); + else if(files.has(mp3)) + return files.lookup(mp3); + } + else + { + if(music && musicpathStrict.has(str)) + return musicpathStrict.lookup(str); + else if(strict.has(str)) + return strict.lookup(str); - // Give up - return ""; + // Try mp3 if the wav wasn't found + std::string mp3 = toMp3(str); + if(music && musicpathStrict.has(mp3)) + return musicpathStrict.lookup(mp3); + else if(strict.has(str)) + return strict.lookup(mp3); + } + + // Give up + return ""; } // Convert a soundId to file name, and modify the volume @@ -304,15 +359,31 @@ namespace MWSound } }; + void SoundManager::streamMusicFull (const std::string& filename) + { + if(!mData) return; + + // Play the sound and tell it to stream, if possible. TODO: + // Store the reference, the jukebox will need to check status, + // control volume etc. + if (mData->music) + mData->music->stop(); + mData->music = mData->mgr->load(filename); + mData->music->setStreaming(true); + mData->music->setVolume(0.4); + mData->music->play(); + + } + SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera, const ESMS::ESMStore &store, boost::filesystem::path dataDir, - bool useSound) - : mData(NULL) + bool useSound, bool fsstrict) + : mData(NULL), fsStrict (fsstrict) { MP3Lookup(dataDir / "Music/Explore/"); if(useSound) - mData = new SoundImpl(root, camera, store, (dataDir / "Sound").string()); + mData = new SoundImpl(root, camera, store, (dataDir / "Sound").string(), (dataDir / "Music").string(), fsstrict); } SoundManager::~SoundManager() @@ -321,6 +392,16 @@ namespace MWSound delete mData; } + void SoundManager::streamMusic(const std::string& filename) + { + if(mData->hasFile(filename, true)) + { + std::string fullpath = mData->convertPath(filename, true); + streamMusicFull(fullpath); + } + } + + void SoundManager::MP3Lookup(boost::filesystem::path dir) { boost::filesystem::directory_iterator dir_iter(dir), dir_end; @@ -353,7 +434,7 @@ namespace MWSound try { std::cout << "Playing " << music << "\n"; - streamMusic(music); + streamMusicFull(music); } catch(std::exception &e) { @@ -397,21 +478,6 @@ namespace MWSound return !mData->isPlaying(ptr, "_say_sound"); } - void SoundManager::streamMusic (const std::string& filename) - { - if(!mData) return; - - // Play the sound and tell it to stream, if possible. TODO: - // Store the reference, the jukebox will need to check status, - // control volume etc. - if (mData->music) - mData->music->stop(); - mData->music = mData->mgr->load(filename); - mData->music->setStreaming(true); - mData->music->setVolume(0.4); - mData->music->play(); - - } void SoundManager::playSound (const std::string& soundId, float volume, float pitch) { diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index 8c48986db4..ab9559176e 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -27,19 +27,28 @@ namespace MWSound // Hide implementation details - engine.cpp is compiling // enough as it is. struct SoundImpl; - + SoundImpl *mData; std::vector files; - + bool fsStrict; + + void streamMusicFull (const std::string& filename); + ///< Play a soundifle + /// \param absolute filename public: + SoundManager(Ogre::Root*, Ogre::Camera*, const ESMS::ESMStore &store, - boost::filesystem::path dataDir, bool useSound); + boost::filesystem::path dataDir, bool useSound, bool fsstrict); ~SoundManager(); + void streamMusic(const std::string& filename); + ///< Play a soundifle + /// \param filename name of a sound file in "Music/" in the data directory. + void startRandomTitle(); void MP3Lookup(boost::filesystem::path dir); - //struct SoundImpl; + bool isMusicPlaying(); SoundImpl getMData(); @@ -51,9 +60,7 @@ namespace MWSound bool sayDone (MWWorld::Ptr reference) const; ///< Is actor not speaking? - void streamMusic (const std::string& filename); - ///< Play a soundifle - /// \param filename name of a sound file in "Music/" in the data directory. + void playSound (const std::string& soundId, float volume, float pitch); ///< Play a sound, independently of 3D-position diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index c05bfe1561..971a79c15a 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -16,7 +16,7 @@ namespace MWWorld ESMS::CellRefList ingreds; ESMS::CellRefList lights; ESMS::CellRefList lockpicks; - ESMS::CellRefList miscItems; + ESMS::CellRefList miscItems; ESMS::CellRefList probes; ESMS::CellRefList repairs; ESMS::CellRefList weapons; diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index 68e3a745d3..063f54d6f8 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -135,7 +135,7 @@ namespace MWWorld if (ESMS::LiveCellRef *ref = cell.lockpicks.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.miscItems.find (name)) + if (ESMS::LiveCellRef *ref = cell.miscItems.find (name)) return Ptr (ref, &cell); if (ESMS::LiveCellRef *ref = cell.npcs.find (name)) @@ -198,7 +198,7 @@ namespace MWWorld if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.lockpicks)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.miscItems)) + if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.miscItems)) return Ptr (ref, &cell); if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.npcs)) @@ -284,7 +284,6 @@ namespace MWWorld removeScripts (iter->first); mEnvironment.mMechanicsManager->dropActors (iter->first); - iter->second->destroy(); mEnvironment.mSoundManager->stopSound (iter->first); delete iter->second; mActiveCells.erase (iter); @@ -410,9 +409,9 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, - bool newGame, Environment& environment) + bool newGame, Environment& environment, const std::string& encoding) : mSkyManager (0), mScene (renderer,physEng), mPlayer (0), mCurrentCell (0), mGlobalVariables (0), - mSky (false), mCellChanged (false), mEnvironment (environment) + mSky (false), mCellChanged (false), mEnvironment (environment), mNextDynamicRecord (0) { mPhysEngine = physEng; @@ -421,6 +420,7 @@ namespace MWWorld std::cout << "Loading ESM " << masterPath.string() << "\n"; // This parses the ESM file and loads a sample cell + mEsm.setEncoding(encoding); mEsm.open (masterPath.string()); mStore.load (mEsm); @@ -871,4 +871,36 @@ namespace MWWorld { return mScene.toggleRenderMode (mode); } + + std::pair World::createRecord (const ESM::Potion& record) + { + /// \todo Rewrite the ESMStore so that a dynamic 2nd ESMStore can be attached to it. + /// This function should then insert the record into the 2nd store (the code for this + /// should also be moved to the ESMStore class). It might be a good idea to review + /// the STL-container usage of the ESMStore before the rewrite. + + std::ostringstream stream; + stream << "$dynamic" << mNextDynamicRecord++; + + const ESM::Potion *created = + &mStore.potions.list.insert (std::make_pair (stream.str(), record)).first->second; + + mStore.all.insert (std::make_pair (stream.str(), ESM::REC_ALCH)); + + return std::make_pair (stream.str(), created); + } + + std::pair World::createRecord (const ESM::Class& record) + { + /// \todo See function above. + std::ostringstream stream; + stream << "$dynamic" << mNextDynamicRecord++; + + const ESM::Class *created = + &mStore.classes.list.insert (std::make_pair (stream.str(), record)).first->second; + + mStore.all.insert (std::make_pair (stream.str(), ESM::REC_CLAS)); + + return std::make_pair (stream.str(), created); + } } diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/world.hpp index 160c20314e..cccd8816df 100644 --- a/apps/openmw/mwworld/world.hpp +++ b/apps/openmw/mwworld/world.hpp @@ -79,6 +79,7 @@ namespace MWWorld bool mSky; bool mCellChanged; Environment& mEnvironment; + int mNextDynamicRecord; OEngine::Physic::PhysicEngine* mPhysEngine; @@ -115,7 +116,7 @@ namespace MWWorld World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, bool newGame, - Environment& environment); + Environment& environment, const std::string& encoding); ~World(); @@ -202,6 +203,14 @@ namespace MWWorld bool toggleRenderMode (RenderMode mode); ///< Toggle a render mode. ///< \return Resulting mode + + std::pair createRecord (const ESM::Potion& record); + ///< Create a new recrod (of type potion) in the ESM store. + /// \return ID, pointer to created record + + std::pair createRecord (const ESM::Class& record); + ///< Create a new recrod (of type class) in the ESM store. + /// \return ID, pointer to created record }; } diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 4691eb5468..2178be318e 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -23,16 +23,200 @@ #include "bsa_archive.hpp" +#include #include #include #include #include "bsa_file.hpp" #include +namespace +{ + using namespace Ogre; using namespace Mangle::Stream; +using namespace Bsa; + +struct ciLessBoost : std::binary_function +{ + bool operator() (const std::string & s1, const std::string & s2) const { + //case insensitive version of is_less + return lexicographical_compare(s1, s2, boost::algorithm::is_iless()); + } +}; + +static bool fsstrict = false; /// An OGRE Archive wrapping a BSAFile archive +class DirArchive: public Ogre::FileSystemArchive +{ + + boost::filesystem::path currentdir; + std::map, ciLessBoost> m; + unsigned int cutoff; + + bool comparePortion(std::string file1, std::string file2, int start, int size) const + { + for(int i = start; i < start+size; i++) + { + char one = file1.at(i); + char two = file2.at(i); + if(tolower(one) != tolower(two) ) + return false; + } + return true; + } + + public: + + DirArchive(const String& name) + : FileSystemArchive(name, "Dir"), currentdir (name) + { + mType = "Dir"; + std::string s = name; + cutoff = s.size() + 1; + if(fsstrict == false) + populateMap(currentdir); + + } + void populateMap(boost::filesystem::path d){ + //need to cut off first + boost::filesystem::directory_iterator dir_iter(d), dir_end; + std::vector filesind; + boost::filesystem::path f; + for(;dir_iter != dir_end; dir_iter++) + { + if(boost::filesystem::is_directory(*dir_iter)) + populateMap(*dir_iter); + else + { + + f = *dir_iter; + std::string s = f.string(); + + std::string small; + if(cutoff < s.size()) + small = s.substr(cutoff, s.size() - cutoff); + else + small = s.substr(cutoff - 1, s.size() - cutoff); + + filesind.push_back(small); + } + } + std::string small; + std::string original = d.string(); + if(cutoff < original.size()) + small = original.substr(cutoff, original.size() - cutoff); + else + small = original.substr(cutoff - 1, original.size() - cutoff); + m[small] = filesind; + + } + + bool isCaseSensitive() const { return fsstrict; } + + // The archive is loaded in the constructor, and never unloaded. + void load() {} + void unload() {} + + bool exists(const String& filename) { + std::string copy = filename; + + + + for (unsigned int i = 0; i < filename.size(); i++) + { + if(copy.at(i) == '\\' ){ + copy.replace(i, 1, "/"); + } + } + + + if(copy.at(0) == '\\' || copy.at(0) == '/') + { + copy.erase(0, 1); + } + if(fsstrict == true) + { + //std::cout << "fsstrict " << copy << "\n"; + return FileSystemArchive::exists(copy); + } + + + int last = copy.size() - 1; + int i = last; + + for (;last >= 0; i--) + { + if(copy.at(i) == '/' || copy.at(i) == '\\') + break; + } + + std::string folder = copy.substr(0, i); //folder with no slash + + std::vector& current = m[folder]; + + for(std::vector::iterator iter = current.begin(); iter != current.end(); iter++) + { + if(comparePortion(*iter, copy, i + 1, copy.size() - i -1) == true){ + return FileSystemArchive::exists(*iter); + } + } + + + return false; + } + + DataStreamPtr open(const String& filename, bool readonly = true) const + { + std::map, ciLessBoost> mlocal = m; + std::string copy = filename; + + + + for (unsigned int i = 0; i < filename.size(); i++) + { + if(copy.at(i) == '\\' ){ + copy.replace(i, 1, "/"); + } + } + + + if(copy.at(0) == '\\' || copy.at(0) == '/') + { + copy.erase(0, 1); + } + + if(fsstrict == true) + { + return FileSystemArchive::open(copy, readonly); + } + + + int last = copy.size() - 1; + int i = last; + + for (;last >= 0; i--) + { + if(copy.at(i) == '/' || copy.at(i) == '\\') + break; + } + + std::string folder = copy.substr(0, i); //folder with no slash + std::vector current = mlocal[folder]; + + for(std::vector::iterator iter = current.begin(); iter != current.end(); iter++) + { + if(comparePortion(*iter, copy, i + 1, copy.size() - i -1) == true){ + return FileSystemArchive::open(*iter, readonly); + } + } + DataStreamPtr p; + return p; + } + +}; + class BSAArchive : public Archive { BSAFile arc; @@ -145,7 +329,27 @@ public: void destroyInstance( Archive* arch) { delete arch; } }; +class DirArchiveFactory : public FileSystemArchiveFactory +{ +public: + const String& getType() const + { + static String name = "Dir"; + return name; + } + + Archive *createInstance( const String& name ) + { + return new DirArchive(name); + } + + void destroyInstance( Archive* arch) { delete arch; } +}; + + static bool init = false; +static bool init2 = false; + static void insertBSAFactory() { if(!init) @@ -155,6 +359,20 @@ static void insertBSAFactory() } } +static void insertDirFactory() +{ + if(!init2) + { + ArchiveManager::getSingleton().addArchiveFactory( new DirArchiveFactory ); + init2 = true; + } +} + +} + +namespace Bsa +{ + // The function below is the only publicly exposed part of this file void addBSA(const std::string& name, const std::string& group) @@ -163,3 +381,14 @@ void addBSA(const std::string& name, const std::string& group) ResourceGroupManager::getSingleton(). addResourceLocation(name, "BSA", group); } + +void addDir(const std::string& name, const bool& fs, const std::string& group) +{ + fsstrict = fs; + insertDirFactory(); + + ResourceGroupManager::getSingleton(). + addResourceLocation(name, "Dir", group); +} + +} diff --git a/components/bsa/bsa_archive.hpp b/components/bsa/bsa_archive.hpp index bde6d9c3b3..18f7377ff2 100644 --- a/components/bsa/bsa_archive.hpp +++ b/components/bsa/bsa_archive.hpp @@ -22,12 +22,21 @@ */ #include +#include +#include +#include -#ifndef _BSA_ARCHIVE_H_ -#define _BSA_ARCHIVE_H_ +#ifndef BSA_BSA_ARCHIVE_H +#define BSA_BSA_ARCHIVE_H + +namespace Bsa +{ /// Add the given BSA file as an input archive in the Ogre resource /// system. void addBSA(const std::string& file, const std::string& group="General"); +void addDir(const std::string& file, const bool& fs, const std::string& group="General"); + +} #endif diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index e37d38bb2d..95358a3628 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -32,6 +32,7 @@ using namespace std; using namespace Mangle::Stream; +using namespace Bsa; /// Error handling void BSAFile::fail(const string &msg) diff --git a/components/bsa/bsa_file.hpp b/components/bsa/bsa_file.hpp index 7ec101b43b..f54a64d2af 100644 --- a/components/bsa/bsa_file.hpp +++ b/components/bsa/bsa_file.hpp @@ -21,8 +21,8 @@ */ -#ifndef _BSA_FILE_H_ -#define _BSA_FILE_H_ +#ifndef BSA_BSA_FILE_H +#define BSA_BSA_FILE_H #include #include @@ -31,6 +31,9 @@ #include #include +namespace Bsa +{ + /** This class is used to read "Bethesda Archive Files", or BSAs. */ @@ -131,4 +134,6 @@ class BSAFile { return files; } }; +} + #endif diff --git a/components/bsa/tests/bsa_file_test.cpp b/components/bsa/tests/bsa_file_test.cpp index ea3f9291c8..07ee73d17e 100644 --- a/components/bsa/tests/bsa_file_test.cpp +++ b/components/bsa/tests/bsa_file_test.cpp @@ -11,6 +11,7 @@ #include using namespace std; +using namespace Bsa; BSAFile bsa; diff --git a/components/bsa/tests/bsatool.cpp b/components/bsa/tests/bsatool.cpp index da96c2ed5a..df37e3827c 100644 --- a/components/bsa/tests/bsatool.cpp +++ b/components/bsa/tests/bsatool.cpp @@ -11,6 +11,7 @@ using namespace std; using namespace Mangle::Stream; +using namespace Bsa; int main(int argc, char** argv) { diff --git a/components/bsa/tests/ogre_archive_test.cpp b/components/bsa/tests/ogre_archive_test.cpp index 1a4334e60f..9cd57240f8 100644 --- a/components/bsa/tests/ogre_archive_test.cpp +++ b/components/bsa/tests/ogre_archive_test.cpp @@ -19,7 +19,7 @@ int main() Root *root = new Root("","",""); // Add the BSA - addBSA("../../data/Morrowind.bsa"); + Bsa::addBSA("../../data/Morrowind.bsa"); // Pick a sample file String tex = "textures\\tx_natural_cavern_wall13.dds"; diff --git a/components/esm/esm_reader.cpp b/components/esm/esm_reader.cpp index 00a34e2df9..e9bfcf5eee 100644 --- a/components/esm/esm_reader.cpp +++ b/components/esm/esm_reader.cpp @@ -3,6 +3,8 @@ namespace ESM { +using namespace Misc; + ESM_Context ESMReader::getContext() { // Update the file position before returning @@ -324,7 +326,7 @@ std::string ESMReader::getString(int size) mEsm->read(ptr, size); // Convert to UTF8 and return - return ToUTF8::getUtf8(ToUTF8::WINDOWS_1252); + return ToUTF8::getUtf8(mEncoding); } void ESMReader::fail(const std::string &msg) @@ -342,4 +344,21 @@ void ESMReader::fail(const std::string &msg) throw std::runtime_error(ss.str()); } +void ESMReader::setEncoding(const std::string& encoding) +{ + if (encoding == "win1250") + { + mEncoding = ToUTF8::WINDOWS_1250; + } + else if (encoding == "win1251") + { + mEncoding = ToUTF8::WINDOWS_1251; + } + else + { + // Default Latin encoding + mEncoding = ToUTF8::WINDOWS_1252; + } +} + } diff --git a/components/esm/esm_reader.hpp b/components/esm/esm_reader.hpp index dea1980e2e..e5b230748c 100644 --- a/components/esm/esm_reader.hpp +++ b/components/esm/esm_reader.hpp @@ -350,6 +350,9 @@ public: /// Used for error handling void fail(const std::string &msg); + /// Sets font encoding for ESM strings + void setEncoding(const std::string& encoding); + private: Mangle::Stream::StreamPtr mEsm; @@ -360,6 +363,7 @@ private: SaveData mSaveData; MasterList mMasters; + ToUTF8::FromType mEncoding; }; } #endif diff --git a/components/esm/loadmisc.cpp b/components/esm/loadmisc.cpp index dc3d04f50a..0206661c43 100644 --- a/components/esm/loadmisc.cpp +++ b/components/esm/loadmisc.cpp @@ -3,7 +3,7 @@ namespace ESM { -void Misc::load(ESMReader &esm) +void Miscellaneous::load(ESMReader &esm) { model = esm.getHNString("MODL"); name = esm.getHNOString("FNAM"); diff --git a/components/esm/loadmisc.hpp b/components/esm/loadmisc.hpp index 36400eb74a..7e151f7970 100644 --- a/components/esm/loadmisc.hpp +++ b/components/esm/loadmisc.hpp @@ -11,7 +11,7 @@ namespace ESM * carried, bought and sold. It also includes keys. */ -struct Misc +struct Miscellaneous { struct MCDTstruct { diff --git a/components/esm_store/cell_store.hpp b/components/esm_store/cell_store.hpp index 43860dff38..59464236fd 100644 --- a/components/esm_store/cell_store.hpp +++ b/components/esm_store/cell_store.hpp @@ -17,6 +17,7 @@ #include #include +#include namespace ESMS { @@ -99,7 +100,7 @@ namespace ESMS CellRefList itemLists; CellRefList lights; CellRefList lockpicks; - CellRefList miscItems; + CellRefList miscItems; CellRefList npcs; CellRefList probes; CellRefList repairs; @@ -187,7 +188,14 @@ namespace ESMS // Get each reference in turn while(cell->getNextRef(esm, ref)) { - int rec = store.find(ref.refID); + std::string lowerCase; + + std::transform (ref.refID.begin(), ref.refID.end(), std::back_inserter (lowerCase), + (int(*)(int)) std::tolower); + + int rec = store.find(ref.refID); + + ref.refID = lowerCase; /* We can optimize this further by storing the pointer to the record itself in store.all, so that we don't need to look it diff --git a/components/esm_store/store.cpp b/components/esm_store/store.cpp index 9f301d1637..2b5b977aa0 100644 --- a/components/esm_store/store.cpp +++ b/components/esm_store/store.cpp @@ -85,7 +85,13 @@ void ESMStore::load(ESMReader &esm) dialogue = 0; // Insert the reference into the global lookup - if(!id.empty()) + if(!id.empty() && + (n.val==REC_ACTI || n.val==REC_ALCH || n.val==REC_APPA || n.val==REC_ARMO || + n.val==REC_BOOK || n.val==REC_CLOT || n.val==REC_CONT || n.val==REC_CREA || + n.val==REC_DOOR || n.val==REC_INGR || n.val==REC_LEVC || n.val==REC_LEVI || + n.val==REC_LIGH || n.val==REC_LOCK || n.val==REC_MISC || n.val==REC_NPC_ || + n.val==REC_PROB || n.val==REC_REPA || n.val==REC_STAT || n.val==REC_WEAP) + ) all[id] = n.val; } } diff --git a/components/esm_store/store.hpp b/components/esm_store/store.hpp index 2af6fab37b..e3bbf9e82c 100644 --- a/components/esm_store/store.hpp +++ b/components/esm_store/store.hpp @@ -52,7 +52,7 @@ namespace ESMS RecListT itemLists; RecListT lights; RecListT lockpicks; - RecListT miscItems; + RecListT miscItems; RecListWithIDT npcs; RecListT npcChange; RecListT probes; diff --git a/components/file_finder/file_finder.hpp b/components/file_finder/file_finder.hpp index cfa07dce9c..0e1e072263 100644 --- a/components/file_finder/file_finder.hpp +++ b/components/file_finder/file_finder.hpp @@ -20,9 +20,9 @@ class FileFinderT void add(const boost::filesystem::path &pth) { - std::string file = pth.string(); - std::string key = file.substr(cut); - owner->table[key] = file; + std::string file = pth.string(); + std::string key = file.substr(cut); + owner->table[key] = file; } }; @@ -50,17 +50,18 @@ public: bool has(const std::string& file) const { - return table.find(file) != table.end(); + return table.find(file) != table.end(); } // Find the full path from a relative path. const std::string &lookup(const std::string& file) const { - return table.find(file)->second; + return table.find(file)->second; } }; // The default is to use path_less for equality checks typedef FileFinderT FileFinder; +typedef FileFinderT FileFinderStrict; } #endif diff --git a/components/file_finder/filename_less.hpp b/components/file_finder/filename_less.hpp index 9f251b1c84..bc3186ce98 100644 --- a/components/file_finder/filename_less.hpp +++ b/components/file_finder/filename_less.hpp @@ -44,6 +44,41 @@ struct path_less return compareString(a.c_str(), b.c_str()) < 0; } }; + +struct path_slash +{ + int compareChar(char a, char b) const + { + if(a>b) return 1; + else if(a #include #include +#include +#include #include @@ -21,11 +23,12 @@ namespace Files return left #endif -#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX +#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE #include //getenv #endif @@ -19,7 +19,9 @@ std::string Files::getPath (PathTypeEnum parType, const std::string parApp, cons if (parType==Path_ConfigGlobal) { #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE - theBasePath = Ogre::macBundlePath() + "/Contents/MacOS/"; //FIXME do we have global/local with OSX? + boost::filesystem::path path(Ogre::macBundlePath()); + path = path.parent_path(); + theBasePath = path.string() + "/"; #elif OGRE_PLATFORM == OGRE_PLATFORM_LINUX theBasePath = "/etc/"+parApp+"/"; #else @@ -29,9 +31,7 @@ std::string Files::getPath (PathTypeEnum parType, const std::string parApp, cons } else if (parType==Path_ConfigUser) { -#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE - theBasePath = Ogre::macBundlePath() + "/Contents/MacOS/"; //FIXME do we have global/local with OSX? -#elif OGRE_PLATFORM == OGRE_PLATFORM_LINUX +#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE const char* theDir; if ((theDir = getenv("OPENMW_HOME")) != NULL) { diff --git a/components/interpreter/interpreter.cpp b/components/interpreter/interpreter.cpp index 44f626aadb..10937e6bca 100644 --- a/components/interpreter/interpreter.cpp +++ b/components/interpreter/interpreter.cpp @@ -134,8 +134,7 @@ namespace Interpreter throw std::runtime_error (error.str()); } - Interpreter::Interpreter (Context& context) - : mRuntime (context) + Interpreter::Interpreter() {} Interpreter::~Interpreter() @@ -195,11 +194,11 @@ namespace Interpreter mSegment5.insert (std::make_pair (code, opcode)); } - void Interpreter::run (const Type_Code *code, int codeSize) + void Interpreter::run (const Type_Code *code, int codeSize, Context& context) { assert (codeSize>=4); - mRuntime.configure (code, codeSize); + mRuntime.configure (code, codeSize, context); int opcodes = static_cast (code[0]); diff --git a/components/interpreter/interpreter.hpp b/components/interpreter/interpreter.hpp index c67273707d..e1016235a0 100644 --- a/components/interpreter/interpreter.hpp +++ b/components/interpreter/interpreter.hpp @@ -21,23 +21,23 @@ namespace Interpreter std::map mSegment3; std::map mSegment4; std::map mSegment5; - + // not implemented Interpreter (const Interpreter&); Interpreter& operator= (const Interpreter&); - + void execute (Type_Code code); - + void abortUnknownCode (int segment, int opcode); - + void abortUnknownSegment (Type_Code code); - + public: - - Interpreter (Context& context); - + + Interpreter(); + ~Interpreter(); - + void installSegment0 (int code, Opcode1 *opcode); ///< ownership of \a opcode is transferred to *this. @@ -55,10 +55,9 @@ namespace Interpreter void installSegment5 (int code, Opcode0 *opcode); ///< ownership of \a opcode is transferred to *this. - - void run (const Type_Code *code, int codeSize); + + void run (const Type_Code *code, int codeSize, Context& context); }; } #endif - diff --git a/components/interpreter/runtime.cpp b/components/interpreter/runtime.cpp index f3a3a905df..dcf17d2558 100644 --- a/components/interpreter/runtime.cpp +++ b/components/interpreter/runtime.cpp @@ -7,50 +7,51 @@ namespace Interpreter { - Runtime::Runtime (Context& context) : mContext (context), mCode (0), mPC (0) {} - + Runtime::Runtime() : mContext (0), mCode (0), mPC (0) {} + int Runtime::getPC() const { return mPC; } - + int Runtime::getIntegerLiteral (int index) const { assert (index>=0 && index (mCode[1])); - + const Type_Code *literalBlock = mCode + 4 + mCode[0]; - + return *reinterpret_cast (&literalBlock[index]); } - + float Runtime::getFloatLiteral (int index) const { assert (index>=0 && index (mCode[2])); - + const Type_Code *literalBlock = mCode + 4 + mCode[0] + mCode[1]; - + return *reinterpret_cast (&literalBlock[index]); } - + std::string Runtime::getStringLiteral (int index) const { assert (index>=0 && index (mCode[3])); - + const char *literalBlock = reinterpret_cast (mCode + 4 + mCode[0] + mCode[1] + mCode[2]); - + for (; index; --index) { literalBlock += std::strlen (literalBlock) + 1; } - + return literalBlock; } - - void Runtime::configure (const Interpreter::Type_Code *code, int codeSize) - { + + void Runtime::configure (const Interpreter::Type_Code *code, int codeSize, Context& context) + { clear(); - + + mContext = &context; mCode = code; mCodeSize = codeSize; mPC = 0; @@ -58,54 +59,55 @@ namespace Interpreter void Runtime::clear() { + mContext = 0; mCode = 0; mCodeSize = 0; mStack.clear(); } - + void Runtime::setPC (int PC) { mPC = PC; - } - + } + void Runtime::push (const Data& data) { mStack.push_back (data); } - + void Runtime::push (Type_Integer value) { Data data; data.mInteger = value; push (data); } - + void Runtime::push (Type_Float value) { Data data; data.mFloat = value; push (data); } - + void Runtime::pop() { if (mStack.empty()) throw std::runtime_error ("stack underflow"); - + mStack.resize (mStack.size()-1); } - + Data& Runtime::operator[] (int Index) { if (Index<0 || Index>=static_cast (mStack.size())) throw std::runtime_error ("stack index out of range"); - + return mStack[mStack.size()-Index-1]; } - + Context& Runtime::getContext() { - return mContext; + assert (mContext); + return *mContext; } } - diff --git a/components/interpreter/runtime.hpp b/components/interpreter/runtime.hpp index e9ba01041d..2811ab0f0c 100644 --- a/components/interpreter/runtime.hpp +++ b/components/interpreter/runtime.hpp @@ -11,52 +11,52 @@ namespace Interpreter class Context; /// Runtime data and engine interface - + class Runtime { - Context& mContext; + Context *mContext; const Type_Code *mCode; int mCodeSize; int mPC; std::vector mStack; - + public: - - Runtime (Context& context); - + + Runtime (); + int getPC() const; ///< return program counter. - + int getIntegerLiteral (int index) const; - + float getFloatLiteral (int index) const; - + std::string getStringLiteral (int index) const; - - void configure (const Type_Code *code, int codeSize); + + void configure (const Type_Code *code, int codeSize, Context& context); ///< \a context and \a code must exist as least until either configure, clear or /// the destructor is called. \a codeSize is given in 32-bit words. - + void clear(); - + void setPC (int PC); ///< set program counter. - + void push (const Data& data); ///< push data on stack - + void push (Type_Integer value); ///< push integer data on stack. - + void push (Type_Float value); ///< push float data on stack. - + void pop(); ///< pop stack - + Data& operator[] (int Index); ///< Access stack member, counted from the top. - + Context& getContext(); }; } diff --git a/components/misc/fileops.cpp b/components/misc/fileops.cpp index b7b9a8d781..eb3b58d84d 100644 --- a/components/misc/fileops.cpp +++ b/components/misc/fileops.cpp @@ -4,9 +4,13 @@ #include +namespace Misc +{ + bool isFile(const char *name) { boost::filesystem::path cfg_file_path(name); return boost::filesystem::exists(cfg_file_path); } +} diff --git a/components/misc/fileops.hpp b/components/misc/fileops.hpp index 004f01dd6b..f4e7701bb8 100644 --- a/components/misc/fileops.hpp +++ b/components/misc/fileops.hpp @@ -1,8 +1,11 @@ -#ifndef __FILEOPS_H_ -#define __FILEOPS_H_ +#ifndef MISC_FILEOPS_H +#define MISC_FILEOPS_H #include +namespace Misc +{ + /// Check if a given path is an existing file (not a directory) bool isFile(const char *name); @@ -10,4 +13,6 @@ bool isFile(const char *name); std::string macBundlePath(); #endif +} + #endif diff --git a/components/misc/slice_array.hpp b/components/misc/slice_array.hpp index f7e2ffeffb..cd58e7bd69 100644 --- a/components/misc/slice_array.hpp +++ b/components/misc/slice_array.hpp @@ -21,14 +21,17 @@ */ -#ifndef _SLICE_ARRAY_H_ -#define _SLICE_ARRAY_H_ +#ifndef MISC_SLICE_ARRAY_H +#define MISC_SLICE_ARRAY_H // A simple array implementation containing a pointer and a // length. Used for holding slices into a data buffer. #include #include +namespace Misc +{ + template struct SliceArray { @@ -74,4 +77,6 @@ typedef SliceArray SString; typedef SliceArray IntArray; typedef SliceArray FloatArray; +} + #endif diff --git a/components/misc/stringops.cpp b/components/misc/stringops.cpp index f6b63372c2..53eed1fdce 100644 --- a/components/misc/stringops.cpp +++ b/components/misc/stringops.cpp @@ -3,6 +3,9 @@ #include #include +namespace Misc +{ + bool begins(const char* str1, const char* str2) { while(*str2) @@ -57,3 +60,5 @@ bool iends(const char* str1, const char* str2) return strcasecmp(str2, str1+len1-len2) == 0; } + +} diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index e76b8a87a2..a32104bf37 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -1,5 +1,8 @@ -#ifndef __STRINGOPS_H -#define __STRINGOPS_H +#ifndef MISC_STRINGOPS_H +#define MISC_STRINGOPS_H + +namespace Misc +{ /// Returns true if str1 begins with substring str2 bool begins(const char* str1, const char* str2); @@ -13,4 +16,6 @@ bool ibegins(const char* str1, const char* str2); /// Case insensitive, returns true if str1 ends with substring str2 bool iends(const char* str1, const char* str2); +} + #endif diff --git a/components/misc/tests/slice_test.cpp b/components/misc/tests/slice_test.cpp index 950194279f..a0ea55311d 100644 --- a/components/misc/tests/slice_test.cpp +++ b/components/misc/tests/slice_test.cpp @@ -8,21 +8,21 @@ using namespace std; int main() { - SString s, t; - s = SString("hello"); + Misc::SString s, t; + s = Misc::SString("hello"); cout << s.toString() << ", len=" << s.length << endl; cout << (s=="hel") << (s=="hell") << (s=="hello") << endl; t = s; - s = SString("othello"+2, 4); + s = Misc::SString("othello"+2, 4); cout << s.toString() << ", len=" << s.length << endl; cout << (s=="hel") << (s=="hell") << (s=="hello") << endl; - cout << (s==t) << (SString("hello")==t) << endl; + cout << (s==t) << (Misc::SString("hello")==t) << endl; const int arr[4] = {1,2,3,4}; - IntArray ia(arr,4); + Misc::IntArray ia(arr,4); cout << ia.length << " " << ia.ptr[2] << endl; diff --git a/components/misc/tests/strops_test.cpp b/components/misc/tests/strops_test.cpp index ca5bd55a9c..2a1fdd77d0 100644 --- a/components/misc/tests/strops_test.cpp +++ b/components/misc/tests/strops_test.cpp @@ -4,45 +4,45 @@ int main() { - assert(begins("abc", "a")); - assert(begins("abc", "ab")); - assert(begins("abc", "abc")); - assert(begins("abcd", "abc")); + assert(Misc::begins("abc", "a")); + assert(Misc::begins("abc", "ab")); + assert(Misc::begins("abc", "abc")); + assert(Misc::begins("abcd", "abc")); - assert(!begins("abc", "b")); - assert(!begins("abc", "bc")); - assert(!begins("abc", "bcd")); - assert(!begins("abc", "abcd")); + assert(!Misc::begins("abc", "b")); + assert(!Misc::begins("abc", "bc")); + assert(!Misc::begins("abc", "bcd")); + assert(!Misc::begins("abc", "abcd")); - assert(ibegins("Abc", "a")); - assert(ibegins("aBc", "ab")); - assert(ibegins("abC", "abc")); - assert(ibegins("abcD", "abc")); + assert(Misc::ibegins("Abc", "a")); + assert(Misc::ibegins("aBc", "ab")); + assert(Misc::ibegins("abC", "abc")); + assert(Misc::ibegins("abcD", "abc")); - assert(!ibegins("abc", "b")); - assert(!ibegins("abc", "bc")); - assert(!ibegins("abc", "bcd")); - assert(!ibegins("abc", "abcd")); + assert(!Misc::ibegins("abc", "b")); + assert(!Misc::ibegins("abc", "bc")); + assert(!Misc::ibegins("abc", "bcd")); + assert(!Misc::ibegins("abc", "abcd")); - assert(ends("abc", "c")); - assert(ends("abc", "bc")); - assert(ends("abc", "abc")); - assert(ends("abcd", "abcd")); + assert(Misc::ends("abc", "c")); + assert(Misc::ends("abc", "bc")); + assert(Misc::ends("abc", "abc")); + assert(Misc::ends("abcd", "abcd")); - assert(!ends("abc", "b")); - assert(!ends("abc", "ab")); - assert(!ends("abc", "bcd")); - assert(!ends("abc", "abcd")); + assert(!Misc::ends("abc", "b")); + assert(!Misc::ends("abc", "ab")); + assert(!Misc::ends("abc", "bcd")); + assert(!Misc::ends("abc", "abcd")); - assert(iends("Abc", "c")); - assert(iends("aBc", "bc")); - assert(iends("abC", "abc")); - assert(iends("abcD", "abcd")); + assert(Misc::iends("Abc", "c")); + assert(Misc::iends("aBc", "bc")); + assert(Misc::iends("abC", "abc")); + assert(Misc::iends("abcD", "abcd")); - assert(!iends("abc", "b")); - assert(!iends("abc", "ab")); - assert(!iends("abc", "bcd")); - assert(!iends("abc", "abcd")); + assert(!Misc::iends("abc", "b")); + assert(!Misc::iends("abc", "ab")); + assert(!Misc::iends("abc", "bcd")); + assert(!Misc::iends("abc", "abcd")); return 0; } diff --git a/components/nif/controlled.hpp b/components/nif/controlled.hpp index 67f2dec36f..d599501321 100644 --- a/components/nif/controlled.hpp +++ b/components/nif/controlled.hpp @@ -46,7 +46,7 @@ public: class Named : public Controlled { public: - SString name; + Misc::SString name; void read(NIFFile *nif) { diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 6fc2c4b820..c483b48735 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -25,6 +25,7 @@ #define _NIF_DATA_H_ #include "controlled.hpp" +#include namespace Nif { @@ -37,7 +38,7 @@ public: // internal (data is inside the nif itself) texture? bool external; - SString filename; // In case of external textures + Misc::SString filename; // In case of external textures NiPixelDataPtr data; // In case of internal textures /* Pixel layout @@ -88,7 +89,7 @@ public: class ShapeData : public Record { public: - FloatArray vertices, normals, colors, uvlist; + Misc::FloatArray vertices, normals, colors, uvlist; const Vector *center; float radius; @@ -123,7 +124,7 @@ class NiTriShapeData : public ShapeData { public: // Triangles, three vertex indices per triangle - SliceArray triangles; + Misc::SliceArray triangles; void read(NIFFile *nif) { @@ -377,7 +378,7 @@ public: { const BoneTrafo *trafo; const Vector4 *unknown; - SliceArray weights; + Misc::SliceArray weights; }; const BoneTrafo *trafo; @@ -433,67 +434,70 @@ public: class NiKeyframeData : public Record { -public: - void read(NIFFile *nif) - { - // Rotations first - int count = nif->getInt(); - if(count) - { - int type = nif->getInt(); + public: - if(type == 1) - nif->skip(count*4*5); // time + quaternion - else if(type == 3) - nif->skip(count*4*8); // rot1 + tension+bias+continuity - else if(type == 4) - { - for(int j=0;jgetFloat(); // time - for(int i=0; i<3; i++) - { - int cnt = nif->getInt(); - int type = nif->getInt(); - if(type == 1) - nif->skip(cnt*4*2); // time + unknown - else if(type == 2) - nif->skip(cnt*4*4); // time + unknown vector - else nif->fail("Unknown sub-rotation type"); - } - } - } - else nif->fail("Unknown rotation type in NiKeyframeData"); - } + void read(NIFFile *nif) + { + // Rotations first + int count = nif->getInt(); + if(count) + { + int type = nif->getInt(); - // Then translation - count = nif->getInt(); - if(count) - { - int type = nif->getInt(); + if(type == 1) + nif->skip(count*4*5); // time + quaternion + else if(type == 3) + nif->skip(count*4*8); // rot1 + tension+bias+continuity + else if(type == 4) + { + for(int j=0;jgetFloat(); // time + for(int i=0; i<3; i++) + { + int cnt = nif->getInt(); + int type = nif->getInt(); + if(type == 1) + nif->skip(cnt*4*2); // time + unknown + else if(type == 2) + nif->skip(cnt*4*4); // time + unknown vector + else nif->fail("Unknown sub-rotation type"); + } + } + } + else nif->fail("Unknown rotation type in NiKeyframeData"); + } - if(type == 1) nif->getFloatLen(count*4); // time + translation - else if(type == 2) - nif->getFloatLen(count*10); // trans1 + forward + backward - else if(type == 3) - nif->getFloatLen(count*7); // trans1 + tension,bias,continuity - else nif->fail("Unknown translation type"); - } + // Then translation + count = nif->getInt(); - // Finally, scalings - count = nif->getInt(); - if(count) - { - int type = nif->getInt(); + if(count) + { + int type = nif->getInt(); - int size = 0; - if(type == 1) size = 2; // time+scale - else if(type == 2) size = 4; // 1 + forward + backward (floats) - else if(type == 3) size = 5; // 1 + tbc - else nif->fail("Unknown scaling type"); - nif->getFloatLen(count*size); - } - } + if(type == 1) + nif->getFloatLen(count*4); // time + translation + else if(type == 2) + nif->getFloatLen(count*10); // trans1 + forward + backward + else if(type == 3) + nif->getFloatLen(count*7); // trans1 + tension,bias,continuity + else nif->fail("Unknown translation type"); + } + + // Finally, scalings + count = nif->getInt(); + if(count) + { + int type = nif->getInt(); + + int size = 0; + if(type == 1) size = 2; // time+scale + else if(type == 2) size = 4; // 1 + forward + backward (floats) + else if(type == 3) size = 5; // 1 + tbc + else nif->fail("Unknown scaling type"); + nif->getFloatLen(count*size); + } + } }; } // Namespace diff --git a/components/nif/extra.hpp b/components/nif/extra.hpp index 25375c3258..eec1aa7b48 100644 --- a/components/nif/extra.hpp +++ b/components/nif/extra.hpp @@ -65,7 +65,7 @@ public: struct TextKey { float time; - SString text; + Misc::SString text; }; std::vector list; @@ -93,7 +93,7 @@ public: "MRK" - marker, only visible in the editor, not rendered in-game "NCO" - no collision */ - SString string; + Misc::SString string; void read(NIFFile *nif) { diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 3c7d7fb17c..361e5f6e9e 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -36,6 +36,7 @@ #include using namespace std; using namespace Nif; +using namespace Misc; /* This file implements functions from the NIFFile class. It is also where we stash all the functions we couldn't add as inline diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index ebf1fe4a45..951ae1f752 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -128,24 +128,24 @@ class NIFFile char getByte() { return getType(); } template - SliceArray getArrayLen(int num) - { return SliceArray((const X*)inp->getPtr(num*sizeof(X)),num); } + Misc::SliceArray getArrayLen(int num) + { return Misc::SliceArray((const X*)inp->getPtr(num*sizeof(X)),num); } template - SliceArray getArray() + Misc::SliceArray getArray() { int len = getInt(); return getArrayLen(len); } - SString getString() { return getArray(); } + Misc::SString getString() { return getArray(); } const Vector *getVector() { return getPtr(); } const Matrix *getMatrix() { return getPtr(); } const Transformation *getTrafo() { return getPtr(); } const Vector4 *getVector4() { return getPtr(); } - FloatArray getFloatLen(int num) + Misc::FloatArray getFloatLen(int num) { return getArrayLen(num); } // For fixed-size strings where you already know the size diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 8b8910118d..e94e3d0d34 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -24,6 +24,8 @@ #ifndef _NIF_RECORD_H_ #define _NIF_RECORD_H_ +#include + namespace Nif { @@ -88,7 +90,7 @@ struct Record { // Record type and type name int recType; - SString recName; + Misc::SString recName; Record() : recType(RC_MISSING) {} diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index 1ff78023d9..94a3a5aac8 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -49,7 +49,7 @@ using namespace Ogre; using namespace Nif; using namespace Mangle::VFS; - +using namespace NifBullet; //==================================================================================================== Ogre::Matrix3 ManualBulletShapeLoader::getMatrix(Nif::Transformation* tr) diff --git a/components/nifbullet/bullet_nif_loader.hpp b/components/nifbullet/bullet_nif_loader.hpp index d960c6629c..360d9850bf 100644 --- a/components/nifbullet/bullet_nif_loader.hpp +++ b/components/nifbullet/bullet_nif_loader.hpp @@ -58,6 +58,8 @@ namespace Mangle } } +namespace NifBullet +{ /** *Load bulletShape from NIF files. @@ -126,4 +128,6 @@ private: btCompoundShape* currentShape;//the shape curently under construction }; +} + #endif diff --git a/components/nifbullet/test/test.cpp b/components/nifbullet/test/test.cpp index b91c486666..261edf512a 100644 --- a/components/nifbullet/test/test.cpp +++ b/components/nifbullet/test/test.cpp @@ -90,11 +90,11 @@ int main() //Ressources stuff - addBSA("Morrowind.bsa"); + Bsa::addBSA("Morrowind.bsa"); //Ogre::ResourceGroupManager::getSingleton().createResourceGroup("general"); Ogre::ResourcePtr ptr = BulletShapeManager::getSingleton().getByName(mesh,"General"); - ManualBulletShapeLoader* ShapeLoader = new ManualBulletShapeLoader(); + NifBullet::ManualBulletShapeLoader* ShapeLoader = new NifBullet::ManualBulletShapeLoader(); ShapeLoader->load(mesh,"General"); //BulletShapeManager::getSingleton().unload(mesh); diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index a33fde55ad..668fdcceb4 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -50,6 +50,8 @@ using namespace std; using namespace Ogre; using namespace Nif; using namespace Mangle::VFS; +using namespace Misc; +using namespace NifOgre; NIFLoader& NIFLoader::getSingleton() { @@ -81,11 +83,11 @@ Vector3 NIFLoader::convertVector3(const Nif::Vector& vec) Quaternion NIFLoader::convertRotation(const Nif::Matrix& rot) { Real matrix[3][3]; - + for (int i=0; i<3; i++) for (int j=0; j<3; j++) matrix[i][j] = rot.v[i].array[j]; - + return Quaternion(Matrix3(matrix)); } @@ -265,7 +267,7 @@ void NIFLoader::createMaterial(const String &name, } else pass->setDepthWriteEnabled(true); */ - + // Add transparency if NiAlphaProperty was present if (alphaFlags != -1) @@ -346,16 +348,16 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std sub->vertexData = new VertexData(); sub->vertexData->vertexCount = numVerts; sub->useSharedVertices = false; - + VertexDeclaration *decl = sub->vertexData->vertexDeclaration; decl->addElement(nextBuf, 0, VET_FLOAT3, VES_POSITION); - + HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( VertexElement::getTypeSize(VET_FLOAT3), numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY); vbuf->writeData(0, vbuf->getSizeInBytes(), data->vertices.ptr, true); - + VertexBufferBinding* bind = sub->vertexData->vertexBufferBinding; bind->setBinding(nextBuf++, vbuf); @@ -581,8 +583,27 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou { // Use NiMaterialProperty data to create the data const S_MaterialProperty *d = m->data; - createMaterial(material, d->ambient, d->diffuse, d->specular, d->emissive, - d->glossiness, d->alpha, alphaFlags, alphaTest, texName); + + std::multimap::iterator itr = MaterialMap.find(texName); + std::multimap::iterator lastElement; + lastElement = MaterialMap.upper_bound(texName); + if (itr != MaterialMap.end()) + { + for ( ; itr != lastElement; ++itr) + { + //std::cout << "OK!"; + //MaterialPtr mat = MaterialManager::getSingleton().getByName(itr->second,recourceGroup); + material = itr->second; + //if( mat->getA + } + } + else + { + //std::cout << "new"; + createMaterial(material, d->ambient, d->diffuse, d->specular, d->emissive, + d->glossiness, d->alpha, alphaFlags, alphaTest, texName); + MaterialMap.insert(std::make_pair(texName,material)); + } } else { @@ -673,7 +694,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou if (verIndex < data->normals.length) { Vector3 absNormalsPos = vecRot * Vector3(ptrNormals + verIndex *3); - + for (int j=0; j<3; j++) (ptrNormals + verIndex*3)[j] = absNormalsPos[j]; } @@ -833,7 +854,7 @@ void NIFLoader::handleNode(Nif::Node *node, int flags, } for (; itrafo, bounds, bone); } @@ -848,7 +869,7 @@ void NIFLoader::handleNode(Nif::Node *node, int flags, Tri Chest */ if((isChest && stack < 10 ) || (isHands && counter < 3) || !(isChest || isHands)){ //(isBeast && isChest && stack < 10 && counter == skincounter ) - + std::string name = node->name.toString(); //if (isChest) //std::cout << "NAME: " << name << "\n"; @@ -875,15 +896,15 @@ void NIFLoader::handleNode(Nif::Node *node, int flags, //if(isBeast && isChest) //cout << "Handling Shape, Stack " << stack <<"\n"; - - + + counter++; } /*if(isHands){ //cout << "Handling Shape, Stack " << stack <<"\n"; counter++; }*/ - + } stack--; @@ -904,7 +925,7 @@ void NIFLoader::loadResource(Resource *resource) //std::cout <<"NAME:" << name; //if(name.length() >= 20) // {std::string split = name.substr(name.length() - 20, 20); - //if(name == + //if(name == //std::cout <<"NAME:" << name << "LEN: " << name.length() << "\n"; const std::string test ="meshes\\b\\B_N_Dark Elf_M_Skins.NIF"; const std::string test2 ="meshes\\b\\B_N_Dark Elf_M_Skins.nif"; @@ -932,15 +953,15 @@ void NIFLoader::loadResource(Resource *resource) const std::string test24 ="meshes\\b\\B_N_High Elf_M_Skins.nif"; //std::cout <<"LEN1:" << test.length() << "TEST: " << test << "\n"; - - + + if(name.compare(test) == 0 || name.compare(test2) == 0 || name.compare(test3) == 0 || name.compare(test4) == 0 || name.compare(test5) == 0 || name.compare(test6) == 0 || name.compare(test7) == 0 || name.compare(test8) == 0 || name.compare(test9) == 0 || name.compare(test10) == 0 || name.compare(test11) == 0 || name.compare(test12) == 0 || name.compare(test13) == 0 || name.compare(test14) == 0 || name.compare(test15) == 0 || name.compare(test16) == 0 || name.compare(test17) == 0 || name.compare(test18) == 0 || name.compare(test19) == 0 || name.compare(test20) == 0 || name.compare(test21) == 0 || name.compare(test22) == 0 || name.compare(test23) == 0 || name.compare(test24) == 0 - + ){ //std::cout << "Welcome Chest\n"; isChest = true; @@ -1009,7 +1030,7 @@ void NIFLoader::loadResource(Resource *resource) std::cout << "\n\n\nWelcome FRedguard Chest\n\n\n"; isChest = true; }*/ - + //if(split== "Skins.NIF") // std::cout << "\nSPECIAL PROPS\n"; resourceName = ""; @@ -1081,14 +1102,14 @@ void NIFLoader::loadResource(Resource *resource) // mesh->setSkeletonName(getSkeletonName()); } -MeshPtr NIFLoader::load(const std::string &name, +MeshPtr NIFLoader::load(const std::string &name, const std::string &group) { MeshManager *m = MeshManager::getSingletonPtr(); // Check if the resource already exists ResourcePtr ptr = m->getByName(name, group); MeshPtr resize; - + const std::string beast1 ="meshes\\b\\B_N_Khajiit_F_Skins.nif"; const std::string beast2 ="meshes\\b\\B_N_Khajiit_M_Skins.nif"; const std::string beast3 ="meshes\\b\\B_N_Argonian_F_Skins.nif"; @@ -1099,8 +1120,8 @@ MeshPtr NIFLoader::load(const std::string &name, const std::string beasttail3 ="tail\\b\\B_N_Argonian_F_Skins.nif"; const std::string beasttail4 ="tail\\b\\B_N_Argonian_M_Skins.nif"; - if (!ptr.isNull()){ - + if (!ptr.isNull()){ + //if(pieces > 1) //cout << "It exists\n"; resize = MeshPtr(ptr); @@ -1111,17 +1132,17 @@ MeshPtr NIFLoader::load(const std::string &name, { resize = MeshManager::getSingleton().createManual(name, group, NIFLoader::getSingletonPtr()); //cout <<"EXISTING" << name << "\n"; - + //if(pieces > 1) //cout << "Creating it\n"; - - + + //resize->load(); //resize->reload(); //return 0; ResourcePtr ptr = m->getByName(name, group); resize = MeshPtr(ptr); - + //NIFLoader::getSingletonPtr()-> /*ResourcePtr ptr = m->getByName(name, group); if (!ptr.isNull()){ @@ -1132,7 +1153,7 @@ MeshPtr NIFLoader::load(const std::string &name, } return resize; } - + /* More code currently not in use, from the old D source. This was used in the first attempt at loading NIF meshes, where each submesh diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 6ab22aa2fa..bf8a94266f 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -49,6 +49,9 @@ namespace Mangle } } +namespace NifOgre +{ + /** Manual resource loader for NIF meshes. This is the main class responsible for translating the internal NIF mesh structure into something Ogre can use. Later it will also handle the insertion of @@ -73,11 +76,11 @@ class NIFLoader : Ogre::ManualResourceLoader virtual void loadResource(Ogre::Resource *resource); - static Ogre::MeshPtr load(const std::string &name, + static Ogre::MeshPtr load(const std::string &name, const std::string &group="General"); - + Ogre::Vector3 convertVector3(const Nif::Vector& vec); Ogre::Quaternion convertRotation(const Nif::Matrix& rot); @@ -113,7 +116,7 @@ class NIFLoader : Ogre::ManualResourceLoader { return resourceName + ".skel"; } - + // This is the interface to the Ogre resource system. It allows us to // load NIFs from BSAs, in the file system and in any other place we // tell Ogre to look (eg. in zip or rar files.) It's also used to @@ -131,11 +134,13 @@ class NIFLoader : Ogre::ManualResourceLoader int counter; int numbers; int stack; - + std::multimap MaterialMap; // pointer to the ogre mesh which is currently build Ogre::Mesh *mesh; Ogre::SkeletonPtr mSkel; }; +} + #endif diff --git a/components/nifogre/tests/ogre_nif_test.cpp b/components/nifogre/tests/ogre_nif_test.cpp index 89f99e8ff6..decd43df57 100644 --- a/components/nifogre/tests/ogre_nif_test.cpp +++ b/components/nifogre/tests/ogre_nif_test.cpp @@ -13,11 +13,11 @@ const char* mesh = "meshes\\f\\ex_ashl_a_banner_r.nif"; void C::doTest() { // Add Morrowind.bsa resource location - addBSA("../../data/Morrowind.bsa"); + Bsa::addBSA("../../data/Morrowind.bsa"); // Insert the mesh - NIFLoader::load(mesh); - NIFLoader::load(mesh); + NifOgre::NIFLoader::load(mesh); + NifOgre::NIFLoader::load(mesh); /* SceneNode *node = mgr->getRootSceneNode()->createChildSceneNode("node"); diff --git a/components/to_utf8/Makefile b/components/to_utf8/Makefile index a84cc240e3..5234d455ae 100644 --- a/components/to_utf8/Makefile +++ b/components/to_utf8/Makefile @@ -1,5 +1,8 @@ tables_gen.hpp: gen_iconv - gen_iconv > tables_gen.hpp + ./gen_iconv > tables_gen.hpp gen_iconv: gen_iconv.cpp g++ -Wall $^ -o $@ + +clean: + rm -f ./gen_iconv \ No newline at end of file diff --git a/components/to_utf8/gen_iconv.cpp b/components/to_utf8/gen_iconv.cpp index 42e997783c..6202052455 100644 --- a/components/to_utf8/gen_iconv.cpp +++ b/components/to_utf8/gen_iconv.cpp @@ -74,13 +74,44 @@ int write_table(const std::string &charset, const std::string &tableName) // Finish table cout << "};\n"; + + return 0; } int main() { cout << hex; + // Write header guard + cout << "#ifndef COMPONENTS_TOUTF8_TABLE_GEN_H\n#define COMPONENTS_TOUTF8_TABLE_GEN_H\n\n"; + + // Write namespace + cout << "namespace ToUTF8\n{\n\n"; + + // Central European and Eastern European languages that use Latin script, such as + // Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian. + cout << "\n/// Central European and Eastern European languages that use Latin script," + "\n/// such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian," + "\n/// Serbian (Latin script), Romanian and Albanian." + "\n"; + write_table("WINDOWS-1250", "windows_1250"); + + // Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages + cout << "\n/// Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic" + "\n/// and other languages" + "\n"; + write_table("WINDOWS-1251", "windows_1251"); + // English + cout << "\n/// Latin alphabet used by English and some other Western languages" + "\n"; write_table("WINDOWS-1252", "windows_1252"); + + // Close namespace + cout << "\n}\n\n"; + + // Close header guard + cout << "#endif\n\n"; + return 0; } diff --git a/components/to_utf8/tables_gen.hpp b/components/to_utf8/tables_gen.hpp index 55a06cd94c..79945bddca 100644 --- a/components/to_utf8/tables_gen.hpp +++ b/components/to_utf8/tables_gen.hpp @@ -1,3 +1,536 @@ +#ifndef COMPONENTS_TOUTF8_TABLE_GEN_H +#define COMPONENTS_TOUTF8_TABLE_GEN_H + +namespace ToUTF8 +{ + + +/// Central European and Eastern European languages that use Latin script, +/// such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, +/// Serbian (Latin script), Romanian and Albanian. +static char windows_1250[] = +{ + 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x2, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x3, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x4, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x5, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x6, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x7, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x8, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x9, 0x0, 0x0, 0x0, 0x0, + 0x1, 0xa, 0x0, 0x0, 0x0, 0x0, + 0x1, 0xb, 0x0, 0x0, 0x0, 0x0, + 0x1, 0xc, 0x0, 0x0, 0x0, 0x0, + 0x1, 0xd, 0x0, 0x0, 0x0, 0x0, + 0x1, 0xe, 0x0, 0x0, 0x0, 0x0, + 0x1, 0xf, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x10, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x11, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x12, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x13, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x14, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x15, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x16, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x17, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x18, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x19, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x1a, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x1b, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x1c, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x1d, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x1e, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x1f, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x20, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x21, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x22, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x23, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x24, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x25, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x26, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x27, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x28, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x29, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x2a, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x2b, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x2c, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x2d, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x2e, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x2f, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x30, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x31, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x32, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x33, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x34, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x35, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x36, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x37, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x38, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x39, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x3a, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x3b, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x3c, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x3d, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x3e, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x3f, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x41, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x42, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x43, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x44, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x45, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x46, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x47, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x48, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x49, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x4a, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x4b, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x4c, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x4d, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x4e, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x4f, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x50, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x51, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x52, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x53, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x54, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x55, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x56, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x57, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x58, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x59, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x5a, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x5b, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x5c, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x5d, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x5e, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x5f, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x60, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x61, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x62, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x63, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x64, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x65, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x66, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x67, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x68, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x69, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x6a, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x6b, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x6c, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x6d, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x6e, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x6f, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x70, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x71, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x72, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x73, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x74, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x75, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x76, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x77, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x78, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x79, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x7a, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x7b, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x7c, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x7d, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x7e, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x7f, 0x0, 0x0, 0x0, 0x0, + 0x3, 0xe2, 0x82, 0xac, 0x0, 0x0, + 0x1, 0x20, 0x0, 0x0, 0x0, 0x0, // not part of this charset + 0x3, 0xe2, 0x80, 0x9a, 0x0, 0x0, + 0x1, 0x20, 0x0, 0x0, 0x0, 0x0, // not part of this charset + 0x3, 0xe2, 0x80, 0x9e, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0xa6, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0xa0, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0xa1, 0x0, 0x0, + 0x1, 0x20, 0x0, 0x0, 0x0, 0x0, // not part of this charset + 0x3, 0xe2, 0x80, 0xb0, 0x0, 0x0, + 0x2, 0xc5, 0xa0, 0x0, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0xb9, 0x0, 0x0, + 0x2, 0xc5, 0x9a, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0xa4, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0xbd, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0xb9, 0x0, 0x0, 0x0, + 0x1, 0x20, 0x0, 0x0, 0x0, 0x0, // not part of this charset + 0x3, 0xe2, 0x80, 0x98, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0x99, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0x9c, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0x9d, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0xa2, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0x93, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0x94, 0x0, 0x0, + 0x1, 0x20, 0x0, 0x0, 0x0, 0x0, // not part of this charset + 0x3, 0xe2, 0x84, 0xa2, 0x0, 0x0, + 0x2, 0xc5, 0xa1, 0x0, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0xba, 0x0, 0x0, + 0x2, 0xc5, 0x9b, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0xa5, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0xbe, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0xba, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xa0, 0x0, 0x0, 0x0, + 0x2, 0xcb, 0x87, 0x0, 0x0, 0x0, + 0x2, 0xcb, 0x98, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0x81, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xa4, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0x84, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xa6, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xa7, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xa8, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xa9, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0x9e, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xab, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xac, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xad, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xae, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0xbb, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xb0, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xb1, 0x0, 0x0, 0x0, + 0x2, 0xcb, 0x9b, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0x82, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xb4, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xb5, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xb6, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xb7, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xb8, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0x85, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0x9f, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xbb, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0xbd, 0x0, 0x0, 0x0, + 0x2, 0xcb, 0x9d, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0xbe, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0xbc, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0x94, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0x81, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0x82, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0x82, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0x84, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0xb9, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0x86, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0x87, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0x8c, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0x89, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0x98, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0x8b, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0x9a, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0x8d, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0x8e, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0x8e, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0x90, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0x83, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0x87, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0x93, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0x94, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0x90, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0x96, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0x97, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0x98, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0xae, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0x9a, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0xb0, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0x9c, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0x9d, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0xa2, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0x9f, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0x95, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0xa1, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0xa2, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0x83, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0xa4, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0xba, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0x87, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0xa7, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0x8d, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0xa9, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0x99, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0xab, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0x9b, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0xad, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0xae, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0x8f, 0x0, 0x0, 0x0, + 0x2, 0xc4, 0x91, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0x84, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0x88, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0xb3, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0xb4, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0x91, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0xb6, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0xb7, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0x99, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0xaf, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0xba, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0xb1, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0xbc, 0x0, 0x0, 0x0, + 0x2, 0xc3, 0xbd, 0x0, 0x0, 0x0, + 0x2, 0xc5, 0xa3, 0x0, 0x0, 0x0, + 0x2, 0xcb, 0x99, 0x0, 0x0, 0x0 +}; + +/// Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic +/// and other languages +static char windows_1251[] = +{ + 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x2, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x3, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x4, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x5, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x6, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x7, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x8, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x9, 0x0, 0x0, 0x0, 0x0, + 0x1, 0xa, 0x0, 0x0, 0x0, 0x0, + 0x1, 0xb, 0x0, 0x0, 0x0, 0x0, + 0x1, 0xc, 0x0, 0x0, 0x0, 0x0, + 0x1, 0xd, 0x0, 0x0, 0x0, 0x0, + 0x1, 0xe, 0x0, 0x0, 0x0, 0x0, + 0x1, 0xf, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x10, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x11, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x12, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x13, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x14, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x15, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x16, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x17, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x18, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x19, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x1a, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x1b, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x1c, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x1d, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x1e, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x1f, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x20, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x21, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x22, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x23, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x24, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x25, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x26, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x27, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x28, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x29, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x2a, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x2b, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x2c, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x2d, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x2e, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x2f, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x30, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x31, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x32, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x33, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x34, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x35, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x36, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x37, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x38, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x39, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x3a, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x3b, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x3c, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x3d, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x3e, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x3f, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x41, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x42, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x43, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x44, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x45, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x46, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x47, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x48, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x49, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x4a, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x4b, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x4c, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x4d, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x4e, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x4f, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x50, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x51, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x52, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x53, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x54, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x55, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x56, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x57, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x58, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x59, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x5a, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x5b, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x5c, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x5d, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x5e, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x5f, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x60, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x61, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x62, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x63, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x64, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x65, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x66, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x67, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x68, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x69, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x6a, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x6b, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x6c, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x6d, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x6e, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x6f, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x70, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x71, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x72, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x73, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x74, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x75, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x76, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x77, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x78, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x79, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x7a, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x7b, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x7c, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x7d, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x7e, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x7f, 0x0, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x82, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x83, 0x0, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0x9a, 0x0, 0x0, + 0x2, 0xd1, 0x93, 0x0, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0x9e, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0xa6, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0xa0, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0xa1, 0x0, 0x0, + 0x3, 0xe2, 0x82, 0xac, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0xb0, 0x0, 0x0, + 0x2, 0xd0, 0x89, 0x0, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0xb9, 0x0, 0x0, + 0x2, 0xd0, 0x8a, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x8c, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x8b, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x8f, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x92, 0x0, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0x98, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0x99, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0x9c, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0x9d, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0xa2, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0x93, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0x94, 0x0, 0x0, + 0x1, 0x20, 0x0, 0x0, 0x0, 0x0, // not part of this charset + 0x3, 0xe2, 0x84, 0xa2, 0x0, 0x0, + 0x2, 0xd1, 0x99, 0x0, 0x0, 0x0, + 0x3, 0xe2, 0x80, 0xba, 0x0, 0x0, + 0x2, 0xd1, 0x9a, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x9c, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x9b, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x9f, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xa0, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x8e, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x9e, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x88, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xa4, 0x0, 0x0, 0x0, + 0x2, 0xd2, 0x90, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xa6, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xa7, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x81, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xa9, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x84, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xab, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xac, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xad, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xae, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x87, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xb0, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xb1, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x86, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x96, 0x0, 0x0, 0x0, + 0x2, 0xd2, 0x91, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xb5, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xb6, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xb7, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x91, 0x0, 0x0, 0x0, + 0x3, 0xe2, 0x84, 0x96, 0x0, 0x0, + 0x2, 0xd1, 0x94, 0x0, 0x0, 0x0, + 0x2, 0xc2, 0xbb, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x98, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x85, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x95, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x97, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x90, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x91, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x92, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x93, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x94, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x95, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x96, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x97, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x98, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x99, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x9a, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x9b, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x9c, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x9d, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x9e, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0x9f, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xa0, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xa1, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xa2, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xa3, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xa4, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xa5, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xa6, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xa7, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xa8, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xa9, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xaa, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xab, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xac, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xad, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xae, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xaf, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xb0, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xb1, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xb2, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xb3, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xb4, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xb5, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xb6, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xb7, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xb8, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xb9, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xba, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xbb, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xbc, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xbd, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xbe, 0x0, 0x0, 0x0, + 0x2, 0xd0, 0xbf, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x80, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x81, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x82, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x83, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x84, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x85, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x86, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x87, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x88, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x89, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x8a, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x8b, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x8c, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x8d, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x8e, 0x0, 0x0, 0x0, + 0x2, 0xd1, 0x8f, 0x0, 0x0, 0x0 +}; + +/// Latin alphabet used by English and some other Western languages static char windows_1252[] = { 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, @@ -257,3 +790,8 @@ static char windows_1252[] = 0x2, 0xc3, 0xbe, 0x0, 0x0, 0x0, 0x2, 0xc3, 0xbf, 0x0, 0x0, 0x0 }; + +} + +#endif + diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index 6f69331439..3fbbeb733c 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -130,10 +130,28 @@ std::string ToUTF8::getUtf8(ToUTF8::FromType from) { // Pick translation array const char *arr; - if(from == ToUTF8::WINDOWS_1252) - arr = windows_1252; - else - assert(0); + switch (from) + { + case ToUTF8::WINDOWS_1252: + { + arr = ToUTF8::windows_1252; + break; + } + case ToUTF8::WINDOWS_1250: + { + arr = ToUTF8::windows_1250; + break; + } + case ToUTF8::WINDOWS_1251: + { + arr = ToUTF8::windows_1251; + break; + } + default: + { + assert(0); + } + } // Double check that the input string stops at some point (it might // contain zero terminators before this, inside its own data, which diff --git a/components/to_utf8/to_utf8.hpp b/components/to_utf8/to_utf8.hpp index ec2231be77..4cbee10196 100644 --- a/components/to_utf8/to_utf8.hpp +++ b/components/to_utf8/to_utf8.hpp @@ -8,8 +8,10 @@ namespace ToUTF8 // These are all the currently supported code pages enum FromType { - WINDOWS_1252 // Used by English version of Morrowind (and - // probably others) + WINDOWS_1250, // Central ane Eastern European languages + WINDOWS_1251, // Cyrillic languages + WINDOWS_1252 // Used by English version of Morrowind (and + // probably others) }; // Return a writable buffer of at least 'size' bytes. The buffer diff --git a/extern/caelum/CMakeLists.txt b/extern/caelum/CMakeLists.txt index 88ed51fad4..b747fefc8e 100755 --- a/extern/caelum/CMakeLists.txt +++ b/extern/caelum/CMakeLists.txt @@ -19,11 +19,7 @@ add_library(caelum STATIC ${SOURCES}) # # Resources # -if (APPLE) - SET(CAELUM_RES_DEST "${APP_BUNDLE_DIR}/Contents/Resources") -else (APPLE) - SET(CAELUM_RES_DEST "${OpenMW_BINARY_DIR}") -endif (APPLE) +SET(CAELUM_RES_DEST "${OpenMW_BINARY_DIR}") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/resources/AtmosphereDepth.png "${CAELUM_RES_DEST}/resources/caelum/AtmosphereDepth.png" COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/resources/CaelumGroundFog.cg "${CAELUM_RES_DEST}/resources/caelum/CaelumGroundFog.cg" COPYONLY) diff --git a/extern/mygui_3.0.1/CMakeLists.txt b/extern/mygui_3.0.1/CMakeLists.txt index 8101ad91a7..6e54d1adc6 100644 --- a/extern/mygui_3.0.1/CMakeLists.txt +++ b/extern/mygui_3.0.1/CMakeLists.txt @@ -22,11 +22,7 @@ add_subdirectory(OgrePlatform) # Copy resource files into the build directory set(SDIR ${CMAKE_CURRENT_SOURCE_DIR}/openmw_resources) -if (APPLE) - set(DDIR ${APP_BUNDLE_DIR}/Contents/Resources/resources/mygui) -else (APPLE) - set(DDIR ${OpenMW_BINARY_DIR}/resources/mygui) -endif (APPLE) +set(DDIR ${OpenMW_BINARY_DIR}/resources/mygui) configure_file("${SDIR}/bigbars.png" "${DDIR}/bigbars.png" COPYONLY) configure_file("${SDIR}/black.png" "${DDIR}/black.png" COPYONLY) @@ -68,6 +64,8 @@ configure_file("${SDIR}/openmw_progress.skin.xml" "${DDIR}/openmw_progress.skin. configure_file("${SDIR}/openmw_stats_window_layout.xml" "${DDIR}/openmw_stats_window_layout.xml" COPYONLY) configure_file("${SDIR}/openmw_text.skin.xml" "${DDIR}/openmw_text.skin.xml" COPYONLY) configure_file("${SDIR}/openmw_windows.skin.xml" "${DDIR}/openmw_windows.skin.xml" COPYONLY) +configure_file("${SDIR}/openmw_messagebox_layout.xml" "${DDIR}/openmw_messagebox_layout.xml" COPYONLY) +configure_file("${SDIR}/openmw_interactive_messagebox_layout.xml" "${DDIR}/openmw_interactive_messagebox_layout.xml" COPYONLY) configure_file("${SDIR}/smallbars.png" "${DDIR}/smallbars.png" COPYONLY) configure_file("${SDIR}/transparent.png" "${DDIR}/transparent.png" COPYONLY) configure_file("${SDIR}/VeraMono.ttf" "${DDIR}/VeraMono.ttf" COPYONLY) diff --git a/extern/mygui_3.0.1/openmw_resources/openmw.font.xml b/extern/mygui_3.0.1/openmw_resources/openmw.font.xml index 5e283c5492..76f164bcb6 100644 --- a/extern/mygui_3.0.1/openmw_resources/openmw.font.xml +++ b/extern/mygui_3.0.1/openmw_resources/openmw.font.xml @@ -2,8 +2,9 @@ - + + @@ -11,15 +12,16 @@ - - + + - + + diff --git a/extern/mygui_3.0.1/openmw_resources/openmw_interactive_messagebox_layout.xml b/extern/mygui_3.0.1/openmw_resources/openmw_interactive_messagebox_layout.xml new file mode 100644 index 0000000000..40c0787b88 --- /dev/null +++ b/extern/mygui_3.0.1/openmw_resources/openmw_interactive_messagebox_layout.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/extern/mygui_3.0.1/openmw_resources/openmw_messagebox_layout.xml b/extern/mygui_3.0.1/openmw_resources/openmw_messagebox_layout.xml new file mode 100644 index 0000000000..890752db3d --- /dev/null +++ b/extern/mygui_3.0.1/openmw_resources/openmw_messagebox_layout.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/files/launcher.cfg b/files/launcher.cfg new file mode 100644 index 0000000000..bc0e2b7fb8 --- /dev/null +++ b/files/launcher.cfg @@ -0,0 +1,5 @@ +[Profiles] +CurrentProfile=Default +Default\Master0=Morrowind.esm +Default\Master1=Tribunal.esm +Default\Master2=Bloodmoon.esm diff --git a/files/launcher.qss b/files/launcher.qss new file mode 100644 index 0000000000..dad87022cb --- /dev/null +++ b/files/launcher.qss @@ -0,0 +1,120 @@ +#PlayGroup { + background-image: url(":/images/playpage-background.png"); + background-repeat: no-repeat; + background-position: top; + padding-left: 30px; + padding-right: 30px; +} + +#MastersWidget { + selection-background-color: palette(highlight); +} + +#PlayButton { + height: 50px; + margin-bottom: 30px; + + background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, + stop:0 rgba(255, 255, 255, 200), + stop:0.1 rgba(255, 255, 255, 15), + stop:0.49 rgba(255, 255, 255, 75), + stop:0.5 rgba(0, 0, 0, 0), + stop:0.9 rgba(0, 0, 0, 55), + stop:1 rgba(0, 0, 0, 100)); + + font: 24pt "Trebuchet MS"; + color: black; + + border-right: 1px solid rgba(0, 0, 0, 155); + border-left: 1px solid rgba(0, 0, 0, 55); + border-top: 1px solid rgba(0, 0, 0, 55); + border-bottom: 1px solid rgba(0, 0, 0, 155); + + border-radius: 5px; +} + +#PlayButton:hover { + border-bottom: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0)); + border-top: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0)); + border-right: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0)); + border-left: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0)); + border-width: 2px; + border-style: solid; +} + +#PlayButton:pressed { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 rgba(0, 0, 0, 75), + stop:0.1 rgba(0, 0, 0, 15), + stop:0.2 rgba(255, 255, 255, 55) + stop:0.95 rgba(255, 255, 255, 55), + stop:1 rgba(255, 255, 255, 155)); + + border: 1px solid rgba(0, 0, 0, 55); +} + +#ProfileLabel { + font: 14pt "Trebuchet MS"; +} + +#ProfilesComboBox { + padding: 1px 18px 1px 3px; + + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, stop:0.2 rgba(0, 0, 0, 25), stop:1 white); + border-width: 1px; + border-color: rgba(0, 0, 0, 125); + border-style: solid; + border-radius: 2px; +} + +/*QComboBox gets the "on" state when the popup is open */ +#ProfilesComboBox:!editable:on, #ProfilesComboBox::drop-down:editable:on { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 rgba(0, 0, 0, 75), + stop:0.1 rgba(0, 0, 0, 15), + stop:0.2 rgba(255, 255, 255, 55)); + + border: 1px solid rgba(0, 0, 0, 55); +} + + +#ProfilesComboBox { /* shift the text when the popup opens */ + padding-top: 3px; + padding-left: 4px; + + font: 11pt "Trebuchet MS"; +} + +#ProfilesComboBox::drop-down { + subcontrol-origin: padding; + subcontrol-position: top right; + + border-width: 1px; + border-left-width: 1px; + border-left-color: darkgray; + border-left-style: solid; /* just a single line */ + border-top-right-radius: 3px; /* same radius as the QComboBox */ + border-bottom-right-radius: 3px; +} + +#ProfilesComboBox::down-arrow { + image: url(":/images/down.png"); +} + +#ProfilesComboBox::down-arrow:on { /* shift the arrow when popup is open */ + top: 1px; + left: 1px; +} + +#ProfilesComboBox QAbstractItemView { + border: 2px solid lightgray; + border-radius: 5px; +} + +#IconWidget { + background-image: url(":/images/openmw-header.png"); + background-color: white; + background-repeat: no-repeat; + background-attachment: scroll; + background-position: right; +} diff --git a/files/mac/Info.plist b/files/mac/Info.plist index 6b350d2501..1872425e7c 100644 --- a/files/mac/Info.plist +++ b/files/mac/Info.plist @@ -7,7 +7,7 @@ CFBundleDevelopmentRegion English CFBundleExecutable - openmw + omwlauncher CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString diff --git a/files/openmw.desktop b/files/openmw.desktop new file mode 100644 index 0000000000..8643d4b138 --- /dev/null +++ b/files/openmw.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Version=0.11 +Type=Application +Name=OpenMW Launcher +GenericName=Role Playing Game +Comment=An engine replacement for The Elder Scrolls III: Morrowind +TryExec=omwlauncher +Exec=omwlauncher +Icon=openmw +Categories=Game;RolePlaying; diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000000..66e7c12ec3 --- /dev/null +++ b/readme.txt @@ -0,0 +1,51 @@ +OpenMW: A reimplementation of The Elder Scrolls III: Morrowind + +OpenMW is an attempt at recreating the engine for the popular role-playing game +Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. + +Version: 0.11 +License: GPL (see GPL3.txt for more information) +Website: www.openmw.com + + +THIS IS A WORK IN PROGRESS + +INSTALLATION + +Windows: +TODO add description for Windows + +Linux: +Ubuntu +TODO add description for Ubuntu + +Arch Linux +There's an OpenMW package available in the AUR Repository: +http://aur.archlinux.org/packages.php?ID=21419 + +OS X: +TODO add description for OS X + +BUILD FROM SOURCE + +TODO add description here + +COMMAND LINE OPTIONS +TODO add description of command line options + +CREDITS + +Developers: +TODO add list of developers + +OpenMW: +Thanks to DokterDume for kindly providing us with the Moon and Star logo +used as the application icon and project logo. + +Launcher: +Thanks to Kevin Ryan for kindly providing us with the icon used for the Data Files tab. + + +CHANGELOG + +TODO add changelog (take pre 0.11.0 changelog from wiki when it is up again; take 0.11.0 and later changelog from tracker)