diff --git a/indra/linux_crash_logger/linux_crash_logger.cpp b/indra/linux_crash_logger/linux_crash_logger.cpp
index a49dfe1b2b5d5823fb511783a08e6a575eebfaa8..5310093f6993ea59ccd5187f6407880f291f6aac 100644
--- a/indra/linux_crash_logger/linux_crash_logger.cpp
+++ b/indra/linux_crash_logger/linux_crash_logger.cpp
@@ -29,542 +29,16 @@
  * $/LicenseInfo$
  */
 
-#include "linden_common.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <curl/curl.h>
-
-#if LL_GTK
-# include "gtk/gtk.h"
-#endif // LL_GTK
-
-#include "indra_constants.h"	// CRASH_BEHAVIOR_ASK
-#include "llerror.h"
-#include "lltimer.h"
-#include "lldir.h"
-
-#include "llstring.h"
-
-
-// These need to be localized.
-static const char dialog_text[] =
-"Second Life appears to have crashed.\n"
-"This crash reporter collects information about your computer's hardware, operating system, and some Second Life logs, which are used for debugging purposes only.\n"
-"Sending crash reports is the best way to help us improve the quality of Second Life.\n"
-"If you continue to experience this problem, please try:\n"
-"- Contacting support by visiting http://www.secondlife.com/support\n"
-"\n"
-"Send crash report?";
-
-static const char dialog_title[] =
-"Second Life Crash Logger";
-
-
-class LLFileEncoder
-{
-public:
-	LLFileEncoder(const char *formname, const char *filename, bool isCrashLog = false);
-
-	BOOL isValid() const { return mIsValid; }
-	LLString encodeURL(const S32 max_length = 0);
-public:
-	BOOL mIsValid;
-	LLString mFilename;
-	LLString mFormname;
-	LLString mBuf;
-};
-
-LLString encode_string(const char *formname, const LLString &str);
-
-LLString gServerResponse;
-BOOL gSendReport = FALSE;
-LLString gUserserver;
-LLString gUserText;
-BOOL gCrashInPreviousExec = FALSE;
-time_t gLaunchTime;
-
-static size_t curl_download_callback(void *data, size_t size, size_t nmemb,
-				     void *user_data)
-{
-	S32 bytes = size * nmemb;
-	char *cdata = (char *) data;
-	for (int i =0; i < bytes; i += 1)
-	{
-		gServerResponse += (cdata[i]);
-	}
-	return bytes;
-}
-
-#if LL_GTK
-static void response_callback (GtkDialog *dialog,
-			       gint       arg1,
-			       gpointer   user_data)
-{
-	gint *response = (gint*)user_data;
-	*response = arg1;
-	gtk_widget_destroy(GTK_WIDGET(dialog));
-	gtk_main_quit();
-}
-#endif // LL_GTK
-
-static BOOL do_ask_dialog(void)
-{
-#if LL_GTK
-	gtk_disable_setlocale();
-	if (!gtk_init_check(NULL, NULL)) {
-		llinfos << "Could not initialize GTK for 'ask to send crash report' dialog; not sending report." << llendl;
-		return FALSE;
-	}
-	
-	GtkWidget *win = NULL;
-	GtkDialogFlags flags = GTK_DIALOG_MODAL;
-	GtkMessageType messagetype = GTK_MESSAGE_QUESTION;
-	GtkButtonsType buttons = GTK_BUTTONS_YES_NO;
-	gint response = GTK_RESPONSE_NONE;
-
-	win = gtk_message_dialog_new(NULL,
-				     flags, messagetype, buttons,
-				     dialog_text);
-	gtk_window_set_type_hint(GTK_WINDOW(win),
-				 GDK_WINDOW_TYPE_HINT_DIALOG);
-	gtk_window_set_title(GTK_WINDOW(win), dialog_title);
-	g_signal_connect (win,
-			  "response", 
-			  G_CALLBACK (response_callback),
-			  &response);
-	gtk_widget_show_all (win);
-	gtk_main();
-
-	return (GTK_RESPONSE_OK == response ||
-		GTK_RESPONSE_YES == response ||
-		GTK_RESPONSE_APPLY == response);
-#else
-	return FALSE;
-#endif // LL_GTK
-}
-
+#include "llcrashloggerlinux.h"
 
 int main(int argc, char **argv)
 {
-	const S32 BT_MAX_SIZE = 100000;			// Maximum size to transmit of the backtrace file
-	const S32 SL_MAX_SIZE = 100000;			// Maximum size of the Second Life log file.
-	int i;
-	S32 crash_behavior = CRASH_BEHAVIOR_ALWAYS_SEND;
-	
-	time(&gLaunchTime);
-	
-	llinfos << "Starting Second Life Viewer Crash Reporter" << llendl;
-	
-	for(i=1; i<argc; i++)
-	{
-		if(!strcmp(argv[i], "-dialog"))
-		{
-			llinfos << "Show the user dialog" << llendl;
-			crash_behavior = CRASH_BEHAVIOR_ASK;
-		}
-		if(!strcmp(argv[i], "-previous"))
-		{
-			gCrashInPreviousExec = TRUE;
-		}
-		if(!strcmp(argv[i], "-user"))
-		{
-			if ((i + 1) < argc)
-			{
-				i++;
-				gUserserver = argv[i];
-				llinfos << "Got userserver " << gUserserver << llendl;
-			}
-		}
-	}
-	
-	if( gCrashInPreviousExec )
-	{
-		llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl;
-	}
-	
-	if(!gCrashInPreviousExec)
-	{
-		// Wait a while to let the crashed client finish exiting,
-		// freeing up the screen/etc.
-		sleep(5);
-	}
-
-	// *FIX: do some dialog stuff here?
-	if (CRASH_BEHAVIOR_ALWAYS_SEND == crash_behavior)
-	{
-		gSendReport = TRUE;
-	}
-	else if (CRASH_BEHAVIOR_ASK == crash_behavior)
-	{
-		gSendReport = do_ask_dialog();
-	}
-	
-	if(!gSendReport)
-	{
-		// Only send the report if the user agreed to it.
-		llinfos << "User cancelled, not sending report" << llendl;
-
-		return(0);
-	}
-
-	// We assume that all the logs we're looking for reside on the current drive
-	gDirUtilp->initAppDirs("SecondLife");
-
-	// Lots of silly variable, replicated for each log file.
-	LLString db_file_name;
-	LLString sl_file_name;
-	LLString bt_file_name; // stack_trace.log file
-	LLString st_file_name; // stats.log file
-	LLString si_file_name; // settings.xml file
-
-	LLFileEncoder *db_filep = NULL;
-	LLFileEncoder *sl_filep = NULL;
-	LLFileEncoder *st_filep = NULL;
-	LLFileEncoder *bt_filep = NULL;
-	LLFileEncoder *si_filep = NULL;
-
-	///////////////////////////////////
-	//
-	// We do the parsing for the debug_info file first, as that will
-	// give us the location of the SecondLife.log file.
-	//
-
-	// Figure out the filename of the debug log
-	db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log").c_str();
-	db_filep = new LLFileEncoder("DB", db_file_name.c_str());
-
-	// Get the filename of the SecondLife.log file
-	// *NOTE: These buffer sizes are hardcoded into a scanf() below.
-	char tmp_sl_name[LL_MAX_PATH];
-	tmp_sl_name[0] = '\0';
-	char tmp_space[256];
-	tmp_space[0] = '\0';
-
-	// Look for it in the debug_info.log file
-	if (db_filep->isValid())
-	{
-		// This was originally scanning for "SL Log: %[^\r\n]", which happily skipped to the next line
-		// on debug logs (which don't have anything after "SL Log:" and tried to open a nonsensical filename.
-		sscanf(db_filep->mBuf.c_str(), "SL Log:%255[ ]%1023[^\r\n]", tmp_space, tmp_sl_name);
-	}
-	else
-	{
-		delete db_filep;
-		db_filep = NULL;
-	}
-
-	// If we actually have a legitimate file name, use it.
-	if (gCrashInPreviousExec)
-	{
-		// If we froze, the crash log this time around isn't useful.
-		// Use the old one.
-		sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.old");
-	}
-	else if (tmp_sl_name[0])
-	{
-		sl_file_name = tmp_sl_name;
-		llinfos << "Using log file from debug log: " << sl_file_name << llendl;
-	}
-	else
-	{
-		// Figure out the filename of the second life log
-		sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log").c_str();
-	}
-
-	// Now we get the SecondLife.log file if it's there, and recent enough...
-	sl_filep = new LLFileEncoder("SL", sl_file_name.c_str());
-	if (!sl_filep->isValid())
-	{
-		delete sl_filep;
-		sl_filep = NULL;
-	}
-
-	st_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log").c_str();
-	st_filep = new LLFileEncoder("ST", st_file_name.c_str());
-	if (!st_filep->isValid())
-	{
-		delete st_filep;
-		st_filep = NULL;
-	}
-
-	si_file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml").c_str();
-	si_filep = new LLFileEncoder("SI", si_file_name.c_str());
-	if (!si_filep->isValid())
-	{
-		delete si_filep;
-		si_filep = NULL;
-	}
-
-	// encode this as if it were a 'Dr Watson' plain-text backtrace
-	bt_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log").c_str();
-	bt_filep = new LLFileEncoder("DW", bt_file_name.c_str());
-	if (!bt_filep->isValid())
-	{
-		delete bt_filep;
-		bt_filep = NULL;
-	}
-
-	LLString post_data;
-	LLString tmp_url_buf;
-
-	// Append the userserver
-	tmp_url_buf = encode_string("USER", gUserserver);
-	post_data += tmp_url_buf;
-	llinfos << "PostData:" << post_data << llendl;
-
-	if (gCrashInPreviousExec)
-	{
-		post_data.append("&");
-		tmp_url_buf = encode_string("EF", "Y");
-		post_data += tmp_url_buf;
-	}
-
-	if (db_filep)
-	{
-		post_data.append("&");
-		tmp_url_buf = db_filep->encodeURL();
-		post_data += tmp_url_buf;
-		llinfos << "Sending DB log file" << llendl;
-	}
-	else
-	{
-		llinfos << "Not sending DB log file" << llendl;
-	}
-
-	if (sl_filep)
-	{
-		post_data.append("&");
-		tmp_url_buf = sl_filep->encodeURL(SL_MAX_SIZE);
-		post_data += tmp_url_buf;
-		llinfos << "Sending SL log file" << llendl;
-	}
-	else
-	{
-		llinfos << "Not sending SL log file" << llendl;
-	}
-
-	if (st_filep)
-	{
-		post_data.append("&");
-		tmp_url_buf = st_filep->encodeURL(SL_MAX_SIZE);
-		post_data += tmp_url_buf;
-		llinfos << "Sending stats log file" << llendl;
-	}
-	else
-	{
-		llinfos << "Not sending stats log file" << llendl;
-	}
-
-	if (bt_filep)
-	{
-		post_data.append("&");
-		tmp_url_buf = bt_filep->encodeURL(BT_MAX_SIZE);
-		post_data += tmp_url_buf;
-		llinfos << "Sending crash log file" << llendl;
-	}
-	else
-	{
-		llinfos << "Not sending crash log file" << llendl;
-	}
-
-	if (si_filep)
-	{
-		post_data.append("&");
-		tmp_url_buf = si_filep->encodeURL();
-		post_data += tmp_url_buf;
-		llinfos << "Sending settings log file" << llendl;
-	}
-	else
-	{
-		llinfos << "Not sending settings.xml file" << llendl;
-	}
-
-	if (gUserText.size())
-	{
-		post_data.append("&");
-		tmp_url_buf = encode_string("UN", gUserText);
-		post_data += tmp_url_buf;
-	}
-
-	delete db_filep;
-	db_filep = NULL;
-	delete sl_filep;
-	sl_filep = NULL;
-	delete bt_filep;
-	bt_filep = NULL;
-
-	// Debugging spam
-#if 0
-	printf("Crash report post data:\n--------\n");
-	printf("%s", post_data.getString());
-	printf("\n--------\n");
-#endif
-	
-	// Send the report.  Yes, it's this easy.
-	{
-		CURL *curl = curl_easy_init();
-
-		curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
-		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback);
-		curl_easy_setopt(curl, CURLOPT_POST, 1); 
-		curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data.c_str()); 
-		curl_easy_setopt(curl, CURLOPT_URL,	"http://secondlife.com/cgi-bin/viewer_crash_reporter2");
-		
-		llinfos << "Connecting to crash report server" << llendl;
-		CURLcode result = curl_easy_perform(curl);
-		
-		curl_easy_cleanup(curl);
-		
-		if(result != CURLE_OK)
-		{
-			llinfos << "Couldn't talk to crash report server" << llendl;
-		}
-		else
-		{
-			llinfos << "Response from crash report server:" << llendl;
-			llinfos << gServerResponse << llendl;			
-		}
-	}
-	
+	LLCrashLoggerLinux app;
+	app.parseCommandOptions(argc, argv);
+	app.init();
+	app.mainLoop();
+	app.cleanup();
 	return 0;
 }
 
