mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 03:59:19 +02:00
feat(editor): keep content added by the editor ahead of FirstInit
This commit is contained in:
@@ -60,6 +60,7 @@ layer_handle node_layers::open(std::string const &Name)
|
||||
m_layers.emplace_back();
|
||||
m_layers.back().name = Name;
|
||||
m_layers.back().parent = handle();
|
||||
m_layers.back().late = m_initialized;
|
||||
stat(layerhandle);
|
||||
}
|
||||
else
|
||||
@@ -116,9 +117,83 @@ void node_layers::clear()
|
||||
m_templates.clear();
|
||||
m_instances.clear();
|
||||
m_instance = 0;
|
||||
m_initialized = false;
|
||||
m_terraindirective = false;
|
||||
}
|
||||
|
||||
// indicates the scenario initialization (FirstInit) is being performed
|
||||
void node_layers::initialization(source_span const &Span, bool const Infile)
|
||||
{
|
||||
if (m_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_initialized = true;
|
||||
if (m_stack.empty() || false == valid(m_stack.back()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// the layer file being loaded holds the directive, or includes the template which does
|
||||
auto &holder{layer(m_stack.back())};
|
||||
if (Infile)
|
||||
{
|
||||
// NOTE: scenario without the directive is initialized at its end, with nothing in the file to point at
|
||||
std::string text(Span.valid() ? static_cast<std::size_t>(Span.end - Span.begin) : 0, '\0');
|
||||
std::ifstream input{path(m_stack.back()), std::ios_base::binary};
|
||||
if (text.empty() || false == input.is_open() || false == input.seekg(Span.begin).good() || false == input.read(text.data(), static_cast<std::streamsize>(text.size())).good() ||
|
||||
ToLower(text) != "firstinit")
|
||||
{
|
||||
return;
|
||||
}
|
||||
holder.init = Span;
|
||||
}
|
||||
else if (tracked(m_instance) && instance(m_instance).layer == m_stack.back())
|
||||
{
|
||||
holder.init = instance(m_instance).span;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
holder.context_init = m_context;
|
||||
// for the files it was included through the initialization takes place at their include directives
|
||||
for (auto level{m_stack.size() - 1}; level > 0; --level)
|
||||
{
|
||||
auto const child{m_stack[level]};
|
||||
auto const parent{m_stack[level - 1]};
|
||||
if (false == valid(parent) || parent == child || layer(child).sites.empty() || layer(child).sites.back().parent != parent || layer(child).sites.back().fixed ||
|
||||
false == layer(child).sites.back().span.valid())
|
||||
{
|
||||
break;
|
||||
}
|
||||
layer(parent).init = layer(child).sites.back().span;
|
||||
layer(parent).context_init = layer(child).context_begin;
|
||||
}
|
||||
}
|
||||
|
||||
// true if specified layer can receive content added in the editor
|
||||
bool node_layers::accepts(layer_handle Layer, std::string *Reason) const
|
||||
{
|
||||
Layer = resolve(Layer);
|
||||
if (false == valid(Layer) || layer(Layer).removed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (false == writable(Layer, Reason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (layer(Layer).late)
|
||||
{
|
||||
if (Reason != nullptr)
|
||||
{
|
||||
*Reason = "the file is loaded after the scenario initialization (FirstInit), which only vehicles should follow";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// follows layer merges. returns: handle of the layer which currently holds content of specified layer
|
||||
layer_handle node_layers::resolve(layer_handle Layer) const
|
||||
{
|
||||
@@ -183,7 +258,7 @@ void node_layers::count(layer_handle Layer, layer_item const Item, int const Cha
|
||||
void node_layers::active(layer_handle Layer)
|
||||
{
|
||||
Layer = resolve(Layer);
|
||||
if (false == valid(Layer) || layer(Layer).removed || false == writable(Layer))
|
||||
if (false == accepts(Layer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -422,6 +497,11 @@ layer_handle node_layers::create(std::string Name, layer_handle Parent, std::str
|
||||
Reason = "the parent layer can't include other files";
|
||||
return null_handle;
|
||||
}
|
||||
if (layer(Parent).late)
|
||||
{
|
||||
Reason = "the parent layer is loaded after the scenario initialization (FirstInit), which only vehicles should follow";
|
||||
return null_handle;
|
||||
}
|
||||
// file names in the scenery are processed in lower case, with forward slashes
|
||||
Name = ToLower(Name);
|
||||
replace_slashes(Name);
|
||||
@@ -458,10 +538,11 @@ layer_handle node_layers::create(std::string Name, layer_handle Parent, std::str
|
||||
return null_handle;
|
||||
}
|
||||
|
||||
// the include directive goes after the last file included by the parent, so the scenery files stay ahead of
|
||||
// what may follow them in a scenario, like the vehicles
|
||||
// the include directive goes after the last file included by the parent ahead of the scenario initialization,
|
||||
// so the scenery files stay ahead of what follows them in a scenario, like the vehicles
|
||||
auto anchor{null_handle};
|
||||
std::streamoff anchorend{-1};
|
||||
auto const &init{layer(Parent).init};
|
||||
for (std::size_t idx = 0; idx < m_layers.size(); ++idx)
|
||||
{
|
||||
auto const &sibling{m_layers[idx]};
|
||||
@@ -470,7 +551,7 @@ layer_handle node_layers::create(std::string Name, layer_handle Parent, std::str
|
||||
continue;
|
||||
}
|
||||
auto const &site{sibling.sites.front()};
|
||||
if (site.parent == Parent && false == site.fixed && site.span.end > anchorend)
|
||||
if (site.parent == Parent && false == site.fixed && site.span.end > anchorend && (false == init.valid() || site.span.end <= init.begin))
|
||||
{
|
||||
anchor = static_cast<layer_handle>(idx + 1);
|
||||
anchorend = site.span.end;
|
||||
@@ -486,7 +567,7 @@ layer_handle node_layers::create(std::string Name, layer_handle Parent, std::str
|
||||
created.editormade = true;
|
||||
created.anchor = anchor;
|
||||
// placement the new file is going to be loaded with
|
||||
created.context_begin = valid(anchor) ? layer(anchor).context_end : layer(Parent).context_end;
|
||||
created.context_begin = valid(anchor) ? layer(anchor).context_end : layer(Parent).context_insert();
|
||||
created.context_end = created.context_begin;
|
||||
|
||||
active(layerhandle);
|
||||
@@ -609,9 +690,16 @@ bool node_layers::can_merge(layer_handle Source, layer_handle Target, std::strin
|
||||
Reason = "the target layer is included by the merged one";
|
||||
return false;
|
||||
}
|
||||
if (false == source.context_end.matches(target.context_end))
|
||||
if (target.late != source.late && (source.created || source.sites.empty() || resolve(source.sites.front().parent) != Target))
|
||||
{
|
||||
// directives of includes placed in the editor are prepared for the end of their layer file
|
||||
// the content would change sides of the scenario initialization
|
||||
Reason = (target.late ? "the target layer is loaded after the scenario initialization (FirstInit), which only vehicles should follow" :
|
||||
"the merged layer is loaded after the scenario initialization (FirstInit), its content has to stay there");
|
||||
return false;
|
||||
}
|
||||
if (false == source.context_insert().matches(target.context_insert()))
|
||||
{
|
||||
// directives of includes placed in the editor are prepared for the place their layer file receives them at
|
||||
auto const unsaved{std::any_of(std::begin(m_instances), std::end(m_instances), [&](include_instance const &Instance) {
|
||||
return false == Instance.dead && false == Instance.removed && false == Instance.span.valid() && resolve(Instance.layer) == Source;
|
||||
})};
|
||||
@@ -642,9 +730,9 @@ bool node_layers::can_merge(layer_handle Source, layer_handle Target, std::strin
|
||||
}
|
||||
if (resolve(source.sites.front().parent) != Target)
|
||||
{
|
||||
// the text is moved to the end of another file. its statements have to keep their meaning there,
|
||||
// the text is moved to another file. its statements have to keep their meaning there,
|
||||
// and can't leave anything behind for what used to follow it
|
||||
if (false == source.context_begin.matches(target.context_end) || false == source.context_end.matches(source.context_begin))
|
||||
if (false == source.context_begin.matches(target.context_insert()) || false == source.context_end.matches(source.context_begin))
|
||||
{
|
||||
Reason = "the layers are loaded with different origin, rotation or scale";
|
||||
return false;
|
||||
@@ -779,7 +867,7 @@ void node_layers::removed(instance_handle Instance, bool Removed)
|
||||
instance_handle node_layers::place(layer_handle Layer, std::string const &File, std::string const &Directive)
|
||||
{
|
||||
Layer = resolve(Layer);
|
||||
if (false == valid(Layer) || m_instances.size() + 1 >= untracked_instance)
|
||||
if (false == accepts(Layer) || m_instances.size() + 1 >= untracked_instance)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -788,7 +876,7 @@ instance_handle node_layers::place(layer_handle Layer, std::string const &File,
|
||||
included.layer = Layer;
|
||||
included.file = &(*m_templates.emplace(File).first);
|
||||
included.directive = Directive;
|
||||
included.context = layer(Layer).context_end;
|
||||
included.context = layer(Layer).context_insert();
|
||||
return static_cast<instance_handle>(m_instances.size());
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,12 @@ struct basic_layer
|
||||
std::vector<include_site> sites; // include directives bringing the file in; empty for the scenario file
|
||||
layer_context context_begin; // placement in effect at the start of the file...
|
||||
layer_context context_end; // ...and right after its end
|
||||
// location in the file of what performs the scenario initialization, if the file takes a part in it: the FirstInit
|
||||
// directive, or the directive including the file which holds it. only vehicles are meant to follow the
|
||||
// initialization, so what the editor adds to the file goes ahead of this point rather than at the end
|
||||
source_span init;
|
||||
layer_context context_init; // placement in effect at that point
|
||||
bool late{false}; // the file is loaded after the scenario initialization
|
||||
std::uintmax_t filesize{0}; // state of the file when it was loaded or saved the last time
|
||||
std::filesystem::file_time_type filetime{};
|
||||
// pending changes, applied by the scenery save
|
||||
@@ -92,6 +98,11 @@ struct basic_layer
|
||||
bool dead{false}; // removal or merge of the layer was saved, it's no longer a part of the scenery
|
||||
|
||||
std::size_t item_count() const;
|
||||
// placement in effect at the point of the file which receives the content added by the editor
|
||||
layer_context const &context_insert() const
|
||||
{
|
||||
return init.valid() ? context_init : context_end;
|
||||
}
|
||||
};
|
||||
|
||||
// include directive bringing an *.inc template into a scenery layer file
|
||||
@@ -235,6 +246,11 @@ class node_layers
|
||||
void track(basic_node const *Node, source_span const &Span, glm::vec3 const &Angles = glm::vec3{0.f}, glm::vec3 const &Scale = glm::vec3{1.f});
|
||||
// indicates specified node was removed from the scene
|
||||
void forget(basic_node const *Node);
|
||||
// indicates the scenario initialization (FirstInit) is being performed. Span: location of the directive.
|
||||
// Infile: the directive comes straight from the layer file being loaded
|
||||
void initialization(source_span const &Span, bool Infile);
|
||||
// true if specified layer can receive content added in the editor. optionally explains why it can't
|
||||
bool accepts(layer_handle Layer, std::string *Reason = nullptr) const;
|
||||
// notes the scenery already holds the directive setting up terrain made in the editor
|
||||
void terrain_directive(bool const Present)
|
||||
{
|
||||
@@ -317,6 +333,7 @@ class node_layers
|
||||
std::set<std::string> m_templates; // *.inc templates used by the scenery
|
||||
std::vector<include_instance> m_instances; // includes of the templates; instance handle is index in the vector + 1
|
||||
instance_handle m_instance{0}; // helper, include whose template is being loaded
|
||||
bool m_initialized{false}; // helper, the scenario initialization was performed already
|
||||
bool m_terraindirective{false};
|
||||
};
|
||||
|
||||
|
||||
@@ -304,28 +304,61 @@ struct node_layers::composition
|
||||
std::vector<node_place> nodes;
|
||||
std::vector<site_place> sites;
|
||||
std::vector<instance_place> instances;
|
||||
source_span init; // location of what performs the scenario initialization, see basic_layer::init
|
||||
|
||||
// adds another piece of text at the end
|
||||
void append(composition const &Other)
|
||||
{
|
||||
auto const offset{static_cast<std::streamoff>(text.size())};
|
||||
text += Other.text;
|
||||
insert(static_cast<std::streamoff>(text.size()), Other);
|
||||
}
|
||||
// adds another piece of text at specified location
|
||||
void insert(std::streamoff const Location, composition const &Other)
|
||||
{
|
||||
auto const size{static_cast<std::streamoff>(Other.text.size())};
|
||||
auto const move = [=](source_span &Span) {
|
||||
if (Span.begin >= Location)
|
||||
{
|
||||
Span.begin += size;
|
||||
Span.end += size;
|
||||
}
|
||||
};
|
||||
for (auto &place : nodes)
|
||||
{
|
||||
move(place.span);
|
||||
}
|
||||
for (auto &place : sites)
|
||||
{
|
||||
move(place.span);
|
||||
}
|
||||
for (auto &place : instances)
|
||||
{
|
||||
move(place.span);
|
||||
}
|
||||
if (init.valid())
|
||||
{
|
||||
move(init);
|
||||
}
|
||||
else if (Other.init.valid())
|
||||
{
|
||||
init = {Other.init.begin + Location, Other.init.end + Location};
|
||||
}
|
||||
text.insert(static_cast<std::size_t>(Location), Other.text);
|
||||
for (auto place : Other.nodes)
|
||||
{
|
||||
place.span.begin += offset;
|
||||
place.span.end += offset;
|
||||
place.span.begin += Location;
|
||||
place.span.end += Location;
|
||||
nodes.emplace_back(place);
|
||||
}
|
||||
for (auto place : Other.sites)
|
||||
{
|
||||
place.span.begin += offset;
|
||||
place.span.end += offset;
|
||||
place.span.begin += Location;
|
||||
place.span.end += Location;
|
||||
sites.emplace_back(place);
|
||||
}
|
||||
for (auto place : Other.instances)
|
||||
{
|
||||
place.span.begin += offset;
|
||||
place.span.end += offset;
|
||||
place.span.begin += Location;
|
||||
place.span.end += Location;
|
||||
instances.emplace_back(place);
|
||||
}
|
||||
}
|
||||
@@ -361,12 +394,32 @@ bool node_layers::compose(save_state &State, layer_handle const Layer, compositi
|
||||
{
|
||||
edits = lookup->second.edits;
|
||||
}
|
||||
// what's added to the file goes ahead of the scenario initialization, if the file takes a part in it
|
||||
auto const &init{layer(Layer).init};
|
||||
auto const hasinit{init.valid() && init.end <= basesize};
|
||||
auto addition{basesize};
|
||||
if (hasinit)
|
||||
{
|
||||
addition = init.begin;
|
||||
// on a line of its own, where the initialization has one
|
||||
auto linebegin{addition};
|
||||
while (linebegin > 0 && (base[linebegin - 1] == ' ' || base[linebegin - 1] == '\t'))
|
||||
{
|
||||
--linebegin;
|
||||
}
|
||||
if (linebegin == 0 || base[linebegin - 1] == '\n')
|
||||
{
|
||||
addition = linebegin;
|
||||
}
|
||||
}
|
||||
auto initedited{false}; // the directive standing for the initialization is replaced or removed
|
||||
for (auto &edit : edits)
|
||||
{
|
||||
if (edit.begin < 0)
|
||||
{
|
||||
edit.begin = edit.end = basesize;
|
||||
edit.begin = edit.end = addition;
|
||||
}
|
||||
initedited |= hasinit && edit.begin == init.begin && edit.end == init.end;
|
||||
}
|
||||
std::stable_sort(std::begin(edits), std::end(edits), [](file_patch::edit const &Left, file_patch::edit const &Right) { return Left.begin < Right.begin; });
|
||||
std::streamoff cursor{0};
|
||||
@@ -502,6 +555,11 @@ bool node_layers::compose(save_state &State, layer_handle const Layer, compositi
|
||||
Output.instances.push_back({included, shift(instance(included).span)});
|
||||
}
|
||||
}
|
||||
// NOTE: with the file holding the initialization inlined, its own mark was carried over along with its text
|
||||
if (hasinit && false == initedited && false == Output.init.valid())
|
||||
{
|
||||
Output.init = shift(init);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -635,11 +693,11 @@ save_result node_layers::save(std::vector<std::string> const &Rootstatements)
|
||||
}
|
||||
else if (Model != nullptr && false == Node->from_template())
|
||||
{
|
||||
// created in the editor. the definition is placed at the end of the layer file, with the placement in effect there
|
||||
// created in the editor. the definition is added to the layer file, with the placement in effect at the place it goes to
|
||||
std::string text;
|
||||
Node->export_as_text(text);
|
||||
text.erase(text.find_last_not_of(" \t\r\n") + 1);
|
||||
auto const &context{layer(nodelayer).context_end};
|
||||
auto const &context{layer(nodelayer).context_insert()};
|
||||
if (false == context.matches(layer_context()))
|
||||
{
|
||||
model_placement placement;
|
||||
@@ -716,10 +774,10 @@ save_result node_layers::save(std::vector<std::string> const &Rootstatements)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (false == included.context.matches(layer(target).context_end))
|
||||
if (false == included.context.matches(layer(target).context_insert()))
|
||||
{
|
||||
// shouldn't happen, merge of layers is refused when it'd lead to this
|
||||
return fail("include \"" + *included.file + "\" was placed for different origin, rotation or scale than its layer ends with");
|
||||
return fail("include \"" + *included.file + "\" was placed for different origin, rotation or scale than its layer file receives it with");
|
||||
}
|
||||
placed[target].emplace_back(static_cast<instance_handle>(idx + 1));
|
||||
}
|
||||
@@ -790,6 +848,7 @@ save_result node_layers::save(std::vector<std::string> const &Rootstatements)
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: negative location stands for the place the file receives additions at
|
||||
edit.begin = edit.end = -1;
|
||||
if (false == layer(parent).created)
|
||||
{
|
||||
@@ -797,12 +856,9 @@ save_result node_layers::save(std::vector<std::string> const &Rootstatements)
|
||||
{
|
||||
return fail(state.error);
|
||||
}
|
||||
auto const &content{state.content[parent]};
|
||||
if (false == content.empty() && content.back() != '\n')
|
||||
{
|
||||
edit.text = state.eol;
|
||||
edit.lead = state.eol.size();
|
||||
}
|
||||
// dropped by compose() if the text ahead ends with a line break already
|
||||
edit.text = state.eol;
|
||||
edit.lead = state.eol.size();
|
||||
}
|
||||
edit.text += directive + state.eol;
|
||||
}
|
||||
@@ -878,6 +934,8 @@ save_result node_layers::save(std::vector<std::string> const &Rootstatements)
|
||||
return fail(state.error);
|
||||
}
|
||||
text.append(body);
|
||||
// what the editor adds to the file
|
||||
composition added;
|
||||
if (auto const lookup{appended.find(output)}; lookup != appended.end())
|
||||
{
|
||||
for (auto const merged : lookup->second)
|
||||
@@ -887,18 +945,18 @@ save_result node_layers::save(std::vector<std::string> const &Rootstatements)
|
||||
{
|
||||
return fail(state.error);
|
||||
}
|
||||
ensure_newline(text.text, eol);
|
||||
text.append(mergedtext);
|
||||
ensure_newline(added.text, eol);
|
||||
added.append(mergedtext);
|
||||
}
|
||||
}
|
||||
if (auto const lookup{created.find(output)}; lookup != created.end())
|
||||
{
|
||||
for (auto const &definition : lookup->second)
|
||||
{
|
||||
ensure_newline(text.text, eol);
|
||||
auto const offset{static_cast<std::streamoff>(text.text.size())};
|
||||
text.nodes.push_back({definition.first, {offset, offset + static_cast<std::streamoff>(definition.second.size())}, true});
|
||||
text.text += definition.second + eol;
|
||||
ensure_newline(added.text, eol);
|
||||
auto const offset{static_cast<std::streamoff>(added.text.size())};
|
||||
added.nodes.push_back({definition.first, {offset, offset + static_cast<std::streamoff>(definition.second.size())}, true});
|
||||
added.text += definition.second + eol;
|
||||
}
|
||||
}
|
||||
if (auto const lookup{placed.find(output)}; lookup != placed.end())
|
||||
@@ -906,20 +964,49 @@ save_result node_layers::save(std::vector<std::string> const &Rootstatements)
|
||||
for (auto const included : lookup->second)
|
||||
{
|
||||
auto const &directive{instance(included).directive};
|
||||
ensure_newline(text.text, eol);
|
||||
auto const offset{static_cast<std::streamoff>(text.text.size())};
|
||||
text.instances.push_back({included, {offset, offset + static_cast<std::streamoff>(directive.size())}});
|
||||
text.text += directive + eol;
|
||||
ensure_newline(added.text, eol);
|
||||
auto const offset{static_cast<std::streamoff>(added.text.size())};
|
||||
added.instances.push_back({included, {offset, offset + static_cast<std::streamoff>(directive.size())}});
|
||||
added.text += directive + eol;
|
||||
}
|
||||
}
|
||||
if (output == root)
|
||||
{
|
||||
for (auto const &statement : Rootstatements)
|
||||
{
|
||||
ensure_newline(text.text, eol);
|
||||
text.text += statement + eol;
|
||||
ensure_newline(added.text, eol);
|
||||
added.text += statement + eol;
|
||||
}
|
||||
}
|
||||
if (added.text.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (false == text.init.valid())
|
||||
{
|
||||
ensure_newline(text.text, eol);
|
||||
text.append(added);
|
||||
continue;
|
||||
}
|
||||
// the scenario initialization is meant to be followed by nothing but the vehicles, so the additions go ahead
|
||||
// of it. they take whole lines, with the initialization left at the start of a line the way it was
|
||||
ensure_newline(added.text, eol);
|
||||
auto location{text.init.begin};
|
||||
while (location > 0 && (text.text[location - 1] == ' ' || text.text[location - 1] == '\t'))
|
||||
{
|
||||
--location;
|
||||
}
|
||||
if (location == 0 || text.text[location - 1] == '\n')
|
||||
{
|
||||
text.insert(location, added);
|
||||
}
|
||||
else
|
||||
{
|
||||
composition separated;
|
||||
separated.text = eol;
|
||||
separated.append(added);
|
||||
text.insert(text.init.begin, separated);
|
||||
}
|
||||
}
|
||||
|
||||
// with everything worked out it's safe to touch the files. new content goes to temporary files first,
|
||||
@@ -1007,7 +1094,7 @@ save_result node_layers::save(std::vector<std::string> const &Rootstatements)
|
||||
source.span = place.span;
|
||||
if (isnew)
|
||||
{
|
||||
source.context = layer(output).context_end;
|
||||
source.context = layer(output).context_insert();
|
||||
}
|
||||
if (isnew || place.rewritten)
|
||||
{
|
||||
@@ -1036,6 +1123,19 @@ save_result node_layers::save(std::vector<std::string> const &Rootstatements)
|
||||
included.sites.front().span = place.span;
|
||||
included.parent = output;
|
||||
}
|
||||
// the mark of the scenario initialization moved along with the text around it, or came with the text of a merged layer
|
||||
auto &saved{layer(output)};
|
||||
if (false == saved.init.valid() && texts[output].init.valid())
|
||||
{
|
||||
for (std::size_t idx = 0; idx < m_layers.size(); ++idx)
|
||||
{
|
||||
if (m_layers[idx].init.valid() && m_layers[idx].merged != null_handle && resolve(static_cast<layer_handle>(idx + 1)) == output)
|
||||
{
|
||||
saved.context_init = m_layers[idx].context_init;
|
||||
}
|
||||
}
|
||||
}
|
||||
saved.init = texts[output].init;
|
||||
}
|
||||
for (std::size_t idx = 0; idx < m_layers.size(); ++idx)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user