]> git.tdb.fi Git - poefilter.git/commitdiff
Initial version from when I last played
authorMikko Rasa <tdb@tdb.fi>
Mon, 13 Aug 2018 19:32:23 +0000 (22:32 +0300)
committerMikko Rasa <tdb@tdb.fi>
Mon, 13 Aug 2018 19:32:23 +0000 (22:32 +0300)
15 files changed:
Build [new file with mode: 0644]
source/category.cpp [new file with mode: 0644]
source/category.h [new file with mode: 0644]
source/choicecondition.h [new file with mode: 0644]
source/condition.cpp [new file with mode: 0644]
source/condition.h [new file with mode: 0644]
source/filter.cpp [new file with mode: 0644]
source/filter.h [new file with mode: 0644]
source/poefilter.cpp [new file with mode: 0644]
source/poefilter.h [new file with mode: 0644]
source/rangecondition.h [new file with mode: 0644]
source/rarity.cpp [new file with mode: 0644]
source/rarity.h [new file with mode: 0644]
source/theme.cpp [new file with mode: 0644]
source/theme.h [new file with mode: 0644]

diff --git a/Build b/Build
new file mode 100644 (file)
index 0000000..5e575bd
--- /dev/null
+++ b/Build
@@ -0,0 +1,10 @@
+package "poefilter"
+{
+       require "mspcore";
+       require "mspdatafile";
+
+       program "poefilter"
+       {
+               source "source";
+       };
+};
diff --git a/source/category.cpp b/source/category.cpp
new file mode 100644 (file)
index 0000000..2ea4818
--- /dev/null
@@ -0,0 +1,151 @@
+#include "category.h"
+#include "choicecondition.h"
+#include "filter.h"
+#include "rangecondition.h"
+#include "theme.h"
+
+using namespace std;
+using namespace Msp;
+
+Category::Category():
+       condition(0),
+       font_size(1.0f),
+       order(0),
+       sound_type(0),
+       sound_volume(100)
+{ }
+
+Category::Category(const Category &other):
+       condition(other.condition ? other.condition->clone() : 0),
+       font_size(other.font_size),
+       border_color(other.border_color),
+       order(other.order),
+       sound_type(other.sound_type),
+       sound_volume(other.sound_volume)
+{ }
+
+Category &Category::operator=(const Category &other)
+{
+       delete condition;
+       condition = (other.condition ? other.condition->clone() : 0);
+       font_size = other.font_size;
+       border_color = other.border_color;
+       order = other.order;
+       sound_type = other.sound_type;
+       sound_volume = other.sound_volume;
+       return *this;
+}
+
+Category::~Category()
+{
+       delete condition;
+}
+
+void Category::create_statements(list<FilterStatement> &st, const Theme &theme) const
+{
+       st.clear();
+       st.push_back(FilterStatement());
+       st.back().add_line(format("SetFontSize %d", static_cast<int>(font_size*theme.get_base_font_size()+0.5)));
+       if(!border_color.empty())
+       {
+               const Color &color = theme.get_color(border_color);
+               st.back().add_line(format("SetBorderColor %d %d %d", color.r, color.g, color.b));
+       }
+       if(sound_type)
+               st.back().add_line(format("PlayAlertSound %d %d", sound_type, sound_volume));
+       if(condition)
+               condition->add_lines(st);
+}
+
+
+Category::Loader::Loader(Category &c, CompoundCondition *n):
+       DataFile::ObjectLoader<Category>(c),
+       compound(n)
+{
+       add("alert_sound", &Category::sound_type, &Category::sound_volume);
+       add("and", &Loader::and_);
+       add("base_type", &Loader::condition<BaseTypeCondition>);
+       add("border_color", &Category::border_color);
+       add("class", &Loader::condition<ClassCondition>);
+       add_range<DropLevelCondition>("drop_level");
+       add("font_size", &Category::font_size);
+       add_range<HeightCondition>("height");
+       add_range<ItemLevelCondition>("item_level");
+       add("linked_colors", &Loader::linked_colors);
+       add_range<LinkedSocketsCondition>("linked_sockets");
+       add_range<SocketsCondition>("sockets");
+       add("or", &Loader::or_);
+       add("order", &Category::order);
+       add_range<QualityCondition>("quality");
+       add_range<RarityCondition>("rarity");
+       add_range<WidthCondition>("width");
+}
+
+template<typename T>
+void Category::Loader::add_range(const string &keyword)
+{
+       add(keyword, &Loader::condition<T>);
+       add(keyword, &Loader::condition_range<T>);
+       add("min_"+keyword, &Loader::condition_min<T>);
+       add("max_"+keyword, &Loader::condition_max<T>);
+}
+
+void Category::Loader::add_condition(Condition *cond)
+{
+       if(compound)
+               compound->add(cond);
+       else if(!obj.condition)
+               obj.condition = cond;
+       else
+       {
+               compound = new AndCondition;
+               compound->add(obj.condition);
+               compound->add(cond);
+               obj.condition = compound;
+       }
+}
+
+void Category::Loader::and_()
+{
+       RefPtr<AndCondition> cond = new AndCondition;
+       Loader sub_ldr(obj, cond.get());
+       load_sub_with(sub_ldr);
+       add_condition(cond.release());
+}
+
+template<typename T>
+void Category::Loader::condition(typename T::Type value)
+{
+       add_condition(new T(value));
+}
+
+template<typename T>
+void Category::Loader::condition_max(typename T::Type max)
+{
+       add_condition(new T(T::Traits::get_min(), max));
+}
+
+template<typename T>
+void Category::Loader::condition_min(typename T::Type min)
+{
+       add_condition(new T(min, T::Traits::get_max()));
+}
+
+template<typename T>
+void Category::Loader::condition_range(typename T::Type min, typename T::Type max)
+{
+       add_condition(new T(min, max));
+}
+
+void Category::Loader::linked_colors(const LinkedColorsCondition::Colors &colors)
+{
+       add_condition(new LinkedColorsCondition(colors));
+}
+
+void Category::Loader::or_()
+{
+       RefPtr<OrCondition> cond = new OrCondition;
+       Loader sub_ldr(obj, cond.get());
+       load_sub_with(sub_ldr);
+       add_condition(cond.release());
+}
diff --git a/source/category.h b/source/category.h
new file mode 100644 (file)
index 0000000..53b1beb
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef CATEGORY_H_
+#define CATEGORY_H_
+
+#include <list>
+#include <string>
+#include <msp/datafile/objectloader.h>
+#include "condition.h"
+#include "rarity.h"
+
+class FilterStatement;
+class Theme;
+
+class Category
+{
+public:
+       class Loader: public Msp::DataFile::ObjectLoader<Category>
+       {
+       private:
+               CompoundCondition *compound;
+
+       public:
+               Loader(Category &, CompoundCondition * = 0);
+
+       protected:
+               template<typename T>
+               void add_range(const std::string &);
+
+               void add_condition(Condition *);
+
+       private:
+               void and_();
+
+               template<typename T>
+               void condition(typename T::Type);
+
+               template<typename T>
+               void condition_max(typename T::Type);
+
+               template<typename T>
+               void condition_min(typename T::Type);
+
+               template<typename T>
+               void condition_range(typename T::Type, typename T::Type);
+
+               void linked_colors(const LinkedColorsCondition::Colors &);
+               void or_();
+       };
+
+private:
+       Condition *condition;
+       float font_size;
+       std::string border_color;
+       unsigned order;
+       unsigned sound_type;
+       unsigned sound_volume;
+
+public:
+       Category();
+       Category(const Category &);
+       Category &operator=(const Category &);
+       ~Category();
+
+       unsigned get_order() const { return order; }
+       void create_statements(std::list<FilterStatement> &, const Theme &) const;
+};
+
+#endif
diff --git a/source/choicecondition.h b/source/choicecondition.h
new file mode 100644 (file)
index 0000000..5a45329
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef CHOICECONDITION_H_
+#define CHOICECONDITION_H_
+
+#include "condition.h"
+#include "filter.h"
+
+template<typename T>
+class ChoiceCondition: public Condition
+{
+public:
+       typedef T Traits;
+       typedef typename Traits::Type Type;
+
+private:
+       Type value;
+
+public:
+       ChoiceCondition(Type);
+
+       virtual Condition *clone() const;
+       virtual bool can_merge(const Condition &, const CompoundCondition &) const;
+       virtual void add_lines(std::list<FilterStatement> &) const;
+       virtual void add_merged_lines(const std::list<Condition *> &, const CompoundCondition &, std::list<FilterStatement> &) const;
+};
+
+
+struct ClassTraits
+{
+       typedef std::string Type;
+       static const char *get_keyword() { return "Class"; }
+};
+
+typedef ChoiceCondition<ClassTraits> ClassCondition;
+
+
+struct BaseTypeTraits
+{
+       typedef std::string Type;
+       static const char *get_keyword() { return "BaseType"; }
+};
+
+typedef ChoiceCondition<BaseTypeTraits> BaseTypeCondition;
+
+
+template<typename Traits>
+ChoiceCondition<Traits>::ChoiceCondition(Type v):
+       value(v)
+{ }
+
+template<typename Traits>
+Condition *ChoiceCondition<Traits>::clone() const
+{
+       return new ChoiceCondition<Traits>(value);
+}
+
+template<typename Traits>
+bool ChoiceCondition<Traits>::can_merge(const Condition &other, const CompoundCondition &parent) const
+{
+       return dynamic_cast<const ChoiceCondition<Traits> *>(&other) && dynamic_cast<const OrCondition *>(&parent);
+}
+
+template<typename Traits>
+void ChoiceCondition<Traits>::add_lines(std::list<FilterStatement> &st) const
+{
+       const char *keyword = Traits::get_keyword();
+       FilterStatement::add_line(st, Msp::format("%s \"%s\"", keyword, value));
+}
+
+template<typename Traits>
+void ChoiceCondition<Traits>::add_merged_lines(const std::list<Condition *> &conditions, const CompoundCondition &parent, std::list<FilterStatement> &st) const
+{
+       if(dynamic_cast<const OrCondition *>(&parent))
+       {
+               std::string line = Traits::get_keyword();
+               for(std::list<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
+                       line += Msp::format(" \"%s\"", static_cast<const ChoiceCondition<Traits> *>(*i)->value);
+               FilterStatement::add_line(st, line);
+       }
+}
+
+#endif
diff --git a/source/condition.cpp b/source/condition.cpp
new file mode 100644 (file)
index 0000000..12ee730
--- /dev/null
@@ -0,0 +1,94 @@
+#include "condition.h"
+#include "filter.h"
+
+using namespace std;
+using namespace Msp;
+
+CompoundCondition::~CompoundCondition()
+{
+       for(list<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
+               delete *i;
+}
+
+void CompoundCondition::clone_to(CompoundCondition &other) const
+{
+       for(list<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
+               other.add((*i)->clone());
+}
+
+void CompoundCondition::add(Condition *cond)
+{
+       conditions.push_back(cond);
+}
+
+
+Condition *AndCondition::clone() const
+{
+       AndCondition *result = new AndCondition;
+       clone_to(*result);
+       return result;
+}
+
+void AndCondition::add_lines(list<FilterStatement> &st) const
+{
+       for(list<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
+               (*i)->add_lines(st);
+}
+
+
+Condition *OrCondition::clone() const
+{
+       OrCondition *result = new OrCondition;
+       clone_to(*result);
+       return result;
+}
+
+void OrCondition::add_lines(list<FilterStatement> &st) const
+{
+       bool merge = conditions.size()>1;
+       for(list<Condition *>::const_iterator i=conditions.begin(); (merge && ++i!=conditions.end()); )
+               merge = conditions.front()->can_merge(**i, *this);
+
+       if(merge)
+               conditions.front()->add_merged_lines(conditions, *this, st);
+       else
+       {
+               list<FilterStatement> result;
+               for(list<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
+               {
+                       list<FilterStatement> sub_result = st;
+                       (*i)->add_lines(sub_result);
+                       result.splice(result.end(), sub_result);
+               }
+               swap(result, st);
+       }
+}
+
+
+LinkedColorsCondition::LinkedColorsCondition(const Colors &c):
+       colors(c)
+{ }
+
+Condition *LinkedColorsCondition::clone() const
+{
+       return new LinkedColorsCondition(colors);
+}
+
+void LinkedColorsCondition::add_lines(list<FilterStatement> &st) const
+{
+       FilterStatement::add_line(st, format("SocketGroup %s", colors.colors));
+}
+
+
+void operator>>(const LexicalConverter &conv, LinkedColorsCondition::Colors &colors)
+{
+       const string &str = conv.get();
+       bool rgb = true;
+       for(string::const_iterator i=str.begin(); (rgb && i!=str.end()); ++i)
+               rgb = (*i=='R' || *i=='G' || *i=='B');
+       if(str.size()>6 || !rgb)
+               throw lexical_error(format("conversion of '%s' to LinkedColorsCondition::Colors", str));
+
+       fill(colors.colors, colors.colors+7, '\0');
+       copy(str.begin(), str.end(), colors.colors);
+}
diff --git a/source/condition.h b/source/condition.h
new file mode 100644 (file)
index 0000000..59976aa
--- /dev/null
@@ -0,0 +1,78 @@
+#ifndef CONDITION_H_
+#define CONDITION_H_
+
+#include <list>
+#include <string>
+#include <msp/strings/format.h>
+#include "rarity.h"
+
+class CompoundCondition;
+class FilterStatement;
+
+class Condition
+{
+protected:
+       Condition() { }
+public:
+       virtual ~Condition() { }
+
+       virtual Condition *clone() const = 0;
+       virtual bool can_merge(const Condition &, const CompoundCondition &) const { return false; }
+       virtual void add_lines(std::list<FilterStatement> &) const = 0;
+       virtual void add_merged_lines(const std::list<Condition *> &, const CompoundCondition &, std::list<FilterStatement> &) const { }
+};
+
+
+class CompoundCondition: public Condition
+{
+protected:
+       std::list<Condition *> conditions;
+
+public:
+       virtual ~CompoundCondition();
+
+protected:
+       void clone_to(CompoundCondition &) const;
+
+public:
+       void add(Condition *);
+};
+
+
+class AndCondition: public CompoundCondition
+{
+public:
+       virtual Condition *clone() const;
+       virtual void add_lines(std::list<FilterStatement> &) const;
+};
+
+
+class OrCondition: public CompoundCondition
+{
+public:
+       virtual Condition *clone() const;
+       virtual void add_lines(std::list<FilterStatement> &) const;
+};
+
+
+class LinkedColorsCondition: public Condition
+{
+public:
+       struct Colors
+       {
+               char colors[7];
+       };
+
+private:
+       Colors colors;
+       
+public:
+       LinkedColorsCondition(const Colors &);
+
+       virtual Condition *clone() const;
+       virtual void add_lines(std::list<FilterStatement> &) const;
+};
+
+void operator>>(const Msp::LexicalConverter &, LinkedColorsCondition::Colors &);
+
+#endif
diff --git a/source/filter.cpp b/source/filter.cpp
new file mode 100644 (file)
index 0000000..c694ca6
--- /dev/null
@@ -0,0 +1,138 @@
+#include <algorithm>
+#include <msp/core/maputils.h>
+#include <msp/io/print.h>
+#include "category.h"
+#include "filter.h"
+#include "poefilter.h"
+
+using namespace std;
+using namespace Msp;
+
+FilterStatement::FilterStatement():
+       show(true)
+{ }
+
+void FilterStatement::set_show(bool s)
+{
+       show = s;
+}
+
+void FilterStatement::add_line(const string &l)
+{
+       lines.push_back(l);
+}
+
+void FilterStatement::add_line(list<FilterStatement> &st, const string &line)
+{
+       for(list<FilterStatement>::iterator i=st.begin(); i!=st.end(); ++i)
+               i->add_line(line);
+}
+
+void FilterStatement::write(IO::Base &out) const
+{
+       if(show)
+               out.write("Show\n");
+       else
+               out.write("Hide\n");
+
+       for(list<string>::const_iterator i=lines.begin(); i!=lines.end(); ++i)
+               IO::print(out, "\t%s\n", *i);
+}
+
+
+Filter::Filter():
+       abstract(false)
+{ }
+
+void Filter::write(IO::Base &out, const Theme &theme) const
+{
+       for(list<const Category *>::const_iterator i=categories.begin(); i!=categories.end(); ++i)
+       {
+               list<FilterStatement> st;
+               //IO::print(out, "# %s\n", i->first);
+               (*i)->create_statements(st, theme);
+               for(list<FilterStatement>::const_iterator j=st.begin(); j!=st.end(); ++j)
+                       j->write(out);
+       }
+
+       out.write("# unmatched\nHide\n");
+}
+
+
+Filter::Loader::Loader(Filter &f, const PoeFilter &p):
+       DataFile::ObjectLoader<Filter>(f),
+       poe(p)
+{
+       add("abstract", &Filter::abstract);
+       add("hide", &Loader::hide);
+       add("include", &Loader::include);
+       add("show", &Loader::show);
+}
+
+string Filter::Loader::glob_to_re(const string &glob)
+{
+       string result = "^";
+       for(string::const_iterator i=glob.begin(); i!=glob.end(); ++i)
+       {
+               if(*i=='*')
+                       result += '.';
+               else if(!isalnum(*i))
+                       result += '\\';
+               result += *i;
+       }
+       result += '$';
+
+       return result;
+}
+
+bool Filter::Loader::category_order(const Category *c1, const Category *c2)
+{
+       return c1->get_order()<c2->get_order();
+}
+
+void Filter::Loader::hide(const string &name)
+{
+       list<const Category *> categs;
+       if(name.find('*')!=string::npos)
+       {
+               poe.find_categories(glob_to_re(name), categs);
+
+               if(categs.empty())
+                       throw key_error(name);
+       }
+       else
+               categs.push_back(&poe.get_category(name));
+
+       for(list<const Category *>::const_iterator i=categs.begin(); i!=categs.end(); ++i)
+       {
+               list<const Category *>::iterator j = find(obj.categories.begin(), obj.categories.end(), *i);
+               if(j!=obj.categories.end())
+                       obj.categories.erase(j);
+       }
+}
+
+void Filter::Loader::include(const string &name)
+{
+       const Filter &base = poe.get_filter(name);
+       obj.categories.insert(obj.categories.end(), base.categories.begin(), base.categories.end());
+}
+
+void Filter::Loader::show(const string &name)
+{
+       list<const Category *> categs;
+       if(name.find('*')!=string::npos)
+       {
+               poe.find_categories(glob_to_re(name), categs);
+
+               if(categs.empty())
+                       throw key_error(name);
+
+               categs.sort(&category_order);
+       }
+       else
+               categs.push_back(&poe.get_category(name));
+
+       for(list<const Category *>::const_iterator i=categs.begin(); i!=categs.end(); ++i)
+               if(find(obj.categories.begin(), obj.categories.end(), *i)==obj.categories.end())
+                       obj.categories.push_back(*i);
+}
diff --git a/source/filter.h b/source/filter.h
new file mode 100644 (file)
index 0000000..4642943
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef FILTER_H_
+#define FILTER_H_
+
+#include <list>
+#include <map>
+#include <string>
+#include <msp/datafile/objectloader.h>
+#include <msp/io/base.h>
+
+class Category;
+class PoeFilter;
+class Theme;
+
+class FilterStatement
+{
+private:
+       bool show;
+       std::list<std::string> lines;
+
+public:
+       FilterStatement();
+
+       void set_show(bool);
+       void add_line(const std::string &);
+
+       static void add_line(std::list<FilterStatement> &, const std::string &);
+
+       void write(Msp::IO::Base &) const;
+};
+
+
+class Filter
+{
+public:
+       class Loader: public Msp::DataFile::ObjectLoader<Filter>
+       {
+       private:
+               const PoeFilter &poe;
+
+       public:
+               Loader(Filter &, const PoeFilter &);
+
+       private:
+               static std::string glob_to_re(const std::string &);
+               static bool category_order(const Category *, const Category *);
+
+               void hide(const std::string &);
+               void include(const std::string &);
+               void show(const std::string &);
+       };
+
+private:
+       std::list<const Category *> categories;
+       bool abstract;
+
+public:
+       Filter();
+
+       bool is_abstract() const { return abstract; }
+       void write(Msp::IO::Base &, const Theme &) const;
+};
+
+#endif
diff --git a/source/poefilter.cpp b/source/poefilter.cpp
new file mode 100644 (file)
index 0000000..032b871
--- /dev/null
@@ -0,0 +1,75 @@
+#include <msp/core/getopt.h>
+#include <msp/core/maputils.h>
+#include <msp/io/file.h>
+#include "category.h"
+#include "filter.h"
+#include "poefilter.h"
+
+using namespace std;
+using namespace Msp;
+
+PoeFilter::PoeFilter(int argc, char **argv)
+{
+       GetOpt getopt;
+       getopt.add_argument("filename", filename, GetOpt::REQUIRED_ARG);
+       getopt(argc, argv);
+}
+
+int PoeFilter::main()
+{
+       DataFile::load(*this, filename);
+
+       for(map<string, Filter>::const_iterator i=filters.begin(); i!=filters.end(); ++i)
+               if(!i->second.is_abstract())
+               {
+                       IO::BufferedFile out(i->first+".filter", IO::M_WRITE);
+                       i->second.write(out, theme);
+               }
+
+       return 0;
+}
+
+const Category &PoeFilter::get_category(const string &name) const
+{
+       return get_item(categories, name);
+}
+
+void PoeFilter::find_categories(const Regex &re, list<const Category *> &categs) const
+{
+       for(map<string, Category>::const_iterator i=categories.begin(); i!=categories.end(); ++i)
+               if(re.match(i->first))
+                       categs.push_back(&i->second);
+}
+
+const Filter &PoeFilter::get_filter(const string &name) const
+{
+       return get_item(filters, name);
+}
+
+
+PoeFilter::Loader::Loader(PoeFilter &f):
+       DataFile::ObjectLoader<PoeFilter>(f)
+{
+       add("category", &Loader::category);
+       add("filter", &Loader::filter);
+       add("theme", &Loader::theme);
+}
+
+void PoeFilter::Loader::category(const string &name)
+{
+       Category cat;
+       load_sub(cat);
+       obj.categories[name] = cat;
+}
+
+void PoeFilter::Loader::filter(const string &name)
+{
+       Filter flt;
+       load_sub(flt, obj);
+       obj.filters[name] = flt;
+}
+
+void PoeFilter::Loader::theme()
+{
+       load_sub(obj.theme);
+}
diff --git a/source/poefilter.h b/source/poefilter.h
new file mode 100644 (file)
index 0000000..0e87d2d
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef POEFILTER_H_
+#define POEFILTER_H_
+
+#include <map>
+#include <string>
+#include <msp/core/application.h>
+#include <msp/strings/regex.h>
+#include "theme.h"
+
+class Category;
+class Filter;
+
+class PoeFilter: public Msp::RegisteredApplication<PoeFilter>
+{
+public:
+       class Loader: public Msp::DataFile::ObjectLoader<PoeFilter>
+       {
+       public:
+               Loader(PoeFilter &);
+
+       private:
+               void category(const std::string &);
+               void filter(const std::string &);
+               void theme();
+       };
+
+private:
+       std::string filename;
+       Theme theme;
+       std::map<std::string, Category> categories;
+       std::map<std::string, Filter> filters;
+
+public:
+       PoeFilter(int, char **);
+
+       virtual int main();
+
+       const Category &get_category(const std::string &) const;
+       void find_categories(const Msp::Regex &, std::list<const Category *> &) const;
+       const Filter &get_filter(const std::string &) const;
+};
+
+#endif
diff --git a/source/rangecondition.h b/source/rangecondition.h
new file mode 100644 (file)
index 0000000..d4cc19a
--- /dev/null
@@ -0,0 +1,148 @@
+#ifndef RANGECONDITION_H_
+#define RANGECONDITION_H_
+
+#include "condition.h"
+#include "filter.h"
+
+template<typename T>
+class RangeCondition: public Condition
+{
+public:
+       typedef T Traits;
+       typedef typename Traits::Type Type;
+
+protected:
+       Type min;
+       Type max;
+
+public:
+       RangeCondition(Type);
+       RangeCondition(Type, Type);
+
+       virtual Condition *clone() const;
+       virtual void add_lines(std::list<FilterStatement> &) const;
+};
+
+
+struct RarityTraits
+{
+       typedef Rarity Type;
+       static Rarity get_min() { return NORMAL; }
+       static Rarity get_max() { return UNIQUE; }
+       static const char *get_keyword() { return "Rarity"; }
+};
+
+typedef RangeCondition<RarityTraits> RarityCondition;
+
+
+struct QualityTraits
+{
+       typedef unsigned Type;
+       static unsigned get_min() { return 0; }
+       static unsigned get_max() { return 20; }
+       static const char *get_keyword() { return "Quality"; }
+};
+
+typedef RangeCondition<QualityTraits> QualityCondition;
+
+
+struct ItemLevelTraits
+{
+       typedef unsigned Type;
+       static unsigned get_min() { return 0; }
+       static unsigned get_max() { return 100; }
+       static const char *get_keyword() { return "ItemLevel"; }
+};
+
+typedef RangeCondition<ItemLevelTraits> ItemLevelCondition;
+
+
+struct DropLevelTraits
+{
+       typedef unsigned Type;
+       static unsigned get_min() { return 0; }
+       static unsigned get_max() { return 100; }
+       static const char *get_keyword() { return "DropLevel"; }
+};
+
+typedef RangeCondition<DropLevelTraits> DropLevelCondition;
+
+
+struct WidthTraits
+{
+       typedef unsigned Type;
+       static unsigned get_min() { return 1; }
+       static unsigned get_max() { return 2; }
+       static const char *get_keyword() { return "Width"; }
+};
+
+typedef RangeCondition<WidthTraits> WidthCondition;
+
+
+struct HeightTraits
+{
+       typedef unsigned Type;
+       static unsigned get_min() { return 1; }
+       static unsigned get_max() { return 4; }
+       static const char *get_keyword() { return "Height"; }
+};
+
+typedef RangeCondition<HeightTraits> HeightCondition;
+
+
+struct SocketsTraits
+{
+       typedef unsigned Type;
+       static unsigned get_min() { return 1; }
+       static unsigned get_max() { return 6; }
+       static const char *get_keyword() { return "Sockets"; }
+};
+
+typedef RangeCondition<SocketsTraits> SocketsCondition;
+
+
+struct LinkedSocketsTraits
+{
+       typedef unsigned Type;
+       static unsigned get_min() { return 1; }
+       static unsigned get_max() { return 6; }
+       static const char *get_keyword() { return "LinkedSockets"; }
+};
+
+typedef RangeCondition<LinkedSocketsTraits> LinkedSocketsCondition;
+
+
+template<typename Traits>
+RangeCondition<Traits>::RangeCondition(Type v):
+       min(v),
+       max(v)
+{ }
+
+template<typename Traits>
+RangeCondition<Traits>::RangeCondition(Type n, Type x):
+       min(n),
+       max(x)
+{ }
+
+template<typename Traits>
+Condition *RangeCondition<Traits>::clone() const
+{
+       return new RangeCondition<Traits>(min, max);
+}
+
+template<typename Traits>
+void RangeCondition<Traits>::add_lines(std::list<FilterStatement> &st) const
+{
+       const char *keyword = Traits::get_keyword();
+       if(min==max)
+               FilterStatement::add_line(st, Msp::format("%s %s", keyword, min));
+       else
+       {
+               if(min!=Traits::get_min())
+                       FilterStatement::add_line(st, Msp::format("%s >= %s", keyword, min));
+               if(max!=Traits::get_max())
+                       FilterStatement::add_line(st, Msp::format("%s <= %s", keyword, max));
+       }
+}
+
+#endif
diff --git a/source/rarity.cpp b/source/rarity.cpp
new file mode 100644 (file)
index 0000000..4cfc570
--- /dev/null
@@ -0,0 +1,32 @@
+#include <msp/strings/format.h>
+#include "rarity.h"
+
+using namespace std;
+using namespace Msp;
+
+void operator>>(const LexicalConverter &conv, Rarity &rarity)
+{
+       const string &str = conv.get();
+       if(str=="Normal")
+               rarity = NORMAL;
+       else if(str=="Magic")
+               rarity = MAGIC;
+       else if(str=="Rare")
+               rarity = RARE;
+       else if(str=="Unique")
+               rarity = UNIQUE;
+       else
+               throw lexical_error(format("Conversion of %s to Rarity", str));
+}
+
+void operator<<(LexicalConverter &conv, Rarity rarity)
+{
+       switch(rarity)
+       {
+       case NORMAL: conv.result("Normal"); return;
+       case MAGIC: conv.result("Magic"); return;
+       case RARE: conv.result("Rare"); return;
+       case UNIQUE: conv.result("Unique"); return;
+       default: conv.result(format("Rarity(%d)", static_cast<int>(rarity))); return;
+       }
+}
diff --git a/source/rarity.h b/source/rarity.h
new file mode 100644 (file)
index 0000000..1a82c9e
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef RARITY_H_
+#define RARITY_H_
+
+#include <msp/strings/lexicalcast.h>
+
+enum Rarity
+{
+       NORMAL,
+       MAGIC,
+       RARE,
+       UNIQUE
+};
+
+void operator>>(const Msp::LexicalConverter &, Rarity &);
+void operator<<(Msp::LexicalConverter &, Rarity);
+
+#endif
diff --git a/source/theme.cpp b/source/theme.cpp
new file mode 100644 (file)
index 0000000..3b09dc3
--- /dev/null
@@ -0,0 +1,50 @@
+#include <msp/core/maputils.h>
+#include "theme.h"
+
+using namespace std;
+using namespace Msp;
+
+Color::Color():
+       r(200),
+       g(200),
+       b(200)
+{ }
+
+Color::Color(unsigned r_, unsigned g_, unsigned b_):
+       r(r_),
+       g(g_),
+       b(b_)
+{ }
+
+
+Theme::Theme():
+       base_font_size(32)
+{ }
+
+void Theme::set_color(const string &name, const Color &color)
+{
+       colors[name] = color;
+}
+
+const Color &Theme::get_color(const string &name) const
+{
+       return get_item(colors, name);
+}
+
+void Theme::set_base_font_size(unsigned size)
+{
+       base_font_size = size;
+}
+
+
+Theme::Loader::Loader(Theme &t):
+       DataFile::ObjectLoader<Theme>(t)
+{
+       add("base_font_size", &Theme::base_font_size);
+       add("color", &Loader::color);
+}
+
+void Theme::Loader::color(const string &name, unsigned r, unsigned g, unsigned b)
+{
+       obj.set_color(name, Color(r, g, b));
+}
diff --git a/source/theme.h b/source/theme.h
new file mode 100644 (file)
index 0000000..7e296ae
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef THEME_H_
+#define THEME_H_
+
+#include <map>
+#include <string>
+#include <msp/datafile/objectloader.h>
+
+struct Color
+{
+       unsigned r;
+       unsigned g;
+       unsigned b;
+
+       Color();
+       Color(unsigned, unsigned, unsigned);
+};
+
+
+class Theme
+{
+public:
+       class Loader: public Msp::DataFile::ObjectLoader<Theme>
+       {
+       public:
+               Loader(Theme &);
+
+       private:
+               void color(const std::string &, unsigned, unsigned, unsigned);
+       };
+
+private:
+       typedef std::map<std::string, Color> ColorMap;
+
+       ColorMap colors;
+       unsigned base_font_size;
+
+public:
+       Theme();
+
+       void set_color(const std::string &, const Color &);
+       const Color &get_color(const std::string &) const;
+
+       void set_base_font_size(unsigned);
+       unsigned get_base_font_size() const { return base_font_size; }
+};
+
+#endif