All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ru.sbtqa.monte.media.iff.AnimMerger Maven / Gradle / Ivy

/* @(#)AnimMerger.java
 * Copyright © 2004-2013 Werner Randelshofer, Switzerland.
 * You may only use this software in accordance with the license terms.
 */
package ru.sbtqa.monte.media.iff;

import java.io.*;
import java.text.NumberFormat;
import java.util.*;
import javax.swing.*;
import ru.sbtqa.monte.media.AbortException;
import ru.sbtqa.monte.media.ParseException;
import ru.sbtqa.monte.media.gui.BackgroundTask;
import ru.sbtqa.monte.media.gui.tree.TreeNodeImpl;

/**
 * Merges two IFF ANIM files.
 *
 * @author Werner Randelshofer
 * @version $Id: AnimMerger.java 364 2016-11-09 19:54:25Z werner $
 */
public class AnimMerger extends javax.swing.JFrame {

    private final static long serialVersionUID = 1L;
    private File file1, file2;
    private JFileChooser chooser;
    private static NumberFormat numFormat = NumberFormat.getInstance();

    /**
     * Creates new form AnimMerger
     */
    public AnimMerger() {
        initComponents();
    }

    public final static int CMAP_ID = IFFParser.stringToID("CMAP");
    public final static int ILBM_ID = IFFParser.stringToID("ILBM");
    public final static int ANHD_ID = IFFParser.stringToID("ANHD");

    private final static int[] sequence = {
        IFFParser.stringToID("ANIM"),
        IFFParser.stringToID("8SVX"),
        IFFParser.stringToID("ILBM"),
        IFFParser.stringToID("VHDR"),
        IFFParser.stringToID("ANNO"),
        IFFParser.stringToID("CAMG"),
        IFFParser.stringToID("BMHD"),
        IFFParser.stringToID("ANHD"),
        IFFParser.stringToID("CHAN"),
        IFFParser.stringToID("SCTL"),
        IFFParser.stringToID("CMAP"),
        IFFParser.stringToID("DPPS"),
        IFFParser.stringToID("DPAN"),
        IFFParser.stringToID("DLTA"),
        IFFParser.stringToID("BODY"),};

    private static Comparator nodeComparator = new Comparator() {
        /*
        public int compare(Object o1, Object o2) {
            return compare((IFFChunkNode) o1, (IFFChunkNode) o2);
        }*/
        public int compare(IFFChunkNode o1, IFFChunkNode o2) {
            return o1.getSeqIndex() - o2.getSeqIndex();
        }
    };

    protected static class IFFChunkNode extends TreeNodeImpl {

        private final static long serialVersionUID = 1L;
        private int type;
        private int id;
        private int size;
        private int offset;
        private byte[] data;

        public int getPaddedChunkSize() {
            return size + (size % 2) + 8;
        }

        public void write(DataOutputStream out) throws IOException {
            System.out.println("write " + IFFParser.idToString(id) + " size=" + size);
            if (data != null) {
                out.writeInt(id);
                out.writeInt(size);
                out.write(data);
                if (data.length % 2 == 1) {
                    out.writeByte(0);
                }
            } else {
                out.writeInt(type);
                out.writeInt(size);
                out.writeInt(id);
                for (Enumeration i = children(); i.hasMoreElements();) {
                    IFFChunkNode child = (IFFChunkNode) i.nextElement();
                    child.write(out);
                }
            }
        }

        @SuppressWarnings("unchecked")
        public void mergeFrom(IFFChunkNode that) {
            if (data != null) {
                if (id == ANHD_ID) {
                    mergeANHD(that);
                }
            } else if (this.isSameAs(that)) {
                final ArrayList mergedChildren = new ArrayList(Math.max(this.getChildCount(), that.getChildCount()));
                Collections.sort(this.getChildren(), nodeComparator);
                Collections.sort(that.getChildren(), nodeComparator);

                int thatCount = that.getChildCount();
                int thisCount = this.getChildCount();
                int thisIndex = 0, thatIndex = 0;
                int comparison;
                while (thisIndex < thisCount || thatIndex < thatCount) {
                    if (thisIndex >= thisCount) {
                        comparison = 1;
                    } else if (thatIndex >= thatCount) {
                        comparison = -1;
                    } else {
                        comparison = this.getChildAt(thisIndex).compareTo(that.getChildAt(thatIndex));
                    }

                    if (comparison < 0) {
                        System.out.println("Inserting from File1:" + this.getChildAt(thisIndex) + " into " + this);
                        mergedChildren.add(this.getChildAt(thisIndex));
                        thisIndex++;
                    } else if (comparison == 0) {
                        (this.getChildAt(thisIndex)).mergeFrom(that.getChildAt(thatIndex));
                        mergedChildren.add(this.getChildAt(thisIndex));
                        thatIndex++;
                        thisIndex++;
                    } else {
                        IFFChunkNode thatNode = that.getChildAt(thatIndex);
                        if (thatNode.id == CMAP_ID || thatNode.id == ILBM_ID) {
                            System.out.println("Skipping from File2:" + that.getChildAt(thatIndex));
                        } else {
                            System.out.println("Inserting from File2:" + that.getChildAt(thatIndex) + " into " + this);
                            mergedChildren.add(that.getChildAt(thatIndex));
                        }
                        thatIndex++;
                    }
                }

                this.removeAllChildren();
                this.size = 4;
                for (IFFChunkNode newChild : mergedChildren) {
                    this.size += newChild.getPaddedChunkSize();
                    this.add(newChild);
                }
            } else {
                System.out.println(this + "!=" + that);
            }
        }

