correctly writing multiple string tables; stub for real launcher generator

This commit is contained in:
Dmitry Jemerov
2013-02-20 20:14:11 +01:00
parent 8459153c5f
commit dad0e25a4c
11 changed files with 254 additions and 131 deletions
+9
View File
@@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="jdom">
<CLASSES>
<root url="jar://$PROJECT_DIR$/../../lib/jdom.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
+1
View File
@@ -2,6 +2,7 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/../../community-resources/community-resources.iml" filepath="$PROJECT_DIR$/../../community-resources/community-resources.iml" />
<module fileurl="file://$PROJECT_DIR$/launcher-generator.iml" filepath="$PROJECT_DIR$/launcher-generator.iml" />
</modules>
</component>
@@ -7,6 +7,8 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="community-resources" />
<orderEntry type="library" name="jdom" level="project" />
</component>
</module>
@@ -582,20 +582,20 @@ public abstract class Bin {
}
}
public static class ArrayOfBins extends Bin {
public static class ArrayOfBins<T extends Bin> extends Bin {
private Bin[] myValues;
private Bin.Value mySize;
private Class myClass;
private Bin.Value myCountHolder = null;
public ArrayOfBins(String name, Class cl, Bin.Value size) {
public ArrayOfBins(String name, Class<T> cl, Bin.Value size) {
super(name);
myClass = cl;
mySize = size;
init();
}
public ArrayOfBins(String name, Class cl, int size) {
public ArrayOfBins(String name, Class<T> cl, int size) {
this(name, cl, new DWord("size").setValue(size));
}
@@ -654,8 +654,9 @@ public abstract class Bin {
return myValues;
}
public Bin get(int index) {
return myValues[index];
public T get(int index) {
//noinspection unchecked
return (T) myValues[index];
}
public long sizeInBytes() {
@@ -54,7 +54,7 @@ public class ImageOptionalHeader extends Bin.Structure {
addMember( new DWord( "SizeOfHeapCommit" ) );
addMember( new DWord( "LoaderFlags" ) );
DWord numberOfRvaAndSizes = (DWord)addMember( new DWord( "NumberOfRvaAndSizes") );
ArrayOfBins imageDataDirectories = new ArrayOfBins( "ImageDataDirectories", ImageDataDirectory.class, numberOfRvaAndSizes);
ArrayOfBins<ImageDataDirectory> imageDataDirectories = new ArrayOfBins<ImageDataDirectory>("ImageDataDirectories", ImageDataDirectory.class, numberOfRvaAndSizes);
imageDataDirectories.setCountHolder( numberOfRvaAndSizes );
addMember( imageDataDirectories );
@@ -27,14 +27,16 @@ import java.util.ArrayList;
* Time: 11:22:12 PM
*/
public class DirectoryEntry extends LevelEntry {
private ArrayOfBins myNamedEntries;
private ArrayOfBins myIdEntries;
private ArrayOfBins<EntryDescription> myNamedEntries;
private ArrayOfBins<EntryDescription> myIdEntries;
private ResourceSectionReader mySection;
private ArrayList mySubDirs = new ArrayList();
private ArrayList myDatas = new ArrayList();
private ArrayList<DirectoryEntry> mySubDirs = new ArrayList<DirectoryEntry>();
private ArrayList<DataEntry> myDatas = new ArrayList<DataEntry>();
private long myIdOrName;
public DirectoryEntry( ResourceSectionReader section, EntryDescription entry ) {
public DirectoryEntry(ResourceSectionReader section, EntryDescription entry, long idOrName) {
super( createName( entry ) );
myIdOrName = idOrName;
addMember(new DWord("Characteristics"));
addMember(new DWord("TimeDateStamp"));
addMember(new Word("MajorVersion"));
@@ -49,6 +51,10 @@ public class DirectoryEntry extends LevelEntry {
}
}
public long getIdOrName() {
return myIdOrName;
}
public ResourceSectionReader getSection(){
return mySection;
}
@@ -61,27 +67,23 @@ public class DirectoryEntry extends LevelEntry {
return name;
}
public ArrayList<DirectoryEntry> getSubDirs() {
return mySubDirs;
}
public DirectoryEntry findSubDir( String name ){
for (int i = 0; i < mySubDirs.size(); i++) {
DirectoryEntry directoryEntry = (DirectoryEntry) mySubDirs.get(i);
if ( directoryEntry.getName().equals( name ) ){
for (DirectoryEntry directoryEntry : mySubDirs) {
if (directoryEntry.getName().equals(name)) {
return directoryEntry;
}
}
return null;
}
public ArrayList getSubDirs(){
return mySubDirs;
}
public RawResource getRawResource( int index ){
DataEntry dataEntry = (DataEntry)myDatas.get(index);
public RawResource getRawResource(int index) {
DataEntry dataEntry = myDatas.get(index);
return dataEntry.getRawResource();
}
public ArrayList getDatas() {
return myDatas;
}
public long sizeInBytes() {
return super.sizeInBytes();
@@ -106,10 +108,11 @@ public class DirectoryEntry extends LevelEntry {
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);
myIdEntries = new ArrayOfBins<EntryDescription>("Id entries", EntryDescription.class, 0);
addMember(myIdEntries);
myIdEntries.setCountHolder(numberOfNamedEntries);
}
@@ -120,13 +123,13 @@ public class DirectoryEntry extends LevelEntry {
super.read(stream);
Word numberOfNamedEntries = (Word) getMember("NumberOfNamedEntries");
myNamedEntries = new ArrayOfBins("Named entries", EntryDescription.class, numberOfNamedEntries);
myNamedEntries = new ArrayOfBins<EntryDescription>("Named entries", EntryDescription.class, numberOfNamedEntries);
addMember(myNamedEntries);
myNamedEntries.setCountHolder(numberOfNamedEntries);
myNamedEntries.read(stream);
Word numberOfIdEntries = (Word) getMember("NumberOfIdEntries");
myIdEntries = new ArrayOfBins("Id entries", EntryDescription.class, numberOfIdEntries);
myIdEntries = new ArrayOfBins<EntryDescription>("Id entries", EntryDescription.class, numberOfIdEntries);
addMember(myIdEntries);
myIdEntries.setCountHolder(numberOfIdEntries);
myIdEntries.read(stream);
@@ -135,14 +138,15 @@ public class DirectoryEntry extends LevelEntry {
processEntries(myIdEntries);
}
private void processEntries(ArrayOfBins entries) {
private void processEntries(ArrayOfBins<EntryDescription> entries) {
for (int i = 0; i < entries.size(); ++i) {
EntryDescription entry = (EntryDescription) entries.get(i);
EntryDescription entry = entries.get(i);
Bin.Value offset = entry.getValueMember("OffsetToData");
Bin.Value name = entry.getValueMember("Name");
if ((offset.getValue() & 0x80000000) != 0) {
addDirectoryEntry( new DirectoryEntry( mySection, entry ) );
addDirectoryEntry( new DirectoryEntry( mySection, entry, name.getValue()) );
} else {
addDataEntry( new DataEntry( mySection, offset ) );
addDataEntry( new DataEntry( mySection, offset) );
}
}
}
@@ -51,7 +51,7 @@ public class ResourceSectionReader extends Bin.Structure {
addSizeHolder(mySize);
addSizeHolder(virtualSize);
addSizeHolder(size);
myRoot.addLevelEntry(new DirectoryEntry( this, null ));
myRoot.addLevelEntry(new DirectoryEntry( this, null, 0));
addMember(myRoot);
}
@@ -0,0 +1,59 @@
/*
* Copyright 2006 ProductiveMe Inc.
* Copyright 2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pme.exe.res;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author yole
*/
public class StringTableDirectory {
private static class Entry {
int startID;
StringTable table;
RawResource resource;
}
private final List<Entry> myEntries = new ArrayList<Entry>();
public StringTableDirectory(DirectoryEntry directoryEntry) throws IOException {
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());
myEntries.add(e);
}
}
public void setString(int id, String value) {
for (Entry entry : myEntries) {
if (entry.startID == (id / 16)-1) {
entry.table.setString(id % 16, value);
}
}
}
public void save() throws IOException {
for (Entry entry : myEntries) {
entry.resource.setBytes(entry.table.getBytes());
}
}
}
@@ -73,7 +73,7 @@ public class IconResourceInjector {
Bin.Value name = entryDescription.getValueMember("Name");
name.setValue(index + 1);
DirectoryEntry entryDirIcon2 = new DirectoryEntry(iconsDir.getSection(), entryDescription);
DirectoryEntry entryDirIcon2 = new DirectoryEntry(iconsDir.getSection(), entryDescription, name.getValue());
iconsDir.insertDirectoryEntry(index, entryDirIcon2);
iconsDir.addIdEntry(entryDescription);
@@ -20,10 +20,7 @@ package com.pme.launcher;
import com.pme.exe.ExeReader;
import com.pme.exe.SectionReader;
import com.pme.exe.Bin;
import com.pme.exe.res.ResourceSectionReader;
import com.pme.exe.res.DirectoryEntry;
import com.pme.exe.res.RawResource;
import com.pme.exe.res.StringTable;
import com.pme.exe.res.*;
import com.pme.exe.res.bmp.PictureResourceInjector;
import com.pme.exe.res.icon.IconResourceInjector;
import com.pme.exe.res.vi.VersionInfo;
@@ -40,132 +37,58 @@ public class LauncherGenerator {
private File myIcon;
private File myBmp;
private File myExePath;
private int mySplashTimeout = 3;
private String myJreBundlePath;
private String myVmParameters;
private String myStartJar;
private String myWorkDir;
private String myJavaVersion;
private boolean mySearchJdkOnly = false;
private StringTableDirectory myStringTableDirectory;
private DirectoryEntry myRoot;
private ExeReader myReader;
public LauncherGenerator(File template, File exePath) {
myTemplate = template;
myExePath = exePath;
}
public void setWorkDir(String workDir) {
myWorkDir = workDir;
}
public void setSplashTimeout(int splashTimeout) {
mySplashTimeout = splashTimeout;
}
public void setJreBundlePath(String jreBundlePath) {
myJreBundlePath = jreBundlePath;
}
public void setVmParameters(String vmParameters) {
myVmParameters = vmParameters;
}
public void setStartJar(String startJar) {
myStartJar = startJar;
}
public void setIcon(File icon) {
myIcon = icon;
}
public void setBmp(File bmp) {
myBmp = bmp;
public void load() throws IOException {
myReader = new ExeReader(myTemplate.getName());
RandomAccessFile stream = new RandomAccessFile(myTemplate, "r");
myReader.read(stream);
stream.close();
SectionReader sectionReader = myReader.getSectionReader(".rsrc");
ResourceSectionReader resourceReader = (ResourceSectionReader) sectionReader.getMember(".rsrc");
myRoot = resourceReader.getRoot();
DirectoryEntry subDir = myRoot.findSubDir("IRD6");
myStringTableDirectory = new StringTableDirectory(subDir);
}
public void generate() throws IOException {
ExeReader reader = new ExeReader(myTemplate.getName());
RandomAccessFile stream = new RandomAccessFile(myTemplate, "r");
reader.read(stream);
stream.close();
SectionReader sectionReader = reader.getSectionReader(".rsrc");
ResourceSectionReader resourceReader = (ResourceSectionReader) sectionReader.getMember(".rsrc");
DirectoryEntry root = resourceReader.getRoot();
DirectoryEntry subDir = root.findSubDir("IRD6");
DirectoryEntry ird7 = subDir.findSubDir("IRD7");
RawResource rawResource = ird7.getRawResource(0);
Bin.Bytes bytes = rawResource.getBytes();
StringTable srtingTable = new StringTable(bytes.getBytes());
if (myBmp != null) {
srtingTable.setString(0, "splash"); // show splash
}
srtingTable.setString(1, "alive"); // stay alive
srtingTable.setString(2, "" + mySplashTimeout); // splash timeout
if (myJreBundlePath != null) {
srtingTable.setString(3, myJreBundlePath); // jre bundled path
}
if (myVmParameters != null) {
srtingTable.setString(4, myVmParameters); // PROP_VM_PARAMETERS
}
if (myStartJar != null) {
srtingTable.setString(5, myStartJar); // PROP_START_JAR
}
srtingTable.setString(6, "waitForWindow"); // PROP_WAIT_FOR_WINDOW
if (myWorkDir != null) {
srtingTable.setString(7, myWorkDir); // PROP_WORK_DIR
}
if (myJavaVersion != null) {
StringBuffer buffer = new StringBuffer( myJavaVersion.length() );
for( int i = 0; i < myJavaVersion.length(); ++i){
char ch = myJavaVersion.charAt( i );
if ( Character.isDigit( ch ) || ch == '.' ){
buffer.append( ch );
}
}
srtingTable.setString(8, buffer.toString()); // PROP_JAVA_VERSION
}
if (mySearchJdkOnly) {
srtingTable.setString(9, "SearchJdkOnly"); // PROP_SEARCH_JDK_ONLY
}
byte[] a = srtingTable.getBytes();
rawResource.setBytes(a);
myStringTableDirectory.save();
if (myIcon != null) {
IconResourceInjector iconInjector = new IconResourceInjector();
iconInjector.injectIcon(myIcon, root, "IRD101");
iconInjector.injectIcon(myIcon, myRoot, "IRD101");
}
if (myBmp != null) {
PictureResourceInjector bmpInjector = new PictureResourceInjector();
bmpInjector.inject(myBmp, root, "IRD104");
bmpInjector.inject(myBmp, myRoot, "IRD104");
}
DirectoryEntry viDir = root.findSubDir("IRD16").findSubDir( "IRD1" );
DirectoryEntry viDir = myRoot.findSubDir("IRD16").findSubDir( "IRD1" );
Bin.Bytes viBytes = viDir.getRawResource( 0 ).getBytes();
ByteArrayInputStream bytesStream = new ByteArrayInputStream(viBytes.getBytes());
VersionInfo viReader = new VersionInfo();
viReader.read(new OffsetTrackingInputStream(new DataInputStream(bytesStream)));
reader.resetOffsets(0);
myReader.resetOffsets(0);
myExePath.getParentFile().mkdirs();
myExePath.createNewFile();
RandomAccessFile exeStream = new RandomAccessFile(myExePath, "rw");
reader.write(exeStream);
myReader.write(exeStream);
exeStream.close();
}
public void setJavaVersion(String javaVersion) {
myJavaVersion = javaVersion;
}
public void setSearchJdkOnly(boolean searchJdkOnly) {
mySearchJdkOnly = searchJdkOnly;
public void setResourceString(int id, String value) {
myStringTableDirectory.setString(id, value);
}
}
@@ -0,0 +1,124 @@
/*
* Copyright 2006 ProductiveMe Inc.
* Copyright 2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pme.launcher;
import org.jdom.Document;
import org.jdom.input.SAXBuilder;
import java.io.*;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author yole
*/
public class LauncherGeneratorMain {
public static void main(String[] args) {
if (args.length != 5) {
System.err.println("Usage: LauncherGeneratorMain <template EXE file> <app info file> <resource.h file> <properties> <output>");
System.exit(1);
}
File template = new File(args[0]);
if (!template.exists()) {
System.err.println("Launcher template EXE file " + args[0] + " not found");
System.exit(1);
}
InputStream appInfoStream = LauncherGeneratorMain.class.getClassLoader().getResourceAsStream(args[1]);
if (appInfoStream == null) {
System.err.println("Application info file " + appInfoStream + " not found");
System.exit(1);
}
Document appInfo;
try {
appInfo = new SAXBuilder().build(appInfoStream);
} catch (Exception e) {
System.err.println("Error loading application info file " + appInfoStream + ": " + e.getMessage());
System.exit(1);
}
Map<String, Integer> resourceIDs;
try {
resourceIDs = loadResourceIDs(args[2]);
}
catch (Exception e) {
System.err.println("Error loading resource.h: " + e.getMessage());
System.exit(1);
return;
}
Properties properties = new Properties();
try {
FileInputStream fis = new FileInputStream(args[3]);
try {
properties.load(fis);
}
finally {
fis.close();
}
}
catch (IOException e) {
System.err.println("Error loading launcher properties: " + e.getMessage());
System.exit(1);
}
LauncherGenerator generator = new LauncherGenerator(template, new File(args[4]));
try {
generator.load();
for (Map.Entry<Object, Object> pair : properties.entrySet()) {
String key = (String) pair.getKey();
Integer id = resourceIDs.get(key);
if (id == null) {
System.err.println("Invalid stringtable ID found: " + key);
System.exit(1);
}
generator.setResourceString(id, (String) pair.getValue());
}
generator.generate();
} catch (IOException e) {
e.printStackTrace();
}
}
private static Map<String, Integer> loadResourceIDs(String arg) throws IOException {
Map<String, Integer> result = new HashMap<String, Integer>();
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(arg), Charset.forName("UTF-16")));
Pattern pattern = Pattern.compile("#define (\\w+)\\s+(\\d+)");
try {
while(true) {
String line = reader.readLine();
if (line == null) break;
Matcher m = pattern.matcher(line);
if (m.matches()) {
result.put(m.group(1), Integer.parseInt(m.group(2)));
}
}
}
finally {
reader.close();
}
return result;
}
}