| /* |
| * Copyright 2006 ProductiveMe Inc. |
| * Copyright 2013 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.pme.exe; |
| |
| import com.pme.util.BitsUtil; |
| import com.pme.util.OffsetTrackingInputStream; |
| |
| import java.io.*; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.lang.reflect.Array; |
| |
| /** |
| * Date: Mar 31, 2006 |
| * Time: 6:17:24 PM |
| */ |
| public abstract class Bin { |
| private String myName; |
| private String myDescription; |
| private long myOffset = 0; |
| ArrayList<Bin.Value> myOffsetHolders = new ArrayList<Bin.Value>(0); |
| ArrayList<Bin.Value> mySizeHolders = new ArrayList<Bin.Value>(0); |
| |
| public Bin(String name) { |
| myName = name; |
| } |
| |
| public String getName() { |
| return myName; |
| } |
| |
| public void setName(String name) { |
| myName = name; |
| } |
| |
| public String getDescription() { |
| if (myDescription != null) { |
| return myDescription; |
| } |
| return myName; |
| } |
| |
| public long getOffset() { |
| return myOffset; |
| } |
| |
| public void resetOffsets(long offset) { |
| myOffset = offset; |
| updateSizeOffsetHolders(); |
| } |
| |
| protected void updateSizeOffsetHolders() { |
| for (Value holder : myOffsetHolders) { |
| holder.setValue(myOffset); |
| } |
| for (Value holder : mySizeHolders) { |
| holder.setValue(sizeInBytes()); |
| } |
| } |
| |
| public void copyFrom(Bin value) { |
| } |
| |
| public void addOffsetHolder(Value offsetHolder) { |
| myOffsetHolders.add( offsetHolder ); |
| } |
| |
| public void addSizeHolder(Value sizeHolder) { |
| mySizeHolders.add( sizeHolder ); |
| } |
| |
| public abstract long sizeInBytes(); |
| |
| public void setDescription(String description) { |
| myDescription = description; |
| } |
| |
| public abstract void read(DataInput stream) throws IOException; |
| |
| public abstract void write(DataOutput stream) throws IOException; |
| |
| public abstract void report(OutputStreamWriter writer) throws IOException; |
| |
| public static class Comment extends Bin { |
| public Comment(String comment) { |
| super(comment); |
| } |
| |
| public long sizeInBytes() { |
| return 0; |
| } |
| |
| public void read(DataInput stream) throws IOException { |
| } |
| |
| public void write(DataOutput stream) throws IOException { |
| } |
| |
| public void report(OutputStreamWriter writer) throws IOException { |
| _report(writer, getName()); |
| } |
| } |
| |
| public static class Structure extends Bin { |
| private ArrayList<Bin> myMembers = new ArrayList<Bin>(1); |
| private HashMap<String, Bin> myMembersMap = new HashMap<String, Bin>(1); |
| |
| public Structure(String name) { |
| super(name); |
| } |
| |
| public void resetOffsets(long newOffset) { |
| super.resetOffsets(newOffset); |
| long offset = getOffset(); |
| for (Bin bin : myMembers) { |
| bin.resetOffsets(offset); |
| offset = offset + bin.sizeInBytes(); |
| } |
| updateSizeOffsetHolders(); |
| } |
| |
| public void copyFrom(Bin binStructure) { |
| Bin.Structure structure = (Bin.Structure)binStructure; |
| ArrayList<Bin> members = structure.getMembers(); |
| for (Bin bin : members) { |
| Bin valueMember = getMember(bin.getName()); |
| if (valueMember != null) { |
| valueMember.copyFrom(bin); |
| } |
| } |
| } |
| |
| public long sizeInBytes() { |
| long size = 0; |
| for (Bin bin : myMembers) { |
| size += bin.sizeInBytes(); |
| } |
| return size; |
| } |
| |
| public ArrayList<Bin> getMembers() { |
| return myMembers; |
| } |
| public void addMember(Bin bin, String description) { |
| bin.setDescription(description); |
| addMember(bin); |
| } |
| |
| public void addComment(String message) { |
| myMembers.add(new Comment(message)); |
| } |
| |
| public Bin insertMember(int index, Bin bin) { |
| ArrayList<Bin> list = new ArrayList<Bin>( myMembers.size() + 1 ); |
| for ( int i = 0; i < index; ++i ){ |
| list.add( myMembers.get( i ) ); |
| } |
| list.add( bin ); |
| for ( int i = index; i < myMembers.size(); ++i ){ |
| list.add( myMembers.get( i ) ); |
| } |
| myMembers = list; |
| addMemberToMapOnly(bin); |
| return bin; |
| } |
| |
| public Bin addMember(Bin bin) { |
| myMembers.add(bin); |
| addMemberToMapOnly(bin); |
| return bin; |
| } |
| |
| //such members are not read by framework |
| //it is read by parent in 'read' overrided method |
| public void addMemberToMapOnly(Bin bin) { |
| myMembersMap.put(bin.getName(), bin); |
| } |
| |
| public Bin getMember(String name) { |
| return myMembersMap.get(name); |
| } |
| |
| public Bin.Value getValueMember(String name) { |
| return (Bin.Value) myMembersMap.get(name); |
| } |
| |
| public Bin.Structure getStructureMember(String name) { |
| return (Bin.Structure) myMembersMap.get(name); |
| } |
| |
| public long getValue(String name) { |
| return ((Bin.Value) myMembersMap.get(name)).getValue(); |
| } |
| |
| public void read(DataInput stream) throws IOException { |
| for (Bin bin : myMembers) { |
| bin.read(stream); |
| } |
| } |
| |
| public void write(DataOutput stream) throws IOException { |
| for (Bin bin : myMembers) { |
| bin.write(stream); |
| } |
| } |
| |
| public void report(OutputStreamWriter writer) throws IOException { |
| _report(writer, "--- '" + getName() + "' Structure --- size = " + sizeInBytes()); |
| _report(writer, "{Offset = " + Long.toHexString(getOffset()) + "}"); |
| for (Bin bin : myMembers) { |
| bin.report(writer); |
| } |
| _report(writer, "--- End Of '" + getName() + "' Structure ---"); |
| } |
| } |
| |
| public abstract static class Value extends Bin { |
| private long myValue; |
| |
| public Value(String name) { |
| super(name); |
| } |
| |
| public abstract long getValue(); |
| public abstract Value setValue(long value); |
| |
| public void copyFrom(Bin value) { |
| setValue(((Value)value).getValue()); |
| } |
| |
| public long getRawValue() { |
| return myValue; |
| } |
| |
| public Value setRawValue(long value) { |
| myValue = value; |
| return this; |
| } |
| } |
| |
| public static class Byte extends Value { |
| public Byte(String name) { |
| super(name); |
| } |
| |
| public long sizeInBytes() { |
| return 1; |
| } |
| |
| public long getValue() { |
| return getRawValue(); |
| } |
| public Value setValue(long value) { |
| setRawValue(value); |
| return this; |
| } |
| |
| public void read(DataInput stream) throws IOException { |
| setRawValue(stream.readByte()); |
| } |
| |
| public void write(DataOutput stream) throws IOException { |
| stream.writeByte((byte) getRawValue()); |
| } |
| |
| public void report(OutputStreamWriter writer) throws IOException { |
| _report(writer, getDescription(), (byte) getValue()); |
| } |
| |
| } |
| |
| public static class Word extends Value { |
| public Word() { |
| super(""); |
| } |
| |
| public Word(String name) { |
| super(name); |
| } |
| |
| public long sizeInBytes() { |
| return 2; |
| } |
| |
| public long getValue() { |
| return BitsUtil.revertBytesOfShort((short) super.getRawValue()); |
| } |
| |
| public Value setValue(long value) { |
| setRawValue(BitsUtil.revertBytesOfShort((short) value)); |
| return this; |
| } |
| |
| public void read(DataInput stream) throws IOException { |
| setRawValue(stream.readShort()); |
| } |
| |
| public void write(DataOutput stream) throws IOException { |
| stream.writeShort((short) getRawValue()); |
| } |
| |
| public void report(OutputStreamWriter writer) throws IOException { |
| short sh = (short) getValue(); |
| _report(writer, getDescription(), sh); |
| } |
| public String toString() { |
| return BitsUtil.shortToHexString( (int)getValue() ); |
| } |
| } |
| |
| public static class DWord extends Value { |
| public DWord(String name) { |
| super(name); |
| } |
| |
| public long sizeInBytes() { |
| return 4; |
| } |
| |
| public Value setValue(long value) { |
| setRawValue(BitsUtil.revertBytesOfInt((int) value)); |
| return this; |
| } |
| |
| public long getValue() { |
| return BitsUtil.revertBytesOfInt((int) super.getRawValue()); |
| } |
| |
| public void read(DataInput stream) throws IOException { |
| setRawValue(stream.readInt()); |
| } |
| |
| public void write(DataOutput stream) throws IOException { |
| stream.writeInt((int) getRawValue()); |
| } |
| |
| public void report(OutputStreamWriter writer) throws IOException { |
| _report(writer, getDescription(), (int) getValue()); |
| } |
| public String toString() { |
| return BitsUtil.intToHexString( (int)getValue() ); |
| } |
| } |
| |
| public static class LongLong extends Value { |
| public LongLong(String name) { |
| super(name); |
| } |
| |
| @Override |
| public long getValue() { |
| return BitsUtil.revertBytesOfLong(getRawValue()); |
| } |
| |
| @Override |
| public Value setValue(long value) { |
| setRawValue(BitsUtil.revertBytesOfLong(value)); |
| return this; |
| } |
| |
| @Override |
| public long sizeInBytes() { |
| return 8; |
| } |
| |
| @Override |
| public void read(DataInput stream) throws IOException { |
| setRawValue(stream.readLong()); |
| } |
| |
| @Override |
| public void write(DataOutput stream) throws IOException { |
| stream.writeLong(getRawValue()); |
| } |
| |
| @Override |
| public void report(OutputStreamWriter writer) throws IOException { |
| _report(writer, getDescription() + " : " + Long.toHexString(getValue())); |
| } |
| } |
| |
| public static class Padding extends Bin { |
| private int myBytes; |
| |
| public Padding(int bytes) { |
| super("Padding"); |
| myBytes = bytes; |
| } |
| |
| @Override |
| public long sizeInBytes() { |
| return bytesToSkip(getOffset()); |
| } |
| |
| @Override |
| public void read(DataInput stream) throws IOException { |
| if (stream instanceof OffsetTrackingInputStream) { |
| long offset = ((OffsetTrackingInputStream) stream).getOffset(); |
| int skip = bytesToSkip(offset); |
| if (skip > 0) { |
| stream.skipBytes(skip); |
| } |
| } |
| } |
| |
| private int bytesToSkip(long offset) { |
| int offsetMask = myBytes-1; |
| long offsetBits = offset & offsetMask; |
| return offsetBits == 0 ? 0 : (int) (myBytes - offsetBits); |
| } |
| |
| @Override |
| public void write(DataOutput stream) throws IOException { |
| int skip = bytesToSkip(getOffset()); |
| for (int i = 0; i < skip; i++) { |
| stream.writeByte(0); |
| } |
| } |
| |
| @Override |
| public void report(OutputStreamWriter writer) throws IOException { |
| } |
| } |
| |
| public static class Txt extends Bin { |
| private StringBuffer myBuffer = new StringBuffer(); |
| private Bin.Value mySize; |
| private byte[] myBytes; |
| |
| public Txt(String name, byte[] bytes) { |
| super(name); |
| myBytes = bytes; |
| mySize = new DWord("").setValue(bytes.length); |
| setValue(); |
| } |
| |
| public Txt(String name, String string) { |
| super(name); |
| myBytes = new byte[string.length() * 2]; |
| byte[] bytes = string.getBytes(); |
| for (int i = 0; i < bytes.length; ++i) { |
| myBytes[i * 2] = bytes[i]; |
| myBytes[i * 2 + 1] = 0; |
| } |
| mySize = new DWord("").setValue(myBytes.length); |
| setValue(); |
| } |
| |
| public Txt(String name, Bin.Value size) { |
| super(name); |
| mySize = size; |
| } |
| |
| public Txt(String name, int size) { |
| this(name, new DWord("size").setValue(size)); |
| } |
| |
| public String getText() { |
| return myBuffer.toString(); |
| } |
| |
| public long sizeInBytes() { |
| return mySize.getValue(); |
| } |
| |
| private void setValue(){ |
| for (int i = 0; i < mySize.getValue(); ++i) { |
| int b = BitsUtil.unsignedByte(myBytes[i]); |
| if (b != 0) { |
| myBuffer.append((char) b); |
| } |
| } |
| } |
| |
| public void read(DataInput stream) throws IOException { |
| myBuffer.setLength(0); |
| myBytes = new byte[(int) mySize.getValue()]; |
| for (int i = 0; i < mySize.getValue(); ++i) { |
| myBytes[i] = stream.readByte(); |
| } |
| setValue(); |
| } |
| |
| public void write(DataOutput stream) throws IOException { |
| stream.write(myBytes); |
| } |
| |
| public void report(OutputStreamWriter writer) throws IOException { |
| _report(writer, myBuffer.toString()); |
| } |
| } |
| |
| public static class WChar extends Bin { |
| private String myValue; |
| |
| public WChar(String name) { |
| super(name); |
| } |
| |
| @Override |
| public long sizeInBytes() { |
| return myValue.length() * 2 + 2; |
| } |
| |
| @Override |
| public void read(DataInput stream) throws IOException { |
| StringBuilder valueBuilder = new StringBuilder(); |
| while(true) { |
| char c = BitsUtil.readChar(stream); |
| if (c == 0) break; |
| valueBuilder.append(c); |
| } |
| myValue = valueBuilder.toString(); |
| } |
| |
| @Override |
| public void write(DataOutput stream) throws IOException { |
| for (int i = 0; i < myValue.length(); i++) { |
| stream.writeShort(BitsUtil.revertBytesOfShort((short) myValue.charAt(i))); |
| } |
| stream.writeShort(0); |
| } |
| |
| @Override |
| public void report(OutputStreamWriter writer) throws IOException { |
| _report(writer, myValue); |
| } |
| |
| public String getValue() { |
| return myValue; |
| } |
| |
| public void setValue(String value) { |
| myValue = value; |
| } |
| } |
| |
| public static class Bytes extends Bin { |
| private byte[] myBytes; |
| private Value myStartOffset; |
| private Value mySize; |
| private int myBytesInRow = 16; |
| |
| public Bytes(String name, Bin.Value size) { |
| super(name); |
| mySize = size; |
| } |
| |
| public Bytes(String name, long size) { |
| super(name); |
| mySize = new DWord("size").setValue(size); |
| } |
| |
| public Bytes(String name, Bin.Value startOffset, Bin.Value size) { |
| super(name); |
| reset(startOffset, size); |
| } |
| |
| public void reset(int startOffset, int size) { |
| reset(new DWord("startOffset").setValue(startOffset), new DWord("size").setValue(size)); |
| } |
| |
| public void reset(Bin.Value startOffset, Bin.Value size) { |
| myStartOffset = startOffset; |
| mySize = size; |
| } |
| |
| public byte[] getBytes() { |
| return myBytes; |
| } |
| |
| public void setBytes(byte[] bytes) { |
| setBytes(bytes, bytes.length); |
| } |
| |
| public void setBytes(byte[] bytes, int size) { |
| if (bytes.length < size) { |
| throw new RuntimeException("bytes.length < size"); |
| } |
| myBytes = bytes; |
| mySize.setValue(size); |
| } |
| |
| public long sizeInBytes() { |
| return mySize.getValue(); |
| } |
| |
| public void read(DataInput stream) throws IOException { |
| if (myStartOffset != null) { |
| RandomAccessFile file = (RandomAccessFile) stream; |
| file.seek(myStartOffset.getValue()); |
| } |
| myBytes = new byte[(int) mySize.getValue()]; |
| stream.readFully(myBytes); |
| } |
| |
| public void write(DataOutput stream) throws IOException { |
| stream.write(myBytes, 0, (int) sizeInBytes()); |
| } |
| |
| private StringBuffer myBuffer = new StringBuffer(); |
| |
| public void report(OutputStreamWriter writer) throws IOException { |
| _report(writer, getName()); |
| _report(writer, "Number of bytes: " + mySize.getValue()); |
| int rowCount = (myBytes.length / myBytesInRow); |
| if (myBytes.length % myBytesInRow != 0) { |
| rowCount++; |
| } |
| int byteCount = 0; |
| for (int i = 0; i < rowCount; i++) { |
| myBuffer.setLength(0); |
| myBuffer.append("\n"); |
| for (int j = 0; j < myBytesInRow && byteCount < myBytes.length; j++) { |
| byte aByte = myBytes[byteCount++]; |
| myBuffer.append(" ").append(BitsUtil.byteToHexString(aByte)); |
| } |
| writer.write(myBuffer.toString()); |
| } |
| } |
| } |
| |
| public static class ArrayOfBins<T extends Bin> extends Bin { |
| private Bin[] myValues; |
| private Bin.Value mySize; |
| private Class myClass; |
| private Bin.Value myCountHolder = null; |
| |
| public ArrayOfBins(String name, Class<T> cl, Bin.Value size) { |
| super(name); |
| myClass = cl; |
| mySize = size; |
| init(); |
| } |
| |
| public ArrayOfBins(String name, Class<T> cl, int size) { |
| this(name, cl, new DWord("size").setValue(size)); |
| } |
| |
| public void addBin( Bin bin ){ |
| Bin[] newArray = new Bin[myValues.length+1]; |
| System.arraycopy( myValues, 0, newArray, 0, myValues.length ); |
| newArray[myValues.length] = bin; |
| myValues = newArray; |
| mySize.setValue( mySize.getValue() + 1 ); |
| } |
| |
| public void copyFrom(Bin bin) { |
| ArrayOfBins value = (ArrayOfBins)bin; |
| for (int i = 0; i < myValues.length; i++) { |
| myValues[i].copyFrom( value.get(i) ); |
| } |
| } |
| |
| public void setCountHolder(Value countHolder) { |
| myCountHolder = countHolder; |
| } |
| |
| private void init() { |
| myValues = (Bin[]) Array.newInstance(myClass, (int) mySize.getValue()); |
| |
| for (int i = 0; i < myValues.length; i++) { |
| try { |
| Bin bin = (Bin) myClass.newInstance(); |
| bin.setName("[" + i + "]"); |
| myValues[i] = bin; |
| } catch (InstantiationException e) { |
| throw new RuntimeException(e.getMessage()); |
| } catch (IllegalAccessException e) { |
| throw new RuntimeException(e.getMessage()); |
| } |
| } |
| } |
| |
| public void resetOffsets(long newOffset) { |
| super.resetOffsets(newOffset); |
| long offset = getOffset(); |
| if (myCountHolder != null) { |
| myCountHolder.setValue(myValues.length); |
| } |
| for (Bin bin : myValues) { |
| bin.resetOffsets(offset); |
| offset = offset + bin.sizeInBytes(); |
| } |
| } |
| |
| public int size() { |
| return myValues.length; |
| } |
| |
| public Bin[] getArray() { |
| return myValues; |
| } |
| |
| public T get(int index) { |
| //noinspection unchecked |
| return (T) myValues[index]; |
| } |
| |
| public long sizeInBytes() { |
| int size = 0; |
| for (Bin value : myValues) { |
| size += value.sizeInBytes(); |
| } |
| return size; |
| } |
| |
| public void read(DataInput stream) throws IOException { |
| init(); |
| for (Bin value : myValues) { |
| value.read(stream); |
| } |
| } |
| |
| public void write(DataOutput stream) throws IOException { |
| for (Bin value : myValues) { |
| value.write(stream); |
| } |
| } |
| |
| public void report(OutputStreamWriter writer) throws IOException { |
| writer.write("\n" + "Array size: " + myValues.length); |
| for (Bin value : myValues) { |
| value.report(writer); |
| } |
| } |
| } |
| |
| protected void _report(OutputStreamWriter buffer, String name, int value) throws IOException { |
| buffer.write("\n" + name + " : " + BitsUtil.intToHexString(value)); |
| } |
| |
| protected void _report(OutputStreamWriter buffer, String name, short value) throws IOException { |
| buffer.write("\n" + name + " : " + BitsUtil.shortToHexString(value)); |
| } |
| |
| protected void _report(OutputStreamWriter buffer, String name, byte value) throws IOException { |
| buffer.write("\n" + name + " : " + BitsUtil.byteToHexString(value)); |
| } |
| |
| protected void _report(OutputStreamWriter buffer, String message) throws IOException { |
| buffer.write("\n" + message); |
| } |
| } |