1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/file.h"
24 #include "common/memstream.h"
25 #include "common/str.h"
26 #include "common/ustr.h"
27 #include "common/winexe.h"
28 #include "common/winexe_ne.h"
29 #include "common/winexe_pe.h"
30
31 namespace Common {
32
operator =(const String & x)33 WinResourceID &WinResourceID::operator=(const String &x) {
34 _name = x;
35 _idType = kIDTypeString;
36 return *this;
37 }
38
operator =(uint32 x)39 WinResourceID &WinResourceID::operator=(uint32 x) {
40 _id = x;
41 _idType = kIDTypeNumerical;
42 return *this;
43 }
44
operator ==(const String & x) const45 bool WinResourceID::operator==(const String &x) const {
46 return _idType == kIDTypeString && _name.equalsIgnoreCase(x);
47 }
48
operator ==(const uint32 & x) const49 bool WinResourceID::operator==(const uint32 &x) const {
50 return _idType == kIDTypeNumerical && _id == x;
51 }
52
operator ==(const WinResourceID & x) const53 bool WinResourceID::operator==(const WinResourceID &x) const {
54 if (_idType != x._idType)
55 return false;
56 if (_idType == kIDTypeString)
57 return _name.equalsIgnoreCase(x._name);
58 if (_idType == kIDTypeNumerical)
59 return _id == x._id;
60 return true;
61 }
62
getString() const63 String WinResourceID::getString() const {
64 if (_idType != kIDTypeString)
65 return "";
66
67 return _name;
68 }
69
getID() const70 uint32 WinResourceID::getID() const {
71 if (_idType != kIDTypeNumerical)
72 return 0xffffffff;
73
74 return _id;
75 }
76
toString() const77 String WinResourceID::toString() const {
78 if (_idType == kIDTypeString)
79 return _name;
80 else if (_idType == kIDTypeNumerical)
81 return String::format("0x%08x", _id);
82
83 return "";
84 }
85
loadFromEXE(const String & fileName)86 bool WinResources::loadFromEXE(const String &fileName) {
87 if (fileName.empty())
88 return false;
89
90 File *file = new File();
91
92 if (!file->open(fileName)) {
93 delete file;
94 return false;
95 }
96
97 return loadFromEXE(file);
98 }
99
loadFromCompressedEXE(const String & fileName)100 bool WinResources::loadFromCompressedEXE(const String &fileName) {
101 // Based on http://www.cabextract.org.uk/libmspack/doc/szdd_kwaj_format.html
102
103 // TODO: Merge this with with loadFromEXE() so the handling of the compressed
104 // EXE's is transparent
105
106 File file;
107
108 if (!file.open(fileName))
109 return false;
110
111 // First part of the signature
112 if (file.readUint32BE() != MKTAG('S','Z','D','D'))
113 return false;
114
115 // Second part of the signature
116 if (file.readUint32BE() != 0x88F02733)
117 return false;
118
119 // Compression mode must be 'A'
120 if (file.readByte() != 'A')
121 return false;
122
123 file.readByte(); // file name character change
124 uint32 unpackedLength = file.readUint32LE();
125
126 byte *window = new byte[0x1000];
127 int pos = 0x1000 - 16;
128 memset(window, 0x20, 0x1000); // Initialize to all spaces
129
130 byte *unpackedData = (byte *)malloc(unpackedLength);
131 assert(unpackedData);
132 byte *dataPos = unpackedData;
133
134 uint32 remaining = unpackedLength;
135
136 // Apply simple LZSS decompression
137 for (;;) {
138 byte controlByte = file.readByte();
139
140 if (remaining == 0 || file.eos())
141 break;
142
143 for (byte i = 0; i < 8; i++) {
144 if (controlByte & (1 << i)) {
145 *dataPos++ = window[pos++] = file.readByte();
146 pos &= 0xFFF;
147 if (--remaining == 0)
148 break;
149 } else {
150 int matchPos = file.readByte();
151 int matchLen = file.readByte();
152 matchPos |= (matchLen & 0xF0) << 4;
153 matchLen = (matchLen & 0xF) + 3;
154 if ((uint32)matchLen > remaining)
155 matchLen = remaining;
156 remaining -= matchLen;
157
158 while (matchLen--) {
159 *dataPos++ = window[pos++] = window[matchPos++];
160 pos &= 0xFFF;
161 matchPos &= 0xFFF;
162 }
163
164 if (remaining == 0)
165 break;
166 }
167 }
168 }
169
170 delete[] window;
171 SeekableReadStream *stream = new MemoryReadStream(unpackedData, unpackedLength);
172
173 return loadFromEXE(stream);
174 }
175
176
createFromEXE(const String & fileName)177 WinResources *WinResources::createFromEXE(const String &fileName) {
178 WinResources *exe;
179
180 // First try loading via the NE code
181 exe = new Common::NEResources();
182 if (exe->loadFromEXE(fileName)) {
183 return exe;
184 }
185 delete exe;
186
187 // Then try loading via the PE code
188 exe = new Common::PEResources();
189 if (exe->loadFromEXE(fileName)) {
190 return exe;
191 }
192 delete exe;
193
194 return nullptr;
195 }
196
createFromEXE(SeekableReadStream * stream)197 WinResources *WinResources::createFromEXE(SeekableReadStream *stream) {
198 WinResources *exe;
199
200 // First try loading via the NE code
201 stream->seek(0);
202 exe = new Common::NEResources();
203 if (exe->loadFromEXE(stream, DisposeAfterUse::NO)) {
204 return exe;
205 }
206 delete exe;
207
208 // Then try loading via the PE code
209 stream->seek(0);
210 exe = new Common::PEResources();
211 if (exe->loadFromEXE(stream, DisposeAfterUse::NO)) {
212 return exe;
213 }
214 delete exe;
215
216 return nullptr;
217 }
218
getVersionResource(const WinResourceID & id)219 WinResources::VersionInfo *WinResources::getVersionResource(const WinResourceID &id) {
220 VersionInfo *info = nullptr;
221
222 SeekableReadStream *res = getResource(kWinVersion, id);
223 if (res) {
224 info = parseVersionInfo(res);
225 delete res;
226 }
227
228 return info;
229 }
230
VersionInfo()231 WinResources::VersionInfo::VersionInfo() {
232 fileVersion[0] = fileVersion[1] = fileVersion[2] = fileVersion[3] = 0;
233 productVersion[0] = productVersion[1] = productVersion[2] = productVersion[3] = 0;
234 fileFlagsMask = 0;
235 fileFlags = 0;
236 fileOS = 0;
237 fileType = 0;
238 fileSubtype = 0;
239 fileDate[0] = fileDate[1] = 0;
240 }
241
242
readVSVersionInfo(SeekableReadStream * res)243 bool WinResources::VersionInfo::readVSVersionInfo(SeekableReadStream *res) {
244 // Signature check
245 if (res->readUint32LE() != 0xFEEF04BD)
246 return false;
247
248 res->readUint32LE(); // struct version
249
250 // The versions are stored a bit weird
251 fileVersion[1] = res->readUint16LE();
252 fileVersion[0] = res->readUint16LE();
253 fileVersion[3] = res->readUint16LE();
254 fileVersion[2] = res->readUint16LE();
255 productVersion[1] = res->readUint16LE();
256 productVersion[0] = res->readUint16LE();
257 productVersion[3] = res->readUint16LE();
258 productVersion[2] = res->readUint16LE();
259
260 fileFlagsMask = res->readUint32LE();
261 fileFlags = res->readUint32LE();
262 fileOS = res->readUint32LE();
263 fileType = res->readUint32LE();
264 fileSubtype = res->readUint32LE();
265 fileDate[0] = res->readUint32LE();
266 fileDate[1] = res->readUint32LE();
267
268 hash.setVal("File:", Common::U32String::format("%d.%d.%d.%d", fileVersion[0], fileVersion[1], fileVersion[2], fileVersion[3]));
269 hash.setVal("Prod:", Common::U32String::format("%d.%d.%d.%d", productVersion[0], productVersion[1], productVersion[2], productVersion[3]));
270
271 return true;
272 }
273
274 } // End of namespace Common
275