davmail/archive/jsmooth-0.9.9-7-patch/src/net/charabia/jsmoothgen/pe/PEResourceDirectory.java

626 lines
20 KiB
Java

/*
JSmooth: a VM wrapper toolkit for Windows
Copyright (C) 2003 Rodrigo Reyes <reyes@charabia.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* PEResourceDirectory.java
*
* Created on 2 aout 2003, 01:28
*/
package net.charabia.jsmoothgen.pe;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.Iterator;
import java.util.Vector;
/**
* @author Rodrigo
*/
public class PEResourceDirectory {
/*
typedef struct IMAGE_RESOURCE_DIRECTORY {
uint32_t Characteristics;
uint32_t TimeDateStamp;
uint16_t MajorVersion;
uint16_t MinorVersion;
uint16_t NumberOfNamedEntries;
uint16_t NumberOfIdEntries;
}
*/
public class DataEntry {
long OffsetToData; // To update at each change
long Size;
long CodePage; // never changed
long Reserved; // never changed
ByteBuffer Data;
public DataEntry(ByteBuffer data) {
this.Data = data;
this.Size = data.capacity();
}
public DataEntry(FileChannel chan, long offset) throws IOException {
long orgpos = chan.position();
chan.position(PEResourceDirectory.this.m_offsetBase + offset);
ByteBuffer buf = ByteBuffer.allocate(16);
buf.order(ByteOrder.LITTLE_ENDIAN);
chan.read(buf);
buf.position(0);
OffsetToData = buf.getInt();
Size = buf.getInt();
CodePage = buf.getInt();
Reserved = buf.getInt();
long datapos = PEResourceDirectory.this.m_master.PointerToRawData + (OffsetToData - PEResourceDirectory.this.m_master.VirtualAddress);
Data = ByteBuffer.allocate((int)Size);
Data.order(ByteOrder.LITTLE_ENDIAN);
chan.position(datapos);
chan.read(Data);
Data.position(0);
chan.position(orgpos);
}
public int diskSize() {
int size = 16 + (int)this.Size;
if ((size % 4) > 0)
size += 4 - (size % 4);
return size;
}
public void dump(PrintStream out, int level) {
indent(level, out);
out.println("OffsetToData=" + OffsetToData);
indent(level, out);
out.println("Size=" + Size);
indent(level, out);
out.println("CodePage=" + CodePage);
indent(level, out);
out.println("Reserved=" + Reserved);
indent(level, out);
out.print("Data={ ");
for (int i = 0; i < this.Data.capacity(); i++) {
out.print("" + Integer.toHexString((byte)Data.get()) + ",");
}
out.println(" }");
}
private void indent(int level, PrintStream out) {
for (int i = 0; i < level; i++)
out.print(" ");
}
public int buildBuffer(ByteBuffer buffer, long virtualBaseOffset, int dataOffset) {
// System.out.println("Building Data Entry buffer @ " + buffer.position() + " (" + dataOffset + ")");
dataOffset = buffer.position() + 16;
buffer.putInt((int)(dataOffset + virtualBaseOffset));
buffer.putInt((int)Size);
buffer.putInt((int)CodePage);
buffer.putInt((int)Reserved);
Data.position(0);
buffer.put(Data);
dataOffset += Size;
if ((dataOffset % 4) > 0)
dataOffset += (4 - (dataOffset % 4));
return dataOffset;
}
public void setData(ByteBuffer data) {
Data = data;
Size = data.capacity();
}
}
public class ResourceEntry {
int Id;
String Name;
ImageResourceDirectory Directory;
DataEntry Data;
public ResourceEntry(int id, DataEntry data) {
this.Id = id;
this.Data = data;
}
public ResourceEntry(String name, DataEntry data) {
this.Name = name;
this.Data = data;
}
public ResourceEntry(int id, ImageResourceDirectory dir) {
this.Id = id;
this.Directory = dir;
}
public ResourceEntry(String name, ImageResourceDirectory dir) {
this.Name = name;
this.Directory = dir;
}
public ResourceEntry(FileChannel chan) throws IOException {
// System.out.println("Resource Entry Offset: " + chan.position());
ByteBuffer buf = ByteBuffer.allocate(8);
buf.order(ByteOrder.LITTLE_ENDIAN);
chan.read(buf);
buf.position(0);
long orgchanpos = chan.position();
int val = buf.getInt();
long offsetToData = buf.getInt();
// System.out.println("Entry: Val=" + val);
// System.out.println(" Off=" + offsetToData);
if (val < 0) {
val &= 0x7FFFFFFF;
Name = extractStringAt(chan, val);
Id = -1;
// System.out.println(" String at " + val + " = " + Name);
} else {
Id = val;
}
if (offsetToData < 0) {
offsetToData &= 0x7FFFFFFF;
long orgpos = chan.position();
chan.position(PEResourceDirectory.this.m_offsetBase + offsetToData);
Directory = new PEResourceDirectory.ImageResourceDirectory(chan);
chan.position(orgpos);
} else {
Data = new DataEntry(chan, offsetToData);
}
}
public String extractStringAt(FileChannel chan, int offset) throws IOException {
long orgchanpos = chan.position();
chan.position(PEResourceDirectory.this.m_offsetBase + offset);
ByteBuffer sizebuf = ByteBuffer.allocate(2);
sizebuf.order(ByteOrder.LITTLE_ENDIAN);
chan.read(sizebuf);
sizebuf.position(0);
int size = sizebuf.getShort();
ByteBuffer buffer = ByteBuffer.allocate(size * 2);
buffer.order(ByteOrder.LITTLE_ENDIAN);
chan.read(buffer);
buffer.position(0);
StringBuffer buf = new StringBuffer(size);
for (int i = 0; i < size; i++) {
int c = buffer.getShort();
buf.append((char)c);
}
chan.position(orgchanpos);
return buf.toString();
}
public int diskSize() {
int size = 8;
if (Name != null)
size += (Name.length() * 2) + 2;
if (Directory != null)
size += Directory.diskSize();
else if (Data != null)
size += Data.diskSize();
if ((size % 4) > 0)
size += 4 - (size % 4);
return size;
}
public void dump(PrintStream out, int level) {
indent(level, out);
if (this.Name != null)
out.println("Name=" + Name);
else
out.println("Id=#" + Id);
indent(level, out);
if (this.Directory != null) {
out.println("ENTRY: DIRECTORY POINTER");
this.Directory.dump(out, level + 1);
} else {
out.println("ENTRY: DATA ENTRY");
Data.dump(out, level + 1);
}
}
private void indent(int level, PrintStream out) {
for (int i = 0; i < level; i++)
out.print(" ");
}
public int buildBuffer(ByteBuffer buffer, long virtualBaseOffset, int dataOffset) {
// System.out.println("Building Resource Entry buffer " + Name + "/" + Id + " @ " + buffer.position() + " (" + dataOffset + ")");
if (Name != null) {
buffer.putInt((int)(dataOffset | 0x80000000));
int stringoffset = dataOffset;
ByteBuffer strbuf = ByteBuffer.allocate(Name.length() * 2 + 2);
strbuf.order(ByteOrder.LITTLE_ENDIAN);
strbuf.putShort((short)Name.length());
for (int i = 0; i < Name.length(); i++) {
strbuf.putShort((short)Name.charAt(i));
}
strbuf.position(0);
long oldpos = buffer.position();
buffer.position(dataOffset);
buffer.put(strbuf);
dataOffset += Name.length() * 2 + 2;
if ((dataOffset % 4) != 0)
dataOffset += 4 - (dataOffset % 4);
buffer.position((int) oldpos);
} else {
buffer.putInt(Id);
}
if (Directory != null) {
buffer.putInt((int)(dataOffset | 0x80000000));
int oldpos = buffer.position();
buffer.position(dataOffset);
int dirsize = Directory.buildBuffer(buffer, virtualBaseOffset);
dataOffset = dirsize;
buffer.position(oldpos);
} else if (Data != null) {
buffer.putInt(dataOffset);
int oldpos = buffer.position();
buffer.position(dataOffset);
dataOffset = Data.buildBuffer(buffer, virtualBaseOffset, dataOffset);
buffer.position(oldpos);
} else {
throw new RuntimeException("Directory and Data are both null!");
}
return dataOffset;
}
}
public class ImageResourceDirectory {
long Characteristics; // uint32_t
long TimeDateStamp; // uint32_t
int MajorVersion; // uint16_t
int MinorVersion; // uint16_t
int NumberOfNamedEntries; // uint16_t
int NumberOfIdEntries; // uint16_t
Vector NamedEntries = new Vector();
Vector IdEntries = new Vector();
public ImageResourceDirectory() {
}
public ImageResourceDirectory(FileChannel chan) throws IOException {
ByteBuffer header = ByteBuffer.allocate(16);
header.order(ByteOrder.LITTLE_ENDIAN);
chan.read(header);
header.position(0);
Characteristics = header.getInt();
TimeDateStamp = header.getInt();
MajorVersion = header.getShort();
MinorVersion = header.getShort();
NumberOfNamedEntries = header.getShort();
NumberOfIdEntries = header.getShort();
for (int i = 0; i < NumberOfNamedEntries; i++) {
ResourceEntry re = new ResourceEntry(chan);
NamedEntries.add(re);
}
for (int i = 0; i < NumberOfIdEntries; i++) {
ResourceEntry re = new ResourceEntry(chan);
IdEntries.add(re);
}
}
public void addNamedEntry(ResourceEntry entry) {
this.NamedEntries.add(entry);
}
public void addIdEntry(ResourceEntry entry) {
this.IdEntries.add(entry);
}
public void addEntry(ResourceEntry entry) {
if (entry.Name != null)
addNamedEntry(entry);
else
addIdEntry(entry);
}
public void dump(PrintStream out, int level) {
indent(level, out);
out.println("Directory: ");
indent(level, out);
out.println("Characteristics=" + this.Characteristics);
indent(level, out);
out.println("TimeDateStamp=" + this.TimeDateStamp);
indent(level, out);
out.println("MajorVersion=" + this.MajorVersion);
indent(level, out);
out.println("MinorVersion=" + this.MinorVersion);
indent(level, out);
out.println("NumberOfNamedEntries=" + this.NumberOfNamedEntries);
indent(level, out);
out.println("NumberOfIdEntries=" + this.NumberOfIdEntries);
indent(level, out);
out.println("Named Entries:");
for (int i = 0; i < NumberOfNamedEntries; i++) {
ResourceEntry re = (ResourceEntry)NamedEntries.get(i);
re.dump(out, level + 1);
}
indent(level, out);
out.println("Id Entries:");
for (int i = 0; i < NumberOfIdEntries; i++) {
ResourceEntry re = (ResourceEntry)IdEntries.get(i);
re.dump(out, level + 1);
}
}
private void indent(int level, PrintStream out) {
for (int i = 0; i < level; i++)
out.print(" ");
}
public int diskSize() {
int size = 16;
for (int i = 0; i < this.NamedEntries.size(); i++) {
ResourceEntry re = (ResourceEntry)NamedEntries.get(i);
size += re.diskSize();
}
for (int i = 0; i < this.IdEntries.size(); i++) {
ResourceEntry re = (ResourceEntry)IdEntries.get(i);
size += re.diskSize();
}
if ((size % 4) > 0)
size += 4 - (size % 4);
return size;
}
public int buildBuffer(ByteBuffer buffer, long virtualBaseOffset) {
// System.out.println("Building Directory Entry buffer @ " + buffer.position());
buffer.putInt((int)this.Characteristics);
buffer.putInt((int)this.TimeDateStamp);
buffer.putShort((short)this.MajorVersion);
buffer.putShort((short)this.MinorVersion);
buffer.putShort((short)this.NamedEntries.size());
buffer.putShort((short)this.IdEntries.size());
int dataOffset = buffer.position() + (NamedEntries.size() * 8) + (IdEntries.size() * 8);
for (int i = 0; i < this.NamedEntries.size(); i++) {
ResourceEntry re = (ResourceEntry)this.NamedEntries.get(i);
dataOffset = re.buildBuffer(buffer, virtualBaseOffset, dataOffset);
}
for (int i = 0; i < this.IdEntries.size(); i++) {
ResourceEntry re = (ResourceEntry)this.IdEntries.get(i);
dataOffset = re.buildBuffer(buffer, virtualBaseOffset, dataOffset);
}
buffer.position(dataOffset);
return dataOffset;
}
public ResourceEntry getResourceEntry(String name) {
// If name == null, get the first entry in lexical
// order. If no entry in lexical order, choose the
// lowest integer id entry.
if (name == null) {
if (NamedEntries.size() > 0) {
return (PEResourceDirectory.ResourceEntry)NamedEntries.get(0);
}
if (IdEntries.size() > 0) {
return (PEResourceDirectory.ResourceEntry)IdEntries.get(0);
}
return null;
}
if ((name.length() > 0) && (name.charAt(0) == '#')) {
try {
String nb = name.substring(1);
int i = Integer.parseInt(nb);
return getResourceEntry(i);
} catch (Exception exc) {
exc.printStackTrace();
}
}
for (Iterator i = this.NamedEntries.iterator(); i.hasNext();) {
ResourceEntry re = (ResourceEntry)i.next();
if (name.equals(re.Name)) {
return re;
}
}
return null;
}
public ResourceEntry getResourceEntry(int id) {
for (Iterator i = this.IdEntries.iterator(); i.hasNext();) {
ResourceEntry re = (ResourceEntry)i.next();
if (id == re.Id) {
return re;
}
}
return null;
}
}
PESection m_master;
PEFile m_file;
long m_offsetBase;
PEResourceDirectory.ImageResourceDirectory m_root;
/**
* Creates a new instance of PEResourceDirectory
*/
public PEResourceDirectory(PEFile file, PESection sect) throws IOException {
m_master = sect;
m_file = file;
m_offsetBase = sect.PointerToRawData;
init();
// System.out.println("--------\nTOTAL SIZE: " + m_root.diskSize());
// System.out.println("\n\n");
}
public void init() throws IOException {
// / System.out.println("RESOURCE INIT");
// System.out.println(" Offset: " + m_master.PointerToRawData);
FileChannel chan = m_file.getChannel();
chan.position(m_master.PointerToRawData);
PEResourceDirectory.ImageResourceDirectory dir = new PEResourceDirectory.ImageResourceDirectory(chan);
// System.out.println("-----------------\nDUMP\n---------------");
m_root = dir;
// dir.dump(System.out, 0);
}
public void dump(PrintStream out) {
m_root.dump(out, 0);
}
public int size() {
return m_root.diskSize();
}
public ByteBuffer buildResource(long virtualBaseOffset) {
// System.out.println("BUILDING RESOURCE / VIRTUAL: " + virtualBaseOffset);
int resourceSize = m_root.diskSize();
ByteBuffer resbuf = ByteBuffer.allocate(resourceSize);
resbuf.order(ByteOrder.LITTLE_ENDIAN);
resbuf.position(0);
m_root.buildBuffer(resbuf, virtualBaseOffset);
return resbuf;
}
public PEResourceDirectory.ImageResourceDirectory getRoot() {
return m_root;
}
public boolean replaceManifest(int resourceId, int langId, ByteBuffer data) {
ResourceEntry catEntry = m_root.getResourceEntry(24);
if ((catEntry != null) && (catEntry.Directory != null)) {
ResourceEntry identEntry = catEntry.Directory.getResourceEntry(resourceId);
if ((identEntry != null) && (identEntry.Directory != null)) {
ResourceEntry langEntry = identEntry.Directory.getResourceEntry(langId);
if ((langEntry != null) && (langEntry.Data != null)) {
DataEntry dataslot = langEntry.Data;
dataslot.setData(data);
return true;
}
}
}
return false;
}
public boolean replaceResource(String catId, int resourceId, int langId, ByteBuffer data) {
ResourceEntry catEntry = m_root.getResourceEntry(catId);
if ((catEntry != null) && (catEntry.Directory != null)) {
ResourceEntry identEntry = catEntry.Directory.getResourceEntry(resourceId);
if ((identEntry != null) && (identEntry.Directory != null)) {
ResourceEntry langEntry = identEntry.Directory.getResourceEntry(langId);
if ((langEntry != null) && (langEntry.Data != null)) {
DataEntry dataslot = langEntry.Data;
dataslot.setData(data);
return true;
}
}
}
return false;
}
public void addNewResource(String catId, String resourceId, String languageId, ByteBuffer data) {
DataEntry dataEntry = new DataEntry(data);
ResourceEntry languageEntry = buildResourceEntry(languageId, dataEntry);
ImageResourceDirectory languageDir = new ImageResourceDirectory();
languageDir.TimeDateStamp = 0x3F2CCF64;
languageDir.addEntry(languageEntry);
ResourceEntry identEntry = buildResourceEntry(resourceId, languageDir);
ImageResourceDirectory identDir = new ImageResourceDirectory();
identDir.TimeDateStamp = 0x3F2CCF64;
identDir.addEntry(identEntry);
ResourceEntry catEntry = buildResourceEntry(catId, identDir);
m_root.addEntry(catEntry);
}
public DataEntry getData(String catId, String resourceId, String langId) {
ResourceEntry catEntry = m_root.getResourceEntry(catId);
if ((catEntry != null) && (catEntry.Directory != null)) {
ResourceEntry identEntry = catEntry.Directory.getResourceEntry(resourceId);
if ((identEntry != null) && (identEntry.Directory != null)) {
ResourceEntry langEntry = identEntry.Directory.getResourceEntry(langId);
if ((langEntry != null) && (langEntry.Data != null)) {
DataEntry dataslot = langEntry.Data;
return dataslot;
}
}
}
return null;
}
public ResourceEntry buildResourceEntry(String id, DataEntry data) {
if ((id.length() > 1) && (id.charAt(0) == '#')) {
int intid = Integer.parseInt(id.substring(1));
return new ResourceEntry(intid, data);
}
return new ResourceEntry(id, data);
}
public ResourceEntry buildResourceEntry(String id, ImageResourceDirectory dir) {
if ((id.length() > 1) && (id.charAt(0) == '#')) {
int intid = Integer.parseInt(id.substring(1));
return new ResourceEntry(intid, dir);
}
return new ResourceEntry(id, dir);
}
}