        public void mergeANHD(IFFChunkNode that) {
            System.out.print(IFFParser.idToString(id) + " " + IFFParser.idToString(that.id) + " reltime=");
            for (int i = 0; i < 4; i++) {
                this.data[10 + i] = 0;
            }
            for (int i = 0; i < 4; i++) {
                this.data[14 + i] = that.data[14 + i];
                System.out.print(that.data[14 + i] + " ");
            }
            System.out.println();
        }

        public int compareTo(IFFChunkNode that) {
            return this.getSeqIndex() - that.getSeqIndex();
        }

        public int getSeqIndex() {
            for (int i = 0; i < sequence.length; i++) {
                if (id == sequence[i]) {
                    return i;
                }
            }
            throw new ArrayIndexOutOfBoundsException("no index for " + IFFParser.idToString(id));
            //return -1;
        }

        public IFFChunkNode(int type, int id, int size, int offset, byte[] data) {
            this.type = type;
            this.id = id;
            this.size = size;
            this.offset = offset;
            this.data = data;
        }

        public String getType() {
            return IFFParser.idToString(type);
        }

        public String getID() {
            return IFFParser.idToString(id);
        }

        public int getSize() {
            return size;
        }

        public int getOffset() {
            return offset;
        }

        public byte[] getRawData() {
            return data;
        }

        public boolean isSameAs(IFFChunkNode that) {
            return this.type == that.type && this.id == that.id;
        }

        public String toString() {
            return IFFParser.idToString(type) + " " + IFFParser.idToString(id);
        }

        public void dump(int depth) {
            StringBuffer buf = new StringBuffer(depth);
            for (int i = 0; i < depth; i++) {
                buf.append('.');
            }
            buf.append(IFFParser.idToString(type) + " " + IFFParser.idToString(id) + " " + numFormat.format(size));
            System.out.println(buf.toString());

            for (IFFChunkNode child : getChildren()) {
                child.dump(depth + 1);
            }
        }
    }

    protected static class Loader implements IFFVisitor {

        private IFFChunkNode root;
        private IFFChunkNode current;

        public Loader() {
        }

        public IFFChunkNode getRoot() {
            return root;
        }

        public void enterGroup(IFFChunk group) throws ParseException, AbortException {
            IFFChunkNode groupNode = new IFFChunkNode(
                  group.getID(),
                  group.getType(),
                  (int) group.getSize(),
                  (int) group.getScan(),
                  null
            );

            if (current == null) {
                root = current = groupNode;
            } else {
                current.add(groupNode);
                current = groupNode;
            }
        }

        public void leaveGroup(IFFChunk group) throws ParseException, AbortException {
            current = current.getParent();
        }

        public void visitChunk(IFFChunk group, IFFChunk chunk) throws ParseException, AbortException {
            IFFChunkNode chunkNode = new IFFChunkNode(
                  group.getType(),
                  chunk.getID(),
                  (int) chunk.getSize(),
                  (int) chunk.getScan(),
                  chunk.getData()
            );
            current.add(chunkNode);
        }
    }

    /*
    protected static class Merger implements IFFVisitor {
        private IFFChunkNode root;
        private IFFChunkNode current;
 
        public Merger(IFFChunkNode root) {
            this.root = root;
            this.current = null;
        }
 
        public IFFChunkNode getRoot() {
            return root;
        }
 
        public void enterGroup(IFFChunk group) throws ParseException, AbortException {
            IFFChunkNode groupNode = new IFFChunkNode(
            group.getID(),
            group.getType(),
            (int) group.getSize(),
            (int) group.getScan(),
            null
            );
            if (current == null) {
                if (! group.isSameAs(root)) {
                    throw new ParseException("Root nodes don't match "+groupNode);
                }
                current = root;
            } else {
                current.add(groupNode);
                current = groupNode;
            }
        }
 
        public void leaveGroup(IFFChunk group) throws ParseException, AbortException {
            current = (IFFChunkNode) current.getParent();
        }
 
        public void visitChunk(IFFChunk group, IFFChunk chunk) throws ParseException, AbortException {
            IFFChunkNode chunkNode = new IFFChunkNode(
            group.getType(),
            chunk.getID(),
            (int) chunk.getSize(),
            (int) chunk.getScan(),
            chunk.getData()
            );
            current.add(chunkNode);
        }
    }
     */
    private void merge(File f1, File f2) throws IOException {
        IFFChunkNode root1, root2;

        Loader loader = new Loader();
        InputStream in = new FileInputStream(f1);
        try {
            IFFParser iff = new IFFParser();
            iff.parse(in, loader);
        } catch (ParseException e) {
            throw new IOException(e.getMessage());
        } catch (AbortException e) {
            throw new IOException(e.getMessage());
        } finally {
            in.close();
        }

        root1 = loader.getRoot();

        loader = new Loader();
        in = new FileInputStream(f2);
        try {
            IFFParser iff = new IFFParser();
            iff.parse(in, loader);
        } catch (ParseException e) {
            throw new IOException(e.getMessage());
        } catch (AbortException e) {
            throw new IOException(e.getMessage());
        } finally {
            in.close();
        }

        root2 = loader.getRoot();

        root1.mergeFrom(root2);

        File file3 = new File(file1.getParentFile(), "merged" + file1.getName());

        DataOutputStream out = new DataOutputStream(new FileOutputStream(file3));
        try {
            root1.write(out);
            out.flush();
        } finally {
            out.close();
        }
//        root1.dump(0);

    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    private void initComponents() {//GEN-BEGIN:initComponents
        menuBar = new javax.swing.JMenuBar();
        fileMenu = new javax.swing.JMenu();
        mergeMenuItem = new javax.swing.JMenuItem();
        exitMenuItem = new javax.swing.JMenuItem();

        setTitle("ANIM Merger");
        fileMenu.setText("File");
        mergeMenuItem.setText("Merge...");
        mergeMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                merge(evt);
            }
        });

        fileMenu.add(mergeMenuItem);

        exitMenuItem.setText("Exit");
        exitMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                exitMenuItemActionPerformed(evt);
            }
        });

        fileMenu.add(exitMenuItem);

        menuBar.add(fileMenu);

        setJMenuBar(menuBar);

        pack();
    }//GEN-END:initComponents

    private void merge(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_merge
        // TODO add your handling code here:
        if (chooser == null) {
            chooser = new JFileChooser();
        }
        chooser.setDialogType(JFileChooser.OPEN_DIALOG);
        if (file1 != null) {
            chooser.setSelectedFile(file1);
        }
        if (chooser.showDialog(this, "Open File 1") == JFileChooser.APPROVE_OPTION) {
            file1 = chooser.getSelectedFile();
            if (file2 != null) {
                chooser.setSelectedFile(file2);
            }
            if (chooser.showDialog(this, "Open File 2") == JFileChooser.APPROVE_OPTION) {
                file2 = chooser.getSelectedFile();
                new BackgroundTask() {
                    @Override
                    public void construct() throws IOException {
                        merge(file1, file2);
                    }

                    @Override
                    public void failed(Throwable result) {
                        result.printStackTrace();
                        JOptionPane.showMessageDialog(AnimMerger.this, "Error merging files
" + result, "AnimMerger", JOptionPane.ERROR_MESSAGE); } @Override public void done() { JOptionPane.showMessageDialog(AnimMerger.this, "Done", "AnimMerger", JOptionPane.INFORMATION_MESSAGE); } }.start(); } } }//GEN-LAST:event_merge private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exitMenuItemActionPerformed System.exit(0); }//GEN-LAST:event_exitMenuItemActionPerformed /** * @param args the command line arguments */ public static void main(String args[]) { JFrame f = new AnimMerger(); f.setSize(400, 300); f.setVisible(true); } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JMenuItem exitMenuItem; private javax.swing.JMenu fileMenu; private javax.swing.JMenuBar menuBar; private javax.swing.JMenuItem mergeMenuItem; // End of variables declaration//GEN-END:variables }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy