Hi!
I'm trying to decompress FAR V.3 archives from The Sims Online. The Uncompress() function in the SimPE source-tree seems to do the trick, except extracted textures/images gets alot of artifacts.
This is a problem. It indicates the algorithm is slightly different than the one used in The Sims 2, which is also something I've gotten confirmed by talking to another developer on another forum (Peter Gould).
He's currently the only guy who's managed to write a program to properly decompress these archives, but it's written in RealBASIC and is for Mac.
I was wondering if any C# developers here could take a gander at my code and see if there are any obvious mistakes?
It currently manages to decompress some files, but seems to stop midway through the archive I'm testing it on, and the files extracted are missing alot of color (though the artifacts seem to be gone).
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Windows.Forms;
namespace FarExtractor
{
class Decompresser
{
public static byte[] Decompress(BinaryReader Reader)
{
/*byte[] HeaderBytes = new byte[9];
HeaderBytes = Reader.ReadBytes(9);
string Hex = HexConverter.ByteArrayToHexString(HeaderBytes, " ");
if (!Hex.Contains("10 FB")) //The data wasn't QFS compressed!
return null;
int CompressedSize = HeaderBytes[0] + (256 * HeaderBytes[1]) + (256 * 256 * HeaderBytes[2]);
int DecompressedSize = HeaderBytes[8] + (256 * HeaderBytes[7]) + (256 * 267 * HeaderBytes[6]);*/
int CompressedSize = Reader.ReadInt32();
byte[] CompressionID = Reader.ReadBytes(2);
byte[] Dummy = Reader.ReadBytes(3);
uint DecompressedSize = (uint)((Dummy[0] << 0x10) | (Dummy[1] << 0x08) | + Dummy[2]);
string Hex = HexConverter.ByteArrayToHexString(CompressionID, " ");
if (!Hex.Contains("10 FB")) //The data wasn't QFS compressed!
return null;
int K = 0;
int X = 0, Y = 0, Z = 0, I = 0, Q = 0, PP = 0, CCC = 0, OO = 0;
int Offset;
string DecompressedString = "";
int Counter = 0;
while ((Counter < CompressedSize) && (K < DecompressedSize))
{
X = Reader.ReadByte();
Counter++;
if (X < 128) //2 byte control 0x00 - 0x7F
{
//0oocccpp
Y = Reader.ReadByte();
PP = X % 4;
CCC = ((X / 4) % 8) + 3;
OO = 256 * (X / 32) + Y;
}
else if (X < 192) //3 byte control
{
// 10cc.cccc ppoo.oooo oooo.oooo
Y = Reader.ReadByte();
Z = Reader.ReadByte();
PP = Y / 64;
CCC = (X % 64) + 4;
OO = 256 * (Y % 64) + Z;
}
else if (X < 224) //4 byte control
{
// 110cccpp oooooooo oooooooo cccccccc // SimCity4
// 110occpp oooooooo oooooooo cccccccc // TS2
Y = Reader.ReadByte();
Z = Reader.ReadByte();
I = Reader.ReadByte();
PP = X % 4;
CCC = 256 * ((X / 4) % 4) + I + 5;
Q = X & 16;
if (Q == 0)
OO = 256 * Y + Z;
else
OO = 256 * (256 + Y) + Z;
}
else //1 byte
{
K = (X - 223) * 4;
DecompressedString += Encoding.ASCII.GetString(Reader.ReadBytes(K));
}
if (X < 224)
{
DecompressedString += Encoding.ASCII.GetString(Reader.ReadBytes(PP));
K = DecompressedString.Length;
Offset = K - OO - 1;
if (DecompressedSize > K)
{
MemoryStream TempStream = new MemoryStream();
BinaryWriter TempWriter = new BinaryWriter(TempStream);
TempWriter.Write(Encoding.ASCII.GetBytes(DecompressedString));
string Tmp = "";
if ((CCC > OO) && (K < DecompressedSize))
{
while (CCC > 0)
{
Offset = K - OO - 1;
if (CCC > OO)
{
Tmp += Encoding.ASCII.GetString(
TempStream.ToArray()).Substring(Offset, OO + 1);
}
else
{
Tmp += Encoding.ASCII.GetString(
TempStream.ToArray()).Substring(Offset, CCC);
}
CCC = CCC - OO - 1;
}
}
else
{
Tmp += Encoding.ASCII.GetString(
TempStream.ToArray()).Substring(Offset, CCC);
}
DecompressedString += Tmp;
}
}
K = DecompressedString.Length;
}
return Encoding.ASCII.GetBytes(DecompressedString);
}
}
}
Please don't tell me the code is ugly. I am completely aware that it is. I don't really understand the algorithm, so I've been trying to convert it from the RealBASIC code as best I could.
Here's the original RealBASIC code (copyright Peter Gould);
// DataWindow.DecompressInLine
// 07 Apr 05
// parameters: binaryInput as binaryStream, init as integer
// return type: string
Dim hbyte(9) as integer
Dim i, j, k, q, x, y, z as integer
Dim ccc, pp, oo, offset, trap1 as integer
Dim etype, header, longname, shortname, temp1, temp2 As String
dim output as string
dim mb as memoryBlock
// start reading at init
binaryInput.position = init
for i = 1 to 9
hbyte(i) = binaryInput.ReadByte
header = header + " " + hex(hbyte(i))
next
if InStr(header,"10 FB") = 0 then
output = ""
return output
else
headerfileSize = hbyte(1)+(256*hbyte(2))+(256*256*hbyte(3))
headerOutputSize = hbyte(9)+(256*hbyte(8))+(256*256*hbyte(7))
end if
mb = NewMemoryBlock(headerOutputSize + 1)
while binaryInput.position < init + headerfileSize and k < headerOutputSize
x = binaryInput.ReadByte
if x < 128 then
// 2 byte control 0x00 - 0x7F
// 0oocccpp
y = binaryInput.ReadByte
pp = x mod 4
ccc = ((x \ 4) mod 8) + 3
oo = 256*(x \ 32) + y
elseif x < 192 then
// 3 byte control
// 10cc.cccc ppoo.oooo oooo.oooo
y = binaryInput.ReadByte
z = binaryInput.ReadByte
pp = y \ 64
ccc = (x mod 64) + 4
oo = 256*(y mod 64) + z
elseif x < 224 then
// 4 byte control
// 110cccpp oooooooo oooooooo cccccccc // SimCity4
// 110occpp oooooooo oooooooo cccccccc // TS2
y = binaryInput.ReadByte
z = binaryInput.ReadByte
i = binaryInput.ReadByte
pp = x mod 4
ccc = 256*((x \ 4) mod 4) + i + 5
q = BitwiseAnd(x,16)
if q = 0 then
oo = 256*y + z
else
oo = 256*(256 + y) + z
end if
else
// 1 byte
k = (x - 223) * 4
temp1 = binaryInput.Read(k)
output = output + temp1
end if
if x < 224 then
temp1 = binaryInput.Read(pp)
output = output + temp1
k = len(output)
offset = k - oo - 1
if mb.Size > k then
mb.CString(0) = output
if ccc > oo and k < headerOutputSize then
temp2 = ""
while ccc > 0
offset = k - oo - 1
if ccc > oo then
temp2 = temp2 + mb.StringValue(offset,oo + 1)
else
temp2 = temp2 + mb.StringValue(offset,ccc)
end if
ccc = ccc - oo - 1
wend
else
temp2 = mb.StringValue(offset,ccc)
end if
output = output + temp2
end if
end if
k = len(output)
wend
if k <> headerOutputSize then
output = LeftB(output, headerOutputSize)
end if
return output
Exception err as OutOfBoundsException
MsgBox "DecompressInLine() went out of bounds!"