final int unpackObject(Object obj, ClassDescriptor desc,
			boolean recursiveLoading, byte[] body, int offs, Object parent)
			throws Exception {
		ClassDescriptor.FieldDescriptor[] all = desc.allFields;
		ReflectionProvider provider = ClassDescriptor.getReflectionProvider();
		int len;

		for (int i = 0, n = all.length; i < n; i++) {
			ClassDescriptor.FieldDescriptor fd = all[i];
			Field f = fd.field;

			if (f == null || obj == null) {
				switch (fd.type) {
				case ClassDescriptor.tpBoolean:
				case ClassDescriptor.tpByte:
					offs += 1;
					continue;
				case ClassDescriptor.tpChar:
				case ClassDescriptor.tpShort:
					offs += 2;
					continue;
				case ClassDescriptor.tpInt:
				case ClassDescriptor.tpFloat:
				case ClassDescriptor.tpEnum:
					offs += 4;
					continue;
				case ClassDescriptor.tpObject:
					offs = skipObjectReference(body, offs);
					continue;
				case ClassDescriptor.tpLong:
				case ClassDescriptor.tpDouble:
				case ClassDescriptor.tpDate:
					offs += 8;
					continue;
				case ClassDescriptor.tpString:
				case ClassDescriptor.tpClass:
					offs = Bytes.skipString(body, offs);
					continue;
				case ClassDescriptor.tpValue:
					offs = unpackObject(null, fd.valueDesc, recursiveLoading,
							body, offs, parent);
					continue;
				case ClassDescriptor.tpRaw:
				case ClassDescriptor.tpArrayOfByte:
				case ClassDescriptor.tpArrayOfBoolean:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len > 0) {
						offs += len;
					}
					else if (len < -1) {
						offs += ClassDescriptor.sizeof[-2 - len];
					}

					continue;
				case ClassDescriptor.tpCustom: {
					ByteArrayObjectInputStream in = new ByteArrayObjectInputStream(
							body, offs, parent, recursiveLoading, false);
					serializer.unpack(in);
					offs = in.getPosition();
					continue;
				}
				case ClassDescriptor.tpArrayOfShort:
				case ClassDescriptor.tpArrayOfChar:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len > 0) {
						offs += len * 2;
					}

					continue;
				case ClassDescriptor.tpArrayOfObject:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					for (int j = 0; j < len; j++) {
						offs = skipObjectReference(body, offs);
					}

					continue;
				case ClassDescriptor.tpArrayOfInt:
				case ClassDescriptor.tpArrayOfEnum:
				case ClassDescriptor.tpArrayOfFloat:
				case ClassDescriptor.tpLink:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len > 0) {
						offs += len * 4;
					}

					continue;
				case ClassDescriptor.tpArrayOfLong:
				case ClassDescriptor.tpArrayOfDouble:
				case ClassDescriptor.tpArrayOfDate:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len > 0) {
						offs += len * 8;
					}

					continue;
				case ClassDescriptor.tpArrayOfString:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len > 0) {
						for (int j = 0; j < len; j++) {
							offs = Bytes.skipString(body, offs);
						}
					}

					continue;
				case ClassDescriptor.tpArrayOfValue:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len > 0) {
						ClassDescriptor valueDesc = fd.valueDesc;

						for (int j = 0; j < len; j++) {
							offs = unpackObject(null, valueDesc,
									recursiveLoading, body, offs, parent);
						}
					}

					continue;
				}
			}
			else if (offs < body.length) {
				switch (fd.type) {
				case ClassDescriptor.tpBoolean:
					provider.setBoolean(f, obj, body[offs++] != 0);
					continue;
				case ClassDescriptor.tpByte:
					provider.setByte(f, obj, body[offs++]);
					continue;
				case ClassDescriptor.tpChar:
					provider.setChar(f, obj, (char) Bytes.unpack2(body, offs));
					offs += 2;
					continue;
				case ClassDescriptor.tpShort:
					provider.setShort(f, obj, Bytes.unpack2(body, offs));
					offs += 2;
					continue;
				case ClassDescriptor.tpInt:
					provider.setInt(f, obj, Bytes.unpack4(body, offs));
					offs += 4;
					continue;
				case ClassDescriptor.tpLong:
					provider.setLong(f, obj, Bytes.unpack8(body, offs));
					offs += 8;
					continue;
				case ClassDescriptor.tpFloat:
					provider.setFloat(f, obj, Bytes.unpackF4(body, offs));
					offs += 4;
					continue;
				case ClassDescriptor.tpDouble:
					provider.setDouble(f, obj, Bytes.unpackF8(body, offs));
					offs += 8;
					continue;
				case ClassDescriptor.tpEnum: {
					int index = Bytes.unpack4(body, offs);

					if (index >= 0) {
						provider.set(f, obj, fd.field.getType()
								.getEnumConstants()[index]);
					}
					else {
						provider.set(f, obj, null);
					}

					offs += 4;
					continue;
				}
				case ClassDescriptor.tpString: {
					ArrayPos pos = new ArrayPos(body, offs);
					provider.set(f, obj, Bytes.unpackString(pos, encoding));
					offs = pos.offs;
					continue;
				}
				case ClassDescriptor.tpClass: {
					ArrayPos pos = new ArrayPos(body, offs);
					Class cls = ClassDescriptor.loadClass(this,
							Bytes.unpackString(pos, encoding));
					provider.set(f, obj, cls);
					offs = pos.offs;
					continue;
				}
				case ClassDescriptor.tpDate: {
					long msec = Bytes.unpack8(body, offs);
					offs += 8;
					Date date = null;

					if (msec >= 0) {
						date = new Date(msec);
					}

					provider.set(f, obj, date);
					continue;
				}
				case ClassDescriptor.tpObject: {
					ArrayPos pos = new ArrayPos(body, offs);
					provider.set(
							f,
							obj,
							unswizzle(pos, f.getType(), parent,
									recursiveLoading));
					offs = pos.offs;
					continue;
				}
				case ClassDescriptor.tpValue: {
					Object value = fd.valueDesc.newInstance();
					offs = unpackObject(value, fd.valueDesc, recursiveLoading,
							body, offs, parent);
					provider.set(f, obj, value);
					continue;
				}
				case ClassDescriptor.tpRaw:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len >= 0) {
						ByteArrayInputStream bin = new ByteArrayInputStream(
								body, offs, len);
						ObjectInputStream in = new PersistentObjectInputStream(
								bin);
						provider.set(f, obj, in.readObject());
						in.close();
						offs += len;
					}
					else if (len < 0) {
						Object val = null;

						switch (-2 - len) {
						case ClassDescriptor.tpBoolean:
							val = Boolean.valueOf(body[offs++] != 0);
							break;
						case ClassDescriptor.tpByte:
							val = new Byte(body[offs++]);
							break;
						case ClassDescriptor.tpChar:
							val = new Character(
									(char) Bytes.unpack2(body, offs));
							offs += 2;
							break;
						case ClassDescriptor.tpShort:
							val = new Short(Bytes.unpack2(body, offs));
							offs += 2;
							break;
						case ClassDescriptor.tpInt:
							val = new Integer(Bytes.unpack4(body, offs));
							offs += 4;
							break;
						case ClassDescriptor.tpLong:
							val = new Long(Bytes.unpack8(body, offs));
							offs += 8;
							break;
						case ClassDescriptor.tpFloat:
							val = new Float(Float.intBitsToFloat(Bytes.unpack4(
									body, offs)));
							offs += 4;
							break;
						case ClassDescriptor.tpDouble:
							val = new Double(Double.longBitsToDouble(Bytes
									.unpack8(body, offs)));
							offs += 8;
							break;
						case ClassDescriptor.tpDate:
							val = new Date(Bytes.unpack8(body, offs));
							offs += 8;
							break;
						case ClassDescriptor.tpObject:
							val = unswizzle(Bytes.unpack4(body, offs),
									Persistent.class, recursiveLoading);
							offs += 4;
						}

						provider.set(f, obj, val);
					}

					continue;
				case ClassDescriptor.tpCustom: {
					ByteArrayObjectInputStream in = new ByteArrayObjectInputStream(
							body, offs, parent, recursiveLoading, false);
					serializer.unpack(in);
					provider.set(f, obj, serializer.unpack(in));
					offs = in.getPosition();
					continue;
				}
				case ClassDescriptor.tpArrayOfByte:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len < 0) {
						provider.set(f, obj, null);
					}
					else {
						byte[] arr = new byte[len];
						System.arraycopy(body, offs, arr, 0, len);
						offs += len;
						provider.set(f, obj, arr);
					}

					continue;
				case ClassDescriptor.tpArrayOfBoolean:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len < 0) {
						provider.set(f, obj, null);
					}
					else {
						boolean[] arr = new boolean[len];

						for (int j = 0; j < len; j++) {
							arr[j] = body[offs++] != 0;
						}

						provider.set(f, obj, arr);
					}

					continue;
				case ClassDescriptor.tpArrayOfShort:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len < 0) {
						provider.set(f, obj, null);
					}
					else {
						short[] arr = new short[len];

						for (int j = 0; j < len; j++) {
							arr[j] = Bytes.unpack2(body, offs);
							offs += 2;
						}

						provider.set(f, obj, arr);
					}

					continue;
				case ClassDescriptor.tpArrayOfChar:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len < 0) {
						provider.set(f, obj, null);
					}
					else {
						char[] arr = new char[len];

						for (int j = 0; j < len; j++) {
							arr[j] = (char) Bytes.unpack2(body, offs);
							offs += 2;
						}

						provider.set(f, obj, arr);
					}

					continue;
				case ClassDescriptor.tpArrayOfInt:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len < 0) {
						provider.set(f, obj, null);
					}
					else {
						int[] arr = new int[len];

						for (int j = 0; j < len; j++) {
							arr[j] = Bytes.unpack4(body, offs);
							offs += 4;
						}

						provider.set(f, obj, arr);
					}

					continue;
				case ClassDescriptor.tpArrayOfEnum:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len < 0) {
						f.set(obj, null);
					}
					else {
						Class elemType = f.getType().getComponentType();
						Enum[] enumConstants = (Enum[]) elemType
								.getEnumConstants();
						Enum[] arr = (Enum[]) Array.newInstance(elemType, len);

						for (int j = 0; j < len; j++) {
							int index = Bytes.unpack4(body, offs);

							if (index >= 0) {
								arr[j] = enumConstants[index];
							}

							offs += 4;
						}

						f.set(obj, arr);
					}

					continue;
				case ClassDescriptor.tpArrayOfLong:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len < 0) {
						provider.set(f, obj, null);
					}
					else {
						long[] arr = new long[len];

						for (int j = 0; j < len; j++) {
							arr[j] = Bytes.unpack8(body, offs);
							offs += 8;
						}

						provider.set(f, obj, arr);
					}

					continue;
				case ClassDescriptor.tpArrayOfFloat:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len < 0) {
						provider.set(f, obj, null);
					}
					else {
						float[] arr = new float[len];

						for (int j = 0; j < len; j++) {
							arr[j] = Bytes.unpackF4(body, offs);
							offs += 4;
						}

						provider.set(f, obj, arr);
					}

					continue;
				case ClassDescriptor.tpArrayOfDouble:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len < 0) {
						provider.set(f, obj, null);
					}
					else {
						double[] arr = new double[len];

						for (int j = 0; j < len; j++) {
							arr[j] = Bytes.unpackF8(body, offs);
							offs += 8;
						}

						provider.set(f, obj, arr);
					}

					continue;
				case ClassDescriptor.tpArrayOfDate:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len < 0) {
						provider.set(f, obj, null);
					}
					else {
						Date[] arr = new Date[len];

						for (int j = 0; j < len; j++) {
							long msec = Bytes.unpack8(body, offs);
							offs += 8;

							if (msec >= 0) {
								arr[j] = new Date(msec);
							}
						}

						provider.set(f, obj, arr);
					}

					continue;
				case ClassDescriptor.tpArrayOfString:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len < 0) {
						provider.set(f, obj, null);
					}
					else {
						String[] arr = new String[len];
						ArrayPos pos = new ArrayPos(body, offs);

						for (int j = 0; j < len; j++) {
							arr[j] = Bytes.unpackString(pos, encoding);
						}

						offs = pos.offs;
						provider.set(f, obj, arr);
					}

					continue;
				case ClassDescriptor.tpArrayOfObject:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len < 0) {
						provider.set(f, obj, null);
					}
					else {
						Class elemType = f.getType().getComponentType();
						Object[] arr = (Object[]) Array.newInstance(elemType,
								len);
						ArrayPos pos = new ArrayPos(body, offs);

						for (int j = 0; j < len; j++) {
							arr[j] = unswizzle(pos, elemType, parent,
									recursiveLoading);
						}

						offs = pos.offs;
						provider.set(f, obj, arr);
					}

					continue;
				case ClassDescriptor.tpArrayOfValue:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len < 0) {
						provider.set(f, obj, null);
					}
					else {
						Class elemType = f.getType().getComponentType();
						Object[] arr = (Object[]) Array.newInstance(elemType,
								len);
						ClassDescriptor valueDesc = fd.valueDesc;

						for (int j = 0; j < len; j++) {
							Object value = valueDesc.newInstance();
							offs = unpackObject(value, valueDesc,
									recursiveLoading, body, offs, parent);
							arr[j] = value;
						}

						provider.set(f, obj, arr);
					}

					continue;
				case ClassDescriptor.tpLink:
					len = Bytes.unpack4(body, offs);
					offs += 4;

					if (len < 0) {
						provider.set(f, obj, null);
					}
					else {
						Object[] arr = new Object[len];

						for (int j = 0; j < len; j++) {
							int elemOid = Bytes.unpack4(body, offs);
							offs += 4;

							if (elemOid != 0) {
								arr[j] = new PersistentStub(this, elemOid);
							}
						}

						provider.set(f, obj, new LinkImpl(this, arr, parent));
					}
				}
			}
		}

		return offs;
	}