-LLFileEncoder::LLFileEncoder(const char *form_name, const char *filename, bool isCrashLog)
-{
-	mFormname = form_name;
-	mFilename = filename;
-	mIsValid = FALSE;
-
-	int res;
-	
-	struct stat stat_data;
-	res = stat(mFilename.c_str(), &stat_data);
-	if (res)
-	{
-		llwarns << "File " << mFilename << " is missing!" << llendl;
-		return;
-	}
-	else
-	{
-		// Debugging spam
-//		llinfos << "File " << mFilename << " is present..." << llendl;
-
-		if(!gCrashInPreviousExec && isCrashLog)
-		{
-			// Make sure the file isn't too old.
-			double age = difftime(gLaunchTime, stat_data.st_mtim.tv_sec);
-
-//			llinfos << "age is " << age << llendl;
 
-			if(age > 60.0)
-			{
-				// The file was last modified more than 60 seconds before the crash reporter was launched.  Assume it's stale.
-				llwarns << "File " << mFilename << " is too old!" << llendl;
-				return;
-			}
-		}
-
-	}
-
-	S32 buf_size = stat_data.st_size;
-	FILE *fp = fopen(mFilename.c_str(), "rb");
-	U8 *buf = new U8[buf_size + 1];
-	size_t nread = fread(buf, 1, buf_size, fp);
-	fclose(fp);
-	buf[nread] = 0;
-
-	mBuf = (char *)buf;
-	
-	if(isCrashLog)
-	{
-		// Crash logs consist of a number of entries, one per crash.
-		// Each entry is preceeded by "**********" on a line by itself.
-		// We want only the most recent (i.e. last) one.
-		const char *sep = "**********";
-		const char *start = mBuf.c_str();
-		const char *cur = start;
-		const char *temp = strstr(cur, sep);
-		
-		while(temp != NULL)
-		{
-			// Skip past the marker we just found
-			cur = temp + strlen(sep);
-			
-			// and try to find another
-			temp = strstr(cur, sep);
-		}
-		
-		// If there's more than one entry in the log file, strip all but the last one.
-		if(cur != start)
-		{
-			mBuf.erase(0, cur - start);
-		}
-	}
-
-	mIsValid = TRUE;
-	delete[] buf;
-}
-
-LLString LLFileEncoder::encodeURL(const S32 max_length)
-{
-	LLString result = mFormname;
-	result.append("=");
-
-	S32 i = 0;
-
-	if (max_length)
-	{
-		if ((S32)mBuf.size() > max_length)
-		{
-			i = mBuf.size() - max_length;
-		}
-	}
-
-#if 0
-	// Plain text version for debugging
-	result.append(mBuf);
-#else
-	// Not using LLString because of bad performance issues
-	S32 buf_size = mBuf.size();
-	S32 url_buf_size = 3*mBuf.size() + 1;
-	char *url_buf = new char[url_buf_size];
-
-	S32 cur_pos = 0;
-	for (; i < buf_size; i++)
-	{
-		sprintf(url_buf + cur_pos, "%%%02x", mBuf[i]);
-		cur_pos += 3;
-	}
-	url_buf[i*3] = 0;
-
-	result.append(url_buf);
-	delete[] url_buf;
-#endif
-	return result;
-}
-
-LLString encode_string(const char *formname, const LLString &str)
-{
-	LLString result = formname;
-	result.append("=");
-	// Not using LLString because of bad performance issues
-	S32 buf_size = str.size();
-	S32 url_buf_size = 3*str.size() + 1;
-	char *url_buf = new char[url_buf_size];
-
-	S32 cur_pos = 0;
-	S32 i;
-	for (i = 0; i < buf_size; i++)
-	{
-		sprintf(url_buf + cur_pos, "%%%02x", str[i]);
-		cur_pos += 3;
-	}
-	url_buf[i*3] = 0;
-
-	result.append(url_buf);
-	delete[] url_buf;
-	return result;
-}
diff --git a/indra/linux_crash_logger/llcrashloggerlinux.cpp b/indra/linux_crash_logger/llcrashloggerlinux.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..26a8f0cb2ad30ca383a835d594654f1467fd7106
--- /dev/null
+++ b/indra/linux_crash_logger/llcrashloggerlinux.cpp
@@ -0,0 +1,140 @@
+/** 
+ * @file llcrashloggerlinux.cpp
+ * @brief Linux crash logger implementation
+ *
+ * $LicenseInfo:firstyear=2003&license=viewergpl$
+ * 
+ * Copyright (c) 2003-2007, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llcrashloggerlinux.h"
+
+#include <iostream>
+
+#include "linden_common.h"
+
+#include "boost/tokenizer.hpp"
+
+#include "indra_constants.h"	// CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
+#include "llerror.h"
+#include "llfile.h"
+#include "lltimer.h"
+#include "llstring.h"
+#include "lldir.h"
+#include "llsdserialize.h"
+
+#if LL_GTK
+# include "gtk/gtk.h"
+#endif // LL_GTK
+
+#define MAX_LOADSTRING 100
+
+// These need to be localized.
+static const char dialog_text[] =
+"Second Life appears to have crashed or frozen last time it ran.\n"
+"This crash reporter collects information about your computer's hardware, operating system, and some Second Life logs, all of which are used for debugging purposes only.\n"
+"In the space below, please briefly describe what you were doing or trying to do just prior to the crash. Thank you for your help!\n"
+"This report is NOT read by Customer Support. If you have billing or other questions, contact support by visiting http://www.secondlife.com/support\n"
+"\n"
+"Send crash report?";
+
+static const char dialog_title[] =
+"Second Life Crash Logger";
+
+#if LL_GTK
+static void response_callback (GtkDialog *dialog,
+			       gint       arg1,
+			       gpointer   user_data)
+{
+	gint *response = (gint*)user_data;
+	*response = arg1;
+	gtk_widget_destroy(GTK_WIDGET(dialog));
+	gtk_main_quit();
+}
+#endif // LL_GTK
+
+static BOOL do_ask_dialog(void)
+{
+#if LL_GTK
+	gtk_disable_setlocale();
+	if (!gtk_init_check(NULL, NULL)) {
+		llinfos << "Could not initialize GTK for 'ask to send crash report' dialog; not sending report." << llendl;
+		return FALSE;
+	}
+	
+	GtkWidget *win = NULL;
+	GtkDialogFlags flags = GTK_DIALOG_MODAL;
+	GtkMessageType messagetype = GTK_MESSAGE_QUESTION;
+	GtkButtonsType buttons = GTK_BUTTONS_YES_NO;
+	gint response = GTK_RESPONSE_NONE;
+
+	win = gtk_message_dialog_new(NULL,
+				     flags, messagetype, buttons,
+				     dialog_text);
+	gtk_window_set_type_hint(GTK_WINDOW(win),
+				 GDK_WINDOW_TYPE_HINT_DIALOG);
+	gtk_window_set_title(GTK_WINDOW(win), dialog_title);
+	g_signal_connect (win,
+			  "response", 
+			  G_CALLBACK (response_callback),
+			  &response);
+	gtk_widget_show_all (win);
+	gtk_main();
+
+	return (GTK_RESPONSE_OK == response ||
+		GTK_RESPONSE_YES == response ||
+		GTK_RESPONSE_APPLY == response);
+#else
+	return FALSE;
+#endif // LL_GTK
+}
+
+LLCrashLoggerLinux::LLCrashLoggerLinux(void)
+{
+}
+
+LLCrashLoggerLinux::~LLCrashLoggerLinux(void)
+{
+}
+
+void LLCrashLoggerLinux::gatherPlatformSpecificFiles()
+{
+	mFileMap["CrashLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log").c_str();
+}
+
+bool LLCrashLoggerLinux::mainLoop()
+{
+	if(!do_ask_dialog())
+	{
+		return true;
+	}
+	sendCrashLogs();
+	return true;
+}
+
+void LLCrashLoggerLinux::updateApplication(LLString message)
+{
+	LLCrashLogger::updateApplication(message);
+}
diff --git a/indra/linux_crash_logger/llcrashloggerlinux.h b/indra/linux_crash_logger/llcrashloggerlinux.h
new file mode 100644
index 0000000000000000000000000000000000000000..a84ee00e1cfe1b66cd3a5c099bc623daf9c7ed69
--- /dev/null
+++ b/indra/linux_crash_logger/llcrashloggerlinux.h
@@ -0,0 +1,49 @@
+/** 
+ * @file llcrashloggerlinux.h
+ * @brief Linux crash logger definition
+ *
+ * $LicenseInfo:firstyear=2003&license=viewergpl$
+ * 
+ * Copyright (c) 2003-2007, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 LLCRASHLOGGERLINUX_H
+#define LLCRASHLOGGERLINUX_H
+
+#include "linden_common.h"
+#include "llcrashlogger.h"
+#include "llstring.h"
+
+class LLCrashLoggerLinux : public LLCrashLogger
+{
+public:
+	LLCrashLoggerLinux(void);
+	~LLCrashLoggerLinux(void);
+	virtual bool mainLoop();
+	virtual void updateApplication(LLString message = "");
+	virtual void gatherPlatformSpecificFiles();
+};
+
+#endif
diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp
new file mode 100755
index 0000000000000000000000000000000000000000..cec2b2e2e9de6d640f7ad2cf9ea674254dfddadf
--- /dev/null
+++ b/indra/llcrashlogger/llcrashlogger.cpp
@@ -0,0 +1,309 @@
+/** 
+* @file llcrashlogger.cpp
+* @brief Crash logger implementation
+*
+* $LicenseInfo:firstyear=2003&license=viewergpl$
+* 
+* Copyright (c) 2003-2007, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 <cstdio>
+#include <cstdlib>
+#include <sstream>
+#include <map>
+
+#include "llcrashlogger.h"
+#include "linden_common.h"
+#include "llstring.h"
+#include "indra_constants.h"	// CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
+#include "llerror.h"
+#include "lltimer.h"
+#include "lldir.h"
+#include "llsdserialize.h"
+#include "lliopipe.h"
+#include "llpumpio.h"
+#include "llhttpclient.h"
+#include "llsdserialize.h"
+
+LLPumpIO* gServicePump;
+BOOL gBreak = false;
+BOOL gSent = false;
+
+class LLCrashLoggerResponder : public LLHTTPClient::Responder
+{
+public:
+	LLCrashLoggerResponder() 
+	{
+	}
+
+	virtual void error(U32 status, const std::string& reason)
+	{
+		gBreak = true;		
+	}
+
+	virtual void result(const LLSD& content)
+	{	
+		gBreak = true;
+		gSent = true;
+	}
+};
+
+bool LLCrashLoggerText::mainLoop()
+{
+	std::cout << "Entering main loop" << std::endl;
+	sendCrashLogs();
+	return true;	
+}
+
+void LLCrashLoggerText::updateApplication(LLString message)
+{
+	LLCrashLogger::updateApplication(message);
+	std::cout << message << std::endl;
+}
+
+LLCrashLogger::LLCrashLogger() :
+mSentCrashLogs(false)
+{
+
+}
+
+LLCrashLogger::~LLCrashLogger()
+{
+
+}
+
+void LLCrashLogger::gatherFiles()
+{
+
+	/*
+	//TODO:This function needs to be reimplemented somewhere in here...
+	if(!previous_crash && is_crash_log)
+	{
+		// Make sure the file isn't too old.
+		double age = difftime(gLaunchTime, stat_data.st_mtimespec.tv_sec);
+		
+		//			llinfos << "age is " << age << llendl;
+		
+		if(age > 60.0)
+		{
+				// The file was last modified more than 60 seconds before the crash reporter was launched.  Assume it's stale.
+			llwarns << "File " << mFilename << " is too old!" << llendl;
+			return;
+		}
+	}
+	*/
+
+	updateApplication("Gathering logs...");
+
+	// Figure out the filename of the debug log
+	LLString db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log").c_str();
+	std::ifstream debug_log_file(db_file_name.c_str());
+
+	// Look for it in the debug_info.log file
+	if (debug_log_file.is_open())
+	{		
+		LLSDSerialize::fromXML(mDebugLog, debug_log_file);
+		mFileMap["SecondLifeLog"] = mDebugLog["SLLog"].asString();
+		mFileMap["SettingsXml"] = mDebugLog["SettingsFilename"].asString();
+		LLHTTPClient::setCABundle(mDebugLog["CAFilename"].asString());
+		llinfos << "Using log file from debug log " << mFileMap["SecondLifeLog"] << llendl;
+		llinfos << "Using settings file from debug log " << mFileMap["SettingsXml"] << llendl;
+	}
+	else
+	{
+		// Figure out the filename of the second life log
+		LLHTTPClient::setCABundle(gDirUtilp->getCAFile());
+		mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log");
+		mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml");
+	}
+
+	gatherPlatformSpecificFiles();
+
+	//Use the debug log to reconstruct the URL to send the crash report to
+	mCrashHost = "https://";
+	mCrashHost += mDebugLog["CurrentSimHost"].asString();
+	mCrashHost += ":12043/crash/report";
+	mAltCrashHost = "https://";
+	mAltCrashHost += mDebugLog["GridUtilHost"].asString();
+	mAltCrashHost += ":12043/crash/report";
+
+	mCrashInfo["DebugLog"] = mDebugLog;
+	mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log");
+	mFileMap["StackTrace"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
+	
+	updateApplication("Encoding files...");
+
+	for(std::map<LLString, LLString>::iterator itr = mFileMap.begin(); itr != mFileMap.end(); ++itr)
+	{
+		std::ifstream f((*itr).second.c_str());
+		if(!f.is_open())
+		{
+			std::cout << "Can't find file " << (*itr).second.c_str() << std::endl;
+			continue;
+		}
+		std::stringstream s;
+		s << f.rdbuf();
+		mCrashInfo[(*itr).first] = s.str();
+	}
+}
+
+LLSD LLCrashLogger::constructPostData()
+{
+	LLSD ret;
+
+	if(mCrashInPreviousExec)
+	{
+		mCrashInfo["CrashInPreviousExecution"] = "Y";
+	}
+
+	return mCrashInfo;
+}
+
+S32 LLCrashLogger::loadCrashBehaviorSetting()
+{
+	std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
+
+	mCrashSettings.loadFromFile(filename);
+		
+	S32 value = mCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
+	
+	if (value < CRASH_BEHAVIOR_ASK || CRASH_BEHAVIOR_NEVER_SEND < value) return CRASH_BEHAVIOR_ASK;
+
+	return value;
+}
+
+bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior)
+{
+	if (crash_behavior < CRASH_BEHAVIOR_ASK) return false;
+	if (crash_behavior > CRASH_BEHAVIOR_NEVER_SEND) return false;
+
+	mCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, crash_behavior);
+	std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
+
+	mCrashSettings.saveToFile(filename, FALSE);
+
+	return true;
+}
+
+bool LLCrashLogger::sendCrashLogs()
+{
+	gatherFiles();
+
+	LLSD post_data;
+	post_data = constructPostData();
+
+	updateApplication("Sending reports...");
+
+	std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
+															   "SecondLifeCrashReport");
+	std::string report_file = dump_path + ".log";
+
+	std::ofstream out_file(report_file.c_str());
+	LLSDSerialize::toPrettyXML(post_data, out_file);
+	out_file.close();
+	LLHTTPClient::post(mCrashHost, post_data, new LLCrashLoggerResponder(), 5);
+
+	gBreak = false;
+	while(!gBreak)
+	{
+		updateApplication("Sending logs...");
+	}
+
+	if(!gSent)
+	{
+		gBreak = false;
+		LLHTTPClient::post(mAltCrashHost, post_data, new LLCrashLoggerResponder(), 5);
+
+		while(!gBreak)
+		{
+			updateApplication("Sending logs to Alternate Server...");
+		}
+	}
+	mSentCrashLogs = gSent;
+
+	return true;
+}
+
+void LLCrashLogger::updateApplication(LLString message)
+{
+	gServicePump->pump();
+    gServicePump->callback();
+}
+
+bool LLCrashLogger::init()
+{
+	// We assume that all the logs we're looking for reside on the current drive
+	gDirUtilp->initAppDirs("SecondLife");
+
+	// Default to the product name "Second Life" (this is overridden by the -name argument)
+	mProductName = "Second Life";
+	
+	mCrashSettings.declareS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ASK, "Controls behavior when viewer crashes "
+		"(0 = ask before sending crash report, 1 = always send crash report, 2 = never send crash report)");
+
+	llinfos << "Loading crash behavior setting" << llendl;
+	mCrashBehavior = loadCrashBehaviorSetting();
+
+	//Run through command line options
+	if(getOption("previous").isDefined())
+	{
+		llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl;
+		mCrashInPreviousExec = TRUE;
+	}
+
+	if(getOption("dialog").isDefined())
+	{
+		llinfos << "Show the user dialog" << llendl;
+		mCrashBehavior = CRASH_BEHAVIOR_ASK;
+	}
+
+	LLSD server = getOption("user");
+	if(server.isDefined())
+	{
+		mGridName = server.asString();
+		llinfos << "Got userserver " << mGridName << llendl;
+	}
+	else
+	{
+		mGridName = "agni";
+	}
+
+	LLSD name = getOption("name");
+	if(name.isDefined())
+	{	
+		mProductName = name.asString();
+	}
+
+	// If user doesn't want to send, bail out
+	if (mCrashBehavior == CRASH_BEHAVIOR_NEVER_SEND)
+	{
+		llinfos << "Crash behavior is never_send, quitting" << llendl;
+		return false;
+	}
+
+	gServicePump = new LLPumpIO(gAPRPoolp);
+	gServicePump->prime(gAPRPoolp);
+	LLHTTPClient::setPump(*gServicePump);
+	return true;
+}
diff --git a/indra/llcrashlogger/llcrashlogger.h b/indra/llcrashlogger/llcrashlogger.h
new file mode 100755
index 0000000000000000000000000000000000000000..d3218ab81c0c65c292e681b816a7314a71f91d54
--- /dev/null
+++ b/indra/llcrashlogger/llcrashlogger.h
@@ -0,0 +1,85 @@
+/** 
+* @file llcrashlogger.h
+* @brief Crash Logger Definition
+*
+* $LicenseInfo:firstyear=2003&license=viewergpl$
+* 
+* Copyright (c) 2003-2007, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 LLCRASHLOGGER_H
+#define LLCRASHLOGGER_H
+
+#include <vector>
+
+#include "linden_common.h"
+
+#include "llapp.h"
+#include "llsd.h"
+#include "llcontrol.h"
+
+class LLCrashLogger : public LLApp
+{
+public:
+	LLCrashLogger();
+	virtual ~LLCrashLogger();
+	S32 loadCrashBehaviorSetting();
+	void gatherFiles();
+	virtual void gatherPlatformSpecificFiles() {}
+	bool saveCrashBehaviorSetting(S32 crash_behavior);
+	bool sendCrashLogs();
+	LLSD constructPostData();
+	virtual void updateApplication(LLString message = "");
+	virtual bool init();
+	virtual bool mainLoop() = 0;
+	virtual bool cleanup() { return true; }
+	void setUserText(LLString& text) { mCrashInfo["UserNotes"] = text; }
+	S32 getCrashBehavior() { return mCrashBehavior; }
+protected:
+	S32 mCrashBehavior;
+	BOOL mCrashInPreviousExec;
+	std::map<LLString, LLString> mFileMap;
+	static const int mMaxSendSize = 200000;
+	LLString mGridName;
+	LLControlGroup mCrashSettings;
+	LLString mProductName;
+	LLSD mCrashInfo;
+	LLString mCrashHost;
+	LLString mAltCrashHost;
+	LLSD mDebugLog;
+	bool mSentCrashLogs;
+};
+
+class LLCrashLoggerText : public LLCrashLogger
+{
+public:
+	LLCrashLoggerText(void) {}
+	~LLCrashLoggerText(void) {}
+
+	virtual bool mainLoop();
+	virtual void updateApplication(LLString message = "");
+};
+
+
+#endif //LLCRASHLOGGER_H
diff --git a/indra/llmessage/llcircuit.cpp b/indra/llmessage/llcircuit.cpp
index 1d1be5634971771211f46fbe4884abef28721d01..0db9f8e2f1b6125ea6dec28e84f775a14fbf4507 100644
--- a/indra/llmessage/llcircuit.cpp
+++ b/indra/llmessage/llcircuit.cpp
@@ -1170,13 +1170,11 @@ std::ostream& operator<<(std::ostream& s, LLCircuitData& circuit)
 	return s;
 }
 
-const LLString LLCircuitData::getInfoString() const
+void LLCircuitData::getInfo(LLSD& info) const
 {
-	std::ostringstream info;
-	info << "Circuit: " << mHost << std::endl
-		 << (mbAlive ? "Alive" : "Not Alive") << std::endl
-		 << "Age: " << mExistenceTimer.getElapsedTimeF32() << std::endl;
-	return LLString(info.str());
+	info["Host"] = mHost.getIPandPort();
+	info["Alive"] = mbAlive;
+	info["Age"] = mExistenceTimer.getElapsedTimeF32();
 }
 
 void LLCircuitData::dumpResendCountAndReset()
@@ -1200,17 +1198,16 @@ std::ostream& operator<<(std::ostream& s, LLCircuit &circuit)
 	return s;
 }
 
-const LLString LLCircuit::getInfoString() const
+void LLCircuit::getInfo(LLSD& info) const
 {
-	std::ostringstream info;
-	info << "Circuit Info:" << std::endl;
 	LLCircuit::circuit_data_map::const_iterator end = mCircuitData.end();
 	LLCircuit::circuit_data_map::const_iterator it;
+	LLSD circuit_info;
 	for(it = mCircuitData.begin(); it != end; ++it)
 	{
-		info << (*it).second->getInfoString() << std::endl;
+		(*it).second->getInfo(circuit_info);
+		info["Circuits"].append(circuit_info);
 	}
-	return LLString(info.str());
 }
 
 void LLCircuit::getCircuitRange(
diff --git a/indra/llmessage/llcircuit.h b/indra/llmessage/llcircuit.h
index 128b1bc222c9470f5b9fb7447b96100689630206..1a6611f5d4a42a04232182e766ce12ac4abb2e14 100644
--- a/indra/llmessage/llcircuit.h
+++ b/indra/llmessage/llcircuit.h
@@ -75,6 +75,7 @@ const S32 LL_MAX_ACKED_PACKETS_PER_FRAME = 200;
 //
 class LLMessageSystem;
 class LLEncodedDatagramService;
+class LLSD;
 
 //
 // Classes
@@ -158,7 +159,7 @@ class LLCircuitData
 	//
 	void					checkPeriodTime();		// Reset per-period counters if necessary.
 	friend std::ostream&	operator<<(std::ostream& s, LLCircuitData &circuit);
-	const LLString getInfoString() const;
+	void getInfo(LLSD& info) const;
 
 	friend class LLCircuit;
 	friend class LLMessageSystem;
@@ -304,7 +305,7 @@ class LLCircuit
 	void sendAcks();
 
 	friend std::ostream& operator<<(std::ostream& s, LLCircuit &circuit);
-	const LLString getInfoString() const;
+	void getInfo(LLSD& info) const;
 
 	void			dumpResends();
 
diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp
index e2cd19b2647d26c3b4253b6c0836815d746ba1d6..d446730c335f5707267c40774575f4f270c55aff 100644
--- a/indra/llmessage/message.cpp
+++ b/indra/llmessage/message.cpp
@@ -1562,12 +1562,9 @@ U32 LLMessageSystem::getOurCircuitCode()
 	return mOurCircuitCode;
 }
 
-LLString LLMessageSystem::getCircuitInfoString()
+void LLMessageSystem::getCircuitInfo(LLSD& info) const
 {
-	LLString info_string;
-
-	info_string += mCircuitInfo.getInfoString();
-	return info_string;
+	mCircuitInfo.getInfo(info);
 }
 
 // returns whether the given host is on a trusted circuit
diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h
index 4454b40ab99cd9a0eac9102b5d8416d636f6a92e..3381ece222609bb7c96d9a2b9b0625d7588f4d1c 100644
--- a/indra/llmessage/message.h
+++ b/indra/llmessage/message.h
@@ -529,7 +529,7 @@ class LLMessageSystem
 	bool isMatchingDigestForWindow(const char* digest, const S32 window) const;
 
 	void	showCircuitInfo();
-	LLString getCircuitInfoString();
+	void getCircuitInfo(LLSD& info) const;
 
 	U32 getOurCircuitCode();
 	
diff --git a/indra/llvfs/lldir_win32.cpp b/indra/llvfs/lldir_win32.cpp
index b5db6efb4d6c9f5d7f0052185b881feb78b5dcd1..f415fe56ed354f860297418ac109f640d2488562 100644
--- a/indra/llvfs/lldir_win32.cpp
+++ b/indra/llvfs/lldir_win32.cpp
@@ -42,7 +42,6 @@
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <errno.h>
 
 // Utility stuff to get versions of the sh
 #define PACKVERSION(major,minor) MAKELONG(minor,major)
diff --git a/indra/llwindow/lldxhardware.cpp b/indra/llwindow/lldxhardware.cpp
index 1a39e68e3cd0fad5f3a98e005915ef70e9fbcad0..2aff05232e28b567b3ef7cbffd6077fd5535cd28 100644
--- a/indra/llwindow/lldxhardware.cpp
+++ b/indra/llwindow/lldxhardware.cpp
@@ -411,6 +411,7 @@ BOOL LLDXHardware::getInfo(BOOL vram_only)
 			}
 
 			std::string device_name = get_string(device_containerp, L"szDescription");
+
 			std::string device_id = get_string(device_containerp, L"szDeviceID");
 
 			LLDXDevice *dxdevicep = new LLDXDevice;
@@ -451,6 +452,8 @@ BOOL LLDXHardware::getInfo(BOOL vram_only)
 			}
 
 
+
+
 			// Now, iterate through the related drivers
 			hr = device_containerp->GetChildContainer(L"Drivers", &driver_containerp);
 			if (FAILED(hr) || !driver_containerp)
@@ -468,6 +471,7 @@ BOOL LLDXHardware::getInfo(BOOL vram_only)
 			S32 file_num = 0;
 			for (file_num = 0; file_num < (S32)num_files; file_num++ )
 			{
+
 				hr = driver_containerp->EnumChildContainerNames(file_num, wszContainer, 256);
 				if (FAILED(hr))
 				{
@@ -522,6 +526,104 @@ BOOL LLDXHardware::getInfo(BOOL vram_only)
     return ok;
 }
 
+LLSD LLDXHardware::getDisplayInfo()
+{
+	LLTimer hw_timer;
+    HRESULT       hr;
+	LLSD ret;
+    CoInitialize(NULL);
+
+    IDxDiagProvider *dx_diag_providerp = NULL;
+    IDxDiagContainer *dx_diag_rootp = NULL;
+	IDxDiagContainer *devices_containerp = NULL;
+	IDxDiagContainer *device_containerp = NULL;
+	IDxDiagContainer *file_containerp = NULL;
+	IDxDiagContainer *driver_containerp = NULL;
+
+    // CoCreate a IDxDiagProvider*
+	llinfos << "CoCreateInstance IID_IDxDiagProvider" << llendl;
+    hr = CoCreateInstance(CLSID_DxDiagProvider,
+                          NULL,
+                          CLSCTX_INPROC_SERVER,
+                          IID_IDxDiagProvider,
+                          (LPVOID*) &dx_diag_providerp);
+
+	if (FAILED(hr))
+	{
+		llwarns << "No DXDiag provider found!  DirectX 9 not installed!" << llendl;
+		gWriteDebug("No DXDiag provider found!  DirectX 9 not installed!\n");
+		goto LCleanup;
+	}
+    if (SUCCEEDED(hr)) // if FAILED(hr) then dx9 is not installed
+    {
+        // Fill out a DXDIAG_INIT_PARAMS struct and pass it to IDxDiagContainer::Initialize
+        // Passing in TRUE for bAllowWHQLChecks, allows dxdiag to check if drivers are 
+        // digital signed as logo'd by WHQL which may connect via internet to update 
+        // WHQL certificates.    
+        DXDIAG_INIT_PARAMS dx_diag_init_params;
+        ZeroMemory(&dx_diag_init_params, sizeof(DXDIAG_INIT_PARAMS));
+
+        dx_diag_init_params.dwSize                  = sizeof(DXDIAG_INIT_PARAMS);
+        dx_diag_init_params.dwDxDiagHeaderVersion   = DXDIAG_DX9_SDK_VERSION;
+        dx_diag_init_params.bAllowWHQLChecks        = TRUE;
+        dx_diag_init_params.pReserved               = NULL;
+
+		llinfos << "dx_diag_providerp->Initialize" << llendl;
+        hr = dx_diag_providerp->Initialize(&dx_diag_init_params);
+        if(FAILED(hr))
+		{
+            goto LCleanup;
+		}
+
+		llinfos << "dx_diag_providerp->GetRootContainer" << llendl;
+        hr = dx_diag_providerp->GetRootContainer( &dx_diag_rootp );
+        if(FAILED(hr) || !dx_diag_rootp)
+		{
+            goto LCleanup;
+		}
+
+		HRESULT hr;
+
+		// Get display driver information
+		llinfos << "dx_diag_rootp->GetChildContainer" << llendl;
+		hr = dx_diag_rootp->GetChildContainer(L"DxDiag_DisplayDevices", &devices_containerp);
+		if(FAILED(hr) || !devices_containerp)
+		{
+            goto LCleanup;
+		}
+
+		// Get device 0
+		llinfos << "devices_containerp->GetChildContainer" << llendl;
+		hr = devices_containerp->GetChildContainer(L"0", &device_containerp);
+		if(FAILED(hr) || !device_containerp)
+		{
+            goto LCleanup;
+		}
+		
+		// Get the English VRAM string
+		std::string ram_str = get_string(device_containerp, L"szDisplayMemoryEnglish");
+
+
+		// Dump the string as an int into the structure
+		char *stopstring;
+		ret["VRAM"] = strtol(ram_str.c_str(), &stopstring, 10);
+		std::string device_name = get_string(device_containerp, L"szDescription");
+		ret["DeviceName"] = device_name;
+		std::string device_driver=  get_string(device_containerp, L"szDriverVersion");
+		ret["DriverVersion"] = device_driver;
+	}
+LCleanup:
+	SAFE_RELEASE(file_containerp);
+	SAFE_RELEASE(driver_containerp);
+	SAFE_RELEASE(device_containerp);
+	SAFE_RELEASE(devices_containerp);
+    SAFE_RELEASE(dx_diag_rootp);
+    SAFE_RELEASE(dx_diag_providerp);
+    
+    CoUninitialize();
+	return ret;
+}
+
 void LLDXHardware::setWriteDebugFunc(void (*func)(const char*))
 {
 	gWriteDebug = func;
diff --git a/indra/llwindow/lldxhardware.h b/indra/llwindow/lldxhardware.h
index 1d5d69d8ca49f44f3cc08d99531f3579ed8bbb9a..e2a255da76bb54dcc2933d2eb5165e91696cf2e1 100644
--- a/indra/llwindow/lldxhardware.h
+++ b/indra/llwindow/lldxhardware.h
@@ -36,6 +36,7 @@
 
 #include "stdtypes.h"
 #include "llstring.h"
+#include "llsd.h"
 
 class LLVersion
 {
@@ -93,6 +94,8 @@ class LLDXHardware
 
 	S32 getVRAM() const { return mVRAM; }
 
+	LLSD getDisplayInfo();
+
 	// Find a particular device that matches the following specs.
 	// Empty strings indicate that you don't care.
 	// You can separate multiple devices with '|' chars to indicate you want
diff --git a/indra/mac_crash_logger/llcrashloggermac.cpp b/indra/mac_crash_logger/llcrashloggermac.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ba50af9d000dc53c88025adc056f817832178e26
--- /dev/null
+++ b/indra/mac_crash_logger/llcrashloggermac.cpp
@@ -0,0 +1,310 @@
+/** 
+ * @file llcrashloggermac.cpp
+ * @brief Mac OSX crash logger implementation
+ *
+ * $LicenseInfo:firstyear=2003&license=viewergpl$
+ * 
+ * Copyright (c) 2003-2007, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llcrashloggermac.h"
+
+#include <Carbon/Carbon.h>
+#include <iostream>
+#include <sstream>
+
+#include "boost/tokenizer.hpp"
+
+#include "indra_constants.h"	// CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
+#include "llerror.h"
+#include "llfile.h"
+#include "lltimer.h"
+#include "llstring.h"
+#include "lldir.h"
+#include "llsdserialize.h"
+
+#define MAX_LOADSTRING 100
+const char* const SETTINGS_FILE_HEADER = "version";
+const S32 SETTINGS_FILE_VERSION = 101;
+
+// Windows Message Handlers
+
+BOOL gFirstDialog = TRUE;	// Are we currently handling the Send/Don't Send dialog?
+FILE *gDebugFile = NULL;
+
+WindowRef gWindow = NULL;
+EventHandlerRef gEventHandler = NULL;
+LLString gUserNotes = "";
+bool gSendReport = false;
+bool gRememberChoice = false;
+IBNibRef nib = NULL;
+
+OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata)
+{
+	OSStatus result = eventNotHandledErr;
+	OSStatus err;
+	UInt32 evtClass = GetEventClass(event);
+	UInt32 evtKind = GetEventKind(event);
+	if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
+	{
+		HICommand cmd;
+		err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd);
+		
+
+		
+		if(err == noErr)
+		{
+			//Get the value of the checkbox
+			ControlID id;
+			ControlRef checkBox = NULL;
+			id.signature = 'remb';
+			id.id = 0;
+			err = GetControlByID(gWindow, &id, &checkBox);
+			
+			if(err == noErr)
+			{
+				if(GetControl32BitValue(checkBox) == kControlCheckBoxCheckedValue)
+				{
+					gRememberChoice = true;
+				}
+				else
+				{
+					gRememberChoice = false;
+				}
+			}	
+			switch(cmd.commandID)
+			{
+				case kHICommandOK:
+				{
+					char buffer[65535];		/* Flawfinder: ignore */
+					Size size = sizeof(buffer) - 1;
+					ControlRef textField = NULL;
+
+					id.signature = 'text';
+					id.id = 0;
+
+					err = GetControlByID(gWindow, &id, &textField);
+					if(err == noErr)
+					{
+						// Get the user response text
+						err = GetControlData(textField, kControlNoPart, kControlEditTextTextTag, size, (Ptr)buffer, &size);
+					}
+					if(err == noErr)
+					{
+						// Make sure the string is terminated.
+						buffer[size] = 0;
+						//setUserText(buffer);
+						llinfos << buffer << llendl;
+					}
+					
+					// Send the report.
+
+					QuitAppModalLoopForWindow(gWindow);
+					gSendReport = true;
+					result = noErr;
+				}
+				break;
+				
+				case kHICommandCancel:
+					QuitAppModalLoopForWindow(gWindow);
+					result = noErr;
+				break;
+			}
+		}
+	}
+
+	return(result);
+}
+
+
+LLCrashLoggerMac::LLCrashLoggerMac(void)
+{
+}
+
+LLCrashLoggerMac::~LLCrashLoggerMac(void)
+{
+}
+
+bool LLCrashLoggerMac::init(void)
+{	
+	bool ok = LLCrashLogger::init();
+	if(!ok) return false;
+	// Real UI...
+	OSStatus err;
+	
+	err = CreateNibReference(CFSTR("CrashReporter"), &nib);
+	
+	if(err == noErr)
+	{
+		err = CreateWindowFromNib(nib, CFSTR("CrashReporter"), &gWindow);
+	}
+
+	if(err == noErr)
+	{
+		// Set focus to the edit text area
+		ControlRef textField = NULL;
+		ControlID id;
+
+		id.signature = 'text';
+		id.id = 0;
+		
+		// Don't set err if any of this fails, since it's non-critical.
+		if(GetControlByID(gWindow, &id, &textField) == noErr)
+		{
+			SetKeyboardFocus(gWindow, textField, kControlFocusNextPart);
+		}
+	}
+	
+	if(err == noErr)
+	{
+		ShowWindow(gWindow);
+	}
+	
+	if(err == noErr)
+	{
+		// Set up an event handler for the window.
+		EventTypeSpec handlerEvents[] = 
+		{
+			{ kEventClassCommand, kEventCommandProcess }
+		};
+
+		InstallWindowEventHandler(
+				gWindow, 
+				NewEventHandlerUPP(dialogHandler), 
+				GetEventTypeCount (handlerEvents), 
+				handlerEvents, 
+				0, 
+				&gEventHandler);
+	}
+	return true;
+}
+
+void LLCrashLoggerMac::gatherPlatformSpecificFiles()
+{
+	updateApplication("Gathering hardware information...");
+	char path[MAX_PATH];		
+	FSRef folder;
+	
+	if(FSFindFolder(kUserDomain, kLogsFolderType, false, &folder) == noErr)
+	{
+		// folder is an FSRef to ~/Library/Logs/
+		if(FSRefMakePath(&folder, (UInt8*)&path, sizeof(path)) == noErr)
+		{
+			struct stat dw_stat;
+			LLString mBuf;
+			// Try the 10.3 path first...
+			LLString dw_file_name = LLString(path) + LLString("/CrashReporter/Second Life.crash.log");
+			int res = stat(dw_file_name.c_str(), &dw_stat);
+
+			if (res)
+			{
+				// Try the 10.2 one next...
+				dw_file_name = LLString(path) + LLString("/Second Life.crash.log");
+				res = stat(dw_file_name.c_str(), &dw_stat);
+			}
+				
+			if (!res)
+			{
+				std::ifstream fp(dw_file_name.c_str());
+				std::stringstream str;
+				if(!fp.is_open()) return;
+				str << fp.rdbuf();
+				mBuf = str.str();
+				
+				// Crash logs consist of a number of entries, one per crash.
+				// Each entry is preceeded by "**********" on a line by itself.
+				// We want only the most recent (i.e. last) one.
+				const char *sep = "**********";
+				const char *start = mBuf.c_str();
+				const char *cur = start;
+				const char *temp = strstr(cur, sep);
+				
+				while(temp != NULL)
+				{
+					// Skip past the marker we just found
+					cur = temp + strlen(sep);		/* Flawfinder: ignore */
+					
+					// and try to find another
+					temp = strstr(cur, sep);
+				}
+				
+				// If there's more than one entry in the log file, strip all but the last one.
+				if(cur != start)
+				{
+					mBuf.erase(0, cur - start);
+				}
+				mDebugLog["CrashInfo"] = mBuf;
+			}
+			else
+			{
+				llwarns << "Couldn't find any CrashReporter files..." << llendl;
+			}
+		}
+	}
+}
+
+bool LLCrashLoggerMac::mainLoop()
+{
+	OSStatus err = noErr;
+				
+	if(err == noErr)
+	{
+		RunAppModalLoopForWindow(gWindow);
+	}
+	
+	if(gRememberChoice)
+	{
+		if(gSendReport) saveCrashBehaviorSetting(CRASH_BEHAVIOR_ALWAYS_SEND);
+		else saveCrashBehaviorSetting(CRASH_BEHAVIOR_NEVER_SEND);
+	}
+	
+	if(gSendReport)
+	{
+		sendCrashLogs();
+	}		
+	
+	if(gWindow != NULL)
+	{
+		DisposeWindow(gWindow);
+	}
+	
+	if(nib != NULL)
+	{
+		DisposeNibReference(nib);
+	}
+	
+	return true;
+}
+
+void LLCrashLoggerMac::updateApplication(LLString message)
+{
+	LLCrashLogger::updateApplication();
+}
+
+bool LLCrashLoggerMac::cleanup()
+{
+	return true;
+}
diff --git a/indra/mac_crash_logger/llcrashloggermac.h b/indra/mac_crash_logger/llcrashloggermac.h
new file mode 100644
index 0000000000000000000000000000000000000000..cf4e766bda5c3aae9c3e023ba385704dc3ce4a3e
--- /dev/null
+++ b/indra/mac_crash_logger/llcrashloggermac.h
@@ -0,0 +1,51 @@
+/** 
+ * @file llcrashloggermac.h
+ * @brief Mac OSX crash logger definition
+ *
+ * $LicenseInfo:firstyear=2003&license=viewergpl$
+ * 
+ * Copyright (c) 2003-2007, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 LLCRASHLOGGERMAC_H
+#define LLCRASHLOGGERMAC_H
+
+#include "linden_common.h"
+#include "llcrashlogger.h"
+#include "llstring.h"
+
+class LLCrashLoggerMac : public LLCrashLogger
+{
+public:
+	LLCrashLoggerMac(void);
+	~LLCrashLoggerMac(void);
+	virtual bool init();
+	virtual bool mainLoop();
+	virtual void updateApplication(LLString message = "");
+	virtual bool cleanup();
+	virtual void gatherPlatformSpecificFiles();
+};
+
+#endif
diff --git a/indra/mac_crash_logger/mac_crash_logger.cpp b/indra/mac_crash_logger/mac_crash_logger.cpp
index 2501b4a521816920325a9cfbef6314ed4744a7cb..bf3151a490e1c136ab9e080a17f808e60ccd6f3f 100644
--- a/indra/mac_crash_logger/mac_crash_logger.cpp
+++ b/indra/mac_crash_logger/mac_crash_logger.cpp
@@ -31,675 +31,21 @@
 
 #include "linden_common.h"
 
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <curl/curl.h>
-
-#include "llerror.h"
-#include "lltimer.h"
-#include "lldir.h"
-
-#include "llstring.h"
-
-class LLFileEncoder
-{
-public:
-	LLFileEncoder(const char *formname, const char *filename, bool isCrashLog = false);
-
-	BOOL isValid() const { return mIsValid; }
-	LLString encodeURL(const S32 max_length = 0);
-public:
-	BOOL mIsValid;
-	LLString mFilename;
-	LLString mFormname;
-	LLString mBuf;
-};
-
-LLString encode_string(const char *formname, const LLString &str);
-
-#include <Carbon/Carbon.h>
-
-LLString gServerResponse;
-BOOL gSendReport = FALSE;
-LLString gUserserver;
-LLString gUserText;
-WindowRef gWindow = NULL;
-EventHandlerRef gEventHandler = NULL;
-BOOL gCrashInPreviousExec = FALSE;
-time_t gLaunchTime;
-
-size_t curl_download_callback(void *data, size_t size, size_t nmemb,
-										  void *user_data)
-{
-	S32 bytes = size * nmemb;
-	char *cdata = (char *) data;
-	for (int i =0; i < bytes; i += 1)
-	{
-		gServerResponse += (cdata[i]);
-	}
-	return bytes;
-}
-
-OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata)
-{
-	OSStatus result = eventNotHandledErr;
-	OSStatus err;
-	UInt32 evtClass = GetEventClass(event);
-	UInt32 evtKind = GetEventKind(event);
-	
-	if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
-	{
-		HICommand cmd;
-		err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd);
-		
-		if(err == noErr)
-		{
-			switch(cmd.commandID)
-			{
-				case kHICommandOK:
-				{
-					char buffer[65535];		/* Flawfinder: ignore */
-					Size size = sizeof(buffer) - 1;
-					ControlRef textField = NULL;
-					ControlID id;
-
-					id.signature = 'text';
-					id.id = 0;
-
-					err = GetControlByID(gWindow, &id, &textField);
-					if(err == noErr)
-					{
-						// Get the user response text
-						err = GetControlData(textField, kControlNoPart, kControlEditTextTextTag, size, (Ptr)buffer, &size);
-					}
-					if(err == noErr)
-					{
-						// Make sure the string is terminated.
-						buffer[size] = 0;
-						gUserText = buffer;
-						llinfos << buffer << llendl;
-					}
-					
-					// Send the report.
-					gSendReport = TRUE;
-
-					QuitAppModalLoopForWindow(gWindow);
-					result = noErr;
-				}
-				break;
-				
-				case kHICommandCancel:
-					QuitAppModalLoopForWindow(gWindow);
-					result = noErr;
-				break;
-			}
-		}
-	}
-	
-	return(result);
-}
+#include "llcrashloggermac.h"
 
 int main(int argc, char **argv)
 {
-	const S32 DW_MAX_SIZE = 100000;			// Maximum size to transmit of the Dr. Watson log file
-	const S32 SL_MAX_SIZE = 100000;			// Maximum size of the Second Life log file.
-	int i;
-	
-	time(&gLaunchTime);
+	//time(&gLaunchTime);
 	
 	llinfos << "Starting Second Life Viewer Crash Reporter" << llendl;
-	
-	for(i=1; i<argc; i++)
-	{
-		if(!strcmp(argv[i], "-previous"))
-		{
-			gCrashInPreviousExec = TRUE;
-		}
-		if(!strcmp(argv[i], "-user"))
-		{
-			if ((i + 1) < argc)
-			{
-				i++;
-				gUserserver = argv[i];
-				llinfos << "Got userserver " << gUserserver << llendl;
-			}
-		}
-	}
-	
-	if( gCrashInPreviousExec )
-	{
-		llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl;
-	}
-	
-	if(!gCrashInPreviousExec)
-	{
-		// Delay five seconds to let CrashReporter do its thing.
-		sleep(5);
-	}
-		
-#if 1
-	// Real UI...
-	OSStatus err;
-	IBNibRef nib = NULL;
-	
-	err = CreateNibReference(CFSTR("CrashReporter"), &nib);
-	
-	if(err == noErr)
-	{
-		if(gCrashInPreviousExec)
-		{
-			err = CreateWindowFromNib(nib, CFSTR("CrashReporterDelayed"), &gWindow);
-		}
-		else
-		{
-			err = CreateWindowFromNib(nib, CFSTR("CrashReporter"), &gWindow);
-		}
-	}
-
-	if(err == noErr)
-	{
-		// Set focus to the edit text area
-		ControlRef textField = NULL;
-		ControlID id;
-
-		id.signature = 'text';
-		id.id = 0;
-		
-		// Don't set err if any of this fails, since it's non-critical.
-		if(GetControlByID(gWindow, &id, &textField) == noErr)
-		{
-			SetKeyboardFocus(gWindow, textField, kControlFocusNextPart);
-		}
-	}
-	
-	if(err == noErr)
-	{
-		ShowWindow(gWindow);
-	}
-	
-	if(err == noErr)
-	{
-		// Set up an event handler for the window.
-		EventTypeSpec handlerEvents[] = 
-		{
-			{ kEventClassCommand, kEventCommandProcess }
-		};
-
-		InstallWindowEventHandler(
-				gWindow, 
-				NewEventHandlerUPP(dialogHandler), 
-				GetEventTypeCount (handlerEvents), 
-				handlerEvents, 
-				0, 
-				&gEventHandler);
-	}
-			
-	if(err == noErr)
-	{
-		RunAppModalLoopForWindow(gWindow);
-	}
-			
-	if(gWindow != NULL)
-	{
-		DisposeWindow(gWindow);
-	}
-	
-	if(nib != NULL)
-	{
-		DisposeNibReference(nib);
-	}
-#else
-	// Cheap version -- just use the standard system alert.
-	SInt16 itemHit = 0;
-	AlertStdCFStringAlertParamRec params;
-	OSStatus err = noErr;
-	DialogRef alert = NULL;
-
-	params.version = kStdCFStringAlertVersionOne;
-	params.movable = false;
-	params.helpButton = false;
-	params.defaultText = CFSTR("Send Report");
-	params.cancelText = CFSTR("Don't Send Report");
-	params.otherText = 0;
-	params.defaultButton = kAlertStdAlertOKButton;
-	params.cancelButton = kAlertStdAlertCancelButton;
-	params.position = kWindowDefaultPosition;
-	params.flags = 0;
-
-	err = CreateStandardAlert(
-			kAlertCautionAlert,
-			CFSTR("Second Life appears to have crashed."),
-			CFSTR(
-			"To help us debug the problem, you can send a crash report to Linden Lab.  "
-			"The report contains information about your microprocessor type, graphics card, "
-			"memory, things that happened during the last run of the program, and the Crash Log "
-			"of where the crash occurred.\r"
-			"\r"
-			"You may also report crashes in the forums, or by sending e-mail to peter@lindenlab.com"),
-			&params,
-			&alert);
-	
-	if(err == noErr)
-	{
-		err = RunStandardAlert(
-				alert,
-				NULL,
-				&itemHit);
-  	}
-	
-	if(itemHit == kAlertStdAlertOKButton)
-		gSendReport = TRUE;
-#endif
-	
-	if(!gSendReport)
-	{
-		// Only send the report if the user agreed to it.
-		llinfos << "User cancelled, not sending report" << llendl;
-
-		return(0);
-	}
-
-	// We assume that all the logs we're looking for reside on the current drive
-	gDirUtilp->initAppDirs("SecondLife");
-
-	int res;
-
-	// Lots of silly variable, replicated for each log file.
-	LLString db_file_name;
-	LLString sl_file_name;
-	LLString dw_file_name; // DW file name is a hack for now...
-	LLString st_file_name; // stats.log file
-	LLString si_file_name; // settings.ini file
-
-	LLFileEncoder *db_filep = NULL;
-	LLFileEncoder *sl_filep = NULL;
-	LLFileEncoder *st_filep = NULL;
-	LLFileEncoder *dw_filep = NULL;
-	LLFileEncoder *si_filep = NULL;
-
-	///////////////////////////////////
-	//
-	// We do the parsing for the debug_info file first, as that will
-	// give us the location of the SecondLife.log file.
-	//
-
-	// Figure out the filename of the debug log
-	db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log").c_str();
-	db_filep = new LLFileEncoder("DB", db_file_name.c_str());
-
-	// Get the filename of the SecondLife.log file
-
-	// *NOTE: changing the size of either of these buffers will
-	// require changing the sscanf() format string to correctly
-	// account for it.
-	char tmp_sl_name[LL_MAX_PATH];	/* Flawfinder: ignore */
-	tmp_sl_name[0] = '\0';
-	char tmp_space[MAX_STRING];		/* Flawfinder: ignore */
-	tmp_space[0] = '\0';
-
-	// Look for it in the debug_info.log file
-	if (db_filep->isValid())
-	{
-		// This was originally scanning for "SL Log: %[^\r\n]", which happily skipped to the next line
-		// on debug logs (which don't have anything after "SL Log:" and tried to open a nonsensical filename.
-		sscanf(
-			db_filep->mBuf.c_str(),
-			"SL Log:%254[ ]%1023[^\r\n]",
-			tmp_space,
-			tmp_sl_name);
-	}
-	else
-	{
-		delete db_filep;
-		db_filep = NULL;
-	}
-
-	// If we actually have a legitimate file name, use it.
-	if (tmp_sl_name[0])
-	{
-		sl_file_name = tmp_sl_name;
-		llinfos << "Using log file from debug log " << sl_file_name << llendl;
-	}
-	else
-	{
-		// Figure out the filename of the second life log
-		sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log").c_str();
-	}
-
-	// Now we get the SecondLife.log file if it's there, and recent enough...
-	sl_filep = new LLFileEncoder("SL", sl_file_name.c_str());
-	if (!sl_filep->isValid())
-	{
-		delete sl_filep;
-		sl_filep = NULL;
-	}
-
-	st_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log").c_str();
-	st_filep = new LLFileEncoder("ST", st_file_name.c_str());
-	if (!st_filep->isValid())
-	{
-		delete st_filep;
-		st_filep = NULL;
-	}
-
-	si_file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.ini").c_str();
-	si_filep = new LLFileEncoder("SI", si_file_name.c_str());
-	if (!si_filep->isValid())
-	{
-		delete si_filep;
-		si_filep = NULL;
-	}
-
-	// MBW -- This needs to go find "~/Library/Logs/CrashReporter/Second Life.crash.log" on 10.3
-	// or "~/Library/Logs/Second Life.crash.log" on 10.2.
-	{
-		char path[MAX_PATH];		/* Flawfinder: ignore */
-		FSRef folder;
-		
-		if(FSFindFolder(kUserDomain, kLogsFolderType, false, &folder) == noErr)
-		{
-			// folder is an FSRef to ~/Library/Logs/
-			if(FSRefMakePath(&folder, (UInt8*)&path, sizeof(path)) == noErr)
-			{
-				struct stat dw_stat;
-//				printf("path is %s\n", path);
-				
-				// Try the 10.3 path first...
-				dw_file_name = LLString(path) + LLString("/CrashReporter/Second Life.crash.log");
-				res = stat(dw_file_name.c_str(), &dw_stat);
-
-				if (res)
-				{
-					// Try the 10.2 one next...
-					dw_file_name = LLString(path) + LLString("/Second Life.crash.log");
-					res = stat(dw_file_name.c_str(), &dw_stat);
-				}
-				
-				if (!res)
-				{
-					dw_filep = new LLFileEncoder("DW", dw_file_name.c_str(), true);
-					if (!dw_filep->isValid())
-					{
-						delete dw_filep;
-						dw_filep = NULL;
-					}
-				}
-				else
-				{
-					llwarns << "Couldn't find any CrashReporter files..." << llendl;
-				}
-			}
-		}
-	}
-
-	LLString post_data;
-	LLString tmp_url_buf;
-
-	// Append the userserver
-	tmp_url_buf = encode_string("USER", gUserserver);
-	post_data += tmp_url_buf;
-	llinfos << "PostData:" << post_data << llendl;
-
-	if (gCrashInPreviousExec)
-	{
-		post_data.append("&");
-		tmp_url_buf = encode_string("EF", "Y");
-		post_data += tmp_url_buf;
-	}
-
-	if (db_filep)
-	{
-		post_data.append("&");
-		tmp_url_buf = db_filep->encodeURL();
-		post_data += tmp_url_buf;
-		llinfos << "Sending DB log file" << llendl;
-	}
-	else
-	{
-		llinfos << "Not sending DB log file" << llendl;
-	}
-
-	if (sl_filep)
-	{
-		post_data.append("&");
-		tmp_url_buf = sl_filep->encodeURL(SL_MAX_SIZE);
-		post_data += tmp_url_buf;
-		llinfos << "Sending SL log file" << llendl;
-	}
-	else
-	{
-		llinfos << "Not sending SL log file" << llendl;
-	}
-
-	if (st_filep)
-	{
-		post_data.append("&");
-		tmp_url_buf = st_filep->encodeURL(SL_MAX_SIZE);
-		post_data += tmp_url_buf;
-		llinfos << "Sending stats log file" << llendl;
-	}
-	else
-	{
-		llinfos << "Not sending stats log file" << llendl;
-	}
 
-	if (dw_filep)
+	LLCrashLoggerMac app;
+	app.parseCommandOptions(argc, argv);
+	if(!app.init())
 	{
-		post_data.append("&");
-		tmp_url_buf = dw_filep->encodeURL(DW_MAX_SIZE);
-		post_data += tmp_url_buf;
+		return 0;
 	}
-	else
-	{
-		llinfos << "Not sending crash log file" << llendl;
-	}
-
-	if (si_filep)
-	{
-		post_data.append("&");
-		tmp_url_buf = si_filep->encodeURL();
-		post_data += tmp_url_buf;
-		llinfos << "Sending settings log file" << llendl;
-	}
-	else
-	{
-		llinfos << "Not sending settings.ini file" << llendl;
-	}
-
-	if (gUserText.size())
-	{
-		post_data.append("&");
-		tmp_url_buf = encode_string("UN", gUserText);
-		post_data += tmp_url_buf;
-	}
-
-	delete db_filep;
-	db_filep = NULL;
-	delete sl_filep;
-	sl_filep = NULL;
-	delete dw_filep;
-	dw_filep = NULL;
-
-	// Debugging spam
-#if 0
-	printf("Crash report post data:\n--------\n");
-	printf("%s", post_data.getString());
-	printf("\n--------\n");
-#endif
-	
-	// Send the report.  Yes, it's this easy.
-	{
-		CURL *curl = curl_easy_init();
-
-		curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
-		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback);
-		curl_easy_setopt(curl, CURLOPT_POST, 1); 
-		curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data.c_str()); 
-		curl_easy_setopt(curl, CURLOPT_URL,	"http://secondlife.com/cgi-bin/viewer_crash_reporter2");
-		
-		llinfos << "Connecting to crash report server" << llendl;
-		CURLcode result = curl_easy_perform(curl);
-		
-		curl_easy_cleanup(curl);
+	app.mainLoop();
 		
