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

Return-To-The-Roots / s25client / 13521743833

25 Feb 2025 12:56PM UTC coverage: 50.353%. First build
13521743833

Pull #1745

github

web-flow
Merge fdbd9d9ab into a9e4c6e57
Pull Request #1745: Don't draw content of ship window if minimized

24 of 73 new or added lines in 9 files covered. (32.88%)

22384 of 44454 relevant lines covered (50.35%)

38156.13 hits per line

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

26.46
/libs/s25main/Loader.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
///////////////////////////////////////////////////////////////////////////////
6

7
#include "Loader.h"
8
#include "ListDir.h"
9
#include "RttrConfig.h"
10
#include "Settings.h"
11
#include "Timer.h"
12
#include "WineLoader.h"
13
#include "addons/const_addons.h"
14
#include "commonDefines.h"
15
#include "convertSounds.h"
16
#include "files.h"
17
#include "helpers/EnumRange.h"
18
#include "helpers/Range.h"
19
#include "helpers/containerUtils.h"
20
#include "ogl/MusicItem.h"
21
#include "ogl/SoundEffectItem.h"
22
#include "ogl/glArchivItem_Bitmap_Player.h"
23
#include "ogl/glArchivItem_Bitmap_RLE.h"
24
#include "ogl/glArchivItem_Bitmap_Raw.h"
25
#include "ogl/glArchivItem_Bob.h"
26
#include "ogl/glArchivItem_Sound_Wave.h"
27
#include "ogl/glFont.h"
28
#include "ogl/glSmartBitmap.h"
29
#include "ogl/glTexturePacker.h"
30
#include "resources/ArchiveLoader.h"
31
#include "resources/ArchiveLocator.h"
32
#include "resources/ResolvedFile.h"
33
#include "gameTypes/Direction.h"
34
#include "gameTypes/DirectionToImgDir.h"
35
#include "gameData/JobConsts.h"
36
#include "gameData/MilitaryConsts.h"
37
#include "gameData/NationConsts.h"
38
#include "libsiedler2/ArchivItem_Font.h"
39
#include "libsiedler2/ArchivItem_Palette.h"
40
#include "libsiedler2/ArchivItem_PaletteAnimation.h"
41
#include "libsiedler2/ArchivItem_Text.h"
42
#include "libsiedler2/ErrorCodes.h"
43
#include "libsiedler2/PixelBufferBGRA.h"
44
#include "libsiedler2/PixelBufferPaletted.h"
45
#include "libsiedler2/libsiedler2.h"
46
#include "s25util/Log.h"
47
#include "s25util/StringConversion.h"
48
#include "s25util/System.h"
49
#include "s25util/strAlgos.h"
50
#include <boost/filesystem.hpp>
51
#include <boost/pointer_cast.hpp>
52
#include <boost/range/adaptor/map.hpp>
53
#include <algorithm>
54
#include <chrono>
55
#include <cstdio>
56
#include <iomanip>
57
#include <memory>
58
#include <sstream>
59
#include <stdexcept>
60

61
struct Loader::FileEntry
62
{
63
    libsiedler2::Archiv archive;
64
    /// List of files used to build this archive
65
    ResolvedFile resolvedFile;
66
};
67

68
template<typename T>
69
static T convertChecked(libsiedler2::ArchivItem* item)
1,604✔
70
{
71
    T res = dynamic_cast<T>(item);
1,604✔
72
    RTTR_Assert(!item || res);
1,604✔
73
    return res;
1,604✔
74
}
75

76
Loader::Loader(Log& logger, const RttrConfig& config)
5✔
77
    : logger_(logger), config_(config), archiveLocator_(std::make_unique<ArchiveLocator>(logger)),
78
      archiveLoader_(std::make_unique<ArchiveLoader>(logger)), isWinterGFX_(false), nation_gfx(), nationIcons_(),
79
      map_gfx(nullptr), stp(nullptr)
45✔
80
{}
5✔
81

82
Loader::~Loader() = default;
5✔
83

84
void Loader::initResourceFolders(const std::vector<Nation>& usedNations, const std::vector<AddonId>& enabledAddons)
1✔
85
{
86
    addDefaultResourceFolders(config_, *archiveLocator_, usedNations, enabledAddons);
1✔
87
}
1✔
88

89
glArchivItem_Bitmap* Loader::GetImageN(const ResourceId& file, unsigned nr)
1,582✔
90
{
91
    return convertChecked<glArchivItem_Bitmap*>(files_[file].archive[nr]);
1,582✔
92
}
93

94
ITexture* Loader::GetTextureN(const ResourceId& file, unsigned nr)
14✔
95
{
96
    return convertChecked<ITexture*>(files_[file].archive[nr]);
14✔
97
}
98

99
glArchivItem_Bitmap* Loader::GetImage(const ResourceId& file, const std::string& name)
×
100
{
101
    return convertChecked<glArchivItem_Bitmap*>(files_[file].archive.find(name));
×
102
}
103

104
glArchivItem_Bitmap_Player* Loader::GetPlayerImage(const ResourceId& file, unsigned nr)
×
105
{
106
    return convertChecked<glArchivItem_Bitmap_Player*>(files_[file].archive[nr]);
×
107
}
108

109
glFont* Loader::GetFont(FontSize size)
464✔
110
{
111
    return fonts.empty() ? nullptr : &fonts[static_cast<unsigned>(size)];
464✔
112
}
113

114
libsiedler2::ArchivItem_Palette* Loader::GetPaletteN(const ResourceId& file, unsigned nr)
58✔
115
{
116
    return dynamic_cast<libsiedler2::ArchivItem_Palette*>(files_[file].archive[nr]);
58✔
117
}
118

119
SoundEffectItem* Loader::GetSoundN(const ResourceId& file, unsigned nr)
131✔
120
{
121
    return dynamic_cast<SoundEffectItem*>(files_[file].archive[nr]);
131✔
122
}
123

124
std::string Loader::GetTextN(const ResourceId& file, unsigned nr)
×
125
{
126
    auto* archive = dynamic_cast<libsiedler2::ArchivItem_Text*>(files_[file].archive[nr]);
×
127
    return archive ? archive->getText() : "text missing";
×
128
}
129

130
libsiedler2::Archiv& Loader::GetArchive(const ResourceId& file)
16✔
131
{
132
    RTTR_Assert(helpers::contains(files_, file));
16✔
133
    return files_[file].archive;
16✔
134
}
135

136
glArchivItem_Bob* Loader::GetBob(const ResourceId& file)
×
137
{
138
    return dynamic_cast<glArchivItem_Bob*>(files_[file].archive.get(0));
×
139
}
140

