1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-22 10:39:41 +00:00

Merge branch 'master' of gitlab.com:openmw/openmw into fix_global_iteration

This commit is contained in:
Zackhasacat 2024-02-05 15:01:59 -06:00
commit c8eaa5976a
60 changed files with 7909 additions and 443 deletions

View file

@ -172,6 +172,20 @@ Clang_Format:
- CI/check_file_names.sh
- CI/check_clang_format.sh
Lupdate:
extends: .Ubuntu_Image
stage: checks
cache:
key: Ubuntu_lupdate.ubuntu_22.04.v1
paths:
- apt-cache/
variables:
LUPDATE: lupdate
before_script:
- CI/install_debian_deps.sh openmw-qt-translations
script:
- CI/check_qt_translations.sh
Teal:
stage: checks
extends: .Ubuntu_Image

View file

@ -24,6 +24,7 @@
Bug #5129: Stuttering animation on Centurion Archer
Bug #5280: Unskinned shapes in skinned equipment are rendered in the wrong place
Bug #5371: Keyframe animation tracks are used for any file that begins with an X
Bug #5413: Enemies do a battlecry everytime the player summons a creature
Bug #5714: Touch spells cast using ExplodeSpell don't always explode
Bug #5849: Paralysis breaks landing
Bug #5870: Disposing of actors who were selected in the console doesn't deselect them like vanilla
@ -37,6 +38,7 @@
Bug #6402: The sound of a thunderstorm does not stop playing after entering the premises
Bug #6427: Enemy health bar disappears before damaging effect ends
Bug #6550: Cloned body parts don't inherit texture effects
Bug #6574: Crash at far away from world origin coordinates
Bug #6645: Enemy block sounds align with animation instead of blocked hits
Bug #6657: Distant terrain tiles become black when using FWIW mod
Bug #6661: Saved games that have no preview screenshot cause issues or crashes
@ -132,6 +134,7 @@
Bug #7758: Water walking is not taken into account to compute path cost on the water
Bug #7761: Rain and ambient loop sounds are mutually exclusive
Bug #7765: OpenMW-CS: Touch Record option is broken
Bug #7769: Sword of the Perithia: Broken NPCs
Bug #7770: Sword of the Perithia: Script execution failure
Bug #7780: Non-ASCII texture paths in NIF files don't work
Bug #7785: OpenMW-CS initialising Skill and Attribute fields to 0 instead of -1 on non-FortifyStat spells
@ -143,6 +146,7 @@
Feature #6149: Dehardcode Lua API_REVISION
Feature #6152: Playing music via lua scripts
Feature #6188: Specular lighting from point light sources
Feature #6411: Support translations in openmw-launcher
Feature #6447: Add LOD support to Object Paging
Feature #6491: Add support for Qt6
Feature #6556: Lua API for sounds

11
CI/check_qt_translations.sh Executable file
View file

@ -0,0 +1,11 @@
#!/bin/bash -ex
set -o pipefail
LUPDATE="${LUPDATE:-lupdate}"
${LUPDATE:?} apps/wizard -ts files/lang/wizard_*.ts
${LUPDATE:?} apps/launcher -ts files/lang/launcher_*.ts
${LUPDATE:?} components/contentselector components/process -ts files/lang/components_*.ts
! (git diff --name-only | grep -q "^") || (echo -e "\033[0;31mBuild a 'translations' CMake target to update Qt localization for these files:\033[0;0m"; git diff --name-only | xargs -i echo -e "\033[0;31m{}\033[0;0m"; exit -1)

View file

@ -33,9 +33,10 @@ declare -rA GROUPED_DEPS=(
libboost-system-dev libboost-iostreams-dev
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
libsdl2-dev libqt5opengl5-dev libopenal-dev libunshield-dev libtinyxml-dev
libbullet-dev liblz4-dev libpng-dev libjpeg-dev libluajit-5.1-dev
librecast-dev libsqlite3-dev ca-certificates libicu-dev libyaml-cpp-dev
libsdl2-dev libqt5opengl5-dev qttools5-dev qttools5-dev-tools libopenal-dev
libunshield-dev libtinyxml-dev libbullet-dev liblz4-dev libpng-dev libjpeg-dev
libluajit-5.1-dev librecast-dev libsqlite3-dev ca-certificates libicu-dev
libyaml-cpp-dev
"
# These dependencies can alternatively be built and linked statically.
@ -99,6 +100,12 @@ declare -rA GROUPED_DEPS=(
clang-format-14
git-core
"
[openmw-qt-translations]="
qttools5-dev
qttools5-dev-tools
git-core
"
)
if [[ $# -eq 0 ]]; then

View file

@ -224,9 +224,9 @@ find_package(LZ4 REQUIRED)
if (USE_QT)
find_package(QT REQUIRED COMPONENTS Core NAMES Qt6 Qt5)
if (QT_VERSION_MAJOR VERSION_EQUAL 5)
find_package(Qt5 5.15 COMPONENTS Core Widgets Network OpenGL REQUIRED)
find_package(Qt5 5.15 COMPONENTS Core Widgets Network OpenGL LinguistTools REQUIRED)
else()
find_package(Qt6 COMPONENTS Core Widgets Network OpenGL OpenGLWidgets REQUIRED)
find_package(Qt6 COMPONENTS Core Widgets Network OpenGL OpenGLWidgets LinguistTools REQUIRED)
endif()
message(STATUS "Using Qt${QT_VERSION}")
endif()
@ -1074,3 +1074,57 @@ if (DOXYGEN_FOUND)
WORKING_DIRECTORY ${OpenMW_BINARY_DIR}
COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM)
endif ()
# Qt localization
if (USE_QT)
file(GLOB LAUNCHER_TS_FILES ${CMAKE_SOURCE_DIR}/files/lang/launcher_*.ts)
file(GLOB WIZARD_TS_FILES ${CMAKE_SOURCE_DIR}/files/lang/wizard_*.ts)
file(GLOB COMPONENTS_TS_FILES ${CMAKE_SOURCE_DIR}/files/lang/components_*.ts)
get_target_property(QT_LUPDATE_EXECUTABLE Qt::lupdate IMPORTED_LOCATION)
add_custom_target(translations
COMMAND ${QT_LUPDATE_EXECUTABLE} ${CMAKE_SOURCE_DIR}/components/contentselector ${CMAKE_SOURCE_DIR}/components/process -ts ${COMPONENTS_TS_FILES}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/components
VERBATIM
COMMAND_EXPAND_LISTS
COMMAND ${QT_LUPDATE_EXECUTABLE} ${CMAKE_SOURCE_DIR}/apps/wizard -ts ${WIZARD_TS_FILES}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/apps/wizard
VERBATIM
COMMAND_EXPAND_LISTS
COMMAND ${QT_LUPDATE_EXECUTABLE} ${CMAKE_SOURCE_DIR}/apps/launcher -ts ${LAUNCHER_TS_FILES}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/apps/launcher
VERBATIM
COMMAND_EXPAND_LISTS)
if (BUILD_LAUNCHER OR BUILD_WIZARD)
if (APPLE)
set(QT_TRANSLATIONS_PATH "${APP_BUNDLE_DIR}/Contents/Resources/resources/translations")
else ()
get_generator_is_multi_config(multi_config)
if (multi_config)
set(QT_TRANSLATIONS_PATH "${OpenMW_BINARY_DIR}/$<CONFIG>/resources/translations")
else ()
set(QT_TRANSLATIONS_PATH "${OpenMW_BINARY_DIR}/resources/translations")
endif ()
endif ()
file(GLOB TS_FILES ${COMPONENTS_TS_FILES})
if (BUILD_LAUNCHER)
set(TS_FILES ${TS_FILES} ${LAUNCHER_TS_FILES})
endif ()
if (BUILD_WIZARD)
set(TS_FILES ${TS_FILES} ${WIZARD_TS_FILES})
endif ()
qt_add_translation(QM_FILES ${TS_FILES} OPTIONS -silent)
add_custom_target(qm-files
COMMAND ${CMAKE_COMMAND} -E make_directory ${QT_TRANSLATIONS_PATH}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QM_FILES} ${QT_TRANSLATIONS_PATH}
DEPENDS ${QM_FILES}
COMMENT "Copy *.qm files to resources folder")
endif ()
endif()

View file

@ -76,10 +76,6 @@ namespace
bpo::value<StringsVector>()->default_value(StringsVector(), "fallback-archive")->multitoken()->composing(),
"set fallback BSA archives (later archives have higher priority)");
addOption("resources",
bpo::value<Files::MaybeQuotedPath>()->default_value(Files::MaybeQuotedPath(), "resources"),
"set resources directory");
addOption("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")->multitoken()->composing(),
"content file(s): esm/esp, or omwgame/omwaddon/omwscripts");

View file

@ -71,6 +71,8 @@ openmw_add_executable(openmw-launcher
${UI_HDRS}
)
add_dependencies(openmw-launcher qm-files)
if (WIN32)
INSTALL(TARGETS openmw-launcher RUNTIME DESTINATION ".")
endif (WIN32)

View file

