mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-05 08:06:56 +07:00
[build-scripts] launcher-generator: great refactoring
* add checks for produced output * proper padding for resource section * support adding more icons that were in template binary (fixed `updateGroupIcon`) * modernize code: use fields and getters instead of accessing struct parts by name GitOrigin-RevId: 9263568e575e2b8490c9af420836c6ac82056f71
This commit is contained in:
committed by
intellij-monorepo-bot
parent
50995f90ec
commit
37f1d76f9a
@@ -18,13 +18,17 @@
|
||||
package com.pme.exe;
|
||||
|
||||
import com.pme.util.BitsUtil;
|
||||
import com.pme.util.OffsetTrackingInputStream;
|
||||
import com.pme.util.StreamUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Array;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* @author Sergey Zhulin
|
||||
@@ -99,8 +103,8 @@ public abstract class Bin {
|
||||
public abstract void report(OutputStreamWriter writer) throws IOException;
|
||||
|
||||
public static class Structure extends Bin {
|
||||
private ArrayList<Bin> myMembers = new ArrayList<>(1);
|
||||
private final HashMap<String, Bin> myMembersMap = new HashMap<>(1);
|
||||
private final ArrayList<Bin> myMembers = new ArrayList<>(1);
|
||||
private final HashMap<String, Bin> myMembersMap = new LinkedHashMap<>(1);
|
||||
|
||||
public Structure(String name) {
|
||||
super(name);
|
||||
@@ -122,7 +126,7 @@ public abstract class Bin {
|
||||
Bin.Structure structure = (Bin.Structure)binStructure;
|
||||
ArrayList<Bin> members = structure.getMembers();
|
||||
for (Bin bin : members) {
|
||||
Bin valueMember = getMember(bin.getName());
|
||||
Bin valueMember = myMembersMap.get(bin.getName());
|
||||
if (valueMember != null) {
|
||||
valueMember.copyFrom(bin);
|
||||
}
|
||||
@@ -141,25 +145,13 @@ public abstract class Bin {
|
||||
public ArrayList<Bin> getMembers() {
|
||||
return myMembers;
|
||||
}
|
||||
public void addMember(Bin bin, String description) {
|
||||
|
||||
public <T extends Bin> T addMember(T bin, String description) {
|
||||
bin.setDescription(description);
|
||||
addMember(bin);
|
||||
return addMember(bin);
|
||||
}
|
||||
|
||||
public void insertMember(int index, Bin bin) {
|
||||
ArrayList<Bin> list = new ArrayList<>(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);
|
||||
}
|
||||
|
||||
public Bin addMember(Bin bin) {
|
||||
public <T extends Bin> T addMember(T bin) {
|
||||
myMembers.add(bin);
|
||||
addMemberToMapOnly(bin);
|
||||
return bin;
|
||||
@@ -171,24 +163,6 @@ public abstract class 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 Bin.Txt getTxtMember(String name) { return (Bin.Txt)myMembersMap.get(name); }
|
||||
|
||||
public long getValue(String name) {
|
||||
return ((Bin.Value) myMembersMap.get(name)).getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException {
|
||||
for (Bin bin : myMembers) {
|
||||
@@ -238,6 +212,37 @@ public abstract class Bin {
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class ReadOnlyValue extends Value {
|
||||
public ReadOnlyValue(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long sizeInBytes() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput stream) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(OutputStreamWriter writer) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value setValue(long value) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Byte extends Value {
|
||||
public Byte(String name) {
|
||||
super(name);
|
||||
@@ -252,8 +257,9 @@ public abstract class Bin {
|
||||
public long getValue() {
|
||||
return getRawValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value setValue(long value) {
|
||||
public Byte setValue(long value) {
|
||||
setRawValue(value);
|
||||
return this;
|
||||
}
|
||||
@@ -265,14 +271,17 @@ public abstract class Bin {
|
||||
|
||||
@Override
|
||||
public void write(DataOutput stream) throws IOException {
|
||||
stream.writeByte((byte) getRawValue());
|
||||
stream.writeByte((byte)getRawValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(OutputStreamWriter writer) throws IOException {
|
||||
_report(writer, getDescription(), (byte) getValue());
|
||||
_report(writer, getDescription(), (byte)getValue());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return BitsUtil.byteToHexString((int)getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public static class Word extends Value {
|
||||
@@ -295,7 +304,7 @@ public abstract class Bin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value setValue(long value) {
|
||||
public Word setValue(long value) {
|
||||
setRawValue(Short.toUnsignedLong(Short.reverseBytes((short)value)));
|
||||
return this;
|
||||
}
|
||||
@@ -321,6 +330,10 @@ public abstract class Bin {
|
||||
}
|
||||
|
||||
public static class DWord extends Value {
|
||||
public DWord() {
|
||||
super("");
|
||||
}
|
||||
|
||||
public DWord(String name) {
|
||||
super(name);
|
||||
}
|
||||
@@ -331,7 +344,7 @@ public abstract class Bin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value setValue(long value) {
|
||||
public DWord setValue(long value) {
|
||||
setRawValue(Integer.toUnsignedLong(Integer.reverseBytes((int)value)));
|
||||
return this;
|
||||
}
|
||||
@@ -371,7 +384,7 @@ public abstract class Bin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value setValue(long value) {
|
||||
public LongLong setValue(long value) {
|
||||
setRawValue(Long.reverseBytes(value));
|
||||
return this;
|
||||
}
|
||||
@@ -400,8 +413,8 @@ public abstract class Bin {
|
||||
public static class Padding extends Bin {
|
||||
private final int myBytes;
|
||||
|
||||
public Padding(int bytes) {
|
||||
super("Padding");
|
||||
public Padding(String name, int bytes) {
|
||||
super(name);
|
||||
myBytes = bytes;
|
||||
}
|
||||
|
||||
@@ -412,13 +425,12 @@ public abstract class Bin {
|
||||
|
||||
@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);
|
||||
}
|
||||
long offset = StreamUtil.getOffset(stream);
|
||||
int skip = bytesToSkip(offset);
|
||||
if (skip > 0) {
|
||||
stream.skipBytes(skip);
|
||||
}
|
||||
//resetOffsets(offset);
|
||||
}
|
||||
|
||||
private int bytesToSkip(long offset) {
|
||||
@@ -438,67 +450,49 @@ public abstract class Bin {
|
||||
@Override
|
||||
public void report(OutputStreamWriter writer) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Padding{" + getName() + "," + myBytes + '}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class Txt extends Bin {
|
||||
/**
|
||||
* Fixed size character (1 byte) string, UTF-8
|
||||
*/
|
||||
public static class CharStringFS extends Bin {
|
||||
private final StringBuffer myBuffer = new StringBuffer();
|
||||
private final 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(StandardCharsets.US_ASCII);
|
||||
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) {
|
||||
public CharStringFS(String name, Bin.Value size) {
|
||||
super(name);
|
||||
mySize = size;
|
||||
}
|
||||
|
||||
public Txt(String name, int size) {
|
||||
public CharStringFS(String name, int size) {
|
||||
this(name, new DWord("size").setValue(size));
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return myBuffer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long sizeInBytes() {
|
||||
return mySize.getValue();
|
||||
}
|
||||
|
||||
private void setValue(){
|
||||
for (int i = 0; i < mySize.getValue(); ++i) {
|
||||
int b = java.lang.Byte.toUnsignedInt(myBytes[i]);
|
||||
if (b != 0) {
|
||||
myBuffer.append((char) b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException {
|
||||
long size = mySize.getValue();
|
||||
myBuffer.setLength(0);
|
||||
myBytes = new byte[(int) mySize.getValue()];
|
||||
for (int i = 0; i < mySize.getValue(); ++i) {
|
||||
myBytes = new byte[(int)size];
|
||||
for (int i = 0; i < size; ++i) {
|
||||
myBytes[i] = stream.readByte();
|
||||
}
|
||||
setValue();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
int b = java.lang.Byte.toUnsignedInt(myBytes[i]);
|
||||
if (b != 0) {
|
||||
myBuffer.append((char)b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -510,12 +504,96 @@ public abstract class Bin {
|
||||
public void report(OutputStreamWriter writer) throws IOException {
|
||||
_report(writer, myBuffer.toString());
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return myBuffer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CharStringFS{size=" + mySize.getValue() + ", value=" + getValue() + "}";
|
||||
}
|
||||
}
|
||||
|
||||
public static class WChar extends Bin {
|
||||
/**
|
||||
* Size-prefixed wide character (2 bytes) string, UTF-16.
|
||||
* <p>
|
||||
* Size is {@linkplain Word} by default.
|
||||
*/
|
||||
public static class WCharStringSP extends Bin {
|
||||
private static final String EMPTY_STRING = "";
|
||||
private final Value mySize;
|
||||
private String myValue;
|
||||
|
||||
public WChar(String name) {
|
||||
public WCharStringSP() {
|
||||
this(new Word());
|
||||
}
|
||||
|
||||
public WCharStringSP(Value size) {
|
||||
super("");
|
||||
mySize = size;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
myValue = value;
|
||||
mySize.setValue(value.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long sizeInBytes() {
|
||||
return mySize.sizeInBytes() + mySize.getValue() * 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException {
|
||||
mySize.read(stream);
|
||||
long size = mySize.getValue();
|
||||
if (size == 0) {
|
||||
myValue = EMPTY_STRING;
|
||||
return;
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
char c = BitsUtil.readChar(stream);
|
||||
builder.append(c);
|
||||
}
|
||||
myValue = builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput stream) throws IOException {
|
||||
assert mySize.getValue() == myValue.length();
|
||||
mySize.write(stream);
|
||||
for (int i = 0; i < myValue.length(); i++) {
|
||||
stream.writeShort(Short.toUnsignedInt(Short.reverseBytes((short)myValue.charAt(i))));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(OutputStreamWriter writer) throws IOException {
|
||||
_report(writer, myValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WCharStringSP{" +
|
||||
"size=" + mySize +
|
||||
", value=" + myValue +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-terminated wide character (2 bytes) string, UTF-16
|
||||
*/
|
||||
public static class WCharStringNT extends Bin {
|
||||
private String myValue;
|
||||
|
||||
public WCharStringNT(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@@ -555,34 +633,33 @@ public abstract class Bin {
|
||||
public void setValue(String value) {
|
||||
myValue = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WCharStringNT{value=" + myValue + '}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class Bytes extends Bin {
|
||||
private byte[] myBytes;
|
||||
private Value myStartOffset;
|
||||
private Value mySize;
|
||||
private final Value myStartOffset;
|
||||
private final Value mySize;
|
||||
private static final int ourBytesInRow = 16;
|
||||
|
||||
public Bytes(String name, Bin.Value size) {
|
||||
super(name);
|
||||
myStartOffset = null;
|
||||
mySize = size;
|
||||
}
|
||||
|
||||
public Bytes(String name, long size) {
|
||||
super(name);
|
||||
myStartOffset = null;
|
||||
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;
|
||||
}
|
||||
@@ -611,8 +688,18 @@ public abstract class Bin {
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException {
|
||||
if (myStartOffset != null) {
|
||||
RandomAccessFile file = (RandomAccessFile) stream;
|
||||
file.seek(myStartOffset.getValue());
|
||||
long offset = myStartOffset.getValue();
|
||||
long streamOffset = StreamUtil.getOffset(stream);
|
||||
if (streamOffset != offset) {
|
||||
if (offset > streamOffset) {
|
||||
//noinspection UseOfSystemOutOrSystemErr
|
||||
System.err.printf("WARN: non-continuous read: reading offset %#x, current stream offset %#x %n", offset, streamOffset);
|
||||
} else {
|
||||
//noinspection UseOfSystemOutOrSystemErr
|
||||
System.err.printf("WARN: out of order read: reading offset %#x, current stream offset %#x %n", offset, streamOffset);
|
||||
}
|
||||
StreamUtil.seek(stream, offset);
|
||||
}
|
||||
}
|
||||
myBytes = new byte[(int) mySize.getValue()];
|
||||
stream.readFully(myBytes);
|
||||
@@ -623,8 +710,6 @@ public abstract class Bin {
|
||||
stream.write(myBytes, 0, (int) sizeInBytes());
|
||||
}
|
||||
|
||||
private final StringBuffer myBuffer = new StringBuffer();
|
||||
|
||||
@Override
|
||||
public void report(OutputStreamWriter writer) throws IOException {
|
||||
_report(writer, getName());
|
||||
@@ -634,6 +719,7 @@ public abstract class Bin {
|
||||
rowCount++;
|
||||
}
|
||||
int byteCount = 0;
|
||||
StringBuilder myBuffer = new StringBuilder();
|
||||
for (int i = 0; i < rowCount; i++) {
|
||||
myBuffer.setLength(0);
|
||||
myBuffer.append("\n");
|
||||
@@ -644,10 +730,18 @@ public abstract class Bin {
|
||||
writer.write(myBuffer.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Bytes{" +
|
||||
"StartOffset=" + myStartOffset +
|
||||
", Size=" + mySize +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class ArrayOfBins<T extends Bin> extends Bin {
|
||||
private Bin[] myValues;
|
||||
public static class ArrayOfBins<T extends Bin> extends Bin implements Iterable<T> {
|
||||
private ArrayList<T> myValues;
|
||||
private final Bin.Value mySize;
|
||||
private final Class<T> myClass;
|
||||
private Bin.Value myCountHolder = null;
|
||||
@@ -663,20 +757,19 @@ public abstract class Bin {
|
||||
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 addBin(T bin) {
|
||||
myValues.add(bin);
|
||||
if (myCountHolder != null) {
|
||||
myCountHolder.setValue(myValues.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyFrom(Bin bin) {
|
||||
//noinspection unchecked
|
||||
ArrayOfBins<T> value = (ArrayOfBins<T>)bin;
|
||||
for (int i = 0; i < myValues.length; i++) {
|
||||
myValues[i].copyFrom( value.get(i) );
|
||||
for (int i = 0; i < myValues.size(); i++) {
|
||||
myValues.get(i).copyFrom(value.myValues.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,14 +778,16 @@ public abstract class Bin {
|
||||
}
|
||||
|
||||
private void init() {
|
||||
myValues = (Bin[]) Array.newInstance(myClass, (int) mySize.getValue());
|
||||
int size = (int)mySize.getValue();
|
||||
myValues = new ArrayList<>(size);
|
||||
|
||||
for (int i = 0; i < myValues.length; i++) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
try {
|
||||
Bin bin = myClass.newInstance();
|
||||
T bin = myClass.getDeclaredConstructor().newInstance();
|
||||
bin.setName("[" + i + "]");
|
||||
myValues[i] = bin;
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
myValues.add(bin);
|
||||
}
|
||||
catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -703,7 +798,7 @@ public abstract class Bin {
|
||||
super.resetOffsets(newOffset);
|
||||
long offset = getOffset();
|
||||
if (myCountHolder != null) {
|
||||
myCountHolder.setValue(myValues.length);
|
||||
myCountHolder.setValue(myValues.size());
|
||||
}
|
||||
for (Bin bin : myValues) {
|
||||
bin.resetOffsets(offset);
|
||||
@@ -712,16 +807,11 @@ public abstract class Bin {
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return myValues.length;
|
||||
}
|
||||
|
||||
public Bin[] getArray() {
|
||||
return myValues;
|
||||
return myValues.size();
|
||||
}
|
||||
|
||||
public T get(int index) {
|
||||
//noinspection unchecked
|
||||
return (T) myValues[index];
|
||||
return myValues.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -750,11 +840,16 @@ public abstract class Bin {
|
||||
|
||||
@Override
|
||||
public void report(OutputStreamWriter writer) throws IOException {
|
||||
writer.write("\n" + "Array size: " + myValues.length);
|
||||
writer.write("\n" + getName() + " array size: " + myValues.size());
|
||||
for (Bin value : myValues) {
|
||||
value.report(writer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return myValues.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
protected void _report(OutputStreamWriter buffer, String name, int value) throws IOException {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
|
||||
package com.pme.exe;
|
||||
|
||||
|
||||
public enum ExeFormat {
|
||||
UNKNOWN, X86, X64, ARM64
|
||||
}
|
||||
@@ -23,76 +23,77 @@ import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author Sergey Zhulin
|
||||
* Date: Mar 30, 2006
|
||||
* Time: 4:14:38 PM
|
||||
*/
|
||||
public class ExeReader extends Bin.Structure{
|
||||
private ArrayOfBins<ImageSectionHeader> mySectionHeaders;
|
||||
private SectionReader[] mySections;
|
||||
public class ExeReader extends Bin.Structure {
|
||||
private final ArrayOfBins<ImageSectionHeader> mySectionHeaders;
|
||||
private final ArrayList<Section> mySections = new ArrayList<>();
|
||||
private final PeHeaderReader myPeHeader;
|
||||
private ImageOptionalHeader myImageOptionalHeader;
|
||||
private Bin.Bytes myBytes;
|
||||
private final ImageOptionalHeader myImageOptionalHeader;
|
||||
private final Bin.Bytes myPadding;
|
||||
private final Bin.Bytes myMsDosStub;
|
||||
private final MsDosHeader myMsDosHeader;
|
||||
|
||||
public ExeReader(String name, ExeFormat exeFormat) {
|
||||
public ExeReader(String name) {
|
||||
super(name);
|
||||
myMsDosHeader = new MsDosHeader();
|
||||
addMember( myMsDosHeader );
|
||||
//noinspection SpellCheckingInspection
|
||||
Bin.Value member = myMsDosHeader.getValueMember("lfanew");
|
||||
ValuesAdd size = new ValuesAdd( member, new DWord("").setValue( myMsDosHeader.sizeInBytes() ) );
|
||||
myMsDosStub = new Bytes( "MsDos stub program", size );
|
||||
addMember( myMsDosStub );
|
||||
myPeHeader = new PeHeaderReader(member, exeFormat);
|
||||
addMember( myPeHeader );
|
||||
if (exeFormat == ExeFormat.UNKNOWN) {
|
||||
return;
|
||||
}
|
||||
myImageOptionalHeader = (ImageOptionalHeader) myPeHeader.getMember("Image Optional Header");
|
||||
//noinspection unchecked
|
||||
mySectionHeaders = (ArrayOfBins<ImageSectionHeader>)myPeHeader.getMember( "ImageSectionHeaders" );
|
||||
addSizeHolder( myImageOptionalHeader.getValueMember( "SizeOfImage" ) ); //b164
|
||||
}
|
||||
|
||||
public long sizeOfHeaders(){
|
||||
return myPeHeader.sizeInBytes() + myMsDosStub.sizeInBytes() + myMsDosHeader.sizeInBytes();
|
||||
myMsDosHeader = addMember(new MsDosHeader());
|
||||
ValuesAdd msDosStubSize = new ValuesAdd(myMsDosHeader.getPEHeaderOffset(), new DWord("").setValue(myMsDosHeader.sizeInBytes()));
|
||||
myMsDosStub = addMember(new Bytes("MsDos stub program", msDosStubSize));
|
||||
myPeHeader = addMember(new PeHeaderReader(myMsDosHeader.getPEHeaderOffset()));
|
||||
myPadding = new Bytes("Padding between headers and first segment",
|
||||
new ValuesAdd(myPeHeader.getImageOptionalHeader().getSizeOfHeaders(), new ReadOnlyValue("") {
|
||||
@Override
|
||||
public long getValue() {
|
||||
return myPeHeader.sizeInBytes() + myMsDosStub.sizeInBytes() + myMsDosHeader.sizeInBytes();
|
||||
}
|
||||
}));
|
||||
addMember(myPadding);
|
||||
myImageOptionalHeader = myPeHeader.getImageOptionalHeader();
|
||||
mySectionHeaders = myPeHeader.getImageSectionHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long sizeInBytes() {
|
||||
long result = 0;
|
||||
long va = 0;
|
||||
for ( int i = 0; i < mySectionHeaders.size(); ++i){
|
||||
ImageSectionHeader header = mySectionHeaders.get(i);
|
||||
Value virtualAddress = header.getValueMember("VirtualAddress");
|
||||
if ( va < virtualAddress.getValue() ){
|
||||
result = mySections[i].sizeInBytes() + virtualAddress.getValue();
|
||||
}
|
||||
long result = myPeHeader.sizeInBytes() + myMsDosStub.sizeInBytes() + myMsDosHeader.sizeInBytes() + myPadding.sizeInBytes();
|
||||
for (Section section : mySections) {
|
||||
result += section.sizeInBytes();
|
||||
}
|
||||
long div = result / 0x1000;
|
||||
long r = result % 0x1000;
|
||||
if ( r != 0 ){
|
||||
div++;
|
||||
}
|
||||
result = div * 0x1000;
|
||||
return result;
|
||||
}
|
||||
|
||||
public SectionReader[] getSections(){
|
||||
return mySections;
|
||||
public long virtualSizeInBytes() {
|
||||
// AKA VirtualAddress + VirtualSize of the latest section, must be dividable by SectionAlignment
|
||||
|
||||
long sectionAlignment = myImageOptionalHeader.getSectionAlignment().getValue();
|
||||
long result = 0;
|
||||
long lastVA = 0;
|
||||
for (ImageSectionHeader header : mySectionHeaders) {
|
||||
long va = ((Value)header.getVirtualAddress()).getValue();
|
||||
if (lastVA < va) {
|
||||
result = header.getVirtualSize().getValue() + va;
|
||||
}
|
||||
}
|
||||
if (result % sectionAlignment != 0) {
|
||||
result += sectionAlignment - result % sectionAlignment;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public ArrayOfBins<ImageSectionHeader> getSectionHeaders(){
|
||||
|
||||
public ArrayOfBins<ImageSectionHeader> getSectionHeaders() {
|
||||
return mySectionHeaders;
|
||||
}
|
||||
|
||||
public SectionReader getSectionReader( String sectionName ){
|
||||
for (SectionReader section : mySections) {
|
||||
public PeHeaderReader getPeHeader() {
|
||||
return myPeHeader;
|
||||
}
|
||||
|
||||
public Section getSectionReader(String sectionName) {
|
||||
for (Section section : mySections) {
|
||||
if (sectionName.equals(section.getSectionName())) {
|
||||
return section;
|
||||
}
|
||||
@@ -107,43 +108,33 @@ public class ExeReader extends Bin.Structure{
|
||||
return;
|
||||
}
|
||||
|
||||
long filePointer = getOffset() + sizeOfHeaders();
|
||||
DWord sizeOfHeaders = myPeHeader.getImageOptionalHeader().getSizeOfHeaders();
|
||||
assert sizeOfHeaders.getValue() == mySectionHeaders.get(0).getPointerToRawData().getValue();
|
||||
|
||||
Bin.Value mainSectionsOffset;
|
||||
mySections = new SectionReader[mySectionHeaders.size()];
|
||||
for ( int i = 0; i < mySectionHeaders.size(); ++i ){
|
||||
ImageSectionHeader sectionHeader = mySectionHeaders.get(i);
|
||||
Bin.Value startOffset = sectionHeader.getValueMember( "PointerToRawData" );
|
||||
Bin.Value rva = sectionHeader.getValueMember( "VirtualAddress" );
|
||||
|
||||
if ( i == 0 ){
|
||||
long size = startOffset.getValue() - filePointer;
|
||||
if ( myBytes == null ){
|
||||
myBytes = new Bytes( "Alignment", size );
|
||||
addMemberToMapOnly( myBytes );
|
||||
} else {
|
||||
myBytes = (Bytes)getMember( "Alignment" );
|
||||
myBytes.reset( (int)filePointer, (int)size );
|
||||
}
|
||||
myBytes.read( stream );
|
||||
}
|
||||
|
||||
mainSectionsOffset = new ValuesAdd( rva, startOffset );
|
||||
mySections[i] = new SectionReader( sectionHeader, startOffset, mainSectionsOffset, myImageOptionalHeader );
|
||||
mySections[i].read( stream );
|
||||
mySections.clear();
|
||||
for (ImageSectionHeader sectionHeader : mySectionHeaders) {
|
||||
Section section = Section.newSection(sectionHeader, myImageOptionalHeader);
|
||||
mySections.add(section);
|
||||
section.read(stream);
|
||||
}
|
||||
resetOffsets( 0 );
|
||||
resetOffsets(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetOffsets(long newOffset) {
|
||||
super.resetOffsets(newOffset);
|
||||
long mainOffset = myPeHeader.getOffset() + myPeHeader.sizeInBytes() + myBytes.sizeInBytes();
|
||||
long mainOffset = myPeHeader.getOffset() + myPeHeader.sizeInBytes() + myPadding.sizeInBytes();
|
||||
long mainOffset2 = myPeHeader.sizeInBytes() + myMsDosStub.sizeInBytes() + myMsDosHeader.sizeInBytes() + myPadding.sizeInBytes();
|
||||
assert mainOffset == myPeHeader.getImageOptionalHeader().getSizeOfHeaders().getValue();
|
||||
assert mainOffset == mainOffset2;
|
||||
long offset = 0;
|
||||
for (SectionReader section : mySections) {
|
||||
for (Section section : mySections) {
|
||||
section.resetOffsets(mainOffset + offset);
|
||||
offset += section.sizeInBytes();
|
||||
}
|
||||
|
||||
// Update SizeOfImage with not real bytes size, but with virtual size
|
||||
myImageOptionalHeader.getSizeOfImage().setValue(virtualSizeInBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,39 +142,40 @@ public class ExeReader extends Bin.Structure{
|
||||
* afterwards.
|
||||
*/
|
||||
public void sectionVirtualAddressFixup() {
|
||||
long virtualAddress = mySectionHeaders.get(0).getValueMember("VirtualAddress").getValue();
|
||||
long virtualAddress = mySectionHeaders.get(0).getVirtualAddress().getValue();
|
||||
|
||||
long sectionAlignment = myImageOptionalHeader.getValue("SectionAlignment");
|
||||
for (Bin sectionHeader : mySectionHeaders.getArray()) {
|
||||
Value virtualAddressMember = ((ImageSectionHeader)sectionHeader).getValueMember("VirtualAddress");
|
||||
long sectionAlignment = myImageOptionalHeader.getSectionAlignment().getValue();
|
||||
for (ImageSectionHeader header : mySectionHeaders) {
|
||||
|
||||
// Section always starts from an address aligned to IMAGE_OPTIONAL_HEADER::SectionAlignment, which is 0x1000 by default
|
||||
if (virtualAddress % sectionAlignment != 0)
|
||||
if (virtualAddress % sectionAlignment != 0) {
|
||||
virtualAddress += sectionAlignment - virtualAddress % sectionAlignment;
|
||||
}
|
||||
|
||||
virtualAddressMember.setValue(virtualAddress);
|
||||
virtualAddress += ((ImageSectionHeader)sectionHeader).getValueMember("VirtualSize").getValue();
|
||||
header.getVirtualAddress().setValue(virtualAddress);
|
||||
virtualAddress += header.getVirtualSize().getValue();
|
||||
}
|
||||
|
||||
// Update the relative virtual address of the Base Relocation Table, if any.
|
||||
Optional<ImageSectionHeader> relocationSectionHeader = Arrays.stream((ImageSectionHeader[])mySectionHeaders.getArray())
|
||||
.filter(sectionHeader -> ".reloc".equals(sectionHeader.getTxtMember("Name").getText())).findFirst();
|
||||
if (relocationSectionHeader.isPresent()) {
|
||||
Bin.ArrayOfBins imageDataDirectories = (Bin.ArrayOfBins)myImageOptionalHeader.getMember("ImageDataDirectories");
|
||||
ImageDataDirectory relocationDataDirectory = (ImageDataDirectory)imageDataDirectories.get(5);
|
||||
Value virtualAddressMember = relocationDataDirectory.getValueMember("VirtualAddress");
|
||||
virtualAddressMember.setValue(relocationSectionHeader.get().getValue("VirtualAddress"));
|
||||
}
|
||||
updateDataDirectoryFromSectionHeader(Section.RESOURCES_SECTION_NAME, ImageDataDirectory.IMAGE_DIRECTORY_ENTRY_RESOURCE);
|
||||
updateDataDirectoryFromSectionHeader(Section.RELOCATIONS_SECTION_NAME, ImageDataDirectory.IMAGE_DIRECTORY_ENTRY_BASERELOC);
|
||||
|
||||
// The binary size has been changed as the result, update it in the size holders:
|
||||
updateSizeOffsetHolders();
|
||||
// Resources section may have changed virtual offset, re-shake of all nested structures required to have correct addresses,
|
||||
// also the binary size may have been changed as the result
|
||||
resetOffsets(0);
|
||||
}
|
||||
|
||||
private void updateDataDirectoryFromSectionHeader(String name, int index) {
|
||||
ImageSectionHeader header = getSection(name);
|
||||
ImageDataDirectory directory = myImageOptionalHeader.getImageDataDirectories().get(index);
|
||||
directory.getVirtualAddress().setValue(header.getVirtualAddress().getValue());
|
||||
directory.getSize().setValue(header.getVirtualSize().getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput stream) throws IOException {
|
||||
super.write(stream);
|
||||
myBytes.write(stream);
|
||||
for (SectionReader section : mySections) {
|
||||
for (Section section : mySections) {
|
||||
section.write(stream);
|
||||
}
|
||||
}
|
||||
@@ -191,24 +183,19 @@ public class ExeReader extends Bin.Structure{
|
||||
@Override
|
||||
public void report(OutputStreamWriter writer) throws IOException {
|
||||
super.report(writer);
|
||||
myBytes.report( writer );
|
||||
myPadding.report(writer);
|
||||
mySectionHeaders.report(writer);
|
||||
for (SectionReader section : mySections) {
|
||||
for (Section section : mySections) {
|
||||
section.report(writer);
|
||||
}
|
||||
}
|
||||
|
||||
public ExeFormat getExeFormat() {
|
||||
long machine = myPeHeader.getImageFileHeader().getMachine();
|
||||
if (machine == 0x14c) {
|
||||
return ExeFormat.X86;
|
||||
private ImageSectionHeader getSection(String name) {
|
||||
for (ImageSectionHeader header : mySectionHeaders) {
|
||||
if (name.equals(header.getSectionName())) {
|
||||
return header;
|
||||
}
|
||||
}
|
||||
if (machine == 0x8664) {
|
||||
return ExeFormat.X64;
|
||||
}
|
||||
if (machine == 0xAA64) {
|
||||
return ExeFormat.ARM64;
|
||||
}
|
||||
throw new UnsupportedOperationException("Unsupported machine code " + machine);
|
||||
throw new IllegalStateException("Cannot find section with name " + name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2006 ProductiveMe Inc.
|
||||
* Copyright 2013-2018 JetBrains s.r.o.
|
||||
* Copyright 2013-2022 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.
|
||||
@@ -23,12 +23,30 @@ package com.pme.exe;
|
||||
* Time: 10:40:56 PM
|
||||
*/
|
||||
public class ImageDataDirectory extends Bin.Structure {
|
||||
public static final int IMAGE_DIRECTORY_ENTRY_RESOURCE = 2;
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
public static final int IMAGE_DIRECTORY_ENTRY_BASERELOC = 5;
|
||||
|
||||
private final DWord myVirtualAddress;
|
||||
private final DWord mySize;
|
||||
|
||||
public ImageDataDirectory(String name) {
|
||||
super(name);
|
||||
addMember( new DWord( "VirtualAddress" ) );
|
||||
addMember( new DWord( "Size" ) );
|
||||
myVirtualAddress = addMember(new DWord("VirtualAddress"));
|
||||
mySize = addMember(new DWord("Size"));
|
||||
}
|
||||
public ImageDataDirectory( ){
|
||||
this("");
|
||||
|
||||
// Used by Bin.ArrayOfBins
|
||||
@SuppressWarnings("unused")
|
||||
public ImageDataDirectory() {
|
||||
this("");
|
||||
}
|
||||
|
||||
public DWord getVirtualAddress() {
|
||||
return myVirtualAddress;
|
||||
}
|
||||
|
||||
public DWord getSize() {
|
||||
return mySize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,12 +27,12 @@ import java.io.OutputStreamWriter;
|
||||
*/
|
||||
public class ImageFileHeader extends Bin.Structure {
|
||||
private final ImageFileHeader.Machine myMachine;
|
||||
private final Word myNumberOfSections;
|
||||
|
||||
public ImageFileHeader() {
|
||||
super("Image File Header");
|
||||
myMachine = new Machine();
|
||||
addMember(myMachine);
|
||||
addMember( new Word( "NumberOfSections" ) );
|
||||
myMachine = addMember(new Machine());
|
||||
myNumberOfSections = addMember(new Word("NumberOfSections"));
|
||||
addMember( new DWord( "TimeDateStamp" ) );
|
||||
addMember( new DWord( "PointerToSymbolTable" ) );
|
||||
addMember( new DWord( "NumberOfSymbols" ) );
|
||||
@@ -44,6 +44,10 @@ public class ImageFileHeader extends Bin.Structure {
|
||||
return myMachine.getValue();
|
||||
}
|
||||
|
||||
public Word getNumberOfSections() {
|
||||
return myNumberOfSections;
|
||||
}
|
||||
|
||||
static class Machine extends Bin.Word{
|
||||
|
||||
Machine() {
|
||||
@@ -58,8 +62,12 @@ public class ImageFileHeader extends Bin.Structure {
|
||||
_report(writer ,"Machine: Intel 386" );
|
||||
} else if ( machine == 0x0002 ) {
|
||||
_report( writer, "Machine: Intel 64" );
|
||||
} else if ( machine == 0x8664 ) {
|
||||
_report( writer, "Machine: AMD64" );
|
||||
} else if ( machine == 0xAA64 ) {
|
||||
_report( writer, "Machine: ARM64" );
|
||||
} else {
|
||||
_report( writer, "Machine: Unknown" );
|
||||
_report( writer, String.format("Machine: Unknown (%#06x)", machine));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,61 +17,159 @@
|
||||
|
||||
package com.pme.exe;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* @author Sergey Zhulin
|
||||
* Date: Mar 31, 2006
|
||||
* Time: 6:01:16 PM
|
||||
*/
|
||||
public class ImageOptionalHeader extends Bin.Structure {
|
||||
public ImageOptionalHeader(ExeFormat format) {
|
||||
super("Image Optional Header");
|
||||
addMember( new Word( "Magic" ) );
|
||||
addMember( new Byte( "MajorLinkerVersion"));
|
||||
addMember( new Byte( "MinorLinkerVersion"));
|
||||
addMember( new DWord( "SizeOfCode" ) );
|
||||
addMember( new DWord( "SizeOfInitializedData" ) );
|
||||
addMember( new DWord( "SizeOfUninitializedData" ) );
|
||||
addMember( new DWord( "AddressOfEntryPoint" ) );
|
||||
addMember( new DWord( "BaseOfCode" ) );
|
||||
if (format == ExeFormat.X86) {
|
||||
addMember( new DWord( "BaseOfData" ) );
|
||||
addMember( new DWord( "ImageBase" ) );
|
||||
}
|
||||
else {
|
||||
addMember(new LongLong("ImageBase"));
|
||||
}
|
||||
addMember( new DWord( "SectionAlignment" ) );
|
||||
addMember( new DWord( "FileAlignment" ) );
|
||||
addMember( new Word( "MajorOperatingSystemVersion" ) );
|
||||
addMember( new Word( "MinorOperatingSystemVersion" ) );
|
||||
addMember( new Word( "MajorImageVersion" ) );
|
||||
addMember( new Word( "MinorImageVersion" ) );
|
||||
addMember( new Word( "MajorSubsystemVersion" ) );
|
||||
addMember( new Word( "MinorSubsystemVersion" ) );
|
||||
addMember( new DWord( "Win32VersionValue" ) );
|
||||
addMember( new DWord( "SizeOfImage" ) );
|
||||
addMember( new DWord( "SizeOfHeaders" ) );
|
||||
addMember( new DWord( "CheckSum" ) );
|
||||
addMember( new Word( "Subsystem" ) );
|
||||
addMember( new Word( "DllCharacteristics" ) );
|
||||
if (format == ExeFormat.X86) {
|
||||
addMember( new DWord( "SizeOfStackReserve" ) );
|
||||
addMember( new DWord( "SizeOfStackCommit" ) );
|
||||
addMember( new DWord( "SizeOfHeapReserve" ) );
|
||||
addMember( new DWord( "SizeOfHeapCommit" ) );
|
||||
}
|
||||
else {
|
||||
addMember( new LongLong( "SizeOfStackReserve" ) );
|
||||
addMember( new LongLong( "SizeOfStackCommit" ) );
|
||||
addMember( new LongLong( "SizeOfHeapReserve" ) );
|
||||
addMember( new LongLong( "SizeOfHeapCommit" ) );
|
||||
}
|
||||
addMember( new DWord( "LoaderFlags" ) );
|
||||
DWord numberOfRvaAndSizes = (DWord)addMember( new DWord( "NumberOfRvaAndSizes") );
|
||||
ArrayOfBins<ImageDataDirectory> imageDataDirectories =
|
||||
new ArrayOfBins<>("ImageDataDirectories", ImageDataDirectory.class, numberOfRvaAndSizes);
|
||||
imageDataDirectories.setCountHolder( numberOfRvaAndSizes );
|
||||
|
||||
addMember( imageDataDirectories );
|
||||
private final ArrayOfBins<ImageDataDirectory> myImageDataDirectories;
|
||||
private final DWord mySectionAlignment;
|
||||
private final DWord myFileAlignment;
|
||||
private final DWord mySizeOfImage;
|
||||
private final DWord mySizeOfHeaders;
|
||||
|
||||
public ImageOptionalHeader() {
|
||||
super("Image Optional Header");
|
||||
Magic magic = addMember(new Magic());
|
||||
addMember(new Byte("MajorLinkerVersion"));
|
||||
addMember(new Byte("MinorLinkerVersion"));
|
||||
addMember(new DWord("SizeOfCode"));
|
||||
addMember(new DWord("SizeOfInitializedData"));
|
||||
addMember(new DWord("SizeOfUninitializedData"));
|
||||
addMember(new DWord("AddressOfEntryPoint"));
|
||||
addMember(new DWord("BaseOfCode"));
|
||||
addMember(new Bytes("BaseOfData,ImageBase", 8));
|
||||
mySectionAlignment = addMember(new DWord("SectionAlignment"));
|
||||
myFileAlignment = addMember(new DWord("FileAlignment"));
|
||||
addMember(new Word("MajorOperatingSystemVersion"));
|
||||
addMember(new Word("MinorOperatingSystemVersion"));
|
||||
addMember(new Word("MajorImageVersion"));
|
||||
addMember(new Word("MinorImageVersion"));
|
||||
addMember(new Word("MajorSubsystemVersion"));
|
||||
addMember(new Word("MinorSubsystemVersion"));
|
||||
addMember(new DWord("Win32VersionValue"));
|
||||
mySizeOfImage = addMember(new DWord("SizeOfImage"));
|
||||
mySizeOfHeaders = addMember(new DWord("SizeOfHeaders"));
|
||||
addMember(new DWord("CheckSum"));
|
||||
addMember(new Word("Subsystem"));
|
||||
addMember(new Word("DllCharacteristics"));
|
||||
addMember(new Dependent(magic, DWord.class, LongLong.class, "SizeOfStackReserve"));
|
||||
addMember(new Dependent(magic, DWord.class, LongLong.class, "SizeOfStackCommit"));
|
||||
addMember(new Dependent(magic, DWord.class, LongLong.class, "SizeOfHeapReserve"));
|
||||
addMember(new Dependent(magic, DWord.class, LongLong.class, "SizeOfHeapCommit"));
|
||||
addMember(new DWord("LoaderFlags"));
|
||||
DWord numberOfImageDataDirectories = addMember(new DWord("NumberOfRvaAndSizes"));
|
||||
myImageDataDirectories = new ArrayOfBins<>("ImageDataDirectories", ImageDataDirectory.class, numberOfImageDataDirectories);
|
||||
myImageDataDirectories.setCountHolder(numberOfImageDataDirectories);
|
||||
|
||||
addMember(myImageDataDirectories);
|
||||
}
|
||||
|
||||
public ArrayOfBins<ImageDataDirectory> getImageDataDirectories() {
|
||||
return myImageDataDirectories;
|
||||
}
|
||||
|
||||
public DWord getSectionAlignment() {
|
||||
return mySectionAlignment;
|
||||
}
|
||||
|
||||
public DWord getFileAlignment() {
|
||||
return myFileAlignment;
|
||||
}
|
||||
|
||||
public DWord getSizeOfImage() {
|
||||
return mySizeOfImage;
|
||||
}
|
||||
|
||||
public DWord getSizeOfHeaders() {
|
||||
return mySizeOfHeaders;
|
||||
}
|
||||
|
||||
enum Type {
|
||||
PE32,
|
||||
PE32Plus,
|
||||
}
|
||||
|
||||
private static class Magic extends Word {
|
||||
private Magic() {
|
||||
super("Magic");
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
short value = (short)getValue();
|
||||
switch (value) {
|
||||
case 0x10B -> {
|
||||
return Type.PE32;
|
||||
}
|
||||
case 0x20B -> {
|
||||
return Type.PE32Plus;
|
||||
}
|
||||
default -> throw new IllegalStateException("Unsupported magic: " + Integer.toHexString(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Dependent extends Value {
|
||||
private final Magic myMagic;
|
||||
private final DWord myPe32;
|
||||
private final LongLong myPe32Plus;
|
||||
|
||||
private Dependent(Magic magic, Class<DWord> pe32, Class<LongLong> pe32plus, String name) {
|
||||
super(name);
|
||||
myMagic = magic;
|
||||
try {
|
||||
myPe32 = pe32.getDeclaredConstructor(String.class).newInstance(name);
|
||||
myPe32Plus = pe32plus.getDeclaredConstructor(String.class).newInstance(name);
|
||||
}
|
||||
catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Value getCurrent() {
|
||||
return switch (myMagic.getType()) {
|
||||
case PE32 -> myPe32;
|
||||
case PE32Plus -> myPe32Plus;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public long sizeInBytes() {
|
||||
return getCurrent().sizeInBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException {
|
||||
getCurrent().read(stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput stream) throws IOException {
|
||||
getCurrent().write(stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(OutputStreamWriter writer) throws IOException {
|
||||
getCurrent().report(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getValue() {
|
||||
return getCurrent().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value setValue(long value) {
|
||||
getCurrent().setValue(value);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,18 +22,44 @@ package com.pme.exe;
|
||||
* Date: Apr 1, 2006
|
||||
* Time: 1:54:57 PM
|
||||
*/
|
||||
public class ImageSectionHeader extends Bin.Structure{
|
||||
public class ImageSectionHeader extends Bin.Structure {
|
||||
private final CharStringFS myNameTxt;
|
||||
private final DWord myVirtualSize;
|
||||
private final DWord myVirtualAddress;
|
||||
private final DWord mySizeOfRawData;
|
||||
private final DWord myPointerToRawData;
|
||||
|
||||
public ImageSectionHeader() {
|
||||
super("Image section header");
|
||||
addMember( new Txt( "Name", 8 ));
|
||||
addMember( new DWord( "VirtualSize"));
|
||||
addMember( new DWord( "VirtualAddress"));
|
||||
addMember( new DWord( "SizeOfRawData"));
|
||||
addMember( new DWord( "PointerToRawData"));
|
||||
addMember( new DWord( "PointerToRelocations"));
|
||||
addMember( new DWord( "PointerToLineNumbers"));
|
||||
addMember( new Word( "NumberOfRelocations"));
|
||||
addMember( new Word( "NumberOfLineNumbers"));
|
||||
addMember( new DWord( "Characteristics"));
|
||||
addMember(myNameTxt = new CharStringFS("Name", 8));
|
||||
addMember(myVirtualSize = new DWord("VirtualSize"));
|
||||
addMember(myVirtualAddress = new DWord("VirtualAddress"));
|
||||
addMember(mySizeOfRawData = new DWord("SizeOfRawData"));
|
||||
addMember(myPointerToRawData = new DWord("PointerToRawData"));
|
||||
addMember(new DWord("PointerToRelocations"));
|
||||
addMember(new DWord("PointerToLineNumbers"));
|
||||
addMember(new Word("NumberOfRelocations"));
|
||||
addMember(new Word("NumberOfLineNumbers"));
|
||||
addMember(new DWord("Characteristics"));
|
||||
}
|
||||
|
||||
public String getSectionName() {
|
||||
return myNameTxt.getValue();
|
||||
}
|
||||
|
||||
public DWord getVirtualSize() {
|
||||
return myVirtualSize;
|
||||
}
|
||||
|
||||
public DWord getVirtualAddress() {
|
||||
return myVirtualAddress;
|
||||
}
|
||||
|
||||
public DWord getSizeOfRawData() {
|
||||
return mySizeOfRawData;
|
||||
}
|
||||
|
||||
public DWord getPointerToRawData() {
|
||||
return myPointerToRawData;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
package com.pme.exe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Sergey Zhulin
|
||||
@@ -26,10 +26,13 @@ import java.io.DataInput;
|
||||
* Time: 2:10:01 PM
|
||||
*/
|
||||
public class MsDosHeader extends Bin.Structure {
|
||||
private final Word myMagic;
|
||||
private final DWord myPEHeaderOffset;
|
||||
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
public MsDosHeader() {
|
||||
super("MSDOS Header");
|
||||
addMember( new Word( "magic" ), "Magic number" );
|
||||
myMagic = addMember( new Word( "magic" ), "Magic number" );
|
||||
addMember( new Word( "cblp" ), "Bytes on last page of file" );
|
||||
addMember( new Word( "cp" ), "Pages in file" );
|
||||
addMember( new Word( "crlc" ), "Relocations" );
|
||||
@@ -47,15 +50,19 @@ public class MsDosHeader extends Bin.Structure {
|
||||
addMember( new Word( "oemid" ), "OEM identifier (for e_oeminfo)" );
|
||||
addMember( new Word( "oeminfo" ), "OEM information; e_oemid specific" );
|
||||
addMember( new ArrayOfBins<>( "res2", Word.class, 10 ), "Reserved words" );
|
||||
addMember( new DWord( "lfanew" ), "File address of new exe header" );
|
||||
myPEHeaderOffset = addMember( new DWord( "lfanew" ), "File address of new exe header" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException {
|
||||
super.read( stream );
|
||||
long magic = getValue( "magic" );
|
||||
long magic = myMagic.getValue();
|
||||
if (magic != 0x5a4d) {
|
||||
throw new InvalidMsDosHeaderException("First two chars in exe file must be 'MZ'");
|
||||
}
|
||||
}
|
||||
|
||||
public DWord getPEHeaderOffset() {
|
||||
return myPEHeaderOffset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,26 +22,31 @@ package com.pme.exe;
|
||||
* Date: Mar 31, 2006
|
||||
* Time: 5:00:49 PM
|
||||
*/
|
||||
public class PeHeaderReader extends Bin.Structure{
|
||||
public class PeHeaderReader extends Bin.Structure {
|
||||
private final ImageFileHeader myImageFileHeader;
|
||||
private final ImageOptionalHeader myImageOptionalHeader;
|
||||
private final ArrayOfBins<ImageSectionHeader> myImageSectionHeaders;
|
||||
|
||||
public PeHeaderReader(Bin.Value startOffset, ExeFormat exeFormat) {
|
||||
super( "PE Header" );
|
||||
public PeHeaderReader(Bin.Value startOffset) {
|
||||
super("PE Header");
|
||||
addOffsetHolder(startOffset);
|
||||
addMember( new DWord( "Signature" ) );
|
||||
myImageFileHeader = new ImageFileHeader();
|
||||
addMember(myImageFileHeader);
|
||||
if (exeFormat == ExeFormat.UNKNOWN) {
|
||||
return;
|
||||
}
|
||||
addMember(new ImageOptionalHeader(exeFormat));
|
||||
Bin.Value numberOfSections = myImageFileHeader.getValueMember("NumberOfSections");
|
||||
ArrayOfBins<ImageSectionHeader> imageSectionHeaders = new ArrayOfBins<>("ImageSectionHeaders", ImageSectionHeader.class, numberOfSections);
|
||||
imageSectionHeaders.setCountHolder( numberOfSections );
|
||||
addMember( imageSectionHeaders );
|
||||
addMember(new DWord("Signature"));
|
||||
myImageFileHeader = addMember(new ImageFileHeader());
|
||||
myImageOptionalHeader = addMember(new ImageOptionalHeader());
|
||||
Bin.Word numberOfSections = myImageFileHeader.getNumberOfSections();
|
||||
myImageSectionHeaders = addMember(new ArrayOfBins<>("ImageSectionHeaders", ImageSectionHeader.class, numberOfSections));
|
||||
myImageSectionHeaders.setCountHolder(numberOfSections);
|
||||
}
|
||||
|
||||
public ImageFileHeader getImageFileHeader() {
|
||||
return myImageFileHeader;
|
||||
}
|
||||
|
||||
public ImageOptionalHeader getImageOptionalHeader() {
|
||||
return myImageOptionalHeader;
|
||||
}
|
||||
|
||||
public ArrayOfBins<ImageSectionHeader> getImageSectionHeaders() {
|
||||
return myImageSectionHeaders;
|
||||
}
|
||||
}
|
||||
|
||||
65
tools/launcher-generator/src/com/pme/exe/Section.java
Normal file
65
tools/launcher-generator/src/com/pme/exe/Section.java
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2006 ProductiveMe Inc.
|
||||
* Copyright 2013-2022 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.exe.res.ResourceSectionReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
|
||||
public abstract class Section extends Bin.Structure {
|
||||
@SuppressWarnings("SpellCheckingInspection") public static final String RESOURCES_SECTION_NAME = ".rsrc";
|
||||
@SuppressWarnings("SpellCheckingInspection") public static final String RELOCATIONS_SECTION_NAME = ".reloc";
|
||||
|
||||
private final String myName;
|
||||
|
||||
protected Section(ImageSectionHeader header, String name) {
|
||||
super(name);
|
||||
myName = name;
|
||||
addOffsetHolder(header.getPointerToRawData());
|
||||
addSizeHolder(header.getSizeOfRawData());
|
||||
}
|
||||
|
||||
public static Section newSection(ImageSectionHeader header, ImageOptionalHeader imageOptionalHeader) {
|
||||
if (RESOURCES_SECTION_NAME.equals(header.getSectionName())) {
|
||||
return new ResourceSectionReader(header, imageOptionalHeader);
|
||||
}
|
||||
else {
|
||||
return new Simple(header);
|
||||
}
|
||||
}
|
||||
|
||||
public String getSectionName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(OutputStreamWriter writer) throws IOException {
|
||||
_report(writer, "Section name: " + myName);
|
||||
super.report(writer);
|
||||
}
|
||||
|
||||
public static class Simple extends Section {
|
||||
|
||||
public Simple(ImageSectionHeader header) {
|
||||
super(header, header.getSectionName());
|
||||
addMember(new Bin.Bytes("Section Raw Data", header.getSizeOfRawData()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006 ProductiveMe Inc.
|
||||
* Copyright 2013-2022 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.exe.res.ResourceSectionReader;
|
||||
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Sergey Zhulin
|
||||
* Date: Apr 5, 2006
|
||||
* Time: 9:48:35 AM
|
||||
*/
|
||||
public class SectionReader extends Bin.Structure{
|
||||
private final Bin.Txt myName;
|
||||
public SectionReader( ImageSectionHeader sectionHeader, Bin.Value startOffset, Bin.Value mainSectionsOffset, ImageOptionalHeader imageOptionalHeader ) {
|
||||
super("Section");
|
||||
|
||||
myName = (Bin.Txt)sectionHeader.getMember("Name");
|
||||
String sectionName = myName.getText();
|
||||
if ( ".rsrc".equals(sectionName) ){
|
||||
addMember( new ResourceSectionReader( sectionHeader, startOffset, mainSectionsOffset, imageOptionalHeader ) );
|
||||
} else {
|
||||
Bin.Value size = sectionHeader.getValueMember( "SizeOfRawData" );
|
||||
addMember( new Simple( sectionName, startOffset, size ) );
|
||||
}
|
||||
}
|
||||
|
||||
public String getSectionName(){
|
||||
return myName.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(OutputStreamWriter writer) throws IOException {
|
||||
_report( writer, "Section name: " + myName.getText() );
|
||||
super.report(writer);
|
||||
}
|
||||
|
||||
public static class Simple extends Bin.Structure{
|
||||
|
||||
public Simple(String name, Bin.Value startOffset, Bin.Value size) {
|
||||
super(name);
|
||||
addOffsetHolder( startOffset );
|
||||
addSizeHolder( size );
|
||||
addMember( new Bin.Bytes( "Raw data", size ) );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,11 +17,9 @@
|
||||
|
||||
package com.pme.exe.res;
|
||||
|
||||
import com.pme.exe.Bin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Sergey Zhulin
|
||||
@@ -29,29 +27,32 @@ import java.io.DataOutput;
|
||||
* Time: 3:58:05 PM
|
||||
*/
|
||||
public class DataEntry extends LevelEntry {
|
||||
private final ResourceSectionReader mySection;
|
||||
private RawResource myRawResource = null;
|
||||
public DataEntry( ResourceSectionReader section, Bin.Value offsetHolder ) {
|
||||
private final DWord myRVA;
|
||||
private final DWord mySize;
|
||||
private final RawResource myRawResource;
|
||||
private final DWord myLanguage;
|
||||
|
||||
public DataEntry(ResourceSectionReader section, DWord offset, DWord language) {
|
||||
super("DataEntry");
|
||||
addMember(new DWord("RVA"));
|
||||
addMember(new DWord("Size"));
|
||||
myLanguage = language;
|
||||
addMember(myRVA = new DWord("RVA"));
|
||||
addMember(mySize = new DWord("Size"));
|
||||
addMember(new DWord("Code Page"));
|
||||
addMember(new DWord("Reserved"));
|
||||
mySection = section;
|
||||
addOffsetHolder( new ValuesSub( offsetHolder, mySection.getStartOffset() ) );
|
||||
addOffsetHolder(new ValuesSub(offset, section.getStartOffset()));
|
||||
myRawResource = new RawResource(section, myRVA, mySize);
|
||||
}
|
||||
|
||||
public RawResource getRawResource() {
|
||||
return myRawResource;
|
||||
}
|
||||
|
||||
public void initRawData(){
|
||||
myRawResource = new RawResource( mySection, (DWord) getValueMember("RVA"), (DWord) getValueMember("Size"));
|
||||
getLevel().addLevelEntry( myRawResource );
|
||||
public DWord getLanguage() {
|
||||
return myLanguage;
|
||||
}
|
||||
public void insertRawData( int index ){
|
||||
myRawResource = new RawResource( mySection, (DWord) getValueMember("RVA"), (DWord) getValueMember("Size"));
|
||||
getLevel().insertLevelEntry( index, myRawResource );
|
||||
|
||||
public void initRawData(){
|
||||
getLevel().addLevelEntry(myRawResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,4 +65,12 @@ public class DataEntry extends LevelEntry {
|
||||
public void write(DataOutput stream) throws IOException {
|
||||
super.write(stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DataEntry{" +
|
||||
"RVA=" + myRVA +
|
||||
", Size=" + mySize +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,9 @@ package com.pme.exe.res;
|
||||
import com.pme.exe.Bin;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* @author Sergey Zhulin
|
||||
@@ -31,27 +30,40 @@ import java.util.ArrayList;
|
||||
* Time: 11:22:12 PM
|
||||
*/
|
||||
public class DirectoryEntry extends LevelEntry {
|
||||
private ArrayOfBins<EntryDescription> myIdEntries;
|
||||
private static final long DIRECTORY_ENTRY_FLAG = 0xffff_ffff_8000_0000L;
|
||||
public static final int RT_BITMAP = 2;
|
||||
public static final int RT_ICON = 3;
|
||||
public static final int RT_STRING = 6;
|
||||
public static final int RT_GROUP_ICON = RT_ICON + 11;
|
||||
public static final int RT_VERSION = 16;
|
||||
|
||||
private final ArrayOfBins<EntryDescription> myNamedEntries;
|
||||
private final ArrayOfBins<EntryDescription> myIdEntries;
|
||||
private final ResourceSectionReader mySection;
|
||||
private final ArrayList<DirectoryEntry> mySubDirs = new ArrayList<>();
|
||||
private final ArrayList<DataEntry> myData = new ArrayList<>();
|
||||
private final long myIdOrName;
|
||||
|
||||
public DirectoryEntry(ResourceSectionReader section, EntryDescription entry, long idOrName) {
|
||||
super( createName( entry ) );
|
||||
super(String.valueOf(idOrName));
|
||||
myIdOrName = idOrName;
|
||||
addMember(new DWord("Characteristics"));
|
||||
addMember(new DWord("TimeDateStamp"));
|
||||
addMember(new Word("MajorVersion"));
|
||||
addMember(new Word("MinorVersion"));
|
||||
addMember(new Word("NumberOfNamedEntries"));
|
||||
addMember(new Word("NumberOfIdEntries"));
|
||||
Word numberOfNamedEntries = addMember(new Word("NumberOfNamedEntries"));
|
||||
Word numberOfIdEntries = addMember(new Word("NumberOfIdEntries"));
|
||||
mySection = section;
|
||||
if ( entry != null ){
|
||||
Value flagValue = new DWord( "flag" ).setValue(0xffff_ffff_8000_0000L);
|
||||
Bin.Value offset = entry.getValueMember("OffsetToData");
|
||||
addOffsetHolder( new ValuesSub( offset, new ValuesAdd( mySection.getStartOffset(), flagValue ) ));
|
||||
if (entry != null) {
|
||||
Value flagValue = new DWord("flag").setValue(DIRECTORY_ENTRY_FLAG);
|
||||
Bin.Value offset = entry.getOffsetToData();
|
||||
addOffsetHolder(new ValuesSub(offset, new ValuesAdd(mySection.getStartOffset(), flagValue)));
|
||||
}
|
||||
myNamedEntries = addMember(new ArrayOfBins<>("Named entries", EntryDescription.class, numberOfNamedEntries));
|
||||
myNamedEntries.setCountHolder(numberOfNamedEntries);
|
||||
|
||||
myIdEntries = addMember(new ArrayOfBins<>("Id entries", EntryDescription.class, numberOfIdEntries));
|
||||
myIdEntries.setCountHolder(numberOfIdEntries);
|
||||
}
|
||||
|
||||
public long getIdOrName() {
|
||||
@@ -62,103 +74,72 @@ public class DirectoryEntry extends LevelEntry {
|
||||
return mySection;
|
||||
}
|
||||
|
||||
public static String createName( EntryDescription entry ){
|
||||
String name = "IRD";
|
||||
if ( entry != null ){
|
||||
name += entry.getValueMember( "Name" ).getValue();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public ArrayList<DirectoryEntry> getSubDirs() {
|
||||
return mySubDirs;
|
||||
}
|
||||
|
||||
public DirectoryEntry findSubDir( String name ){
|
||||
public ArrayList<DataEntry> getData() {
|
||||
return myData;
|
||||
}
|
||||
|
||||
public DirectoryEntry findSubDir(long id) {
|
||||
for (DirectoryEntry directoryEntry : mySubDirs) {
|
||||
if (directoryEntry.getName().equals(name)) {
|
||||
if (directoryEntry.getIdOrName() == id) {
|
||||
return directoryEntry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public RawResource getRawResource(int index) {
|
||||
DataEntry dataEntry = myData.get(index);
|
||||
public RawResource getRawResource() {
|
||||
Iterator<DataEntry> iterator = myData.iterator();
|
||||
assert iterator.hasNext();
|
||||
DataEntry dataEntry = iterator.next();
|
||||
assert !iterator.hasNext();
|
||||
return dataEntry.getRawResource();
|
||||
}
|
||||
|
||||
public void insertDataEntry( int index, DataEntry dataEntry ){
|
||||
myData.add(dataEntry );
|
||||
getLevel().insertLevelEntry( index, dataEntry );
|
||||
public void addDataEntry(DataEntry dataEntry) {
|
||||
getLevel().addLevelEntry(dataEntry);
|
||||
myData.add(dataEntry);
|
||||
}
|
||||
|
||||
public void addDataEntry( DataEntry dataEntry ){
|
||||
myData.add(dataEntry );
|
||||
getLevel().addLevelEntry( dataEntry );
|
||||
public void addDirectoryEntry(DirectoryEntry dir) {
|
||||
getLevel().addLevelEntry(dir);
|
||||
mySubDirs.add(dir);
|
||||
}
|
||||
|
||||
public void insertDirectoryEntry( int index, DirectoryEntry dir ){
|
||||
getLevel().insertLevelEntry( index, dir );
|
||||
mySubDirs.add( dir );
|
||||
}
|
||||
|
||||
public void addDirectoryEntry( DirectoryEntry dir ){
|
||||
getLevel().addLevelEntry( dir );
|
||||
mySubDirs.add( dir );
|
||||
}
|
||||
|
||||
public void addIdEntry( EntryDescription entry ){
|
||||
if ( myIdEntries == null ){
|
||||
Word numberOfNamedEntries = (Word) getMember("NumberOfIdEntries");
|
||||
myIdEntries = new ArrayOfBins<>("Id entries", EntryDescription.class, 0);
|
||||
addMember(myIdEntries);
|
||||
myIdEntries.setCountHolder(numberOfNamedEntries);
|
||||
}
|
||||
myIdEntries.addBin( entry );
|
||||
public void addIdEntry(EntryDescription entry) {
|
||||
myIdEntries.addBin(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException {
|
||||
super.read(stream);
|
||||
|
||||
Word numberOfNamedEntries = (Word) getMember("NumberOfNamedEntries");
|
||||
ArrayOfBins<EntryDescription> namedEntries = new ArrayOfBins<>("Named entries", EntryDescription.class, numberOfNamedEntries);
|
||||
addMember(namedEntries);
|
||||
namedEntries.setCountHolder(numberOfNamedEntries);
|
||||
namedEntries.read(stream);
|
||||
|
||||
Word numberOfIdEntries = (Word) getMember("NumberOfIdEntries");
|
||||
myIdEntries = new ArrayOfBins<>("Id entries", EntryDescription.class, numberOfIdEntries);
|
||||
addMember(myIdEntries);
|
||||
myIdEntries.setCountHolder(numberOfIdEntries);
|
||||
myIdEntries.read(stream);
|
||||
|
||||
processEntries(namedEntries);
|
||||
processEntries(myNamedEntries);
|
||||
processEntries(myIdEntries);
|
||||
}
|
||||
|
||||
private void processEntries(ArrayOfBins<EntryDescription> entries) {
|
||||
for (int i = 0; i < entries.size(); ++i) {
|
||||
EntryDescription entry = entries.get(i);
|
||||
Bin.Value offset = entry.getValueMember("OffsetToData");
|
||||
Bin.Value name = entry.getValueMember("Name");
|
||||
if ((offset.getValue() & 0xffff_ffff_8000_0000L) != 0) {
|
||||
addDirectoryEntry( new DirectoryEntry( mySection, entry, name.getValue()) );
|
||||
} else {
|
||||
addDataEntry( new DataEntry( mySection, offset) );
|
||||
for (EntryDescription entry : entries) {
|
||||
DWord offset = entry.getOffsetToData();
|
||||
DWord name = entry.getNameW();
|
||||
if ((offset.getValue() & DIRECTORY_ENTRY_FLAG) != 0) {
|
||||
addDirectoryEntry(new DirectoryEntry(mySection, entry, name.getValue()));
|
||||
}
|
||||
else {
|
||||
addDataEntry(new DataEntry(mySection, offset, name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(OutputStreamWriter writer) throws IOException {
|
||||
super.report(writer);
|
||||
public String toString() {
|
||||
return "DirectoryEntry{" +
|
||||
", idOrName=" + myIdOrName +
|
||||
", subs=" + mySubDirs +
|
||||
", data=" + myData +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput stream) throws IOException {
|
||||
super.write(stream);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2006 ProductiveMe Inc.
|
||||
* Copyright 2013-2018 JetBrains s.r.o.
|
||||
* Copyright 2013-2022 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.
|
||||
@@ -25,9 +25,20 @@ import com.pme.exe.Bin;
|
||||
* Time: 3:59:15 PM
|
||||
*/
|
||||
public class EntryDescription extends Bin.Structure {
|
||||
private final DWord myNameW;
|
||||
private final DWord myOffsetToData;
|
||||
|
||||
public EntryDescription() {
|
||||
super("Entry");
|
||||
addMember(new DWord("Name"));
|
||||
addMember(new DWord("OffsetToData"));
|
||||
addMember(myNameW = new DWord("Name"));
|
||||
addMember(myOffsetToData = new DWord("OffsetToData"));
|
||||
}
|
||||
|
||||
public DWord getNameW() {
|
||||
return myNameW;
|
||||
}
|
||||
|
||||
public DWord getOffsetToData() {
|
||||
return myOffsetToData;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,11 @@ package com.pme.exe.res;
|
||||
|
||||
import com.pme.exe.Bin;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author Sergey Zhulin
|
||||
@@ -28,33 +32,28 @@ import java.io.*;
|
||||
*/
|
||||
public class Level extends Bin.Structure {
|
||||
private Level mySubLevel = null;
|
||||
private final int myDepth;
|
||||
|
||||
public Level() {
|
||||
super("Level");
|
||||
public Level(int depth) {
|
||||
super("Level" + depth);
|
||||
myDepth = depth;
|
||||
}
|
||||
|
||||
public void addLevelEntry(LevelEntry dir) {
|
||||
if (mySubLevel == null) {
|
||||
mySubLevel = new Level();
|
||||
mySubLevel = new Level(myDepth + 1);
|
||||
}
|
||||
dir.setLevel(mySubLevel);
|
||||
addMember(dir);
|
||||
}
|
||||
|
||||
public void insertLevelEntry(int index, LevelEntry dir) {
|
||||
if (mySubLevel == null) {
|
||||
mySubLevel = new Level();
|
||||
}
|
||||
dir.setLevel(mySubLevel);
|
||||
insertMember(index,dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long sizeInBytes() {
|
||||
long size = super.sizeInBytes();
|
||||
if (mySubLevel != null) {
|
||||
return super.sizeInBytes() + mySubLevel.sizeInBytes();
|
||||
size += mySubLevel.sizeInBytes();
|
||||
}
|
||||
return super.sizeInBytes();
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,6 +66,7 @@ public class Level extends Bin.Structure {
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException {
|
||||
sortMembersIfNeeded();
|
||||
super.read(stream);
|
||||
if (mySubLevel != null) {
|
||||
mySubLevel.read(stream);
|
||||
@@ -81,6 +81,19 @@ public class Level extends Bin.Structure {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("SSBasedInspection")
|
||||
private void sortMembersIfNeeded() {
|
||||
ArrayList<Bin> members = getMembers();
|
||||
if (members.stream().allMatch(bin -> bin instanceof RawResource)) {
|
||||
// Somehow RawResources could go out of order on disk, let's sort them, so we won't need to seek them in input stream
|
||||
members.sort((o1, o2) -> {
|
||||
assert o1 instanceof RawResource;
|
||||
assert o2 instanceof RawResource;
|
||||
return Long.compare(((RawResource)o1).getRawOffset().getValue(), ((RawResource)o2).getRawOffset().getValue());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(OutputStreamWriter writer) throws IOException {
|
||||
super.report(writer);
|
||||
@@ -88,4 +101,13 @@ public class Level extends Bin.Structure {
|
||||
mySubLevel.report(writer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Level{" +
|
||||
"depth=" + myDepth +
|
||||
", members_num=" + getMembers().size() +
|
||||
", sub=" + mySubLevel +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2006 ProductiveMe Inc.
|
||||
* Copyright 2013-2018 JetBrains s.r.o.
|
||||
* Copyright 2013-2022 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.
|
||||
@@ -23,20 +23,39 @@ package com.pme.exe.res;
|
||||
* Time: 3:12:58 PM
|
||||
*/
|
||||
public class RawResource extends LevelEntry {
|
||||
private final Bytes myRawResource;
|
||||
private final DWord myVirtualAddress;
|
||||
private final DWord mySize;
|
||||
private final Value myRawOffset;
|
||||
|
||||
public RawResource(ResourceSectionReader section, DWord rva, DWord size) {
|
||||
super("Raw Resource");
|
||||
Value offsetHolder = new ValuesAdd(rva, section.getMainSectionsOffset());
|
||||
Bytes bytes = new Bytes("Raw Resource", offsetHolder, size);
|
||||
bytes.addOffsetHolder(offsetHolder);
|
||||
bytes.addSizeHolder(size);
|
||||
addMember(bytes);
|
||||
myVirtualAddress = rva;
|
||||
mySize = size;
|
||||
myRawOffset = new ValuesAdd(myVirtualAddress, section.getSectionVAtoRawOffset());
|
||||
myRawResource = new Bytes("Raw Resource Data", myRawOffset, size);
|
||||
myRawResource.addOffsetHolder(myRawOffset);
|
||||
myRawResource.addSizeHolder(size);
|
||||
addMember(myRawResource);
|
||||
addMember(new Padding("Resource Padding", 8));
|
||||
}
|
||||
public Bytes getBytes(){
|
||||
return (Bytes)getMember( "Raw Resource" );
|
||||
public byte[] getBytes(){
|
||||
return myRawResource.getBytes();
|
||||
}
|
||||
public void setBytes( byte[] bytes ){
|
||||
Bytes mem = (Bytes) getMember("Raw Resource");
|
||||
mem.setBytes( bytes );
|
||||
myRawResource.setBytes(bytes);
|
||||
}
|
||||
|
||||
public Value getRawOffset() {
|
||||
return myRawOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RawResource{" +
|
||||
"VirtualAddress=" + myVirtualAddress +
|
||||
", RawOffset=" + myRawOffset +
|
||||
", Size=" + mySize +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,94 +18,45 @@
|
||||
package com.pme.exe.res;
|
||||
|
||||
import com.pme.exe.Bin;
|
||||
import com.pme.exe.ImageDataDirectory;
|
||||
import com.pme.exe.ImageOptionalHeader;
|
||||
import com.pme.exe.ImageSectionHeader;
|
||||
import com.pme.exe.Section;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* @author Sergey Zhulin
|
||||
* Date: Apr 6, 2006
|
||||
* Time: 7:10:09 PM
|
||||
*/
|
||||
public class ResourceSectionReader extends Bin.Structure {
|
||||
private final Level myRoot = new Level();
|
||||
private final Bin.Value myStartOffset;
|
||||
private final Bin.Value myMainSectionsOffset;
|
||||
private final Bin.Value mySize;
|
||||
private Bin.Bytes myBytes;
|
||||
private long myFileAlignment;
|
||||
public class ResourceSectionReader extends Section {
|
||||
private final Level myRoot = new Level(0);
|
||||
private final Bin.DWord myStartOffset;
|
||||
private final Bin.DWord mySectionVAtoRawOffset;
|
||||
|
||||
public ResourceSectionReader( ImageSectionHeader sectionHeader, Bin.Value startOffset, Bin.Value mainSectionsOffset, ImageOptionalHeader imageOptionalHeader) {
|
||||
super(((Bin.Txt)sectionHeader.getMember("Name")).getText());
|
||||
myStartOffset = startOffset;
|
||||
mySize = sectionHeader.getValueMember( "SizeOfRawData" );
|
||||
Bin.Value virtualSize = sectionHeader.getValueMember( "VirtualSize" );
|
||||
myMainSectionsOffset = mainSectionsOffset;
|
||||
addOffsetHolder(startOffset);
|
||||
myFileAlignment = imageOptionalHeader.getValue("FileAlignment");
|
||||
public ResourceSectionReader(ImageSectionHeader header, ImageOptionalHeader imageOptionalHeader) {
|
||||
super(header, header.getSectionName());
|
||||
myStartOffset = header.getPointerToRawData();
|
||||
mySectionVAtoRawOffset = new ValuesAdd(header.getVirtualAddress(), myStartOffset);
|
||||
|
||||
//noinspection unchecked
|
||||
ArrayOfBins<ImageDataDirectory> imageDataDirs = (ArrayOfBins<ImageDataDirectory>)imageOptionalHeader.getMember( "ImageDataDirectories" );
|
||||
Bin[] bins = imageDataDirs.getArray();
|
||||
ImageDataDirectory imageDataDirectory = (ImageDataDirectory)bins[2];
|
||||
Value size = imageDataDirectory.getValueMember("Size");
|
||||
|
||||
addSizeHolder(mySize);
|
||||
addSizeHolder(virtualSize);
|
||||
addSizeHolder(size);
|
||||
myRoot.addLevelEntry(new DirectoryEntry( this, null, 0));
|
||||
myRoot.addLevelEntry(new DirectoryEntry(this, null, 0));
|
||||
addMember(myRoot);
|
||||
addMember(new Padding("FileAlignment in ResourceSection", (int)imageOptionalHeader.getFileAlignment().getValue()));
|
||||
addSizeHolder(header.getVirtualSize());
|
||||
}
|
||||
|
||||
public DirectoryEntry getRoot(){
|
||||
return (DirectoryEntry)myRoot.getMember( "IRD" );
|
||||
public DirectoryEntry getRoot() {
|
||||
Iterator<Bin> iterator = myRoot.getMembers().iterator();
|
||||
DirectoryEntry entry = (DirectoryEntry)iterator.next();
|
||||
assert !iterator.hasNext();
|
||||
return entry;
|
||||
}
|
||||
|
||||
public Value getStartOffset() {
|
||||
public DWord getStartOffset() {
|
||||
return myStartOffset;
|
||||
}
|
||||
|
||||
public Value getMainSectionsOffset() {
|
||||
return myMainSectionsOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long sizeInBytes() {
|
||||
long size = super.sizeInBytes() + myBytes.sizeInBytes();
|
||||
if (size % myFileAlignment != 0)
|
||||
size = (size / myFileAlignment + 1) * myFileAlignment;
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException {
|
||||
super.read(stream);
|
||||
DWord size = new DWord("size");
|
||||
size.setValue((mySize.getValue() - myRoot.sizeInBytes()));
|
||||
DWord startOffset = new DWord("startOffset");
|
||||
startOffset.setValue((myStartOffset.getValue() + myRoot.sizeInBytes()));
|
||||
myBytes = new Bin.Bytes("Raw data", startOffset, size);
|
||||
myBytes.read(stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput stream) throws IOException {
|
||||
super.write(stream);
|
||||
myBytes.write(stream);
|
||||
|
||||
long paddingSize = sizeInBytes() - super.sizeInBytes() - myBytes.sizeInBytes();
|
||||
if (paddingSize != 0)
|
||||
stream.write(new byte[(int)paddingSize]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(OutputStreamWriter writer) throws IOException {
|
||||
super.report(writer);
|
||||
myBytes.report(writer);
|
||||
public DWord getSectionVAtoRawOffset() {
|
||||
return mySectionVAtoRawOffset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
package com.pme.exe.res;
|
||||
|
||||
import com.pme.exe.Bin;
|
||||
import com.pme.exe.Bin.WCharStringSP;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
@@ -27,41 +27,30 @@ import java.io.*;
|
||||
* Time: 12:43:28 PM
|
||||
*/
|
||||
public class StringTable {
|
||||
final String[] strings = new String[16];
|
||||
final WCharStringSP[] strings = new WCharStringSP[16];
|
||||
|
||||
public StringTable(byte[] bytes) throws IOException {
|
||||
ByteArrayInputStream bytesStream = new ByteArrayInputStream(bytes);
|
||||
DataInputStream stream = new DataInputStream(bytesStream);
|
||||
Bin.Word count = new Bin.Word();
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
count.read(stream);
|
||||
if ( count.getValue() != 0 ){
|
||||
Bin.Txt txt = new Bin.Txt("", (int)(count.getValue() * 2));
|
||||
txt.read( stream );
|
||||
strings[i] = txt.getText();
|
||||
} else {
|
||||
strings[i] = "";
|
||||
}
|
||||
strings[i] = new WCharStringSP();
|
||||
strings[i].read(stream);
|
||||
}
|
||||
}
|
||||
|
||||
public void setString( int index, String string ){
|
||||
strings[index] = string;
|
||||
strings[index].setValue(string);
|
||||
}
|
||||
|
||||
public byte[] getBytes() throws IOException {
|
||||
int size = 0;
|
||||
for (String string : strings) {
|
||||
size += 2 + string.length() * 2;
|
||||
for (WCharStringSP string : strings) {
|
||||
size += string.sizeInBytes();
|
||||
}
|
||||
ByteArrayOutputStream bytesStream = new ByteArrayOutputStream(size);
|
||||
DataOutputStream stream = new DataOutputStream(bytesStream);
|
||||
for (String string : strings) {
|
||||
int count = string.length();
|
||||
new Bin.Word().setValue(count).write(stream);
|
||||
if (count != 0) {
|
||||
new Bin.Txt("", string).write(stream);
|
||||
}
|
||||
for (WCharStringSP string : strings) {
|
||||
string.write(stream);
|
||||
}
|
||||
return bytesStream.toByteArray();
|
||||
}
|
||||
|
||||
@@ -20,24 +20,20 @@ public class StringTableDirectory {
|
||||
for (DirectoryEntry subDir : directoryEntry.getSubDirs()) {
|
||||
Entry e = new Entry();
|
||||
e.startID = (int) subDir.getIdOrName();
|
||||
e.resource = subDir.getRawResource(0);
|
||||
e.table = new StringTable(e.resource.getBytes().getBytes());
|
||||
e.resource = subDir.getRawResource();
|
||||
e.table = new StringTable(e.resource.getBytes());
|
||||
myEntries.add(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setString(int id, String value) {
|
||||
boolean found = false;
|
||||
for (Entry entry : myEntries) {
|
||||
if (entry.startID == (id / 16)+1) {
|
||||
entry.table.setString(id % 16, value);
|
||||
found = true;
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
throw new IllegalArgumentException("Cannot find string entry with ID " + id);
|
||||
}
|
||||
throw new IllegalArgumentException("Cannot find string entry with ID " + id);
|
||||
}
|
||||
|
||||
public void save() throws IOException {
|
||||
|
||||
@@ -35,8 +35,9 @@ public class ValuesAdd extends Bin.DWord {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value setValue(long value) {
|
||||
return myActual.setValue(value + myMinus.getValue());
|
||||
public DWord setValue(long value) {
|
||||
myActual.setValue(value + myMinus.getValue());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,8 +35,9 @@ public class ValuesSub extends Bin.DWord {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value setValue(long value) {
|
||||
return myActual.setValue(value - myMinus.getValue());
|
||||
public DWord setValue(long value) {
|
||||
myActual.setValue(value - myMinus.getValue());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
package com.pme.exe.res.icon;
|
||||
|
||||
import com.pme.exe.Bin;
|
||||
import com.pme.exe.res.Level;
|
||||
import com.pme.exe.res.LevelEntry;
|
||||
|
||||
/**
|
||||
* @author Sergey Zhulin
|
||||
@@ -27,17 +25,23 @@ import com.pme.exe.res.LevelEntry;
|
||||
* Time: 11:35:48 AM
|
||||
*/
|
||||
public class GroupIconResource extends Bin.Structure {
|
||||
public GroupIconResource( Bin.Value idCount ) {
|
||||
|
||||
private final ArrayOfBins<GroupIconResourceDirectory> myIcons;
|
||||
private final IconHeader myHeader;
|
||||
|
||||
public GroupIconResource() {
|
||||
super("GroupIcon");
|
||||
addMember(new IconHeader());
|
||||
Level level = new Level();
|
||||
ArrayOfBins<GroupIconResourceDirectory> arrayOfBins = new ArrayOfBins<>("Icon directories", GroupIconResourceDirectory.class, idCount);
|
||||
myHeader = addMember(new IconHeader());
|
||||
Word count = myHeader.getCount();
|
||||
myIcons = addMember(new ArrayOfBins<>("Icon directories", GroupIconResourceDirectory.class, count));
|
||||
myIcons.setCountHolder(count);
|
||||
}
|
||||
|
||||
addMember(level);
|
||||
Bin[] array = arrayOfBins.getArray();
|
||||
for (Bin bin : array) {
|
||||
level.addLevelEntry((LevelEntry) bin);
|
||||
}
|
||||
public IconHeader getHeader() {
|
||||
return myHeader;
|
||||
}
|
||||
|
||||
public ArrayOfBins<GroupIconResourceDirectory> getIcons() {
|
||||
return myIcons;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2006 ProductiveMe Inc.
|
||||
* Copyright 2013-2018 JetBrains s.r.o.
|
||||
* Copyright 2013-2022 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.
|
||||
@@ -17,23 +17,20 @@
|
||||
|
||||
package com.pme.exe.res.icon;
|
||||
|
||||
import com.pme.exe.res.LevelEntry;
|
||||
|
||||
/**
|
||||
* @author Sergey Zhulin
|
||||
* Date: Apr 28, 2006
|
||||
* Time: 11:38:20 AM
|
||||
*/
|
||||
public class GroupIconResourceDirectory extends LevelEntry {
|
||||
public class GroupIconResourceDirectory extends IconBase {
|
||||
private final Word myDwId;
|
||||
|
||||
public GroupIconResourceDirectory() {
|
||||
super("Icon Directory");
|
||||
addMember( new Byte( "bWidth" ) );
|
||||
addMember( new Byte( "bHeight" ) );
|
||||
addMember( new Byte( "bColorCount" ) );
|
||||
addMember( new Byte( "bReserved" ) );
|
||||
addMember( new Word( "wPlanes" ) );
|
||||
addMember( new Word( "wBitCount" ) );
|
||||
addMember( new DWord( "dwBytesInRes" ) );
|
||||
addMember( new Word( "dwId" ) );
|
||||
myDwId = addMember(new Word("dwId" ) );
|
||||
}
|
||||
|
||||
public Word getDwId() {
|
||||
return myDwId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2006 ProductiveMe Inc.
|
||||
* Copyright 2013-2018 JetBrains s.r.o.
|
||||
* Copyright 2013-2022 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.
|
||||
@@ -18,19 +18,19 @@
|
||||
package com.pme.exe.res.icon;
|
||||
|
||||
import com.pme.exe.res.LevelEntry;
|
||||
import com.pme.exe.Bin;
|
||||
|
||||
/**
|
||||
* @author Sergey Zhulin
|
||||
* Date: Apr 27, 2006
|
||||
* Time: 1:44:11 PM
|
||||
*/
|
||||
public class RawBytes extends LevelEntry {
|
||||
public RawBytes( Bin.Value offsetHolder, Bin.Value size ) {
|
||||
super("Raw Bytes");
|
||||
Bytes bytes = new Bytes("Raw Bytes", offsetHolder, size);
|
||||
bytes.addOffsetHolder( offsetHolder );
|
||||
bytes.addSizeHolder( size );
|
||||
addMember( bytes );
|
||||
|
||||
public abstract class IconBase extends LevelEntry {
|
||||
protected final DWord myDwBytesInRes;
|
||||
|
||||
public IconBase(String name) {
|
||||
super(name);
|
||||
addMember(new Byte("bWidth"));
|
||||
addMember(new Byte("bHeight"));
|
||||
addMember(new Byte("bColorCount"));
|
||||
addMember(new Byte("bReserved"));
|
||||
addMember(new Word("wPlanes"));
|
||||
addMember(new Word("wBitCount"));
|
||||
myDwBytesInRes = addMember(new DWord("dwBytesInRes"));
|
||||
}
|
||||
}
|
||||
@@ -17,41 +17,23 @@
|
||||
|
||||
package com.pme.exe.res.icon;
|
||||
|
||||
import com.pme.exe.res.LevelEntry;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Sergey Zhulin
|
||||
* Date: Apr 27, 2006
|
||||
* Time: 1:13:15 PM
|
||||
*/
|
||||
public class IconDirectory extends LevelEntry {
|
||||
private RawBytes myRawBytes;
|
||||
public class IconDirectory extends IconBase {
|
||||
private final Bytes myBytes;
|
||||
|
||||
public IconDirectory() {
|
||||
super("Icon Directory");
|
||||
addMember( new Byte( "bWidth" ) );
|
||||
addMember( new Byte( "bHeight" ) );
|
||||
addMember( new Byte( "bColorCount" ) );
|
||||
addMember( new Byte( "bReserved" ) );
|
||||
addMember( new Word( "wPlanes" ) );
|
||||
addMember( new Word( "wBitCount" ) );
|
||||
addMember( new DWord( "dwBytesInRes" ) );
|
||||
addMember( new DWord( "dwImageOffset" ) );
|
||||
DWord dwImageOffset = addMember(new DWord("dwImageOffset"));
|
||||
myBytes = new Bytes("Raw Bytes internal", dwImageOffset, myDwBytesInRes);
|
||||
myBytes.addOffsetHolder(dwImageOffset);
|
||||
myBytes.addSizeHolder(myDwBytesInRes);
|
||||
}
|
||||
|
||||
public byte[] getRawBytes(){
|
||||
Bytes bytes = (Bytes)myRawBytes.getMember( "Raw Bytes" );
|
||||
return bytes.getBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException {
|
||||
super.read(stream);
|
||||
RawBytes bytes = new RawBytes( getValueMember( "dwImageOffset" ),
|
||||
getValueMember( "dwBytesInRes" ));
|
||||
myRawBytes = bytes;
|
||||
getLevel().addLevelEntry( bytes );
|
||||
public Bytes getBytes() {
|
||||
return myBytes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
package com.pme.exe.res.icon;
|
||||
|
||||
import com.pme.exe.Bin;
|
||||
import com.pme.exe.res.Level;
|
||||
import com.pme.exe.res.LevelEntry;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
@@ -29,43 +27,54 @@ import java.io.*;
|
||||
* Time: 12:52:09 PM
|
||||
*/
|
||||
public class IconFile extends Bin.Structure {
|
||||
private final Level myImages = new Level();
|
||||
private final File myFile;
|
||||
private final IconHeader myHeader;
|
||||
private final ArrayOfBins<IconDirectory> myIcons;
|
||||
|
||||
public static class IconWrongFormat extends IOException {
|
||||
public IconWrongFormat( File file ) {
|
||||
public IconWrongFormat(File file) {
|
||||
super("Icon file has wrong format:" + file.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
public IconFile(File file) {
|
||||
super(file.getName());
|
||||
myFile = file;
|
||||
addMember(new IconHeader());
|
||||
public IconFile() {
|
||||
super("IconFile");
|
||||
myHeader = addMember(new IconHeader());
|
||||
Word idCount = myHeader.getCount();
|
||||
myIcons = addMember(new ArrayOfBins<>("Icon directories", IconDirectory.class, idCount));
|
||||
myIcons.setCountHolder(idCount);
|
||||
}
|
||||
|
||||
public void read() throws IOException {
|
||||
try (RandomAccessFile stream = new RandomAccessFile(myFile, "r")) {
|
||||
public IconHeader getHeader() {
|
||||
return myHeader;
|
||||
}
|
||||
|
||||
public ArrayOfBins<IconDirectory> getIcons() {
|
||||
return myIcons;
|
||||
}
|
||||
|
||||
public void read(File file) throws IOException {
|
||||
setName(file.getName());
|
||||
try (RandomAccessFile stream = new RandomAccessFile(file, "r")) {
|
||||
read(stream);
|
||||
}
|
||||
catch (IOException exception) {
|
||||
throw new IconWrongFormat(file);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException {
|
||||
try {
|
||||
super.read(stream);
|
||||
Word idCount = (Word) ((Bin.Structure) getMember("Header")).getMember("idCount");
|
||||
ArrayOfBins<IconDirectory> iconDirs = new ArrayOfBins<>("Icon directories", IconDirectory.class, idCount);
|
||||
iconDirs.setCountHolder(idCount);
|
||||
addMember(myImages);
|
||||
Bin[] array = iconDirs.getArray();
|
||||
for (Bin bin : array) {
|
||||
myImages.addLevelEntry((LevelEntry) bin);
|
||||
}
|
||||
myImages.read(stream);
|
||||
super.read(stream);
|
||||
for (IconDirectory icon : myIcons) {
|
||||
icon.getBytes().read(stream);
|
||||
}
|
||||
catch (IOException exception) {
|
||||
throw new IconWrongFormat(myFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput stream) throws IOException {
|
||||
super.write(stream);
|
||||
for (IconDirectory icon : myIcons) {
|
||||
icon.getBytes().write(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2006 ProductiveMe Inc.
|
||||
* Copyright 2013-2018 JetBrains s.r.o.
|
||||
* Copyright 2013-2022 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.
|
||||
@@ -25,10 +25,16 @@ import com.pme.exe.Bin;
|
||||
* Time: 11:30:10 AM
|
||||
*/
|
||||
public class IconHeader extends Bin.Structure{
|
||||
private final Word myCount;
|
||||
|
||||
public IconHeader() {
|
||||
super("Header");
|
||||
super("Icon Header");
|
||||
addMember(new Word("idReserved"));
|
||||
addMember(new Word("idType"));
|
||||
addMember(new Word("idCount"));
|
||||
myCount = addMember(new Word("idCount"));
|
||||
}
|
||||
|
||||
public Word getCount() {
|
||||
return myCount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,11 @@ package com.pme.exe.res.icon;
|
||||
|
||||
import com.pme.exe.Bin;
|
||||
import com.pme.exe.res.*;
|
||||
import com.pme.util.OffsetTrackingInputStream;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.io.*;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Sergey Zhulin
|
||||
@@ -33,70 +32,109 @@ import java.util.ArrayList;
|
||||
*/
|
||||
public class IconResourceInjector {
|
||||
|
||||
public void injectIcon(File file, DirectoryEntry root, String iconId) throws IOException {
|
||||
IconFile iconFile = new IconFile(file);
|
||||
iconFile.read();
|
||||
public static void injectIcon(InputStream stream, DirectoryEntry root, int iconId) throws IOException {
|
||||
IconFile iconFile = new IconFile();
|
||||
iconFile.read(new OffsetTrackingInputStream(new DataInputStream(stream)));
|
||||
|
||||
//updateGroupIcon(root, iconId, iconFile);
|
||||
long languageId = getLanguage(root);
|
||||
|
||||
DirectoryEntry iconsDir = root.findSubDir("IRD3");
|
||||
Level iconFileLevel = (Level) iconFile.getMember("Level");
|
||||
ArrayList<Bin> icons = iconFileLevel.getMembers();
|
||||
if (icons.size() == iconsDir.getSubDirs().size()) {
|
||||
for (int i = 0; i < icons.size(); i++) {
|
||||
DirectoryEntry subDirIcon = iconsDir.findSubDir("IRD" + (i+1));
|
||||
IconDirectory iconDirectory = (IconDirectory) iconFileLevel.getMembers().get(i);
|
||||
RawResource rawResource = subDirIcon.getRawResource(0);
|
||||
rawResource.setBytes(iconDirectory.getRawBytes());
|
||||
DirectoryEntry iconsDir = root.findSubDir(DirectoryEntry.RT_ICON);
|
||||
Bin.ArrayOfBins<IconDirectory> sourceIcons = iconFile.getIcons();
|
||||
if (sourceIcons.size() < iconsDir.getSubDirs().size()) {
|
||||
throw new IOException(
|
||||
String.format("Number of icons in template file '%d' is larger than in provided icon file '%d'",
|
||||
iconsDir.getSubDirs().size(), sourceIcons.size()));
|
||||
}
|
||||
|
||||
updateGroupIcon(root, iconId, iconFile);
|
||||
|
||||
for (int i = 0; i < sourceIcons.size(); i++) {
|
||||
IconDirectory iconDirectory = sourceIcons.get(i);
|
||||
DirectoryEntry subDirIcon = iconsDir.findSubDir(i + 1);
|
||||
if (subDirIcon == null) {
|
||||
subDirIcon = insertIcon(iconsDir, i+1, languageId);
|
||||
}
|
||||
subDirIcon.getRawResource().setBytes(iconDirectory.getBytes().getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
private static long getLanguage(DirectoryEntry root) {
|
||||
// Language ID is Name of third level entries
|
||||
Set<Long> languages = new HashSet<>();
|
||||
|
||||
// First search across icons only
|
||||
{
|
||||
DirectoryEntry first = root.findSubDir(DirectoryEntry.RT_ICON);
|
||||
for (DirectoryEntry second : first.getSubDirs()) {
|
||||
for (DataEntry third : second.getData()) {
|
||||
languages.add(third.getLanguage().getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IOException("Count of icons in template file doesn't match the count of icons in provided icon file");
|
||||
if (languages.size() == 1) {
|
||||
return languages.iterator().next();
|
||||
}
|
||||
|
||||
// Then across whole file
|
||||
for (DirectoryEntry first : root.getSubDirs()) {
|
||||
for (DirectoryEntry second : first.getSubDirs()) {
|
||||
for (DataEntry third : second.getData()) {
|
||||
languages.add(third.getLanguage().getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (languages.size() != 1) {
|
||||
throw new IllegalStateException("There should be only one language in resources, but found: " + languages);
|
||||
}
|
||||
return languages.iterator().next();
|
||||
}
|
||||
|
||||
private static void updateGroupIcon(DirectoryEntry root, String iconId, IconFile iconFile) throws IOException {
|
||||
DirectoryEntry subDirGroupIcon = root.findSubDir("IRD14").findSubDir(iconId);
|
||||
RawResource groupIcon = subDirGroupIcon.getRawResource(0);
|
||||
private static void updateGroupIcon(DirectoryEntry root, int iconId, IconFile iconFile) throws IOException {
|
||||
DirectoryEntry subDirGroupIcon = root.findSubDir(DirectoryEntry.RT_GROUP_ICON).findSubDir(iconId);
|
||||
RawResource groupIcon = subDirGroupIcon.getRawResource();
|
||||
|
||||
Bin.Value idCount = iconFile.getStructureMember("Header").getValueMember("idCount");
|
||||
|
||||
GroupIconResource groupIconResource = new GroupIconResource(idCount);
|
||||
groupIconResource.copyFrom(iconFile);
|
||||
Level level = (Level) groupIconResource.getMember("Level");
|
||||
ArrayList<Bin> directories = level.getMembers();
|
||||
for (int i = 0; i < directories.size(); ++i) {
|
||||
GroupIconResourceDirectory grpDir = (GroupIconResourceDirectory) directories.get(i);
|
||||
grpDir.getValueMember("dwId").setValue(i + 1);
|
||||
GroupIconResource gir = new GroupIconResource();
|
||||
gir.getHeader().copyFrom(iconFile.getHeader());
|
||||
Bin.ArrayOfBins<IconDirectory> sourceIcons = iconFile.getIcons();
|
||||
for (int i = 0; i < sourceIcons.size(); i++) {
|
||||
IconDirectory icon = sourceIcons.get(i);
|
||||
GroupIconResourceDirectory bin = new GroupIconResourceDirectory();
|
||||
bin.copyFrom(icon);
|
||||
bin.getDwId().setValue(i + 1);
|
||||
gir.getIcons().addBin(bin);
|
||||
}
|
||||
long size = groupIconResource.sizeInBytes();
|
||||
ByteArrayOutputStream bytesStream = new ByteArrayOutputStream((int) size);
|
||||
|
||||
long size = gir.sizeInBytes();
|
||||
ByteArrayOutputStream bytesStream = new ByteArrayOutputStream((int)size);
|
||||
DataOutputStream stream = new DataOutputStream(bytesStream);
|
||||
groupIconResource.write(stream);
|
||||
groupIcon.getBytes().setBytes(bytesStream.toByteArray());
|
||||
gir.write(stream);
|
||||
groupIcon.setBytes(bytesStream.toByteArray());
|
||||
}
|
||||
|
||||
private static void insertIcon(DirectoryEntry iconsDir, IconDirectory iconDirectory, int index) {
|
||||
EntryDescription entryDescription = new EntryDescription();
|
||||
Bin.Value name = entryDescription.getValueMember("Name");
|
||||
name.setValue(index + 1);
|
||||
private static DirectoryEntry insertIcon(DirectoryEntry level1, int index, long languageId) {
|
||||
// We should construct and correctly add second and third level entries to a resource
|
||||
// Second level contains ID of icon
|
||||
// Third level contains languageId and raw data
|
||||
|
||||
DirectoryEntry entryDirIcon2 = new DirectoryEntry(iconsDir.getSection(), entryDescription, name.getValue());
|
||||
iconsDir.insertDirectoryEntry(index, entryDirIcon2);
|
||||
iconsDir.addIdEntry(entryDescription);
|
||||
ResourceSectionReader section = level1.getSection();
|
||||
|
||||
EntryDescription entry409 = new EntryDescription();
|
||||
Bin.Value name409 = entry409.getValueMember("Name");
|
||||
name409.setValue(0x409);
|
||||
EntryDescription description2 = new EntryDescription();
|
||||
description2.getNameW().setValue(index);
|
||||
DirectoryEntry level2 = new DirectoryEntry(section, description2, index);
|
||||
|
||||
entryDirIcon2.addIdEntry(entry409);
|
||||
level1.addDirectoryEntry(level2);
|
||||
level1.addIdEntry(description2);
|
||||
|
||||
Bin.Value offset = entry409.getValueMember("OffsetToData");
|
||||
DataEntry dataEntry = new DataEntry(entryDirIcon2.getSection(), offset);
|
||||
EntryDescription description3 = new EntryDescription();
|
||||
description3.getNameW().setValue(languageId);
|
||||
Bin.DWord offset = description3.getOffsetToData();
|
||||
DataEntry level3 = new DataEntry(section, offset, description3.getNameW());
|
||||
|
||||
entryDirIcon2.insertDataEntry(index, dataEntry);
|
||||
dataEntry.insertRawData(index);
|
||||
RawResource rawRes = dataEntry.getRawResource();
|
||||
rawRes.setBytes(iconDirectory.getRawBytes());
|
||||
level2.addIdEntry(description3);
|
||||
level2.addDataEntry(level3);
|
||||
|
||||
level3.initRawData();
|
||||
|
||||
return level2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2006 ProductiveMe Inc.
|
||||
* Copyright 2013-2018 JetBrains s.r.o.
|
||||
* Copyright 2013-2022 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.
|
||||
@@ -28,19 +28,21 @@ import java.io.IOException;
|
||||
* Time: 1:35:49 PM
|
||||
*/
|
||||
public class FixedFileInfo extends Bin.Structure {
|
||||
public static final String FILE_VERSION_MS = "dwFileVersionMS";
|
||||
public static final String FILE_VERSION_LS = "dwFileVersionLS";
|
||||
public static final String PRODUCT_VERSION_MS = "dwProductVersionMS";
|
||||
public static final String PRODUCT_VERSION_LS = "dwProductVersionLS";
|
||||
public static final long MAGIC = 0xFEEF04BDL;
|
||||
private final DWord mySignature;
|
||||
private final DWord myFileVersionMS;
|
||||
private final DWord myFileVersionLS;
|
||||
private final DWord myProductVersionMS;
|
||||
private final DWord myProductVersionLS;
|
||||
|
||||
public FixedFileInfo() {
|
||||
super("FixedFileInfo");
|
||||
addMember( new DWord( "dwSignature" ) );
|
||||
mySignature = addMember(new DWord("dwSignature"));
|
||||
addMember( new DWord( "dwStrucVersion" ) );
|
||||
addMember( new DWord(FILE_VERSION_MS) );
|
||||
addMember( new DWord(FILE_VERSION_LS) );
|
||||
addMember( new DWord(PRODUCT_VERSION_MS) );
|
||||
addMember( new DWord(PRODUCT_VERSION_LS) );
|
||||
myFileVersionMS = addMember(new DWord("dwFileVersionMS"));
|
||||
myFileVersionLS = addMember(new DWord("dwFileVersionLS"));
|
||||
myProductVersionMS = addMember(new DWord("dwProductVersionMS"));
|
||||
myProductVersionLS = addMember(new DWord("dwProductVersionLS"));
|
||||
addMember( new DWord( "dwFileFlagsMask" ) );
|
||||
addMember( new DWord( "dwFileFlags" ) );
|
||||
addMember( new DWord( "dwFileOS" ) );
|
||||
@@ -53,17 +55,19 @@ public class FixedFileInfo extends Bin.Structure {
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException {
|
||||
super.read(stream);
|
||||
long signature = getValue("dwSignature");
|
||||
assert signature == 0xFEEF04BDL : "Incorrect signature; expected " + 0xFEEF04BDL + ", found " + signature;
|
||||
long signature = mySignature.getValue();
|
||||
if (signature != MAGIC) {
|
||||
throw new IllegalStateException(String.format("Incorrect signature; expected %#010x, found %#010x", MAGIC, signature));
|
||||
}
|
||||
}
|
||||
|
||||
public void setFileVersion(int mostSignificantVersion, int leastSignificantVersion) {
|
||||
((DWord) getMember(FILE_VERSION_MS)).setValue(mostSignificantVersion);
|
||||
((DWord) getMember(FILE_VERSION_LS)).setValue(leastSignificantVersion);
|
||||
myFileVersionMS.setValue(mostSignificantVersion);
|
||||
myFileVersionLS.setValue(leastSignificantVersion);
|
||||
}
|
||||
|
||||
public void setProductVersion(int mostSignificantVersion, int leastSignificantVersion) {
|
||||
((DWord) getMember(PRODUCT_VERSION_MS)).setValue(mostSignificantVersion);
|
||||
((DWord) getMember(PRODUCT_VERSION_LS)).setValue(leastSignificantVersion);
|
||||
myProductVersionMS.setValue(mostSignificantVersion);
|
||||
myProductVersionLS.setValue(leastSignificantVersion);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2006 ProductiveMe Inc.
|
||||
* Copyright 2013-2018 JetBrains s.r.o.
|
||||
* Copyright 2013-2022 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.
|
||||
@@ -17,6 +17,10 @@
|
||||
|
||||
package com.pme.exe.res.vi;
|
||||
|
||||
import com.pme.exe.Bin;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author Sergey Zhulin
|
||||
* Date: May 10, 2006
|
||||
@@ -32,7 +36,15 @@ public class StringFileInfo extends VersionInfoBin {
|
||||
});
|
||||
}
|
||||
|
||||
public StringTable getFirstStringTable() {
|
||||
return (StringTable) getMember("StringTable0");
|
||||
public StringTable getSoleStringTable() {
|
||||
long count = getMembers().stream().filter(bin -> bin instanceof StringTable).count();
|
||||
if (count > 1) {
|
||||
throw new IllegalStateException("More than one StringTable found, indicates that there's more than one lanugage in executable");
|
||||
}
|
||||
Optional<Bin> optional = getMembers().stream().filter(bin -> bin instanceof StringTable).findFirst();
|
||||
if (optional.isEmpty()) {
|
||||
throw new IllegalStateException("No StringTable's found");
|
||||
}
|
||||
return (StringTable)optional.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2006 ProductiveMe Inc.
|
||||
* Copyright 2013 JetBrains s.r.o.
|
||||
* Copyright 2013-2022 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.
|
||||
@@ -19,6 +19,11 @@ package com.pme.exe.res.vi;
|
||||
|
||||
import com.pme.exe.Bin;
|
||||
|
||||
/**
|
||||
* <a href="https://learn.microsoft.com/en-us/windows/win32/menurc/stringtable">StringTable structure</a>
|
||||
* <p>
|
||||
* Each StringTable structure's szKey member indicates the appropriate language and code page for displaying the text in that StringTable structure.
|
||||
*/
|
||||
public class StringTable extends VersionInfoBin {
|
||||
|
||||
public StringTable(String name) {
|
||||
@@ -30,15 +35,21 @@ public class StringTable extends VersionInfoBin {
|
||||
});
|
||||
}
|
||||
|
||||
public String getStringValue(String key) {
|
||||
for (Bin bin : getMembers()) {
|
||||
if (bin instanceof StringTableEntry entry && bin.getName().equals(key)) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Could not find string with key: " + key);
|
||||
}
|
||||
public void setStringValue(String key, String value) {
|
||||
for (Bin bin : getMembers()) {
|
||||
if (bin.getName().equals(key)) {
|
||||
StringTableEntry entry = (StringTableEntry) bin;
|
||||
((WChar) entry.getMember("Value")).setValue(value);
|
||||
((Word) entry.getMember("wValueLength")).setValue(value.length() + 1);
|
||||
if (bin instanceof StringTableEntry entry && bin.getName().equals(key)) {
|
||||
entry.setValue(value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
assert false: "Could not find string with key " + key;
|
||||
throw new IllegalStateException("Could not find string with key: " + key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2006 ProductiveMe Inc.
|
||||
* Copyright 2013-2018 JetBrains s.r.o.
|
||||
* Copyright 2013-2022 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.
|
||||
@@ -21,28 +21,40 @@ import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* <a href="https://learn.microsoft.com/en-us/windows/win32/menurc/string-str">String stucture</a>
|
||||
*
|
||||
* @author Sergey Zhulin
|
||||
* Date: May 10, 2006
|
||||
* Time: 8:26:03 PM
|
||||
*/
|
||||
public class StringTableEntry extends VersionInfoBin {
|
||||
private final WCharStringNT myValue;
|
||||
|
||||
public StringTableEntry() {
|
||||
super("<unnamed>");
|
||||
addMember(new WChar("Value"));
|
||||
addMember(new Padding(4));
|
||||
myValue = addMember(new WCharStringNT("Value"));
|
||||
addMember(new Padding("Padding2", 4));
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return myValue.getValue();
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
myValue.setValue(value);
|
||||
// myValueLength has size in words, not bytes
|
||||
myValueLength.setValue(value.length() + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException {
|
||||
super.read(stream);
|
||||
WChar key = (WChar) getMember("szKey");
|
||||
setName(key.getValue());
|
||||
setName(myKey.getValue());
|
||||
assert myValue.sizeInBytes() == myValueLength.getValue() * 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
WChar key = (WChar) getMember("szKey");
|
||||
WChar value = (WChar) getMember("Value");
|
||||
return key.getValue() + " = " + value.getValue();
|
||||
return myKey.getValue() + " = " + myValue.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
|
||||
package com.pme.exe.res.vi;
|
||||
|
||||
@@ -6,6 +6,13 @@ package com.pme.exe.res.vi;
|
||||
public class Var extends VersionInfoBin {
|
||||
public Var(String name) {
|
||||
super(name, "Translation");
|
||||
addMember(new DWord("Translation"));
|
||||
addMember(new ArrayOfBins<>("Translation", DWord.class, new ReadOnlyValue("ValueLength/4") {
|
||||
@Override
|
||||
public long getValue() {
|
||||
long size = myValueLength.getValue();
|
||||
assert size % 4 == 0;
|
||||
return size / 4; // DWord size in bytes
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2006 ProductiveMe Inc.
|
||||
* Copyright 2013-2018 JetBrains s.r.o.
|
||||
* Copyright 2013-2022 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.
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
package com.pme.exe.res.vi;
|
||||
|
||||
import com.pme.util.OffsetTrackingInputStream;
|
||||
import com.pme.util.StreamUtil;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
@@ -28,26 +28,20 @@ public class VersionInfo extends VersionInfoBin {
|
||||
|
||||
public VersionInfo() {
|
||||
super("VersionInfo", "VS_VERSION_INFO");
|
||||
myFixedFileInfo = new FixedFileInfo();
|
||||
addMember(myFixedFileInfo);
|
||||
addMember(new Padding(4));
|
||||
myStringFileInfo = new StringFileInfo();
|
||||
addMember(myStringFileInfo);
|
||||
myFixedFileInfo = addMember(new FixedFileInfo());
|
||||
addMember(new Padding("Padding2", 4));
|
||||
myStringFileInfo = addMember(new StringFileInfo());
|
||||
addMember(new VarFileInfo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException {
|
||||
long startOffset = -1;
|
||||
if (stream instanceof OffsetTrackingInputStream) {
|
||||
startOffset = ((OffsetTrackingInputStream) stream).getOffset();
|
||||
}
|
||||
long startOffset = StreamUtil.getOffset(stream);
|
||||
super.read(stream);
|
||||
if (stream instanceof OffsetTrackingInputStream) {
|
||||
long offset = ((OffsetTrackingInputStream) stream).getOffset();
|
||||
long length = getValue("wLength");
|
||||
assert startOffset + length == offset: "Length specified in version info header (" + length +
|
||||
") does not match actual version info length (" + (offset - startOffset) + ")";
|
||||
long offset = StreamUtil.getOffset(stream);
|
||||
long length = myLength.getValue();
|
||||
if (startOffset + length != offset) {
|
||||
throw new IOException(String.format("Length specified in version info header %#4x does not match actual version info length %#4x", length, (offset - startOffset)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
|
||||
package com.pme.exe.res.vi;
|
||||
|
||||
import com.pme.exe.Bin;
|
||||
import com.pme.util.OffsetTrackingInputStream;
|
||||
import com.pme.util.StreamUtil;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
|
||||
public class VersionInfoBin extends Bin.Structure {
|
||||
public abstract class VersionInfoBin extends Bin.Structure {
|
||||
private String myExpectedName;
|
||||
private VersionInfoFactory myChildFactory;
|
||||
protected final Word myLength;
|
||||
protected final WCharStringNT myKey;
|
||||
protected final Word myValueLength;
|
||||
|
||||
public VersionInfoBin(String name) {
|
||||
super(name);
|
||||
Word length = new Word("wLength");
|
||||
addMember(length);
|
||||
addSizeHolder(length);
|
||||
addMember(new Word("wValueLength"));
|
||||
myLength = addMember(new Word("wLength"));
|
||||
addSizeHolder(myLength);
|
||||
myValueLength = addMember(new Word("wValueLength"));
|
||||
addMember(new Word("wType"));
|
||||
addMember(new WChar("szKey"));
|
||||
addMember(new Padding(4));
|
||||
myKey = addMember(new WCharStringNT("szKey"));
|
||||
addMember(new Padding("Padding", 4));
|
||||
}
|
||||
|
||||
public VersionInfoBin(String versionInfo, String expectedName) {
|
||||
@@ -38,20 +39,21 @@ public class VersionInfoBin extends Bin.Structure {
|
||||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException {
|
||||
OffsetTrackingInputStream inputStream = (OffsetTrackingInputStream) stream;
|
||||
long startOffset = inputStream.getOffset();
|
||||
long startOffset = StreamUtil.getOffset(stream);
|
||||
assert startOffset % 4 == 0;
|
||||
super.read(stream);
|
||||
if (myExpectedName != null) {
|
||||
String signature = ((WChar) getMember("szKey")).getValue();
|
||||
assert signature.equals(myExpectedName): "Expected signature " + myExpectedName + ", found '" + signature + "'";
|
||||
String signature = myKey.getValue();
|
||||
if (!signature.equals(myExpectedName)) {
|
||||
throw new IllegalStateException("Expected signature '" + myExpectedName + "', found '" + signature + "'");
|
||||
}
|
||||
}
|
||||
if (myChildFactory != null) {
|
||||
long length = getValue("wLength");
|
||||
long length = myLength.getValue();
|
||||
int i = 0;
|
||||
while(inputStream.getOffset() < startOffset + length) {
|
||||
while (StreamUtil.getOffset(stream) < startOffset + length) {
|
||||
VersionInfoBin child = myChildFactory.createChild(i++);
|
||||
child.read(inputStream);
|
||||
child.read(stream);
|
||||
addMember(child);
|
||||
}
|
||||
}
|
||||
@@ -59,18 +61,12 @@ public class VersionInfoBin extends Bin.Structure {
|
||||
|
||||
@Override
|
||||
public void write(DataOutput stream) throws IOException {
|
||||
long startOffset = -1;
|
||||
if (stream instanceof RandomAccessFile) {
|
||||
startOffset = ((RandomAccessFile) stream).getFilePointer();
|
||||
assert startOffset % 4 == 0;
|
||||
}
|
||||
long startOffset = StreamUtil.getOffset(stream);
|
||||
super.write(stream);
|
||||
if (stream instanceof RandomAccessFile) {
|
||||
long offset = ((RandomAccessFile) stream).getFilePointer();
|
||||
long realLength = offset - startOffset;
|
||||
long expectedLength = getValue("wLength");
|
||||
assert realLength == expectedLength: "Actual length does not match calculated length for " + getName() +
|
||||
": expected " + expectedLength + ", actual " + realLength + ", sizeInBytes() " + sizeInBytes();
|
||||
}
|
||||
long offset = StreamUtil.getOffset(stream);
|
||||
long realLength = offset - startOffset;
|
||||
long expectedLength = myLength.getValue();
|
||||
assert realLength == expectedLength : "Actual length does not match calculated length for " + getName() +
|
||||
": expected " + expectedLength + ", actual " + realLength + ", sizeInBytes() " + sizeInBytes();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
|
||||
package com.pme.launcher;
|
||||
|
||||
import com.pme.exe.ExeFormat;
|
||||
import com.pme.exe.ExeReader;
|
||||
import com.pme.exe.SectionReader;
|
||||
import com.pme.exe.*;
|
||||
import com.pme.exe.res.DirectoryEntry;
|
||||
import com.pme.exe.res.RawResource;
|
||||
import com.pme.exe.res.ResourceSectionReader;
|
||||
@@ -31,8 +29,11 @@ import com.pme.util.OffsetTrackingInputStream;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* @author Sergey Zhulin
|
||||
@@ -54,28 +55,22 @@ public class LauncherGenerator {
|
||||
|
||||
public void load() throws IOException {
|
||||
RandomAccessFile stream = new RandomAccessFile(myTemplate, "r");
|
||||
ExeReader formatReader = new ExeReader(myTemplate.getName(), ExeFormat.UNKNOWN);
|
||||
formatReader.read(stream);
|
||||
stream.seek(0L);
|
||||
myReader = new ExeReader(myTemplate.getName(), formatReader.getExeFormat());
|
||||
myReader = new ExeReader(myTemplate.getName());
|
||||
myReader.read(stream);
|
||||
stream.close();
|
||||
SectionReader sectionReader = myReader.getSectionReader(".rsrc");
|
||||
ResourceSectionReader resourceReader = (ResourceSectionReader) sectionReader.getMember(".rsrc");
|
||||
myRoot = resourceReader.getRoot();
|
||||
DirectoryEntry subDir = myRoot.findSubDir("IRD6");
|
||||
ResourceSectionReader resourceSection = (ResourceSectionReader)myReader.getSectionReader(Section.RESOURCES_SECTION_NAME);
|
||||
myRoot = resourceSection.getRoot();
|
||||
DirectoryEntry subDir = myRoot.findSubDir(DirectoryEntry.RT_STRING);
|
||||
myStringTableDirectory = new StringTableDirectory(subDir);
|
||||
|
||||
RawResource versionInfoResource = getVersionInfoResource();
|
||||
ByteArrayInputStream bytesStream = new ByteArrayInputStream(versionInfoResource.getBytes().getBytes());
|
||||
|
||||
myVersionInfo = new VersionInfo();
|
||||
myVersionInfo.read(new OffsetTrackingInputStream(new DataInputStream(bytesStream)));
|
||||
myVersionInfo.read(new OffsetTrackingInputStream(new DataInputStream(new ByteArrayInputStream(versionInfoResource.getBytes()))));
|
||||
}
|
||||
|
||||
private RawResource getVersionInfoResource() {
|
||||
DirectoryEntry viDir = myRoot.findSubDir("IRD16").findSubDir( "IRD1" );
|
||||
return viDir.getRawResource(0);
|
||||
DirectoryEntry viDir = myRoot.findSubDir(DirectoryEntry.RT_VERSION).findSubDir(1);
|
||||
return viDir.getRawResource();
|
||||
}
|
||||
|
||||
private void saveVersionInfo() throws IOException {
|
||||
@@ -92,24 +87,137 @@ public class LauncherGenerator {
|
||||
myReader.resetOffsets(0);
|
||||
myReader.sectionVirtualAddressFixup();
|
||||
|
||||
if (myExePath.exists()) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
myExePath.delete();
|
||||
}
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
myExePath.getParentFile().mkdirs();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
myExePath.createNewFile();
|
||||
RandomAccessFile exeStream = new RandomAccessFile(myExePath, "rw");
|
||||
myReader.write(exeStream);
|
||||
exeStream.close();
|
||||
|
||||
// verifyVersionInfo();
|
||||
verifySize();
|
||||
verifySections();
|
||||
verifyVersionInfo();
|
||||
}
|
||||
|
||||
private void verifySize() throws IOException {
|
||||
long fileSize = Files.size(myExePath.toPath());
|
||||
long exeSize = myReader.sizeInBytes();
|
||||
if (fileSize != exeSize) {
|
||||
throw new RuntimeException(format("Produced file size mismatch, on disk: %d, in memory %d", fileSize, exeSize));
|
||||
}
|
||||
}
|
||||
|
||||
private void verifySections() {
|
||||
ImageOptionalHeader imageOptionalHeader = myReader.getPeHeader().getImageOptionalHeader();
|
||||
final int FileAlignment = (int)imageOptionalHeader.getFileAlignment().getValue();
|
||||
final int SectionAlignment = (int)imageOptionalHeader.getSectionAlignment().getValue();
|
||||
|
||||
List<String> errors = new ArrayList<>();
|
||||
Bin.ArrayOfBins<ImageSectionHeader> sections = myReader.getSectionHeaders();
|
||||
for (ImageSectionHeader header : sections) {
|
||||
String name = header.getSectionName();
|
||||
long sizeOfRawData = header.getSizeOfRawData().getValue();
|
||||
long pointerToRawData = header.getPointerToRawData().getValue();
|
||||
long virtualAddress = header.getVirtualAddress().getValue();
|
||||
long virtualSize = header.getVirtualSize().getValue();
|
||||
if (pointerToRawData == 0) {
|
||||
errors.add(format("Section '%s' may not have zero PointerToRawData", name));
|
||||
}
|
||||
if (virtualAddress == 0) {
|
||||
errors.add(format("Section '%s' may not have zero VirtualAddress", name));
|
||||
}
|
||||
if (sizeOfRawData % FileAlignment != 0) {
|
||||
errors.add(
|
||||
format("SizeOfRawData of section '%s' isn't dividable by 'FileAlignment' (%#x): %#x", name, FileAlignment, sizeOfRawData));
|
||||
}
|
||||
if (pointerToRawData % FileAlignment != 0) {
|
||||
errors.add(
|
||||
format("PointerToRawData of section '%s' isn't dividable by 'FileAlignment' (%#x): %#x", name, FileAlignment, pointerToRawData));
|
||||
}
|
||||
if (virtualAddress % SectionAlignment != 0) {
|
||||
errors.add(
|
||||
format("VirtualAddress of section '%s' isn't dividable by 'SectionAlignment' (%#x): %#x", name, SectionAlignment,
|
||||
virtualAddress));
|
||||
}
|
||||
if (name.equals(Section.RESOURCES_SECTION_NAME) && virtualSize < sizeOfRawData) {
|
||||
errors.add(
|
||||
format("VirtualSize of section '%s' is smaller than SizeOfRawData (%#x): %#x", name, sizeOfRawData, virtualSize));
|
||||
}
|
||||
}
|
||||
Bin.ArrayOfBins<ImageDataDirectory> imageDataDirs = imageOptionalHeader.getImageDataDirectories();
|
||||
|
||||
check(errors, "resources",
|
||||
imageDataDirs.get(ImageDataDirectory.IMAGE_DIRECTORY_ENTRY_RESOURCE),
|
||||
getSection(sections, Section.RESOURCES_SECTION_NAME));
|
||||
check(errors, "relocations",
|
||||
imageDataDirs.get(ImageDataDirectory.IMAGE_DIRECTORY_ENTRY_BASERELOC),
|
||||
getSection(sections, Section.RELOCATIONS_SECTION_NAME));
|
||||
|
||||
|
||||
//SizeOfImage
|
||||
//The size of the image, in bytes, including all headers. Must be a multiple of SectionAlignment.
|
||||
long sizeOfImage = imageOptionalHeader.getSizeOfImage().getValue();
|
||||
if (sizeOfImage % SectionAlignment != 0) {
|
||||
errors.add(format("SizeOfImage isn't dividable by 'SectionAlignment' (%#x): %#x", SectionAlignment, sizeOfImage));
|
||||
}
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
StringBuilder msg = new StringBuilder();
|
||||
msg.append("Output verification failed with ").append(errors.size()).append(" error");
|
||||
if (errors.size() > 1) msg.append("s");
|
||||
msg.append(":\n");
|
||||
for (String error : errors) {
|
||||
msg.append('\t').append(error).append('\n');
|
||||
}
|
||||
throw new RuntimeException(msg.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void check(List<String> errors, String name, ImageDataDirectory idd, ImageSectionHeader ish) {
|
||||
long iddSize = idd.getSize().getValue();
|
||||
long ishVirtualSize = ish.getVirtualSize().getValue();
|
||||
|
||||
if (iddSize != ishVirtualSize) {
|
||||
errors.add(format("Incorrect '%s' sizes: %#x, %#x", name, iddSize, ishVirtualSize));
|
||||
}
|
||||
|
||||
long iddAddress = idd.getVirtualAddress().getValue();
|
||||
long ishAddress = ish.getVirtualAddress().getValue();
|
||||
|
||||
if (iddAddress != ishAddress) {
|
||||
errors.add(format("Incorrect '%s' virtual address: %#x, %#x", name, iddAddress, ishAddress));
|
||||
}
|
||||
}
|
||||
|
||||
private static ImageSectionHeader getSection(Bin.ArrayOfBins<ImageSectionHeader> sections, String name) {
|
||||
for (ImageSectionHeader header : sections) {
|
||||
if (name.equals(header.getSectionName())) {
|
||||
return header;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Cannot find section with name " + name);
|
||||
}
|
||||
|
||||
private void verifyVersionInfo() throws IOException {
|
||||
String versionInfoPath = myExePath + ".version";
|
||||
try (RandomAccessFile versionInfoStream = new RandomAccessFile(versionInfoPath, "rw")) {
|
||||
myVersionInfo.resetOffsets(0);
|
||||
myVersionInfo.write(versionInfoStream);
|
||||
}
|
||||
ByteArrayOutputStream data1 = new ByteArrayOutputStream((int)myVersionInfo.sizeInBytes());
|
||||
myVersionInfo.resetOffsets(0);
|
||||
myVersionInfo.write(new DataOutputStream(data1));
|
||||
|
||||
VersionInfo copy = new VersionInfo();
|
||||
copy.read(new OffsetTrackingInputStream(new DataInputStream(new FileInputStream(versionInfoPath))));
|
||||
copy.read(new OffsetTrackingInputStream(new DataInputStream(new ByteArrayInputStream(data1.toByteArray()))));
|
||||
|
||||
ByteArrayOutputStream data2 = new ByteArrayOutputStream((int)copy.sizeInBytes());
|
||||
copy.resetOffsets(0);
|
||||
copy.write(new DataOutputStream(data2));
|
||||
|
||||
if (!Arrays.equals(data1.toByteArray(), data2.toByteArray())) {
|
||||
throw new IllegalStateException("Load and save of VersionInfo produced different binary results");
|
||||
}
|
||||
}
|
||||
|
||||
public void setResourceString(int id, String value) {
|
||||
@@ -117,27 +225,21 @@ public class LauncherGenerator {
|
||||
}
|
||||
|
||||
public void setVersionInfoString(String key, String value) {
|
||||
StringTable stringTable = myVersionInfo.getStringFileInfo().getFirstStringTable();
|
||||
if (stringTable != null) {
|
||||
stringTable.setStringValue(key, value);
|
||||
}
|
||||
StringTable stringTable = myVersionInfo.getStringFileInfo().getSoleStringTable();
|
||||
stringTable.setStringValue(key, value);
|
||||
}
|
||||
|
||||
public void injectBitmap(int id, byte[] bitmapFileData) {
|
||||
DirectoryEntry subDirBmp = myRoot.findSubDir("IRD2").findSubDir("IRD" + id);
|
||||
RawResource bmpRes = subDirBmp.getRawResource(0);
|
||||
DirectoryEntry subDirBmp = myRoot.findSubDir(DirectoryEntry.RT_BITMAP).findSubDir(id);
|
||||
RawResource bmpRes = subDirBmp.getRawResource();
|
||||
// strip off BITMAPFILEHEADER
|
||||
byte[] bitmapResourceData = new byte[bitmapFileData.length-14];
|
||||
byte[] bitmapResourceData = new byte[bitmapFileData.length - 14];
|
||||
System.arraycopy(bitmapFileData, 14, bitmapResourceData, 0, bitmapResourceData.length);
|
||||
bmpRes.setBytes(bitmapResourceData);
|
||||
}
|
||||
|
||||
public void injectIcon(int id, final InputStream iconStream) throws IOException {
|
||||
Path f = Files.createTempFile("launcher", "ico");
|
||||
try (iconStream) {
|
||||
Files.copy(iconStream, f, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
new IconResourceInjector().injectIcon(f.toFile(), myRoot, "IRD" + id);
|
||||
IconResourceInjector.injectIcon(iconStream, myRoot, id);
|
||||
}
|
||||
|
||||
public void setVersionNumber(int majorVersion, int minorVersion, int bugfixVersion) {
|
||||
|
||||
@@ -39,6 +39,6 @@ public class BitsUtil {
|
||||
public static char readChar(DataInput stream) throws IOException {
|
||||
int b1 = stream.readByte();
|
||||
int b2 = stream.readByte();
|
||||
return (char) (b1 + (b2 << 8));
|
||||
return (char) (b1 & 0xFF | ((b2 & 0xFF) << 8));
|
||||
}
|
||||
}
|
||||
|
||||
42
tools/launcher-generator/src/com/pme/util/StreamUtil.java
Normal file
42
tools/launcher-generator/src/com/pme/util/StreamUtil.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.pme.util;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class StreamUtil {
|
||||
public static long getOffset(DataInput stream) throws IOException {
|
||||
if (stream instanceof OffsetTrackingInputStream) {
|
||||
return ((OffsetTrackingInputStream)stream).getOffset();
|
||||
}
|
||||
if (stream instanceof RandomAccessFile) {
|
||||
return ((RandomAccessFile)stream).getFilePointer();
|
||||
}
|
||||
throw new IOException("OffsetTrackingInputStream or RandomAccessFile expected, got " + stream.getClass().getName());
|
||||
}
|
||||
|
||||
public static long getOffset(DataOutput stream) throws IOException {
|
||||
if (stream instanceof RandomAccessFile) {
|
||||
return ((RandomAccessFile)stream).getFilePointer();
|
||||
}
|
||||
if (stream instanceof DataOutputStream) {
|
||||
return ((DataOutputStream)stream).size();
|
||||
}
|
||||
throw new IOException("RandomAccessFile or DataOutputStream expected, got " + stream.getClass().getName());
|
||||
}
|
||||
|
||||
public static void seek(DataInput stream, long pos) throws IOException {
|
||||
if (stream instanceof OffsetTrackingInputStream inputStream) {
|
||||
long current = inputStream.getOffset();
|
||||
if (current < pos) {
|
||||
inputStream.skipBytes((int)(pos - current));
|
||||
} else {
|
||||
throw new IOException(String.format("Cannot go backwards in OffsetTrackingInputStream: current offset %#010x, seek %#010x", current, pos));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (stream instanceof RandomAccessFile) {
|
||||
((RandomAccessFile)stream).seek(pos);
|
||||
return;
|
||||
}
|
||||
throw new IOException("OffsetTrackingInputStream or RandomAccessFile expected, got " + stream.getClass().getName());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user