Finished coding (just a few small tidy-ups needed). Untested.