@ -308,7 +308,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
// Display new content with custom formatting
if (mNewDataDirs.contains(canonicalDirPath))
{
tooltip += "Will be added to the current profile\n";
tooltip += tr("Will be added to the current profile");
QFont font = item->font();
font.setBold(true);
font.setItalic(true);
@ -326,7 +326,10 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
if (mSelector->containsDataFiles(currentDir))
{
item->setIcon(QIcon(":/images/openmw-plugin.png"));
tooltip += "Contains content file(s)";
if (!tooltip.isEmpty())
tooltip += "\n";
tooltip += tr("Contains content file(s)");
}
else
{

View file

@ -8,6 +8,7 @@
#include <components/debug/debugging.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/files/qtconversion.hpp>
#include <components/platform/platform.hpp>
#ifdef MAC_OS_X_VERSION_MIN_REQUIRED
@ -34,13 +35,23 @@ int runLauncher(int argc, char* argv[])
{
QApplication app(argc, argv);
QString resourcesPath(".");
if (!variables["resources"].empty())
{
resourcesPath = Files::pathToQString(variables["resources"].as<Files::MaybeQuotedPath>().u8string());
}
// Internationalization
QString locale = QLocale::system().name().section('_', 0, 0);
QTranslator appTranslator;
appTranslator.load(":/translations/" + locale + ".qm");
appTranslator.load(resourcesPath + "/translations/launcher_" + locale + ".qm");
app.installTranslator(&appTranslator);
QTranslator componentsAppTranslator;
componentsAppTranslator.load(resourcesPath + "/translations/components_" + locale + ".qm");
app.installTranslator(&componentsAppTranslator);
Launcher::MainDialog mainWin(configurationManager);
Launcher::FirstRunDialogResult result = mainWin.showFirstRunDialog();

View file

@ -6,13 +6,13 @@
<rect>
<x>0</x>
<y>0</y>
<width>720</width>
<width>750</width>
<height>635</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>720</width>
<width>750</width>
<height>635</height>
</size>
</property>
@ -148,7 +148,7 @@ QToolButton {
<string>Display</string>
</property>
<property name="toolTip">
<string>Allows to change graphics settings</string>
<string>Allows to change display settings</string>
</property>
</action>
<action name="settingsAction">

View file

@ -36,7 +36,7 @@
<item row="6" column="0">
<widget class="QCheckBox" name="allowNPCToFollowOverWaterSurfaceCheckBox">
<property name="toolTip">
<string>Give actors an ability to swim over the water surface when they follow other actor independently from their ability to swim. Has effect only when nav mesh building is enabled.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Give actors an ability to swim over the water surface when they follow other actor independently from their ability to swim. Has effect only when nav mesh building is enabled.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Always allow actors to follow over water</string>
@ -206,7 +206,7 @@
<item row="5" column="1">
<widget class="QCheckBox" name="classicReflectedAbsorbSpellsCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Effects of reflected Absorb spells are not mirrored -- like in Morrowind.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Effects of reflected Absorb spells are not mirrored - like in Morrowind.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Classic reflected Absorb spells behavior</string>

View file

@ -88,10 +88,6 @@ namespace NavMeshTool
->composing(),
"set fallback BSA archives (later archives have higher priority)");
addOption("resources",
bpo::value<Files::MaybeQuotedPath>()->default_value(Files::MaybeQuotedPath(), "resources"),
"set resources directory");
addOption("content",
bpo::value<StringsVector>()->default_value(StringsVector(), "")->multitoken()->composing(),
"content file(s): esm/esp, or omwgame/omwaddon/omwscripts");

View file

@ -119,8 +119,6 @@ boost::program_options::variables_map CS::Editor::readConfiguration()
boost::program_options::value<Files::MaybeQuotedPathContainer::value_type>()->default_value(
Files::MaybeQuotedPathContainer::value_type(), ""));
addOption("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
addOption("resources",
boost::program_options::value<Files::MaybeQuotedPath>()->default_value(Files::MaybeQuotedPath(), "resources"));
addOption("fallback-archive",
boost::program_options::value<std::vector<std::string>>()
->default_value(std::vector<std::string>(), "fallback-archive")

View file

@ -56,6 +56,7 @@ namespace MWBase
virtual void newGameStarted() = 0;
virtual void gameLoaded() = 0;
virtual void gameEnded() = 0;
virtual void noGame() = 0;
virtual void objectAddedToScene(const MWWorld::Ptr& ptr) = 0;
virtual void objectRemovedFromScene(const MWWorld::Ptr& ptr) = 0;
virtual void objectTeleported(const MWWorld::Ptr& ptr) = 0;

View file

@ -110,7 +110,9 @@ namespace MWBase
virtual bool awarenessCheck(const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) = 0;
/// Makes \a ptr fight \a target. Also shouts a combat taunt.
virtual void startCombat(const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0;
virtual void startCombat(
const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, const std::set<MWWorld::Ptr>* targetAllies)
= 0;
/// Removes an actor and its allies from combat with the actor's targets.
virtual void stopCombat(const MWWorld::Ptr& ptr) = 0;

View file

@ -375,12 +375,18 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons
return mChoice;
case SelectWrapper::Function_AiSetting:
{
int argument = select.getArgument();
if (argument < 0 || argument > 3)
{
throw std::runtime_error("AiSetting index is out of range");
}
return mActor.getClass()
.getCreatureStats(mActor)
.getAiSetting((MWMechanics::AiSetting)select.getArgument())
.getAiSetting(static_cast<MWMechanics::AiSetting>(argument))
.getModified(false);
}
case SelectWrapper::Function_PcAttribute:
{
ESM::RefId attribute = ESM::Attribute::indexToRefId(select.getArgument());

View file

@ -344,18 +344,19 @@ namespace MWLua
[world, context](const sol::object& staticOrID, const osg::Vec3f& worldPos) {
auto model = getStaticModelOrThrow(staticOrID);
context.mLuaManager->addAction(
[world, model, worldPos]() { world->spawnEffect(model, "", worldPos); }, "openmw.vfx.spawn");
[world, model = std::move(model), worldPos]() { world->spawnEffect(model, "", worldPos); },
"openmw.vfx.spawn");
},
[world, context](const sol::object& staticOrID, const osg::Vec3f& worldPos, const sol::table& options) {
auto model = getStaticModelOrThrow(staticOrID);
bool magicVfx = options.get_or("mwMagicVfx", true);
std::string textureOverride = options.get_or<std::string>("particleTextureOverride", "");
std::string texture = options.get_or<std::string>("particleTextureOverride", "");
float scale = options.get_or("scale", 1.f);
context.mLuaManager->addAction(
[world, model, textureOverride, worldPos, scale, magicVfx]() {
world->spawnEffect(model, textureOverride, worldPos, scale, magicVfx);
[world, model = std::move(model), texture = std::move(texture), worldPos, scale, magicVfx]() {
world->spawnEffect(model, texture, worldPos, scale, magicVfx);
},
"openmw.vfx.spawn");
});

View file

@ -1,6 +1,7 @@
#include "corebindings.hpp"
#include <chrono>
#include <stdexcept>
#include <components/debug/debuglog.hpp>
#include <components/esm3/loadfact.hpp>
@ -133,7 +134,14 @@ namespace MWLua
sol::table api(context.mLua->sol(), sol::create);
for (auto& [k, v] : LuaUtil::getMutableFromReadOnly(initCorePackage(context)))
api[k] = v;
api["sendGlobalEvent"] = sol::nil;
api["sendGlobalEvent"] = [context](std::string eventName, const sol::object& eventData) {
if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame)
{
throw std::logic_error("Can't send global events when no game is loaded");
}
context.mLuaEvents->addGlobalEvent(
{ std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) });
};
api["sound"] = sol::nil;
api["vfx"] = sol::nil;
return LuaUtil::makeReadOnly(api);

View file

@ -42,7 +42,7 @@ namespace MWLua
ObjectVariant mObject;
public:
ItemData(ObjectVariant object)
ItemData(const ObjectVariant& object)
: mObject(object)
{
}
@ -116,7 +116,7 @@ namespace MWLua
item["itemData"] = [](const sol::object& object) -> sol::optional<ItemData> {
ObjectVariant o(object);
if (o.ptr().getClass().isItem(o.ptr()) || o.ptr().mRef->getType() == ESM::REC_LIGH)
return ItemData(std::move(o));
return ItemData(o);
return {};
};

View file

@ -379,6 +379,12 @@ namespace MWLua
mMenuScripts.stateChanged();
}
void LuaManager::noGame()
{
clear();
mMenuScripts.stateChanged();
}
void LuaManager::uiModeChanged(const MWWorld::Ptr& arg)
{
if (mPlayer.isEmpty())
@ -643,7 +649,7 @@ namespace MWLua
scripts->setSavedDataDeserializer(mLocalSerializer.get());
ESM::LuaScripts data;
scripts->save(data);
localData[id] = data;
localData[id] = std::move(data);
}
mMenuScripts.removeAllScripts();

View file

@ -70,6 +70,7 @@ namespace MWLua
void newGameStarted() override;
void gameLoaded() override;
void gameEnded() override;
void noGame() override;
void objectAddedToScene(const MWWorld::Ptr& ptr) override;
void objectRemovedFromScene(const MWWorld::Ptr& ptr) override;
void inputEvent(const InputEvent& event) override;

View file

@ -85,7 +85,7 @@ namespace MWLua
}
context.mLuaManager->addAction(
[=] {
[shader, name, values = std::move(values)] {
MWBase::Environment::get().getWorld()->getPostProcessor()->setUniform(shader.mShader, name, values);
},
"SetUniformShaderAction");

View file

@ -274,7 +274,8 @@ namespace MWLua
{
ei = LuaUtil::cast<std::string>(item);
}
context.mLuaManager->addAction([obj = Object(ptr), ei = ei] { setSelectedEnchantedItem(obj.ptr(), ei); },
context.mLuaManager->addAction(
[obj = Object(ptr), ei = std::move(ei)] { setSelectedEnchantedItem(obj.ptr(), ei); },
"setSelectedEnchantedItemAction");
};

View file

@ -117,7 +117,7 @@ namespace MWLua
// The journal mwscript function has a try function here, we will make the lua function throw an
// error. However, the addAction will cause it to error outside of this function.
context.mLuaManager->addAction(
[actor, q, stage] {
[actor = std::move(actor), q, stage] {
MWWorld::Ptr actorPtr;
if (actor)
actorPtr = actor->ptr();

View file

@ -241,7 +241,7 @@ namespace MWLua
for (unsigned i = 0; i < newStack.size(); ++i)
newStack[i] = nameToMode.at(LuaUtil::cast<std::string_view>(modes[i + 1]));
luaManager->addAction(
[windowManager, newStack, arg]() {
[windowManager, newStack = std::move(newStack), arg]() {
MWWorld::Ptr ptr;
if (arg.has_value())
ptr = arg->ptr();
@ -319,7 +319,7 @@ namespace MWLua
};
auto uiLayer = context.mLua->sol().new_usertype<LuaUi::Layer>("UiLayer");
uiLayer["name"] = sol::readonly_property([](LuaUi::Layer& self) { return self.name(); });
uiLayer["name"] = sol::readonly_property([](LuaUi::Layer& self) -> std::string_view { return self.name(); });
uiLayer["size"] = sol::readonly_property([](LuaUi::Layer& self) { return self.size(); });
uiLayer[sol::meta_function::to_string]
= [](LuaUi::Layer& self) { return Misc::StringUtils::format("UiLayer(%s)", self.name()); };

View file

@ -577,8 +577,8 @@ namespace MWMechanics
}
}
void Actors::engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2,
std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr>>& cachedAllies, bool againstPlayer) const
void Actors::engageCombat(
const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, SidingCache& cachedAllies, bool againstPlayer) const
{
// No combat for totally static creatures
if (!actor1.getClass().isMobile(actor1))
@ -606,9 +606,7 @@ namespace MWMechanics
// Get actors allied with actor1. Includes those following or escorting actor1, actors following or escorting
// those actors, (recursive) and any actor currently being followed or escorted by actor1
std::set<MWWorld::Ptr> allies1;
getActorsSidingWith(actor1, allies1, cachedAllies);
const std::set<MWWorld::Ptr>& allies1 = cachedAllies.getActorsSidingWith(actor1);
const auto mechanicsManager = MWBase::Environment::get().getMechanicsManager();
// If an ally of actor1 has been attacked by actor2 or has attacked actor2, start combat between actor1 and
@ -620,7 +618,7 @@ namespace MWMechanics
if (creatureStats2.matchesActorId(ally.getClass().getCreatureStats(ally).getHitAttemptActorId()))
{
mechanicsManager->startCombat(actor1, actor2);
mechanicsManager->startCombat(actor1, actor2, &cachedAllies.getActorsSidingWith(actor2));
// Also set the same hit attempt actor. Otherwise, if fighting the player, they may stop combat
// if the player gets out of reach, while the ally would continue combat with the player
creatureStats1.setHitAttemptActorId(ally.getClass().getCreatureStats(ally).getHitAttemptActorId());
@ -633,9 +631,8 @@ namespace MWMechanics
aggressive = true;
}
std::set<MWWorld::Ptr> playerAllies;
MWWorld::Ptr player = MWMechanics::getPlayer();
getActorsSidingWith(player, playerAllies, cachedAllies);
const std::set<MWWorld::Ptr>& playerAllies = cachedAllies.getActorsSidingWith(player);
bool isPlayerFollowerOrEscorter = playerAllies.find(actor1) != playerAllies.end();
@ -646,20 +643,17 @@ namespace MWMechanics
// Check that actor2 is in combat with actor1
if (creatureStats2.getAiSequence().isInCombat(actor1))
{
std::set<MWWorld::Ptr> allies2;
getActorsSidingWith(actor2, allies2, cachedAllies);
const std::set<MWWorld::Ptr>& allies2 = cachedAllies.getActorsSidingWith(actor2);
// Check that an ally of actor2 is also in combat with actor1
for (const MWWorld::Ptr& ally2 : allies2)
{
if (ally2 != actor2 && ally2.getClass().getCreatureStats(ally2).getAiSequence().isInCombat(actor1))
{
mechanicsManager->startCombat(actor1, actor2);
mechanicsManager->startCombat(actor1, actor2, &allies2);
// Also have actor1's allies start combat
for (const MWWorld::Ptr& ally1 : allies1)
if (ally1 != player)
mechanicsManager->startCombat(ally1, actor2);
mechanicsManager->startCombat(ally1, actor2, &allies2);
return;
}
}
@ -747,7 +741,7 @@ namespace MWMechanics
bool LOS = world->getLOS(actor1, actor2) && mechanicsManager->awarenessCheck(actor2, actor1);
if (LOS)
mechanicsManager->startCombat(actor1, actor2);
mechanicsManager->startCombat(actor1, actor2, &cachedAllies.getActorsSidingWith(actor2));
}
}
@ -1093,7 +1087,7 @@ namespace MWMechanics
}
}
void Actors::updateCrimePursuit(const MWWorld::Ptr& ptr, float duration) const
void Actors::updateCrimePursuit(const MWWorld::Ptr& ptr, float duration, SidingCache& cachedAllies) const
{
const MWWorld::Ptr player = getPlayer();
if (ptr == player)
@ -1133,7 +1127,7 @@ namespace MWMechanics
= esmStore.get<ESM::GameSetting>().find("iCrimeThresholdMultiplier")->mValue.getInteger();
if (playerStats.getBounty() >= cutoff * iCrimeThresholdMultiplier)
{
mechanicsManager->startCombat(ptr, player);
mechanicsManager->startCombat(ptr, player, &cachedAllies.getActorsSidingWith(player));
creatureStats.setHitAttemptActorId(
playerClass.getCreatureStats(player)
.getActorId()); // Stops the guard from quitting combat if player is unreachable
@ -1508,8 +1502,7 @@ namespace MWMechanics
/// \todo move update logic to Actor class where appropriate
std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr>>
cachedAllies; // will be filled as engageCombat iterates
SidingCache cachedAllies{ *this, true }; // will be filled as engageCombat iterates
const bool aiActive = MWBase::Environment::get().getMechanicsManager()->isAIActive();
const int attackedByPlayerId = player.getClass().getCreatureStats(player).getHitAttemptActorId();
@ -1597,7 +1590,7 @@ namespace MWMechanics
updateHeadTracking(actor.getPtr(), mActors, isPlayer, ctrl);
if (actor.getPtr().getClass().isNpc() && !isPlayer)
updateCrimePursuit(actor.getPtr(), duration);
updateCrimePursuit(actor.getPtr(), duration, cachedAllies);
if (!isPlayer)
{
@ -2115,7 +2108,7 @@ namespace MWMechanics
for (const auto& package : stats.getAiSequence())
{
if (excludeInfighting && !sameActor && package->getTypeId() == AiPackageTypeId::Combat
&& package->getTarget() == actorPtr)
&& package->targetIs(actorPtr))
break;
if (package->sideWithTarget() && !package->getTarget().isEmpty())
{
@ -2135,7 +2128,7 @@ namespace MWMechanics
}
list.push_back(package->getTarget());
}
else if (package->getTarget() == actorPtr)
else if (package->targetIs(actorPtr))
{
list.push_back(iteratedActor);
}
@ -2154,7 +2147,7 @@ namespace MWMechanics
std::vector<MWWorld::Ptr> list;
forEachFollowingPackage(
mActors, actorPtr, getPlayer(), [&](const Actor& actor, const std::shared_ptr<AiPackage>& package) {
if (package->followTargetThroughDoors() && package->getTarget() == actorPtr)
if (package->followTargetThroughDoors() && package->targetIs(actorPtr))
list.push_back(actor.getPtr());
else if (package->getTypeId() != AiPackageTypeId::Combat
&& package->getTypeId() != AiPackageTypeId::Wander)
@ -2181,38 +2174,12 @@ namespace MWMechanics
getActorsSidingWith(follower, out, excludeInfighting);
}
void Actors::getActorsSidingWith(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out,
std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr>>& cachedAllies) const
{
// If we have already found actor's allies, use the cache
std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr>>::const_iterator search = cachedAllies.find(actor);
if (search != cachedAllies.end())
out.insert(search->second.begin(), search->second.end());
else
{
for (const MWWorld::Ptr& follower : getActorsSidingWith(actor, true))
if (out.insert(follower).second && follower != actor)
getActorsSidingWith(follower, out, cachedAllies);
// Cache ptrs and their sets of allies
cachedAllies.insert(std::make_pair(actor, out));
for (const MWWorld::Ptr& iter : out)
{
if (iter == actor)
continue;
search = cachedAllies.find(iter);
if (search == cachedAllies.end())
cachedAllies.insert(std::make_pair(iter, out));
}
}
}
std::vector<int> Actors::getActorsFollowingIndices(const MWWorld::Ptr& actor) const
{
std::vector<int> list;
forEachFollowingPackage(
mActors, actor, getPlayer(), [&](const Actor&, const std::shared_ptr<AiPackage>& package) {
if (package->followTargetThroughDoors() && package->getTarget() == actor)
if (package->followTargetThroughDoors() && package->targetIs(actor))
{
list.push_back(static_cast<const AiFollow*>(package.get())->getFollowIndex());
return false;
@ -2230,7 +2197,7 @@ namespace MWMechanics
std::map<int, MWWorld::Ptr> map;
forEachFollowingPackage(
mActors, actor, getPlayer(), [&](const Actor& otherActor, const std::shared_ptr<AiPackage>& package) {
if (package->followTargetThroughDoors() && package->getTarget() == actor)
if (package->followTargetThroughDoors() && package->targetIs(actor))
{
const int index = static_cast<const AiFollow*>(package.get())->getFollowIndex();
map[index] = otherActor.getPtr();
@ -2405,4 +2372,32 @@ namespace MWMechanics
seq.fastForward(ptr);
}
}
const std::set<MWWorld::Ptr>& SidingCache::getActorsSidingWith(const MWWorld::Ptr& actor)
{
// If we have already found actor's allies, use the cache
auto search = mCache.find(actor);
if (search != mCache.end())
return search->second;
std::set<MWWorld::Ptr>& out = mCache[actor];
for (const MWWorld::Ptr& follower : mActors.getActorsSidingWith(actor, mExcludeInfighting))
{
if (out.insert(follower).second && follower != actor)
{
const auto& allies = getActorsSidingWith(follower);
out.insert(allies.begin(), allies.end());
}
}
// Cache ptrs and their sets of allies
for (const MWWorld::Ptr& iter : out)
{
if (iter == actor)
continue;
search = mCache.find(iter);
if (search == mCache.end())
mCache.emplace(iter, out);
}
return out;
}
}

View file

@ -36,6 +36,7 @@ namespace MWMechanics
class Actor;
class CharacterController;
class CreatureStats;
class SidingCache;
class Actors
{
@ -186,7 +187,7 @@ namespace MWMechanics
void calculateRestoration(const MWWorld::Ptr& ptr, float duration) const;
void updateCrimePursuit(const MWWorld::Ptr& ptr, float duration) const;
void updateCrimePursuit(const MWWorld::Ptr& ptr, float duration, SidingCache& cachedAllies) const;
void killDeadActors();
@ -198,13 +199,25 @@ namespace MWMechanics
@Notes: If againstPlayer = true then actor2 should be the Player.
If one of the combatants is creature it should be actor1.
*/
void engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2,
std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr>>& cachedAllies, bool againstPlayer) const;
void engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, SidingCache& cachedAllies,
bool againstPlayer) const;
};
/// Recursive version of getActorsSidingWith that takes, adds to and returns a cache of
/// actors mapped to their allies. Excludes infighting
void getActorsSidingWith(const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out,
std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr>>& cachedAllies) const;
class SidingCache
{
const Actors& mActors;
const bool mExcludeInfighting;
std::map<MWWorld::Ptr, std::set<MWWorld::Ptr>> mCache;
public:
SidingCache(const Actors& actors, bool excludeInfighting)
: mActors(actors)
, mExcludeInfighting(excludeInfighting)
{
}
/// Recursive version of getActorsSidingWith that takes, returns a cached set of allies
const std::set<MWWorld::Ptr>& getActorsSidingWith(const MWWorld::Ptr& actor);
};
}

View file

@ -270,7 +270,15 @@ namespace MWMechanics
{
storage.startCombatMove(isRangedCombat, distToTarget, rangeAttack, actor, target);
// start new attack
storage.startAttackIfReady(actor, characterController, weapon, isRangedCombat);
bool canShout = true;
ESM::RefId spellId = storage.mCurrentAction->getSpell();
if (!spellId.empty())
{
const ESM::Spell* spell = MWBase::Environment::get().getESMStore()->get<ESM::Spell>().find(spellId);
if (spell->mEffects.mList.empty() || spell->mEffects.mList[0].mRange != ESM::RT_Target)
canShout = false;
}
storage.startAttackIfReady(actor, characterController, weapon, isRangedCombat, canShout);
}
// If actor uses custom destination it has to try to rebuild path because environment can change
@ -635,7 +643,7 @@ namespace MWMechanics
}
void AiCombatStorage::startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController,
const ESM::Weapon* weapon, bool distantCombat)
const ESM::Weapon* weapon, bool distantCombat, bool canShout)
{
if (mReadyToAttack && characterController.readyToStartAttack())
{
@ -658,12 +666,15 @@ namespace MWMechanics
baseDelay = store.get<ESM::GameSetting>().find("fCombatDelayNPC")->mValue.getFloat();
}
// Say a provoking combat phrase
const int iVoiceAttackOdds
= store.get<ESM::GameSetting>().find("iVoiceAttackOdds")->mValue.getInteger();
if (Misc::Rng::roll0to99(prng) < iVoiceAttackOdds)
if (canShout)
{
MWBase::Environment::get().getDialogueManager()->say(actor, ESM::RefId::stringRefId("attack"));
// Say a provoking combat phrase
const int iVoiceAttackOdds
= store.get<ESM::GameSetting>().find("iVoiceAttackOdds")->mValue.getInteger();
if (Misc::Rng::roll0to99(prng) < iVoiceAttackOdds)
{
MWBase::Environment::get().getDialogueManager()->say(actor, ESM::RefId::stringRefId("attack"));
}
}
mAttackCooldown = std::min(baseDelay + 0.01 * Misc::Rng::roll0to99(prng), baseDelay + 0.9);
}

View file

@ -64,7 +64,7 @@ namespace MWMechanics
void updateCombatMove(float duration);
void stopCombatMove();
void startAttackIfReady(const MWWorld::Ptr& actor, CharacterController& characterController,
const ESM::Weapon* weapon, bool distantCombat);
const ESM::Weapon* weapon, bool distantCombat, bool canShout);
void updateAttack(const MWWorld::Ptr& actor, CharacterController& characterController);
void stopAttack();

View file

@ -16,6 +16,7 @@ namespace MWMechanics
virtual float getCombatRange(bool& isRanged) const = 0;
virtual float getActionCooldown() const { return 0.f; }
virtual const ESM::Weapon* getWeapon() const { return nullptr; }
virtual ESM::RefId getSpell() const { return {}; }
virtual bool isAttackingOrSpell() const { return true; }
virtual bool isFleeing() const { return false; }
};
@ -43,6 +44,7 @@ namespace MWMechanics
void prepare(const MWWorld::Ptr& actor) override;
float getCombatRange(bool& isRanged) const override;
ESM::RefId getSpell() const override { return mSpellId; }
};
class ActionEnchantedItem : public Action

