Page 1 of 1

Settlers 2:Next Generation (Finished)

Posted: Wed Oct 18, 2006 10:10 pm
by Rheini
I could need some help with the files of The Settlers 2:Next Generation. It doesn't seem to be archives, so I don't know if this is the right forum.
The devs seem to use compression/encryption on each file.
I've attached 2 of them. One must be a normal texture, the other one the credits text. The files always start with this ...rc00 header, but the rest is different.
Can somebody help me? Is this any known compression/encryption algorithm or is it own code?
A german demo is available here:
http://www.giga.de/tv/gigagamespc/00132 ... er_2_demo/

EDIT: Get some decryption code below.

Posted: Thu Oct 19, 2006 10:32 am
by Rheini
The international version of this game seems to be called Settlers 2: 10th Anniversary.
A demo can be found here: http://www.3dgamers.com/games/settlers2ng/downloads/

same

Posted: Mon Dec 18, 2006 1:04 am
by takysoft
I have the same problem. Same game.
Tried to localize the game to hungarian(translate), but the files are encrypted.

If anyone has a decrypter, please email me.

Posted: Thu Jan 18, 2007 9:09 pm
by Espio
The contents of this post was deleted because of possible forum rules violation.

Posted: Thu Jan 18, 2007 9:55 pm
by Rheini
Where did you get that from?

Posted: Fri Jan 19, 2007 11:28 am
by Espio
2Rheini: Found it half month ago on some community board.

Posted: Fri Jan 19, 2007 12:42 pm
by Rheini
cool. do you still know which board this was?
I managed to write a working decryptor on my own in the meantime but if there already is an encryptor that's of course even better. :D

Posted: Sat Jan 20, 2007 3:11 am
by Deniz Oezmen
You did finally break the last byte? Good work!

Care to explain to the rest of the world how the system works? ;-)

Posted: Thu Feb 01, 2007 10:54 pm
by john_doe
@Rheini:

I'd also be interested in how the encryption works. I toyed around with the two command line tools but when I read you had a working decryptor I stopped.

