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