-		if(result != CURLE_OK)
-		{
-			llinfos << "Couldn't talk to crash report server" << llendl;
-		}
-		else
-		{
-			llinfos << "Response from crash report server:" << llendl;
-			llinfos << gServerResponse << llendl;			
-		}
-	}
-	
 	return 0;
 }
-
-LLFileEncoder::LLFileEncoder(const char *form_name, const char *filename, bool isCrashLog)
-{
-	mFormname = form_name;
-	mFilename = filename;
-	mIsValid = FALSE;
-
-	int res;
-	
-	struct stat stat_data;
-	res = stat(mFilename.c_str(), &stat_data);
-	if (res)
-	{
-		llwarns << "File " << mFilename << " is missing!" << llendl;
-		return;
-	}
-	else
-	{
-		// Debugging spam
-//		llinfos << "File " << mFilename << " is present..." << llendl;
-
-		if(!gCrashInPreviousExec && isCrashLog)
-		{
-			// Make sure the file isn't too old.
-			double age = difftime(gLaunchTime, stat_data.st_mtimespec.tv_sec);
-
-//			llinfos << "age is " << age << llendl;
-
-			if(age > 60.0)
-			{
-				// The file was last modified more than 60 seconds before the crash reporter was launched.  Assume it's stale.
-				llwarns << "File " << mFilename << " is too old!" << llendl;
-				return;
-			}
-		}
-
-	}
-
-	S32 buf_size = stat_data.st_size;
-	FILE* fp = fopen(mFilename.c_str(), "rb");		/* Flawfinder: ignore */
-	U8 *buf = new U8[buf_size + 1];
-	fread(buf, 1, buf_size, fp);
-	fclose(fp);
-	buf[buf_size] = 0;
-
-	mBuf = (char *)buf;
-	
-	if(isCrashLog)
-	{
-		// Crash logs consist of a number of entries, one per crash.
-		// Each entry is preceeded by "**********" on a line by itself.
-		// We want only the most recent (i.e. last) one.
-		const char *sep = "**********";
-		const char *start = mBuf.c_str();
-		const char *cur = start;
-		const char *temp = strstr(cur, sep);
-		
-		while(temp != NULL)
-		{
-			// Skip past the marker we just found
-			cur = temp + strlen(sep);		/* Flawfinder: ignore */
-			
-			// and try to find another
-			temp = strstr(cur, sep);
-		}
-		
-		// If there's more than one entry in the log file, strip all but the last one.
-		if(cur != start)
-		{
-			mBuf.erase(0, cur - start);
-		}
-	}
-
-	mIsValid = TRUE;
-	delete[] buf;
-}
-
-LLString LLFileEncoder::encodeURL(const S32 max_length)
-{
-	LLString result = mFormname;
-	result.append("=");
-
-	S32 i = 0;
-
-	if (max_length)
-	{
-		if (mBuf.size() > max_length)
-		{
-			i = mBuf.size() - max_length;
-		}
-	}
-
-#if 0
-	// Plain text version for debugging
-	result.append(mBuf);
-#else
-	// Not using LLString because of bad performance issues
-	S32 buf_size = mBuf.size();
-	S32 url_buf_size = 3*mBuf.size() + 1;
-	char *url_buf = new char[url_buf_size];
-	if (url_buf == NULL)
-	{
-		llerrs << "Memory Allocation Failed" << llendl;
-		return result;
-	}
-	S32 cur_pos = 0;
-	for (; i < buf_size; i++)
-	{
-		sprintf(url_buf + cur_pos, "%%%02x", mBuf[i]);		/* Flawfinder: ignore */
-		cur_pos += 3;
-	}
-	url_buf[i*3] = 0;
-
-	result.append(url_buf);
-	delete[] url_buf;
-#endif
-	return result;
-}
-
-LLString encode_string(const char *formname, const LLString &str)
-{
-	LLString result = formname;
-	result.append("=");
-	// Not using LLString because of bad performance issues
-	S32 buf_size = str.size();
-	S32 url_buf_size = 3*str.size() + 1;
-	char *url_buf = new char[url_buf_size];
-	if (url_buf == NULL)
-	{
-		llerrs << "Memory Allocation Failed" << llendl;
-	    return result;
-	}
-
-	S32 cur_pos = 0;
-	S32 i;
-	for (i = 0; i < buf_size; i++)
-	{
-		sprintf(url_buf + cur_pos, "%%%02x", str[i]);		/* Flawfinder: ignore */
-		cur_pos += 3;
-	}
-	url_buf[i*3] = 0;
-
-	result.append(url_buf);
-	delete[] url_buf;
-	return result;
-}
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index c0e983382966addf66e1ae59f02748f61b33340c..63d1986deca0f8ff20f955064efe1f00b67fc4a5 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -106,6 +106,8 @@
 #include "llcontainerview.h"
 #include "llhoverview.h"
 
