diff --git a/xtrn/DDMsgReader/DDMsgReader.js b/xtrn/DDMsgReader/DDMsgReader.js
index 0f96595351bb8e1d24b532414bbd498d6458816f..238ec8a0b0f057d35f0ecafa872fab916efd71db 100644
--- a/xtrn/DDMsgReader/DDMsgReader.js
+++ b/xtrn/DDMsgReader/DDMsgReader.js
@@ -128,6 +128,9 @@
  *                              Bug fix: When getting header lines to view, ensure the header lines
  *                              are not too wide for the user's terminal. Header lines that are too
  *                              long will be split into no more than 2 lines.
+ * 2023-04-25 Eric Oulashin     Version 1.73a
+ *                              Refactored the functions for getting message header lines. Also, now
+ *                              all message header information is retrieved.
  */
 
 "use strict";
@@ -228,13 +231,14 @@ require("dd_lightbar_menu.js", "DDLightbarMenu");
 require("html2asc.js", 'html2asc');
 require("attr_conv.js", "convertAttrsToSyncPerSysCfg");
 require("graphic.js", 'Graphic');
+require("smbdefs.js", "SMB_POLL_ANSWER");
 load('822header.js');
 var ansiterm = require("ansiterm_lib.js", 'expand_ctrl_a');
 
 
 // Reader version information
-var READER_VERSION = "1.73";
-var READER_DATE = "2023-04-17";
+var READER_VERSION = "1.73a";
+var READER_DATE = "2023-04-25";
 
 // Keyboard key codes for displaying on the screen
 var UP_ARROW = ascii(24);
@@ -491,13 +495,10 @@ var gFileAttachDir = backslash(system.node_dir + "DDMsgReader_Attachments");
 if (file_exists(gFileAttachDir))
 	deltree(gFileAttachDir);
 
-// See if the avatar support files are available, and load them if so
+// See if the avatar support file is available, and load is if so
 var gAvatar = null;
-if (file_exists(backslash(system.exec_dir) + "load/smbdefs.js") && file_exists(backslash(system.exec_dir) + "load/avatar_lib.js"))
-{
-	require("smbdefs.js", "SMB_POLL_ANSWER");
+if (file_exists(backslash(system.exec_dir) + "load/avatar_lib.js"))
 	gAvatar = load({}, "avatar_lib.js");
-}
 
 // User twitlist filename (and settings filename)
 var gUserTwitListFilename = backslash(system.data_dir + "user") + format("%04d", user.number) + ".DDMsgReader_twitlist";
@@ -854,6 +855,7 @@ function DigDistMsgReader(pSubBoardCode, pScriptArgs)
 	this.PromptAndDeleteOrUndeleteMessage = DigDistMsgReader_PromptAndDeleteOrUndeleteMessage;
 	this.PromptAndDeleteOrUndeleteSelectedMessages = DigDistMsgReader_PromptAndDeleteOrUndeleteSelectedMessages;
 	this.GetExtdMsgHdrInfo = DigDistMsgReader_GetExtdMsgHdrInfo;
+	this.GetMsgHdrFieldListText = DigDistMsgReader_GetMsgHdrFieldListText;
 	this.GetMsgInfoForEnhancedReader = DigDistMsgReader_GetMsgInfoForEnhancedReader;
 	this.GetLastReadMsgIdxAndNum = DigDistMsgReader_GetLastReadMsgIdxAndNum;
 	this.GetScanPtrMsgIdx = DigDistMsgReader_GetScanPtrMsgIdx;
@@ -1028,6 +1030,7 @@ function DigDistMsgReader(pSubBoardCode, pScriptArgs)
 		invalidMsgNumText: "\x01n\x01y\x01hInvalid message number: %d",
 		readMsgNumPromptText: "\x01n\x01g\x01h\x01i* \x01n\x01cRead message #: \x01h",
 		msgHasBeenDeletedText: "\x01n\x01h\x01g* \x01yMessage #\x01w%d \x01yhas been deleted.",
+		noHdrLinesForThisMsgText: "\x01n\x01h\x01yThere are no header lines for this message.",
 		noKludgeLinesForThisMsgText: "\x01n\x01h\x01yThere are no kludge lines for this message.",
 		searchingPersonalMailText: "\x01w\x01hSearching personal mail\x01n",
 		searchTextPromptText: "\x01cEnter the search text\x01g\x01h:\x01n\x01c ",
@@ -5564,7 +5567,8 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA
 
 					// Get an array of the extended header info/kludge lines and then
 					// allow the user to scroll through them.
-					var extdHdrInfoLines = this.GetExtdMsgHdrInfo(msgHeader, (retObj.lastKeypress == this.enhReaderKeys.showKludgeLines));
+					var onlyKludgeLines = (retObj.lastKeypress == this.enhReaderKeys.showKludgeLines);
+					var extdHdrInfoLines = this.GetExtdMsgHdrInfo(this.subBoardCode, msgHeader.number, onlyKludgeLines);
 					if (extdHdrInfoLines.length > 0)
 					{
 						if (this.userSettings.useEnhReaderScrollbar)
@@ -5598,8 +5602,9 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA
 					}
 					else
 					{
-						// There are no kludge lines for this message
-						this.DisplayEnhReaderError(replaceAtCodesInStr(this.text.noKludgeLinesForThisMsgText), msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr);
+						// There are no header/kludge lines for this message
+						var msgText = onlyKludgeLines ? this.text.noKludgeLinesForThisMsgText : this.text.noHdrLinesForThisMsgText;
+						this.DisplayEnhReaderError(replaceAtCodesInStr(msgText), msgInfo.messageLines, topMsgLineIdx, msgLineFormatStr);
 						console.gotoxy(originalCurPos);
 						writeMessage = false;
 					}
@@ -6770,7 +6775,7 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg
 					console.crlf();
 					// Get an array of the extended header info/kludge lines and then
 					// display them.
