diff --git a/docs/rh-log.txt b/docs/rh-log.txt
index 11fd39fc4..19799c2a4 100644
--- a/docs/rh-log.txt
+++ b/docs/rh-log.txt
@@ -1,3 +1,12 @@
+October 4, 2006
+- Added alias parameter substitution. Instances of %x in the alias command
+  string will be replaced with parameter x when the alias is executed.
+- Fixed: When FString::LockBuffer() had to create a new string because the
+  RefCount was higher than one, the new string was not actually locked.
+- FConsoleAlias now stores the commands in FStrings.
+- BuildString() now returns an FString.
+- Added the muslib license text to muslib.h.
+
 October 4, 2006 (Changes by Graf Zahl)
 - When exiting the level on a damaging floor the player could be in the pain
   state and make its pain noise at the start of the next level.
diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp
index b8b901010..4058c1afa 100644
--- a/src/c_dispatch.cpp
+++ b/src/c_dispatch.cpp
@@ -846,53 +846,117 @@ void FConsoleCommand::Run (FCommandLine &argv, APlayerPawn *who, int key)
 
 FConsoleAlias::FConsoleAlias (const char *name, const char *command, bool noSave)
 	: FConsoleCommand (name, NULL),
-	  bRunning (false), bKill (false)
+	  bRunning(false), bKill(false)
 {
-	m_Command[noSave] = copystring (command);
-	m_Command[!noSave] = NULL;
+	m_Command[noSave] = command;
+	m_Command[!noSave] = FString();
+	// If the command contains % characters, assume they are parameter markers
+	// for substitution when the command is executed.
+	bDoSubstitution = (strchr (command, '%') != NULL);
 }
 
 FConsoleAlias::~FConsoleAlias ()
 {
-	for (int i = 0; i < 2; ++i)
-	{
-		if (m_Command[i] != NULL)
-		{
-			delete[] m_Command[i];
-			m_Command[i] = NULL;
-		}
-	}
+	m_Command[1] = m_Command[0] = FString();
 }
 
-char *BuildString (int argc, char **argv)
+FString BuildString (int argc, char **argv)
 {
-	char temp[1024];
-	char *cur;
-	int arg;
-
 	if (argc == 1)
 	{
-		return copystring (*argv);
+		return *argv;
 	}
 	else
 	{
-		cur = temp;
+		FString buf;
+		int arg;
+
 		for (arg = 0; arg < argc; arg++)
 		{
 			if (strchr (argv[arg], ' '))
 			{
-				cur += sprintf (cur, "\"%s\" ", argv[arg]);
+				buf.AppendFormat ("\"%s\" ", argv[arg]);
 			}
 			else
 			{
-				cur += sprintf (cur, "%s ", argv[arg]);
+				buf.AppendFormat ("%s ", argv[arg]);
 			}
 		}
-		temp[strlen (temp) - 1] = 0;
-		return copystring (temp);
+		return buf;
 	}
 }
 
