GCC Code Coverage Report


Directory: ./
File: src/mpq_library/mpq.cpp
Date: 2026-04-01 15:09:43
Exec Total Coverage
Lines: 46 51 90.2%
Functions: 6 6 100.0%
Branches: 48 98 49.0%

Line Branch Exec Source
1 #include <string>
2 #include <algorithm>
3 #include <functional>
4 #include <ranges>
5 #include <filesystem>
6 #include <expected>
7 #include <spanstream>
8
9 #include <re2/re2.h>
10
11 #include <platform.hpp>
12 #include <exception.hpp>
13 #include <mpq/archive.hpp>
14 #include <mpq/listfile.hpp>
15
16 #include "assets_mpq_importer/mpq.hpp"
17
18 // NOLINTBEGIN(maybe-uninitialized)
19
20 namespace assmpq::mpq {
21
22 using filelist_filter_t = std::function<bool(const std::string&)>;
23
24 2 static auto wildcard_to_regex(std::string mask)-> re2::RE2 {
25 // Convert wildcard to regex pattern
26 // Simple replacements; for production, escape other regex special chars
27 2 std::size_t pos = 0;
28
1/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
2 while ((pos = mask.find('.', pos)) != std::string::npos) { mask.replace(pos, 1, "\\."); pos += 2; }
29 2 pos = 0;
30
1/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
2 while ((pos = mask.find('?', pos)) != std::string::npos) { mask.replace(pos, 1, "."); pos += 1; }
31 2 pos = 0;
32
3/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
4 while ((pos = mask.find('*', pos)) != std::string::npos) { mask.replace(pos, 1, ".*"); pos += 2; }
33
34 2 RE2::Options options;
35 2 options.set_case_sensitive(false);
36
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 return { mask, options };
37 }
38
39 3 auto list_mpq_files(const std::filesystem::path& archive_path, const std::string& mask)
40 -> std::expected<ArchiveEntries, ErrorMessage>
41 {
42
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 wc3lib::mpq::Archive archive;
43 try {
44
2/4
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 3 times.
✗ Branch 12 not taken.
3 archive.open(archive_path.c_str());
45 } catch (const wc3lib::Exception &exception) {
46 return std::unexpected(exception.what());
47 }
48
49
3/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
3 if (archive.containsListfileFile()) {
50
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 const wc3lib::mpq::Listfile filelist = archive.listfileFile();
51
52
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (filelist.isValid()) {
53
2/4
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
2 const auto mask_regex = wildcard_to_regex(mask);
54
55 2 auto filter = mask.empty() ?
56
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
6 filelist_filter_t([](const std::string &entry)-> bool { return !entry.empty(); }) :
57
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
3 filelist_filter_t([&mask_regex](const std::string &entry)-> bool {
58 3 return re2::RE2::PartialMatch(entry, mask_regex);
59
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
3 });
60
61
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 wc3lib::mpq::Listfile::Entries entries = filelist.entries();
62
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 std::ranges::sort(entries);
63
64 8 return entries |
65
2/4
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
12 std::views::filter(filter) |
66
1/2
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
4 std::views::transform([&archive](const auto &entry)-> FileEntry {
67
2/4
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
4 const wc3lib::mpq::File file = archive.findFile(entry);
68
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
8 return FileEntry{ .filename = entry, .size = file.size() };
69
2/6
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 19 taken 2 times.
✗ Branch 20 not taken.
18 }) |
70
2/4
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
8 std::ranges::to<ArchiveEntries>();
71 2 }
72
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 }
73
1/2
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 return std::unexpected("List file not found.");
74 3 }
75
76 2 auto extract_mpq_file(const std::filesystem::path& archive_path, const std::string& filename)
77 -> std::expected<FileData, ErrorMessage>
78 try {
79
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 wc3lib::mpq::Archive archive;
80
2/4
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 2 times.
✗ Branch 12 not taken.
2 archive.open(archive_path.c_str());
81
82
2/4
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
2 const wc3lib::mpq::File file = archive.findFile(filename);
83
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2 if (!file.isValid()) {
84
1/2
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 return std::unexpected("File not found.");
85 }
86
87
2/4
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
3 std::vector<char> output_buffer(file.size());
88
1/2
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
1 std::ospanstream output_stream(output_buffer, std::ios::out | std::ios::binary);
89
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 file.decompress(output_stream);
90
91 1 return output_buffer;
92
0/2
✗ Branch 16 not taken.
✗ Branch 17 not taken.
2 } catch (const wc3lib::Exception &exception) {
93 return std::unexpected(exception.what());
94 }
95 // NOLINTEND(maybe-uninitialized)
96
97
98 } // namespace assmpq::mpq
99