View file

@ -98,6 +98,26 @@ MWWorld::Ptr MWMechanics::AiPackage::getTarget() const
return mCachedTarget;
}
bool MWMechanics::AiPackage::targetIs(const MWWorld::Ptr& ptr) const
{
if (mTargetActorId == -2)
return ptr.isEmpty();
else if (mTargetActorId == -1)
{
if (mTargetActorRefId.empty())
{
mTargetActorId = -2;
return ptr.isEmpty();
}
if (!ptr.isEmpty() && ptr.getCellRef().getRefId() == mTargetActorRefId)
return getTarget() == ptr;
return false;
}
if (ptr.isEmpty() || !ptr.getClass().isActor())
return false;
return ptr.getClass().getCreatureStats(ptr).getActorId() == mTargetActorId;
}
void MWMechanics::AiPackage::reset()
{
// reset all members

View file

@ -87,6 +87,8 @@ namespace MWMechanics
/// Get the target actor the AI is targeted at (not applicable to all AI packages, default return empty Ptr)
virtual MWWorld::Ptr getTarget() const;
/// Optimized version of getTarget() == ptr
virtual bool targetIs(const MWWorld::Ptr& ptr) const;
/// Get the destination point of the AI package (not applicable to all AI packages, default return (0, 0, 0))
virtual osg::Vec3f getDestination(const MWWorld::Ptr& actor) const { return osg::Vec3f(0, 0, 0); }

View file

@ -176,11 +176,11 @@ namespace MWMechanics
if (!isInCombat())
return false;
for (auto it = mPackages.begin(); it != mPackages.end(); ++it)
for (const auto& package : mPackages)
{
if ((*it)->getTypeId() == AiPackageTypeId::Combat)
if (package->getTypeId() == AiPackageTypeId::Combat)
{
if ((*it)->getTarget() == actor)
if (package->targetIs(actor))
return true;
}
}

View file

@ -132,9 +132,6 @@ namespace MWMechanics
/// Are we in combat with this particular actor?
bool isInCombat(const MWWorld::Ptr& actor) const;
bool canAddTarget(const ESM::Position& actorPos, float distToTarget) const;
///< Function assumes that actor can have only 1 target apart player
/// Removes all combat packages until first non-combat or stack empty.
void stopCombat();

View file

@ -30,6 +30,7 @@
#include "../mwbase/world.hpp"
#include "actor.hpp"
#include "actors.hpp"
#include "actorutil.hpp"
#include "aicombat.hpp"
#include "aipursue.hpp"
@ -1192,9 +1193,9 @@ namespace MWMechanics
&& !victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackageTypeId::Pursue))
reported = reportCrime(player, victim, type, ESM::RefId(), arg);
// TODO: combat should be started with an "unaware" flag, which makes the victim flee?
if (!reported)
startCombat(victim,
player); // TODO: combat should be started with an "unaware" flag, which makes the victim flee?
startCombat(victim, player, &playerFollowers);
}
return crimeSeen;
}
@ -1435,7 +1436,7 @@ namespace MWMechanics
observerStats.modCrimeDispositionModifier(dispositionModifier);
}
startCombat(actor, player);
startCombat(actor, player, &playerFollowers);
// Apply aggression value to the base Fight rating, so that the actor can continue fighting
// after a Calm spell wears off
@ -1486,7 +1487,7 @@ namespace MWMechanics
// Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back.
// Note: accidental or collateral damage attacks are ignored.
if (!victim.getClass().getCreatureStats(victim).getAiSequence().isInPursuit())
startCombat(victim, player);
startCombat(victim, player, &playerFollowers);
// Set the crime ID, which we will use to calm down participants
// once the bounty has been paid.
@ -1531,14 +1532,14 @@ namespace MWMechanics
if (!peaceful)
{
startCombat(target, attacker);
SidingCache cachedAllies{ mActors, false };
const std::set<MWWorld::Ptr>& attackerAllies = cachedAllies.getActorsSidingWith(attacker);
startCombat(target, attacker, &attackerAllies);
// Force friendly actors into combat to prevent infighting between followers
std::set<MWWorld::Ptr> followersTarget;
getActorsSidingWith(target, followersTarget);
for (const auto& follower : followersTarget)
for (const auto& follower : cachedAllies.getActorsSidingWith(target))
{
if (follower != attacker && follower != player)
startCombat(follower, attacker);
startCombat(follower, attacker, &attackerAllies);
}
}
}
@ -1687,7 +1688,8 @@ namespace MWMechanics
}
}
void MechanicsManager::startCombat(const MWWorld::Ptr& ptr, const MWWorld::Ptr& target)
void MechanicsManager::startCombat(
const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, const std::set<MWWorld::Ptr>* targetAllies)
{
CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
@ -1704,6 +1706,31 @@ namespace MWMechanics
return;
}
const bool inCombat = stats.getAiSequence().isInCombat();
bool shout = !inCombat;
if (inCombat)
{
const auto isInCombatWithOneOf = [&](const auto& allies) {
for (const MWWorld::Ptr& ally : allies)
{
if (stats.getAiSequence().isInCombat(ally))
return true;
}
return false;
};
if (targetAllies)
shout = !isInCombatWithOneOf(*targetAllies);
else
{
shout = stats.getAiSequence().isInCombat(target);
if (!shout)
{
std::set<MWWorld::Ptr> sidingActors;
getActorsSidingWith(target, sidingActors);
shout = !isInCombatWithOneOf(sidingActors);
}
}
}
stats.getAiSequence().stack(MWMechanics::AiCombat(target), ptr);
if (target == getPlayer())
{
@ -1738,7 +1765,8 @@ namespace MWMechanics
}
// Must be done after the target is set up, so that CreatureTargetted dialogue filter works properly
MWBase::Environment::get().getDialogueManager()->say(ptr, ESM::RefId::stringRefId("attack"));
if (shout)
MWBase::Environment::get().getDialogueManager()->say(ptr, ESM::RefId::stringRefId("attack"));
}
void MechanicsManager::stopCombat(const MWWorld::Ptr& actor)

View file

@ -107,7 +107,8 @@ namespace MWMechanics
bool awarenessCheck(const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) override;
/// Makes \a ptr fight \a target. Also shouts a combat taunt.
void startCombat(const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) override;
void startCombat(
const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, const std::set<MWWorld::Ptr>* targetAllies) override;
void stopCombat(const MWWorld::Ptr& ptr) override;

View file

@ -507,8 +507,9 @@ namespace MWScript
runtime.pop();
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(targetID, true, false);
if (!target.isEmpty())
MWBase::Environment::get().getMechanicsManager()->startCombat(actor, target);
if (!target.isEmpty() && !target.getBase()->isDeleted()
&& !target.getClass().getCreatureStats(target).isDead())
MWBase::Environment::get().getMechanicsManager()->startCombat(actor, target, nullptr);
}
};

View file

@ -62,16 +62,19 @@ void MWState::StateManager::cleanup(bool force)
MWBase::Environment::get().getInputManager()->clear();
MWBase::Environment::get().getMechanicsManager()->clear();
mState = State_NoGame;
mCharacterManager.setCurrentCharacter(nullptr);
mTimePlayed = 0;
mLastSavegame.clear();
MWMechanics::CreatureStats::cleanup();
endGame();
mState = State_NoGame;
MWBase::Environment::get().getLuaManager()->noGame();
}
else
{
// TODO: do we need this cleanup?
MWBase::Environment::get().getLuaManager()->clear();
}
MWBase::Environment::get().getLuaManager()->clear();
}
std::map<int, int> MWState::StateManager::buildContentFileIndexMap(const ESM::ESMReader& reader) const
@ -693,7 +696,8 @@ void MWState::StateManager::quickLoad()
{
if (currentCharacter->begin() == currentCharacter->end())
return;
loadGame(currentCharacter, currentCharacter->begin()->mPath); // Get newest save
// use requestLoad, otherwise we can crash by loading during the wrong part of the frame
requestLoad(currentCharacter->begin()->mPath);
}
}

View file

@ -35,10 +35,6 @@ namespace OpenMW
bpo::value<StringsVector>()->default_value(StringsVector(), "fallback-archive")->multitoken()->composing(),
"set fallback BSA archives (later archives have higher priority)");
addOption("resources",
bpo::value<Files::MaybeQuotedPath>()->default_value(Files::MaybeQuotedPath(), "resources"),
"set resources directory");
addOption("start", bpo::value<std::string>()->default_value(""), "set initial cell");
addOption("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")->multitoken()->composing(),

View file

@ -871,7 +871,7 @@ namespace
TEST_F(DetourNavigatorNavigatorTest, update_for_very_big_object_should_be_limited)
{
const float size = static_cast<float>(2 * static_cast<std::int64_t>(std::numeric_limits<int>::max()) - 1);
const float size = static_cast<float>((1 << 22) - 1);
CollisionShapeInstance bigBox(std::make_unique<btBoxShape>(btVector3(size, size, 1)));
const ObjectTransform objectTransform{
.mPosition = ESM::Position{ .pos = { 0, 0, 0 }, .rot{ 0, 0, 0 } },

View file

@ -79,6 +79,8 @@ openmw_add_executable(openmw-wizard
${UI_HDRS}
)
add_dependencies(openmw-wizard qm-files)
target_link_libraries(openmw-wizard
components_qt
)

View file

@ -37,14 +37,14 @@ void Wizard::ConclusionPage::initializePage()
if (field(QLatin1String("installation.retailDisc")).toBool() == true)
{
textLabel->setText(
tr("<html><head/><body><p>The OpenMW Wizard successfully installed Morrowind on your computer.</p>"
"<p>Click Finish to close the Wizard.</p></body></html>"));
tr("<html><head/><body><p>The OpenMW Wizard successfully installed Morrowind on your "
"computer.</p></body></html>"));
}
else
{
textLabel->setText(
tr("<html><head/><body><p>The OpenMW Wizard successfully modified your existing Morrowind "
"installation.</p><p>Click Finish to close the Wizard.</p></body></html>"));
"installation.</body></html>"));
}
}
else

View file

