ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

winexe_pe.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/array.h"
00024 #include "common/debug.h"
00025 #include "common/endian.h"
00026 #include "common/file.h"
00027 #include "common/str.h"
00028 #include "common/stream.h"
00029 #include "common/winexe_pe.h"
00030 
00031 namespace Common {
00032 
00033 PEResources::PEResources() {
00034     _exe = nullptr;
00035 }
00036 
00037 PEResources::~PEResources() {
00038     clear();
00039 }
00040 
00041 void PEResources::clear() {
00042     _sections.clear();
00043     _resources.clear();
00044     delete _exe; _exe = nullptr;
00045 }
00046 
00047 bool PEResources::loadFromEXE(const String &fileName) {
00048     if (fileName.empty())
00049         return false;
00050 
00051     File *file = new File();
00052 
00053     if (!file->open(fileName)) {
00054         delete file;
00055         return false;
00056     }
00057 
00058     return loadFromEXE(file);
00059 }
00060 
00061 bool PEResources::loadFromEXE(SeekableReadStream *stream) {
00062     clear();
00063 
00064     if (!stream)
00065         return false;
00066 
00067     if (stream->readUint16BE() != MKTAG16('M', 'Z'))
00068         return false;
00069 
00070     stream->skip(58);
00071 
00072     uint32 peOffset = stream->readUint32LE();
00073 
00074     if (!peOffset || peOffset >= (uint32)stream->size())
00075         return false;
00076 
00077     stream->seek(peOffset);
00078 
00079     if (stream->readUint32BE() != MKTAG('P','E',0,0))
00080         return false;
00081 
00082     stream->skip(2);
00083     uint16 sectionCount = stream->readUint16LE();
00084     stream->skip(12);
00085     uint16 optionalHeaderSize = stream->readUint16LE();
00086     stream->skip(optionalHeaderSize + 2);
00087 
00088     // Read in all the sections
00089     for (uint16 i = 0; i < sectionCount; i++) {
00090         char sectionName[9];
00091         stream->read(sectionName, 8);
00092         sectionName[8] = 0;
00093 
00094         Section section;
00095         stream->skip(4);
00096         section.virtualAddress = stream->readUint32LE();
00097         section.size = stream->readUint32LE();
00098         section.offset = stream->readUint32LE();
00099         stream->skip(16);
00100 
00101         _sections[sectionName] = section;
00102     }
00103 
00104     // Currently, we require loading a resource section
00105     if (!_sections.contains(".rsrc")) {
00106         clear();
00107         return false;
00108     }
00109 
00110     _exe = stream;
00111 
00112     Section &resSection = _sections[".rsrc"];
00113     parseResourceLevel(resSection, resSection.offset, 0);
00114 
00115     return true;
00116 }
00117 
00118 void PEResources::parseResourceLevel(Section &section, uint32 offset, int level) {
00119     _exe->seek(offset + 12);
00120 
00121     uint16 namedEntryCount = _exe->readUint16LE();
00122     uint16 intEntryCount = _exe->readUint16LE();
00123 
00124     for (uint32 i = 0; i < (uint32)(namedEntryCount + intEntryCount); i++) {
00125         uint32 value = _exe->readUint32LE();
00126 
00127         WinResourceID id;
00128 
00129         if (value & 0x80000000) {
00130             value &= 0x7fffffff;
00131 
00132             uint32 startPos = _exe->pos();
00133             _exe->seek(section.offset + (value & 0x7fffffff));
00134 
00135             // Read in the name, truncating from unicode to ascii
00136             String name;
00137             uint16 nameLength = _exe->readUint16LE();
00138             while (nameLength--)
00139                 name += (char)(_exe->readUint16LE() & 0xff);
00140 
00141             _exe->seek(startPos);
00142 
00143             id = name;
00144         } else {
00145             id = value;
00146         }
00147 
00148         uint32 nextOffset = _exe->readUint32LE();
00149         uint32 lastOffset = _exe->pos();
00150 
00151         if (level == 0)
00152             _curType = id;
00153         else if (level == 1)
00154             _curName = id;
00155         else if (level == 2)
00156             _curLang = id;
00157 
00158         if (level < 2) {
00159             // Time to dive down further
00160             parseResourceLevel(section, section.offset + (nextOffset & 0x7fffffff), level + 1);
00161         } else {
00162             _exe->seek(section.offset + nextOffset);
00163 
00164             Resource resource;
00165             resource.offset = _exe->readUint32LE() + section.offset - section.virtualAddress;
00166             resource.size = _exe->readUint32LE();
00167 
00168             debug(4, "Found resource '%s' '%s' '%s' at %d of size %d", _curType.toString().c_str(),
00169                     _curName.toString().c_str(), _curLang.toString().c_str(), resource.offset, resource.size);
00170 
00171             _resources[_curType][_curName][_curLang] = resource;
00172         }
00173 
00174         _exe->seek(lastOffset);
00175     }
00176 }
00177 
00178 const Array<WinResourceID> PEResources::getTypeList() const {
00179     Array<WinResourceID> array;
00180 
00181     if (!_exe)
00182         return array;
00183 
00184     for (TypeMap::const_iterator it = _resources.begin(); it != _resources.end(); it++)
00185         array.push_back(it->_key);
00186 
00187     return array;
00188 }
00189 
00190 const Array<WinResourceID> PEResources::getNameList(const WinResourceID &type) const {
00191     Array<WinResourceID> array;
00192 
00193     if (!_exe || !_resources.contains(type))
00194         return array;
00195 
00196     const NameMap &nameMap = _resources[type];
00197 
00198     for (NameMap::const_iterator it = nameMap.begin(); it != nameMap.end(); it++)
00199         array.push_back(it->_key);
00200 
00201     return array;
00202 }
00203 
00204 const Array<WinResourceID> PEResources::getLangList(const WinResourceID &type, const WinResourceID &name) const {
00205     Array<WinResourceID> array;
00206 
00207     if (!_exe || !_resources.contains(type))
00208         return array;
00209 
00210     const NameMap &nameMap = _resources[type];
00211 
00212     if (!nameMap.contains(name))
00213         return array;
00214 
00215     const LangMap &langMap = nameMap[name];
00216 
00217     for (LangMap::const_iterator it = langMap.begin(); it != langMap.end(); it++)
00218         array.push_back(it->_key);
00219 
00220     return array;
00221 }
00222 
00223 SeekableReadStream *PEResources::getResource(const WinResourceID &type, const WinResourceID &name) {
00224     Array<WinResourceID> langList = getLangList(type, name);
00225 
00226     if (langList.empty())
00227         return nullptr;
00228 
00229     const Resource &resource = _resources[type][name][langList[0]];
00230     _exe->seek(resource.offset);
00231     return _exe->readStream(resource.size);
00232 }
00233 
00234 SeekableReadStream *PEResources::getResource(const WinResourceID &type, const WinResourceID &name, const WinResourceID &lang) {
00235     if (!_exe || !_resources.contains(type))
00236         return nullptr;
00237 
00238     const NameMap &nameMap = _resources[type];
00239 
00240     if (!nameMap.contains(name))
00241         return nullptr;
00242 
00243     const LangMap &langMap = nameMap[name];
00244 
00245     if (!langMap.contains(lang))
00246         return nullptr;
00247 
00248     const Resource &resource = langMap[lang];
00249     _exe->seek(resource.offset);
00250     return _exe->readStream(resource.size);
00251 }
00252 
00253 } // End of namespace Common


Generated on Sat Apr 20 2019 05:04:11 for ResidualVM by doxygen 1.7.1
curved edge   curved edge