+#include "llsdserialize.h"
+
 #if LL_WINDOWS && LL_LCD_COMPILE
 	#include "lllcd.h"
 #endif
@@ -267,6 +269,8 @@ BOOL gAcceptCriticalMessage = FALSE;
 LLUUID				gViewerDigest;	// MD5 digest of the viewer's executable file.
 BOOL gLastExecFroze = FALSE;
 
+LLSD gDebugInfo;
+
 U32	gFrameCount = 0;
 U32 gForegroundFrameCount = 0; // number of frames that app window was in foreground
 LLPumpIO*			gServicePump = NULL;
@@ -915,7 +919,6 @@ LLTextureFetch* LLAppViewer::sTextureFetch = NULL;
 LLAppViewer::LLAppViewer() : 
 	mMarkerFile(NULL),
 	mLastExecFroze(false),
-	mDebugFile(NULL),
 	mCrashBehavior(CRASH_BEHAVIOR_ASK),
 	mReportedCrash(false),
 	mNumSessions(0),
@@ -1220,7 +1223,7 @@ bool LLAppViewer::init()
 		CreateLCDDebugWindows();
 	#endif
 
-	writeDebug(gGLManager.getGLInfoString());
+	gGLManager.getGLInfo(gDebugInfo);
 	llinfos << gGLManager.getGLInfoString() << llendl;
 
 	//load key settings
@@ -2351,30 +2354,13 @@ bool LLAppViewer::initWindow()
 	return true;
 }
 
-void LLAppViewer::writeDebug(const char *str)
-{
-	if (!mDebugFile)
-	{
-		std::string debug_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
-		llinfos << "Opening debug file " << debug_filename << llendl;
-		mDebugFile = LLFile::fopen(debug_filename.c_str(), "w");		/* Flawfinder: ignore */
-        if (!mDebugFile)
-        {
-		    llinfos << "Opening debug file " << debug_filename << " failed. Using stderr." << llendl;
-            mDebugFile = stderr;
-        }
-	}
-	fputs(str, mDebugFile);
-	fflush(mDebugFile);
-}
-
 void LLAppViewer::closeDebug()
 {
-	if (mDebugFile)
-	{
-		fclose(mDebugFile);
-	}
-	mDebugFile = NULL;
+	std::string debug_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
+	llinfos << "Opening debug file " << debug_filename << llendl;
+	std::ofstream out_file(debug_filename.c_str());
+	LLSDSerialize::toPrettyXML(gDebugInfo, out_file);
+	out_file.close();
 }
 
 void LLAppViewer::cleanupSavedSettings()
@@ -2443,23 +2429,22 @@ void LLAppViewer::removeCacheFiles(const char* file_mask)
 
 void LLAppViewer::writeSystemInfo()
 {
-	writeDebug("SL Log: ");
-	writeDebug(LLError::logFileName());
-	writeDebug("\n");
-
-	std::string tmp_str = gSecondLife
-		+ llformat(" version %d.%d.%d build %d",
-				   LL_VERSION_MAJOR, LL_VERSION_MINOR, LL_VERSION_PATCH, LL_VERSION_BUILD);
-	writeDebug(tmp_str.c_str());
-	writeDebug("\n");
-	writeDebug(gSysCPU.getCPUString());
-	writeDebug("\n");
+	gDebugInfo["SLLog"] = LLError::logFileName();
+
+	gDebugInfo["ClientInfo"]["Name"] = gSecondLife;
+	gDebugInfo["ClientInfo"]["MajorVersion"] = LL_VERSION_MAJOR;
+	gDebugInfo["ClientInfo"]["MinorVersion"] = LL_VERSION_MINOR;
+	gDebugInfo["ClientInfo"]["PatchVersion"] = LL_VERSION_PATCH;
+	gDebugInfo["ClientInfo"]["BuildVersion"] = LL_VERSION_BUILD;
+
+	gDebugInfo["CPUInfo"]["CPUFamily"] = gSysCPU.getFamily();
+	gDebugInfo["CPUInfo"]["CPUMhz"] = gSysCPU.getMhz();
+	gDebugInfo["CPUInfo"]["CPUAltivec"] = gSysCPU.hasAltivec();
+	gDebugInfo["CPUInfo"]["CPUSSE"] = gSysCPU.hasSSE();
+	gDebugInfo["CPUInfo"]["CPUSSE2"] = gSysCPU.hasSSE2();
 	
-	tmp_str = llformat("RAM: %u KB\n", gSysMemory.getPhysicalMemoryKB());
-	writeDebug(tmp_str.c_str());
-	writeDebug("OS: ");
-	writeDebug(getOSInfo().getOSString().c_str());
-	writeDebug("\n");
+	gDebugInfo["RAMInfo"] = llformat("%u", gSysMemory.getPhysicalMemoryKB());
+	gDebugInfo["OSInfo"] = mSysOSInfo.getOSString().c_str();
 
 	// Dump some debugging info
 	llinfos << gSecondLife << " version "
@@ -2498,16 +2483,11 @@ void LLAppViewer::handleViewerCrash()
 	}
 	pApp->mReportedCrash = TRUE;
 
-	BOOL do_crash_report = FALSE;
-
-	do_crash_report = TRUE;
-
-	pApp->writeDebug("Viewer exe: ");
-	pApp->writeDebug(gDirUtilp->getExecutablePathAndName().c_str());
-	pApp->writeDebug("\n");
-	pApp->writeDebug("Cur path: ");
-	pApp->writeDebug(gDirUtilp->getCurPath().c_str());
-	pApp->writeDebug("\n\n");
+	gDebugInfo["SettingsFilename"] = gSettingsFileName;
+	gDebugInfo["CAFilename"] = gDirUtilp->getCAFile();
+	gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName().c_str();
+	gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath().c_str();
+	gDebugInfo["CurrentSimHost"] = gAgent.getRegionHost().getHostName();
 
 	if (gMessageSystem && gDirUtilp)
 	{
@@ -2517,27 +2497,24 @@ void LLAppViewer::handleViewerCrash()
 		if(file.good())
 		{
 			gMessageSystem->summarizeLogs(file);
+			file.close();
 		}
 	}
 
 	if (gMessageSystem)
 	{
-		pApp->writeDebug(gMessageSystem->getCircuitInfoString());
+		gMessageSystem->getCircuitInfo(gDebugInfo["CircuitInfo"]);
 		gMessageSystem->stopLogging();
 	}