141
glArchivItem_BitmapBase* Loader::GetNationImageN(Nation nation, unsigned nr)
×
142
{
143
    return dynamic_cast<glArchivItem_BitmapBase*>(nation_gfx[nation]->get(nr));
×
144
}
145

146
glArchivItem_Bitmap* Loader::GetNationImage(Nation nation, unsigned nr)
×
147
{
148
    return checkedCast<glArchivItem_Bitmap*>(GetNationImageN(nation, nr));
×
149
}
150

151
glArchivItem_Bitmap* Loader::GetNationIcon(Nation nation, BuildingType bld)
×
152
{
153
    if(bld == BuildingType::Charburner)
×
154
        return LOADER.GetImageN("charburner", rttr::enum_cast(nation) * 8 + 8);
×
155
    else
156
        return convertChecked<glArchivItem_Bitmap*>(nationIcons_[nation]->get(rttr::enum_cast(bld)));
×
157
}
158

159
ITexture* Loader::GetBuildingTex(Nation nation, BuildingType bld)
×
160
{
161
    if(bld == BuildingType::Charburner)
×
162
    {
163
        unsigned id = rttr::enum_cast(nation) * 8;
×
164
        return GetImageN("charburner", id + (isWinterGFX_ ? 6 : 1));
×
165
    } else
166
        return checkedCast<ITexture*>(GetNationImage(nation, 250 + rttr::enum_cast(bld) * 5));
×
167
    return nullptr;
168
}
169

170
glArchivItem_Bitmap_Player* Loader::GetNationPlayerImage(Nation nation, unsigned nr)
×
171
{
172
    return checkedCast<glArchivItem_Bitmap_Player*>(GetNationImageN(nation, nr));
×
173
}
174

175
glArchivItem_Bitmap* Loader::GetMapImage(unsigned nr)
×
176
{
177
    return convertChecked<glArchivItem_Bitmap*>(map_gfx->get(nr));
×
178
}
179

180
ITexture* Loader::GetMapTexture(unsigned nr)
8✔
181
{
182
    return convertChecked<ITexture*>(map_gfx->get(nr));
8✔
183
}
184

185
ITexture* Loader::GetWareTex(GoodType ware)
×
186
{
187
    if(wineaddon::isWineAddonGoodType(ware))
×
188
        return wineaddon::GetWareTex(ware);
×
189
    else
190
        return GetMapTexture(WARES_TEX_MAP_OFFSET + rttr::enum_cast(ware));
×
191
}
192

193
ITexture* Loader::GetWareStackTex(GoodType ware)
×
194
{
195
    if(wineaddon::isWineAddonGoodType(ware))
×
196
        return wineaddon::GetWareStackTex(ware);
×
197
    else
198
        return GetMapTexture(WARE_STACK_TEX_MAP_OFFSET + rttr::enum_cast(ware));
×
199
}
200

201
ITexture* Loader::GetWareDonkeyTex(GoodType ware)
×
202
{
203
    if(wineaddon::isWineAddonGoodType(ware))
×
204
        return wineaddon::GetWareDonkeyTex(ware);
×
205
    else
206
        return GetMapTexture(WARES_DONKEY_TEX_MAP_OFFSET + rttr::enum_cast(ware));
×
207
}
208

209
ITexture* Loader::GetJobTex(Job job)
×
210
{
211
    if(wineaddon::isWineAddonJobType(job))
×
212
        return wineaddon::GetJobTex(job);
×
213
    else
214
        return (job == Job::CharBurner) ? GetTextureN("io_new", 5) : GetMapTexture(2300 + rttr::enum_cast(job));
×
215
}
216

217
glArchivItem_Bitmap_Player* Loader::GetMapPlayerImage(unsigned nr)
×
218
{
219
    return convertChecked<glArchivItem_Bitmap_Player*>(map_gfx->get(nr));
×
220
}
221

222
/**
223
 *  Load general files required also outside of games
224
 *
225
 *  @return @p true on success, @p false on error.
226
 */
227
bool Loader::LoadFilesAtStart()
×
228
{
229
    namespace res = s25::resources;
230
    // Palettes
231
    if(!LoadFiles({res::pal5, res::pal6, res::pal7, res::paletti0, res::paletti1, res::paletti8})
×
232
       || !Load(ResourceId("colors")))
×
233
        return false;
×
234

235
    if(!LoadFonts())
×
236
        return false;
×
237

238
    std::vector<std::string> files = {res::resource,
239
                                      res::io,                       // Menu graphics
240
                                      res::setup013, res::setup015}; // Backgrounds for options and free play
×
241

242
    const std::array<bfs::path, 2> loadScreenFolders{config_.ExpandPath(s25::folders::loadScreens),
×
243
                                                     config_.ExpandPath(s25::folders::loadScreensMissions)};
×
244
    for(const std::string& loadScreenId : LOAD_SCREENS)
×
245
    {
246
        const std::string filename = s25util::toUpper(loadScreenId) + ".LBM";
×
247
        if(exists(loadScreenFolders[0] / filename))
×
248
            files.push_back((loadScreenFolders[0] / filename).string());
×
249
        else
250
            files.push_back((loadScreenFolders[1] / filename).string());
×
251
    }
252

253
    if(!LoadFiles(files))
×
254
        return false;
×
255

256
    if(!LoadSounds())
×
257
        return false;
×
258

259
    return LoadResources({"io_new", "client", "languages", "logo", "menu", "rttr"});
×
260
}
261

262
bool Loader::LoadSounds()
×
263
{
264
    if(!Load(config_.ExpandPath(s25::files::soundOrig)))
×
265
        return false;
×
266
    const Timer timer(true);
×
267
    logger_.write(_("Starting sound conversion: "));
×
268
    try
269
    {
270
        convertSounds(GetArchive("sound"), config_.ExpandPath(s25::files::soundScript));
×
271
    } catch(const std::runtime_error& e)
×
272
    {
273
        logger_.write(_("failed: %1%\n")) % e.what();
×
274
        return false;
×
275
    }
276
    using namespace std::chrono;
277
    logger_.write(_("done in %ums\n")) % duration_cast<milliseconds>(timer.getElapsed()).count();
×
278

279
    const bfs::path oggPath = config_.ExpandPath(s25::folders::sng);
×
280
    std::vector<bfs::path> oggFiles = ListDir(oggPath, "ogg");
×
281

282
    sng_lst.reserve(oggFiles.size());
×
283
    for(const auto& oggFile : oggFiles)
×
284
    {
285
        try
286
        {
287
            libsiedler2::Archiv sng = archiveLoader_->loadFileOrDir(oggFile);
×
288
            auto music = boost::dynamic_pointer_cast<MusicItem>(sng.release(0));
×
289
            if(music)
×
290
                sng_lst.emplace_back(std::move(music));
×
291
            else
292
                logger_.write(_("WARNING: Found invalid music item for %1%\n")) % oggFile;
×
293
        } catch(const LoadError&)
×
294
        {
295
            return false;
×
296
        }
297
    }
298

299
    if(sng_lst.empty())
×
300
    {
301
        logger_.write(_("WARNING: Did not find the music files.\n\tYou have to run the updater once or copy the .ogg "
×
302
                        "files manually to %1% or you won't be able to hear the music.\n"))
303
          % oggPath;
×
304
    }
305

306
    return true;
×
307
}
308

