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