-	pApp->writeDebug("\n");
 	if (gWorldp)
 	{
-		pApp->writeDebug(gWorldp->getInfoString());
+		gWorldp->getInfo(gDebugInfo);
 	}
 
 	// Close the debug file
 	pApp->closeDebug();
 	LLError::logToFile("");
 
-	// Close the SecondLife.log
-	//pApp->removeMarkerFile();
-
 	// Call to pure virtual, handled by platform specifc llappviewer instance.
 	pApp->handleCrashReporting(); 
 
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index 227a27a8ac88cde055f7d9c55af3d6107446888a..e97aead9556af250b3d08b4325d72a68c16cbdd7 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -68,9 +68,6 @@ class LLAppViewer : public LLApp
 	// This version stores the argc and argv for later usage, make sure the params passed in last as long as this class.
 	bool tempStoreCommandOptions(int argc, char** argv);
 
-	// write string to "debug_info.log", used for crash reporting.
-	void writeDebug(const char *str); 
-	void writeDebug(const std::string& str) { writeDebug(str.c_str()); };
 	void closeDebug();
 
 	const LLOSInfo& getOSInfo() const { return mSysOSInfo; }
@@ -165,8 +162,6 @@ class LLAppViewer : public LLApp
 	FILE *mMarkerFile; // A file created to indicate the app is running.
 	bool mLastExecFroze; // Set on init if the marker file was found.
 
-	FILE* mDebugFile; // output stream written to via writeDebug()
-
 	LLOSInfo mSysOSInfo; 
 	S32 mCrashBehavior;
 	bool mReportedCrash;
@@ -202,6 +197,7 @@ extern BOOL gProbeHardware;
 extern LLString gDisabledMessage; // llstartup
 extern BOOL gHideLinks; // used by llpanellogin, lllfloaterbuycurrency, llstartup
 extern BOOL gInProductionGrid; 
+extern LLSD gDebugInfo;
 
 extern BOOL	gAllowIdleAFK;
 extern F32 gAFKTimeout;
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 1d7a6690fc37f530d04a54701adc1b550cebba54..814c209e67a48c234a6064e5fffdc9415d19cd1b 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -322,9 +322,16 @@ bool LLAppViewerWin32::initWindow()
 	return LLAppViewer::initWindow();
 }
 
-void write_debug_callback(const char* str)
+void write_debug_dx(const char* str)
 {
-	LLAppViewer::instance()->writeDebug(str);
+	LLString value = gDebugInfo["DXInfo"].asString();
+	value += str;
+	gDebugInfo["DXInfo"] = value;
+}
+
+void write_debug_dx(const std::string& str)
+{
+	write_debug_dx(str.c_str());
 }
 
 bool LLAppViewerWin32::initHardwareTest()
@@ -340,7 +347,7 @@ bool LLAppViewerWin32::initHardwareTest()
 		LLSplashScreen::update("Detecting hardware...");
 
 		llinfos << "Attempting to poll DirectX for hardware info" << llendl;
-		gDXHardware.setWriteDebugFunc(write_debug_callback);
+		gDXHardware.setWriteDebugFunc(write_debug_dx);
 		BOOL probe_ok = gDXHardware.getInfo(vram_only);
 
 		if (!probe_ok
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index c43c4e86854f94fe55a6c6a0c1547d6568d77998..dda155d0df0fb7886757b5e87c2d3fb871d0d5e1 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -773,12 +773,11 @@ BOOL idle_startup()
 			gSavedSettings.setString("FirstName", firstname);
 			gSavedSettings.setString("LastName", lastname);
 
+
+			
+
 			llinfos << "Attempting login as: " << firstname << " " << lastname << llendl;
-			LLAppViewer::instance()->writeDebug("Attempting login as: ");
-			LLAppViewer::instance()->writeDebug(firstname);
-			LLAppViewer::instance()->writeDebug(" ");
-			LLAppViewer::instance()->writeDebug(lastname);
-			LLAppViewer::instance()->writeDebug("\n");
+			gDebugInfo["LoginName"] = firstname + " " + lastname;	
 		}
 
 		// create necessary directories
@@ -826,6 +825,7 @@ BOOL idle_startup()
 			LLPanelLogin::close();
 		}
 
+		
 		//For HTML parsing in text boxes.
 		LLTextEditor::setLinkColor( gSavedSettings.getColor4("HTMLLinkColor") );
 		LLTextEditor::setURLCallbacks ( &LLWeb::loadURL, &LLURLDispatcher::dispatch, &LLURLDispatcher::dispatch   );
@@ -895,6 +895,8 @@ BOOL idle_startup()
 	if(STATE_LOGIN_AUTH_INIT == LLStartUp::getStartupState())
 	{
 //#define LL_MINIMIAL_REQUESTED_OPTIONS
+		gDebugInfo["GridUtilHost"] = gGridInfo[gGridChoice].mName;
+
 		lldebugs << "STATE_LOGIN_AUTH_INIT" << llendl;
 		if (!gUserAuthp)
 		{
@@ -932,6 +934,7 @@ BOOL idle_startup()
 		}
 		LLAppViewer::instance()->getLoginURIs();
 		sAuthUris = LLAppViewer::instance()->getLoginURIs();
+
 		sAuthUriNum = 0;
 		auth_method = "login_to_simulator";
 		auth_desc = "Logging in.  ";
@@ -1226,15 +1229,11 @@ BOOL idle_startup()
 			const char* text;
 			text = gUserAuthp->getResponse("agent_id");
 			if(text) gAgentID.set(text);
-			LLAppViewer::instance()->writeDebug("AgentID: ");
-			LLAppViewer::instance()->writeDebug(text);
-			LLAppViewer::instance()->writeDebug("\n");
+			gDebugInfo["AgentID"] = text;
 			
 			text = gUserAuthp->getResponse("session_id");
 			if(text) gAgentSessionID.set(text);
-			LLAppViewer::instance()->writeDebug("SessionID: ");
-			LLAppViewer::instance()->writeDebug(text);
-			LLAppViewer::instance()->writeDebug("\n");
+			gDebugInfo["SessionID"] = text;
 			
 			text = gUserAuthp->getResponse("secure_session_id");
 			if(text) gAgent.mSecureSessionID.set(text);
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 95a1db12dfbc5ed69fbf6a1869067cede30abefe..5bfe023168eeb38f714d4bbc9b88cb7bda5c2e53 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -964,28 +964,16 @@ void LLViewerRegion::updateCoarseLocations(LLMessageSystem* msg)
 	}
 }
 
-LLString LLViewerRegion::getInfoString()
+void LLViewerRegion::getInfo(LLSD& info)
 {
-	char tmp_buf[256];		/* Flawfinder: ignore */
-	LLString info;
-	
-	info = "Region: ";
-	getHost().getString(tmp_buf, 256);
-	info += tmp_buf;
-	info += ":";
-	info += getName();
-	info += "\n";
-
+	info["Region"]["Host"] = getHost().getIPandPort();
+	info["Region"]["Name"] = getName();
 	U32 x, y;
 	from_region_handle(getHandle(), &x, &y);
-	snprintf(tmp_buf, sizeof(tmp_buf), "%d:%d", x, y);		/* Flawfinder: ignore */
-	info += "Handle:";
-	info += tmp_buf;
-	info += "\n";
-	return info;
+	info["Region"]["Handle"]["x"] = (LLSD::Integer)x;
+	info["Region"]["Handle"]["y"] = (LLSD::Integer)y;
 }
 
-
 void LLViewerRegion::cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp)
 {
 	U32 local_id = objectp->getLocalID();
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
index 013a96f2d1a274f2b0ed8e0b17111125b383b852..a0953e561e9069399efcd9ba3acee75075d476cb 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -223,7 +223,7 @@ class LLViewerRegion
 
 	F32 getLandHeightRegion(const LLVector3& region_pos);
 
-	LLString getInfoString();
+	void getInfo(LLSD& info);
 
 	// handle a full update message
 	void cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp);
diff --git a/indra/newview/llwindebug.cpp b/indra/newview/llwindebug.cpp
index 88ba5822eb423232524940e28e7f9a7699c390ba..13c5ac3bbb811dff52fc60449b90d29fd0ce7eeb 100644
--- a/indra/newview/llwindebug.cpp
+++ b/indra/newview/llwindebug.cpp
@@ -33,12 +33,70 @@
 
 #ifdef LL_WINDOWS
 
+#include <tchar.h>
+#include <tlhelp32.h>
+#include <atlbase.h>
+#include "llappviewer.h"
 #include "llwindebug.h"
 #include "llviewercontrol.h"
 #include "lldir.h"
-
-#include "llappviewer.h"
-
+#include "llsd.h"
+#include "llsdserialize.h"
+
+#pragma warning(disable: 4200)	//nonstandard extension used : zero-sized array in struct/union
+#pragma warning(disable: 4100)	//unreferenced formal parameter
+
+/*
+LLSD Block for Windows Dump Information
+<llsd>
+  <map>
+    <key>Platform</key>
+    <string></string>
+    <key>Process</key>
+    <string></string>
+    <key>Module</key>
+    <string></string>
+    <key>Date Modified</key>
+    <string></string>
+    <key>Exception Code</key>
+    <string></string>
+    <key>Exception Read/Write Address</key>
+    <string></string>
+    <key>Instruction</key>
+    <string></string>
+    <key>Registers</key>
+    <map>
+      <!-- Continued for all registers -->
+      <key>EIP</key>
+      <string>...</string>
+      <!-- ... -->
+    </map>
+    <key>Call Stack</key>
+    <array>
+      <!-- One map per stack frame -->
+      <map>
+	<key>Module Name</key>
+	<string></string>
+	<key>Module Base Address</key>
+	<string></string>
+	<key>Module Offset Address</key>
+	<string></string>
+	<key>Parameters</key>
+	<array>
+	  <string></string>
+	</array>
+      </map>
+      <!-- ... -->
+    </array>
+  </map>
+</llsd>
+
+*/
+
+// From viewer.h
+extern BOOL gInProductionGrid;
+
+extern void (*gCrashCallback)(void);
 
 // based on dbghelp.h
 typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
@@ -49,6 +107,255 @@ typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hF
 
 MINIDUMPWRITEDUMP f_mdwp = NULL;
 
+#undef UNICODE
+
+HMODULE	hDbgHelp;
+
+// Tool Help functions.
+typedef	HANDLE (WINAPI * CREATE_TOOL_HELP32_SNAPSHOT)(DWORD dwFlags, DWORD th32ProcessID);
+typedef	BOOL (WINAPI * MODULE32_FIRST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
+typedef	BOOL (WINAPI * MODULE32_NEST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
+
+CREATE_TOOL_HELP32_SNAPSHOT	CreateToolhelp32Snapshot_;
+MODULE32_FIRST	Module32First_;
+MODULE32_NEST	Module32Next_;
+
+#define	DUMP_SIZE_MAX	8000	//max size of our dump
+#define	CALL_TRACE_MAX	((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40))	//max number of traced calls
+#define	NL				L"\r\n"	//new line
+
+//****************************************************************************************
+BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, LPWSTR Module_Name, PBYTE & Module_Addr)
+//****************************************************************************************
+// Find module by Ret_Addr (address in the module).
+// Return Module_Name (full path) and Module_Addr (start address).
+// Return TRUE if found.
+{
+	MODULEENTRY32	M = {sizeof(M)};
+	HANDLE	hSnapshot;
+
+	bool found = false;
+	
+	if (CreateToolhelp32Snapshot_)
+	{
+		hSnapshot = CreateToolhelp32Snapshot_(TH32CS_SNAPMODULE, 0);
+		
+		if ((hSnapshot != INVALID_HANDLE_VALUE) &&
+			Module32First_(hSnapshot, &M))
+		{
+			do
+			{
+				if (DWORD(Ret_Addr - M.modBaseAddr) < M.modBaseSize)
+				{
+					lstrcpyn(Module_Name, M.szExePath, MAX_PATH);
+					Module_Addr = M.modBaseAddr;
+					found = true;
+					break;
+				}
+			} while (Module32Next_(hSnapshot, &M));
+		}
+
+		CloseHandle(hSnapshot);
+	}
+
+	return found;
+} //Get_Module_By_Ret_Addr
+
+//******************************************************************
+void WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, LLSD& info)
+//******************************************************************
+// Fill Str with call stack info.
+// pException can be either GetExceptionInformation() or NULL.
+// If pException = NULL - get current call stack.
+{	
+
+	USES_CONVERSION;
+
+	LPWSTR	Module_Name = new WCHAR[MAX_PATH];
+	PBYTE	Module_Addr = 0;
+	
+	typedef struct STACK
+	{
+		STACK *	Ebp;
+		PBYTE	Ret_Addr;
+		DWORD	Param[0];
+	} STACK, * PSTACK;
+
+	STACK	Stack = {0, 0};
+	PSTACK	Ebp;
+
+	if (pException)		//fake frame for exception address
+	{
+		Stack.Ebp = (PSTACK)pException->ContextRecord->Ebp;
+		Stack.Ret_Addr = (PBYTE)pException->ExceptionRecord->ExceptionAddress;
+		Ebp = &Stack;
+	}
+	else
+	{
+		Ebp = (PSTACK)&pException - 1;	//frame addr of Get_Call_Stack()
+
+		// Skip frame of Get_Call_Stack().
+		if (!IsBadReadPtr(Ebp, sizeof(PSTACK)))
+			Ebp = Ebp->Ebp;		//caller ebp
+	}
+
+	// Trace CALL_TRACE_MAX calls maximum - not to exceed DUMP_SIZE_MAX.
+	// Break trace on wrong stack frame.
+	for (int Ret_Addr_I = 0, i = 0;
+		(Ret_Addr_I < CALL_TRACE_MAX) && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr));
+		Ret_Addr_I++, Ebp = Ebp->Ebp, ++i)
+	{
+		// If module with Ebp->Ret_Addr found.
+
+		if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr))
+		{
+			// Save module's address and full path.
+			info["Call Stack"][i]["Module Name"] = W2A(Module_Name);
+			info["Call Stack"][i]["Module Address"] = (int)Module_Addr;
+			info["Call Stack"][i]["Call Offset"] = (int)(Ebp->Ret_Addr - Module_Addr);
+
+			LLSD params;
+			// Save 5 params of the call. We don't know the real number of params.
+			if (pException && !Ret_Addr_I)	//fake frame for exception address
+				params[0] = "Exception Offset";
+			else if (!IsBadReadPtr(Ebp, sizeof(PSTACK) + 5 * sizeof(DWORD)))
+			{
+				for(int j = 0; j < 5; ++j)
+				{
+					params[j] = (int)Ebp->Param[j];
+				}
+			}
+			info["Call Stack"][i]["Parameters"] = params;
+		}
+		info["Call Stack"][i]["Return Address"] = (int)Ebp->Ret_Addr;			
+	}
+} //Get_Call_Stack
+
+//***********************************
+void WINAPI Get_Version_Str(LLSD& info)
+//***********************************
+// Fill Str with Windows version.
+{
+	OSVERSIONINFOEX	V = {sizeof(OSVERSIONINFOEX)};	//EX for NT 5.0 and later
+
+	if (!GetVersionEx((POSVERSIONINFO)&V))
+	{
+		ZeroMemory(&V, sizeof(V));
+		V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+		GetVersionEx((POSVERSIONINFO)&V);
+	}
+
+	if (V.dwPlatformId != VER_PLATFORM_WIN32_NT)
+		V.dwBuildNumber = LOWORD(V.dwBuildNumber);	//for 9x HIWORD(dwBuildNumber) = 0x04xx
+
+	info["Platform"] = llformat("Windows:  %d.%d.%d, SP %d.%d, Product Type %d",	//SP - service pack, Product Type - VER_NT_WORKSTATION,...
+		V.dwMajorVersion, V.dwMinorVersion, V.dwBuildNumber, V.wServicePackMajor, V.wServicePackMinor, V.wProductType);
+} //Get_Version_Str
+
+//*************************************************************
+LLSD WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException)
+//*************************************************************
+// Allocate Str[DUMP_SIZE_MAX] and return Str with dump, if !pException - just return call stack in Str.
+{
+	USES_CONVERSION;
+
+	LLSD info;
+	LPWSTR		Str;
+	int			Str_Len;
+	int			i;
+	LPWSTR		Module_Name = new WCHAR[MAX_PATH];
+	PBYTE		Module_Addr;
+	HANDLE		hFile;
+	FILETIME	Last_Write_Time;
+	FILETIME	Local_File_Time;
+	SYSTEMTIME	T;
+	
+	Str = new WCHAR[DUMP_SIZE_MAX];
+	Str_Len = 0;
+	if (!Str)
+		return NULL;
+	
+	Get_Version_Str(info);
+
+	
+	GetModuleFileName(NULL, Str, MAX_PATH);
+	info["Process"] = W2A(Str);
+
+	// If exception occurred.
+	if (pException)
+	{
+		EXCEPTION_RECORD &	E = *pException->ExceptionRecord;
+		CONTEXT &			C = *pException->ContextRecord;
+
+		// If module with E.ExceptionAddress found - save its path and date.
+		if (Get_Module_By_Ret_Addr((PBYTE)E.ExceptionAddress, Module_Name, Module_Addr))
+		{
+			info["Module"] = W2A(Module_Name);
+
+			if ((hFile = CreateFile(Module_Name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+				FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
+			{
+				if (GetFileTime(hFile, NULL, NULL, &Last_Write_Time))
+				{
+					FileTimeToLocalFileTime(&Last_Write_Time, &Local_File_Time);
+					FileTimeToSystemTime(&Local_File_Time, &T);
+
+					info["Date Modified"] = llformat("%02d/%02d/%d", T.wMonth, T.wDay, T.wYear);
+				}
+				CloseHandle(hFile);
+			}
+		}
+		else
+		{
+			info["Exception Addr"] = (int)E.ExceptionAddress;
+		}
+		
+		info["Exception Code"] = (int)E.ExceptionCode;
+		
+		/*
+		//TODO: Fix this
+		if (E.ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
+		{
+			// Access violation type - Write/Read.
+			LLSD exception_info;
+			exception_info["Type"] = E.ExceptionInformation[0] ? "Write" : "Read";
+			exception_info["Address"] = llformat("%08x", E.ExceptionInformation[1]);
+			info["Exception Information"] = exception_info;
+		}
+		*/
+
+		
+		// Save instruction that caused exception.
+		Str_Len = 0;
+		for (i = 0; i < 16; i++)
+			Str_Len += wsprintf(Str + Str_Len, L" %02X", PBYTE(E.ExceptionAddress)[i]);
+		info["Instruction"] = W2A(Str);
+
+		LLSD registers;
+		registers["EAX"] = (int)C.Eax;
+		registers["EBX"] = (int)C.Ebx;
+		registers["ECX"] = (int)C.Ecx;
+		registers["EDX"] = (int)C.Edx;
+		registers["ESI"] = (int)C.Esi;
+		registers["EDI"] = (int)C.Edi;
+		registers["ESP"] = (int)C.Esp;
+		registers["EBP"] = (int)C.Ebp;
+		registers["EIP"] = (int)C.Eip;
+		registers["EFlags"] = (int)C.EFlags;
+		info["Registers"] = registers;
+	} //if (pException)
+	
+	// Save call stack info.
+	Get_Call_Stack(pException, info);
+
+	if (Str[0] == NL[0])
+		lstrcpy(Str, Str + sizeof(NL) - 1);
+	
+
+	return info;
+} //Get_Exception_Info
+
+#define UNICODE
 
 
 class LLMemoryReserve {
@@ -92,7 +399,6 @@ static LLMemoryReserve gEmergencyMemoryReserve;
 // static
 BOOL LLWinDebug::setupExceptionHandler()
 {
-#ifdef LL_RELEASE_FOR_DOWNLOAD
 
 	static BOOL s_first_run = TRUE;
 	// Load the dbghelp dll now, instead of waiting for the crash.
@@ -119,7 +425,7 @@ BOOL LLWinDebug::setupExceptionHandler()
 			msg += local_dll_name;
 			msg += "!\n";
 
-			LLAppViewer::instance()->writeDebug(msg.c_str());
+			//write_debug(msg.c_str());
 
 			ok = FALSE;
 		}
@@ -129,7 +435,7 @@ BOOL LLWinDebug::setupExceptionHandler()
 
 			if (!f_mdwp)
 			{
-				LLAppViewer::instance()->writeDebug("No MiniDumpWriteDump!\n");
+				//write_debug("No MiniDumpWriteDump!\n");
 				FreeLibrary(hDll);
 				hDll = NULL;
 				ok = FALSE;
@@ -139,76 +445,37 @@ BOOL LLWinDebug::setupExceptionHandler()
 		gEmergencyMemoryReserve.reserve();
 	}
 
-	// *REMOVE: LLApp now handles the exception handing. 
-	//            LLAppViewerWin32 calls SetUnhandledExceptionFilter()
-
-	//LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
-	//prev_filter = SetUnhandledExceptionFilter(LLWinDebug::handleException);
-
-	//if (s_first_run)
-	//{
-	//	// We're fine, this is the first run.
-	//	s_first_run = FALSE;
-	//	return ok;
-	//}
-	//if (!prev_filter)
-	//{
-	//	llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with NULL!" << llendl;
-	//	ok = FALSE;
-	//}
-	//if (prev_filter != LLWinDebug::handleException)
-	//{
-	//	llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with " << prev_filter << "!" << llendl;
-	//	ok = FALSE;
-	//}
+	LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
+	prev_filter = SetUnhandledExceptionFilter(LLWinDebug::handleException);
 
-	return ok;
-#else
-	// Internal builds don't mess with exception handling.
-	return TRUE;
-#endif
-}
+	// Try to get Tool Help library functions.
+	HMODULE hKernel32;
+	hKernel32 = GetModuleHandle(_T("KERNEL32"));
+	CreateToolhelp32Snapshot_ = (CREATE_TOOL_HELP32_SNAPSHOT)GetProcAddress(hKernel32, "CreateToolhelp32Snapshot");
+	Module32First_ = (MODULE32_FIRST)GetProcAddress(hKernel32, "Module32FirstW");
+	Module32Next_ = (MODULE32_NEST)GetProcAddress(hKernel32, "Module32NextW");
 
-void LLWinDebug::writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const char *filename)
-{
-	if(f_mdwp == NULL) 
+	if (s_first_run)
 	{
-		LLAppViewer::instance()->writeDebug("No way to generate a minidump, no MiniDumpWriteDump function!\n");
+		// We're fine, this is the first run.
+		s_first_run = FALSE;
+		return ok;
 	}
-	else if(gDirUtilp == NULL)
+	if (!prev_filter)
 	{
-		LLAppViewer::instance()->writeDebug("No way to generate a minidump, no gDirUtilp!\n");
+		llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with NULL!" << llendl;
+		ok = FALSE;
 	}
-	else
+	if (prev_filter != LLWinDebug::handleException)
 	{
-		std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
-															   filename);
-
-		HANDLE hFile = CreateFileA(dump_path.c_str(),
-									GENERIC_WRITE,
-									FILE_SHARE_WRITE,
-									NULL,
-									CREATE_ALWAYS,
-									FILE_ATTRIBUTE_NORMAL,
-									NULL);
-
-		if (hFile != INVALID_HANDLE_VALUE)
-		{
-			// Write the dump, ignoring the return value
-			f_mdwp(GetCurrentProcess(),
-					GetCurrentProcessId(),
-					hFile,
-					type,
-					ExInfop,
-					NULL,
-					NULL);
-
-			CloseHandle(hFile);
-		}
-
+		llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with " << prev_filter << "!" << llendl;
+		ok = FALSE;
 	}
-}
 
+	return ok;
+	// Internal builds don't mess with exception handling.
+	//return TRUE;
+}
 // static
 LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop)
 {
@@ -222,42 +489,35 @@ LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop)
 	//
 	gEmergencyMemoryReserve.release();
 
