diff -ur ScummVM-cvs20030212/scummvm/scumm/gfx.cpp ScummVM-cvs20030212+hack/scummvm/scumm/gfx.cpp
--- ScummVM-cvs20030212/scummvm/scumm/gfx.cpp	2003-02-08 19:08:16.000000000 +0100
+++ ScummVM-cvs20030212+hack/scummvm/scumm/gfx.cpp	2003-02-13 22:13:27.000000000 +0100
@@ -19,6 +19,8 @@
  *
  */
 
+#include <math.h>
+
 #include "stdafx.h"
 #include "scumm.h"
 #include "actor.h"
@@ -2880,6 +2882,143 @@
 	}
 }
 
+void Scumm::desaturatePalette(int hueScale, int satScale, int valScale, int startColor, int endColor)
+{
+	// This is used by CMI when Guybrush walks from the fort towards the
+	// swamp. He should gradually fade to gray as he walks into the mist.
+
+	// FIXME: The result doesn't look quite like the original. There are
+	// several possible explanations for this. Maybe CMI isn't scaling
+	// the HSV components of the colour after all, or maybe it uses a
+	// simplified version of the algorithm.
+
+	// FIXME: Simplify, and eliminate the floating-point arithmetics?
+
+	if (startColor <= endColor) {
+		byte *cptr, *cur;
+		double R, G, B;
+		double H, S, V;
+		double Rp, Gp, Bp;
+		double min, max;
+		int j;
+
+		cptr = getPalettePtr() + startColor * 3;
+		cur = _currentPalette + startColor * 3;
+
+		for (j = startColor; j <= endColor; j++) {
+			R = ((double) *cptr++) / 255.0;
+			G = ((double) *cptr++) / 255.0;
+			B = ((double) *cptr++) / 255.0;
+
+			// Convert RGB to HSV
+
+			min = MIN(R, MIN(G, B));
+			max = MAX(R, MAX(G, B));
+
+			S = (max - min) / max;
+			V = max;
+
+			Rp = (max - R) / (max - min);
+			Gp = (max - G) / (max - min);
+			Bp = (max - B) / (max - min);
+
+			// The original algorithm says I should test if S is
+			// non-zero, but I don't dare to test for that kind of
+			// equality with floating-point numbers.
+
+			if (fabs(S) > 0.00001) {
+				if (R == max && G == min) {
+					H = 5 + Bp;
+				} else if (R == max && G != min) {
+					H = 1 - Gp;
+				} else if (G == max && B == min) {
+					H = Rp + 1;
+				} else if (G == max && B != min) {
+					H = 3 - Bp;
+				} else if (R == max) {
+					H = 3 + Gp;
+				} else {
+					H = 5 - Rp;
+				}
+			} else
+				H = 0.0; // undefined
+
+			H *= 60.0;
+
+			// Scale HSV
+
+			H = (H * hueScale) / 255.0;
+			S = (S * satScale) / 255.0;
+			V = (V * valScale) / 255.0;
+
+			// Convert HSV to RGB.
+
+			double Hex, primary_color, secondary_color;
+			double a, b, c;
+
+			Hex = H / 60.0;
+			primary_color = floor(Hex);
+			secondary_color = Hex - primary_color;
+
+			a = (1.0 - S) * V;
+			b = (1.0 - (S * secondary_color)) * V;
+			c = (1.0 - (S * (1.0 - secondary_color))) * V;
+
+			switch ((int) Hex) {
+				case 0:
+				case 6:
+					R = V;
+					G = c;
+					B = a;
+					break;
+				case 1:
+					R = b;
+					G = V;
+					B = a;
+					break;
+				case 2:
+					R = a;
+					G = V;
+					B = c;
+					break;
+				case 3:
+					R = a;
+					G = b;
+					B = V;
+					break;
+				case 4:
+					R = c;
+					G = a;
+					B = V;
+					break;
+				case 5:
+					R = V;
+					G = a;
+					B = b;
+					break;
+			}
+
+			int red, green, blue;
+
+			red = (int) (255.0 * R + 0.5);
+			green = (int) (255.0 * G + 0.5);
+			blue = (int) (255.0 * B + 0.5);
+
+			if (red > 255)
+				red = 255;
+			if (green > 255)
+				green = 255;
+			if (blue > 255)
+				blue = 255;
+
+			*cur++ = red;
+			*cur++ = green;
+			*cur++ = blue;
+		}
+		setDirtyColors(startColor, endColor);
+	}
+}
+
 int Scumm::remapPaletteColor(int r, int g, int b, uint threshold)
 {
 	int i;
diff -ur ScummVM-cvs20030212/scummvm/scumm/script_v8.cpp ScummVM-cvs20030212+hack/scummvm/scumm/script_v8.cpp
--- ScummVM-cvs20030212/scummvm/scumm/script_v8.cpp	2003-02-08 19:08:17.000000000 +0100
+++ ScummVM-cvs20030212+hack/scummvm/scumm/script_v8.cpp	2003-02-13 22:17:13.000000000 +0100
@@ -1018,12 +1018,16 @@
 		c = pop();
 		b = pop();
 		a = pop();
-		// FIXME - this probably has the same format as for darkenPalette:
-		// thre values for R, G, B and a start/end palette range to modify.
-		// Now, how on earth does on modify the saturation of a single color channel?
-		// Change the hue/saturation of a color, no problem, I know how to do that,
-		// but for only a channel alone, I don't even know what that should mean... :-/
+		// FIXME - this almost certainly uses the same format as for
+		// darkenPalette: three scaling values and a start/end palette
+		// range to modify.
+		//
+		// For now, I have assumed that we should scale H, S and V,
+		// but the result doesn't look quite right to me so either my
+		// theory isn't quite right, or my implementation isn't, or
+		// the original used a simplified version of the algorithm.
 //		warning("o8_roomOps: SO_ROOM_SATURATION(%d, %d, %d, %d, %d)", a, b, c, d, e);
+		desaturatePalette(a, b, c, d, e);
 		break;
 	default:
 		error("o8_roomOps: default case 0x%x", subOp);
diff -ur ScummVM-cvs20030212/scummvm/scumm/scumm.h ScummVM-cvs20030212+hack/scummvm/scumm/scumm.h
--- ScummVM-cvs20030212/scummvm/scumm/scumm.h	2003-02-08 19:08:17.000000000 +0100
+++ ScummVM-cvs20030212+hack/scummvm/scumm/scumm.h	2003-02-13 20:46:50.000000000 +0100
@@ -770,6 +770,7 @@
 	void setupShadowPalette(int slot, int redScale, int greenScale, int blueScale, int startColor, int endColor);
 	void setupShadowPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor);
 	void darkenPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor);
+	void desaturatePalette(int hueScale, int satScale, int valScale, int startColor, int endColor);
 
 	void setCursor(int cursor);
 	void setCursorImg(uint img, uint room, uint imgindex);
