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

Return-To-The-Roots / s25client / 22044569181

15 Feb 2026 10:50PM UTC coverage: 50.34% (-0.5%) from 50.826%
22044569181

Pull #1720

github

web-flow
Merge 4dbe54b70 into 6db06730b
Pull Request #1720: Add leather addon

274 of 1055 new or added lines in 65 files covered. (25.97%)

286 existing lines in 28 files now uncovered.

23017 of 45723 relevant lines covered (50.34%)

43559.46 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

196
ITexture* Loader::GetWareStackTex(GoodType ware)
×
197
{
198
    if(wineaddon::isWineAddonGoodType(ware))
×
199
        return wineaddon::GetWareStackTex(ware);
×
NEW
200
    else if(leatheraddon::isLeatherAddonGoodType(ware))
×
NEW
201
        return leatheraddon::GetWareStackTex(ware);
×
202
    else
203
        return GetMapTexture(WARE_STACK_TEX_MAP_OFFSET + rttr::enum_cast(ware));
×
204
}
205

206
ITexture* Loader::GetWareDonkeyTex(GoodType ware)
×
207
{
208
    if(wineaddon::isWineAddonGoodType(ware))
×
209
        return wineaddon::GetWareDonkeyTex(ware);
×
NEW
210
    if(leatheraddon::isLeatherAddonGoodType(ware))
×
NEW
211
        return leatheraddon::GetWareDonkeyTex(ware);
×
212
    else
213
        return GetMapTexture(WARES_DONKEY_TEX_MAP_OFFSET + rttr::enum_cast(ware));
×
214
}
215

216
ITexture* Loader::GetJobTex(Job job)
×
217
{
218
    if(wineaddon::isWineAddonJobType(job))
×
219
        return wineaddon::GetJobTex(job);
×
NEW
220
    else if(leatheraddon::isLeatherAddonJobType(job))
×
NEW
221
        return leatheraddon::GetJobTex(job);
×
222
    else
223
        return (job == Job::CharBurner) ? GetTextureN("io_new", 5) : GetMapTexture(2300 + rttr::enum_cast(job));
×
224
}
225

226
glArchivItem_Bitmap_Player* Loader::GetMapPlayerImage(unsigned nr)
×
227
{
228
    return convertChecked<glArchivItem_Bitmap_Player*>(map_gfx->get(nr));
×
229
}
230

231
/**
232
 *  Load general files required also outside of games
233
 *
234
 *  @return @p true on success, @p false on error.
235
 */
236
bool Loader::LoadFilesAtStart()
×
237
{
238
    namespace res = s25::resources;
239
    // Palettes
240
    if(!LoadFiles({res::pal5, res::pal6, res::pal7, res::paletti0, res::paletti1, res::paletti8})
×
241
       || !Load(ResourceId("colors")))
×
242
        return false;
×
243

244
    if(!LoadFonts())
×
245
        return false;
×
246

247
    std::vector<std::string> files = {res::resource,
248
                                      res::io,                       // Menu graphics
249
                                      res::setup013, res::setup015}; // Backgrounds for options and free play
×
250

251
    const std::array<bfs::path, 2> loadScreenFolders{config_.ExpandPath(s25::folders::loadScreens),
×
252
                                                     config_.ExpandPath(s25::folders::loadScreensMissions)};
×
253
    for(const std::string& loadScreenId : LOAD_SCREENS)
×
254
    {
255
        const std::string filename = s25util::toUpper(loadScreenId) + ".LBM";
×
256
        if(exists(loadScreenFolders[0] / filename))
×
257
            files.push_back((loadScreenFolders[0] / filename).string());
×
258
        else
259
            files.push_back((loadScreenFolders[1] / filename).string());
×
260
    }
261

262
    if(!LoadFiles(files))
×
263
        return false;
×
264

265
    if(!LoadSounds())
×
266
        return false;
×
267

268
    return LoadResources({"io_new", "client", "languages", "logo", "menu", "rttr"});
×
269
}
270

271
bool Loader::LoadSounds()
×
272
{
273
    if(!Load(config_.ExpandPath(s25::files::soundOrig)))
×
274
        return false;
×
275
    const Timer timer(true);
×
276
    logger_.write(_("Starting sound conversion: "));
×
277
    try
278
    {
279
        convertSounds(GetArchive("sound"), config_.ExpandPath(s25::files::soundScript));
×
280
    } catch(const std::runtime_error& e)
×
281
    {
282
        logger_.write(_("failed: %1%\n")) % e.what();
×
283
        return false;
×
284
    }
285
    using namespace std::chrono;
286
    logger_.write(_("done in %ums\n")) % duration_cast<milliseconds>(timer.getElapsed()).count();
×
287

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

291
    sng_lst.reserve(oggFiles.size());
×
292
    for(const auto& oggFile : oggFiles)
×
293
    {
294
        try
295
        {
296
            libsiedler2::Archiv sng = archiveLoader_->loadFileOrDir(oggFile);
×
297
            auto music = boost::dynamic_pointer_cast<MusicItem>(sng.release(0));
×
298
            if(music)
×
299
                sng_lst.emplace_back(std::move(music));
×
300
            else
301
                logger_.write(_("WARNING: Found invalid music item for %1%\n")) % oggFile;
×
302
        } catch(const LoadError&)
×
303
        {
304
            return false;
×
305
        }
306
    }
307

308
    if(sng_lst.empty())
×
309
    {
310
        logger_.write(_("WARNING: Did not find the music files.\n\tYou have to run the updater once or copy the .ogg "
×
311
                        "files manually to %1% or you won't be able to hear the music.\n"))
312
          % oggPath;
×
313
    }
314

315
    return true;
×
316
}
317