-	BOOL userWantsMaxiDump =
-		(stricmp(gSavedSettings.getString("LastName").c_str(), "linden") == 0)
-		|| (stricmp(gSavedSettings.getString("LastName").c_str(), "tester") == 0);
-
-	BOOL alsoSaveMaxiDump = userWantsMaxiDump && !gInProductionGrid;
-
-	/* Calculate alsoSaveMaxiDump here */
-
 	if (exception_infop)
 	{
-		_MINIDUMP_EXCEPTION_INFORMATION ExInfo;
 
-		ExInfo.ThreadId = ::GetCurrentThreadId();
-		ExInfo.ExceptionPointers = exception_infop;
-		ExInfo.ClientPointers = NULL;
+		std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
+															   "SecondLifeException");
 
-		writeDumpToFile(MiniDumpNormal, &ExInfo, "SecondLife.dmp");
+		std::string log_path = dump_path + ".log";
 
-		if(alsoSaveMaxiDump)
-			writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), &ExInfo, "SecondLifePlus.dmp");
+		LLSD info;
+		info = Get_Exception_Info(exception_infop);
+		if (info)
+		{
+			std::ofstream out_file(log_path.c_str());
+			LLSDSerialize::toPrettyXML(info, out_file);
+			out_file.close();
+		}
 	}
 	else
-	{
-		writeDumpToFile(MiniDumpNormal, NULL, "SecondLife.dmp");
-
-		if(alsoSaveMaxiDump)
-			writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), NULL, "SecondLifePlus.dmp");
-	}
-
-	if (!exception_infop)
 	{
 		// We're calling this due to a network error, not due to an actual exception.
 		// It doesn't realy matter what we return.
 		return EXCEPTION_CONTINUE_SEARCH;
 	}
 
+	//handle viewer crash must be called here since
+	//we don't return handling of the application 
+	//back to the process. 
+	LLAppViewer::handleViewerCrash();
+
 	//
 	// At this point, we always want to exit the app.  There's no graceful
 	// recovery for an unhandled exception.
@@ -269,4 +529,3 @@ LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop)
 }
 
 #endif
-
diff --git a/indra/newview/llwindebug.h b/indra/newview/llwindebug.h
index bb1f11df67473463a481be711c74248c1ee39bdf..e4201382160fd7d58389d1616266e74578f8f155 100644
--- a/indra/newview/llwindebug.h
+++ b/indra/newview/llwindebug.h
@@ -41,7 +41,7 @@ class LLWinDebug
 	static BOOL setupExceptionHandler();
 
 	static LONG WINAPI handleException(struct _EXCEPTION_POINTERS *pExceptionInfo);
-	static void writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const char *filename);
+	//static void writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const char *filename);
 };
 
 #endif // LL_LLWINDEBUG_H
diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp
index 36be05fe7f4594a4714da4081c88cfa537e56c41..e76123557cdcaf44f01f7c20be0a6ffb74ba5900 100644
--- a/indra/newview/llworld.cpp
+++ b/indra/newview/llworld.cpp
@@ -972,16 +972,16 @@ void LLWorld::requestCacheMisses()
 	}
 }
 