@ -1,5 +1,11 @@
#include <QApplication>
#include <QDir>
#include <QTranslator>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <components/files/qtconversion.hpp>
#include "mainwizard.hpp"
@ -11,6 +17,11 @@
int main(int argc, char* argv[])
{
boost::program_options::variables_map variables;
boost::program_options::options_description description;
Files::ConfigurationManager configurationManager;
configurationManager.addCommonOptions(description);
configurationManager.readConfiguration(variables, description, true);
QApplication app(argc, argv);
@ -28,6 +39,23 @@ int main(int argc, char* argv[])
app.setLibraryPaths(libraryPaths);
#endif
QString resourcesPath(".");
if (!variables["resources"].empty())
{
resourcesPath = Files::pathToQString(variables["resources"].as<Files::MaybeQuotedPath>().u8string());
}
// Internationalization
QString locale = QLocale::system().name().section('_', 0, 0);
QTranslator appTranslator;
appTranslator.load(resourcesPath + "/translations/wizard_" + locale + ".qm");
app.installTranslator(&appTranslator);
QTranslator componentsAppTranslator;
componentsAppTranslator.load(resourcesPath + "/translations/components_" + locale + ".qm");
app.installTranslator(&componentsAppTranslator);
Wizard::MainWizard wizard;
wizard.show();

View file

@ -595,6 +595,10 @@ if (USE_QT)
target_link_libraries(components_qt components Qt::Widgets Qt::Core)
target_compile_definitions(components_qt PRIVATE OPENMW_DOC_BASEURL="${OPENMW_DOC_BASEURL}")
if (BUILD_LAUNCHER OR BUILD_WIZARD)
add_dependencies(components_qt qm-files)
endif()
if (BUILD_WITH_CODE_COVERAGE)
target_compile_options(components_qt PRIVATE --coverage)
target_link_libraries(components_qt gcov)

View file

@ -1,23 +1,27 @@
#include <algorithm>
#include <cstring>
#include <errno.h>
#include <filesystem>
#include <fstream>
#include <optional>
#include <span>
#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
#include <sys/ptrace.h>
#include <components/debug/debuglog.hpp>
#include <components/files/conversion.hpp>
#include <SDL_messagebox.h>
@ -59,149 +63,214 @@ static struct
{
int signum;
pid_t pid;
int has_siginfo;
siginfo_t siginfo;
char buf[1024];
std::optional<siginfo_t> siginfo;
} crash_info;
static const struct
namespace
{
const char* name;
int signum;
} signals[] = { { "Segmentation fault", SIGSEGV }, { "Illegal instruction", SIGILL }, { "FPU exception", SIGFPE },
{ "System BUS error", SIGBUS }, { nullptr, 0 } };
constexpr char crash_switch[] = "--cc-handle-crash";
static const struct
{
int code;
const char* name;
} sigill_codes[] = {
struct SignalInfo
{
int mCode;
const char* mDescription;
const char* mName = "";
};
constexpr SignalInfo signals[] = {
{ SIGSEGV, "Segmentation fault", "SIGSEGV" },
{ SIGILL, "Illegal instruction", "SIGILL" },
{ SIGFPE, "FPU exception", "SIGFPE" },
{ SIGBUS, "System BUS error", "SIGBUS" },
{ SIGABRT, "Abnormal termination condition", "SIGABRT" },
};
constexpr SignalInfo sigIllCodes[] = {
#if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
{ ILL_ILLOPC, "Illegal opcode" }, { ILL_ILLOPN, "Illegal operand" }, { ILL_ILLADR, "Illegal addressing mode" },
{ ILL_ILLTRP, "Illegal trap" }, { ILL_PRVOPC, "Privileged opcode" }, { ILL_PRVREG, "Privileged register" },
{ ILL_COPROC, "Coprocessor error" }, { ILL_BADSTK, "Internal stack error" },
{ ILL_ILLOPC, "Illegal opcode" },
{ ILL_ILLOPN, "Illegal operand" },
{ ILL_ILLADR, "Illegal addressing mode" },
{ ILL_ILLTRP, "Illegal trap" },
{ ILL_PRVOPC, "Privileged opcode" },
{ ILL_PRVREG, "Privileged register" },
{ ILL_COPROC, "Coprocessor error" },
{ ILL_BADSTK, "Internal stack error" },
#endif
{ 0, nullptr }
};
};
static const struct
{
int code;
const char* name;
} sigfpe_codes[] = { { FPE_INTDIV, "Integer divide by zero" }, { FPE_INTOVF, "Integer overflow" },
{ FPE_FLTDIV, "Floating point divide by zero" }, { FPE_FLTOVF, "Floating point overflow" },
{ FPE_FLTUND, "Floating point underflow" }, { FPE_FLTRES, "Floating point inexact result" },
{ FPE_FLTINV, "Floating point invalid operation" }, { FPE_FLTSUB, "Subscript out of range" }, { 0, nullptr } };
constexpr SignalInfo sigFpeCodes[] = {
{ FPE_INTDIV, "Integer divide by zero" },
{ FPE_INTOVF, "Integer overflow" },
{ FPE_FLTDIV, "Floating point divide by zero" },
{ FPE_FLTOVF, "Floating point overflow" },
{ FPE_FLTUND, "Floating point underflow" },
{ FPE_FLTRES, "Floating point inexact result" },
{ FPE_FLTINV, "Floating point invalid operation" },
{ FPE_FLTSUB, "Subscript out of range" },
};
static const struct
{
int code;
const char* name;
} sigsegv_codes[] = {
constexpr SignalInfo sigSegvCodes[] = {
#ifndef __FreeBSD__
{ SEGV_MAPERR, "Address not mapped to object" }, { SEGV_ACCERR, "Invalid permissions for mapped object" },
{ SEGV_MAPERR, "Address not mapped to object" },
{ SEGV_ACCERR, "Invalid permissions for mapped object" },
#endif
{ 0, nullptr }
};
};
static const struct
{
int code;
const char* name;
} sigbus_codes[] = {
constexpr SignalInfo sigBusCodes[] = {
#ifndef __FreeBSD__
{ BUS_ADRALN, "Invalid address alignment" }, { BUS_ADRERR, "Non-existent physical address" },
{ BUS_OBJERR, "Object specific hardware error" },
{ BUS_ADRALN, "Invalid address alignment" },
{ BUS_ADRERR, "Non-existent physical address" },
{ BUS_OBJERR, "Object specific hardware error" },
#endif
{ 0, nullptr }
};
};
static int (*cc_user_info)(char*, char*);
const char* findSignalDescription(std::span<const SignalInfo> info, int code)
{
const auto it = std::find_if(info.begin(), info.end(), [&](const SignalInfo& v) { return v.mCode == code; });
return it == info.end() ? "" : it->mDescription;
}
static void gdb_info(pid_t pid)
{
char respfile[64];
FILE* f;
int fd;
struct Close
{
void operator()(const int* value) { close(*value); }
};
struct CloseFile
{
void operator()(FILE* value) { fclose(value); }
};
struct Remove
{
void operator()(const char* value) { remove(value); }
};
template <class T>
bool printDebuggerInfo(pid_t pid)
{
// Create a temp file to put gdb commands into.
// Note: POSIX.1-2008 declares that the file should be already created with mode 0600 by default.
// Modern systems implement it and suggest to do not touch masks in multithreaded applications.
// So CoverityScan warning is valid only for ancient versions of stdlib.
char scriptPath[64];
std::snprintf(scriptPath, sizeof(scriptPath), "/tmp/%s-script-XXXXXX", T::sName);
/*
* Create a temp file to put gdb commands into.
* Note: POSIX.1-2008 declares that the file should be already created with mode 0600 by default.
* Modern systems implement it and suggest to do not touch masks in multithreaded applications.
* So CoverityScan warning is valid only for ancient versions of stdlib.
*/
strcpy(respfile, "/tmp/gdb-respfile-XXXXXX");
#ifdef __COVERITY__
umask(0600);
umask(0600);
#endif
if ((fd = mkstemp(respfile)) >= 0 && (f = fdopen(fd, "w")) != nullptr)
{
fprintf(f,
"attach %d\n"
"shell echo \"\"\n"
"shell echo \"* Loaded Libraries\"\n"
"info sharedlibrary\n"
"shell echo \"\"\n"
"shell echo \"* Threads\"\n"
"info threads\n"
"shell echo \"\"\n"
"shell echo \"* FPU Status\"\n"
"info float\n"
"shell echo \"\"\n"
"shell echo \"* Registers\"\n"
"info registers\n"
"shell echo \"\"\n"
"shell echo \"* Backtrace\"\n"
"thread apply all backtrace full 1000\n"
"detach\n"
"quit\n",
pid);
fclose(f);
/* Run gdb and print process info. */
char cmd_buf[128];
snprintf(cmd_buf, sizeof(cmd_buf), "gdb --quiet --batch --command=%s", respfile);
printf("Executing: %s\n", cmd_buf);
fflush(stdout);
int ret = system(cmd_buf);
if (ret != 0)
printf(
"\nFailed to create a crash report. Please make sure that 'gdb' is installed and present in PATH then "
"crash again."
"\nCurrent PATH: %s\n",
getenv("PATH"));
fflush(stdout);
/* Clean up */
if (remove(respfile) != 0)
Log(Debug::Warning) << "Warning: can not remove file '" << respfile
<< "': " << std::generic_category().message(errno);
}
else
{
/* Error creating temp file */
if (fd >= 0)
const int fd = mkstemp(scriptPath);
if (fd == -1)
{
if (close(fd) != 0)
Log(Debug::Warning) << "Warning: can not close file '" << respfile
<< "': " << std::generic_category().message(errno);
else if (remove(respfile) != 0)
Log(Debug::Warning) << "Warning: can not remove file '" << respfile
<< "': " << std::generic_category().message(errno);
printf("Failed to call mkstemp: %s\n", std::generic_category().message(errno).c_str());
return false;
}
printf("!!! Could not create gdb command file\n");
std::unique_ptr<const char, Remove> tempFile(scriptPath);
std::unique_ptr<const int, Close> scopedFd(&fd);
FILE* const file = fdopen(fd, "w");
if (file == nullptr)
{
printf("Failed to open file for %s output \"%s\": %s\n", T::sName, scriptPath,
std::generic_category().message(errno).c_str());
return false;
}
std::unique_ptr<FILE, CloseFile> scopedFile(file);
if (fprintf(file, "%s", T::sScript) < 0)
{
printf("Failed to write debugger script to file \"%s\": %s\n", scriptPath,
std::generic_category().message(errno).c_str());
return false;
}
scopedFile = nullptr;
scopedFd = nullptr;
char command[128];
snprintf(command, sizeof(command), T::sCommandTemplate, pid, scriptPath);
printf("Executing: %s\n", command);
fflush(stdout);
const int ret = system(command);
const bool result = (ret == 0);
if (ret == -1)
printf(
"\nFailed to create a crash report: %s.\n"
"Please make sure that '%s' is installed and present in PATH then crash again.\n"
"Current PATH: %s\n",
T::sName, std::generic_category().message(errno).c_str(), getenv("PATH"));
else if (ret != 0)
printf(
"\nFailed to create a crash report.\n"
"Please make sure that '%s' is installed and present in PATH then crash again.\n"
"Current PATH: %s\n",
T::sName, getenv("PATH"));
fflush(stdout);
return result;
}
struct Gdb
{
static constexpr char sName[] = "gdb";
static constexpr char sScript[] = R"(shell echo ""
shell echo "* Loaded Libraries"
info sharedlibrary
shell echo ""
shell echo "* Threads"
info threads
shell echo ""
shell echo "* FPU Status"
info float
shell echo ""
shell echo "* Registers"
info registers
shell echo ""
shell echo "* Backtrace"
thread apply all backtrace full 1000
detach
quit
)";
static constexpr char sCommandTemplate[] = "gdb --pid %d --quiet --batch --command %s";
};
struct Lldb
{
static constexpr char sName[] = "lldb";
static constexpr char sScript[] = R"(script print("\n* Loaded Libraries")
image list
script print('\n* Threads')
thread list
script print('\n* Registers')
register read --all
script print('\n* Backtrace')
script print(''.join(f'{t}\n' + ''.join(''.join([f' {f}\n', ''.join(f' {s}\n' for s in f.statics), ''.join(f' {v}\n' for v in f.variables)]) for f in t.frames) for t in lldb.process.threads))
detach
quit
)";
static constexpr char sCommandTemplate[] = "lldb --attach-pid %d --batch --source %s";
};
void printProcessInfo(pid_t pid)
{
if (printDebuggerInfo<Gdb>(pid))
return;
if (printDebuggerInfo<Lldb>(pid))
return;
}
fflush(stdout);
}
static void sys_info(void)
static void printSystemInfo(void)
{
#ifdef __unix__
struct utsname info;
if (uname(&info))
printf("!!! Failed to get system information\n");
if (uname(&info) == -1)
printf("Failed to get system information: %s\n", std::generic_category().message(errno).c_str());
else
printf("System: %s %s %s %s %s\n", info.sysname, info.nodename, info.release, info.version, info.machine);
@ -214,8 +283,8 @@ static size_t safe_write(int fd, const void* buf, size_t len)
size_t ret = 0;
while (ret < len)
{
ssize_t rem;
if ((rem = write(fd, (const char*)buf + ret, len - ret)) == -1)
const ssize_t rem = write(fd, (const char*)buf + ret, len - ret);
if (rem == -1)
{
if (errno == EINTR)
continue;
@ -226,12 +295,8 @@ static size_t safe_write(int fd, const void* buf, size_t len)
return ret;
}
static void crash_catcher(int signum, siginfo_t* siginfo, void* context)
static void crash_catcher(int signum, siginfo_t* siginfo, void* /*context*/)
{
// ucontext_t *ucontext = (ucontext_t*)context;
pid_t dbg_pid;
int fd[2];
/* Make sure the effective uid is the real uid */
if (getuid() != geteuid())
{
@ -240,6 +305,7 @@ static void crash_catcher(int signum, siginfo_t* siginfo, void* context)
}
safe_write(STDERR_FILENO, fatal_err, sizeof(fatal_err) - 1);
int fd[2];
if (pipe(fd) == -1)
{
safe_write(STDERR_FILENO, pipe_err, sizeof(pipe_err) - 1);
@ -249,14 +315,14 @@ static void crash_catcher(int signum, siginfo_t* siginfo, void* context)
crash_info.signum = signum;
crash_info.pid = getpid();
crash_info.has_siginfo = !!siginfo;
if (siginfo)
if (siginfo == nullptr)
crash_info.siginfo = std::nullopt;
else
crash_info.siginfo = *siginfo;
if (cc_user_info)
cc_user_info(crash_info.buf, crash_info.buf + sizeof(crash_info.buf));
const pid_t dbg_pid = fork();
/* Fork off to start a crash handler */
switch ((dbg_pid = fork()))
switch (dbg_pid)
{
/* Error */
case -1:
@ -296,110 +362,68 @@ static void crash_catcher(int signum, siginfo_t* siginfo, void* context)
}
}
static void crash_handler(const char* logfile)
[[noreturn]] static void handleCrash(const char* logfile)
{
const char* sigdesc = "";
int i;
if (fread(&crash_info, sizeof(crash_info), 1, stdin) != 1)
{
fprintf(stderr, "!!! Failed to retrieve info from crashed process\n");
fprintf(stderr, "Failed to retrieve info from crashed process: %s\n",
std::generic_category().message(errno).c_str());
exit(1);
}
/* Get the signal description */
for (i = 0; signals[i].name; ++i)
{
if (signals[i].signum == crash_info.signum)
{
sigdesc = signals[i].name;
break;
}
}
const char* sigdesc = findSignalDescription(signals, crash_info.signum);
if (crash_info.has_siginfo)
if (crash_info.siginfo.has_value())
{
switch (crash_info.signum)
{
case SIGSEGV:
for (i = 0; sigsegv_codes[i].name; ++i)
{
if (sigsegv_codes[i].code == crash_info.siginfo.si_code)
{
sigdesc = sigsegv_codes[i].name;
break;
}
}
sigdesc = findSignalDescription(sigSegvCodes, crash_info.siginfo->si_code);
break;
case SIGFPE:
for (i = 0; sigfpe_codes[i].name; ++i)
{
if (sigfpe_codes[i].code == crash_info.siginfo.si_code)
{
sigdesc = sigfpe_codes[i].name;
break;
}
}
sigdesc = findSignalDescription(sigFpeCodes, crash_info.siginfo->si_code);
break;
case SIGILL:
for (i = 0; sigill_codes[i].name; ++i)
{
if (sigill_codes[i].code == crash_info.siginfo.si_code)
{
sigdesc = sigill_codes[i].name;
break;
}
}
sigdesc = findSignalDescription(sigIllCodes, crash_info.siginfo->si_code);
break;
case SIGBUS:
for (i = 0; sigbus_codes[i].name; ++i)
{
if (sigbus_codes[i].code == crash_info.siginfo.si_code)
{
sigdesc = sigbus_codes[i].name;
break;
}
}
sigdesc = findSignalDescription(sigBusCodes, crash_info.siginfo->si_code);
break;
}
}
fprintf(stderr, "%s (signal %i)\n", sigdesc, crash_info.signum);
if (crash_info.has_siginfo)
fprintf(stderr, "Address: %p\n", crash_info.siginfo.si_addr);
if (crash_info.siginfo.has_value())
fprintf(stderr, "Address: %p\n", crash_info.siginfo->si_addr);
fputc('\n', stderr);
if (logfile)
/* Create crash log file and redirect shell output to it */
if (freopen(logfile, "wa", stdout) != stdout)
{
/* Create crash log file and redirect shell output to it */
if (freopen(logfile, "wa", stdout) != stdout)
{
fprintf(stderr, "!!! Could not create %s following signal\n", logfile);
exit(1);
}
fprintf(stderr, "Generating %s and killing process %d, please wait... ", logfile, crash_info.pid);
printf(
"*** Fatal Error ***\n"
"%s (signal %i)\n",
sigdesc, crash_info.signum);
if (crash_info.has_siginfo)
printf("Address: %p\n", crash_info.siginfo.si_addr);
fputc('\n', stdout);
fflush(stdout);
fprintf(stderr, "Could not create %s following signal: %s\n", logfile,
std::generic_category().message(errno).c_str());
exit(1);
}
fprintf(stderr, "Generating %s and killing process %d, please wait... ", logfile, crash_info.pid);
sys_info();
printf(
"*** Fatal Error ***\n"
"%s (signal %i)\n",
sigdesc, crash_info.signum);
if (crash_info.siginfo.has_value())
printf("Address: %p\n", crash_info.siginfo->si_addr);
fputc('\n', stdout);
fflush(stdout);
printSystemInfo();
crash_info.buf[sizeof(crash_info.buf) - 1] = '\0';
printf("%s\n", crash_info.buf);
fflush(stdout);
if (crash_info.pid > 0)
{
gdb_info(crash_info.pid);
printProcessInfo(crash_info.pid);
kill(crash_info.pid, SIGKILL);
}
@ -408,12 +432,9 @@ static void crash_handler(const char* logfile)
// even faulty applications shouldn't be able to freeze the X server.
usleep(100000);
if (logfile)
{
std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(logfile)
+ "'.\n Please report this to https://gitlab.com/OpenMW/openmw/issues !";
SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr);
}
const std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(logfile)
+ "'.\n Please report this to https://gitlab.com/OpenMW/openmw/issues !";
SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr);
exit(0);
}
@ -426,13 +447,16 @@ static void getExecPath(char** argv)
if (sysctl(mib, 4, argv0, &size, nullptr, 0) == 0)
return;
Log(Debug::Warning) << "Failed to call sysctl: " << std::generic_category().message(errno);
#endif
#if defined(__APPLE__)
if (proc_pidpath(getpid(), argv0, sizeof(argv0)) > 0)
return;
Log(Debug::Warning) << "Failed to call proc_pidpath: " << std::generic_category().message(errno);
#endif
int cwdlen;
const char* statusPaths[] = { "/proc/self/exe", "/proc/self/file", "/proc/curproc/exe", "/proc/curproc/file" };
memset(argv0, 0, sizeof(argv0));
@ -440,60 +464,83 @@ static void getExecPath(char** argv)
{
if (readlink(path, argv0, sizeof(argv0)) != -1)
return;
Log(Debug::Warning) << "Failed to call readlink for \"" << path
<< "\": " << std::generic_category().message(errno);
}
if (argv[0][0] == '/')
snprintf(argv0, sizeof(argv0), "%s", argv[0]);
else if (getcwd(argv0, sizeof(argv0)) != nullptr)
{
cwdlen = strlen(argv0);
snprintf(argv0 + cwdlen, sizeof(argv0) - cwdlen, "/%s", argv[0]);
snprintf(argv0, sizeof(argv0), "%s", argv[0]);
return;
}
if (getcwd(argv0, sizeof(argv0)) == nullptr)
{
Log(Debug::Error) << "Failed to call getcwd: " << std::generic_category().message(errno);
return;
}
const int cwdlen = strlen(argv0);
snprintf(argv0 + cwdlen, sizeof(argv0) - cwdlen, "/%s", argv[0]);
}
int crashCatcherInstallHandlers(
int argc, char** argv, int num_signals, int* signals, const char* logfile, int (*user_info)(char*, char*))
static bool crashCatcherInstallHandlers(char** argv)
{
struct sigaction sa;
stack_t altss;
int retval;
if (argc == 2 && strcmp(argv[1], crash_switch) == 0)
crash_handler(logfile);
cc_user_info = user_info;
getExecPath(argv);
/* Set an alternate signal stack so SIGSEGVs caused by stack overflows
* still run */
static char* altstack = new char[SIGSTKSZ];
stack_t altss;
altss.ss_sp = altstack;
altss.ss_flags = 0;
altss.ss_size = SIGSTKSZ;
sigaltstack(&altss, nullptr);
if (sigaltstack(&altss, nullptr) == -1)
{
Log(Debug::Error) << "Failed to call sigaltstack: " << std::generic_category().message(errno);
return false;
}
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = crash_catcher;
sa.sa_flags = SA_RESETHAND | SA_NODEFER | SA_SIGINFO | SA_ONSTACK;
sigemptyset(&sa.sa_mask);
retval = 0;
while (num_signals--)
if (sigemptyset(&sa.sa_mask) == -1)
{
if ((*signals != SIGSEGV && *signals != SIGILL && *signals != SIGFPE && *signals != SIGABRT
&& *signals != SIGBUS)
|| sigaction(*signals, &sa, nullptr) == -1)
{
*signals = 0;
retval = -1;
}
++signals;
Log(Debug::Error) << "Failed to call sigemptyset: " << std::generic_category().message(errno);
return false;
}
return retval;
for (const SignalInfo& signal : signals)
{
if (sigaction(signal.mCode, &sa, nullptr) == -1)
{
Log(Debug::Error) << "Failed to call sigaction for signal " << signal.mName << " (" << signal.mCode
<< "): " << std::generic_category().message(errno);
return false;
}
}
return true;
}
static bool is_debugger_present()
namespace
{
#if defined(__APPLE__)
bool isDebuggerPresent(const auto& info)
{
return (info.kp_proc.p_flag & P_TRACED) != 0;
}
#elif defined(__FreeBSD__)
bool isDebuggerPresent(const auto& info)
{
return (info.ki_flag & P_TRACED) != 0;
}
#endif
}
static bool isDebuggerPresent()
{
#if defined(__linux__)
std::filesystem::path procstatus = std::filesystem::path("/proc/self/status");
@ -512,44 +559,23 @@ static bool is_debugger_present()
}
}
return false;
#elif defined(__APPLE__)
int junk;
int mib[4];
#elif defined(__APPLE__) || defined(__FreeBSD__)
struct kinfo_proc info;
size_t size;
// Initialize the flags so that, if sysctl fails for some bizarre
// reason, we get a predictable result.
info.kp_proc.p_flag = 0;
std::memset(&info, 0, sizeof(info));
// Initialize mib, which tells sysctl the info we want, in this case
// we're looking for information about a specific process ID.
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
// Call sysctl.
size = sizeof(info);
junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0);
assert(junk == 0);
// We're being debugged if the P_TRACED flag is set.
return (info.kp_proc.p_flag & P_TRACED) != 0;
#elif defined(__FreeBSD__)
struct kinfo_proc info;
size_t size = sizeof(info);
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
if (sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) == 0)
return (info.ki_flag & P_TRACED) != 0;
else
perror("Failed to retrieve process info");
return false;
size_t size = sizeof(info);
if (sysctl(mib, std::size(mib), &info, &size, nullptr, 0) == -1)
{
Log(Debug::Warning) << "Failed to call sysctl, assuming no debugger: "
<< std::generic_category().message(errno);
return false;
}
return isDebuggerPresent(info);
#else
return false;
#endif
@ -557,14 +583,17 @@ static bool is_debugger_present()
void crashCatcherInstall(int argc, char** argv, const std::filesystem::path& crashLogPath)
{
if ((argc == 2 && strcmp(argv[1], crash_switch) == 0) || !is_debugger_present())
{
int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT };
if (crashCatcherInstallHandlers(argc, argv, 5, s, crashLogPath.c_str(), nullptr) == -1)
{
Log(Debug::Warning) << "Installing crash handler failed";
}
else
Log(Debug::Info) << "Crash handler installed";
}
#if (defined(__APPLE__) || (defined(__linux) && !defined(ANDROID)) || (defined(__unix) && !defined(ANDROID)) \
|| defined(__posix))
if (argc == 2 && strcmp(argv[1], crash_switch) == 0)
handleCrash(Files::pathToUnicodeString(crashLogPath).c_str());
if (isDebuggerPresent())
return;
if (crashCatcherInstallHandlers(argv))
Log(Debug::Info) << "Crash handler installed";
else
Log(Debug::Warning) << "Installing crash handler failed";
#endif
}

View file

@ -2,21 +2,7 @@
#define CRASHCATCHER_H
#include <filesystem>
#include <string>
#if (defined(__APPLE__) || (defined(__linux) && !defined(ANDROID)) || (defined(__unix) && !defined(ANDROID)) \
|| defined(__posix))
#define USE_CRASH_CATCHER 1
#else
#define USE_CRASH_CATCHER 0
#endif
constexpr char crash_switch[] = "--cc-handle-crash";
#if USE_CRASH_CATCHER
extern void crashCatcherInstall(int argc, char** argv, const std::filesystem::path& crashLogPath);
#else
inline void crashCatcherInstall(int, char**, const std::string& crashLogPath) {}
#endif
void crashCatcherInstall(int argc, char** argv, const std::filesystem::path& crashLogPath);
#endif

View file

@ -352,8 +352,7 @@ int wrapApplication(int (*innerApplication)(int argc, char* argv[]), int argc, c
#else
const std::string crashLogName = Misc::StringUtils::lowerCase(appName) + "-crash.log";
// install the crash handler as soon as possible.
crashCatcherInstall(
argc, argv, Files::pathToUnicodeString(std::filesystem::temp_directory_path() / crashLogName));
crashCatcherInstall(argc, argv, std::filesystem::temp_directory_path() / crashLogName);
#endif
ret = innerApplication(argc, argv);
}

View file

