commit x64 compilation from lulu cause the other branch dont seems to compile properly at home
This commit is contained in:
380
include/framework/metafile/nml_load.cpp
Normal file
380
include/framework/metafile/nml_load.cpp
Normal file
@ -0,0 +1,380 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cstring>
|
||||
#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 <IO::Handle> h(Platform::Get().io->Open(name));
|
||||
if (h.IsNull())
|
||||
return false;
|
||||
|
||||
char header[9];
|
||||
h->Read(header, 9);
|
||||
if (Memory::Compare(header, "<Version=", 9) && Memory::Compare(header, "<NML=", 5))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
const char *Parser::ParseTagPreprocessorDirective(Tag &tag, const char *s, const char *e)
|
||||
{
|
||||
s++;
|
||||
|
||||
// Catch preprocessor directive.
|
||||
if (!strncmp(s, "include", 7))
|
||||
{
|
||||
// Ensure base coherency.
|
||||
if (tag.GetValue().GetType() != Variant::VariantNone)
|
||||
__ERR__(__LOG_E__ << "Incoherent type in tag '" << tag.name << "' declaration.\n", NULL)
|
||||
|
||||
// Parse directive.
|
||||
s += SkipSpace(s + 7, e) - s;
|
||||
if (s[0] != '(')
|
||||
__ERR__(__LOG_E__ << "Mangled #include directive.\n", NULL);
|
||||
|
||||
// Fetch arguments.
|
||||
String include_path;
|
||||
|
||||
forever
|
||||
{
|
||||
if (s[0] == ')')
|
||||
break;
|
||||
s += SkipSpace(s + 1, e) - s;
|
||||
|
||||
// Path.
|
||||
if (s[0] == '"')
|
||||
{
|
||||
s++;
|
||||
uint len = (uint)(RunToEOS(s, e) - s);
|
||||
include_path.Set(s, s + len);
|
||||
|
||||
s += SkipSpace(s + len + 1, e) - s;
|
||||
}
|
||||
else
|
||||
__ERR__(__LOG_E__ << "Unexpected character in #include directive.\n", NULL);
|
||||
}
|
||||
s++;
|
||||
|
||||
// Load external metafile.
|
||||
File tmp_file;
|
||||
|
||||
if (Parser::Load(include_path, tmp_file, true))
|
||||
{
|
||||
// Then transfer all tags to our current file.
|
||||
NMLFileForeach(t, tmp_file)
|
||||
{
|
||||
if (tmp_file.UnlinkRoot(t))
|
||||
tag.AddChild(t);
|
||||
else
|
||||
__LOG_E__ << "Could not relink root tag from included metafile.\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
__LOG_E__ << "Failed to include external metafile '" << include_path << "'.\n";
|
||||
}
|
||||
else if (!strncmp(s, "append", 6))
|
||||
__LOG_E__ << "Preprocessor directive #append deprecated.\n";
|
||||
else
|
||||
__ERR__(__LOG_E__ << "Unknown preprocessor directive, metatag '" << tag.name << "'.\n", NULL)
|
||||
|
||||
return s;
|
||||
}
|
||||
bool Parser::ParseTag(Tag &tag, const char *s, const char *e, const char **es)
|
||||
{
|
||||
// Safety net.
|
||||
s += SkipSpace(s, e) - s;
|
||||
if (s[0] != '<')
|
||||
return false;
|
||||
|
||||
// Get the tag id.
|
||||
#define MLTAG_ERROR(c) { __LOG_E__ << c; return false; }
|
||||
|
||||
const char *etn = s + 1;
|
||||
etn += SkipEntry(etn, e) - etn;
|
||||
if (String::strfindchar(s + 1, ':', etn) != etn)
|
||||
MLTAG_ERROR("':' is a path character and cannot be used in tag name.\n")
|
||||
if (String::strfindchar(s + 1, ';', etn) != etn)
|
||||
MLTAG_ERROR("';' is a path character and cannot be used in tag name.\n")
|
||||
|
||||
tag.name.Set(s + 1, etn);
|
||||
s = etn;
|
||||
|
||||
// Get tag type.
|
||||
s += SkipSpace(s, e) - s;
|
||||
if (s == e)
|
||||
MLTAG_ERROR("Mangled definition, metatag '" << tag.name << "'.\n")
|
||||
|
||||
switch (s[0])
|
||||
{
|
||||
case '>':
|
||||
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 <uchar> 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 <uchar> 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 <char> 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 <File> mfl(new File);
|
||||
if (mfl.IsNull())
|
||||
__ERR__(__LOG_E__ << "Failed to allocate file.\n", NULL)
|
||||
return Load(metafile, *mfl, verbose) ? mfl.Detach() : NULL;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
Reference in New Issue
Block a user