scummvm/backends/platform/wince/wince-sdl.cpp
2014-02-18 02:39:40 +01:00

696 lines
19 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
// Disable symbol overrides so that we can use system headers.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "backends/platform/wince/wince-sdl.h"
#include "common/config-manager.h"
#include "common/debug.h"
#include "common/events.h"
#include "common/util.h"
#include "common/textconsole.h"
#include "common/timer.h"
#include "common/translation.h"
#include "engines/engine.h"
#include "base/main.h"
#include "base/plugins.h"
#include "audio/mixer_intern.h"
#include "audio/fmopl.h"
#include "backends/mutex/sdl/sdl-mutex.h"
#include "backends/timer/sdl/sdl-timer.h"
#include "gui/Actions.h"
#include "gui/KeysDialog.h"
#include "gui/message.h"
#include "backends/platform/wince/CEActionsPocket.h"
#include "backends/platform/wince/CEActionsSmartphone.h"
#include "backends/platform/wince/CEgui/ItemAction.h"
#include "graphics/scaler/downscaler.h"
#include "graphics/scaler/aspect.h"
#include "backends/platform/wince/CEException.h"
#include "backends/platform/wince/CEScaler.h"
#include "backends/graphics/wincesdl/wincesdl-graphics.h"
#include "backends/events/wincesdl/wincesdl-events.h"
#include "backends/mixer/wincesdl/wincesdl-mixer.h"
#ifdef DYNAMIC_MODULES
#include <malloc.h>
#include "backends/plugins/win32/win32-provider.h"
#endif
#ifdef __GNUC__
extern "C" _CRTIMP FILE *__cdecl _wfreopen(const wchar_t *, const wchar_t *, FILE *);
#endif
#ifdef WRAP_MALLOC
extern "C" void *__real_malloc(size_t size);
extern "C" void __real_free(void *ptr);
extern "C" void *__wrap_malloc(size_t size) {
/*
void *ptr = __real_malloc(size);
printf("malloc(%d) = %p\n", size, ptr);
return ptr;
*/
if (size < 64 * 1024) {
void *ptr = __real_malloc(size+4);
// printf("malloc(%d) = %p\n", size, ptr);
if (ptr != NULL) {
*((HANDLE *)ptr) = 0;
return 4+(char *)ptr;
}
return NULL;
}
HANDLE H = CreateFileMapping((HANDLE)INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, size+4, 0);
void *ptr = MapViewOfFile(H, FILE_MAP_ALL_ACCESS, 0, 0, 0);
*((HANDLE *)ptr) = H;
return 4+(char *)ptr;
}
extern "C" void __wrap_free(void *ptr) {
/*
__real_free(ptr);
printf("free(%p)\n", ptr);
*/
if (ptr != NULL) {
HANDLE H = *(HANDLE *)((char *)ptr-4);
if (H == 0) {
__real_free((char *)ptr-4);
return;
}
UnmapViewOfFile((char *)ptr-4);
CloseHandle(H);
}
}
#endif
using namespace CEGUI;
// ********************************************************************************************
// stdin/err redirection
#define STDOUT_FNAME "\\scummvm_stdout.txt"
#define STDERR_FNAME "\\scummvm_stderr.txt"
static FILE *stdout_file = NULL, *stderr_file = NULL;
static char stdout_fname[MAX_PATH], stderr_fname[MAX_PATH];
// Static member inits
typedef void (*SoundProc)(void *param, byte *buf, int len);
bool OSystem_WINCE3::_soundMaster = true;
bool _isSmartphone = false;
bool _hasSmartphoneResolution = false;
#define DEFAULT_CONFIG_FILE "scummvm.ini"
// ********************************************************************************************
bool isSmartphone() {
//return _isSmartphone;
return _hasSmartphoneResolution;
}
const TCHAR *ASCIItoUnicode(const char *str) {
static TCHAR ustr[MAX_PATH]; // size good enough
MultiByteToWideChar(CP_ACP, 0, str, strlen(str) + 1, ustr, sizeof(ustr) / sizeof(TCHAR));
return ustr;
}
// MAIN
#ifndef __GNUC__
int handleException(EXCEPTION_POINTERS *exceptionPointers) {
CEException::writeException(TEXT("\\scummvmCrash"), exceptionPointers);
drawError("Unrecoverable exception occurred - see crash dump in latest \\scummvmCrash file");
fclose(stdout_file);
fclose(stderr_file);
CEDevice::end();
SDL_Quit();
exit(0);
return EXCEPTION_EXECUTE_HANDLER;
}
#endif
extern "C" char *getcwd(char *buf, int size);
int SDL_main(int argc, char **argv) {
FILE *newfp = NULL;
#ifdef __GNUC__
// Due to incomplete crt0.o implementation, we go through the constructor function
// list provided by the linker and init all of them
// thanks to joostp and DJWillis
extern void (*__CTOR_LIST__)();
void (**constructor)() = &__CTOR_LIST__;
constructor++; // First item in list of constructors has special meaning (platform dependent), ignore it.
while (*constructor) {
(*constructor)();
constructor++;
}
#endif
CEDevice::init();
/* Redirect standard input and standard output */
strcpy(stdout_fname, getcwd(NULL, MAX_PATH));
strcpy(stderr_fname, getcwd(NULL, MAX_PATH));
strcat(stdout_fname, STDOUT_FNAME);
strcat(stderr_fname, STDERR_FNAME);
#ifndef __GNUC__
stdout_file = fopen(stdout_fname, "w");
stderr_file = fopen(stderr_fname, "w");
#else
stdout_file = newfp = _wfreopen(ASCIItoUnicode(stdout_fname), TEXT("w"), stdout);
if (newfp == NULL) {
#if !defined(stdout)
stdout = fopen(stdout_fname, "w");
stdout_file = stdout;
#else
newfp = fopen(stdout_fname, "w");
if (newfp) {
//*stdout = *newfp;
stdout_file = stdout;
}
#endif
}
stderr_file = newfp = _wfreopen(ASCIItoUnicode(stderr_fname), TEXT("w"), stderr);
if (newfp == NULL) {
#if !defined(stderr)
stderr = fopen(stderr_fname, "w");
stderr_file = stderr;
#else
newfp = fopen(stderr_fname, "w");
if (newfp) {
//*stderr = *newfp;
stderr_file = stderr;
}
#endif
}
#endif
#ifdef DYNAMIC_MODULES
PluginManager::instance().addPluginProvider(new Win32PluginProvider());
#endif
int res = 0;
#if !defined(DEBUG) && !defined(__GNUC__)
__try {
#endif
g_system = new OSystem_WINCE3();
assert(g_system);
// Pre initialize the backend
((OSystem_WINCE3 *)g_system)->init();
// Invoke the actual ScummVM main entry point:
res = scummvm_main(argc, argv);
// Free OSystem
delete(OSystem_WINCE3 *)g_system;
#if !defined(DEBUG) && !defined(__GNUC__)
}
__except(handleException(GetExceptionInformation())) {
}
#endif
return res;
}
#ifdef DYNAMIC_MODULES
/* This is the OS startup code in the case of a plugin-enabled build.
* It contains copied and slightly modified parts of SDL's win32/ce startup functions.
* We copy these here because the calling stub already has a WinMain procedure
* which overrides SDL's one and hence we essentially re-implement the startup procedure.
* Note also that this has to be here and not in the stub because SDL is statically
* linked in the scummvm.dll archive.
* Take a look at the comments in stub.cpp as well.
*/
int console_main(int argc, char *argv[]) {
int n;
char *bufp, *appname;
appname = argv[0];
if ((bufp = strrchr(argv[0], '\\')) != NULL)
appname = bufp + 1;
else if ((bufp = strrchr(argv[0], '/')) != NULL)
appname = bufp + 1;
if ((bufp = strrchr(appname, '.')) == NULL)
n = strlen(appname);
else
n = (bufp - appname);
bufp = (char *) alloca(n + 1);
strncpy(bufp, appname, n);
bufp[n] = '\0';
appname = bufp;
if (SDL_Init(SDL_INIT_NOPARACHUTE) < 0) {
error("WinMain() error: %d", SDL_GetError());
return(FALSE);
}
SDL_SetModuleHandle(GetModuleHandle(NULL));
// Run the application main() code
SDL_main(argc, argv);
return(0);
}
static int ParseCommandLine(char *cmdline, char **argv) {
char *bufp;
int argc;
argc = 0;
for (bufp = cmdline; *bufp;) {
// Skip leading whitespace
while (isspace(*bufp))
++bufp;
// Skip over argument
if (*bufp == '"') {
++bufp;
if (*bufp) {
if (argv)
argv[argc] = bufp;
++argc;
}
// Skip over word
while (*bufp && (*bufp != '"'))
++bufp;
} else {
if (*bufp) {
if (argv)
argv[argc] = bufp;
++argc;
}
// Skip over word
while (*bufp && ! isspace(*bufp))
++bufp;
}
if (*bufp) {
if (argv)
*bufp = '\0';
++bufp;
}
}
if (argv)
argv[argc] = NULL;
return(argc);
}
int dynamic_modules_main(HINSTANCE hInst, HINSTANCE hPrev, LPWSTR szCmdLine, int sw) {
HINSTANCE handle;
char **argv;
int argc;
char *cmdline;
wchar_t *bufp;
int nLen;
if (wcsncmp(szCmdLine, TEXT("\\"), 1)) {
nLen = wcslen(szCmdLine) + 128 + 1;
bufp = (wchar_t *) alloca(nLen * 2);
wcscpy(bufp, TEXT("\""));
GetModuleFileName(NULL, bufp + 1, 128 - 3);
wcscpy(bufp + wcslen(bufp), TEXT("\" "));
wcsncpy(bufp + wcslen(bufp), szCmdLine, nLen - wcslen(bufp));
} else
bufp = szCmdLine;
nLen = wcslen(bufp) + 1;
cmdline = (char *) alloca(nLen);
WideCharToMultiByte(CP_ACP, 0, bufp, -1, cmdline, nLen, NULL, NULL);
// Parse command line into argv and argc
argc = ParseCommandLine(cmdline, NULL);
argv = (char **) alloca((argc + 1) * (sizeof * argv));
ParseCommandLine(cmdline, argv);
// fix gdb-emulator combo
while (argc > 1 && !strstr(argv[0], ".exe")) {
OutputDebugString(TEXT("SDL: gdb argv[0] fixup\n"));
*(argv[1] - 1) = ' ';
int i;
for (i = 1; i < argc; i++)
argv[i] = argv[i + 1];
argc--;
}
// Run the main program (after a little SDL initialization)
return(console_main(argc, argv));
}
#endif
// ********************************************************************************************
// ********************************************************************************************
void pumpMessages() {
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
void drawError(char *error) {
TCHAR errorUnicode[200];
MultiByteToWideChar(CP_ACP, 0, error, strlen(error) + 1, errorUnicode, sizeof(errorUnicode));
pumpMessages();
MessageBox(GetActiveWindow(), errorUnicode, TEXT("ScummVM error"), MB_OK | MB_ICONERROR);
pumpMessages();
}
// ********************************************************************************************
static Uint32 timer_handler_wrapper(Uint32 interval) {
DefaultTimerManager *tm = (DefaultTimerManager *)g_system->getTimerManager();
tm->handler();
return interval;
}
void OSystem_WINCE3::initBackend() {
assert(!_inited);
// Create the backend custom managers
if (_eventSource == 0)
_eventSource = new WINCESdlEventSource();
if (_mixerManager == 0) {
_mixerManager = new WINCESdlMixerManager();
// Setup and start mixer
_mixerManager->init();
}
if (_graphicsManager == 0)
_graphicsManager = new WINCESdlGraphicsManager(_eventSource);
((WINCESdlEventSource *)_eventSource)->init(dynamic_cast<WINCESdlGraphicsManager *>(_graphicsManager));
// Call parent implementation of this method
OSystem_SDL::initBackend();
// Initialize global key mapping
GUI::Actions::init();
GUI_Actions::Instance()->initInstanceMain(this);
if (!GUI_Actions::Instance()->loadMapping()) { // error during loading means not present/wrong version
warning("Setting default action mappings");
GUI_Actions::Instance()->saveMapping(); // write defaults
}
_inited = true;
}
int OSystem_WINCE3::getScreenWidth() {
return _platformScreenWidth;
}
void OSystem_WINCE3::initScreenInfos() {
// sdl port ensures that we use correctly full screen
_isOzone = 0;
SDL_Rect **r;
r = SDL_ListModes(NULL, 0);
_platformScreenWidth = r[0]->w;
_platformScreenHeight = r[0]->h;
}
int OSystem_WINCE3::getScreenHeight() {
return _platformScreenHeight;
}
bool OSystem_WINCE3::isOzone() {
return _isOzone;
}
Common::String OSystem_WINCE3::getDefaultConfigFileName() {
char configFile[MAXPATHLEN];
strcpy(configFile, getcwd(NULL, MAX_PATH));
strcat(configFile, "\\");
strcat(configFile, DEFAULT_CONFIG_FILE);
return configFile;
}
// ********************************************************************************************
OSystem_WINCE3::OSystem_WINCE3() : OSystem_SDL(),
_forcePanelInvisible(false) {
// Initialze File System Factory
_fsFactory = new WindowsFilesystemFactory();
_mixer = 0;
}
OSystem_WINCE3::~OSystem_WINCE3() {
delete _mixer;
}
void OSystem_WINCE3::swap_sound_master() {
_soundMaster = !_soundMaster;
//WINCESdlGraphicsManager _graphicsManager
WINCESdlGraphicsManager *graphicsManager = dynamic_cast<WINCESdlGraphicsManager *>(_graphicsManager);
if (graphicsManager->_toolbarHandler.activeName() == NAME_MAIN_PANEL)
graphicsManager->_toolbarHandler.forceRedraw(); // redraw sound icon
}
void OSystem_WINCE3::engineInit() {
check_mappings(); // called here to initialize virtual keys handling
dynamic_cast<WINCESdlGraphicsManager *>(_graphicsManager)->update_game_settings();
// finalize mixer init
_mixerManager->init();
}
void OSystem_WINCE3::check_mappings() {
CEActionsPocket *instance;
Common::String gameid(ConfMan.get("gameid"));
if (gameid.empty() || GUI_Actions::Instance()->initialized())
return;
GUI_Actions::Instance()->initInstanceGame();
instance = (CEActionsPocket *)GUI_Actions::Instance();
// Some games need to map the right click button, signal it here if it wasn't done
if (instance->needsRightClickMapping()) {
GUI::KeysDialog *keysDialog = new GUI::KeysDialog(_("Map right click action"));
while (!instance->getMapping(POCKET_ACTION_RIGHTCLICK)) {
keysDialog->runModal();
if (!instance->getMapping(POCKET_ACTION_RIGHTCLICK)) {
GUI::MessageDialog alert(_("You must map a key to the 'Right Click' action to play this game"));
alert.runModal();
}
}
delete keysDialog;
}
// Map the "hide toolbar" action if needed
if (instance->needsHideToolbarMapping()) {
GUI::KeysDialog *keysDialog = new GUI::KeysDialog(_("Map hide toolbar action"));
while (!instance->getMapping(POCKET_ACTION_HIDE)) {
keysDialog->runModal();
if (!instance->getMapping(POCKET_ACTION_HIDE)) {
GUI::MessageDialog alert(_("You must map a key to the 'Hide toolbar' action to play this game"));
alert.runModal();
}
}
delete keysDialog;
}
// Map the "zoom" actions if needed
if (instance->needsZoomMapping()) {
GUI::KeysDialog *keysDialog = new GUI::KeysDialog(_("Map Zoom Up action (optional)"));
keysDialog->runModal();
delete keysDialog;
keysDialog = new GUI::KeysDialog(_("Map Zoom Down action (optional)"));
keysDialog->runModal();
delete keysDialog;
}
// Extra warning for Zak Mc Kracken
if (strncmp(gameid.c_str(), "zak", 3) == 0 &&
!GUI_Actions::Instance()->getMapping(POCKET_ACTION_HIDE)) {
GUI::MessageDialog alert(_("Don't forget to map a key to 'Hide Toolbar' action to see the whole inventory"));
alert.runModal();
}
}
void OSystem_WINCE3::setGraphicsModeIntern() {
// Scalers have been pre-selected for the desired mode.
// No further tuning required.
}
void OSystem_WINCE3::initSDL() {
// Check if SDL has not been initialized
if (!_initedSDL) {
uint32 sdlFlags = SDL_INIT_EVENTTHREAD | SDL_INIT_VIDEO;
if (ConfMan.hasKey("disable_sdl_parachute"))
sdlFlags |= SDL_INIT_NOPARACHUTE;
if (ConfMan.hasKey("use_GDI") && ConfMan.getBool("use_GDI")) {
SDL_VideoInit("windib", 0);
sdlFlags ^= SDL_INIT_VIDEO;
}
// Initialize SDL (SDL Subsystems are initiliazed in the corresponding sdl managers)
if (SDL_Init(sdlFlags) == -1)
error("Could not initialize SDL: %s", SDL_GetError());
_initedSDL = true;
}
}
void OSystem_WINCE3::init() {
// Create SdlMutexManager instance as the TimerManager relies on the
// MutexManager being already initialized
if (_mutexManager == 0)
_mutexManager = new SdlMutexManager();
// Create the timer. CE SDL does not support multiple timers (SDL_AddTimer).
// We work around this by using the SetTimer function, since we only use
// one timer in scummvm (for the time being)
if (_timerManager == 0) {
_timerManager = new DefaultTimerManager();
SDL_SetTimer(10, &timer_handler_wrapper);
}
// Call parent implementation of this method
OSystem_SDL::init();
}
void OSystem_WINCE3::quit() {
fclose(stdout_file);
fclose(stderr_file);
if (gDebugLevel <= 0) {
DeleteFile(ASCIItoUnicode(stdout_fname));
DeleteFile(ASCIItoUnicode(stderr_fname));
}
CEDevice::end();
OSystem_SDL::quit();
}
void OSystem_WINCE3::getTimeAndDate(TimeDate &t) const {
SYSTEMTIME systime;
GetLocalTime(&systime);
t.tm_year = systime.wYear - 1900;
t.tm_mon = systime.wMonth - 1;
t.tm_mday = systime.wDay;
t.tm_hour = systime.wHour;
t.tm_min = systime.wMinute;
t.tm_sec = systime.wSecond;
t.tm_wday = systime.wDayOfWeek;
}
Common::String OSystem_WINCE3::getSystemLanguage() const {
#ifdef USE_DETECTLANG
// We can not use "setlocale" (at least not for MSVC builds), since it
// will return locales like: "English_USA.1252", thus we need a special
// way to determine the locale string for Win32.
char langName[9];
char ctryName[9];
TCHAR langNameW[32];
TCHAR ctryNameW[32];
int i = 0;
bool localeFound = false;
Common::String localeName;
// Really not nice, but the only way to map Windows CE language/country codes to posix NLS names,
// because Windows CE doesn't support LOCALE_SISO639LANGNAME and LOCALE_SISO3166CTRYNAME,
// according to this: http://msdn.microsoft.com/en-us/library/aa912934.aspx
//
// See http://msdn.microsoft.com/en-us/goglobal/bb896001.aspx for a translation table
// This table has to be updated manually when new translations are added
const char *posixMappingTable[][3] = {
{"CAT", "ESP", "ca_ES"},
{"CSY", "CZE", "cs_CZ"},
{"DAN", "DNK", "da_DA"},
{"DEU", "DEU", "de_DE"},
{"ESN", "ESP", "es_ES"},
{"ESP", "ESP", "es_ES"},
{"FRA", "FRA", "fr_FR"},
{"HUN", "HUN", "hu_HU"},
{"ITA", "ITA", "it_IT"},
{"NOR", "NOR", "nb_NO"},
{"NON", "NOR", "nn_NO"},
{"PLK", "POL", "pl_PL"},
{"PTB", "BRA", "pt_BR"},
{"RUS", "RUS", "ru_RU"},
{"SVE", "SWE", "se_SE"},
{"UKR", "UKR", "uk_UA"},
{NULL, NULL, NULL}
};
if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, langNameW, sizeof(langNameW)) != 0 &&
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVCTRYNAME, ctryNameW, sizeof(ctryNameW)) != 0) {
WideCharToMultiByte(CP_ACP, 0, langNameW, -1, langName, (wcslen(langNameW) + 1), NULL, NULL);
WideCharToMultiByte(CP_ACP, 0, ctryNameW, -1, ctryName, (wcslen(ctryNameW) + 1), NULL, NULL);
debug(1, "Trying to find posix locale name for %s_%s", langName, ctryName);
while (posixMappingTable[i][0] && !localeFound) {
if ( (!strcmp(posixMappingTable[i][0], langName) || !strcmp(posixMappingTable[i][0], "*")) &&
(!strcmp(posixMappingTable[i][1], ctryName) || !strcmp(posixMappingTable[i][0], "*")) ) {
localeFound = true;
localeName = posixMappingTable[i][2];
}
i++;
}
if (!localeFound) warning("No posix locale name found for %s_%s", langName, ctryName);
}
if (localeFound) {
debug(1, "Found posix locale name: %s", localeName.c_str());
return localeName;
} else {
return ModularBackend::getSystemLanguage();
}
#else // USE_DETECTLANG
return ModularBackend::getSystemLanguage();
#endif // USE_DETECTLANG
}
int OSystem_WINCE3::_platformScreenWidth;
int OSystem_WINCE3::_platformScreenHeight;
bool OSystem_WINCE3::_isOzone;