Posted: Mon Jun 11, 2007 1:23 am
by Rheini
Here is a code snippet with the most important parts of my quick n dirty tool written in D. Please PM me if you make any optimizations.
Comments and variable names are in German (Learn it or die tryin' ;))

The entry point for code analysis is the Entpacken function at the end.

Code: Select all

	private ubyte[] puffer; /// Hauptpuffer für die Dateidaten
	private ubyte[16] basis; /// der 16-Byte Basiswert für pwcrc

	/// Die Kopfstruktur einer Siedler2-Datei
	struct Kopf {
		uint uk; /// seems to belong to the header 
		uint fcc; /// header 
		uint datacrc; /// crc32 checksum of decompressed data 
		uint pwcrc; /// tricky checksum
		uint datasize; /// decompressed data size
	}

	/**
	 * Ver- oder Entschlüsseln
	 * Params:
	 * 		puffer = Ein- und Ausgabepuffer
	 */
	void Schlüsseln(ubyte[] puffer)
	{
		uint x = kopf.pwcrc; // Startwert
		BerechneStartwert(x);
		Verschlüsselungsfunktion(x); // einmal initial durchlaufen

		uint xorgröße = (x & 0x7F) + 0x80; // das ist die Größe des XOR-Schlüssels

		byte[256] xorpuffer;

		for(int i=0; i<xorgröße; i++)
		{
			Verschlüsselungsfunktion(x);
			xorpuffer[i] = x & 0xFF;
		}

 		for(int i=0; i<puffer.length; i++)
 		{
 			puffer[i] = puffer[i] ^ xorpuffer[i%xorgröße];
 		}

// -----hier beginnt zweite Stufe----
		Verschlüsselungsfunktion(x);
		xorgröße = ((x & 0x1F) | 0x10) + 1;

		for(uint i=0; i<xorgröße; i++)
		{
			Verschlüsselungsfunktion(x);
			xorpuffer[i] = x & 0xFF;
		}
		Verschlüsselungsfunktion(x);
		uint offset = x % puffer.length; // Offset des falschen Bytes im Datenpuffer
		Verschlüsselungsfunktion(x);

		uint abstand = (x & 0x3FFF) | 0x2000;
		for(uint i=offset; i<puffer.length; i+=abstand)
		{
			puffer[i] ^= xorpuffer[(basis[i % 16] ^ i) % xorgröße];
		}
	}

	/// den 16er Basiswert intialisieren
	void InitialisiereBasiswert()
	{
		this.basis[] = cast(ubyte[]) [0xC9, 0x59, 0x46, 0xCA, 0xD9, 0xF0, 0x4F, 0x0A, 0xA1, 0x00, 0xAA, 0xB8, 0xCB, 0xE8, 0xDB, 0x6B];
	}

	/**
	 * berechnet den pwcrc-Wert
	 *
	 * muss vor Schlüsseln() aufgerufen werden
	 * übergeben wird der Dateiname
	 */
	uint BerechnePwCRC(string dateiname)
	{
		uint x = crc32(cast(ubyte[])dateiname); // Startwert für die x-Variable
		BerechneStartwert(x);

		for (uint i=0; i<16; i++)
		{
			Verschlüsselungsfunktion(x);
			 basis[i] ^= x & 0xFF;
		}

		return crc32(basis);
	}

	/// Berechnet den Startwert der x-Variable
	void BerechneStartwert(inout uint x)
	{
		uint[32] puffer = [12u, 0x17, 0x0A, 0x19, 0x08, 0x1B, 0x06, 0x1D, 0x04, 0x1E, 0x01, 0x16, 0x09, 0x0D, 0x15, 0x00,
						0x11, 0x1A, 0x05, 0x0F, 0x12, 0x1C, 0x0B, 0x02, 0x0E, 0x03, 0x18, 0x07, 0x13, 0x10, 0x14, 0x1F];

		uint ecx=0;
		uint edi=0;
		uint edx;

		x &= 0x7FFFFFFF;
		do 
		{
			edx = 1;
			edx <<= ecx & 0xFF;
			if (x & edx) // != 0
				edi++;
			ecx++;
		} while(ecx < 0x1F);

		if (edi < 8)
		{
			uint esi = 8;
			esi -= edi;
			edx =0;
			if (esi != 0)
			{
				uint ebp; // ebp ist nur lokal hier, ist richtig so
				do 
				{
					ecx = puffer[edx];
					ebp = 1 << (ecx & 0xFF);
					x |= ebp;
					edx++;
				} while(edx < esi);
			}
		}
		if (edi > 24)
		{
			uint esi = 32;
			esi -= edi;
			edx = 0;
			if (esi != 0)
			{
				do 
				{
					ecx = puffer[edx];
					edi = 1 << (ecx & 0xFF);
					edi ^= 0xFFFFFFFF;
					x &= edi;
					edx++;


				} while(edx < esi);
			}
		}
		if (x == 0)
			x = 1;
		else
			x &= 0x7FFFFFFF;
	}

	/// Hilfsfunktion zu Schlüsseln()
	void Verschlüsselungsfunktion(inout uint x)
	{
		uint oben  = (x >> 16) * 0x41A7; // 41A7=16807
		uint unten = (x & 0x0FFFF) * 0x41A7;
		x = ((oben & 0x7FFF) << 16) + unten;
		if (x > 0x7FFFFFFF)
			x = (x & 0x7FFFFFFF) + 1;
		x += oben >> 15;
		if (x > 0x7FFFFFFF)
			x = (x & 0x7FFFFFFF) + 1;
	}

	///
	int Entpacken(string dateiname)
	{
			if (Lesen(dateiname))
				return 1;

			// Dateinamen herausfiltern
			uint index = std.string.rfind(dateiname, cast(dchar) '\\');
			if (index != -1)
			{
				dateiname = dateiname[index+1 .. dateiname.length];
			}

			InitialisiereBasiswert();

			if (dateiname[dateiname.length-3 .. dateiname.length] != "s2m")
			{
				dateiname = std.string.tolower(dateiname);
				if (BerechnePwCRC(dateiname) != kopf.pwcrc)
				{
					writef("Prüfsumme stimmt nicht überein!\n");
					return 2;
				}
			}

			Schlüsseln(puffer); //decrypt
			ubyte[] AusPuf;
			AusPuf.length = kopf.datasize;
			Dekomprimieren(puffer, AusPuf); // LZSS

			if (crc32(AusPuf) != kopf.datacrc)
			{
				writef("crc fehlgeschlagen!\n");
			}
			// Entpackte Daten in Datei schreiben
			std.file.write(dateiname, AusPuf);

		return 0;
	}

Re: Settlers 2:Next Generation (Finished)

Posted: Mon Apr 28, 2008 3:00 am
by Rheini
Attached the S2Tools posted above.