User Tools

Site Tools


Install Manager Config (to DAZ Studio)

Summary

Below is an example demonstrating how one might go about parsing information from an .ini file, converting that information into a JSON object, providing a dialog that allows an end user to select options for importing the data, and then modify the application settings to incorporate the data.

API Areas of Interest

Example

InstallManagerConfig_to_DAZStudio.dsa
// DAZ Studio version 4.7.0.12 filetype DAZ Script
 
// Define an anonymous function;
// serves as our main loop,
// limits the scope of variables
(function(){
 
	var g_sToolName = "Install Manager Configuration";
	var g_sScriptPath = getScriptFileName();
	var g_oScriptFile = new DzFileInfo( g_sScriptPath );
 
	// Install Manager configuration file keywords
	var g_sAccount = "Account";
	var g_sAccountTitle = "AccountTitle";
	var g_sAllowDesktopShorcuts = "AllowDesktopShorcuts";
	var g_sAllowStartMenuShorcuts = "AllowStartMenuShorcuts";
	var g_sAppBitArch = "AppBitArch";
	var g_sApplicationPaths = "ApplicationPaths";
	var g_sAppName = "AppName";
	var g_sAppPath = "AppPath";
	var g_sAppVersion = "AppVersion";
	var g_sAutoDelete = "AutoDelete";
	var g_sAutoInstall = "AutoInstall";
	var g_sCurInstallPath = "CurInstallPath";
	var g_sCurrentTabIndex = "CurrentTabIndex";
	var g_sDownloadDetailsToggled = "DownloadDetailsToggled";
	var g_sDownloadPath = "DownloadPath";
	var g_sDownloadSortIndex = "DownloadSortIndex";
	var g_sGeneral = "General";
	var g_sInstallDetailsToggled = "InstallDetailsToggled";
	var g_sInstalledDetailsToggled = "InstalledDetailsToggled";
	var g_sInstalledSortIndex = "InstalledSortIndex";
	var g_sInstallPath = "InstallPath";
	var g_sInstallPaths = "InstallPaths";
	var g_sInstallPathTitle = "InstallPathTitle";
	var g_sInstallSortIndex = "InstallSortIndex";
	var g_sMarkInstalledContentAsNew = "MarkInstalledContentAsNew";
	var g_sOverrideManifestDir = "OverrideManifestDir";
	var g_sOverrideThumbnailDir = "OverrideThumbnailDir";
	var g_sRememberPassword = "RememberPassword";
	var g_sShowProductInfo = "ShowProductInfo";
	var g_sShowTips = "ShowTips";
	var g_sSize = "size";
	var g_sSoftware32Path = "Software32Path";
	var g_sSoftware64Path = "Software64Path";
	var g_sTagID = "TagID";
	var g_sTags = "Tags";
	var g_sTagValue = "TagValue";
	var g_sUpdatesToPrevious = "UpdatesToPrevious";
 
	// Local keywords
	var g_sItems = "items";
 
	// Keyword collections
	var g_aMultiPartSectionNames = [ g_sInstallPaths, g_sApplicationPaths, g_sTags ];
	var g_aValidSectionNames = [ g_sGeneral ].concat( g_aMultiPartSectionNames );
 
	var g_aGeneralKeyNames = [ 
			g_sAccount, g_sAccountTitle, g_sAllowDesktopShorcuts, g_sAllowStartMenuShorcuts,
			g_sAutoDelete, g_sAutoInstall, g_sCurInstallPath, g_sCurrentTabIndex,
			g_sDownloadDetailsToggled, g_sDownloadPath, g_sDownloadSortIndex,
			g_sInstallDetailsToggled, g_sInstalledDetailsToggled, g_sInstalledSortIndex,
			g_sInstallSortIndex, g_sMarkInstalledContentAsNew, g_sOverrideManifestDir,
			g_sOverrideThumbnailDir, g_sRememberPassword, g_sShowProductInfo, g_sShowTips,
			g_sSoftware32Path, g_sSoftware64Path, g_sUpdatesToPrevious
		];
	var g_aInstallPathsKeyNames = [ g_sInstallPathTitle, g_sInstallPath ];
	var g_aApplicationPathsKeyNames = [ g_sAppName, g_sAppVersion, g_sAppBitArch, g_sAppPath ];
	var g_aTagsKeyNames = [ g_sTagID, g_sTagValue ];
 
	var g_aMultiPartKeyNames = [ g_sSize ].concat(
			g_aInstallPathsKeyNames, g_aApplicationPathsKeyNames, g_aTagsKeyNames );
 
	var g_aValidKeyNames = g_aGeneralKeyNames.concat( g_aMultiPartKeyNames );
 
	// Common strings
	var g_sNativeFormats = text( "DAZ Studio Formats" );
	var g_sPoserFormats = text( "Poser Formats" );
	var g_sOtherFormats = text( "Other Import Formats" );
 
	// Helpers
	var g_oStringHelper = new DzStringHelper();
	var g_oArrayHelper = new DzArrayHelper();
 
	// Listview created in one function; referenced in another
	var g_wLocalView = undefined;
 
	/*********************************************************************/
	// String : A function for retrieving a translation if one exists
	function text( sText )
	{
		// If the version of the application supports qsTr()
		if( typeof( qsTr ) != "undefined" ){
			// Return the translated (if any) text
			return qsTr( sText );
		}
 
		// Return the original text
		return sText;
	};
 
	/*********************************************************************/
	// void : Checks unique mapped items, unchecks non-unique, non-mapped items
	function setUniqueMappedItems( wListView )
	{
		// Get the content manager
		var oContentMgr = App.getContentMgr();
 
		// Declare working variables
		var wListViewItem;
		var sType, sLabel, sPath, sLast;
		var aPaths, aValue;
		var bMapped;
		var nPaths;
 
		// Get all of the items from the listview
		var aListItems = wListView.getItems( DzListView.All );
		// Iterate over the items
		for( var i = 0; i < aListItems.length; i += 1 ){
			// Get the 'current' item
			wListViewItem = aListItems[ i ];
			// Get the label of the item
			sLabel = wListViewItem.text( 0 );
			// If the item is a root
			if( wListViewItem.depth() == 0 ){
				// Set the type to the label of the item
				sType = sLabel;
 
				// Initialize unique path variables
				aPaths = [];
				nPaths = aPaths.length;		
 
				// Uncheck it; if it's a controller it'll get checked
				// according to the checked state of it's children
				wListViewItem.on = false;
 
				// Next!!
				continue;
			}
 
			// Get the path of the item
			sPath = wListViewItem.text( 1 );
 
			// Based on the type, check whether the path is mapped
			switch( sType ){
				case g_sNativeFormats:
					bMapped = oContentMgr.contentDirectoryIsMapped( sPath );
					break;
				case g_sPoserFormats:
					bMapped = oContentMgr.poserDirectoryIsMapped( sPath );
					break;
				case g_sOtherFormats:
					bMapped = oContentMgr.importDirectoryIsMapped( sPath );
					break;
				default:
					bMapped = false;
					return;			
			}
 
			// If the path is mapped
			if( bMapped ){			
				// Split the path into parts
				aValue = sPath.split( "/" );
				// Get the last part; lowercased for case-insensitive compares
				sLast = aValue[ aValue.length - 1 ].toLowerCase();
 
				// Lowercase the label; for case-insensitive compares
				sLabel = sLabel.toLowerCase();
 
				// If the end of the path does not match the label
				if( sLast != sLabel ){
					// It [tenitively] does not match the pattern used by the application
					bMapped = false;
 
					// If this is a special case
					if( sLast == "content" ){
						// Remove the last part
						aValue.pop();
						// Get the new last part; lowercase for case-insensitive compares
						sLast = aValue[ aValue.length - 1 ].toLowerCase();
						// If the end of the special case matches the label
						if( sLast == sLabel ){
							// It matches the pattern the application uses
							bMapped = true;
						}
					}
				}
 
				// Append the path to our list, if it is not already there
				aPaths = g_oArrayHelper.addToArray( aPaths, sPath );
				// If the path did not already exist in the list;
				// the length of the array has grown
				if( aPaths.length > nPaths ){
					// Check the item; i.e. it's unique
					wListViewItem.on = true;
					// Update our count
					nPaths = aPaths.length;
				// If the path already existed
				} else {
					// Uncheck the item; i.e. it is not unique
					wListViewItem.on = false;
				}
			// If the path is not mapped
			} else {
				// Uncheck the item
				wListViewItem.on = false;
			}
		}
	};
 
	/*********************************************************************/
	// void : Checks uniquely mapped directory items, unchecks non-unique items
	function setUniqueMappedDirs()
	{
		// If our listview is not defined
		if( typeof( g_wLocalView ) == undefined ){
			// We are done...
			return;
		}
 
		// Check the items that are uniquely mapped
		setUniqueMappedItems( g_wLocalView );
	};
 
	/*********************************************************************/
	// void : Adds the mapped directories known by Install Manager
	function addUniqueRemoteMappedPaths( wListView, aPathItems )
	{
		// If we do not have paths
		if( aPathItems.length < 1 ){
			// We are done
			return;
		}
 
		// Declare working variables
		var wPathItem, wLastItem;
		var sPath;
		var oPathItem;
		var nChildIdx;
		var aPaths;
 
		// Create the root item
		var wRootItem = wListView.firstChild();
		// Iterate until we no longer have a root item
		while( wRootItem ){		
			// Adding a new item prepends it to te list, so
			// we need to get the last item before we start
			// adding new items so that we can move new
			// items to the end of the list
 
			// Get the first child
			wLastItem = wRootItem.firstChild();
			// Set the child index based on whether we have a child
			nChildIdx = (wLastItem ? 1 : 0);
 
			// Pre-size an array of mapped paths
			aPaths = new Array( wRootItem.childCount() );
 
			// Until we've reached the last child
			while( nChildIdx < aPaths.length ){
				// Set the value of the n'th element; offset for 0 vs 1 based
				aPaths[ nChildIdx - 1 ] = wLastItem.text( 1 );
 
				// Get the next child item
				wLastItem = wLastItem.nextSibling();
				// Increment the child index
				nChildIdx += 1;
			}
 
			// Iterate over the mapped items, in reverse order,
			// because we want the same order as they currently
			// exist, and adding an item prepends to the list
			for( var i = aPathItems.length; i > 0; i -= 1 ){
				// Get the 'current' item; we adjust the index
				// because we are iterating in reverse order
				oPathItem = aPathItems[ i - 1 ];
				// Get the path from the item
				sPath = oPathItem[ g_sInstallPath ];
 
				// If the path is already in the list
				if( aPaths.indexOf( sPath ) > -1 ){
					// Next!!
					continue;
				}
 
				// Create an item for the path
				wPathItem = new DzCheckListItem( wRootItem, DzCheckListItem.CheckBox );
				// Set the label
				wPathItem.setText( 0, oPathItem[ g_sInstallPathTitle ] );
				// Set the path
				wPathItem.setText( 1, sPath );
 
				// If we have a last item
				if( wLastItem ){
					// Move the new item after it
					wPathItem.moveItem( wLastItem );
				}
 
				// Update the last item to the newly added item
				wLastItem = wPathItem;
			}
 
			// Get the next root item
			wRootItem = wRootItem.nextSibling();
		}
	};
 
	/*********************************************************************/
	// void : Adds the mapped directories known by DAZ Studio as items, based on type
	function addLocalMappedPaths( wListView, sType )
	{
		// Get the content manager
		var oContentMgr = App.getContentMgr();
 
		// Initialize the number of directories
		var nDirs = -1;
 
		// Based on type, set the number of directories
		switch( sType ){
			case g_sNativeFormats:
				nDirs = oContentMgr.getNumContentDirectories();
				break;
			case g_sPoserFormats:
				nDirs = oContentMgr.getNumPoserDirectories();
				break;
			case g_sOtherFormats:
				nDirs = oContentMgr.getNumImportDirectories();
				break;
			default:
				return;
		}
 
		// If we have directories
		if( nDirs >= 0 ){
			// Create the root item for the type
			var wRootItem = new DzCheckListItem( wListView, DzCheckListItem.CheckBoxController );
			// Set the text to the type
			wRootItem.setText( 0, sType );
			// Expand the item; show it's children
			wRootItem.open = true;
 
			// Declare working variables
			var nDir;
			var sPath;
			var oFolder;
			var wPathItem;
 
			// Iterate over the directories, in reverse order,
			// because we want them displayed in the same order
			// as they currently exist, and adding an item
			// prepends to the list
			for( var i = nDirs; i > 0; i -= 1 ){
				// Adjust the index because we are iterating in reverse order
				nDir = (i - 1);
				// Based on type, get the n'th folder
				switch( sType ){
					case g_sNativeFormats:
						oFolder = oContentMgr.getContentDirectory( nDir );
						break;
					case g_sPoserFormats:
						oFolder = oContentMgr.getPoserDirectory( nDir );
						break;
					case g_sOtherFormats:
						oFolder = oContentMgr.getImportDirectory( nDir );
						break;
					default:
						oFolder = undefined;
						break;
				}
 
				// If we do not have a folder
				if( !oFolder ){
					// Next!!
					continue;
				}
 
				// Get the full path of the folder
				sPath = oFolder.fullPath;
 
				// Create an item for the path
				wPathItem = new DzCheckListItem( wRootItem, DzCheckListItem.CheckBox );
				// Set the label
				wPathItem.setText( 0, oFolder.label );
				// Set the path
				wPathItem.setText( 1, sPath );
			}
		}
	};
 
	/*********************************************************************/
	// Object : Retrieve checked path items from a listview
	function getCheckedPathItems( wListView )
	{
		// Define working variables
		var oPaths = {};
		var aPaths = [];	
		var sType = "";
 
		// Declare working variables
		var wItem;
		var oItem;
 
		// Get all of the items
		var aCheckedItems = wListView.getItems( DzListView.All );
		// Iterate over the checked items
		for( var i = 0; i < aCheckedItems.length; i += 1 ){
			// Get the 'current' item
			wItem = aCheckedItems[ i ];
			// If the item is at the root depth; i.e. it's a checkbox controller
			if( wItem.depth() == 0 ){
				// If we have a type
				if( !sType.isEmpty() ){
					// Set the paths for the type
					oPaths[ sType ] = aPaths;
				}
 
				// Get the label
				sType = wItem.text( 0 );
 
				// Get the paths for the type
				aPaths = oPaths[ sType ];
				// If paths haven't been defined for the type yet
				if( aPaths == undefined ){
					// Create a new array
					aPaths = [];
				}
 
				// Next!!
				continue;
			}
 
			// If the item is checked
			if( wItem.on ){
				// Add the path to the array
				aPaths = g_oArrayHelper.addToArray( aPaths, wItem.text( 1 ) );
			}
 
			// If we have a type
			if( !sType.isEmpty() ){
				// Set the paths for the type
				oPaths[ sType ] = aPaths;
			}
		}
 
		// Return the path items
		return oPaths;
	};
 
	/*********************************************************************/
	// Object : Prompt the user to choose directories to be mapped
	function getUserMappedDirs( sAccount, aAccountMappedItems )
	{
		// Define common values
		var sObjectNamePrefix = g_oStringHelper.stripSpaces( g_sToolName );
		var sLabel = text( "Label" );
		var sPath = text( "Path" );	
		var sInstallManager = text( "Install Manager" );
		var sContentPathShortcuts = text( "Content Path Shortcuts" );
		var sRemoteTitle = String("%1 %2 :").arg( sInstallManager ).arg( sContentPathShortcuts );
		var sLocalTitle = text( "%1 Mapped Directories :" ).arg( App.appName );
		var sSelectCurrent = text( "Select Currently Mapped" );
 
		// Create a basic dialog
		var wDlg = new DzBasicDialog();
		// Set the title
		wDlg.caption = String("%1 (%2)").arg( g_sToolName ).arg( sAccount );
 
		// Get the wrapped QWidget
		var oDlg = wDlg.getWidget();
		// Set the object name; used to store/retrieve unique dialog geometry
		oDlg.objectName = sObjectNamePrefix;
 
		// Create a label
		var wLbl = new DzLabel( wDlg );
		// Get the wrapped QWidget
		var oLbl = wLbl.getWidget();
		// Set the object name; used for interactive lessons and inline help
		oLbl.objectName = String("%1DescriptionLbl").arg( sObjectNamePrefix );
		// Set styling options on the label
		wLbl.wordWrap = true;
		// If the application version is 4.10.0.22 or newer
		if( App.version64 >= 0x0004000a00000016 ){
			// Disable eliding
			wLbl.elideMode = DzWidget.ElideNone;
		}
		// Set the text
		wLbl.text = text( "Use the list displayed below to select which paths to " +
			"map within %1.<br/><br/>The \"What's This?\" button, in the lower left " +
			"corner of this dialog, can be used to view more information about a " +
			"particular widget; simply press the button, then click on the widget." +
			"<hr>").arg( App.appName );
		// Add the label to the dialog
		wDlg.addWidget( wLbl );
 
		// Get the current style
		var oStyle = App.getStyle();
		// Get pixel metrics from the style
		var nMargin = oStyle.pixelMetric( "DZ_GeneralMargin" );
		var nStepSize = oStyle.pixelMetric( "DZ_TreeStepSize" );
 
		// Create a label
		var wLocalGrpBx = new DzVGroupBox( wDlg );
		// Get the wrapped QWidget
		var oLocalGrpBx = wLocalGrpBx.getWidget();
		// Set the object name; used for interactive lessons and inline help
		oLocalGrpBx.objectName = String("%1LocalGrpBx").arg( sObjectNamePrefix );
		// Set the title
		wLocalGrpBx.title = sLocalTitle;
		// Set styling options on the group
		wLocalGrpBx.flat = true;
		wLocalGrpBx.insideMargin = nMargin;
		// Set the what's this text
		wLocalGrpBx.whatsThis = String("<b>%1</b><br/><br/>%2")
			.arg( sLocalTitle.replace( " :", "" ) )
			.arg( text( "Use this list to indicate which of the paths you would like to " +
			"make available within %1.<br/><br/>" +
			"Avoid selecting more than one entry with the same path in this list.  " +
			"Use the \"%2\" button to help select the paths that are currently mapped " +
			"and unique.")
			.arg( App.appName )
			.arg( sSelectCurrent ) );
		// Add the label to the dialog
		wDlg.addWidget( wLocalGrpBx );
 
		// Create a listview; for locally known mappings
		// we'll see why its defined as a global shortly
		g_wLocalView = new DzListView( wLocalGrpBx );
		// Get the wrapped QWidget
		oListView = g_wLocalView.getWidget();
		// Set the object name; used for interactive lessons and inline help
		oListView.objectName = String("%1LocalLView").arg( sObjectNamePrefix );
 
		// Add the label column
		g_wLocalView.addColumn( sLabel );
		// Add the path column
		g_wLocalView.addColumn( sPath );
		// Set selection to highlight all columns
		g_wLocalView.allColumnsShowFocus = true;
		// Disable sorting; set the sorting column to none
		g_wLocalView.setSorting( -1 );
		// Set styling options on the listview
		g_wLocalView.itemMargin = nMargin;
		g_wLocalView.treeStepSize = nStepSize;
		// Set the what's this text
		g_wLocalView.whatsThis = wLocalGrpBx.whatsThis;
 
		// Add items for the mapped directories;
		// we do this in reverse of the order we want them
		// to be displayed in, because adding an item
		// prepends it to the list and we are not sorting
		addLocalMappedPaths( g_wLocalView, g_sOtherFormats );
		addLocalMappedPaths( g_wLocalView, g_sPoserFormats );
		addLocalMappedPaths( g_wLocalView, g_sNativeFormats );
 
		// Add items for the Install Manager mappings;
		addUniqueRemoteMappedPaths( g_wLocalView, aAccountMappedItems );
 
		// Set the [unique] paths checked by default
		setUniqueMappedDirs();
 
		// Create a button
		var wCurrentBtn = new DzPushButton( wDlg );
		// Get the wrapped QWidget
		var oCurrentBtn = wCurrentBtn.getWidget();
		// Set the object name; used for interactive lessons and inline help
		oCurrentBtn.objectName = String("%1CurrentBtn").arg( sObjectNamePrefix );
		// Set the text
		wCurrentBtn.text = sSelectCurrent;
		// Set the tooltip text
		wCurrentBtn.toolTip = text( "Click to restore the selection of currently mapped unique paths" );
		// Set the what's this text
		wCurrentBtn.whatsThis = String("<b>%1</b><br/><br/>%2.<br/><br/>%3")
			.arg( wCurrentBtn.text ).arg( wCurrentBtn.toolTip )
			.arg( text( "The path of each item in the \"%1\" list is checked for whether it " +
				"is mapped and unique.  If the path of an item is mapped and unique, " +
				"that item becomes checked.  If the path of an item is not mapped and " +
				"unique, that item becomes unchecked.  Root items are \"tri-state\"; " +
				"they become checked or unchecked according to the checked state of " +
				"their respective children.").arg( sLocalTitle.replace( " :", "" ) ) );
		// Add the button to the dialog button box
		wDlg.addButton( wCurrentBtn );
 
		// Connect the button pressed signal to the function that checks unique paths;
		// the function depends on the listview being defined, and so this is why we
		// defined the listview in the global scope
		connect( wCurrentBtn, "pressed()", setUniqueMappedDirs );
 
		// If the user cancelled
		if( !wDlg.exec() ){
			// Return an empty array
			return {};
		}
 
		// Return the checked items
		return getCheckedPathItems( g_wLocalView );
	};
 
	/*********************************************************************/
	// String : Prompt the user to choose an account
	function getUserAccount( aAccounts )
	{
		// Get the current author
		var oAuthor = App.getCurrentAuthor();
		// Get the name of the author
		var sAuthor = oAuthor.name
 
		// Create a basic dialog
		var wDlg = new DzBasicDialog();
		// Set the title
		wDlg.caption = g_sToolName;
 
		// Get the wrapped QWidget
		var oDlg = wDlg.getWidget();
		// Set the object name; used to store/retrieve unique dialog geometry
		oDlg.objectName = g_oStringHelper.stripSpaces( g_sToolName ) + "Account";
 
		// Create a button group
		var wAccountGrp = new DzVButtonGroup( wDlg );
		// Get the wrapped QWidget
		oAccountGrp = wAccountGrp.getWidget();
		// Set the object name; used for interactive lessons and inline help
		oAccountGrp.objectName = oDlg.objectName + "BGrp";
		// Set the title of the group
		wAccountGrp.title = text( "Choose an Account :" );
 
		// Declare working variables
		var wRadioBtn;
		var oRadioBtn;
		var sLabel;
 
		var sWhatsThis = String("<b>%1</b><br/><br/>%2.");
 
		// Iterate over the accounts
		for( var i = 0; i < aAccounts.length; i += 1 ){
			// Create a radio button
			wRadioBtn = new DzRadioButton( wAccountGrp );
			// Get the wrapped QWidget
			oRadioBtn = wRadioBtn.getWidget();
			// Set the object name; used for interactive lessons and inline help
			oRadioBtn.objectName = oDlg.objectName + "RBtn";
 
			// Get the URI decoded name of the account
			sLabel = decodeURIComponent( aAccounts[i] );
			// Strip the file extension from the label
			sLabel = sLabel.substring( 0, sLabel.lastIndexOf(".") );
			// Set the button text
			wRadioBtn.text = sLabel;
			// Set the tooltip text
			wRadioBtn.toolTip = text( "Select to read from the \"%1\" account" ).arg( sLabel );
			// Set the what's this text
			wRadioBtn.whatsThis = sWhatsThis.arg( sLabel ).arg( wRadioBtn.toolTip );
			// Add the button to the group
			wAccountGrp.addButton( wRadioBtn, i );
 
			// If the label is the current author
			if( sLabel == sAuthor ){
				// Set that button as the default
				wRadioBtn.checked = true;
			}
		}
 
		// If no default was set
		if( wAccountGrp.selected < 0 && wRadioBtn ){
			// Set the last button as the defalt
			wRadioBtn.checked = true;
		}
 
		// Add the group to the dialog
		wDlg.addWidget( wAccountGrp );
 
		// If the user doesn't cancel
		if( wDlg.exec() ){
			// Return the user's choice
			return aAccounts[ wAccountGrp.selected ];
		}
 
		// Return no choice made
		return "";
	};
 
	/*********************************************************************/
	// Object : Reads an Install Manager configuration *.ini and converts it to a JSON object
	function parseInstallManagerConfig( sConfigPath )
	{
		// Initialize the return object
		var oData = {};
 
		// Create a file object for the config
		var oConfigFile = new DzFile( sConfigPath );
		// Open the file for reading
		oConfigFile.open( DzFile.ReadOnly );
 
		// Read the lines from the file
		var aLines = oConfigFile.readLines();
 
		// Close the file
		oConfigFile.close();
 
		// Declare working variables
		var sLine = "";
		var aParts = [], aItems = [];
		var sSection = "", sKey = "", sValue = "";
		var nKey = 0, nValue = 0;
		var vValue = undefined;
		var oSection, oItem;
		var bInRange;
 
		// Iterate over the lines
		for( var i = 0; i < aLines.length; i += 1 ){
			// Get the 'current' line, trimmed of leading/trailing whitespace
			sLine = aLines[i].trim();
			// If the line is empty
			if( sLine.isEmpty() ){
				// Next!!
				continue;
			}
 
			// Split the line between key and value
			aParts = sLine.split( "=" );
			// Set the key to the first part
			sKey = aParts.shift();
			// Set the value to the rest
			sValue = aParts.join( "=" );
 
			// If the key starts and ends with square brackets
			if( sKey.startsWith( "[" ) && sKey.endsWith( "]" ) ){
				// If we have a section and a name
				if( oSection && !sSection.isEmpty() ){
					// Update the section
					oData[ sSection ] = oSection;
				}
 
				// Get the name of the section
				sSection = sKey.substring( 1, sKey.length - 1 );
				// If the section name is not valid
				if( g_aValidSectionNames.indexOf( sSection ) < 0 ){
					// Reset the section name
					sSection = "";
					// Record/report the problem
					print( "Invalid Section :", sKey );
				}
 
				// Create a section object
				oSection = {};
 
				// Next line!!
				continue;
			}
 
			// If we do not have a value
			if( sValue.isEmpty() ){
				// Next!!
				continue;
			}
 
			// If the key is multi-part;  index\key
			if( sKey.indexOf( "\\" ) > -1 ){
				// Split the key
				aParts = sKey.split( "\\" );
				// Set the index from the first part; adjust for zero-based
				nKey = Number( aParts.shift() ) - 1;
				// Set the key to the rest
				sKey = aParts.join( "\\" );
			// If the key is not multi-part
			} else {
				// Set the index to be outside the valid multi-part range
				nKey = -1;
			}
 
			// If the key name is not valid
			if( g_aValidKeyNames.indexOf( sKey ) == -1 ) {
				// Record/report the problem
				print( "Invalid Key :", sKey );
				// Next line!!
				continue;
			}
 
			// If the value is the string representation of true
			if( sValue == "true" ){
				// Set the value to record to the boolean
				vValue = true;
			// If the value is the string representation of false
			} else if( sValue == "false" ){
				// Set the value to record to the boolean
				vValue = false;
			// If the value is the string representation of a number
			} else if( Number( sValue ) == sValue ){
				// Convert the string to a number
				nValue = Number( sValue );
				// If the number is fractional
				if( nValue % 1 > 0 ){
					// Set the value to record to the floating point conversion
					vValue = parseFloat( sValue );
				// If the number is whole
				} else {
					// Set the value to record to the integer conversion
					vValue = parseInt( sValue );
				}
			// If the value is a string
			} else {
				// Set the value to record to the string
				vValue = sValue;
			}
 
			// If the name of the key is 'size'
			if( sKey == g_sSize ){
				// Pre-size an array for the items
				aItems = new Array( vValue );
			}
 
			// If the key index is within range; it's a multi-part key
			bInRange = (nKey > -1 && nKey < aItems.length);
			if( bInRange ){
				// Get the 'current' item
				oItem = aItems[ nKey ];
				// If the object is null; it will be the first time,
				// due to pre-sizing the array
				if( oItem == null ){
					// Create a new object
					oItem = {};
				}
			}
 
			// If we have an item and the index is in range; multi-part key
			if( oItem && bInRange ){
				// Set the item's key to the value
				oItem[ sKey ] = vValue;
				// Update the item in the array
				aItems[ nKey ] = oItem;
				// Update the section with the items
				oSection[ g_sItems ] = aItems;
			// If the key is anything other than 'size'
			} else if( sKey != g_sSize ){
				// Set the value of the key, in the 'current' section
				oSection[ sKey ] = vValue;
			}
		}
 
		// If the section is named
		if( !sSection.isEmpty() ){
			// Update the section on the data object
			oData[ sSection ] = oSection;
		}
 
		// Return the data object
		return oData;
	};
 
	/*********************************************************************/
	// void : Set the directories that are mapped
	function setMappedPaths( oPaths )
	{
		// Get the content manager
		var oContentMgr = App.getContentMgr();
 
		// Declare working variables
		var sType, sPath;
		var aPaths;
 
		// Get the mapping types from the object
		var aTypes = Object.keys( oPaths );
 
		// If we have types
		if( aTypes.length > 0 ){
			// Remove all mapped directories
			oContentMgr.removeAllContentDirectories();
			oContentMgr.removeAllPoserDirectories();
			oContentMgr.removeAllImportDirectories();
		}
 
		// Iterate over the type names
		for( var i = 0; i < aTypes.length; i += 1  ){
			// Get the 'current' type
			sType = aTypes[ i ];
			// Get the 'current' paths
			aPaths = oPaths[ sType ];
			// Iterate over the paths
			for( var j = 0; j < aPaths.length; j += 1  ){
				// Get the 'current' path
				sPath = aPaths[ j ];
				// Based on mapping type, add the path
				switch( sType ){
					case g_sNativeFormats:
						oContentMgr.addContentDirectory( sPath, false );
						break;
					case g_sPoserFormats:
						oContentMgr.addPoserDirectory( sPath, false );
						break;
					case g_sOtherFormats:
						oContentMgr.addImportDirectory( sPath, false );
						break;
					default:
						break;
				}
			}
		}
 
		// Saves all currently mapped directories
		oContentMgr.saveAllMappedDirectories();
	};
 
	/*********************************************************************/
	// void : Do... whatever it is we do
	function begin()
	{
		// Get the appdata directory
		var oConfigDir = new DzDir( App.getAppDataPath() );
		// Up a directory; out of the application's directory
		oConfigDir.cdUp();
		// Change to the Install Manager directory
		oConfigDir.cd( "InstallManager" );
		// Change to the user accounts directory
		oConfigDir.cd( "UserAccounts" );
 
		// Declare working variable for the config path
		var sConfigPath = String("%1/Account.ini").arg( oConfigDir.path() );
 
		// Get all of the ini files in the directory
		var aConfigFiles = oConfigDir.entryList( "*.ini", DzDir.Files );
 
		// If there is only one
		if( aConfigFiles.length == 1 ){
			// Use it
			sConfigPath = String("%1/%2").arg( oConfigDir.path() ).arg( aConfigFiles[0] );
		// If there are multiple
		} else if( aConfigFiles.length > 1 ) {
			// Prompt the user to pick one
			sConfigPath = String("%1/%2").arg( oConfigDir.path() ).arg( getUserAccount( aConfigFiles ) );
		}
 
		// Create a file info object
		var oConfigFile = new DzFileInfo( sConfigPath );
		// If the file doesn't exist
		if( !oConfigFile.exists() ){
			// Inform the user
			MessageBox.information(
				text( "An account configuration file could not be found. Create an account " +
					"using Install Manager and then try again."),
				g_sToolName, text( "&OK" ) );
			// We cannot continue
			return;
		}
 
		// Convert the config file at the path to a JSON object
		var oConfigData = parseInstallManagerConfig( sConfigPath );
 
		// If the JSON object has install paths defined
		if( oConfigData.hasOwnProperty( g_sInstallPaths ) ){
			// Prompt the user to choose which ones they want mapped
			var oPaths = getUserMappedDirs( decodeURIComponent( oConfigFile.baseName() ), oConfigData[ g_sInstallPaths ][ g_sItems ] );
			// Update mapped directories
			setMappedPaths( oPaths );
		}
	};
 
	/*********************************************************************/
	// Start doing stuff
	begin();
 
// Finalize the function and invoke
})();