309
bool Loader::LoadFonts()
1✔
310
{
311
    if(!Load(ResourceId("fonts"), GetPaletteN("pal5")))
2✔
312
        return false;
×
313
    fonts.clear();
1✔
314
    const auto& loadedFonts = GetArchive("fonts");
1✔
315
    for(const auto i : helpers::enumRange<FontSize>())
10✔
316
    {
317
        const auto* curFont = dynamic_cast<const libsiedler2::ArchivItem_Font*>(loadedFonts[rttr::enum_cast(i)]);
3✔
318
        if(!curFont)
3✔
319
        {
NEW
320
            logger_.write(_("Unable to load font at index %1%\n")) % rttr::enum_cast(i);
×
321
            return false;
×
322
        }
323
        fonts.emplace_back(*curFont);
3✔
324
    }
325
    return true;
1✔
326
}
327

328
void Loader::LoadDummyGUIFiles()
3✔
329
{
330
    // Palettes
331
    {
332
        auto palette = std::make_unique<libsiedler2::ArchivItem_Palette>();
3✔
333
        for(const auto i : helpers::range(256))
1,548✔
334
            palette->set(i, libsiedler2::ColorRGB(42, 137, i));
768✔
335
        files_["pal5"].archive.pushC(*palette);
6✔
336
        // Player color palette
337
        for(const auto i : helpers::range(128, 128 + libsiedler2::ArchivItem_Bitmap_Player::numPlayerClrs))
36✔
338
            palette->set(i, libsiedler2::ColorRGB(i, i, i));
12✔
339
        files_["colors"].archive.push(std::move(palette));
6✔
340
    }
341
    // GUI elements
342
    libsiedler2::Archiv& resource = files_["resource"].archive;
6✔
343
    resource.alloc(57);
3✔
344
    for(const auto id : helpers::range(4u, 36u))
204✔
345
    {
346
        auto bmp = std::make_unique<glArchivItem_Bitmap_RLE>();
192✔
347
        libsiedler2::PixelBufferBGRA buffer(1, 1);
384✔
348
        bmp->create(buffer);
96✔
349
        resource.set(id, std::move(bmp));
96✔
350
    }
351
    for(const auto id : helpers::range(36u, 57u))
138✔
352
    {
353
        auto bmp = std::make_unique<glArchivItem_Bitmap_Raw>();
126✔
354
        libsiedler2::PixelBufferBGRA buffer(1, 1);
252✔
355
        bmp->create(buffer);
63✔
356
        resource.set(id, std::move(bmp));
63✔
357
    }
358
    libsiedler2::Archiv& io = files_["io"].archive;
6✔
359
    for([[maybe_unused]] const auto id : helpers::range(264u))
1,596✔
360
    {
361
        auto bmp = std::make_unique<glArchivItem_Bitmap_Raw>();
1,584✔
362
        libsiedler2::PixelBufferBGRA buffer(1, 1);
3,168✔
363
        bmp->create(buffer);
792✔
364
        io.push(std::move(bmp));
792✔
365
    }
366
    libsiedler2::Archiv& io_new = files_["io_new"].archive;
6✔
367
    for([[maybe_unused]] const auto id : helpers::range(21u))
138✔
368
    {
369
        auto bmp = std::make_unique<glArchivItem_Bitmap_Raw>();
126✔
370
        libsiedler2::PixelBufferBGRA buffer(1, 1);
252✔
371
        bmp->create(buffer);
63✔
372
        io_new.push(std::move(bmp));
63✔
373
    }
374
    // Fonts
375
    auto* palette = GetPaletteN("colors");
3✔
376
    libsiedler2::PixelBufferBGRA buffer(15, 16);
15✔
377
    for(const auto i : helpers::enumRange<FontSize>())
30✔
378
    {
379
        auto font = std::make_unique<libsiedler2::ArchivItem_Font>();
18✔
380
        const auto offset = rttr::enum_cast(i) * (helpers::MaxEnumValue_v<FontSize> + 1);
9✔
381
        const unsigned dx = 9 + offset;
9✔
382
        const unsigned dy = 10 + offset;
9✔
383
        font->setDx(dx);
9✔
384
        font->setDy(dy);
9✔
385
        font->alloc(255);
9✔
386
        for(const auto id : helpers::range(0x20u, 255u))
4,050✔
387
        {
388
            auto bmp = std::make_unique<glArchivItem_Bitmap_Player>();
2,007✔
389
            bmp->create(dx, dy, buffer, palette, 0);
2,007✔
390
            font->set(id, std::move(bmp));
2,007✔
391
        }
392
        fonts.emplace_back(*font);
9✔
393
    }
394
}
3✔
395

396
void Loader::LoadDummyMapFiles()
1✔
397
{
398
    libsiedler2::Archiv& map = files_["map_0_z"].archive;
2✔
399
    if(!map.empty())
1✔
400
        return;
×
401
    const auto pushRange = [&map](unsigned from, unsigned to) {
322✔
402
        map.alloc_inc(to - map.size() + 1);
10✔
403
        libsiedler2::PixelBufferBGRA buffer(1, 1);
50✔
404
        for(const auto i : helpers::range(from, to + 1))
664✔
405
        {
406
            auto bmp = std::make_unique<glArchivItem_Bitmap_Raw>();
312✔
407
            bmp->create(buffer);
312✔
408
            map.set(i, std::move(bmp));
312✔
409
        };
410
    };
10✔
411
    map_gfx = &map;
1✔
412

413
    // Some ID ranges as found in map_0_z.lst
414
    pushRange(20, 23);
1✔
415
    pushRange(40, 46);
1✔
416
    pushRange(50, 55);
1✔
417
    pushRange(59, 67);
1✔
418
    pushRange(200, 282);
1✔
419
    pushRange(290, 334);
1✔
420
    pushRange(350, 432);
1✔
421
    pushRange(440, 484);
1✔
422
    pushRange(500, 527);
1✔
423
    pushRange(560, 561);
1✔
424

425
    for(const auto j : helpers::range(0, 6))
16✔
426
    {
427
        libsiedler2::Archiv& bobs = files_[ResourceId("mis" + std::to_string(j) + "bobs")].archive;
12✔
428
        libsiedler2::PixelBufferBGRA buffer(1, 1);
30✔
429
        for([[maybe_unused]] const auto i : helpers::range(0, 11))
156✔
430
        {
431
            auto bmp = std::make_unique<glArchivItem_Bitmap_Raw>();
66✔
432
            bmp->create(buffer);
66✔
433
            bobs.push(std::move(bmp));
66✔
434
        }
435
    }
436
}
437