318
bool Loader::LoadFonts()
1✔
319
{
320
    if(!Load(ResourceId("fonts"), GetPaletteN("pal5")))
2✔
321
        return false;
×
322
    fonts.clear();
1✔
323
    const auto& loadedFonts = GetArchive("fonts");
1✔
324
    for(const auto i : helpers::enumRange<FontSize>())
10✔
325
    {
326
        const auto* curFont = dynamic_cast<const libsiedler2::ArchivItem_Font*>(loadedFonts[rttr::enum_cast(i)]);
3✔
327
        if(!curFont)
3✔
328
        {
329
            logger_.write(_("Unable to load font at index %1%\n")) % rttr::enum_cast(i);
×
330
            return false;
×
331
        }
332
        fonts.emplace_back(*curFont);
3✔
333
    }
334
    return true;
1✔
335
}
336

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

405
void Loader::LoadDummyMapFiles()
1✔
406
{
407
    libsiedler2::Archiv& map = files_["map_0_z"].archive;
2✔
408
    if(!map.empty())
1✔
409
        return;
×
410
    const auto pushRange = [&map](unsigned from, unsigned to) {
322✔
411
        map.alloc_inc(to - map.size() + 1);
10✔
412
        libsiedler2::PixelBufferBGRA buffer(1, 1);
50✔
413
        for(const auto i : helpers::range(from, to + 1))
664✔
414
        {
415
            auto bmp = std::make_unique<glArchivItem_Bitmap_Raw>();
312✔
416
            bmp->create(buffer);
312✔
417
            map.set(i, std::move(bmp));
312✔
418
        };
419
    };
10✔
420
    map_gfx = &map;
1✔
421

422
    // Some ID ranges as found in map_0_z.lst
423
    pushRange(20, 23);
1✔
424
    pushRange(40, 46);
1✔
425
    pushRange(50, 55);
1✔
426
    pushRange(59, 67);
1✔
427
    pushRange(200, 282);
1✔
428
    pushRange(290, 334);
1✔
429
    pushRange(350, 432);
1✔
430
    pushRange(440, 484);
1✔
431
    pushRange(500, 527);
1✔
432
    pushRange(560, 561);
1✔
433

434
    for(const auto j : helpers::range(0, 6))
16✔
435
    {
436
        libsiedler2::Archiv& bobs = files_[ResourceId("mis" + std::to_string(j) + "bobs")].archive;
12✔
437
        libsiedler2::PixelBufferBGRA buffer(1, 1);
30✔
438
        for([[maybe_unused]] const auto i : helpers::range(0, 11))
156✔
439
        {
440
            auto bmp = std::make_unique<glArchivItem_Bitmap_Raw>();
66✔
441
            bmp->create(buffer);
66✔
442
            bobs.push(std::move(bmp));
66✔
443
        }
444
    }
445
}
446

447
void Loader::LoadDummySoundFiles()
3✔
448
{
449
    libsiedler2::Archiv& archive = files_["sound"].archive;
6✔
450
    archive.alloc(116);
3✔
451
    for(const auto id : helpers::range<unsigned>(51u, archive.size()))
402✔
452
    {
453
        auto snd = std::make_unique<glArchivItem_Sound_Wave>();
195✔
454
        archive.set(id, std::move(snd));
195✔
455
    }
456
}
3✔
457

