ResourceParser.java 11.2 KB
/*
 * Decompiled with CFR 0_118.
 */
package com.adobe.internal.mac.resource;

import com.adobe.internal.io.CountingInputStream;
import com.adobe.internal.io.ExtendedDataInputStream;
import com.adobe.internal.io.RangedInputStream;
import com.adobe.internal.mac.resource.ScriptUtility;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

public class ResourceParser {
    private static final boolean DEBUG = false;
    private URL url;
    private List resourceTypes;
    private List resources;
    private List resourceNames;
    private Map resourceHandlers = new HashMap();

    public void setURL(URL url) {
        this.url = url;
    }

    public void addHandler(ResourceHandler handler) {
        this.resourceHandlers.put(new ResouceHandlerKey(handler.getResourceType()), handler);
    }

    private void init() {
        this.resourceTypes = new ArrayList();
        this.resources = new LinkedList();
        this.resourceNames = new LinkedList();
    }

    public void parse() throws IOException {
        int typeIndex;
        this.init();
        InputStream is = this.url.openStream();
        CountingInputStream cis = new CountingInputStream(is);
        ExtendedDataInputStream dis = new ExtendedDataInputStream(cis);
        long dataOffset = dis.readUnsignedInt();
        long mapOffset = dis.readUnsignedInt();
        long dataLength = dis.readUnsignedInt();
        long mapLength = dis.readUnsignedInt();
        long bytesToSkip = mapOffset - 16 + 22;
        dis.skipFully(bytesToSkip);
        int rsrcForkAttributes = dis.readUnsignedShort();
        int resourceTypeListOffset = dis.readUnsignedShort();
        int resourceNameListOffset = dis.readUnsignedShort();
        int numberOfTypes = dis.readUnsignedShort();
        for (typeIndex = 0; typeIndex <= numberOfTypes; ++typeIndex) {
            byte[] type = new byte[4];
            dis.readFully(type);
            int number = dis.readUnsignedShort();
            short offset = dis.readShort();
            ResourceTypeEntry typeEntry = new ResourceTypeEntry(type, number, offset);
            this.insertIntoList(this.resourceTypes, typeEntry, new OrderInt(){

                @Override
                public int getOrderInt(Object obj) {
                    return ((ResourceTypeEntry)obj).getOffset();
                }
            });
        }
        for (typeIndex = 0; typeIndex <= numberOfTypes; ++typeIndex) {
            ResourceTypeEntry currentType = (ResourceTypeEntry)this.resourceTypes.get(typeIndex);
            for (int resourceEntry = 0; resourceEntry <= currentType.getNumberOfEntries(); ++resourceEntry) {
                int id = dis.readUnsignedShort();
                short resourceNameOffset = dis.readShort();
                byte attributes = dis.readByte();
                int resourceDataOffset = dis.readUnsigned3ByteInt();
                dis.readInt();
                ResourceEntry resource = new ResourceEntry(currentType.getResourceType(), id, attributes, resourceDataOffset, resourceNameOffset);
                currentType.addResource(resource);
                this.insertIntoList(this.resources, resource, new OrderInt(){

                    @Override
                    public int getOrderInt(Object obj) {
                        return ((ResourceEntry)obj).getDataOffset();
                    }
                });
                this.insertIntoList(this.resourceNames, resource, new OrderInt(){

                    @Override
                    public int getOrderInt(Object obj) {
                        return ((ResourceEntry)obj).getNameOffset();
                    }
                });
            }
        }
        for (int resourceIndex = 0; resourceIndex < this.resourceNames.size(); ++resourceIndex) {
            ResourceEntry currentResource = (ResourceEntry)this.resourceNames.get(resourceIndex);
            int length = dis.readUnsignedByte();
            byte[] nameBytes = new byte[length];
            dis.readFully(nameBytes);
            currentResource.setNameBytes(nameBytes);
        }
        dis.close();
        dis = null;
        cis.close();
        cis = null;
        is.close();
        is = null;
        InputStream dataIS = this.url.openStream();
        CountingInputStream dataCIS = new CountingInputStream(dataIS);
        ExtendedDataInputStream dataDIS = new ExtendedDataInputStream(dataCIS);
        dataDIS.skipFully(dataOffset);
        for (int resourceIndex2 = 0; resourceIndex2 < this.resources.size(); ++resourceIndex2) {
            ResourceEntry currentResource = (ResourceEntry)this.resources.get(resourceIndex2);
            long dataToSkip = (long)currentResource.getDataOffset() + dataOffset - dataCIS.getOffset();
            dataDIS.skipFully(dataToSkip);
            long length = dataDIS.readUnsignedInt();
            RangedInputStream subStream = new RangedInputStream(dataDIS, length);
            ResourceHandler handler = (ResourceHandler)this.resourceHandlers.get(new ResouceHandlerKey(currentResource.getType()));
            if (handler == null) continue;
            handler.handleResource(currentResource, length, subStream);
        }
    }