438
void Loader::LoadDummySoundFiles()
3✔
439
{
440
    libsiedler2::Archiv& archive = files_["sound"].archive;
6✔
441
    archive.alloc(116);
3✔
442
    for(const auto id : helpers::range<unsigned>(51u, archive.size()))
402✔
443
    {
444
        auto snd = std::make_unique<glArchivItem_Sound_Wave>();
195✔
445
        archive.set(id, std::move(snd));
195✔
446
    }
447
}
3✔
448

449
namespace {
450
struct NationResourcesSource
451
{
452
    bfs::path buildingsFilePath, iconsFilePath;
453
};
454

455
NationResourcesSource getNationResourcesSource(const Nation nation, bool isWinter, const RttrConfig& config)
×
456
{
457
    const auto shortName = std::string(NationNames[nation], 0, 3);
×
458
    auto buildingsFilename = shortName + "_Z.LST";
×
459
    auto iconsFilename = shortName + "_ICON.LST";
×
460
    if(isWinter)
×
461
        buildingsFilename.insert(buildingsFilename.begin(), 'W');
×
462
    bfs::path nationFolder;
×
463
    // The original ("native") nations use uppercase file names while we use lowercase names for the new ones
464
    // Especially relevant for case sensitive file systems
465
    if(rttr::enum_cast(nation) < NUM_NATIVE_NATIONS)
×
466
    {
467
        nationFolder = config.ExpandPath(s25::folders::mbob);
×
468
        buildingsFilename = s25util::toUpper(buildingsFilename);
×
469
        iconsFilename = s25util::toUpper(iconsFilename);
×
470
    } else
471
    {
472
        nationFolder = config.ExpandPath(s25::folders::assetsNations) / NationNames[nation];
×
473
        buildingsFilename = s25util::toLower(buildingsFilename);
×
474
        iconsFilename = s25util::toLower(iconsFilename);
×
475
    }
476
    NationResourcesSource result;
×
477
    result.buildingsFilePath = nationFolder / buildingsFilename;
×
478
    result.iconsFilePath = nationFolder / iconsFilename;
×
479
    return result;
×
480
}
481
} // namespace
482

483
/**
484
 *  Load files required during a game
485
 *
486
 *  @param[in] mapGfxPath Path to map gfx files
487
 *  @param[in] isWinterGFX True iff winter nation files should be loaded
488
 *  @param[in] nations True entry for each nation to load
489
 *
490
 *  @return @p true on success
491
 */
492
bool Loader::LoadFilesAtGame(const std::string& mapGfxPath, bool isWinterGFX, const std::vector<Nation>& nations,
×
493
                             const std::vector<AddonId>& enabledAddons)
494
{
495
    initResourceFolders(nations, enabledAddons);
×
496

497
    namespace res = s25::resources;
498
    std::vector<std::string> files = {res::rom_bobs, res::carrier,  res::jobs,     res::boat,
499
                                      res::boot_z,   res::mis0bobs, res::mis1bobs, res::mis2bobs,
500
                                      res::mis3bobs, res::mis4bobs, res::mis5bobs};
×
501

502
    const libsiedler2::ArchivItem_Palette* pal5 = GetPaletteN("pal5");
×
503

504
    if(!LoadFiles(files) || !Load(ResourceId("map_new"), pal5))
×
505
        return false;
×
506

507
    // Load nation building and icon graphics
508
    nation_gfx = nationIcons_ = {};
×
509
    for(Nation nation : nations)
×
510
    {
511
        const auto resourceSource = getNationResourcesSource(nation, isWinterGFX, config_);
×
512
        if(!Load(resourceSource.buildingsFilePath, pal5) || !Load(resourceSource.iconsFilePath, pal5))
×
513
            return false;
×
514
        nation_gfx[nation] = &files_[ResourceId::make(resourceSource.buildingsFilePath)].archive;
×
515
        nationIcons_[nation] = &files_[ResourceId::make(resourceSource.iconsFilePath)].archive;
×
516
    }
517

518
    // TODO: Move to addon folder and make it overwrite existing file
519
    if(!LoadResources({"charburner", "charburner_bobs"}))
×
520
        return false;
×
521

522
    if(!LoadResources({"wine_bobs"}))
×
523
        return false;
×
524

525
    const bfs::path mapGFXFile = config_.ExpandPath(mapGfxPath);
×
526
    if(!Load(mapGFXFile, pal5))
×
527
        return false;
×
528
    map_gfx = &GetArchive(ResourceId::make(mapGFXFile));
×
529

530
    isWinterGFX_ = isWinterGFX;
×
531

532
    return true;
×
533
}
534

535
bool Loader::LoadFiles(const std::vector<std::string>& files)
3✔
536
{
537
    const libsiedler2::ArchivItem_Palette* pal5 = GetPaletteN("pal5");
3✔
538
    // load the files
539
    for(const std::string& curFile : files)
18✔
540
    {
541
        const bfs::path filePath = config_.ExpandPath(curFile);
15✔
542
        if(!Load(filePath, pal5))
15✔
543
        {
544
            logger_.write(_("Failed to load %s\n")) % filePath;
×
545
            return false;
×
546
        }
547
    }
548

549
    return true;
3✔
550
}
551

552
bool Loader::LoadResources(const std::vector<ResourceId>& resources)
×
553
{
554
    const libsiedler2::ArchivItem_Palette* pal5 = GetPaletteN("pal5");
×
555
    for(const ResourceId& curResource : resources)
×
556
    {
557
        if(!Load(curResource, pal5))
×
558
        {
559
            logger_.write(_("Failed to load %s\n")) % curResource;
×
560
            return false;
×
561
        }
562
    }
563

564
    return true;
×
565
}
566