458
namespace {
459
struct NationResourcesSource
460
{
461
    bfs::path buildingsFilePath, iconsFilePath;
462
};
463

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

492
/**
493
 *  Load files required during a game
494
 *
495
 *  @param[in] mapGfxPath Path to map gfx files
496
 *  @param[in] isWinterGFX True iff winter nation files should be loaded
497
 *  @param[in] nations True entry for each nation to load
498
 *
499
 *  @return @p true on success
500
 */
501
bool Loader::LoadFilesAtGame(const std::string& mapGfxPath, bool isWinterGFX, const std::vector<Nation>& nations,
×
502
                             const std::vector<AddonId>& enabledAddons)
503
{
504
    initResourceFolders(nations, enabledAddons);
×
505

506
    namespace res = s25::resources;
507
    std::vector<std::string> files = {res::rom_bobs, res::carrier,  res::jobs,     res::boat,
508
                                      res::boot_z,   res::mis0bobs, res::mis1bobs, res::mis2bobs,
509
                                      res::mis3bobs, res::mis4bobs, res::mis5bobs};
×
510

511
    const libsiedler2::ArchivItem_Palette* pal5 = GetPaletteN("pal5");
×
512

513
    if(!LoadFiles(files) || !Load(ResourceId("map_new"), pal5))
×
514
        return false;
×
515

516
    // Load nation building and icon graphics
517
    nation_gfx = nationIcons_ = {};
×
518
    for(Nation nation : nations)
×
519
    {
520
        const auto resourceSource = getNationResourcesSource(nation, isWinterGFX, config_);
×
521
        if(!Load(resourceSource.buildingsFilePath, pal5) || !Load(resourceSource.iconsFilePath, pal5))
×
522
            return false;
×
523
        nation_gfx[nation] = &files_[ResourceId::make(resourceSource.buildingsFilePath)].archive;
×
524
        nationIcons_[nation] = &files_[ResourceId::make(resourceSource.iconsFilePath)].archive;
×
525
    }
526

527
    // TODO: Move to addon folder and make it overwrite existing file
528
    if(!LoadResources({"charburner", "charburner_bobs"}))
×
529
        return false;
×
530

531
    if(!LoadResources({"wine_bobs"}))
×
532
        return false;
×
533

NEW
534
    if(!LoadResources({"leather_bobs"}))
×
NEW
535
        return false;
×
536

537
    const bfs::path mapGFXFile = config_.ExpandPath(mapGfxPath);
×
538
    if(!Load(mapGFXFile, pal5))
×
539
        return false;
×
540
    map_gfx = &GetArchive(ResourceId::make(mapGFXFile));
×
541

542
    isWinterGFX_ = isWinterGFX;
×
543

544
    return true;
×
545
}
546

547
bool Loader::LoadFiles(const std::vector<std::string>& files)
3✔
548
{
549
    const libsiedler2::ArchivItem_Palette* pal5 = GetPaletteN("pal5");
3✔
550
    // load the files
551
    for(const std::string& curFile : files)
18✔
552
    {
553
        const bfs::path filePath = config_.ExpandPath(curFile);
15✔
554
        if(!Load(filePath, pal5))
15✔
555
        {
556
            logger_.write(_("Failed to load %s\n")) % filePath;
×
557
            return false;
×
558
        }
559
    }
560

561
    return true;
3✔
562
}
563

564
bool Loader::LoadResources(const std::vector<ResourceId>& resources)
×
565
{
566
    const libsiedler2::ArchivItem_Palette* pal5 = GetPaletteN("pal5");
×
567
    for(const ResourceId& curResource : resources)
×
568
    {
569
        if(!Load(curResource, pal5))
×
570
        {
571
            logger_.write(_("Failed to load %s\n")) % curResource;
×
572
            return false;
×
573
        }
574
    }
575

576
    return true;
×
577
}
578

579
void Loader::fillCaches()
×
580
{
581
    stp = std::make_unique<glTexturePacker>();
×
582

583
    // Animals
584
    for(const auto species : helpers::EnumRange<Species>{})
×
585
    {
586
        for(const auto dir : helpers::EnumRange<Direction>{})
×
587
        {
588
            for(const auto ani_step : helpers::range(ANIMALCONSTS[species].animation_steps))
×
589
            {
590
                glSmartBitmap& bmp = getAnimalSprite(Species(species), dir, ani_step);
×
591

592
                bmp.reset();
×
593

594
                bmp.add(GetMapImage(ANIMALCONSTS[species].walking_id
×
595
                                    + ANIMALCONSTS[species].animation_steps * rttr::enum_cast(dir + 3u) + ani_step));
×
596

597
                if(ANIMALCONSTS[species].shadow_id)
×
598
                {
599
                    if(species == Species::Duck)
×
600
                        // Ente Sonderfall, da gibts nur einen Schatten für jede Richtung!
601
                        bmp.addShadow(GetMapImage(ANIMALCONSTS[species].shadow_id));
×
602
                    else
603
                        // ansonsten immer pro Richtung einen Schatten
604
                        bmp.addShadow(GetMapImage(ANIMALCONSTS[species].shadow_id + rttr::enum_cast(dir + 3u)));
×
605
                }
606

607
                stp->add(bmp);
×
608
            }
609
        }
610

611
        glSmartBitmap& bmp = getDeadAnimalSprite(species);
×
612

613
        bmp.reset();
×
614

615
        if(ANIMALCONSTS[species].dead_id)
×
616
        {
617
            bmp.add(GetMapImage(ANIMALCONSTS[species].dead_id));
×
618

619
            if(ANIMALCONSTS[species].shadow_dead_id)
×
620
            {
621
                bmp.addShadow(GetMapImage(ANIMALCONSTS[species].shadow_dead_id));
×
622
            }
623

624
            stp->add(bmp);
×
625
        }
626
    }
627

628
    glArchivItem_Bob* bob_jobs = GetBob("jobs");
×
629
    if(!bob_jobs)
×
630
        throw std::runtime_error("jobs not found");
×
631

632
    for(const auto nation : helpers::enumRange<Nation>())
×
633
    {
634
        if(!nation_gfx[nation])
×
635
            continue;
×
636
        // BUILDINGS
637
        for(const auto type : helpers::enumRange<BuildingType>())
×
638
        {
639
            BuildingSprites& sprites = building_cache[nation][type];
×
640

641
            sprites.building.reset();
×
642
            sprites.skeleton.reset();
×
643
            sprites.door.reset();
×
644

645
            if(type == BuildingType::Charburner)
×
646
            {
647
                unsigned id = rttr::enum_cast(nation) * 8;
×
648

649
                sprites.building.add(GetImageN("charburner", id + (isWinterGFX_ ? 6 : 1)));
×
650
                sprites.building.addShadow(GetImageN("charburner", id + 2));
×
651

652
                sprites.skeleton.add(GetImageN("charburner", id + 3));
×
653
                sprites.skeleton.addShadow(GetImageN("charburner", id + 4));
×
654

655
                sprites.door.add(GetImageN("charburner", id + (isWinterGFX_ ? 7 : 5)));
×
656
            } else
657
            {
658
                sprites.building.add(GetNationImage(nation, 250 + 5 * rttr::enum_cast(type)));
×
659
                sprites.building.addShadow(GetNationImage(nation, 250 + 5 * rttr::enum_cast(type) + 1));
×
660
                if(type == BuildingType::Headquarters)
×
661
                {
662
                    // HQ has no skeleton, but we have a tent that can act as an HQ
663
                    sprites.skeleton.add(GetImageN("mis0bobs", 6));
×
664
                    sprites.skeleton.addShadow(GetImageN("mis0bobs", 7));
×
665
                } else
666
                {
667
                    sprites.skeleton.add(GetNationImage(nation, 250 + 5 * rttr::enum_cast(type) + 2));
×
668
                    sprites.skeleton.addShadow(GetNationImage(nation, 250 + 5 * rttr::enum_cast(type) + 3));
×
669
                }
670
                sprites.door.add(GetNationImage(nation, 250 + 5 * rttr::enum_cast(type) + 4));
×
671
            }
672

673
            stp->add(sprites.building);
×
674
            stp->add(sprites.skeleton);
×
675
            stp->add(sprites.door);
×
676
        }
677

678
        // FLAGS
679
        for(const auto type : helpers::enumRange<FlagType>())
×
680
        {
681
            for(const auto ani_step : helpers::range(8))
×
682
            {
683
                // Flaggentyp berücksichtigen
684
                int nr = ani_step + 100 + 20 * rttr::enum_cast(type);
×
685

686
                glSmartBitmap& bmp = flag_cache[nation][type][ani_step];
×
687

688
                bmp.reset();
×
689

690
                bmp.add(GetNationPlayerImage(nation, nr));
×
691
                bmp.addShadow(GetNationImage(nation, nr + 10));
×
692

693
                stp->add(bmp);
×
694
            }
695
        }
696

697
        // Bobs from jobs.bob.
698
        for(const auto job : helpers::enumRange<Job>())
×
699
        {
700
            for(Direction dir : helpers::EnumRange<Direction>{})
×
701
            {
702
                for(const auto ani_step : helpers::range(8u))
×
703
                {
704
                    glSmartBitmap& bmp = bob_jobs_cache[nation][job][dir][ani_step];
×
705
                    bmp.reset();
×
706

707
                    const auto& spriteData = JOB_SPRITE_CONSTS[Job(job)];
×
708
                    const libsiedler2::ImgDir imgDir = toImgDir(dir);
×
709

710
                    bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(
×
711
                      bob_jobs->getBody(spriteData.isFat(), imgDir, ani_step)));
×
712
                    bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(
×
713
                      bob_jobs->getOverlay(spriteData.getBobId(Nation(nation)), spriteData.isFat(), imgDir, ani_step)));
×
714
                    bmp.addShadow(GetMapImage(900 + static_cast<unsigned>(imgDir) * 8 + ani_step));
×
715

716
                    stp->add(bmp);
×
717
                }
718
            }
719
        }
720
        // Fat carrier
721
        for(Direction dir : helpers::EnumRange<Direction>{})
×
722
        {
723
            for(const auto ani_step : helpers::range(8u))
×
724
            {
725
                glSmartBitmap& bmp = fat_carrier_cache[nation][dir][ani_step];
×
726
                bmp.reset();
×
727

728
                const libsiedler2::ImgDir imgDir = toImgDir(dir);
×
729

730
                bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(bob_jobs->getBody(true, imgDir, ani_step)));
×
731
                bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(bob_jobs->getOverlay(0, true, imgDir, ani_step)));
×
732
                bmp.addShadow(GetMapImage(900 + static_cast<unsigned>(imgDir) * 8 + ani_step));
×
733

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

738
        {
739
            glSmartBitmap& bmp = boundary_stone_cache[nation];
×
740
            bmp.reset();
×
741

742
            bmp.add(GetNationPlayerImage(nation, 0));
×
743
            bmp.addShadow(GetNationImage(nation, 1));
×
744

745
            stp->add(bmp);
×
746
        }
747

748
        libsiedler2::Archiv& romBobs = GetArchive("rom_bobs");
×
749
        const auto getAlternative = [&romBobs](unsigned id, unsigned altId) {
×
750
            auto* bmp = convertChecked<glArchivItem_Bitmap_Player*>(romBobs[id]);
×
751
            return bmp ? bmp : convertChecked<glArchivItem_Bitmap_Player*>(romBobs[altId]);
×
752
        };
×
753
        // Special handling for non-native nations: Use roman animations if own are missing
754
        const Nation fallbackNation = rttr::enum_cast(nation) < NUM_NATIVE_NATIONS ? nation : Nation::Romans;
×
755
        const auto& natFightAnimIds = FIGHT_ANIMATIONS[nation];
×
756
        const auto& altNatFightAnimIds = FIGHT_ANIMATIONS[fallbackNation];
×
757
        const auto& natHitIds = HIT_SOLDIERS[nation];
×
758
        const auto& altNatHitIds = HIT_SOLDIERS[fallbackNation];
×
759
        for(const auto rank : helpers::range(NUM_SOLDIER_RANKS))
×
760
        {
761
            for(const auto dir : helpers::range(2))
×
762
            {
763
                FightSprites& sprites = fight_cache[nation][rank][dir];
×
764
                const auto& fightAnimIds = natFightAnimIds[rank][dir];
×
765
                const auto& altFightAnimIds = altNatFightAnimIds[rank][dir];
×
766
                for(const auto ani_step : helpers::range(8u))
×
767
                {
768
                    glSmartBitmap& bmp = sprites.attacking[ani_step];
×
769
                    bmp.reset();
×
770
                    bmp.add(getAlternative(fightAnimIds.attacking[ani_step], altFightAnimIds.attacking[ani_step]));
×
771
                    stp->add(bmp);
×
772
                }
773

774
                for(const auto i : helpers::range(sprites.defending.size()))
×
775
                {
776
                    for(const auto ani_step : helpers::range(8u))
×
777
                    {
778
                        glSmartBitmap& bmp = sprites.defending[i][ani_step];
×
779
                        bmp.reset();
×
780
                        bmp.add(
×
781
                          getAlternative(fightAnimIds.defending[i][ani_step], altFightAnimIds.defending[i][ani_step]));
×
782
                        stp->add(bmp);
×
783
                    }
784
                }
785
                glSmartBitmap& bmp = sprites.hit;
×
786
                bmp.reset();
×
787
                // Hit sprites for left/right are consecutive
788
                bmp.add(getAlternative(natHitIds[rank] + dir, altNatHitIds[rank] + dir));
×
789
                stp->add(bmp);
×
790
            }
791
        }
792
    }
