001: /*
002: JSmooth: a VM wrapper toolkit for Windows
003: Copyright (C) 2003 Rodrigo Reyes <reyes@charabia.net>
004:
005: This program is free software; you can redistribute it and/or modify
006: it under the terms of the GNU General Public License as published by
007: the Free Software Foundation; either version 2 of the License, or
008: (at your option) any later version.
009:
010: This program is distributed in the hope that it will be useful,
011: but WITHOUT ANY WARRANTY; without even the implied warranty of
012: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013: GNU General Public License for more details.
014:
015: You should have received a copy of the GNU General Public License
016: along with this program; if not, write to the Free Software
017: Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
018:
019: */
020:
021: /*
022: * PEResourceDirectory.java
023: *
024: * Created on 2 août 2003, 01:28
025: */
026:
027: package net.charabia.jsmoothgen.pe;
028:
029: import java.util.*;
030: import java.io.*;
031: import java.nio.*;
032: import java.nio.channels.*;
033:
034: /**
035: *
036: * @author Rodrigo
037: */
038: public class PEResourceDirectory {
039:
040: /*
041: typedef struct IMAGE_RESOURCE_DIRECTORY {
042: uint32_t Characteristics;
043: uint32_t TimeDateStamp;
044: uint16_t MajorVersion;
045: uint16_t MinorVersion;
046: uint16_t NumberOfNamedEntries;
047: uint16_t NumberOfIdEntries;
048: }
049: */
050:
051: public class DataEntry {
052: long OffsetToData; // To update at each change
053: long Size;
054: long CodePage; // never changed
055: long Reserved; // never changed
056: ByteBuffer Data;
057:
058: public DataEntry(ByteBuffer data) {
059: this .Data = data;
060: this .Size = data.capacity();
061: }
062:
063: public DataEntry(FileChannel chan, long offset)
064: throws IOException {
065: long orgpos = chan.position();
066: chan.position(PEResourceDirectory.this .m_offsetBase
067: + offset);
068: ByteBuffer buf = ByteBuffer.allocate(16);
069: buf.order(ByteOrder.LITTLE_ENDIAN);
070: chan.read(buf);
071: buf.position(0);
072:
073: OffsetToData = buf.getInt();
074: Size = buf.getInt();
075: CodePage = buf.getInt();
076: Reserved = buf.getInt();
077:
078: long datapos = PEResourceDirectory.this .m_master.PointerToRawData
079: + (OffsetToData - PEResourceDirectory.this .m_master.VirtualAddress);
080: Data = ByteBuffer.allocate((int) Size);
081: Data.order(ByteOrder.LITTLE_ENDIAN);
082: chan.position(datapos);
083: chan.read(Data);
084: Data.position(0);
085:
086: chan.position(orgpos);
087: }
088:
089: public int diskSize() {
090: int size = 16 + (int) this .Size;
091: if ((size % 4) > 0)
092: size += 4 - (size % 4);
093: return size;
094: }
095:
096: public void dump(PrintStream out, int level) {
097: indent(level, out);
098: out.println("OffsetToData=" + OffsetToData);
099: indent(level, out);
100: out.println("Size=" + Size);
101: indent(level, out);
102: out.println("CodePage=" + CodePage);
103: indent(level, out);
104: out.println("Reserved=" + Reserved);
105: indent(level, out);
106: out.print("Data={ ");
107: for (int i = 0; i < this .Data.capacity(); i++) {
108: out.print("" + Integer.toHexString((byte) Data.get())
109: + ",");
110: }
111: out.println(" }");
112:
113: }
114:
115: private void indent(int level, PrintStream out) {
116: for (int i = 0; i < level; i++)
117: out.print(" ");
118: }
119:
120: public int buildBuffer(ByteBuffer buffer,
121: long virtualBaseOffset, int dataOffset) {
122: // System.out.println("Building Data Entry buffer @ " + buffer.position() + " (" + dataOffset + ")");
123:
124: dataOffset = buffer.position() + 16;
125:
126: buffer.putInt((int) (dataOffset + virtualBaseOffset));
127: buffer.putInt((int) Size);
128: buffer.putInt((int) CodePage);
129: buffer.putInt((int) Reserved);
130:
131: Data.position(0);
132: buffer.put(Data);
133:
134: dataOffset += Size;
135: if ((dataOffset % 4) > 0)
136: dataOffset += (4 - (dataOffset % 4));
137:
138: return dataOffset;
139: }
140:
141: public void setData(ByteBuffer data) {
142: Data = data;
143: Size = data.capacity();
144: }
145: }
146:
147: public class ResourceEntry {
148: int Id;
149: String Name;
150:
151: ImageResourceDirectory Directory;
152: DataEntry Data;
153:
154: public ResourceEntry(int id, DataEntry data) {
155: this .Id = id;
156: this .Data = data;
157: }
158:
159: public ResourceEntry(String name, DataEntry data) {
160: this .Name = name;
161: this .Data = data;
162: }
163:
164: public ResourceEntry(int id, ImageResourceDirectory dir) {
165: this .Id = id;
166: this .Directory = dir;
167: }
168:
169: public ResourceEntry(String name, ImageResourceDirectory dir) {
170: this .Name = name;
171: this .Directory = dir;
172: }
173:
174: public ResourceEntry(FileChannel chan) throws IOException {
175: // System.out.println("Resource Entry Offset: " + chan.position());
176: ByteBuffer buf = ByteBuffer.allocate(8);
177: buf.order(ByteOrder.LITTLE_ENDIAN);
178: chan.read(buf);
179: buf.position(0);
180: long orgchanpos = chan.position();
181: int val = buf.getInt();
182: long offsetToData = buf.getInt();
183: // System.out.println("Entry: Val=" + val);
184: // System.out.println(" Off=" + offsetToData);
185:
186: if (val < 0) {
187: val &= 0x7FFFFFFF;
188: Name = extractStringAt(chan, val);
189: Id = -1;
190: // System.out.println(" String at " + val + " = " + Name);
191: } else {
192: Id = val;
193: }
194:
195: if (offsetToData < 0) {
196: offsetToData &= 0x7FFFFFFF;
197: long orgpos = chan.position();
198: chan.position(PEResourceDirectory.this .m_offsetBase
199: + offsetToData);
200: Directory = new PEResourceDirectory.ImageResourceDirectory(
201: chan);
202: chan.position(orgpos);
203: } else {
204: Data = new DataEntry(chan, offsetToData);
205: }
206: }
207:
208: public String extractStringAt(FileChannel chan, int offset)
209: throws IOException {
210: long orgchanpos = chan.position();
211: chan.position(PEResourceDirectory.this .m_offsetBase
212: + offset);
213:
214: ByteBuffer sizebuf = ByteBuffer.allocate(2);
215: sizebuf.order(ByteOrder.LITTLE_ENDIAN);
216: chan.read(sizebuf);
217: sizebuf.position(0);
218:
219: int size = sizebuf.getShort();
220: ByteBuffer buffer = ByteBuffer.allocate(size * 2);
221: buffer.order(ByteOrder.LITTLE_ENDIAN);
222: chan.read(buffer);
223: buffer.position(0);
224:
225: StringBuffer buf = new StringBuffer(size);
226: for (int i = 0; i < size; i++) {
227: int c = buffer.getShort();
228: buf.append((char) c);
229: }
230:
231: chan.position(orgchanpos);
232: return buf.toString();
233: }
234:
235: public int diskSize() {
236: int size = 8;
237: if (Name != null)
238: size += (Name.length() * 2) + 2;
239:
240: if (Directory != null)
241: size += Directory.diskSize();
242: else if (Data != null)
243: size += Data.diskSize();
244:
245: if ((size % 4) > 0)
246: size += 4 - (size % 4);
247: return size;
248: }
249:
250: public void dump(PrintStream out, int level) {
251: indent(level, out);
252: if (this .Name != null)
253: out.println("Name=" + Name);
254: else
255: out.println("Id=#" + Id);
256:
257: indent(level, out);
258: if (this .Directory != null) {
259: out.println("ENTRY: DIRECTORY POINTER");
260: this .Directory.dump(out, level + 1);
261: } else {
262: out.println("ENTRY: DATA ENTRY");
263: Data.dump(out, level + 1);
264: }
265: }
266:
267: private void indent(int level, PrintStream out) {
268: for (int i = 0; i < level; i++)
269: out.print(" ");
270: }
271:
272: public int buildBuffer(ByteBuffer buffer,
273: long virtualBaseOffset, int dataOffset) {
274: // System.out.println("Building Resource Entry buffer " + Name + "/" + Id + " @ " + buffer.position() + " (" + dataOffset + ")");
275: if (Name != null) {
276: buffer.putInt((int) (dataOffset | 0x80000000));
277:
278: int stringoffset = dataOffset;
279: ByteBuffer strbuf = ByteBuffer
280: .allocate(Name.length() * 2 + 2);
281: strbuf.order(ByteOrder.LITTLE_ENDIAN);
282:
283: strbuf.putShort((short) Name.length());
284: for (int i = 0; i < Name.length(); i++) {
285: strbuf.putShort((short) Name.charAt(i));
286: }
287: strbuf.position(0);
288:
289: long oldpos = buffer.position();
290: buffer.position(dataOffset);
291: buffer.put(strbuf);
292: dataOffset += Name.length() * 2 + 2;
293: if ((dataOffset % 4) != 0)
294: dataOffset += 4 - (dataOffset % 4);
295: buffer.position((int) oldpos);
296: } else {
297: buffer.putInt(Id);
298: }
299:
300: if (Directory != null) {
301: buffer.putInt((int) (dataOffset | 0x80000000));
302:
303: int oldpos = buffer.position();
304: buffer.position(dataOffset);
305: int dirsize = Directory.buildBuffer(buffer,
306: virtualBaseOffset);
307: dataOffset = dirsize;
308: buffer.position(oldpos);
309:
310: } else if (Data != null) {
311: buffer.putInt(dataOffset);
312: int oldpos = buffer.position();
313: buffer.position(dataOffset);
314: dataOffset = Data.buildBuffer(buffer,
315: virtualBaseOffset, dataOffset);
316: buffer.position(oldpos);
317: } else {
318: throw new RuntimeException(
319: "Directory and Data are both null!");
320: }
321:
322: return dataOffset;
323: }
324: }
325:
326: public class ImageResourceDirectory {
327: long Characteristics; // uint32_t
328: long TimeDateStamp; // uint32_t
329: int MajorVersion; // uint16_t
330: int MinorVersion; // uint16_t
331: int NumberOfNamedEntries; // uint16_t
332: int NumberOfIdEntries; // uint16_t
333: Vector NamedEntries = new Vector();
334: Vector IdEntries = new Vector();
335:
336: public ImageResourceDirectory() {
337: }
338:
339: public ImageResourceDirectory(FileChannel chan)
340: throws IOException {
341: ByteBuffer header = ByteBuffer.allocate(16);
342: header.order(ByteOrder.LITTLE_ENDIAN);
343: chan.read(header);
344: header.position(0);
345:
346: Characteristics = header.getInt();
347: TimeDateStamp = header.getInt();
348: MajorVersion = header.getShort();
349: MinorVersion = header.getShort();
350: NumberOfNamedEntries = header.getShort();
351: NumberOfIdEntries = header.getShort();
352:
353: for (int i = 0; i < NumberOfNamedEntries; i++) {
354: ResourceEntry re = new ResourceEntry(chan);
355: NamedEntries.add(re);
356: }
357: for (int i = 0; i < NumberOfIdEntries; i++) {
358: ResourceEntry re = new ResourceEntry(chan);
359: IdEntries.add(re);
360: }
361: }
362:
363: public void addNamedEntry(ResourceEntry entry) {
364: this .NamedEntries.add(entry);
365: }
366:
367: public void addIdEntry(ResourceEntry entry) {
368: this .IdEntries.add(entry);
369: }
370:
371: public void addEntry(ResourceEntry entry) {
372: if (entry.Name != null)
373: addNamedEntry(entry);
374: else
375: addIdEntry(entry);
376: }
377:
378: public void dump(PrintStream out, int level) {
379: indent(level, out);
380: out.println("Directory: ");
381: indent(level, out);
382: out.println("Characteristics=" + this .Characteristics);
383: indent(level, out);
384: out.println("TimeDateStamp=" + this .TimeDateStamp);
385: indent(level, out);
386: out.println("MajorVersion=" + this .MajorVersion);
387: indent(level, out);
388: out.println("MinorVersion=" + this .MinorVersion);
389: indent(level, out);
390: out.println("NumberOfNamedEntries="
391: + this .NumberOfNamedEntries);
392: indent(level, out);
393: out.println("NumberOfIdEntries=" + this .NumberOfIdEntries);
394: indent(level, out);
395: out.println("Named Entries:");
396: for (int i = 0; i < NumberOfNamedEntries; i++) {
397: ResourceEntry re = (ResourceEntry) NamedEntries.get(i);
398: re.dump(out, level + 1);
399: }
400: indent(level, out);
401: out.println("Id Entries:");
402: for (int i = 0; i < NumberOfIdEntries; i++) {
403: ResourceEntry re = (ResourceEntry) IdEntries.get(i);
404: re.dump(out, level + 1);
405: }
406: }
407:
408: private void indent(int level, PrintStream out) {
409: for (int i = 0; i < level; i++)
410: out.print(" ");
411: }
412:
413: public int diskSize() {
414: int size = 16;
415: for (int i = 0; i < this .NamedEntries.size(); i++) {
416: ResourceEntry re = (ResourceEntry) NamedEntries.get(i);
417: size += re.diskSize();
418: }
419: for (int i = 0; i < this .IdEntries.size(); i++) {
420: ResourceEntry re = (ResourceEntry) IdEntries.get(i);
421: size += re.diskSize();
422: }
423:
424: if ((size % 4) > 0)
425: size += 4 - (size % 4);
426:
427: return size;
428: }
429:
430: public int buildBuffer(ByteBuffer buffer, long virtualBaseOffset) {
431: // System.out.println("Building Directory Entry buffer @ " + buffer.position());
432:
433: buffer.putInt((int) this .Characteristics);
434: buffer.putInt((int) this .TimeDateStamp);
435: buffer.putShort((short) this .MajorVersion);
436: buffer.putShort((short) this .MinorVersion);
437: buffer.putShort((short) this .NamedEntries.size());
438: buffer.putShort((short) this .IdEntries.size());
439:
440: int dataOffset = buffer.position()
441: + (NamedEntries.size() * 8)
442: + (IdEntries.size() * 8);
443:
444: for (int i = 0; i < this .NamedEntries.size(); i++) {
445: ResourceEntry re = (ResourceEntry) this .NamedEntries
446: .get(i);
447: dataOffset = re.buildBuffer(buffer, virtualBaseOffset,
448: dataOffset);
449: }
450:
451: for (int i = 0; i < this .IdEntries.size(); i++) {
452: ResourceEntry re = (ResourceEntry) this .IdEntries
453: .get(i);
454: dataOffset = re.buildBuffer(buffer, virtualBaseOffset,
455: dataOffset);
456: }
457:
458: buffer.position(dataOffset);
459: return dataOffset;
460: }
461:
462: public ResourceEntry getResourceEntry(String name) {
463: // If name == null, get the first entry in lexical
464: // order. If no entry in lexical order, choose the
465: // lowest integer id entry.
466: if (name == null) {
467: if (NamedEntries.size() > 0) {
468: return (PEResourceDirectory.ResourceEntry) NamedEntries
469: .get(0);
470: }
471: if (IdEntries.size() > 0) {
472: return (PEResourceDirectory.ResourceEntry) IdEntries
473: .get(0);
474: }
475: return null;
476: }
477:
478: if ((name.length() > 0) && (name.charAt(0) == '#')) {
479: try {
480: String nb = name.substring(1);
481: int i = Integer.parseInt(nb);
482: return getResourceEntry(i);
483: } catch (Exception exc) {
484: exc.printStackTrace();
485: }
486: }
487:
488: for (Iterator i = this .NamedEntries.iterator(); i.hasNext();) {
489: ResourceEntry re = (ResourceEntry) i.next();
490: if (name.equals(re.Name)) {
491: return re;
492: }
493: }
494: return null;
495: }
496:
497: public ResourceEntry getResourceEntry(int id) {
498: for (Iterator i = this .IdEntries.iterator(); i.hasNext();) {
499: ResourceEntry re = (ResourceEntry) i.next();
500: if (id == re.Id) {
501: return re;
502: }
503: }
504: return null;
505: }
506:
507: }
508:
509: PESection m_master;
510: PEFile m_file;
511: long m_offsetBase;
512:
513: PEResourceDirectory.ImageResourceDirectory m_root;
514:
515: /** Creates a new instance of PEResourceDirectory */
516: public PEResourceDirectory(PEFile file, PESection sect)
517: throws IOException {
518: m_master = sect;
519: m_file = file;
520: m_offsetBase = sect.PointerToRawData;
521: init();
522:
523: // System.out.println("--------\nTOTAL SIZE: " + m_root.diskSize());
524:
525: // System.out.println("\n\n");
526: }
527:
528: public void init() throws IOException {
529: /// System.out.println("RESOURCE INIT");
530: // System.out.println(" Offset: " + m_master.PointerToRawData);
531: FileChannel chan = m_file.getChannel();
532: chan.position(m_master.PointerToRawData);
533: PEResourceDirectory.ImageResourceDirectory dir = new PEResourceDirectory.ImageResourceDirectory(
534: chan);
535: // System.out.println("-----------------\nDUMP\n---------------");
536: m_root = dir;
537:
538: // dir.dump(System.out, 0);
539: }
540:
541: public void dump(PrintStream out) {
542: m_root.dump(out, 0);
543: }
544:
545: public int size() {
546: return m_root.diskSize();
547: }
548:
549: public ByteBuffer buildResource(long virtualBaseOffset) {
550: // System.out.println("BUILDING RESOURCE / VIRTUAL: " + virtualBaseOffset);
551: int resourceSize = m_root.diskSize();
552: ByteBuffer resbuf = ByteBuffer.allocate(resourceSize);
553: resbuf.order(ByteOrder.LITTLE_ENDIAN);
554: resbuf.position(0);
555: m_root.buildBuffer(resbuf, virtualBaseOffset);
556: return resbuf;
557: }
558:
559: public PEResourceDirectory.ImageResourceDirectory getRoot() {
560: return m_root;
561: }
562:
563: public boolean replaceResource(String catId, int resourceId,
564: int langId, ByteBuffer data) {
565: ResourceEntry catEntry = m_root.getResourceEntry(catId);
566: if ((catEntry != null) && (catEntry.Directory != null)) {
567: ResourceEntry identEntry = catEntry.Directory
568: .getResourceEntry(resourceId);
569: if ((identEntry != null) && (identEntry.Directory != null)) {
570: ResourceEntry langEntry = identEntry.Directory
571: .getResourceEntry(langId);
572: if ((langEntry != null) && (langEntry.Data != null)) {
573: DataEntry dataslot = langEntry.Data;
574: dataslot.setData(data);
575: return true;
576: }
577: }
578: }
579: return false;
580: }
581:
582: public void addNewResource(String catId, String resourceId,
583: String languageId, ByteBuffer data) {
584: DataEntry dataEntry = new DataEntry(data);
585: ResourceEntry languageEntry = buildResourceEntry(languageId,
586: dataEntry);
587: ImageResourceDirectory languageDir = new ImageResourceDirectory();
588:
589: languageDir.TimeDateStamp = 0x3F2CCF64;
590: languageDir.addEntry(languageEntry);
591:
592: ResourceEntry identEntry = buildResourceEntry(resourceId,
593: languageDir);
594:
595: ImageResourceDirectory identDir = new ImageResourceDirectory();
596: identDir.TimeDateStamp = 0x3F2CCF64;
597: identDir.addEntry(identEntry);
598:
599: ResourceEntry catEntry = buildResourceEntry(catId, identDir);
600: m_root.addEntry(catEntry);
601: }
602:
603: public DataEntry getData(String catId, String resourceId,
604: String langId) {
605: ResourceEntry catEntry = m_root.getResourceEntry(catId);
606: if ((catEntry != null) && (catEntry.Directory != null)) {
607: ResourceEntry identEntry = catEntry.Directory
608: .getResourceEntry(resourceId);
609: if ((identEntry != null) && (identEntry.Directory != null)) {
610: ResourceEntry langEntry = identEntry.Directory
611: .getResourceEntry(langId);
612: if ((langEntry != null) && (langEntry.Data != null)) {
613: DataEntry dataslot = langEntry.Data;
614: return dataslot;
615: }
616: }
617: }
618: return null;
619: }
620:
621: public ResourceEntry buildResourceEntry(String id, DataEntry data) {
622: if ((id.length() > 1) && (id.charAt(0) == '#')) {
623: int intid = Integer.parseInt(id.substring(1));
624: return new ResourceEntry(intid, data);
625: }
626:
627: return new ResourceEntry(id, data);
628: }
629:
630: public ResourceEntry buildResourceEntry(String id,
631: ImageResourceDirectory dir) {
632: if ((id.length() > 1) && (id.charAt(0) == '#')) {
633: int intid = Integer.parseInt(id.substring(1));
634: return new ResourceEntry(intid, dir);
635: }
636:
637: return new ResourceEntry(id, dir);
638: }
639:
640: }
|