• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Return-To-The-Roots / s25client / 14284294327

05 Apr 2025 05:41PM UTC coverage: 50.311% (+0.02%) from 50.288%
14284294327

Pull #1750

github

web-flow
Merge 6b1072a28 into c8cf430f2
Pull Request #1750: Fix size of Settings window

8 of 26 new or added lines in 15 files covered. (30.77%)

7 existing lines in 2 files now uncovered.

22370 of 44463 relevant lines covered (50.31%)

35678.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.94
/libs/libGamedata/lua/LuaInterfaceBase.cpp
1
// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org)
2
//
3
// SPDX-License-Identifier: GPL-2.0-or-later
4

5
#include "LuaInterfaceBase.h"
6
#include "helpers/strUtils.h"
7
#include "mygettext/LocaleInfo.h"
8
#include "mygettext/mygettext.h"
9
#include "mygettext/utils.h"
10
#include "s25util/Log.h"
11
#include "s25util/utf8.h"
12
#include <boost/nowide/convert.hpp>
13
#include <boost/nowide/detail/convert.hpp> // TODO: Remove when requiring Nowide 11 / Boost 1.74
14
#include <boost/nowide/fstream.hpp>
15
#include <algorithm>
16

17
LuaInterfaceBase::LuaInterfaceBase() : lua(kaguya::NoLoadLib()), logger_(LOG), errorOccured_(false)
254✔
18
{
19
    lua.openlib("base", luaopen_base);
254✔
20
    lua.openlib("package", luaopen_package);
254✔
21
    lua.openlib("string", luaopen_string);
254✔
22
    lua.openlib("table", luaopen_table);
254✔
23
    lua.openlib("math", luaopen_math);
254✔
24

25
    Register(lua);
254✔
26
    lua.setErrorHandler([this](int status, const char* msg) { errorHandler(status, msg); });
254✔
27
    // Quasi-Standard translate function
28
    lua["_"] = kaguya::function([this](const std::string& s) { return translate(s); });
262✔
29
    // No-op translate (translated later)
30
    lua["__"] = gettext_noop;
254✔
31
}
254✔
32

33
LuaInterfaceBase::~LuaInterfaceBase() = default;
254✔
34

35
void LuaInterfaceBase::Register(kaguya::State& state)
254✔
36
{
37
    state["RTTRBase"].setClass(kaguya::UserdataMetatable<LuaInterfaceBase>()
508✔
38
                                 .addFunction("Log", &LuaInterfaceBase::log)
254✔
39
                                 .addFunction("RegisterTranslations", &LuaInterfaceBase::registerTranslations));
254✔
40
}
254✔
41

42
void LuaInterfaceBase::errorHandlerNoThrow(int /*status*/, const char* message)
33✔
43
{
44
    logger_.write(_("Lua error: %s\n")) % (message ? message : _("Unknown error"));
33✔
45
    errorOccured_ = true;
33✔
46
}
33✔
47

48
void LuaInterfaceBase::errorHandler(int status, const char* message)
31✔
49
{
50
    errorHandlerNoThrow(status, message);
31✔
51
    throw LuaExecutionError(message ? message : _("Unknown error"));
31✔
52
}
53

54
bool LuaInterfaceBase::loadScript(const boost::filesystem::path& scriptPath)
841✔
55
{
56
    boost::nowide::ifstream scriptFile(scriptPath);
1,682✔
57
    std::string tmpScript;
1,682✔
58
    tmpScript.assign(std::istreambuf_iterator<char>(scriptFile), std::istreambuf_iterator<char>());
841✔
59
    if(!scriptFile)
841✔
60
    {
UNCOV
61
        logger_.write(_("Failed to read lua file '%1%'\n")) % scriptPath;
×
62
        return false;
×
63
    }
64
    // Remove UTF-8 BOM if present
65
    if(tmpScript.substr(0, 3) == "\xef\xbb\xbf")
841✔
UNCOV
66
        tmpScript.erase(0, 3);
×
67
    if(!loadScriptString(tmpScript))
841✔
68
    {
69
        logger_.write(_("Failed to load lua file '%1%'\n")) % scriptPath;
8✔
70
        return false;
8✔
71
    }
72
    return true;
833✔
73
}
74