793

794
    // BUILDING FLAG ANIMATION (for military buildings)
795
    /*
796
        for(const auto ani_step: helpers::range(8))
797
        {
798
            glSmartBitmap &bmp = building_flag_cache[ani_step];
799

800
            bmp.reset();
801

802
            bmp.add(static_cast<glArchivItem_Bitmap_Player *>(GetMapTexture(3162+ani_step)));
803

804
            int a, b, c, d;
805
            static_cast<glArchivItem_Bitmap_Player *>(GetMapTexture(3162+ani_step))->getVisibleArea(a, b, c, d);
806
            fprintf(stderr, "%i,%i (%ix%i)\n", a, b, c, d);
807

808

809
            stp->add(bmp);
810
        }
811
    */
812
    // Trees
813
    for(const auto type : helpers::range(9))
×
814
    {
815
        for(const auto ani_step : helpers::range(15))
×
816
        {
817
            glSmartBitmap& bmp = tree_cache[type][ani_step];
×
818

819
            bmp.reset();
×
820

821
            bmp.add(GetMapImage(200 + type * 15 + ani_step));
×
822
            bmp.addShadow(GetMapImage(350 + type * 15 + ani_step));
×
823

824
            stp->add(bmp);
×
825
        }
826
    }
827

828
    // Granite
