public static SharedObject Parse(string filename, SharedObject so = null)
    {
        if (so == null)
        {
            so = new SharedObject();
        }
        if (!File.Exists(filename))
        {
            Console.WriteLine("SharedObject " + filename + " doesn't exist.");
            return(so);
        }
        SOReader      file         = new SOReader(filename);
        List <string> string_table = new List <string>();

        //Read header
        SOHeader header = new SOHeader();

        header.padding1  = file.Read16();
        header.file_size = file.Read32();
        file.file_size   = (int)header.file_size + 6;
        header.padding2  = file.Read32();
        header.padding3  = file.Read16();
        header.padding4  = file.Read32();
        Console.WriteLine("Data size: " + header.file_size);

        //Read SO name and othe rparameters
        UInt16 so_name_length = file.Read16();
        string so_name        = file.ReadString(so_name_length);

        //string_table.Add(so_name);
        Console.WriteLine("SO name: " + so_name);
        UInt32 so_type = file.Read32();

        Console.WriteLine("SO type: " + so_type);

        while (file.pos < file.file_size)
        {
            SOValue so_value = new SOValue();

            // Read parameter name. Name length is encoded into 7 bits, 8th bit is flag if name is inline or indexed.
            UInt32 length_int  = (UInt32)file.ReadCompressedInt();
            bool   name_inline = (length_int & 0x01) > 0;
            length_int >>= 1;
            if (name_inline)
            {
                so_value.key = file.ReadString((int)length_int);
                string_table.Add(so_value.key);
            }
            else
            {
                so_value.key = string_table[(int)length_int];
            }
            Console.WriteLine(so_value.key + " (inline: " + name_inline + ")");

            // Read parameter value. First byte is value type.
            so_value.type = file.Read8();
            if (so_value.type == SOTypes.TYPE_NULL)
            {
                Console.WriteLine("\tNULL");
            }
            else if (so_value.type == SOTypes.TYPE_BOOL_FALSE)
            {
                so_value.bool_val = false;
                Console.WriteLine("\tFalse");
            }
            else if (so_value.type == SOTypes.TYPE_BOOL_TRUE)
            {
                so_value.bool_val = true;
                Console.WriteLine("\tTrue");
            }
            else if (so_value.type == SOTypes.TYPE_INT)
            {
                so_value.int_val = (int)file.ReadCompressedInt();
                Console.WriteLine("\t" + so_value.int_val);
            }
            else if (so_value.type == SOTypes.TYPE_DOUBLE)
            {
                so_value.double_val = file.ReadDouble();
                Console.WriteLine("\t" + so_value.double_val);
            }
            else if (so_value.type == SOTypes.TYPE_STRING)
            {
                Int32 val_length = file.ReadCompressedInt();
                bool  val_inline = (val_length & 0x00000001) > 0;
                val_length >>= 1;
                if (!val_inline)
                {
                    Console.WriteLine("\tReference to string: " + val_length);
                    if (val_length < string_table.Count)
                    {
                        so_value.string_val = string_table[(int)val_length];
                        Console.WriteLine("\t" + so_value.string_val);
                    }
                }
                else
                {
                    so_value.string_val = file.ReadString((int)val_length);
                    string_table.Add(so_value.string_val);
                    Console.WriteLine("\t" + so_value.string_val + " (" + val_length + ")");
                }
            }
            else
            {
                Console.WriteLine("Type not implemented yet: " + so_value.type);
                //Move read position to next item
                while (file.pos < file.file_size)
                {
                    byte next_byte = file.Read8();
                    if (next_byte == 0)
                    {
                        --file.pos;
                        break;
                    }
                }
            }
            so.values.Add(so_value);
            if (file.pos < file.file_size)
            {
                file.Read8();   //Padding
            }
        }
        return(so);
    }