summaryrefslogtreecommitdiff
path: root/src/dgxmlparser.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/dgxmlparser.cc')
-rw-r--r--src/dgxmlparser.cc166
1 files changed, 116 insertions, 50 deletions
diff --git a/src/dgxmlparser.cc b/src/dgxmlparser.cc
index ae0842a..7f62a64 100644
--- a/src/dgxmlparser.cc
+++ b/src/dgxmlparser.cc
@@ -33,16 +33,34 @@
#include "nolocale.h"
-bool probeDrumkitFile(const std::string& filename)
+static int getLineNumberFromOffset(const std::string& filename, ptrdiff_t offset)
+{
+ FILE* fp = fopen(filename.data(), "rt");
+ if(!fp)
+ {
+ return 0;
+ }
+
+ int lineno{1};
+ char c = 0;
+ while((c = fgetc(fp)) != EOF && offset--)
+ {
+ lineno += c == '\n' ? 1 : 0;
+ }
+ fclose(fp);
+ return lineno;
+}
+
+bool probeDrumkitFile(const std::string& filename, LogFunction logger)
{
DrumkitDOM d;
- return parseDrumkitFile(filename, d);
+ return parseDrumkitFile(filename, d, logger);
}
-bool probeInstrumentFile(const std::string& filename)
+bool probeInstrumentFile(const std::string& filename, LogFunction logger)
{
InstrumentDOM d;
- return parseInstrumentFile(filename, d);
+ return parseInstrumentFile(filename, d, logger);
}
static bool assign(double& dest, const std::string& val)
@@ -77,7 +95,7 @@ static bool assign(main_state_t& dest, const std::string& val)
}
template<typename T>
-static bool attrcpy(T& dest, const pugi::xml_node& src, const std::string& attr, bool opt = false)
+static bool attrcpy(T& dest, const pugi::xml_node& src, const std::string& attr, LogFunction logger, const std::string& filename, bool opt = false)
{
const char* val = src.attribute(attr.c_str()).as_string(nullptr);
if(!val)
@@ -86,6 +104,12 @@ static bool attrcpy(T& dest, const pugi::xml_node& src, const std::string& attr,
{
ERR(dgxmlparser, "Attribute %s not found in %s, offset %d\n",
attr.data(), src.path().data(), (int)src.offset_debug());
+ if(logger)
+ {
+ auto lineno = getLineNumberFromOffset(filename, src.offset_debug());
+ logger(LogLevel::Error, "Missing attribute '" + attr +
+ "' at line " + std::to_string(lineno));
+ }
}
return opt;
}
@@ -94,6 +118,12 @@ static bool attrcpy(T& dest, const pugi::xml_node& src, const std::string& attr,
{
ERR(dgxmlparser, "Attribute %s could not be assigned, offset %d\n",
attr.data(), (int)src.offset_debug());
+ if(logger)
+ {
+ auto lineno = getLineNumberFromOffset(filename, src.offset_debug());
+ logger(LogLevel::Error, "Attribute '" + attr +
+ "' could not be assigned at line " + std::to_string(lineno));
+ }
return false;
}
@@ -101,7 +131,7 @@ static bool attrcpy(T& dest, const pugi::xml_node& src, const std::string& attr,
}
template<typename T>
-static bool nodecpy(T& dest, const pugi::xml_node& src, const std::string& node, bool opt = false)
+static bool nodecpy(T& dest, const pugi::xml_node& src, const std::string& node, LogFunction logger, const std::string& filename, bool opt = false)
{
auto val = src.child(node.c_str());
if(val == pugi::xml_node())
@@ -109,7 +139,13 @@ static bool nodecpy(T& dest, const pugi::xml_node& src, const std::string& node,
if(!opt)
{
ERR(dgxmlparser, "Node %s not found in %s, offset %d\n",
- node.data(), src.path().data(), (int)src.offset_debug());
+ node.data(), filename.data(), (int)src.offset_debug());
+ if(logger)
+ {
+ auto lineno = getLineNumberFromOffset(filename, src.offset_debug());
+ logger(LogLevel::Error, "Node '" + node +
+ "' not found at line " + std::to_string(lineno));
+ }
}
return opt;
}
@@ -118,16 +154,27 @@ static bool nodecpy(T& dest, const pugi::xml_node& src, const std::string& node,
{
ERR(dgxmlparser, "Attribute %s could not be assigned, offset %d\n",
node.data(), (int)src.offset_debug());
+ if(logger)
+ {
+ auto lineno = getLineNumberFromOffset(filename, src.offset_debug());
+ logger(LogLevel::Error, "Node '" + node +
+ "' could not be assigned at line " + std::to_string(lineno));
+ }
return false;
}
return true;
}
-bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom)
+bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom, LogFunction logger)
{
bool res = true;
+ if(logger)
+ {
+ logger(LogLevel::Info, "Loading " + filename);
+ }
+
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(filename.c_str());
res &= !result.status;
@@ -135,49 +182,56 @@ bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom)
{
ERR(dgxmlparser, "XML parse error: '%s' %d", filename.data(),
(int) result.offset);
+ if(logger)
+ {
+ auto lineno = getLineNumberFromOffset(filename, result.offset);
+ logger(LogLevel::Error, "XML parse error in '" + filename +
+ "': " + result.description() + " at line " +
+ std::to_string(lineno));
+ }
return false;
}
pugi::xml_node drumkit = doc.child("drumkit");
dom.version = "1.0";
- res &= attrcpy(dom.version, drumkit, "version", true);
+ res &= attrcpy(dom.version, drumkit, "version", logger, filename, true);
dom.samplerate = 44100.0;
- res &= attrcpy(dom.samplerate, drumkit, "samplerate", true);
+ res &= attrcpy(dom.samplerate, drumkit, "samplerate", logger, filename, true);
// Use the old name and description attributes on the drumkit node as fallback
- res &= attrcpy(dom.metadata.title, drumkit, "name", true);
- res &= attrcpy(dom.metadata.description, drumkit, "description", true);
+ res &= attrcpy(dom.metadata.title, drumkit, "name", logger, filename, true);
+ res &= attrcpy(dom.metadata.description, drumkit, "description", logger, filename, true);
pugi::xml_node metadata = drumkit.child("metadata");
if(metadata != pugi::xml_node())
{
auto& meta = dom.metadata;
- res &= nodecpy(meta.version, metadata, "version", true);
- res &= nodecpy(meta.title, metadata, "title", true);
+ res &= nodecpy(meta.version, metadata, "version", logger, filename, true);
+ res &= nodecpy(meta.title, metadata, "title", logger, filename, true);
pugi::xml_node logo = metadata.child("logo");
if(logo != pugi::xml_node())
{
- res &= attrcpy(meta.logo, logo, "src", true);
+ res &= attrcpy(meta.logo, logo, "src", logger, filename, true);
}
- res &= nodecpy(meta.description, metadata, "description", true);
- res &= nodecpy(meta.license, metadata, "license", true);
- res &= nodecpy(meta.notes, metadata, "notes", true);
- res &= nodecpy(meta.author, metadata, "author", true);
- res &= nodecpy(meta.email, metadata, "email", true);
- res &= nodecpy(meta.website, metadata, "website", true);
+ res &= nodecpy(meta.description, metadata, "description", logger, filename, true);
+ res &= nodecpy(meta.license, metadata, "license", logger, filename, true);
+ res &= nodecpy(meta.notes, metadata, "notes", logger, filename, true);
+ res &= nodecpy(meta.author, metadata, "author", logger, filename, true);
+ res &= nodecpy(meta.email, metadata, "email", logger, filename, true);
+ res &= nodecpy(meta.website, metadata, "website", logger, filename, true);
pugi::xml_node image = metadata.child("image");
if(image != pugi::xml_node())
{
- res &= attrcpy(meta.image, image, "src", true);
- res &= attrcpy(meta.image_map, image, "map", true);
+ res &= attrcpy(meta.image, image, "src", logger, filename, true);
+ res &= attrcpy(meta.image_map, image, "map", logger, filename, true);
for(auto clickmap : image.children("clickmap"))
{
meta.clickmaps.emplace_back();
res &= attrcpy(meta.clickmaps.back().instrument,
- clickmap, "instrument", true);
+ clickmap, "instrument", logger, filename, true);
res &= attrcpy(meta.clickmaps.back().colour,
- clickmap, "colour", true);
+ clickmap, "colour", logger, filename, true);
}
}
}
@@ -186,7 +240,7 @@ bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom)
for(pugi::xml_node channel: channels.children("channel"))
{
dom.channels.emplace_back();
- res &= attrcpy(dom.channels.back().name, channel, "name");
+ res &= attrcpy(dom.channels.back().name, channel, "name", logger, filename);
}
pugi::xml_node instruments = doc.child("drumkit").child("instruments");
@@ -194,18 +248,18 @@ bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom)
{
dom.instruments.emplace_back();
auto& instrument_ref = dom.instruments.back();
- res &= attrcpy(instrument_ref.name, instrument, "name");
- res &= attrcpy(instrument_ref.file, instrument, "file");
- res &= attrcpy(instrument_ref.group, instrument, "group", true);
+ res &= attrcpy(instrument_ref.name, instrument, "name", logger, filename);
+ res &= attrcpy(instrument_ref.file, instrument, "file", logger, filename);
+ res &= attrcpy(instrument_ref.group, instrument, "group", logger, filename, true);
for(pugi::xml_node cmap: instrument.children("channelmap"))
{
instrument_ref.channel_map.emplace_back();
auto& channel_map_ref = instrument_ref.channel_map.back();
- res &= attrcpy(channel_map_ref.in, cmap, "in");
- res &= attrcpy(channel_map_ref.out, cmap, "out");
+ res &= attrcpy(channel_map_ref.in, cmap, "in", logger, filename);
+ res &= attrcpy(channel_map_ref.out, cmap, "out", logger, filename);
channel_map_ref.main = main_state_t::unset;
- res &= attrcpy(channel_map_ref.main, cmap, "main", true);
+ res &= attrcpy(channel_map_ref.main, cmap, "main", logger, filename, true);
}
auto num_chokes = std::distance(instrument.children("chokes").begin(),
@@ -223,9 +277,9 @@ bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom)
{
instrument_ref.chokes.emplace_back();
auto& choke_ref = instrument_ref.chokes.back();
- res &= attrcpy(choke_ref.instrument, choke, "instrument");
+ res &= attrcpy(choke_ref.instrument, choke, "instrument", logger, filename);
choke_ref.choketime = 68; // default to 68 ms
- res &= attrcpy(choke_ref.choketime, choke, "choketime", true);
+ res &= attrcpy(choke_ref.choketime, choke, "choketime", logger, filename, true);
}
}
}
@@ -233,32 +287,44 @@ bool parseDrumkitFile(const std::string& filename, DrumkitDOM& dom)
return res;
}
-bool parseInstrumentFile(const std::string& filename, InstrumentDOM& dom)
+bool parseInstrumentFile(const std::string& filename, InstrumentDOM& dom, LogFunction logger)
{
bool res = true;
+ if(logger)
+ {
+ logger(LogLevel::Info, "Loading " + filename);
+ }
+
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(filename.data());
res &= !result.status;
if(!res)
{
- ERR(dgxmlparser, "XML parse error: '%s'", filename.data());
+ WARN(dgxmlparser, "XML parse error: '%s'", filename.data());
+ if(logger)
+ {
+ auto lineno = getLineNumberFromOffset(filename, result.offset);
+ logger(LogLevel::Warning, "XML parse error in '" + filename +
+ "': " + result.description() + " at line " +
+ std::to_string(lineno));
+ }
}
//TODO: handle version
pugi::xml_node instrument = doc.child("instrument");
- res &= attrcpy(dom.name, instrument, "name");
+ res &= attrcpy(dom.name, instrument, "name", logger, filename);
dom.version = "1.0";
- res &= attrcpy(dom.version, instrument, "version", true);
- res &= attrcpy(dom.description, instrument, "description", true);
+ res &= attrcpy(dom.version, instrument, "version", logger, filename, true);
+ res &= attrcpy(dom.description, instrument, "description", logger, filename, true);
pugi::xml_node channels = instrument.child("channels");
for(pugi::xml_node channel : channels.children("channel"))
{
dom.instrument_channels.emplace_back();
- res &= attrcpy(dom.instrument_channels.back().name, channel, "name");
+ res &= attrcpy(dom.instrument_channels.back().name, channel, "name", logger, filename);
dom.instrument_channels.back().main = main_state_t::unset;
- res &= attrcpy(dom.instrument_channels.back().main, channel, "main", true);
+ res &= attrcpy(dom.instrument_channels.back().main, channel, "main", logger, filename, true);
}
INFO(dgxmlparser, "XML version: %s\n", dom.version.data());
@@ -267,7 +333,7 @@ bool parseInstrumentFile(const std::string& filename, InstrumentDOM& dom)
for(pugi::xml_node sample: samples.children("sample"))
{
dom.samples.emplace_back();
- res &= attrcpy(dom.samples.back().name, sample, "name");
+ res &= attrcpy(dom.samples.back().name, sample, "name", logger, filename);
// Power only part of >= v2.0 instruments.
if(dom.version == "1.0")
@@ -276,20 +342,20 @@ bool parseInstrumentFile(const std::string& filename, InstrumentDOM& dom)
}
else
{
- res &= attrcpy(dom.samples.back().power, sample, "power");
+ res &= attrcpy(dom.samples.back().power, sample, "power", logger, filename);
}
for(pugi::xml_node audiofile: sample.children("audiofile"))
{
dom.samples.back().audiofiles.emplace_back();
res &= attrcpy(dom.samples.back().audiofiles.back().instrument_channel,
- audiofile, "channel");
+ audiofile, "channel", logger, filename);
res &= attrcpy(dom.samples.back().audiofiles.back().file,
- audiofile, "file");
+ audiofile, "file", logger, filename);
// Defaults to channel 1 in mono (1-based)
dom.samples.back().audiofiles.back().filechannel = 1;
res &= attrcpy(dom.samples.back().audiofiles.back().filechannel,
- audiofile, "filechannel", true);
+ audiofile, "filechannel", logger, filename, true);
}
}
@@ -301,14 +367,14 @@ bool parseInstrumentFile(const std::string& filename, InstrumentDOM& dom)
{
dom.velocities.emplace_back();
- res &= attrcpy(dom.velocities.back().lower, velocity, "lower");
- res &= attrcpy(dom.velocities.back().upper, velocity, "upper");
+ res &= attrcpy(dom.velocities.back().lower, velocity, "lower", logger, filename);
+ res &= attrcpy(dom.velocities.back().upper, velocity, "upper", logger, filename);
for(auto sampleref : velocity.children("sampleref"))
{
dom.velocities.back().samplerefs.emplace_back();
auto& sref = dom.velocities.back().samplerefs.back();
- res &= attrcpy(sref.probability, sampleref, "probability");
- res &= attrcpy(sref.name, sampleref, "name");
+ res &= attrcpy(sref.probability, sampleref, "probability", logger, filename);
+ res &= attrcpy(sref.name, sampleref, "name", logger, filename);
}
}
}