1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
#include "impl.hpp"
#include "compat/assert.hpp"
#include "compat/exception.hpp"
#include "src/emplacer.hpp"
#include "src/ground-atlas.hpp"
#include "src/anim-atlas.hpp"
#include <cstdio>
#include <algorithm>
#include <Corrade/Containers/ArrayViewStl.h>
#include <Corrade/Containers/Pair.h>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Containers/StringStlView.h>
#include <Corrade/Utility/Path.h>
#include <Magnum/Trade/ImageData.h>
namespace floormat {
StringView loader_::make_atlas_path(char(&buf)[FILENAME_MAX], StringView dir, StringView name)
{
fm_soft_assert(!dir || dir[dir.size()-1] == '/');
const auto dirsiz = dir.size(), namesiz = name.size();
fm_soft_assert(dirsiz + namesiz + 1 < FILENAME_MAX);
std::memcpy(buf, dir.data(), dirsiz);
std::memcpy(&buf[dirsiz], name.data(), namesiz);
auto len = dirsiz + namesiz;
buf[len] = '\0';
return StringView{buf, len, StringViewFlag::NullTerminated};
}
bool loader_::check_atlas_name(StringView str) noexcept
{
constexpr auto first_char =
"@_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"_s;
if (str == "<invalid>"_s)
return true;
if (!str || !first_char.find(str[0]))
return false;
if (str.findAny("\\\"'\n\r\t\a\033\0|$!%{}^*?<>&;:^"_s) || str.find("/."_s) || str.find("//"_s))
return false;
return true;
}
} // namespace floormat
namespace floormat::loader_detail {
std::shared_ptr<ground_atlas> loader_impl::ground_atlas(StringView name, Vector2ub size, pass_mode pass) noexcept(false)
{
if (auto it = ground_atlas_map.find(name); it != ground_atlas_map.end())
{
fm_assert(it->second->pass_mode() == pass);
return it->second;
}
fm_soft_assert(check_atlas_name(name));
char buf[FILENAME_MAX];
auto path = make_atlas_path(buf, GROUND_TILESET_PATH, name);
auto atlas = std::make_shared<class ground_atlas>(path, name, texture(""_s, path, false), size, pass);
ground_atlas_map[atlas->name()] = atlas;
return atlas;
}
std::shared_ptr<class ground_atlas> loader_impl::ground_atlas(StringView filename) noexcept(false)
{
fm_assert(!ground_atlas_map.empty());
auto it = ground_atlas_map.find(filename);
if (it == ground_atlas_map.end())
fm_throw("no such tile atlas '{}'"_cf, filename);
return it->second;
}
ArrayView<const String> loader_impl::anim_atlas_list()
{
if (anim_atlases.empty())
get_anim_atlas_list();
fm_assert(!anim_atlases.empty());
return anim_atlases;
}
std::shared_ptr<anim_atlas> loader_impl::anim_atlas(StringView name, StringView dir) noexcept(false)
{
fm_soft_assert(dir && dir[dir.size()-1] == '/');
char buf[FILENAME_MAX];
auto path = make_atlas_path(buf, dir, name);
if (auto it = anim_atlas_map.find(path); it != anim_atlas_map.end())
return it->second;
else
{
fm_soft_assert(check_atlas_name(name));
auto anim_info = deserialize_anim(path + ".json");
for (anim_group& group : anim_info.groups)
{
if (!group.mirror_from.isEmpty())
{
auto it = std::find_if(anim_info.groups.cbegin(), anim_info.groups.cend(),
[&](const anim_group& x) { return x.name == group.mirror_from; });
if (it == anim_info.groups.cend())
fm_throw("can't find group '{}' to mirror from '{}'"_cf, group.mirror_from, group.name);
group.frames = it->frames;
for (anim_frame& f : group.frames)
f.ground = Vector2i((Int)f.size[0] - f.ground[0], f.ground[1]);
}
}
auto tex = texture(""_s, path, false);
fm_soft_assert(!anim_info.object_name.isEmpty());
fm_soft_assert(anim_info.pixel_size.product() > 0);
fm_soft_assert(!anim_info.groups.empty());
fm_soft_assert(anim_info.nframes > 0);
fm_soft_assert(anim_info.nframes == 1 || anim_info.fps > 0);
const auto size = tex.pixels().size();
const auto width = size[1], height = size[0];
fm_soft_assert(anim_info.pixel_size[0] == width && anim_info.pixel_size[1] == height);
auto atlas = std::make_shared<class anim_atlas>(path, tex, std::move(anim_info));
return anim_atlas_map[atlas->name()] = atlas;
}
}
void loader_impl::get_anim_atlas_list()
{
anim_atlases.clear();
using f = Path::ListFlag;
constexpr auto flags = f::SkipDirectories | f::SkipDotAndDotDot | f::SkipSpecial | f::SortAscending;
if (const auto list = Path::list(ANIM_PATH, flags); list)
{
anim_atlases.reserve(list->size());
constexpr auto suffix = ".json"_s;
for (StringView str : *list)
if (str.hasSuffix(suffix))
anim_atlases.emplace_back(str.exceptSuffix(suffix.size()));
}
anim_atlases.shrink_to_fit();
fm_assert(!anim_atlases.empty());
}
} // namespace floormat::loader_detail
|