Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
TextureCatalog.cpp
Go to the documentation of this file.
1#include "TextureCatalog.hpp"
2#include "ResourcePaths.hpp"
3
4#include <array>
5#include <cstddef>
6#include <string>
7#include <utility>
8
9namespace monopoly::data
10{
11 namespace
12 {
13 constexpr std::array<TextureCoordinate, 8> UsaClassicMediumPositions
14 {{
15 {448, 0},
16 {320, 0},
17 {256, 0},
18 {192, 0},
19 {64, 0},
20 {0, 0},
21 {128, 0},
22 {384, 0}
23 }};
24
25 constexpr std::array<TextureCoordinate, 16> UsaClassicHighPositions
26 {{
27 {192, 0},
28 {128, 0},
29 {64, 0},
30 {448, 0},
31 {384, 0},
32 {320, 0},
33 {256, 0},
34 {384, 128},
35 {320, 128},
36 {256, 128},
37 {192, 128},
38 {128, 128},
39 {64, 128},
40 {0, 128},
41 {0, 0},
42 {448, 128}
43 }};
44
45 constexpr std::array<TextureCoordinate, 14> UsaCityMediumPositions
46 {{
47 {320, 0},
48 {256, 0},
49 {192, 0},
50 {128, 0},
51 {64, 0},
52 {0, 0},
53 {320, 128},
54 {384, 128},
55 {256, 128},
56 {192, 128},
57 {64, 128},
58 {0, 128},
59 {128, 128},
60 {384, 0}
61 }};
62
63 constexpr std::array<TextureCoordinate, 22> UsaCityHighPositions
64 {{
65 {512, 0},
66 {448, 0},
67 {128, 128},
68 {64, 128},
69 {0, 128},
70 {0, 0},
71 {192, 0},
72 {128, 0},
73 {64, 0},
74 {640, 0},
75 {384, 0},
76 {320, 0},
77 {256, 0},
78 {448, 128},
79 {384, 128},
80 {320, 128},
81 {256, 128},
82 {192, 128},
83 {576, 0},
84 {576, 128},
85 {512, 128},
86 {640, 128}
87 }};
88
89 constexpr std::array<TextureCoordinate, 8> EuroClassicMediumPositions
90 {{
91 {448, 0},
92 {320, 0},
93 {256, 0},
94 {192, 0},
95 {64, 0},
96 {128, 0},
97 {0, 0},
98 {384, 0}
99 }};
100
101 constexpr std::array<TextureCoordinate, 16> EuroClassicHighPositions
102 {{
103 {192, 0},
104 {128, 0},
105 {64, 0},
106 {384, 0},
107 {320, 0},
108 {256, 0},
109 {448, 128},
110 {0, 128},
111 {64, 128},
112 {448, 0},
113 {0, 0},
114 {384, 128},
115 {320, 128},
116 {256, 128},
117 {192, 128},
118 {128, 128}
119 }};
120
121 constexpr std::array<TextureCoordinate, 14> EuroCityMediumPositions
122 {{
123 {320, 0},
124 {256, 0},
125 {192, 0},
126 {128, 0},
127 {64, 0},
128 {0, 0},
129 {320, 128},
130 {384, 128},
131 {256, 128},
132 {192, 128},
133 {64, 128},
134 {128, 128},
135 {0, 128},
136 {384, 0}
137 }};
138
139 constexpr std::array<TextureCoordinate, 22> EuroCityHighPositions
140 {{
141 {512, 0},
142 {448, 0},
143 {128, 128},
144 {64, 128},
145 {0, 128},
146 {0, 0},
147 {192, 0},
148 {128, 0},
149 {64, 0},
150 {384, 0},
151 {320, 0},
152 {256, 0},
153 {640, 128},
154 {576, 128},
155 {576, 0},
156 {640, 0},
157 {512, 128},
158 {448, 128},
159 {384, 128},
160 {320, 128},
161 {256, 128},
162 {192, 128}
163 }};
164
165 constexpr std::array<std::string_view, 4> CommonMedium128
166 {{
167 "CT11_128.bmp",
168 "CT12_128.bmp",
169 "CT13_128.bmp",
170 "CT14_128.bmp"
171 }};
172
173 constexpr std::array<std::string_view, 4> CommonMedium256
174 {{
175 "CT11_256.bmp",
176 "CT12_256.bmp",
177 "CT13_256.bmp",
178 "CT14_256.bmp"
179 }};
180
181 constexpr std::array<std::string_view, 13> CommonHigh128
182 {{
183 "CT10_128.bmp",
184 "CT11_128.bmp",
185 "CT12_128.bmp",
186 "CT13_128.bmp",
187 "CT14_128.bmp",
188 "CT15_128.bmp",
189 "CT16_128.bmp",
190 "CT17_128.bmp",
191 "CT18_128.bmp",
192 "CT19_128.bmp",
193 "CT20_128.bmp",
194 "CT21_128.bmp",
195 "CT22_128.bmp"
196 }};
197
198 constexpr std::array<std::string_view, 13> CommonHigh256
199 {{
200 "CT10_256.bmp",
201 "CT11_256.bmp",
202 "CT12_256.bmp",
203 "CT13_256.bmp",
204 "CT14_256.bmp",
205 "CT15_256.bmp",
206 "CT16_256.bmp",
207 "CT17_256.bmp",
208 "CT18_256.bmp",
209 "CT19_256.bmp",
210 "CT20_256.bmp",
211 "CT21_256.bmp",
212 "CT22_256.bmp"
213 }};
214
215 constexpr std::array<std::string_view, 6> CityPhotos128
216 {{
217 "CT01_128.BMP",
218 "CT02_128.BMP",
219 "CT03_128.BMP",
220 "CT04_128.BMP",
221 "CT05_128.BMP",
222 "CT06_128.BMP"
223 }};
224
225 constexpr std::array<std::string_view, 6> CityPhotos256
226 {{
227 "CT01_256.BMP",
228 "CT02_256.BMP",
229 "CT03_256.BMP",
230 "CT04_256.BMP",
231 "CT05_256.BMP",
232 "CT06_256.BMP"
233 }};
234
235 constexpr std::array<std::string_view, 4> ClassicMediumNames256
236 {{
237 "BRD01_256.bmp",
238 "BRD02_256.bmp",
239 "BRD03_256.bmp",
240 "BRD04_256.bmp"
241 }};
242
243 constexpr std::array<std::string_view, 3> ClassicHighNames256
244 {{
245 "BRD01_256.bmp",
246 "BRD02_256.bmp",
247 "BRD03_256.bmp"
248 }};
249
250 constexpr std::array<std::string_view, 4> CityMediumNames128
251 {{
252 "CT07_128.BMP",
253 "CT08_128.BMP",
254 "CT09_128.BMP",
255 "CT10_128.BMP"
256 }};
257
258 constexpr std::array<std::string_view, 4> CityMediumNames256
259 {{
260 "CT07_256.BMP",
261 "CT08_256.BMP",
262 "CT09_256.BMP",
263 "CT10_256.BMP"
264 }};
265
266 // Anomalie historique volontaire : TexInfo.cpp nomme ces trois
267 // textures *_256 même dans la table CityHigh128.
268 constexpr std::array<std::string_view, 3> CityHighNames128
269 {{
270 "CT07_256.BMP",
271 "CT08_256.BMP",
272 "CT09_256.BMP"
273 }};
274
275 constexpr std::array<std::string_view, 3> CityHighNames256
276 {{
277 "CT07_256.BMP",
278 "CT08_256.BMP",
279 "CT09_256.BMP"
280 }};
281
282 // Ces fichiers existent dans le corpus physique, même si le caller
283 // historique ne les sélectionne pas à cause de l'anomalie ci-dessus.
284 constexpr std::array<std::string_view, 3> PhysicalCityHighNames128
285 {{
286 "CT07_128.bmp",
287 "CT08_128.bmp",
288 "CT09_128.bmp"
289 }};
290
291 constexpr std::array<std::string_view, 4> EuroMediumBoards128
292 {{
293 "BRD01_128.BMP",
294 "BRD02_128.BMP",
295 "BRD03_128.BMP",
296 "BRD04_128.BMP"
297 }};
298
299 constexpr std::array<std::string_view, 4> EuroMediumBoards256
300 {{
301 "BRD01_256.BMP",
302 "BRD02_256.BMP",
303 "BRD03_256.BMP",
304 "BRD04_256.BMP"
305 }};
306
307 constexpr std::array<std::string_view, 3> EuroHighBoards128
308 {{
309 "BRD01_128.BMP",
310 "BRD02_128.BMP",
311 "BRD03_128.BMP"
312 }};
313
314 constexpr std::array<std::string_view, 3> EuroHighBoards256
315 {{
316 "BRD01_256.BMP",
317 "BRD02_256.BMP",
318 "BRD03_256.BMP"
319 }};
320
321 constexpr std::array<std::string_view, 1> EuroMediumCurrencies128
322 {{
323 "CUR01_128.BMP"
324 }};
325
326 constexpr std::array<std::string_view, 1> EuroMediumCurrencies256
327 {{
328 "CUR01_256.BMP"
329 }};
330
331 constexpr std::array<std::string_view, 5> EuroHighCurrencies128
332 {{
333 "CUR01_128.BMP",
334 "CUR02_128.BMP",
335 "CUR03_128.BMP",
336 "CUR04_128.BMP",
337 "CUR05_128.BMP"
338 }};
339
340 constexpr std::array<std::string_view, 5> EuroHighCurrencies256
341 {{
342 "CUR01_256.BMP",
343 "CUR02_256.BMP",
344 "CUR03_256.BMP",
345 "CUR04_256.BMP",
346 "CUR05_256.BMP"
347 }};
348
349 constexpr std::array<std::string_view, 7> EuroMediumLanguage128
350 {{
351 "LANG01_128.BMP",
352 "LANG02_128.BMP",
353 "LANG03_128.BMP",
354 "LANG04_128.BMP",
355 "LANG05_128.BMP",
356 "LANG06_128.BMP",
357 "LANG07_128.BMP"
358 }};
359
360 constexpr std::array<std::string_view, 7> EuroMediumLanguage256
361 {{
362 "LANG01_256.BMP",
363 "LANG02_256.BMP",
364 "LANG03_256.BMP",
365 "LANG04_256.BMP",
366 "LANG05_256.BMP",
367 "LANG06_256.BMP",
368 "LANG07_256.BMP"
369 }};
370
371 constexpr std::array<std::string_view, 11> EuroHighLanguage128
372 {{
373 "LANG01_128.BMP",
374 "LANG02_128.BMP",
375 "LANG03_128.BMP",
376 "LANG04_128.BMP",
377 "LANG05_128.BMP",
378 "LANG06_128.BMP",
379 "LANG07_128.BMP",
380 "LANG08_128.BMP",
381 "LANG09_128.BMP",
382 "LANG10_128.BMP",
383 "LANG11_128.BMP"
384 }};
385
386 constexpr std::array<std::string_view, 11> EuroHighLanguage256
387 {{
388 "LANG01_256.BMP",
389 "LANG02_256.BMP",
390 "LANG03_256.BMP",
391 "LANG04_256.BMP",
392 "LANG05_256.BMP",
393 "LANG06_256.BMP",
394 "LANG07_256.BMP",
395 "LANG08_256.BMP",
396 "LANG09_256.BMP",
397 "LANG10_256.BMP",
398 "LANG11_256.BMP"
399 }};
400
401 constexpr std::array<std::string_view, 39> TwoDimensionalBoards
402 {{
403 "2DVIEW01.BMP",
404 "2DVIEW02.BMP",
405 "2DVIEW03.BMP",
406 "2DVIEW04.BMP",
407 "2DVIEW05.BMP",
408 "2DVIEW06.BMP",
409 "2DVIEW07.BMP",
410 "2DVIEW08.BMP",
411 "2DVIEW09.BMP",
412 "2DVIEW10.BMP",
413 "2DVIEW11.BMP",
414 "2DVIEW12.BMP",
415 "2DVIEW13.BMP",
416 "2DVIEW14.BMP",
417 "2DVIEW15.BMP",
418 "2DVIEW16.BMP",
419 "2DVIEW17.BMP",
420 "2DVIEW18.BMP",
421 "2DVIEW19.BMP",
422 "2DVIEW20.BMP",
423 "2DVIEW21.BMP",
424 "2DVIEW22.BMP",
425 "2DVIEW23.BMP",
426 "2DVIEW24.BMP",
427 "2DVIEW25.BMP",
428 "2DVIEW26.BMP",
429 "2DVIEW27.BMP",
430 "2DVIEW28.BMP",
431 "2DVIEW29.BMP",
432 "2DVIEW30.BMP",
433 "2DVIEW31.BMP",
434 "2DVIEW32.BMP",
435 "2DVIEW33.BMP",
436 "2DVIEW34.BMP",
437 "2DVIEW35.BMP",
438 "2DVIEW36.BMP",
439 "2DVIEW37.BMP",
440 "2DVIEW38.BMP",
441 "2DVIEW39.BMP"
442 }};
443
444
446 BoardTextureRecipe& recipe,
447 std::span<const TextureCoordinate> coordinates,
448 std::size_t coordinateOffset,
449 std::span<const std::string_view> names,
450 TextureRole role,
451 TextureLocation location)
452 {
453 for (std::size_t index = 0; index < names.size(); ++index)
454 {
455 recipe.textures.push_back(
456 TextureReference
457 {
458 .coordinate = coordinates[coordinateOffset + index],
459 .resolution = recipe.resolution,
460 .role = role,
461 .location = location,
462 .fileName = names[index]
463 }
464 );
465 }
466 }
467
468
469 [[nodiscard]] std::string twoDigitIndex(std::size_t index)
470 {
471 std::string result;
472 result.reserve(2);
473 result.push_back(
474 static_cast<char>('0' + ((index / 10) % 10))
475 );
476 result.push_back(static_cast<char>('0' + (index % 10)));
477 return result;
478 }
479
480
481 [[nodiscard]] std::string physicalBmpName(std::string_view sourceName)
482 {
483 std::string result{sourceName};
484
485 if (result.ends_with(".BMP"))
486 {
487 result.replace(result.size() - 4, 4, ".bmp");
488 }
489
490 return result;
491 }
492
493
494 [[nodiscard]] std::string indexedTextureDirectory(
495 std::string_view root,
496 std::string_view prefix,
497 std::size_t index,
498 std::string_view detail)
499 {
500 std::string result;
501 result.reserve(
502 root.size() + prefix.size() + detail.size() + 6
503 );
504 result.append(root);
505 result.push_back('/');
506 result.append(prefix);
507 result.append(twoDigitIndex(index));
508 result.push_back('/');
509 result.append(detail);
510 return result;
511 }
512
513
515 std::vector<LegacyTextureAsset>& manifest,
516 std::string_view directory,
517 std::span<const std::string_view> names,
518 TextureRole role,
519 TextureLocation location,
520 TextureResolution resolution,
521 std::uint16_t bitsPerPixel)
522 {
523 for (const auto name : names)
524 {
525 std::string relativePath;
526 relativePath.reserve(directory.size() + name.size() + 1);
527 relativePath.append(directory);
528 relativePath.push_back('/');
529 relativePath.append(physicalBmpName(name));
530
531 manifest.push_back(
532 LegacyTextureAsset
533 {
534 .relativePath = std::move(relativePath),
535 .role = role,
536 .location = location,
537 .resolution = resolution,
538 .expectedDimensions = textureDimensions(resolution),
539 .expectedBitsPerPixel = bitsPerPixel,
540 .expectedCompression = 0
541 }
542 );
543 }
544 }
545
546
548 std::vector<LegacyTextureAsset>& manifest,
549 std::string_view directory,
550 std::string_view name,
551 TextureRole role,
552 TextureLocation location,
553 TextureResolution resolution,
554 std::uint16_t bitsPerPixel)
555 {
556 const std::array names{name};
558 manifest,
559 directory,
560 names,
561 role,
562 location,
563 resolution,
564 bitsPerPixel
565 );
566 }
567 }
568
569
571 TextureCatalogErrorCode code) noexcept
572 {
573 switch (code)
574 {
576 return "InvalidBoardMeshKind";
577
579 return "InvalidTextureResolution";
580
582 return "InvalidTextureContext";
583
585 return "InvalidTextureLocation";
586
588 return "InvalidTextureFileName";
589 }
590
591 return "InvalidTextureCatalogErrorCode";
592 }
593
594
595 std::expected<std::string, TextureCatalogError> boardTextureRelativePath(
596 BoardMeshKind mesh,
597 TextureLocation location,
598 std::string_view fileName,
599 const BoardTextureContext& context)
600 {
601 const auto fail = [](TextureCatalogErrorCode code,
602 std::uint32_t rawValue, std::string_view detail)
603 {
604 return std::unexpected(TextureCatalogError{code, rawValue, detail});
605 };
606 if (!isValidBoardMeshKind(mesh))
607 {
609 static_cast<std::uint32_t>(boardMeshTag(mesh)),
610 "board mesh must be one of the four TexInfo HMD tags");
611 }
612 if (context.edition != BoardEdition::Usa &&
613 context.edition != BoardEdition::Europe)
614 {
616 static_cast<std::uint32_t>(context.edition),
617 "texture edition must be USA or Europe");
618 }
619 const bool custom = context.city == -1;
620 if (context.city < -1 || (custom &&
621 (context.customRoot.empty() || !context.customRoot.is_absolute() || !isCityMesh(mesh))))
622 {
624 static_cast<std::uint32_t>(context.city),
625 "custom city -1 requires a city mesh and an explicit absolute asset root");
626 }
627 const bool usa = context.edition == BoardEdition::Usa;
628 // DISPLAY_CITY_MAX is 11 for USA and 12 for Europe (Display.h).
629 if (context.city >= (usa ? 11 : 12))
630 {
632 static_cast<std::uint32_t>(context.city),
633 "city is outside the stock edition range");
634 }
635 if (!usa)
636 {
637 const auto language = static_cast<unsigned>(context.language);
639 {
641 language, "European texture language must be UK through Norwegian");
642 }
643 if (context.currency < 0 || context.currency > 12)
644 {
646 static_cast<std::uint32_t>(context.currency),
647 "European texture currency must be in the stock range 0..12");
648 }
649 if (isCityMesh(mesh) && !custom)
650 {
652 static_cast<std::uint32_t>(boardMeshTag(mesh)),
653 "European city meshes require custom city -1");
654 }
655 }
656 if (fileName.empty() || fileName == "." || fileName == ".." ||
657 fileName.find_first_of("/\\:") != std::string_view::npos ||
658 fileName.find('\0') != std::string_view::npos)
659 {
661 "texture filename must be a nonempty basename");
662 }
663
664 const std::string_view detail = isHighDetailMesh(mesh) ? "High" : "Medium";
665 std::string directory;
666 switch (location)
667 {
670 if (custom)
671 {
672 directory = location == TextureLocation::SelectedCityPhotos ? "Photos" : detail;
673 break;
674 }
675 if (!usa || (isCityMesh(mesh) && context.city == 0) ||
676 (location == TextureLocation::SelectedCityPhotos && !isCityMesh(mesh)))
677 {
679 static_cast<std::uint32_t>(context.city),
680 "stock city photos and names require the matching USA board mesh");
681 }
682 directory = indexedTextureDirectory("Cities", "City",
683 isCityMesh(mesh) ? static_cast<std::size_t>(context.city) : 0,
684 location == TextureLocation::SelectedCityPhotos ? "Photos" : detail);
685 break;
686
688 if (usa)
689 {
690 directory = "Cities/Common/";
691 directory.append(detail);
692 }
693 break;
694
696 if (!usa)
697 directory = indexedTextureDirectory("Languages", "Lang",
698 static_cast<unsigned>(context.language) - 2, detail);
699 break;
700
702 if (!usa && !custom)
703 directory = indexedTextureDirectory("Boards", "Board",
704 static_cast<std::size_t>(context.city), detail);
705 break;
706
708 if (!usa)
709 directory = indexedTextureDirectory("Currency", "Curr",
710 static_cast<std::size_t>(context.currency), detail);
711 break;
712
714 if (custom) directory = "2DBoards";
715 break;
716 }
717 if (directory.empty())
718 {
720 static_cast<std::uint32_t>(location),
721 "texture location is not used by this stock board edition");
722 }
723 directory.push_back('/');
724 directory.append(fileName);
725 return directory;
726 }
727
728 std::expected<std::filesystem::path, std::string> resolveBoardTexturePath(
730 std::string_view fileName, const BoardTextureContext& context)
731 {
732 const auto relative = boardTextureRelativePath(mesh, location, fileName, context);
733 if (!relative) return std::unexpected(std::string(relative.error().detail));
734 const bool custom = context.city == -1 &&
738 if (custom)
739 {
740 const std::array roots{context.customRoot};
741 const auto paths = ResourcePaths::create(roots);
742 if (!paths) return std::unexpected(paths.error().detail);
743 const auto resolved = paths->resolve(*relative);
744 if (!resolved) return std::unexpected(*relative + ": " + resolved.error().detail);
745 return *resolved;
746 }
747 const auto resolved = stockPaths.resolve(*relative);
748 if (!resolved) return std::unexpected(*relative + ": " + resolved.error().detail);
749 return *resolved;
750 }
751
752
753 std::span<const TextureCoordinate> usaTextureCoordinates(
754 BoardMeshKind mesh) noexcept
755 {
756 switch (mesh)
757 {
760
763
766
769 }
770
771 return {};
772 }
773
774
775 std::span<const TextureCoordinate> europeanTextureCoordinates(
776 BoardMeshKind mesh) noexcept
777 {
778 switch (mesh)
779 {
782
785
788
791 }
792
793 return {};
794 }
795
796
797 std::span<const std::string_view> cityPhotoTextureNames(
798 TextureResolution resolution) noexcept
799 {
800 if (!isValidTextureResolution(resolution))
801 {
802 return {};
803 }
804
805 return resolution == TextureResolution::Pixels256
806 ? std::span<const std::string_view>{CityPhotos256}
807 : std::span<const std::string_view>{CityPhotos128};
808 }
809
810
811 std::span<const std::string_view> usaCityNameTextureNames(
812 BoardMeshKind mesh,
813 TextureResolution resolution) noexcept
814 {
815 if (!isValidTextureResolution(resolution))
816 {
817 return {};
818 }
819
820 switch (mesh)
821 {
823 return resolution == TextureResolution::Pixels256
824 ? std::span<const std::string_view>{CityHighNames256}
825 : std::span<const std::string_view>{CityHighNames128};
826
828 return resolution == TextureResolution::Pixels256
829 ? std::span<const std::string_view>{CityMediumNames256}
830 : std::span<const std::string_view>{CityMediumNames128};
831
833 return resolution == TextureResolution::Pixels256
834 ? std::span<const std::string_view>{ClassicHighNames256}
835 : std::span<const std::string_view>{};
836
838 return resolution == TextureResolution::Pixels256
839 ? std::span<const std::string_view>{ClassicMediumNames256}
840 : std::span<const std::string_view>{};
841 }
842
843 return {};
844 }
845
846
847 std::span<const std::string_view> commonTextureNames(
848 BoardMeshKind mesh,
849 TextureResolution resolution) noexcept
850 {
851 if (!isValidBoardMeshKind(mesh) ||
852 !isValidTextureResolution(resolution))
853 {
854 return {};
855 }
856
857 if (isHighDetailMesh(mesh))
858 {
859 return resolution == TextureResolution::Pixels256
860 ? std::span<const std::string_view>{CommonHigh256}
861 : std::span<const std::string_view>{CommonHigh128};
862 }
863
864 return resolution == TextureResolution::Pixels256
865 ? std::span<const std::string_view>{CommonMedium256}
866 : std::span<const std::string_view>{CommonMedium128};
867 }
868
869
870 std::span<const std::string_view> europeanLanguageTextureNames(
871 BoardMeshKind mesh,
872 TextureResolution resolution) noexcept
873 {
874 if (!isValidBoardMeshKind(mesh) ||
875 !isValidTextureResolution(resolution))
876 {
877 return {};
878 }
879
880 if (isHighDetailMesh(mesh))
881 {
882 return resolution == TextureResolution::Pixels256
883 ? std::span<const std::string_view>{EuroHighLanguage256}
884 : std::span<const std::string_view>{EuroHighLanguage128};
885 }
886
887 return resolution == TextureResolution::Pixels256
888 ? std::span<const std::string_view>{EuroMediumLanguage256}
889 : std::span<const std::string_view>{EuroMediumLanguage128};
890 }
891
892
893 std::span<const std::string_view> europeanBoardOverlayTextureNames(
894 BoardMeshKind mesh,
895 TextureResolution resolution) noexcept
896 {
897 if (!isValidBoardMeshKind(mesh) ||
898 !isValidTextureResolution(resolution))
899 {
900 return {};
901 }
902
903 if (isHighDetailMesh(mesh))
904 {
905 return resolution == TextureResolution::Pixels256
906 ? std::span<const std::string_view>{EuroHighBoards256}
907 : std::span<const std::string_view>{EuroHighBoards128};
908 }
909
910 return resolution == TextureResolution::Pixels256
911 ? std::span<const std::string_view>{EuroMediumBoards256}
912 : std::span<const std::string_view>{EuroMediumBoards128};
913 }
914
915
916 std::span<const std::string_view> europeanCurrencyTextureNames(
917 BoardMeshKind mesh,
918 TextureResolution resolution) noexcept
919 {
920 if (!isValidBoardMeshKind(mesh) ||
921 !isValidTextureResolution(resolution))
922 {
923 return {};
924 }
925
926 if (isHighDetailMesh(mesh))
927 {
928 return resolution == TextureResolution::Pixels256
929 ? std::span<const std::string_view>{EuroHighCurrencies256}
930 : std::span<const std::string_view>{EuroHighCurrencies128};
931 }
932
933 return resolution == TextureResolution::Pixels256
934 ? std::span<const std::string_view>{EuroMediumCurrencies256}
935 : std::span<const std::string_view>{EuroMediumCurrencies128};
936 }
937
938
940 TextureResolution resolution) noexcept
941 {
942 if (!isValidTextureResolution(resolution))
943 {
944 return {};
945 }
946
947 return resolution == TextureResolution::Pixels256
948 ? "GO_OVERLAY256.bmp"
949 : "GO_OVERLAY128.bmp";
950 }
951
952
953 std::span<const std::string_view>
958
959
960 const std::vector<LegacyTextureAsset>& legacyTextureCorpusManifest()
961 {
962 static const auto manifest = []
963 {
964 std::vector<LegacyTextureAsset> result;
965 result.reserve(LegacyTextureCorpusSize);
966
967 // 12 jeux d'overlays de plateaux européens.
968 for (std::size_t board = 0; board < 12; ++board)
969 {
970 for (const auto resolution :
973 {
975 result,
977 "Boards", "Board", board, "Medium"),
980 resolution),
983 resolution,
984 8
985 );
987 result,
989 "Boards", "Board", board, "High"),
992 resolution),
995 resolution,
996 8
997 );
998 }
999 }
1000
1001 // City00 ne contient que les sept substitutions classiques 256.
1003 result,
1004 "Cities/City00/Medium",
1009 8
1010 );
1012 result,
1013 "Cities/City00/High",
1018 8
1019 );
1020
1021 // Dix villes : 6 photos, 4 noms medium et 3 noms high dans les
1022 // deux résolutions. Les photos sont les seuls BMP 24 bits.
1023 for (std::size_t city = 1; city <= 10; ++city)
1024 {
1025 for (const auto resolution :
1028 {
1030 result,
1032 "Cities", "City", city, "Photos"),
1033 cityPhotoTextureNames(resolution),
1036 resolution,
1037 24
1038 );
1040 result,
1042 "Cities", "City", city, "Medium"),
1045 resolution),
1048 resolution,
1049 8
1050 );
1051
1052 const auto highNames =
1053 resolution == TextureResolution::Pixels128
1054 ? std::span<const std::string_view>
1058 resolution);
1059
1061 result,
1063 "Cities", "City", city, "High"),
1064 highNames,
1067 resolution,
1068 8
1069 );
1070 }
1071 }
1072
1073 for (const auto resolution :
1076 {
1078 result,
1079 "Cities/Common/Medium",
1082 resolution),
1085 resolution,
1086 8
1087 );
1089 result,
1090 "Cities/Common/High",
1093 resolution),
1096 resolution,
1097 8
1098 );
1099 }
1100
1101 // Treize systèmes monétaires, chacun avec medium/high et les
1102 // overlays GO correspondants.
1103 for (std::size_t currency = 0; currency < 13; ++currency)
1104 {
1105 for (const auto resolution :
1108 {
1110 "Currency", "Curr", currency, "Medium");
1112 "Currency", "Curr", currency, "High");
1113
1115 result,
1119 resolution),
1122 resolution,
1123 8
1124 );
1126 result,
1128 europeanGoOverlayTextureName(resolution),
1131 resolution,
1132 8
1133 );
1135 result,
1139 resolution),
1142 resolution,
1143 8
1144 );
1146 result,
1148 europeanGoOverlayTextureName(resolution),
1151 resolution,
1152 8
1153 );
1154 }
1155 }
1156
1157 // Neuf dossiers localisés (UK puis huit langues européennes).
1158 for (std::size_t language = 0; language < 9; ++language)
1159 {
1160 for (const auto resolution :
1163 {
1165 result,
1167 "Languages", "Lang", language, "Medium"),
1170 resolution),
1173 resolution,
1174 8
1175 );
1177 result,
1179 "Languages", "Lang", language, "High"),
1182 resolution),
1185 resolution,
1186 8
1187 );
1188 }
1189 }
1190
1191 return result;
1192 }();
1193
1194 return manifest;
1195 }
1196
1197
1199 BoardMeshKind mesh,
1200 TextureResolution resolution)
1201 {
1202 if (!isValidBoardMeshKind(mesh))
1203 {
1204 return std::unexpected(TextureCatalogError
1205 {
1207 .rawValue = static_cast<std::uint32_t>(boardMeshTag(mesh)),
1208 .detail = "board mesh must be one of the four TexInfo HMD tags"
1209 });
1210 }
1211
1212 if (!isValidTextureResolution(resolution))
1213 {
1214 return std::unexpected(TextureCatalogError
1215 {
1217 .rawValue = static_cast<std::uint32_t>(resolution),
1218 .detail = "texture resolution must be exactly 128 or 256"
1219 });
1220 }
1221
1223 {
1224 .mesh = mesh,
1225 .resolution = resolution,
1226 .meshDataId = boardMeshDataId(mesh),
1228 };
1229
1230 if (!isCityMesh(mesh) &&
1231 resolution == TextureResolution::Pixels128)
1232 {
1234 return recipe;
1235 }
1236
1237 const auto coordinates = usaTextureCoordinates(mesh);
1238 const auto photos = isCityMesh(mesh)
1239 ? cityPhotoTextureNames(resolution)
1240 : std::span<const std::string_view>{};
1241 const auto names = usaCityNameTextureNames(mesh, resolution);
1242 const auto common = commonTextureNames(mesh, resolution);
1243
1244 recipe.textures.reserve(
1245 photos.size() + names.size() + common.size()
1246 );
1247
1249 recipe,
1251 0,
1252 photos,
1255 );
1257 recipe,
1259 photos.size(),
1260 names,
1263 );
1265 recipe,
1267 photos.size() + names.size(),
1268 common,
1271 );
1272
1273 return recipe;
1274 }
1275
1276
1278 BoardMeshKind mesh,
1279 TextureResolution resolution)
1280 {
1281 if (!isValidBoardMeshKind(mesh))
1282 {
1283 return std::unexpected(TextureCatalogError
1284 {
1286 .rawValue = static_cast<std::uint32_t>(boardMeshTag(mesh)),
1287 .detail = "board mesh must be one of the four TexInfo HMD tags"
1288 });
1289 }
1290
1291 if (!isValidTextureResolution(resolution))
1292 {
1293 return std::unexpected(TextureCatalogError
1294 {
1296 .rawValue = static_cast<std::uint32_t>(resolution),
1297 .detail = "texture resolution must be exactly 128 or 256"
1298 });
1299 }
1300
1302 {
1303 .mesh = mesh,
1304 .resolution = resolution,
1305 .meshDataId = boardMeshDataId(mesh),
1307 };
1308
1309 const auto coordinates = europeanTextureCoordinates(mesh);
1310 const auto photos = isCityMesh(mesh)
1311 ? cityPhotoTextureNames(resolution)
1312 : std::span<const std::string_view>{};
1313 const auto names = isCityMesh(mesh)
1314 ? usaCityNameTextureNames(mesh, resolution)
1315 : std::span<const std::string_view>{};
1316 const auto languages =
1317 europeanLanguageTextureNames(mesh, resolution);
1318 const auto boardOverlays =
1319 europeanBoardOverlayTextureNames(mesh, resolution);
1320 const auto currencies =
1321 europeanCurrencyTextureNames(mesh, resolution);
1322
1323 // Dans le code original, les textures de langue déjà remplacées par
1324 // les noms de ville sont sautées : indices 4..6 (medium) ou 3..10
1325 // (high) seulement pour un mesh de ville.
1326 const std::size_t firstLanguage = names.size();
1327 const std::size_t languageCount = languages.size() - firstLanguage;
1328
1329 recipe.textures.reserve(
1330 photos.size() + names.size() + languageCount + currencies.size()
1331 );
1332
1334 recipe,
1336 0,
1337 photos,
1340 );
1342 recipe,
1344 photos.size(),
1345 names,
1348 );
1349
1350 const std::size_t languageCoordinateOffset = photos.size();
1351
1352 for (
1353 std::size_t languageIndex = firstLanguage;
1354 languageIndex < languages.size();
1355 ++languageIndex)
1356 {
1358 {
1361 .resolution = resolution,
1362 .role = TextureRole::Language,
1363 .location = TextureLocation::Language,
1364 .fileName = languages[languageIndex]
1365 };
1366
1367 // Cette condition volontairement exprimée avec l'indice global
1368 // reproduit le caller historique. Elle applique les overlays de
1369 // plateau au classique et aucun aux meshes de ville.
1370 if (languageIndex < boardOverlays.size())
1371 {
1373 {
1376 .fileName = boardOverlays[
1378 };
1379 }
1380 else if (languageIndex == languages.size() - 1)
1381 {
1383 {
1385 .location = TextureLocation::Currency,
1386 .fileName = europeanGoOverlayTextureName(resolution)
1387 };
1388 }
1389
1390 recipe.textures.push_back(reference);
1391 }
1392
1394 recipe,
1396 photos.size() + languages.size(),
1397 currencies,
1400 );
1401
1402 return recipe;
1403 }
1404}
const char * name
fs::path relative
std::size_t root
unsigned index
std::array< data::DataTag, 3 > roots
std::filesystem::path directory
static std::expected< ResourcePaths, DataError > create(std::span< const std::filesystem::path > roots)
constexpr DataId boardMeshDataId(BoardMeshKind mesh) noexcept
constexpr DataTag boardMeshTag(BoardMeshKind mesh) noexcept
constexpr bool isHighDetailMesh(BoardMeshKind mesh) noexcept
std::span< const TextureCoordinate > europeanTextureCoordinates(BoardMeshKind mesh) noexcept
constexpr std::size_t LegacyTextureCorpusSize
std::string_view textureCatalogErrorCodeName(TextureCatalogErrorCode code) noexcept
std::span< const std::string_view > cityPhotoTextureNames(TextureResolution resolution) noexcept
std::span< const std::string_view > europeanLanguageTextureNames(BoardMeshKind mesh, TextureResolution resolution) noexcept
std::string_view europeanGoOverlayTextureName(TextureResolution resolution) noexcept
BoardTextureRecipeResult buildUsaTextureRecipe(BoardMeshKind mesh, TextureResolution resolution)
constexpr TextureDimensions textureDimensions(TextureResolution resolution) noexcept
std::span< const std::string_view > twoDimensionalBoardTextureNames() noexcept
constexpr bool isCityMesh(BoardMeshKind mesh) noexcept
constexpr bool isValidTextureResolution(TextureResolution resolution) noexcept
std::expected< BoardTextureRecipe, TextureCatalogError > BoardTextureRecipeResult
std::span< const std::string_view > europeanBoardOverlayTextureNames(BoardMeshKind mesh, TextureResolution resolution) noexcept
constexpr bool isValidBoardMeshKind(BoardMeshKind mesh) noexcept
std::span< const std::string_view > europeanCurrencyTextureNames(BoardMeshKind mesh, TextureResolution resolution) noexcept
std::expected< std::string, TextureCatalogError > boardTextureRelativePath(BoardMeshKind mesh, TextureLocation location, std::string_view fileName, const BoardTextureContext &context)
std::expected< std::filesystem::path, std::string > resolveBoardTexturePath(const ResourcePaths &stockPaths, BoardMeshKind mesh, TextureLocation location, std::string_view fileName, const BoardTextureContext &context)
std::span< const std::string_view > usaCityNameTextureNames(BoardMeshKind mesh, TextureResolution resolution) noexcept
const std::vector< LegacyTextureAsset > & legacyTextureCorpusManifest()
std::span< const TextureCoordinate > usaTextureCoordinates(BoardMeshKind mesh) noexcept
std::span< const std::string_view > commonTextureNames(BoardMeshKind mesh, TextureResolution resolution) noexcept
BoardTextureRecipeResult buildEuropeanTextureRecipe(BoardMeshKind mesh, TextureResolution resolution)