829
    for(const auto type : helpers::enumRange<GraniteType>())
×
830
    {
831
        for(const auto size : helpers::range(6))
×
832
        {
833
            glSmartBitmap& bmp = granite_cache[type][size];
×
834

835
            bmp.reset();
×
836

837
            bmp.add(GetMapImage(516 + rttr::enum_cast(type) * 6 + size));
×
838
            bmp.addShadow(GetMapImage(616 + rttr::enum_cast(type) * 6 + size));
×
839

840
            stp->add(bmp);
×
841
        }
842
    }
843

844
    // Grainfields
845
    for(const auto type : helpers::range(2))
×
846
    {
847
        for(const auto size : helpers::range(4))
×
848
        {
849
            glSmartBitmap& bmp = grainfield_cache[type][size];
×
850

851
            bmp.reset();
×
852

853
            bmp.add(GetMapImage(532 + type * 5 + size));
×
854
            bmp.addShadow(GetMapImage(632 + type * 5 + size));
×
855

856
            stp->add(bmp);
×
857
        }
858
    }
859

860
    // Donkeys
861
    for(const auto dir : helpers::EnumRange<Direction>{})
×
862
    {
863
        for(const auto ani_step : helpers::range(8u))
×
864
        {
865
            glSmartBitmap& bmp = getDonkeySprite(dir, ani_step);
×
866

867
            bmp.reset();
×
868

869
            bmp.add(GetMapImage(2000 + rttr::enum_cast(dir + 3u) * 8 + ani_step));
×
870
            bmp.addShadow(GetMapImage(2048 + rttr::enum_cast(dir) % 3));
×
871

872
            stp->add(bmp);
×
873
        }
874
    }
