auto import from //depot/cupcake/@135843
diff --git a/tools/localize/xmb.cpp b/tools/localize/xmb.cpp
new file mode 100644
index 0000000..236705f
--- /dev/null
+++ b/tools/localize/xmb.cpp
@@ -0,0 +1,181 @@
+#include "xmb.h"
+
+#include "file_utils.h"
+#include "localize.h"
+#include "ValuesFile.h"
+#include "XMLHandler.h"
+#include "XLIFFFile.h"
+
+#include <map>
+
+using namespace std;
+
+const char *const NS_MAP[] = {
+    "xml", XMLNS_XMLNS,
+    NULL, NULL
+};
+
+set<string> g_tags;
+
+static string
+strip_newlines(const string& str)
+{
+    string res;
+    const size_t N = str.length();
+    for (size_t i=0; i<N; i++) {
+        char c = str[i];
+        if (c != '\n' && c != '\r') {
+            res += c;
+        } else {
+            res += ' ';
+        }
+    }
+    return res;
+}
+
+static int
+rename_id_attribute(XMLNode* node)
+{
+    vector<XMLAttribute>& attrs = node->EditAttributes();
+    const size_t I = attrs.size();
+    for (size_t i=0; i<I; i++) {
+        XMLAttribute attr = attrs[i];
+        if (attr.name == "id") {
+            attr.name = "name";
+            attrs.erase(attrs.begin()+i);
+            attrs.push_back(attr);
+            return 0;
+        }
+    }
+    return 1;
+}
+
+static int
+convert_xliff_to_ph(XMLNode* node, int* phID)
+{
+    int err = 0;
+    if (node->Type() == XMLNode::ELEMENT) {
+        if (node->Namespace() == XLIFF_XMLNS) {
+            g_tags.insert(node->Name());
+            node->SetName("", "ph");
+
+            err = rename_id_attribute(node);
+            if (err != 0) {
+                char name[30];
+                (*phID)++;
+                sprintf(name, "id-%d", *phID);
+                node->EditAttributes().push_back(XMLAttribute("", "name", name));
+                err = 0;
+            }
+        }
+        vector<XMLNode*>& children = node->EditChildren();
+        const size_t I = children.size();
+        for (size_t i=0; i<I; i++) {
+            err |= convert_xliff_to_ph(children[i], phID);
+        }
+    }
+    return err;
+}
+
+XMLNode*
+resource_to_xmb_msg(const StringResource& res)
+{
+    // the msg element
+    vector<XMLAttribute> attrs;
+    string name = res.pos.file;
+    name += ":";
+    name += res.TypedID();
+    attrs.push_back(XMLAttribute("", "name", name));
+    attrs.push_back(XMLAttribute("", "desc", strip_newlines(res.comment)));
+    attrs.push_back(XMLAttribute(XMLNS_XMLNS, "space", "preserve"));
+    XMLNode* msg = XMLNode::NewElement(res.pos, "", "msg", attrs, XMLNode::EXACT);
+
+    // the contents are in xliff/html, convert it to xliff
+    int err = 0;
+    XMLNode* value = res.value;
+    string tag = value->Name();
+    int phID = 0;
+    for (vector<XMLNode*>::const_iterator it=value->Children().begin();
+            it!=value->Children().end(); it++) {
+        err |= convert_html_to_xliff(*it, tag, msg, &phID);
+    }
+
+    if (err != 0) {
+        return NULL;
+    }
+
+    // and then convert that to xmb
+    for (vector<XMLNode*>::iterator it=msg->EditChildren().begin();
+            it!=msg->EditChildren().end(); it++) {
+        err |= convert_xliff_to_ph(*it, &phID);
+    }
+
+    if (err == 0) {
+        return msg;
+    } else {
+        return NULL;
+    }
+}
+
+int
+do_xlb_export(const string& outfile, const vector<string>& resFiles)
+{
+    int err = 0;
+
+    size_t totalFileCount = resFiles.size();
+
+    Configuration english;
+        english.locale = "en_US";
+
+    set<StringResource> allResources;
+
+    const size_t J = resFiles.size();
+    for (size_t j=0; j<J; j++) {
+        string resFile = resFiles[j];
+
+        ValuesFile* valuesFile = get_local_values_file(resFile, english, CURRENT_VERSION, "", true);
+        if (valuesFile != NULL) {
+            set<StringResource> resources = valuesFile->GetStrings();
+            allResources.insert(resources.begin(), resources.end());
+        } else {
+            fprintf(stderr, "error reading file %s\n", resFile.c_str());
+        }
+
+        delete valuesFile;
+    }
+
+    // Construct the XLB xml
+    vector<XMLAttribute> attrs;
+    attrs.push_back(XMLAttribute("", "locale", "en"));
+    XMLNode* localizationbundle = XMLNode::NewElement(GENERATED_POS, "", "localizationbundle",
+            attrs, XMLNode::PRETTY);
+
+    for (set<StringResource>::iterator it=allResources.begin(); it!=allResources.end(); it++) {
+        XMLNode* msg = resource_to_xmb_msg(*it);
+        if (msg) {
+            localizationbundle->EditChildren().push_back(msg);
+        } else {
+            err = 1;
+        }
+    }
+
+#if 0
+    for (set<string>::iterator it=g_tags.begin(); it!=g_tags.end(); it++) {
+        printf("tag: %s\n", it->c_str());
+    }
+    printf("err=%d\n", err);
+#endif
+    if (err == 0) {
+        FILE* f = fopen(outfile.c_str(), "wb");
+        if (f == NULL) {
+            fprintf(stderr, "can't open outputfile: %s\n", outfile.c_str());
+            return 1;
+        }
+        fprintf(f, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+        fprintf(f, "%s\n", localizationbundle->ToString(NS_MAP).c_str());
+        fclose(f);
+    }
+
+    return err;
+}
+