75
bool LuaInterfaceBase::loadScriptString(const std::string& script, bool rethrowError)
1,116✔
76
{
77
    script_.clear();
1,116✔
78
    if(!validateUTF8(script))
1,116✔
79
        return false;
1✔
80
    try
81
    {
82
        if(!lua.dostring(script))
1,145✔
UNCOV
83
            return false;
×
84
        else
85
            script_ = script;
1,085✔
86
    } catch(const LuaExecutionError&)
51✔
87
    {
88
        if(rethrowError)
30✔
89
            throw;
21✔
90
        return false;
9✔
91
    }
92
    return true;
1,085✔
93
}
94

95
void LuaInterfaceBase::setThrowOnError(bool doThrow)
2✔
96
{
97
    if(doThrow)
2✔
98
        lua.setErrorHandler([this](int status, const char* msg) { errorHandler(status, msg); });
1✔
99
    else
100
        lua.setErrorHandler([this](int status, const char* msg) { errorHandlerNoThrow(status, msg); });
3✔
101
}
2✔
102

103
std::map<std::string, std::string> LuaInterfaceBase::getTranslation(const kaguya::LuaRef& luaTranslations,
10✔
104
                                                                    const std::string& code)
105
{
106
    std::vector<std::string> folders = mygettext::getPossibleFoldersForLangCode(code);
20✔
107
    for(const std::string& folder : folders)
13✔
108
    {
109
        kaguya::LuaRef entry = luaTranslations[folder];
24✔
110
        if(entry.type() == LUA_TTABLE)
12✔
111
            return entry;
9✔
112
    }
113
    return std::map<std::string, std::string>();
1✔
114
}
115

116
void LuaInterfaceBase::registerTranslations(const kaguya::LuaRef& luaTranslations)
5✔
117
{
118
    // Init with default
119
    translations_ = getTranslation(luaTranslations, "en_GB");
5✔
120
    // Replace with entries of current locale
121
    const std::string locale = mygettext::setlocale(LC_ALL, nullptr);
10✔
122
    const std::map<std::string, std::string> translated = getTranslation(luaTranslations, locale);
10✔
123
    if(translated.empty())
5✔
124
    {
125
        boost::format fmt("Did not found translation for language '%1%' in LUA file. Available translations:  %2%\n");
1✔
126
        fmt % mygettext::LocaleInfo(locale).getName() % helpers::join(luaTranslations.keys<std::string>(), ", ");
1✔
127
        log(fmt.str());
1✔
128
    } else
129
    {
130
        for(const auto& it : translated)
13✔
131
            translations_[it.first] = it.second;
9✔
132
    }
133
}
5✔
134

135
std::string LuaInterfaceBase::translate(const std::string& key)
8✔
136
{
137
    const auto entry = translations_.find(key);
8✔
138
    if(entry == translations_.end())
8✔
139
        return key;
1✔
140
    else
141
        return entry->second;
7✔
142
}
143

144
bool LuaInterfaceBase::validateUTF8(const std::string& scriptTxt)
1,116✔
145
{
146
    const auto it = s25util::findInvalidUTF8(scriptTxt);
1,116✔
147
    if(it == scriptTxt.end())
1,116✔
148
        return true;
1,115✔
149
    size_t invPos = std::distance(scriptTxt.begin(), it);
1✔
150
    size_t lineNum = std::count(scriptTxt.begin(), it, '\n') + 1;
1✔
151
    size_t lineBegin = scriptTxt.rfind('\n', invPos);
1✔
152
    size_t lineEnd = scriptTxt.find('\n', invPos);
1✔
153
    if(lineBegin == std::string::npos)
1✔
UNCOV
154
        lineBegin = 0;
×
155
    else
156
        lineBegin++;
1✔
157
    if(lineEnd == std::string::npos)
1✔
UNCOV
158
        lineEnd = scriptTxt.size();
×
159
    else
160
        lineEnd--;
1✔
161
    const std::string faultyLine =
162
      boost::nowide::detail::convert_string<char>(&scriptTxt[lineBegin], &scriptTxt[lineEnd]);
2✔
163
    boost::format fmt("Found invalid UTF8 char at line %1%.\nContent: %2%\n");
1✔
164
    fmt % lineNum % faultyLine;
1✔
165
    log(fmt.str());
1✔
166
    return false;
1✔
167
}
168

169
void LuaInterfaceBase::log(const std::string& msg)
33✔
170
{
171
    logger_.write("%s\n") % msg;
33✔
172
}
33✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc