public void writeProperties(DataOutput buffer, List<L2Property> list, UnrealPackageReadOnly up)
      throws UnrealException {
    try {
      for (L2Property property : list) {
        Property template = property.getTemplate();

        for (int i = 0; i < property.getSize(); i++) {
          Object obj = property.getAt(i);
          if (obj == null) continue;

          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          DataOutput objBuffer = new DataOutputStream(baos, buffer.getCharset());
          AtomicBoolean array = new AtomicBoolean(i > 0);
          AtomicReference<String> structName = new AtomicReference<>();
          AtomicReference<Type> type =
              new AtomicReference<>(
                  Type.valueOf(
                      template.getClass().getSimpleName().replace("Property", "").toUpperCase()));
          write(objBuffer, template, obj, array, structName, type, up);
          byte[] bytes = baos.toByteArray();

          int size = getPropertySize(bytes.length);
          int ord = type.get().ordinal();
          if (ord == 8) // FIXME
          ord = 5;
          int info = (array.get() ? 1 << 7 : 0) | (size << 4) | ord;

          buffer.writeCompactInt(up.nameReference(template.getEntry().getObjectName().getName()));
          buffer.writeByte(info);

          if (type.get() == Type.STRUCT) buffer.writeCompactInt(up.nameReference(structName.get()));
          switch (size) {
            case 5:
              buffer.writeByte(bytes.length);
              break;
            case 6:
              buffer.writeShort(bytes.length);
              break;
            case 7:
              buffer.writeInt(bytes.length);
              break;
          }
          if (i > 0) buffer.writeByte(i);
          buffer.write(bytes);
        }
      }
      buffer.writeCompactInt(up.nameReference("None"));
    } catch (IOException e) {
      throw new UnrealException(e);
    }
  }
  private void write(
      DataOutput objBuffer,
      Property template,
      Object obj,
      AtomicBoolean array,
      AtomicReference<String> structName,
      AtomicReference<Type> type,
      UnrealPackageReadOnly up)
      throws IOException {
    if (template instanceof ByteProperty) {
      System.out.println(template.getEntry().getObjectInnerFullName() + " [BYTE]");
      objBuffer.writeByte((Integer) obj);
    } else if (template instanceof IntProperty) {
      System.out.println(template.getEntry().getObjectInnerFullName() + " [INT]");
      objBuffer.writeInt((Integer) obj);
    } else if (template instanceof BoolProperty) {
      System.out.println(template.getEntry().getObjectInnerFullName() + " [BOOL]");
      array.set((Boolean) obj);
    } else if (template instanceof FloatProperty) {
      System.out.println(template.getEntry().getObjectInnerFullName() + " [FLOAT]");
      objBuffer.writeFloat((Float) obj);
    } else if (template instanceof ObjectProperty) {
      System.out.println(template.getEntry().getObjectInnerFullName() + " [OBJ]");
      objBuffer.writeCompactInt((Integer) obj);
    } else if (template instanceof NameProperty) {
      System.out.println(template.getEntry().getObjectInnerFullName() + " [NAME]");
      objBuffer.writeCompactInt((Integer) obj);
    } else if (template instanceof ArrayProperty) {
      System.out.println(template.getEntry().getObjectInnerFullName() + " [ARRAY]");
      ArrayProperty arrayProperty = (ArrayProperty) template;

      List<Object> arrayList = (List<Object>) obj;
      objBuffer.writeCompactInt(arrayList.size());

      UnrealPackageReadOnly.ExportEntry arrayInner =
          (UnrealPackageReadOnly.ExportEntry) arrayProperty.getInner();
      String a = arrayInner.getObjectClass().getObjectName().getName();
      try {
        Class<? extends Property> pc =
            Class.forName("acmi.l2.clientmod.unreal." + a).asSubclass(Property.class);
        Property f =
            pc.getConstructor(
                    ByteBuffer.class, UnrealPackageReadOnly.ExportEntry.class, PropertiesUtil.class)
                .newInstance(
                    ByteBuffer.wrap(arrayInner.getObjectRawDataExternally())
                        .order(ByteOrder.LITTLE_ENDIAN),
                    arrayInner,
                    this);

        for (Object arrayObj : arrayList) {
          write(
              objBuffer,
              f,
              arrayObj,
              new AtomicBoolean(),
              new AtomicReference<>(),
              new AtomicReference<>(),
              up);
        }
      } catch (ReflectiveOperationException e) {
        throw new RuntimeException(e);
      }
    } else if (template instanceof StructProperty) {
      System.out.println(template.getEntry().getObjectInnerFullName() + " [STRUCT]");
      StructProperty structProperty = (StructProperty) template;
      structName.set(structProperty.getStructType().getObjectName().getName());
      writeStruct(objBuffer, structName.get(), up, (List<L2Property>) obj);
      //            if (false) { //Not used in L2?
      //                switch (structName.get()) {
      //                    case "Vector":
      //                        type.set(Type.VECTOR);
      //                        break;
      //                    case "Rotator":
      //                        type.set(Type.ROTATOR);
      //                        break;
      //                }
      //            }
    } else if (template instanceof StrProperty) {
      System.out.println(template.getEntry().getObjectInnerFullName() + " [STR]");
      objBuffer.writeLine((String) obj);
    } else {
      throw new UnsupportedOperationException(
          template.getClass().getSimpleName() + " serialization not implemented");
    }
  }