COMMON: Add basic PE EXE parser

Much thanks to fuzzie for her assistance
This commit is contained in:
Matthew Hoops 2011-02-22 01:47:11 -05:00
parent 0ec91de76d
commit 91287b2e18
3 changed files with 431 additions and 0 deletions

View file

@ -18,6 +18,7 @@ MODULE_OBJS := \
md5.o \
mutex.o \
ne_exe.o \
pe_exe.o \
random.o \
rational.o \
str.o \

297
common/pe_exe.cpp Normal file
View file

@ -0,0 +1,297 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/debug.h"
#include "common/file.h"
#include "common/memstream.h"
#include "common/pe_exe.h"
#include "common/str.h"
#include "common/stream.h"
namespace Common {
PEResourceID &PEResourceID::operator=(String string) {
_name = string;
_idType = kIDTypeString;
return *this;
}
PEResourceID &PEResourceID::operator=(uint32 x) {
_id = x;
_idType = kIDTypeNumerical;
return *this;
}
bool PEResourceID::operator==(const String &x) const {
return _idType == kIDTypeString && _name.equalsIgnoreCase(x);
}
bool PEResourceID::operator==(const uint32 &x) const {
return _idType == kIDTypeNumerical && _id == x;
}
bool PEResourceID::operator==(const PEResourceID &x) const {
if (_idType != x._idType)
return false;
if (_idType == kIDTypeString)
return _name.equalsIgnoreCase(x._name);
if (_idType == kIDTypeNumerical)
return _id == x._id;
return true;
}
String PEResourceID::getString() const {
if (_idType != kIDTypeString)
return "";
return _name;
}
uint32 PEResourceID::getID() const {
if (_idType != kIDTypeNumerical)
return 0xffffffff;
return _idType;
}
String PEResourceID::toString() const {
if (_idType == kIDTypeString)
return _name;
else if (_idType == kIDTypeNumerical)
return String::format("%08x", _id);
return "";
}
PEResources::PEResources() {
_exe = 0;
}
PEResources::~PEResources() {
clear();
}
void PEResources::clear() {
_sections.clear();
_resources.clear();
delete _exe; _exe = 0;
}
bool PEResources::loadFromEXE(const String &fileName) {
if (fileName.empty())
return false;
File *file = new File();
if (!file->open(fileName)) {
delete file;
return false;
}
return loadFromEXE(file);
}
bool PEResources::loadFromEXE(SeekableReadStream *stream) {
clear();
if (!stream)
return false;
if (stream->readUint16BE() != 'MZ')
return false;
stream->skip(58);
uint32 peOffset = stream->readUint32LE();
if (!peOffset || peOffset >= (uint32)stream->size())
return false;
stream->seek(peOffset);
if (stream->readUint32BE() != MKID_BE('PE\0\0'))
return false;
stream->skip(2);
uint16 sectionCount = stream->readUint16LE();
stream->skip(12);
uint16 optionalHeaderSize = stream->readUint16LE();
stream->skip(optionalHeaderSize + 2);
// Read in all the sections
for (uint16 i = 0; i < sectionCount; i++) {
char sectionName[9];
stream->read(sectionName, 8);
sectionName[8] = 0;
Section section;
stream->skip(4);
section.virtualAddress = stream->readUint32LE();
section.size = stream->readUint32LE();
section.offset = stream->readUint32LE();
stream->skip(16);
_sections[sectionName] = section;
}
// Currently, we require loading a resource section
if (!_sections.contains(".rsrc")) {
clear();
return false;
}
_exe = stream;
Section &resSection = _sections[".rsrc"];
parseResourceLevel(resSection, resSection.offset, 0);
return true;
}
void PEResources::parseResourceLevel(Section &section, uint32 offset, int level) {
_exe->seek(offset + 12);
uint16 namedEntryCount = _exe->readUint16LE();
uint16 intEntryCount = _exe->readUint16LE();
for (uint32 i = 0; i < namedEntryCount + intEntryCount; i++) {
uint32 value = _exe->readUint32LE();
PEResourceID id;
if (value & 0x80000000) {
value &= 0x7fffffff;
uint32 startPos = _exe->pos();
_exe->seek(section.offset + (value & 0x7fffffff));
// Read in the name, truncating from unicode to ascii
Common::String name;
uint16 nameLength = _exe->readUint16LE();
while (nameLength--)
name += (char)(_exe->readUint16LE() & 0xff);
_exe->seek(startPos);
id = name;
} else {
id = value;
}
uint32 nextOffset = _exe->readUint32LE();
uint32 lastOffset = _exe->pos();
if (level == 0)
_curType = id;
else if (level == 1)
_curName = id;
else if (level == 2)
_curLang = id;
if (level < 2) {
// Time to dive down further
parseResourceLevel(section, section.offset + (nextOffset & 0x7fffffff), level + 1);
} else {
_exe->seek(section.offset + nextOffset);
Resource resource;
resource.offset = _exe->readUint32LE() + section.offset - section.virtualAddress;
resource.size = _exe->readUint32LE();
debug(4, "Found resource '%s' '%s' '%s' at %d of size %d", _curType.toString().c_str(),
_curName.toString().c_str(), _curLang.toString().c_str(), resource.offset, resource.size);
_resources[_curType][_curName][_curLang] = resource;
}
_exe->seek(lastOffset);
}
}
const Array<PEResourceID> PEResources::getTypeList() const {
Array<PEResourceID> array;
if (!_exe)
return array;
for (TypeMap::const_iterator it = _resources.begin(); it != _resources.end(); it++)
array.push_back(it->_key);
return array;
}
const Array<PEResourceID> PEResources::getNameList(const PEResourceID &type) const {
Array<PEResourceID> array;
if (!_exe || !_resources.contains(type))
return array;
const NameMap &nameMap = _resources[type];
for (NameMap::const_iterator it = nameMap.begin(); it != nameMap.end(); it++)
array.push_back(it->_key);
return array;
}
const Array<PEResourceID> PEResources::getLangList(const PEResourceID &type, const PEResourceID &name) const {
Array<PEResourceID> array;
if (!_exe || !_resources.contains(type))
return array;
const NameMap &nameMap = _resources[type];
if (!nameMap.contains(name))
return array;
const LangMap &langMap = nameMap[name];
for (LangMap::const_iterator it = langMap.begin(); it != langMap.end(); it++)
array.push_back(it->_key);
return array;
}
SeekableReadStream *PEResources::getResource(const PEResourceID &type, const PEResourceID &name, const PEResourceID &lang) {
if (!_exe || !_resources.contains(type))
return 0;
const NameMap &nameMap = _resources[type];
if (!nameMap.contains(name))
return 0;
const LangMap &langMap = nameMap[name];
if (!langMap.contains(lang))
return 0;
const Resource &resource = langMap[lang];
_exe->seek(resource.offset);
return _exe->readStream(resource.size);
}
} // End of namespace Common

133
common/pe_exe.h Normal file
View file

@ -0,0 +1,133 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef COMMON_PE_EXE_H
#define COMMON_PE_EXE_H
#include "common/array.h"
#include "common/hashmap.h"
#include "common/hash-str.h"
namespace Common {
class SeekableReadStream;
class String;
class PEResourceID {
public:
PEResourceID() { _idType = kIDTypeNull; }
PEResourceID(String x) { _idType = kIDTypeString; _name = x; }
PEResourceID(uint32 x) { _idType = kIDTypeNumerical; _id = x; }
PEResourceID &operator=(String string);
PEResourceID &operator=(uint32 x);
bool operator==(const String &x) const;
bool operator==(const uint32 &x) const;
bool operator==(const PEResourceID &x) const;
String getString() const;
uint32 getID() const;
String toString() const;
private:
/** An ID Type. */
enum IDType {
kIDTypeNull, ///< No type set
kIDTypeNumerical, ///< A numerical ID.
kIDTypeString ///< A string ID.
} _idType;
String _name; ///< The resource's string ID.
uint32 _id; ///< The resource's numerical ID.
};
struct PEResourceID_Hash {
uint operator()(const PEResourceID &id) const { return hashit(id.toString()); }
};
struct PEResourceID_EqualTo {
bool operator()(const PEResourceID &id1, const PEResourceID &id2) const { return id1 == id2; }
};
/**
* A class able to load resources from a Windows Portable Executable, such
* as cursors, bitmaps, and sounds.
*/
class PEResources {
public:
PEResources();
~PEResources();
/** Clear all information. */
void clear();
/** Load from an EXE file. */
bool loadFromEXE(const String &fileName);
/** Load from a stream. */
bool loadFromEXE(SeekableReadStream *stream);
/** Return a list of resource types. */
const Array<PEResourceID> getTypeList() const;
/** Return a list of names for a given type. */
const Array<PEResourceID> getNameList(const PEResourceID &type) const;
/** Return a list of languages for a given type and name. */
const Array<PEResourceID> getLangList(const PEResourceID &type, const PEResourceID &name) const;
/** Return a stream to the specified resource (or 0 if non-existent). */
SeekableReadStream *getResource(const PEResourceID &type, const PEResourceID &name, const PEResourceID &lang);
private:
struct Section {
uint32 virtualAddress;
uint32 size;
uint32 offset;
};
HashMap<String, Section, IgnoreCase_Hash, IgnoreCase_EqualTo> _sections;
SeekableReadStream *_exe;
void parseResourceLevel(Section &section, uint32 offset, int level);
PEResourceID _curType, _curName, _curLang;
struct Resource {
uint32 offset;
uint32 size;
};
typedef HashMap<PEResourceID, Resource, PEResourceID_Hash, PEResourceID_EqualTo> LangMap;
typedef HashMap<PEResourceID, LangMap, PEResourceID_Hash, PEResourceID_EqualTo> NameMap;
typedef HashMap<PEResourceID, NameMap, PEResourceID_Hash, PEResourceID_EqualTo> TypeMap;
TypeMap _resources;
};
} // End of namespace Common
#endif