16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-18 01:59:19 +02:00

Add v6 node-class markers (Stage 2 foundation for progressive loading)

Each top-level node is wrapped in a marker carrying its class (infrastructure vs
visual) and byte span, so the reader can serve or skip a whole node per load pass
without knowing its terminator token. The bake recognizes nodes by the "node"
keyword + type token (map type->terminator confirmed against the deserializers)
and classifies them; the writer buffers a node's entries and emits the marker.
The reader gains a pass selector (all / infrastructure / visual) and skips nodes
not in the pass by advancing past their byte span.

This is the foundation only: the default pass is "all", so loading is identical
to v5 (markers are transparent) -- verified by loading td.scn to the same point.
The eager/visual split is wired up by the upcoming loader/driver integration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
maj00r
2026-06-22 20:09:09 +02:00
parent 8a91559895
commit c337866c58
4 changed files with 190 additions and 29 deletions

View File

@@ -90,6 +90,34 @@ inline std::string formatNumber(double Value)
return std::string(buffer, result.ptr);
}
// classifies a scenery node by its `type` token (lower-cased) for v6 markers: sets Visual
// and the node's terminator token. returns false for an unrecognized type. mapping
// confirmed against the node deserializers.
inline bool classifyNodeType(std::string const &Type, bool &Visual, std::string &Endtoken)
{
struct entry { char const *type; bool visual; char const *end; };
static const entry table[] = {
{ "model", true, "endmodel" },
{ "triangles", true, "endtri" },
{ "triangle_strip", true, "endtri" },
{ "triangle_fan", true, "endtri" },
{ "lines", true, "endline" },
{ "line_strip", true, "endline" },
{ "line_loop", true, "endline" },
{ "track", false, "endtrack" },
{ "traction", false, "endtraction" },
{ "tractionpowersource", false, "end" },
{ "memcell", false, "endmemcell" },
{ "eventlauncher", false, "endevent" },
{ "sound", false, "endsound" },
{ "dynamic", false, "enddynamic" },
};
for (auto const &e : table) {
if (Type == e.type) { Visual = e.visual; Endtoken = e.end; return true; }
}
return false;
}
inline bool endsWithLower(const std::string &s, const char *suffix)
{
const std::string suf(suffix);
@@ -286,12 +314,23 @@ cParser::~cParser()
}
}
void cParser::bakeFinishNode()
{
// flush a node still open at end-of-file or one whose type was unrecognized
if (m_bakenode_active && m_writer)
{
m_writer->end_node(m_bakenode_visual);
}
m_bakenode_active = false;
}
std::vector<std::string> cParser::bakeFile()
{
// drain the file: every token is captured into the twin, include directives are
// recorded (their candidate filenames collected) but not opened
std::string token;
do { token = getToken<std::string>(); } while (false == token.empty());
bakeFinishNode();
// write the twin synchronously -- the parallel baker already runs one file per
// worker thread, so there is no point handing this to the async pool
@@ -319,6 +358,7 @@ void cParser::flushBinaryTwin()
// source not fully consumed (aborted parse): don't leave a truncated twin
return;
}
bakeFinishNode(); // close a node still open at end-of-file
m_twinwritten = true;
// hand the finished writer to the background pool; serialization and file I/O then
@@ -698,6 +738,30 @@ void cParser::readToken(std::string &out, bool ToLower, const char *Break)
if (m_compiling && m_writer && fromOwn
&& (false == m_capturesuppress) && (false == rawtoken.empty()))
{
// v6: wrap each top-level node in a marker so the reader can serve/skip it per
// load pass. a node starts at the "node" keyword and ends at its type-specific
// terminator; nodes are never nested.
std::string const lowered = ::ToLower(rawtoken); // ::-qualified; the param `ToLower` shadows it
if ((false == m_bakenode_active) && (lowered == "node"))
{
m_writer->begin_node();
m_bakenode_active = true;
m_bakenode_count = 0;
m_bakenode_visual = false;
m_bakenode_end.clear();
}
else if (m_bakenode_active && m_bakenode_end.empty() && (lowered == "node"))
{
// previous node had an unrecognized type (no terminator found): close it
// just before this new node starts
bakeFinishNode();
m_writer->begin_node();
m_bakenode_active = true;
m_bakenode_count = 0;
m_bakenode_visual = false;
m_bakenode_end.clear();
}
// numeric tokens -> typed values; quoted strings preserve case at replay;
// other strings (names, paths, keywords, "(pN)") may be lower-cased per consumer
double value = 0.0;
@@ -709,6 +773,22 @@ void cParser::readToken(std::string &out, bool ToLower, const char *Break)
{
m_writer->add_token(rawtoken, rawquoted);
}
if (m_bakenode_active)
{
++m_bakenode_count;
if ((m_bakenode_count == 5) && m_bakenode_end.empty())
{
// 5th node entry is the type token (node, range_max, range_min, name, type)
classifyNodeType(lowered, m_bakenode_visual, m_bakenode_end);
}
else if ((false == m_bakenode_end.empty()) && (lowered == m_bakenode_end))
{
// terminator captured: close the node
m_writer->end_node(m_bakenode_visual);
m_bakenode_active = false;
}
}
}
return;
}