567
void Loader::fillCaches()
×
568
{
569
    stp = std::make_unique<glTexturePacker>();
×
570

571
    // Animals
572
    for(const auto species : helpers::EnumRange<Species>{})
×
573
    {
574
        for(const auto dir : helpers::EnumRange<Direction>{})
×
575
        {
NEW
576
            for(const auto ani_step : helpers::range(ANIMALCONSTS[species].animation_steps))
×
577
            {
578
                glSmartBitmap& bmp = getAnimalSprite(Species(species), dir, ani_step);
×
579

580
                bmp.reset();
×
581

582
                bmp.add(GetMapImage(ANIMALCONSTS[species].walking_id
×
583
                                    + ANIMALCONSTS[species].animation_steps * rttr::enum_cast(dir + 3u) + ani_step));
×
584

585
                if(ANIMALCONSTS[species].shadow_id)
×
586
                {
587
                    if(species == Species::Duck)
×
588
                        // Ente Sonderfall, da gibts nur einen Schatten für jede Richtung!
589
                        bmp.addShadow(GetMapImage(ANIMALCONSTS[species].shadow_id));
×
590
                    else
591
                        // ansonsten immer pro Richtung einen Schatten
592
                        bmp.addShadow(GetMapImage(ANIMALCONSTS[species].shadow_id + rttr::enum_cast(dir + 3u)));
×
593
                }
594

595
                stp->add(bmp);
×
596
            }
597
        }
598

599
        glSmartBitmap& bmp = getDeadAnimalSprite(species);
×
600

601
        bmp.reset();
×
602

603
        if(ANIMALCONSTS[species].dead_id)
×
604
        {
605
            bmp.add(GetMapImage(ANIMALCONSTS[species].dead_id));
×
606

607
            if(ANIMALCONSTS[species].shadow_dead_id)
×
608
            {
609
                bmp.addShadow(GetMapImage(ANIMALCONSTS[species].shadow_dead_id));
×
610
            }
611

612
            stp->add(bmp);
×
613
        }
614
    }
615

616
    glArchivItem_Bob* bob_jobs = GetBob("jobs");
×
617
    if(!bob_jobs)
×
618
        throw std::runtime_error("jobs not found");
×
619

620
    for(const auto nation : helpers::enumRange<Nation>())
×
621
    {
622
        if(!nation_gfx[nation])
×
623
            continue;
×
624
        // BUILDINGS
625
        for(const auto type : helpers::enumRange<BuildingType>())
×
626
        {
627
            BuildingSprites& sprites = building_cache[nation][type];
×
628

629
            sprites.building.reset();
×
630
            sprites.skeleton.reset();
×
631
            sprites.door.reset();
×
632

633
            if(type == BuildingType::Charburner)
×
634
            {
635
                unsigned id = rttr::enum_cast(nation) * 8;
×
636

637
                sprites.building.add(GetImageN("charburner", id + (isWinterGFX_ ? 6 : 1)));
×
638
                sprites.building.addShadow(GetImageN("charburner", id + 2));
×
639

640
                sprites.skeleton.add(GetImageN("charburner", id + 3));
×
641
                sprites.skeleton.addShadow(GetImageN("charburner", id + 4));
×
642

643
                sprites.door.add(GetImageN("charburner", id + (isWinterGFX_ ? 7 : 5)));
×
644
            } else
645
            {
646
                sprites.building.add(GetNationImage(nation, 250 + 5 * rttr::enum_cast(type)));
×
647
                sprites.building.addShadow(GetNationImage(nation, 250 + 5 * rttr::enum_cast(type) + 1));
×
648
                if(type == BuildingType::Headquarters)
×
649
                {
650
                    // HQ has no skeleton, but we have a tent that can act as an HQ
651
                    sprites.skeleton.add(GetImageN("mis0bobs", 6));
×
652
                    sprites.skeleton.addShadow(GetImageN("mis0bobs", 7));
×
653
                } else
654
                {
655
                    sprites.skeleton.add(GetNationImage(nation, 250 + 5 * rttr::enum_cast(type) + 2));
×
656
                    sprites.skeleton.addShadow(GetNationImage(nation, 250 + 5 * rttr::enum_cast(type) + 3));
×
657
                }
658
                sprites.door.add(GetNationImage(nation, 250 + 5 * rttr::enum_cast(type) + 4));
×
659
            }
660

661
            stp->add(sprites.building);
×
662
            stp->add(sprites.skeleton);
×
663
            stp->add(sprites.door);
×
664
        }
665

666
        // FLAGS
667
        for(const auto type : helpers::enumRange<FlagType>())
×
668
        {
NEW
669
            for(const auto ani_step : helpers::range(8))
×
670
            {
671
                // Flaggentyp berücksichtigen
672
                int nr = ani_step + 100 + 20 * rttr::enum_cast(type);
×
673

674
                glSmartBitmap& bmp = flag_cache[nation][type][ani_step];
×
675

676
                bmp.reset();
×
677

678
                bmp.add(GetNationPlayerImage(nation, nr));
×
679
                bmp.addShadow(GetNationImage(nation, nr + 10));
×
680

681
                stp->add(bmp);
×
682
            }
683
        }
684

685
        // Bobs from jobs.bob.
686
        for(const auto job : helpers::enumRange<Job>())
×
687
        {
688
            for(Direction dir : helpers::EnumRange<Direction>{})
×
689
            {
NEW
690
                for(const auto ani_step : helpers::range(8u))
×
691
                {
692
                    glSmartBitmap& bmp = bob_jobs_cache[nation][job][dir][ani_step];
×
693
                    bmp.reset();
×
694

695
                    const auto& spriteData = JOB_SPRITE_CONSTS[Job(job)];
×
696
                    const libsiedler2::ImgDir imgDir = toImgDir(dir);
×
697

698
                    bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(
×
699
                      bob_jobs->getBody(spriteData.isFat(), imgDir, ani_step)));
×
700
                    bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(
×
701
                      bob_jobs->getOverlay(spriteData.getBobId(Nation(nation)), spriteData.isFat(), imgDir, ani_step)));
×
702
                    bmp.addShadow(GetMapImage(900 + static_cast<unsigned>(imgDir) * 8 + ani_step));
×
703

704
                    stp->add(bmp);
×
705
                }
706
            }
707
        }
708
        // Fat carrier
709
        for(Direction dir : helpers::EnumRange<Direction>{})
×
710
        {
NEW
711
            for(const auto ani_step : helpers::range(8u))
×
712
            {
713
                glSmartBitmap& bmp = fat_carrier_cache[nation][dir][ani_step];
×
714
                bmp.reset();
×
715

716
                const libsiedler2::ImgDir imgDir = toImgDir(dir);
×
717

718
                bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(bob_jobs->getBody(true, imgDir, ani_step)));
×
719
                bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(bob_jobs->getOverlay(0, true, imgDir, ani_step)));
