#include "lotro.h" #include "filesystem.h" #include #include #include #include #include #include Lotro::Lotro(QSettings& app_settings_, QObject *parent) : app_settings(app_settings_), QObject(parent) { } void Lotro::initialiseDatFile(QString file_name) { if (!tryToBlockFile()) return; emit processStarted("initialiseDatFile", {file_name}); qDebug() << "Initialising file " << file_name; if (!FileSystem::fileExists(file_path)) { emit caughtError("initialiseDatFile", {QString("Ошибка инициализации"), QString("Файл " + file_path + " несуществует! Невозможно инициализировать файл ресурсов.")}); busy = false; return; } file.Initialise((file_path).toStdString(), 0); busy = false; emit processFinished("initialiseDatFile", {QString("Success")}); } void Lotro::changeLocale() { if (!tryToBlockFile()) return; qDebug() << "Changing locale of dat file..."; emit processStarted("changeLocale", {current_locale, new_locale}); // Setting locale, opposite to current auto current_locale = file.GetLocaleManager().GetCurrentLocale(); auto new_locale = current_locale == LOTRO_DAT::DatLocaleManager::PATCHED ? LOTRO_DAT::DatLocaleManager::ORIGINAL : LOTRO_DAT::DatLocaleManager::PATCHED; QString old_locale_name = (current_locale == LOTRO_DAT::DatLocaleManager::PATCHED ? "Русифицированная" : "Оригинальная"); QString new_locale_name = (new_locale == LOTRO_DAT::DatLocaleManager::PATCHED ? "Русифицированная" : "Оригинальная"); auto operation = file.GetLocaleManager().SetLocale(new_locale); auto new_current_locale = file.GetLocaleManager().GetCurrentLocale(); QString new_current_locale_name = (new_current_locale == LOTRO_DAT::DatLocaleManager::PATCHED ? "Русифицированная" : "Оригинальная"); busy = false; if (operation.result == LOTRO_DAT::SUCCESS) { emit processFinished("changeLocale", {"Success", new_current_locale_name}); } else { emit caughtError("changeLocale", {"Ошибка смены локали!", QString("Не удалось сменить локаль игры! Текущая локаль: ") + new_current_locale_name}); emit processFinished("changeLocale", {"Error", new_current_locale_name}); } } void Lotro::getLocaleFileContents(long long file_id, int locale) { if (!tryToBlockFile()) return; emit processStarted("getFileContents", {file_id, locale}); auto getfile_op = file.GetLocaleManager().GetLocaleFile(file_id, locale); if (!getfile_op.result) { emit caughtError("getFileContents", {"Файл не найден!", QString("Не удаётся найти файл с id ") + QString::number(file_id)}); emit processFinished("getFileContents", {"Error"}); busy = false; return; } LOTRO_DAT::SubFile subfile = getfile_op.value; auto getfiledata_op = file.GetFileSystem().GetFileData(subfile, 8); if (getfile_op.result == LOTRO_DAT::ERROR) { emit caughtError("getFileContents", {"Ошибка извлечения!", QString("Обнаружены некорректные данные файла в словаре! Файл ресурсов мог быть повреждён!\nid = ") + QString::number(file_id) + ", locale_id = " + QString::number(locale)}); emit processFinished("getFileContents", {"Error"}); busy = false; return; } LOTRO_DAT::SubfileData result = subfile.PrepareForExport(getfiledata_op.value); busy = false; emit localeFileContentsReceived(locale, result); emit processFinished("getFileContents", {"Success", file_id, locale}); } void Lotro::importFilesFromDatabase(QString database_path) { if (!tryToBlockFile()) return; emit processStarted("importFilesFromDatabase", {database_path}); if (!FileSystem::fileExists(database_path)) { emit caughtError("importFilesFromDatabase", {QString("Ошибка импорта!"), QString("Файл " + file_path + " не существует! Невозможно инициализировать базу данных!")}); emit processFinished("importFilesFromDatabase", {QString("Error")}); busy = false; return; } LOTRO_DAT::Database db; if (!db.InitDatabase(database_path.toStdString())) { emit caughtError("importFilesFromDatabase", {QString("Ошибка импорта!"), QString("Внутренняя ошибка инициализации базы данных!")}); emit processFinished("importFilesFromDatabase", {QString("Error")}); busy = false; return; } auto patch_operation = file.GetPatcher().PatchAllDatabase(&db); busy = false; if (patch_operation.result == LOTRO_DAT::SUCCESS) { emit processFinished("importFilesFromDatabase", {QString("Success"), patch_operation.value}); } else { emit processFinished("importFilesFromDatabase", {QString("Error"), 0}); } } void Lotro::importFile(long long file_id, QString file_path) { if (!tryToBlockFile()) return; emit processStarted("importFile", {file_id, file_path}); if (!FileSystem::fileExists(file_path)) { emit caughtError("importFile", {QString("Ошибка импорта!"), QString("Файл ") + file_path + QString(" не существует!")}); emit processFinished("importFile", {QString("Error")}); busy = false; return; } LOTRO_DAT::SubfileData data; data.options["fid"] = file_id; auto getfile_op = file.GetFileSystem().GetFile(file_id); if (getfile_op.value == LOTRO_DAT::ERROR) { emit caughtError("importFile", {QString("Ошибка импорта!"), QString("Файл с id ") + QString::number(file_id) + QString(" не существует в ресурсах игры! Невозможно импортировать :/")}); emit processFinished("importFile", {QString("Error")}); busy = false; return; } LOTRO_DAT::SubFile subfile = *(getfile_op.value); data.options["ext"] = subfile.Extension(); data.options["cat"] = subfile.category; if (subfile.FileType() == LOTRO_DAT::TEXT) { std::basic_ifstream input_stream(file_path.toStdString(), std::ios::in); if (!input_stream.is_open()) { emit caughtError("importFile", {QString("Ошибка импорта!"), QString("Текстовый файл ") + file_path + QString(" не удаётся открыть!")}); emit processFinished("importFile", {QString("Error")}); busy = false; return; } std::basic_stringstream strStream; strStream << input_stream.rdbuf();//read the file data.text_data = strStream.str();//str holds the content of the file input_stream.close(); } else { QFile data_file(file_path); data_file.open(QIODevice::ReadOnly); if (!data_file.isOpen()) { emit caughtError("importFile", {QString("Ошибка импорта!"), QString("Файл ") + file_path + QString(" не удаётся открыть!")}); emit processFinished("importFile", {QString("Error")}); busy = false; return; } QByteArray contents = data_file.readAll(); data.binary_data = LOTRO_DAT::BinaryData(contents.constData(), contents.size()); } auto patchfile_op = file.GetPatcher().PatchFile(data); busy = false; if (patchfile_op.result == LOTRO_DAT::SUCCESS) { emit processFinished("importFile", {QString("Success")}); } else { emit caughtError("importFile", {QString("Ошибка импорта!"), QString("Возникла внутренняя ошибка применения патча. Импорт завершился с ошибкой")}); emit processFinished("importFile", {QString("Error")}); } return; } void Lotro::importTextFragment(long long file_id, long long fragment_id, QString fragment_contents, QString arguments) { if (!tryToBlockFile()) return; emit processStarted("importTextFragment", {file_id, fragment_id}); if (fragment_contents.contains("DO_NOT_TOUCH")) { emit caughtError("importTextFragment", {"Ошибка формата!", QString("Текстовые данные содержат указатели на аргументы DO_NOT_TOUCH! Такого быть не должно! Пользуйтесь порядком перечисления аргументов внизу")}); emit processFinished("importTextFragment", {"Error"}); busy = false; return false; } auto getfile_op = file.GetFileSystem().GetFile(file_id); if (!getfile_op.result) { emit caughtError("importTextFragment", {"Файл не найден!", QString("Не удаётся найти в ресурсах файл с id ") + QString::number(file_id)}); emit processFinished("importTextFragment", {"Error"}); busy = false; return false; } LOTRO_DAT::SubFile subfile = getfile_op.value; auto getfiledata_op = file.GetFileSystem().GetFileData(subfile, 8); if (getfile_op.result == LOTRO_DAT::ERROR) { emit caughtError("importTextFragment", {"Ошибка импорта!", QString("Обнаружены некорректные данные файла в словаре! Файл ресурсов мог быть повреждён!\nid = ") + QString::number(file_id)}); emit processFinished("importTextFragment", {"Error"}); busy = false; return false; } LOTRO_DAT::SubfileData data = subfile.PrepareForExport(getfiledata_op.value); if (!result) { emit caughtError("importTextFragment", {"Ошибка импорта!", QString("Не удалось подготовить файл к изменению фрагмента!\nid = ") + QString::number(file_id)}); emit processFinished("importTextFragment", {"Error"}); busy = false; return false; } int beginning = data.text_data.find(QString::number(fragment_id).toStdU16String(), 0); if (beginning == std::u16string::npos) { emit caughtError("importTextFragment", {"Ошибка импорта!", QString("Не удалось найти фрагмент в файле!\nid = ") + QString::number(file_id) + "\nfragment_id = " + QString::number(fragment_id)}); emit processFinished("importTextFragment", {"Error"}); busy = false; return false; } int ending = data.text_data.find(QString("|||").toStdU16String(), beginning); QString new_fragment = QString::number(fragment_id) + ":::" + arguments + ":::" + fragment_contents; std::u16string new_text = data.text_data.substr(0, beginning) + new_fragment.toStdU16String() + result.text_data.substr(ending); data.text_data = new_text; auto patchfile_op = file.GetPatcher().PatchFile(data); busy = false; if (patchfile_op.result == LOTRO_DAT::SUCCESS) { emit processFinished("importTextFragment", {QString("Success")}); } else { emit caughtError("importTextFragment", {QString("Ошибка импорта!"), QString("Возникла внутренняя ошибка применения патча. Импорт завершился с ошибкой")}); emit processFinished("importTextFragment", {QString("Error")}); } } void Lotro::getTextFragment(long long file_id, long long fragment_id) { if (!tryToBlockFile()) return; emit processStarted("getTextFragment", {file_id, fragment_id}); auto getfile_op = file.GetFileSystem().GetFile(file_id); if (!getfile_op.result) { emit caughtError("getTextFragment", {"Файл не найден!", QString("Не удаётся найти в ресурсах файл с id ") + QString::number(file_id)}); emit processFinished("getTextFragment", {"Error"}); busy = false; return false; } LOTRO_DAT::SubFile subfile = getfile_op.value; auto getfiledata_op = file.GetFileSystem().GetFileData(subfile, 8); if (getfile_op.result == LOTRO_DAT::ERROR) { emit caughtError("getTextFragment", {"Ошибка импорта!", QString("Обнаружены некорректные данные файла в словаре! Файл ресурсов мог быть повреждён!\nid = ") + QString::number(file_id)}); emit processFinished("getTextFragment", {"Error"}); busy = false; return false; } LOTRO_DAT::SubfileData data = subfile.PrepareForExport(getfiledata_op.value); if (!result) { emit caughtError("getTextFragment", {"Ошибка импорта!", QString("Не удалось подготовить файл к изменению фрагмента!\nid = ") + QString::number(file_id)}); emit processFinished("getTextFragment", {"Error"}); busy = false; return false; } int beginning = data.text_data.find(QString::number(fragment_id).toStdU16String(), 0); if (beginning == std::u16string::npos) { emit caughtError("importTextFragment", {"Ошибка импорта!", QString("Не удалось найти фрагмент в файле!\nid = ") + QString::number(file_id) + "\nfragment_id = " + QString::number(fragment_id)}); emit processFinished("importTextFragment", {"Error"}); busy = false; return false; } int ending = data.text_data.find(QString("|||").toStdU16String(), beginning); int size = std::u16string::npos; if (ending != std::u16string::npos) size = ending - beginning; std::u16string str = data.text_data.substr(beginning, size); QStringList splitted_fragment = QString::fromStdU16String(str).split(":::"); if (splitted_fragment.size() != 3) { emit caughtError("importTextFragment", {"Ошибка импорта!", QString("Получены некорректные данные фрагмента!\nДанные:") + QString::fromStdU16String(str)}); emit processFinished("importTextFragment", {"Error"}); busy = false; return false; } busy = false; emit textFragmentReceived(splitted_fragment.at(1), splitted_fragment.at(2)); emit processFinished("importTextFragment", {"Success"}); } void Lotro::createCoreStatusFile(QString output_filename) { if (!tryToBlockFile()) return; emit processStarted("createCoreStatusFile", {output_filename}); auto gatherinfo_op = file.GatherInformation(output_filename.toStdString()); busy = false; if (gatherinfo_op.result == LOTRO_DAT::SUCCESS) { emit processFinished("createCoreStatusFile", {"Success", output_filename}); } else { emit caughtError("createCoreStatusFile", {"Ошибка сбора информации!", QString("Не удаётся создать файл информации ядра")}); emit processFinished("createCoreStatusFile", {"Error", output_filename}); } } void Lotro::extractSingleFile(QString output_filename, long long file_id) { if (!tryToBlockFile()) return; emit processStarted("extractSingleFile", {output_filename, file_id}); auto extractfile_op = file.GetExporter().ExtractFileById(file_id, output_filename.toStdString()); busy = false; if (extractfile_op.result == LOTRO_DAT::SUCCESS) { emit processFinished("extractSingleFile", {"Success", output_filename, file_id}); } else { emit caughtError("extractSingleFile", {"Ошибка экспорта!", QString("Не удаётся экспортировать файл " + QString::number(file_id) + " в файл " + output_filename)}); emit processFinished("extractSingleFile", {"Error", output_filename}); } } void Lotro::extractSingleFileToDatabase(QString database_path, long long file_id) { if (!tryToBlockFile()) return; emit processStarted("extractSingleFileToDatabase", {database_path, file_id}); LOTRO_DAT::Database db; if (!db.InitDatabase(database_path.toStdString())) { emit caughtError("extractSingleFileToDatabase", {"Ошибка экспорта!", QString("Не удаётся создать/открыть базу данных " + database_path)}); emit processFinished("extractSingleFileToDatabase", {"Error", database_path}); busy = false; return; } auto extractfile_op = file.GetExporter().ExtractFileById(file_id, &db); busy = false; if (extractfile_op.result == LOTRO_DAT::SUCCESS) { emit processFinished("extractSingleFileToDatabase", {"Success", database_path, file_id}); } else { emit caughtError("extractSingleFileToDatabase", {"Ошибка экспорта!", QString("Не удаётся экспортировать файл " + QString::number(file_id) + " в базу данных " + database_path)}); emit processFinished("extractSingleFileToDatabase", {"Error", database_path}); } } void Lotro::extractGrouppedFiles(QString output_foldername, LOTRO_DAT::FILE_TYPE type) { if (!tryToBlockFile()) return; emit processStarted("extractSingleFile", {output_foldername, file_id}); auto extractfile_op = file.GetExporter().ExtractAllFilesByType(type, output_foldername.toStdString()); busy = false; if (extractfile_op.result == LOTRO_DAT::SUCCESS) { emit processFinished("extractSingleFile", {"Success", output_foldername, type}); } else { emit caughtError("extractSingleFile", {"Ошибка экспорта!", QString("Не удаётся экспортировать файл " + QString::number(file_id) + " в файл " + output_filename)}); emit processFinished("extractSingleFile", {"Error", output_foldername}); } } void Lotro::extractGrouppedFilesToDatabase(QString database_path, LOTRO_DAT::FILE_TYPE type) { if (!tryToBlockFile()) return; emit processStarted("extractGrouppedFilesToDatabase", {database_path, file_id}); LOTRO_DAT::Database db; if (!db.InitDatabase(database_path.toStdString())) { emit caughtError("extractGrouppedFilesToDatabase", {"Ошибка экспорта!", QString("Не удаётся создать/открыть базу данных " + database_path)}); emit processFinished("extractGrouppedFilesToDatabase", {"Error", database_path}); busy = false; return; } auto extractfile_op = file.GetExporter().ExtractFileById(file_id, &db); busy = false; if (extractfile_op.result == LOTRO_DAT::SUCCESS) { emit processFinished("extractGrouppedFilesToDatabase", {"Success", database_path, type}); } else { emit caughtError("extractGrouppedFilesToDatabase", {"Ошибка экспорта!", QString("Не удаётся экспортировать файлы с типом " + QString::number(type) + " в базу данных " + database_path)}); emit processFinished("extractGrouppedFilesToDatabase", {"Error", output_foldername}); } } void Lotro::getUnactiveCategories() { if (!tryToBlockFile()) return; emit processStarted("getUnactiveCategories", {}); const std::set& categories = file.GetLocaleManager().GetInactiveCategories(); QStringList result; for (long long category : categories) { result << QString::number(category); } busy = false; emit unactiveCategoriesReceived(result); emit processFinished("getUnactiveCategories", {"Success"}); } void Lotro::startGame() { if (!tryToBlockFile()) return; emit processStarted("startGame", {}); QStringList args; args << "-skiprawdownload" << "-nosplash"; if (file.GetLocaleManager().GetCurrentLocale() == LOTRO_DAT::DatLocaleManager::PATCHED) args << "-disablePatch"; file.Deinitialize(); if(FileSystem::fileExists(QApplication::applicationDirPath() + "/user.ini")){ QSettings login(QApplication::applicationDirPath() + "/user.ini", QSettings::IniFormat ); login.beginGroup("Account"); QString username = login.value("username", "").toString(); QString password = login.value("password", "").toString(); login.endGroup(); args << "-username" << username << "-password" << password; } qDebug() << "Запускаем игру со следующими аргументами: " << args; QFile f(app_settings("Local", "folder") + "/LotroLauncher.exe"); QProcess process; if (FileSystem::fileExists(f.fileName())) { if(f.fileName().contains(" ")) f.setFileName("\"" + f.fileName() + "\""); process.startDetached(f.fileName(), args); process.waitForFinished(-1); emit processFinished("startGame", {}); } else { emit caughtError("startGame", {"Ошибка запуска игры!", QString("Не удалось найти файл LotroLauncher в папке: ") + app_settings("Local", "folder")}); emit processFinished("startGame", {"Error"}); } busy = false; } void Lotro::getLocaleFileInfo(long long file_id, int locale) { if (!tryToBlockFile()) return; emit processStarted("getLocaleFileInfo", {file_id, locale}); auto getfile_op = file.GetLocaleManager().GetLocaleFile(file_id, locale); if (!getfile_op.result) { emit caughtError("getLocaleFileInfo", {"Файл не найден!", QString("Не удаётся найти в ресурсах файл с id ") + QString::number(file_id)}); emit processFinished("getLocaleFileInfo", {"Error"}); busy = false; return; } LOTRO_DAT::SubFile subfile = getfile_op.value; QString result = "Locale file info:\n " "dictionary_offset: " + QString::number(subfile.dictionary_offset()) + "\n" "unknown1: " + QString::number(subfile.unknown1()) + "\n" "file_id: " + QString::number(subfile.file_id()) + "\n" "file_offset: " + QString::number(subfile.file_offset()) + "\n" "file_size: " + QString::number(subfile.file_size()) + "\n" "timestamp: " + QString::number(subfile.timestamp()) + "\n" "version: " + QString::number(subfile.version()) + "\n" "block_size: " + QString::number(subfile.block_size()) + "\n" "unknown2: " + QString::number(subfile.file_id()) + "\n"; busy = false; emit localeFileInfoReceived(result); emit processFinished("getLocaleFileInfo", {"Success"}); } void Lotro::getFileInfo(long long file_id) { if (!tryToBlockFile()) return; emit processStarted("getFileInfo", {file_id, locale}); auto getfile_op = file.GetFileSystem().GetFile(file_id); if (!getfile_op.result) { emit caughtError("getFileInfo", {"Файл не найден!", QString("Не удаётся найти файл с id ") + QString::number(file_id)}); emit processFinished("getFileInfo", {"Error"}); busy = false; return; } LOTRO_DAT::SubFile subfile = *getfile_op.value; QString result = "Locale file info:\n " "dictionary_offset: " + QString::number(subfile.dictionary_offset()) + "\n" "unknown1: " + QString::number(subfile.unknown1()) + "\n" "file_id: " + QString::number(subfile.file_id()) + "\n" "file_offset: " + QString::number(subfile.file_offset()) + "\n" "file_size: " + QString::number(subfile.file_size()) + "\n" "timestamp: " + QString::number(subfile.timestamp()) + "\n" "version: " + QString::number(subfile.version()) + "\n" "block_size: " + QString::number(subfile.block_size()) + "\n" "unknown2: " + QString::number(subfile.file_id()) + "\n"; busy = false; emit localeFileInfoReceived(result); emit processFinished("getFileInfo", {"Success"}); } bool Lotro::initialised() { return file.Initialized(); } int Lotro::currentLocale() { return file.GetLocaleManager().GetCurrentLocale(); } bool Lotro::notPatched() { return file.GetStatusModule().CheckIfNotPatched(); } bool Lotro::tryToBlockFile() { if (busy) { emit caughtError("common", {QString("Ошибка инициализации!"), QString("Уже выполняется другой процесс с ресурсами игры! Невозможно инициализировать во время выполнения другого процесса!")}); return false; } busy = true; }