diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt
index 1010b199a17fb8f4f83a973a6732ae39e6aca909..a5ad24815c49887aa3632c24bd7da420f38c6f44 100644
--- a/indra/CMakeLists.txt
+++ b/indra/CMakeLists.txt
@@ -65,10 +65,12 @@ add_custom_target(viewer)
 if (VIEWER)
   add_subdirectory(${LIBS_OPEN_PREFIX}llcrashlogger)
   add_subdirectory(${LIBS_OPEN_PREFIX}llui)
+  add_subdirectory(${LIBS_OPEN_PREFIX}llxuixml)
 
   if (LINUX)
     add_subdirectory(${VIEWER_PREFIX}linux_crash_logger)
-    add_dependencies(viewer linux-crash-logger-strip-target)
+    add_subdirectory(${VIEWER_PREFIX}linux_updater)
+    add_dependencies(viewer linux-crash-logger-strip-target linux-updater)
   elseif (DARWIN)
     add_subdirectory(${VIEWER_PREFIX}mac_crash_logger)
     add_subdirectory(${VIEWER_PREFIX}mac_updater)
diff --git a/indra/cmake/LLXUIXML.cmake b/indra/cmake/LLXUIXML.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..b8bfe48c77660cb81495e23314795dfa48a8afc4
--- /dev/null
+++ b/indra/cmake/LLXUIXML.cmake
@@ -0,0 +1,7 @@
+# -*- cmake -*-
+
+set(LLXUIXML_INCLUDE_DIRS
+    ${LIBS_OPEN_DIR}/llxuixml
+    )
+
+set(LLXUIXML_LIBRARIES llxuixml)
diff --git a/indra/integration_tests/llui_libtest/CMakeLists.txt b/indra/integration_tests/llui_libtest/CMakeLists.txt
index 88564c60853cb4c17919541105ca46b5ba49ea38..1ccdb0f20bb0343bb33acc7b57641bca1ace5600 100644
--- a/indra/integration_tests/llui_libtest/CMakeLists.txt
+++ b/indra/integration_tests/llui_libtest/CMakeLists.txt
@@ -16,6 +16,7 @@ include(LLWindow)
 include(LLUI)
 include(LLVFS)        # ugh, needed for LLDir
 include(LLXML)
+include(LLXUIXML)
 include(Linking)
 # include(Tut)
 
@@ -29,6 +30,7 @@ include_directories(
     ${LLVFS_INCLUDE_DIRS}
     ${LLWINDOW_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
+    ${LLXUIXML_INCLUDE_DIRS}
     )
 
 set(llui_libtest_SOURCE_FILES
diff --git a/indra/integration_tests/llui_libtest/llui_libtest.cpp b/indra/integration_tests/llui_libtest/llui_libtest.cpp
index 3d433fdfdc54b1506e0fe07773fa2a92cffe58c8..3631761c93b81419b2f8d9e97c9ab81b87f9682d 100644
--- a/indra/integration_tests/llui_libtest/llui_libtest.cpp
+++ b/indra/integration_tests/llui_libtest/llui_libtest.cpp
@@ -43,7 +43,7 @@
 #include "llfloater.h"
 #include "llfontfreetype.h"
 #include "llfontgl.h"
-#include "lltrans.h"
+#include "lltransutil.h"
 #include "llui.h"
 #include "lluictrlfactory.h"
 
@@ -154,8 +154,8 @@ void init_llui()
 	// Otherwise we get translation warnings when setting up floaters
 	// (tooltips for buttons)
 	std::set<std::string> default_args;
-	LLTrans::parseStrings("strings.xml", default_args);
-    LLTrans::parseLanguageStrings("language_settings.xml");
+	LLTransUtil::parseStrings("strings.xml", default_args);
+	LLTransUtil::parseLanguageStrings("language_settings.xml");
 	LLFontManager::initClass();
 	
 	// Creating widgets apparently requires fonts to be initialized,
diff --git a/indra/linux_updater/CMakeLists.txt b/indra/linux_updater/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9fe32ecb46fc4e0a284cbc269a79baf130dc7848
--- /dev/null
+++ b/indra/linux_updater/CMakeLists.txt
@@ -0,0 +1,58 @@
+# -*- cmake -*-
+
+project(linux_updater)
+
+include(00-Common)
+include(CURL)
+include(CARes)
+include(OpenSSL)
+include(UI)
+include(LLCommon)
+include(LLVFS)
+include(LLXML)
+include(LLXUIXML)
+include(Linking)
+
+include_directories(
+    ${LLCOMMON_INCLUDE_DIRS}
+    ${LLVFS_INCLUDE_DIRS}
+    ${LLXML_INCLUDE_DIRS}
+    ${LLXUIXML_INCLUDE_DIRS}
+    ${CURL_INCLUDE_DIRS}
+    ${CARES_INCLUDE_DIRS}
+    ${OPENSSL_INCLUDE_DIRS}
+    ${UI_INCLUDE_DIRS}
+    )
+
+set(linux_updater_SOURCE_FILES linux_updater.cpp)
+
+set(linux_updater_HEADER_FILES CMakeLists.txt)
+
+set_source_files_properties(${linux_updater_HEADER_FILES}
+                            PROPERTIES HEADER_FILES_ONLY TRUE)
+
+list(APPEND linux_updater_SOURCE_FILES ${linux_updater_HEADER_FILES})
+
+add_executable(linux-updater ${linux_updater_SOURCE_FILES})
+
+target_link_libraries(linux-updater
+    ${CURL_LIBRARIES}
+    ${CARES_LIBRARIES}
+    ${OPENSSL_LIBRARIES}
+    ${CRYPTO_LIBRARIES}
+    ${UI_LIBRARIES}
+    ${LLXML_LIBRARIES}
+    ${LLXUIXML_LIBRARIES}
+    ${LLVFS_LIBRARIES}
+    ${LLCOMMON_LIBRARIES}
+    )
+
+add_custom_command(
+    OUTPUT linux-updater-stripped
+    COMMAND strip
+    ARGS --strip-debug -o linux-updater-stripped linux-updater
+    DEPENDS linux-updater
+    )
+
+add_custom_target(linux-updater-strip-target ALL
+                  DEPENDS linux-updater-stripped)
diff --git a/indra/linux_updater/linux_updater.cpp b/indra/linux_updater/linux_updater.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..acc60d42bf52e52fe93d4e8c09757794b8820ba8
--- /dev/null
+++ b/indra/linux_updater/linux_updater.cpp
@@ -0,0 +1,818 @@
+/** 
+ * @file linux_updater.cpp
+ * @author Kyle Ambroff <ambroff@lindenlab.com>, Tofu Linden
+ * @brief Viewer update program for unix platforms that support GTK+
+ *
+ * $LicenseInfo:firstyear=2008&license=viewergpl$
+ * 
+ * Copyright (c) 2008, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "linden_common.h"
+#include "llerrorcontrol.h"
+#include "llfile.h"
+#include "lldir.h"
+#include "llxmlnode.h"
+#include "lltrans.h"
+
+#include <curl/curl.h>
+
+extern "C" {
+#include <gtk/gtk.h>
+}
+
+const guint UPDATE_PROGRESS_TIMEOUT = 100;
+const guint UPDATE_PROGRESS_TEXT_TIMEOUT = 1000;
+const guint ROTATE_IMAGE_TIMEOUT = 8000;
+
+typedef struct _updater_app_state {
+	std::string app_name;
+	std::string url;
+	std::string image_dir;
+	std::string dest_dir;
+	std::string strings_dirs;
+	std::string strings_file;
+
+	GtkWidget *window;
+	GtkWidget *progress_bar;
+	GtkWidget *image;
+
+	double progress_value;
+	bool activity_mode;
+
+	guint image_rotation_timeout_id;
+	guint progress_update_timeout_id;
+	guint update_progress_text_timeout_id;
+
+	bool failure;
+} UpdaterAppState;
+
+// List of entries from strings.xml to always replace
+static std::set<std::string> default_trans_args;
+void init_default_trans_args()
+{
+        default_trans_args.insert("SECOND_LIFE"); // World
+        default_trans_args.insert("SECOND_LIFE_VIEWER");
+        default_trans_args.insert("SECOND_LIFE_GRID");
+        default_trans_args.insert("SECOND_LIFE_SUPPORT");
+}
+
+bool translate_init(std::string comma_delim_path_list,
+		    std::string base_xml_name)
+{
+	init_default_trans_args();
+
+	// extract paths string vector from comma-delimited flat string
+	std::vector<std::string> paths;
+	LLStringUtil::getTokens(comma_delim_path_list, paths); // split over ','
+
+	// suck the translation xml files into memory
+	LLXMLNodePtr root;
+	bool success = LLXMLNode::getLayeredXMLNode(base_xml_name, root, paths);
+	if (!success)
+	{
+		// couldn't load string table XML
+		return false;
+	}
+	else
+	{
+		// get those strings out of the XML
+		LLTrans::parseStrings(root, default_trans_args);
+		return true;
+	}
+}
+
+
+void updater_app_ui_init(void);
+void updater_app_quit(UpdaterAppState *app_state);
+void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state);
+std::string next_image_filename(std::string& image_path);
+void display_error(GtkWidget *parent, std::string title, std::string message);
+BOOL install_package(std::string package_file, std::string destination);
+BOOL spawn_viewer(UpdaterAppState *app_state);
+
+extern "C" {
+	void on_window_closed(GtkWidget *sender, gpointer state);
+	gpointer worker_thread_cb(gpointer *data);
+	int download_progress_cb(gpointer data, double t, double d, double utotal, double ulnow);
+	gboolean rotate_image_cb(gpointer data);
+	gboolean progress_update_timeout(gpointer data);
+	gboolean update_progress_text_timeout(gpointer data);
+}
+
+void updater_app_ui_init(UpdaterAppState *app_state)
+{
+	GtkWidget *vbox;
+	GtkWidget *summary_label;
+	GtkWidget *description_label;
+	GtkWidget *frame;
+
+	llassert(app_state != NULL);
+
+	// set up window and main container
+	std::string window_title = LLTrans::getString("UpdaterWindowTitle");
+	app_state->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_title(GTK_WINDOW(app_state->window),
+			     window_title.c_str());
+	gtk_window_set_resizable(GTK_WINDOW(app_state->window), FALSE);
+	gtk_window_set_position(GTK_WINDOW(app_state->window),
+				GTK_WIN_POS_CENTER_ALWAYS);
+
+	gtk_container_set_border_width(GTK_CONTAINER(app_state->window), 12);
+	g_signal_connect(G_OBJECT(app_state->window), "delete-event", 
+			 G_CALLBACK(on_window_closed), app_state);
+
+	vbox = gtk_vbox_new(FALSE, 6);
+	gtk_container_add(GTK_CONTAINER(app_state->window), vbox);
+
+	// set top label
+	std::ostringstream label_ostr;
+	label_ostr << "<big><b>"
+		   << LLTrans::getString("UpdaterNowUpdating")
+		   << "</b></big>";
+
+	summary_label = gtk_label_new(NULL);
+	gtk_label_set_use_markup(GTK_LABEL(summary_label), TRUE);
+	gtk_label_set_markup(GTK_LABEL(summary_label), 
+			     label_ostr.str().c_str());
+	gtk_misc_set_alignment(GTK_MISC(summary_label), 0, 0.5);
+	gtk_box_pack_start(GTK_BOX(vbox), summary_label, FALSE, FALSE, 0);
+
+	// create the description label
+	description_label = gtk_label_new(LLTrans::getString("UpdaterUpdatingDescriptive").c_str());
+	gtk_label_set_line_wrap(GTK_LABEL(description_label), TRUE);
+	gtk_misc_set_alignment(GTK_MISC(description_label), 0, 0.5);
+	gtk_box_pack_start(GTK_BOX(vbox), description_label, FALSE, FALSE, 0);
+
+	// If an image path has been set, load the background images
+	if (!app_state->image_dir.empty()) {
+		frame = gtk_frame_new(NULL);
+		gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
+		gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
+
+		// load the first image
+		app_state->image = gtk_image_new_from_file
+			(next_image_filename(app_state->image_dir).c_str());
+		gtk_widget_set_size_request(app_state->image, 340, 310);
+		gtk_container_add(GTK_CONTAINER(frame), app_state->image);
+
+		// rotate the images every 5 seconds
+		app_state->image_rotation_timeout_id = g_timeout_add
+			(ROTATE_IMAGE_TIMEOUT, rotate_image_cb, app_state);
+	}
+
+	// set up progress bar, and update it roughly every 1/10 of a second
+	app_state->progress_bar = gtk_progress_bar_new();
+	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(app_state->progress_bar), 
+				  LLTrans::getString("UpdaterProgressBarTextWithEllipses").c_str());
+	gtk_box_pack_start(GTK_BOX(vbox), 
+			   app_state->progress_bar, FALSE, TRUE, 0);
+	app_state->progress_update_timeout_id = g_timeout_add
+		(UPDATE_PROGRESS_TIMEOUT, progress_update_timeout, app_state);
+	app_state->update_progress_text_timeout_id = g_timeout_add
+		(UPDATE_PROGRESS_TEXT_TIMEOUT, update_progress_text_timeout, app_state);
+
+	gtk_widget_show_all(app_state->window);
+}
+
+gboolean rotate_image_cb(gpointer data)
+{
+	UpdaterAppState *app_state;
+	std::string filename;
+
+	llassert(data != NULL);
+	app_state = (UpdaterAppState *) data;
+
+	filename = next_image_filename(app_state->image_dir);
+
+	gdk_threads_enter();
+	gtk_image_set_from_file(GTK_IMAGE(app_state->image), filename.c_str());
+	gdk_threads_leave();
+
+	return TRUE;
+}
+
+std::string next_image_filename(std::string& image_path)
+{
+	std::string image_filename;
+	gDirUtilp->getNextFileInDir(image_path, "/*.jpg", image_filename, true);
+	return image_path + "/" + image_filename;
+}
+
+void on_window_closed(GtkWidget *sender, gpointer data)
+{
+	UpdaterAppState *app_state;
+
+	llassert(data != NULL);
+	app_state = (UpdaterAppState *) data;
+
+	updater_app_quit(app_state);
+}
+
+void updater_app_quit(UpdaterAppState *app_state)
+{
+	if (app_state != NULL)
+	{
+		g_source_remove(app_state->progress_update_timeout_id);
+
+		if (!app_state->image_dir.empty())
+		{
+			g_source_remove(app_state->image_rotation_timeout_id);
+		}
+	}
+
+	gtk_main_quit();
+}
+
+void display_error(GtkWidget *parent, std::string title, std::string message)
+{
+	GtkWidget *dialog;
+
+	dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
+					GTK_DIALOG_DESTROY_WITH_PARENT,
+					GTK_MESSAGE_ERROR,
+					GTK_BUTTONS_OK,
+					message.c_str());
+	gtk_window_set_title(GTK_WINDOW(dialog), title.c_str());
+	gtk_dialog_run(GTK_DIALOG(dialog));
+	gtk_widget_destroy(dialog);
+}
+
+gpointer worker_thread_cb(gpointer data)
+{
+	UpdaterAppState *app_state;
+	CURL *curl;
+	CURLcode result;
+	FILE *package_file;
+	GError *error = NULL;
+	char *tmp_filename = NULL;
+	int fd;
+
+	//g_return_val_if_fail (data != NULL, NULL);
+	app_state = (UpdaterAppState *) data;
+
+	try {
+		// create temporary file to store the package.
+		fd = g_file_open_tmp
+			("secondlife-update-XXXXXX", &tmp_filename, &error);
+		if (error != NULL)
+		{
+			llerrs << "Unable to create temporary file: "
+			       << error->message
+			       << llendl;
+
+			g_error_free(error);
+			throw 0;
+		}
+
+		package_file = fdopen(fd, "wb");
+		if (package_file == NULL)
+		{
+			llerrs << "Failed to create temporary file: "
+			       << tmp_filename
+			       << llendl;
+
+			gdk_threads_enter();
+			display_error(app_state->window,
+				      LLTrans::getString("UpdaterFailDownloadTitle"),
+				      LLTrans::getString("UpdaterFailUpdateDescriptive"));
+			gdk_threads_leave();
+			throw 0;
+		}
+
+		// initialize curl and start downloading the package
+		llinfos << "Downloading package: " << app_state->url << llendl;
+
+		curl = curl_easy_init();
+		if (curl == NULL)
+		{
+			llerrs << "Failed to initialize libcurl" << llendl;
+
+			gdk_threads_enter();
+			display_error(app_state->window,
+				      LLTrans::getString("UpdaterFailDownloadTitle"),
+				      LLTrans::getString("UpdaterFailUpdateDescriptive"));
+			gdk_threads_leave();
+			throw 0;
+		}
+
+		curl_easy_setopt(curl, CURLOPT_URL, app_state->url.c_str());
+		curl_easy_setopt(curl, CURLOPT_NOSIGNAL, TRUE);
+		curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, TRUE);
+		curl_easy_setopt(curl, CURLOPT_WRITEDATA, package_file);
+		curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE);
+		curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, 
+				 &download_progress_cb);
+		curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, app_state);
+
+		result = curl_easy_perform(curl);
+		fclose(package_file);
+		curl_easy_cleanup(curl);
+
+		if (result)
+		{
+			llerrs << "Failed to download update: " 
+			       << app_state->url 
+			       << llendl;
+
+			gdk_threads_enter();
+			display_error(app_state->window,
+				      LLTrans::getString("UpdaterFailDownloadTitle"),
+				      LLTrans::getString("UpdaterFailUpdateDescriptive"));
+			gdk_threads_leave();
+
+			throw 0;
+		}
+
+		// now pulse the progres bar back and forth while the package is
+		// being unpacked
+		gdk_threads_enter();
+		std::string installing_msg = LLTrans::getString("UpdaterNowInstalling");
+		gtk_progress_bar_set_text(
+			GTK_PROGRESS_BAR(app_state->progress_bar),
+			installing_msg.c_str());
+		app_state->activity_mode = TRUE;
+		gdk_threads_leave();
+
+		// *TODO: if the destination is not writable, terminate this
+		// thread and show file chooser?
+		if (!install_package(tmp_filename, app_state->dest_dir))
+		{
+			llwarns << "Failed to install package to destination: "
+				<< app_state->dest_dir
+				<< llendl;
+
+			gdk_threads_enter();
+			display_error(app_state->window,
+				      LLTrans::getString("UpdaterFailInstallTitle"),
+				      LLTrans::getString("UpdaterFailUpdateDescriptive"));
+			//"Failed to update " + app_state->app_name,
+			gdk_threads_leave();
+			throw 0;
+		}
+
+		// try to spawn the new viewer
+		if (!spawn_viewer(app_state))
+		{
+			llwarns << "Viewer was not installed properly in : "
+				<< app_state->dest_dir
+				<< llendl;
+
+			gdk_threads_enter();
+			display_error(app_state->window,
+				      LLTrans::getString("UpdaterFailStartTitle"),
+				      LLTrans::getString("UpdaterFailUpdateDescriptive"));
+			gdk_threads_leave();
+			throw 0;
+		}
+	}
+	catch (...)
+	{
+		app_state->failure = TRUE;
+	}
+
+	// FIXME: delete package file also if delete-event is raised on window
+	if (tmp_filename != NULL)
+	{
+		if (gDirUtilp->fileExists(tmp_filename))
+		{
+			LLFile::remove(tmp_filename);
+		}
+	}
+
+	gdk_threads_enter();
+	updater_app_quit(app_state);
+	gdk_threads_leave();
+
+	return NULL;
+}
+
+
+gboolean less_anal_gspawnsync(gchar **argv,
+			      gchar **stderr_output,
+			      gint *child_exit_status,
+			      GError **spawn_error)
+{
+	// store current SIGCHLD handler if there is one, replace with default
+	// handler to make glib happy
+	struct sigaction sigchld_backup;
+	struct sigaction sigchld_appease_glib;
+	sigchld_appease_glib.sa_handler = SIG_DFL;
+	sigemptyset(&sigchld_appease_glib.sa_mask);
+	sigchld_appease_glib.sa_flags = 0;
+	sigaction(SIGCHLD, &sigchld_appease_glib, &sigchld_backup);
+
+	gboolean rtn = g_spawn_sync(NULL,
+				    argv,
+				    NULL,
+				    (GSpawnFlags) (G_SPAWN_STDOUT_TO_DEV_NULL),
+				    NULL,
+				    NULL,
+				    NULL,
+				    stderr_output,
+				    child_exit_status,
+				    spawn_error);
+
+	// restore SIGCHLD handler
+	sigaction(SIGCHLD, &sigchld_backup, NULL);
+	
+	return rtn;
+}
+
+
+// perform a rename, or perform a (prompted) root rename if that fails
+int
+rename_with_sudo_fallback(const std::string& filename, const std::string& newname)
+{
+	int rtncode = ::rename(filename.c_str(), newname.c_str());
+	lldebugs << "rename result is: " << rtncode << " / " << errno << llendl;
+	if (rtncode && (EACCES == errno || EPERM == errno || EXDEV == errno))
+	{
+		llinfos << "Permission problem in rename, or moving between different mount points.  Retrying as a mv under a sudo." << llendl;
+		// failed due to permissions, try again as a gksudo or kdesu mv wrapper hack
+		char *sudo_cmd = NULL;
+		sudo_cmd = g_find_program_in_path("gksudo");
+		if (!sudo_cmd)
+		{
+			sudo_cmd = g_find_program_in_path("kdesu");
+		}
+		if (sudo_cmd)
+		{
+			char *mv_cmd = NULL;
+			mv_cmd = g_find_program_in_path("mv");
+			if (mv_cmd)
+			{
+				char *src_string_copy = g_strdup(filename.c_str());
+				char *dst_string_copy = g_strdup(newname.c_str());
+				char* argv[] = 
+					{
+						sudo_cmd,
+						mv_cmd,
+						src_string_copy,
+						dst_string_copy,
+						NULL
+					};
+
+				gchar *stderr_output = NULL;
+				gint child_exit_status = 0;
+				GError *spawn_error = NULL;
+				if (!less_anal_gspawnsync(argv, &stderr_output,
+							  &child_exit_status, &spawn_error))
+				{
+					llwarns << "Failed to spawn child process: " 
+						<< spawn_error->message 
+						<< llendl;
+				}
+				else if (child_exit_status)
+				{
+					llwarns << "mv command failed: "
+						<< (stderr_output ? stderr_output : "(no reason given)")
+						<< llendl;
+				}
+				else
+				{
+					// everything looks good, clear the error code
+					rtncode = 0;
+				}				
+
+				g_free(src_string_copy);
+				g_free(dst_string_copy);
+				if (spawn_error) g_error_free(spawn_error);
+			}
+		}
+	}
+	return rtncode;
+}
+
+gboolean install_package(std::string package_file, std::string destination)
+{
+	char *tar_cmd = NULL;
+	std::ostringstream command;
+
+	// Find the absolute path to the 'tar' command.
+	tar_cmd = g_find_program_in_path("tar");
+	if (!tar_cmd)
+	{
+		llerrs << "`tar' was not found in $PATH" << llendl;
+		return FALSE;
+	}
+	llinfos << "Found tar command: " << tar_cmd << llendl;
+
+	// Unpack the tarball in a temporary place first, then move it to 
+	// its final destination
+	std::string tmp_dest_dir = gDirUtilp->getTempFilename();
+	if (LLFile::mkdir(tmp_dest_dir, 0744))
+	{
+		llerrs << "Failed to create directory: "
+		       << destination
+		       << llendl;
+
+		return FALSE;
+	}
+
+	char *package_file_string_copy = g_strdup(package_file.c_str());
+	char *tmp_dest_dir_string_copy = g_strdup(tmp_dest_dir.c_str());
+	char *argv[8] = {
+		tar_cmd,
+		"--strip", "1",
+		"-xjf",
+		package_file_string_copy,
+		"-C", tmp_dest_dir_string_copy,
+		NULL,
+	};
+
+	llinfos << "Untarring package: " << package_file << llendl;
+
+	// store current SIGCHLD handler if there is one, replace with default
+	// handler to make glib happy
+	struct sigaction sigchld_backup;
+	struct sigaction sigchld_appease_glib;
+	sigchld_appease_glib.sa_handler = SIG_DFL;
+	sigemptyset(&sigchld_appease_glib.sa_mask);
+	sigchld_appease_glib.sa_flags = 0;
+	sigaction(SIGCHLD, &sigchld_appease_glib, &sigchld_backup);
+
+	gchar *stderr_output = NULL;
+	gint child_exit_status = 0;
+	GError *untar_error = NULL;
+	if (!less_anal_gspawnsync(argv, &stderr_output,
+				  &child_exit_status, &untar_error))
+	{
+		llwarns << "Failed to spawn child process: " 
+			<< untar_error->message 
+			<< llendl;
+		return FALSE;
+	}
+
+	if (child_exit_status)
+	{
+	 	llwarns << "Untar command failed: "
+			<< (stderr_output ? stderr_output : "(no reason given)")
+			<< llendl;
+		return FALSE;
+	}
+
+	g_free(tar_cmd);
+	g_free(package_file_string_copy);
+	g_free(tmp_dest_dir_string_copy);
+	g_free(stderr_output);
+	if (untar_error) g_error_free(untar_error);
+
+	// move the existing package out of the way if it exists
+	if (gDirUtilp->fileExists(destination))
+	{
+		std::string backup_dir = destination + ".backup";
+		int oldcounter = 1;
+		while (gDirUtilp->fileExists(backup_dir))
+		{
+			// find a foo.backup.N folder name that isn't taken yet
+			backup_dir = destination + ".backup." + llformat("%d", oldcounter);
+			++oldcounter;
+		}
+
+		if (rename_with_sudo_fallback(destination, backup_dir))
+		{
+			llwarns << "Failed to move directory: '" 
+				<< destination << "' -> '" << backup_dir 
+				<< llendl;
+			return FALSE;
+		}
+	}
+
+	// The package has been unpacked in a staging directory, now we just
+	// need to move it to its destination.
+	if (rename_with_sudo_fallback(tmp_dest_dir, destination))
+	{
+		llwarns << "Failed to move installation to the destination: "
+			<< destination 
+			<< llendl;
+		return FALSE;
+	}
+
+	// \0/ Success!
+	return TRUE;
+}
+
+gboolean progress_update_timeout(gpointer data)
+{
+	UpdaterAppState *app_state;
+
+	llassert(data != NULL);
+
+	app_state = (UpdaterAppState *) data;
+
+	gdk_threads_enter();
+	if (app_state->activity_mode)
+	{
+		gtk_progress_bar_pulse
+			(GTK_PROGRESS_BAR(app_state->progress_bar));
+	}
+	else
+	{
+		gtk_progress_set_value(GTK_PROGRESS(app_state->progress_bar),
+				       app_state->progress_value);
+	}
+	gdk_threads_leave();
+
+	return TRUE;
+}
+
+gboolean update_progress_text_timeout(gpointer data)
+{
+	UpdaterAppState *app_state;
+
+	llassert(data != NULL);
+	app_state = (UpdaterAppState *) data;
+
+	if (app_state->activity_mode == TRUE)
+	{
+		// We no longer need this timeout, it will be removed.
+		return FALSE;
+	}
+
+	if (!app_state->progress_value)
+	{
+		return TRUE;
+	}
+
+	std::string progress_text = llformat((LLTrans::getString("UpdaterProgressBarText")+" (%.0f%%)").c_str(), app_state->progress_value);
+
+	gdk_threads_enter();
+	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(app_state->progress_bar),
+				  progress_text.c_str());
+	gdk_threads_leave();
+
+	return TRUE;
+}
+
+int download_progress_cb(gpointer data,
+			 double t,
+			 double d,
+			 double utotal,
+			 double ulnow)
+{
+	UpdaterAppState *app_state;
+
+	llassert(data != NULL);
+	app_state = (UpdaterAppState *) data;
+
+	if (t <= 0.0)
+	{
+		app_state->progress_value = 0;
+	}
+	else
+	{
+		app_state->progress_value = d * 100.0 / t;
+	}
+	return 0;
+}
+
+BOOL spawn_viewer(UpdaterAppState *app_state)
+{
+	llassert(app_state != NULL);
+
+	std::string cmd = app_state->dest_dir + "/secondlife";
+	GError *error = NULL;
+
+	// We want to spawn the Viewer on the same display as the updater app
+	gboolean success = gdk_spawn_command_line_on_screen
+		(gtk_widget_get_screen(app_state->window), cmd.c_str(), &error);
+
+	if (!success)
+	{
+		llwarns << "Failed to launch viewer: " << error->message 
+			<< llendl;
+	}
+
+	if (error) g_error_free(error);
+
+	return success;
+}
+
+void show_usage_and_exit()
+{
+	std::cout << "Usage: linux-updater --url URL --name NAME --dest PATH --stringsdir PATH1,PATH2 --stringsfile FILE"
+		  << "[--image-dir PATH]"
+		  << std::endl;
+	exit(1);
+}
+
+void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state)
+{
+	int i;
+
+	for (i = 1; i < argc; i++)
+	{
+		if ((!strcmp(argv[i], "--url")) && (++i < argc))
+		{
+			app_state->url = argv[i];
+		}
+		else if ((!strcmp(argv[i], "--name")) && (++i < argc))
+		{
+			app_state->app_name = argv[i];
+		}
+		else if ((!strcmp(argv[i], "--image-dir")) && (++i < argc))
+		{
+			app_state->image_dir = argv[i];
+		}
+		else if ((!strcmp(argv[i], "--dest")) && (++i < argc))
+		{
+			app_state->dest_dir = argv[i];
+		}
+		else if ((!strcmp(argv[i], "--stringsdir")) && (++i < argc))
+		{
+			app_state->strings_dirs = argv[i];
+		}
+		else if ((!strcmp(argv[i], "--stringsfile")) && (++i < argc))
+		{
+			app_state->strings_file = argv[i];
+		}
+		else
+		{
+			// show usage, an invalid option was given.
+			show_usage_and_exit();
+		}
+	}
+
+	if (app_state->app_name.empty() 
+	    || app_state->url.empty() 
+	    || app_state->dest_dir.empty())
+	{
+		show_usage_and_exit();
+	}
+
+	app_state->progress_value = 0.0;
+	app_state->activity_mode = FALSE;
+	app_state->failure = FALSE;
+
+	translate_init(app_state->strings_dirs, app_state->strings_file);
+}
+
+int main(int argc, char **argv)
+{
+	UpdaterAppState app_state;
+	GThread *worker_thread;
+
+	parse_args_and_init(argc, argv, &app_state);
+
+	// Initialize logger, and rename old log file
+	gDirUtilp->initAppDirs("SecondLife");
+	LLError::initForApplication
+		(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
+	std::string old_log_file = gDirUtilp->getExpandedFilename
+		(LL_PATH_LOGS, "updater.log.old");
+	std::string log_file = 
+		gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "updater.log");
+	LLFile::rename(log_file, old_log_file);
+	LLError::logToFile(log_file);
+
+	// initialize gthreads and gtk+
+	if (!g_thread_supported())
+	{
+		g_thread_init(NULL);
+		gdk_threads_init();
+	}
+
+	gtk_init(&argc, &argv);
+
+	// create UI
+	updater_app_ui_init(&app_state);
+
+	//llinfos << "SAMPLE TRANSLATION IS: " << LLTrans::getString("LoginInProgress") << llendl;
+
+	// create download thread
+	worker_thread = g_thread_create
+		(GThreadFunc(worker_thread_cb), &app_state, FALSE, NULL);
+
+	gdk_threads_enter();
+	gtk_main();
+	gdk_threads_leave();
+
+	return (app_state.failure == FALSE) ? 0 : 1;
+}
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index e8d95b44a5257332010c43c62461102d7197d474..7f71ff6a531443ffb56e25de4783656fa3f87fbc 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -12,6 +12,7 @@ include(LLRender)
 include(LLWindow)
 include(LLVFS)
 include(LLXML)
+include(LLXUIXML)
 
 include_directories(
     ${LLAUDIO_INCLUDE_DIRS}
@@ -23,6 +24,7 @@ include_directories(
     ${LLWINDOW_INCLUDE_DIRS}
     ${LLVFS_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
+    ${LLXUIXML_INCLUDE_DIRS}
     )
 
 set(llui_SOURCE_FILES
@@ -44,7 +46,6 @@ set(llui_SOURCE_FILES
     llfocusmgr.cpp
     llfunctorregistry.cpp
     lliconctrl.cpp
-    llinitparam.cpp
     llkeywords.cpp
     lllayoutstack.cpp
     lllineeditor.cpp
@@ -82,9 +83,8 @@ set(llui_SOURCE_FILES
     lltextbox.cpp
     lltexteditor.cpp
     lltextparser.cpp
-    lltrans.cpp
+    lltransutil.cpp
     llui.cpp
-    lluicolor.cpp
     lluicolortable.cpp
     lluictrl.cpp
     lluictrlfactory.cpp
@@ -121,7 +121,6 @@ set(llui_HEADER_FILES
     llhandle.h
     llhtmlhelp.h
     lliconctrl.h
-    llinitparam.h
     llkeywords.h
     lllayoutstack.h
     lllazyvalue.h
@@ -136,7 +135,6 @@ set(llui_HEADER_FILES
     llpanel.h
     llprogressbar.h
     llradiogroup.h
-    llregistry.h
     llresizebar.h
     llresizehandle.h
     llresmgr.h
@@ -161,8 +159,7 @@ set(llui_HEADER_FILES
     lltextbox.h
     lltexteditor.h
     lltextparser.h
-    lltrans.h
-    lluicolor.h
+    lltransutil.h
     lluicolortable.h
     lluiconstants.h
     lluictrlfactory.h
@@ -191,6 +188,7 @@ target_link_libraries(llui
     llwindow
     llimage
     llvfs       # ugh, just for LLDir
+    llxuixml
     llxml
     llcommon    # must be after llimage, llwindow, llrender
     llmath
diff --git a/indra/llui/lltransutil.cpp b/indra/llui/lltransutil.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eaee260c7a454179db9bf2a6d4fbbb95d34bd9c5
--- /dev/null
+++ b/indra/llui/lltransutil.cpp
@@ -0,0 +1,67 @@
+/**
+ * @file lltrans.cpp
+ * @brief LLTrans implementation
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ * 
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltrans.h"
+#include "lluictrlfactory.h"
+
+#include "lltransutil.h"
+
+
+bool LLTransUtil::parseStrings(const std::string& xml_filename, const std::set<std::string>& default_args)
+{
+	LLXMLNodePtr root;
+	BOOL success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);
+	if (!success)
+	{
+		llerrs << "Couldn't load string table" << llendl;
+		return false;
+	}
+
+	return LLTrans::parseStrings(root, default_args);
+}
+
+
+bool LLTransUtil::parseLanguageStrings(const std::string& xml_filename)
+{
+	LLXMLNodePtr root;
+	BOOL success  = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);
+	
+	if (!success)
+	{
+		llerrs << "Couldn't load string table " << xml_filename << llendl;
+		return false;
+	}
+	
+	return LLTrans::parseLanguageStrings(root);
+}
diff --git a/indra/llui/lltransutil.h b/indra/llui/lltransutil.h
new file mode 100644
index 0000000000000000000000000000000000000000..2ddfd81361d4b170310266226b35906a51460241
--- /dev/null
+++ b/indra/llui/lltransutil.h
@@ -0,0 +1,51 @@
+/**
+ * @file lltransutil.h
+ * @brief LLTrans helper
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ * 
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_TRANSUTIL_H
+#define LL_TRANSUTIL_H
+
+#include "lltrans.h"
+
+namespace LLTransUtil
+{
+	/**
+	 * @brief Parses the xml file that holds the strings. Used once on startup
+	 * @param xml_filename Filename to parse
+	 * @param default_args Set of strings (expected to be in the file) to use as default replacement args, e.g. "SECOND_LIFE"
+	 * @returns true if the file was parsed successfully, true if something went wrong
+	 */
+	bool parseStrings(const std::string& xml_filename, const std::set<std::string>& default_args);
+
+	bool parseLanguageStrings(const std::string& xml_filename);
+};
+
+#endif
diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp
index a4c9728402130fd38d66d56435cd0d54901c8609..2bbede8c133e756f93256cda227dff50049dbf04 100644
--- a/indra/llui/lluictrlfactory.cpp
+++ b/indra/llui/lluictrlfactory.cpp
@@ -71,14 +71,6 @@
 #include "llui.h"
 #include "llviewborder.h"
 
-const char XML_HEADER[] = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n";
-
-const S32 HPAD = 4;
-const S32 VPAD = 4;
-const S32 FLOATER_H_MARGIN = 15;
-const S32 MIN_WIDGET_HEIGHT = 10;
-const S32 MAX_STRING_ATTRIBUTE_SIZE = 40;
-
 LLFastTimer::DeclareTimer FTM_WIDGET_CONSTRUCTION("Widget Construction");
 LLFastTimer::DeclareTimer FTM_INIT_FROM_PARAMS("Widget InitFromParams");
 LLFastTimer::DeclareTimer FTM_WIDGET_SETUP("Widget Setup");
@@ -436,929 +428,3 @@ void LLUICtrlFactory::popFactoryFunctions()
 		mFactoryStack.pop_back();
 	}
 }