×
720
                bmp.addShadow(GetMapImage(900 + static_cast<unsigned>(imgDir) * 8 + ani_step));
×
721

722
                stp->add(bmp);
×
723
            }
724
        }
725

726
        {
727
            glSmartBitmap& bmp = boundary_stone_cache[nation];
×
728
            bmp.reset();
×
729

730
            bmp.add(GetNationPlayerImage(nation, 0));
×
731
            bmp.addShadow(GetNationImage(nation, 1));
×
732

733
            stp->add(bmp);
×
734
        }
735

736
        libsiedler2::Archiv& romBobs = GetArchive("rom_bobs");
×
737
        const auto getAlternative = [&romBobs](unsigned id, unsigned altId) {
×
738
            auto* bmp = convertChecked<glArchivItem_Bitmap_Player*>(romBobs[id]);
×
739
            return bmp ? bmp : convertChecked<glArchivItem_Bitmap_Player*>(romBobs[altId]);
×
740
        };
×
741
        // Special handling for non-native nations: Use roman animations if own are missing
742
        const Nation fallbackNation = rttr::enum_cast(nation) < NUM_NATIVE_NATIONS ? nation : Nation::Romans;
×
743
        const auto& natFightAnimIds = FIGHT_ANIMATIONS[nation];
×
744
        const auto& altNatFightAnimIds = FIGHT_ANIMATIONS[fallbackNation];
×
745
        const auto& natHitIds = HIT_SOLDIERS[nation];
×
746
        const auto& altNatHitIds = HIT_SOLDIERS[fallbackNation];
×
NEW
747
        for(const auto rank : helpers::range(NUM_SOLDIER_RANKS))
×
748
        {
NEW
749
            for(const auto dir : helpers::range(2))
×
750
            {
751
                FightSprites& sprites = fight_cache[nation][rank][dir];
×
752
                const auto& fightAnimIds = natFightAnimIds[rank][dir];
×
753
                const auto& altFightAnimIds = altNatFightAnimIds[rank][dir];
×
NEW
754
                for(const auto ani_step : helpers::range(8u))
×
755
                {
756
                    glSmartBitmap& bmp = sprites.attacking[ani_step];
×
757
                    bmp.reset();
×
758
                    bmp.add(getAlternative(fightAnimIds.attacking[ani_step], altFightAnimIds.attacking[ani_step]));
×
759
                    stp->add(bmp);
×
760
                }
761

NEW
762
                for(const auto i : helpers::range(sprites.defending.size()))
×
763
                {
NEW
764
                    for(const auto ani_step : helpers::range(8u))
×
765
                    {
766
                        glSmartBitmap& bmp = sprites.defending[i][ani_step];
×
767
                        bmp.reset();
×
768
                        bmp.add(
×
769
                          getAlternative(fightAnimIds.defending[i][ani_step], altFightAnimIds.defending[i][ani_step]));
×
770
                        stp->add(bmp);
×
771
                    }
772
                }
773
                glSmartBitmap& bmp = sprites.hit;
×
774
                bmp.reset();
×
775
                // Hit sprites for left/right are consecutive
776
                bmp.add(getAlternative(natHitIds[rank] + dir, altNatHitIds[rank] + dir));
×
777
                stp->add(bmp);
×
778
            }
779
        }
780
    }
781

782
    // BUILDING FLAG ANIMATION (for military buildings)
783
    /*
784
        for(const auto ani_step: helpers::range(8))
785
        {
786
            glSmartBitmap &bmp = building_flag_cache[ani_step];
787

788
            bmp.reset();
789

790
            bmp.add(static_cast<glArchivItem_Bitmap_Player *>(GetMapTexture(3162+ani_step)));
791

792
            int a, b, c, d;
793
            static_cast<glArchivItem_Bitmap_Player *>(GetMapTexture(3162+ani_step))->getVisibleArea(a, b, c, d);
794
            fprintf(stderr, "%i,%i (%ix%i)\n", a, b, c, d);
795

796

797
            stp->add(bmp);
798
        }
799
    */
800
    // Trees
NEW
801
    for(const auto type : helpers::range(9))
×
802
    {
NEW
803
        for(const auto ani_step : helpers::range(15))
×
804
        {
805
            glSmartBitmap& bmp = tree_cache[type][ani_step];
×
806

807
            bmp.reset();
×
808

809
            bmp.add(GetMapImage(200 + type * 15 + ani_step));
×
810
            bmp.addShadow(GetMapImage(350 + type * 15 + ani_step));
×
811

812
            stp->add(bmp);
×
813
        }
814
    }
815

816
    // Granite
817
    for(const auto type : helpers::enumRange<GraniteType>())
×
818
    {
NEW
819
        for(const auto size : helpers::range(6))
×
820
        {
821
            glSmartBitmap& bmp = granite_cache[type][size];
×
822

823
            bmp.reset();
×
824

825
            bmp.add(GetMapImage(516 + rttr::enum_cast(type) * 6 + size));
×
826
            bmp.addShadow(GetMapImage(616 + rttr::enum_cast(type) * 6 + size));
×
827

828
            stp->add(bmp);
×
829
        }
830
    }
831

832
    // Grainfields
NEW
833
    for(const auto type : helpers::range(2))
×
834
    {
NEW
835
        for(const auto size : helpers::range(4))
×
836
        {
837
            glSmartBitmap& bmp = grainfield_cache[type][size];
×
838

839
            bmp.reset();
×
840

841
            bmp.add(GetMapImage(532 + type * 5 + size));
×
842
            bmp.addShadow(GetMapImage(632 + type * 5 + size));
×
843

844
            stp->add(bmp);
×
845
        }
846
    }
847

848
    // Donkeys
849
    for(const auto dir : helpers::EnumRange<Direction>{})
×
850
    {
NEW
851
        for(const auto ani_step : helpers::range(8u))
×
852
        {
853
            glSmartBitmap& bmp = getDonkeySprite(dir, ani_step);
×
854

855
            bmp.reset();
×
856

857
            bmp.add(GetMapImage(2000 + rttr::enum_cast(dir + 3u) * 8 + ani_step));
×
858
            bmp.addShadow(GetMapImage(2048 + rttr::enum_cast(dir) % 3));
×
859

860
            stp->add(bmp);
×
861
        }
862
    }
863

864
    // Boats
865
    for(const auto dir : helpers::EnumRange<Direction>{})