-					var extdHdrInfoLines = this.GetExtdMsgHdrInfo(msgHeader, (retObj.lastKeypress == this.enhReaderKeys.showKludgeLines));
+					var extdHdrInfoLines = this.GetExtdMsgHdrInfo(this.subBoardCode, msgHeader.number, (retObj.lastKeypress == this.enhReaderKeys.showKludgeLines));
 					if (extdHdrInfoLines.length > 0)
 					{
 						console.crlf();
@@ -8597,6 +8602,7 @@ function DigDistMsgReader_ReadConfigFile()
 					         (setting == "invalidMsgNumText") ||
 					         (setting == "readMsgNumPromptText") ||
 							 (setting == "msgHasBeenDeletedText") ||
+					         (setting == "noHdrLinesForThisMsgText") ||
 					         (setting == "noKludgeLinesForThisMsgText") ||
 							 (setting == "searchingPersonalMailText") ||
 					         (setting == "searchingSubBoardAbovePromptText") ||
@@ -9302,8 +9308,8 @@ function DigDistMsgReader_GetMsgHdrByAbsoluteNum(pMsgNum, pExpandFields, pGetVot
 	var msgbase = new MsgBase(this.subBoardCode);
 	if (msgbase.open())
 	{
-		var expandFields = (typeof(pExpandFields) == "boolean" ? pExpandFields : false);
-		var getVoteInfo = (typeof(pGetVoteInfo) == "boolean" ? pGetVoteInfo : false);
+		var expandFields = (typeof(pExpandFields) === "boolean" ? pExpandFields : false);
+		var getVoteInfo = (typeof(pGetVoteInfo) === "boolean" ? pGetVoteInfo : false);
 		msgHdr = msgbase.get_msg_header(false, pMsgNum, expandFields, getVoteInfo);
 		msgbase.close();
 	}
@@ -9311,6 +9317,22 @@ function DigDistMsgReader_GetMsgHdrByAbsoluteNum(pMsgNum, pExpandFields, pGetVot
 		msgHdr = getBogusMsgHdr();
 	return msgHdr;
 }
+function DumpMsgHdr(pSubCode, pMsgNum, pExpandFields, pGetVoteInfo)
+{
+	var hdrLines = [];
+	var msgHdr = null;
+	var msgbase = new MsgBase(pSubCode);
+	if (msgbase.open())
+	{
+		var expandFields = (typeof(pExpandFields) === "boolean" ? pExpandFields : false);
+		var getVoteInfo = (typeof(pGetVoteInfo) === "boolean" ? pGetVoteInfo : false);
+		msgHdr = msgbase.get_msg_header(false, pMsgNum, expandFields, getVoteInfo);
+		if (msgHdr != null)
+			hdrLines = msgbase.dump_msg_header(msgHdr);
+		msgbase.close();
+	}
+	return hdrLines;
+}
 
 // For the DigDistMsgReader class: Takes an absolute message number and returns
 // its message index (offset).  On error, returns -1.
@@ -12081,7 +12103,7 @@ function DigDistMsgReader_ListSubBoardsInMsgGroup_Traditional(pGrpIndex, pMarkIn
 	this.WriteSubBrdListHdrLine(grpIndex);
 	console.crlf();
 	printf(this.subBoardListHdrPrintfStr, "Sub #", "Name", "# Posts", "Latest date & time");
-	console.print("\x01n");
+	console.attributes = "N";
 
 	// List each sub-board in the message group.
 	var searchText = (typeof(pSearchText) == "string" ? pSearchText.toUpperCase() : "");
@@ -12538,27 +12560,46 @@ function DigDistMsgReader_BuildSubBoardPrintfInfoForGrp(pGrpIndex)
 // retrieves will only be retrieved if they exist in the given message header.
 //
 // Parameters:
-//  pMsgHdr: A message header
+//  pSubCodeOrMsgbase: An internal sub-board code of the messagebase, or a MsgBase object representing
+//                     the sub-board the message is in (assumed to be open)
+//  pMsgNum: The message number of the message headers to retrieve
 //  pKludgeOnly: Boolean - Whether or not to only get the kludge lines.  If false,
 //               then all header fields will be retrieved.
+//  pUseColors: Optional boolean: Whether or not to add colors to the strings. Defaults to true.
+//  pWordWrap: Optional boolean: Whether or not to word-wrap the header lines to the user's
+//             terminal width. Defaults to true.
+//  pPrependHdrLabelLines: Optional boolean - Whether or not to prepend a couple lines labeling
+//                         that these are header/kludge lines. Defaults to true.
 //
 // Return value: An array of strings containing the extended message header information
-function DigDistMsgReader_GetExtdMsgHdrInfo(pMsgHdr, pKludgeOnly)
+function DigDistMsgReader_GetExtdMsgHdrInfo(pSubCodeOrMsgbase, pMsgNum, pKludgeOnly, pUseColors, pWordWrap, pPrependHdrLabelLines)
 {
-	// If pMsgHdr is not valid, then just return an empty array.
-	if (typeof(pMsgHdr) != "object")
+	if (typeof(pMsgNum) !== "number")
 		return [];
 
 	// Get the message header with fields expanded so we can get the most info possible.
-	var msgHdr = this.GetMsgHdrByAbsoluteNum(pMsgHdr.number, true, true);
+	var msgHdr = null;
+	if (typeof(pSubCodeOrMsgbase) === "string")
+	{
+		var msgbase = new MsgBase(pSubCodeOrMsgbase);
+		if (msgbase.open())
+		{
+			msgHdr = msgbase.get_msg_header(false, pMsgNum, true, true);
+			msgbase.close();
+		}
+	}
+	else if (typeof(pSubCodeOrMsgbase) === "object" && pSubCodeOrMsgbase.hasOwnProperty("get_msg_header") && pSubCodeOrMsgbase.is_open)
+		msgHdr = pSubCodeOrMsgbase.get_msg_header(false, pMsgNum, true, true);
+
 	if (msgHdr == null)
 		return [];
+
 	// The message header retrieved that way might not have vote information,
 	// so copy any additional header information from this.hdrsForCurrentSubBoard
 	// if there's a header there for this message.
-	if (this.hdrsForCurrentSubBoardByMsgNum.hasOwnProperty(pMsgHdr.number))
+	if (this.hdrsForCurrentSubBoardByMsgNum.hasOwnProperty(pMsgNum))
 	{
-		var tmpHdrIdx = this.hdrsForCurrentSubBoardByMsgNum[pMsgHdr.number];
+		var tmpHdrIdx = this.hdrsForCurrentSubBoardByMsgNum[pMsgNum];
 		if (this.hdrsForCurrentSubBoard.hasOwnProperty(tmpHdrIdx))
 		{
 			for (var hdrProp in this.hdrsForCurrentSubBoard[tmpHdrIdx])
@@ -12569,29 +12610,207 @@ function DigDistMsgReader_GetExtdMsgHdrInfo(pMsgHdr, pKludgeOnly)
 		}
 	}
 
+	var kludgeOnly = (typeof(pKludgeOnly) == "boolean" ? pKludgeOnly : false);
+	var useColors = (typeof(pUseColors) === "boolean" ? pUseColors : true);
+	var wordWrap = (typeof(pWordWrap) === "boolean" ? pWordWrap : true);
+	var prependHdrLabelLines = (typeof(pPrependHdrLabelLines) === "boolean" ? pPrependHdrLabelLines : true);
+
+
+
 	var msgHdrInfoLines = [];
 
-	var hdrInfoLineFields = [];
-	var kludgeOnly = (typeof(pKludgeOnly) == "boolean" ? pKludgeOnly : false);
-	if (kludgeOnly)
-	{
-		hdrInfoLineFields.push({ field: "ftn_msgid", label: "MSG ID:" });
-		hdrInfoLineFields.push({ field: "X-FTN-MSGID", label: "MSG ID:" });
-		hdrInfoLineFields.push({ field: "ftn_reply", label: "Reply ID:" });
-		hdrInfoLineFields.push({ field: "X-FTN-REPLY", label: "Reply ID:" });
-		hdrInfoLineFields.push({ field: "ftn_area", label: "Area tag:" });
-		hdrInfoLineFields.push({ field: "X-FTN-AREA", label: "Area tag:" });
-		hdrInfoLineFields.push({ field: "ftn_flags", label: "Flags:" });
-		hdrInfoLineFields.push({ field: "ftn_pid", label: "Program ID:" });
-		hdrInfoLineFields.push({ field: "ftn_tid", label: "Tosser ID:" });
-		hdrInfoLineFields.push({ field: "X-FTN-TID", label: "Tosser ID:" });
-		hdrInfoLineFields.push({ field: "X-FTN-Kludge", label: "Kludge:" });
-		hdrInfoLineFields.push({ field: "X-FTN-SEEN-BY", label: "Seen-By:" });
-		hdrInfoLineFields.push({ field: "X-FTN-PATH", label: "Path:" });
-		hdrInfoLineFields.push({ field: "when_written_time", label: "When written time:" });
-		hdrInfoLineFields.push({ field: "when_imported_time", label: "When imported time:" });
+	var formatStr;
+	if (useColors)
+		formatStr = "\x01n" + this.colors.hdrLineLabelColor + "%s: \x01n" + this.colors.hdrLineValueColor + "%-s";
+	else
+		formatStr = "%s: %-s";
+
+	// Make an array of objects with 'prop' and 'textArray' fields; this array will be sorted by prop
+	var msgInfoObjs = [];
+	for (var prop in msgHdr)
+	{
+		if (prop == "field_list")
+		{
+			//var fieldListLines = this.GetMsgHdrFieldListText(msgHdr[prop], true);
+			//for (var i = 0; i < fieldListLines.length; ++i)
+			//	msgHdrInfoLines.push(fieldListLines[i]);
+			
+			var fieldListObj = GetMsgHdrFieldListObj(msgHdr.field_list);
+			var fieldListKeys = Object.keys(msgHdr);
+			fieldListKeys.sort();
+			for (var fieldListProp in fieldListObj)
+			//for (var fieldListKeyIdx = 0; fieldListKeyIdx < fieldListKeys.length; ++fieldListKeyIdx)
+			{
+				//var fieldListProp = fieldListKeys[fieldListKeyIdx];
+				//msgHdrInfoLines.push(format(formatStr, fieldListProp, fieldListObj[fieldListProp]));
+				if (Array.isArray(fieldListObj[fieldListProp]))
+				{
+					var tmpTxtLines = [];
+					if (fieldListObj[fieldListProp].length > 0)
+					{
+						//msgHdrInfoLines.push(format(formatStr, fieldListProp, fieldListObj[fieldListProp][0]));
+						tmpTxtLines.push(format(formatStr, fieldListProp, fieldListObj[fieldListProp][0]));
+						for (var i = 1; i < fieldListObj[fieldListProp].length; ++i)
+						{
+							//msgHdrInfoLines.push(fieldListObj[fieldListProp][i]);
+							tmpTxtLines.push(fieldListObj[fieldListProp][i]);
+						}
+					}
+					msgInfoObjs.push({
+						'prop': fieldListProp,
+						'textArray': tmpTxtLines
+					});
+				}
+				else
+				{
+					//msgHdrInfoLines.push(format(formatStr, fieldListProp, fieldListObj[fieldListProp]));
+					msgInfoObjs.push({
+						'prop': fieldListProp,
+						'textArray': [ format(formatStr, fieldListProp, fieldListObj[fieldListProp]) ]
+					})
+				}
+			}
+			
+		}
+		else
+		{
+			var addIt = kludgeOnly ? MsgHdrPropIsKludgeLine(prop) : true;
+			if (addIt)
+			{
+				// Remove underscores from the property for the label
+				var propLabel = prop.replace(/_/g, " ");
+				// Apply good-looking capitalization to the property label
+				if ((propLabel == "id") || (propLabel == "ftn tid"))
+					propLabel = propLabel.toUpperCase();
+				else if (propLabel == "ftn area")
+					propLabel = "FTN Area";
+				else if (propLabel == "ftn pid")
+					propLabel = "Program ID";
+				else if (propLabel == "thread id")
+					propLabel = "Thread ID";
+				else if (propLabel == "attr")
+					propLabel = "Attributes";
+				else if (propLabel == "auxattr")
+					propLabel = "Auxiliary attributes";
+				else if (propLabel == "netattr")
+					propLabel = "Network attributes";
+				else
+					propLabel = capitalizeFirstChar(propLabel);
+
+				// Value
+				var propValue = "";
+				if (typeof(msgHdr[prop]) === "function") // Such as get_rfc822_header
+					continue;
+				if (prop == "when_written_time") //itemValue = system.timestr(msgHdr.when_written_time);
+					propValue = system.timestr(msgHdr.when_written_time) + " " + system.zonestr(msgHdr.when_written_zone);
+				else if (prop == "when_imported_time") //propValue = system.timestr(msgHdr.when_imported_time);
+					propValue = system.timestr(msgHdr.when_imported_time) + " " + system.zonestr(msgHdr.when_imported_zone);
+				else if ((prop == "when_imported_zone") || (prop == "when_written_zone"))
+					propValue = system.zonestr(msgHdr[prop]);
+				else if (prop == "attr")
+					propValue = makeMainMsgAttrStr(msgHdr[prop], "None");
+				else if (prop == "auxattr")
+					propValue = makeAuxMsgAttrStr(msgHdr[prop], "None");
+				else if (prop == "netattr")
+					propValue = makeNetMsgAttrStr(msgHdr[prop], "None");
+				else
+					propValue = msgHdr[prop];
+				if (typeof(propValue) === "string")
+				{
+					// Replace tabs with spaces, and strip CRLF characters
+					// TODO: Should CRLF characters actually be split into separate lines?
+					propValue = propValue.trim().replace(/\t/g, "  ").replace(/[^\x20-\x7E]/g, '');
+					propValue = propValue.replace(/\r\n/g, "");
+					propValue = propValue.replace(/\n/g, "").replace(/\r/g, "");
+				}
+
+				//msgHdrInfoLines.push(format(formatStr, propLabel, propValue));
+				msgInfoObjs.push({
+					'prop': propLabel,
+					'textArray': [ format(formatStr, propLabel, propValue) ]
+				})
+			}
+		}
+	}
+	// Sort the header lines alphabetically
+	msgInfoObjs.sort(function(pA, pB)
+	{
+		if (pA.prop < pB.prop)
+			return -1;
+		else if (pA.prop == pB.prop)
+			return 0;
+		else
+			return 1;
+	});
+	for (var i = 0; i < msgInfoObjs.length; ++i)
+	{
+		for (var j = 0; j < msgInfoObjs[i].textArray.length; ++j)
+			msgHdrInfoLines.push(msgInfoObjs[i].textArray[j]);
+	}
+	// Free some memory
+	for (var prop in msgInfoObjs)
+		delete msgInfoObjs[prop];
+
+
+	// If the caller wants to word-wrap, make sure the header lines aren't too long for the
+	// user's terminal. And leave a column for the scrollbar.
+	var hdrInfoLinesWrapped;
+	if (wordWrap)
+	{
+		hdrInfoLinesWrapped = [];
+		var maxLen = console.screen_columns - 1;
+		var colorFormatStr = "\x01n" + this.colors.hdrLineLabelColor + "%-s: \x01n" + this.colors.hdrLineValueColor + "%-s";
+		for (var i = 0; i < msgHdrInfoLines.length; ++i)
+		{
+			//var wrappedLines = word_wrap(msgHdrInfoLines[i], maxLen).split("\n");
+			var wrappedLines = lfexpand(word_wrap(msgHdrInfoLines[i], maxLen)).split("\r\n");
+			for (var wrappedI = 0; wrappedI < wrappedLines.length; ++wrappedI)
+			{
+				if (wrappedLines[wrappedI].length == 0) continue;
+				hdrInfoLinesWrapped.push(wrappedLines[wrappedI]);
+			}
+		}
+	}
+	else // No word wrapping
+		hdrInfoLinesWrapped = msgHdrInfoLines;
+
+	// If some info lines were added, then insert a header line & blank line to
+	// the beginning of the array, and remove the last empty line from the array.
+	if (hdrInfoLinesWrapped.length > 0)
+	{
+		if (prependHdrLabelLines)
+		{
+			if (kludgeOnly)
+			{
+				hdrInfoLinesWrapped.splice(0, 0, "\x01n\x01c\x01hMessage Information/Kludge Lines\x01n");
+				hdrInfoLinesWrapped.splice(1, 0, "\x01n\x01g\x01h--------------------------------\x01n");
+			}
+			else
+			{
+				hdrInfoLinesWrapped.splice(0, 0, "\x01n\x01c\x01hMessage Headers\x01n");
+				hdrInfoLinesWrapped.splice(1, 0, "\x01n\x01g\x01h---------------\x01n");
+			}
+		}
+		if (hdrInfoLinesWrapped[hdrInfoLinesWrapped.length-1].length == 0)
+			hdrInfoLinesWrapped.pop();
 	}
 
+	return hdrInfoLinesWrapped;
+}
+
+// For the DDMsgReader class: Helper for GetExtdMsgHdrInfo() - Gets
+// text lines for the field_list property in a message header
+//
+// Parameters:
+//  pHdrFieldList: The value of the field_list property in a message header
+//  pUseColors: Boolean - Whether or not to add attribute codes to the lines
+//
+// Return value: An array with text lines for the field list, with colors
+//               for displaying message header information
+function DigDistMsgReader_GetMsgHdrFieldListText(pHdrFieldList, pUseColors)
+{
+	var textLines = [];
+
 	// This function returns the number of non-blank lines in a header info array.
 	//
 	// Return value: An object with the following properties:
@@ -12648,238 +12867,235 @@ function DigDistMsgReader_GetExtdMsgHdrInfo(pMsgHdr, pKludgeOnly)
 		return itemCount;
 	}
 
-	// Get the header fields
-	var addTheField = true;
-	var customFieldLabel = "";
-	var propCounter = 1;
-	for (var prop in msgHdr)
-	{
-		addTheField = true;
-		customFieldLabel = "";
-
-		if (prop == "field_list")
-		{
-			var hdrFieldLabel = "";
-			var lastHdrFieldLabel = null;
-			var addBlankLineAfterIdx = -1;
-			for (var fieldI = 0; fieldI < msgHdr.field_list.length; ++fieldI)
+	var fieldsAndValues = {};
+
+	var hdrFieldLabel = "";
+	var lastHdrFieldLabel = null;
+	var addBlankLineAfterIdx = -1;
+	for (var fieldI = 0; fieldI < pHdrFieldList.length; ++fieldI)
+	{
+		// TODO: Some field types can be in the array multiple times but only
+		// the last is valid.  For those, only get the last one:
+		//  32 (Reply To)
+		//  33 (Reply To agent)
+		//  34 (Reply To net type)
+		//  35 (Reply To net address)
+		//  36 (Reply To extended)
+		//  37 (Reply To position)
+		//  38 (Reply To Organization)
+		if (pUseColors)
+			hdrFieldLabel = "\x01n" + this.colors.hdrLineLabelColor + msgHdrFieldListTypeToLabel(pHdrFieldList[fieldI].type) + "\x01n";
+		else
+			hdrFieldLabel = msgHdrFieldListTypeToLabel(pHdrFieldList[fieldI].type);
+		hdrFieldLabel = hdrFieldLabel.replace(/\t/g, "  ");
+		fieldsAndValues[hdrFieldLabel] = true; // TODO: Change to the actual text
+		var infoLineWrapped = pHdrFieldList[fieldI].data;
+		var infoLineWrappedArray = lfexpand(infoLineWrapped).split("\r\n");
+		var hdrArrayNonBlankLines = findHdrFieldDataArrayNonBlankLines(infoLineWrappedArray);
+		if (hdrArrayNonBlankLines.numNonBlankLines > 0)
+		{
+			if (hdrArrayNonBlankLines.numNonBlankLines == 1)
 			{
-				// TODO: Some field types can be in the array multiple times but only
-				// the last is valid.  For those, only get the last one:
-				//  32 (Reply To)
-				//  33 (Reply To agent)
-				//  34 (Reply To net type)
-				//  35 (Reply To net address)
-				//  36 (Reply To extended)
-				//  37 (Reply To position)
-				//  38 (Reply To Organization)
-				hdrFieldLabel = "\x01n" + this.colors.hdrLineLabelColor + msgHdrFieldListTypeToLabel(msgHdr.field_list[fieldI].type) + "\x01n";
-				var infoLineWrapped = msgHdr.field_list[fieldI].data;
-				var infoLineWrappedArray = lfexpand(infoLineWrapped).split("\r\n");
-				var hdrArrayNonBlankLines = findHdrFieldDataArrayNonBlankLines(infoLineWrappedArray);
-				if (hdrArrayNonBlankLines.numNonBlankLines > 0)
-				{
-					if (hdrArrayNonBlankLines.numNonBlankLines == 1)
+				var addExtraBlankLineAtEnd = false;
+				var hdrItem = "";
+				if (pUseColors)
+					hdrItem = "\x01n" + this.colors.hdrLineValueColor + infoLineWrappedArray[hdrArrayNonBlankLines.firstNonBlankLineIdx] + "\x01n";
+				else
+					hdrItem = infoLineWrappedArray[hdrArrayNonBlankLines.firstNonBlankLineIdx];
+				hdrItem = hdrItem.replace(/\t/g, "  ");
+				// If the header field label is different, then add it to the
+				// header info lines
+				if ((lastHdrFieldLabel == null) || (hdrFieldLabel != lastHdrFieldLabel))
+				{
+					var numFieldItemsWithSameType = fieldListCountSameTypes(pHdrFieldList, fieldI);
+					if (numFieldItemsWithSameType > 1)
 					{
-						var addExtraBlankLineAtEnd = false;
-						var hdrItem = "\x01n" + this.colors.hdrLineValueColor + infoLineWrappedArray[hdrArrayNonBlankLines.firstNonBlankLineIdx] + "\x01n";
-						// If the header field label is different, then add it to the
-						// header info lines
-						if ((lastHdrFieldLabel == null) || (hdrFieldLabel != lastHdrFieldLabel))
-						{
-							var numFieldItemsWithSameType = fieldListCountSameTypes(msgHdr.field_list, fieldI);
-							if (numFieldItemsWithSameType > 1)
-							{
-								msgHdrInfoLines.push("");
-								msgHdrInfoLines.push(hdrFieldLabel);
-								addExtraBlankLineAtEnd = true;
-								addBlankLineAfterIdx = fieldI + numFieldItemsWithSameType - 1;
-							}
-							else
-							{
-								hdrItem = hdrFieldLabel + " " + hdrItem;
-								numFieldItemsWithSameType = -1;
-							}
-						}
-						if (strip_ctrl(hdrItem).length < this.msgAreaWidth)
-							msgHdrInfoLines.push(hdrItem);
-						else
-						{
-							// If the header field label is different, then add a blank line
-							// to the header info lines
-							if ((lastHdrFieldLabel == null) || (hdrFieldLabel != lastHdrFieldLabel))
-								msgHdrInfoLines.push("");
-							msgHdrInfoLines.push(hdrFieldLabel);
-							msgHdrInfoLines.push(infoLineWrappedArray[hdrArrayNonBlankLines.firstNonBlankLineIdx]);
-							if ((lastHdrFieldLabel == null) || (hdrFieldLabel != lastHdrFieldLabel))
-								msgHdrInfoLines.push("");
-						}
+						textLines.push("");
+						textLines.push(hdrFieldLabel);
+						addExtraBlankLineAtEnd = true;
+						addBlankLineAfterIdx = fieldI + numFieldItemsWithSameType - 1;
 					}
 					else
 					{
-						// If the header field label is different, then add it to the
-						// header info lines
-						if ((lastHdrFieldLabel == null) || (hdrFieldLabel != lastHdrFieldLabel))
-						{
-							msgHdrInfoLines.push("");
-							msgHdrInfoLines.push(hdrFieldLabel);
-						}
-						var infoLineWrapped = msgHdr.field_list[fieldI].data;
-						var infoLineWrappedArray = lfexpand(infoLineWrapped).split("\r\n");
-						for (var lineIdx = 0; lineIdx < infoLineWrappedArray.length; ++lineIdx)
-						{
-							if (infoLineWrappedArray[lineIdx].length > 0)
-								msgHdrInfoLines.push(infoLineWrappedArray[lineIdx]);
-						}
-						// If the header field label is different, then add a blank line to the
-						// header info lines
-						if ((lastHdrFieldLabel == null) || (hdrFieldLabel != lastHdrFieldLabel))
-							msgHdrInfoLines.push("");
+						hdrItem = hdrFieldLabel + " " + hdrItem;
+						numFieldItemsWithSameType = -1;
 					}
-					if (addBlankLineAfterIdx == fieldI)
-						msgHdrInfoLines.push("");
 				}
-				lastHdrFieldLabel = hdrFieldLabel;
-			}
-		}
-		else
-		{
-			// See if we should add this field
-			addTheField = true;
-			if (hdrInfoLineFields.length > 0)
-			{
-				addTheField = false;
-				for (var infoLineFieldIdx = 0; infoLineFieldIdx < hdrInfoLineFields.length; ++infoLineFieldIdx)
+				textLines.push(hdrItem);
+				/*
+				if (console.strlen((hdrItem) < this.msgAreaWidth)
+					textLines.push(hdrItem);
+				else
 				{
-					if (prop == hdrInfoLineFields[infoLineFieldIdx].field)
-					{
-						addTheField = true;
-						customFieldLabel = hdrInfoLineFields[infoLineFieldIdx].label;
-						break;
-					}
+					// If the header field label is different, then add a blank line
+					// to the header info lines
+					if ((lastHdrFieldLabel == null) || (hdrFieldLabel != lastHdrFieldLabel))
+						textLines.push("");
+					textLines.push(hdrFieldLabel);
+					//textLines.push(infoLineWrappedArray[hdrArrayNonBlankLines.firstNonBlankLineIdx]);
+					if ((lastHdrFieldLabel == null) || (hdrFieldLabel != lastHdrFieldLabel))
+						textLines.push("");
 				}
+				*/
 			}
-			if (!addTheField)
-				continue;
-
-			var propLabel = "";
-			if (customFieldLabel.length > 0)
-				propLabel = "\x01n" + this.colors.hdrLineLabelColor + customFieldLabel + "\x01n";
 			else
 			{
-				// Remove underscores from the property for the label
-				propLabel = prop.replace(/_/g, " ");
-				// Apply good-looking capitalization to the property label
-				if ((propLabel == "id") || (propLabel == "ftn tid"))
-					propLabel = propLabel.toUpperCase();
-				else if (propLabel == "ftn area")
-					propLabel = "FTN Area";
-				else if (propLabel == "ftn pid")
-					propLabel = "Program ID";
-				else if (propLabel == "thread id")
-					propLabel = "Thread ID";
-				else if (propLabel == "attr")
-					propLabel = "Attributes";
-				else if (propLabel == "auxattr")
-					propLabel = "Auxiliary attributes";
-				else if (propLabel == "netattr")
-					propLabel = "Network attributes";
-				else
-					propLabel = capitalizeFirstChar(propLabel);
-				// Add the label color and trailing colon to the label text
-				propLabel = "\x01n" + this.colors.hdrLineLabelColor + propLabel + ":\x01n";
-			}
-			var infoLineWrapped = word_wrap(msgHdr[prop], this.msgAreaWidth);
-			var infoLineWrappedArray = lfexpand(infoLineWrapped).split("\r\n");
-			var itemValue = "";
-			for (var lineIdx = 0; lineIdx < infoLineWrappedArray.length; ++lineIdx)
-			{
-				if (infoLineWrappedArray[lineIdx].length > 0)
+				// If the header field label is different, then add it to the
+				// header info lines
+				if ((lastHdrFieldLabel == null) || (hdrFieldLabel != lastHdrFieldLabel))
 				{
-					// Set itemValue to the value that should be displayed
-					if (typeof(msgHdr[prop]) === "function") // Such as get_rfc822_header
-						continue;
-					else if (prop == "when_written_time") //itemValue = system.timestr(msgHdr.when_written_time);
-						itemValue = system.timestr(msgHdr.when_written_time) + " " + system.zonestr(msgHdr.when_written_zone);
-					else if (prop == "when_imported_time") //itemValue = system.timestr(msgHdr.when_imported_time);
-						itemValue = system.timestr(msgHdr.when_imported_time) + " " + system.zonestr(msgHdr.when_imported_zone);
-					else if ((prop == "when_imported_zone") || (prop == "when_written_zone"))
-						itemValue = system.zonestr(msgHdr[prop]);
-					else if (prop == "attr")
-						itemValue = makeMainMsgAttrStr(msgHdr[prop], "None");
-					else if (prop == "auxattr")
-						itemValue = makeAuxMsgAttrStr(msgHdr[prop], "None");
-					else if (prop == "netattr")
-						itemValue = makeNetMsgAttrStr(msgHdr[prop], "None");
-					else
-						itemValue = infoLineWrappedArray[lineIdx];
-					// Add the value color to the value text
-					itemValue = "\x01n" + this.colors.hdrLineValueColor + itemValue + "\x01n";
-
-					var hdrItem = propLabel + " " + itemValue;
-					if (strip_ctrl(hdrItem).length < this.msgAreaWidth)
-						msgHdrInfoLines.push(hdrItem);
-					else
-					{
-						// If this isn't the first header property, then add an empty
-						// line to the info lines array for spacing
-						if (propCounter > 1)
-							msgHdrInfoLines.push("");
-						msgHdrInfoLines.push(propLabel);
-						// In case the item value is too long to fit into the
-						// message reading area, split it and add all the split lines.
-						var itemValueWrapped = word_wrap(itemValue, this.msgAreaWidth);
-						var itemValueWrappedArray = lfexpand(itemValueWrapped).split("\r\n");
-						for (var lineIdx2 = 0; lineIdx2 < itemValueWrappedArray.length; ++lineIdx2)
-						{
-							if (itemValueWrappedArray[lineIdx2].length > 0)
-								msgHdrInfoLines.push(itemValueWrappedArray[lineIdx2]);
-						}
-
-						// If this isn't the first header property, then add an empty
-						// line to the info lines array for spacing
-						if (propCounter > 1)
-							msgHdrInfoLines.push("");
-					}
+					textLines.push("");
+					textLines.push(hdrFieldLabel);
+				}
+				var infoLineWrapped = pHdrFieldList[fieldI].data;
+				var infoLineWrappedArray = lfexpand(infoLineWrapped).split("\r\n");
+				var preAttrs = pUseColors ? "\x01n" + this.colors.hdrLineValueColor : "";
+				var postAttrs = pUseColors ? "\x01n" : "";
+				for (var lineIdx = 0; lineIdx < infoLineWrappedArray.length; ++lineIdx)
+				{
+					if (infoLineWrappedArray[lineIdx].length > 0)
+						textLines.push(preAttrs + infoLineWrappedArray[lineIdx] + postAttrs);
 				}
+				// If the header field label is different, then add a blank line to the
+				// header info lines
+				if ((lastHdrFieldLabel == null) || (hdrFieldLabel != lastHdrFieldLabel))
+					textLines.push("");
 			}
+			if (addBlankLineAfterIdx == fieldI)
+				textLines.push("");
 		}
-
-		++propCounter;
+		lastHdrFieldLabel = hdrFieldLabel;
 	}
 
-	// If some info lines were added, then insert a header line & blank line to
-	// the beginning of the array, and remove the last empty line from the array.
-	if (msgHdrInfoLines.length > 0)
+	// For each line, replace tabs with spaces, remove any unprintable characters,
+	// and in case any line has any CRLF characters, split the lines on CRLF
+	for (var i = 0; i < textLines.length; ++i)
 	{
-		if (kludgeOnly)
+		textLines[i] = textLines[i].replace(/\t/g, "  ");
+		//textLines[i] = textLines[i].replace(/\r|\n/g, "");
+		var array = textLines[i].split("\r\n");
+		if (array.length > 1)
 		{
-			msgHdrInfoLines.splice(0, 0, "\x01n\x01c\x01hMessage Information/Kludge Lines\x01n");
-			msgHdrInfoLines.splice(1, 0, "\x01n\x01g\x01h--------------------------------\x01n");
+			textLines[i] = array[0];
+			for (var array2Idx = 1; array2Idx < array.length; ++array2Idx)
+			{
+				if (array[array2Idx].length > 0)
+					textLines.splice(i+array2Idx, 0, array[array2Idx]);
+			}
 		}
-		else
+		//textLines[i] = textLines[i].replace(/[^\x20-\x7E]/g, ''); // Remove unprintable characters
+	}
+
+	return textLines;
+}
+// Helper for GetExtdMsgHdrInfo() - For the "field_list"
+// property of a message header, this gathers the field labels & values into an
+// object (where the object properties are the labels)
+//
+// Parameters:
+//  pHdrFieldArray: The value of the field_list property in a message header
+//                  (this is normally an array of objects containing 'type' and
+//                  'data' properties)
+//
+// Return value: An object where the properties are the field labels, and the
+//               value for each is usually an array of strings
+function GetMsgHdrFieldListObj(pHdrFieldArray)
+{
+	if (!Array.isArray(pHdrFieldArray))
+		return {};
+
+	// This function returns the number of non-blank lines in a header info array.
+	//
+	// Return value: An object with the following properties:
+	//               numNonBlankLines: The number of non-blank lines in the array
+	//               firstNonBlankLineIdx: The index of the first non-blank line
+	//               lastNonBlankLineIdx: The index of the last non-blank line
+	function findHdrFieldDataArrayNonBlankLines(pHdrArray)
+	{
+		var retObj = {
+			numNonBlankLines: 0,
+			firstNonBlankLineIdx: -1,
+			lastNonBlankLineIdx: -1
+		};
+
+		for (var lineIdx = 0; lineIdx < pHdrArray.length; ++lineIdx)
 		{
-			msgHdrInfoLines.splice(0, 0, "\x01n\x01c\x01hMessage Headers\x01n");
-			msgHdrInfoLines.splice(1, 0, "\x01n\x01g\x01h---------------\x01n");
+			if (pHdrArray[lineIdx].length > 0)
+			{
+				++retObj.numNonBlankLines;
+				if (retObj.firstNonBlankLineIdx == -1)
+					retObj.firstNonBlankLineIdx = lineIdx;
+				retObj.lastNonBlankLineIdx = lineIdx;
+			}
 		}
-		if (msgHdrInfoLines[msgHdrInfoLines.length-1].length == 0)
-			msgHdrInfoLines.pop();
+
+		return retObj;
 	}
 
-	// Make sure the header lines aren't too long for the user's terminal.
-	// Leave a column for the scrollbar.
-	var maxLen = console.screen_columns - 1;
-	var hdrInfoLinesWrapped = [];
-	for (var i = 0; i < msgHdrInfoLines.length; ++i)
-	{
-		var wrappedLines = word_wrap(msgHdrInfoLines[i], maxLen).split("\n");
-		for (var wrappedI = 0; wrappedI < wrappedLines.length; ++wrappedI)
+	var fieldListObj = {};
+	for (var fieldI = 0; fieldI < pHdrFieldArray.length; ++fieldI)
+	{
+		// TODO: Some field types can be in the array multiple times but only
+		// the last is valid.  For those, only get the last one:
+		//  32 (Reply To)
+		//  33 (Reply To agent)
+		//  34 (Reply To net type)
+		//  35 (Reply To net address)
+		//  36 (Reply To extended)
+		//  37 (Reply To position)
+		//  38 (Reply To Organization)
+		var hdrFieldLabel = msgHdrFieldListTypeToLabel(pHdrFieldArray[fieldI].type, false);
+		hdrFieldLabel = hdrFieldLabel.replace(/\t/g, "  ");
+
+		// Add the data to fieldListObj, with the label as the property
+		//fieldListObj[hdrFieldLabel] = pHdrFieldArray[fieldI].data.replace(/\t/g, "  ");
+		// Make the data an array of strings, split based on CRLF characters
+		var infoLineWrappedArray = lfexpand(pHdrFieldArray[fieldI].data).replace(/\t/g, "  ").split("\r\n");
+		// If any of the strings starts with a date/time ("WhenExported" or "WhenImported"),
+		// then format it so that the date & time are more readable
+		for (var i = 0; i < infoLineWrappedArray.length; ++i)
+		{
+			if ((infoLineWrappedArray[i].indexOf("WhenExported") == 0 || infoLineWrappedArray[i].indexOf("WhenImported") == 0) && infoLineWrappedArray[i].length >= 28)
+			{
+				//system.timestr(msgHdr.when_imported_time) + " " + system.zonestr(msgHdr.when_imported_zone)
+				var firstPart = infoLineWrappedArray[i].substr(0, 14);
+				var yearStr = infoLineWrappedArray[i].substr(14, 4);
+				var monthStr = infoLineWrappedArray[i].substr(18, 2);
+				var dayStr = infoLineWrappedArray[i].substr(20, 2);
+				var hourStr = infoLineWrappedArray[i].substr(22, 2);
+				var minStr = infoLineWrappedArray[i].substr(24, 2);
+				var secStr = infoLineWrappedArray[i].substr(26, 2);
+				var remaining = infoLineWrappedArray[i].substr(28);
+				infoLineWrappedArray[i] = format("%s%s-%s-%s %s:%s:%s %s", firstPart, yearStr, monthStr, dayStr, hourStr, minStr, secStr, remaining);
+			}
+		}
+		if (!fieldListObj.hasOwnProperty(hdrFieldLabel))
+			fieldListObj[hdrFieldLabel] = infoLineWrappedArray;
+		else
 		{
-			if (console.strlen(wrappedLines[wrappedI]) > 0)
-				hdrInfoLinesWrapped.push(wrappedLines[wrappedI]);
+			// Append to the data already there
+			fieldListObj[hdrFieldLabel].push("");
+			for (var i = 0; i < infoLineWrappedArray.length; ++i)
+				fieldListObj[hdrFieldLabel].push(infoLineWrappedArray[i]);
 		}
 	}
-	return hdrInfoLinesWrapped;
+
+	return fieldListObj;
+}
+
+// Returns whether a message header property name can be considered a "kludge line"
+function MsgHdrPropIsKludgeLine(pPropName)
+{
+	if (typeof(pPropName) !== "string" || pPropName.length == 0)
+		return false;
+
+	var propNameUpper = pPropName.toUpperCase();
+	return (propNameUpper == "FTN_MSGID" || propNameUpper == "X-FTN-MSGID" || propNameUpper == "FTN_REPLY" ||
+	        propNameUpper == "X-FTN-REPLY" || propNameUpper == "FTN_AREA" || propNameUpper == "X-FTN-AREA" ||
+	        propNameUpper == "FTN_FLAGS" || propNameUpper == "FTN_PID" || propNameUpper == "FTN_TID" ||
+	        propNameUpper == "X-FTN-TID" || propNameUpper == "X-FTN-KLUDGE" ||
+	        propNameUpper == "X-FTN-SEEN-BY" || propNameUpper == "X-FTN-PATH" ||
+	        propNameUpper == "WHEN_WRITTEN_TIME" || propNameUpper == "WHEN_IMPORTED_TIME");
 }
 
 // For the DigDistMsgReader class: Gets & prepares message information for
@@ -14666,32 +14882,15 @@ function DigDistMsgReader_SaveMsgToFile(pMsgHdr, pFilename)
 	if (msgbase.open())
 	{
 		var msgBody = msgbase.get_msg_body(false, pMsgHdr.number, false, false, true, true);
+		var hdrLines = this.GetExtdMsgHdrInfo(msgbase, pMsgHdr.number, false, false, false, false);
 		msgbase.close();
 
 		var messageSaveFile = new File(pFilename);
 		if (messageSaveFile.open("w"))
 		{
-			// Write some header information to the file
-			if (pMsgHdr.hasOwnProperty("from"))
-				messageSaveFile.writeln("From: " + pMsgHdr.from);
-			if (pMsgHdr.hasOwnProperty("to"))
-				messageSaveFile.writeln("  To: " + pMsgHdr.to);
-			if (pMsgHdr.hasOwnProperty("subject"))
-				messageSaveFile.writeln("Subj: " + pMsgHdr.subject);
-			/*
-			if (pMsgHdr.hasOwnProperty("when_written_time"))
-				messageSaveFile.writeln(strftime("Date: %Y-%m-%d %H:%M:%S", msgHeader.when_written_time));
-			*/
-			if (pMsgHdr.hasOwnProperty("date"))
-				messageSaveFile.writeln("Date: " + pMsgHdr.date);
-			if (pMsgHdr.hasOwnProperty("from_net_addr"))
-				messageSaveFile.writeln("From net address: " + pMsgHdr.from_net_addr);
-			if (pMsgHdr.hasOwnProperty("to_net_addr"))
-				messageSaveFile.writeln("To net address: " + pMsgHdr.to_net_addr);
-			if (pMsgHdr.hasOwnProperty("id"))
-				messageSaveFile.writeln("ID: " + pMsgHdr.id);
-			if (pMsgHdr.hasOwnProperty("reply_id"))
-				messageSaveFile.writeln("Reply ID: " + pMsgHdr.reply_id);
+			// Write the header information to the file
+			for (var i = 0; i < hdrLines.length; ++i)
+				messageSaveFile.writeln(hdrLines[i]);
 			messageSaveFile.writeln("===============================");
 
 			// If the message body has ANSI, then use the Graphic object to strip it
@@ -18302,102 +18501,54 @@ function quoteStrWithSpaces(pStr)
 // Return value: A text label for the field (a string)
 function msgHdrFieldListTypeToLabel(pFieldListType, pIncludeTrailingColon)
 {
-	// The page at this URL lists the header field types:
-	// http://synchro.net/docs/smb.html#Header Field Types:
-
-	var fieldTypeLabel = "Unknown (" + pFieldListType.toString() + ")";
+	var fieldTypeLabel = "";
 	switch (pFieldListType)
 	{
-		case 0: // Sender
-			fieldTypeLabel = "Sender";
-			break;
-		case 1: // Sender Agent
-			fieldTypeLabel = "Sender Agent";
-			break;
-		case 2: // Sender net type
-			fieldTypeLabel = "Sender Net Type";
-			break;
-		case 3: // Sender Net Address
-			fieldTypeLabel = "Sender Net Address";
-			break;
-		case 4: // Sender Agent Extension
-			fieldTypeLabel = "Sender Agent Extension";
-			break;
-		case 5: // Sending agent (Sender POS)
-			fieldTypeLabel = "Sender Agent";
-			break;
-		case 6: // Sender organization
-			fieldTypeLabel = "Sender Organization";
+		case SMB_COMMENT:
+			fieldTypeLabel = "Comment";
+			break
+		case SMB_POLL_ANSWER:
+			fieldTypeLabel = "Poll answer";
 			break;
-		case 16: // Author
-			fieldTypeLabel = "Author";
+		case 0x64: // SMB_GROUP
+			fieldTypeLabel = "Group";
+			break;			
+		case FIDOCTRL:
+			fieldTypeLabel = "FIDO control";
 			break;
-		case 17: // Author Agent
-			fieldTypeLabel = "Author Agent";
-			break;
-		case 18: // Author Net Type
-			fieldTypeLabel = "Author Net Type";
-			break;
-		case 19: // Author Net Address
-			fieldTypeLabel = "Author Net Address";
-			break;
-		case 20: // Author Extension
-			fieldTypeLabel = "Author Extension";
-			break;
-		case 21: // Author Agent (Author POS)
-			fieldTypeLabel = "Author Agent";
-			break;
-		case 22: // Author Organization
-			fieldTypeLabel = "Author Organization";
-			break;
-		case 32: // Reply To
-			fieldTypeLabel = "Reply To";
-			break;
-		case 33: // Reply To agent
-			fieldTypeLabel = "Reply To Agent";
-			break;
-		case 34: // Reply To net type
-			fieldTypeLabel = "Reply To net type";
-			break;
-		case 35: // Reply To net address
-			fieldTypeLabel = "Reply To net address";
-			break;
-		case 36: // Reply To extension
-			fieldTypeLabel = "Reply To (extended)";
+		case FIDOSEENBY:
+			fieldTypeLabel = "Seen-by";
 			break;
-		case 37: // Reply To position
-			fieldTypeLabel = "Reply To position";
+		case FIDOPATH:
+			fieldTypeLabel = "FIDO Path";
 			break;
-		case 38: // Reply To organization (0x26 hex)
-			fieldTypeLabel = "Reply To organization";
+		case RFC822HEADER:
+			fieldTypeLabel = "RFCC822 Header";
 			break;
-		case 48: // Recipient (0x30 hex)
-			fieldTypeLabel = "Recipient";
+		case 0xB3: // RFC822TO
+			fieldTypeLabel = "RFC822 To";
 			break;
-		case 162: // Seen-by
-			fieldTypeLabel = "Seen-by";
+		case 0xB6: // RFC822CC
+			fieldTypeLabel = "RFC822 CC";
 			break;
-		case 163: // Path
-			fieldTypeLabel = "Path";
+		case 0xB7: // RFC822ORG
+			fieldTypeLabel = "RFC822 Org";
 			break;
-		case 176: // RFCC822 Header
-			fieldTypeLabel = "RFCC822 Header";
+		case 0xB4: // RFC822FROM
+			fieldTypeLabel = "RFC822 From";
 			break;
-		case 177: // RFC822 MSGID
-			fieldTypeLabel = "RFC822 MSGID";
+		case 0xB5: // RFC822REPLYTO
+			fieldTypeLabel = "RFC822 Reply To";
 			break;
-		case 178: // RFC822 REPLYID
-			fieldTypeLabel = "RFC822 REPLYID";
+		case 0xB8: // RFC822SUBJECT
+			fieldTypeLabel = "RFC822 Subject";
 			break;
-		case 240: // UNKNOWN
-			fieldTypeLabel = "UNKNOWN";
+		case SMTPRECEIVED:
+			fieldTypeLabel = "SMTP Received";
 			break;
-		case 241: // UNKNOWNASCII
+		case 0xF1: // UNKNOWNASCII
 			fieldTypeLabel = "UNKNOWN (ASCII)";
 			break;
-		case 255:
-			fieldTypeLabel = "UNUSED";
-			break;
 		default:
 			fieldTypeLabel = "Unknown (" + pFieldListType.toString() + ")";
 			break;
@@ -21700,6 +21851,7 @@ function clearScreenRectangle(pX, pY, pWidth, pHeight)
 	}
 }
 
+
 ///////////////////////////////////////////////////////////////////////////////////
 
 // For debugging: Writes some text on the screen at a given location with a given pause.
diff --git a/xtrn/DDMsgReader/readme.txt b/xtrn/DDMsgReader/readme.txt
index 42ae2d5729e024d26b925320885992eaafdbf060..bade25928ad3b5c57d1f7b0c823f0a302ebfa8ba 100644
--- a/xtrn/DDMsgReader/readme.txt
+++ b/xtrn/DDMsgReader/readme.txt
@@ -1,6 +1,6 @@
                       Digital Distortion Message Reader
-                                 Version 1.73
-                           Release date: 2023-04-17
+                                 Version 1.73a
+                           Release date: 2023-04-25
 
                                      by
 
diff --git a/xtrn/DDMsgReader/revision_history.txt b/xtrn/DDMsgReader/revision_history.txt
index f3b88cd48267b657a802132940d8eaeec06669c3..59d5de8f2d8f913fbba893dcd7bbca952cdd555b 100644
--- a/xtrn/DDMsgReader/revision_history.txt
+++ b/xtrn/DDMsgReader/revision_history.txt
@@ -5,6 +5,9 @@ Revision History (change log)
 =============================
 Version  Date         Description
 -------  ----         -----------
+1.73a    2023-04-25   For viewing message headers, now all message header
+                      information is displayed & sorted alphabetically by field
+                      name (same with saving a message to a file).
 1.73     2023-04-17   Bug fix: When getting header lines to view, ensure the
                       header lines are not too wide for the user's terminal.
                       Header lines that are too long will be split into no more