Index: applications/SLProxy/ScriptInject.cs
===================================================================
--- applications/SLProxy/ScriptInject.cs	(revision 0)
+++ applications/SLProxy/ScriptInject.cs	(revision 0)
@@ -0,0 +1,128 @@
+using SLProxy;
+using libsecondlife;
+using Nwc.XmlRpc;
+using libsecondlife.Packets;
+using System.Reflection;
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Net;
+using System.Text.RegularExpressions;
+
+public class ScriptInject : ProxyPlugin
+{
+    private ProxyFrame frame;
+    private Proxy proxy;
+    private Dictionary<ulong,Upload> xfers = new  Dictionary<ulong,Upload>();
+    private bool swapIt = false;
+    private byte[] newdata = null;
+
+    private class Upload {
+	uint nextpacket = 0; int length = 0; 
+	Dictionary<uint,int> packets = new Dictionary<uint,int>();
+	private byte[] newdata;
+	public Upload(ulong xferid, byte[] newdata) {
+		this.newdata = newdata;
+	}
+	
+	public Packet GotPacket(SendXferPacketPacket xfer) { lock(this) {
+		uint packetno = xfer.XferID.Packet & 0x7fffffff;
+		byte[] pdata = xfer.DataPacket.Data;
+		if(newdata == null) return xfer;
+		if(packetno > nextpacket) {
+			// Can't fix up this packet until we know the size of the previous ones.
+			Console.WriteLine("Got xfer packet "+packetno+" expecting "+nextpacket+"; dropping");
+			return null;
+		} else if(packetno == nextpacket) {
+			if(packetno == 0) {
+				length = (int)(pdata[0] + (pdata[1] << 8) + (pdata[2] << 16) + (pdata[3] << 24));
+				if(length != newdata.Length) {
+					Console.WriteLine("Can't replace "+length+" bytes with "+newdata.Length+" bytes");
+					packetno += 1; newdata = null; return xfer;
+				}
+				packets[0] = pdata.Length - 4;
+			} else {
+				packets[packetno] = packets[packetno - 1] + pdata.Length;
+			}
+			Console.WriteLine("Replacing xfer packet "+packetno);
+			nextpacket += 1;
+		} else {
+			Console.WriteLine("Got duplicate xfer packet "+packetno);
+		}
+
+		if(packetno == 0) {
+			Array.Copy(newdata, 0, pdata, 4, pdata.Length - 4);
+		} else {
+			Array.Copy(newdata,packets[packetno-1],pdata,0,pdata.Length);
+		}
+
+		return xfer;
+	}}
+    }
+
+    public ScriptInject(ProxyFrame frame)
+    {
+	this.frame = frame; this.proxy = frame.proxy;
+    }
+
+    public override void Init()
+    {
+	proxy.AddDelegate(PacketType.AssetUploadRequest, Direction.Outgoing, new PacketDelegate(OnAssetUploadRequest));
+	proxy.AddDelegate(PacketType.RequestXfer, Direction.Incoming, new PacketDelegate(OnRequestXfer));
+	proxy.AddDelegate(PacketType.SendXferPacket, Direction.Outgoing, new PacketDelegate(OnSendXferPacket));
+        frame.AddCommand("/lso",  new ProxyFrame.CommandDelegate(CmdLso));
+    }
+
+    // CmdLog: handle a /log command
+    private void CmdLso(string[] words)
+    {
+        if (words.Length != 2) {
+            frame.SayToUser("Usage: /lso <file name>");
+	    return;
+	}
+	FileStream fin = File.Open(words[1], FileMode.Open, FileAccess.Read);
+	byte[] data = new byte[fin.Length];
+	int count = 0;
+	while(count < data.Length) {
+		int ret = fin.Read(data,count,data.Length-count);
+		if(ret <= 0) {
+			frame.SayToUser("Read failed?");
+			fin.Close(); return;
+		}
+		count += ret;
+	}
+	fin.Close();
+	lock(this) { newdata = data; }
+	frame.SayToUser("Upload swap prepared!");
+    }
+
+
+    private Packet OnAssetUploadRequest(Packet packet, IPEndPoint endPoint) { lock(this) {
+	AssetUploadRequestPacket upload = (AssetUploadRequestPacket) packet;
+	Console.WriteLine("Asset transfer: "+upload.AssetBlock.TransactionID.ToStringHyphenated() + "-" + upload.AssetBlock.Type);
+	if(upload.AssetBlock.AssetData.Length == 0 && upload.AssetBlock.Type == 11 && newdata != null) {
+		swapIt = true;
+	}
+	return packet;
+    }}
+
+    private Packet OnRequestXfer(Packet packet, IPEndPoint endPoint) { lock(this) {
+	RequestXferPacket xfer = (RequestXferPacket)packet;
+	if(swapIt && xfer.XferID.FilePath == 0 && xfer.XferID.VFileType == 11) {
+		xfers.Add(xfer.XferID.ID, new Upload(xfer.XferID.ID,newdata));
+		newdata = null; swapIt = false;
+	}
+	return packet;
+    }}
+
+    private Packet OnSendXferPacket(Packet packet, IPEndPoint endPoint) { lock(this) {
+	SendXferPacketPacket xfer = (SendXferPacketPacket)packet;
+	if(xfers.ContainsKey(xfer.XferID.ID)) {
+		return xfers[xfer.XferID.ID].GotPacket(xfer);
+	}
+	return packet;
+    }}
+}
\ No newline at end of file
Index: applications/SLProxy/SLProxy.build
===================================================================
--- applications/SLProxy/SLProxy.build	(revision 803)
+++ applications/SLProxy/SLProxy.build	(working copy)
@@ -43,7 +43,7 @@
   </target>
 
   <!-- build tasks -->