×
866
    {
NEW
867
        for(const auto ani_step : helpers::range(8u))
×
868
        {
869
            glSmartBitmap& bmp = getBoatCarrierSprite(dir, ani_step);
×
870

871
            bmp.reset();
×
872

873
            bmp.add(GetPlayerImage("boat", rttr::enum_cast(dir + 3u) * 8 + ani_step));
×
874
            bmp.addShadow(GetMapImage(2048 + rttr::enum_cast(dir) % 3));
×
875

876
            stp->add(bmp);
×
877
        }
878
    }
879

880
    wineaddon::fillCache(*stp);
×
881

882
    // carrier_cache[ware][direction][animation_step][fat]
883
    glArchivItem_Bob* bob_carrier = GetBob("carrier");
×
884
    if(!bob_carrier)
×
885
        throw std::runtime_error("carrier not found");
×
886

887
    libsiedler2::Archiv wine_bob_carrier = GetArchive("wine_bobs");
×
888

889
    for(bool fat : {true, false})
×
890
    {
891
        for(const auto ware : helpers::EnumRange<GoodType>{})
×
892
        {
893
            for(Direction dir : helpers::EnumRange<Direction>{})
×
894
            {
NEW
895
                for(const auto ani_step : helpers::range(8u))
×
896
                {
897
                    glSmartBitmap& bmp = getCarrierSprite(ware, fat, dir, ani_step);
×
898
                    bmp.reset();
×
899

900
                    // Japanese shield is missing
901
                    const unsigned id =
902
                      rttr::enum_cast((ware == GoodType::ShieldJapanese) ? GoodType::ShieldRomans : ware);
×
903

904
                    const libsiedler2::ImgDir imgDir = toImgDir(dir);
×
905

906
                    if(ware == GoodType::Grapes)
×
907
                    {
908
                        const unsigned bodyIdx = static_cast<unsigned>(imgDir) * 8 + ani_step;
×
909
                        bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(wine_bob_carrier.get(
×
910
                          wineaddon::bobIndex[fat ? wineaddon::BobTypes::FAT_CARRIER_CARRYING_GRAPES :
911
                                                    wineaddon::BobTypes::THIN_CARRIER_CARRYING_GRAPES]
×
912
                          + bodyIdx)));
×
913
                    } else if(ware == GoodType::Wine)
×
914
                    {
915
                        bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(bob_carrier->getBody(fat, imgDir, ani_step)));
×
916
                        bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(wine_bob_carrier.get(
×
917
                          wineaddon::bobIndex[fat ? wineaddon::BobTypes::FAT_CARRIER_CARRYING_WINE :
918
                                                    wineaddon::BobTypes::THIN_CARRIER_CARRYING_WINE]
×
919
                          + static_cast<unsigned>(imgDir))));
×
920
                    } else
921
                    {
922
                        bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(bob_carrier->getBody(fat, imgDir, ani_step)));
×
923
                        bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(
×
924
                          bob_carrier->getOverlay(id, fat, imgDir, ani_step)));
×
925
                    }
926
                    bmp.addShadow(GetMapImage(900 + static_cast<unsigned>(imgDir) * 8 + ani_step));
×
927

928
                    stp->add(bmp);
×
929
                }
930
            }
931
        }
932
    }
933

934
    // gateway animation :)
935
    {
936
        const unsigned char start_index = 248;
×
937
        const unsigned char color_count = 4;
×
938

939
        libsiedler2::ArchivItem_Palette* palette = GetPaletteN("pal5");
×
940
        auto* image = GetMapImage(561);
×
941
        auto* shadow = GetMapImage(661);
×
942

943
        if((image) && (shadow) && (palette))
×
944
        {
945
            unsigned short width = image->getWidth();
×
946
            unsigned short height = image->getHeight();
×
947

948
            std::vector<unsigned char> buffer(width * height, 254);
×
949

950
            image->print(&buffer.front(), width, height, libsiedler2::TextureFormat::Paletted, palette, 0, 0, 0, 0,
×
951
                         width, height);
952

NEW
953
            for(const auto i : helpers::range(color_count))
×
954
            {
955
                glSmartBitmap& bmp = gateway_cache[i + 1];
×
956

957
                bmp.reset();
×
958

NEW
959
                for(const auto x : helpers::range(width))
×
960
                {
NEW
961
                    for(const auto y : helpers::range(height))
×
962
                    {
963
                        if(buffer[y * width + x] >= start_index && buffer[y * width + x] < start_index + color_count)
×
964
                        {
965
                            if(++buffer[y * width + x] >= start_index + color_count)
×
966
                                buffer[y * width + x] = start_index;
×
967
                        }
968
                    }
969
                }
970

971
                auto bitmap = std::make_unique<glArchivItem_Bitmap_Raw>();
×
972
                bitmap->create(width, height, &buffer.front(), width, height, libsiedler2::TextureFormat::Paletted,
×
973
                               palette);
974
                bitmap->setNx(image->getNx());
×
975
                bitmap->setNy(image->getNy());
×
976

977
                bmp.add(std::move(bitmap));
×
978
                bmp.addShadow(shadow);
×
979

980
                stp->add(bmp);
×
981
            }
×
982
        } else
983
        {
NEW
984
            for(const auto i : helpers::range(color_count))
×
985
            {
986
                glSmartBitmap& bmp = gateway_cache[i + 1];
×
987

988
                bmp.reset();
×
989
            }
990
        }
991
    }
992

993
    if(SETTINGS.video.shared_textures)
×
994
    {
995
        // generate mega texture
996
        stp->pack();
×
997
    } else
998
        stp.reset();
×
999
}
×
1000

1001
/**
1002
 *  Extrahiert eine Textur aus den Daten.
1003
 */
1004
std::unique_ptr<glArchivItem_Bitmap> Loader::ExtractTexture(const glArchivItem_Bitmap& srcImg, const Rect& rect)
×
1005
{
1006
    Extent texSize = rect.getSize();
×
1007
    if(texSize.x == 0 && rect.right < srcImg.getWidth())
×
1008
        texSize.x = srcImg.getWidth() - rect.right;
×
1009
    if(texSize.y == 0 && rect.bottom < srcImg.getHeight())
×
1010
        texSize.y = srcImg.getHeight() - rect.bottom;
×
1011
    libsiedler2::PixelBufferPaletted buffer(texSize.x, texSize.y);
×
1012

1013
    if(int ec = srcImg.print(buffer, nullptr, 0, 0, rect.left, rect.top))
×
1014
        throw std::runtime_error(std::string("Error loading texture: ") + libsiedler2::getErrorString(ec));
×
1015

1016
    std::unique_ptr<glArchivItem_Bitmap> bitmap = std::make_unique<glArchivItem_Bitmap_Raw>();
×
1017
    if(int ec = bitmap->create(buffer, srcImg.getPalette()))
×
1018
    {
1019
        throw std::runtime_error(std::string("Error loading texture: ") + libsiedler2::getErrorString(ec));
×
1020
    }
1021
    return bitmap;
×
1022
}
1023

