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 namespace Engines {
38 
39 namespace KotOR {
40 
41 /* Latest version numbers by platform:
42  * - Windows: 1.03.514077
43  * - MacOS X: 1.03.514078
44  * - Xbox: 1.00.545501
45  */
46 
47 Version::Version(Aurora::Platform platform) : _platform(platform),
48  _versionMajor(0), _versionMinor(0), _versionBuild(0) {
49 
50 }
51 
53 }
54 
55 bool Version::hasVersion() const {
56  return _versionMajor != 0 || _versionMinor != 0 || _versionBuild != 0;
57 }
58 
60  return _platform;
61 }
62 
65 }
66 
68  return _versionMajor;
69 }
70 
72  return _versionMinor;
73 }
74 
76  return _versionBuild;
77 }
78 
81 }
82 
84  switch (_platform) {
88  return 1;
89 
90  default:
91  break;
92  }
93 
94  return 0;
95 }
96 
98  switch (_platform) {
101  return 3;
102 
104  return 0;
105 
106  default:
107  break;
108  }
109 
110  return 0;
111 }
112 
114  return Common::UString::format("%u.%02u",
116 }
117 
118 static uint32 makeCombinedVersion(uint32 major, uint32 minor) {
119  return (major << 16) + minor;
120 }
121 
122 bool Version::isTooOld() const {
125 }
126 
127 bool Version::isTooNew() const {
130 }
131 
132 // Detect the version of the KotOR installation by inspecting the binaries
133 bool Version::detect(const Common::UString &directory) {
135 
136  bool success = false;
137 
139  success = detectWindows(directory);
141  success = detectMacOSX(directory);
142  else if (_platform == Aurora::kPlatformXbox)
143  success = detectXbox(directory);
144 
145  if (!success)
147 
148  return success;
149 }
150 
151 // In the Windows binary, read the FileVersion and PrivateBuild fields
152 // from the VERSIONINFO resource. They're UTF-16LE strings.
153 bool Version::detectWindows(const Common::UString &directory) {
154  static const byte kVersionWin[23] = {
155  '\0','F','\0','i','\0','l','\0','e','\0','V','\0','e','\0','r','\0','s','\0','i','\0','o','\0','n','\0'
156  };
157 
158  static const byte kBuildWin[25] = {
159  '\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'
160  };
161 
162  size_t size;
163  Common::ScopedArray<byte> binary(readFile(directory, "/swkotor.exe", size));
164  if (!binary)
165  return false;
166 
167  // Search for the pattern where the version information in the binary starts
168  byte *version = std::search(binary.get(), binary.get() + size, kVersionWin, kVersionWin + sizeof(kVersionWin));
169  if ((size - (version - binary.get())) < (sizeof(kVersionWin) + 24))
170  return false;
171 
172  // Search for the pattern where the build information in the binary starts
173  byte *build = std::search(binary.get(), binary.get() + size, kBuildWin, kBuildWin + sizeof(kBuildWin));
174  if ((size - (build - binary.get())) < (sizeof(kBuildWin) + 14))
175  return false;
176 
177  bool success = false;
178 
179  // Check that the field separators are there and the upper bytes of UTF-16 character are 0
180  if ((version[sizeof(kVersionWin) + 0] == 0) &&
181  (version[sizeof(kVersionWin) + 1] == 0) &&
182  (version[sizeof(kVersionWin) + 2] == 0) &&
183  (version[sizeof(kVersionWin) + 3] == 0) &&
184  (version[sizeof(kVersionWin) + 5] == 0) &&
185  (version[sizeof(kVersionWin) + 7] == 0) &&
186  (version[sizeof(kVersionWin) + 9] == 0) &&
187  (version[sizeof(kVersionWin) + 11] == 0) &&
188  (version[sizeof(kVersionWin) + 13] == 0) &&
189  (version[sizeof(kVersionWin) + 17] == 0) &&
190  (version[sizeof(kVersionWin) + 21] == 0) &&
191  (version[sizeof(kVersionWin) + 23] == 0) &&
192  (build [sizeof(kBuildWin ) + 0] == 0) &&
193  (build [sizeof(kBuildWin ) + 1] == 0) &&
194  (build [sizeof(kBuildWin ) + 3] == 0) &&
195  (build [sizeof(kBuildWin ) + 5] == 0) &&
196  (build [sizeof(kBuildWin ) + 7] == 0) &&
197  (build [sizeof(kBuildWin ) + 9] == 0) &&
198  (build [sizeof(kBuildWin ) + 11] == 0) &&
199  (build [sizeof(kBuildWin ) + 13] == 0) &&
200  (version[sizeof(kVersionWin) + 6] == ',') &&
201  (version[sizeof(kVersionWin) + 12] == ',') &&
202  (version[sizeof(kVersionWin) + 18] == ',') &&
203  (version[sizeof(kVersionWin) + 8] == ' ') &&
204  (version[sizeof(kVersionWin) + 14] == ' ') &&
205  (version[sizeof(kVersionWin) + 20] == ' ')) {
206 
207  // Check that the version strings are numeric
208  if (isdigit(version[sizeof(kVersionWin) + 4]) &&
209  isdigit(version[sizeof(kVersionWin) + 10]) &&
210  isdigit(version[sizeof(kVersionWin) + 16]) &&
211  isdigit(build [sizeof(kBuildWin ) + 2]) &&
212  isdigit(build [sizeof(kBuildWin ) + 4]) &&
213  isdigit(build [sizeof(kBuildWin ) + 6]) &&
214  isdigit(build [sizeof(kBuildWin ) + 8]) &&
215  isdigit(build [sizeof(kBuildWin ) + 10]) &&
216  isdigit(build [sizeof(kBuildWin ) + 12])) {
217 
218  // Read the version information
219 
220  _versionMajor = version[sizeof(kVersionWin) + 4] - '0';
221 
222  _versionMinor = (version[sizeof(kVersionWin) + 10] - '0') * 10;
223  _versionMinor += version[sizeof(kVersionWin) + 16] - '0';
224 
225  _versionBuild = (build [sizeof(kBuildWin ) + 2] - '0') * 100000;
226  _versionBuild += (build [sizeof(kBuildWin ) + 4] - '0') * 10000;
227  _versionBuild += (build [sizeof(kBuildWin ) + 6] - '0') * 1000;
228  _versionBuild += (build [sizeof(kBuildWin ) + 8] - '0') * 100;
229  _versionBuild += (build [sizeof(kBuildWin ) + 10] - '0') * 10;
230  _versionBuild += build [sizeof(kBuildWin ) + 12] - '0';
231 
232  success = true;
233 
234  }
235  }
236 
237  return success;
238 }
239 
240 // In the Mac OS X binaries, the version number ("1, 0, 3, 0") and the build number ("514078")
241 // precede the name "Star Wars: Knights of the Old Republic", each field separated by 2 0-byte.
242 bool Version::detectMacOSX(const Common::UString &directory) {
243  static const byte kVersionMac[42] = {
244  '\0','\0','S','t','a','r',' ','W','a','r','s',':',' ','K','n','i','g','h','t','s',' ','o','f',' ',
245  't','h','e',' ','O','l','d',' ','R','e','p','u','b','l','i','c','\0','\0'
246  };
247 
248  Common::UString appDir = Common::FilePath::findSubDirectory(directory, "KOTOR.app/Contents/MacOS", true);
249  if (appDir.empty())
250  appDir = Common::FilePath::findSubDirectory(directory, "Knights of the Old Republic.app/Contents/MacOS", true);
251  if (appDir.empty())
252  return false;
253 
254  size_t size;
255  Common::ScopedArray<byte> binary(readFile(appDir, "XKOTOR", size));
256  if (!binary)
257  binary.reset(readFile(appDir, "Knights of the Old Republic", size));
258  if (!binary)
259  return false;
260 
261  // Search for the pattern where the version information in the binary starts
262  byte *version = std::search(binary.get(), binary.get() + size, kVersionMac, kVersionMac + sizeof(kVersionMac));
263  if ((version - binary.get()) < 20)
264  return false;
265 
266  bool success = false;
267 
268  // Check that the field separators are there
269  if ((version[-11] == 0) &&
270  (version[-12] == 0) &&
271  (version[-19] == 0) &&
272  (version[- 3] == ',') &&
273  (version[- 6] == ',') &&
274  (version[- 9] == ',') &&
275  (version[- 9] == ',') &&
276  (version[- 2] == ' ') &&
277  (version[- 5] == ' ') &&
278  (version[- 8] == ' ')) {
279 
280  // Check that the version strings are numeric
281  if (isdigit(version[- 1]) &&
282  isdigit(version[- 4]) &&
283  isdigit(version[- 7]) &&
284  isdigit(version[-10]) &&
285  isdigit(version[-13]) &&
286  isdigit(version[-14]) &&
287  isdigit(version[-15]) &&
288  isdigit(version[-16]) &&
289  isdigit(version[-17]) &&
290  isdigit(version[-18])) {
291 
292  // Read the version information
293 
294  _versionMajor = version[-10] - '0';
295 
296  _versionMinor = (version[- 7] - '0') * 10;
297  _versionMinor += version[- 4] - '0';
298 
299  _versionBuild = (version[-18] - '0') * 100000;
300  _versionBuild += (version[-17] - '0') * 10000;
301  _versionBuild += (version[-16] - '0') * 1000;
302  _versionBuild += (version[-15] - '0') * 100;
303  _versionBuild += (version[-14] - '0') * 10;
304  _versionBuild += version[-13] - '0';
305 
306  success = true;
307 
308  }
309  }
310 
311  return success;
312 }
313 
314 // In the Xbox binaries, the version number ("1.00") and the build number ("5455.1")
315 // preceed the string "Release", separated by a 0-byte.
316 bool Version::detectXbox(const Common::UString &directory) {
317  static const byte kVersionXbox[9] = {
318  '\0','R','e','l','e','a','s','e','\0'
319  };
320 
321  size_t size;
322  Common::ScopedArray<byte> binary(readFile(directory, "/default.xbe", size));
323  if (!binary)
324  return false;
325 
326  // Search for the pattern where the version information in the binary starts
327  byte *version = std::search(binary.get(), binary.get() + size, kVersionXbox, kVersionXbox + sizeof(kVersionXbox));
328  if ((version - binary.get()) < 13)
329  return false;
330 
331  bool success = false;
332 
333  // Check that the field separators are there
334  if ((version[-12] == 0) &&
335  (version[- 2] == '.') &&
336  (version[- 7] == '.') &&
337  (version[-10] == '.')) {
338 
339  // Check that the version strings are numeric
340  if (isdigit(version[- 1]) &&
341  isdigit(version[- 3]) &&
342  isdigit(version[- 4]) &&
343  isdigit(version[- 5]) &&
344  isdigit(version[- 6]) &&
345  isdigit(version[- 8]) &&
346  isdigit(version[- 9]) &&
347  isdigit(version[-11])) {
348 
349  // Read the version information
350 
351  _versionMajor = version[-11] - '0';
352 
353  _versionMinor = (version[- 9] - '0') * 10;
354  _versionMinor += version[- 8] - '0';
355 
356  _versionBuild = (version[- 6] - '0') * 100000;
357  _versionBuild += (version[- 5] - '0') * 10000;
358  _versionBuild += (version[- 4] - '0') * 1000;
359  _versionBuild += (version[- 3] - '0') * 100;
360  _versionBuild += version[- 1] - '0';
361 
362  success = true;
363 
364  }
365  }
366 
367  return success;
368 }
369 
370 byte *Version::readFile(const Common::UString &directory, const Common::UString &file, size_t &size) {
371  Common::FileList files;
372 
373  if (!files.addDirectory(directory))
374  return 0;
375 
376  return readFile(files.findFirst(file, true), size);
377 }
378 
379 byte *Version::readFile(const Common::UString &path, size_t &size) {
380  if (path.empty())
381  return 0;
382 
383  Common::ReadFile file;
384  if (!file.open(path))
385  return 0;
386 
387  size = file.size();
388 
389  Common::ScopedArray<byte> buffer(new byte[size]);
390  if (file.read(buffer.get(), size) != size)
391  return 0;
392 
393  return buffer.release();
394 }
395 
396 } // End of namespace KotOR
397 
398 } // End of namespace Engines
bool isTooNew() const
Is this version newer than the optimum?
Definition: version.cpp:127
uint32 getVersionBuild() const
Return the build number.
Definition: version.cpp:75
A class holding an UTF-8 string.
Definition: ustring.h:48
void reset(PointerType o=0)
Resets the pointer with the new value.
Definition: scopedptr.h:87
bool detectWindows(const Common::UString &directory)
Definition: version.cpp:153
PointerType release()
Returns the plain pointer value and releases ScopedPtr.
Definition: scopedptr.h:103
bool detect(const Common::UString &directory)
Try to detect the version of the KotOR installation in this directory.
Definition: version.cpp:133
bool detectXbox(const Common::UString &directory)
Definition: version.cpp:316
A simple streaming file reading class.
Definition: readfile.h:40
Utility functions to handle files used in BioWare&#39;s Aurora engine.
byte * readFile(const Common::UString &directory, const Common::UString &file, size_t &size)
Definition: version.cpp:370
Common::UString getPlatformName() const
Definition: version.cpp:63
A simple scoped smart pointer template.
bool isTooOld() const
Is this version older than the optimum?
Definition: version.cpp:122
UString findFirst(const UString &str, bool caseInsensitive) const
Find the first file ending with the given string.
Definition: filelist.cpp:193
Aurora::Platform _platform
Definition: version.h:70
Aurora::Platform getPlatform() const
Definition: version.cpp:59
static UString format(const char *s,...) GCC_PRINTF(1
Print formatted data into an UString object, similar to sprintf().
Definition: ustring.cpp:718
bool hasVersion() const
Did we detect a version?
Definition: version.cpp:55
Version(Aurora::Platform platform)
Definition: version.cpp:47
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
static uint32 makeCombinedVersion(uint32 major, uint32 minor)
Definition: version.cpp:118
uint32 _versionBuild
v1.03.514078 -> 514078
Definition: version.h:74
Microsoft Xbox.
Definition: types.h:433
Star Wars: Knights of the Old Republic installation version detection.
size_t read(void *dataPtr, size_t dataSize)
Read data from the stream.
Definition: readfile.cpp:134
uint16 getOptimumVersionMinor() const
Return the optimum minor version.
Definition: version.cpp:97
bool empty() const
Is the string empty?
Definition: ustring.cpp:245
static const byte kBuildWin[25]
Definition: version.cpp:46
uint16 getVersionMinor() const
Return the minor version.
Definition: version.cpp:71
static const byte kVersionWin[23]
Definition: version.cpp:42
bool detectMacOSX(const Common::UString &directory)
Definition: version.cpp:242
Implementing the stream reading interfaces for files.
uint16 getVersionMajor() const
Return the major version.
Definition: version.cpp:67
Unicode string handling.
PointerType get() const
Returns the plain pointer value.
Definition: scopedptr.h:96
size_t size() const
Obtains the total size of the stream, measured in bytes.
Definition: readfile.cpp:110
uint16 _versionMinor
v1.03.514078 -> 3
Definition: version.h:73
Common::UString getOptimumVersionString()
Definition: version.cpp:113
uint32_t uint32
Definition: types.h:204
A list of files.
Definition: filelist.h:35
Microsoft Windows.
Definition: types.h:430
bool addDirectory(const UString &directory, int recurseDepth=0)
Add a directory to the list.
Definition: filelist.cpp:102
A list of files.
Common::UString getPlatformDescription(Platform platform)
Return the human readable string of a Platform.
Definition: util.cpp:430
uint16 _versionMajor
v1.03.514078 -> 1
Definition: version.h:72
Common::UString getVersionString() const
Definition: version.cpp:79
Platform
Definition: types.h:429
uint16 getOptimumVersionMajor() const
Return the optimum major version.
Definition: version.cpp:83
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