875

876
    // Boats
877
    for(const auto dir : helpers::EnumRange<Direction>{})
×
878
    {
879
        for(const auto ani_step : helpers::range(8u))
×
880
        {
881
            glSmartBitmap& bmp = getBoatCarrierSprite(dir, ani_step);
×
882

883
            bmp.reset();
×
884

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

888
            stp->add(bmp);
×
889
        }
890
    }
891

892
    wineaddon::fillCache(*stp);
×
893

894
    // carrier_cache[ware][direction][animation_step][fat]
895
    glArchivItem_Bob* bob_carrier = GetBob("carrier");
×
896
    if(!bob_carrier)
×
897
        throw std::runtime_error("carrier not found");
×
898

899
    libsiedler2::Archiv wine_bob_carrier = GetArchive("wine_bobs");
×
NEW
900
    libsiedler2::Archiv leather_bob_carrier = GetArchive("leather_bobs");
×
901

902
    for(bool fat : {true, false})
×
903
    {
904
        for(const auto ware : helpers::EnumRange<GoodType>{})
×
905
        {
906
            for(Direction dir : helpers::EnumRange<Direction>{})
×
907
            {
908
                for(const auto ani_step : helpers::range(8u))
×
909
                {
910
                    glSmartBitmap& bmp = getCarrierSprite(ware, fat, dir, ani_step);
×
911
                    bmp.reset();
×
912

913
                    // Japanese shield is missing
914
                    const unsigned id =
915
                      rttr::enum_cast((ware == GoodType::ShieldJapanese) ? GoodType::ShieldRomans : ware);
×
916

917
                    const libsiedler2::ImgDir imgDir = toImgDir(dir);
×
918

919
                    if(ware == GoodType::Grapes)
×
920
                    {
921
                        const unsigned bodyIdx = static_cast<unsigned>(imgDir) * 8 + ani_step;
×
922
                        bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(wine_bob_carrier.get(
×
923
                          wineaddon::bobIndex[fat ? wineaddon::BobTypes::FAT_CARRIER_CARRYING_GRAPES :
924
                                                    wineaddon::BobTypes::THIN_CARRIER_CARRYING_GRAPES]
×
925
                          + bodyIdx)));
×
926
                    } else if(ware == GoodType::Wine)
×
927
                    {
928
                        bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(bob_carrier->getBody(fat, imgDir, ani_step)));
×
929
                        bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(wine_bob_carrier.get(
×
930
                          wineaddon::bobIndex[fat ? wineaddon::BobTypes::FAT_CARRIER_CARRYING_WINE :
931
                                                    wineaddon::BobTypes::THIN_CARRIER_CARRYING_WINE]
×
932
                          + static_cast<unsigned>(imgDir))));
×
NEW
933
                    } else if(leatheraddon::isLeatherAddonGoodType(ware))
×
934
                    {
NEW
935
                        const auto carrierEnum = leatheraddon::wareToCarrierBobIndex(ware, fat);
×
NEW
936
                        const unsigned bodyIdx = static_cast<unsigned>(imgDir) * 8 + ani_step;
×
NEW
937
                        bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(
×
NEW
938
                          leather_bob_carrier.get(leatheraddon::bobIndex[carrierEnum] + bodyIdx)));
×
939
                    } else
940
                    {
941
                        bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(bob_carrier->getBody(fat, imgDir, ani_step)));
×
942
                        bmp.add(dynamic_cast<glArchivItem_Bitmap_Player*>(
×
943
                          bob_carrier->getOverlay(id, fat, imgDir, ani_step)));
×
944
                    }
945
                    bmp.addShadow(GetMapImage(900 + static_cast<unsigned>(imgDir) * 8 + ani_step));
×
946

947
                    stp->add(bmp);
×
948
                }
949
            }
950
        }
951
    }
952

953
    // gateway animation :)
