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 // Disable symbol overrides so that we can use system headers.
24 #define FORBIDDEN_SYMBOL_ALLOW_ALL
25
26 #ifdef WIN32
27
28 #define WIN32_LEAN_AND_MEAN
29 #include <windows.h>
30 #undef ARRAYSIZE // winnt.h defines ARRAYSIZE, but we want our own one...
31 #include <shellapi.h>
32 #if defined(__GNUC__) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
33 // required for SHGFP_TYPE_CURRENT in shlobj.h
34 #define _WIN32_IE 0x500
35 #endif
36 #include <shlobj.h>
37
38 #include "common/scummsys.h"
39 #include "common/config-manager.h"
40 #include "common/error.h"
41 #include "common/textconsole.h"
42
43 #include "backends/audiocd/win32/win32-audiocd.h"
44 #include "backends/platform/sdl/win32/win32.h"
45 #include "backends/platform/sdl/win32/win32-window.h"
46 #include "backends/saves/windows/windows-saves.h"
47 #include "backends/fs/windows/windows-fs-factory.h"
48 #include "backends/taskbar/win32/win32-taskbar.h"
49 #include "backends/updates/win32/win32-updates.h"
50
51 #include "common/memstream.h"
52
53 #define DEFAULT_CONFIG_FILE "residualvm.ini"
54
init()55 void OSystem_Win32::init() {
56 // Initialize File System Factory
57 _fsFactory = new WindowsFilesystemFactory();
58
59 // Create Win32 specific window
60 _window = new SdlWindow_Win32();
61
62 #if defined(USE_TASKBAR)
63 // Initialize taskbar manager
64 _taskbarManager = new Win32TaskbarManager(_window);
65 #endif
66
67 // Invoke parent implementation of this method
68 OSystem_SDL::init();
69 }
70
initBackend()71 void OSystem_Win32::initBackend() {
72 // Console window is enabled by default on Windows
73 ConfMan.registerDefault("console", true);
74
75 // Enable or disable the window console window
76 if (ConfMan.getBool("console")) {
77 if (AllocConsole()) {
78 freopen("CONIN$","r",stdin);
79 freopen("CONOUT$","w",stdout);
80 freopen("CONOUT$","w",stderr);
81 }
82 SetConsoleTitle("ResidualVM Status Window");
83 } else {
84 FreeConsole();
85 }
86
87 // Create the savefile manager
88 if (_savefileManager == 0)
89 _savefileManager = new WindowsSaveFileManager();
90
91 #if defined(USE_SPARKLE)
92 // Initialize updates manager
93 _updateManager = new Win32UpdateManager();
94 #endif
95
96 // Invoke parent implementation of this method
97 OSystem_SDL::initBackend();
98 }
99
100
hasFeature(Feature f)101 bool OSystem_Win32::hasFeature(Feature f) {
102 if (f == kFeatureDisplayLogFile || f == kFeatureOpenUrl)
103 return true;
104
105 return OSystem_SDL::hasFeature(f);
106 }
107
displayLogFile()108 bool OSystem_Win32::displayLogFile() {
109 if (_logFilePath.empty())
110 return false;
111
112 // Try opening the log file with the default text editor
113 // log files should be registered as "txtfile" by default and thus open in the default text editor
114 HINSTANCE shellExec = ShellExecute(NULL, NULL, _logFilePath.c_str(), NULL, NULL, SW_SHOWNORMAL);
115 if ((intptr_t)shellExec > 32)
116 return true;
117
118 // ShellExecute with the default verb failed, try the "Open with..." dialog
119 PROCESS_INFORMATION processInformation;
120 STARTUPINFO startupInfo;
121 memset(&processInformation, 0, sizeof(processInformation));
122 memset(&startupInfo, 0, sizeof(startupInfo));
123 startupInfo.cb = sizeof(startupInfo);
124
125 char cmdLine[MAX_PATH * 2]; // CreateProcess may change the contents of cmdLine
126 sprintf(cmdLine, "rundll32 shell32.dll,OpenAs_RunDLL %s", _logFilePath.c_str());
127 BOOL result = CreateProcess(NULL,
128 cmdLine,
129 NULL,
130 NULL,
131 FALSE,
132 NORMAL_PRIORITY_CLASS,
133 NULL,
134 NULL,
135 &startupInfo,
136 &processInformation);
137 if (result)
138 return true;
139
140 return false;
141 }
142
openUrl(const Common::String & url)143 bool OSystem_Win32::openUrl(const Common::String &url) {
144 const uint64 result = (uint64)ShellExecute(0, 0, /*(wchar_t*)nativeFilePath.utf16()*/url.c_str(), 0, 0, SW_SHOWNORMAL);
145 // ShellExecute returns a value greater than 32 if successful
146 if (result <= 32) {
147 warning("ShellExecute failed: error = %u", result);
148 return false;
149 }
150 return true;
151 }
152
getScreenshotsPath()153 Common::String OSystem_Win32::getScreenshotsPath() {
154 Common::String screenshotsPath = ConfMan.get("screenshotpath");
155 if (!screenshotsPath.empty()) {
156 if (!screenshotsPath.hasSuffix("\\") && !screenshotsPath.hasSuffix("/"))
157 screenshotsPath += "\\";
158 return screenshotsPath;
159 }
160
161 char picturesPath[MAXPATHLEN];
162
163 // Use the My Pictures folder.
164 if (SHGetFolderPath(NULL, CSIDL_MYPICTURES, NULL, SHGFP_TYPE_CURRENT, picturesPath) != S_OK) {
165 warning("Unable to access My Pictures directory");
166 return Common::String();
167 }
168
169 screenshotsPath = Common::String(picturesPath) + "\\ResidualVM Screenshots\\";
170
171 // If the directory already exists (as it should in most cases),
172 // we don't want to fail, but we need to stop on other errors (such as ERROR_PATH_NOT_FOUND)
173 if (!CreateDirectory(screenshotsPath.c_str(), NULL)) {
174 if (GetLastError() != ERROR_ALREADY_EXISTS)
175 error("Cannot create ResidualVM Screenshots folder");
176 }
177
178 return screenshotsPath;
179 }
180
getDefaultConfigFileName()181 Common::String OSystem_Win32::getDefaultConfigFileName() {
182 char configFile[MAXPATHLEN];
183
184 OSVERSIONINFO win32OsVersion;
185 ZeroMemory(&win32OsVersion, sizeof(OSVERSIONINFO));
186 win32OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
187 GetVersionEx(&win32OsVersion);
188 // Check for non-9X version of Windows.
189 if (win32OsVersion.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
190 // Use the Application Data directory of the user profile.
191 if (win32OsVersion.dwMajorVersion >= 5) {
192 if (!GetEnvironmentVariable("APPDATA", configFile, sizeof(configFile)))
193 error("Unable to access application data directory");
194 } else {
195 if (!GetEnvironmentVariable("USERPROFILE", configFile, sizeof(configFile)))
196 error("Unable to access user profile directory");
197
198 strcat(configFile, "\\Application Data");
199
200 // If the directory already exists (as it should in most cases),
201 // we don't want to fail, but we need to stop on other errors (such as ERROR_PATH_NOT_FOUND)
202 if (!CreateDirectory(configFile, NULL)) {
203 if (GetLastError() != ERROR_ALREADY_EXISTS)
204 error("Cannot create Application data folder");
205 }
206 }
207
208 strcat(configFile, "\\ResidualVM");
209 if (!CreateDirectory(configFile, NULL)) {
210 if (GetLastError() != ERROR_ALREADY_EXISTS)
211 error("Cannot create ResidualVM application data folder");
212 }
213
214 strcat(configFile, "\\" DEFAULT_CONFIG_FILE);
215
216 FILE *tmp = NULL;
217 if ((tmp = fopen(configFile, "r")) == NULL) {
218 // Check windows directory
219 char oldConfigFile[MAXPATHLEN];
220 uint ret = GetWindowsDirectory(oldConfigFile, MAXPATHLEN);
221 if (ret == 0 || ret > MAXPATHLEN)
222 error("Cannot retrieve the path of the Windows directory");
223
224 strcat(oldConfigFile, "\\" DEFAULT_CONFIG_FILE);
225 if ((tmp = fopen(oldConfigFile, "r"))) {
226 strcpy(configFile, oldConfigFile);
227
228 fclose(tmp);
229 }
230 } else {
231 fclose(tmp);
232 }
233 } else {
234 // Check windows directory
235 uint ret = GetWindowsDirectory(configFile, MAXPATHLEN);
236 if (ret == 0 || ret > MAXPATHLEN)
237 error("Cannot retrieve the path of the Windows directory");
238
239 strcat(configFile, "\\" DEFAULT_CONFIG_FILE);
240 }
241
242 return configFile;
243 }
244
createLogFile()245 Common::WriteStream *OSystem_Win32::createLogFile() {
246 // Start out by resetting _logFilePath, so that in case
247 // of a failure, we know that no log file is open.
248 _logFilePath.clear();
249
250 char logFile[MAXPATHLEN];
251
252 OSVERSIONINFO win32OsVersion;
253 ZeroMemory(&win32OsVersion, sizeof(OSVERSIONINFO));
254 win32OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
255 GetVersionEx(&win32OsVersion);
256 // Check for non-9X version of Windows.
257 if (win32OsVersion.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
258 // Use the Application Data directory of the user profile.
259 if (win32OsVersion.dwMajorVersion >= 5) {
260 if (!GetEnvironmentVariable("APPDATA", logFile, sizeof(logFile)))
261 error("Unable to access application data directory");
262 } else {
263 if (!GetEnvironmentVariable("USERPROFILE", logFile, sizeof(logFile)))
264 error("Unable to access user profile directory");
265
266 strcat(logFile, "\\Application Data");
267 CreateDirectory(logFile, NULL);
268 }
269
270 strcat(logFile, "\\ResidualVM");
271 CreateDirectory(logFile, NULL);
272 strcat(logFile, "\\Logs");
273 CreateDirectory(logFile, NULL);
274 strcat(logFile, "\\residualvm.log");
275
276 Common::FSNode file(logFile);
277 Common::WriteStream *stream = file.createWriteStream();
278 if (stream)
279 _logFilePath= logFile;
280
281 return stream;
282 } else {
283 return 0;
284 }
285 }
286
287 namespace {
288
289 class Win32ResourceArchive : public Common::Archive {
290 friend BOOL CALLBACK EnumResNameProc(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam);
291 public:
292 Win32ResourceArchive();
293
294 virtual bool hasFile(const Common::String &name) const;
295 virtual int listMembers(Common::ArchiveMemberList &list) const;
296 virtual const Common::ArchiveMemberPtr getMember(const Common::String &name) const;
297 virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
298 private:
299 typedef Common::List<Common::String> FilenameList;
300
301 FilenameList _files;
302 };
303
EnumResNameProc(HMODULE hModule,LPCTSTR lpszType,LPTSTR lpszName,LONG_PTR lParam)304 BOOL CALLBACK EnumResNameProc(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam) {
305 if (IS_INTRESOURCE(lpszName))
306 return TRUE;
307
308 Win32ResourceArchive *arch = (Win32ResourceArchive *)lParam;
309 arch->_files.push_back(lpszName);
310 return TRUE;
311 }
312
Win32ResourceArchive()313 Win32ResourceArchive::Win32ResourceArchive() {
314 EnumResourceNames(NULL, MAKEINTRESOURCE(256), &EnumResNameProc, (LONG_PTR)this);
315 }
316
hasFile(const Common::String & name) const317 bool Win32ResourceArchive::hasFile(const Common::String &name) const {
318 for (FilenameList::const_iterator i = _files.begin(); i != _files.end(); ++i) {
319 if (i->equalsIgnoreCase(name))
320 return true;
321 }
322
323 return false;
324 }
325
listMembers(Common::ArchiveMemberList & list) const326 int Win32ResourceArchive::listMembers(Common::ArchiveMemberList &list) const {
327 int count = 0;
328
329 for (FilenameList::const_iterator i = _files.begin(); i != _files.end(); ++i, ++count)
330 list.push_back(Common::ArchiveMemberPtr(new Common::GenericArchiveMember(*i, this)));
331
332 return count;
333 }
334
getMember(const Common::String & name) const335 const Common::ArchiveMemberPtr Win32ResourceArchive::getMember(const Common::String &name) const {
336 return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
337 }
338
createReadStreamForMember(const Common::String & name) const339 Common::SeekableReadStream *Win32ResourceArchive::createReadStreamForMember(const Common::String &name) const {
340 HRSRC resource = FindResource(NULL, name.c_str(), MAKEINTRESOURCE(256));
341
342 if (resource == NULL)
343 return 0;
344
345 HGLOBAL handle = LoadResource(NULL, resource);
346
347 if (handle == NULL)
348 return 0;
349
350 const byte *data = (const byte *)LockResource(handle);
351
352 if (data == NULL)
353 return 0;
354
355 uint32 size = SizeofResource(NULL, resource);
356
357 if (size == 0)
358 return 0;
359
360 return new Common::MemoryReadStream(data, size);
361 }
362
363 } // End of anonymous namespace
364
addSysArchivesToSearchSet(Common::SearchSet & s,int priority)365 void OSystem_Win32::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
366 s.add("Win32Res", new Win32ResourceArchive(), priority);
367
368 OSystem_SDL::addSysArchivesToSearchSet(s, priority);
369 }
370
createAudioCDManager()371 AudioCDManager *OSystem_Win32::createAudioCDManager() {
372 return createWin32AudioCDManager();
373 }
374
375 #endif
376