    private void dumpResourceList() {
        ListIterator iter = this.resources.listIterator();
        System.out.println("Resources");
        System.out.println("===============");
        while (iter.hasNext()) {
            ResourceEntry entry = (ResourceEntry)iter.next();
            System.out.println(entry);
            System.out.println("---------------");
        }
    }

    private void dumpResourceTypeList() {
        ListIterator iter = this.resourceTypes.listIterator();
        System.out.println("Resource Types");
        System.out.println("===============");
        while (iter.hasNext()) {
            ResourceTypeEntry entry = (ResourceTypeEntry)iter.next();
            System.out.println(entry);
            System.out.println("---------------");
        }
    }

    void insertIntoList(List list, Object entry, OrderInt order) {
        if (order.getOrderInt(entry) == -1) {
            return;
        }
        if (list.isEmpty()) {
            list.add(entry);
        } else {
            boolean added = false;
            ListIterator<Object> iter = list.listIterator();
            while (iter.hasNext()) {
                Object current = iter.next();
                if (order.getOrderInt(entry) >= order.getOrderInt(current)) continue;
                iter.previous();
                iter.add(entry);
                added = true;
                break;
            }
            if (!added) {
                list.add(entry);
            }
        }
    }

    static interface OrderInt {
        public int getOrderInt(Object var1);
    }

    private static final class ResouceHandlerKey {
        private final byte[] type;

        public ResouceHandlerKey(byte[] type) {
            this.type = type;
        }

        public int hashCode() {
            int prime = 31;
            int result = 1;
            for (int index = 0; index < this.type.length; ++index) {
                result = 31 * result + this.type[index];
            }
            return result;
        }

        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null) {
                return false;
            }
            if (!(obj instanceof ResouceHandlerKey)) {
                return false;
            }
            ResouceHandlerKey other = (ResouceHandlerKey)obj;
            if (!Arrays.equals(this.type, other.type)) {
                return false;
            }
            return true;
        }
    }

    public static interface ResourceHandler {
        public void handleResource(ResourceEntry var1, long var2, InputStream var4);

        public byte[] getResourceType();
    }

    public static class ResourceEntry {
        private byte[] type;
        private int id;
        private byte attributes;
        private int dataOffset;
        private int nameOffset;
        private String name;
        private byte[] nameBytes;
        private int script;

        protected ResourceEntry(byte[] type, int id, byte attributes, int dataOffset, int nameOffset) {
            this.type = type;
            this.id = id;
            this.attributes = attributes;
            this.dataOffset = dataOffset;
            this.nameOffset = nameOffset;
        }

        public int getDataOffset() {
            return this.dataOffset;
        }

        public int getNameOffset() {
            return this.nameOffset;
        }

        public String toString() {
            StringBuilder sb = new StringBuilder("type = ");
            sb.append(new String(this.type)).append("\n").append("id = ").append(this.id).append("\n").append("attributes = ").append(this.attributes).append("\n").append("data offset = ").append(this.dataOffset).append("\n").append("name offset = ").append(this.nameOffset).append("\n").append("name = ").append(this.name);
            return sb.toString();
        }

        protected void setNameBytes(byte[] nameBytes) {
            this.nameBytes = nameBytes;
            this.generateName();
        }

        private void generateName() {
            this.script = ScriptUtility.scriptCodeFromRsrcID(this.id);
            try {
                this.name = new String(this.nameBytes, ScriptUtility.scriptCodeToCharset(this.script));
            }
            catch (UnsupportedEncodingException e) {
                // empty catch block
            }
        }

        public byte[] getType() {
            return this.type;
        }

        public byte getAttributes() {
            return this.attributes;
        }

        public byte[] getNameBytes() {
            return this.nameBytes;
        }

        public String getName() {
            return this.name;
        }

        public int getScriptCode() {
            return this.script;
        }

        public int getID() {
            return this.id;
        }
    }

    private static class ResourceTypeEntry {
        private byte[] type;
        private int numberOfEntries;
        private List resources;
        private int offset;

        public ResourceTypeEntry(byte[] type, int number, int offset) {
            this.type = type;
            this.numberOfEntries = number;
            this.resources = new ArrayList(this.numberOfEntries + 1);
            this.offset = offset;
        }

        public int getNumberOfEntries() {
            return this.numberOfEntries;
        }

        public byte[] getResourceType() {
            return this.type;
        }

        public int getOffset() {
            return this.offset;
        }

        public void addResource(ResourceEntry resource) {
            this.resources.add(resource);
        }

        public String toString() {
            StringBuilder sb = new StringBuilder("type = ");
            sb.append(new String(this.type)).append("\n").append("number of entries = ").append(this.numberOfEntries).append("\n").append("offset = ").append(this.offset).append("\n");
            return sb.toString();
        }
    }

}