954
    {
955
        const unsigned char start_index = 248;
×
956
        const unsigned char color_count = 4;
×
957

958
        libsiedler2::ArchivItem_Palette* palette = GetPaletteN("pal5");
×
959
        auto* image = GetMapImage(561);
×
960
        auto* shadow = GetMapImage(661);
×
961

962
        if((image) && (shadow) && (palette))
×
963
        {
964
            unsigned short width = image->getWidth();
×
965
            unsigned short height = image->getHeight();
×
966

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

969
            image->print(&buffer.front(), width, height, libsiedler2::TextureFormat::Paletted, palette, 0, 0, 0, 0,
×
970
                         width, height);
971

972
            for(const auto i : helpers::range(color_count))
×
973
            {
974
                glSmartBitmap& bmp = gateway_cache[i + 1];
×
975

976
                bmp.reset();
×
977

978
                for(const auto x : helpers::range(width))
×
979
                {
980
                    for(const auto y : helpers::range(height))
×
981
                    {
982
                        if(buffer[y * width + x] >= start_index && buffer[y * width + x] < start_index + color_count)
×
983
                        {
984
                            if(++buffer[y * width + x] >= start_index + color_count)
×
985
                                buffer[y * width + x] = start_index;
×
986
                        }
987
                    }
988
                }
989

990
                auto bitmap = std::make_unique<glArchivItem_Bitmap_Raw>();
×
991
                bitmap->create(width, height, &buffer.front(), width, height, libsiedler2::TextureFormat::Paletted,
×
992
                               palette);
993
                bitmap->setNx(image->getNx());
×
994
                bitmap->setNy(image->getNy());
×
995

996
                bmp.add(std::move(bitmap));
×
997
                bmp.addShadow(shadow);
×
998

999
                stp->add(bmp);
×
1000
            }
×
1001
        } else
1002
        {
1003
            for(const auto i : helpers::range(color_count))
×
1004
            {
1005
                glSmartBitmap& bmp = gateway_cache[i + 1];
×
1006

1007
                bmp.reset();
×
1008
            }
1009
        }
1010
    }
1011

1012
    if(SETTINGS.video.shared_textures)
×
1013
    {
1014
        // generate mega texture
1015
        stp->pack();
×
1016
    } else
1017
        stp.reset();
×
1018
}
×
1019

1020
/**
1021
 *  Extrahiert eine Textur aus den Daten.
1022
 */
1023
std::unique_ptr<glArchivItem_Bitmap> Loader::ExtractTexture(const glArchivItem_Bitmap& srcImg, const Rect& rect)
×
1024
{
1025
    Extent texSize = rect.getSize();
×
1026
    if(texSize.x == 0 && rect.right < srcImg.getWidth())
×
1027
        texSize.x = srcImg.getWidth() - rect.right;
×
1028
    if(texSize.y == 0 && rect.bottom < srcImg.getHeight())
×
1029
        texSize.y = srcImg.getHeight() - rect.bottom;
×
1030
    libsiedler2::PixelBufferPaletted buffer(texSize.x, texSize.y);
×
1031

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

1035
    std::unique_ptr<glArchivItem_Bitmap> bitmap = std::make_unique<glArchivItem_Bitmap_Raw>();
×
1036
    if(int ec = bitmap->create(buffer, srcImg.getPalette()))
×
1037
    {
1038
        throw std::runtime_error(std::string("Error loading texture: ") + libsiedler2::getErrorString(ec));
×
1039
    }
1040
    return bitmap;
×
1041
}
1042

1043
/**
1044
 *  Extrahiert mehrere (animierte) Texturen aus den Daten.
1045
 */
1046
std::unique_ptr<libsiedler2::Archiv> Loader::ExtractAnimatedTexture(const glArchivItem_Bitmap& srcImg, const Rect& rect,
×
1047
                                                                    uint8_t start_index, uint8_t color_count)
1048
{
1049
    Extent texSize = rect.getSize();
×
1050
    if(texSize.x == 0 && rect.right < srcImg.getWidth())
×
1051
        texSize.x = srcImg.getWidth() - rect.right;
×
1052
    if(texSize.y == 0 && rect.bottom < srcImg.getHeight())
×
1053
        texSize.y = srcImg.getHeight() - rect.bottom;
×
1054
    libsiedler2::PixelBufferPaletted buffer(texSize.x, texSize.y);
×
1055

1056
    srcImg.print(buffer, nullptr, 0, 0, rect.left, rect.top);
×
1057

1058
    auto destination = std::make_unique<libsiedler2::Archiv>();
×
1059
    libsiedler2::ArchivItem_PaletteAnimation anim;
×
1060
    anim.isActive = true;
×
1061
    anim.moveUp = false;
×
1062
    anim.firstClr = start_index;
×
1063
    anim.lastClr = start_index + color_count - 1u;
×
1064
    const libsiedler2::ArchivItem_Palette* curPal = nullptr;
×
1065
    for(const auto i : helpers::range(color_count))
×
1066
    {
1067
        auto newPal = (i == 0) ? clone(srcImg.getPalette()) : anim.apply(*curPal);
×
1068
        auto bitmap = std::make_unique<glArchivItem_Bitmap_Raw>();
×
1069
        bitmap->setPalette(std::move(newPal));
×
1070
        if(int ec = bitmap->create(buffer))
×
1071
            throw std::runtime_error("Error extracting animated texture: " + libsiedler2::getErrorString(ec));
×
1072
        curPal = bitmap->getPalette();
×
1073
        destination->push(std::move(bitmap));
×
1074
    }
1075
    return destination;
×
1076
}
1077