-  <target name="build" depends="build-analyst build-chatconsole build-main"
+  <target name="build" depends="build-analyst build-chatconsole build-main build-dumpassetupload build-injectscript"
      description="build analyst and chatconsole">
   </target>
 
@@ -104,6 +104,44 @@
     </csc>
   </target>
 
+  <target name="build-dumpassetupload" depends="init build-slproxy"
+    description="Builds the binaries for the current configuration">
+    <echo message="Build Directory is ${bin_dir}/" />
+    <mkdir dir="${bin_dir}" failonerror="false" />
+    <csc
+      target="library"
+      debug="${build.debug}"
+      output="${bin_dir}/DumpAssetUpload.dll">
+      <sources failonempty="true">
+        <include name="DumpAssetUpload.cs" />
+        <!-- <include name="legacy/*.cs" /> -->
+      </sources>
+      <references basedir="${bin_dir}/">
+        <include name="libsecondlife.dll"/>
+        <include name="SLProxy.dll"/>
+      </references>
+    </csc>
+  </target>
+
+  <target name="build-injectscript" depends="init build-slproxy"
+    description="Builds the binaries for the current configuration">
+    <echo message="Build Directory is ${bin_dir}/" />
+    <mkdir dir="${bin_dir}" failonerror="false" />
+    <csc
+      target="library"
+      debug="${build.debug}"
+      output="${bin_dir}/ScriptInject.dll">
+      <sources failonempty="true">
+        <include name="ScriptInject.cs" />
+        <!-- <include name="legacy/*.cs" /> -->
+      </sources>
+      <references basedir="${bin_dir}/">
+        <include name="libsecondlife.dll"/>
+        <include name="SLProxy.dll"/>
+      </references>
+    </csc>
+  </target>
+
   <target name="build-chatconsole"
     depends="init build-slproxy"
     description="build SLProxy ChatConsole">
