diff --git a/indra/llvfs/lldir_mac.cpp b/indra/llvfs/lldir_mac.cpp
index 3e296831ae6b276c83732c04724c53671a0d6375..346f7dd8ed762f668185b956cc7b4837f6f9c95b 100644
--- a/indra/llvfs/lldir_mac.cpp
+++ b/indra/llvfs/lldir_mac.cpp
@@ -424,7 +424,7 @@ BOOL LLDir_Mac::fileExists(const std::string &filename) const
 
 /*virtual*/ std::string LLDir_Mac::getLLPluginLauncher()
 {
-	return gDirUtilp->getExecutableDir() + gDirUtilp->getDirDelimiter() +
+	return gDirUtilp->getAppRODataDir() + gDirUtilp->getDirDelimiter() +
 		"SLPlugin";
 }
 
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 7084fca865d32f4c08442b3f6d4e640938314ff8..6ef905cca7359d4f3c31d8a19da72fa92fee72bd 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -1,796 +1,796 @@
-#!/usr/bin/python
-# @file viewer_manifest.py
-# @author Ryan Williams
-# @brief Description of all installer viewer files, and methods for packaging
-#        them into installers for all supported platforms.
-#
-# $LicenseInfo:firstyear=2006&license=viewergpl$
-# 
-# Copyright (c) 2006-2009, Linden Research, Inc.
-# 
-# Second Life Viewer Source Code
-# The source code in this file ("Source Code") is provided by Linden Lab
-# to you under the terms of the GNU General Public License, version 2.0
-# ("GPL"), unless you have obtained a separate licensing agreement
-# ("Other License"), formally executed by you and Linden Lab.  Terms of
-# the GPL can be found in doc/GPL-license.txt in this distribution, or
-# online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
-# 
-# There are special exceptions to the terms and conditions of the GPL as
-# it is applied to this Source Code. View the full text of the exception
-# in the file doc/FLOSS-exception.txt in this software distribution, or
-# online at
-# http://secondlifegrid.net/programs/open_source/licensing/flossexception
-# 
-# By copying, modifying or distributing this software, you acknowledge
-# that you have read and understood your obligations described above,
-# and agree to abide by those obligations.
-# 
-# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
-# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
-# COMPLETENESS OR PERFORMANCE.
-# $/LicenseInfo$
-import sys
-import os.path
-import re
-import tarfile
-viewer_dir = os.path.dirname(__file__)
-# add llmanifest library to our path so we don't have to muck with PYTHONPATH
-sys.path.append(os.path.join(viewer_dir, '../lib/python/indra/util'))
-from llmanifest import LLManifest, main, proper_windows_path, path_ancestors
-
-class ViewerManifest(LLManifest):
-    def construct(self):
-        super(ViewerManifest, self).construct()
-        self.exclude("*.svn*")
-        self.path(src="../../scripts/messages/message_template.msg", dst="app_settings/message_template.msg")
-        self.path(src="../../etc/message.xml", dst="app_settings/message.xml")
-
-        if self.prefix(src="app_settings"):
-            self.exclude("logcontrol.xml")
-            self.exclude("logcontrol-dev.xml")
-            self.path("*.pem")
-            self.path("*.ini")
-            self.path("*.xml")
-            self.path("*.db2")
-
-            # include the entire shaders directory recursively
-            self.path("shaders")
-            # ... and the entire windlight directory
-            self.path("windlight")
-            self.end_prefix("app_settings")
-
-        if self.prefix(src="character"):
-            self.path("*.llm")
-            self.path("*.xml")
-            self.path("*.tga")
-            self.end_prefix("character")
-
-        # Include our fonts
-        if self.prefix(src="fonts"):
-            self.path("*.ttf")
-            self.path("*.txt")
-            self.end_prefix("fonts")
-
-        # skins
-        if self.prefix(src="skins"):
-                self.path("paths.xml")
-                # include the entire textures directory recursively
-                if self.prefix(src="*/textures"):
-                        self.path("*/*.tga")
-                        self.path("*/*.j2c")
-                        self.path("*/*.jpg")
-                        self.path("*/*.png")
-                        self.path("*.tga")
-                        self.path("*.j2c")
-                        self.path("*.jpg")
-                        self.path("*.png")
-                        self.path("textures.xml")
-                        self.end_prefix("*/textures")
-                self.path("*/xui/*/*.xml")
-                self.path("*/xui/*/widgets/*.xml")
-                self.path("*/*.xml")
-                
-                # Local HTML files (e.g. loading screen)
-                if self.prefix(src="*/html"):
-                        self.path("*.png")
-                        self.path("*/*/*.html")
-                        self.path("*/*/*.gif")
-                        self.end_prefix("*/html")
-                self.end_prefix("skins")
-        
-        # Files in the newview/ directory
-        self.path("gpu_table.txt")
-
-    def login_channel(self):
-        """Channel reported for login and upgrade purposes ONLY;
-        used for A/B testing"""
-        # NOTE: Do not return the normal channel if login_channel
-        # is not specified, as some code may branch depending on
-        # whether or not this is present
-        return self.args.get('login_channel')
-
-    def grid(self):
-        return self.args['grid']
-    def channel(self):
-        return self.args['channel']
-    def channel_unique(self):
-        return self.channel().replace("Second Life", "").strip()
-    def channel_oneword(self):
-        return "".join(self.channel_unique().split())
-    def channel_lowerword(self):
-        return self.channel_oneword().lower()
-
-    def flags_list(self):
-        """ Convenience function that returns the command-line flags
-        for the grid"""
-
-        # Set command line flags relating to the target grid
-        grid_flags = ''
-        if not self.default_grid():
-            grid_flags = "--grid %(grid)s "\
-                         "--helperuri http://preview-%(grid)s.secondlife.com/helpers/" %\
-                           {'grid':self.grid()}
-
-        # set command line flags for channel
-        channel_flags = ''
-        if self.login_channel() and self.login_channel() != self.channel():
-            # Report a special channel during login, but use default
-            channel_flags = '--channel "%s"' % (self.login_channel())
-        elif not self.default_channel():
-            channel_flags = '--channel "%s"' % self.channel()
-
-        # Deal with settings 
-        setting_flags = ''
-        if not self.default_channel() or not self.default_grid():
-            if self.default_grid():
-                setting_flags = '--settings settings_%s.xml'\
-                                % self.channel_lowerword()
-            else:
-                setting_flags = '--settings settings_%s_%s.xml'\
-                                % (self.grid(), self.channel_lowerword())
-                                                
-        return " ".join((channel_flags, grid_flags, setting_flags)).strip()
-
-
-class WindowsManifest(ViewerManifest):
-    def final_exe(self):
-        if self.default_channel():
-            if self.default_grid():
-                return "SecondLife.exe"
-            else:
-                return "SecondLifePreview.exe"
-        else:
-            return ''.join(self.channel().split()) + '.exe'
-
-
-    def test_msvcrt_and_copy_action(self, src, dst):
-        # This can is used to test a dll manifest.
-        # It is used as a temporary override during the construct method
-        from test_win32_manifest import test_assembly_binding
-        if src and (os.path.exists(src) or os.path.islink(src)):
-            # ensure that destination path exists
-            self.cmakedirs(os.path.dirname(dst))
-            self.created_paths.append(dst)
-            if not os.path.isdir(src):
-                if(self.args['configuration'].lower() == 'debug'):
-                    test_assembly_binding(src, "Microsoft.VC80.DebugCRT", "8.0.50727.4053")
-                else:
-                    test_assembly_binding(src, "Microsoft.VC80.CRT", "8.0.50727.4053")
-                self.ccopy(src,dst)
-            else:
-                raise Exception("Directories are not supported by test_CRT_and_copy_action()")
-        else:
-            print "Doesn't exist:", src
-
-    def enable_crt_check(self):
-        WindowsManifest.copy_action = WindowsManifest.test_msvcrt_and_copy_action
-
-    def disable_crt_check(self):
-        del WindowsManifest.copy_action
-
-    def construct(self):
-        super(WindowsManifest, self).construct()
-        # Find secondlife-bin.exe in the 'configuration' dir, then rename it to the result of final_exe.
-        self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe())
-
-        self.enable_crt_check()
-
-        # Plugin host application
-        self.path(os.path.join(os.pardir,
-                               'llplugin', 'slplugin', self.args['configuration'], "slplugin.exe"),
-                  "slplugin.exe")
-        
-        # need to get the llcommon.dll from the build directory as well
-        if self.prefix(src=self.args['configuration'], dst=""):
-            try:
-                self.path('llcommon.dll')
-                self.path('libapr-1.dll')
-                self.path('libaprutil-1.dll')
-                self.path('libapriconv-1.dll')
-            except RuntimeError:
-                print "Skipping llcommon.dll (assuming llcommon was linked statically)"
-        self.end_prefix()
-
-        # need to get the kdu dll from the build directory as well
-        try:
-            self.path('%s/llkdu.dll' % self.args['configuration'], dst='llkdu.dll')
-        except RuntimeError:
-            print "Skipping llkdu.dll"
-
-        self.disable_crt_check()
-
-        self.path(src="licenses-win32.txt", dst="licenses.txt")
-        self.path("featuretable.txt")
-
-        # For use in crash reporting (generates minidumps)
-        self.path("dbghelp.dll")
-
-        # For using FMOD for sound... DJS
-        self.path("fmod.dll")
-
-        self.enable_crt_check()
-
-        # For textures
-        if self.prefix(src=self.args['configuration'], dst=""):
-            if(self.args['configuration'].lower() == 'debug'):
-                self.path("openjpegd.dll")
-            else:
-                self.path("openjpeg.dll")
-            self.end_prefix()
-
-        # Media plugins - QuickTime
-        if self.prefix(src='../media_plugins/quicktime/%s' % self.args['configuration'], dst="llplugin"):
-            self.path("media_plugin_quicktime.dll")
-            self.end_prefix()
-
-        # Media plugins - WebKit/Qt
-        if self.prefix(src='../media_plugins/webkit/%s' % self.args['configuration'], dst="llplugin"):
-            self.path("media_plugin_webkit.dll")
-            self.end_prefix()
-            
-        if self.prefix(src="../../libraries/i686-win32/lib/release", dst="llplugin"):
-            self.path("libeay32.dll")
-            self.path("qtcore4.dll")
-            self.path("qtgui4.dll")
-            self.path("qtnetwork4.dll")
-            self.path("qtopengl4.dll")
-            self.path("qtwebkit4.dll")
-            self.path("ssleay32.dll")
-            self.end_prefix()
-
-        # For WebKit/Qt plugin runtimes (image format plugins)
-        if self.prefix(src="../../libraries/i686-win32/lib/release/imageformats", dst="llplugin/imageformats"):
-            self.path("qgif4.dll")
-            self.path("qico4.dll")
-            self.path("qjpeg4.dll")
-            self.path("qmng4.dll")
-            self.path("qsvg4.dll")
-            self.path("qtiff4.dll")
-            self.end_prefix()
-
-        self.disable_crt_check()
-
-        # These need to be installed as a SxS assembly, currently a 'private' assembly.
-        # See http://msdn.microsoft.com/en-us/library/ms235291(VS.80).aspx
-        if self.prefix(src=self.args['configuration'], dst=""):
-            if self.args['configuration'] == 'Debug':
-                self.path("msvcr80d.dll")
-                self.path("msvcp80d.dll")
-                self.path("Microsoft.VC80.DebugCRT.manifest")
-            else:
-                self.path("msvcr80.dll")
-                self.path("msvcp80.dll")
-                self.path("Microsoft.VC80.CRT.manifest")
-            self.end_prefix()
-
-        # The config file name needs to match the exe's name.
-        self.path(src="%s/secondlife-bin.exe.config" % self.args['configuration'], dst=self.final_exe() + ".config")
-
-        # Vivox runtimes
-        if self.prefix(src=self.args['configuration'], dst=""):
-            self.path("SLVoice.exe")
-            self.path("alut.dll")
-            self.path("vivoxsdk.dll")
-            self.path("ortp.dll")
-            self.path("wrap_oal.dll")
-            self.end_prefix()
-
-        # pull in the crash logger and updater from other projects
-        # tag:"crash-logger" here as a cue to the exporter
-        self.path(src='../win_crash_logger/%s/windows-crash-logger.exe' % self.args['configuration'],
-                  dst="win_crash_logger.exe")
-        self.path(src='../win_updater/%s/windows-updater.exe' % self.args['configuration'],
-                  dst="updater.exe")
-
-        # For google-perftools tcmalloc allocator.
-        try:
-            self.path('%s/libtcmalloc_minimal.dll' % self.args['configuration'])
-        except:
-            print "Skipping libtcmalloc_minimal.dll"
-            pass           
-
-    def nsi_file_commands(self, install=True):
-        def wpath(path):
-            if path.endswith('/') or path.endswith(os.path.sep):
-                path = path[:-1]
-            path = path.replace('/', '\\')
-            return path
-
-        result = ""
-        dest_files = [pair[1] for pair in self.file_list if pair[0] and os.path.isfile(pair[1])]
-        # sort deepest hierarchy first
-        dest_files.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b))
-        dest_files.reverse()
-        out_path = None
-        for pkg_file in dest_files:
-            rel_file = os.path.normpath(pkg_file.replace(self.get_dst_prefix()+os.path.sep,''))
-            installed_dir = wpath(os.path.join('$INSTDIR', os.path.dirname(rel_file)))
-            pkg_file = wpath(os.path.normpath(pkg_file))
-            if installed_dir != out_path:
-                if install:
-                    out_path = installed_dir
-                    result += 'SetOutPath ' + out_path + '\n'
-            if install:
-                result += 'File ' + pkg_file + '\n'
-            else:
-                result += 'Delete ' + wpath(os.path.join('$INSTDIR', rel_file)) + '\n'
-        # at the end of a delete, just rmdir all the directories
-        if not install:
-            deleted_file_dirs = [os.path.dirname(pair[1].replace(self.get_dst_prefix()+os.path.sep,'')) for pair in self.file_list]
-            # find all ancestors so that we don't skip any dirs that happened to have no non-dir children
-            deleted_dirs = []
-            for d in deleted_file_dirs:
-                deleted_dirs.extend(path_ancestors(d))
-            # sort deepest hierarchy first
-            deleted_dirs.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b))
-            deleted_dirs.reverse()
-            prev = None
-            for d in deleted_dirs:
-                if d != prev:   # skip duplicates
-                    result += 'RMDir ' + wpath(os.path.join('$INSTDIR', os.path.normpath(d))) + '\n'
-                prev = d
-
-        return result
-
-    def package_finish(self):
-        # a standard map of strings for replacing in the templates
-        substitution_strings = {
-            'version' : '.'.join(self.args['version']),
-            'version_short' : '.'.join(self.args['version'][:-1]),
-            'version_dashes' : '-'.join(self.args['version']),
-            'final_exe' : self.final_exe(),
-            'grid':self.args['grid'],
-            'grid_caps':self.args['grid'].upper(),
-            # escape quotes becase NSIS doesn't handle them well
-            'flags':self.flags_list().replace('"', '$\\"'),
-            'channel':self.channel(),
-            'channel_oneword':self.channel_oneword(),
-            'channel_unique':self.channel_unique(),
-            }
-
-        version_vars = """
-        !define INSTEXE  "%(final_exe)s"
-        !define VERSION "%(version_short)s"
-        !define VERSION_LONG "%(version)s"
-        !define VERSION_DASHES "%(version_dashes)s"
-        """ % substitution_strings
-        if self.default_channel():
-            if self.default_grid():
-                # release viewer
-                installer_file = "Second_Life_%(version_dashes)s_Setup.exe"
-                grid_vars_template = """
-                OutFile "%(installer_file)s"
-                !define INSTFLAGS "%(flags)s"
-                !define INSTNAME   "SecondLife"
-                !define SHORTCUT   "Second Life"
-                !define URLNAME   "secondlife"
-                Caption "Second Life ${VERSION}"
-                """
-            else:
-                # beta grid viewer
-                installer_file = "Second_Life_%(version_dashes)s_(%(grid_caps)s)_Setup.exe"
-                grid_vars_template = """
-                OutFile "%(installer_file)s"
-                !define INSTFLAGS "%(flags)s"
-                !define INSTNAME   "SecondLife%(grid_caps)s"
-                !define SHORTCUT   "Second Life (%(grid_caps)s)"
-                !define URLNAME   "secondlife%(grid)s"
-                !define UNINSTALL_SETTINGS 1
-                Caption "Second Life %(grid)s ${VERSION}"
-                """
-        else:
-            # some other channel on some grid
-            installer_file = "Second_Life_%(version_dashes)s_%(channel_oneword)s_Setup.exe"
-            grid_vars_template = """
-            OutFile "%(installer_file)s"
-            !define INSTFLAGS "%(flags)s"
-            !define INSTNAME   "SecondLife%(channel_oneword)s"
-            !define SHORTCUT   "%(channel)s"
-            !define URLNAME   "secondlife"
-            !define UNINSTALL_SETTINGS 1
-            Caption "%(channel)s ${VERSION}"
-            """
-        if 'installer_name' in self.args:
-            installer_file = self.args['installer_name']
-        else:
-            installer_file = installer_file % substitution_strings
-        substitution_strings['installer_file'] = installer_file
-
-        tempfile = "secondlife_setup_tmp.nsi"
-        # the following replaces strings in the nsi template
-        # it also does python-style % substitution
-        self.replace_in("installers/windows/installer_template.nsi", tempfile, {
-                "%%VERSION%%":version_vars,
-                "%%SOURCE%%":self.get_src_prefix(),
-                "%%GRID_VARS%%":grid_vars_template % substitution_strings,
-                "%%INSTALL_FILES%%":self.nsi_file_commands(True),
-                "%%DELETE_FILES%%":self.nsi_file_commands(False)})
-
-        # We use the Unicode version of NSIS, available from
-        # http://www.scratchpaper.com/
-        # Check two paths, one for Program Files, and one for Program Files (x86).
-        # Yay 64bit windows.
-        NSIS_path = os.path.expandvars('${ProgramFiles}\\NSIS\\Unicode\\makensis.exe')
-        if not os.path.exists(NSIS_path):
-            NSIS_path = os.path.expandvars('${ProgramFiles(x86)}\\NSIS\\Unicode\\makensis.exe')
-        self.run_command('"' + proper_windows_path(NSIS_path) + '" ' + self.dst_path_of(tempfile))
-        # self.remove(self.dst_path_of(tempfile))
-        # If we're on a build machine, sign the code using our Authenticode certificate. JC
-        sign_py = os.path.expandvars("${SIGN}")
-        if not sign_py or sign_py == "${SIGN}":
-            sign_py = 'C:\\buildscripts\\code-signing\\sign.py'
-        else:
-            sign_py = sign_py.replace('\\', '\\\\\\\\')
-        python = os.path.expandvars("${PYTHON}")
-        if not python or python == "${PYTHON}":
-            python = 'python'
-        if os.path.exists(sign_py):
-            self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of(installer_file).replace('\\', '\\\\\\\\')))
-        else:
-            print "Skipping code signing,", sign_py, "does not exist"
-        self.created_path(self.dst_path_of(installer_file))
-        self.package_file = installer_file
-
-
-class DarwinManifest(ViewerManifest):
-    def construct(self):
-        # copy over the build result (this is a no-op if run within the xcode script)
-        self.path(self.args['configuration'] + "/Second Life.app", dst="")
-
-        if self.prefix(src="", dst="Contents"):  # everything goes in Contents
-            self.path("Info-SecondLife.plist", dst="Info.plist")
-
-            # copy additional libs in <bundle>/Contents/MacOS/
-            self.path("../../libraries/universal-darwin/lib_release/libndofdev.dylib", dst="MacOS/libndofdev.dylib")
-            self.path(os.path.join(os.pardir, "llplugin", "slplugin", self.args['configuration'],
-                                   "SLPlugin"),
-                      os.path.join("MacOS", "SLPlugin"))
-
-            # most everything goes in the Resources directory
-            if self.prefix(src="", dst="Resources"):
-                super(DarwinManifest, self).construct()
-
-                if self.prefix("cursors_mac"):
-                    self.path("*.tif")
-                    self.end_prefix("cursors_mac")
-
-                self.path("licenses-mac.txt", dst="licenses.txt")
-                self.path("featuretable_mac.txt")
-                self.path("SecondLife.nib")
-
-                # If we are not using the default channel, use the 'Firstlook
-                # icon' to show that it isn't a stable release.
-                if self.default_channel() and self.default_grid():
-                    self.path("secondlife.icns")
-                else:
-                    self.path("secondlife_firstlook.icns", "secondlife.icns")
-                self.path("SecondLife.nib")
-                
-                # Translations
-                self.path("English.lproj")
-                self.path("German.lproj")
-                self.path("Japanese.lproj")
-                self.path("Korean.lproj")
-                self.path("da.lproj")
-                self.path("es.lproj")
-                self.path("fr.lproj")
-                self.path("hu.lproj")
-                self.path("it.lproj")
-                self.path("nl.lproj")
-                self.path("pl.lproj")
-                self.path("pt.lproj")
-                self.path("ru.lproj")
-                self.path("tr.lproj")
-                self.path("uk.lproj")
-                self.path("zh-Hans.lproj")
-
-                # SLVoice and vivox lols
-                self.path("vivox-runtime/universal-darwin/libalut.dylib", "libalut.dylib")
-                self.path("vivox-runtime/universal-darwin/libopenal.dylib", "libopenal.dylib")
-                self.path("vivox-runtime/universal-darwin/libortp.dylib", "libortp.dylib")
-                self.path("vivox-runtime/universal-darwin/libvivoxsdk.dylib", "libvivoxsdk.dylib")
-                self.path("vivox-runtime/universal-darwin/SLVoice", "SLVoice")
-
-                libdir = "../../libraries/universal-darwin/lib_release"
-                dylibs = {}
-
-                # need to get the kdu dll from any of the build directories as well
-                for lib in "llkdu", "llcommon":
-                    libfile = "lib%s.dylib" % lib
-                    try:
-                        self.path(self.find_existing_file(os.path.join(os.pardir,
-                                                                       lib,
-                                                                       self.args['configuration'],
-                                                                       libfile),
-                                                          os.path.join(libdir, libfile)),
-                                  dst=libfile)
-                    except RuntimeError:
-                        print "Skipping %s" % libfile
-                        dylibs[lib] = False
-                    else:
-                        dylibs[lib] = True
-
-                if dylibs["llcommon"]:
-                    for libfile in ("libapr-1.0.3.7.dylib",
-                                    "libaprutil-1.0.3.8.dylib",
-                                    "libexpat.0.5.0.dylib"):
-                        self.path(os.path.join(libdir, libfile), libfile)
-
-                #libfmodwrapper.dylib
-                self.path(self.args['configuration'] + "/libfmodwrapper.dylib", "libfmodwrapper.dylib")
-                
-                # our apps
-                self.path("../mac_crash_logger/" + self.args['configuration'] + "/mac-crash-logger.app", "mac-crash-logger.app")
-                self.path("../mac_updater/" + self.args['configuration'] + "/mac-updater.app", "mac-updater.app")
-
-                # plugins
-                if self.prefix(src="", dst="llplugin"):
-                    self.path("../media_plugins/quicktime/" + self.args['configuration'] + "/media_plugin_quicktime.dylib", "media_plugin_quicktime.dylib")
-                    self.path("../media_plugins/webkit/" + self.args['configuration'] + "/media_plugin_webkit.dylib", "media_plugin_webkit.dylib")
-                    self.path("../../libraries/universal-darwin/lib_release/libllqtwebkit.dylib", "libllqtwebkit.dylib")
-
-                    self.end_prefix("llplugin")
-
-                # command line arguments for connecting to the proper grid
-                self.put_in_file(self.flags_list(), 'arguments.txt')
-
-                self.end_prefix("Resources")
-
-            self.end_prefix("Contents")
-
-        # NOTE: the -S argument to strip causes it to keep enough info for
-        # annotated backtraces (i.e. function names in the crash log).  'strip' with no
-        # arguments yields a slightly smaller binary but makes crash logs mostly useless.
-        # This may be desirable for the final release.  Or not.
-        if ("package" in self.args['actions'] or 
-            "unpacked" in self.args['actions']):
-            self.run_command('strip -S "%(viewer_binary)s"' %
-                             { 'viewer_binary' : self.dst_path_of('Contents/MacOS/Second Life')})
-
-
-    def package_finish(self):
-        channel_standin = 'Second Life'  # hah, our default channel is not usable on its own
-        if not self.default_channel():
-            channel_standin = self.channel()
-
-        imagename="SecondLife_" + '_'.join(self.args['version'])
-
-        # MBW -- If the mounted volume name changes, it breaks the .DS_Store's background image and icon positioning.
-        #  If we really need differently named volumes, we'll need to create multiple DS_Store file images, or use some other trick.
-
-        volname="Second Life Installer"  # DO NOT CHANGE without understanding comment above
-
-        if self.default_channel():
-            if not self.default_grid():
-                # beta case
-                imagename = imagename + '_' + self.args['grid'].upper()
-        else:
-            # first look, etc
-            imagename = imagename + '_' + self.channel_oneword().upper()
-
-        sparsename = imagename + ".sparseimage"
-        finalname = imagename + ".dmg"
-        # make sure we don't have stale files laying about
-        self.remove(sparsename, finalname)
-
-        self.run_command('hdiutil create "%(sparse)s" -volname "%(vol)s" -fs HFS+ -type SPARSE -megabytes 400 -layout SPUD' % {
-                'sparse':sparsename,
-                'vol':volname})
-
-        # mount the image and get the name of the mount point and device node
-        hdi_output = self.run_command('hdiutil attach -private "' + sparsename + '"')
-        devfile = re.search("/dev/disk([0-9]+)[^s]", hdi_output).group(0).strip()
-        volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip()
-
-        # Copy everything in to the mounted .dmg
-
-        if self.default_channel() and not self.default_grid():
-            app_name = "Second Life " + self.args['grid']
-        else:
-            app_name = channel_standin.strip()
-
-        # Hack:
-        # Because there is no easy way to coerce the Finder into positioning
-        # the app bundle in the same place with different app names, we are
-        # adding multiple .DS_Store files to svn. There is one for release,
-        # one for release candidate and one for first look. Any other channels
-        # will use the release .DS_Store, and will look broken.
-        # - Ambroff 2008-08-20
-        dmg_template = os.path.join(
-            'installers', 
-            'darwin',
-            '%s-dmg' % "".join(self.channel_unique().split()).lower())
-
-        if not os.path.exists (self.src_path_of(dmg_template)):
-            dmg_template = os.path.join ('installers', 'darwin', 'release-dmg')
-
-        for s,d in {self.get_dst_prefix():app_name + ".app",
-                    os.path.join(dmg_template, "_VolumeIcon.icns"): ".VolumeIcon.icns",
-                    os.path.join(dmg_template, "background.jpg"): "background.jpg",
-                    os.path.join(dmg_template, "_DS_Store"): ".DS_Store"}.items():
-            print "Copying to dmg", s, d
-            self.copy_action(self.src_path_of(s), os.path.join(volpath, d))
-
-        # Hide the background image, DS_Store file, and volume icon file (set their "visible" bit)
-        self.run_command('SetFile -a V "' + os.path.join(volpath, ".VolumeIcon.icns") + '"')
-        self.run_command('SetFile -a V "' + os.path.join(volpath, "background.jpg") + '"')
-        self.run_command('SetFile -a V "' + os.path.join(volpath, ".DS_Store") + '"')
-
-        # Create the alias file (which is a resource file) from the .r
-        self.run_command('rez "' + self.src_path_of("installers/darwin/release-dmg/Applications-alias.r") + '" -o "' + os.path.join(volpath, "Applications") + '"')
-
-        # Set the alias file's alias and custom icon bits
-        self.run_command('SetFile -a AC "' + os.path.join(volpath, "Applications") + '"')
-
-        # Set the disk image root's custom icon bit
-        self.run_command('SetFile -a C "' + volpath + '"')
-
-        # Unmount the image
-        self.run_command('hdiutil detach -force "' + devfile + '"')
-
-        print "Converting temp disk image to final disk image"
-        self.run_command('hdiutil convert "%(sparse)s" -format UDZO -imagekey zlib-level=9 -o "%(final)s"' % {'sparse':sparsename, 'final':finalname})
-        # get rid of the temp file
-        self.package_file = finalname
-        self.remove(sparsename)
-
-class LinuxManifest(ViewerManifest):
-    def construct(self):
-        super(LinuxManifest, self).construct()
-        self.path("licenses-linux.txt","licenses.txt")
-        self.path("res/ll_icon.png","secondlife_icon.png")
-        if self.prefix("linux_tools", dst=""):
-            self.path("client-readme.txt","README-linux.txt")
-            self.path("client-readme-voice.txt","README-linux-voice.txt")
-            self.path("client-readme-joystick.txt","README-linux-joystick.txt")
-            self.path("wrapper.sh","secondlife")
-            self.path("handle_secondlifeprotocol.sh", "etc/handle_secondlifeprotocol.sh")
-            self.path("register_secondlifeprotocol.sh", "etc/register_secondlifeprotocol.sh")
-            self.path("refresh_desktop_app_entry.sh", "etc/refresh_desktop_app_entry.sh")
-            self.path("launch_url.sh","etc/launch_url.sh")
-            self.path("install.sh")
-            self.end_prefix("linux_tools")
-
-        # Create an appropriate gridargs.dat for this package, denoting required grid.
-        self.put_in_file(self.flags_list(), 'etc/gridargs.dat')
-
-
-    def package_finish(self):
-        if 'installer_name' in self.args:
-            installer_name = self.args['installer_name']
-        else:
-            installer_name_components = ['SecondLife_', self.args.get('arch')]
-            installer_name_components.extend(self.args['version'])
-            installer_name = "_".join(installer_name_components)
-            if self.default_channel():
-                if not self.default_grid():
-                    installer_name += '_' + self.args['grid'].upper()
-            else:
-                installer_name += '_' + self.channel_oneword().upper()
-
-        # Fix access permissions
-        self.run_command("""
-                find %(dst)s -type d | xargs --no-run-if-empty chmod 755;
-                find %(dst)s -type f -perm 0700 | xargs --no-run-if-empty chmod 0755;
-                find %(dst)s -type f -perm 0500 | xargs --no-run-if-empty chmod 0555;
-                find %(dst)s -type f -perm 0600 | xargs --no-run-if-empty chmod 0644;
-                find %(dst)s -type f -perm 0400 | xargs --no-run-if-empty chmod 0444;
-                true""" %  {'dst':self.get_dst_prefix() })
-        self.package_file = installer_name + '.tar.bz2'
-
-        # temporarily move directory tree so that it has the right
-        # name in the tarfile
-        self.run_command("mv %(dst)s %(inst)s" % {
-            'dst': self.get_dst_prefix(),
-            'inst': self.build_path_of(installer_name)})
-        try:
-            # --numeric-owner hides the username of the builder for
-            # security etc.
-            self.run_command('tar -C %(dir)s --numeric-owner -cjf '
-                             '%(inst_path)s.tar.bz2 %(inst_name)s' % {
-                'dir': self.get_build_prefix(),
-                'inst_name': installer_name,
-                'inst_path':self.build_path_of(installer_name)})
-        finally:
-            self.run_command("mv %(inst)s %(dst)s" % {
-                'dst': self.get_dst_prefix(),
-                'inst': self.build_path_of(installer_name)})
-
-class Linux_i686Manifest(LinuxManifest):
-    def construct(self):
-        super(Linux_i686Manifest, self).construct()
-
-        # install either the libllkdu we just built, or a prebuilt one, in
-        # decreasing order of preference.  for linux package, this goes to bin/
-        for lib, destdir in ("llkdu", "bin"), ("llcommon", "lib"):
-            libfile = "lib%s.so" % lib
-            try:
-                self.path(self.find_existing_file(os.path.join(os.pardir, lib, libfile),
-                    '../../libraries/i686-linux/lib_release_client/%s' % libfile), 
-                      dst=os.path.join(destdir, libfile))
-                # keep this one to preserve syntax, open source mangling removes previous lines
-                pass
-            except RuntimeError:
-                print "Skipping %s - not found" % libfile
-                pass
-
-        self.path("secondlife-stripped","bin/do-not-directly-run-secondlife-bin")
-        self.path("../linux_crash_logger/linux-crash-logger-stripped","bin/linux-crash-logger.bin")
-        self.path("../linux_updater/linux-updater-stripped", "bin/linux-updater.bin")
-        self.path("../llplugin/slplugin/SLPlugin", "bin/SLPlugin")
-        if self.prefix("res-sdl"):
-            self.path("*")
-            # recurse
-            self.end_prefix("res-sdl")
-
-        # plugins
-        if self.prefix(src="", dst="bin/llplugin"):
-            self.path("../media_plugins/webkit/libmedia_plugin_webkit.so", "libmedia_plugin_webkit.so")
-            self.path("../media_plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_quicktime.so")
-            self.end_prefix("bin/llplugin")
-
-        self.path("featuretable_linux.txt")
-        #self.path("secondlife-i686.supp")
-
-        if self.prefix("../../libraries/i686-linux/lib_release_client", dst="lib"):
-            #self.path("libkdu_v42R.so", "libkdu.so")
-            self.path("libfmod-3.75.so")
-            self.path("libapr-1.so.0")
-            self.path("libaprutil-1.so.0")
-            self.path("libdb-4.2.so")
-            self.path("libcrypto.so.0.9.7")
-            self.path("libexpat.so.1")
-            self.path("libssl.so.0.9.7")
-            self.path("libuuid.so", "libuuid.so.1")
-            self.path("libSDL-1.2.so.0")
-            self.path("libELFIO.so")
-            self.path("libopenjpeg.so.1.3.0", "libopenjpeg.so.1.3")
-            self.path("libalut.so")
-            self.path("libopenal.so", "libopenal.so.1")
-            self.end_prefix("lib")
-
-            # Vivox runtimes
-            if self.prefix(src="vivox-runtime/i686-linux", dst="bin"):
-                    self.path("SLVoice")
-                    self.end_prefix()
-            if self.prefix(src="vivox-runtime/i686-linux", dst="lib"):
-                    self.path("libortp.so")
-                    self.path("libvivoxsdk.so")
-                    self.end_prefix("lib")
-
-class Linux_x86_64Manifest(LinuxManifest):
-    def construct(self):
-        super(Linux_x86_64Manifest, self).construct()
-        self.path("secondlife-stripped","bin/do-not-directly-run-secondlife-bin")
-        self.path("../linux_crash_logger/linux-crash-logger-stripped","linux-crash-logger.bin")
-        if self.prefix("res-sdl"):
-            self.path("*")
-            # recurse
-            self.end_prefix("res-sdl")
-
-        self.path("featuretable_linux.txt")
-        self.path("secondlife-i686.supp")
-
-if __name__ == "__main__":
-    main()
+#!/usr/bin/python
+# @file viewer_manifest.py
+# @author Ryan Williams
+# @brief Description of all installer viewer files, and methods for packaging
+#        them into installers for all supported platforms.
+#
+# $LicenseInfo:firstyear=2006&license=viewergpl$
+# 
+# Copyright (c) 2006-2009, Linden Research, Inc.
+# 
+# Second Life Viewer Source Code
+# The source code in this file ("Source Code") is provided by Linden Lab
+# to you under the terms of the GNU General Public License, version 2.0
+# ("GPL"), unless you have obtained a separate licensing agreement
+# ("Other License"), formally executed by you and Linden Lab.  Terms of
+# the GPL can be found in doc/GPL-license.txt in this distribution, or
+# online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+# 
+# There are special exceptions to the terms and conditions of the GPL as
+# it is applied to this Source Code. View the full text of the exception
+# in the file doc/FLOSS-exception.txt in this software distribution, or
+# online at
+# http://secondlifegrid.net/programs/open_source/licensing/flossexception
+# 
+# By copying, modifying or distributing this software, you acknowledge
+# that you have read and understood your obligations described above,
+# and agree to abide by those obligations.
+# 
+# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+# COMPLETENESS OR PERFORMANCE.
+# $/LicenseInfo$
+import sys
+import os.path
+import re
+import tarfile
+viewer_dir = os.path.dirname(__file__)
+# add llmanifest library to our path so we don't have to muck with PYTHONPATH
+sys.path.append(os.path.join(viewer_dir, '../lib/python/indra/util'))
+from llmanifest import LLManifest, main, proper_windows_path, path_ancestors
+
+class ViewerManifest(LLManifest):
+    def construct(self):
+        super(ViewerManifest, self).construct()
+        self.exclude("*.svn*")
+        self.path(src="../../scripts/messages/message_template.msg", dst="app_settings/message_template.msg")
+        self.path(src="../../etc/message.xml", dst="app_settings/message.xml")
+
+        if self.prefix(src="app_settings"):
+            self.exclude("logcontrol.xml")
+            self.exclude("logcontrol-dev.xml")
+            self.path("*.pem")
+            self.path("*.ini")
+            self.path("*.xml")
+            self.path("*.db2")
+
+            # include the entire shaders directory recursively
+            self.path("shaders")
+            # ... and the entire windlight directory
+            self.path("windlight")
+            self.end_prefix("app_settings")
+
+        if self.prefix(src="character"):
+            self.path("*.llm")
+            self.path("*.xml")
+            self.path("*.tga")
+            self.end_prefix("character")
+
+        # Include our fonts
+        if self.prefix(src="fonts"):
+            self.path("*.ttf")
+            self.path("*.txt")
+            self.end_prefix("fonts")
+
+        # skins
+        if self.prefix(src="skins"):
+                self.path("paths.xml")
+                # include the entire textures directory recursively
+                if self.prefix(src="*/textures"):
+                        self.path("*/*.tga")
+                        self.path("*/*.j2c")
+                        self.path("*/*.jpg")
+                        self.path("*/*.png")
+                        self.path("*.tga")
+                        self.path("*.j2c")
+                        self.path("*.jpg")
+                        self.path("*.png")
+                        self.path("textures.xml")
+                        self.end_prefix("*/textures")
+                self.path("*/xui/*/*.xml")
+                self.path("*/xui/*/widgets/*.xml")
+                self.path("*/*.xml")
+                
+                # Local HTML files (e.g. loading screen)
+                if self.prefix(src="*/html"):
+                        self.path("*.png")
+                        self.path("*/*/*.html")
+                        self.path("*/*/*.gif")
+                        self.end_prefix("*/html")
+                self.end_prefix("skins")
+        
+        # Files in the newview/ directory
+        self.path("gpu_table.txt")
+
+    def login_channel(self):
+        """Channel reported for login and upgrade purposes ONLY;
+        used for A/B testing"""
+        # NOTE: Do not return the normal channel if login_channel
+        # is not specified, as some code may branch depending on
+        # whether or not this is present
+        return self.args.get('login_channel')
+
+    def grid(self):
+        return self.args['grid']
+    def channel(self):
+        return self.args['channel']
+    def channel_unique(self):
+        return self.channel().replace("Second Life", "").strip()
+    def channel_oneword(self):
+        return "".join(self.channel_unique().split())
+    def channel_lowerword(self):
+        return self.channel_oneword().lower()
+
+    def flags_list(self):
+        """ Convenience function that returns the command-line flags
+        for the grid"""
+
+        # Set command line flags relating to the target grid
+        grid_flags = ''
+        if not self.default_grid():
+            grid_flags = "--grid %(grid)s "\
+                         "--helperuri http://preview-%(grid)s.secondlife.com/helpers/" %\
+                           {'grid':self.grid()}
+
+        # set command line flags for channel
+        channel_flags = ''
+        if self.login_channel() and self.login_channel() != self.channel():
+            # Report a special channel during login, but use default
+            channel_flags = '--channel "%s"' % (self.login_channel())
+        elif not self.default_channel():
+            channel_flags = '--channel "%s"' % self.channel()
+
+        # Deal with settings 
+        setting_flags = ''
+        if not self.default_channel() or not self.default_grid():
+            if self.default_grid():
+                setting_flags = '--settings settings_%s.xml'\
+                                % self.channel_lowerword()
+            else:
+                setting_flags = '--settings settings_%s_%s.xml'\
+                                % (self.grid(), self.channel_lowerword())
+                                                
+        return " ".join((channel_flags, grid_flags, setting_flags)).strip()
+
+
+class WindowsManifest(ViewerManifest):
+    def final_exe(self):
+        if self.default_channel():
+            if self.default_grid():
+                return "SecondLife.exe"
+            else:
+                return "SecondLifePreview.exe"
+        else:
+            return ''.join(self.channel().split()) + '.exe'
+
+
+    def test_msvcrt_and_copy_action(self, src, dst):
+        # This can is used to test a dll manifest.
+        # It is used as a temporary override during the construct method
+        from test_win32_manifest import test_assembly_binding
+        if src and (os.path.exists(src) or os.path.islink(src)):
+            # ensure that destination path exists
+            self.cmakedirs(os.path.dirname(dst))
+            self.created_paths.append(dst)
+            if not os.path.isdir(src):
+                if(self.args['configuration'].lower() == 'debug'):
+                    test_assembly_binding(src, "Microsoft.VC80.DebugCRT", "8.0.50727.4053")
+                else:
+                    test_assembly_binding(src, "Microsoft.VC80.CRT", "8.0.50727.4053")
+                self.ccopy(src,dst)
+            else:
+                raise Exception("Directories are not supported by test_CRT_and_copy_action()")
+        else:
+            print "Doesn't exist:", src
+
+    def enable_crt_check(self):
+        WindowsManifest.copy_action = WindowsManifest.test_msvcrt_and_copy_action
+
+    def disable_crt_check(self):
+        del WindowsManifest.copy_action
+
+    def construct(self):
+        super(WindowsManifest, self).construct()
+        # Find secondlife-bin.exe in the 'configuration' dir, then rename it to the result of final_exe.
+        self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe())
+
+        self.enable_crt_check()
+
+        # Plugin host application
+        self.path(os.path.join(os.pardir,
+                               'llplugin', 'slplugin', self.args['configuration'], "slplugin.exe"),
+                  "slplugin.exe")
+        
+        # need to get the llcommon.dll from the build directory as well
+        if self.prefix(src=self.args['configuration'], dst=""):
+            try:
+                self.path('llcommon.dll')
+                self.path('libapr-1.dll')
+                self.path('libaprutil-1.dll')
+                self.path('libapriconv-1.dll')
+            except RuntimeError:
+                print "Skipping llcommon.dll (assuming llcommon was linked statically)"
+        self.end_prefix()
+
+        # need to get the kdu dll from the build directory as well
+        try:
+            self.path('%s/llkdu.dll' % self.args['configuration'], dst='llkdu.dll')
+        except RuntimeError:
+            print "Skipping llkdu.dll"
+
+        self.disable_crt_check()
+
+        self.path(src="licenses-win32.txt", dst="licenses.txt")
+        self.path("featuretable.txt")
+
+        # For use in crash reporting (generates minidumps)
+        self.path("dbghelp.dll")
+
+        # For using FMOD for sound... DJS
+        self.path("fmod.dll")
+
+        self.enable_crt_check()
+
+        # For textures
+        if self.prefix(src=self.args['configuration'], dst=""):
+            if(self.args['configuration'].lower() == 'debug'):
+                self.path("openjpegd.dll")
+            else:
+                self.path("openjpeg.dll")
+            self.end_prefix()
+
+        # Media plugins - QuickTime
+        if self.prefix(src='../media_plugins/quicktime/%s' % self.args['configuration'], dst="llplugin"):
+            self.path("media_plugin_quicktime.dll")
+            self.end_prefix()
+
+        # Media plugins - WebKit/Qt
+        if self.prefix(src='../media_plugins/webkit/%s' % self.args['configuration'], dst="llplugin"):
+            self.path("media_plugin_webkit.dll")
+            self.end_prefix()
+            
+        if self.prefix(src="../../libraries/i686-win32/lib/release", dst="llplugin"):
+            self.path("libeay32.dll")
+            self.path("qtcore4.dll")
+            self.path("qtgui4.dll")
+            self.path("qtnetwork4.dll")
+            self.path("qtopengl4.dll")
+            self.path("qtwebkit4.dll")
+            self.path("ssleay32.dll")
+            self.end_prefix()
+
+        # For WebKit/Qt plugin runtimes (image format plugins)
+        if self.prefix(src="../../libraries/i686-win32/lib/release/imageformats", dst="llplugin/imageformats"):
+            self.path("qgif4.dll")
+            self.path("qico4.dll")
+            self.path("qjpeg4.dll")
+            self.path("qmng4.dll")
+            self.path("qsvg4.dll")
+            self.path("qtiff4.dll")
+            self.end_prefix()
+
+        self.disable_crt_check()
+
+        # These need to be installed as a SxS assembly, currently a 'private' assembly.
+        # See http://msdn.microsoft.com/en-us/library/ms235291(VS.80).aspx
+        if self.prefix(src=self.args['configuration'], dst=""):
+            if self.args['configuration'] == 'Debug':
+                self.path("msvcr80d.dll")
+                self.path("msvcp80d.dll")
+                self.path("Microsoft.VC80.DebugCRT.manifest")
+            else:
+                self.path("msvcr80.dll")
+                self.path("msvcp80.dll")
+                self.path("Microsoft.VC80.CRT.manifest")
+            self.end_prefix()
+
+        # The config file name needs to match the exe's name.
+        self.path(src="%s/secondlife-bin.exe.config" % self.args['configuration'], dst=self.final_exe() + ".config")
+
+        # Vivox runtimes
+        if self.prefix(src=self.args['configuration'], dst=""):
+            self.path("SLVoice.exe")
+            self.path("alut.dll")
+            self.path("vivoxsdk.dll")
+            self.path("ortp.dll")
+            self.path("wrap_oal.dll")
+            self.end_prefix()
+
+        # pull in the crash logger and updater from other projects
+        # tag:"crash-logger" here as a cue to the exporter
+        self.path(src='../win_crash_logger/%s/windows-crash-logger.exe' % self.args['configuration'],
+                  dst="win_crash_logger.exe")
+        self.path(src='../win_updater/%s/windows-updater.exe' % self.args['configuration'],
+                  dst="updater.exe")
+
+        # For google-perftools tcmalloc allocator.
+        try:
+            self.path('%s/libtcmalloc_minimal.dll' % self.args['configuration'])
+        except:
+            print "Skipping libtcmalloc_minimal.dll"
+            pass           
+
+    def nsi_file_commands(self, install=True):
+        def wpath(path):
+            if path.endswith('/') or path.endswith(os.path.sep):
+                path = path[:-1]
+            path = path.replace('/', '\\')
+            return path
+
+        result = ""
+        dest_files = [pair[1] for pair in self.file_list if pair[0] and os.path.isfile(pair[1])]
+        # sort deepest hierarchy first
+        dest_files.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b))
+        dest_files.reverse()
+        out_path = None
+        for pkg_file in dest_files:
+            rel_file = os.path.normpath(pkg_file.replace(self.get_dst_prefix()+os.path.sep,''))
+            installed_dir = wpath(os.path.join('$INSTDIR', os.path.dirname(rel_file)))
+            pkg_file = wpath(os.path.normpath(pkg_file))
+            if installed_dir != out_path:
+                if install:
+                    out_path = installed_dir
+                    result += 'SetOutPath ' + out_path + '\n'
+            if install:
+                result += 'File ' + pkg_file + '\n'
+            else:
+                result += 'Delete ' + wpath(os.path.join('$INSTDIR', rel_file)) + '\n'
+        # at the end of a delete, just rmdir all the directories
+        if not install:
+            deleted_file_dirs = [os.path.dirname(pair[1].replace(self.get_dst_prefix()+os.path.sep,'')) for pair in self.file_list]
+            # find all ancestors so that we don't skip any dirs that happened to have no non-dir children
+            deleted_dirs = []
+            for d in deleted_file_dirs:
+                deleted_dirs.extend(path_ancestors(d))
+            # sort deepest hierarchy first
+            deleted_dirs.sort(lambda a,b: cmp(a.count(os.path.sep),b.count(os.path.sep)) or cmp(a,b))
+            deleted_dirs.reverse()
+            prev = None
+            for d in deleted_dirs:
+                if d != prev:   # skip duplicates
+                    result += 'RMDir ' + wpath(os.path.join('$INSTDIR', os.path.normpath(d))) + '\n'
+                prev = d
+
+        return result
+
+    def package_finish(self):
+        # a standard map of strings for replacing in the templates
+        substitution_strings = {
+            'version' : '.'.join(self.args['version']),
+            'version_short' : '.'.join(self.args['version'][:-1]),
+            'version_dashes' : '-'.join(self.args['version']),
+            'final_exe' : self.final_exe(),
+            'grid':self.args['grid'],
+            'grid_caps':self.args['grid'].upper(),
+            # escape quotes becase NSIS doesn't handle them well
+            'flags':self.flags_list().replace('"', '$\\"'),
+            'channel':self.channel(),
+            'channel_oneword':self.channel_oneword(),
+            'channel_unique':self.channel_unique(),
+            }
+
+        version_vars = """
+        !define INSTEXE  "%(final_exe)s"
+        !define VERSION "%(version_short)s"
+        !define VERSION_LONG "%(version)s"
+        !define VERSION_DASHES "%(version_dashes)s"
+        """ % substitution_strings
+        if self.default_channel():
+            if self.default_grid():
+                # release viewer
+                installer_file = "Second_Life_%(version_dashes)s_Setup.exe"
+                grid_vars_template = """
+                OutFile "%(installer_file)s"
+                !define INSTFLAGS "%(flags)s"
+                !define INSTNAME   "SecondLife"
+                !define SHORTCUT   "Second Life"
+                !define URLNAME   "secondlife"
+                Caption "Second Life ${VERSION}"
+                """
+            else:
+                # beta grid viewer
+                installer_file = "Second_Life_%(version_dashes)s_(%(grid_caps)s)_Setup.exe"
+                grid_vars_template = """
+                OutFile "%(installer_file)s"
+                !define INSTFLAGS "%(flags)s"
+                !define INSTNAME   "SecondLife%(grid_caps)s"
+                !define SHORTCUT   "Second Life (%(grid_caps)s)"
+                !define URLNAME   "secondlife%(grid)s"
+                !define UNINSTALL_SETTINGS 1
+                Caption "Second Life %(grid)s ${VERSION}"
+                """
+        else:
+            # some other channel on some grid
+            installer_file = "Second_Life_%(version_dashes)s_%(channel_oneword)s_Setup.exe"
+            grid_vars_template = """
+            OutFile "%(installer_file)s"
+            !define INSTFLAGS "%(flags)s"
+            !define INSTNAME   "SecondLife%(channel_oneword)s"
+            !define SHORTCUT   "%(channel)s"
+            !define URLNAME   "secondlife"
+            !define UNINSTALL_SETTINGS 1
+            Caption "%(channel)s ${VERSION}"
+            """
+        if 'installer_name' in self.args:
+            installer_file = self.args['installer_name']
+        else:
+            installer_file = installer_file % substitution_strings
+        substitution_strings['installer_file'] = installer_file
+
+        tempfile = "secondlife_setup_tmp.nsi"
+        # the following replaces strings in the nsi template
+        # it also does python-style % substitution
+        self.replace_in("installers/windows/installer_template.nsi", tempfile, {
+                "%%VERSION%%":version_vars,
+                "%%SOURCE%%":self.get_src_prefix(),
+                "%%GRID_VARS%%":grid_vars_template % substitution_strings,
+                "%%INSTALL_FILES%%":self.nsi_file_commands(True),
+                "%%DELETE_FILES%%":self.nsi_file_commands(False)})
+
+        # We use the Unicode version of NSIS, available from
+        # http://www.scratchpaper.com/
+        # Check two paths, one for Program Files, and one for Program Files (x86).
+        # Yay 64bit windows.
+        NSIS_path = os.path.expandvars('${ProgramFiles}\\NSIS\\Unicode\\makensis.exe')
+        if not os.path.exists(NSIS_path):
+            NSIS_path = os.path.expandvars('${ProgramFiles(x86)}\\NSIS\\Unicode\\makensis.exe')
+        self.run_command('"' + proper_windows_path(NSIS_path) + '" ' + self.dst_path_of(tempfile))
+        # self.remove(self.dst_path_of(tempfile))
+        # If we're on a build machine, sign the code using our Authenticode certificate. JC
+        sign_py = os.path.expandvars("${SIGN}")
+        if not sign_py or sign_py == "${SIGN}":
+            sign_py = 'C:\\buildscripts\\code-signing\\sign.py'
+        else:
+            sign_py = sign_py.replace('\\', '\\\\\\\\')
+        python = os.path.expandvars("${PYTHON}")
+        if not python or python == "${PYTHON}":
+            python = 'python'
+        if os.path.exists(sign_py):
+            self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of(installer_file).replace('\\', '\\\\\\\\')))
+        else:
+            print "Skipping code signing,", sign_py, "does not exist"
+        self.created_path(self.dst_path_of(installer_file))
+        self.package_file = installer_file
+
+
+class DarwinManifest(ViewerManifest):
+    def construct(self):
+        # copy over the build result (this is a no-op if run within the xcode script)
+        self.path(self.args['configuration'] + "/Second Life.app", dst="")
+
+        if self.prefix(src="", dst="Contents"):  # everything goes in Contents
+            self.path("Info-SecondLife.plist", dst="Info.plist")
+
+            # copy additional libs in <bundle>/Contents/MacOS/
+            self.path("../../libraries/universal-darwin/lib_release/libndofdev.dylib", dst="MacOS/libndofdev.dylib")
+
+            # most everything goes in the Resources directory
+            if self.prefix(src="", dst="Resources"):
+                super(DarwinManifest, self).construct()
+
+                if self.prefix("cursors_mac"):
+                    self.path("*.tif")
+                    self.end_prefix("cursors_mac")
+
+                self.path("licenses-mac.txt", dst="licenses.txt")
+                self.path("featuretable_mac.txt")
+                self.path("SecondLife.nib")
+
+                # If we are not using the default channel, use the 'Firstlook
+                # icon' to show that it isn't a stable release.
+                if self.default_channel() and self.default_grid():
+                    self.path("secondlife.icns")
+                else:
+                    self.path("secondlife_firstlook.icns", "secondlife.icns")
+                self.path("SecondLife.nib")
+                
+                # Translations
+                self.path("English.lproj")
+                self.path("German.lproj")
+                self.path("Japanese.lproj")
+                self.path("Korean.lproj")
+                self.path("da.lproj")
+                self.path("es.lproj")
+                self.path("fr.lproj")
+                self.path("hu.lproj")
+                self.path("it.lproj")
+                self.path("nl.lproj")
+                self.path("pl.lproj")
+                self.path("pt.lproj")
+                self.path("ru.lproj")
+                self.path("tr.lproj")
+                self.path("uk.lproj")
+                self.path("zh-Hans.lproj")
+
+                # SLVoice and vivox lols
+                self.path("vivox-runtime/universal-darwin/libalut.dylib", "libalut.dylib")
+                self.path("vivox-runtime/universal-darwin/libopenal.dylib", "libopenal.dylib")
+                self.path("vivox-runtime/universal-darwin/libortp.dylib", "libortp.dylib")
+                self.path("vivox-runtime/universal-darwin/libvivoxsdk.dylib", "libvivoxsdk.dylib")
+                self.path("vivox-runtime/universal-darwin/SLVoice", "SLVoice")
+
+                libdir = "../../libraries/universal-darwin/lib_release"
+                dylibs = {}
+
+                # need to get the kdu dll from any of the build directories as well
+                for lib in "llkdu", "llcommon":
+                    libfile = "lib%s.dylib" % lib
+                    try:
+                        self.path(self.find_existing_file(os.path.join(os.pardir,
+                                                                       lib,
+                                                                       self.args['configuration'],
+                                                                       libfile),
+                                                          os.path.join(libdir, libfile)),
+                                  dst=libfile)
+                    except RuntimeError:
+                        print "Skipping %s" % libfile
+                        dylibs[lib] = False
+                    else:
+                        dylibs[lib] = True
+
+                if dylibs["llcommon"]:
+                    for libfile in ("libapr-1.0.3.7.dylib",
+                                    "libaprutil-1.0.3.8.dylib",
+                                    "libexpat.0.5.0.dylib"):
+                        self.path(os.path.join(libdir, libfile), libfile)
+
+                #libfmodwrapper.dylib
+                self.path(self.args['configuration'] + "/libfmodwrapper.dylib", "libfmodwrapper.dylib")
+                
+                # our apps
+                self.path("../mac_crash_logger/" + self.args['configuration'] + "/mac-crash-logger.app", "mac-crash-logger.app")
+                self.path("../mac_updater/" + self.args['configuration'] + "/mac-updater.app", "mac-updater.app")
+
+                # plugin launcher
+                self.path("../llplugin/slplugin/" + self.args['configuration'] + "/SLPlugin", "SLPlugin")
+
+                # plugins
+                if self.prefix(src="", dst="llplugin"):
+                    self.path("../media_plugins/quicktime/" + self.args['configuration'] + "/media_plugin_quicktime.dylib", "media_plugin_quicktime.dylib")
+                    self.path("../media_plugins/webkit/" + self.args['configuration'] + "/media_plugin_webkit.dylib", "media_plugin_webkit.dylib")
+                    self.path("../../libraries/universal-darwin/lib_release/libllqtwebkit.dylib", "libllqtwebkit.dylib")
+
+                    self.end_prefix("llplugin")
+
+                # command line arguments for connecting to the proper grid
+                self.put_in_file(self.flags_list(), 'arguments.txt')
+
+                self.end_prefix("Resources")
+
+            self.end_prefix("Contents")
+
+        # NOTE: the -S argument to strip causes it to keep enough info for
+        # annotated backtraces (i.e. function names in the crash log).  'strip' with no
+        # arguments yields a slightly smaller binary but makes crash logs mostly useless.
+        # This may be desirable for the final release.  Or not.
+        if ("package" in self.args['actions'] or 
+            "unpacked" in self.args['actions']):
+            self.run_command('strip -S "%(viewer_binary)s"' %
+                             { 'viewer_binary' : self.dst_path_of('Contents/MacOS/Second Life')})
+
+
+    def package_finish(self):
+        channel_standin = 'Second Life'  # hah, our default channel is not usable on its own
+        if not self.default_channel():
+            channel_standin = self.channel()
+
+        imagename="SecondLife_" + '_'.join(self.args['version'])
+
+        # MBW -- If the mounted volume name changes, it breaks the .DS_Store's background image and icon positioning.
+        #  If we really need differently named volumes, we'll need to create multiple DS_Store file images, or use some other trick.
+
+        volname="Second Life Installer"  # DO NOT CHANGE without understanding comment above
+
+        if self.default_channel():
+            if not self.default_grid():
+                # beta case
+                imagename = imagename + '_' + self.args['grid'].upper()
+        else:
+            # first look, etc
+            imagename = imagename + '_' + self.channel_oneword().upper()
+
+        sparsename = imagename + ".sparseimage"
+        finalname = imagename + ".dmg"
+        # make sure we don't have stale files laying about
+        self.remove(sparsename, finalname)
+
+        self.run_command('hdiutil create "%(sparse)s" -volname "%(vol)s" -fs HFS+ -type SPARSE -megabytes 400 -layout SPUD' % {
+                'sparse':sparsename,
+                'vol':volname})
+
+        # mount the image and get the name of the mount point and device node
+        hdi_output = self.run_command('hdiutil attach -private "' + sparsename + '"')
+        devfile = re.search("/dev/disk([0-9]+)[^s]", hdi_output).group(0).strip()
+        volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip()
+
+        # Copy everything in to the mounted .dmg
+
+        if self.default_channel() and not self.default_grid():
+            app_name = "Second Life " + self.args['grid']
+        else:
+            app_name = channel_standin.strip()
+
+        # Hack:
+        # Because there is no easy way to coerce the Finder into positioning
+        # the app bundle in the same place with different app names, we are
+        # adding multiple .DS_Store files to svn. There is one for release,
+        # one for release candidate and one for first look. Any other channels
+        # will use the release .DS_Store, and will look broken.
+        # - Ambroff 2008-08-20
+        dmg_template = os.path.join(
+            'installers', 
+            'darwin',
+            '%s-dmg' % "".join(self.channel_unique().split()).lower())
+
+        if not os.path.exists (self.src_path_of(dmg_template)):
+            dmg_template = os.path.join ('installers', 'darwin', 'release-dmg')
+
+        for s,d in {self.get_dst_prefix():app_name + ".app",
+                    os.path.join(dmg_template, "_VolumeIcon.icns"): ".VolumeIcon.icns",
+                    os.path.join(dmg_template, "background.jpg"): "background.jpg",
+                    os.path.join(dmg_template, "_DS_Store"): ".DS_Store"}.items():
+            print "Copying to dmg", s, d
+            self.copy_action(self.src_path_of(s), os.path.join(volpath, d))
+
+        # Hide the background image, DS_Store file, and volume icon file (set their "visible" bit)
+        self.run_command('SetFile -a V "' + os.path.join(volpath, ".VolumeIcon.icns") + '"')
+        self.run_command('SetFile -a V "' + os.path.join(volpath, "background.jpg") + '"')
+        self.run_command('SetFile -a V "' + os.path.join(volpath, ".DS_Store") + '"')
+
+        # Create the alias file (which is a resource file) from the .r
+        self.run_command('rez "' + self.src_path_of("installers/darwin/release-dmg/Applications-alias.r") + '" -o "' + os.path.join(volpath, "Applications") + '"')
+
+        # Set the alias file's alias and custom icon bits
+        self.run_command('SetFile -a AC "' + os.path.join(volpath, "Applications") + '"')
+
+        # Set the disk image root's custom icon bit
+        self.run_command('SetFile -a C "' + volpath + '"')
+
+        # Unmount the image
+        self.run_command('hdiutil detach -force "' + devfile + '"')
+
+        print "Converting temp disk image to final disk image"
+        self.run_command('hdiutil convert "%(sparse)s" -format UDZO -imagekey zlib-level=9 -o "%(final)s"' % {'sparse':sparsename, 'final':finalname})
+        # get rid of the temp file
+        self.package_file = finalname
+        self.remove(sparsename)
+
+class LinuxManifest(ViewerManifest):
+    def construct(self):
+        super(LinuxManifest, self).construct()
+        self.path("licenses-linux.txt","licenses.txt")
+        self.path("res/ll_icon.png","secondlife_icon.png")
+        if self.prefix("linux_tools", dst=""):
+            self.path("client-readme.txt","README-linux.txt")
+            self.path("client-readme-voice.txt","README-linux-voice.txt")
+            self.path("client-readme-joystick.txt","README-linux-joystick.txt")
+            self.path("wrapper.sh","secondlife")
+            self.path("handle_secondlifeprotocol.sh", "etc/handle_secondlifeprotocol.sh")
+            self.path("register_secondlifeprotocol.sh", "etc/register_secondlifeprotocol.sh")
+            self.path("refresh_desktop_app_entry.sh", "etc/refresh_desktop_app_entry.sh")
+            self.path("launch_url.sh","etc/launch_url.sh")
+            self.path("install.sh")
+            self.end_prefix("linux_tools")
+
+        # Create an appropriate gridargs.dat for this package, denoting required grid.
+        self.put_in_file(self.flags_list(), 'etc/gridargs.dat')
+
+
+    def package_finish(self):
+        if 'installer_name' in self.args:
+            installer_name = self.args['installer_name']
+        else:
+            installer_name_components = ['SecondLife_', self.args.get('arch')]
+            installer_name_components.extend(self.args['version'])
+            installer_name = "_".join(installer_name_components)
+            if self.default_channel():
+                if not self.default_grid():
+                    installer_name += '_' + self.args['grid'].upper()
+            else:
+                installer_name += '_' + self.channel_oneword().upper()
+
+        # Fix access permissions
+        self.run_command("""
+                find %(dst)s -type d | xargs --no-run-if-empty chmod 755;
+                find %(dst)s -type f -perm 0700 | xargs --no-run-if-empty chmod 0755;
+                find %(dst)s -type f -perm 0500 | xargs --no-run-if-empty chmod 0555;
+                find %(dst)s -type f -perm 0600 | xargs --no-run-if-empty chmod 0644;
+                find %(dst)s -type f -perm 0400 | xargs --no-run-if-empty chmod 0444;
+                true""" %  {'dst':self.get_dst_prefix() })
+        self.package_file = installer_name + '.tar.bz2'
+
+        # temporarily move directory tree so that it has the right
+        # name in the tarfile
+        self.run_command("mv %(dst)s %(inst)s" % {
+            'dst': self.get_dst_prefix(),
+            'inst': self.build_path_of(installer_name)})
+        try:
+            # --numeric-owner hides the username of the builder for
+            # security etc.
+            self.run_command('tar -C %(dir)s --numeric-owner -cjf '
+                             '%(inst_path)s.tar.bz2 %(inst_name)s' % {
+                'dir': self.get_build_prefix(),
+                'inst_name': installer_name,
+                'inst_path':self.build_path_of(installer_name)})
+        finally:
+            self.run_command("mv %(inst)s %(dst)s" % {
+                'dst': self.get_dst_prefix(),
+                'inst': self.build_path_of(installer_name)})
+
+class Linux_i686Manifest(LinuxManifest):
+    def construct(self):
+        super(Linux_i686Manifest, self).construct()
+
+        # install either the libllkdu we just built, or a prebuilt one, in
+        # decreasing order of preference.  for linux package, this goes to bin/
+        for lib, destdir in ("llkdu", "bin"), ("llcommon", "lib"):
+            libfile = "lib%s.so" % lib
+            try:
+                self.path(self.find_existing_file(os.path.join(os.pardir, lib, libfile),
+                    '../../libraries/i686-linux/lib_release_client/%s' % libfile), 
+                      dst=os.path.join(destdir, libfile))
+                # keep this one to preserve syntax, open source mangling removes previous lines
+                pass
+            except RuntimeError:
+                print "Skipping %s - not found" % libfile
+                pass
+
+        self.path("secondlife-stripped","bin/do-not-directly-run-secondlife-bin")
+        self.path("../linux_crash_logger/linux-crash-logger-stripped","bin/linux-crash-logger.bin")
+        self.path("../linux_updater/linux-updater-stripped", "bin/linux-updater.bin")
+        self.path("../llplugin/slplugin/SLPlugin", "bin/SLPlugin")
+        if self.prefix("res-sdl"):
+            self.path("*")
+            # recurse
+            self.end_prefix("res-sdl")
+
+        # plugins
+        if self.prefix(src="", dst="bin/llplugin"):
+            self.path("../media_plugins/webkit/libmedia_plugin_webkit.so", "libmedia_plugin_webkit.so")
+            self.path("../media_plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_quicktime.so")
+            self.end_prefix("bin/llplugin")
+
+        self.path("featuretable_linux.txt")
+        #self.path("secondlife-i686.supp")
+
+        if self.prefix("../../libraries/i686-linux/lib_release_client", dst="lib"):
+            #self.path("libkdu_v42R.so", "libkdu.so")
+            self.path("libfmod-3.75.so")
+            self.path("libapr-1.so.0")
+            self.path("libaprutil-1.so.0")
+            self.path("libdb-4.2.so")
+            self.path("libcrypto.so.0.9.7")
+            self.path("libexpat.so.1")
+            self.path("libssl.so.0.9.7")
+            self.path("libuuid.so", "libuuid.so.1")
+            self.path("libSDL-1.2.so.0")
+            self.path("libELFIO.so")
+            self.path("libopenjpeg.so.1.3.0", "libopenjpeg.so.1.3")
+            self.path("libalut.so")
+            self.path("libopenal.so", "libopenal.so.1")
+            self.end_prefix("lib")
+
+            # Vivox runtimes
+            if self.prefix(src="vivox-runtime/i686-linux", dst="bin"):
+                    self.path("SLVoice")
+                    self.end_prefix()
+            if self.prefix(src="vivox-runtime/i686-linux", dst="lib"):
+                    self.path("libortp.so")
+                    self.path("libvivoxsdk.so")
+                    self.end_prefix("lib")
+
+class Linux_x86_64Manifest(LinuxManifest):
+    def construct(self):
+        super(Linux_x86_64Manifest, self).construct()
+        self.path("secondlife-stripped","bin/do-not-directly-run-secondlife-bin")
+        self.path("../linux_crash_logger/linux-crash-logger-stripped","linux-crash-logger.bin")
+        if self.prefix("res-sdl"):
+            self.path("*")
+            # recurse
+            self.end_prefix("res-sdl")
+
+        self.path("featuretable_linux.txt")
+        self.path("secondlife-i686.supp")
+
+if __name__ == "__main__":
+    main()