예제 #1
0
        /// <summary>
        ///     Parses an object inside the currently parsed binary property list.
        ///     For the format specification check
        ///     <a href="http://www.opensource.apple.com/source/CF/CF-855.17/CFBinaryPList.c">
        ///         Apple's binary property list parser implementation
        ///     </a>
        ///     .
        /// </summary>
        /// <returns>The parsed object.</returns>
        /// <param name="obj">The object ID.</param>
        /// <exception cref="PropertyListFormatException">When the property list's format could not be parsed.</exception>
        protected virtual NSObject ParseObject(ReadOnlySpan <byte> bytes, int obj)
        {
            int  offset  = offsetTable[obj];
            byte type    = bytes[offset];
            int  objType = (type & 0xF0) >> 4; //First  4 bits
            int  objInfo = type & 0x0F;        //Second 4 bits

            switch (objType)
            {
            case 0x0:
            {
                //Simple
                switch (objInfo)
                {
                case 0x0:
                {
                    //null object (v1.0 and later)
                    return(null);
                }

                case 0x8:
                {
                    //false
                    return(new NSNumber(false));
                }

                case 0x9:
                {
                    //true
                    return(new NSNumber(true));
                }

                case 0xC:
                {
                    //URL with no base URL (v1.0 and later)
                    //TODO Implement binary URL parsing (not yet even implemented in Core Foundation as of revision 855.17)
                    break;
                }

                case 0xD:
                {
                    //URL with base URL (v1.0 and later)
                    //TODO Implement binary URL parsing (not yet even implemented in Core Foundation as of revision 855.17)
                    break;
                }

                case 0xE:
                {
                    //16-byte UUID (v1.0 and later)
                    //TODO Implement binary UUID parsing (not yet even implemented in Core Foundation as of revision 855.17)
                    break;
                }

                case 0xF:
                {
                    //filler byte
                    return(null);
                }
                }

                break;
            }

            case 0x1:
            {
                //integer
                int length = 1 << objInfo;
                return(new NSNumber(bytes.Slice(offset + 1, length), NSNumber.INTEGER));
            }

            case 0x2:
            {
                //real
                int length = 1 << objInfo;
                return(new NSNumber(bytes.Slice(offset + 1, length), NSNumber.REAL));
            }

            case 0x3:
            {
                //Date
                if (objInfo != 0x3)
                {
                    throw new
                          PropertyListFormatException("The given binary property list contains a date object of an unknown type (" +
                                                      objInfo +
                                                      ")");
                }

                return(new NSDate(bytes.Slice(offset + 1, 8)));
            }

            case 0x4:
            {
                //Data
                ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int dataoffset);
                return(new NSData(CopyOfRange(bytes, offset + dataoffset, offset + dataoffset + length)));
            }

            case 0x5:
            {
                //ASCII String, each character is 1 byte
                ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int stroffset);
                return(new NSString(bytes.Slice(offset + stroffset, length), Encoding.ASCII));
            }

            case 0x6:
            {
                //UTF-16-BE String
                ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int stroffset);

                //UTF-16 characters can have variable length, but the Core Foundation reference implementation
                //assumes 2 byte characters, thus only covering the Basic Multilingual Plane
                length *= 2;
                return(new NSString(bytes.Slice(offset + stroffset, length), utf16BigEndian));
            }

            case 0x7:
            {
                //UTF-8 string (v1.0 and later)
                ReadLengthAndOffset(bytes, objInfo, offset, out int strOffset, out int characters);

                //UTF-8 characters can have variable length, so we need to calculate the byte length dynamically
                //by reading the UTF-8 characters one by one
                int length = CalculateUtf8StringLength(bytes, offset + strOffset, characters);
                return(new NSString(bytes.Slice(offset + strOffset, length), Encoding.UTF8));
            }

            case 0x8:
            {
                //UID (v1.0 and later)
                int length = objInfo + 1;
                return(new UID(bytes.Slice(offset + 1, length)));
            }

            case 0xA:
            {
                //Array
                ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int arrayOffset);

                NSArray array = new NSArray(length);
                for (int i = 0; i < length; i++)
                {
                    int objRef =
                        (int)ParseUnsignedInt(bytes.Slice(offset + arrayOffset + i * objectRefSize, objectRefSize));
                    array.Add(ParseObject(bytes, objRef));
                }

                return(array);
            }

            case 0xB:
            {
                //Ordered set (v1.0 and later)
                ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int contentOffset);

                NSSet set = new NSSet(true);
                for (int i = 0; i < length; i++)
                {
                    int objRef =
                        (int)ParseUnsignedInt(bytes.Slice(offset + contentOffset + i * objectRefSize,
                                                          objectRefSize));
                    set.AddObject(ParseObject(bytes, objRef));
                }

                return(set);
            }

            case 0xC:
            {
                //Set (v1.0 and later)
                ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int contentOffset);

                NSSet set = new NSSet();
                for (int i = 0; i < length; i++)
                {
                    int objRef =
                        (int)ParseUnsignedInt(bytes.Slice(offset + contentOffset + i * objectRefSize,
                                                          objectRefSize));
                    set.AddObject(ParseObject(bytes, objRef));
                }

                return(set);
            }

            case 0xD:
            {
                //Dictionary
                ReadLengthAndOffset(bytes, objInfo, offset, out int length, out int contentOffset);

                //System.out.println("Parsing dictionary #"+obj);
                NSDictionary dict = new NSDictionary(length);
                for (int i = 0; i < length; i++)
                {
                    int keyRef =
                        (int)ParseUnsignedInt(bytes.Slice(offset + contentOffset + i * objectRefSize,
                                                          objectRefSize));
                    int valRef =
                        (int)
                        ParseUnsignedInt(bytes.Slice(offset + contentOffset + length * objectRefSize + i * objectRefSize,
                                                     objectRefSize));
                    NSObject key = ParseObject(bytes, keyRef);
                    NSObject val = ParseObject(bytes, valRef);
                    dict.Add(key.ToString(), val);
                }

                return(dict);
            }

            default:
            {
                Debug.WriteLine("WARNING: The given binary property list contains an object of unknown type (" +
                                objType +
                                ")");
                break;
            }
            }

            return(null);
        }
        /// <summary>
        /// Parses an object inside the currently parsed binary property list.
        /// For the format specification check
        /// <a href="http://www.opensource.apple.com/source/CF/CF-855.17/CFBinaryPList.c">
        /// Apple's binary property list parser implementation</a>.
        /// </summary>
        /// <returns>The parsed object.</returns>
        /// <param name="obj">The object ID.</param>
        /// <exception cref="PropertyListFormatException">When the property list's format could not be parsed.</exception>
        NSObject ParseObject(int obj)
        {
            int  offset  = offsetTable[obj];
            byte type    = bytes[offset];
            int  objType = (type & 0xF0) >> 4; //First  4 bits
            int  objInfo = (type & 0x0F);      //Second 4 bits

            switch (objType)
            {
            case 0x0:
            {
                //Simple
                switch (objInfo)
                {
                case 0x0:
                {
                    //null object (v1.0 and later)
                    return(null);
                }

                case 0x8:
                {
                    //false
                    return(new NSNumber(false, new BinaryOrigin(offset, 1)));
                }

                case 0x9:
                {
                    //true
                    return(new NSNumber(true, new BinaryOrigin(offset, 1)));
                }

                case 0xC:
                {
                    //URL with no base URL (v1.0 and later)
                    //TODO Implement binary URL parsing (not yet even implemented in Core Foundation as of revision 855.17)
                    break;
                }

                case 0xD:
                {
                    //URL with base URL (v1.0 and later)
                    //TODO Implement binary URL parsing (not yet even implemented in Core Foundation as of revision 855.17)
                    break;
                }

                case 0xE:
                {
                    //16-byte UUID (v1.0 and later)
                    //TODO Implement binary UUID parsing (not yet even implemented in Core Foundation as of revision 855.17)
                    break;
                }

                case 0xF:
                {
                    //filler byte
                    return(null);
                }
                }
                break;
            }

            case 0x1:
            {
                //integer
                int length = (int)Math.Pow(2, objInfo);
                return(new NSNumber(CopyOfRange(bytes, offset + 1, offset + 1 + length), NumberType.Integer, new BinaryOrigin(offset, length)));
            }

            case 0x2:
            {
                //real
                int length = (int)Math.Pow(2, objInfo);
                return(new NSNumber(CopyOfRange(bytes, offset + 1, offset + 1 + length), NumberType.Real, new BinaryOrigin(offset, length)));
            }

            case 0x3:
            {
                //Date
                if (objInfo != 0x3)
                {
                    throw new PropertyListFormatException("The given binary property list contains a date object of an unknown type (" + objInfo + ")");
                }
                return(new NSDate(CopyOfRange(bytes, offset + 1, offset + 9), new BinaryOrigin(offset, 9)));
            }

            case 0x4:
            {
                //Data
                int[] lengthAndOffset = ReadLengthAndOffset(objInfo, offset);
                int   length          = lengthAndOffset[0];
                int   dataoffset      = lengthAndOffset[1];

                return(new NSData(CopyOfRange(bytes, offset + dataoffset, offset + dataoffset + length), new BinaryOrigin(offset + dataoffset, length)));
            }

            case 0x5:
            {
                //ASCII String
                int[] lengthAndOffset = ReadLengthAndOffset(objInfo, offset);
                int   length          = lengthAndOffset[0]; //Each character is 1 byte
                int   stroffset       = lengthAndOffset[1];

                return(new NSString(CopyOfRange(bytes, offset + stroffset, offset + stroffset + length), "ASCII", new BinaryOrigin(offset + stroffset, length)));
            }

            case 0x6:
            {
                //UTF-16-BE String
                int[] lengthAndOffset = ReadLengthAndOffset(objInfo, offset);
                int   length          = lengthAndOffset[0];
                int   stroffset       = lengthAndOffset[1];

                //UTF-16 characters can have variable length, but the Core Foundation reference implementation
                //assumes 2 byte characters, thus only covering the Basic Multilingual Plane
                length *= 2;
                return(new NSString(CopyOfRange(bytes, offset + stroffset, offset + stroffset + length), "UTF-16BE", new BinaryOrigin(offset + stroffset, length)));
            }

            case 0x7:
            {
                //UTF-8 string (v1.0 and later)
                int[] lengthAndOffset = ReadLengthAndOffset(objInfo, offset);
                int   strOffset       = lengthAndOffset[1];
                int   characters      = lengthAndOffset[0];
                //UTF-8 characters can have variable length, so we need to calculate the byte length dynamically
                //by reading the UTF-8 characters one by one
                int length = CalculateUtf8StringLength(bytes, offset + strOffset, characters);
                return(new NSString(CopyOfRange(bytes, offset + strOffset, offset + strOffset + length), "UTF-8", new BinaryOrigin(offset + strOffset, length)));
            }

            case 0x8:
            {
                //UID (v1.0 and later)
                int length = objInfo + 1;
                return(new UID(obj.ToString(), CopyOfRange(bytes, offset + 1, offset + 1 + length), new BinaryOrigin(offset + 1, length)));
            }

            case 0xA:
            {
                //Array
                int[] lengthAndOffset = ReadLengthAndOffset(objInfo, offset);
                int   length          = lengthAndOffset[0];
                int   arrayOffset     = lengthAndOffset[1];

                NSArray array = new NSArray(length, new BinaryOrigin(offset + arrayOffset, length * objectRefSize));
                for (int i = 0; i < length; i++)
                {
                    int objRef = (int)ParseUnsignedInt(CopyOfRange(bytes,
                                                                   offset + arrayOffset + i * objectRefSize,
                                                                   offset + arrayOffset + (i + 1) * objectRefSize));
                    array.Add(ParseObject(objRef));
                }
                return(array);
            }

            case 0xB:
            {
                //Ordered set (v1.0 and later)
                int[] lengthAndOffset = ReadLengthAndOffset(objInfo, offset);
                int   length          = lengthAndOffset[0];
                int   contentOffset   = lengthAndOffset[1];

                NSSet set = new NSSet(true, new BinaryOrigin(offset + contentOffset, length * objectRefSize));
                for (int i = 0; i < length; i++)
                {
                    int objRef = (int)ParseUnsignedInt(CopyOfRange(bytes,
                                                                   offset + contentOffset + i * objectRefSize,
                                                                   offset + contentOffset + (i + 1) * objectRefSize));
                    set.AddObject(ParseObject(objRef));
                }
                return(set);
            }

            case 0xC:
            {
                //Set (v1.0 and later)
                int[] lengthAndOffset = ReadLengthAndOffset(objInfo, offset);
                int   length          = lengthAndOffset[0];
                int   contentOffset   = lengthAndOffset[1];

                NSSet set = new NSSet(new BinaryOrigin(offset + contentOffset, length * objectRefSize));
                for (int i = 0; i < length; i++)
                {
                    int objRef = (int)ParseUnsignedInt(CopyOfRange(bytes,
                                                                   offset + contentOffset + i * objectRefSize,
                                                                   offset + contentOffset + (i + 1) * objectRefSize));
                    set.AddObject(ParseObject(objRef));
                }
                return(set);
            }

            case 0xD:
            {
                //Dictionary
                int[] lengthAndOffset = ReadLengthAndOffset(objInfo, offset);
                int   length          = lengthAndOffset[0];
                int   contentOffset   = lengthAndOffset[1];

                //System.out.println("Parsing dictionary #"+obj);
                NSDictionary dict = new NSDictionary(new BinaryOrigin(offset + contentOffset, 2 * length * objectRefSize));
                for (int i = 0; i < length; i++)
                {
                    int keyRef = (int)ParseUnsignedInt(CopyOfRange(bytes,
                                                                   offset + contentOffset + i * objectRefSize,
                                                                   offset + contentOffset + (i + 1) * objectRefSize));
                    int valRef = (int)ParseUnsignedInt(CopyOfRange(bytes,
                                                                   offset + contentOffset + (length * objectRefSize) + i * objectRefSize,
                                                                   offset + contentOffset + (length * objectRefSize) + (i + 1) * objectRefSize));
                    NSObject key = ParseObject(keyRef);
                    NSObject val = ParseObject(valRef);
                    dict.Add(key.ToString(), val);
                }
                return(dict);
            }

            default:
            {
                Debug.WriteLine("WARNING: The given binary property list contains an object of unknown type (" + objType + ")");
                break;
            }
            }
            return(null);
        }