/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include #include "metafile/nml.h" #include "ascii/parser.h" #include "ascii/ascii_encoder.h" #include "filesystem/io_handle.h" #include "filesystem/filesystem.h" #include "platform.h" #include "log/log.h" using namespace GS; using namespace GS::NML; using namespace GS::AsciiParser; //------------------------------------------------------------------------------ bool Parser::IsMetafile(const char *name) { AutoPtr h(Platform::Get().io->Open(name)); if (h.IsNull()) return false; char header[9]; h->Read(header, 9); if (Memory::Compare(header, "': s++; break; // Node/real/integer/string. case '=': { s++; forever { s += SkipSpace(s, e) - s; if (s == e) MLTAG_ERROR("Mangled definition, metatag '" << tag.name << "'.\n") // End of tag. if (s[0] == '>') { s++; break; } // Preprocessor directive. if (s[0] == '#') s = ParseTagPreprocessorDirective(tag, s + 1, e); // Node. else if (s[0] == '<') { if (tag.GetValue().GetType() != Variant::VariantNone) MLTAG_ERROR("Incoherent type in tag '" << tag.name << "' declaration.\n") Tag *stag = tag.tags.Add(new Tag)->Object(); if (!ParseTag(*stag, s, e, &s)) MLTAG_ERROR("") s += SkipSpace(s, e) - s; } // Constant. else { if (tag.GetValue().GetType() != Variant::VariantNone) MLTAG_ERROR("Incoherent type in tag '" << tag.name << "' declaration.\n") // Binary. if (s[0] == '=') { s++; if (!(s[0] >= '0' && s[0] <= '9')) MLTAG_ERROR("Expected encoded size in binary tag '" << tag.name << "' declaration.\n") const char *ye = s; while (ye[0] >= '0' && ye[0] <= '9') ye++; if (ye[0] != ':') MLTAG_ERROR("Expected size delimiter in binary tag '" << tag.name << "' declaration.\n") uint asize = String(s, ye).Integer(); s = ye + 1; if (!(s[0] >= '0' && s[0] <= '9')) MLTAG_ERROR("Expected binary size in binary tag '" << tag.name << "' declaration.\n") ye = s; while (ye[0] >= '0' && ye[0] <= '9') ye++; // Trailing @ means yEnc binary. File::Binary encoding = File::Binary_UU; if (ye[0] == '@') { encoding = File::Binary_yEnc; ye++; } // Detect EOL if ((ye[0] != 0x0a) && ((ye[0] != 0x0d) && (ye[1] != 0x0a))) MLTAG_ERROR("Expected EOL following binary size in binary tag '" << tag.name << "' declaration.\n") size_t eol_size = (ye[0] == 0x0a) ? 1 : 2; uint bsize = String(s, ye).Integer(); uchar *astart = (uchar *)(ye + eol_size); // [EJ] Adjust asize to account for Windows EOL (historically NML only specifies Unix ascii size). if (eol_size > 1) { __LOG_V__ << "CRLF reduces NML binary load performance.\n"; size_t a_size_in = asize; asize = 0; for (; a_size_in > 0; --a_size_in) if ((astart[asize] == 0x0d) && (astart[asize + 1] == 0x0a)) asize += 2; else ++asize; } // Load ASCII encoded data. Array aenc(asize, Alloc::Metatag); if (!aenc) MLTAG_ERROR("Failed to allocate binary buffer in binary tag '" << tag.name << "'.\n") memcpy(&aenc[0], astart, asize); s = ye + eol_size + asize; if (s[0] != '>') MLTAG_ERROR("Expected closing tag in tag '" << tag.name << "'.\n") Array data(bsize, Alloc::Metatag); if (data) { switch (encoding) { case File::Binary_UU: nAsciiEncoder::UUDecode(&aenc[0], asize, &data[0], bsize); break; case File::Binary_yEnc: nAsciiEncoder::yDecode(&aenc[0], asize, &data[0], bsize); break; } tag.GetValue().SetBinary(&data[0], bsize); } } // Real/Integer. else if ((s[0] >= '0' && s[0] <= '9') || (s[0] == '.') || (s[0] == '-')) { if (IsConstantFloat(s, e)) tag.GetValue() = String::atof(s, e, true); else tag.GetValue() = String::atoi(s); if (s[0] == '-') s++; s += SkipEntry(s, e) - s; if (s[0] != '>') MLTAG_ERROR("Unexpected trailing expression following value, metatag '" << tag.name << "'.\n") } // String. else if (s[0] == '\"') { s++; ptrdiff_t len = RunToEOS(s, e) - s; if ((s + len) == e) MLTAG_ERROR("Mangled string declaration, metatag '" << tag.name << "'.\n") // Copy string. tag.GetValue() = String(s, s + len); s += SkipSpace(s + len + 1, e) - s; // Jump over string. if (s == e) MLTAG_ERROR("Unexpected EOF after string declaration, metatag '" << tag.name << "'.\n") if (s[0] != '>') MLTAG_ERROR("Unexpected trailing expression following string object, metatag '" << tag.name << "'.\n") tag.GetValue().s_value.ReplaceAll("\\n", "\n"); // convert CF } // Boolean. else if (!strncmp(s, "True", 4)) { tag.GetValue() = true; s += SkipSpace(s + 4, e) - s; if (s[0] != '>') MLTAG_ERROR("Unexpected trailing expression following value, metatag '" << tag.name << "'.\n") } else if (!strncmp(s, "False", 5)) { tag.GetValue() = false; s += SkipSpace(s + 5, e) - s; if (s[0] != '>') MLTAG_ERROR("Unexpected trailing expression following value, metatag '" << tag.name << "'.\n") } else MLTAG_ERROR("Unexpected '" << s[0] << "' in assignation, metatag '" << tag.name << "'.\n") } } } break; default: MLTAG_ERROR("Unexpected trailing expression after metatag '" << tag.name << "' name declaration.\n") } if (es) es[0] = s; return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Parser::LoadFromMemory(const char *data, size_t size, File &mfl) { mfl.Free(); if (!data) return false; // Read header tag, expected to be NML version. const char *pof = data, *eof = data + size; #define MEM_MLP_ERROR(c) { (c); return false; } Tag header_tag; if (!ParseTag(header_tag, pof, eof, &pof)) MEM_MLP_ERROR(__LOG_E__ << "Invalid metafile.\n") if ((header_tag.name != "Version") && (header_tag.name != "NML")) MEM_MLP_ERROR(__LOG_E__ << "Unknown metafile variant.\n") switch (header_tag.GetValue().GetType()) { case Variant::VariantInteger: if (header_tag.GetInteger() > version) __LOG_W__ << "Newer version NML header found (" << header_tag.GetInteger() << ">" << version << ").\n"; break; case Variant::VariantFloat: if (header_tag.GetReal() > version) __LOG_W__ << "Newer version NML header found (" << header_tag.GetReal() << ">" << version << ").\n"; break; default: __LOG_W__ << "Unknown NML header version identification method.\n"; break; } // Read all root tags. while (pof < eof) { Tag *tag = mfl.tags.Add(new Tag)->Object(); if (!ParseTag(*tag, pof, eof, &pof)) return false; pof += SkipSpace(pof, eof) - pof; } return true; } bool Parser::Load(const char *path, File &file, bool verbose) { if (!path) return false; Array data; if (!Platform::Get().io->FileLoad(path, data, verbose)) return false; try { if (!LoadFromMemory(data.c_ptr(), data.GetSize(), file)) return false; } catch (char *e) { __LOG_E__ << "Failed to load file LoadFromMemory. " << path << "\n"; return false; } file.name = path; return true; } File *Parser::Load(const char *metafile, bool verbose) { AutoPtr mfl(new File); if (mfl.IsNull()) __ERR__(__LOG_E__ << "Failed to allocate file.\n", NULL) return Load(metafile, *mfl, verbose) ? mfl.Detach() : NULL; } //------------------------------------------------------------------------------