+//===========================================================================
+//
+// SubstituteAliasParams
+//
+// Given an command line and a set of arguments, replace instances of
+// %x or %{x} in the command line with argument x. If argument x does not
+// exist, then the empty string is substituted in its place.
+//
+// Substitution is not done inside of quoted strings.
+//
+// To avoid a substitution, use %%. The %% will be replaced by a single %.
+//
+//===========================================================================
+
+FString SubstituteAliasParams (FString &command, FCommandLine &args)
+{
+	// Do substitution by replacing %x with the argument x.
+	// If there is no argument x, then %x is simply removed.
+
+	// For some reason, strtoul's stop parameter is non-const.
+	char *p = command.LockBuffer(), *start = p;
+	unsigned long argnum;
+	FString buf;
+
+	while (*p != '\0')
+	{
+		if (*p == '%' && ((p[1] >= '0' && p[1] <= '9') || p[1] == '{' || p[1] == '%'))
+		{
+			if (p[1] == '%')
+			{
+				// Do not substitute. Just collapse to a single %.
+				buf.AppendCStrPart (start, p - start + 1);
+				start = p = p + 2;
+				continue;
+			}
+
+			// Do a substitution. Output what came before this.
+			buf.AppendCStrPart (start, p - start);
+
+			// Extract the argument number and substitute the corresponding argument.
+			argnum = strtoul (p + 1 + (p[1] == '{'), &start, 10);
+			if ((p[1] != '{' || *start == '}') && argnum < args.argc())
+			{
+				buf += args[argnum];
+			}
+			p = (start += (p[1] == '{' && *start == '}'));
+		}
+		else if (*p == '"')
+		{
+			// Don't substitute inside quoted strings.
+			p++;
+			while (*p != '\0' && (*p != '"' || *(p-1) == '\\'))
+				p++;
+			if (*p != '\0')
+				p++;
+		}
+		else
+		{
+			p++;
+		}
+	}
+	// Return whatever was after the final substitution.
+	if (p > start)
+	{
+		buf.AppendCStrPart (start, p - start);
+	}
+	command.UnlockBuffer();
+
+	return buf;
+}
+
 static int DumpHash (FConsoleCommand **table, bool aliases, const char *pattern=NULL)
 {
 	int bucket, count;
@@ -939,7 +1003,7 @@ void FConsoleAlias::PrintAlias ()
 
 void FConsoleAlias::Archive (FConfigFile *f)
 {
-	if (f != NULL && m_Command[0] != NULL)
+	if (f != NULL && !m_Command[0].IsEmpty())
 	{
 		f->SetValueForKey ("Name", m_Name, true);
 		f->SetValueForKey ("Command", m_Command[0], true);
@@ -1066,7 +1130,7 @@ void C_ExecCmdLineParams ()
 	{
 		if (*Args.GetArg (currArg++) == '+')
 		{
-			char *cmdString;
+			FString cmdString;
 			int cmdlen = 1;
 			int argstart = currArg - 1;
 
@@ -1078,10 +1142,10 @@ void C_ExecCmdLineParams ()
 				cmdlen++;
 			}
 
-			if ( (cmdString = BuildString (cmdlen, Args.GetArgList (argstart))) )
+			cmdString = BuildString (cmdlen, Args.GetArgList (argstart));
+			if (!cmdString.IsEmpty())
 			{
-				C_DoCommand (cmdString + 1);
-				delete[] cmdString;
+				C_DoCommand (&cmdString[1]);
 			}
 		}
 	}
@@ -1105,19 +1169,29 @@ void FConsoleAlias::Run (FCommandLine &args, APlayerPawn *who, int key)
 		return;
 	}
 
-	int index = m_Command[1] != NULL;
-	char *mycommand = m_Command[index];
-	m_Command[index] = NULL;
-	bRunning = true;
-	AddCommandString (mycommand, key);
-	bRunning = false;
-	if (m_Command[index] != NULL)
-	{ // The alias realiased itself, so delete the memory used by this command.
-		delete[] mycommand;
+	int index = !m_Command[1].IsEmpty();
+	FString savedcommand = m_Command[index], mycommand;
+	m_Command[index] = FString();
+
+	if (bDoSubstitution)
+	{
+		mycommand = SubstituteAliasParams (savedcommand, args);
 	}
 	else
+	{
+		mycommand = savedcommand;
+	}
+
+	bRunning = true;
+	AddCommandString (mycommand.LockBuffer(), key);
+	mycommand.UnlockBuffer();
+	bRunning = false;
+	if (m_Command[index].IsEmpty())
 	{ // The alias is unchanged, so put the command back so it can be used again.
-		m_Command[index] = mycommand;
+	  // If the command had been non-empty, then that means that executing this
+	  // alias caused it to realias itself, so the old command will be forgotten
+	  // once this function returns.
+		m_Command[index] = savedcommand;
 	}
 	if (bKill)
 	{ // The alias wants to remove itself
@@ -1127,15 +1201,15 @@ void FConsoleAlias::Run (FCommandLine &args, APlayerPawn *who, int key)
 
 void FConsoleAlias::Realias (const char *command, bool noSave)
 {
-	if (m_Command[1] != NULL)
+	if (!noSave && !m_Command[1].IsEmpty())
 	{
 		noSave = true;
 	}
-	if (m_Command[noSave] != NULL)
-	{
-		delete[] m_Command[noSave];
-	}
-	m_Command[noSave] = copystring (command);
+	m_Command[noSave] = command;
+
+	// If the command contains % characters, assume they are parameter markers
+	// for substitution when the command is executed.
+	bDoSubstitution = (strchr (command, '%') != NULL);
 	bKill = false;
 }
 
diff --git a/src/c_dispatch.h b/src/c_dispatch.h
index e8720fb16..f05cfd718 100644
--- a/src/c_dispatch.h
+++ b/src/c_dispatch.h
@@ -58,7 +58,7 @@ void C_ArchiveAliases (FConfigFile *f);
 void C_SetAlias (const char *name, const char *cmd);
 
 // build a single string out of multiple strings
-char *BuildString (int argc, char **argv);
+FString BuildString (int argc, char **argv);
 
 // Class that can parse command lines
 class FCommandLine
@@ -121,7 +121,8 @@ public:
 	void Realias (const char *command, bool noSave);
 	void SafeDelete ();
 protected:
-	char *m_Command[2];
+	FString m_Command[2];	// Slot 0 is saved to the ini, slot 1 is not.
+	bool bDoSubstitution;
 	bool bRunning;
 	bool bKill;
 };
diff --git a/src/oplsynth/muslib.h b/src/oplsynth/muslib.h
index 6cc71def5..4a6d34e2f 100644
--- a/src/oplsynth/muslib.h
+++ b/src/oplsynth/muslib.h
@@ -8,6 +8,70 @@
  *
  */
 
+/* From muslib175.zip/README.1ST:
+
+1.1 - Disclaimer of Warranties
+------------------------------
+
+#ifdef LAWYER
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+#else
+
+Use this software at your own risk.
+
+#endif
+
+
+1.2 - Terms of Use
+------------------
+
+This library may be used in any freeware or shareware product free of
+charge. The product may not be sold for profit (except for shareware) and
+should be freely available to the public. It would be nice of you if you
+credited me in your product and notified me if you use this library.
+
+If you want to use this library in a commercial product, contact me
+and we will make an agreement. It is a violation of the law to make money
+of this product without prior signing an agreement and paying a license fee.
+This licence will allow its holder to sell any products based on MUSLib,
+royalty-free. There is no need to buy separate licences for different
+products once the licence fee is paid.
+
+
+1.3 - Contacting the Author
+---------------------------
+
+Internet (address valid probably until the end of year 1998):
+  xarnos00@dcse.fee.vutbr.cz
+
+FIDO:
+  2:423/36.2
+
+Snail-mail:
+
+  Vladimir Arnost
+  Ceska 921
+  Chrudim 4
+  537 01
+  CZECH REPUBLIC
+
+Voice-mail (Czech language only, not recommended; weekends only):
+
+  +42-455-2154
+*/
+
 #ifndef __MUSLIB_H_
 #define __MUSLIB_H_
 
diff --git a/src/zstring.cpp b/src/zstring.cpp
index a35443be2..13ec7a405 100644
--- a/src/zstring.cpp
+++ b/src/zstring.cpp
@@ -138,6 +138,7 @@ char *FString::LockBuffer()
 		AllocBuffer (old->Len);
 		StrCopy (Chars, old->Chars(), old->Len);
 		old->Release();
+		Data()->RefCount = -1;
 	}
 	return Chars;
 }