Member Controls

English Discussions > Refpack/QFS compression - HELP
Afr0Link to postposted: Thu Dec 03, 2009 10:02 pm

Member since:
 2007-08-07
Posts:
 3

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!"

 

GhostLink to postposted: Sat Dec 05, 2009 10:54 pm
Avator for Ghost

Member since:
 2006-03-19
Posts:
 775
Considering that almost everyone here has moved on to Sims 3, you might do better asking at Modthesims. I have no idea what you are even talking about. Laughing


viewpost, 30918, 0