version info reader implemented
[idea/community.git] / tools / launcher-generator / src / com / pme / exe / Bin.java
1 /*
2  * Copyright 2006 ProductiveMe Inc.
3  * Copyright 2013 JetBrains s.r.o.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 package com.pme.exe;
19
20 import com.pme.util.BitsUtil;
21 import com.pme.util.OffsetTrackingInputStream;
22
23 import java.io.*;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.lang.reflect.Array;
27
28 /**
29  * Date: Mar 31, 2006
30  * Time: 6:17:24 PM
31  */
32 public abstract class Bin {
33   private String myName;
34   private String myDescription;
35   private long myOffset = 0;
36   ArrayList<Bin.Value> myOffsetHolders = new ArrayList<Bin.Value>(0);
37   ArrayList<Bin.Value> mySizeHolders = new ArrayList<Bin.Value>(0);
38
39   public Bin(String name) {
40     myName = name;
41   }
42
43   public String getName() {
44     return myName;
45   }
46
47   public void setName(String name) {
48     myName = name;
49   }
50
51   public String getDescription() {
52     if (myDescription != null) {
53       return myDescription;
54     }
55     return myName;
56   }
57
58   public long getOffset() {
59     return myOffset;
60   }
61
62   public void resetOffsets(long offset) {
63     myOffset = offset;
64     for (Value holder : myOffsetHolders) {
65       holder.setValue(offset);
66     }
67     for (Value holder : mySizeHolders) {
68       holder.setValue(sizeInBytes());
69     }
70   }
71
72   public void copyFrom(Bin value) {
73   }
74
75   public void addOffsetHolder(Value offsetHolder) {
76     myOffsetHolders.add( offsetHolder );
77   }
78
79   public void addSizeHolder(Value sizeHolder) {
80     mySizeHolders.add( sizeHolder );
81   }
82
83   public abstract long sizeInBytes();
84
85   public void setDescription(String description) {
86     myDescription = description;
87   }
88
89   public abstract void read(DataInput stream) throws IOException;
90
91   public abstract void write(DataOutput stream) throws IOException;
92
93   public abstract void report(OutputStreamWriter writer) throws IOException;
94
95   public static class Comment extends Bin {
96     public Comment(String comment) {
97       super(comment);
98     }
99
100     public long sizeInBytes() {
101       return 0;
102     }
103
104     public void read(DataInput stream) throws IOException {
105     }
106
107     public void write(DataOutput stream) throws IOException {
108     }
109
110     public void report(OutputStreamWriter writer) throws IOException {
111       _report(writer, getName());
112     }
113   }
114
115   public static class Structure extends Bin {
116     private ArrayList<Bin> myMembers = new ArrayList<Bin>(1);
117     private HashMap<String, Bin> myMembersMap = new HashMap<String, Bin>(1);
118
119     public Structure(String name) {
120       super(name);
121     }
122
123     public void resetOffsets(long newOffset) {
124       super.resetOffsets(newOffset);
125       long offset = getOffset();
126       for (Bin bin : myMembers) {
127         bin.resetOffsets(offset);
128         offset = offset + bin.sizeInBytes();
129       }
130     }
131
132     public void copyFrom(Bin binStructure) {
133       Bin.Structure structure = (Bin.Structure)binStructure;
134       ArrayList<Bin> members = structure.getMembers();
135       for (Bin bin : members) {
136         Bin valueMember = getMember(bin.getName());
137         if (valueMember != null) {
138           valueMember.copyFrom(bin);
139         }
140       }
141     }
142
143     public long sizeInBytes() {
144       long size = 0;
145       for (Bin bin : myMembers) {
146         size += bin.sizeInBytes();
147       }
148       return size;
149     }
150
151     public ArrayList<Bin> getMembers() {
152       return myMembers;
153     }
154     public void addMember(Bin bin, String description) {
155       bin.setDescription(description);
156       addMember(bin);
157     }
158
159     public void addComment(String message) {
160       myMembers.add(new Comment(message));
161     }
162
163     public Bin insertMember(int index, Bin bin) {
164       ArrayList<Bin> list = new ArrayList<Bin>( myMembers.size() + 1 );
165       for ( int i = 0; i < index; ++i ){
166         list.add( myMembers.get( i ) );
167       }
168       list.add( bin );
169       for ( int i = index; i < myMembers.size(); ++i ){
170         list.add( myMembers.get( i ) );
171       }
172       myMembers = list;
173       addMemberToMapOnly(bin);
174       return bin;
175     }
176
177     public Bin addMember(Bin bin) {
178       myMembers.add(bin);
179       addMemberToMapOnly(bin);
180       return bin;
181     }
182
183     //such members are not read by framework
184     //it is read by parent in 'read' overrided method
185     public void addMemberToMapOnly(Bin bin) {
186       myMembersMap.put(bin.getName(), bin);
187     }
188
189     public Bin getMember(String name) {
190       return (Bin) myMembersMap.get(name);
191     }
192
193     public Bin.Value getValueMember(String name) {
194       return (Bin.Value) myMembersMap.get(name);
195     }
196
197     public Bin.Structure getStructureMember(String name) {
198       return (Bin.Structure) myMembersMap.get(name);
199     }
200
201     public long getValue(String name) {
202       return ((Bin.Value) myMembersMap.get(name)).getValue();
203     }
204
205     public void read(DataInput stream) throws IOException {
206       for (Bin bin : myMembers) {
207         bin.read(stream);
208       }
209     }
210
211     public void write(DataOutput stream) throws IOException {
212       for (Bin bin : myMembers) {
213         bin.write(stream);
214       }
215     }
216
217     public void report(OutputStreamWriter writer) throws IOException {
218       _report(writer, "--- '" + getName() + "' Structure --- size = " + sizeInBytes());
219       _report(writer, "{Offset = " + Long.toHexString(getOffset()) + "}");
220       for (Bin bin : myMembers) {
221         bin.report(writer);
222       }
223       _report(writer, "--- End Of '" + getName() + "' Structure ---");
224     }
225   }
226
227   public abstract static class Value extends Bin {
228     private long myValue;
229
230     public Value(String name) {
231       super(name);
232     }
233
234     public abstract long getValue();
235     public abstract Value setValue(long value);
236
237     public void copyFrom(Bin value) {
238       setValue(((Value)value).getValue());
239     }
240
241     public long getRawValue() {
242       return myValue;
243     }
244
245     public Value setRawValue(long value) {
246       myValue = value;
247       return this;
248     }
249
250     public Value setValue(short value) {
251       myValue = BitsUtil.revertBytesOfShort(value);
252       return this;
253     }
254   }
255
256   public static class Byte extends Value {
257     public Byte(String name) {
258       super(name);
259     }
260
261     public long sizeInBytes() {
262       return 1;
263     }
264
265     public long getValue() {
266       return getRawValue();
267     }
268     public Value setValue(long value) {
269       setRawValue(value);
270       return this;
271     }
272
273     public void read(DataInput stream) throws IOException {
274       setRawValue(stream.readByte());
275     }
276
277     public void write(DataOutput stream) throws IOException {
278       stream.writeByte((byte) getRawValue());
279     }
280
281     public void report(OutputStreamWriter writer) throws IOException {
282       _report(writer, getDescription(), (byte) getValue());
283     }
284
285   }
286
287   public static class Word extends Value {
288     public Word() {
289       super("");
290     }
291
292     public Word(String name) {
293       super(name);
294     }
295
296     public long sizeInBytes() {
297       return 2;
298     }
299
300     public long getValue() {
301       return BitsUtil.revertBytesOfShort((short) super.getRawValue());
302     }
303
304     public Value setValue(long value) {
305       setRawValue(BitsUtil.revertBytesOfShort((short) value));
306       return this;
307     }
308
309     public void read(DataInput stream) throws IOException {
310       setRawValue(stream.readShort());
311     }
312
313     public void write(DataOutput stream) throws IOException {
314       stream.writeShort((short) getRawValue());
315     }
316
317     public void report(OutputStreamWriter writer) throws IOException {
318       short sh = (short) getValue();
319       _report(writer, getDescription(), sh);
320     }
321     public String toString() {
322       return BitsUtil.shortToHexString( (int)getValue() );
323     }
324   }
325
326   public static class DWord extends Value {
327     public DWord(String name) {
328       super(name);
329     }
330
331     public long sizeInBytes() {
332       return 4;
333     }
334
335     public Value setValue(long value) {
336       setRawValue(BitsUtil.revertBytesOfInt((int) value));
337       return this;
338     }
339
340     public long getValue() {
341       return BitsUtil.revertBytesOfInt((int) super.getRawValue());
342     }
343
344     public void read(DataInput stream) throws IOException {
345       setRawValue(stream.readInt());
346     }
347
348     public void write(DataOutput stream) throws IOException {
349       stream.writeInt((int) getRawValue());
350     }
351
352     public void report(OutputStreamWriter writer) throws IOException {
353       _report(writer, getDescription(), (int) getValue());
354     }
355     public String toString() {
356       return BitsUtil.intToHexString( (int)getValue() );
357     }
358   }
359
360   public static class Padding extends Bin {
361     private int myBytes;
362
363     public Padding(int bytes) {
364       super("Padding");
365       myBytes = bytes;
366     }
367
368     @Override
369     public long sizeInBytes() {
370       throw new UnsupportedOperationException();
371     }
372
373     @Override
374     public void read(DataInput stream) throws IOException {
375       if (stream instanceof OffsetTrackingInputStream) {
376         long offset = ((OffsetTrackingInputStream) stream).getOffset();
377         int offsetMask = myBytes-1;
378         long offsetBits = offset & offsetMask;
379         if (offsetBits > 0) {
380           stream.skipBytes((int) (myBytes - offsetBits));
381         }
382       }
383     }
384
385     @Override
386     public void write(DataOutput stream) throws IOException {
387     }
388
389     @Override
390     public void report(OutputStreamWriter writer) throws IOException {
391     }
392   }
393
394   public static class Txt extends Bin {
395     private StringBuffer myBuffer = new StringBuffer();
396     private Bin.Value mySize;
397     private byte[] myBytes;
398
399     public Txt(String name, byte[] bytes) {
400       super(name);
401       myBytes = bytes;
402       mySize = new DWord("").setValue(bytes.length);
403       setValue();
404     }
405
406     public Txt(String name, String string) {
407       super(name);
408       myBytes = new byte[string.length() * 2];
409       byte[] bytes = string.getBytes();
410       for (int i = 0; i < bytes.length; ++i) {
411         myBytes[i * 2] = bytes[i];
412         myBytes[i * 2 + 1] = 0;
413       }
414       mySize = new DWord("").setValue(myBytes.length);
415       setValue();
416     }
417
418     public Txt(String name, Bin.Value size) {
419       super(name);
420       mySize = size;
421     }
422
423     public Txt(String name, int size) {
424       this(name, new DWord("size").setValue(size));
425     }
426
427     public String getText() {
428       return myBuffer.toString();
429     }
430
431     public long sizeInBytes() {
432       return mySize.getValue();
433     }
434
435     private void setValue(){
436       for (int i = 0; i < mySize.getValue(); ++i) {
437         int b = BitsUtil.unsignedByte(myBytes[i]);
438         if (b != 0) {
439           myBuffer.append((char) b);
440         }
441       }
442     }
443
444     public void read(DataInput stream) throws IOException {
445       myBuffer.setLength(0);
446       myBytes = new byte[(int) mySize.getValue()];
447       for (int i = 0; i < mySize.getValue(); ++i) {
448         myBytes[i] = stream.readByte();
449       }
450       setValue();
451     }
452
453     public void write(DataOutput stream) throws IOException {
454       stream.write(myBytes);
455     }
456
457     public void report(OutputStreamWriter writer) throws IOException {
458       _report(writer, myBuffer.toString());
459     }
460   }
461
462   public static class WChar extends Bin {
463     private String myValue;
464
465     public WChar(String name) {
466       super(name);
467     }
468
469     @Override
470     public long sizeInBytes() {
471       throw new UnsupportedOperationException();
472     }
473
474     @Override
475     public void read(DataInput stream) throws IOException {
476       StringBuilder valueBuilder = new StringBuilder();
477       while(true) {
478         char c = BitsUtil.readChar(stream);
479         if (c == 0) break;
480         valueBuilder.append(c);
481       }
482       myValue = valueBuilder.toString();
483     }
484
485     @Override
486     public void write(DataOutput stream) throws IOException {
487       throw new UnsupportedOperationException();
488     }
489
490     @Override
491     public void report(OutputStreamWriter writer) throws IOException {
492       _report(writer, myValue);
493     }
494
495     public String getValue() {
496       return myValue;
497     }
498   }
499
500   public static class Bytes extends Bin {
501     private byte[] myBytes;
502     private Value myStartOffset;
503     private Value mySize;
504     private int myBytesInRow = 16;
505
506     public Bytes(String name, Bin.Value size) {
507       super(name);
508       mySize = size;
509     }
510
511     public Bytes(String name, long size) {
512       super(name);
513       mySize = new DWord("size").setValue(size);
514     }
515
516     public Bytes(String name, Bin.Value startOffset, Bin.Value size) {
517       super(name);
518       reset(startOffset, size);
519     }
520
521     public void reset(int startOffset, int size) {
522       reset(new DWord("startOffset").setValue(startOffset), new DWord("size").setValue(size));
523     }
524
525     public void reset(Bin.Value startOffset, Bin.Value size) {
526       myStartOffset = startOffset;
527       mySize = size;
528     }
529
530     public byte[] getBytes() {
531       return myBytes;
532     }
533
534     public void setBytes(byte[] bytes) {
535       setBytes(bytes, bytes.length);
536     }
537
538     public void setBytes(byte[] bytes, int size) {
539       if (bytes.length < size) {
540         throw new RuntimeException("bytes.length < size");
541       }
542       myBytes = bytes;
543       mySize.setValue(size);
544     }
545
546     public long sizeInBytes() {
547       return mySize.getValue();
548     }
549
550     public void read(DataInput stream) throws IOException {
551       if (myStartOffset != null) {
552         RandomAccessFile file = (RandomAccessFile) stream;
553         file.seek(myStartOffset.getValue());
554       }
555       myBytes = new byte[(int) mySize.getValue()];
556       stream.readFully(myBytes);
557     }
558
559     public void write(DataOutput stream) throws IOException {
560       stream.write(myBytes, 0, (int) sizeInBytes());
561     }
562
563     private StringBuffer myBuffer = new StringBuffer();
564
565     public void report(OutputStreamWriter writer) throws IOException {
566       _report(writer, getName());
567       _report(writer, "Number of bytes: " + mySize.getValue());
568       int rowCount = (myBytes.length / myBytesInRow);
569       if (myBytes.length % myBytesInRow != 0) {
570         rowCount++;
571       }
572       int byteCount = 0;
573       for (int i = 0; i < rowCount; i++) {
574         myBuffer.setLength(0);
575         myBuffer.append("\n");
576         for (int j = 0; j < myBytesInRow && byteCount < myBytes.length; j++) {
577           byte aByte = myBytes[byteCount++];
578           myBuffer.append(" ").append(BitsUtil.byteToHexString(aByte));
579         }
580         writer.write(myBuffer.toString());
581       }
582     }
583   }
584
585   public static class ArrayOfBins extends Bin {
586     private Bin[] myValues;
587     private Bin.Value mySize;
588     private Class myClass;
589     private Bin.Value myCountHolder = null;
590
591     public ArrayOfBins(String name, Class cl, Bin.Value size) {
592       super(name);
593       myClass = cl;
594       mySize = size;
595       init();
596     }
597
598     public ArrayOfBins(String name, Class cl, int size) {
599       this(name, cl, new DWord("size").setValue(size));
600     }
601
602     public void addBin( Bin bin ){
603       Bin[] newArray = new Bin[myValues.length+1];
604       System.arraycopy( myValues, 0, newArray, 0, myValues.length );
605       newArray[myValues.length] = bin;
606       myValues = newArray;
607       mySize.setValue( mySize.getValue() + 1 );
608     }
609
610     public void copyFrom(Bin bin) {
611       ArrayOfBins value = (ArrayOfBins)bin;
612       for (int i = 0; i < myValues.length; i++) {
613         myValues[i].copyFrom( value.get(i) );
614       }
615     }
616
617     public void setCountHolder(Value countHolder) {
618       myCountHolder = countHolder;
619     }
620
621     private void init() {
622       myValues = (Bin[]) Array.newInstance(myClass, (int) mySize.getValue());
623
624       for (int i = 0; i < myValues.length; i++) {
625         try {
626           Bin bin = (Bin) myClass.newInstance();
627           bin.setName("[" + i + "]");
628           myValues[i] = bin;
629         } catch (InstantiationException e) {
630           throw new RuntimeException(e.getMessage());
631         } catch (IllegalAccessException e) {
632           throw new RuntimeException(e.getMessage());
633         }
634       }
635     }
636
637     public void resetOffsets(long newOffset) {
638       super.resetOffsets(newOffset);
639       long offset = getOffset();
640       if (myCountHolder != null) {
641         myCountHolder.setValue(myValues.length);
642       }
643       for (Bin bin : myValues) {
644         bin.resetOffsets(offset);
645         offset = offset + bin.sizeInBytes();
646       }
647     }
648
649     public int size() {
650       return myValues.length;
651     }
652
653     public Bin[] getArray() {
654       return myValues;
655     }
656
657     public Bin get(int index) {
658       return myValues[index];
659     }
660
661     public long sizeInBytes() {
662       int size = 0;
663       for (Bin value : myValues) {
664         size += value.sizeInBytes();
665       }
666       return size;
667     }
668
669     public void read(DataInput stream) throws IOException {
670       init();
671       for (Bin value : myValues) {
672         value.read(stream);
673       }
674     }
675
676     public void write(DataOutput stream) throws IOException {
677       for (Bin value : myValues) {
678         value.write(stream);
679       }
680     }
681
682     public void report(OutputStreamWriter writer) throws IOException {
683       writer.write("\n" + "Array size: " + myValues.length);
684       for (Bin value : myValues) {
685         value.report(writer);
686       }
687     }
688   }
689
690   protected void _report(OutputStreamWriter buffer, String name, int value) throws IOException {
691     buffer.write("\n" + name + " : " + BitsUtil.intToHexString(value));
692   }
693
694   protected void _report(OutputStreamWriter buffer, String name, short value) throws IOException {
695     buffer.write("\n" + name + " : " + BitsUtil.shortToHexString(value));
696   }
697
698   protected void _report(OutputStreamWriter buffer, String name, byte value) throws IOException {
699     buffer.write("\n" + name + " : " + BitsUtil.byteToHexString(value));
700   }
701
702   protected void _report(OutputStreamWriter buffer, String message) throws IOException {
703     buffer.write("\n" + message);
704   }
705 }