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

winexe_ne.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/debug.h"
00024 #include "common/file.h"
00025 #include "common/memstream.h"
00026 #include "common/str.h"
00027 #include "common/stream.h"
00028 #include "common/winexe_ne.h"
00029 
00030 namespace Common {
00031 
00032 NEResources::NEResources() {
00033     _exe = nullptr;
00034 }
00035 
00036 NEResources::~NEResources() {
00037     clear();
00038 }
00039 
00040 void NEResources::clear() {
00041     if (_exe) {
00042         delete _exe;
00043         _exe = nullptr;
00044     }
00045 
00046     _resources.clear();
00047 }
00048 
00049 bool NEResources::loadFromEXE(const String &fileName) {
00050     if (fileName.empty())
00051         return false;
00052 
00053     File *file = new File();
00054 
00055     if (!file->open(fileName)) {
00056         delete file;
00057         return false;
00058     }
00059 
00060     return loadFromEXE(file);
00061 }
00062 
00063 bool NEResources::loadFromEXE(SeekableReadStream *stream) {
00064     clear();
00065 
00066     if (!stream)
00067         return false;
00068 
00069     _exe = stream;
00070 
00071     uint32 offsetResourceTable = getResourceTableOffset();
00072     if (offsetResourceTable == 0xFFFFFFFF)
00073         return false;
00074     if (offsetResourceTable == 0)
00075         return true;
00076 
00077     if (!readResourceTable(offsetResourceTable))
00078         return false;
00079 
00080     return true;
00081 }
00082 
00083 bool NEResources::loadFromCompressedEXE(const String &fileName) {
00084     // Based on http://www.cabextract.org.uk/libmspack/doc/szdd_kwaj_format.html
00085 
00086     // TODO: Merge this with with loadFromEXE() so the handling of the compressed
00087     // EXE's is transparent
00088 
00089     File file;
00090 
00091     if (!file.open(fileName))
00092         return false;
00093 
00094     // First part of the signature
00095     if (file.readUint32BE() != MKTAG('S','Z','D','D'))
00096         return false;
00097 
00098     // Second part of the signature
00099     if (file.readUint32BE() != 0x88F02733)
00100         return false;
00101 
00102     // Compression mode must be 'A'
00103     if (file.readByte() != 'A')
00104         return false;
00105 
00106     file.readByte(); // file name character change
00107     uint32 unpackedLength = file.readUint32LE();
00108 
00109     byte *window = new byte[0x1000];
00110     int pos = 0x1000 - 16;
00111     memset(window, 0x20, 0x1000); // Initialize to all spaces
00112 
00113     byte *unpackedData = (byte *)malloc(unpackedLength);
00114     assert(unpackedData);
00115     byte *dataPos = unpackedData;
00116 
00117     // Apply simple LZSS decompression
00118     for (;;) {
00119         byte controlByte = file.readByte();
00120 
00121         if (file.eos())
00122             break;
00123 
00124         for (byte i = 0; i < 8; i++) {
00125             if (controlByte & (1 << i)) {
00126                 *dataPos++ = window[pos++] = file.readByte();
00127                 pos &= 0xFFF;
00128             } else {
00129                 int matchPos = file.readByte();
00130                 int matchLen = file.readByte();
00131                 matchPos |= (matchLen & 0xF0) << 4;
00132                 matchLen = (matchLen & 0xF) + 3;
00133                 while (matchLen--) {
00134                     *dataPos++ = window[pos++] = window[matchPos++];
00135                     pos &= 0xFFF;
00136                     matchPos &= 0xFFF;
00137                 }
00138             }
00139 
00140         }
00141     }
00142 
00143     delete[] window;
00144     SeekableReadStream *stream = new MemoryReadStream(unpackedData, unpackedLength);
00145 
00146     return loadFromEXE(stream);
00147 }
00148 
00149 uint32 NEResources::getResourceTableOffset() {
00150     if (!_exe)
00151         return 0xFFFFFFFF;
00152 
00153     if (!_exe->seek(0))
00154         return 0xFFFFFFFF;
00155 
00156     //                          'MZ'
00157     if (_exe->readUint16BE() != 0x4D5A)
00158         return 0xFFFFFFFF;
00159 
00160     if (!_exe->seek(60))
00161         return 0xFFFFFFFF;
00162 
00163     uint32 offsetSegmentEXE = _exe->readUint16LE();
00164     if (!_exe->seek(offsetSegmentEXE))
00165         return 0xFFFFFFFF;
00166 
00167     //                          'NE'
00168     if (_exe->readUint16BE() != 0x4E45)
00169         return 0xFFFFFFFF;
00170 
00171     if (!_exe->seek(offsetSegmentEXE + 36))
00172         return 0xFFFFFFFF;
00173 
00174     uint32 offsetResourceTable = _exe->readUint16LE();
00175     if (offsetResourceTable == 0)
00176         // No resource table
00177         return 0;
00178 
00179     // Offset relative to the segment _exe header
00180     offsetResourceTable += offsetSegmentEXE;
00181     if (!_exe->seek(offsetResourceTable))
00182         return 0xFFFFFFFF;
00183 
00184     return offsetResourceTable;
00185 }
00186 
00187 static const char *s_resTypeNames[] = {
00188     "", "cursor", "bitmap", "icon", "menu", "dialog", "string",
00189     "font_dir", "font", "accelerator", "rc_data", "msg_table",
00190     "group_cursor", "", "group_icon", "", "version", "dlg_include",
00191     "", "plug_play", "vxd", "ani_cursor", "ani_icon", "html",
00192     "manifest"
00193 };
00194 
00195 bool NEResources::readResourceTable(uint32 offset) {
00196     if (!_exe)
00197         return false;
00198 
00199     if (!_exe->seek(offset))
00200         return false;
00201 
00202     uint32 align = 1 << _exe->readUint16LE();
00203     uint16 typeID = _exe->readUint16LE();
00204 
00205     while (typeID != 0) {
00206         // High bit of the type means integer type
00207         WinResourceID type;
00208         if (typeID & 0x8000)
00209             type = typeID & 0x7FFF;
00210         else
00211             type = getResourceString(*_exe, offset + typeID);
00212 
00213         uint16 resCount = _exe->readUint16LE();
00214 
00215         _exe->skip(4); // reserved
00216 
00217         for (int i = 0; i < resCount; i++) {
00218             Resource res;
00219 
00220             // Resource properties
00221             res.offset = _exe->readUint16LE() * align;
00222             res.size   = _exe->readUint16LE() * align;
00223             res.flags  = _exe->readUint16LE();
00224             uint16 id  = _exe->readUint16LE();
00225             res.handle = _exe->readUint16LE();
00226             res.usage  = _exe->readUint16LE();
00227 
00228             res.type = type;
00229 
00230             // High bit means integer type
00231             if (id & 0x8000)
00232                 res.id = id & 0x7FFF;
00233             else
00234                 res.id = getResourceString(*_exe, offset + id);
00235 
00236             if (typeID & 0x8000 && ((typeID & 0x7FFF) < ARRAYSIZE(s_resTypeNames)) && s_resTypeNames[typeID & 0x7FFF][0] != 0)
00237                 debug(2, "Found resource %s %s", s_resTypeNames[typeID & 0x7FFF], res.id.toString().c_str());
00238             else
00239                 debug(2, "Found resource %s %s", type.toString().c_str(), res.id.toString().c_str());
00240 
00241             _resources.push_back(res);
00242         }
00243 
00244         typeID = _exe->readUint16LE();
00245     }
00246 
00247     return true;
00248 }
00249 
00250 String NEResources::getResourceString(SeekableReadStream &exe, uint32 offset) {
00251     uint32 curPos = exe.pos();
00252 
00253     if (!exe.seek(offset)) {
00254         exe.seek(curPos);
00255         return "";
00256     }
00257 
00258     uint8 length = exe.readByte();
00259 
00260     String string;
00261     for (uint16 i = 0; i < length; i++)
00262         string += (char)exe.readByte();
00263 
00264     exe.seek(curPos);
00265     return string;
00266 }
00267 
00268 const NEResources::Resource *NEResources::findResource(const WinResourceID &type, const WinResourceID &id) const {
00269     for (List<Resource>::const_iterator it = _resources.begin(); it != _resources.end(); ++it)
00270         if (it->type == type && it->id == id)
00271             return &*it;
00272 
00273     return nullptr;
00274 }
00275 
00276 SeekableReadStream *NEResources::getResource(const WinResourceID &type, const WinResourceID &id) {
00277     const Resource *res = findResource(type, id);
00278 
00279     if (!res)
00280         return nullptr;
00281 
00282     _exe->seek(res->offset);
00283     return _exe->readStream(res->size);
00284 }
00285 
00286 const Array<WinResourceID> NEResources::getIDList(const WinResourceID &type) const {
00287     Array<WinResourceID> idArray;
00288 
00289     for (List<Resource>::const_iterator it = _resources.begin(); it != _resources.end(); ++it)
00290         if (it->type == type)
00291             idArray.push_back(it->id);
00292 
00293     return idArray;
00294 }
00295 
00296 } // End of namespace Common


Generated on Sat Feb 16 2019 05:01:12 for ResidualVM by doxygen 1.7.1
curved edge   curved edge