diff --git a/.travis.yml b/.travis.yml index 5c69f49f0..caf2e3389 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ before_install: - sudo apt-get install -qq libqt4-dev libxaw7-dev libxrandr-dev libfreeimage-dev libpng-dev - sudo apt-get install -qq libopenal-dev libmpg123-dev libsndfile1-dev - sudo apt-get install -qq libavcodec-dev libavformat-dev libavdevice-dev libavutil-dev libswscale-dev libpostproc-dev - - sudo apt-get install -qq libbullet-dev libogre-static-dev libmygui-static-dev libsdl2-static-dev + - sudo apt-get install -qq libbullet-dev libogre-static-dev libmygui-static-dev libsdl2-static-dev libunshield-dev - sudo mkdir /usr/src/gtest/build - cd /usr/src/gtest/build - sudo cmake .. -DBUILD_SHARED_LIBS=1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e5e5fcc1..4d6daf9f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -520,6 +520,13 @@ if (BUILD_ESMTOOL) endif() if (BUILD_LAUNCHER) + if(NOT WIN32) + find_package(LIBUNSHIELD REQUIRED) + if(NOT LIBUNSHIELD_FOUND) + message(SEND_ERROR "Failed to find libunshield") + endif(NOT LIBUNSHIELD_FOUND) + endif(NOT WIN32) + add_subdirectory( apps/launcher ) endif() diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index bff26b63c..5908deb90 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -4,6 +4,7 @@ set(LAUNCHER main.cpp maindialog.cpp playpage.cpp + textslotmsgbox.cpp settings/gamesettings.cpp settings/graphicssettings.cpp @@ -14,12 +15,17 @@ set(LAUNCHER ${CMAKE_SOURCE_DIR}/files/launcher/launcher.rc ) +if(NOT WIN32) + LIST(APPEND LAUNCHER unshieldthread.cpp) +endif(NOT WIN32) set(LAUNCHER_HEADER datafilespage.hpp graphicspage.hpp maindialog.hpp playpage.hpp + unshieldthread.hpp + textslotmsgbox.hpp settings/gamesettings.hpp settings/graphicssettings.hpp @@ -30,6 +36,10 @@ set(LAUNCHER_HEADER utils/textinputdialog.hpp ) +if(NOT WIN32) + LIST(APPEND LAUNCHER_HEADER unshieldthread.hpp) +endif(NOT WIN32) + # Headers that must be pre-processed set(LAUNCHER_HEADER_MOC @@ -37,11 +47,18 @@ set(LAUNCHER_HEADER_MOC graphicspage.hpp maindialog.hpp playpage.hpp + unshieldthread.hpp + textslotmsgbox.hpp utils/checkablemessagebox.hpp utils/textinputdialog.hpp ) +if(NOT WIN32) + LIST(APPEND LAUNCHER_HEADER_MOC unshieldthread.hpp) +endif(NOT WIN32) + + set(LAUNCHER_UI ${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui ${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui @@ -64,8 +81,12 @@ QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc) QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) QT4_WRAP_UI(UI_HDRS ${LAUNCHER_UI}) + include(${QT_USE_FILE}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) +if(NOT WIN32) + include_directories(${LIBUNSHIELD_INCLUDE}) +endif(NOT WIN32) # Main executable IF(OGRE_STATIC) @@ -94,6 +115,13 @@ target_link_libraries(omwlauncher ${QT_LIBRARIES} components ) +if(NOT WIN32) + target_link_libraries(omwlauncher + ${LIBUNSHIELD_LIBRARY} + ) +endif(NOT WIN32) + + if(DPKG_PROGRAM) INSTALL(TARGETS omwlauncher RUNTIME DESTINATION games COMPONENT omwlauncher) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index b75d09c51..032f70916 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -11,6 +11,12 @@ #include +#ifndef WIN32 + #include "unshieldthread.hpp" +#endif + +#include "textslotmsgbox.hpp" + #include "utils/checkablemessagebox.hpp" #include "playpage.hpp" @@ -128,11 +134,16 @@ bool MainDialog::showFirstRunDialog() QDir dir(path); dir.setPath(dir.canonicalPath()); // Resolve symlinks - if (!dir.cdUp()) - continue; // Cannot move from Data Files - if (dir.exists(QString("Morrowind.ini"))) iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini"))); + else + { + if (!dir.cdUp()) + continue; // Cannot move from Data Files + + if (dir.exists(QString("Morrowind.ini"))) + iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini"))); + } } // Ask the user where the Morrowind.ini is @@ -344,6 +355,78 @@ bool MainDialog::setupLauncherSettings() return true; } +#ifndef WIN32 +bool expansions(UnshieldThread& cd) +{ + if(cd.BloodmoonDone()) + { + cd.Done(); + return false; + } + + QMessageBox expansionsBox; + expansionsBox.setText(QObject::tr("
Would you like to install expansions now ? (make sure you have the disc)
\ + If you want to install both Bloodmoon and Tribunal, you have to install Tribunal first.
")); + + QAbstractButton* tribunalButton = NULL; + if(!cd.TribunalDone()) + tribunalButton = expansionsBox.addButton(QObject::tr("&Tribunal"), QMessageBox::ActionRole); + + QAbstractButton* bloodmoonButton = expansionsBox.addButton(QObject::tr("&Bloodmoon"), QMessageBox::ActionRole); + QAbstractButton* noneButton = expansionsBox.addButton(QObject::tr("&None"), QMessageBox::ActionRole); + + expansionsBox.exec(); + + if(expansionsBox.clickedButton() == noneButton) + { + cd.Done(); + return false; + } + else if(expansionsBox.clickedButton() == tribunalButton) + { + + TextSlotMsgBox cdbox; + cdbox.setStandardButtons(QMessageBox::Cancel); + + QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&))); + QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject())); + + cd.SetTribunalPath( + QFileDialog::getOpenFileName( + NULL, + QObject::tr("Select data1.hdr from Tribunal Installation CD (Tribunal/data1.hdr on GOTY CDs)"), + QDir::currentPath(), + QString(QObject::tr("Installshield hdr file (*.hdr)"))).toUtf8().constData()); + + cd.start(); + cdbox.exec(); + } + else if(expansionsBox.clickedButton() == bloodmoonButton) + { + + TextSlotMsgBox cdbox; + cdbox.setStandardButtons(QMessageBox::Cancel); + + QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&))); + QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject())); + + cd.SetBloodmoonPath( + QFileDialog::getOpenFileName( + NULL, + QObject::tr("Select data1.hdr from Bloodmoon Installation CD (Bloodmoon/data1.hdr on GOTY CDs)"), + QDir::currentPath(), + QString(QObject::tr("Installshield hdr file (*.hdr)"))).toUtf8().constData()); + + cd.start(); + cdbox.exec(); + } + + + + return true; +} +#endif // WIN32 + bool MainDialog::setupGameSettings() { QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); @@ -401,9 +484,15 @@ bool MainDialog::setupGameSettings() Press \"Browse...\" to specify the location manually.
")); QAbstractButton *dirSelectButton = - msgBox.addButton(QObject::tr("B&rowse..."), QMessageBox::ActionRole); + msgBox.addButton(QObject::tr("Browse to &Install..."), QMessageBox::ActionRole); + + #ifndef WIN32 + QAbstractButton *cdSelectButton = + msgBox.addButton(QObject::tr("Browse to &CD..."), QMessageBox::ActionRole); + #endif + - msgBox.exec(); + msgBox.exec(); QString selectedFile; if (msgBox.clickedButton() == dirSelectButton) { @@ -413,6 +502,40 @@ bool MainDialog::setupGameSettings() QDir::currentPath(), QString(tr("Morrowind master file (*.esm)"))); } + #ifndef WIN32 + else if(msgBox.clickedButton() == cdSelectButton) { + UnshieldThread cd; + + { + TextSlotMsgBox cdbox; + cdbox.setStandardButtons(QMessageBox::Cancel); + + QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&))); + QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject())); + + cd.SetMorrowindPath( + QFileDialog::getOpenFileName( + NULL, + QObject::tr("Select data1.hdr from Morrowind Installation CD"), + QDir::currentPath(), + QString(tr("Installshield hdr file (*.hdr)"))).toUtf8().constData()); + + cd.SetOutputPath( + QFileDialog::getExistingDirectory( + NULL, + QObject::tr("Select where to extract files to"), + QDir::currentPath(), + QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks).toUtf8().constData()); + + cd.start(); + cdbox.exec(); + } + + while(expansions(cd)); + + selectedFile = QString::fromStdString(cd.GetMWEsmPath()); + } + #endif // WIN32 if (selectedFile.isEmpty()) return false; // Cancel was clicked; diff --git a/apps/launcher/textslotmsgbox.cpp b/apps/launcher/textslotmsgbox.cpp new file mode 100644 index 000000000..0607d1cc6 --- /dev/null +++ b/apps/launcher/textslotmsgbox.cpp @@ -0,0 +1,6 @@ +#include "textslotmsgbox.hpp" + +void TextSlotMsgBox::setTextSlot(const QString& string) +{ + setText(string); +} diff --git a/apps/launcher/textslotmsgbox.hpp b/apps/launcher/textslotmsgbox.hpp new file mode 100644 index 000000000..a29e2c354 --- /dev/null +++ b/apps/launcher/textslotmsgbox.hpp @@ -0,0 +1,13 @@ +#ifndef TEXT_SLOT_MSG_BOX +#define TEXT_SLOT_MSG_BOX + +#include + +class TextSlotMsgBox : public QMessageBox +{ +Q_OBJECT + public slots: + void setTextSlot(const QString& string); +}; + +#endif diff --git a/apps/launcher/unshieldthread.cpp b/apps/launcher/unshieldthread.cpp new file mode 100644 index 000000000..ab9d984e1 --- /dev/null +++ b/apps/launcher/unshieldthread.cpp @@ -0,0 +1,487 @@ +#include "unshieldthread.hpp" + +#include +#include + +namespace bfs = boost::filesystem; + +namespace +{ + static bool make_sure_directory_exists(bfs::path directory) + { + + if(!bfs::exists(directory)) + { + bfs::create_directories(directory); + } + + return bfs::exists(directory); + } + + void fill_path(bfs::path& path, const std::string& name) + { + size_t start = 0; + + size_t i; + for(i = 0; i < name.length(); i++) + { + switch(name[i]) + { + case '\\': + path /= name.substr(start, i-start); + start = i+1; + break; + } + } + + path /= name.substr(start, i-start); + } + + std::string get_setting(const std::string& category, const std::string& setting, const std::string& inx) + { + size_t start = inx.find(category); + start = inx.find(setting, start) + setting.length() + 3; + + size_t end = inx.find("!", start); + + return inx.substr(start, end-start); + } + + std::string read_to_string(const bfs::path& path) + { + std::ifstream strstream(path.c_str(), std::ios::in | std::ios::binary); + std::string str; + + strstream.seekg(0, std::ios::end); + str.resize(strstream.tellg()); + strstream.seekg(0, std::ios::beg); + strstream.read(&str[0], str.size()); + strstream.close(); + + return str; + } + + void add_setting(const std::string& category, const std::string& setting, const std::string& val, std::string& ini) + { + size_t loc; + loc = ini.find("[" + category + "]"); + + // If category is not found, create it + if(loc == std::string::npos) + { + loc = ini.size() + 2; + ini += ("\r\n[" + category + "]\r\n"); + } + + loc += category.length() +2 +2; + ini.insert(loc, setting + "=" + val + "\r\n"); + } + + void bloodmoon_fix_ini(std::string& ini, const bfs::path inxPath) + { + std::string inx = read_to_string(inxPath); + + // Remove this one setting (the only one actually changed by bloodmoon, as opposed to just adding new ones) + size_t start = ini.find("[Weather Blight]"); + start = ini.find("Ambient Loop Sound ID", start); + size_t end = ini.find("\r\n", start) +2; + ini.erase(start, end-start); + + std::string category; + std::string setting; + + category = "General"; + { + setting = "Werewolf FOV"; add_setting(category, setting, get_setting(category, setting, inx), ini); + } + category = "Moons"; + { + setting = "Script Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + } + category = "Weather"; + { + setting = "Snow Ripples"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Ripple Radius"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Ripples Per Flake"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Ripple Scale"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Ripple Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Gravity Scale"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow High Kill"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Low Kill"; add_setting(category, setting, get_setting(category, setting, inx), ini); + } + category = "Weather Blight"; + { + setting = "Ambient Loop Sound ID"; add_setting(category, setting, get_setting(category, setting, inx), ini); + } + category = "Weather Snow"; + { + setting = "Sky Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sky Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sky Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sky Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Fog Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Fog Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Fog Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Fog Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Disc Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Transition Delta"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Land Fog Day Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Land Fog Night Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Clouds Maximum Percent"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Wind Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Cloud Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Glare View"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Cloud Texture"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Loop Sound ID"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Threshold"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Diameter"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Height Min"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Height Max"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Snow Entrance Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Max Snowflakes"; add_setting(category, setting, get_setting(category, setting, inx), ini); + } + category = "Weather Blizzard"; + { + setting = "Sky Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sky Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sky Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sky Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Fog Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Fog Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Fog Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Fog Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Sun Disc Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Transition Delta"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Land Fog Day Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Land Fog Night Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Clouds Maximum Percent"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Wind Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Cloud Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Glare View"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Cloud Texture"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Ambient Loop Sound ID"; add_setting(category, setting, get_setting(category, setting, inx), ini); + setting = "Storm Threshold"; add_setting(category, setting, get_setting(category, setting, inx), ini); + } + } + + + void fix_ini(const bfs::path& output_dir, bfs::path cdPath, bool tribunal, bool bloodmoon) + { + bfs::path ini_path = output_dir; + ini_path /= "Morrowind.ini"; + + std::string ini = read_to_string(ini_path.string()); + + if(tribunal) + { + add_setting("Game Files", "GameFile1", "Tribunal.esm", ini); + add_setting("Archives", "Archive 0", "Tribunal.bsa", ini); + } + if(bloodmoon) + { + bloodmoon_fix_ini(ini, cdPath / "setup.inx"); + add_setting("Game Files", "GameFile2", "Bloodmoon.esm", ini); + add_setting("Archives", "Archive 1", "Bloodmoon.bsa", ini); + } + + std::ofstream inistream(ini_path.c_str()); + inistream << ini; + inistream.close(); + } + + void installToPath(const bfs::path& from, const bfs::path& to, bool copy = false) + { + make_sure_directory_exists(to); + + for ( bfs::directory_iterator end, dir(from); dir != end; ++dir ) + { + if(bfs::is_directory(dir->path())) + installToPath(dir->path(), to / dir->path().filename(), copy); + else + { + if(copy) + bfs::copy_file(dir->path(), to / dir->path().filename()); + else + bfs::rename(dir->path(), to / dir->path().filename()); + } + } + } + + bfs::path findFile(const bfs::path& in, std::string filename, bool recursive = true) + { + if(recursive) + { + for ( bfs::recursive_directory_iterator end, dir(in); dir != end; ++dir ) + { + if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename) + return dir->path(); + } + } + else + { + for ( bfs::directory_iterator end, dir(in); dir != end; ++dir ) + { + if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename) + return dir->path(); + } + } + + return ""; + } + + bool contains(const bfs::path& in, std::string filename) + { + for(bfs::directory_iterator end, dir(in); dir != end; ++dir) + { + if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename) + return true; + } + + return false; + } + + time_t getTime(const char* time) + { + struct tm tms; + memset(&tms, 0, sizeof(struct tm)); + strptime(time, "%d %B %Y", &tms); + return mktime(&tms); + } +} + +bool UnshieldThread::SetMorrowindPath(const std::string& path) +{ + mMorrowindPath = path; + return true; +} + +bool UnshieldThread::SetTribunalPath(const std::string& path) +{ + mTribunalPath = path; + return true; +} + +bool UnshieldThread::SetBloodmoonPath(const std::string& path) +{ + mBloodmoonPath = path; + return true; +} + +void UnshieldThread::SetOutputPath(const std::string& path) +{ + mOutputPath = path; +} + +bool UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, const char* prefix, int index) +{ + bool success; + bfs::path dirname; + bfs::path filename; + int directory = unshield_file_directory(unshield, index); + + dirname = output_dir; + + if (prefix && prefix[0]) + dirname /= prefix; + + if (directory >= 0) + { + const char* tmp = unshield_directory_name(unshield, directory); + if (tmp && tmp[0]) + fill_path(dirname, tmp); + } + + make_sure_directory_exists(dirname); + + filename = dirname; + filename /= unshield_file_name(unshield, index); + + emit signalGUI(QString("Extracting: ") + QString(filename.c_str())); + + success = unshield_file_save(unshield, index, filename.c_str()); + + if (!success) + bfs::remove(filename); + + return success; +} + +void UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_dir, bool extract_ini) +{ + Unshield * unshield; + unshield = unshield_open(cab.c_str()); + + int i; + for (i = 0; i < unshield_file_group_count(unshield); i++) + { + UnshieldFileGroup* file_group = unshield_file_group_get(unshield, i); + + for (size_t j = file_group->first_file; j <= file_group->last_file; j++) + { + if (unshield_file_is_valid(unshield, j)) + extract_file(unshield, output_dir, file_group->name, j); + } + } + unshield_close(unshield); +} + + +bool UnshieldThread::extract() +{ + bfs::path outputDataFilesDir = mOutputPath; + outputDataFilesDir /= "Data Files"; + bfs::path extractPath = mOutputPath; + extractPath /= "extract-temp"; + + if(!mMorrowindDone && mMorrowindPath.string().length() > 0) + { + mMorrowindDone = true; + + bfs::path mwExtractPath = extractPath / "morrowind"; + extract_cab(mMorrowindPath, mwExtractPath, true); + + bfs::path dFilesDir = findFile(mwExtractPath, "morrowind.esm").parent_path(); + + installToPath(dFilesDir, outputDataFilesDir); + + // Videos are often kept uncompressed on the cd + bfs::path videosPath = findFile(mMorrowindPath.parent_path(), "video", false); + if(videosPath.string() != "") + { + emit signalGUI(QString("Installing Videos...")); + installToPath(videosPath, outputDataFilesDir / "Video", true); + } + + bfs::path cdDFiles = findFile(mMorrowindPath.parent_path(), "data files", false); + if(cdDFiles.string() != "") + { + emit signalGUI(QString("Installing Uncompressed Data files from CD...")); + installToPath(cdDFiles, outputDataFilesDir, true); + } + + + bfs::rename(findFile(mwExtractPath, "morrowind.ini"), outputDataFilesDir / "Morrowind.ini"); + + mTribunalDone = contains(outputDataFilesDir, "tribunal.esm"); + mBloodmoonDone = contains(outputDataFilesDir, "bloodmoon.esm"); + + } + + else if(!mTribunalDone && mTribunalPath.string().length() > 0) + { + mTribunalDone = true; + + bfs::path tbExtractPath = extractPath / "tribunal"; + extract_cab(mTribunalPath, tbExtractPath, true); + + bfs::path dFilesDir = findFile(tbExtractPath, "tribunal.esm").parent_path(); + + installToPath(dFilesDir, outputDataFilesDir); + + // Mt GOTY CD has Sounds in a seperate folder from the rest of the data files + bfs::path soundsPath = findFile(tbExtractPath, "sounds", false); + if(soundsPath.string() != "") + installToPath(soundsPath, outputDataFilesDir / "Sounds"); + + bfs::path cdDFiles = findFile(mTribunalPath.parent_path(), "data files", false); + if(cdDFiles.string() != "") + { + emit signalGUI(QString("Installing Uncompressed Data files from CD...")); + installToPath(cdDFiles, outputDataFilesDir, true); + } + + mBloodmoonDone = contains(outputDataFilesDir, "bloodmoon.esm"); + + fix_ini(outputDataFilesDir, bfs::path(mTribunalPath).parent_path(), mTribunalDone, mBloodmoonDone); + } + + else if(!mBloodmoonDone && mBloodmoonPath.string().length() > 0) + { + mBloodmoonDone = true; + + bfs::path bmExtractPath = extractPath / "bloodmoon"; + extract_cab(mBloodmoonPath, bmExtractPath, true); + + bfs::path dFilesDir = findFile(bmExtractPath, "bloodmoon.esm").parent_path(); + + installToPath(dFilesDir, outputDataFilesDir); + + // My GOTY CD contains a folder within cab files called Tribunal patch, + // which contains Tribunal.esm + bfs::path tbPatchPath = findFile(bmExtractPath, "tribunal.esm"); + if(tbPatchPath.string() != "") + bfs::rename(tbPatchPath, outputDataFilesDir / "Tribunal.esm"); + + bfs::path cdDFiles = findFile(mBloodmoonPath.parent_path(), "data files", false); + if(cdDFiles.string() != "") + { + emit signalGUI(QString("Installing Uncompressed Data files from CD...")); + installToPath(cdDFiles, outputDataFilesDir, true); + } + + fix_ini(outputDataFilesDir, bfs::path(mBloodmoonPath).parent_path(), false, mBloodmoonDone); + } + + + return true; +} + +void UnshieldThread::Done() +{ + // Get rid of unnecessary files + bfs::remove_all(mOutputPath / "extract-temp"); + + // Set modified time to release dates, to preserve load order + if(mMorrowindDone) + bfs::last_write_time(findFile(mOutputPath, "morrowind.esm"), getTime("1 May 2002")); + + if(mTribunalDone) + bfs::last_write_time(findFile(mOutputPath, "tribunal.esm"), getTime("6 November 2002")); + + if(mBloodmoonDone) + bfs::last_write_time(findFile(mOutputPath, "bloodmoon.esm"), getTime("3 June 2003")); +} + +std::string UnshieldThread::GetMWEsmPath() +{ + return findFile(mOutputPath / "Data Files", "morrowind.esm").string(); +} + +bool UnshieldThread::TribunalDone() +{ + return mTribunalDone; +} + +bool UnshieldThread::BloodmoonDone() +{ + return mBloodmoonDone; +} + +void UnshieldThread::run() +{ + extract(); + emit close(); +} + +UnshieldThread::UnshieldThread() +{ + mMorrowindDone = false; + mTribunalDone = false; + mBloodmoonDone = false; +} diff --git a/apps/launcher/unshieldthread.hpp b/apps/launcher/unshieldthread.hpp new file mode 100644 index 000000000..b48d3d987 --- /dev/null +++ b/apps/launcher/unshieldthread.hpp @@ -0,0 +1,57 @@ +#ifndef UNSHIELD_THREAD_H +#define UNSHIELD_THREAD_H + +#include + +#include + +#include + + +class UnshieldThread : public QThread +{ + Q_OBJECT + + public: + bool SetMorrowindPath(const std::string& path); + bool SetTribunalPath(const std::string& path); + bool SetBloodmoonPath(const std::string& path); + + void SetOutputPath(const std::string& path); + + bool extract(); + + bool TribunalDone(); + bool BloodmoonDone(); + + void Done(); + + std::string GetMWEsmPath(); + + UnshieldThread(); + + private: + + void extract_cab(const boost::filesystem::path& cab, const boost::filesystem::path& output_dir, bool extract_ini = false); + bool extract_file(Unshield* unshield, boost::filesystem::path output_dir, const char* prefix, int index); + + boost::filesystem::path mMorrowindPath; + boost::filesystem::path mTribunalPath; + boost::filesystem::path mBloodmoonPath; + + bool mMorrowindDone; + bool mTribunalDone; + bool mBloodmoonDone; + + boost::filesystem::path mOutputPath; + + + protected: + virtual void run(); + + signals: + void signalGUI(QString); + void close(); +}; + +#endif diff --git a/cmake/FindLIBUNSHIELD.cmake b/cmake/FindLIBUNSHIELD.cmake new file mode 100644 index 000000000..4f4e98a1c --- /dev/null +++ b/cmake/FindLIBUNSHIELD.cmake @@ -0,0 +1,48 @@ +# Locate LIBUNSHIELD +# This module defines +# LIBUNSHIELD_LIBRARY +# LIBUNSHIELD_FOUND, if false, do not try to link to LibUnshield +# LIBUNSHIELD_INCLUDE_DIR, where to find the headers +# +# Created by Tom Mason (wheybags) for OpenMW (http://openmw.com), based on FindMPG123.cmake +# +# Ripped off from other sources. In fact, this file is so generic (I +# just did a search and replace on another file) that I wonder why the +# CMake guys haven't wrapped this entire thing in a single +# function. Do we really need to repeat this stuff for every single +# library when they all work the same? + +FIND_PATH(LIBUNSHIELD_INCLUDE_DIR libunshield.h + HINTS + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt + /usr/include +) + +FIND_LIBRARY(LIBUNSHIELD_LIBRARY + unshield + HINTS +# PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64 + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw + /opt/local + /opt/csw + /opt + /usr/lib +) + +IF(LIBUNSHIELD_LIBRARY AND LIBUNSHIELD_INCLUDE_DIR) + SET(LIBUNSHIELD_FOUND "YES") +ENDIF(LIBUNSHIELD_LIBRARY AND LIBUNSHIELD_INCLUDE_DIR) +