-
-//
-// LLXSDWriter
-//
-LLXSDWriter::LLXSDWriter()
-{
-	registerInspectFunc<bool>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:boolean", _1, _2, _3, _4));
-	registerInspectFunc<std::string>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
-	registerInspectFunc<U8>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedByte", _1, _2, _3, _4));
-	registerInspectFunc<S8>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedByte", _1, _2, _3, _4));
-	registerInspectFunc<U16>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedShort", _1, _2, _3, _4));
-	registerInspectFunc<S16>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedShort", _1, _2, _3, _4));
-	registerInspectFunc<U32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedInt", _1, _2, _3, _4));
-	registerInspectFunc<S32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:integer", _1, _2, _3, _4));
-	registerInspectFunc<F32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:float", _1, _2, _3, _4));
-	registerInspectFunc<F64>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:double", _1, _2, _3, _4));
-	registerInspectFunc<LLColor4>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
-	registerInspectFunc<LLUIColor>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
-	registerInspectFunc<LLUUID>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
-	registerInspectFunc<LLSD>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
-}
-
-void LLXSDWriter::writeXSD(const std::string& type_name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace)
-{
-	mSchemaNode = node;
-	node->setName("xs:schema");
-	node->createChild("attributeFormDefault", true)->setStringValue("unqualified");
-	node->createChild("elementFormDefault", true)->setStringValue("qualified");
-	node->createChild("targetNamespace", true)->setStringValue(xml_namespace);
-	node->createChild("xmlns:xs", true)->setStringValue("http://www.w3.org/2001/XMLSchema");
-	node->createChild("xmlns", true)->setStringValue(xml_namespace);
-
-	node = node->createChild("xs:complexType", false);
-	node->createChild("name", true)->setStringValue(type_name);
-	node->createChild("mixed", true)->setStringValue("true");
-
-	mAttributeNode = node;
-	mElementNode = node->createChild("xs:choice", false);
-	mElementNode->createChild("minOccurs", true)->setStringValue("0");
-	mElementNode->createChild("maxOccurs", true)->setStringValue("unbounded");
-	block.inspectBlock(*this);
-
-	// duplicate element choices
-	LLXMLNodeList children;
-	mElementNode->getChildren("xs:element", children, FALSE);
-	for (LLXMLNodeList::iterator child_it = children.begin(); child_it != children.end(); ++child_it)
-	{
-		LLXMLNodePtr child_copy = child_it->second->deepCopy();
-		std::string child_name;
-		child_copy->getAttributeString("name", child_name);
-		child_copy->setAttributeString("name", type_name + "." + child_name);
-		mElementNode->addChild(child_copy);
-	}
-
-	LLXMLNodePtr element_declaration_node = mSchemaNode->createChild("xs:element", false);
-	element_declaration_node->createChild("name", true)->setStringValue(type_name);
-	element_declaration_node->createChild("type", true)->setStringValue(type_name);
-}
-
-void LLXSDWriter::writeAttribute(const std::string& type, const Parser::name_stack_t& stack, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values)
-{
-	name_stack_t non_empty_names;
-	std::string attribute_name;
-	for (name_stack_t::const_iterator it = stack.begin();
-		it != stack.end();
-		++it)
-	{
-		const std::string& name = it->first;
-		if (!name.empty())
-		{
-			non_empty_names.push_back(*it);
-		}
-	}
-
-	for (name_stack_t::const_iterator it = non_empty_names.begin();
-		it != non_empty_names.end();
-		++it)
-	{
-		if (!attribute_name.empty())
-		{
-			attribute_name += ".";
-		}
-		attribute_name += it->first;
-	}
-
-	// only flag non-nested attributes as mandatory, nested attributes have variant syntax
-	// that can't be properly constrained in XSD
-	// e.g. <foo mandatory.value="bar"/> vs <foo><mandatory value="bar"/></foo>
-	bool attribute_mandatory = min_count == 1 && max_count == 1 && non_empty_names.size() == 1;
-
-	// don't bother supporting "Multiple" params as xml attributes
-	if (max_count <= 1)
-	{
-		// add compound attribute to root node
-		addAttributeToSchema(mAttributeNode, attribute_name, type, attribute_mandatory, possible_values);
-	}
-
-	// now generated nested elements for compound attributes
-	if (non_empty_names.size() > 1 && !attribute_mandatory)
-	{
-		std::string element_name;
-
-		// traverse all but last element, leaving that as an attribute name
-		name_stack_t::const_iterator end_it = non_empty_names.end();
-		end_it--;
-
-		for (name_stack_t::const_iterator it = non_empty_names.begin();
-			it != end_it;
-			++it)
-		{
-			if (it != non_empty_names.begin())
-			{
-				element_name += ".";
-			}
-			element_name += it->first;
-		}
-
-		std::string short_attribute_name = non_empty_names.back().first;
-
-		LLXMLNodePtr complex_type_node;
-
-		// find existing element node here, starting at tail of child list
-		if (mElementNode->mChildren.notNull())
-		{
-			for(LLXMLNodePtr element = mElementNode->mChildren->tail;
-				element.notNull(); 
-				element = element->mPrev)
-			{
-				std::string name;
-				if(element->getAttributeString("name", name) && name == element_name)
-				{
-					complex_type_node = element->mChildren->head;
-					break;
-				}
-			}
-		}
-		//create complex_type node
-		//
-		//<xs:element
-        //    maxOccurs="1"
-        //    minOccurs="0"
-        //    name="name">
-        //       <xs:complexType>
-        //       </xs:complexType>
-        //</xs:element>
-		if(complex_type_node.isNull())
-		{
-			complex_type_node = mElementNode->createChild("xs:element", false);
-
-			complex_type_node->createChild("minOccurs", true)->setIntValue(min_count);
-			complex_type_node->createChild("maxOccurs", true)->setIntValue(max_count);
-			complex_type_node->createChild("name",		true)->setStringValue(element_name);
-			complex_type_node = complex_type_node->createChild("xs:complexType", false);
-		}
-
-		addAttributeToSchema(complex_type_node, short_attribute_name, type, false, possible_values);
-	}
-}
-
-void LLXSDWriter::addAttributeToSchema(LLXMLNodePtr type_declaration_node, const std::string& attribute_name, const std::string& type, bool mandatory, const std::vector<std::string>* possible_values)
-{
-	if (!attribute_name.empty())
-	{
-		LLXMLNodePtr new_enum_type_node;
-		if (possible_values != NULL)
-		{
-			// custom attribute type, for example
-			//<xs:simpleType>
-			 // <xs:restriction
-			 //    base="xs:string">
-			 //     <xs:enumeration
-			 //      value="a" />
-			 //     <xs:enumeration
-			 //      value="b" />
-			 //   </xs:restriction>
-			 // </xs:simpleType>
-			new_enum_type_node = new LLXMLNode("xs:simpleType", false);
-
-			LLXMLNodePtr restriction_node = new_enum_type_node->createChild("xs:restriction", false);
-			restriction_node->createChild("base", true)->setStringValue("xs:string");
-
-			for (std::vector<std::string>::const_iterator it = possible_values->begin();
-				it != possible_values->end();
-				++it)
-			{
-				LLXMLNodePtr enum_node = restriction_node->createChild("xs:enumeration", false);
-				enum_node->createChild("value", true)->setStringValue(*it);
-			}
-		}
-
-		string_set_t& attributes_written = mAttributesWritten[type_declaration_node];
-
-		string_set_t::iterator found_it = attributes_written.lower_bound(attribute_name);
-
-		// attribute not yet declared
-		if (found_it == attributes_written.end() || attributes_written.key_comp()(attribute_name, *found_it))
-		{
-			attributes_written.insert(found_it, attribute_name);
-
-			LLXMLNodePtr attribute_node = type_declaration_node->createChild("xs:attribute", false);
-
-			// attribute name
-			attribute_node->createChild("name", true)->setStringValue(attribute_name);
-
-			if (new_enum_type_node.notNull())
-			{
-				attribute_node->addChild(new_enum_type_node);
-			}
-			else
-			{
-				// simple attribute type
-				attribute_node->createChild("type", true)->setStringValue(type);
-			}
-
-			// required or optional
-			attribute_node->createChild("use", true)->setStringValue(mandatory ? "required" : "optional");
-		}
-		 // attribute exists...handle collision of same name attributes with potentially different types
-		else
-		{
-			LLXMLNodePtr attribute_declaration;
-			if (type_declaration_node.notNull())
-			{
-				for(LLXMLNodePtr node = type_declaration_node->mChildren->tail; 
-					node.notNull(); 
-					node = node->mPrev)
-				{
-					std::string name;
-					if (node->getAttributeString("name", name) && name == attribute_name)
-					{
-						attribute_declaration = node;
-						break;
-					}
-				}
-			}
-
-			bool new_type_is_enum = new_enum_type_node.notNull();
-			bool existing_type_is_enum = !attribute_declaration->hasAttribute("type");
-
-			// either type is enum, revert to string in collision
-			// don't bother to check for enum equivalence
-			if (new_type_is_enum || existing_type_is_enum)
-			{
-				if (attribute_declaration->hasAttribute("type"))
-				{
-					attribute_declaration->setAttributeString("type", "xs:string");
-				}
-				else
-				{
-					attribute_declaration->createChild("type", true)->setStringValue("xs:string");
-				}
-				attribute_declaration->deleteChildren("xs:simpleType");
-			}
-			else 
-			{
-				// check for collision of different standard types
-				std::string existing_type;
-				attribute_declaration->getAttributeString("type", existing_type);
-				// if current type is not the same as the new type, revert to strnig
-				if (existing_type != type)
-				{
-					// ...than use most general type, string
-					attribute_declaration->setAttributeString("type", "string");
-				}
-			}
-		}
-	}
-}
-
-//
-// LLXUIXSDWriter
-//
-void LLXUIXSDWriter::writeXSD(const std::string& type_name, const std::string& path, const LLInitParam::BaseBlock& block)
-{
-	std::string file_name(path);
-	file_name += type_name + ".xsd";
-	LLXMLNodePtr root_nodep = new LLXMLNode();
-
-	LLXSDWriter::writeXSD(type_name, root_nodep, block, "http://www.lindenlab.com/xui");
-
-	// add includes for all possible children
-	const std::type_info* type = *LLWidgetTypeRegistry::instance().getValue(type_name);
-	const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type);
-	
-	// add include declarations for all valid children
-	for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
-		it != widget_registryp->currentRegistrar().endItems();
-		++it)
-	{
-		std::string widget_name = it->first;
-		if (widget_name == type_name)
-		{
-			continue;
-		}
-		LLXMLNodePtr nodep = new LLXMLNode("xs:include", false);
-		nodep->createChild("schemaLocation", true)->setStringValue(widget_name + ".xsd");
-
-		// add to front of schema
-		mSchemaNode->addChild(nodep, mSchemaNode);
-	}
-
-	// add choices for valid children
-	if (widget_registryp)
-	{
-		for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
-			it != widget_registryp->currentRegistrar().endItems();
-			++it)
-		{
-			std::string widget_name = it->first;
-            //<xs:element name="widget_name" type="widget_name">
-			LLXMLNodePtr widget_node = mElementNode->createChild("xs:element", false);
-			widget_node->createChild("name", true)->setStringValue(widget_name);
-			widget_node->createChild("type", true)->setStringValue(widget_name);
-		}
-	}
-
-	LLFILE* xsd_file = LLFile::fopen(file_name.c_str(), "w");
-	LLXMLNode::writeHeaderToFile(xsd_file);
-	root_nodep->writeToFile(xsd_file);
-	fclose(xsd_file);
-}
-
-//
-// LLXUIParser
-//
-LLXUIParser::LLXUIParser()
-:	mLastWriteGeneration(-1),
-	mCurReadDepth(0)
-{
-	registerParserFuncs<bool>(boost::bind(&LLXUIParser::readBoolValue, this, _1),
-								boost::bind(&LLXUIParser::writeBoolValue, this, _1, _2));
-	registerParserFuncs<std::string>(boost::bind(&LLXUIParser::readStringValue, this, _1),
-								boost::bind(&LLXUIParser::writeStringValue, this, _1, _2));
-	registerParserFuncs<U8>(boost::bind(&LLXUIParser::readU8Value, this, _1),
-								boost::bind(&LLXUIParser::writeU8Value, this, _1, _2));
-	registerParserFuncs<S8>(boost::bind(&LLXUIParser::readS8Value, this, _1),
-								boost::bind(&LLXUIParser::writeS8Value, this, _1, _2));
-	registerParserFuncs<U16>(boost::bind(&LLXUIParser::readU16Value, this, _1),
-								boost::bind(&LLXUIParser::writeU16Value, this, _1, _2));
-	registerParserFuncs<S16>(boost::bind(&LLXUIParser::readS16Value, this, _1),
-								boost::bind(&LLXUIParser::writeS16Value, this, _1, _2));
-	registerParserFuncs<U32>(boost::bind(&LLXUIParser::readU32Value, this, _1),
-								boost::bind(&LLXUIParser::writeU32Value, this, _1, _2));
-	registerParserFuncs<S32>(boost::bind(&LLXUIParser::readS32Value, this, _1),
-								boost::bind(&LLXUIParser::writeS32Value, this, _1, _2));
-	registerParserFuncs<F32>(boost::bind(&LLXUIParser::readF32Value, this, _1),
-								boost::bind(&LLXUIParser::writeF32Value, this, _1, _2));
-	registerParserFuncs<F64>(boost::bind(&LLXUIParser::readF64Value, this, _1),
-								boost::bind(&LLXUIParser::writeF64Value, this, _1, _2));
-	registerParserFuncs<LLColor4>(boost::bind(&LLXUIParser::readColor4Value, this, _1),
-								boost::bind(&LLXUIParser::writeColor4Value, this, _1, _2));
-	registerParserFuncs<LLUIColor>(boost::bind(&LLXUIParser::readUIColorValue, this, _1),
-								boost::bind(&LLXUIParser::writeUIColorValue, this, _1, _2));
-	registerParserFuncs<LLUUID>(boost::bind(&LLXUIParser::readUUIDValue, this, _1),
-								boost::bind(&LLXUIParser::writeUUIDValue, this, _1, _2));
-	registerParserFuncs<LLSD>(boost::bind(&LLXUIParser::readSDValue, this, _1),
-								boost::bind(&LLXUIParser::writeSDValue, this, _1, _2));
-}
-
-static LLFastTimer::DeclareTimer PARSE_XUI("XUI Parsing");
-
-void LLXUIParser::readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, bool silent)
-{
-	LLFastTimer timer(PARSE_XUI);
-	mNameStack.clear();
-	mCurReadDepth = 0;
-	setParseSilently(silent);
-
-	if (node.isNull())
-	{
-		parserWarning("Invalid node");
-	}
-	else
-	{
-		readXUIImpl(node, std::string(node->getName()->mString), block);
-	}
-}
-
-bool LLXUIParser::readXUIImpl(LLXMLNodePtr nodep, const std::string& scope, LLInitParam::BaseBlock& block)
-{
-	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
-	boost::char_separator<char> sep(".");
-
-	bool values_parsed = false;
-
-	// submit attributes for current node
-	values_parsed |= readAttributes(nodep, block);
-
-	// treat text contents of xml node as "value" parameter
-	std::string text_contents = nodep->getSanitizedValue();
-	if (!text_contents.empty())
-	{
-		mCurReadNode = nodep;
-		mNameStack.push_back(std::make_pair(std::string("value"), newParseGeneration()));
-		// child nodes are not necessarily valid parameters (could be a child widget)
-		// so don't complain once we've recursed
-		bool silent = mCurReadDepth > 0;
-		if (!block.submitValue(mNameStack, *this, true))
-		{
-			mNameStack.pop_back();
-			block.submitValue(mNameStack, *this, silent);
-		}
-		else
-		{
-			mNameStack.pop_back();
-		}
-	}
-
-	// then traverse children
-	// child node must start with last name of parent node (our "scope")
-	// for example: "<button><button.param nested_param1="foo"><param.nested_param2 nested_param3="bar"/></button.param></button>"
-	// which equates to the following nesting:
-	// button
-	//     param
-	//         nested_param1
-	//         nested_param2
-	//             nested_param3	
-	mCurReadDepth++;
-	for(LLXMLNodePtr childp = nodep->getFirstChild(); childp.notNull();)
-	{
-		std::string child_name(childp->getName()->mString);
-		S32 num_tokens_pushed = 0;
-
-		// for non "dotted" child nodes	check to see if child node maps to another widget type
-		// and if not, treat as a child element of the current node
-		// e.g. <button><rect left="10"/></button> will interpret <rect> as "button.rect"
-		// since there is no widget named "rect"
-		if (child_name.find(".") == std::string::npos) 
-		{
-			mNameStack.push_back(std::make_pair(child_name, newParseGeneration()));
-			num_tokens_pushed++;
-		}
-		else
-		{
-			// parse out "dotted" name into individual tokens
-			tokenizer name_tokens(child_name, sep);
-
-			tokenizer::iterator name_token_it = name_tokens.begin();
-			if(name_token_it == name_tokens.end()) 
-			{
-				childp = childp->getNextSibling();
-				continue;
-			}
-
-			// check for proper nesting
-			if(!scope.empty() && *name_token_it != scope)
-			{
-				childp = childp->getNextSibling();
-				continue;
-			}
-
-			// now ignore first token
-			++name_token_it; 
-
-			// copy remaining tokens on to our running token list
-			for(tokenizer::iterator token_to_push = name_token_it; token_to_push != name_tokens.end(); ++token_to_push)
-			{
-				mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration()));
-				num_tokens_pushed++;
-			}
-		}
-
-		// recurse and visit children XML nodes
-		if(readXUIImpl(childp, mNameStack.empty() ? scope : mNameStack.back().first, block))
-		{
-			// child node successfully parsed, remove from DOM
-
-			values_parsed = true;
-			LLXMLNodePtr node_to_remove = childp;
-			childp = childp->getNextSibling();
-
-			nodep->deleteChild(node_to_remove);
-		}
-		else
-		{
-			childp = childp->getNextSibling();
-		}
-
-		while(num_tokens_pushed-- > 0)
-		{
-			mNameStack.pop_back();
-		}
-	}
-	mCurReadDepth--;
-	return values_parsed;
-}
-
-bool LLXUIParser::readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block)
-{
-	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
-	boost::char_separator<char> sep(".");
-
-	bool any_parsed = false;
-
-	for(LLXMLAttribList::const_iterator attribute_it = nodep->mAttributes.begin(); 
-		attribute_it != nodep->mAttributes.end(); 
-		++attribute_it)
-	{
-		S32 num_tokens_pushed = 0;
-		std::string attribute_name(attribute_it->first->mString);
-		mCurReadNode = attribute_it->second;
-
-		tokenizer name_tokens(attribute_name, sep);
-		// copy remaining tokens on to our running token list
-		for(tokenizer::iterator token_to_push = name_tokens.begin(); token_to_push != name_tokens.end(); ++token_to_push)
-		{
-			mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration()));
-			num_tokens_pushed++;
-		}
-
-		// child nodes are not necessarily valid attributes, so don't complain once we've recursed
-		bool silent = mCurReadDepth > 0;
-		any_parsed |= block.submitValue(mNameStack, *this, silent);
-		
-		while(num_tokens_pushed-- > 0)
-		{
-			mNameStack.pop_back();
-		}
-	}
-
-	return any_parsed;
-}
-
-void LLXUIParser::writeXUI(LLXMLNodePtr node, const LLInitParam::BaseBlock &block, const LLInitParam::BaseBlock* diff_block)
-{
-	mWriteRootNode = node;
-	block.serializeBlock(*this, Parser::name_stack_t(), diff_block);
-	mOutNodes.clear();
-}
-
-// go from a stack of names to a specific XML node
-LLXMLNodePtr LLXUIParser::getNode(const name_stack_t& stack)
-{
-	name_stack_t name_stack;
-	for (name_stack_t::const_iterator it = stack.begin();
-		it != stack.end();
-		++it)
-	{
-		if (!it->first.empty())
-		{
-			name_stack.push_back(*it);
-		}
-	}
-
-	LLXMLNodePtr out_node = mWriteRootNode;
-
-	name_stack_t::const_iterator next_it = name_stack.begin();
-	for (name_stack_t::const_iterator it = name_stack.begin();
-		it != name_stack.end();
-		it = next_it)
-	{
-		++next_it;
-		if (it->first.empty())
-		{
-			continue;
-		}
-
-		out_nodes_t::iterator found_it = mOutNodes.lower_bound(it->second);
-
-		// node with this name not yet written
-		if (found_it == mOutNodes.end() || mOutNodes.key_comp()(found_it->first, it->second))
-		{
-			// make an attribute if we are the last element on the name stack
-			bool is_attribute = next_it == name_stack.end();
-			LLXMLNodePtr new_node = new LLXMLNode(it->first.c_str(), is_attribute);
-			out_node->addChild(new_node);
-			mOutNodes.insert(found_it, std::make_pair(it->second, new_node));
-			out_node = new_node;
-		}
-		else
-		{
-			out_node = found_it->second;
-		}
-	}
-
-	return (out_node == mWriteRootNode ? LLXMLNodePtr(NULL) : out_node);
-}
-
-
-bool LLXUIParser::readBoolValue(void* val_ptr)
-{
-	S32 value;
-	bool success = mCurReadNode->getBoolValue(1, &value);
-	*((bool*)val_ptr) = (value != FALSE);
-	return success;
-}
-
-bool LLXUIParser::writeBoolValue(const void* val_ptr, const name_stack_t& stack)
-{
-	LLXMLNodePtr node = getNode(stack);
-	if (node.notNull())
-	{
-		node->setBoolValue(*((bool*)val_ptr));
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::readStringValue(void* val_ptr)
-{
-	*((std::string*)val_ptr) = mCurReadNode->getSanitizedValue();
-	return true;
-}
-
-bool LLXUIParser::writeStringValue(const void* val_ptr, const name_stack_t& stack)
-{
-	LLXMLNodePtr node = getNode(stack);
-	if (node.notNull())
-	{
-		const std::string* string_val = reinterpret_cast<const std::string*>(val_ptr);
-		if (string_val->find('\n') != std::string::npos 
-			|| string_val->size() > MAX_STRING_ATTRIBUTE_SIZE)
-		{
-			// don't write strings with newlines into attributes
-			std::string attribute_name = node->getName()->mString;
-			LLXMLNodePtr parent_node = node->mParent;
-			parent_node->deleteChild(node);
-			// write results in text contents of node
-			if (attribute_name == "value")
-			{
-				// "value" is implicit, just write to parent
-				node = parent_node;
-			}
-			else
-			{
-				// create a child that is not an attribute, but with same name
-				node = parent_node->createChild(attribute_name.c_str(), false);
-			}
-		}
-		node->setStringValue(*string_val);
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::readU8Value(void* val_ptr)
-{
-	return mCurReadNode->getByteValue(1, (U8*)val_ptr);
-}
-
-bool LLXUIParser::writeU8Value(const void* val_ptr, const name_stack_t& stack)
-{
-	LLXMLNodePtr node = getNode(stack);
-	if (node.notNull())
-	{
-		node->setUnsignedValue(*((U8*)val_ptr));
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::readS8Value(void* val_ptr)
-{
-	S32 value;
-	if(mCurReadNode->getIntValue(1, &value))
-	{
-		*((S8*)val_ptr) = value;
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::writeS8Value(const void* val_ptr, const name_stack_t& stack)
-{
-	LLXMLNodePtr node = getNode(stack);
-	if (node.notNull())
-	{
-		node->setIntValue(*((S8*)val_ptr));
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::readU16Value(void* val_ptr)
-{
-	U32 value;
-	if(mCurReadNode->getUnsignedValue(1, &value))
-	{
-		*((U16*)val_ptr) = value;
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::writeU16Value(const void* val_ptr, const name_stack_t& stack)
-{
-	LLXMLNodePtr node = getNode(stack);
-	if (node.notNull())
-	{
-		node->setUnsignedValue(*((U16*)val_ptr));
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::readS16Value(void* val_ptr)
-{
-	S32 value;
-	if(mCurReadNode->getIntValue(1, &value))
-	{
-		*((S16*)val_ptr) = value;
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::writeS16Value(const void* val_ptr, const name_stack_t& stack)
-{
-	LLXMLNodePtr node = getNode(stack);
-	if (node.notNull())
-	{
-		node->setIntValue(*((S16*)val_ptr));
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::readU32Value(void* val_ptr)
-{
-	return mCurReadNode->getUnsignedValue(1, (U32*)val_ptr);
-}
-
-bool LLXUIParser::writeU32Value(const void* val_ptr, const name_stack_t& stack)
-{
-	LLXMLNodePtr node = getNode(stack);
-	if (node.notNull())
-	{
-		node->setUnsignedValue(*((U32*)val_ptr));
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::readS32Value(void* val_ptr)
-{
-	return mCurReadNode->getIntValue(1, (S32*)val_ptr);
-}
-
-bool LLXUIParser::writeS32Value(const void* val_ptr, const name_stack_t& stack)
-{
-	LLXMLNodePtr node = getNode(stack);
-	if (node.notNull())
-	{
-		node->setIntValue(*((S32*)val_ptr));
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::readF32Value(void* val_ptr)
-{
-	return mCurReadNode->getFloatValue(1, (F32*)val_ptr);
-}
-
-bool LLXUIParser::writeF32Value(const void* val_ptr, const name_stack_t& stack)
-{
-	LLXMLNodePtr node = getNode(stack);
-	if (node.notNull())
-	{
-		node->setFloatValue(*((F32*)val_ptr));
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::readF64Value(void* val_ptr)
-{
-	return mCurReadNode->getDoubleValue(1, (F64*)val_ptr);
-}
-
-bool LLXUIParser::writeF64Value(const void* val_ptr, const name_stack_t& stack)
-{
-	LLXMLNodePtr node = getNode(stack);
-	if (node.notNull())
-	{
-		node->setDoubleValue(*((F64*)val_ptr));
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::readColor4Value(void* val_ptr)
-{
-	LLColor4* colorp = (LLColor4*)val_ptr;
-	if(mCurReadNode->getFloatValue(4, colorp->mV) >= 3)
-	{
-		return true;
-	}
-
-	return false;
-}
-
-bool LLXUIParser::writeColor4Value(const void* val_ptr, const name_stack_t& stack)
-{
-	LLXMLNodePtr node = getNode(stack);
-	if (node.notNull())
-	{
-		LLColor4 color = *((LLColor4*)val_ptr);
-		node->setFloatValue(4, color.mV);
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::readUIColorValue(void* val_ptr)
-{
-	LLUIColor* param = (LLUIColor*)val_ptr;
-	LLColor4 color;
-	bool success =  mCurReadNode->getFloatValue(4, color.mV) >= 3;
-	if (success)
-	{
-		param->set(color);
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::writeUIColorValue(const void* val_ptr, const name_stack_t& stack)
-{
-	LLXMLNodePtr node = getNode(stack);
-	if (node.notNull())
-	{
-		LLUIColor color = *((LLUIColor*)val_ptr);
-		//RN: don't write out the color that is represented by a function
-		// rely on param block exporting to get the reference to the color settings
-		if (color.isReference()) return false;
-		node->setFloatValue(4, color.get().mV);
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::readUUIDValue(void* val_ptr)
-{
-	LLUUID temp_id;
-	// LLUUID::set is destructive, so use temporary value
-	if (temp_id.set(mCurReadNode->getSanitizedValue()))
-	{
-		*(LLUUID*)(val_ptr) = temp_id;
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::writeUUIDValue(const void* val_ptr, const name_stack_t& stack)
-{
-	LLXMLNodePtr node = getNode(stack);
-	if (node.notNull())
-	{
-		node->setStringValue(((LLUUID*)val_ptr)->asString());
-		return true;
-	}
-	return false;
-}
-
-bool LLXUIParser::readSDValue(void* val_ptr)
-{
-	*((LLSD*)val_ptr) = LLSD(mCurReadNode->getSanitizedValue());
-	return true;
-}
-
-bool LLXUIParser::writeSDValue(const void* val_ptr, const name_stack_t& stack)
-{
-	LLXMLNodePtr node = getNode(stack);
-	if (node.notNull())
-	{
-		std::string string_val = ((LLSD*)val_ptr)->asString();
-		if (string_val.find('\n') != std::string::npos || string_val.size() > MAX_STRING_ATTRIBUTE_SIZE)
-		{
-			// don't write strings with newlines into attributes
-			std::string attribute_name = node->getName()->mString;
-			LLXMLNodePtr parent_node = node->mParent;
-			parent_node->deleteChild(node);
-			// write results in text contents of node
-			if (attribute_name == "value")
-			{
-				// "value" is implicit, just write to parent
-				node = parent_node;
-			}
-			else
-			{
-				node = parent_node->createChild(attribute_name.c_str(), false);
-			}
-		}
-
-		node->setStringValue(string_val);
-		return true;
-	}
-	return false;
-}
-
-/*virtual*/ std::string LLXUIParser::getCurrentElementName()
-{
-	std::string full_name;
-	for (name_stack_t::iterator it = mNameStack.begin();	
-		it != mNameStack.end();
-		++it)
-	{
-		full_name += it->first + "."; // build up dotted names: "button.param.nestedparam."
-	}
-
-	return full_name;
-}
-
-void LLXUIParser::parserWarning(const std::string& message)
-{
-#ifdef LL_WINDOWS
-	// use Visual Studo friendly formatting of output message for easy access to originating xml
-	llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", LLUICtrlFactory::getInstance()->getCurFileName().c_str(), mCurReadNode->getLineNumber(), message.c_str()).c_str());
-	utf16str += '\n';
-	OutputDebugString(utf16str.c_str());
-#else
-	Parser::parserWarning(message);
-#endif
-}
-
-void LLXUIParser::parserError(const std::string& message)
-{
-#ifdef LL_WINDOWS
-	llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", LLUICtrlFactory::getInstance()->getCurFileName().c_str(), mCurReadNode->getLineNumber(), message.c_str()).c_str());
-	utf16str += '\n';
-	OutputDebugString(utf16str.c_str());
-#else
-	Parser::parserError(message);
-#endif
-}
diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h
index b82feb3f58b156e90541be0ff2823dff38a715bf..e47010c3167f2da0d31db64b079682c87769fe32 100644
--- a/indra/llui/lluictrlfactory.h
+++ b/indra/llui/lluictrlfactory.h
@@ -35,9 +35,12 @@
 
 #include "llcallbackmap.h"
 #include "llinitparam.h"
+#include "llregistry.h"
 #include "llxmlnode.h"
 #include "llfasttimer.h"
 
+#include "llxuiparser.h"
+
 #include <boost/function.hpp>
 #include <iosfwd>
 #include <stack>
@@ -47,110 +50,6 @@ class LLPanel;
 class LLFloater;
 class LLView;
 
-class LLXSDWriter : public LLInitParam::Parser
-{
-	LOG_CLASS(LLXSDWriter);
-public:
-	void writeXSD(const std::string& name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace);
-
-	/*virtual*/ std::string getCurrentElementName() { return LLStringUtil::null; }
-
-	LLXSDWriter();
-
-protected:
-	void writeAttribute(const std::string& type, const Parser::name_stack_t&, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values);
-	void addAttributeToSchema(LLXMLNodePtr nodep, const std::string& attribute_name, const std::string& type, bool mandatory, const std::vector<std::string>* possible_values);
-	LLXMLNodePtr mAttributeNode;
-	LLXMLNodePtr mElementNode;
-	LLXMLNodePtr mSchemaNode;
-
-	typedef std::set<std::string> string_set_t;
-	typedef std::map<LLXMLNodePtr, string_set_t> attributes_map_t;
-	attributes_map_t	mAttributesWritten;
-};
-
-// NOTE: DOES NOT WORK YET
-// should support child widgets for XUI
-class LLXUIXSDWriter : public LLXSDWriter
-{
-public:
-	void writeXSD(const std::string& name, const std::string& path, const LLInitParam::BaseBlock& block);
-};
-
-class LLXUIParser : public LLInitParam::Parser, public LLSingleton<LLXUIParser>
-{
-LOG_CLASS(LLXUIParser);
-
-protected:
-	LLXUIParser();
-	friend class LLSingleton<LLXUIParser>;
-public:
-	typedef LLInitParam::Parser::name_stack_t name_stack_t;
-
-	/*virtual*/ std::string getCurrentElementName();
-	/*virtual*/ void parserWarning(const std::string& message);
-	/*virtual*/ void parserError(const std::string& message);
-
-	void readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, bool silent=false);
-	void writeXUI(LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const LLInitParam::BaseBlock* diff_block = NULL);
-
-private:
-	typedef std::list<std::pair<std::string, bool> >	token_list_t;
-
-	bool readXUIImpl(LLXMLNodePtr node, const std::string& scope, LLInitParam::BaseBlock& block);
-	bool readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block);
-
-	//reader helper functions
-	bool readBoolValue(void* val_ptr);
-	bool readStringValue(void* val_ptr);
-	bool readU8Value(void* val_ptr);
-	bool readS8Value(void* val_ptr);
-	bool readU16Value(void* val_ptr);
-	bool readS16Value(void* val_ptr);
-	bool readU32Value(void* val_ptr);
-	bool readS32Value(void* val_ptr);
-	bool readF32Value(void* val_ptr);
-	bool readF64Value(void* val_ptr);
-	bool readColor4Value(void* val_ptr);
-	bool readUIColorValue(void* val_ptr);
-	bool readUUIDValue(void* val_ptr);
-	bool readSDValue(void* val_ptr);
-
-	//writer helper functions
-	bool writeBoolValue(const void* val_ptr, const name_stack_t&);
-	bool writeStringValue(const void* val_ptr, const name_stack_t&);
-	bool writeU8Value(const void* val_ptr, const name_stack_t&);
-	bool writeS8Value(const void* val_ptr, const name_stack_t&);
-	bool writeU16Value(const void* val_ptr, const name_stack_t&);
-	bool writeS16Value(const void* val_ptr, const name_stack_t&);
-	bool writeU32Value(const void* val_ptr, const name_stack_t&);
-	bool writeS32Value(const void* val_ptr, const name_stack_t&);
-	bool writeF32Value(const void* val_ptr, const name_stack_t&);
-	bool writeF64Value(const void* val_ptr, const name_stack_t&);
-	bool writeColor4Value(const void* val_ptr, const name_stack_t&);
-	bool writeUIColorValue(const void* val_ptr, const name_stack_t&);
-	bool writeUUIDValue(const void* val_ptr, const name_stack_t&);
-	bool writeSDValue(const void* val_ptr, const name_stack_t&);
-
-	LLXMLNodePtr getNode(const name_stack_t& stack);
-
-private:
-	Parser::name_stack_t			mNameStack;
-	LLXMLNodePtr					mCurReadNode;
-	// Root of the widget XML sub-tree, for example, "line_editor"
-	LLXMLNodePtr					mWriteRootNode;
-	
-	typedef std::map<S32, LLXMLNodePtr>	out_nodes_t;
-	out_nodes_t						mOutNodes;
-	S32								mLastWriteGeneration;
-	LLXMLNodePtr					mLastWrittenChild;
-	S32								mCurReadDepth;
-};
-
-// global static instance for registering all widget types
-typedef boost::function<LLView* (LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node)> LLWidgetCreatorFunc;
-
-typedef LLRegistry<std::string, LLWidgetCreatorFunc> widget_registry_t;
 
 // sort functor for typeid maps
 struct LLCompareTypeID
@@ -192,11 +91,6 @@ class LLWidgetNameRegistry
 :	public LLRegistrySingleton<const std::type_info*, std::string, LLWidgetNameRegistry , LLCompareTypeID>
 {};
 
-// lookup widget type by name
-class LLWidgetTypeRegistry
-:	public LLRegistrySingleton<std::string, const std::type_info*, LLWidgetTypeRegistry>
-{};
-
 // lookup factory functions for default widget instances by widget type
 typedef LLView* (*dummy_widget_creator_func_t)(const std::string&);
 class LLDefaultWidgetRegistry
@@ -209,10 +103,6 @@ class LLDefaultParamBlockRegistry
 :	public LLRegistrySingleton<const std::type_info*, empty_param_block_func_t, LLDefaultParamBlockRegistry, LLCompareTypeID>
 {};
 
-class LLChildRegistryRegistry
-: public LLRegistrySingleton<const std::type_info*, widget_registry_t, LLChildRegistryRegistry>
-{};
-
 extern LLFastTimer::DeclareTimer FTM_WIDGET_SETUP;
 extern LLFastTimer::DeclareTimer FTM_WIDGET_CONSTRUCTION;
 extern LLFastTimer::DeclareTimer FTM_INIT_FROM_PARAMS;
diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp
index bf40353410664e254dd991fad756ad774042a0e6..ecda880c1ffd6d6274487413a29e0f4d7aa1b676 100644
--- a/indra/llwindow/llwindowsdl.cpp
+++ b/indra/llwindow/llwindowsdl.cpp
@@ -2362,8 +2362,10 @@ void LLWindowSDL::spawnWebBrowser(const std::string& escaped_url)
 # endif // LL_X11
 
 	std::string cmd, arg;
-	cmd  = gDirUtilp->getAppRODataDir().c_str();
-	cmd += gDirUtilp->getDirDelimiter().c_str();
+	cmd  = gDirUtilp->getAppRODataDir();
+	cmd += gDirUtilp->getDirDelimiter();
+	cmd += "etc";
+	cmd += gDirUtilp->getDirDelimiter();
 	cmd += "launch_url.sh";
 	arg = escaped_url;
 	exec_cmd(cmd, arg);
diff --git a/indra/llxuixml/CMakeLists.txt b/indra/llxuixml/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..daed4de6ceb9dc28ef5518dd0e17c11b17335808
--- /dev/null
+++ b/indra/llxuixml/CMakeLists.txt
@@ -0,0 +1,45 @@
+# -*- cmake -*-
+
+project(llxuixml)
+
+include(00-Common)
+include(LLCommon)
+include(LLMath)
+include(LLXML)
+
+include_directories(
+    ${LLCOMMON_INCLUDE_DIRS}
+    ${LLMATH_INCLUDE_DIRS}
+    ${LLXML_INCLUDE_DIRS}
+    )
+
+set(llxuixml_SOURCE_FILES
+    llinitparam.cpp
+    lltrans.cpp
+    lluicolor.cpp
+    llxuiparser.cpp
+    )
+    
+set(llxuixml_HEADER_FILES
+    CMakeLists.txt
+
+    llinitparam.h
+    lltrans.h
+    llregistry.h
+    lluicolor.h
+    llxuiparser.h
+    )
+
+set_source_files_properties(${llxuixml_HEADER_FILES}
+                            PROPERTIES HEADER_FILE_ONLY TRUE)
+
+list(APPEND llxuixml_SOURCE_FILES ${llxuixml_HEADER_FILES})
+
+add_library (llxuixml ${llxuixml_SOURCE_FILES})
+# Libraries on which this library depends, needed for Linux builds
+# Sort by high-level to low-level
+target_link_libraries(llxuixml
+    llxml
+    llcommon
+    llmath
+    )
diff --git a/indra/llxuixml/llinitparam.cpp b/indra/llxuixml/llinitparam.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d35e7b40f809e176d9d0bb4113aa77a2c6a65b14
--- /dev/null
+++ b/indra/llxuixml/llinitparam.cpp
@@ -0,0 +1,524 @@
+/** 
+ * @file llinitparam.cpp
+ * @brief parameter block abstraction for creating complex objects and 
+ * parsing construction parameters from xml and LLSD
+ *
+ * $LicenseInfo:firstyear=2008&license=viewergpl$
+ * 
+ * Copyright (c) 2008-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llinitparam.h"
+
+
+namespace LLInitParam
+{
+	BlockDescriptor BaseBlock::sBlockDescriptor;
+
+	//
+	// Param
+	//
+	Param::Param(BaseBlock* enclosing_block)
+	:	mIsProvided(false)
+	{
+		const U8* my_addr = reinterpret_cast<const U8*>(this);
+		const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block);
+		mEnclosingBlockOffset = (S16)(block_addr - my_addr);
+	}
+
+	//
+	// Parser
+	//
+	Parser::~Parser()
+	{}
+
+	void Parser::parserWarning(const std::string& message)
+	{
+		if (mParseSilently) return;
+		llwarns << message << llendl;
+	}
+	
+	void Parser::parserError(const std::string& message)
+	{
+		if (mParseSilently) return;
+		llerrs << message << llendl;
+	}
+
+
+	//
+	// BlockDescriptor
+	//
+	void BlockDescriptor::aggregateBlockData(BlockDescriptor& src_block_data) 
+	{
+		mNamedParams.insert(src_block_data.mNamedParams.begin(), src_block_data.mNamedParams.end());
+		mSynonyms.insert(src_block_data.mSynonyms.begin(), src_block_data.mSynonyms.end());
+		std::copy(src_block_data.mUnnamedParams.begin(), src_block_data.mUnnamedParams.end(), std::back_inserter(mUnnamedParams));
+		std::copy(src_block_data.mValidationList.begin(), src_block_data.mValidationList.end(), std::back_inserter(mValidationList));
+		std::copy(src_block_data.mAllParams.begin(), src_block_data.mAllParams.end(), std::back_inserter(mAllParams));
+	}
+
+	//
+	// BaseBlock
+	//
+	BaseBlock::BaseBlock()
+	:	mLastChangedParam(0),
+		mChangeVersion(0)
+	{}
+
+	BaseBlock::~BaseBlock()
+	{}
+
+	// called by each derived class in least to most derived order
+	void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size)
+	{
+		mBlockDescriptor = &descriptor;
+
+		descriptor.mCurrentBlockPtr = this;
+		descriptor.mMaxParamOffset = block_size;
+
+		switch(descriptor.mInitializationState)
+		{
+		case BlockDescriptor::UNINITIALIZED:
+			// copy params from base class here
+			descriptor.aggregateBlockData(base_descriptor);
+
+			descriptor.mInitializationState = BlockDescriptor::INITIALIZING;
+			break;
+		case BlockDescriptor::INITIALIZING:
+			descriptor.mInitializationState = BlockDescriptor::INITIALIZED;
+			break;
+		case BlockDescriptor::INITIALIZED:
+			// nothing to do
+			break;
+		}
+	}
+
+	param_handle_t BaseBlock::getHandleFromParam(const Param* param) const
+	{
+		const U8* param_address = reinterpret_cast<const U8*>(param);
+		const U8* baseblock_address = reinterpret_cast<const U8*>(this);
+		return (param_address - baseblock_address);
+	}
+
+	bool BaseBlock::submitValue(const Parser::name_stack_t& name_stack, Parser& p, bool silent)
+	{
+		if (!deserializeBlock(p, boost::make_iterator_range(name_stack.begin(), name_stack.end())))
+		{
+			if (!silent)
+			{
+				p.parserWarning(llformat("Failed to parse parameter \"%s\"", p.getCurrentElementName().c_str()));
+			}
+			return false;
+		}
+		return true;
+	}
+
+
+	bool BaseBlock::validateBlock(bool silent) const
+	{
+		const BlockDescriptor& block_data = getBlockDescriptor();
+		for (BlockDescriptor::param_validation_list_t::const_iterator it = block_data.mValidationList.begin(); it != block_data.mValidationList.end(); ++it)
+		{
+			const Param* param = getParamFromHandle(it->first);
+			if (!it->second(param))
+			{
+				if (!silent)
+				{
+					llwarns << "Invalid param \"" << getParamName(block_data, param) << "\"" << llendl;
+				}
+				return false;
+			}
+		}
+		return true;
+	}
+
+	bool BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t name_stack, const LLInitParam::BaseBlock* diff_block) const
+	{
+		// named param is one like LLView::Params::follows
+		// unnamed param is like LLView::Params::rect - implicit
+		const BlockDescriptor& block_data = getBlockDescriptor();
+
+		for (BlockDescriptor::param_list_t::const_iterator it = block_data.mUnnamedParams.begin(); 
+			it != block_data.mUnnamedParams.end(); 
+			++it)
+		{
+			param_handle_t param_handle = (*it)->mParamHandle;
+			const Param* param = getParamFromHandle(param_handle);
+			ParamDescriptor::serialize_func_t serialize_func = (*it)->mSerializeFunc;
+			if (serialize_func)
+			{
+				const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL;
+				// each param descriptor remembers its serial number
+				// so we can inspect the same param under different names
+				// and see that it has the same number
+				(*it)->mGeneration = parser.newParseGeneration();
+				name_stack.push_back(std::make_pair("", (*it)->mGeneration));
+				serialize_func(*param, parser, name_stack, diff_param);
+				name_stack.pop_back();
+			}
+		}
+
+		for(BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin();
+			it != block_data.mNamedParams.end();
+			++it)
+		{
+			param_handle_t param_handle = it->second->mParamHandle;
+			const Param* param = getParamFromHandle(param_handle);
+			ParamDescriptor::serialize_func_t serialize_func = it->second->mSerializeFunc;
+			if (serialize_func)
+			{
+				// Ensure this param has not already been serialized
+				// Prevents <rect> from being serialized as its own tag.
+				bool duplicate = false;
+				for (BlockDescriptor::param_list_t::const_iterator it2 = block_data.mUnnamedParams.begin(); 
+					it2 != block_data.mUnnamedParams.end(); 
+					++it2)
+				{
+					if (param_handle == (*it2)->mParamHandle)
+					{
+						duplicate = true;
+						break;
+					}
+				}
+
+				//FIXME: for now, don't attempt to serialize values under synonyms, as current parsers
+				// don't know how to detect them
+				if (duplicate) 
+				{
+					continue;
+				}
+
+				if (!duplicate)
+				{
+					it->second->mGeneration = parser.newParseGeneration();
+				}
+
+				name_stack.push_back(std::make_pair(it->first, it->second->mGeneration));
+				const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL;
+				serialize_func(*param, parser, name_stack, diff_param);
+				name_stack.pop_back();
+			}
+		}
+
+		return true;
+	}
+
+	bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack) const
+	{
+		// named param is one like LLView::Params::follows
+		// unnamed param is like LLView::Params::rect - implicit
+		const BlockDescriptor& block_data = getBlockDescriptor();
+
+		for (BlockDescriptor::param_list_t::const_iterator it = block_data.mUnnamedParams.begin(); 
+			it != block_data.mUnnamedParams.end(); 
+			++it)
+		{
+			param_handle_t param_handle = (*it)->mParamHandle;
+			const Param* param = getParamFromHandle(param_handle);
+			ParamDescriptor::inspect_func_t inspect_func = (*it)->mInspectFunc;
+			if (inspect_func)
+			{
+				(*it)->mGeneration = parser.newParseGeneration();
+				name_stack.push_back(std::make_pair("", (*it)->mGeneration));
+				inspect_func(*param, parser, name_stack, (*it)->mMinCount, (*it)->mMaxCount);
+				name_stack.pop_back();
+			}
+		}
+
+		for(BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin();
+			it != block_data.mNamedParams.end();
+			++it)
+		{
+			param_handle_t param_handle = it->second->mParamHandle;
+			const Param* param = getParamFromHandle(param_handle);
+			ParamDescriptor::inspect_func_t inspect_func = it->second->mInspectFunc;
+			if (inspect_func)
+			{
+				// Ensure this param has not already been inspected
+				bool duplicate = false;
+				for (BlockDescriptor::param_list_t::const_iterator it2 = block_data.mUnnamedParams.begin(); 
+					it2 != block_data.mUnnamedParams.end(); 
+					++it2)
+				{
+					if (param_handle == (*it2)->mParamHandle)
+					{
+						duplicate = true;
+						break;
+					}
+				}
+
+				if (!duplicate)
+				{
+					it->second->mGeneration = parser.newParseGeneration();
+				}
+				name_stack.push_back(std::make_pair(it->first, it->second->mGeneration));
+				inspect_func(*param, parser, name_stack, it->second->mMinCount, it->second->mMaxCount);
+				name_stack.pop_back();
+			}
+		}
+
+		for(BlockDescriptor::param_map_t::const_iterator it = block_data.mSynonyms.begin();
+			it != block_data.mSynonyms.end();
+			++it)
+		{
+			param_handle_t param_handle = it->second->mParamHandle;
+			const Param* param = getParamFromHandle(param_handle);
+			ParamDescriptor::inspect_func_t inspect_func = it->second->mInspectFunc;
+			if (inspect_func)
+			{
+				// use existing serial number for param
+				name_stack.push_back(std::make_pair(it->first, it->second->mGeneration));
+				inspect_func(*param, parser, name_stack, it->second->mMinCount, it->second->mMaxCount);
+				name_stack.pop_back();
+			}
+		}
+
+		return true;
+	}
+
+	bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack)
+	{
+		BlockDescriptor& block_data = getBlockDescriptor();
+		bool names_left = !name_stack.empty();
+
+		if (names_left)
+		{
+			const std::string& top_name = name_stack.front().first;
+
+			ParamDescriptor::deserialize_func_t deserialize_func = NULL;
+			Param* paramp = NULL;
+
+			BlockDescriptor::param_map_t::iterator found_it = block_data.mNamedParams.find(top_name);
+			if (found_it != block_data.mNamedParams.end())
+			{
+				// find pointer to member parameter from offset table
+				paramp = getParamFromHandle(found_it->second->mParamHandle);
+				deserialize_func = found_it->second->mDeserializeFunc;
+			}
+			else
+			{
+				BlockDescriptor::param_map_t::iterator found_it = block_data.mSynonyms.find(top_name);
+				if (found_it != block_data.mSynonyms.end())
+				{
+					// find pointer to member parameter from offset table
+					paramp = getParamFromHandle(found_it->second->mParamHandle);
+					deserialize_func = found_it->second->mDeserializeFunc;
+				}
+			}
+					
+			Parser::name_stack_range_t new_name_stack(++name_stack.begin(), name_stack.end());
+			if (deserialize_func)
+			{
+				return deserialize_func(*paramp, p, new_name_stack, name_stack.empty() ? -1 : name_stack.front().second);
+			}
+		}
+
+		// try to parse unnamed parameters, in declaration order
+		for ( BlockDescriptor::param_list_t::iterator it = block_data.mUnnamedParams.begin(); 
+			it != block_data.mUnnamedParams.end(); 
+			++it)
+		{
+			Param* paramp = getParamFromHandle((*it)->mParamHandle);
+			ParamDescriptor::deserialize_func_t deserialize_func = (*it)->mDeserializeFunc;
+
+			if (deserialize_func && deserialize_func(*paramp, p, name_stack, name_stack.empty() ? -1 : name_stack.front().second))
+			{
+				mLastChangedParam = (*it)->mParamHandle;
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	//static 
+	void BaseBlock::addParam(BlockDescriptor& block_data, const ParamDescriptor& in_param, const char* char_name)
+	{
+		// create a copy of the paramdescriptor in allparams
+		// so other data structures can store a pointer to it
+		block_data.mAllParams.push_back(in_param);
+		ParamDescriptor& param(block_data.mAllParams.back());
+
+		std::string name(char_name);
+		if ((size_t)param.mParamHandle > block_data.mMaxParamOffset)
+		{
+			llerrs << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block<YOUR_CLASS, PARAM_BLOCK_BASE_CLASS>" << llendl;
+		}
+
+		if (name.empty())
+		{
+			block_data.mUnnamedParams.push_back(&param);
+		}
+		else
+		{
+			// don't use insert, since we want to overwrite existing entries
+			block_data.mNamedParams[name] = &param;
+		}
+
+		if (param.mValidationFunc)
+		{
+			block_data.mValidationList.push_back(std::make_pair(param.mParamHandle, param.mValidationFunc));
+		}
+	}
+
+	void BaseBlock::addSynonym(Param& param, const std::string& synonym)
+	{
+		BlockDescriptor& block_data = getBlockDescriptor();
+		if (block_data.mInitializationState == BlockDescriptor::INITIALIZING)
+		{
+			param_handle_t handle = getHandleFromParam(&param);
+			
+			// check for invalid derivation from a paramblock (i.e. without using
+			// Block<T, Base_Class>
+			if ((size_t)handle > block_data.mMaxParamOffset)
+			{
+				llerrs << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block<YOUR_CLASS, PARAM_BLOCK_BASE_CLASS>" << llendl;
+			}
+
+			ParamDescriptor* param_descriptor = findParamDescriptor(handle);
+			if (param_descriptor)
+			{
+				if (synonym.empty())
+				{
+					block_data.mUnnamedParams.push_back(param_descriptor);
+				}
+				else
+				{
+					block_data.mSynonyms[synonym] = param_descriptor;
+				}
+			}
+		}
+	}
+
+	void BaseBlock::setLastChangedParam(const Param& last_param, bool user_provided)
+	{ 
+		mLastChangedParam = getHandleFromParam(&last_param); 
+		mChangeVersion++;
+	}
+
+	const std::string& BaseBlock::getParamName(const BlockDescriptor& block_data, const Param* paramp) const
+	{
+		param_handle_t handle = getHandleFromParam(paramp);
+		for (BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin(); it != block_data.mNamedParams.end(); ++it)
+		{
+			if (it->second->mParamHandle == handle)
+			{
+				return it->first;
+			}
+		}
+
+		for (BlockDescriptor::param_map_t::const_iterator it = block_data.mSynonyms.begin(); it != block_data.mSynonyms.end(); ++it)
+		{
+			if (it->second->mParamHandle == handle)
+			{
+				return it->first;
+			}
+		}
+
+		return LLStringUtil::null;
+	}
+
+	ParamDescriptor* BaseBlock::findParamDescriptor(param_handle_t handle)
+	{
+		BlockDescriptor& descriptor = getBlockDescriptor();
+		BlockDescriptor::all_params_list_t::iterator end_it = descriptor.mAllParams.end();
+		for (BlockDescriptor::all_params_list_t::iterator it = descriptor.mAllParams.begin();
+			it != end_it;
+			++it)
+		{
+			if (it->mParamHandle == handle) return &(*it);
+		}
+		return NULL;
+	}
+
+	// take all provided params from other and apply to self
+	// NOTE: this requires that "other" is of the same derived type as this
+	bool BaseBlock::overwriteFromImpl(BlockDescriptor& block_data, const BaseBlock& other)
+	{
+		bool param_changed = false;
+		BlockDescriptor::all_params_list_t::const_iterator end_it = block_data.mAllParams.end();
+		for (BlockDescriptor::all_params_list_t::const_iterator it = block_data.mAllParams.begin();
+			it != end_it;
+			++it)
+		{
+			const Param* other_paramp = other.getParamFromHandle(it->mParamHandle);
+			ParamDescriptor::merge_func_t merge_func = it->mMergeFunc;
+			if (merge_func)
+			{
+				Param* paramp = getParamFromHandle(it->mParamHandle);
+				param_changed |= merge_func(*paramp, *other_paramp, true);
+				mLastChangedParam = it->mParamHandle;
+			}
+		}
+		return param_changed;
+	}
+
+	// take all provided params that are not already provided, and apply to self
+	bool BaseBlock::fillFromImpl(BlockDescriptor& block_data, const BaseBlock& other)
+	{
+		bool param_changed = false;
+		BlockDescriptor::all_params_list_t::const_iterator end_it = block_data.mAllParams.end();
+		for (BlockDescriptor::all_params_list_t::const_iterator it = block_data.mAllParams.begin();
+			it != end_it;
+			++it)
+		{
+			const Param* other_paramp = other.getParamFromHandle(it->mParamHandle);
+			ParamDescriptor::merge_func_t merge_func = it->mMergeFunc;
+			if (merge_func)
+			{
+				Param* paramp = getParamFromHandle(it->mParamHandle);
+				param_changed |= merge_func(*paramp, *other_paramp, false);
+				mLastChangedParam = it->mParamHandle;
+			}
+		}
+		return param_changed;
+	}
+
+
+	template<>
+	bool ParamCompare<boost::function<void (const std::string &,void *)> >::equals(
+	   const boost::function<void (const std::string &,void *)> &a,
+	   const boost::function<void (const std::string &,void *)> &b)
+	{
+		return false;
+	}
+
+	template<>
+	bool ParamCompare<boost::function<void (const LLSD &,const LLSD &)> >::equals(
+	   const boost::function<void (const LLSD &,const LLSD &)> &a,
+	   const boost::function<void (const LLSD &,const LLSD &)> &b)
+	{
+		return false;
+	}
+
+	template<>
+	bool ParamCompare<LLSD>::equals(const LLSD &a, const LLSD &b)
+	{
+		return false;
+	}
+}
diff --git a/indra/llxuixml/llinitparam.h b/indra/llxuixml/llinitparam.h
new file mode 100644
index 0000000000000000000000000000000000000000..cb56049ae2fd7f3aefb7e2efad98f48f6a6c785c
--- /dev/null
+++ b/indra/llxuixml/llinitparam.h
@@ -0,0 +1,1822 @@
+/** 
+ * @file llinitparam.h
+ * @brief parameter block abstraction for creating complex objects and 
+ * parsing construction parameters from xml and LLSD
+ *
+ * $LicenseInfo:firstyear=2008&license=viewergpl$
+ * 
+ * Copyright (c) 2008-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPARAM_H
+#define LL_LLPARAM_H
+
+#include <vector>
+
+#include <stddef.h>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/range/iterator_range.hpp>
+#include "llregistry.h"
+#include "llmemory.h"
+
+
+namespace LLInitParam
+{
+    template <typename T> 
+    class ParamCompare {
+    public:
+    	static bool equals(const T &a, const T &b);
+    };
+    
+    template<class T>
+    bool ParamCompare<T>::equals(const T &a, const T&b)
+    {
+    	return a == b;
+    }
+
+
+	// default constructor adaptor for InitParam Values
+	// constructs default instances of the given type, returned by const reference
+	template <typename T>
+	struct DefaultInitializer
+	{
+		typedef const T&			T_const_ref;
+		// return reference to a single default instance of T
+		// built-in types will be initialized to zero, default constructor otherwise
+		static T_const_ref get() { static T t = T(); return t; } 
+	};
+
+	// helper functions and classes
+	typedef ptrdiff_t param_handle_t;
+
+	template <typename T>
+	class TypeValues
+	{
+	public:
+		// empty default implemenation of key cache
+		class KeyCache
+		{
+		public:
+			void setKey(const std::string& key) {}
+			std::string getKey() const { return ""; }
+			void clearKey(){}
+		};
+
+		static bool get(const std::string& name, T& value)
+		{
+			return false;
+		}
+
+		static bool empty()
+		{
+			return true;
+		}
+
+		static std::vector<std::string>* getPossibleValues() { return NULL; }
+	};
+
+	template <typename T, typename DERIVED_TYPE = TypeValues<T> >
+	class TypeValuesHelper
+	:	public LLRegistrySingleton<std::string, T, DERIVED_TYPE >
+	{
+		typedef LLRegistrySingleton<std::string, T, DERIVED_TYPE>	super_t;
+		typedef LLSingleton<DERIVED_TYPE>							singleton_t;
+	public:
+
+		//TODO: cache key by index to save on param block size
+		class KeyCache
+		{
+		public:
+			void setKey(const std::string& key) 
+			{
+				mKey = key; 
+			}
+
+			void clearKey()
+			{
+				mKey = "";
+			}
+
+			std::string getKey() const
+			{ 
+				return mKey; 
+			}
+
+		private:
+			std::string mKey;
+		};
+
+		static bool get(const std::string& name, T& value)
+		{
+			if (!singleton_t::instance().exists(name)) return false;
+
+			value = *singleton_t::instance().getValue(name);
+			return true;
+		}
+
+		static bool empty()
+		{
+			return singleton_t::instance().LLRegistry<std::string, T>::empty();
+		}
+	
+		//override this to add name value pairs
+		static void declareValues() {}
+	
+		void initSingleton()
+		{
+			DERIVED_TYPE::declareValues();
+		}
+
+		static const std::vector<std::string>* getPossibleValues() 
+		{ 
+			// in order to return a pointer to a member, we lazily
+			// evaluate the result and store it in mValues here
+			if (singleton_t::instance().mValues.empty())
+			{
+				typename super_t::Registrar::registry_map_t::const_iterator it;
+				for (it = super_t::defaultRegistrar().beginItems(); it != super_t::defaultRegistrar().endItems(); ++it)
+				{
+					singleton_t::instance().mValues.push_back(it->first);
+				}
+			}
+			return &singleton_t::instance().mValues; 
+		}
+
+
+	protected:
+		static void declare(const std::string& name, const T& value)
+		{
+			super_t::defaultRegistrar().add(name, value);
+		}
+
+	private:
+		std::vector<std::string> mValues;
+	};
+
+	class Parser
+	{
+		LOG_CLASS(Parser);
+
+	public:
+		
+		struct CompareTypeID
+		{
+			bool operator()(const std::type_info* lhs, const std::type_info* rhs) const
+			{
+				return lhs->before(*rhs);
+			}
+		};
+
+		typedef std::vector<std::pair<std::string, S32> >			name_stack_t;
+		typedef boost::iterator_range<name_stack_t::const_iterator>	name_stack_range_t;
+		typedef std::vector<std::string>							possible_values_t;
+
+		typedef boost::function<bool (void*)>															parser_read_func_t;
+		typedef boost::function<bool (const void*, const name_stack_t&)>								parser_write_func_t;
+		typedef boost::function<void (const name_stack_t&, S32, S32, const possible_values_t*)>	parser_inspect_func_t;
+
+		typedef std::map<const std::type_info*, parser_read_func_t, CompareTypeID>		parser_read_func_map_t;
+		typedef std::map<const std::type_info*, parser_write_func_t, CompareTypeID>		parser_write_func_map_t;
+		typedef std::map<const std::type_info*, parser_inspect_func_t, CompareTypeID>	parser_inspect_func_map_t;
+
+		Parser()
+		:	mParseSilently(false),
+			mParseGeneration(0)
+		{}
+		virtual ~Parser();
+
+		template <typename T> bool readValue(T& param)
+	    {
+		    parser_read_func_map_t::iterator found_it = mParserReadFuncs.find(&typeid(T));
+		    if (found_it != mParserReadFuncs.end())
+		    {
+			    return found_it->second((void*)&param);
+		    }
+		    return false;
+	    }
+
+		template <typename T> bool writeValue(const T& param, const name_stack_t& name_stack)
+		{
+		    parser_write_func_map_t::iterator found_it = mParserWriteFuncs.find(&typeid(T));
+		    if (found_it != mParserWriteFuncs.end())
+		    {
+			    return found_it->second((const void*)&param, name_stack);
+		    }
+		    return false;
+		}
+
+		// dispatch inspection to registered inspection functions, for each parameter in a param block
+		template <typename T> bool inspectValue(const name_stack_t& name_stack, S32 min_count, S32 max_count, const possible_values_t* possible_values)
+		{
+		    parser_inspect_func_map_t::iterator found_it = mParserInspectFuncs.find(&typeid(T));
+		    if (found_it != mParserInspectFuncs.end())
+		    {
+			    found_it->second(name_stack, min_count, max_count, possible_values);
+				return true;
+		    }
+			return false;
+		}
+
+		virtual std::string getCurrentElementName() = 0;
+		virtual void parserWarning(const std::string& message);
+		virtual void parserError(const std::string& message);
+		void setParseSilently(bool silent) { mParseSilently = silent; }
+		bool getParseSilently() { return mParseSilently; }
+
+		S32 getParseGeneration() { return mParseGeneration; }
+		S32 newParseGeneration() { return ++mParseGeneration; }
+
+
+	protected:
+		template <typename T>
+		void registerParserFuncs(parser_read_func_t read_func, parser_write_func_t write_func)
+		{
+			mParserReadFuncs.insert(std::make_pair(&typeid(T), read_func));
+			mParserWriteFuncs.insert(std::make_pair(&typeid(T), write_func));
+		}
+
+		template <typename T>
+		void registerInspectFunc(parser_inspect_func_t inspect_func)
+		{
+			mParserInspectFuncs.insert(std::make_pair(&typeid(T), inspect_func));
+		}
+
+		bool				mParseSilently;
+
+	private:
+		parser_read_func_map_t		mParserReadFuncs;
+		parser_write_func_map_t		mParserWriteFuncs;
+		parser_inspect_func_map_t	mParserInspectFuncs;
+		S32	mParseGeneration;
+	};
+
+	class BaseBlock;
+
+	class Param
+	{
+	public:
+		// public to allow choice blocks to clear provided flag on stale choices
+		void setProvided(bool is_provided) { mIsProvided = is_provided; }
+
+	protected:
+		bool getProvided() const { return mIsProvided; }
+
+		Param(class BaseBlock* enclosing_block);
+
+		// store pointer to enclosing block as offset to reduce space and allow for quick copying
+		BaseBlock& enclosingBlock() const
+		{ 
+			const U8* my_addr = reinterpret_cast<const U8*>(this);
+			// get address of enclosing BLOCK class using stored offset to enclosing BaseBlock class
+			return *const_cast<BaseBlock*>(
+							reinterpret_cast<const BaseBlock*>(my_addr + (ptrdiff_t)mEnclosingBlockOffset));
+		}
+
+	private:
+		friend class BaseBlock;
+
+		bool		mIsProvided;
+		S16			mEnclosingBlockOffset;
+	};
+
+	// various callbacks and constraints associated with an individual param
+	struct ParamDescriptor
+	{
+	public:
+		typedef bool(*merge_func_t)(Param&, const Param&, bool);
+		typedef bool(*deserialize_func_t)(Param&, Parser&, const Parser::name_stack_range_t&, S32);
+		typedef void(*serialize_func_t)(const Param&, Parser&, Parser::name_stack_t&, const Param* diff_param);
+		typedef void(*inspect_func_t)(const Param&, Parser&, Parser::name_stack_t&, S32 min_count, S32 max_count);
+		typedef bool(*validation_func_t)(const Param*);
+
+		ParamDescriptor(param_handle_t p, 
+						merge_func_t merge_func, 
+						deserialize_func_t deserialize_func, 
+						serialize_func_t serialize_func,
+						validation_func_t validation_func,
+						inspect_func_t inspect_func,
+						S32 min_count,
+						S32 max_count)
+		:	mParamHandle(p),
+			mMergeFunc(merge_func),
+			mDeserializeFunc(deserialize_func),
+			mSerializeFunc(serialize_func),
+			mValidationFunc(validation_func),
+			mInspectFunc(inspect_func),
+			mMinCount(min_count),
+			mMaxCount(max_count),
+			mNumRefs(0)
+		{}
+
+		ParamDescriptor()
+		:	mParamHandle(0),
+			mMergeFunc(NULL),
+			mDeserializeFunc(NULL),
+			mSerializeFunc(NULL),
+			mValidationFunc(NULL),
+			mInspectFunc(NULL),
+			mMinCount(0),
+			mMaxCount(0),
+			mGeneration(0),
+			mNumRefs(0)
+		{}
+
+		param_handle_t		mParamHandle;
+	
+		merge_func_t		mMergeFunc;
+		deserialize_func_t	mDeserializeFunc;
+		serialize_func_t	mSerializeFunc;
+		inspect_func_t		mInspectFunc;
+		validation_func_t	mValidationFunc;
+		S32					mMinCount;
+		S32					mMaxCount;
+		S32					mGeneration;
+		S32					mNumRefs;
+	};
+
+	// each derived Block class keeps a static data structure maintaining offsets to various params
+	class BlockDescriptor
+	{
+	public:
+		BlockDescriptor()
+		:	mMaxParamOffset(0),
+			mInitializationState(UNINITIALIZED)
+		{}
+
+		typedef enum e_initialization_state
+		{
+			UNINITIALIZED,
+			INITIALIZING,
+			INITIALIZED
+		} EInitializationState;
+
+		void aggregateBlockData(BlockDescriptor& src_block_data);
+
+	public:
+		typedef std::map<const std::string, ParamDescriptor*> param_map_t; // references param descriptors stored in mAllParams
+		typedef std::vector<ParamDescriptor*> param_list_t; 
+
+		typedef std::list<ParamDescriptor> all_params_list_t;// references param descriptors stored in mAllParams
+		typedef std::vector<std::pair<param_handle_t, ParamDescriptor::validation_func_t> > param_validation_list_t;
+
+		param_map_t						mNamedParams;			// parameters with associated names
+		param_map_t						mSynonyms;				// parameters with alternate names
+		param_list_t					mUnnamedParams;			// parameters with_out_ associated names
+		param_validation_list_t			mValidationList;		// parameters that must be validated
+		all_params_list_t				mAllParams;				// all parameters, owns descriptors
+
+		size_t					mMaxParamOffset;
+
+		EInitializationState	mInitializationState;	// whether or not static block data has been initialized
+		class BaseBlock*		mCurrentBlockPtr;		// pointer to block currently being constructed
+	};
+
+	class BaseBlock
+	{
+	public:
+		// this typedef identifies derived classes as being blocks
+		typedef void baseblock_base_class_t;
+		LOG_CLASS(BaseBlock);
+		friend class Param;
+
+		BaseBlock();
+		virtual ~BaseBlock();
+		bool submitValue(const Parser::name_stack_t& name_stack, Parser& p, bool silent=false);
+
+		param_handle_t getHandleFromParam(const Param* param) const;
+		bool validateBlock(bool silent = false) const;
+
+		Param* getParamFromHandle(const param_handle_t param_handle)
+		{
+			if (param_handle == 0) return NULL;
+			U8* baseblock_address = reinterpret_cast<U8*>(this);
+			return reinterpret_cast<Param*>(baseblock_address + param_handle);
+		}
+
+		const Param* getParamFromHandle(const param_handle_t param_handle) const
+		{
+			const U8* baseblock_address = reinterpret_cast<const U8*>(this);
+			return reinterpret_cast<const Param*>(baseblock_address + param_handle);
+		}
+
+		void addSynonym(Param& param, const std::string& synonym);
+
+		// Blocks can override this to do custom tracking of changes
+		virtual void setLastChangedParam(const Param& last_param, bool user_provided);
+
+		const Param* getLastChangedParam() const { return mLastChangedParam ? getParamFromHandle(mLastChangedParam) : NULL; }
+		S32 getLastChangeVersion() const { return mChangeVersion; }
+
+		bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack);
+		bool serializeBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), const BaseBlock* diff_block = NULL) const;
+		virtual bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t()) const;
+
+		const BlockDescriptor& getBlockDescriptor() const { return *mBlockDescriptor; }
+		BlockDescriptor& getBlockDescriptor() { return *mBlockDescriptor; }
+
+		// take all provided params from other and apply to self
+		bool overwriteFrom(const BaseBlock& other)
+		{
+			return false;
+		}
+
+		// take all provided params that are not already provided, and apply to self
+		bool fillFrom(const BaseBlock& other)
+		{
+			return false;
+		}
+
+		static void addParam(BlockDescriptor& block_data, const ParamDescriptor& param, const char* name);
+	protected:
+		void init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size);
+
+
+		// take all provided params from other and apply to self
+		bool overwriteFromImpl(BlockDescriptor& block_data, const BaseBlock& other);
+
+		// take all provided params that are not already provided, and apply to self
+		bool fillFromImpl(BlockDescriptor& block_data, const BaseBlock& other);
+
+		// can be updated in getters
+		mutable param_handle_t	mLastChangedParam;
+		mutable S32				mChangeVersion;
+
+		BlockDescriptor*		mBlockDescriptor;	// most derived block descriptor
+
+		static BlockDescriptor sBlockDescriptor;
+
+	private:
+		const std::string& getParamName(const BlockDescriptor& block_data, const Param* paramp) const;
+		ParamDescriptor* findParamDescriptor(param_handle_t handle);
+	};
+
+
+	template<typename T>
+	struct ParamIterator
+	{
+		typedef typename std::vector<T>::const_iterator		const_iterator;
+		typedef typename std::vector<T>::iterator			iterator;
+	};
+
+	// these templates allow us to distinguish between template parameters
+	// that derive from BaseBlock and those that don't
+	// this is supposedly faster than boost::is_convertible and the ilk
+	template<typename T, typename Void = void>
+	struct is_BaseBlock
+	: boost::false_type
+	{};
+
+	template<typename T>
+	struct is_BaseBlock<T, typename T::baseblock_base_class_t>
+	:	boost::true_type
+	{};
+
+	// specialize for custom parsing/decomposition of specific classes
+	// e.g. TypedParam<LLRect> has left, top, right, bottom, etc...
+	template<typename	T,
+			typename	NAME_VALUE_LOOKUP = TypeValues<T>,
+			bool		HAS_MULTIPLE_VALUES = false,
+			typename	VALUE_IS_BLOCK = typename is_BaseBlock<T>::type>
+	class TypedParam 
+	:	public Param
+	{
+	public:
+		typedef const T&																	value_const_ref_t;
+		typedef value_const_ref_t															value_assignment_t;
+		typedef typename NAME_VALUE_LOOKUP::KeyCache										key_cache_t;
+		typedef	TypedParam<T, NAME_VALUE_LOOKUP, HAS_MULTIPLE_VALUES, VALUE_IS_BLOCK>		self_t;
+
+		TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) 
+		:	Param(block_descriptor.mCurrentBlockPtr)
+		{
+			if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+			{
+				ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+												&mergeWith,
+												&deserializeParam,
+												&serializeParam,
+												validate_func,
+												&inspectParam,
+												min_count, max_count);
+				BaseBlock::addParam(block_descriptor, param_descriptor, name);
+			}
+
+			mData.mValue = value;
+		} 
+
+		bool isProvided() const { return Param::getProvided(); }
+
+		static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation) 
+		{ 
+			self_t& typed_param = static_cast<self_t&>(param);
+			// no further names in stack, attempt to parse value now
+			if (name_stack.empty())
+			{
+				if (parser.readValue<T>(typed_param.mData.mValue))
+				{
+					typed_param.setProvided(true);
+					typed_param.enclosingBlock().setLastChangedParam(param, true);
+					return true;
+				}
+				
+				// try to parse a known named value
+				if(!NAME_VALUE_LOOKUP::empty())
+				{
+					// try to parse a known named value
+					std::string name;
+					if (parser.readValue<std::string>(name))
+					{
+						// try to parse a per type named value
+						if (NAME_VALUE_LOOKUP::get(name, typed_param.mData.mValue))
+						{
+							typed_param.mData.setKey(name);
+							typed_param.setProvided(true);
+							typed_param.enclosingBlock().setLastChangedParam(param, true);
+							return true;
+						}
+
+					}
+				}
+			}
+			return false;
+		}
+
+		static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
+		{
+			const self_t& typed_param = static_cast<const self_t&>(param);
+			if (!typed_param.isProvided()) return;
+
+			if (!name_stack.empty())
+			{
+				name_stack.back().second = parser.newParseGeneration();
+			}
+
+			std::string key = typed_param.mData.getKey();
+
+			// first try to write out name of name/value pair
+
+			if (!key.empty())
+			{
+				if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->mData.getKey(), key))
+				{
+					if (!parser.writeValue<std::string>(key, name_stack))
+					{
+						return;
+					}
+				}
+			}
+			// then try to serialize value directly
+			else if (!diff_param || !ParamCompare<T>::equals(typed_param.get(), static_cast<const self_t*>(diff_param)->get()))					{
+				if (!parser.writeValue<T>(typed_param.mData.mValue, name_stack)) 
+				{
+					return;
+				}
+			}
+		}
+
+		static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+		{
+			// tell parser about our actual type
+			parser.inspectValue<T>(name_stack, min_count, max_count, NULL);
+			// then tell it about string-based alternatives ("red", "blue", etc. for LLColor4)
+			if (NAME_VALUE_LOOKUP::getPossibleValues())
+			{
+				parser.inspectValue<std::string>(name_stack, min_count, max_count, NAME_VALUE_LOOKUP::getPossibleValues());
+			}
+		}
+
+		void set(value_assignment_t val, bool flag_as_provided = true)
+		{
+			mData.mValue = val;
+			mData.clearKey();
+			setProvided(flag_as_provided);
+			Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
+		}
+
+		void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
+		{
+			if (!isProvided())
+			{
+				set(val, flag_as_provided);
+			}
+		}
+
+		// implicit conversion
+		operator value_assignment_t() const { return get(); } 
+		// explicit conversion
+		value_assignment_t operator()() const { return get(); } 
+
+	protected:
+		value_assignment_t get() const
+		{
+			return mData.mValue;
+		}
+
+		static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+		{
+			const self_t& src_typed_param = static_cast<const self_t&>(src);
+			self_t& dst_typed_param = static_cast<self_t&>(dst);
+			if (src_typed_param.isProvided()
+				&& (overwrite || !dst_typed_param.isProvided()))
+			{
+				dst_typed_param.mData.clearKey();
+				dst_typed_param = src_typed_param;
+				return true;
+			}
+			return false;
+		}
+
+		struct Data : public key_cache_t
+		{
+			T mValue;
+		};
+
+		Data		mData;
+	};
+
+	// parameter that is a block
+	template <typename T, typename NAME_VALUE_LOOKUP>
+	class TypedParam<T, NAME_VALUE_LOOKUP, false, boost::true_type> 
+	:	public T,
+		public Param
+	{
+	public:
+		typedef const T																			value_const_t;
+		typedef T																				value_t;
+		typedef value_const_t&																	value_const_ref_t;
+		typedef value_const_ref_t																value_assignment_t;
+		typedef typename NAME_VALUE_LOOKUP::KeyCache											key_cache_t;
+		typedef TypedParam<T, NAME_VALUE_LOOKUP, false, boost::true_type>						self_t;
+
+		TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
+		:	Param(block_descriptor.mCurrentBlockPtr),
+			T(value)
+		{
+			if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+			{
+				ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+												&mergeWith,
+												&deserializeParam,
+												&serializeParam,
+												validate_func, 
+												&inspectParam,
+												min_count, max_count);
+				BaseBlock::addParam(block_descriptor, param_descriptor, name);
+			}
+		}
+
+		static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation) 
+		{ 
+			self_t& typed_param = static_cast<self_t&>(param);
+			// attempt to parse block...
+			if(typed_param.deserializeBlock(parser, name_stack))
+			{
+				typed_param.enclosingBlock().setLastChangedParam(param, true);
+				return true;
+			}
+
+			if(!NAME_VALUE_LOOKUP::empty())
+			{
+				// try to parse a known named value
+				std::string name;
+				if (parser.readValue<std::string>(name))
+				{
+					// try to parse a per type named value
+					if (NAME_VALUE_LOOKUP::get(name, typed_param))
+					{
+						typed_param.enclosingBlock().setLastChangedParam(param, true);
+						typed_param.mData.setKey(name);
+						typed_param.mData.mKeyVersion = typed_param.getLastChangeVersion();
+						return true;
+					}
+
+				}
+			}
+			return false;
+		}
+
+		static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
+		{
+			const self_t& typed_param = static_cast<const self_t&>(param);
+			if (!name_stack.empty())
+			{
+				name_stack.back().second = parser.newParseGeneration();
+			}
+
+			std::string key = typed_param.mData.getKey();
+			if (!key.empty() && typed_param.mData.mKeyVersion == typed_param.getLastChangeVersion())
+			{
+				if (!parser.writeValue<std::string>(key, name_stack))
+				{
+					return;
+				}
+			}
+			else
+			{
+				typed_param.serializeBlock(parser, name_stack, static_cast<const self_t*>(diff_param));
+			}
+		}
+
+		static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+		{
+			// I am a param that is also a block, so just recurse into my contents
+			const self_t& typed_param = static_cast<const self_t&>(param);
+			typed_param.inspectBlock(parser, name_stack);
+		}
+
+		// a param-that-is-a-block is provided when the user has set one of its child params
+		// *and* the block as a whole validates
+		bool isProvided() const 
+		{ 
+			// only validate block when it hasn't already passed validation and user has supplied *some* value
+			if (Param::getProvided() && mData.mValidatedVersion < T::getLastChangeVersion())
+			{
+				// a sub-block is "provided" when it has been filled in enough to be valid
+				mData.mValidated = T::validateBlock(true);
+				mData.mValidatedVersion = T::getLastChangeVersion();
+			}
+			return Param::getProvided() && mData.mValidated;
+		}
+
+		// assign block contents to this param-that-is-a-block
+		void set(value_assignment_t val, bool flag_as_provided = true)
+		{
+			value_t::operator=(val);
+			mData.clearKey();
+			// force revalidation of block by clearing known provided version
+			// next call to isProvided() will update provision status based on validity
+			mData.mValidatedVersion = 0;
+			setProvided(flag_as_provided);
+			Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
+		}
+
+		void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
+		{
+			if (!isProvided())
+			{
+				set(val, flag_as_provided);
+			}
+		}
+
+		// propagate changed status up to enclosing block
+		/*virtual*/ void setLastChangedParam(const Param& last_param, bool user_provided)
+		{ 
+			T::setLastChangedParam(last_param, user_provided);
+			Param::enclosingBlock().setLastChangedParam(*this, user_provided);
+			if (user_provided)
+			{
+				// a child param has been explicitly changed
+				// so *some* aspect of this block is now provided
+				setProvided(true);
+			}
+		}
+
+		// implicit conversion
+		operator value_assignment_t() const { return get(); } 
+		// explicit conversion
+		value_assignment_t operator()() const { return get(); } 
+
+	protected:
+		value_assignment_t get() const
+		{
+			return *this;
+		}
+
+		static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+		{
+			const self_t& src_typed_param = static_cast<const self_t&>(src);
+			self_t& dst_typed_param = static_cast<self_t&>(dst);
+			if (overwrite)
+			{
+				if (dst_typed_param.T::overwriteFrom(src_typed_param))
+				{
+					dst_typed_param.mData.clearKey();
+					return true;
+				}
+			}
+			else
+			{
+				if (dst_typed_param.T::fillFrom(src_typed_param))
+				{			
+					dst_typed_param.mData.clearKey();
+					return true;
+				}
+			}
+			return false;
+		}
+
+		struct Data : public key_cache_t
+		{
+			S32 			mKeyVersion;
+			mutable S32 	mValidatedVersion;
+			mutable bool 	mValidated; // lazy validation flag
+
+			Data() 
+			:	mKeyVersion(0),
+				mValidatedVersion(0),
+				mValidated(false)
+			{}
+		};
+		Data	mData;
+	};
+
+	// container of non-block parameters
+	template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP>
+	class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::false_type> 
+	:	public Param
+	{
+	public:
+		typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::false_type>				self_t;
+		typedef typename std::vector<VALUE_TYPE>												container_t;
+		typedef const container_t&																value_assignment_t;
+
+		typedef VALUE_TYPE																		value_t;
+		typedef value_t&																		value_ref_t;
+		typedef const value_t&																	value_const_ref_t;
+		
+		typedef typename NAME_VALUE_LOOKUP::KeyCache											key_cache_t;
+
+		TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) 
+		:	Param(block_descriptor.mCurrentBlockPtr),
+			mValues(value)
+		{
+			mCachedKeys.resize(mValues.size());
+			if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+			{
+				ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+												&mergeWith,
+												&deserializeParam,
+												&serializeParam,
+												validate_func,
+												&inspectParam,
+												min_count, max_count);
+				BaseBlock::addParam(block_descriptor, param_descriptor, name);
+			}
+		} 
+
+		bool isProvided() const { return Param::getProvided(); }
+
+		static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation) 
+		{ 
+			self_t& typed_param = static_cast<self_t&>(param);
+			value_t value;
+			// no further names in stack, attempt to parse value now
+			if (name_stack.empty())
+			{
+				// attempt to read value directly
+				if (parser.readValue<value_t>(value))
+				{
+					typed_param.mValues.push_back(value);
+					// save an empty name/value key as a placeholder
+					typed_param.mCachedKeys.push_back(key_cache_t());
+					typed_param.enclosingBlock().setLastChangedParam(param, true);
+					typed_param.setProvided(true);
+					return true;
+				}
+				
+				// try to parse a known named value
+				if(!NAME_VALUE_LOOKUP::empty())
+				{
+					// try to parse a known named value
+					std::string name;
+					if (parser.readValue<std::string>(name))
+					{
+						// try to parse a per type named value
+						if (NAME_VALUE_LOOKUP::get(name, typed_param.mValues))
+						{
+							typed_param.mValues.push_back(value);
+							typed_param.mCachedKeys.push_back(key_cache_t());
+							typed_param.mCachedKeys.back().setKey(name);
+							typed_param.enclosingBlock().setLastChangedParam(param, true);
+							typed_param.setProvided(true);
+							return true;
+						}
+
+					}
+				}
+			}
+			return false;
+		}
+
+		static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
+		{
+			const self_t& typed_param = static_cast<const self_t&>(param);
+			if (!typed_param.isProvided() || name_stack.empty()) return;
+
+			typename container_t::const_iterator it = typed_param.mValues.begin();
+			for (typename std::vector<key_cache_t>::const_iterator key_it = typed_param.mCachedKeys.begin();
+				it != typed_param.mValues.end();
+				++key_it, ++it)
+			{
+				std::string key = key_it->get();
+				name_stack.back().second = parser.newParseGeneration();
+
+				if(!key.empty())
+				{
+					if(!parser.writeValue<std::string>(key, name_stack))
+					{
+						return;
+					}
+				}
+				// not parse via name values, write out value directly
+				else if (!parser.writeValue<VALUE_TYPE>(*it, name_stack))
+				{
+					return;
+				}
+			}
+		}
+
+		static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+		{
+			parser.inspectValue<VALUE_TYPE>(name_stack, min_count, max_count, NULL);
+			if (NAME_VALUE_LOOKUP::getPossibleValues())
+			{
+				parser.inspectValue<std::string>(name_stack, min_count, max_count, NAME_VALUE_LOOKUP::getPossibleValues());
+			}
+		}
+
+		void set(value_assignment_t val, bool flag_as_provided = true)
+		{
+			mValues = val;
+			mCachedKeys.clear();
+			mCachedKeys.resize(mValues.size());
+			setProvided(flag_as_provided);
+			Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
+		}
+
+
+		void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
+		{
+			if (!isProvided())
+			{
+				set(val, flag_as_provided);
+			}
+		}
+
+		value_ref_t add()
+		{
+			mValues.push_back(value_t());
+			mCachedKeys.push_back(key_cache_t());
+			setProvided(true);
+			return mValues.back();
+		}
+
+		void add(value_const_ref_t item)
+		{
+			mValues.push_back(item);
+			mCachedKeys.push_back(key_cache_t());
+			setProvided(true);
+		}
+
+		// implicit conversion
+		operator value_assignment_t() const { return self_t::get(); } 
+		// explicit conversion
+		value_assignment_t operator()() const { return get(); } 
+
+		bool hasNValidElements(S32 n) const
+		{
+			return mValues.size() >= n;
+		}
+
+	protected:
+		value_assignment_t get() const
+		{
+			return mValues;
+		}
+
+		static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+		{
+			const self_t& src_typed_param = static_cast<const self_t&>(src);
+			self_t& dst_typed_param = static_cast<self_t&>(dst);
+
+			if (src_typed_param.isProvided()
+				&& (overwrite || !isProvided()))
+			{
+				dst_typed_param = src_typed_param;
+				return true;
+			}
+			return false;
+		}
+
+		container_t		mValues;
+		std::vector<key_cache_t>	mCachedKeys;
+	};
+
+	// container of block parameters
+	template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP>
+	class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::true_type> 
+	:	public Param
+	{
+	public:
+		typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::true_type>				self_t;
+		typedef typename std::vector<VALUE_TYPE>												container_t;
+		typedef const container_t&																value_assignment_t;
+
+		typedef VALUE_TYPE																		value_t;
+		typedef value_t&																		value_ref_t;
+		typedef const value_t&																	value_const_ref_t;
+
+		typedef typename NAME_VALUE_LOOKUP::KeyCache											key_cache_t;
+
+		TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) 
+		:	Param(block_descriptor.mCurrentBlockPtr),
+			mValues(value),
+			mLastParamGeneration(0)
+		{
+			mCachedKeys.resize(mValues.size());
+			if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+			{
+				ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+												&mergeWith,
+												&deserializeParam,
+												&serializeParam,
+												validate_func,
+												&inspectParam,
+												min_count, max_count);
+				BaseBlock::addParam(block_descriptor, param_descriptor, name);
+			}
+		} 
+
+		bool isProvided() const { return Param::getProvided(); }
+
+		value_ref_t operator[](S32 index) { return mValues[index]; }
+		value_const_ref_t operator[](S32 index) const { return mValues[index]; }
+
+		static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation) 
+		{ 
+			self_t& typed_param = static_cast<self_t&>(param);
+			if (generation != typed_param.mLastParamGeneration || typed_param.mValues.empty())
+			{
+				typed_param.mValues.push_back(value_t());
+				typed_param.mCachedKeys.push_back(Data());
+				typed_param.enclosingBlock().setLastChangedParam(param, true);
+				typed_param.mLastParamGeneration = generation;
+			}
+
+			value_t& value = typed_param.mValues.back();
+
+			// attempt to parse block...
+			if(value.deserializeBlock(parser, name_stack))
+			{
+				typed_param.setProvided(true);
+				return true;
+			}
+
+			if(!NAME_VALUE_LOOKUP::empty())
+			{
+				// try to parse a known named value
+				std::string name;
+				if (parser.readValue<std::string>(name))
+				{
+					// try to parse a per type named value
+					if (NAME_VALUE_LOOKUP::get(name, value))
+					{
+						typed_param.mCachedKeys.back().setKey(name);
+						typed_param.mCachedKeys.back().mKeyVersion = value.getLastChangeVersion();
+						typed_param.enclosingBlock().setLastChangedParam(param, true);
+						typed_param.setProvided(true);
+						return true;
+					}
+
+				}
+			}
+
+			return false;
+		}
+
+		static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
+		{
+			const self_t& typed_param = static_cast<const self_t&>(param);
+			if (!typed_param.isProvided() || name_stack.empty()) return;
+
+			typename container_t::const_iterator it = typed_param.mValues.begin();
+			for (typename std::vector<Data>::const_iterator key_it = typed_param.mCachedKeys.begin();
+				it != typed_param.mValues.end();
+				++key_it, ++it)
+			{
+				name_stack.back().second = parser.newParseGeneration();
+
+				std::string key = key_it->getKey();
+				if (!key.empty() && key_it->mKeyVersion == it->getLastChangeVersion())
+				{
+					if(!parser.writeValue<std::string>(key, name_stack))
+					{
+						return;
+					}
+				}
+				// Not parsed via named values, write out value directly
+				// NOTE: currently we don't worry about removing default values in Multiple
+				else if (!it->serializeBlock(parser, name_stack, NULL))
+				{
+					return;
+				}
+			}
+		}
+
+		static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+		{
+			// I am a vector of blocks, so describe my contents recursively
+			value_t().inspectBlock(parser, name_stack);
+		}
+
+		void set(value_assignment_t val, bool flag_as_provided = true)
+		{
+			mValues = val;
+			mCachedKeys.clear();
+			mCachedKeys.resize(mValues.size());
+			setProvided(flag_as_provided);
+			Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
+		}
+
+		void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
+		{
+			if (!isProvided())
+			{
+				set(val, flag_as_provided);
+			}
+		}
+
+		value_ref_t add()
+		{
+			mValues.push_back(value_t());
+			mCachedKeys.push_back(Data());
+			setProvided(true);
+			return mValues.back();
+		}
+
+		void add(value_const_ref_t item)
+		{
+			mValues.push_back(item);
+			mCachedKeys.push_back(Data());
+			setProvided(true);
+		}
+
+		// implicit conversion
+		operator value_assignment_t() const { return self_t::get(); } 
+		// explicit conversion
+		value_assignment_t operator()() const { return get(); } 
+
+		U32 numValidElements() const
+		{
+			U32 count = 0;
+			for (typename container_t::const_iterator it = mValues.begin();
+				it != mValues.end();
+				++it)
+			{
+				if(it->validateBlock(true)) count++;
+			}
+			return count;
+		}
+
+	protected:
+		value_assignment_t get() const
+		{
+			return mValues;
+		}
+
+		static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+		{
+			const self_t& src_typed_param = static_cast<const self_t&>(src);
+			self_t& dst_typed_param = static_cast<self_t&>(dst);
+
+			if (src_typed_param.isProvided()
+				&& (overwrite || !dst_typed_param.isProvided()))
+			{
+				dst_typed_param = src_typed_param;
+				return true;
+			}
+			return false;
+		}
+
+		struct Data : public key_cache_t
+		{
+			S32 mKeyVersion;	// version of block for which key was last valid
+
+			Data() : mKeyVersion(0) {}
+		};
+
+		container_t			mValues;
+		std::vector<Data>	mCachedKeys;
+
+		S32			mLastParamGeneration;
+	};
+
+	template <typename DERIVED_BLOCK>
+	class Choice : public BaseBlock
+	{
+		typedef Choice<DERIVED_BLOCK> self_t;
+		typedef Choice<DERIVED_BLOCK> enclosing_block_t;
+		
+		LOG_CLASS(self_t);
+	public:
+		// take all provided params from other and apply to self
+		bool overwriteFrom(const self_t& other)
+		{
+			mCurChoice = other.mCurChoice;
+			return BaseBlock::overwriteFromImpl(sBlockDescriptor, other);
+		}
+
+		// take all provided params that are not already provided, and apply to self
+		bool fillFrom(const self_t& other)
+		{
+			return false;
+		}
+
+		// clear out old choice when param has changed
+		/*virtual*/ void setLastChangedParam(const Param& last_param, bool user_provided)
+		{ 
+			param_handle_t changed_param_handle = BaseBlock::getHandleFromParam(&last_param);
+			// if we have a new choice...
+			if (changed_param_handle != mCurChoice)
+			{
+				// clear provided flag on previous choice
+				Param* previous_choice = BaseBlock::getParamFromHandle(mCurChoice);
+				if (previous_choice) 
+				{
+					previous_choice->setProvided(false);
+				}
+				mCurChoice = changed_param_handle;
+			}
+			BaseBlock::setLastChangedParam(last_param, user_provided);
+		}
+
+	protected:
+		Choice()
+		:	mCurChoice(0)
+		{
+			BaseBlock::init(sBlockDescriptor, BaseBlock::sBlockDescriptor, sizeof(DERIVED_BLOCK));
+		}
+
+		// Alternatives are mutually exclusive wrt other Alternatives in the same block.  
+		// One alternative in a block will always have isChosen() == true.
+		// At most one alternative in a block will have isProvided() == true.
+		template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> >
+		class Alternative : public TypedParam<T, NAME_VALUE_LOOKUP, false>
+		{
+		public:
+			friend class Choice<DERIVED_BLOCK>;
+
+			typedef Alternative<T, NAME_VALUE_LOOKUP>						self_t;
+			typedef TypedParam<T, NAME_VALUE_LOOKUP, false>				super_t;
+			typedef typename super_t::value_assignment_t				value_assignment_t;
+
+			explicit Alternative(const char* name, value_assignment_t val = DefaultInitializer<T>::get())
+			:	super_t(DERIVED_BLOCK::sBlockDescriptor, name, val, NULL, 0, 1),
+				mOriginalValue(val)
+			{
+				// assign initial choice to first declared option
+				DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::sBlockDescriptor.mCurrentBlockPtr);
+				if (DERIVED_BLOCK::sBlockDescriptor.mInitializationState == BlockDescriptor::INITIALIZING
+					&& blockp->mCurChoice == 0)
+				{
+					blockp->mCurChoice = Param::enclosingBlock().getHandleFromParam(this);
+				}
+			}
+
+			Alternative& operator=(value_assignment_t val)
+			{
+				super_t::set(val);
+				return *this;
+			}
+
+			void operator()(typename super_t::value_assignment_t val) 
+			{ 
+				super_t::set(val);
+			}
+
+			operator value_assignment_t() const 
+			{ 
+				if (static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this)
+				{
+					return super_t::get(); 
+				}
+				return mOriginalValue;
+			} 
+
+			value_assignment_t operator()() const 
+			{ 
+				if (static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this)
+				{
+					return super_t::get(); 
+				}
+				return mOriginalValue;
+			} 
+
+			bool isChosen() const
+			{
+				return static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this;
+			}
+		
+		private:
+			T			mOriginalValue;
+		};
+
+	protected:
+		static BlockDescriptor sBlockDescriptor;
+
+	private:
+		param_handle_t	mCurChoice;
+
+		const Param* getCurrentChoice() const
+		{
+			return BaseBlock::getParamFromHandle(mCurChoice);
+		}
+	};
+
+	template<typename DERIVED_BLOCK> 
+		BlockDescriptor
+			Choice<DERIVED_BLOCK>::sBlockDescriptor;
+
+	//struct CardinalityConstraint
+	//{
+	//	virtual std::pair<S32, S32> getRange() = 0;
+	//};
+
+	struct AnyAmount
+	{
+		static U32 minCount() { return 0; }
+		static U32 maxCount() { return U32_MAX; }
+	};
+
+	template<U32 MIN_AMOUNT>
+	struct AtLeast
+	{
+		static U32 minCount() { return MIN_AMOUNT; }
+		static U32 maxCount() { return U32_MAX; }
+	};
+
+	template<U32 MAX_AMOUNT>
+	struct AtMost
+	{
+		static U32 minCount() { return 0; }
+		static U32 maxCount() { return MAX_AMOUNT; }
+	};
+
+	template<U32 MIN_AMOUNT, U32 MAX_AMOUNT>
+	struct Between
+	{
+		static U32 minCount() { return MIN_AMOUNT; }
+		static U32 maxCount() { return MAX_AMOUNT; }
+	};
+
+	template<U32 EXACT_COUNT>
+	struct Exactly
+	{
+		static U32 minCount() { return EXACT_COUNT; }
+		static U32 maxCount() { return EXACT_COUNT; }
+	};
+
+	template <typename DERIVED_BLOCK, typename BASE_BLOCK = BaseBlock>
+	class Block 
+	:	public BASE_BLOCK
+	{
+		typedef Block<DERIVED_BLOCK, BASE_BLOCK> self_t;
+		typedef Block<DERIVED_BLOCK, BASE_BLOCK> block_t;
+
+	public:
+		typedef BASE_BLOCK base_block_t;
+
+		// take all provided params from other and apply to self
+		bool overwriteFrom(const self_t& other)
+		{
+			return BaseBlock::overwriteFromImpl(sBlockDescriptor, other);
+		}
+
+		// take all provided params that are not already provided, and apply to self
+		bool fillFrom(const self_t& other)
+		{
+			return BaseBlock::fillFromImpl(sBlockDescriptor, other);
+		}
+	protected:
+		Block()
+		{
+			//#pragma message("Parsing LLInitParam::Block")
+			BaseBlock::init(sBlockDescriptor, BASE_BLOCK::sBlockDescriptor, sizeof(DERIVED_BLOCK));
+		}
+
+		//
+		// Nested classes for declaring parameters
+		//
+		template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> >
+		class Optional : public TypedParam<T, NAME_VALUE_LOOKUP, false>
+		{
+		public:
+			typedef TypedParam<T, NAME_VALUE_LOOKUP, false>				super_t;
+			typedef typename super_t::value_assignment_t				value_assignment_t;
+
+			explicit Optional(const char* name = "", value_assignment_t val = DefaultInitializer<T>::get())
+			:	super_t(DERIVED_BLOCK::sBlockDescriptor, name, val, NULL, 0, 1)
+			{
+				//#pragma message("Parsing LLInitParam::Block::Optional")
+			}
+
+			Optional& operator=(value_assignment_t val)
+			{
+				set(val);
+				return *this;
+			}
+
+			DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val)
+			{
+				super_t::set(val);
+				return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
+			}
+			using super_t::operator();
+		};
+
+		template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> >
+		class Mandatory : public TypedParam<T, NAME_VALUE_LOOKUP, false>
+		{
+		public:
+			typedef TypedParam<T, NAME_VALUE_LOOKUP, false>		super_t;
+			typedef Mandatory<T, NAME_VALUE_LOOKUP>				self_t;
+			typedef typename super_t::value_assignment_t		value_assignment_t;
+
+			// mandatory parameters require a name to be parseable
+			explicit Mandatory(const char* name = "", value_assignment_t val = DefaultInitializer<T>::get())
+			:	super_t(DERIVED_BLOCK::sBlockDescriptor, name, val, &validate, 1, 1)
+			{}
+
+			Mandatory& operator=(value_assignment_t val)
+			{
+				set(val);
+				return *this;
+			}
+
+			DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val)
+			{
+				super_t::set(val);
+				return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
+			}
+			using super_t::operator();
+
+			static bool validate(const Param* p)
+			{
+				// valid only if provided
+				return static_cast<const self_t*>(p)->isProvided();
+			}
+
+		};
+
+		template <typename T, typename RANGE = AnyAmount, typename NAME_VALUE_LOOKUP = TypeValues<T> >
+		class Multiple : public TypedParam<T, NAME_VALUE_LOOKUP, true>
+		{
+		public:
+			typedef TypedParam<T, NAME_VALUE_LOOKUP, true>	super_t;
+			typedef Multiple<T, RANGE, NAME_VALUE_LOOKUP>	self_t;
+			typedef typename super_t::container_t			container_t;
+			typedef typename super_t::value_assignment_t	value_assignment_t;
+			typedef typename container_t::iterator			iterator;
+			typedef typename container_t::const_iterator	const_iterator;
+
+			explicit Multiple(const char* name = "", value_assignment_t val = DefaultInitializer<container_t>::get())
+			:	super_t(DERIVED_BLOCK::sBlockDescriptor, name, val, &validate, RANGE::minCount(), RANGE::maxCount())
+			{}
+
+			using super_t::operator();
+
+			Multiple& operator=(value_assignment_t val)
+			{
+				set(val);
+				return *this;
+			}
+			
+			DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val)
+			{
+				super_t::set(val);
+				return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
+			}
+
+			static bool validate(const Param* paramp) 
+			{
+				U32 num_valid = ((super_t*)paramp)->numValidElements();
+				return RANGE::minCount() <= num_valid && num_valid <= RANGE::maxCount();
+			}
+		};
+
+		class Deprecated : public Param
+		{
+		public:
+			explicit Deprecated(const char* name)
+			:	Param(DERIVED_BLOCK::sBlockDescriptor.mCurrentBlockPtr)
+			{
+				BlockDescriptor& block_descriptor = DERIVED_BLOCK::sBlockDescriptor;
+				if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+				{
+					ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+													NULL,
+													&deserializeParam,
+													NULL,
+													NULL,
+													NULL, 
+													0, S32_MAX);
+					BaseBlock::addParam(block_descriptor, param_descriptor, name);
+				}
+			}
+			
+			static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
+			{
+				if (name_stack.empty())
+				{
+					//std::string message = llformat("Deprecated value %s ignored", getName().c_str());
+					//parser.parserWarning(message);
+					return true;
+				}
+
+				return false;
+			}
+		};
+
+		typedef Deprecated Ignored;
+
+	protected:
+		static BlockDescriptor sBlockDescriptor;
+	};
+	
+	template<typename DERIVED_BLOCK, typename BASE_BLOCK> 
+		BlockDescriptor
+			Block<DERIVED_BLOCK, BASE_BLOCK>::sBlockDescriptor;
+
+	template<typename T, typename DERIVED = TypedParam<T> >
+	class BlockValue
+	:	public Block<TypedParam<T, TypeValues<T>, false> >,
+		public Param
+	{
+	public:
+		typedef BlockValue<T>																self_t;
+		typedef Block<TypedParam<T, TypeValues<T>, false> >									block_t;
+		typedef const T&															value_const_ref_t;
+		typedef value_const_ref_t															value_assignment_t;
+		typedef typename TypeValues<T>::KeyCache											key_cache_t;
+
+		BlockValue(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
+		:	Param(block_descriptor.mCurrentBlockPtr),
+			mData(value)
+		{
+			if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+			{
+				ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+												&mergeWith,
+												&deserializeParam,
+												&serializeParam,
+												validate_func,
+												&inspectParam,
+												min_count, max_count);
+				BaseBlock::addParam(block_descriptor, param_descriptor, name);
+			}
+		}
+
+		// implicit conversion
+		operator value_assignment_t() const { return get(); } 
+		// explicit conversion
+		value_assignment_t operator()() const { return get(); } 
+
+		static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
+		{
+			self_t& typed_param = static_cast<self_t&>(param);
+			// type to apply parse direct value T
+			if (name_stack.empty())
+			{
+				if(parser.readValue<T>(typed_param.mData.mValue))
+				{
+					typed_param.enclosingBlock().setLastChangedParam(param, true);
+					typed_param.setProvided(true);
+					typed_param.mData.mLastParamVersion = typed_param.BaseBlock::getLastChangeVersion();
+					return true;
+				}
+
+				if(!TypeValues<T>::empty())
+				{
+					// try to parse a known named value
+					std::string name;
+					if (parser.readValue<std::string>(name))
+					{
+						// try to parse a per type named value
+						if (TypeValues<T>::get(name, typed_param.mData.mValue))
+						{
+							typed_param.mData.setKey(name);
+							typed_param.enclosingBlock().setLastChangedParam(param, true);
+							typed_param.setProvided(true);
+							typed_param.mData.mLastParamVersion = typed_param.BaseBlock::getLastChangeVersion();
+							return true;
+						}
+					}
+				}
+			}
+
+			// fall back on parsing block components for T
+			// if we deserialized at least one component...
+			if (typed_param.BaseBlock::deserializeBlock(parser, name_stack))
+			{
+				// ...our block is provided, and considered changed
+				typed_param.enclosingBlock().setLastChangedParam(param, true);
+				typed_param.setProvided(true);
+				return true;
+			}
+			return false;
+		}
+
+		static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
+		{
+			const self_t& typed_param = static_cast<const self_t&>(param);
+			
+			if (!typed_param.isProvided()) return;
+			
+			std::string key = typed_param.mData.getKey();
+
+			// first try to write out name of name/value pair
+			if (!key.empty())
+			{
+				if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->mData.getKey(), key))
+				{
+					if (!parser.writeValue<std::string>(key, name_stack))
+					{
+						return;
+					}
+				}
+			}
+			// then try to serialize value directly
+			else if (!diff_param || !ParamCompare<T>::equals(typed_param.get(), (static_cast<const self_t*>(diff_param))->get()))	
+            {
+				
+				if (parser.writeValue<T>(typed_param.mData.mValue, name_stack)) 
+				{
+					return;
+				}
+
+				//RN: *always* serialize provided components of BlockValue (don't pass diff_param on),
+				// since these tend to be viewed as the constructor arguments for the value T.  It seems
+				// cleaner to treat the uniqueness of a BlockValue according to the generated value, and
+				// not the individual components.  This way <color red="0" green="1" blue="0"/> will not
+				// be exported as <color green="1"/>, since it was probably the intent of the user to 
+				// be specific about the RGB color values.  This also fixes an issue where we distinguish
+				// between rect.left not being provided and rect.left being explicitly set to 0 (same as default)
+				typed_param.BaseBlock::serializeBlock(parser, name_stack, NULL);
+			}
+		}
+
+		static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+		{
+			// first, inspect with actual type...
+			parser.inspectValue<T>(name_stack, min_count, max_count, NULL);
+			if (TypeValues<T>::getPossibleValues())
+			{
+				//...then inspect with possible string values...
+				parser.inspectValue<std::string>(name_stack, min_count, max_count, TypeValues<T>::getPossibleValues());
+			}
+			// then recursively inspect contents...
+			const self_t& typed_param = static_cast<const self_t&>(param);
+			typed_param.inspectBlock(parser, name_stack);
+		}
+
+
+		bool isProvided() const 
+		{
+			// either param value provided directly or block is sufficiently filled in
+			// if cached value is stale, regenerate from params
+			if (Param::getProvided() && mData.mLastParamVersion < BaseBlock::getLastChangeVersion())
+			{
+				if (block_t::validateBlock(true))
+				{
+					mData.mValue = static_cast<const DERIVED*>(this)->getValueFromBlock();
+					// clear stale keyword associated with old value
+					mData.clearKey();
+					mData.mLastParamVersion = BaseBlock::getLastChangeVersion();
+					return true;
+				}
+				else
+				{
+					//block value incomplete, so not considered provided
+					// will attempt to revalidate on next call to isProvided()
+					return false;  
+				}
+			}
+			// either no data provided, or we have a valid value in hand
+			return Param::getProvided();
+		}
+
+		void set(value_assignment_t val, bool flag_as_provided = true)
+		{
+			Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
+			// set param version number to be up to date, so we ignore block contents
+			mData.mLastParamVersion = BaseBlock::getLastChangeVersion();
+
+			mData.mValue = val;
+			mData.clearKey();
+			setProvided(flag_as_provided);
+		}
+
+		void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
+		{
+			// don't override any user provided value
+			if (!isProvided())
+			{
+				set(val, flag_as_provided);
+			}
+		}
+
+ 		// propagate change status up to enclosing block
+		/*virtual*/ void setLastChangedParam(const Param& last_param, bool user_provided)
+		{ 
+			BaseBlock::setLastChangedParam(last_param, user_provided);
+			Param::enclosingBlock().setLastChangedParam(*this, user_provided);
+			if (user_provided)
+			{
+				setProvided(true);  // some component provided
+			}
+		}
+
+	protected:
+		value_assignment_t get() const
+		{
+			if (mData.mLastParamVersion < BaseBlock::getLastChangeVersion() && block_t::validateBlock(true))
+			{
+				mData.mValue = static_cast<const DERIVED*>(this)->getValueFromBlock();
+				mData.clearKey();
+				mData.mLastParamVersion = BaseBlock::getLastChangeVersion();
+			}
+
+			return mData.mValue;
+		}
+
+		// mutable to allow lazy updates on get
+		struct Data : public key_cache_t
+		{
+			Data(const T& value) 
+			:	mValue(value),
+				mLastParamVersion(0)
+			{}
+
+			T		mValue;
+			S32		mLastParamVersion;
+		};
+
+		mutable Data		mData;
+
+	private:
+		static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+		{
+			const self_t& src_param = static_cast<const self_t&>(src);
+			self_t& dst_typed_param = static_cast<self_t&>(dst);
+
+			if (src_param.isProvided()
+				&& (overwrite || !dst_typed_param.isProvided()))
+			{
+				// assign individual parameters
+				if (overwrite)
+				{
+					dst_typed_param.BaseBlock::overwriteFromImpl(block_t::sBlockDescriptor, src_param);
+				}
+				else
+				{
+					dst_typed_param.BaseBlock::fillFromImpl(block_t::sBlockDescriptor, src_param);
+				}
+				// then copy actual value
+				dst_typed_param.mData.mValue = src_param.get();
+				dst_typed_param.mData.clearKey();
+				dst_typed_param.setProvided(true);
+				return true;
+			}
+			return false;
+		}
+	};
+
+    template<>
+	bool ParamCompare<boost::function<void (const std::string &,void *)> >::equals(
+		const boost::function<void (const std::string &,void *)> &a,
+		const boost::function<void (const std::string &,void *)> &b);
+	
+	template<>
+	bool ParamCompare<boost::function<void (const LLSD &,const LLSD &)> >::equals(
+		const boost::function<void (const LLSD &,const LLSD &)> &a,
+		const boost::function<void (const LLSD &,const LLSD &)> &b);
+	
+	template<>
+	bool ParamCompare<LLSD>::equals(const LLSD &a, const LLSD &b);
+}
+
+#endif // LL_LLPARAM_H
diff --git a/indra/llxuixml/llregistry.h b/indra/llxuixml/llregistry.h
new file mode 100644
index 0000000000000000000000000000000000000000..2c04d8c41977b36e69813da0f0e2fb827760f940
--- /dev/null
+++ b/indra/llxuixml/llregistry.h
@@ -0,0 +1,347 @@
+/** 
+ * @file llregistry.h
+ * @brief template classes for registering name, value pairs in nested scopes, statically, etc.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ * 
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLREGISTRY_H
+#define LL_LLREGISTRY_H
+
+#include <list>
+
+#include <boost/type_traits.hpp>
+#include "llsingleton.h"
+
+template <typename T>
+class LLRegistryDefaultComparator
+{
+	bool operator()(const T& lhs, const T& rhs) { return lhs < rhs; }
+};
+
+template <typename KEY, typename VALUE, typename COMPARATOR = LLRegistryDefaultComparator<KEY> >
+class LLRegistry
+{
+public:
+	typedef LLRegistry<KEY, VALUE, COMPARATOR>											registry_t;
+	typedef typename boost::add_reference<typename boost::add_const<KEY>::type>::type	ref_const_key_t;
+	typedef typename boost::add_reference<typename boost::add_const<VALUE>::type>::type	ref_const_value_t;
+	typedef typename boost::add_reference<VALUE>::type									ref_value_t;
+	typedef typename boost::add_pointer<typename boost::add_const<VALUE>::type>::type	ptr_const_value_t;
+	typedef typename boost::add_pointer<VALUE>::type									ptr_value_t;
+
+	class Registrar
+	{
+		friend class LLRegistry<KEY, VALUE, COMPARATOR>;
+	public:
+		typedef typename std::map<KEY, VALUE> registry_map_t;
+
+		bool add(ref_const_key_t key, ref_const_value_t value)
+		{
+			if (mMap.insert(std::make_pair(key, value)).second == false)
+			{
+				llwarns << "Tried to register " << key << " but it was already registered!" << llendl;
+				return false;
+			}
+			return true;
+		}
+
+		void remove(ref_const_key_t key)
+		{
+			mMap.erase(key);
+		}
+
+		typename registry_map_t::const_iterator beginItems() const
+		{
+			return mMap.begin();
+		}
+
+		typename registry_map_t::const_iterator endItems() const
+		{
+			return mMap.end();
+		}
+
+	protected:
+		ptr_value_t getValue(ref_const_key_t key)
+		{
+			typename registry_map_t::iterator found_it = mMap.find(key);
+			if (found_it != mMap.end())
+			{
+				return &(found_it->second);
+			}
+			return NULL;
+		}
+
+		ptr_const_value_t getValue(ref_const_key_t key) const
+		{
+			typename registry_map_t::const_iterator found_it = mMap.find(key);
+			if (found_it != mMap.end())
+			{
+				return &(found_it->second);
+			}
+			return NULL;
+		}
+
+		// if the registry is used to store pointers, and null values are valid entries
+		// then use this function to check the existence of an entry
+		bool exists(ref_const_key_t key) const
+		{
+			return mMap.find(key) != mMap.end();
+		}
+
+		bool empty() const
+		{
+			return mMap.empty();
+		}
+
+	protected:
+		// use currentRegistrar() or defaultRegistrar()
+		Registrar() {}
+		~Registrar() {}
+
+	private:
+		registry_map_t											mMap;
+	};
+	
+	typedef typename std::list<Registrar*> scope_list_t;
+	typedef typename std::list<Registrar*>::iterator scope_list_iterator_t;
+	typedef typename std::list<Registrar*>::const_iterator scope_list_const_iterator_t;
+	
+	LLRegistry() 
+	{}
+
+	~LLRegistry() {}
+
+	ptr_value_t getValue(ref_const_key_t key)
+	{
+		for(scope_list_iterator_t it = mActiveScopes.begin();
+			it != mActiveScopes.end();
+			++it)
+		{
+			ptr_value_t valuep = (*it)->getValue(key);
+			if (valuep != NULL) return valuep;
+		}
+		return mDefaultRegistrar.getValue(key);
+	}
+
+	ptr_const_value_t getValue(ref_const_key_t key) const
+	{
+		for(scope_list_const_iterator_t it = mActiveScopes.begin();
+			it != mActiveScopes.end();
+			++it)
+		{
+			ptr_value_t valuep = (*it)->getValue(key);
+			if (valuep != NULL) return valuep;
+		}
+		return mDefaultRegistrar.getValue(key);
+	}
+
+	bool exists(ref_const_key_t key) const
+	{
+		for(scope_list_const_iterator_t it = mActiveScopes.begin();
+			it != mActiveScopes.end();
+			++it)
+		{
+			if ((*it)->exists(key)) return true;
+		}
+
+		return mDefaultRegistrar.exists(key);
+	}
+
+	bool empty() const
+	{
+		for(scope_list_const_iterator_t it = mActiveScopes.begin();
+			it != mActiveScopes.end();
+			++it)
+		{
+			if (!(*it)->empty()) return false;
+		}
+
+		return mDefaultRegistrar.empty();
+	}
+
+
+	Registrar& defaultRegistrar()
+	{
+		return mDefaultRegistrar;
+	}
+
+	const Registrar& defaultRegistrar() const
+	{
+		return mDefaultRegistrar;
+	}
+
+
+	Registrar& currentRegistrar()
+	{
+		if (!mActiveScopes.empty()) 
+		{
+			return *mActiveScopes.front();
+		}
+
+		return mDefaultRegistrar;
+	}
+
+	const Registrar& currentRegistrar() const
+	{
+		if (!mActiveScopes.empty()) 
+		{
+			return *mActiveScopes.front();
+		}
+
+		return mDefaultRegistrar;
+	}
+
+
+protected:
+	void addScope(Registrar* scope)
+	{
+		// newer scopes go up front
+		mActiveScopes.insert(mActiveScopes.begin(), scope);
+	}
+
+	void removeScope(Registrar* scope)
+	{
+		// O(N) but should be near the beggining and N should be small and this is safer than storing iterators
+		scope_list_iterator_t iter = std::find(mActiveScopes.begin(), mActiveScopes.end(), scope);
+		if (iter != mActiveScopes.end())
+		{
+			mActiveScopes.erase(iter);
+		}
+	}
+
+private:
+	scope_list_t	mActiveScopes;
+	Registrar		mDefaultRegistrar;
+};
+
+template <typename KEY, typename VALUE, typename DERIVED_TYPE, typename COMPARATOR = LLRegistryDefaultComparator<KEY> >
+class LLRegistrySingleton
+	:	public LLRegistry<KEY, VALUE, COMPARATOR>,
+		public LLSingleton<DERIVED_TYPE>
+{
+	friend class LLSingleton<DERIVED_TYPE>;
+public:
+	typedef LLRegistry<KEY, VALUE, COMPARATOR>		registry_t;
+	typedef const KEY&								ref_const_key_t;
+	typedef const VALUE&							ref_const_value_t;
+	typedef VALUE*									ptr_value_t;
+	typedef const VALUE*							ptr_const_value_t;
+	typedef LLSingleton<DERIVED_TYPE>				singleton_t;
+
+	class ScopedRegistrar : public registry_t::Registrar
+	{
+	public:
+		ScopedRegistrar(bool push_scope = true) 
+		{
+			if (push_scope)
+			{
+				pushScope();
+			}
+		}
+
+		~ScopedRegistrar()
+		{
+			if (!singleton_t::destroyed())
+			{
+				popScope();
+			}
+		}
+
+		void pushScope()
+		{
+			singleton_t::instance().addScope(this);
+		}
+		
+		void popScope()
+		{
+			singleton_t::instance().removeScope(this);
+		}
+		
+		ptr_value_t getValueFromScope(ref_const_key_t key)
+		{
+			return getValue(key);
+		}
+
+		ptr_const_value_t getValueFromScope(ref_const_key_t key) const
+		{
+			return getValue(key);
+		}
+
+	private:
+		typename std::list<typename registry_t::Registrar*>::iterator	mListIt;
+	};
+
+	class StaticRegistrar : public registry_t::Registrar
+	{
+	public:
+		virtual ~StaticRegistrar() {}
+		StaticRegistrar(ref_const_key_t key, ref_const_value_t value)
+		{
+			singleton_t::instance().mStaticScope->add(key, value);
+		}
+	};
+
+	// convenience functions
+	typedef typename LLRegistry<KEY, VALUE, COMPARATOR>::Registrar& ref_registrar_t;
+	static ref_registrar_t currentRegistrar()
+	{
+		return singleton_t::instance().registry_t::currentRegistrar();
+	}
+
+	static ref_registrar_t defaultRegistrar()
+	{
+		return singleton_t::instance().registry_t::defaultRegistrar();
+	}
+	
+	static ptr_value_t getValue(ref_const_key_t key)
+	{
+		return singleton_t::instance().registry_t::getValue(key);
+	}
+
+protected:
+	// DERIVED_TYPE needs to derive from LLRegistrySingleton
+	LLRegistrySingleton()
+		: mStaticScope(NULL)
+	{}
+
+	virtual void initSingleton()
+	{
+		mStaticScope = new ScopedRegistrar();
+	}
+
+	virtual ~LLRegistrySingleton() 
+	{
+		delete mStaticScope;
+	}
+
+private:
+	ScopedRegistrar*	mStaticScope;
+};
+
+#endif
diff --git a/indra/llxuixml/lltrans.cpp b/indra/llxuixml/lltrans.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..db7421575ce6c7420d8ef12035315aba14394932
--- /dev/null
+++ b/indra/llxuixml/lltrans.cpp
@@ -0,0 +1,171 @@
+/**
+ * @file lltrans.cpp
+ * @brief LLTrans implementation
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ * 
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltrans.h"
+
+#include "llfasttimer.h"	// for call count statistics
+#include "llxuiparser.h"
+
+#include <map>
+
+LLTrans::template_map_t LLTrans::sStringTemplates;
+LLStringUtil::format_map_t LLTrans::sDefaultArgs;
+
+struct StringDef : public LLInitParam::Block<StringDef>
+{
+	Mandatory<std::string> name;
+	Mandatory<std::string> value;
+
+	StringDef()
+	:	name("name"),
+		value("value")
+	{}
+};
+
+struct StringTable : public LLInitParam::Block<StringTable>
+{
+	Multiple<StringDef> strings;
+	StringTable()
+	:	strings("string")
+	{}
+};
+
+//static 
+bool LLTrans::parseStrings(LLXMLNodePtr &root, const std::set<std::string>& default_args)
+{
+	std::string xml_filename = "(strings file)";
+	if (!root->hasName("strings"))
+	{
+		llerrs << "Invalid root node name in " << xml_filename 
+			<< ": was " << root->getName() << ", expected \"strings\"" << llendl;
+	}
+
+	StringTable string_table;
+	LLXUIParser::instance().readXUI(root, string_table);
+
+	if (!string_table.validateBlock())
+	{
+		llerrs << "Problem reading strings: " << xml_filename << llendl;
+		return false;
+	}
+	
+	sStringTemplates.clear();
+	sDefaultArgs.clear();
+	
+	for(LLInitParam::ParamIterator<StringDef>::const_iterator it = string_table.strings().begin();
+		it != string_table.strings().end();
+		++it)
+	{
+		LLTransTemplate xml_template(it->name, it->value);
+		sStringTemplates[xml_template.mName] = xml_template;
+		
+		std::set<std::string>::const_iterator iter = default_args.find(xml_template.mName);
+		if (iter != default_args.end())
+		{
+			std::string name = *iter;
+			if (name[0] != '[')
+				name = llformat("[%s]",name.c_str());
+			sDefaultArgs[name] = xml_template.mText;
+		}
+	}
+
+	return true;
+}
+
+
+//static
+bool LLTrans::parseLanguageStrings(LLXMLNodePtr &root)
+{
+	std::string xml_filename = "(language strings file)";
+	if (!root->hasName("strings"))
+	{
+		llerrs << "Invalid root node name in " << xml_filename 
+		<< ": was " << root->getName() << ", expected \"strings\"" << llendl;
+	}
+	
+	StringTable string_table;
+	LLXUIParser::instance().readXUI(root, string_table);
+	
+	if (!string_table.validateBlock())
+	{
+		llerrs << "Problem reading strings: " << xml_filename << llendl;
+		return false;
+	}
+		
+	for(LLInitParam::ParamIterator<StringDef>::const_iterator it = string_table.strings().begin();
+		it != string_table.strings().end();
+		++it)
+	{
+		// share the same map with parseStrings() so we can search the strings using the same getString() function.- angela
+		LLTransTemplate xml_template(it->name, it->value);
+		sStringTemplates[xml_template.mName] = xml_template;
+	}
+	
+	return true;
+}
+
+
+
+static LLFastTimer::DeclareTimer FTM_GET_TRANS("Translate string");
+
+//static 
+std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::format_map_t& msg_args)
+{
+	// Don't care about time as much as call count.  Make sure we're not
+	// calling LLTrans::getString() in an inner loop. JC
+	LLFastTimer timer(FTM_GET_TRANS);
+	
+	template_map_t::iterator iter = sStringTemplates.find(xml_desc);
+	if (iter != sStringTemplates.end())
+	{
+		std::string text = iter->second.mText;
+		LLStringUtil::format_map_t args = sDefaultArgs;
+		args.insert(msg_args.begin(), msg_args.end());
+		LLStringUtil::format(text, args);
+		
+		return text;
+	}
+	else
+	{
+		LLSD args;
+		args["STRING_NAME"] = xml_desc;
+		LL_WARNS_ONCE("configuration") << "Missing String in strings.xml: [" << xml_desc << "]" << LL_ENDL;
+
+		//LLNotifications::instance().add("MissingString", args); // *TODO: resurrect
+		//return xml_desc;
+
+		return "MissingString("+xml_desc+")";
+	}
+}
+
diff --git a/indra/llxuixml/lltrans.h b/indra/llxuixml/lltrans.h
new file mode 100644
index 0000000000000000000000000000000000000000..6423c882459ee3f3f262be26024856351ae3a18f
--- /dev/null
+++ b/indra/llxuixml/lltrans.h
@@ -0,0 +1,111 @@
+/**
+ * @file lltrans.h
+ * @brief LLTrans definition
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ * 
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_TRANS_H
+#define LL_TRANS_H
+
+#include <map>
+
+#include "llstring.h"
+#include "llxmlnode.h"
+
+/**
+ * @brief String template loaded from strings.xml
+ */
+class LLTransTemplate
+{
+public:
+	LLTransTemplate(const std::string& name = LLStringUtil::null, const std::string& text = LLStringUtil::null) : mName(name), mText(text) {}
+
+	std::string mName;
+	std::string mText;
+};
+
+/**
+ * @brief Localized strings class
+ * This class is used to retrieve translations of strings used to build larger ones, as well as
+ * strings with a general usage that don't belong to any specific floater. For example,
+ * "Owner:", "Retrieving..." used in the place of a not yet known name, etc.
+ */
+class LLTrans
+{
+public:
+	LLTrans();
+
+	/**
+	 * @brief Parses the xml root that holds the strings. Used once on startup
+// *FIXME	 * @param xml_filename Filename to parse
+	 * @param default_args Set of strings (expected to be in the file) to use as default replacement args, e.g. "SECOND_LIFE"
+	 * @returns true if the file was parsed successfully, true if something went wrong
+	 */
+	static bool parseStrings(LLXMLNodePtr& root, const std::set<std::string>& default_args);
+
+	static bool parseLanguageStrings(LLXMLNodePtr &root);
+
+	/**
+	 * @brief Returns a translated string
+	 * @param xml_desc String's description
+	 * @param args A list of substrings to replace in the string
+	 * @returns Translated string
+	 */
+	static std::string getString(const std::string &xml_desc, const LLStringUtil::format_map_t& args);
+
+	/**
+	 * @brief Returns a translated string
+	 * @param xml_desc String's description
+	 * @returns Translated string
+	 */
+	static std::string getString(const std::string &xml_desc)
+	{
+		LLStringUtil::format_map_t empty;
+		return getString(xml_desc, empty);
+	}
+	
+	// get the default args
+	static const LLStringUtil::format_map_t& getDefaultArgs()
+	{
+		return sDefaultArgs;
+	}
+
+	// insert default args into an arg list
+	static void getArgs(LLStringUtil::format_map_t& args)
+	{
+		args.insert(sDefaultArgs.begin(), sDefaultArgs.end());
+	}
+	
+private:
+	typedef std::map<std::string, LLTransTemplate > template_map_t;
+	static template_map_t sStringTemplates;
+	static LLStringUtil::format_map_t sDefaultArgs;
+};
+
+#endif
diff --git a/indra/llxuixml/lluicolor.cpp b/indra/llxuixml/lluicolor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ef0fa5d63424599a4bbe108981e039d91621fbda
--- /dev/null
+++ b/indra/llxuixml/lluicolor.cpp
@@ -0,0 +1,71 @@
+/** 
+ * @file lluicolor.cpp
+ * @brief brief LLUIColor class implementation file
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#include "lluicolor.h"
+
+LLUIColor::LLUIColor()
+	:mColorPtr(NULL)
+{
+}
+
+LLUIColor::LLUIColor(const LLColor4* color)
+	:mColorPtr(color)
+{
+}
+
+LLUIColor::LLUIColor(const LLColor4& color)
+	:mColor(color), mColorPtr(NULL)
+{
+}
+
+void LLUIColor::set(const LLColor4& color)
+{
+	mColor = color;
+	mColorPtr = NULL;
+}
+
+void LLUIColor::set(const LLColor4* color)
+{
+	mColorPtr = color;
+}
+
+const LLColor4& LLUIColor::get() const
+{
+	return (mColorPtr == NULL ? mColor : *mColorPtr);
+}
+
+LLUIColor::operator const LLColor4& () const
+{
+	return get();
+}
+
+const LLColor4& LLUIColor::operator()() const
+{
+	return get();
+}
+
+bool LLUIColor::isReference() const
+{
+	return mColorPtr != NULL;
+}
+
+namespace LLInitParam
+{
+	// used to detect equivalence with default values on export
+	template<>
+	class ParamCompare<LLUIColor>
+	{
+	public:
+		static bool equals(const LLUIColor &a, const LLUIColor &b)
+		{
+			// do not detect value equivalence, treat pointers to colors as distinct from color values
+			return (a.mColorPtr == NULL && b.mColorPtr == NULL ? a.mColor == b.mColor : a.mColorPtr == b.mColorPtr);
+		}
+	};
+}
diff --git a/indra/llxuixml/lluicolor.h b/indra/llxuixml/lluicolor.h
new file mode 100644
index 0000000000000000000000000000000000000000..365f61003b49c2c120b640e6a661ae73c36f272a
--- /dev/null
+++ b/indra/llxuixml/lluicolor.h
@@ -0,0 +1,45 @@
+/** 
+ * @file lluicolor.h
+ * @brief brief LLUIColor class header file
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLUICOLOR_H_
+#define LL_LLUICOLOR_H_
+
+#include "v4color.h"
+
+namespace LLInitParam
+{
+	template<typename T>
+	class ParamCompare;
+}
+
+class LLUIColor
+{
+public:
+	LLUIColor();
+	LLUIColor(const LLColor4* color);
+	LLUIColor(const LLColor4& color);
+
+	void set(const LLColor4& color);
+	void set(const LLColor4* color);
+
+	const LLColor4& get() const;
+
+	operator const LLColor4& () const;
+	const LLColor4& operator()() const;
+
+	bool isReference() const;
+
+private:
+	friend class LLInitParam::ParamCompare<LLUIColor>;
+
+	const LLColor4* mColorPtr;
+	LLColor4 mColor;
+};
+
+#endif
diff --git a/indra/llxuixml/llxuiparser.cpp b/indra/llxuixml/llxuiparser.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e1f61906e20eca02c8eeead82ee78efd0f057473
--- /dev/null
+++ b/indra/llxuixml/llxuiparser.cpp
@@ -0,0 +1,968 @@
+/** 
+ * @file llxuiparser.cpp
+ * @brief Utility functions for handling XUI structures in XML
+ *
+ * $LicenseInfo:firstyear=2003&license=viewergpl$
+ * 
+ * Copyright (c) 2003-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llxuiparser.h"
+
+#include <fstream>
+#include <boost/tokenizer.hpp>
+
+#include "lluicolor.h"
+
+const S32 MAX_STRING_ATTRIBUTE_SIZE = 40;
+
+//
+// LLXSDWriter
+//
+LLXSDWriter::LLXSDWriter()
+{
+	registerInspectFunc<bool>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:boolean", _1, _2, _3, _4));
+	registerInspectFunc<std::string>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+	registerInspectFunc<U8>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedByte", _1, _2, _3, _4));
+	registerInspectFunc<S8>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedByte", _1, _2, _3, _4));
+	registerInspectFunc<U16>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedShort", _1, _2, _3, _4));
+	registerInspectFunc<S16>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedShort", _1, _2, _3, _4));
+	registerInspectFunc<U32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedInt", _1, _2, _3, _4));
+	registerInspectFunc<S32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:integer", _1, _2, _3, _4));
+	registerInspectFunc<F32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:float", _1, _2, _3, _4));
+	registerInspectFunc<F64>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:double", _1, _2, _3, _4));
+	registerInspectFunc<LLColor4>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+	registerInspectFunc<LLUIColor>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+	registerInspectFunc<LLUUID>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+	registerInspectFunc<LLSD>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+}
+
+void LLXSDWriter::writeXSD(const std::string& type_name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace)
+{
+	mSchemaNode = node;
+	node->setName("xs:schema");
+	node->createChild("attributeFormDefault", true)->setStringValue("unqualified");
+	node->createChild("elementFormDefault", true)->setStringValue("qualified");
+	node->createChild("targetNamespace", true)->setStringValue(xml_namespace);
+	node->createChild("xmlns:xs", true)->setStringValue("http://www.w3.org/2001/XMLSchema");
+	node->createChild("xmlns", true)->setStringValue(xml_namespace);
+
+	node = node->createChild("xs:complexType", false);
+	node->createChild("name", true)->setStringValue(type_name);
+	node->createChild("mixed", true)->setStringValue("true");
+
+	mAttributeNode = node;
+	mElementNode = node->createChild("xs:choice", false);
+	mElementNode->createChild("minOccurs", true)->setStringValue("0");
+	mElementNode->createChild("maxOccurs", true)->setStringValue("unbounded");
+	block.inspectBlock(*this);
+
+	// duplicate element choices
+	LLXMLNodeList children;
+	mElementNode->getChildren("xs:element", children, FALSE);
+	for (LLXMLNodeList::iterator child_it = children.begin(); child_it != children.end(); ++child_it)
+	{
+		LLXMLNodePtr child_copy = child_it->second->deepCopy();
+		std::string child_name;
+		child_copy->getAttributeString("name", child_name);
+		child_copy->setAttributeString("name", type_name + "." + child_name);
+		mElementNode->addChild(child_copy);
+	}
+
+	LLXMLNodePtr element_declaration_node = mSchemaNode->createChild("xs:element", false);
+	element_declaration_node->createChild("name", true)->setStringValue(type_name);
+	element_declaration_node->createChild("type", true)->setStringValue(type_name);
+}
+
+void LLXSDWriter::writeAttribute(const std::string& type, const Parser::name_stack_t& stack, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values)
+{
+	name_stack_t non_empty_names;
+	std::string attribute_name;
+	for (name_stack_t::const_iterator it = stack.begin();
+		it != stack.end();
+		++it)
+	{
+		const std::string& name = it->first;
+		if (!name.empty())
+		{
+			non_empty_names.push_back(*it);
+		}
+	}
+
+	for (name_stack_t::const_iterator it = non_empty_names.begin();
+		it != non_empty_names.end();
+		++it)
+	{
+		if (!attribute_name.empty())
+		{
+			attribute_name += ".";
+		}
+		attribute_name += it->first;
+	}
+
+	// only flag non-nested attributes as mandatory, nested attributes have variant syntax
+	// that can't be properly constrained in XSD
+	// e.g. <foo mandatory.value="bar"/> vs <foo><mandatory value="bar"/></foo>
+	bool attribute_mandatory = min_count == 1 && max_count == 1 && non_empty_names.size() == 1;
+
+	// don't bother supporting "Multiple" params as xml attributes
+	if (max_count <= 1)
+	{
+		// add compound attribute to root node
+		addAttributeToSchema(mAttributeNode, attribute_name, type, attribute_mandatory, possible_values);
+	}
+
+	// now generated nested elements for compound attributes
+	if (non_empty_names.size() > 1 && !attribute_mandatory)
+	{
+		std::string element_name;
+
+		// traverse all but last element, leaving that as an attribute name
+		name_stack_t::const_iterator end_it = non_empty_names.end();
+		end_it--;
+
+		for (name_stack_t::const_iterator it = non_empty_names.begin();
+			it != end_it;
+			++it)
+		{
+			if (it != non_empty_names.begin())
+			{
+				element_name += ".";
+			}
+			element_name += it->first;
+		}
+
+		std::string short_attribute_name = non_empty_names.back().first;
+
+		LLXMLNodePtr complex_type_node;
+
+		// find existing element node here, starting at tail of child list
+		if (mElementNode->mChildren.notNull())
+		{
+			for(LLXMLNodePtr element = mElementNode->mChildren->tail;
+				element.notNull(); 
+				element = element->mPrev)
+			{
+				std::string name;
+				if(element->getAttributeString("name", name) && name == element_name)
+				{
+					complex_type_node = element->mChildren->head;
+					break;
+				}
+			}
+		}
+		//create complex_type node
+		//
+		//<xs:element
+        //    maxOccurs="1"
+        //    minOccurs="0"
+        //    name="name">
+        //       <xs:complexType>
+        //       </xs:complexType>
+        //</xs:element>
+		if(complex_type_node.isNull())
+		{
+			complex_type_node = mElementNode->createChild("xs:element", false);
+
+			complex_type_node->createChild("minOccurs", true)->setIntValue(min_count);
+			complex_type_node->createChild("maxOccurs", true)->setIntValue(max_count);
+			complex_type_node->createChild("name",		true)->setStringValue(element_name);
+			complex_type_node = complex_type_node->createChild("xs:complexType", false);
+		}
+
+		addAttributeToSchema(complex_type_node, short_attribute_name, type, false, possible_values);
+	}
+}
+
+void LLXSDWriter::addAttributeToSchema(LLXMLNodePtr type_declaration_node, const std::string& attribute_name, const std::string& type, bool mandatory, const std::vector<std::string>* possible_values)
+{
+	if (!attribute_name.empty())
+	{
+		LLXMLNodePtr new_enum_type_node;
+		if (possible_values != NULL)
+		{
+			// custom attribute type, for example
+			//<xs:simpleType>
+			 // <xs:restriction
+			 //    base="xs:string">
+			 //     <xs:enumeration
+			 //      value="a" />
+			 //     <xs:enumeration
+			 //      value="b" />
+			 //   </xs:restriction>
+			 // </xs:simpleType>
+			new_enum_type_node = new LLXMLNode("xs:simpleType", false);
+
+			LLXMLNodePtr restriction_node = new_enum_type_node->createChild("xs:restriction", false);
+			restriction_node->createChild("base", true)->setStringValue("xs:string");
+
+			for (std::vector<std::string>::const_iterator it = possible_values->begin();
+				it != possible_values->end();
+				++it)
+			{
+				LLXMLNodePtr enum_node = restriction_node->createChild("xs:enumeration", false);
+				enum_node->createChild("value", true)->setStringValue(*it);
+			}
+		}
+
+		string_set_t& attributes_written = mAttributesWritten[type_declaration_node];
+
+		string_set_t::iterator found_it = attributes_written.lower_bound(attribute_name);
+
+		// attribute not yet declared
+		if (found_it == attributes_written.end() || attributes_written.key_comp()(attribute_name, *found_it))
+		{
+			attributes_written.insert(found_it, attribute_name);
+
+			LLXMLNodePtr attribute_node = type_declaration_node->createChild("xs:attribute", false);
+
+			// attribute name
+			attribute_node->createChild("name", true)->setStringValue(attribute_name);
+
+			if (new_enum_type_node.notNull())
+			{
+				attribute_node->addChild(new_enum_type_node);
+			}
+			else
+			{
+				// simple attribute type
+				attribute_node->createChild("type", true)->setStringValue(type);
+			}
+
+			// required or optional
+			attribute_node->createChild("use", true)->setStringValue(mandatory ? "required" : "optional");
+		}
+		 // attribute exists...handle collision of same name attributes with potentially different types
+		else
+		{
+			LLXMLNodePtr attribute_declaration;
+			if (type_declaration_node.notNull())
+			{
+				for(LLXMLNodePtr node = type_declaration_node->mChildren->tail; 
+					node.notNull(); 
+					node = node->mPrev)
+				{
+					std::string name;
+					if (node->getAttributeString("name", name) && name == attribute_name)
+					{
+						attribute_declaration = node;
+						break;
+					}
+				}
+			}
+
+			bool new_type_is_enum = new_enum_type_node.notNull();
+			bool existing_type_is_enum = !attribute_declaration->hasAttribute("type");
+
+			// either type is enum, revert to string in collision
+			// don't bother to check for enum equivalence
+			if (new_type_is_enum || existing_type_is_enum)
+			{
+				if (attribute_declaration->hasAttribute("type"))
+				{
+					attribute_declaration->setAttributeString("type", "xs:string");
+				}
+				else
+				{
+					attribute_declaration->createChild("type", true)->setStringValue("xs:string");
+				}
+				attribute_declaration->deleteChildren("xs:simpleType");
+			}
+			else 
+			{
+				// check for collision of different standard types
+				std::string existing_type;
+				attribute_declaration->getAttributeString("type", existing_type);
+				// if current type is not the same as the new type, revert to strnig
+				if (existing_type != type)
+				{
+					// ...than use most general type, string
+					attribute_declaration->setAttributeString("type", "string");
+				}
+			}
+		}
+	}
+}
+
+//
+// LLXUIXSDWriter
+//
+void LLXUIXSDWriter::writeXSD(const std::string& type_name, const std::string& path, const LLInitParam::BaseBlock& block)
+{
+	std::string file_name(path);
+	file_name += type_name + ".xsd";
+	LLXMLNodePtr root_nodep = new LLXMLNode();
+
+	LLXSDWriter::writeXSD(type_name, root_nodep, block, "http://www.lindenlab.com/xui");
+
+	// add includes for all possible children
+	const std::type_info* type = *LLWidgetTypeRegistry::instance().getValue(type_name);
+	const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type);
+	
+	// add include declarations for all valid children
+	for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
+		it != widget_registryp->currentRegistrar().endItems();
+		++it)
+	{
+		std::string widget_name = it->first;
+		if (widget_name == type_name)
+		{
+			continue;
+		}
+		LLXMLNodePtr nodep = new LLXMLNode("xs:include", false);
+		nodep->createChild("schemaLocation", true)->setStringValue(widget_name + ".xsd");
+
+		// add to front of schema
+		mSchemaNode->addChild(nodep, mSchemaNode);
+	}
+
+	// add choices for valid children
+	if (widget_registryp)
+	{
+		for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
+			it != widget_registryp->currentRegistrar().endItems();
+			++it)
+		{
+			std::string widget_name = it->first;
+            //<xs:element name="widget_name" type="widget_name">
+			LLXMLNodePtr widget_node = mElementNode->createChild("xs:element", false);
+			widget_node->createChild("name", true)->setStringValue(widget_name);
+			widget_node->createChild("type", true)->setStringValue(widget_name);
+		}
+	}
+
+	LLFILE* xsd_file = LLFile::fopen(file_name.c_str(), "w");
+	LLXMLNode::writeHeaderToFile(xsd_file);
+	root_nodep->writeToFile(xsd_file);
+	fclose(xsd_file);
+}
+
+//
+// LLXUIParser
+//
+LLXUIParser::LLXUIParser()
+:	mLastWriteGeneration(-1),
+	mCurReadDepth(0)
+{
+	registerParserFuncs<bool>(boost::bind(&LLXUIParser::readBoolValue, this, _1),
+								boost::bind(&LLXUIParser::writeBoolValue, this, _1, _2));
+	registerParserFuncs<std::string>(boost::bind(&LLXUIParser::readStringValue, this, _1),
+								boost::bind(&LLXUIParser::writeStringValue, this, _1, _2));
+	registerParserFuncs<U8>(boost::bind(&LLXUIParser::readU8Value, this, _1),
+								boost::bind(&LLXUIParser::writeU8Value, this, _1, _2));
+	registerParserFuncs<S8>(boost::bind(&LLXUIParser::readS8Value, this, _1),
+								boost::bind(&LLXUIParser::writeS8Value, this, _1, _2));
+	registerParserFuncs<U16>(boost::bind(&LLXUIParser::readU16Value, this, _1),
+								boost::bind(&LLXUIParser::writeU16Value, this, _1, _2));
+	registerParserFuncs<S16>(boost::bind(&LLXUIParser::readS16Value, this, _1),
+								boost::bind(&LLXUIParser::writeS16Value, this, _1, _2));
+	registerParserFuncs<U32>(boost::bind(&LLXUIParser::readU32Value, this, _1),
+								boost::bind(&LLXUIParser::writeU32Value, this, _1, _2));
+	registerParserFuncs<S32>(boost::bind(&LLXUIParser::readS32Value, this, _1),
+								boost::bind(&LLXUIParser::writeS32Value, this, _1, _2));
+	registerParserFuncs<F32>(boost::bind(&LLXUIParser::readF32Value, this, _1),
+								boost::bind(&LLXUIParser::writeF32Value, this, _1, _2));
+	registerParserFuncs<F64>(boost::bind(&LLXUIParser::readF64Value, this, _1),
+								boost::bind(&LLXUIParser::writeF64Value, this, _1, _2));
+	registerParserFuncs<LLColor4>(boost::bind(&LLXUIParser::readColor4Value, this, _1),
+								boost::bind(&LLXUIParser::writeColor4Value, this, _1, _2));
+	registerParserFuncs<LLUIColor>(boost::bind(&LLXUIParser::readUIColorValue, this, _1),
+								boost::bind(&LLXUIParser::writeUIColorValue, this, _1, _2));
+	registerParserFuncs<LLUUID>(boost::bind(&LLXUIParser::readUUIDValue, this, _1),
+								boost::bind(&LLXUIParser::writeUUIDValue, this, _1, _2));
+	registerParserFuncs<LLSD>(boost::bind(&LLXUIParser::readSDValue, this, _1),
+								boost::bind(&LLXUIParser::writeSDValue, this, _1, _2));
+}
+
+static LLFastTimer::DeclareTimer PARSE_XUI("XUI Parsing");
+
+void LLXUIParser::readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, bool silent)
+{
+	LLFastTimer timer(PARSE_XUI);
+	mNameStack.clear();
+	mCurReadDepth = 0;
+	setParseSilently(silent);
+
+	if (node.isNull())
+	{
+		parserWarning("Invalid node");
+	}
+	else
+	{
+		readXUIImpl(node, std::string(node->getName()->mString), block);
+	}
+}
+
+bool LLXUIParser::readXUIImpl(LLXMLNodePtr nodep, const std::string& scope, LLInitParam::BaseBlock& block)
+{
+	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+	boost::char_separator<char> sep(".");
+
+	bool values_parsed = false;
+
+	// submit attributes for current node
+	values_parsed |= readAttributes(nodep, block);
+
+	// treat text contents of xml node as "value" parameter
+	std::string text_contents = nodep->getSanitizedValue();
+	if (!text_contents.empty())
+	{
+		mCurReadNode = nodep;
+		mNameStack.push_back(std::make_pair(std::string("value"), newParseGeneration()));
+		// child nodes are not necessarily valid parameters (could be a child widget)
+		// so don't complain once we've recursed
+		bool silent = mCurReadDepth > 0;
+		if (!block.submitValue(mNameStack, *this, true))
+		{
+			mNameStack.pop_back();
+			block.submitValue(mNameStack, *this, silent);
+		}
+		else
+		{
+			mNameStack.pop_back();
+		}
+	}
+
+	// then traverse children
+	// child node must start with last name of parent node (our "scope")
+	// for example: "<button><button.param nested_param1="foo"><param.nested_param2 nested_param3="bar"/></button.param></button>"
+	// which equates to the following nesting:
+	// button
+	//     param
+	//         nested_param1
+	//         nested_param2
+	//             nested_param3	
+	mCurReadDepth++;
+	for(LLXMLNodePtr childp = nodep->getFirstChild(); childp.notNull();)
+	{
+		std::string child_name(childp->getName()->mString);
+		S32 num_tokens_pushed = 0;
+
+		// for non "dotted" child nodes	check to see if child node maps to another widget type
+		// and if not, treat as a child element of the current node
+		// e.g. <button><rect left="10"/></button> will interpret <rect> as "button.rect"
+		// since there is no widget named "rect"
+		if (child_name.find(".") == std::string::npos) 
+		{
+			mNameStack.push_back(std::make_pair(child_name, newParseGeneration()));
+			num_tokens_pushed++;
+		}
+		else
+		{
+			// parse out "dotted" name into individual tokens
+			tokenizer name_tokens(child_name, sep);
+
+			tokenizer::iterator name_token_it = name_tokens.begin();
+			if(name_token_it == name_tokens.end()) 
+			{
+				childp = childp->getNextSibling();
+				continue;
+			}
+
+			// check for proper nesting
+			if(!scope.empty() && *name_token_it != scope)
+			{
+				childp = childp->getNextSibling();
+				continue;
+			}
+
+			// now ignore first token
+			++name_token_it; 
+
+			// copy remaining tokens on to our running token list
+			for(tokenizer::iterator token_to_push = name_token_it; token_to_push != name_tokens.end(); ++token_to_push)
+			{
+				mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration()));
+				num_tokens_pushed++;
+			}
+		}
+
+		// recurse and visit children XML nodes
+		if(readXUIImpl(childp, mNameStack.empty() ? scope : mNameStack.back().first, block))
+		{
+			// child node successfully parsed, remove from DOM
+
+			values_parsed = true;
+			LLXMLNodePtr node_to_remove = childp;
+			childp = childp->getNextSibling();
+
+			nodep->deleteChild(node_to_remove);
+		}
+		else
+		{
+			childp = childp->getNextSibling();
+		}
+
+		while(num_tokens_pushed-- > 0)
+		{
+			mNameStack.pop_back();
+		}
+	}
+	mCurReadDepth--;
+	return values_parsed;
+}
+
+bool LLXUIParser::readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block)
+{
+	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+	boost::char_separator<char> sep(".");
+
+	bool any_parsed = false;
+
+	for(LLXMLAttribList::const_iterator attribute_it = nodep->mAttributes.begin(); 
+		attribute_it != nodep->mAttributes.end(); 
+		++attribute_it)
+	{
+		S32 num_tokens_pushed = 0;
+		std::string attribute_name(attribute_it->first->mString);
+		mCurReadNode = attribute_it->second;
+
+		tokenizer name_tokens(attribute_name, sep);
+		// copy remaining tokens on to our running token list
+		for(tokenizer::iterator token_to_push = name_tokens.begin(); token_to_push != name_tokens.end(); ++token_to_push)
+		{
+			mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration()));
+			num_tokens_pushed++;
+		}
+
+		// child nodes are not necessarily valid attributes, so don't complain once we've recursed
+		bool silent = mCurReadDepth > 0;
+		any_parsed |= block.submitValue(mNameStack, *this, silent);
+		
+		while(num_tokens_pushed-- > 0)
+		{
+			mNameStack.pop_back();
+		}
+	}
+
+	return any_parsed;
+}
+
+void LLXUIParser::writeXUI(LLXMLNodePtr node, const LLInitParam::BaseBlock &block, const LLInitParam::BaseBlock* diff_block)
+{
+	mWriteRootNode = node;
+	block.serializeBlock(*this, Parser::name_stack_t(), diff_block);
+	mOutNodes.clear();
+}
+
+// go from a stack of names to a specific XML node
+LLXMLNodePtr LLXUIParser::getNode(const name_stack_t& stack)
+{
+	name_stack_t name_stack;
+	for (name_stack_t::const_iterator it = stack.begin();
+		it != stack.end();
+		++it)
+	{
+		if (!it->first.empty())
+		{
+			name_stack.push_back(*it);
+		}
+	}
+
+	LLXMLNodePtr out_node = mWriteRootNode;
+
+	name_stack_t::const_iterator next_it = name_stack.begin();
+	for (name_stack_t::const_iterator it = name_stack.begin();
+		it != name_stack.end();
+		it = next_it)
+	{
+		++next_it;
+		if (it->first.empty())
+		{
+			continue;
+		}
+
+		out_nodes_t::iterator found_it = mOutNodes.lower_bound(it->second);
+
+		// node with this name not yet written
+		if (found_it == mOutNodes.end() || mOutNodes.key_comp()(found_it->first, it->second))
+		{
+			// make an attribute if we are the last element on the name stack
+			bool is_attribute = next_it == name_stack.end();
+			LLXMLNodePtr new_node = new LLXMLNode(it->first.c_str(), is_attribute);
+			out_node->addChild(new_node);
+			mOutNodes.insert(found_it, std::make_pair(it->second, new_node));
+			out_node = new_node;
+		}
+		else
+		{
+			out_node = found_it->second;
+		}
+	}
+
+	return (out_node == mWriteRootNode ? LLXMLNodePtr(NULL) : out_node);
+}
+
+
+bool LLXUIParser::readBoolValue(void* val_ptr)
+{
+	S32 value;
+	bool success = mCurReadNode->getBoolValue(1, &value);
+	*((bool*)val_ptr) = (value != FALSE);
+	return success;
+}
+
+bool LLXUIParser::writeBoolValue(const void* val_ptr, const name_stack_t& stack)
+{
+	LLXMLNodePtr node = getNode(stack);
+	if (node.notNull())
+	{
+		node->setBoolValue(*((bool*)val_ptr));
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::readStringValue(void* val_ptr)
+{
+	*((std::string*)val_ptr) = mCurReadNode->getSanitizedValue();
+	return true;
+}
+
+bool LLXUIParser::writeStringValue(const void* val_ptr, const name_stack_t& stack)
+{
+	LLXMLNodePtr node = getNode(stack);
+	if (node.notNull())
+	{
+		const std::string* string_val = reinterpret_cast<const std::string*>(val_ptr);
+		if (string_val->find('\n') != std::string::npos 
+			|| string_val->size() > MAX_STRING_ATTRIBUTE_SIZE)
+		{
+			// don't write strings with newlines into attributes
+			std::string attribute_name = node->getName()->mString;
+			LLXMLNodePtr parent_node = node->mParent;
+			parent_node->deleteChild(node);
+			// write results in text contents of node
+			if (attribute_name == "value")
+			{
+				// "value" is implicit, just write to parent
+				node = parent_node;
+			}
+			else
+			{
+				// create a child that is not an attribute, but with same name
+				node = parent_node->createChild(attribute_name.c_str(), false);
+			}
+		}
+		node->setStringValue(*string_val);
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::readU8Value(void* val_ptr)
+{
+	return mCurReadNode->getByteValue(1, (U8*)val_ptr);
+}
+
+bool LLXUIParser::writeU8Value(const void* val_ptr, const name_stack_t& stack)
+{
+	LLXMLNodePtr node = getNode(stack);
+	if (node.notNull())
+	{
+		node->setUnsignedValue(*((U8*)val_ptr));
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::readS8Value(void* val_ptr)
+{
+	S32 value;
+	if(mCurReadNode->getIntValue(1, &value))
+	{
+		*((S8*)val_ptr) = value;
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::writeS8Value(const void* val_ptr, const name_stack_t& stack)
+{
+	LLXMLNodePtr node = getNode(stack);
+	if (node.notNull())
+	{
+		node->setIntValue(*((S8*)val_ptr));
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::readU16Value(void* val_ptr)
+{
+	U32 value;
+	if(mCurReadNode->getUnsignedValue(1, &value))
+	{
+		*((U16*)val_ptr) = value;
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::writeU16Value(const void* val_ptr, const name_stack_t& stack)
+{
+	LLXMLNodePtr node = getNode(stack);
+	if (node.notNull())
+	{
+		node->setUnsignedValue(*((U16*)val_ptr));
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::readS16Value(void* val_ptr)
+{
+	S32 value;
+	if(mCurReadNode->getIntValue(1, &value))
+	{
+		*((S16*)val_ptr) = value;
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::writeS16Value(const void* val_ptr, const name_stack_t& stack)
+{
+	LLXMLNodePtr node = getNode(stack);
+	if (node.notNull())
+	{
+		node->setIntValue(*((S16*)val_ptr));
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::readU32Value(void* val_ptr)
+{
+	return mCurReadNode->getUnsignedValue(1, (U32*)val_ptr);
+}
+
+bool LLXUIParser::writeU32Value(const void* val_ptr, const name_stack_t& stack)
+{
+	LLXMLNodePtr node = getNode(stack);
+	if (node.notNull())
+	{
+		node->setUnsignedValue(*((U32*)val_ptr));
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::readS32Value(void* val_ptr)
+{
+	return mCurReadNode->getIntValue(1, (S32*)val_ptr);
+}
+
+bool LLXUIParser::writeS32Value(const void* val_ptr, const name_stack_t& stack)
+{
+	LLXMLNodePtr node = getNode(stack);
+	if (node.notNull())
+	{
+		node->setIntValue(*((S32*)val_ptr));
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::readF32Value(void* val_ptr)
+{
+	return mCurReadNode->getFloatValue(1, (F32*)val_ptr);
+}
+
+bool LLXUIParser::writeF32Value(const void* val_ptr, const name_stack_t& stack)
+{
+	LLXMLNodePtr node = getNode(stack);
+	if (node.notNull())
+	{
+		node->setFloatValue(*((F32*)val_ptr));
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::readF64Value(void* val_ptr)
+{
+	return mCurReadNode->getDoubleValue(1, (F64*)val_ptr);
+}
+
+bool LLXUIParser::writeF64Value(const void* val_ptr, const name_stack_t& stack)
+{
+	LLXMLNodePtr node = getNode(stack);
+	if (node.notNull())
+	{
+		node->setDoubleValue(*((F64*)val_ptr));
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::readColor4Value(void* val_ptr)
+{
+	LLColor4* colorp = (LLColor4*)val_ptr;
+	if(mCurReadNode->getFloatValue(4, colorp->mV) >= 3)
+	{
+		return true;
+	}
+
+	return false;
+}
+
+bool LLXUIParser::writeColor4Value(const void* val_ptr, const name_stack_t& stack)
+{
+	LLXMLNodePtr node = getNode(stack);
+	if (node.notNull())
+	{
+		LLColor4 color = *((LLColor4*)val_ptr);
+		node->setFloatValue(4, color.mV);
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::readUIColorValue(void* val_ptr)
+{
+	LLUIColor* param = (LLUIColor*)val_ptr;
+	LLColor4 color;
+	bool success =  mCurReadNode->getFloatValue(4, color.mV) >= 3;
+	if (success)
+	{
+		param->set(color);
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::writeUIColorValue(const void* val_ptr, const name_stack_t& stack)
+{
+	LLXMLNodePtr node = getNode(stack);
+	if (node.notNull())
+	{
+		LLUIColor color = *((LLUIColor*)val_ptr);
+		//RN: don't write out the color that is represented by a function
+		// rely on param block exporting to get the reference to the color settings
+		if (color.isReference()) return false;
+		node->setFloatValue(4, color.get().mV);
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::readUUIDValue(void* val_ptr)
+{
+	LLUUID temp_id;
+	// LLUUID::set is destructive, so use temporary value
+	if (temp_id.set(mCurReadNode->getSanitizedValue()))
+	{
+		*(LLUUID*)(val_ptr) = temp_id;
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::writeUUIDValue(const void* val_ptr, const name_stack_t& stack)
+{
+	LLXMLNodePtr node = getNode(stack);
+	if (node.notNull())
+	{
+		node->setStringValue(((LLUUID*)val_ptr)->asString());
+		return true;
+	}
+	return false;
+}
+
+bool LLXUIParser::readSDValue(void* val_ptr)
+{
+	*((LLSD*)val_ptr) = LLSD(mCurReadNode->getSanitizedValue());
+	return true;
+}
+
+bool LLXUIParser::writeSDValue(const void* val_ptr, const name_stack_t& stack)
+{
+	LLXMLNodePtr node = getNode(stack);
+	if (node.notNull())
+	{
+		std::string string_val = ((LLSD*)val_ptr)->asString();
+		if (string_val.find('\n') != std::string::npos || string_val.size() > MAX_STRING_ATTRIBUTE_SIZE)
+		{
+			// don't write strings with newlines into attributes
+			std::string attribute_name = node->getName()->mString;
+			LLXMLNodePtr parent_node = node->mParent;
+			parent_node->deleteChild(node);
+			// write results in text contents of node
+			if (attribute_name == "value")
+			{
+				// "value" is implicit, just write to parent
+				node = parent_node;
+			}
+			else
+			{
+				node = parent_node->createChild(attribute_name.c_str(), false);
+			}
+		}
+
+		node->setStringValue(string_val);
+		return true;
+	}
+	return false;
+}
+
+/*virtual*/ std::string LLXUIParser::getCurrentElementName()
+{
+	std::string full_name;
+	for (name_stack_t::iterator it = mNameStack.begin();	
+		it != mNameStack.end();
+		++it)
+	{
+		full_name += it->first + "."; // build up dotted names: "button.param.nestedparam."
+	}
+
+	return full_name;
+}
+
+void LLXUIParser::parserWarning(const std::string& message)
+{
+#if 0 //#ifdef LL_WINDOWS
+	// use Visual Studo friendly formatting of output message for easy access to originating xml
+	llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", LLUICtrlFactory::getInstance()->getCurFileName().c_str(), mCurReadNode->getLineNumber(), message.c_str()).c_str());
+	utf16str += '\n';
+	OutputDebugString(utf16str.c_str());
+#else
+	Parser::parserWarning(message);
+#endif
+}
+
+void LLXUIParser::parserError(const std::string& message)
+{
+#if 0 //#ifdef LL_WINDOWS
+	llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", LLUICtrlFactory::getInstance()->getCurFileName().c_str(), mCurReadNode->getLineNumber(), message.c_str()).c_str());
+	utf16str += '\n';
+	OutputDebugString(utf16str.c_str());
+#else
+	Parser::parserError(message);
+#endif
+}
diff --git a/indra/llxuixml/llxuiparser.h b/indra/llxuixml/llxuiparser.h
new file mode 100644
index 0000000000000000000000000000000000000000..6f000f24226f26f086bd162fbd9408bfd9945583
--- /dev/null
+++ b/indra/llxuixml/llxuiparser.h
@@ -0,0 +1,174 @@
+/** 
+ * @file llxuiparser.h
+ * @brief Utility functions for handling XUI structures in XML
+ *
+ * $LicenseInfo:firstyear=2003&license=viewergpl$
+ * 
+ * Copyright (c) 2003-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LLXUIPARSER_H
+#define LLXUIPARSER_H
+
+#include "llinitparam.h"
+#include "llxmlnode.h"
+#include "llfasttimer.h"
+#include "llregistry.h"
+
+#include <boost/function.hpp>
+#include <iosfwd>
+#include <stack>
+#include <set>
+
+
+
+class LLView;
+
+
+
+// lookup widget type by name
+class LLWidgetTypeRegistry
+:	public LLRegistrySingleton<std::string, const std::type_info*, LLWidgetTypeRegistry>
+{};
+
+
+// global static instance for registering all widget types
+typedef boost::function<LLView* (LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node)> LLWidgetCreatorFunc;
+
+typedef LLRegistry<std::string, LLWidgetCreatorFunc> widget_registry_t;
+
+class LLChildRegistryRegistry
+: public LLRegistrySingleton<const std::type_info*, widget_registry_t, LLChildRegistryRegistry>
+{};
+
+
+
+class LLXSDWriter : public LLInitParam::Parser
+{
+	LOG_CLASS(LLXSDWriter);
+public:
+	void writeXSD(const std::string& name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace);
+
+	/*virtual*/ std::string getCurrentElementName() { return LLStringUtil::null; }
+
+	LLXSDWriter();
+
+protected:
+	void writeAttribute(const std::string& type, const Parser::name_stack_t&, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values);
+	void addAttributeToSchema(LLXMLNodePtr nodep, const std::string& attribute_name, const std::string& type, bool mandatory, const std::vector<std::string>* possible_values);
+	LLXMLNodePtr mAttributeNode;
+	LLXMLNodePtr mElementNode;
+	LLXMLNodePtr mSchemaNode;
+
+	typedef std::set<std::string> string_set_t;
+	typedef std::map<LLXMLNodePtr, string_set_t> attributes_map_t;
+	attributes_map_t	mAttributesWritten;
+};
+
+
+
+// NOTE: DOES NOT WORK YET
+// should support child widgets for XUI
+class LLXUIXSDWriter : public LLXSDWriter
+{
+public:
+	void writeXSD(const std::string& name, const std::string& path, const LLInitParam::BaseBlock& block);
+};
+
+
+
+class LLXUIParser : public LLInitParam::Parser, public LLSingleton<LLXUIParser>
+{
+LOG_CLASS(LLXUIParser);
+
+protected:
+	LLXUIParser();
+	friend class LLSingleton<LLXUIParser>;
+public:
+	typedef LLInitParam::Parser::name_stack_t name_stack_t;
+
+	/*virtual*/ std::string getCurrentElementName();
+	/*virtual*/ void parserWarning(const std::string& message);
+	/*virtual*/ void parserError(const std::string& message);
+
+	void readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, bool silent=false);
+	void writeXUI(LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const LLInitParam::BaseBlock* diff_block = NULL);
+
+private:
+	typedef std::list<std::pair<std::string, bool> >	token_list_t;
+
+	bool readXUIImpl(LLXMLNodePtr node, const std::string& scope, LLInitParam::BaseBlock& block);
+	bool readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block);
+
+	//reader helper functions
+	bool readBoolValue(void* val_ptr);
+	bool readStringValue(void* val_ptr);
+	bool readU8Value(void* val_ptr);
+	bool readS8Value(void* val_ptr);
+	bool readU16Value(void* val_ptr);
+	bool readS16Value(void* val_ptr);
+	bool readU32Value(void* val_ptr);
+	bool readS32Value(void* val_ptr);
+	bool readF32Value(void* val_ptr);
+	bool readF64Value(void* val_ptr);
+	bool readColor4Value(void* val_ptr);
+	bool readUIColorValue(void* val_ptr);
+	bool readUUIDValue(void* val_ptr);
+	bool readSDValue(void* val_ptr);
+
+	//writer helper functions
+	bool writeBoolValue(const void* val_ptr, const name_stack_t&);
+	bool writeStringValue(const void* val_ptr, const name_stack_t&);
+	bool writeU8Value(const void* val_ptr, const name_stack_t&);
+	bool writeS8Value(const void* val_ptr, const name_stack_t&);
+	bool writeU16Value(const void* val_ptr, const name_stack_t&);
+	bool writeS16Value(const void* val_ptr, const name_stack_t&);
+	bool writeU32Value(const void* val_ptr, const name_stack_t&);
+	bool writeS32Value(const void* val_ptr, const name_stack_t&);
+	bool writeF32Value(const void* val_ptr, const name_stack_t&);
+	bool writeF64Value(const void* val_ptr, const name_stack_t&);
+	bool writeColor4Value(const void* val_ptr, const name_stack_t&);
+	bool writeUIColorValue(const void* val_ptr, const name_stack_t&);
+	bool writeUUIDValue(const void* val_ptr, const name_stack_t&);
+	bool writeSDValue(const void* val_ptr, const name_stack_t&);
+
+	LLXMLNodePtr getNode(const name_stack_t& stack);
+
+private:
+	Parser::name_stack_t			mNameStack;
+	LLXMLNodePtr					mCurReadNode;
+	// Root of the widget XML sub-tree, for example, "line_editor"
+	LLXMLNodePtr					mWriteRootNode;
+	
+	typedef std::map<S32, LLXMLNodePtr>	out_nodes_t;
+	out_nodes_t						mOutNodes;
+	S32								mLastWriteGeneration;
+	LLXMLNodePtr					mLastWrittenChild;
+	S32								mCurReadDepth;
+};
+
+
+#endif //LLXUIPARSER_H
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index ffdacee22afc536d5b7d4ca95929b59742482eb9..d7eabf33c407c8feafb4c620359e3fe38c7483e5 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -26,6 +26,7 @@ include(LLUI)
 include(LLVFS)
 include(LLWindow)
 include(LLXML)
+include(LLXUIXML)
 include(LScript)
 include(Linking)
 include(Mozlib)
@@ -58,6 +59,7 @@ include_directories(
     ${LLVFS_INCLUDE_DIRS}
     ${LLWINDOW_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
+    ${LLXUIXML_INCLUDE_DIRS}
     ${LSCRIPT_INCLUDE_DIRS}
     ${LSCRIPT_INCLUDE_DIRS}/lscript_compile
     )
@@ -1343,6 +1345,7 @@ target_link_libraries(${VIEWER_BINARY_NAME}
     ${LLVFS_LIBRARIES}
     ${LLWINDOW_LIBRARIES}
     ${LLXML_LIBRARIES}
+    ${LLXUIXML_LIBRARIES}
     ${LSCRIPT_LIBRARIES}
     ${LLMATH_LIBRARIES}
     ${LLCOMMON_LIBRARIES}
@@ -1402,6 +1405,7 @@ if (LINUX)
   if (NOT INSTALL)
     add_custom_target(package ALL DEPENDS ${product}.tar.bz2)
     add_dependencies(package linux-crash-logger-strip-target)
+    add_dependencies(package linux-updater-strip-target)
   endif (NOT INSTALL)
 endif (LINUX)
 
diff --git a/indra/newview/linux_tools/client-readme.txt b/indra/newview/linux_tools/client-readme.txt
index 99c973f7ea65e4a8d24eca496091c7e6e6342fa7..07a8f951eeafe50767aebb5617c2043edf6a362d 100644
--- a/indra/newview/linux_tools/client-readme.txt
+++ b/indra/newview/linux_tools/client-readme.txt
@@ -75,8 +75,9 @@ Life Linux client is very similar to that for Windows, as detailed at:
 3. INSTALLING & RUNNING
 -=-=-=-=-=-=-=-=-=-=-=-
 
-The Second Life Linux client entirely runs out of the directory you have
-unpacked it into - no installation step is required.
+The Second Life Linux client can entirely run from the directory you have
+unpacked it into - no installation step is required.  If you wish to
+perform a separate installation step anyway, you may run './install.sh'
 
 Run ./secondlife from the installation directory to start Second Life.
 
@@ -96,10 +97,7 @@ you wish.
 4. KNOWN ISSUES
 -=-=-=-=-=-=-=-
 
-* UPDATING - when the client detects that a new version of Second Life
-  is available, it will ask you if you wish to download the new version.
-  This option is not implemented; to upgrade, you should manually download a
-  new version from the Second Life web site, <http://www.secondlife.com/>.
+* No significant known issues at this time.
 
 
 5. TROUBLESHOOTING
diff --git a/indra/newview/linux_tools/handle_secondlifeprotocol.sh b/indra/newview/linux_tools/handle_secondlifeprotocol.sh
index 7ff86d1b9344f5352acf1eeb5aa13650c9b59ea9..203012132e7b3b62a7b702e7d2016d4bbb9335d6 100755
--- a/indra/newview/linux_tools/handle_secondlifeprotocol.sh
+++ b/indra/newview/linux_tools/handle_secondlifeprotocol.sh
@@ -11,7 +11,7 @@ if [ -z "$URL" ]; then
 fi
 
 RUN_PATH=`dirname "$0" || echo .`
-cd "${RUN_PATH}"
+cd "${RUN_PATH}/.."
 
 exec ./secondlife -url \'"${URL}"\'
 
diff --git a/indra/newview/linux_tools/install.sh b/indra/newview/linux_tools/install.sh
new file mode 100755
index 0000000000000000000000000000000000000000..c94510267ad34b6ef2fc498accd827177d37243c
--- /dev/null
+++ b/indra/newview/linux_tools/install.sh
@@ -0,0 +1,106 @@
+#!/bin/bash
+
+# Install the Second Life Viewer. This script can install the viewer both
+# system-wide and for an individual user.
+
+VT102_STYLE_NORMAL='\E[0m'
+VT102_COLOR_RED='\E[31m'
+
+SCRIPTSRC=`readlink -f "$0" || echo "$0"`
+RUN_PATH=`dirname "${SCRIPTSRC}" || echo .`
+tarball_path=${RUN_PATH}
+
+function prompt()
+{
+    local prompt=$1
+    local input
+
+    echo -n "$prompt"
+
+    while read input; do
+        case $input in
+            [Yy]* )
+                return 1
+                ;;
+            [Nn]* )
+                return 0
+                ;;
+            * )
+                echo "Please enter yes or no."
+                echo -n "$prompt"
+        esac
+    done
+}
+
+function die()
+{
+    warn $1
+    exit 1
+}
+
+function warn()
+{
+    echo -n -e $VT102_COLOR_RED
+    echo $1
+    echo -n -e $VT102_STYLE_NORMAL
+}
+
+function homedir_install()
+{
+    warn "You are not running as a privileged user, so you will only be able"
+    warn "to install the Second Life Viewer in your home directory. If you"
+    warn "would like to install the Second Life Viewer system-wide, please run"
+    warn "this script as the root user, or with the 'sudo' command."
+    echo
+
+    prompt "Proceed with the installation? [Y/N]: "
+    if [[ $? == 0 ]]; then
+	exit 0
+    fi
+
+    install_to_prefix "$HOME/.secondlife-install"
+    $HOME/.secondlife-install/etc/refresh_desktop_app_entry.sh
+}
+
+function root_install()
+{
+    local default_prefix="/opt/secondlife-install"
+
+    echo -n "Enter the desired installation directory [${default_prefix}]: ";
+    read
+    if [[ "$REPLY" = "" ]] ; then
+	local install_prefix=$default_prefix
+    else
+	local install_prefix=$REPLY
+    fi
+
+    install_to_prefix "$install_prefix"
+
+    mkdir -p /usr/local/share/applications
+    ${install_prefix}/etc/refresh_desktop_app_entry.sh
+}
+
+function install_to_prefix()
+{
+    test -e "$1" && backup_previous_installation "$1"
+    mkdir -p "$1" || die "Failed to create installation directory!"
+
+    echo " - Installing to $1"
+
+    cp -a "${tarball_path}"/* "$1/" || die "Failed to complete the installation!"
+}
+
+function backup_previous_installation()
+{
+    local backup_dir="$1".backup-$(date -I)
+    echo " - Backing up previous installation to $backup_dir"
+
+    mv "$1" "$backup_dir" || die "Failed to create backup of existing installation!"
+}
+
+
+if [ "$UID" == "0" ]; then
+    root_install
+else
+    homedir_install
+fi
diff --git a/indra/newview/linux_tools/refresh_desktop_app_entry.sh b/indra/newview/linux_tools/refresh_desktop_app_entry.sh
new file mode 100755
index 0000000000000000000000000000000000000000..d2b2a732d54293ecda5594d66056e5a65f0a025d
--- /dev/null
+++ b/indra/newview/linux_tools/refresh_desktop_app_entry.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+SCRIPTSRC=`readlink -f "$0" || echo "$0"`
+RUN_PATH=`dirname "${SCRIPTSRC}" || echo .`
+
+install_prefix=${RUN_PATH}/..
+
+function install_desktop_entry()
+{
+    local installation_prefix="$1"
+    local desktop_entries_dir="$2"
+
+    local desktop_entry="\
+[Desktop Entry]\n\
+Name=Second Life\n\
+Comment=Client for the On-line Virtual World, Second Life\n\
+Exec=${installation_prefix}/secondlife\n\
+Icon=${installation_prefix}/secondlife_icon.png\n\
+Terminal=false\n\
+Type=Application\n\
+Categories=Application;Network;\n\
+StartupNotify=true\n\
+X-Desktop-File-Install-Version=3.0"
+
+    echo " - Installing menu entries in ${desktop_entries_dir}"
+    mkdir -vp "${desktop_entries_dir}"
+    echo -e $desktop_entry > "${desktop_entries_dir}/secondlife-viewer.desktop" || "Failed to install application menu!"
+}
+
+if [ "$UID" == "0" ]; then
+    # system-wide
+    install_desktop_entry "$install_prefix" /usr/local/share/applications
+else
+    # user-specific
+    install_desktop_entry "$install_prefix" "$HOME/.local/share/applications"
+fi
diff --git a/indra/newview/linux_tools/register_secondlifeprotocol.sh b/indra/newview/linux_tools/register_secondlifeprotocol.sh
index 4ab96f97d69e000589eaa8c4af07c00740141385..c7b4d5546114c44ea2a4a035d1d45b5a040d60d2 100755
--- a/indra/newview/linux_tools/register_secondlifeprotocol.sh
+++ b/indra/newview/linux_tools/register_secondlifeprotocol.sh
@@ -7,10 +7,10 @@
 HANDLER="$1"
 
 RUN_PATH=`dirname "$0" || echo .`
-cd "${RUN_PATH}"
+cd "${RUN_PATH}/.."
 
 if [ -z "$HANDLER" ]; then
-    HANDLER=`pwd`/handle_secondlifeprotocol.sh
+    HANDLER=`pwd`/etc/handle_secondlifeprotocol.sh
 fi
 
 # Register handler for GNOME-aware apps
diff --git a/indra/newview/linux_tools/wrapper.sh b/indra/newview/linux_tools/wrapper.sh
index e188abe5d27f08c6d2fb9d56b93c23b5086b8287..3209654498c49792d0c8e2ed5454467e4316f510 100755
--- a/indra/newview/linux_tools/wrapper.sh
+++ b/indra/newview/linux_tools/wrapper.sh
@@ -91,12 +91,14 @@ echo "Running from ${RUN_PATH}"
 cd "${RUN_PATH}"
 
 # Re-register the secondlife:// protocol handler every launch, for now.
-./register_secondlifeprotocol.sh
+./etc/register_secondlifeprotocol.sh
+
+# Re-register the application with the desktop system every launch, for now.
+./etc/refresh_desktop_app_entry.sh
+
 ## Before we mess with LD_LIBRARY_PATH, save the old one to restore for
 ##  subprocesses that care.
-if [ "${LD_LIBRARY_PATH+isset}" = "isset" ]; then
-    export SAVED_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}"
-fi
+export SAVED_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}"
 
 if [ -n "$LL_TCMALLOC" ]; then
     tcmalloc_libs='/usr/lib/libtcmalloc.so.0 /usr/lib/libstacktrace.so.0 /lib/libpthread.so.0'
@@ -118,7 +120,7 @@ fi
 
 export SL_ENV='LD_LIBRARY_PATH="`pwd`"/lib:"`pwd`"/app_settings/mozilla-runtime-linux-i686:"${LD_LIBRARY_PATH}"'
 export SL_CMD='$LL_WRAPPER bin/do-not-directly-run-secondlife-bin'
-export SL_OPT="`cat gridargs.dat` $@"
+export SL_OPT="`cat etc/gridargs.dat` $@"
 
 # Run the program
 eval ${SL_ENV} ${SL_CMD} ${SL_OPT} || LL_RUN_ERR=runerr
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index a1484b3c52b55a26dc01aa2b496d604e46ca2ebc..5045f187840f54106a2c01f0fe4a30f3fd04f6c1 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -113,6 +113,7 @@
 #include "llviewermenu.h"
 #include "llselectmgr.h"
 #include "lltrans.h"
+#include "lltransutil.h"
 #include "lltracker.h"
 #include "llviewerparcelmgr.h"
 #include "llworldmapview.h"
@@ -677,8 +678,8 @@ bool LLAppViewer::init()
 	
 	// Setup paths and LLTrans after LLUI::initClass has been called
 	LLUI::setupPaths();
-	LLTrans::parseStrings("strings.xml", default_trans_args);		
-	LLTrans::parseLanguageStrings("language_settings.xml");
+	LLTransUtil::parseStrings("strings.xml", default_trans_args);		
+	LLTransUtil::parseLanguageStrings("language_settings.xml");
 	LLWeb::initClass();			  // do this after LLUI
 
 	LLTextEditor::setURLCallbacks(&LLWeb::loadURL,
@@ -1740,8 +1741,8 @@ bool LLAppViewer::initConfiguration()
 	}
 	
 	LLUI::setupPaths(); // setup paths for LLTrans based on settings files only
-	LLTrans::parseStrings("strings.xml", default_trans_args);
-	LLTrans::parseLanguageStrings("language_settings.xml");
+	LLTransUtil::parseStrings("strings.xml", default_trans_args);
+	LLTransUtil::parseLanguageStrings("language_settings.xml");
 	// - set procedural settings
 	// Note: can't use LL_PATH_PER_SL_ACCOUNT for any of these since we haven't logged in yet
 	gSavedSettings.setString("ClientSettingsFile", 
diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp
index d02e86a5573961358ea71a9380e64e9ff5a02132..cd35c28aa72f3cc36cb32dacea697f0f0edea5f0 100644
--- a/indra/newview/llappviewerlinux.cpp
+++ b/indra/newview/llappviewerlinux.cpp
@@ -553,7 +553,7 @@ void LLAppViewerLinux::handleSyncCrashTrace()
 
 void LLAppViewerLinux::handleCrashReporting(bool reportFreeze)
 {
-	std::string cmd =gDirUtilp->getAppRODataDir();
+	std::string cmd =gDirUtilp->getExecutableDir();
 	cmd += gDirUtilp->getDirDelimiter();
 #if LL_LINUX
 	cmd += "linux-crash-logger.bin";
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 1fc2245a8f3060b96c182074e01c7145e15b410e..04a3b52e98bd3ce5874d42d35e4baf8de0ae4fe3 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -40,6 +40,12 @@
 #	include <sys/stat.h>		// mkdir()
 #endif
 
+#if LL_LINUX && LL_GTK
+extern "C" {
+#	include "glib.h"		// g_spawn_command_line_async()
+}
+#endif
+
 #include "audioengine.h"
 
 #ifdef LL_FMOD
@@ -2872,10 +2878,12 @@ void update_app(BOOL mandatory, const std::string& auth_msg)
 	
 #if LL_WINDOWS
 	notification_name += "Windows";
-#else
+#elif LL_DARWIN
 	notification_name += "Mac";
+#else
+	notification_name += "Linux";	
 #endif
-	
+
 	if (mandatory)
 	{
 		notification_name += "Mandatory";
@@ -2888,7 +2896,6 @@ void update_app(BOOL mandatory, const std::string& auth_msg)
 	}
 	
 	LLNotifications::instance().add(notification_name, args, payload, update_dialog_callback);
-	
 }
 
 bool update_dialog_callback(const LLSD& notification, const LLSD& response)
@@ -2920,6 +2927,13 @@ bool update_dialog_callback(const LLSD& notification, const LLSD& response)
 		}
 		return false;
 	}
+
+	// if a sim name was passed in via command line parameter (typically through a SLURL)
+	if ( LLURLSimString::sInstance.mSimString.length() )
+	{
+		// record the location to start at next time
+		gSavedSettings.setString("NextLoginLocation", LLURLSimString::sInstance.mSimString);
+	}
 	
 	LLSD query_map = LLSD::emptyMap();
 	// *TODO place os string in a global constant
@@ -2980,13 +2994,6 @@ bool update_dialog_callback(const LLSD& notification, const LLSD& response)
 		return false;
 	}
 
-	// if a sim name was passed in via command line parameter (typically through a SLURL)
-	if ( LLURLSimString::sInstance.mSimString.length() )
-	{
-		// record the location to start at next time
-		gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString ); 
-	};
-
 	LLAppViewer::sUpdaterInfo->mParams << "-url \"" << update_url.asString() << "\"";
 
 	LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << " " << LLAppViewer::sUpdaterInfo->mParams.str() << LL_ENDL;
@@ -2995,13 +3002,6 @@ bool update_dialog_callback(const LLSD& notification, const LLSD& response)
 	LLAppViewer::instance()->removeMarkerFile(); // In case updater fails
 	
 #elif LL_DARWIN
-	// if a sim name was passed in via command line parameter (typically through a SLURL)
-	if ( LLURLSimString::sInstance.mSimString.length() )
-	{
-		// record the location to start at next time
-		gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString ); 
-	};
-	
 	LLAppViewer::sUpdaterInfo->mUpdateExePath = "'";
 	LLAppViewer::sUpdaterInfo->mUpdateExePath += gDirUtilp->getAppRODataDir();
 	LLAppViewer::sUpdaterInfo->mUpdateExePath += "/mac-updater.app/Contents/MacOS/mac-updater' -url \"";
@@ -3015,7 +3015,49 @@ bool update_dialog_callback(const LLSD& notification, const LLSD& response)
 	// Run the auto-updater.
 	system(LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str()); /* Flawfinder: ignore */
 
-#elif LL_LINUX || LL_SOLARIS
+#elif (LL_LINUX || LL_SOLARIS) && LL_GTK
+	// we tell the updater where to find the xml containing string
+	// translations which it can use for its own UI
+	std::string xml_strings_file = "strings.xml";
+	std::vector<std::string> xui_path_vec = LLUI::getXUIPaths();
+	std::string xml_search_paths;
+	std::vector<std::string>::const_iterator iter;
+	// build comma-delimited list of xml paths to pass to updater
+	for (iter = xui_path_vec.begin(); iter != xui_path_vec.end(); )
+	{
+		std::string this_skin_dir = gDirUtilp->getDefaultSkinDir()
+			+ gDirUtilp->getDirDelimiter()
+			+ (*iter);
+		llinfos << "Got a XUI path: " << this_skin_dir << llendl;
+		xml_search_paths.append(this_skin_dir);
+		++iter;
+		if (iter != xui_path_vec.end())
+			xml_search_paths.append(","); // comma-delimit
+	}
+	// build the overall command-line to run the updater correctly
+	update_exe_path = 
+		gDirUtilp->getExecutableDir() + "/" + "linux-updater.bin" + 
+		" --url \"" + update_url.asString() + "\"" +
+		" --name \"" + LLAppViewer::instance()->getSecondLifeTitle() + "\"" +
+		" --dest \"" + gDirUtilp->getAppRODataDir() + "\"" +
+		" --stringsdir \"" + xml_search_paths + "\"" +
+		" --stringsfile \"" + xml_strings_file + "\"";
+
+	LL_INFOS("AppInit") << "Calling updater: " 
+			    << update_exe_path << LL_ENDL;
+
+	// *TODO: we could use the gdk equivilant to ensure the updater
+	// gets started on the same screen.
+	GError *error = NULL;
+	if (!g_spawn_command_line_async(update_exe_path.c_str(), &error))
+	{
+		llerrs << "Failed to launch updater: "
+		       << error->message
+		       << llendl;
+	}
+	if (error)
+		g_error_free(error);
+#else
 	OSMessageBox(LLTrans::getString("MBNoAutoUpdate"), LLStringUtil::null, OSMB_OK);
 #endif
 	LLAppViewer::instance()->forceQuit();
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index ca9e89723c816cefa136856559af2c4dad1dd9ea..7b01fe4280c7f106c86b495d026ab80cad91177e 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -32,6 +32,8 @@
 
 #include "llviewerprecompiledheaders.h"
 
+#include <sys/stat.h>
+
 #include "llviewertexturelist.h"
 
 #include "imageids.h"
@@ -61,8 +63,7 @@
 #include "llviewerstats.h"
 #include "pipeline.h"
 #include "llappviewer.h"
-#include "lluictrlfactory.h" // for LLXUIParser
-#include <sys/stat.h>
+#include "llxuiparser.h"
 
 ////////////////////////////////////////////////////////////////////////////
 
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 8460e98fa36f81ec57dc3ac0147e9499624b1389..3083a7f689d22519177e8984d20f1491ee333a0f 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -2626,6 +2626,45 @@ This update is not required, but we suggest you install it to improve performanc
      yestext="Download"/>
   </notification>
 
+  <notification
+   icon="alertmodal.tga"
+   name="DownloadLinuxMandatory"
+   type="alertmodal">
+A new version of [SECOND_LIFE] is available.
+[MESSAGE]
+You must download this update to use [SECOND_LIFE].
+    <usetemplate
+     name="okcancelbuttons"
+     notext="Quit"
+     yestext="Download"/>
+  </notification>
+
+  <notification
+   icon="alertmodal.tga"
+   name="DownloadLinux"
+   type="alertmodal">
+An updated version of [SECOND_LIFE] is available.
+[MESSAGE]
+This update is not required, but we suggest you install it to improve performance and stability.
+    <usetemplate
+     name="okcancelbuttons"
+     notext="Continue"
+     yestext="Download"/>
+  </notification>
+
+  <notification
+   icon="alertmodal.tga"
+   name="DownloadLinuxReleaseForDownload"
+   type="alertmodal">
+An updated version of [SECOND_LIFE] is available.
+[MESSAGE]
+This update is not required, but we suggest you install it to improve performance and stability.
+    <usetemplate
+     name="okcancelbuttons"
+     notext="Continue"
+     yestext="Download"/>
+  </notification>
+
   <notification
    icon="alertmodal.tga"
    name="DownloadMacMandatory"
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index f89625c535fefaeab3f26050003065294d2cdf32..3d4ac940448fa6ded728b8bb4ed2f52c05eace42 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -666,7 +666,7 @@ Expected .wav, .tga, .bmp, .jpg, .jpeg, or .bvh
 	<string name="Any">Any</string>	
 	<string name="You">You</string>							
 						
-	<!-- puncutations -->
+	<!-- punctuations -->
 	<string name=":">:</string>			
 	<string name=",">,</string>					
 	<string name="...">...</string>					
@@ -936,4 +936,37 @@ If you continue to receive this message, contact customer support.
     <string name="poofy skirt">poofy skirt</string>
     <string name="tight skirt">tight skirt</string>	
 	<string name="wrinkles">wrinkles</string>	
+	
+	<!-- Strings used by the (currently Linux) auto-updater app -->
+	<string name="UpdaterWindowTitle">
+	  [SECOND_LIFE_VIEWER] Update
+	</string>
+	<string name="UpdaterNowUpdating">
+	  Now updating [SECOND_LIFE_VIEWER]...
+	</string>
+	<string name="UpdaterNowInstalling">
+	  Installing [SECOND_LIFE_VIEWER]...
+	</string>
+	<string name="UpdaterUpdatingDescriptive">
+	  Your [SECOND_LIFE_VIEWER] Viewer is being updated to the latest release.  This may take some time, so please be patient.
+	</string>
+	<string name="UpdaterProgressBarTextWithEllipses">
+	  Downloading update...
+	</string>
+	<string name="UpdaterProgressBarText">
+	  Downloading update
+	</string>
+	<string name="UpdaterFailDownloadTitle">
+	  Failed to download update
+	</string>
+	<string name="UpdaterFailUpdateDescriptive">
+	  An error occurred while updating Second Life. Please download the latest version from www.secondlife.com.
+	</string>
+	<string name="UpdaterFailInstallTitle">
+	  Failed to install update
+	</string>
+	<string name="UpdaterFailStartTitle">
+	  Failed to start viewer
+	</string>
+	
 </strings>
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 54095f866f0131b6fd5097f82430f64c6fb1d7b2..9c122deba09c0c6910ffdfe3490f86002f614fea 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -593,12 +593,15 @@ def construct(self):
             self.path("client-readme-voice.txt","README-linux-voice.txt")
             self.path("client-readme-joystick.txt","README-linux-joystick.txt")
             self.path("wrapper.sh","secondlife")
-            self.path("handle_secondlifeprotocol.sh")
-            self.path("register_secondlifeprotocol.sh")
+            self.path("handle_secondlifeprotocol.sh", "etc/handle_secondlifeprotocol.sh")
+            self.path("register_secondlifeprotocol.sh", "etc/register_secondlifeprotocol.sh")
+            self.path("refresh_desktop_app_entry.sh", "etc/refresh_desktop_app_entry.sh")
+            self.path("launch_url.sh","etc/launch_url.sh")
+            self.path("install.sh")
             self.end_prefix("linux_tools")
 
         # Create an appropriate gridargs.dat for this package, denoting required grid.
-        self.put_in_file(self.flags_list(), 'gridargs.dat')
+        self.put_in_file(self.flags_list(), 'etc/gridargs.dat')
 
 
     def package_finish(self):
@@ -659,8 +662,8 @@ def construct(self):
             pass
 
         self.path("secondlife-stripped","bin/do-not-directly-run-secondlife-bin")
-        self.path("../linux_crash_logger/linux-crash-logger-stripped","linux-crash-logger.bin")
-        self.path("linux_tools/launch_url.sh","launch_url.sh")
+        self.path("../linux_crash_logger/linux-crash-logger-stripped","bin/linux-crash-logger.bin")
+        self.path("../linux_updater/linux-updater-stripped", "bin/linux-updater.bin")
         if self.prefix("res-sdl"):
             self.path("*")
             # recurse
@@ -702,7 +705,6 @@ def construct(self):
         super(Linux_x86_64Manifest, self).construct()
         self.path("secondlife-stripped","bin/do-not-directly-run-secondlife-bin")
         self.path("../linux_crash_logger/linux-crash-logger-stripped","linux-crash-logger.bin")
-        self.path("linux_tools/launch_url.sh","launch_url.sh")
         if self.prefix("res-sdl"):
             self.path("*")
             # recurse