Index: applications/SLProxy/DumpAssetUpload.cs
===================================================================
--- applications/SLProxy/DumpAssetUpload.cs	(revision 0)
+++ applications/SLProxy/DumpAssetUpload.cs	(revision 0)
@@ -0,0 +1,106 @@
+using SLProxy;
+using libsecondlife;
+using Nwc.XmlRpc;
+using libsecondlife.Packets;
+using System.Reflection;
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Net;
+using System.Text.RegularExpressions;
+
+public class DumpAssetUpload : ProxyPlugin
+{
+    private ProxyFrame frame;
+    private Proxy proxy;
+    private Dictionary<ulong,Upload> xfers = new  Dictionary<ulong,Upload>();
+
+    private class Upload {
+	LLUUID trans; int type; uint nextpacket = 0; int length = 0; int got = 0; bool done = false;
+	FileStream wout;
+	Dictionary<uint,byte[]> packets;
+	public Upload(LLUUID transaction, int type, ulong xferid) {
+		this.trans = transaction; this.type = type;
+		wout = File.Create(trans.ToStringHyphenated() + "-" + type + ".bin");
+	}
+	
+	public Packet GotPacket(SendXferPacketPacket xfer) { lock(this) {
+		uint packetno = xfer.XferID.Packet & 0x7fffffff;
+		if(packetno > nextpacket) {
+			// FIXME - evil!
+			Console.WriteLine("Got xfer packet "+packetno+" expecting "+nextpacket+"; dropping");
+			return null;
+		} else if(packetno == nextpacket) {
+			Console.WriteLine("Got xfer packet "+packetno);
+			HandleNextPacket(xfer.DataPacket.Data, (xfer.XferID.Packet & 0x80000000U) != 0);
+		} else {
+			Console.WriteLine("Got duplicate xfer packet "+packetno);
+		}
+
+		return xfer;
+	}}
+
+	private void HandleNextPacket(byte[] data, bool islast) {
+		if(nextpacket == 0) {
+			length = (int)(data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24));
+			Console.WriteLine(trans.ToStringHyphenated() + "-" + type + ": length "+length);
+			wout.Write(data,4,data.Length-4); got += data.Length-4;
+		} else  {
+			wout.Write(data,0,data.Length); got += data.Length;
+		}
+		nextpacket += 1;
+		if(islast) {
+			wout.Close(); wout = null;
+			Console.WriteLine(trans.ToStringHyphenated() + "-" + type + ": done, "+got+" bytes transferred");
+			done = true;
+		}
+	}
+    }
+
+    public DumpAssetUpload(ProxyFrame frame)
+    {
+	this.frame = frame; this.proxy = frame.proxy;
+    }
+
+    public override void Init()
+    {
+	proxy.AddDelegate(PacketType.AssetUploadRequest, Direction.Outgoing, new PacketDelegate(OnAssetUploadRequest));
+	proxy.AddDelegate(PacketType.RequestXfer, Direction.Incoming, new PacketDelegate(OnRequestXfer));
+	proxy.AddDelegate(PacketType.SendXferPacket, Direction.Outgoing, new PacketDelegate(OnSendXferPacket));
+    }
+
+    private LLUUID trans; private int type;
+
+    private Packet OnAssetUploadRequest(Packet packet, IPEndPoint endPoint) {
+	AssetUploadRequestPacket upload = (AssetUploadRequestPacket) packet;
+	Console.WriteLine("Asset transfer: "+upload.AssetBlock.TransactionID.ToStringHyphenated() + "-" + upload.AssetBlock.Type);
+	if(upload.AssetBlock.AssetData.Length > 0) {
+		FileStream wout = File.Create(upload.AssetBlock.TransactionID.ToStringHyphenated() + "-" + upload.AssetBlock.Type + ".bin");
+		wout.Write(upload.AssetBlock.AssetData,0,upload.AssetBlock.AssetData.Length);
+		wout.Close();
+	} else {
+		trans = upload.AssetBlock.TransactionID;
+		type = upload.AssetBlock.Type;
+	}
+	return packet;
+    }
+
+    private Packet OnRequestXfer(Packet packet, IPEndPoint endPoint) {
+	RequestXferPacket xfer = (RequestXferPacket)packet;
+	if(trans != null && xfer.XferID.FilePath == 0 && xfer.XferID.VFileType == type) {
+		xfers.Add(xfer.XferID.ID, new Upload(trans,type,xfer.XferID.ID));
+	}
+	return packet;
+    }
+
+    private Packet OnSendXferPacket(Packet packet, IPEndPoint endPoint) {
+	SendXferPacketPacket xfer = (SendXferPacketPacket)packet;
+	if(xfers.ContainsKey(xfer.XferID.ID)) {
+		return xfers[xfer.XferID.ID].GotPacket(xfer);
+	}
+	return packet;
+    }
+}
\ No newline at end of file