1024
/**
1025
 *  Extrahiert mehrere (animierte) Texturen aus den Daten.
1026
 */
1027
std::unique_ptr<libsiedler2::Archiv> Loader::ExtractAnimatedTexture(const glArchivItem_Bitmap& srcImg, const Rect& rect,
×
1028
                                                                    uint8_t start_index, uint8_t color_count)
1029
{
1030
    Extent texSize = rect.getSize();
×
1031
    if(texSize.x == 0 && rect.right < srcImg.getWidth())
×
1032
        texSize.x = srcImg.getWidth() - rect.right;
×
1033
    if(texSize.y == 0 && rect.bottom < srcImg.getHeight())
×
1034
        texSize.y = srcImg.getHeight() - rect.bottom;
×
1035
    libsiedler2::PixelBufferPaletted buffer(texSize.x, texSize.y);
×
1036

1037
    srcImg.print(buffer, nullptr, 0, 0, rect.left, rect.top);
×
1038

1039
    auto destination = std::make_unique<libsiedler2::Archiv>();
×
1040
    libsiedler2::ArchivItem_PaletteAnimation anim;
×
1041
    anim.isActive = true;
×
1042
    anim.moveUp = false;
×
1043
    anim.firstClr = start_index;
×
1044
    anim.lastClr = start_index + color_count - 1u;
×
1045
    const libsiedler2::ArchivItem_Palette* curPal = nullptr;
×
NEW
1046
    for(const auto i : helpers::range(color_count))
×
1047
    {
1048
        auto newPal = (i == 0) ? clone(srcImg.getPalette()) : anim.apply(*curPal);
×
1049
        auto bitmap = std::make_unique<glArchivItem_Bitmap_Raw>();
×
1050
        bitmap->setPalette(std::move(newPal));
×
1051
        if(int ec = bitmap->create(buffer))
×
1052
            throw std::runtime_error("Error extracting animated texture: " + libsiedler2::getErrorString(ec));
×
1053
        curPal = bitmap->getPalette();
×
1054
        destination->push(std::move(bitmap));
×
1055
    }
1056
    return destination;
×
1057
}
1058

1059
bool Loader::Load(libsiedler2::Archiv& archive, const bfs::path& path, const libsiedler2::ArchivItem_Palette* palette)
×
1060
{
1061
    try
1062
    {
1063
        archive = archiveLoader_->load(archiveLocator_->resolve(path), palette);
×
1064
        return true;
×
1065
    } catch(const LoadError&)
×
1066
    {
1067
        return false;
×
1068
    }
1069
}
1070

1071
bool Loader::Load(libsiedler2::Archiv& archive, const ResourceId& resId, const libsiedler2::ArchivItem_Palette* palette)
×
1072
{
1073
    const ResolvedFile resolvedFile = archiveLocator_->resolve(resId);
×
1074
    if(!resolvedFile)
×
1075
    {
1076
        logger_.write(_("Failed to resolve resource %1%\n")) % resId;
×
1077
        return false;
×
1078
    }
1079
    try
1080
    {
1081
        archive = archiveLoader_->load(resolvedFile, palette);
×
1082
        return true;
×
1083
    } catch(const LoadError&)
×
1084
    {
1085
        return false;
×
1086
    }
1087
}
1088

1089
template<typename T>
1090
bool Loader::LoadImpl(const T& resIdOrPath, const libsiedler2::ArchivItem_Palette* palette)
24✔
1091
{
1092
    const auto resolvedFile = archiveLocator_->resolve(resIdOrPath);
48✔
1093
    if(!resolvedFile)
24✔
1094
    {
1095
        logger_.write(_("Failed to resolve resource %1%\n")) % resIdOrPath;
×
1096
        return false;
×
1097
    }
1098
    FileEntry& entry = files_[ResourceId::make(resIdOrPath)];
24✔
1099
    // Do we really need to reload or can we reused the loaded version?
1100
    if(entry.resolvedFile != resolvedFile)
24✔
1101
    {
1102
        try
1103
        {
1104
            entry.archive = archiveLoader_->load(resolvedFile, palette);
22✔
1105
        } catch(const LoadError&)
×
1106
        {
1107
            return false;
×
1108
        }
1109
        // Update how we loaded this
1110
        entry.resolvedFile = resolvedFile;
22✔
1111
    }
1112
    RTTR_Assert(!entry.archive.empty());
24✔
1113
    return true;
24✔
1114
}
1115

1116
bool Loader::Load(const bfs::path& path, const libsiedler2::ArchivItem_Palette* palette)
23✔
1117
{
1118
    return LoadImpl(path, palette);
23✔
1119
}
1120

1121
bool Loader::Load(const ResourceId& resId, const libsiedler2::ArchivItem_Palette* palette)
1✔
1122
{
1123
    return LoadImpl(resId, palette);
1✔
1124
}
1125

1126
void addDefaultResourceFolders(const RttrConfig& config, ArchiveLocator& locator,
1✔
1127
                               const std::vector<Nation>& usedNations, const std::vector<AddonId>& enabledAddons)
1128
{
1129
    locator.clear();
1✔
1130
    locator.addAssetFolder(config.ExpandPath(s25::folders::assetsBase));
1✔
1131
    for(Nation nation : usedNations)
1✔
1132
    {
1133
        const auto overrideFolder = config.ExpandPath(s25::folders::assetsNations) / NationNames[nation];
×
1134
        if(bfs::exists(overrideFolder))
×
1135
            locator.addOverrideFolder(overrideFolder);
×
1136
    }
1137
    locator.addOverrideFolder(config.ExpandPath(s25::folders::assetsOverrides));
1✔
1138
    for(AddonId addonId : enabledAddons)
1✔
1139
    {
1140
        const auto overrideFolder = config.ExpandPath(s25::folders::assetsAddons)
×
1141
                                    / s25util::toStringClassic(static_cast<uint32_t>(addonId), true);
×
1142
        if(bfs::exists(overrideFolder))
×
1143
            locator.addOverrideFolder(overrideFolder);
×
1144
    }
1145
    const bfs::path userOverrides = config.ExpandPath(s25::folders::assetsUserOverrides);
3✔
1146
    if(exists(userOverrides))
1✔
1147
        locator.addOverrideFolder(userOverrides);
×
1148
}
1✔
1149

1150
Loader& getGlobalLoader()
2,286✔
1151
{
1152
    static Loader loader{LOG, RTTRCONFIG};
2,286✔
1153
    return loader;
2,286✔
1154
}
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