xoreos  0.0.5
version.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos 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 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos 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 xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
25 #include <algorithm>
26 
27 #include "src/common/scopedptr.h"
28 #include "src/common/ustring.h"
29 #include "src/common/readfile.h"
30 #include "src/common/filelist.h"
31 #include "src/common/filepath.h"
32 
33 #include "src/aurora/util.h"
34 
36 
37 
38 #define MAKE_NWN_VERSION(MAJOR, MINOR, BUILD) ((((uint64)(MAJOR)) << 32) + \
39  (((uint64)(MINOR)) << 16) + \
40  (((uint64)(BUILD))))
41 
42 static const byte kVersionWin[23] = {
43  '\0','F','\0','i','\0','l','\0','e','\0','V','\0','e','\0','r','\0','s','\0','i','\0','o','\0','n','\0'
44 };
45 
46 static const byte kBuildWin[25] = {
47  '\0','P','\0','r','\0','i','\0','v','\0','a','\0','t','\0','e','\0','B','\0','u','\0','i','\0','l','\0','d','\0'
48 };
49 
50 static const byte kVersionUnix[20] = {
51  '\0','N','e','v','e','r','w','i','n','t','e','r',' ','N','i','g','h','t','s','\0'
52 };
53 
54 namespace Engines {
55 
56 namespace NWN {
57 
58 Version::Version(Aurora::Platform platform) : _platform(platform),
59  _versionMajor(0), _versionMinor(0), _versionBuild(0), _version(0) {
60 
61 }
62 
64 }
65 
66 bool Version::hasVersion() const {
67  return _version != 0;
68 }
69 
71  return _platform;
72 }
73 
76 }
77 
79  return _version;
80 }
81 
83  return _versionMajor;
84 }
85 
87  return _versionMinor;
88 }
89 
91  return _versionBuild;
92 }
93 
96 }
97 
100 }
101 
103  return kOptimumVersionMajor;
104 }
105 
107  return kOptimumVersionMinor;
108 }
109 
111  return kOptimumVersionBuild;
112 }
113 
115  return Common::UString::format("%d.%d.%d",
117 }
118 
119 bool Version::isTooOld() const {
120  return _version < getOptimumVersion();
121 }
122 
123 bool Version::isTooNew() const {
124  return _version > getOptimumVersion();
125 }
126 
127 // Detect the version of the Neverwinter Nights installation by inspecting the binaries.
128 // Hopefully, the arrangements of the string "Neverwinter Nights", the version and the
129 // build number stay constant for all versions...
130 bool Version::detect(const Common::UString &directory) {
132 
133  bool success = false;
134 
136  success = detectWindows(directory);
138  success = detectMacOSX(directory);
139  else if (_platform == Aurora::kPlatformLinux)
140  success = detectLinux(directory);
141 
143 
144  return success;
145 }
146 
147 // In the Windows binary, read the FileVersion and PrivateBuild fields
148 // from the VERSIONINFO resource. They're UTF-16LE strings.
149 bool Version::detectWindows(const Common::UString &directory) {
150  size_t size;
151  Common::ScopedArray<byte> binary(readFile(directory, "/nwmain.exe", size));
152  if (!binary)
153  return false;
154 
155  // Search for the pattern where the version information in the binary starts
156  byte *version = std::search(binary.get(), binary.get() + size, kVersionWin, kVersionWin + sizeof(kVersionWin));
157  if ((size - (version - binary.get())) < (sizeof(kVersionWin) + 24))
158  return false;
159 
160  // Search for the pattern where the build information in the binary starts
161  byte *build = std::search(binary.get(), binary.get() + size, kBuildWin, kBuildWin + sizeof(kBuildWin));
162  if ((size - (build - binary.get())) < (sizeof(kBuildWin) + 10))
163  return false;
164 
165  bool success = false;
166 
167  // Check that the field separators are there and the upper bytes of UTF-16 character are 0
168  if ((version[sizeof(kVersionWin) + 0] == 0) &&
169  (version[sizeof(kVersionWin) + 1] == 0) &&
170  (version[sizeof(kVersionWin) + 2] == 0) &&
171  (version[sizeof(kVersionWin) + 3] == 0) &&
172  (version[sizeof(kVersionWin) + 5] == 0) &&
173  (version[sizeof(kVersionWin) + 7] == 0) &&
174  (version[sizeof(kVersionWin) + 9] == 0) &&
175  (version[sizeof(kVersionWin) + 11] == 0) &&
176  (version[sizeof(kVersionWin) + 13] == 0) &&
177  (version[sizeof(kVersionWin) + 17] == 0) &&
178  (version[sizeof(kVersionWin) + 21] == 0) &&
179  (version[sizeof(kVersionWin) + 23] == 0) &&
180  (build [sizeof(kBuildWin ) + 0] == 0) &&
181  (build [sizeof(kBuildWin ) + 1] == 0) &&
182  (build [sizeof(kBuildWin ) + 3] == 0) &&
183  (build [sizeof(kBuildWin ) + 5] == 0) &&
184  (build [sizeof(kBuildWin ) + 7] == 0) &&
185  (build [sizeof(kBuildWin ) + 9] == 0) &&
186  (version[sizeof(kVersionWin) + 6] == ',') &&
187  (version[sizeof(kVersionWin) + 12] == ',') &&
188  (version[sizeof(kVersionWin) + 18] == ',') &&
189  (version[sizeof(kVersionWin) + 8] == ' ') &&
190  (version[sizeof(kVersionWin) + 14] == ' ') &&
191  (version[sizeof(kVersionWin) + 20] == ' ')) {
192 
193  // Check that the version strings are numeric
194  if (isdigit(version[sizeof(kVersionWin) + 4]) &&
195  isdigit(version[sizeof(kVersionWin) + 10]) &&
196  isdigit(version[sizeof(kVersionWin) + 16]) &&
197  isdigit(build [sizeof(kBuildWin ) + 2]) &&
198  isdigit(build [sizeof(kBuildWin ) + 4]) &&
199  isdigit(build [sizeof(kBuildWin ) + 6]) &&
200  isdigit(build [sizeof(kBuildWin ) + 8])) {
201 
202  // Read the version information
203 
204  _versionMajor = version[sizeof(kVersionWin) + 4] - '0';
205 
206  _versionMinor = (version[sizeof(kVersionWin) + 10] - '0') * 10;
207  _versionMinor += version[sizeof(kVersionWin) + 16] - '0';
208 
209  _versionBuild = (build [sizeof(kBuildWin ) + 2] - '0') * 1000;
210  _versionBuild += (build [sizeof(kBuildWin ) + 4] - '0') * 100;
211  _versionBuild += (build [sizeof(kBuildWin ) + 6] - '0') * 10;
212  _versionBuild += build [sizeof(kBuildWin ) + 8] - '0';
213 
214  success = true;
215 
216  }
217  }
218 
219  return success;
220 }
221 
222 // In the Mac OS X binaries, the version number ("1.69") and the build number ("8109")
223 // precede the name "Neverwinter Nights", each field separated by 4 0-byte.
224 bool Version::detectMacOSX(const Common::UString &directory) {
225  Common::UString appDir =
226  Common::FilePath::findSubDirectory(directory, "Neverwinter Nights.app/Contents/MacOS", true);
227  if (appDir.empty())
228  return false;
229 
230  size_t size;
231  Common::ScopedArray<byte> binary(readFile(appDir, "Neverwinter Nights", size));
232  if (!binary)
233  return false;
234 
235  // Search for the pattern where the version information in the binary starts
236  byte *version = std::search(binary.get(), binary.get() + size, kVersionUnix, kVersionUnix + sizeof(kVersionUnix));
237  if ((version - binary.get()) < 15)
238  return false;
239 
240  bool success = false;
241 
242  // Check that the field separators are there
243  if ((version[- 1] == 0) &&
244  (version[- 2] == 0) &&
245  (version[- 3] == 0) &&
246  (version[- 8] == 0) &&
247  (version[- 9] == 0) &&
248  (version[-10] == 0) &&
249  (version[-11] == 0) &&
250  (version[- 6] == '.')) {
251 
252  // Check that the version strings are numeric
253  if (isdigit(version[- 4]) &&
254  isdigit(version[- 5]) &&
255  isdigit(version[- 7]) &&
256  isdigit(version[-12]) &&
257  isdigit(version[-13]) &&
258  isdigit(version[-14]) &&
259  isdigit(version[-15])) {
260 
261  // Read the version information
262 
263  _versionMajor = version[- 7] - '0';
264 
265  _versionMinor = (version[- 5] - '0') * 10;
266  _versionMinor += version[- 4] - '0';
267 
268  _versionBuild = (version[-15] - '0') * 1000;
269  _versionBuild += (version[-14] - '0') * 100;
270  _versionBuild += (version[-13] - '0') * 10;
271  _versionBuild += version[-12] - '0';
272 
273  success = true;
274 
275  }
276  }
277 
278  return success;
279 }
280 
281 // In the Linux binaries, the version number ("1.69") and the build number ("8109")
282 // follow the name "Neverwinter Nights", each field separated by a 0-byte.
283 bool Version::detectLinux(const Common::UString &directory) {
284  size_t size;
285  Common::ScopedArray<byte> binary(readFile(directory, "/nwmain", size));
286  if (!binary)
287  return false;
288 
289  // Search for the pattern where the version information in the binary starts
290  byte *version = std::search(binary.get(), binary.get() + size, kVersionUnix, kVersionUnix + sizeof(kVersionUnix));
291  if ((size - (version - binary.get())) < (sizeof(kVersionUnix) + 21))
292  return false;
293 
294  bool success = false;
295 
296  // Check that the field separators are there
297  if ((version[sizeof(kVersionUnix) + 4] == 0) &&
298  (version[sizeof(kVersionUnix) + 15] == 0) &&
299  (version[sizeof(kVersionUnix) + 20] == 0) &&
300  (version[sizeof(kVersionUnix) + 17] == '.')) {
301 
302  // Check that the version strings are numeric
303  if (isdigit(version[sizeof(kVersionUnix) + 0]) &&
304  isdigit(version[sizeof(kVersionUnix) + 1]) &&
305  isdigit(version[sizeof(kVersionUnix) + 2]) &&
306  isdigit(version[sizeof(kVersionUnix) + 3]) &&
307  isdigit(version[sizeof(kVersionUnix) + 16]) &&
308  isdigit(version[sizeof(kVersionUnix) + 18]) &&
309  isdigit(version[sizeof(kVersionUnix) + 19])) {
310 
311  // Read the version information
312 
313  _versionMajor = version[sizeof(kVersionUnix) + 16] - '0';
314 
315  _versionMinor = (version[sizeof(kVersionUnix) + 18] - '0') * 10;
316  _versionMinor += version[sizeof(kVersionUnix) + 19] - '0';
317 
318  _versionBuild = (version[sizeof(kVersionUnix) + 0] - '0') * 1000;
319  _versionBuild += (version[sizeof(kVersionUnix) + 1] - '0') * 100;
320  _versionBuild += (version[sizeof(kVersionUnix) + 2] - '0') * 10;
321  _versionBuild += version[sizeof(kVersionUnix) + 3] - '0';
322 
323  success = true;
324 
325  }
326  }
327 
328  return success;
329 }
330 
331 byte *Version::readFile(const Common::UString &directory, const Common::UString &file, size_t &size) {
332  Common::FileList files;
333 
334  if (!files.addDirectory(directory))
335  return 0;
336 
337  return readFile(files.findFirst(file, true), size);
338 }
339 
340 byte *Version::readFile(const Common::UString &path, size_t &size) {
341  if (path.empty())
342  return 0;
343 
344  Common::ReadFile file;
345  if (!file.open(path))
346  return 0;
347 
348  size = file.size();
349 
350  Common::ScopedArray<byte> buffer(new byte[size]);
351  if (file.read(buffer.get(), size) != size)
352  return 0;
353 
354  return buffer.release();
355 }
356 
357 } // End of namespace NWN
358 
359 } // End of namespace Engines
bool detectWindows(const Common::UString &directory)
Definition: version.cpp:149
#define MAKE_NWN_VERSION(MAJOR, MINOR, BUILD)
Definition: version.cpp:38
static const uint16 kOptimumVersionMinor
Definition: version.h:74
uint16 getVersionBuild() const
Return the build number.
Definition: version.cpp:90
bool isTooNew() const
Is this version newer than the optimum?
Definition: version.cpp:123
uint64 getVersion() const
Return the combined version.
Definition: version.cpp:78
A class holding an UTF-8 string.
Definition: ustring.h:48
bool isTooOld() const
Is this version older than the optimum?
Definition: version.cpp:119
PointerType release()
Returns the plain pointer value and releases ScopedPtr.
Definition: scopedptr.h:103
uint64_t uint64
Definition: types.h:206
A simple streaming file reading class.
Definition: readfile.h:40
Utility functions to handle files used in BioWare&#39;s Aurora engine.
uint16 getVersionMinor() const
Return the minor version.
Definition: version.cpp:86
Neverwinter Nights installation version detection.
bool detect(const Common::UString &directory)
Try to detect the version of the NWN installation in this directory.
Definition: version.cpp:130
uint16 _versionMajor
v1.69.8109 -> 1
Definition: version.h:79
static uint64 getOptimumVersion()
Return the optimum combined version.
Definition: version.cpp:98
A simple scoped smart pointer template.
UString findFirst(const UString &str, bool caseInsensitive) const
Find the first file ending with the given string.
Definition: filelist.cpp:193
static const uint16 kOptimumVersionMajor
Definition: version.h:73
uint16 _versionMinor
v1.69.8109 -> 69
Definition: version.h:80
static UString format(const char *s,...) GCC_PRINTF(1
Print formatted data into an UString object, similar to sprintf().
Definition: ustring.cpp:718
static const uint16 kOptimumVersionBuild
Definition: version.h:75
bool open(const UString &fileName)
Try to open the file with the given fileName.
Definition: readfile.cpp:61
uint16_t uint16
Definition: types.h:202
byte * readFile(const Common::UString &directory, const Common::UString &file, size_t &size)
Definition: version.cpp:331
Common::UString getVersionString() const
Definition: version.cpp:94
Common::UString getPlatformName() const
Definition: version.cpp:74
static uint16 getOptimumVersionMajor()
Return the optimum major version.
Definition: version.cpp:102
static Common::UString getOptimumVersionString()
Definition: version.cpp:114
uint16 _versionBuild
v1.69.8109 -> 8109
Definition: version.h:81
Version(Aurora::Platform platform)
Definition: version.cpp:58
size_t read(void *dataPtr, size_t dataSize)
Read data from the stream.
Definition: readfile.cpp:134
bool empty() const
Is the string empty?
Definition: ustring.cpp:245
static uint16 getOptimumVersionMinor()
Return the optimum minor version.
Definition: version.cpp:106
static const byte kBuildWin[25]
Definition: version.cpp:46
static const byte kVersionWin[23]
Definition: version.cpp:42
Implementing the stream reading interfaces for files.
Unicode string handling.
GNU/Linux.
Definition: types.h:432
Aurora::Platform getPlatform() const
Definition: version.cpp:70
PointerType get() const
Returns the plain pointer value.
Definition: scopedptr.h:96
bool detectMacOSX(const Common::UString &directory)
Definition: version.cpp:224
uint64 _version
The combined version number.
Definition: version.h:82
size_t size() const
Obtains the total size of the stream, measured in bytes.
Definition: readfile.cpp:110
A list of files.
Definition: filelist.h:35
Microsoft Windows.
Definition: types.h:430
static const byte kVersionUnix[20]
Definition: version.cpp:50
static uint16 getOptimumVersionBuild()
Return the optimum build number.
Definition: version.cpp:110
bool addDirectory(const UString &directory, int recurseDepth=0)
Add a directory to the list.
Definition: filelist.cpp:102
A list of files.
Aurora::Platform _platform
Definition: version.h:77
Common::UString getPlatformDescription(Platform platform)
Return the human readable string of a Platform.
Definition: util.cpp:430
bool hasVersion() const
Did we detect a version?
Definition: version.cpp:66
uint16 getVersionMajor() const
Return the major version.
Definition: version.cpp:82
bool detectLinux(const Common::UString &directory)
Definition: version.cpp:283
Platform
Definition: types.h:429
Utility class for manipulating file paths.
uint8 byte
Definition: types.h:209
static UString findSubDirectory(const UString &directory, const UString &subDirectory, bool caseInsensitive=false)
Find a directory&#39;s subdirectory.
Definition: filepath.cpp:318