@ -186,16 +186,35 @@ namespace DetourNavigator
&context, solid, width, height, bmin.ptr(), bmax.ptr(), settings.mCellSize, settings.mCellHeight);
}
bool isSupportedCoordinate(float value)
{
constexpr float maxVertexCoordinate = static_cast<float>(1 << 22);
return -maxVertexCoordinate < value && value < maxVertexCoordinate;
}
template <class Iterator>
bool isSupportedCoordinates(Iterator begin, Iterator end)
{
return std::all_of(begin, end, isSupportedCoordinate);
}
[[nodiscard]] bool rasterizeTriangles(RecastContext& context, const Mesh& mesh, const RecastSettings& settings,
const RecastParams& params, rcHeightfield& solid)
{
std::vector<unsigned char> areas(mesh.getAreaTypes().begin(), mesh.getAreaTypes().end());
std::vector<float> vertices = mesh.getVertices();
for (std::size_t i = 0; i < vertices.size(); i += 3)
constexpr std::size_t verticesPerTriangle = 3;
for (std::size_t i = 0; i < vertices.size(); i += verticesPerTriangle)
{
for (std::size_t j = 0; j < 3; ++j)
vertices[i + j] = toNavMeshCoordinates(settings, vertices[i + j]);
for (std::size_t j = 0; j < verticesPerTriangle; ++j)
{
const float coordinate = toNavMeshCoordinates(settings, vertices[i + j]);
if (!isSupportedCoordinate(coordinate))
return false;
vertices[i + j] = coordinate;
}
std::swap(vertices[i + 1], vertices[i + 2]);
}
@ -217,6 +236,9 @@ namespace DetourNavigator
rectangle.mBounds.mMax.x(), rectangle.mHeight, rectangle.mBounds.mMin.y(), // vertex 3
};
if (!isSupportedCoordinates(vertices.begin(), vertices.end()))
return false;
const std::array indices{
0, 1, 2, // triangle 0
0, 2, 3, // triangle 1

View file

@ -194,6 +194,10 @@ namespace Files
"instead of being appended");
addOption("user-data", bpo::value<Files::MaybeQuotedPath>()->default_value(Files::MaybeQuotedPath(), ""),
"set user data directory (used for saves, screenshots, etc)");
addOption("resources",
boost::program_options::value<Files::MaybeQuotedPath>()->default_value(
Files::MaybeQuotedPath(), "resources"),
"set resources directory");
}
bpo::variables_map separateComposingVariables(

View file

@ -421,7 +421,6 @@ local menuGroups = {}
local menuPages = {}
local function resetPlayerGroups()
print('MENU reset player groups')
local playerGroupsSection = storage.playerSection(common.groupSectionKey)
for pageKey, page in pairs(groups) do
for groupKey, group in pairs(page) do
@ -503,7 +502,7 @@ return {
if menu.getState() == menu.STATE.Running then
updatePlayerGroups()
updateGlobalGroups()
else
elseif menu.getState() == menu.STATE.NoGame then
resetPlayerGroups()
end
end,

View file

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="de_DE">
<context>
<name>ContentSelector</name>
<message>
<source>Select language used by ESM/ESP content files to allow OpenMW to detect their encoding. </source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ContentSelectorModel::ContentModel</name>
<message>
<source>Unable to find dependent file: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dependent file needs to be active: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This file needs to load after %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ContentSelectorModel::EsmFile</name>
<message>
<source>&lt;b&gt;Author:&lt;/b&gt; %1&lt;br/&gt;&lt;b&gt;Format version:&lt;/b&gt; %2&lt;br/&gt;&lt;b&gt;Modified:&lt;/b&gt; %3&lt;br/&gt;&lt;b&gt;Path:&lt;/b&gt;&lt;br/&gt;%4&lt;br/&gt;&lt;br/&gt;&lt;b&gt;Description:&lt;/b&gt;&lt;br/&gt;%5&lt;br/&gt;&lt;br/&gt;&lt;b&gt;Dependencies: &lt;/b&gt;%6&lt;br/&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ContentSelectorView::ContentSelector</name>
<message>
<source>&amp;Check Selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Uncheck Selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Copy Path(s) to Clipboard</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;No game file&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Process::ProcessInvoker</name>
<message>
<source>Error starting executable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Error running executable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>
Arguments:
</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not find %1&lt;/b&gt;&lt;/p&gt;&lt;p&gt;The application is not found.&lt;/p&gt;&lt;p&gt;Please make sure OpenMW is installed correctly and try again.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not start %1&lt;/b&gt;&lt;/p&gt;&lt;p&gt;The application is not executable.&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not start %1&lt;/b&gt;&lt;/p&gt;&lt;p&gt;An error occurred while starting %1.&lt;/p&gt;&lt;p&gt;Press &quot;Show Details...&quot; for more information.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Executable %1 returned an error&lt;/b&gt;&lt;/p&gt;&lt;p&gt;An error occurred while running %1.&lt;/p&gt;&lt;p&gt;Press &quot;Show Details...&quot; for more information.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

View file

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="fr_FR">
<context>
<name>ContentSelector</name>
<message>
<source>Select language used by ESM/ESP content files to allow OpenMW to detect their encoding. </source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ContentSelectorModel::ContentModel</name>
<message>
<source>Unable to find dependent file: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Dependent file needs to be active: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This file needs to load after %1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ContentSelectorModel::EsmFile</name>
<message>
<source>&lt;b&gt;Author:&lt;/b&gt; %1&lt;br/&gt;&lt;b&gt;Format version:&lt;/b&gt; %2&lt;br/&gt;&lt;b&gt;Modified:&lt;/b&gt; %3&lt;br/&gt;&lt;b&gt;Path:&lt;/b&gt;&lt;br/&gt;%4&lt;br/&gt;&lt;br/&gt;&lt;b&gt;Description:&lt;/b&gt;&lt;br/&gt;%5&lt;br/&gt;&lt;br/&gt;&lt;b&gt;Dependencies: &lt;/b&gt;%6&lt;br/&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ContentSelectorView::ContentSelector</name>
<message>
<source>&amp;Check Selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Uncheck Selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Copy Path(s) to Clipboard</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;No game file&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Process::ProcessInvoker</name>
<message>
<source>Error starting executable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Error running executable</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>
Arguments:
</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not find %1&lt;/b&gt;&lt;/p&gt;&lt;p&gt;The application is not found.&lt;/p&gt;&lt;p&gt;Please make sure OpenMW is installed correctly and try again.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not start %1&lt;/b&gt;&lt;/p&gt;&lt;p&gt;The application is not executable.&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not start %1&lt;/b&gt;&lt;/p&gt;&lt;p&gt;An error occurred while starting %1.&lt;/p&gt;&lt;p&gt;Press &quot;Show Details...&quot; for more information.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Executable %1 returned an error&lt;/b&gt;&lt;/p&gt;&lt;p&gt;An error occurred while running %1.&lt;/p&gt;&lt;p&gt;Press &quot;Show Details...&quot; for more information.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

View file

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="ru_RU">
<context>
<name>ContentSelector</name>
<message>
<source>Select language used by ESM/ESP content files to allow OpenMW to detect their encoding. </source>
<translation>Выберите язык, используемый вашими файлами данных ESM/ESP, чтобы позволить OpenMW определить их кодировку. </translation>
</message>
</context>
<context>
<name>ContentSelectorModel::ContentModel</name>
<message>
<source>Unable to find dependent file: %1</source>
<translation>Зависимый файл не найден: %1</translation>
</message>
<message>
<source>Dependent file needs to be active: %1</source>
<translation>Зависимый файл должен быть включен: %1</translation>
</message>
<message>
<source>This file needs to load after %1</source>
<translation>Этот файл должен быть загружен после %1</translation>
</message>
</context>
<context>
<name>ContentSelectorModel::EsmFile</name>
<message>
<source>&lt;b&gt;Author:&lt;/b&gt; %1&lt;br/&gt;&lt;b&gt;Format version:&lt;/b&gt; %2&lt;br/&gt;&lt;b&gt;Modified:&lt;/b&gt; %3&lt;br/&gt;&lt;b&gt;Path:&lt;/b&gt;&lt;br/&gt;%4&lt;br/&gt;&lt;br/&gt;&lt;b&gt;Description:&lt;/b&gt;&lt;br/&gt;%5&lt;br/&gt;&lt;br/&gt;&lt;b&gt;Dependencies: &lt;/b&gt;%6&lt;br/&gt;</source>
<translation>&lt;b&gt;Автор:&lt;/b&gt; %1&lt;br/&gt;&lt;b&gt;Версия формата данных:&lt;/b&gt; %2&lt;br/&gt;&lt;b&gt;Дата изменения:&lt;/b&gt; %3&lt;br/&gt;&lt;b&gt;Путь к файлу:&lt;/b&gt;&lt;br/&gt;%4&lt;br/&gt;&lt;br/&gt;&lt;b&gt;Описание:&lt;/b&gt;&lt;br/&gt;%5&lt;br/&gt;&lt;br/&gt;&lt;b&gt;Зависимости: &lt;/b&gt;%6&lt;br/&gt;</translation>
</message>
</context>
<context>
<name>ContentSelectorView::ContentSelector</name>
<message>
<source>&amp;Check Selected</source>
<translation>&amp;Включить выбранное</translation>
</message>
<message>
<source>&amp;Uncheck Selected</source>
<translation>&amp;Отключить выбранное</translation>
</message>
<message>
<source>&amp;Copy Path(s) to Clipboard</source>
<translation>&amp;Скопировать пути в буфер обмена</translation>
</message>
<message>
<source>&lt;No game file&gt;</source>
<translation>&lt;Без файла игры&gt;</translation>
</message>
</context>
<context>
<name>Process::ProcessInvoker</name>
<message>
<source>Error starting executable</source>
<translation>Не удалось запустить исполняемый файл</translation>
</message>
<message>
<source>Error running executable</source>
<translation>При исполнении приложения произошла ошибка</translation>
</message>
<message>
<source>
Arguments:
</source>
<translation>
Параметры:
</translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not find %1&lt;/b&gt;&lt;/p&gt;&lt;p&gt;The application is not found.&lt;/p&gt;&lt;p&gt;Please make sure OpenMW is installed correctly and try again.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Не удалось найти %1&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Приложение не найдено.&lt;/p&gt;&lt;p&gt;Пожалуйста, убедитесь, что OpenMW установлен правильно, и повторите попытку.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not start %1&lt;/b&gt;&lt;/p&gt;&lt;p&gt;The application is not executable.&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Не удалось запустить %1&lt;/b&gt;&lt;/p&gt;&lt;p&gt;У приложения нет прав на исполнение.&lt;/p&gt;&lt;p&gt;Пожалуйста, проверьте права доступа и повторите попытку.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not start %1&lt;/b&gt;&lt;/p&gt;&lt;p&gt;An error occurred while starting %1.&lt;/p&gt;&lt;p&gt;Press &quot;Show Details...&quot; for more information.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Не удалось запустить %1&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Возникла ошибка при запуске %1.&lt;/p&gt;&lt;p&gt;Нажмите &quot;Показать детали...&quot; для получения дополнительной информации.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Executable %1 returned an error&lt;/b&gt;&lt;/p&gt;&lt;p&gt;An error occurred while running %1.&lt;/p&gt;&lt;p&gt;Press &quot;Show Details...&quot; for more information.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Исполнение файла %1 завершилось с ошибкой&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Возникла ошибка при выполнении %1.&lt;/p&gt;&lt;p&gt;Нажмите &quot;Показать детали...&quot; для получения дополнительной информации.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
</context>
</TS>

1451
files/lang/launcher_de.ts Normal file

File diff suppressed because it is too large Load diff

1451
files/lang/launcher_fr.ts Normal file

File diff suppressed because it is too large Load diff

1466
files/lang/launcher_ru.ts Normal file

File diff suppressed because it is too large Load diff

858
files/lang/wizard_de.ts Normal file
View file

@ -0,0 +1,858 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="de_DE">
<context>
<name>ComponentSelectionPage</name>
<message>
<location filename="../../apps/wizard/ui/componentselectionpage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/componentselectionpage.ui" line="17"/>
<source>Select Components</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/componentselectionpage.ui" line="20"/>
<source>Which components should be installed?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/componentselectionpage.ui" line="26"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select which official Morrowind expansions should be installed. For best results, it is recommended to have both expansions installed.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:bold;&quot;&gt;Note:&lt;/span&gt; It is possible to install expansions later by re-running this Wizard.&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/componentselectionpage.ui" line="36"/>
<source>Selected components:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ConclusionPage</name>
<message>
<location filename="../../apps/wizard/ui/conclusionpage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/conclusionpage.ui" line="17"/>
<source>Completing the OpenMW Wizard</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/conclusionpage.ui" line="23"/>
<source>Placeholder</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ExistingInstallationPage</name>
<message>
<location filename="../../apps/wizard/ui/existinginstallationpage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/existinginstallationpage.ui" line="17"/>
<source>Select Existing Installation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/existinginstallationpage.ui" line="20"/>
<source>Select an existing installation for OpenMW to use or modify.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/existinginstallationpage.ui" line="26"/>
<source>Detected installations:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/existinginstallationpage.ui" line="51"/>
<source>Browse...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ImportPage</name>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="17"/>
<source>Import Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="20"/>
<source>Import settings from the Morrowind installation.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="26"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;OpenMW needs to import settings from the Morrowind configuration file in order to function properly.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:bold;&quot;&gt;Note:&lt;/span&gt; It is possible to import settings later by re-running this Wizard.&lt;/p&gt;&lt;p/&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="36"/>
<source>Import settings from Morrowind.ini</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="46"/>
<source>Import add-on and plugin selection</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="56"/>
<source>Import bitmap fonts setup from Morrowind.ini</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="59"/>
<source>Fonts shipped with the original engine are blurry with UI scaling and support only a small amount of characters,
so OpenMW provides another set of fonts to avoid these issues. These fonts use TrueType technology and are quite similar
to default Morrowind fonts. Check this box if you still prefer original fonts over OpenMW ones or if you use custom bitmap fonts.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>InstallationPage</name>
<message>
<location filename="../../apps/wizard/ui/installationpage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationpage.ui" line="17"/>
<source>Installing</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationpage.ui" line="20"/>
<source>Please wait while Morrowind is installed on your computer.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>InstallationTargetPage</name>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="17"/>
<source>Select Installation Destination</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="20"/>
<source>Where should Morrowind be installed?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="34"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/folder.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="47"/>
<source>Morrowind will be installed to the following location. </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="64"/>
<source>Browse...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>IntroPage</name>
<message>
<location filename="../../apps/wizard/ui/intropage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/intropage.ui" line="17"/>
<source>Welcome to the OpenMW Wizard</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/intropage.ui" line="23"/>
<source>This Wizard will help you install Morrowind and its add-ons for OpenMW to use.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>LanguageSelectionPage</name>
<message>
<location filename="../../apps/wizard/ui/languageselectionpage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/languageselectionpage.ui" line="17"/>
<source>Select Morrowind Language</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/languageselectionpage.ui" line="20"/>
<source>What is the language of the Morrowind installation?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/languageselectionpage.ui" line="34"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/preferences-desktop-locale.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/languageselectionpage.ui" line="47"/>
<source>Select the language of the Morrowind installation.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MethodSelectionPage</name>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="17"/>
<source>Select Installation Method</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="20"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select how you would like to install &lt;i&gt;The Elder Scrolls III: Morrowind&lt;/i&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="29"/>
<source>Retail CD/DVD</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="63"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/system-installer.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="82"/>
<source>Install from a retail disc to a new location.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="97"/>
<source>Existing Installation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="128"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/folder.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="141"/>
<source>Select an existing installation.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="162"/>
<source>Don&apos;t have a copy?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="195"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/dollar.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="202"/>
<source>Buy the game</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="61"/>
<source>&lt;br&gt;&lt;b&gt;Could not find Morrowind.ini&lt;/b&gt;&lt;br&gt;&lt;br&gt;The Wizard needs to update settings in this file.&lt;br&gt;&lt;br&gt;Press &quot;Browse...&quot; to specify the location manually.&lt;br&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="65"/>
<source>B&amp;rowse...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="72"/>
<source>Select configuration file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="110"/>
<source>&lt;b&gt;Morrowind.bsa&lt;/b&gt; is missing!&lt;br&gt;Make sure your Morrowind installation is complete.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="190"/>
<source>&lt;br&gt;&lt;b&gt;There may be a more recent version of Morrowind available.&lt;/b&gt;&lt;br&gt;&lt;br&gt;Do you wish to continue anyway?&lt;br&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="185"/>
<source>Most recent Morrowind not detected</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="168"/>
<source>Select a valid %1 installation media.&lt;br&gt;&lt;b&gt;Hint&lt;/b&gt;: make sure that it contains at least one &lt;b&gt;.cab&lt;/b&gt; file.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="200"/>
<source>There may be a more recent version of Morrowind available.&lt;br&gt;&lt;br&gt;Do you wish to continue anyway?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Wizard::ComponentSelectionPage</name>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="16"/>
<source>&amp;Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="46"/>
<source>&amp;Skip</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="83"/>
<source>Morrowind (installed)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="89"/>
<source>Morrowind</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="97"/>
<source>Tribunal (installed)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="103"/>
<source>Tribunal</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="111"/>
<source>Bloodmoon (installed)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="117"/>
<source>Bloodmoon</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="137"/>
<source>About to install Tribunal after Bloodmoon</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="141"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;You are about to install Tribunal&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Bloodmoon is already installed on your computer.&lt;/p&gt;&lt;p&gt;However, it is recommended that you install Tribunal before Bloodmoon.&lt;/p&gt;&lt;p&gt;Would you like to re-install Bloodmoon?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="147"/>
<source>Re-install &amp;Bloodmoon</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Wizard::ConclusionPage</name>
<message>
<location filename="../../apps/wizard/conclusionpage.cpp" line="40"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The OpenMW Wizard successfully installed Morrowind on your computer.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/conclusionpage.cpp" line="46"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The OpenMW Wizard successfully modified your existing Morrowind installation.&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/conclusionpage.cpp" line="53"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The OpenMW Wizard failed to install Morrowind on your computer.&lt;/p&gt;&lt;p&gt;Please report any bugs you might have encountered to our &lt;a href=&quot;https://gitlab.com/OpenMW/openmw/issues&quot;&gt;bug tracker&lt;/a&gt;.&lt;br/&gt;Make sure to include the installation log.&lt;/p&gt;&lt;br/&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Wizard::ExistingInstallationPage</name>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="18"/>
<source>No existing installations detected</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="57"/>
<source>Error detecting Morrowind configuration</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="73"/>
<source>Morrowind configuration file (*.ini)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="92"/>
<source>Select Morrowind.esm (located in Data Files)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="93"/>
<source>Morrowind master file (Morrowind.esm)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="106"/>
<source>Error detecting Morrowind files</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Wizard::InstallationPage</name>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="161"/>
<source>&lt;p&gt;Attempting to install component %1.&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="162"/>
<source>Attempting to install component %1.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="165"/>
<source>%1 Installation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="174"/>
<source>Select %1 installation media</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="179"/>
<location filename="../../apps/wizard/installationpage.cpp" line="209"/>
<source>&lt;p&gt;&lt;br/&gt;&lt;span style=&quot;color:red;&quot;&gt;&lt;b&gt;Error: The installation was aborted by the user&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="194"/>
<source>&lt;p&gt;Detected old version of component Morrowind.&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="195"/>
<source>Detected old version of component Morrowind.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="198"/>
<source>Morrowind Installation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="225"/>
<source>Installation finished</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="228"/>
<source>Installation completed successfully!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="238"/>
<source>Installation failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="240"/>
<source>&lt;p&gt;&lt;br/&gt;&lt;span style=&quot;color:red;&quot;&gt;&lt;b&gt;Error: %1&lt;/b&gt;&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="241"/>
<source>&lt;p&gt;&lt;span style=&quot;color:red;&quot;&gt;&lt;b&gt;%1&lt;/b&gt;&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="252"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;The Wizard has encountered an error&lt;/b&gt;&lt;/p&gt;&lt;p&gt;The error reported was:&lt;/p&gt;&lt;p&gt;%1&lt;/p&gt;&lt;p&gt;Press &amp;quot;Show Details...&amp;quot; for more information.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="248"/>
<source>An error occurred</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Wizard::InstallationTargetPage</name>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="47"/>
<source>Error creating destination</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="51"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not create the destination directory&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again, or specify a different location.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="68"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not write to the destination directory&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again, or specify a different location.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="82"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;The destination directory is not empty&lt;/b&gt;&lt;/p&gt;&lt;p&gt;An existing Morrowind installation is present in the specified location.&lt;/p&gt;&lt;p&gt;Please specify a different location, or go back and select the location as an existing installation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="64"/>
<source>Insufficient permissions</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="78"/>
<source>Destination not empty</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="95"/>
<source>Select where to install Morrowind</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Wizard::LanguageSelectionPage</name>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="17"/>
<source>English</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="17"/>
<source>French</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="18"/>
<source>German</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="18"/>
<source>Italian</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="18"/>
<source>Polish</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="19"/>
<source>Russian</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="19"/>
<source>Spanish</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Wizard::MainWizard</name>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="41"/>
<source>OpenMW Wizard</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="94"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="118"/>
<source>Error opening Wizard log file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="57"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="450"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="477"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not open %1 for writing&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="142"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="213"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not open %1 for reading&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="158"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="190"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="226"/>
<source>Error opening OpenMW configuration file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="380"/>
<source>Quit Wizard</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="383"/>
<source>Are you sure you want to exit the Wizard?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="425"/>
<source>Error creating OpenMW configuration directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="429"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not create %1&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="446"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="473"/>
<source>Error writing OpenMW configuration file</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Wizard::UnshieldWorker</name>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="177"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="196"/>
<source>Failed to open Morrowind configuration file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="178"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="197"/>
<source>Opening %1 failed: %2.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="203"/>
<source>Failed to write Morrowind configuration file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="204"/>
<source>Writing to %1 failed: %2.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="338"/>
<source>Installing: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="367"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="775"/>
<source>Installing: %1 directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="438"/>
<source>Installation finished!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="462"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="582"/>
<source>Component parameter is invalid!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="462"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="582"/>
<source>An invalid component parameter was supplied.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="543"/>
<source>Failed to find a valid archive containing %1.bsa! Retrying.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="586"/>
<source>Installing %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="592"/>
<source>Installation media path not set!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="592"/>
<source>The source path for %1 was not set.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="606"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="615"/>
<source>Cannot create temporary directory!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="606"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="615"/>
<source>Failed to create %1.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="621"/>
<source>Cannot move into temporary directory!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="622"/>
<source>Failed to move into %1.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="631"/>
<source>Moving installation files</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="644"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="655"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="712"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="779"/>
<source>Could not install directory!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="644"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="655"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="713"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="780"/>
<source>Installing %1 to %2 failed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="668"/>
<source>Could not install translation file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="668"/>
<source>Failed to install *%1 files.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="682"/>
<source>Could not install Morrowind data file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="682"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="691"/>
<source>Failed to install %1.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="690"/>
<source>Could not install Morrowind configuration file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="709"/>
<source>Installing: Sound directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="725"/>
<source>Could not find Tribunal data file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="725"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="740"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="752"/>
<source>Failed to find %1.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="739"/>
<source>Could not find Tribunal patch file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="752"/>
<source>Could not find Bloodmoon data file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="760"/>
<source>Updating Morrowind configuration file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="785"/>
<source>%1 installation finished!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="824"/>
<source>Extracting: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="850"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="983"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="1021"/>
<source>Failed to open InstallShield Cabinet File.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="850"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="983"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="1021"/>
<source>Opening %1 failed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="879"/>
<source>Failed to extract %1.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="880"/>
<source>Complete path: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

858
files/lang/wizard_fr.ts Normal file
View file

@ -0,0 +1,858 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="fr_FR">
<context>
<name>ComponentSelectionPage</name>
<message>
<location filename="../../apps/wizard/ui/componentselectionpage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/componentselectionpage.ui" line="17"/>
<source>Select Components</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/componentselectionpage.ui" line="20"/>
<source>Which components should be installed?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/componentselectionpage.ui" line="26"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select which official Morrowind expansions should be installed. For best results, it is recommended to have both expansions installed.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:bold;&quot;&gt;Note:&lt;/span&gt; It is possible to install expansions later by re-running this Wizard.&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/componentselectionpage.ui" line="36"/>
<source>Selected components:</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ConclusionPage</name>
<message>
<location filename="../../apps/wizard/ui/conclusionpage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/conclusionpage.ui" line="17"/>
<source>Completing the OpenMW Wizard</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/conclusionpage.ui" line="23"/>
<source>Placeholder</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ExistingInstallationPage</name>
<message>
<location filename="../../apps/wizard/ui/existinginstallationpage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/existinginstallationpage.ui" line="17"/>
<source>Select Existing Installation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/existinginstallationpage.ui" line="20"/>
<source>Select an existing installation for OpenMW to use or modify.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/existinginstallationpage.ui" line="26"/>
<source>Detected installations:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/existinginstallationpage.ui" line="51"/>
<source>Browse...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ImportPage</name>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="17"/>
<source>Import Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="20"/>
<source>Import settings from the Morrowind installation.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="26"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;OpenMW needs to import settings from the Morrowind configuration file in order to function properly.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:bold;&quot;&gt;Note:&lt;/span&gt; It is possible to import settings later by re-running this Wizard.&lt;/p&gt;&lt;p/&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="36"/>
<source>Import settings from Morrowind.ini</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="46"/>
<source>Import add-on and plugin selection</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="56"/>
<source>Import bitmap fonts setup from Morrowind.ini</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="59"/>
<source>Fonts shipped with the original engine are blurry with UI scaling and support only a small amount of characters,
so OpenMW provides another set of fonts to avoid these issues. These fonts use TrueType technology and are quite similar
to default Morrowind fonts. Check this box if you still prefer original fonts over OpenMW ones or if you use custom bitmap fonts.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>InstallationPage</name>
<message>
<location filename="../../apps/wizard/ui/installationpage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationpage.ui" line="17"/>
<source>Installing</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationpage.ui" line="20"/>
<source>Please wait while Morrowind is installed on your computer.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>InstallationTargetPage</name>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="17"/>
<source>Select Installation Destination</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="20"/>
<source>Where should Morrowind be installed?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="34"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/folder.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="47"/>
<source>Morrowind will be installed to the following location. </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="64"/>
<source>Browse...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>IntroPage</name>
<message>
<location filename="../../apps/wizard/ui/intropage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/intropage.ui" line="17"/>
<source>Welcome to the OpenMW Wizard</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/intropage.ui" line="23"/>
<source>This Wizard will help you install Morrowind and its add-ons for OpenMW to use.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>LanguageSelectionPage</name>
<message>
<location filename="../../apps/wizard/ui/languageselectionpage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/languageselectionpage.ui" line="17"/>
<source>Select Morrowind Language</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/languageselectionpage.ui" line="20"/>
<source>What is the language of the Morrowind installation?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/languageselectionpage.ui" line="34"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/preferences-desktop-locale.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/languageselectionpage.ui" line="47"/>
<source>Select the language of the Morrowind installation.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MethodSelectionPage</name>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="14"/>
<source>WizardPage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="17"/>
<source>Select Installation Method</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="20"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select how you would like to install &lt;i&gt;The Elder Scrolls III: Morrowind&lt;/i&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="29"/>
<source>Retail CD/DVD</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="63"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/system-installer.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="82"/>
<source>Install from a retail disc to a new location.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="97"/>
<source>Existing Installation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="128"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/folder.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="141"/>
<source>Select an existing installation.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="162"/>
<source>Don&apos;t have a copy?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="195"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/dollar.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="202"/>
<source>Buy the game</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="61"/>
<source>&lt;br&gt;&lt;b&gt;Could not find Morrowind.ini&lt;/b&gt;&lt;br&gt;&lt;br&gt;The Wizard needs to update settings in this file.&lt;br&gt;&lt;br&gt;Press &quot;Browse...&quot; to specify the location manually.&lt;br&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="65"/>
<source>B&amp;rowse...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="72"/>
<source>Select configuration file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="110"/>
<source>&lt;b&gt;Morrowind.bsa&lt;/b&gt; is missing!&lt;br&gt;Make sure your Morrowind installation is complete.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="190"/>
<source>&lt;br&gt;&lt;b&gt;There may be a more recent version of Morrowind available.&lt;/b&gt;&lt;br&gt;&lt;br&gt;Do you wish to continue anyway?&lt;br&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="185"/>
<source>Most recent Morrowind not detected</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="168"/>
<source>Select a valid %1 installation media.&lt;br&gt;&lt;b&gt;Hint&lt;/b&gt;: make sure that it contains at least one &lt;b&gt;.cab&lt;/b&gt; file.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="200"/>
<source>There may be a more recent version of Morrowind available.&lt;br&gt;&lt;br&gt;Do you wish to continue anyway?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Wizard::ComponentSelectionPage</name>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="16"/>
<source>&amp;Install</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="46"/>
<source>&amp;Skip</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="83"/>
<source>Morrowind (installed)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="89"/>
<source>Morrowind</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="97"/>
<source>Tribunal (installed)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="103"/>
<source>Tribunal</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="111"/>
<source>Bloodmoon (installed)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="117"/>
<source>Bloodmoon</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="137"/>
<source>About to install Tribunal after Bloodmoon</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="141"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;You are about to install Tribunal&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Bloodmoon is already installed on your computer.&lt;/p&gt;&lt;p&gt;However, it is recommended that you install Tribunal before Bloodmoon.&lt;/p&gt;&lt;p&gt;Would you like to re-install Bloodmoon?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="147"/>
<source>Re-install &amp;Bloodmoon</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Wizard::ConclusionPage</name>
<message>
<location filename="../../apps/wizard/conclusionpage.cpp" line="40"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The OpenMW Wizard successfully installed Morrowind on your computer.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/conclusionpage.cpp" line="46"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The OpenMW Wizard successfully modified your existing Morrowind installation.&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/conclusionpage.cpp" line="53"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The OpenMW Wizard failed to install Morrowind on your computer.&lt;/p&gt;&lt;p&gt;Please report any bugs you might have encountered to our &lt;a href=&quot;https://gitlab.com/OpenMW/openmw/issues&quot;&gt;bug tracker&lt;/a&gt;.&lt;br/&gt;Make sure to include the installation log.&lt;/p&gt;&lt;br/&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Wizard::ExistingInstallationPage</name>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="18"/>
<source>No existing installations detected</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="57"/>
<source>Error detecting Morrowind configuration</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="73"/>
<source>Morrowind configuration file (*.ini)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="92"/>
<source>Select Morrowind.esm (located in Data Files)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="93"/>
<source>Morrowind master file (Morrowind.esm)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="106"/>
<source>Error detecting Morrowind files</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Wizard::InstallationPage</name>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="161"/>
<source>&lt;p&gt;Attempting to install component %1.&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="162"/>
<source>Attempting to install component %1.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="165"/>
<source>%1 Installation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="174"/>
<source>Select %1 installation media</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="179"/>
<location filename="../../apps/wizard/installationpage.cpp" line="209"/>
<source>&lt;p&gt;&lt;br/&gt;&lt;span style=&quot;color:red;&quot;&gt;&lt;b&gt;Error: The installation was aborted by the user&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="194"/>
<source>&lt;p&gt;Detected old version of component Morrowind.&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="195"/>
<source>Detected old version of component Morrowind.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="198"/>
<source>Morrowind Installation</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="225"/>
<source>Installation finished</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="228"/>
<source>Installation completed successfully!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="238"/>
<source>Installation failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="240"/>
<source>&lt;p&gt;&lt;br/&gt;&lt;span style=&quot;color:red;&quot;&gt;&lt;b&gt;Error: %1&lt;/b&gt;&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="241"/>
<source>&lt;p&gt;&lt;span style=&quot;color:red;&quot;&gt;&lt;b&gt;%1&lt;/b&gt;&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="252"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;The Wizard has encountered an error&lt;/b&gt;&lt;/p&gt;&lt;p&gt;The error reported was:&lt;/p&gt;&lt;p&gt;%1&lt;/p&gt;&lt;p&gt;Press &amp;quot;Show Details...&amp;quot; for more information.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="248"/>
<source>An error occurred</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Wizard::InstallationTargetPage</name>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="47"/>
<source>Error creating destination</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="51"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not create the destination directory&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again, or specify a different location.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="68"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not write to the destination directory&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again, or specify a different location.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="82"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;The destination directory is not empty&lt;/b&gt;&lt;/p&gt;&lt;p&gt;An existing Morrowind installation is present in the specified location.&lt;/p&gt;&lt;p&gt;Please specify a different location, or go back and select the location as an existing installation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="64"/>
<source>Insufficient permissions</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="78"/>
<source>Destination not empty</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="95"/>
<source>Select where to install Morrowind</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Wizard::LanguageSelectionPage</name>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="17"/>
<source>English</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="17"/>
<source>French</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="18"/>
<source>German</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="18"/>
<source>Italian</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="18"/>
<source>Polish</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="19"/>
<source>Russian</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="19"/>
<source>Spanish</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Wizard::MainWizard</name>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="41"/>
<source>OpenMW Wizard</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="94"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="118"/>
<source>Error opening Wizard log file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="57"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="450"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="477"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not open %1 for writing&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="142"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="213"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not open %1 for reading&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="158"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="190"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="226"/>
<source>Error opening OpenMW configuration file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="380"/>
<source>Quit Wizard</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="383"/>
<source>Are you sure you want to exit the Wizard?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="425"/>
<source>Error creating OpenMW configuration directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="429"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not create %1&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="446"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="473"/>
<source>Error writing OpenMW configuration file</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Wizard::UnshieldWorker</name>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="177"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="196"/>
<source>Failed to open Morrowind configuration file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="178"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="197"/>
<source>Opening %1 failed: %2.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="203"/>
<source>Failed to write Morrowind configuration file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="204"/>
<source>Writing to %1 failed: %2.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="338"/>
<source>Installing: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="367"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="775"/>
<source>Installing: %1 directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="438"/>
<source>Installation finished!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="462"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="582"/>
<source>Component parameter is invalid!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="462"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="582"/>
<source>An invalid component parameter was supplied.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="543"/>
<source>Failed to find a valid archive containing %1.bsa! Retrying.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="586"/>
<source>Installing %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="592"/>
<source>Installation media path not set!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="592"/>
<source>The source path for %1 was not set.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="606"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="615"/>
<source>Cannot create temporary directory!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="606"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="615"/>
<source>Failed to create %1.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="621"/>
<source>Cannot move into temporary directory!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="622"/>
<source>Failed to move into %1.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="631"/>
<source>Moving installation files</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="644"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="655"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="712"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="779"/>
<source>Could not install directory!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="644"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="655"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="713"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="780"/>
<source>Installing %1 to %2 failed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="668"/>
<source>Could not install translation file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="668"/>
<source>Failed to install *%1 files.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="682"/>
<source>Could not install Morrowind data file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="682"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="691"/>
<source>Failed to install %1.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="690"/>
<source>Could not install Morrowind configuration file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="709"/>
<source>Installing: Sound directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="725"/>
<source>Could not find Tribunal data file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="725"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="740"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="752"/>
<source>Failed to find %1.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="739"/>
<source>Could not find Tribunal patch file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="752"/>
<source>Could not find Bloodmoon data file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="760"/>
<source>Updating Morrowind configuration file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="785"/>
<source>%1 installation finished!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="824"/>
<source>Extracting: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="850"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="983"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="1021"/>
<source>Failed to open InstallShield Cabinet File.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="850"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="983"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="1021"/>
<source>Opening %1 failed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="879"/>
<source>Failed to extract %1.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="880"/>
<source>Complete path: %1</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

860
files/lang/wizard_ru.ts Normal file
View file

@ -0,0 +1,860 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="ru_RU">
<context>
<name>ComponentSelectionPage</name>
<message>
<location filename="../../apps/wizard/ui/componentselectionpage.ui" line="14"/>
<source>WizardPage</source>
<translation>WizardPage</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/componentselectionpage.ui" line="17"/>
<source>Select Components</source>
<translation>Выбор компонентов</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/componentselectionpage.ui" line="20"/>
<source>Which components should be installed?</source>
<translation>Какие компоненты должны быть установлены?</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/componentselectionpage.ui" line="26"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select which official Morrowind expansions should be installed. For best results, it is recommended to have both expansions installed.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:bold;&quot;&gt;Note:&lt;/span&gt; It is possible to install expansions later by re-running this Wizard.&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Выберите, какие дополнения для Morrowind нужно установить. Для достижения наилучших результатов рекомендуется установить оба дополнения.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:bold;&quot;&gt;Подсказка:&lt;/span&gt; Можно установить дополнения позже, запустив этот Мастер установки заново.&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/componentselectionpage.ui" line="36"/>
<source>Selected components:</source>
<translation>Выбранные компоненты:</translation>
</message>
</context>
<context>
<name>ConclusionPage</name>
<message>
<location filename="../../apps/wizard/ui/conclusionpage.ui" line="14"/>
<source>WizardPage</source>
<translation>WizardPage</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/conclusionpage.ui" line="17"/>
<source>Completing the OpenMW Wizard</source>
<translation>Завершение работы Мастера установки OpenMW</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/conclusionpage.ui" line="23"/>
<source>Placeholder</source>
<translation>Placeholder</translation>
</message>
</context>
<context>
<name>ExistingInstallationPage</name>
<message>
<location filename="../../apps/wizard/ui/existinginstallationpage.ui" line="14"/>
<source>WizardPage</source>
<translation>WizardPage</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/existinginstallationpage.ui" line="17"/>
<source>Select Existing Installation</source>
<translation>Выбрать установленную копию игры</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/existinginstallationpage.ui" line="20"/>
<source>Select an existing installation for OpenMW to use or modify.</source>
<translation>Выбрать установленную копию игры для использования или изменения через OpenMW.</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/existinginstallationpage.ui" line="26"/>
<source>Detected installations:</source>
<translation>Обнаруженные установленные копии:</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/existinginstallationpage.ui" line="51"/>
<source>Browse...</source>
<translation>Выбрать...</translation>
</message>
</context>
<context>
<name>ImportPage</name>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="14"/>
<source>WizardPage</source>
<translation>WizardPage</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="17"/>
<source>Import Settings</source>
<translation>Импортировать настройки</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="20"/>
<source>Import settings from the Morrowind installation.</source>
<translation>Импортировать настройки из установленной копии Morrowind.</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="26"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;OpenMW needs to import settings from the Morrowind configuration file in order to function properly.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:bold;&quot;&gt;Note:&lt;/span&gt; It is possible to import settings later by re-running this Wizard.&lt;/p&gt;&lt;p/&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Чтобы OpenMW мог работать правильно, ему нужно импортировать настройки из файла с настройками Morrowind.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:bold;&quot;&gt;Подсказка:&lt;/span&gt; Также можно импортировать настройки позже, запустив Мастер импорта заново.&lt;/p&gt;&lt;p/&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="36"/>
<source>Import settings from Morrowind.ini</source>
<translation>Импортировать настройки из Morrowind.ini</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="46"/>
<source>Import add-on and plugin selection</source>
<translation>Импортировать список подключенных плагинов</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="56"/>
<source>Import bitmap fonts setup from Morrowind.ini</source>
<translation>Импортировать растровые шрифты из Morrowind.ini</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/importpage.ui" line="59"/>
<source>Fonts shipped with the original engine are blurry with UI scaling and support only a small amount of characters,
so OpenMW provides another set of fonts to avoid these issues. These fonts use TrueType technology and are quite similar
to default Morrowind fonts. Check this box if you still prefer original fonts over OpenMW ones or if you use custom bitmap fonts.</source>
<translation>Шрифты, поставляемые с оригинальной игрой, становятся размытыми при масштабировании интерфейса и поддерживают ограниченное количество символов,
поэтому в комплекте с OpenMW идет набор шрифтов, не имеющих этих проблем. Они используют технологию TrueType и весьма похожи на шрифты Morrowind.
Включите эту опцию, если вы все равно хотите использовать оригинальные шрифты вместо шрифтов OpenMW, или если вы используете сторонние растровые шрифты.</translation>
</message>
</context>
<context>
<name>InstallationPage</name>
<message>
<location filename="../../apps/wizard/ui/installationpage.ui" line="14"/>
<source>WizardPage</source>
<translation>WizardPage</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationpage.ui" line="17"/>
<source>Installing</source>
<translation>Установка</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationpage.ui" line="20"/>
<source>Please wait while Morrowind is installed on your computer.</source>
<translation>Пожалуйста, подождите, пока Morrowind устанавливается на ваш компьютер.</translation>
</message>
</context>
<context>
<name>InstallationTargetPage</name>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="14"/>
<source>WizardPage</source>
<translation>WizardPage</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="17"/>
<source>Select Installation Destination</source>
<translation>Выберите путь для установки</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="20"/>
<source>Where should Morrowind be installed?</source>
<translation>Куда нужно установить Morrowind?</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="34"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/folder.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/folder.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="47"/>
<source>Morrowind will be installed to the following location. </source>
<translation>Morrowind будет установлен в следующее место.</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/installationtargetpage.ui" line="64"/>
<source>Browse...</source>
<translation>Выбрать...</translation>
</message>
</context>
<context>
<name>IntroPage</name>
<message>
<location filename="../../apps/wizard/ui/intropage.ui" line="14"/>
<source>WizardPage</source>
<translation>WizardPage</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/intropage.ui" line="17"/>
<source>Welcome to the OpenMW Wizard</source>
<translation>Добро пожаловать в Мастер установки</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/intropage.ui" line="23"/>
<source>This Wizard will help you install Morrowind and its add-ons for OpenMW to use.</source>
<translation>Этот Мастер поможет вам установить Morrowind и его дополнения, чтобы OpenMW мог их использовать.</translation>
</message>
</context>
<context>
<name>LanguageSelectionPage</name>
<message>
<location filename="../../apps/wizard/ui/languageselectionpage.ui" line="14"/>
<source>WizardPage</source>
<translation>WizardPage</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/languageselectionpage.ui" line="17"/>
<source>Select Morrowind Language</source>
<translation>Выберите язык вашей копии Morrowind</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/languageselectionpage.ui" line="20"/>
<source>What is the language of the Morrowind installation?</source>
<translation>Какой язык использует ваша копия Morrowind?</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/languageselectionpage.ui" line="34"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/preferences-desktop-locale.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&gt;&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/preferences-desktop-locale.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/languageselectionpage.ui" line="47"/>
<source>Select the language of the Morrowind installation.</source>
<translation>Выберите язык, используемый вашей копией Morrowind.</translation>
</message>
</context>
<context>
<name>MethodSelectionPage</name>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="14"/>
<source>WizardPage</source>
<translation>WizardPage</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="17"/>
<source>Select Installation Method</source>
<translation>Выберите способ установки</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="20"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select how you would like to install &lt;i&gt;The Elder Scrolls III: Morrowind&lt;/i&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Выберите способ установки &lt;i&gt;The Elder Scrolls III: Morrowind&lt;/i&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="29"/>
<source>Retail CD/DVD</source>
<translation>CD/DVD-диск</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="63"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/system-installer.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/system-installer.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="82"/>
<source>Install from a retail disc to a new location.</source>
<translation>Установить игру с диска</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="97"/>
<source>Existing Installation</source>
<translation>Установленная копия игры</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="128"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/folder.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/folder.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="141"/>
<source>Select an existing installation.</source>
<translation>Выбрать установленную копию игры.</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="162"/>
<source>Don&apos;t have a copy?</source>
<translation>Нет копии игры?</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="195"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/dollar.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/tango/48x48/dollar.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/ui/methodselectionpage.ui" line="202"/>
<source>Buy the game</source>
<translation>Купить игру</translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="61"/>
<source>&lt;br&gt;&lt;b&gt;Could not find Morrowind.ini&lt;/b&gt;&lt;br&gt;&lt;br&gt;The Wizard needs to update settings in this file.&lt;br&gt;&lt;br&gt;Press &quot;Browse...&quot; to specify the location manually.&lt;br&gt;</source>
<translation>&lt;br&gt;&lt;b&gt;Не удалось найти Morrowind.ini&lt;/b&gt;&lt;br&gt;&lt;br&gt;Мастеру требуется обновить настройки в этом файле.&lt;br&gt;&lt;br&gt;Нажмите &quot;Выбрать...&quot;, чтобы задать местоположение файла вручную.&lt;br&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="65"/>
<source>B&amp;rowse...</source>
<translation>&amp;Выбрать...</translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="72"/>
<source>Select configuration file</source>
<translation>Выберите файл с настройками</translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="110"/>
<source>&lt;b&gt;Morrowind.bsa&lt;/b&gt; is missing!&lt;br&gt;Make sure your Morrowind installation is complete.</source>
<translation>&lt;b&gt;Morrowind.bsa&lt;/b&gt; не найден!&lt;br&gt;Убедитесь, что Morrowind был установлен правильно.</translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="190"/>
<source>&lt;br&gt;&lt;b&gt;There may be a more recent version of Morrowind available.&lt;/b&gt;&lt;br&gt;&lt;br&gt;Do you wish to continue anyway?&lt;br&gt;</source>
<translation>&lt;br&gt;&lt;b&gt;Может существовать более свежая версия Morrowind.&lt;/b&gt;&lt;br&gt;&lt;br&gt;Все равно продолжить?&lt;br&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="185"/>
<source>Most recent Morrowind not detected</source>
<translation>Актуальная версия Morrowind не найдена</translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="168"/>
<source>Select a valid %1 installation media.&lt;br&gt;&lt;b&gt;Hint&lt;/b&gt;: make sure that it contains at least one &lt;b&gt;.cab&lt;/b&gt; file.</source>
<translation>Выберите корректный установочный дистрибутив %1.&lt;br&gt;&lt;b&gt;Подсказка&lt;/b&gt;: он должен содержать как минимум один &lt;b&gt;.cab&lt;/b&gt;-файл.</translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="200"/>
<source>There may be a more recent version of Morrowind available.&lt;br&gt;&lt;br&gt;Do you wish to continue anyway?</source>
<translation>Может существовать более свежая версия Morrowind.&lt;br&gt;&lt;br&gt;Все равно продолжить?</translation>
</message>
</context>
<context>
<name>Wizard::ComponentSelectionPage</name>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="16"/>
<source>&amp;Install</source>
<translation>&amp;Установить</translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="46"/>
<source>&amp;Skip</source>
<translation>&amp;Пропустить</translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="83"/>
<source>Morrowind (installed)</source>
<translation>Morrowind (установлен)</translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="89"/>
<source>Morrowind</source>
<translation>Morrowind</translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="97"/>
<source>Tribunal (installed)</source>
<translation>Tribunal (установлен)</translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="103"/>
<source>Tribunal</source>
<translation>Tribunal</translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="111"/>
<source>Bloodmoon (installed)</source>
<translation>Bloodmoon (установлен)</translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="117"/>
<source>Bloodmoon</source>
<translation>Bloodmoon</translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="137"/>
<source>About to install Tribunal after Bloodmoon</source>
<translation>Попытка установить Tribunal после Bloodmoon</translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="141"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;You are about to install Tribunal&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Bloodmoon is already installed on your computer.&lt;/p&gt;&lt;p&gt;However, it is recommended that you install Tribunal before Bloodmoon.&lt;/p&gt;&lt;p&gt;Would you like to re-install Bloodmoon?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Вы собираетесь установить Tribunal&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Bloodmoon уже установлен на ваш компьютер.&lt;/p&gt;&lt;p&gt;Tribunal рекомендуется устанавлить перед установкой Bloodmoon.&lt;/p&gt;&lt;p&gt;Желаете ли вы переустановить Bloodmoon?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/componentselectionpage.cpp" line="147"/>
<source>Re-install &amp;Bloodmoon</source>
<translation>Переустановить &amp;Bloodmoon</translation>
</message>
</context>
<context>
<name>Wizard::ConclusionPage</name>
<message>
<location filename="../../apps/wizard/conclusionpage.cpp" line="40"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The OpenMW Wizard successfully installed Morrowind on your computer.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Мастер OpenMW успешно установил Morrowind на ваш компьютер.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/conclusionpage.cpp" line="46"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The OpenMW Wizard successfully modified your existing Morrowind installation.&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Мастер OpenMW успешно завершил изменение вашей установленной копии Morrowind.&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/conclusionpage.cpp" line="53"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The OpenMW Wizard failed to install Morrowind on your computer.&lt;/p&gt;&lt;p&gt;Please report any bugs you might have encountered to our &lt;a href=&quot;https://gitlab.com/OpenMW/openmw/issues&quot;&gt;bug tracker&lt;/a&gt;.&lt;br/&gt;Make sure to include the installation log.&lt;/p&gt;&lt;br/&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Мастеру OpenMW не удалось установить Morrowind на ваш компьютер.&lt;/p&gt;&lt;p&gt;Пожалуйста, сообщите о встреченных вами ошибках на наш &lt;a href=&quot;https://gitlab.com/OpenMW/openmw/issues&quot;&gt;багтрекер&lt;/a&gt;.&lt;br/&gt;Не забудьте включить туда лог установки.&lt;/p&gt;&lt;br/&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
</context>
<context>
<name>Wizard::ExistingInstallationPage</name>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="18"/>
<source>No existing installations detected</source>
<translation>Установленные копии игры не найдены</translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="57"/>
<source>Error detecting Morrowind configuration</source>
<translation>Попытка найти настройки Morrowind завершилась ошибкой</translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="73"/>
<source>Morrowind configuration file (*.ini)</source>
<translation>Файл настроек Morrowind (*.ini)</translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="92"/>
<source>Select Morrowind.esm (located in Data Files)</source>
<translation>Выберите Morrowind.esm (расположен в Data Files)</translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="93"/>
<source>Morrowind master file (Morrowind.esm)</source>
<translation>Мастер-файл Morrowind (Morrowind.esm)</translation>
</message>
<message>
<location filename="../../apps/wizard/existinginstallationpage.cpp" line="106"/>
<source>Error detecting Morrowind files</source>
<translation>Не удалось обнаружить файлы Morrowind</translation>
</message>
</context>
<context>
<name>Wizard::InstallationPage</name>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="161"/>
<source>&lt;p&gt;Attempting to install component %1.&lt;/p&gt;</source>
<translation>&lt;p&gt;Попытка установить компонент %1.&lt;/p&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="162"/>
<source>Attempting to install component %1.</source>
<translation>Попытка установить компонент %1.</translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="165"/>
<source>%1 Installation</source>
<translation>Установка %1</translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="174"/>
<source>Select %1 installation media</source>
<translation>Выберите установочный дистрибутив %1</translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="179"/>
<location filename="../../apps/wizard/installationpage.cpp" line="209"/>
<source>&lt;p&gt;&lt;br/&gt;&lt;span style=&quot;color:red;&quot;&gt;&lt;b&gt;Error: The installation was aborted by the user&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;</source>
<translation>&lt;p&gt;&lt;br/&gt;&lt;span style=&quot;color:red;&quot;&gt;&lt;b&gt;Ошибка: Установка была прервана пользователем&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="194"/>
<source>&lt;p&gt;Detected old version of component Morrowind.&lt;/p&gt;</source>
<translation>lt;p&gt;Обнаружена устаревшая версия компонента Morrowind.&lt;/p&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="195"/>
<source>Detected old version of component Morrowind.</source>
<translation>Обнаружена устаревшая версия компонента Morrowind.</translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="198"/>
<source>Morrowind Installation</source>
<translation>Установка Morrowind</translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="225"/>
<source>Installation finished</source>
<translation>Установка завершена</translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="228"/>
<source>Installation completed successfully!</source>
<translation>Установка успешно завершена!</translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="238"/>
<source>Installation failed!</source>
<translation>Установка не удалась!</translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="240"/>
<source>&lt;p&gt;&lt;br/&gt;&lt;span style=&quot;color:red;&quot;&gt;&lt;b&gt;Error: %1&lt;/b&gt;&lt;/p&gt;</source>
<translation>&lt;p&gt;&lt;br/&gt;&lt;span style=&quot;color:red;&quot;&gt;&lt;b&gt;Ошибка: %1&lt;/b&gt;&lt;/p&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="241"/>
<source>&lt;p&gt;&lt;span style=&quot;color:red;&quot;&gt;&lt;b&gt;%1&lt;/b&gt;&lt;/p&gt;</source>
<translation>&lt;p&gt;&lt;span style=&quot;color:red;&quot;&gt;&lt;b&gt;%1&lt;/b&gt;&lt;/p&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="252"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;The Wizard has encountered an error&lt;/b&gt;&lt;/p&gt;&lt;p&gt;The error reported was:&lt;/p&gt;&lt;p&gt;%1&lt;/p&gt;&lt;p&gt;Press &amp;quot;Show Details...&amp;quot; for more information.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;При работе Мастера возникла ошибка&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Обнаруженная ошибка:&lt;/p&gt;&lt;p&gt;%1&lt;/p&gt;&lt;p&gt;Нажмите &amp;quot;Показать детали...&amp;quot; для получения дополнительной информации.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/installationpage.cpp" line="248"/>
<source>An error occurred</source>
<translation>Произошла ошибка</translation>
</message>
</context>
<context>
<name>Wizard::InstallationTargetPage</name>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="47"/>
<source>Error creating destination</source>
<translation>Не удалось создать директорию назначения</translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="51"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not create the destination directory&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again, or specify a different location.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Не удалось создать директорию назначения&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Пожалуйста, проверьте права доступа и повторите попытку, или же выберите другую директорию.&lt;/p&gt;&lt;/body&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="68"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not write to the destination directory&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again, or specify a different location.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Не удалось записать данные в директорию назначения&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Пожалуйста, проверьте права доступа и повторите попытку, или же выберите другую директорию.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="82"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;The destination directory is not empty&lt;/b&gt;&lt;/p&gt;&lt;p&gt;An existing Morrowind installation is present in the specified location.&lt;/p&gt;&lt;p&gt;Please specify a different location, or go back and select the location as an existing installation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Директория назначения содержит файлы&lt;/b&gt;&lt;/p&gt;&lt;p&gt;В указанной директории найдена установленная копия Morrowind.&lt;/p&gt;&lt;p&gt;Пожалуйста, выберите другую директорию, или же вернитесь на предыдущий шаг и выберите подключение установленной копии игры.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="64"/>
<source>Insufficient permissions</source>
<translation>Не хватает прав доступа</translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="78"/>
<source>Destination not empty</source>
<translation>Выбранная директория не пустая</translation>
</message>
<message>
<location filename="../../apps/wizard/installationtargetpage.cpp" line="95"/>
<source>Select where to install Morrowind</source>
<translation>Выберите, куда установить Morrowind</translation>
</message>
</context>
<context>
<name>Wizard::LanguageSelectionPage</name>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="17"/>
<source>English</source>
<translation>Английский</translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="17"/>
<source>French</source>
<translation>Французский</translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="18"/>
<source>German</source>
<translation>Немецкий</translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="18"/>
<source>Italian</source>
<translation>Итальянский</translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="18"/>
<source>Polish</source>
<translation>Польский</translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="19"/>
<source>Russian</source>
<translation>Русский</translation>
</message>
<message>
<location filename="../../apps/wizard/languageselectionpage.cpp" line="19"/>
<source>Spanish</source>
<translation>Испанский</translation>
</message>
</context>
<context>
<name>Wizard::MainWizard</name>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="41"/>
<source>OpenMW Wizard</source>
<translation>Мастер OpenMW</translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="94"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="118"/>
<source>Error opening Wizard log file</source>
<translation>Не удалось открыть лог-файл Мастера</translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="57"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="450"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="477"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not open %1 for writing&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Не удалось открыть %1 для записи&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Пожалуйста, проверьте права доступа и повторите попытку.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="142"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="213"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not open %1 for reading&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Не удалось открыть %1 для чтения&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Пожалуйста, проверьте права доступа и повторите попытку.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="158"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="190"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="226"/>
<source>Error opening OpenMW configuration file</source>
<translation>Не удалось открыть файл с настройками OpenMW</translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="380"/>
<source>Quit Wizard</source>
<translation>Завершить работу Мастера</translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="383"/>
<source>Are you sure you want to exit the Wizard?</source>
<translation>Вы уверены, что хотите завершить работу Мастера?</translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="425"/>
<source>Error creating OpenMW configuration directory</source>
<translation>Не удалось создать директорию для настроек OpenMW</translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="429"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Could not create %1&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Please make sure you have the right permissions and try again.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;b&gt;Не удалось создать %1&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Пожалуйста, проверьте права доступа и повторите попытку.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../../apps/wizard/mainwizard.cpp" line="446"/>
<location filename="../../apps/wizard/mainwizard.cpp" line="473"/>
<source>Error writing OpenMW configuration file</source>
<translation>Не удалось записать данные в файл с настройками OpenMW</translation>
</message>
</context>
<context>
<name>Wizard::UnshieldWorker</name>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="177"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="196"/>
<source>Failed to open Morrowind configuration file!</source>
<translation>Не удалось открыть файл с настройками Morrowind!</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="178"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="197"/>
<source>Opening %1 failed: %2.</source>
<translation>Попытка открыть %1 не удалась: %2.</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="203"/>
<source>Failed to write Morrowind configuration file!</source>
<translation>Не удалось записать данные в файл с настройками Morrowind!</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="204"/>
<source>Writing to %1 failed: %2.</source>
<translation>Запись в %1 завершилась с ошибкой: %2.</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="338"/>
<source>Installing: %1</source>
<translation>Установка: %1</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="367"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="775"/>
<source>Installing: %1 directory</source>
<translation>Установка: директория %1</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="438"/>
<source>Installation finished!</source>
<translation>Установка завершена!</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="462"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="582"/>
<source>Component parameter is invalid!</source>
<translation>Некорректный параметр для компонента!</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="462"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="582"/>
<source>An invalid component parameter was supplied.</source>
<translation>Задан некорректный параметр для компонента.</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="543"/>
<source>Failed to find a valid archive containing %1.bsa! Retrying.</source>
<translation>Не удалось найти архив, содержащий %1.bsa! Повторная попытка.</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="586"/>
<source>Installing %1</source>
<translation>Установка %1</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="592"/>
<source>Installation media path not set!</source>
<translation>путь к установочному дистрибутиву не задан!</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="592"/>
<source>The source path for %1 was not set.</source>
<translation>Исходный пусть для %1 не задан.</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="606"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="615"/>
<source>Cannot create temporary directory!</source>
<translation>Не удалось создать временную директорию!</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="606"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="615"/>
<source>Failed to create %1.</source>
<translation>Не удалось создать %1.</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="621"/>
<source>Cannot move into temporary directory!</source>
<translation>Не удалось переместить во временную директорию!</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="622"/>
<source>Failed to move into %1.</source>
<translation>Не удалось переместить в %1.</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="631"/>
<source>Moving installation files</source>
<translation>Перемещение файлов установки</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="644"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="655"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="712"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="779"/>
<source>Could not install directory!</source>
<translation>Не удалось установить директорию!</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="644"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="655"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="713"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="780"/>
<source>Installing %1 to %2 failed.</source>
<translation>Не удалось установить %1 в %2.</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="668"/>
<source>Could not install translation file!</source>
<translation>Не удалось установить файл с переводом!</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="668"/>
<source>Failed to install *%1 files.</source>
<translation>Не удалось установить файлы *%1.</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="682"/>
<source>Could not install Morrowind data file!</source>
<translation>Не удалось установить файл с данными Morrowind!</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="682"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="691"/>
<source>Failed to install %1.</source>
<translation>Не удалось установить %1.</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="690"/>
<source>Could not install Morrowind configuration file!</source>
<translation>Не удалось установить файл с настройками Morrowind!</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="709"/>
<source>Installing: Sound directory</source>
<translation>Установка: директория Sound</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="725"/>
<source>Could not find Tribunal data file!</source>
<translation>Не удалось найти файл с данными Tribunal!</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="725"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="740"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="752"/>
<source>Failed to find %1.</source>
<translation>Не удалось найти %1.</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="739"/>
<source>Could not find Tribunal patch file!</source>
<translation>Не удалось найти файл с патчем для Tribunal!</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="752"/>
<source>Could not find Bloodmoon data file!</source>
<translation>Не удалось найти файл с данными Bloodmoon!</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="760"/>
<source>Updating Morrowind configuration file</source>
<translation>Обновление файла с настройками Morrowind</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="785"/>
<source>%1 installation finished!</source>
<translation>Установка %1 завершена!</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="824"/>
<source>Extracting: %1</source>
<translation>Извлечение: %1</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="850"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="983"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="1021"/>
<source>Failed to open InstallShield Cabinet File.</source>
<translation>Не удалось открыть файл InstallShield Cabinet.</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="850"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="983"/>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="1021"/>
<source>Opening %1 failed.</source>
<translation>Не удалось открыть %1.</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="879"/>
<source>Failed to extract %1.</source>
<translation>Не удалось извлечь %1.</translation>
</message>
<message>
<location filename="../../apps/wizard/unshield/unshieldworker.cpp" line="880"/>
<source>Complete path: %1</source>
<translation>Полный путь: %1</translation>
</message>
</context>
</TS>

View file

@ -19,7 +19,7 @@
-- @function [parent=#core] quit
---
-- Send an event to global scripts.
-- Send an event to global scripts. Note: in menu scripts, errors if the game is not running (check @{openmw.menu#menu.getState})
-- @function [parent=#core] sendGlobalEvent
-- @param #string eventName
-- @param eventData