jvgariant-ostree: Correctly deserialize delta operations.

Change-Id: Ic6659d7ea5e9411220571c33979e29471cec897e
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaPartPayload.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaPartPayload.java
index ed0f4ff..4e2587f 100644
--- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaPartPayload.java
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaPartPayload.java
@@ -7,6 +7,7 @@
 import java.io.UncheckedIOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.util.ArrayList;
 import java.util.List;
 import org.tukaani.xz.XZInputStream;
 
@@ -31,15 +32,6 @@
     ByteString rawDataSource,
     List<DeltaOperation> operations) {
 
-  private static final Decoder<DeltaPartPayload> DECODER =
-      Decoder.ofStructure(
-              DeltaPartPayload.class,
-              Decoder.ofArray(FileMode.decoder()),
-              Decoder.ofArray(Decoder.ofArray(Xattr.decoder())),
-              ByteString.decoder(),
-              ByteString.decoder().map(DeltaPartPayload::parseDeltaOperationList))
-          .contramap(DeltaPartPayload::preparse);
-
   private static ByteBuffer preparse(ByteBuffer byteBuffer) {
     byte compressionByte = byteBuffer.get(0);
     var dataSlice = byteBuffer.slice(1, byteBuffer.limit() - 1);
@@ -65,25 +57,40 @@
     };
   }
 
-  private static List<DeltaOperation> parseDeltaOperationList(ByteString byteString) {
-    return byteString.stream().map(DeltaOperation::valueOf).toList();
+  private static List<DeltaOperation> parseDeltaOperationList(
+      ByteString byteString, List<ObjectType> objectTypes) {
+    List<DeltaOperation> deltaOperations = new ArrayList<>();
+    var byteBuffer = ByteBuffer.wrap(byteString.bytes());
+    int objectIndex = 0;
+
+    while (byteBuffer.hasRemaining()) {
+      var currentOperation = DeltaOperation.readFrom(byteBuffer, objectTypes.get(objectIndex));
+      deltaOperations.add(currentOperation);
+      if (currentOperation instanceof DeltaOperation.Close
+          || currentOperation instanceof DeltaOperation.OpenSpliceAndCloseMeta
+          || currentOperation instanceof DeltaOperation.OpenSpliceAndCloseReal) {
+        ++objectIndex;
+      }
+    }
+
+    return deltaOperations;
   }
 
   /**
    * A file mode triple (UID, GID, and permission bits).
    *
-   * @param uid
-   * @param gid
-   * @param mode
+   * @param uid the user ID that owns the file.
+   * @param gid the group ID that owns the file.
+   * @param mode the POSIX permission bits.
    */
   public record FileMode(int uid, int gid, int mode) {
 
     private static final Decoder<FileMode> DECODER =
         Decoder.ofStructure(
             FileMode.class,
-            Decoder.ofInt().withByteOrder(ByteOrder.LITTLE_ENDIAN),
-            Decoder.ofInt().withByteOrder(ByteOrder.LITTLE_ENDIAN),
-            Decoder.ofInt().withByteOrder(ByteOrder.LITTLE_ENDIAN));
+            Decoder.ofInt().withByteOrder(ByteOrder.BIG_ENDIAN),
+            Decoder.ofInt().withByteOrder(ByteOrder.BIG_ENDIAN),
+            Decoder.ofInt().withByteOrder(ByteOrder.BIG_ENDIAN));
 
     /**
      * Acquires a {@link Decoder} for the enclosing type.
@@ -98,12 +105,18 @@
   /**
    * Acquires a {@link Decoder} for the enclosing type.
    *
-   * <p>FIXME: The first byte is actually a compression byte: {@code 0} for none, {@code 'x'} for
-   * LZMA.
-   *
    * @return a possibly shared {@link Decoder}.
    */
-  public static Decoder<DeltaPartPayload> decoder() {
-    return DECODER;
+  public static Decoder<DeltaPartPayload> decoder(DeltaMetaEntry deltaMetaEntry) {
+    var objectTypes =
+        deltaMetaEntry.objects().stream().map(DeltaMetaEntry.DeltaObject::objectType).toList();
+    return Decoder.ofStructure(
+            DeltaPartPayload.class,
+            Decoder.ofArray(FileMode.decoder()),
+            Decoder.ofArray(Decoder.ofArray(Xattr.decoder())),
+            ByteString.decoder(),
+            ByteString.decoder()
+                .map(byteString -> parseDeltaOperationList(byteString, objectTypes)))
+        .contramap(DeltaPartPayload::preparse);
   }
 }