1078
bool Loader::Load(libsiedler2::Archiv& archive, const bfs::path& path, const libsiedler2::ArchivItem_Palette* palette)
×
1079
{
1080
    try
1081
    {
1082
        archive = archiveLoader_->load(archiveLocator_->resolve(path), palette);
×
1083
        return true;
×
1084
    } catch(const LoadError&)
×
1085
    {
1086
        return false;
×
1087
    }
1088
}
1089

1090
bool Loader::Load(libsiedler2::Archiv& archive, const ResourceId& resId, const libsiedler2::ArchivItem_Palette* palette)
×
1091
{
1092
    const ResolvedFile resolvedFile = archiveLocator_->resolve(resId);
×
1093
    if(!resolvedFile)
×
1094
    {
1095
        logger_.write(_("Failed to resolve resource %1%\n")) % resId;
×
1096
        return false;
×
1097
    }
1098
    try
1099
    {
1100
        archive = archiveLoader_->load(resolvedFile, palette);
×
1101
        return true;
×
1102
    } catch(const LoadError&)
×
1103
    {
1104
        return false;
×
1105
    }
1106
}
1107

1108
template<typename T>
1109
bool Loader::LoadImpl(const T& resIdOrPath, const libsiedler2::ArchivItem_Palette* palette)
24✔
1110
{
1111
    const auto resolvedFile = archiveLocator_->resolve(resIdOrPath);
48✔
1112
    if(!resolvedFile)
24✔
1113
    {
1114
        logger_.write(_("Failed to resolve resource %1%\n")) % resIdOrPath;
×
1115
        return false;
×
1116
    }
1117
    FileEntry& entry = files_[ResourceId::make(resIdOrPath)];
24✔
1118
    // Do we really need to reload or can we reused the loaded version?
1119
    if(entry.resolvedFile != resolvedFile)
24✔
1120
    {
1121
        try
1122
        {
1123
            entry.archive = archiveLoader_->load(resolvedFile, palette);
22✔
1124
        } catch(const LoadError&)
×
1125
        {
1126
            return false;
×
1127
        }
1128
        // Update how we loaded this
1129
        entry.resolvedFile = resolvedFile;
22✔
1130
    }
1131
    RTTR_Assert(!entry.archive.empty());
24✔
1132
    return true;
24✔
1133
}
1134

1135
bool Loader::Load(const bfs::path& path, const libsiedler2::ArchivItem_Palette* palette)
23✔
1136
{
1137
    return LoadImpl(path, palette);
23✔
1138
}
1139

1140
bool Loader::Load(const ResourceId& resId, const libsiedler2::ArchivItem_Palette* palette)
1✔
1141
{
1142
    return LoadImpl(resId, palette);
1✔
1143
}
1144

1145
void addDefaultResourceFolders(const RttrConfig& config, ArchiveLocator& locator,
1✔
1146
                               const std::vector<Nation>& usedNations, const std::vector<AddonId>& enabledAddons)
1147
{
1148
    locator.clear();
1✔
1149
    locator.addAssetFolder(config.ExpandPath(s25::folders::assetsBase));
1✔
1150
    for(Nation nation : usedNations)
1✔
1151
    {
1152
        const auto overrideFolder = config.ExpandPath(s25::folders::assetsNations) / NationNames[nation];
×
1153
        if(bfs::exists(overrideFolder))
×
1154
            locator.addOverrideFolder(overrideFolder);
×
1155
    }
1156
    locator.addOverrideFolder(config.ExpandPath(s25::folders::assetsOverrides));
1✔
1157
    for(AddonId addonId : enabledAddons)
1✔
1158
    {
1159
        const auto overrideFolder = config.ExpandPath(s25::folders::assetsAddons)
×
1160
                                    / s25util::toStringClassic(static_cast<uint32_t>(addonId), true);
×
1161
        if(bfs::exists(overrideFolder))
×
1162
            locator.addOverrideFolder(overrideFolder);
×
1163
    }
1164
    const bfs::path userOverrides = config.ExpandPath(s25::folders::assetsUserOverrides);
3✔
1165
    if(exists(userOverrides))
1✔
1166
        locator.addOverrideFolder(userOverrides);
×
1167
}
1✔
1168

1169
Loader& getGlobalLoader()
2,338✔
1170
{
1171
    static Loader loader{LOG, RTTRCONFIG};
2,338✔
1172
    return loader;
2,338✔
1173
}
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