+
+ std::string xml_escape(const std::string & str)
+ {
+ std::string result;
+ std::size_t begin = 0;
+
+ for (;;)
+ {
+ std::size_t end = str.find_first_of("\"&'<>", begin);
+ result.append(str, begin, end - begin);
+ if (end == std::string::npos)
+ return result;
+
+ const char * entity = NULL;
+ switch (str[end])
+ {
+ case '"': entity = """; break;
+ case '&': entity = "&"; break;
+ case '\'': entity = "'"; break;
+ case '<': entity = "<"; break;
+ case '>': entity = ">"; break;
+ }
+ assert(entity);
+ result.append(entity);
+
+ begin = end + 1;
+ }
+ }
+
+
+ struct dvd_contents
+ {
+ enum pgc_type { unknown_pgc, menu_pgc, title_pgc };
+
+ struct pgc_ref
+ {
+ explicit pgc_ref(pgc_type type = unknown_pgc,
+ int index = -1,
+ int sub_index = 0)
+ : type(type), index(index), sub_index(sub_index)
+ {}
+ bool operator==(const pgc_ref & other) const
+ {
+ return type == other.type && index == other.index;
+ }
+ bool operator!=(const pgc_ref & other) const
+ {
+ return !(*this == other);
+ }
+
+ pgc_type type; // Menu or title reference?
+ unsigned index; // Menu or title index (within resp. vector)
+ unsigned sub_index; // Button or chapter number (1-based; 0 if
+ // unspecified; not compared!)
+ };
+
+ struct menu
+ {
+ menu()
+ : vob_temp(new temp_file("webdvd-vob-"))
+ {
+ vob_temp->close();
+ }
+
+ boost::shared_ptr<temp_file> vob_temp;
+ std::vector<pgc_ref> entries;
+ };
+
+ struct title
+ {
+ explicit title(const std::string & vob_list)
+ : vob_list(vob_list)
+ {}
+
+ std::string vob_list;
+ };
+
+ std::vector<menu> menus;
+ std::vector<title> titles;
+ };
+
+ void generate_dvd(const dvd_contents & contents,
+ const std::string & output_dir);
+
+ class webdvd_window : public Gtk::Window