-LLString LLWorld::getInfoString()
+void LLWorld::getInfo(LLSD& info)
 {
-	LLString info_string("World Info:\n");
+	LLSD region_info;
 	for (region_list_t::iterator iter = mRegionList.begin();
 		 iter != mRegionList.end(); ++iter)
-	{
+	{	
 		LLViewerRegion* regionp = *iter;
-		info_string += regionp->getInfoString();
+		regionp->getInfo(region_info);
+		info["World"].append(region_info);
 	}
-	return info_string;
 }
 
 void LLWorld::disconnectRegions()
diff --git a/indra/newview/llworld.h b/indra/newview/llworld.h
index 91685cd297f27ecf5ee790a78185ac4d23b30efe..e634459acdc11a8221e9c9a5ed1897addb753ac0 100644
--- a/indra/newview/llworld.h
+++ b/indra/newview/llworld.h
@@ -142,7 +142,7 @@ class LLWorld
 	void setSpaceTimeUSec(const U64 space_time_usec);
 	U64 getSpaceTimeUSec() const;
 
-	LLString getInfoString();
+	void getInfo(LLSD& info);
 
 public:
 	typedef std::list<LLViewerRegion*> region_list_t;
diff --git a/indra/newview/macutil_Prefix.h b/indra/newview/macutil_Prefix.h
index f8050c8d35b87dba15d35e853d8a980ad714984f..145a01c702f19a14f625ed9b88ea342e3b31eed0 100644
--- a/indra/newview/macutil_Prefix.h
+++ b/indra/newview/macutil_Prefix.h
@@ -39,3 +39,6 @@
 
 #include <Carbon/Carbon.h>
 
+#undef check
+#undef verify
+#undef require
diff --git a/indra/win_crash_logger/StdAfx.h b/indra/win_crash_logger/StdAfx.h
index 6f046f9820dfb06d50e9212c8be32b4b958df7df..d954823c7487208be295d94a19b6852280e5b26d 100644
--- a/indra/win_crash_logger/StdAfx.h
+++ b/indra/win_crash_logger/StdAfx.h
@@ -52,6 +52,8 @@
 #include <malloc.h>
 #include <memory.h>
 #include <tchar.h>
+#include <atlbase.h>
+
 
 // Local Header Files
 
diff --git a/indra/win_crash_logger/llcrashloggerwindows.cpp b/indra/win_crash_logger/llcrashloggerwindows.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..007e2b0ed51e4a4acecfbbe9acec8cb1592a5036
--- /dev/null
+++ b/indra/win_crash_logger/llcrashloggerwindows.cpp
@@ -0,0 +1,348 @@
+/** 
+* @file llcrashloggerwindows.cpp
+* @brief Windows crash logger implementation
+*
+* $LicenseInfo:firstyear=2003&license=viewergpl$
+* 
+* Copyright (c) 2003-2007, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "stdafx.h"
+#include "resource.h"
+#include "llcrashloggerwindows.h"
+
+#include <sstream>
+
+#include "boost/tokenizer.hpp"
+
+#include "dbghelp.h"
+#include "indra_constants.h"	// CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
+#include "llerror.h"
+#include "llfile.h"
+#include "lltimer.h"
+#include "llstring.h"
+#include "lldxhardware.h"
+#include "lldir.h"
+#include "llsdserialize.h"
+
+#define MAX_LOADSTRING 100
+const char* const SETTINGS_FILE_HEADER = "version";
+const S32 SETTINGS_FILE_VERSION = 101;
+
+// Windows Message Handlers
+
+// Global Variables:
+HINSTANCE hInst= NULL;					// current instance
+TCHAR szTitle[MAX_LOADSTRING];				/* Flawfinder: ignore */		// The title bar text
+TCHAR szWindowClass[MAX_LOADSTRING];		/* Flawfinder: ignore */		// The title bar text
+
+HWND gHwndReport = NULL;	// Send/Don't Send dialog
+HWND gHwndProgress = NULL;	// Progress window
+HCURSOR gCursorArrow = NULL;
+HCURSOR gCursorWait = NULL;
+BOOL gFirstDialog = TRUE;	// Are we currently handling the Send/Don't Send dialog?
+std::stringstream gDXInfo;
+
+void write_debug(const char *str)
+{
+	gDXInfo << str;		/* Flawfinder: ignore */
+}
+
+void write_debug(std::string& str)
+{
+	write_debug(str.c_str());
+}
+
+void show_progress(const char* message)
+{
+	std::wstring msg = wstring_to_utf16str(utf8str_to_wstring(message));
+	if (gHwndProgress)
+	{
+		SendDlgItemMessage(gHwndProgress,       // handle to destination window 
+							IDC_LOG,
+							WM_SETTEXT,			// message to send
+							FALSE,				// undo option
+							(LPARAM)msg.c_str());
+	}
+}
+
+void update_messages()
+{
+	MSG msg;
+	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+	{
+		if (msg.message == WM_QUIT)
+		{
+			exit(0);
+		}
+		TranslateMessage(&msg);
+		DispatchMessage(&msg);
+	}
+}
+
+void sleep_and_pump_messages( U32 seconds )
+{
+	const U32 CYCLES_PER_SECOND = 10;
+	U32 cycles = seconds * CYCLES_PER_SECOND;
+	while( cycles-- )
+	{
+		update_messages();
+		ms_sleep(1000 / CYCLES_PER_SECOND);
+	}
+}
+
+// Include product name in the window caption.
+void LLCrashLoggerWindows::ProcessCaption(HWND hWnd)
+{
+	TCHAR templateText[1024];		/* Flawfinder: ignore */
+	TCHAR finalText[2048];		/* Flawfinder: ignore */
+	GetWindowText(hWnd, templateText, sizeof(templateText));
+	swprintf(finalText, sizeof(CA2T(mProductName.c_str())), templateText, CA2T(mProductName.c_str()));		/* Flawfinder: ignore */
+	SetWindowText(hWnd, finalText);
+}
+
+
+// Include product name in the diaog item text.
+void LLCrashLoggerWindows::ProcessDlgItemText(HWND hWnd, int nIDDlgItem)
+{
+	TCHAR templateText[1024];		/* Flawfinder: ignore */
+	TCHAR finalText[2048];		/* Flawfinder: ignore */
+	GetDlgItemText(hWnd, nIDDlgItem, templateText, sizeof(templateText));
+	swprintf(finalText, sizeof(CA2T(mProductName.c_str())), templateText, CA2T(mProductName.c_str()));		/* Flawfinder: ignore */
+	SetDlgItemText(hWnd, nIDDlgItem, finalText);
+}
+
+bool handle_button_click(WORD button_id)
+{
+	USES_CONVERSION;
+	// Is this something other than Send or Don't Send?
+	if (button_id != IDOK
+		&& button_id != IDCANCEL)
+	{
+		return false;
+	}
+
+	// See if "do this next time" is checked and save state
+	S32 crash_behavior = CRASH_BEHAVIOR_ASK;
+	LRESULT result = SendDlgItemMessage(gHwndReport, IDC_CHECK_AUTO, BM_GETCHECK, 0, 0);
+	if (result == BST_CHECKED)
+	{
+		if (button_id == IDOK)
+		{
+			crash_behavior = CRASH_BEHAVIOR_ALWAYS_SEND;
+		}
+		else if (button_id == IDCANCEL)
+		{
+			crash_behavior = CRASH_BEHAVIOR_NEVER_SEND;
+		}
+		((LLCrashLoggerWindows*)LLCrashLogger::instance())->saveCrashBehaviorSetting(crash_behavior);
+	}
+	
+	// We're done with this dialog.
+	gFirstDialog = FALSE;
+
+	// Send the crash report if requested
+	if (button_id == IDOK)
+	{
+		WCHAR wbuffer[20000];
+		GetDlgItemText(gHwndReport, // handle to dialog box
+						IDC_EDIT1,  // control identifier
+						wbuffer, // pointer to buffer for text
+						20000 // maximum size of string
+						);
+		LLString user_text(T2CA(wbuffer));
+		// Activate and show the window.
+		ShowWindow(gHwndProgress, SW_SHOW); 
+		// Try doing this second to make the progress window go frontmost.
+		ShowWindow(gHwndReport, SW_HIDE);
+		((LLCrashLoggerWindows*)LLCrashLogger::instance())->setUserText(user_text);
+		((LLCrashLoggerWindows*)LLCrashLogger::instance())->sendCrashLogs();
+	}
+	// Quit the app
+	LLApp::setQuitting();
+	return true;
+}
+
+
+LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
+{
+	switch( message )
+	{
+	case WM_CREATE:
+		return 0;
+
+	case WM_COMMAND:
+		if( gFirstDialog )
+		{
+			WORD button_id = LOWORD(wParam);
+			bool handled = handle_button_click(button_id);
+			if (handled)
+			{
+				return 0;
+			}
+		}
+		break;
+
+	case WM_DESTROY:
+		// Closing the window cancels
+		LLApp::setQuitting();
+		PostQuitMessage(0);
+		return 0;
+	}
+
+	return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+
+LLCrashLoggerWindows::LLCrashLoggerWindows(void)
+{
+}
+
+LLCrashLoggerWindows::~LLCrashLoggerWindows(void)
+{
+}
+
+bool LLCrashLoggerWindows::init(void)
+{	
+	bool ok = LLCrashLogger::init();
+	if(!ok) return false;
+
+	/*
+	mbstowcs(gProductName, mProductName.c_str(), sizeof(gProductName)/sizeof(gProductName[0]));
+	gProductName[ sizeof(gProductName)/sizeof(gProductName[0]) - 1 ] = 0;
+	swprintf(gProductName, L"Second Life");
+	*/
+
+	llinfos << "Loading dialogs" << llendl;
+
+	// Initialize global strings
+	LoadString(mhInst, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
+	LoadString(mhInst, IDC_WIN_CRASH_LOGGER, szWindowClass, MAX_LOADSTRING);
+
+	gCursorArrow = LoadCursor(NULL, IDC_ARROW);
+	gCursorWait = LoadCursor(NULL, IDC_WAIT);
+
+	// Register a window class that will be used by our dialogs
+	WNDCLASS wndclass;
+	wndclass.style = CS_HREDRAW | CS_VREDRAW;
+	wndclass.lpfnWndProc = WndProc;
+	wndclass.cbClsExtra = 0;
+	wndclass.cbWndExtra = DLGWINDOWEXTRA;  // Required, since this is used for dialogs!
+	wndclass.hInstance = mhInst;
+	wndclass.hIcon = LoadIcon(hInst, MAKEINTRESOURCE( IDI_WIN_CRASH_LOGGER ) );
+	wndclass.hCursor = gCursorArrow;
+	wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
+	wndclass.lpszMenuName = NULL;
+	wndclass.lpszClassName = szWindowClass;
+	RegisterClass( &wndclass );
+	
+	return true;
+}
+
+void LLCrashLoggerWindows::gatherPlatformSpecificFiles()
+{
+	updateApplication("Gathering hardware information. App may appear frozen.");
+	// DX hardware probe blocks, so we can't cancel during it
+	//Generate our dx_info.log file 
+	SetCursor(gCursorWait);
+	// At this point we're responsive enough the user could click the close button
+	SetCursor(gCursorArrow);
+	mDebugLog["DisplayDeviceInfo"] = gDXHardware.getDisplayInfo();
+	mFileMap["CrashLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLifeException.log");
+}
+
+bool LLCrashLoggerWindows::mainLoop()
+{	
+
+	USES_CONVERSION;
+
+	// Note: parent hwnd is 0 (the desktop).  No dlg proc.  See Petzold (5th ed) HexCalc example, Chapter 11, p529
+	// win_crash_logger.rc has been edited by hand.
+	// Dialogs defined with CLASS "WIN_CRASH_LOGGER" (must be same as szWindowClass)
+
+	gHwndProgress = CreateDialog(hInst, MAKEINTRESOURCE(IDD_PROGRESS), 0, NULL);
+	ProcessCaption(gHwndProgress);
+	ShowWindow(gHwndProgress, SW_HIDE );
+
+	if (mCrashBehavior == CRASH_BEHAVIOR_ALWAYS_SEND)
+	{
+		ShowWindow(gHwndProgress, SW_SHOW );
+		sendCrashLogs();
+	}
+	else if (mCrashBehavior == CRASH_BEHAVIOR_ASK)
+	{
+		gHwndReport = CreateDialog(hInst, MAKEINTRESOURCE(IDD_PREVREPORTBOX), 0, NULL);
+
+		// Include the product name in the caption and various dialog items.
+		ProcessCaption(gHwndReport);
+		ProcessDlgItemText(gHwndReport, IDC_STATIC_MSG);
+
+		// Update the header to include whether or not we crashed on the last run.
+		TCHAR header[2048];
+		CA2T product(mProductName.c_str());
+		if (mCrashInPreviousExec)
+		{
+			swprintf(header, _T("%s appears to have crashed or frozen the last time it ran."), product);		/* Flawfinder: ignore */
+		}
+		else
+		{
+			swprintf(header, _T("%s appears to have crashed."), product);		/* Flawfinder: ignore */
+		}
+		SetDlgItemText(gHwndReport, IDC_STATIC_HEADER, header);		
+		ShowWindow(gHwndReport, SW_SHOW );
+		
+		MSG msg;
+		while (!LLApp::isQuitting() && GetMessage(&msg, NULL, 0, 0))
+		{
+			TranslateMessage(&msg);
+			DispatchMessage(&msg);
+		}
+		return msg.wParam;
+	}
+	else
+	{
+		llwarns << "Unknown crash behavior " << mCrashBehavior << llendl;
+		return 1;
+	}
+	return 0;
+}
+
+void LLCrashLoggerWindows::updateApplication(LLString message)
+{
+	LLCrashLogger::updateApplication();
+	if(message != "") show_progress(message.c_str());
+	update_messages();
+}
+
+bool LLCrashLoggerWindows::cleanup()
+{
+	if(mSentCrashLogs) show_progress("Done");
+	else show_progress("Could not connect to servers, logs not sent");
+	sleep_and_pump_messages(3);
+
+	PostQuitMessage(0);
+	return true;
+}
+
diff --git a/indra/win_crash_logger/llcrashloggerwindows.h b/indra/win_crash_logger/llcrashloggerwindows.h
new file mode 100644
index 0000000000000000000000000000000000000000..1f5a2169478ebda67d8b73c681b3d8e0067e066b
--- /dev/null
+++ b/indra/win_crash_logger/llcrashloggerwindows.h
@@ -0,0 +1,59 @@
+/** 
+* @file llcrashloggerwindows.h
+* @brief Windows crash logger definition
+*
+* $LicenseInfo:firstyear=2003&license=viewergpl$
+* 
+* Copyright (c) 2003-2007, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 LLCRASHLOGGERWINDOWS_H
+#define LLCRASHLOGGERWINDOWS_H
+
+#include "linden_common.h"
+#include "llcrashlogger.h"
+#include "windows.h"
+#include "llstring.h"
+
+class LLCrashLoggerWindows : public LLCrashLogger
+{
+public:
+	LLCrashLoggerWindows(void);
+	~LLCrashLoggerWindows(void);
+	virtual bool init();
+	virtual bool mainLoop();
+	virtual void updateApplication(LLString message = "");
+	virtual bool cleanup();
+	virtual void gatherPlatformSpecificFiles();
+	//void annotateCallStack();
+	void setHandle(HINSTANCE hInst) { mhInst = hInst; }
+private:
+	void ProcessDlgItemText(HWND hWnd, int nIDDlgItem);
+	void ProcessCaption(HWND hWnd);
+	HINSTANCE mhInst;
+
+};
+
+#endif
diff --git a/indra/win_crash_logger/resource.h b/indra/win_crash_logger/resource.h
index ee6bf3d5756f837208e44c4bde3ef39e6e76ceba..f1ef850f760eb85c5b3865531516eef38b7811a7 100644
--- a/indra/win_crash_logger/resource.h
+++ b/indra/win_crash_logger/resource.h
@@ -1,3 +1,34 @@
+/** 
+* @file resource.h
+* @brief Windows crash logger windows resources
+*
+* $LicenseInfo:firstyear=2003&license=viewergpl$
+* 
+* Copyright (c) 2003-2007, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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$
+*/
+
 //{{NO_DEPENDENCIES}}
 // Microsoft Visual C++ generated include file.
 // Used by win_crash_logger.rc
@@ -22,6 +53,7 @@
 #define IDC_STATIC_HEADER               1007
 #define IDC_STATIC_WHATINFO             1008
 #define IDC_STATIC_MOTIVATION           1009
+#define IDC_STATIC_MSG                  1010
 #define IDC_STATIC                      -1
 
 // Next default values for new objects
@@ -30,7 +62,7 @@
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_NEXT_RESOURCE_VALUE        131
 #define _APS_NEXT_COMMAND_VALUE         32771
-#define _APS_NEXT_CONTROL_VALUE         1010
+#define _APS_NEXT_CONTROL_VALUE         1011
 #define _APS_NEXT_SYMED_VALUE           110
 #endif
 #endif
diff --git a/indra/win_crash_logger/win_crash_logger.cpp b/indra/win_crash_logger/win_crash_logger.cpp
index 266940741b48366777930eebefed6e2371be7ace..c6b4ff1c34d0ec1ba2727601608d8829a128c83c 100644
--- a/indra/win_crash_logger/win_crash_logger.cpp
+++ b/indra/win_crash_logger/win_crash_logger.cpp
@@ -35,98 +35,22 @@
 // Must be first include, precompiled headers.
 #include "stdafx.h"
 
-#include "linden_common.h"
-#include "llcontrol.h"
-#include "resource.h"
+#include <stdlib.h>
 
-#include <direct.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <wininet.h>
+#include "llcrashloggerwindows.h"
 
-#include "indra_constants.h"	// CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
-#include "llerror.h"
-#include "lltimer.h"
-#include "lldir.h"
 
-#include "llstring.h"
-#include "lldxhardware.h"
-
-LLControlGroup gCrashSettings;	// saved at end of session
-
-// Constants
-#define MAX_LOADSTRING 100
-const char* const SETTINGS_FILE_HEADER = "version";
-const S32 SETTINGS_FILE_VERSION = 101;
-
-// Functions
-LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
-bool handle_button_click(WORD button_id);
-S32 load_crash_behavior_setting();
-bool save_crash_behavior_setting(S32 crash_behavior);
-void send_crash_report();
-void write_debug(const char *str);
-void write_debug(std::string& str);
-
-// Global Variables:
-HINSTANCE hInst= NULL;					// current instance
-TCHAR szTitle[MAX_LOADSTRING];				/* Flawfinder: ignore */		// The title bar text
-TCHAR szWindowClass[MAX_LOADSTRING];		/* Flawfinder: ignore */		// The title bar text
-
-LLString gUserText;			// User's description of the problem
-time_t gStartTime = 0;
-HWND gHwndReport = NULL;	// Send/Don't Send dialog
-HWND gHwndProgress = NULL;	// Progress window
-HCURSOR gCursorArrow = NULL;
-HCURSOR gCursorWait = NULL;
-BOOL gFirstDialog = TRUE;	// Are we currently handling the Send/Don't Send dialog?
-BOOL gCrashInPreviousExec = FALSE;
-FILE *gDebugFile = NULL;
-LLString gUserserver;
-WCHAR gProductName[512];
 
 //
 // Implementation
 //
 
-// Include product name in the window caption.
-void ProcessCaption(HWND hWnd)
-{
-	TCHAR templateText[1024];		/* Flawfinder: ignore */
-	TCHAR finalText[2048];		/* Flawfinder: ignore */
-	GetWindowText(hWnd, templateText, sizeof(templateText));
-	swprintf(finalText, templateText, gProductName);		/* Flawfinder: ignore */
-	SetWindowText(hWnd, finalText);
-}
-
-
-// Include product name in the diaog item text.
-void ProcessDlgItemText(HWND hWnd, int nIDDlgItem)
-{
-	TCHAR templateText[1024];		/* Flawfinder: ignore */
-	TCHAR finalText[2048];		/* Flawfinder: ignore */
-	GetDlgItemText(hWnd, nIDDlgItem, templateText, sizeof(templateText));
-	swprintf(finalText, templateText, gProductName);		/* Flawfinder: ignore */
-	SetDlgItemText(hWnd, nIDDlgItem, finalText);
-}
-
 int APIENTRY WinMain(HINSTANCE hInstance,
                      HINSTANCE hPrevInstance,
                      LPSTR     lpCmdLine,
                      int       nCmdShow)
 {
 	llinfos << "Starting crash reporter" << llendl;
-	// We assume that all the logs we're looking for reside on the current drive
-	gDirUtilp->initAppDirs("SecondLife");
-	
-	// Default to the product name "Second Life" (this is overridden by the -name argument)
-	swprintf(gProductName, L"Second Life");		/* Flawfinder: ignore */
-
-	gCrashSettings.declareS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ASK, "Controls behavior when viewer crashes "
-		"(0 = ask before sending crash report, 1 = always send crash report, 2 = never send crash report)");
-
-	llinfos << "Loading crash behavior setting" << llendl;
-	S32 crash_behavior = load_crash_behavior_setting();
 
 	// In Win32, we need to generate argc and argv ourselves...
 	// Note: GetCommandLine() returns a  potentially return a LPTSTR
@@ -139,7 +63,7 @@ int APIENTRY WinMain(HINSTANCE hInstance,
 
 	const S32	MAX_ARGS = 100;
 	int argc = 0;
-	char *argv[MAX_ARGS];		/* Flawfinder: ignore */
+	char *argv[MAX_ARGS];		
 
 	char *token = NULL;
 	if( cmd_line_including_exe_name[0] == '\"' )
@@ -159,7 +83,7 @@ int APIENTRY WinMain(HINSTANCE hInstance,
 	{
 		argv[argc++] = token;
 		/* Get next token: */
-		if (*(token + strlen(token) + 1) == '\"')		/* Flawfinder: ignore */
+		if (*(token + strlen(token) + 1) == '\"')		
 		{
 			token = strtok( NULL, "\"");
 		}
@@ -169,784 +93,32 @@ int APIENTRY WinMain(HINSTANCE hInstance,
 		}
 	}
 
-	S32 i;
-	for (i=0; i<argc; i++)
-	{
-		if(!strcmp(argv[i], "-previous"))
-		{
-			llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl;
-			gCrashInPreviousExec = TRUE;
-		}
-
-		if(!strcmp(argv[i], "-dialog"))
-		{
-			llinfos << "Show the user dialog" << llendl;
-			crash_behavior = CRASH_BEHAVIOR_ASK;
-		}
-
-		if(!strcmp(argv[i], "-user"))
-		{
-			if ((i + 1) < argc)
-			{
-				i++;
-				gUserserver = argv[i];
-				llinfos << "Got userserver " << gUserserver << llendl;
-			}
-		}
-
-		if(!strcmp(argv[i], "-name"))
-		{
-			if ((i + 1) < argc)
-			{
-				i++;
-				
-				mbstowcs(gProductName, argv[i], sizeof(gProductName)/sizeof(gProductName[0]));
-				gProductName[ sizeof(gProductName)/sizeof(gProductName[0]) - 1 ] = 0;
-				llinfos << "Got product name " << argv[i] << llendl;
-			}
-		}
-	}
-
-	// If user doesn't want to send, bail out
-	if (crash_behavior == CRASH_BEHAVIOR_NEVER_SEND)
-	{
-		llinfos << "Crash behavior is never_send, quitting" << llendl;
-		return 0;
-	}
-
-	// Get the current time
-	time(&gStartTime);
-
-	llinfos << "Loading dialogs" << llendl;
-
-	// Store instance handle in our global variable
-	hInst = hInstance;
-
-	// Initialize global strings
-	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
-	LoadString(hInstance, IDC_WIN_CRASH_LOGGER, szWindowClass, MAX_LOADSTRING);
-
-	gCursorArrow = LoadCursor(NULL, IDC_ARROW);
-	gCursorWait = LoadCursor(NULL, IDC_WAIT);
-
-	// Register a window class that will be used by our dialogs
-	WNDCLASS wndclass;
-	wndclass.style = CS_HREDRAW | CS_VREDRAW;
-	wndclass.lpfnWndProc = WndProc;
-	wndclass.cbClsExtra = 0;
-	wndclass.cbWndExtra = DLGWINDOWEXTRA;  // Required, since this is used for dialogs!
-	wndclass.hInstance = hInst;
-	wndclass.hIcon = LoadIcon(hInst, MAKEINTRESOURCE( IDI_WIN_CRASH_LOGGER ) );
-	wndclass.hCursor = gCursorArrow;
-	wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
-	wndclass.lpszMenuName = NULL;
-	wndclass.lpszClassName = szWindowClass;
-	RegisterClass( &wndclass );
-
-	// Note: parent hwnd is 0 (the desktop).  No dlg proc.  See Petzold (5th ed) HexCalc example, Chapter 11, p529
-	// win_crash_logger.rc has been edited by hand.
-	// Dialogs defined with CLASS "WIN_CRASH_LOGGER" (must be same as szWindowClass)
-
-	gHwndProgress = CreateDialog(hInst, MAKEINTRESOURCE(IDD_PROGRESS), 0, NULL);
-	ProcessCaption(gHwndProgress);
-	ShowWindow(gHwndProgress, SW_HIDE );
-
-	if (crash_behavior == CRASH_BEHAVIOR_ALWAYS_SEND)
-	{
-		ShowWindow(gHwndProgress, SW_SHOW );
-		send_crash_report();
-		return 0;
-	}
-
-	if (crash_behavior == CRASH_BEHAVIOR_ASK)
-	{
-		gHwndReport = CreateDialog(hInst, MAKEINTRESOURCE(IDD_REPORT), 0, NULL);
-
-		// Include the product name in the caption and various dialog items.
-		ProcessCaption(gHwndReport);
-		ProcessDlgItemText(gHwndReport, IDC_STATIC_WHATINFO);
-		ProcessDlgItemText(gHwndReport, IDC_STATIC_MOTIVATION);
-
-		// Update the header to include whether or not we crashed on the last run.
-		WCHAR header[2048];
-		if (gCrashInPreviousExec)
-		{
-			swprintf(header, L"%s appears to have crashed or frozen the last time it ran.", gProductName);		/* Flawfinder: ignore */
-		}
-		else
-		{
-			swprintf(header, L"%s appears to have crashed.", gProductName);		/* Flawfinder: ignore */
-		}
-		SetDlgItemText(gHwndReport, IDC_STATIC_HEADER, header);
-		ShowWindow(gHwndReport, SW_SHOW );
-		
-		MSG msg;
-		while (GetMessage(&msg, NULL, 0, 0))
-		{
-			TranslateMessage(&msg);
-			DispatchMessage(&msg);
-		}
-		return msg.wParam;
-	}
-	else
-	{
-		llwarns << "Unknown crash behavior " << crash_behavior << llendl;
-		return 1;
-	}
-}
-
-
-LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
-{
-	switch( message )
-	{
-	case WM_CREATE:
-		return 0;
-
-	case WM_COMMAND:
-		if( gFirstDialog )
-		{
-			WORD button_id = LOWORD(wParam);
-			bool handled = handle_button_click(button_id);
-			if (handled)
-			{
-				return 0;
-			}
-		}
-		break;
-
-	case WM_DESTROY:
-		// Closing the window cancels
-		PostQuitMessage(0);
-		return 0;
-	}
-
-	return DefWindowProc(hwnd, message, wParam, lParam);
-}
-
-
-bool handle_button_click(WORD button_id)
-{
-	// Is this something other than Send or Don't Send?
-	if (button_id != IDOK
-		&& button_id != IDCANCEL)
-	{
-		return false;
-	}
-
-	// See if "do this next time" is checked and save state
-	S32 crash_behavior = CRASH_BEHAVIOR_ASK;
-	LRESULT result = SendDlgItemMessage(gHwndReport, IDC_CHECK_AUTO, BM_GETCHECK, 0, 0);
-	if (result == BST_CHECKED)
-	{
-		if (button_id == IDOK)
-		{
-			crash_behavior = CRASH_BEHAVIOR_ALWAYS_SEND;
-		}
-		else if (button_id == IDCANCEL)
-		{
-			crash_behavior = CRASH_BEHAVIOR_NEVER_SEND;
-		}
-	}
-	bool success = save_crash_behavior_setting(crash_behavior);
-	if (!success)
-	{
-		llwarns << "Failed to save crash settings" << llendl;
-	}
-
-	// We're done with this dialog.
-	gFirstDialog = FALSE;
-
-	// Send the crash report if requested
-	if (button_id == IDOK)
-	{
-		// Don't let users type anything.  They believe the reports
-		// get read by humans, and get angry when we don't respond. JC
-		//WCHAR wbuffer[20000];
-		//GetDlgItemText(gHwndReport, // handle to dialog box
-		//				IDC_EDIT1,  // control identifier
-		//				wbuffer, // pointer to buffer for text
-		//				20000 // maximum size of string
-		//				);
-		//gUserText = wstring_to_utf8str(utf16str_to_wstring(wbuffer)).c_str();
-		//llinfos << gUserText << llendl;
-
-		// Activate and show the window.
-		ShowWindow(gHwndProgress, SW_SHOW); 
-		// Try doing this second to make the progress window go frontmost.
-		ShowWindow(gHwndReport, SW_HIDE);
-
-		send_crash_report();
-	}
-
-	// Quit the app
-	PostQuitMessage(0);
-
-	return true;
-}
-
-
-class LLFileEncoder
-{
-public:
-	LLFileEncoder(const char *formname, const char *filename);
-	~LLFileEncoder();
-
-	BOOL isValid() const { return mIsValid; }
-	LLString encodeURL(const S32 max_length = 0);
-public:
-	BOOL mIsValid;
-	LLString mFilename;
-	LLString mFormname;
-	S32 mBufLength;
-	U8 *mBuf;
-};
-
-LLString encode_string(const char *formname, const LLString &str);
-
-void update_messages()
-{
-	MSG msg;
-	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
-	{
-		if (msg.message == WM_QUIT)
-		{
-			exit(0);
-		}
-		TranslateMessage(&msg);
-		DispatchMessage(&msg);
-	}
-}
-
-void sleep_and_pump_messages( U32 seconds )
-{
-	const U32 CYCLES_PER_SECOND = 10;
-	U32 cycles = seconds * CYCLES_PER_SECOND;
-	while( cycles-- )
+	LLCrashLoggerWindows app;
+	bool ok = app.parseCommandOptions(argc, argv);
+	if(!ok)
 	{
-		update_messages();
-		ms_sleep(1000 / CYCLES_PER_SECOND);
-	}
-}
-
-
-void show_progress(const char* message)
-{
-	std::wstring msg = wstring_to_utf16str(utf8str_to_wstring(message));
-	if (gHwndProgress)
-	{
-		SendDlgItemMessage(gHwndProgress,       // handle to destination window 
-							IDC_LOG,
-							WM_SETTEXT,			// message to send
-							FALSE,				// undo option
-							(LPARAM)msg.c_str());
-	}
-}
-
-
-void send_crash_report()
-{
-	update_messages();
-	show_progress("Starting up...");
-	update_messages();
-
-	const S32 SL_MAX_SIZE = 100000;			// Maximum size of the Second Life log file.
-
-	update_messages();
-
-	// Lots of silly variable, replicated for each log file.
-	std::string db_file_name; // debug.log
-	std::string sl_file_name; // SecondLife.log
-	std::string md_file_name; // minidump (SecondLife.dmp) file name
-	std::string st_file_name; // stats.log file
-	std::string si_file_name; // settings.ini file
-	std::string ml_file_name; // message.log file
-
-	LLFileEncoder *db_filep = NULL;
-	LLFileEncoder *sl_filep = NULL;
-	LLFileEncoder *st_filep = NULL;
-	LLFileEncoder *md_filep = NULL;
-	LLFileEncoder *si_filep = NULL;
-	LLFileEncoder *ml_filep = NULL;
-
-	// DX hardware probe blocks, so we can't cancel during it
-	SetCursor(gCursorWait);
-
-	// Need to do hardware detection before we grab the files, otherwise we don't send the debug log updates
-	// to the server (including the list of hardware).
-	update_messages();
-	show_progress("Detecting hardware, please wait...");
-	update_messages();
-	gDXHardware.setWriteDebugFunc(write_debug);
-	gDXHardware.getInfo(FALSE);
-	update_messages();
-	gDXHardware.dumpDevices();
-	update_messages();
-	fclose(gDebugFile);
-	gDebugFile = NULL;
-
-	// At this point we're responsive enough the user could click the close button
-	SetCursor(gCursorArrow);
-
-	///////////////////////////////////
-	//
-	// We do the parsing for the debug_info file first, as that will
-	// give us the location of the SecondLife.log file.
-	//
-
-	// Figure out the filename of the debug log
-	db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
-	db_filep = new LLFileEncoder("DB", db_file_name.c_str());
-
-	// Get the filename of the SecondLife.log file
-	// *NOTE: This buffer size is hard coded into scanf() below.
-	char tmp_sl_name[256];		/* Flawfinder: ignore */
-	tmp_sl_name[0] = '\0';
-
-	update_messages();
-	show_progress("Looking for files...");
-	update_messages();
-
-	// Look for it in the debug_info.log file
-	if (db_filep->isValid())
-	{
-		sscanf(
-			(const char*)db_filep->mBuf,
-			"SL Log: %255[^\r\n]",
-			tmp_sl_name);
-	}
-	else
-	{
-		delete db_filep;
-		db_filep = NULL;
-	}
-
-	if (gCrashInPreviousExec)
-	{
-		// If we froze, the crash log this time around isn't useful.  Use the
-		// old one.
-		sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.old");
-	}
-	else if (tmp_sl_name[0])
-	{
-		// If debug_info.log gives us a valid log filename, use that.
-		sl_file_name = tmp_sl_name;
-		llinfos << "Using log file from debug log " << sl_file_name << llendl;
-	}
-	else
-	{
-		// Figure out the filename of the default second life log
-		sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log");
-	}
-
-	// Now we get the SecondLife.log file if it's there
-	sl_filep = new LLFileEncoder("SL", sl_file_name.c_str());
-	if (!sl_filep->isValid())
-	{
-		delete sl_filep;
-		sl_filep = NULL;
-	}
-
-	update_messages();
-	show_progress("Looking for stats file...");
-	update_messages();
-
-	st_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log");
-	st_filep = new LLFileEncoder("ST", st_file_name.c_str());
-	if (!st_filep->isValid())
-	{
-		delete st_filep;
-		st_filep = NULL;
-	}
-
-	si_file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.ini");
-	si_filep = new LLFileEncoder("SI", si_file_name.c_str());
-	if (!si_filep->isValid())
-	{
-		delete si_filep;
-		si_filep = NULL;
-	}
-
-	// Now we get the minidump
-	md_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.dmp");
-	md_filep = new LLFileEncoder("MD", md_file_name.c_str());
-	if (!md_filep->isValid())
-	{		
-		delete md_filep;
-		md_filep = NULL;
-	}
-
-	// Now we get the message log
-	ml_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"message.log");
-	ml_filep = new LLFileEncoder("ML", ml_file_name.c_str());
-	if (!ml_filep->isValid())
-	{		
-		delete ml_filep;
-		ml_filep = NULL;
-	}
-
-	LLString post_data;
-	LLString tmp_url_buf;
-
-	// Append the userserver
-	tmp_url_buf = encode_string("USER", gUserserver);
-	post_data += tmp_url_buf;
-	llinfos << "PostData:" << post_data << llendl;
-
-	if (gCrashInPreviousExec)
-	{
-		post_data.append(1, '&');
-		tmp_url_buf = encode_string("EF", "Y");
-		post_data += tmp_url_buf;
-	}
-
-	update_messages();
-	show_progress("Encoding data");
-	update_messages();
-	if (db_filep)
-	{
-		post_data.append(1, '&');
-		tmp_url_buf = db_filep->encodeURL();
-		post_data += tmp_url_buf;
-		llinfos << "Sending DB log file" << llendl;
-	}
-	else
-	{
-		llinfos << "Not sending DB log file" << llendl;
-	}
-	show_progress("Encoding data.");
-	update_messages();
-
-	if (sl_filep)
-	{
-		post_data.append(1, '&');
-		tmp_url_buf = sl_filep->encodeURL(SL_MAX_SIZE);
-		post_data += tmp_url_buf;
-		llinfos << "Sending SL log file" << llendl;
-	}
-	else
-	{
-		llinfos << "Not sending SL log file" << llendl;
-	}
-	show_progress("Encoding data..");
-	update_messages();
-
-	if (st_filep)
-	{
-		post_data.append(1, '&');
-		tmp_url_buf = st_filep->encodeURL(SL_MAX_SIZE);
-		post_data += tmp_url_buf;
-		llinfos << "Sending stats log file" << llendl;
-	}
-	else
-	{
-		llinfos << "Not sending stats log file" << llendl;
-	}
-	show_progress("Encoding data...");
-	update_messages();
-
-	if (md_filep)
-	{
-		post_data.append(1, '&');
-		tmp_url_buf = md_filep->encodeURL();
-		post_data += tmp_url_buf;
-		llinfos << "Sending minidump log file" << llendl;
-	}
-	else
-	{
-		llinfos << "Not sending minidump log file" << llendl;
-	}
-	show_progress("Encoding data....");
-	update_messages();
-
-	if (si_filep)
-	{
-		post_data.append(1, '&');
-		tmp_url_buf = si_filep->encodeURL();
-		post_data += tmp_url_buf;
-		llinfos << "Sending settings log file" << llendl;
-	}
-	else
-	{
-		llinfos << "Not sending settings.ini file" << llendl;
-	}
-	show_progress("Encoding data....");
-	update_messages();
-
-	if (ml_filep)
-	{
-		post_data.append(1, '&');
-		tmp_url_buf = ml_filep->encodeURL(SL_MAX_SIZE);
-		post_data += tmp_url_buf;
-		llinfos << "Sending message log file" << llendl;
-	}
-	else
-	{
-		llinfos << "Not sending message.log file" << llendl;
-	}
-	show_progress("Encoding data....");
-	update_messages();
-
-	if (gUserText.size())
-	{
-		post_data.append(1, '&');
-		tmp_url_buf = encode_string("UN", gUserText);
-		post_data += tmp_url_buf;
-	}
-
-	delete db_filep;
-	db_filep = NULL;
-	delete sl_filep;
-	sl_filep = NULL;
-	delete md_filep;
-	md_filep = NULL;
-
-	// Post data to web server
-	const S32 BUFSIZE = 65536;
-	HINTERNET hinet, hsession, hrequest;
-	char data[BUFSIZE];		/* Flawfinder: ignore */
-	unsigned long bytes_read;
-
-	llinfos << "Connecting to crash report server" << llendl;
-	update_messages();
-	show_progress("Connecting to server...");
-	update_messages();
-
-	// Init wininet subsystem
-	hinet = InternetOpen(L"LindenCrashReporter", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
-	if (hinet == NULL)
-	{
-		llinfos << "Couldn't open connection" << llendl;
-		sleep_and_pump_messages( 5 );
-//		return FALSE;
-	}
-
-	hsession = InternetConnect(hinet,
-								L"secondlife.com",
-								INTERNET_DEFAULT_HTTP_PORT,
-								NULL,
-								NULL,
-								INTERNET_SERVICE_HTTP,
-								NULL,
-								NULL);
-
-	if (!hsession)
-	{
-		llinfos << "Couldn't talk to crash report server" << llendl;
-	}
-
-	hrequest = HttpOpenRequest(hsession, L"POST", L"/cgi-bin/viewer_crash_reporter2", NULL, L"", NULL, 0, 0);
-	if (!hrequest)
-	{
-		llinfos << "Couldn't open crash report URL!" << llendl;
+		llwarns << "Unable to parse command line." << llendl;
 	}
 	
-	llinfos << "Transmitting data" << llendl;
-	llinfos << "Bytes: " << (post_data.size()) << llendl;
-
-	update_messages();
-	show_progress("Transmitting data...");
-	update_messages();
-
-	BOOL ok = HttpSendRequest(hrequest, NULL, 0, (void *)(post_data.c_str()), post_data.size());
-	if (!ok)
-	{
-		llinfos << "Error posting data!" << llendl;
-		sleep_and_pump_messages( 5 );
-	}
-
-	llinfos << "Response from crash report server:" << llendl;
-	do
-	{
-		if (InternetReadFile(hrequest, data, BUFSIZE, &bytes_read))
-		{
-			if (bytes_read == 0)
-			{
-				// If InternetFileRead returns TRUE AND bytes_read == 0
-				// we've successfully downloaded the entire file
-				break;
-			}
-			else
-			{
-				data[bytes_read] = 0;
-				llinfos << data << llendl;
-			}
-		}
-		else
-		{
-			llinfos << "Couldn't read file!" << llendl;
-			sleep_and_pump_messages( 5 );
-//			return FALSE;
-		}
-	} while(TRUE);
-
-	InternetCloseHandle(hrequest);
-	InternetCloseHandle(hsession);
-	InternetCloseHandle(hinet);
-	update_messages();
-	show_progress("Done.");
-	sleep_and_pump_messages( 3 );
-//	return TRUE;
-}
-
-LLFileEncoder::LLFileEncoder(const char *form_name, const char *filename)
-{
-	mFormname = form_name;
-	mFilename = filename;
-	mIsValid = FALSE;
-	mBuf = NULL;
-
-	int res;
-	
-	llstat stat_data;
-	res = LLFile::stat(mFilename.c_str(), &stat_data);
-	if (res)
-	{
-		llwarns << "File " << mFilename << " is missing!" << llendl;
-		return;
-	}
-
-	FILE *fp = NULL;
-	S32 buf_size = 0;
-	S32 count = 0;
-	while (count < 5)
-	{
-		buf_size = stat_data.st_size;
-		fp = LLFile::fopen(mFilename.c_str(), "rb");		/* Flawfinder: ignore */
-		if (!fp)
-		{
-			llwarns << "Can't open file " << mFilename << ", wait for a second" << llendl;
-			// Couldn't open the file, wait a bit and try again
-			count++;
-			ms_sleep(1000);
-		}
-		else
-		{
-			break;
-		}
-	}
-	if (!fp)
-	{
-		return;
-	}
-	U8 *buf = new U8[buf_size + 1];
-	fread(buf, 1, buf_size, fp);
-	fclose(fp);
-
-	mBuf = buf;
-	mBufLength = buf_size;
-
-	mIsValid = TRUE;
-}
-
-LLFileEncoder::~LLFileEncoder()
-{
-	if (mBuf)
-	{
-		delete mBuf;
-		mBuf = NULL;
-	}
-}
-
-LLString LLFileEncoder::encodeURL(const S32 max_length)
-{
-	LLString result = mFormname;
-	result.append(1, '=');
-
-	S32 i = 0;
-
-	if (max_length)
-	{
-		if (mBufLength > max_length)
-		{
-			i = mBufLength - max_length;
-		}
-	}
-
-	S32 url_buf_size = 3*mBufLength + 1;
-	char *url_buf = new char[url_buf_size];
-
-	S32 cur_pos = 0;
-	for (; i < mBufLength; i++)
+	app.setHandle(hInstance);
+	ok = app.init();
+	if(!ok)
 	{
-		S32 byte_val = mBuf[i];
-		sprintf(url_buf + cur_pos, "%%%02x", byte_val);
-		cur_pos += 3;
+		llwarns << "Unable to initialize application." << llendl;
+		return -1;
 	}
-	url_buf[i*3] = 0;
 
-	result.append(url_buf);
-	delete[] url_buf;
-	return result;
-}
-
-LLString encode_string(const char *formname, const LLString &str)
-{
-	LLString result = formname;
-	result.append(1, '=');
-	// Not using LLString because of bad performance issues
-	S32 buf_size = str.size();
-	S32 url_buf_size = 3*str.size() + 1;
-	char *url_buf = new char[url_buf_size];
+		// Run the application main loop
+	if(!LLApp::isQuitting()) app.mainLoop();
 
-	S32 cur_pos = 0;
-	S32 i;
-	for (i = 0; i < buf_size; i++)
+	if (!app.isError())
 	{
-		sprintf(url_buf + cur_pos, "%%%02x", str[i]);
-		cur_pos += 3;
+		//
+		// We don't want to do cleanup here if the error handler got called -
+		// the assumption is that the error handler is responsible for doing
+		// app cleanup if there was a problem.
+		//
+		app.cleanup();
 	}
-	url_buf[i*3] = 0;
-
-	result.append(url_buf);
-	delete[] url_buf;
-	return result;
-}
-
-void write_debug(const char *str)
-{
-	if (!gDebugFile)
-	{
-		std::string debug_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
-		llinfos << "Opening debug file " << debug_filename << llendl;
-		gDebugFile = LLFile::fopen(debug_filename.c_str(), "a+");		/* Flawfinder: ignore */
-        if (!gDebugFile)
-        {
-            fprintf(stderr, "Couldn't open %s: debug log to stderr instead.\n", debug_filename.c_str());
-            gDebugFile = stderr;
-        }
-	}
-	fprintf(gDebugFile, str);		/* Flawfinder: ignore */
-	fflush(gDebugFile);
-}
-
-void write_debug(std::string& str)
-{
-	write_debug(str.c_str());
-}
-
-S32 load_crash_behavior_setting()
-{
-	std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
-
-	gCrashSettings.loadFromFile(filename);
-		
-	S32 value = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
-	
-	if (value < CRASH_BEHAVIOR_ASK || CRASH_BEHAVIOR_NEVER_SEND < value) return CRASH_BEHAVIOR_ASK;
-
-	return value;
-}
-
-bool save_crash_behavior_setting(S32 crash_behavior)
-{
-	if (crash_behavior < CRASH_BEHAVIOR_ASK) return false;
-	if (crash_behavior > CRASH_BEHAVIOR_NEVER_SEND) return false;
-
-	gCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, crash_behavior);
-	std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
-
-	gCrashSettings.saveToFile(filename, FALSE);
-
-	return true;
+	return 0;
 }
diff --git a/indra/win_crash_logger/win_crash_logger.rc b/indra/win_crash_logger/win_crash_logger.rc
index 2c46859b95bc55ec228e1f22428a7c4197d752dd..2819722f6365732f75f83b6111bbce2cc7bc994f 100644
--- a/indra/win_crash_logger/win_crash_logger.rc
+++ b/indra/win_crash_logger/win_crash_logger.rc
@@ -56,13 +56,13 @@ END
 // Dialog
 //
 
-IDD_PROGRESS DIALOG  100, 100, 186, 33
+IDD_PROGRESS DIALOGEX 100, 100, 234, 33
 STYLE DS_SETFONT | DS_SETFOREGROUND | WS_CAPTION | WS_SYSMENU
 CAPTION "%s Crash Logger"
 CLASS "WIN_CRASH_LOGGER"
-FONT 8, "MS Sans Serif"
+FONT 8, "MS Sans Serif", 0, 0, 0x0
 BEGIN
-    LTEXT           "Static",IDC_LOG,7,7,172,8
+    LTEXT           "Static",IDC_LOG,7,7,220,8
 END
 
 IDD_REPORT DIALOGEX 100, 100, 297, 125
@@ -73,48 +73,33 @@ FONT 8, "MS Sans Serif", 0, 0, 0x0
 BEGIN
     DEFPUSHBUTTON   "Send",IDOK,198,104,45,15,WS_GROUP
     PUSHBUTTON      "Don't Send",IDCANCEL,247,104,45,15,WS_GROUP
-    LTEXT           "%s appears to have crashed.",IDC_STATIC_HEADER,4,4,288,
-                    14
-    LTEXT           "This crash reporter collects information about your computer's hardware, operating system, and some %s logs, which are used for debugging purposes only.",
-                    IDC_STATIC_WHATINFO,4,23,288,19,NOT WS_GROUP
-    CONTROL         "Remember this choice",IDC_CHECK_AUTO,"Button",
-                    BS_AUTOCHECKBOX | WS_TABSTOP,4,106,89,13
-    LTEXT           "Sending crash reports is the best way to help us improve the quality of %s.",
-                    IDC_STATIC_MOTIVATION,4,38,288,8
-    LTEXT           "If you continue to experience this problem, please try:",
-                    IDC_STATIC,4,57,251,8
-    LTEXT           "- Contacting support by visiting http://www.secondlife.com/support",
-                    IDC_STATIC,4,67,231,8
+    LTEXT           "%s appears to have crashed.",IDC_STATIC_HEADER,4,4,288,14
+    LTEXT           "This crash reporter collects information about your computer's hardware, operating system, and some %s logs, which are used for debugging purposes only.",IDC_STATIC_WHATINFO,4,23,288,19,NOT WS_GROUP
+    CONTROL         "Remember this choice",IDC_CHECK_AUTO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,4,106,89,13
+    LTEXT           "Sending crash reports is the best way to help us improve the quality of %s.",IDC_STATIC_MOTIVATION,4,43,288,8
+    LTEXT           "If you continue to experience this problem, please try:",IDC_STATIC,4,57,251,8
+    LTEXT           "- Contacting support by visiting http://www.secondlife.com/support",IDC_STATIC,4,67,231,8
 END
 
-IDD_PREVREPORTBOX DIALOG  100, 100, 232, 213
+IDD_PREVREPORTBOX DIALOGEX 100, 100, 232, 213
 STYLE DS_SETFONT | DS_SETFOREGROUND | WS_CAPTION | WS_SYSMENU
 CAPTION "%s Crash Logger"
 CLASS "WIN_CRASH_LOGGER"
-FONT 8, "MS Sans Serif"
+FONT 8, "MS Sans Serif", 0, 0, 0x0
 BEGIN
-    DEFPUSHBUTTON   "OK",IDOK,131,193,45,15,WS_GROUP
-    EDITTEXT        IDC_EDIT1,4,102,223,89,ES_MULTILINE | ES_WANTRETURN | 
-                    WS_VSCROLL
-    PUSHBUTTON      "Cancel",IDCANCEL,181,193,45,15,WS_GROUP
-    LTEXT           "%s appears to have crashed or frozen the last time it ran.",
-                    IDC_STATIC,4,4,214,8
-    LTEXT           "This crash reporter collects information about your computer's",
-                    IDC_STATIC,4,17,201,8
-    LTEXT           "hardware configuration, operating system, and some %s",
-                    IDC_STATIC,4,25,212,8
-    LTEXT           "logs, all of which are used for debugging purposes only.",
-                    IDC_STATIC,4,33,210,8
-    LTEXT           "In the space below, please briefly describe what you were doing",
-                    IDC_STATIC,3,48,208,8
-    LTEXT           "or trying to do just prior to the crash.",IDC_STATIC,3,
-                    56,204,8
-    LTEXT           "If you don't wish to send Linden Lab a crash report, press Cancel.",
-                    IDC_STATIC,3,90,214,8
-    LTEXT           "This report is NOT read by customer support.  If you have billing or",
-                    IDC_STATIC,3,68,208,8
-    LTEXT           "other questions, please go to: www.secondlife.com/support",
-                    IDC_STATIC,3,76,206,8
+    DEFPUSHBUTTON   "Send Report",IDOK,131,193,45,15,WS_GROUP
+    EDITTEXT        IDC_EDIT1,3,100,223,89,ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL
+    PUSHBUTTON      "Don't Send",IDCANCEL,181,193,45,15,WS_GROUP
+    LTEXT           "%s appears to have crashed or frozen the last time it ran.",IDC_STATIC_HEADER,4,4,214,8
+    LTEXT           "This crash reporter collects information about your computer's",IDC_STATIC,4,17,201,8
+    LTEXT           "hardware configuration, operating system, and some %s",IDC_STATIC_MSG,4,25,212,8
+    LTEXT           "logs, all of which are used for debugging purposes only.",IDC_STATIC,4,33,210,8
+    LTEXT           "In the space below, please briefly describe what you were doing",IDC_STATIC,3,48,208,8
+    LTEXT           "or trying to do just prior to the crash.",IDC_STATIC,3,56,204,8
+    LTEXT           "If you don't wish to send Linden Lab a crash report, press Don't Send.",IDC_STATIC,3,90,223,8
+    LTEXT           "This report is NOT read by customer support.  If you have billing or",IDC_STATIC,3,68,208,8
+    LTEXT           "other questions, please go to: www.secondlife.com/support",IDC_STATIC,3,76,206,8
+    CONTROL         "Remember this choice",IDC_CHECK_AUTO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,3,193,89,13
 END
 
 
@@ -158,7 +143,7 @@ BEGIN
     IDD_PROGRESS, DIALOG
     BEGIN
         LEFTMARGIN, 7
-        RIGHTMARGIN, 179
+        RIGHTMARGIN, 227
         TOPMARGIN, 7
         BOTTOMMARGIN, 26
     END