diff options
author | Johannes Schickel | 2013-07-04 23:30:22 +0200 |
---|---|---|
committer | Johannes Schickel | 2013-07-04 23:33:02 +0200 |
commit | be61cb669a5d6de733c9a42b3714de3750e5f2e3 (patch) | |
tree | 0d83ab94ac7895c1a5f7f75a623a5be135230d2d /backends/platform/tizen | |
parent | d089658fb84f67652e9569bc03c76bcc65e7b881 (diff) | |
parent | be399188c4ec25463c4a777a5e96fd69a4b0b1e3 (diff) | |
download | scummvm-rg350-be61cb669a5d6de733c9a42b3714de3750e5f2e3.tar.gz scummvm-rg350-be61cb669a5d6de733c9a42b3714de3750e5f2e3.tar.bz2 scummvm-rg350-be61cb669a5d6de733c9a42b3714de3750e5f2e3.zip |
Merge pull request #350 from chrisws/tizen_port_1_6_0a
TIZEN: bada port updated to tizen
Conflicts:
backends/platform/tizen/system.cpp
Diffstat (limited to 'backends/platform/tizen')
-rw-r--r-- | backends/platform/tizen/README.TXT | 108 | ||||
-rw-r--r-- | backends/platform/tizen/application.cpp | 140 | ||||
-rw-r--r-- | backends/platform/tizen/application.h | 69 | ||||
-rw-r--r-- | backends/platform/tizen/audio.cpp | 181 | ||||
-rw-r--r-- | backends/platform/tizen/audio.h | 74 | ||||
-rw-r--r-- | backends/platform/tizen/form.cpp | 419 | ||||
-rw-r--r-- | backends/platform/tizen/form.h | 113 | ||||
-rw-r--r-- | backends/platform/tizen/fs.cpp | 441 | ||||
-rw-r--r-- | backends/platform/tizen/fs.h | 98 | ||||
-rw-r--r-- | backends/platform/tizen/graphics.cpp | 237 | ||||
-rw-r--r-- | backends/platform/tizen/graphics.h | 73 | ||||
-rw-r--r-- | backends/platform/tizen/main.cpp | 51 | ||||
-rw-r--r-- | backends/platform/tizen/missing.cpp | 82 | ||||
-rw-r--r-- | backends/platform/tizen/portdefs.h | 85 | ||||
-rw-r--r-- | backends/platform/tizen/sscanf.cpp | 213 | ||||
-rw-r--r-- | backends/platform/tizen/system.cpp | 547 | ||||
-rw-r--r-- | backends/platform/tizen/system.h | 102 | ||||
-rw-r--r-- | backends/platform/tizen/tizen.mk | 7 |
18 files changed, 3040 insertions, 0 deletions
diff --git a/backends/platform/tizen/README.TXT b/backends/platform/tizen/README.TXT new file mode 100644 index 0000000000..def3da2cce --- /dev/null +++ b/backends/platform/tizen/README.TXT @@ -0,0 +1,108 @@ +Build instructions (using linux) + +1. Install the Tizen SDK + +http://www.tizen.org + +To use an alternative Java SDK to run the Tizen IDE (eclipse), edit ~/.profile + +export JAVA_HOME=/opt/jdk1.6.0_45 +export PATH=${PATH}:${JAVA_HOME}/bin + +2. Add the following to your ~/.bashrc file + +export TIZEN_SDK=${HOME}/tizen-sdk +export TIZEN_ROOTSTRAP=${TIZEN_SDK}/platforms/tizen2.1/rootstraps/tizen-device-2.1.native +export TIZEN_BIN=${TIZEN_SDK}/tools/arm-linux-gnueabi-gcc-4.5/bin +export TIZEN_LIBS=${HOME}/tizen-lib +export PATH=${PATH}:${TIZEN_BIN}:~/bin +export CHOST=arm-linux-gnueabi +export LDFLAGS="--sysroot=${TIZEN_ROOTSTRAP} -L${TIZEN_LIBS}/lib" +export CPPFLAGS="--sysroot=${TIZEN_ROOTSTRAP} -fmessage-length=0 -fPIC\ + -I${TIZEN_ROOTSTRAP}/usr/include -I${TIZEN_LIBS}/include" +export CFLAGS=${CPPFLAGS} + +3. Build dependencies + + See: "Building the libraries" under: + http://wiki.scummvm.org/index.php/Compiling_ScummVM/MinGW#Building_the_libraries + for instructions on how to obtain these modules + + 3.1 zlib + + $ ./configure --static --prefix=${TIZEN_LIBS} + $ make && make install + + 3.2 freetype, libtheora, libogg, libvorbis, libmad, FLAC + + $ ./configure --host=arm-linux-gnueabi --prefix=${TIZEN_LIBS} --disable-shared + $ make && make install + + Note: you can ignore the ranlib errors when doing make install. + + Modify the resulting ~/tizen-lib/bin/freetype-config file to include -lz when printing libs + + 3.3 Linker ordering: scummvm, freetype, theoradec, vorbis, vorbisfile, mad, FLAC, ogg, z + +4. Build the ScummVM base library: + + ./configure --host=tizen --enable-release --with-freetype2-prefix=${TIZEN_LIBS}/bin + + For development: + + ./configure --host=tizen --enable-verbose-build --enable-debug + +5. Build the front end application using Tizen IDE + + Copy the scummvm/dists/bada folder into a clean directory + outside of the scummvm package. Start the BADA IDE then + choose this folder as the eclipse workspace. Click + Project / Build. + +Links: + +A short turorial on implementing OpenGL ES 1.1 in BADA: + http://forums.badadev.com/viewtopic.php?f=7&t=208 + +HelvB14 font files: + http://www.cl.cam.ac.uk/~mgk25/ucs-fonts.html + http://www.cl.cam.ac.uk/~mgk25/download/ucs-fonts-75dpi100dpi.tar.gz + + Then run the following command: + $ ./ucs2any.pl 100dpi/helvB14.bdf MAPPINGS/8859-1.TXT iso8859-1 \ + MAPPINGS/8859-2.TXT iso8859-2 MAPPINGS/8859-3.TXT iso8859-3 + +===================================================================== +Archived build instruction for BADA/cygwin + +1. Install BADA SDK (requires free registration): + + http://developer.bada.com/apis/index.do + +2. Install Cygwin: + + http://www.cygwin.com/ + + Add the following to your cygwin .bash_profile: + + alias mmake=/cygdrive/c/MinGW/bin/mingw32-make.exe + export BADA_SDK=/cygdrive/c/bada/1.2.1 + export ARM_BIN=c:/bada/1.2.1/Tools/Toolchains/ARM/bin + export CPPFLAGS="-fpic -fshort-wchar -mcpu=cortex-a8 -mfpu=vfpv3 \ + -mfloat-abi=hard -mlittle-endian -mthumb-interwork -Wno-psabi \ + -fno-strict-aliasing -fno-short-enums" + export LDFLAGS="-nostdlib -lc-newlib -lm-newlib -LC:/bada/1.2.1/Model/Wave_LP1/Target" + #export PATH=${BADA_SDK}/Tools/Toolchains/Win32/bin:${PATH} + export PATH=${BADA_SDK}/Tools/Toolchains/ARM/bin:~/utils:${PATH} + alias gcc=${ARM_BIN}/arm-samsung-nucleuseabi-gcc.exe + alias ar=${ARM_BIN}/arm-samsung-nucleuseabi-ar.exe + + The following were added to ~/utils for zlib: + + ar: + #!/bin/sh + ${ARM_BIN}/arm-samsung-nucleuseabi-ar.exe $* + + gcc: + #!/bin/sh + ${ARM_BIN}/arm-samsung-nucleuseabi-gcc.exe $* diff --git a/backends/platform/tizen/application.cpp b/backends/platform/tizen/application.cpp new file mode 100644 index 0000000000..8236ebef67 --- /dev/null +++ b/backends/platform/tizen/application.cpp @@ -0,0 +1,140 @@ +/* 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. + * + */ + +#include "engines/engine.h" + +#include "backends/platform/tizen/form.h" +#include "backends/platform/tizen/system.h" +#include "backends/platform/tizen/application.h" + +Application *TizenScummVM::createInstance() { + logEntered(); + return new TizenScummVM(); +} + +TizenScummVM::TizenScummVM() : _appForm(0) { + logEntered(); +} + +TizenScummVM::~TizenScummVM() { + logEntered(); + if (g_system) { + TizenSystem *system = (TizenSystem *)g_system; + system->destroyBackend(); + delete system; + g_system = 0; + } +} + +bool TizenScummVM::OnAppInitialized(void) { + logEntered(); + _appForm->SetOrientation(Tizen::Ui::ORIENTATION_LANDSCAPE); + return true; +} + +bool TizenScummVM::OnAppWillTerminate(void) { + logEntered(); + return true; +} + +bool TizenScummVM::OnAppInitializing(AppRegistry &appRegistry) { + logEntered(); + _appForm = systemStart(this); + return (_appForm != NULL); +} + +bool TizenScummVM::OnAppTerminating(AppRegistry &appRegistry, bool forcedTermination) { + logEntered(); + return true; +} + +void TizenScummVM::OnUserEventReceivedN(RequestId requestId, IList *args) { + MessageBox messageBox; + int modalResult; + + logEntered(); + + if (requestId == USER_MESSAGE_EXIT) { + // normal program termination + Terminate(); + } else if (requestId == USER_MESSAGE_EXIT_ERR) { + // assertion failure termination + String *message = NULL; + if (args) { + message = (String *)args->GetAt(0); + } + if (!message) { + message = new String("Unknown error"); + } + messageBox.Construct(L"Oops...", *message, MSGBOX_STYLE_OK); + messageBox.ShowAndWait(modalResult); + Terminate(); + } else if (requestId == USER_MESSAGE_EXIT_ERR_CONFIG) { + // the config file was corrupted + messageBox.Construct(L"Config file corrupted", + L"Settings have been reverted, please restart.", MSGBOX_STYLE_OK); + messageBox.ShowAndWait(modalResult); + Terminate(); + } +} + +void TizenScummVM::OnForeground(void) { + logEntered(); + pauseGame(false); +} + +void TizenScummVM::OnBackground(void) { + logEntered(); + pauseGame(true); +} + +void TizenScummVM::OnBatteryLevelChanged(BatteryLevel batteryLevel) { + logEntered(); +} + +void TizenScummVM::OnLowMemory(void) { + logEntered(); +} + +void TizenScummVM::OnScreenOn(void) { + logEntered(); +} + +void TizenScummVM::OnScreenOff(void) { + logEntered(); +} + +void TizenScummVM::OnScreenBrightnessChanged(int brightness) { + logEntered(); +} + +void TizenScummVM::pauseGame(bool pause) { + if (_appForm) { + if (pause && g_engine && !g_engine->isPaused()) { + _appForm->pushKey(Common::KEYCODE_SPACE); + } + + if (g_system) { + ((TizenSystem *)g_system)->setMute(pause); + } + } +} diff --git a/backends/platform/tizen/application.h b/backends/platform/tizen/application.h new file mode 100644 index 0000000000..f18ccb175b --- /dev/null +++ b/backends/platform/tizen/application.h @@ -0,0 +1,69 @@ +/* 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. + * + */ + +#ifndef TIZEN_APPLICATION_H +#define TIZEN_APPLICATION_H + +#include <FBase.h> +#include <FApp.h> +#include <FGraphics.h> +#include <FUi.h> +#include <FSystem.h> + +#include "backends/platform/tizen/system.h" +#include "backends/platform/tizen/form.h" + +using namespace Tizen::App; +using namespace Tizen::System; +using namespace Tizen::Ui; +using namespace Tizen::Ui::Controls; +using namespace Tizen::Base::Collection; + +class TizenScummVM : + public UiApp, + public IScreenEventListener { + +public: + TizenScummVM(); + virtual ~TizenScummVM(); + + static UiApp *createInstance(void); + + virtual bool OnAppInitializing(AppRegistry &appRegistry); + virtual bool OnAppInitialized(void); + virtual bool OnAppWillTerminate(void); + virtual bool OnAppTerminating(AppRegistry &appRegistry, bool forcedTermination = false); + virtual void OnLowMemory(void); + virtual void OnBatteryLevelChanged(BatteryLevel batteryLevel); + virtual void OnUserEventReceivedN(RequestId requestId, IList *pArgs); + virtual void OnForeground(void); + virtual void OnBackground(void); + virtual void OnScreenOn(void); + virtual void OnScreenOff(void); + virtual void OnScreenBrightnessChanged(int brightness); + +private: + void pauseGame(bool pause); + TizenAppForm *_appForm; +}; + +#endif diff --git a/backends/platform/tizen/audio.cpp b/backends/platform/tizen/audio.cpp new file mode 100644 index 0000000000..313a10eaa8 --- /dev/null +++ b/backends/platform/tizen/audio.cpp @@ -0,0 +1,181 @@ +/* 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. + * + */ + +#include <FSysSettingInfo.h> +#include <FAppAppRegistry.h> + +#include "backends/platform/tizen/audio.h" +#include "backends/platform/tizen/system.h" + +#define TIMER_INTERVAL 10 +#define VOLUME 99 + +AudioThread::AudioThread() : + _mixer(0), + _timer(0), + _audioOut(0), + _head(0), + _tail(0), + _ready(0), + _interval(TIMER_INTERVAL), + _playing(-1), + _muted(true) { +} + +Audio::MixerImpl *AudioThread::Construct(OSystem *system) { + logEntered(); + + if (IsFailed(EventDrivenThread::Construct(DEFAULT_STACK_SIZE, THREAD_PRIORITY_HIGH))) { + AppLog("Failed to create AudioThread"); + return NULL; + } + + _mixer = new Audio::MixerImpl(system, 44100); + return _mixer; +} + +AudioThread::~AudioThread() { + logEntered(); +} + +bool AudioThread::isSilentMode() { + bool silentMode; + String key(L"SilentMode"); + Tizen::System::SettingInfo::GetValue(key, silentMode); + return silentMode; +} + +void AudioThread::setMute(bool on) { + if (_audioOut && _timer) { + _muted = on; + if (on) { + _timer->Cancel(); + } else { + _timer->StartAsRepeatable(_interval); + } + } +} + +bool AudioThread::OnStart(void) { + logEntered(); + + _audioOut = new Tizen::Media::AudioOut(); + if (!_audioOut || IsFailed(_audioOut->Construct(*this))) { + AppLog("Failed to create AudioOut"); + return false; + } + + int sampleRate = _mixer->getOutputRate(); + + // ideally we would update _mixer with GetOptimizedSampleRate here + if (_audioOut->GetOptimizedSampleRate() != sampleRate) { + AppLog("Non optimal sample rate %d", _audioOut->GetOptimizedSampleRate()); + } + + if (IsFailed(_audioOut->Prepare(AUDIO_TYPE_PCM_S16_LE, + AUDIO_CHANNEL_TYPE_STEREO, sampleRate))) { + AppLog("Failed to prepare AudioOut %d", sampleRate); + return false; + } + + int bufferSize = _audioOut->GetMinBufferSize(); + for (int i = 0; i < NUM_AUDIO_BUFFERS; i++) { + if (IsFailed(_audioBuffer[i].Construct(bufferSize))) { + AppLog("Failed to create audio buffer"); + return false; + } + } + + _timer = new Timer(); + if (!_timer || IsFailed(_timer->Construct(*this))) { + AppLog("Failed to create audio timer"); + return false; + } + + if (IsFailed(_timer->StartAsRepeatable(_interval))) { + AppLog("failed to start audio timer"); + return false; + } + + _muted = false; + _mixer->setReady(true); + _audioOut->SetVolume(isSilentMode() ? 0 : VOLUME); + _audioOut->Start(); + return true; +} + +void AudioThread::OnStop(void) { + logEntered(); + + _mixer->setReady(false); + + if (_timer) { + if (!_muted) { + _timer->Cancel(); + } + delete _timer; + } + + if (_audioOut) { + _audioOut->Reset(); + delete _audioOut; + } +} + +void AudioThread::OnAudioOutErrorOccurred(Tizen::Media::AudioOut &src, result r) { + logEntered(); +} + +void AudioThread::OnAudioOutInterrupted(Tizen::Media::AudioOut &src) { + logEntered(); +} + +void AudioThread::OnAudioOutReleased(Tizen::Media::AudioOut &src) { + logEntered(); + _audioOut->Start(); +} + +void AudioThread::OnAudioOutBufferEndReached(Tizen::Media::AudioOut &src) { + if (_ready > 0) { + _playing = _tail; + _audioOut->WriteBuffer(_audioBuffer[_tail]); + _tail = (_tail + 1) % NUM_AUDIO_BUFFERS; + _ready--; + } else { + // audio buffer empty: decrease timer inverval + _playing = -1; + } +} + +void AudioThread::OnTimerExpired(Timer &timer) { + if (_ready < NUM_AUDIO_BUFFERS) { + uint len = _audioBuffer[_head].GetCapacity(); + int samples = _mixer->mixCallback((byte *)_audioBuffer[_head].GetPointer(), len); + if (samples) { + _head = (_head + 1) % NUM_AUDIO_BUFFERS; + _ready++; + } + } + if (_ready && _playing == -1) { + OnAudioOutBufferEndReached(*_audioOut); + } +} diff --git a/backends/platform/tizen/audio.h b/backends/platform/tizen/audio.h new file mode 100644 index 0000000000..8d7835042d --- /dev/null +++ b/backends/platform/tizen/audio.h @@ -0,0 +1,74 @@ +/* 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. + * + */ + +#ifndef TIZEN_AUDIO_H +#define TIZEN_AUDIO_H + +#include <FBase.h> +#include <FMedia.h> +#include <FIo.h> +#include <FBaseByteBuffer.h> + +#include "config.h" +#include "common/scummsys.h" +#include "common/system.h" +#include "audio/mixer_intern.h" + +using namespace Tizen::Base; +using namespace Tizen::Base::Collection; +using namespace Tizen::Base::Runtime; +using namespace Tizen::Media; +using namespace Tizen::Io; + +#define NUM_AUDIO_BUFFERS 2 + +class AudioThread: + public Tizen::Media::IAudioOutEventListener, + public Tizen::Base::Runtime::ITimerEventListener, + public Tizen::Base::Runtime::EventDrivenThread { + +public: + AudioThread(void); + ~AudioThread(void); + + Audio::MixerImpl *Construct(OSystem *system); + bool isSilentMode(); + void setMute(bool on); + + bool OnStart(void); + void OnStop(void); + void OnAudioOutErrorOccurred(Tizen::Media::AudioOut &src, result r); + void OnAudioOutInterrupted(Tizen::Media::AudioOut &src); + void OnAudioOutReleased(Tizen::Media::AudioOut &src); + void OnAudioOutBufferEndReached(Tizen::Media::AudioOut &src); + void OnTimerExpired(Timer &timer); + +private: + Audio::MixerImpl *_mixer; + Tizen::Base::Runtime::Timer *_timer; + Tizen::Media::AudioOut *_audioOut; + Tizen::Base::ByteBuffer _audioBuffer[NUM_AUDIO_BUFFERS]; + int _head, _tail, _ready, _interval, _playing; + bool _muted; +}; + +#endif diff --git a/backends/platform/tizen/form.cpp b/backends/platform/tizen/form.cpp new file mode 100644 index 0000000000..cce4b99557 --- /dev/null +++ b/backends/platform/tizen/form.cpp @@ -0,0 +1,419 @@ +/* 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. + * + */ + +#include <FApp.h> +#include <FSysSystemTime.h> + +#include "common/translation.h" +#include "base/main.h" + +#include "backends/platform/tizen/form.h" +#include "backends/platform/tizen/system.h" + +using namespace Tizen::Base::Collection; +using namespace Tizen::Base::Runtime; +using namespace Tizen::Ui::Controls; + +// round down small Y touch values to 1 to allow the +// cursor to be positioned at the top of the screen +#define MIN_TOUCH_Y 20 + +// block for up to 2.5 seconds during shutdown to +// allow the game thread to exit gracefully. +#define EXIT_SLEEP_STEP 10 +#define EXIT_SLEEP 250 + +// +// TizenAppForm +// +TizenAppForm::TizenAppForm() : + _gestureMode(false), + _osdMessage(NULL), + _gameThread(NULL), + _eventQueueLock(NULL), + _state(kInitState), + _buttonState(kLeftButton), + _shortcut(kShowKeypad) { +} + +result TizenAppForm::Construct() { + TizenSystem *tizenSystem = NULL; + result r = Form::Construct(FORM_STYLE_NORMAL); + if (!IsFailed(r)) { + tizenSystem = new TizenSystem(this); + r = tizenSystem != NULL ? E_SUCCESS : E_OUT_OF_MEMORY; + } + if (!IsFailed(r)) { + r = tizenSystem->Construct(); + } + if (!IsFailed(r)) { + _gameThread = new Thread(); + r = _gameThread != NULL ? E_SUCCESS : E_OUT_OF_MEMORY; + } + if (!IsFailed(r)) { + r = _gameThread->Construct(*this); + } + if (!IsFailed(r)) { + _eventQueueLock = new Mutex(); + r = _eventQueueLock != NULL ? E_SUCCESS : E_OUT_OF_MEMORY; + } + if (!IsFailed(r)) { + r = _eventQueueLock->Create(); + } + + if (!IsFailed(r)) { + g_system = tizenSystem; + } else { + AppLog("Form startup failed"); + delete tizenSystem; + delete _gameThread; + _gameThread = NULL; + } + return r; +} + +TizenAppForm::~TizenAppForm() { + logEntered(); + + if (_gameThread && _state != kErrorState) { + terminate(); + + _gameThread->Stop(); + if (_state != kErrorState) { + _gameThread->Join(); + } + + delete _gameThread; + _gameThread = NULL; + } + + delete _eventQueueLock; + _eventQueueLock = NULL; + + logLeaving(); +} + +// +// abort the game thread +// +void TizenAppForm::terminate() { + if (_state == kActiveState) { + ((TizenSystem *)g_system)->setMute(true); + + _eventQueueLock->Acquire(); + + Common::Event e; + e.type = Common::EVENT_QUIT; + _eventQueue.push(e); + _state = kClosingState; + + _eventQueueLock->Release(); + + // block while thread ends + AppLog("waiting for shutdown"); + for (int i = 0; i < EXIT_SLEEP_STEP && _state == kClosingState; i++) { + Thread::Sleep(EXIT_SLEEP); + } + + if (_state == kClosingState) { + // failed to terminate - Join() will freeze + _state = kErrorState; + } + } +} + +void TizenAppForm::exitSystem() { + _state = kErrorState; + + if (_gameThread) { + _gameThread->Stop(); + delete _gameThread; + _gameThread = NULL; + } +} + +result TizenAppForm::OnInitializing(void) { + logEntered(); + + AddOrientationEventListener(*this); + AddTouchEventListener(*this); + SetMultipointTouchEnabled(true); + + // set focus to enable receiving key events + SetEnabled(true); + SetFocusable(true); + SetFocus(); + + return E_SUCCESS; +} + +result TizenAppForm::OnDraw(void) { + logEntered(); + return E_SUCCESS; +} + +void TizenAppForm::OnOrientationChanged(const Control &source, OrientationStatus orientationStatus) { + logEntered(); + if (_state == kInitState) { + _state = kActiveState; + _gameThread->Start(); + } +} + +Tizen::Base::Object *TizenAppForm::Run() { + logEntered(); + + scummvm_main(0, 0); + if (_state == kActiveState) { + Tizen::App::Application::GetInstance()->SendUserEvent(USER_MESSAGE_EXIT, NULL); + } + _state = kDoneState; + return NULL; +} + +bool TizenAppForm::pollEvent(Common::Event &event) { + bool result = false; + + _eventQueueLock->Acquire(); + if (!_eventQueue.empty()) { + event = _eventQueue.pop(); + result = true; + } + if (_osdMessage) { + TizenSystem *system = (TizenSystem *)g_system; + TizenGraphicsManager *graphics = system->getGraphics(); + if (graphics) { + graphics->displayMessageOnOSD(_osdMessage); + _osdMessage = NULL; + } + } + _eventQueueLock->Release(); + + return result; +} + +void TizenAppForm::pushEvent(Common::EventType type, const Point ¤tPosition) { + TizenSystem *system = (TizenSystem *)g_system; + TizenGraphicsManager *graphics = system->getGraphics(); + if (graphics) { + // graphics could be NULL at startup or when + // displaying the system error screen + Common::Event e; + e.type = type; + e.mouse.x = currentPosition.x; + e.mouse.y = currentPosition.y > MIN_TOUCH_Y ? currentPosition.y : 1; + + bool moved = graphics->moveMouse(e.mouse.x, e.mouse.y); + + _eventQueueLock->Acquire(); + + if (moved && type != Common::EVENT_MOUSEMOVE) { + Common::Event moveEvent; + moveEvent.type = Common::EVENT_MOUSEMOVE; + moveEvent.mouse = e.mouse; + _eventQueue.push(moveEvent); + } + + _eventQueue.push(e); + _eventQueueLock->Release(); + } +} + +void TizenAppForm::pushKey(Common::KeyCode keycode) { + if (_eventQueueLock) { + Common::Event e; + e.synthetic = false; + e.kbd.keycode = keycode; + e.kbd.ascii = keycode; + e.kbd.flags = 0; + + _eventQueueLock->Acquire(); + e.type = Common::EVENT_KEYDOWN; + _eventQueue.push(e); + e.type = Common::EVENT_KEYUP; + _eventQueue.push(e); + _eventQueueLock->Release(); + } +} + +void TizenAppForm::setButtonShortcut() { + switch (_buttonState) { + case kLeftButton: + setMessage(_s("Right Click Once")); + _buttonState = kRightButtonOnce; + break; + case kRightButtonOnce: + setMessage(_s("Right Click")); + _buttonState = kRightButton; + break; + case kRightButton: + setMessage(_s("Move Only")); + _buttonState = kMoveOnly; + break; + case kMoveOnly: + setMessage(_s("Left Click")); + _buttonState = kLeftButton; + break; + } +} + +void TizenAppForm::setMessage(const char *message) { + if (_eventQueueLock) { + _eventQueueLock->Acquire(); + _osdMessage = message; + _eventQueueLock->Release(); + } +} + +void TizenAppForm::setShortcut() { + logEntered(); + // cycle to the next shortcut + switch (_shortcut) { + case kControlMouse: + setMessage(_s("Escape Key")); + _shortcut = kEscapeKey; + break; + + case kEscapeKey: + setMessage(_s("Game Menu")); + _shortcut = kGameMenu; + break; + + case kGameMenu: + setMessage(_s("Show Keypad")); + _shortcut = kShowKeypad; + break; + + case kShowKeypad: + setMessage(_s("Control Mouse")); + _shortcut = kControlMouse; + break; + } +} + +void TizenAppForm::invokeShortcut() { + logEntered(); + switch (_shortcut) { + case kControlMouse: + setButtonShortcut(); + break; + + case kEscapeKey: + pushKey(Common::KEYCODE_ESCAPE); + break; + + case kGameMenu: + _buttonState = kLeftButton; + pushKey(Common::KEYCODE_F5); + break; + + case kShowKeypad: + showKeypad(); + break; + } +} + +void TizenAppForm::showKeypad() { + // display the soft keyboard + if (_state == kActiveState) { + _buttonState = kLeftButton; + pushKey(Common::KEYCODE_F7); + } +} + +int TizenAppForm::getTouchCount() { + Tizen::Ui::TouchEventManager *touch = Tizen::Ui::TouchEventManager::GetInstance(); + IListT<TouchEventInfo *> *touchList = touch->GetTouchInfoListN(); + int touchCount = touchList->GetCount(); + touchList->RemoveAll(); + delete touchList; + return touchCount; +} + +void TizenAppForm::OnTouchDoublePressed(const Control &source, + const Point ¤tPosition, const TouchEventInfo &touchInfo) { + if (_buttonState != kMoveOnly) { + pushEvent(_buttonState == kLeftButton ? Common::EVENT_LBUTTONDOWN : Common::EVENT_RBUTTONDOWN, + currentPosition); + pushEvent(_buttonState == kLeftButton ? Common::EVENT_LBUTTONDOWN : Common::EVENT_RBUTTONDOWN, + currentPosition); + } +} + +void TizenAppForm::OnTouchFocusIn(const Control &source, + const Point ¤tPosition, const TouchEventInfo &touchInfo) { +} + +void TizenAppForm::OnTouchFocusOut(const Control &source, + const Point ¤tPosition, const TouchEventInfo &touchInfo) { +} + +void TizenAppForm::OnTouchLongPressed(const Control &source, + const Point ¤tPosition, const TouchEventInfo &touchInfo) { + logEntered(); + if (_buttonState != kLeftButton) { + pushKey(Common::KEYCODE_RETURN); + } +} + +void TizenAppForm::OnTouchMoved(const Control &source, + const Point ¤tPosition, const TouchEventInfo &touchInfo) { + if (!_gestureMode) { + pushEvent(Common::EVENT_MOUSEMOVE, currentPosition); + } +} + +void TizenAppForm::OnTouchPressed(const Control &source, + const Point ¤tPosition, const TouchEventInfo &touchInfo) { + if (getTouchCount() > 1) { + _gestureMode = true; + } else if (_buttonState != kMoveOnly) { + pushEvent(_buttonState == kLeftButton ? Common::EVENT_LBUTTONDOWN : Common::EVENT_RBUTTONDOWN, + currentPosition); + } +} + +void TizenAppForm::OnTouchReleased(const Control &source, + const Point ¤tPosition, const TouchEventInfo &touchInfo) { + if (_gestureMode) { + int touchCount = getTouchCount(); + if (touchCount == 1) { + setShortcut(); + } else { + if (touchCount == 2) { + invokeShortcut(); + } + _gestureMode = false; + } + } else if (_buttonState != kMoveOnly) { + pushEvent(_buttonState == kLeftButton ? Common::EVENT_LBUTTONUP : Common::EVENT_RBUTTONUP, + currentPosition); + if (_buttonState == kRightButtonOnce) { + _buttonState = kLeftButton; + } + // flick to skip dialog + if (touchInfo.IsFlicked()) { + pushKey(Common::KEYCODE_PERIOD); + } + } +} + diff --git a/backends/platform/tizen/form.h b/backends/platform/tizen/form.h new file mode 100644 index 0000000000..64c447d409 --- /dev/null +++ b/backends/platform/tizen/form.h @@ -0,0 +1,113 @@ +/* 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. + * + */ + +#ifndef TIZEN_FORM_H +#define TIZEN_FORM_H + +#include <FApp.h> +#include <FUi.h> +#include <FSystem.h> +#include <FBase.h> +#include <FUiITouchEventListener.h> +#include <FUiITextEventListener.h> + +#include "config.h" +#include "common/scummsys.h" +#include "common/events.h" +#include "common/queue.h" +#include "common/mutex.h" +#include "engines/engine.h" + +using namespace Tizen::Ui; +using namespace Tizen::Graphics; +using namespace Tizen::Base::Runtime; + +// +// TizenAppForm +// +class TizenAppForm : + public Controls::Form, + public IRunnable, + public IOrientationEventListener, + public ITouchEventListener { + +public: + TizenAppForm(); + virtual ~TizenAppForm(); + + result Construct(); + bool pollEvent(Common::Event &event); + bool isClosing() { return _state == kClosingState; } + bool isStarting() { return _state == kInitState; } + void pushKey(Common::KeyCode keycode); + void exitSystem(); + void showKeypad(); + +private: + Tizen::Base::Object *Run(); + result OnInitializing(void); + result OnDraw(void); + void OnOrientationChanged(const Control &source, + OrientationStatus orientationStatus); + void OnTouchDoublePressed(const Control &source, + const Point ¤tPosition, + const TouchEventInfo &touchInfo); + void OnTouchFocusIn(const Control &source, + const Point ¤tPosition, + const TouchEventInfo &touchInfo); + void OnTouchFocusOut(const Control &source, + const Point ¤tPosition, + const TouchEventInfo &touchInfo); + void OnTouchLongPressed(const Control &source, + const Point ¤tPosition, + const TouchEventInfo &touchInfo); + void OnTouchMoved(const Control &source, + const Point ¤tPosition, + const TouchEventInfo &touchInfo); + void OnTouchPressed(const Control &source, + const Point ¤tPosition, + const TouchEventInfo &touchInfo); + void OnTouchReleased(const Control &source, + const Point ¤tPosition, + const TouchEventInfo &touchInfo); + + void pushEvent(Common::EventType type, const Point ¤tPosition); + void terminate(); + void setButtonShortcut(); + void setMessage(const char *message); + void setShortcut(); + void invokeShortcut(); + int getTouchCount(); + bool gameActive() { return _state == kActiveState && g_engine != NULL && !g_engine->isPaused(); } + + // event handling + bool _gestureMode; + const char *_osdMessage; + Tizen::Base::Runtime::Thread *_gameThread; + Tizen::Base::Runtime::Mutex *_eventQueueLock; + Common::Queue<Common::Event> _eventQueue; + enum { kInitState, kActiveState, kClosingState, kDoneState, kErrorState } _state; + enum { kLeftButton, kRightButtonOnce, kRightButton, kMoveOnly } _buttonState; + enum { kControlMouse, kEscapeKey, kGameMenu, kShowKeypad } _shortcut; +}; + +#endif diff --git a/backends/platform/tizen/fs.cpp b/backends/platform/tizen/fs.cpp new file mode 100644 index 0000000000..f8b32f4239 --- /dev/null +++ b/backends/platform/tizen/fs.cpp @@ -0,0 +1,441 @@ +/* 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. + */ + +#include "config.h" +#include "common/translation.h" +#include "backends/platform/tizen/system.h" +#include "backends/platform/tizen/fs.h" + +#include <FAppApp.h> + +#define BUFFER_SIZE 1024 + +using namespace Tizen::App; + +// +// converts a Tizen (wchar) String into a scummVM (char) string +// +Common::String fromString(const Tizen::Base::String &in) { + ByteBuffer *buf = StringUtil::StringToUtf8N(in); + Common::String result((const char*)buf->GetPointer()); + delete buf; + return result; +} + +// +// TizenFileStream +// +class TizenFileStream : + public Common::SeekableReadStream, + public Common::WriteStream, + public Common::NonCopyable { +public: + static TizenFileStream *makeFromPath(const String &path, bool writeMode); + + TizenFileStream(File *file, bool writeMode); + ~TizenFileStream(); + + bool err() const; + void clearErr(); + bool eos() const; + + uint32 write(const void *dataPtr, uint32 dataSize); + bool flush(); + + int32 pos() const; + int32 size() const; + bool seek(int32 offs, int whence = SEEK_SET); + uint32 read(void *dataPtr, uint32 dataSize); + +private: + byte _buffer[BUFFER_SIZE]; + uint32 _bufferIndex; + uint32 _bufferLength; + bool _writeMode; + File *_file; +}; + +TizenFileStream::TizenFileStream(File *ioFile, bool writeMode) : + _bufferIndex(0), + _bufferLength(0), + _writeMode(writeMode), + _file(ioFile) { + AppAssert(ioFile != 0); +} + +TizenFileStream::~TizenFileStream() { + if (_file) { + if (_writeMode) { + flush(); + } + delete _file; + } +} + +bool TizenFileStream::err() const { + result r = GetLastResult(); + return (r != E_SUCCESS && r != E_END_OF_FILE); +} + +void TizenFileStream::clearErr() { + SetLastResult(E_SUCCESS); +} + +bool TizenFileStream::eos() const { + return (_bufferLength - _bufferIndex == 0) && (GetLastResult() == E_END_OF_FILE); +} + +int32 TizenFileStream::pos() const { + return _file->Tell() - (_bufferLength - _bufferIndex); +} + +int32 TizenFileStream::size() const { + int32 oldPos = _file->Tell(); + _file->Seek(FILESEEKPOSITION_END, 0); + + int32 length = _file->Tell(); + SetLastResult(_file->Seek(FILESEEKPOSITION_BEGIN, oldPos)); + + return length; +} + +bool TizenFileStream::seek(int32 offs, int whence) { + bool result = false; + switch (whence) { + case SEEK_SET: + // set from start of file + SetLastResult(_file->Seek(FILESEEKPOSITION_BEGIN, offs)); + result = (E_SUCCESS == GetLastResult()); + break; + + case SEEK_CUR: + // set relative to offs + if (_bufferIndex < _bufferLength && _bufferIndex > (uint32)-offs) { + // re-position within the buffer + SetLastResult(E_SUCCESS); + _bufferIndex += offs; + return true; + } else { + offs -= (_bufferLength - _bufferIndex); + if (offs < 0 && _file->Tell() + offs < 0) { + // avoid negative positioning + offs = 0; + } + if (offs != 0) { + SetLastResult(_file->Seek(FILESEEKPOSITION_CURRENT, offs)); + result = (E_SUCCESS == GetLastResult()); + } else { + result = true; + } + } + break; + + case SEEK_END: + // set relative to end - positive will increase the file size + SetLastResult(_file->Seek(FILESEEKPOSITION_END, offs)); + result = (E_SUCCESS == GetLastResult()); + break; + + default: + AppLog("Invalid whence %d", whence); + return false; + } + + if (!result) { + AppLog("seek failed"); + } + + _bufferIndex = _bufferLength = 0; + return result; +} + +uint32 TizenFileStream::read(void *ptr, uint32 len) { + uint32 result = 0; + if (!eos()) { + if (_bufferIndex < _bufferLength) { + // use existing buffer + uint32 available = _bufferLength - _bufferIndex; + if (len <= available) { + // use allocation + memcpy((byte *)ptr, &_buffer[_bufferIndex], len); + _bufferIndex += len; + result = len; + } else { + // use remaining allocation + memcpy((byte *)ptr, &_buffer[_bufferIndex], available); + uint32 remaining = len - available; + result = available; + + if (remaining) { + result += _file->Read(((byte *)ptr) + available, remaining); + } + _bufferIndex = _bufferLength = 0; + } + } else if (len < BUFFER_SIZE) { + // allocate and use buffer + _bufferIndex = 0; + _bufferLength = _file->Read(_buffer, BUFFER_SIZE); + if (_bufferLength) { + if (_bufferLength < len) { + len = _bufferLength; + } + memcpy((byte *)ptr, _buffer, len); + result = _bufferIndex = len; + } + } else { + result = _file->Read((byte *)ptr, len); + _bufferIndex = _bufferLength = 0; + } + } else { + AppLog("Attempted to read past EOS"); + } + return result; +} + +uint32 TizenFileStream::write(const void *ptr, uint32 len) { + result r = _file->Write(ptr, len); + SetLastResult(r); + return (r == E_SUCCESS ? len : 0); +} + +bool TizenFileStream::flush() { + logEntered(); + SetLastResult(_file->Flush()); + return (E_SUCCESS == GetLastResult()); +} + +TizenFileStream *TizenFileStream::makeFromPath(const String &path, bool writeMode) { + File *ioFile = new File(); + + String filePath = path; + if (writeMode && (path[0] != '.' && path[0] != '/')) { + filePath.Insert(App::GetInstance()->GetAppDataPath() + L"/", 0); + } + + AppLog("Open file %S", filePath.GetPointer()); + TizenFileStream *stream; + result r = ioFile->Construct(filePath, writeMode ? L"w" : L"r", writeMode); + if (r == E_SUCCESS) { + stream = new TizenFileStream(ioFile, writeMode); + } else { + AppLog("Failed to open file"); + delete ioFile; + stream = NULL; + } + return stream; +} + +// +// TizenFilesystemNode +// +TizenFilesystemNode::TizenFilesystemNode(const Common::String &nodePath) { + AppAssert(nodePath.size() > 0); + init(nodePath); +} + +TizenFilesystemNode::TizenFilesystemNode(SystemPath systemPath) { + switch (systemPath) { + case kData: + _unicodePath = App::GetInstance()->GetAppDataPath(); + _displayName = _s("[ Data ]"); + break; + case kResource: + _unicodePath = App::GetInstance()->GetAppResourcePath(); + _displayName = _s("[ Resources ]"); + break; + case kSdCard: + _unicodePath = Tizen::System::Environment::GetExternalStoragePath(); + _displayName = _s("[ SDCard ]"); + break; + case kMedia: + _unicodePath = Tizen::System::Environment::GetMediaPath(); + _displayName = _s("[ Media ]"); + break; + case kShared: + _unicodePath = App::GetInstance()->GetAppSharedPath(); + _displayName = _s("[ Shared ]"); + break; + } + _path = ::fromString(_unicodePath); + _isValid = _isVirtualDir = !IsFailed(File::GetAttributes(_unicodePath, _attr)); +} + +TizenFilesystemNode::TizenFilesystemNode(const Common::String &root, const Common::String &nodePath) { + AppLog("TizenFilesystemNode '%s' '%s'", root.c_str(), nodePath.c_str()); + + // Make sure the string contains no slashes + AppAssert(!nodePath.contains('/')); + + // We assume here that path is already normalized (hence don't bother to + // call Common::normalizePath on the final path). + Common::String newPath(root); + if (root.lastChar() != '/') { + newPath += '/'; + } + newPath += nodePath; + + init(newPath); +} + +void TizenFilesystemNode::init(const Common::String &nodePath) { + // Normalize the path (that is, remove unneeded slashes etc.) + _path = Common::normalizePath(nodePath, '/'); + _displayName = Common::lastPathComponent(_path, '/'); + + StringUtil::Utf8ToString(_path.c_str(), _unicodePath); + _isVirtualDir = (_path == "/"); + _isValid = _isVirtualDir || !IsFailed(File::GetAttributes(_unicodePath, _attr)); +} + +bool TizenFilesystemNode::exists() const { + return _isValid; +} + +bool TizenFilesystemNode::isReadable() const { + return _isVirtualDir || _isValid; +} + +bool TizenFilesystemNode::isDirectory() const { + return _isVirtualDir || (_isValid && _attr.IsDirectory()); +} + +bool TizenFilesystemNode::isWritable() const { + bool result = (_isValid && !_attr.IsReadOnly()); + if (_unicodePath == App::GetInstance()->GetAppResourcePath()) { + result = false; + } + return result; +} + +AbstractFSNode *TizenFilesystemNode::getChild(const Common::String &n) const { + AppAssert(!_path.empty()); + AppAssert(isDirectory()); + return new TizenFilesystemNode(_path, n); +} + +bool TizenFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const { + AppAssert(isDirectory()); + + bool result = false; + + if (_isVirtualDir && mode != Common::FSNode::kListFilesOnly && _path == "/") { + // present well known TIZEN file system areas + myList.push_back(new TizenFilesystemNode(kData)); + myList.push_back(new TizenFilesystemNode(kResource)); + myList.push_back(new TizenFilesystemNode(kSdCard)); + myList.push_back(new TizenFilesystemNode(kMedia)); + myList.push_back(new TizenFilesystemNode(kShared)); + } + + if (!result) { + DirEnumerator *pDirEnum = 0; + Directory *pDir = new Directory(); + + // open directory + if (IsFailed(pDir->Construct(_unicodePath))) { + AppLog("Failed to open directory: %S", _unicodePath.GetPointer()); + } else { + // read all directory entries + pDirEnum = pDir->ReadN(); + if (pDirEnum) { + result = true; + } + + // loop through all directory entries + while (pDirEnum && pDirEnum->MoveNext() == E_SUCCESS) { + DirEntry dirEntry = pDirEnum->GetCurrentDirEntry(); + + // skip 'invisible' files if necessary + Tizen::Base::String fileName = dirEntry.GetName(); + + if (fileName[0] == '.' && !hidden) { + continue; + } + + // skip '.' and '..' to avoid cycles + if (fileName == L"." || fileName == L"..") { + continue; + } + + // Honor the chosen mode + if ((mode == Common::FSNode::kListFilesOnly && dirEntry.IsDirectory()) || + (mode == Common::FSNode::kListDirectoriesOnly && !dirEntry.IsDirectory())) { + continue; + } + myList.push_back(new TizenFilesystemNode(_path, ::fromString(fileName))); + } + } + + // cleanup + if (pDirEnum) { + delete pDirEnum; + } + + // close the opened directory + if (pDir) { + delete pDir; + } + } + + return result; +} + +AbstractFSNode *TizenFilesystemNode::getParent() const { + logEntered(); + if (_path == "/") { + return 0; // The filesystem root has no parent + } + + const char *start = _path.c_str(); + const char *end = start + _path.size(); + + // Strip of the last component. We make use of the fact that at this + // point, path is guaranteed to be normalized + while (end > start && *(end-1) != '/') { + end--; + } + + if (end == start) { + // This only happens if we were called with a relative path, for which + // there simply is no parent. + // TODO: We could also resolve this by assuming that the parent is the + // current working directory, and returning a node referring to that. + return NULL; + } + + return new TizenFilesystemNode(Common::String(start, end)); +} + +Common::SeekableReadStream *TizenFilesystemNode::createReadStream() { + Common::SeekableReadStream *result = TizenFileStream::makeFromPath(_unicodePath, false); + if (result != NULL) { + _isValid = !IsFailed(File::GetAttributes(_unicodePath, _attr)); + } + return result; +} + +Common::WriteStream *TizenFilesystemNode::createWriteStream() { + Common::WriteStream *result = TizenFileStream::makeFromPath(_unicodePath, true); + if (result != NULL) { + _isValid = !IsFailed(File::GetAttributes(_unicodePath, _attr)); + } + return result; +} diff --git a/backends/platform/tizen/fs.h b/backends/platform/tizen/fs.h new file mode 100644 index 0000000000..0356aaad33 --- /dev/null +++ b/backends/platform/tizen/fs.h @@ -0,0 +1,98 @@ +/* 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. + */ + +#ifndef TIZEN_FILESYSTEM_H +#define TIZEN_FILESYSTEM_H + +#include <FBaseString.h> +#include <FBaseUtilStringUtil.h> +#include <FIoDirectory.h> +#include <FIoFile.h> + +#include "config.h" +#include "common/scummsys.h" +#include "common/stream.h" +#include "backends/fs/abstract-fs.h" + +using namespace Tizen::Io; +using namespace Tizen::Base; +using namespace Tizen::Base::Utility; + +// +// converts a Tizen (wchar) String into a scummVM (char) string +// +Common::String fromString(const Tizen::Base::String &in); + +// +// Enumerates the possible system paths +// +enum SystemPath { kData, kResource, kSdCard, kMedia, kShared }; + +/** + * Implementation of the ScummVM file system API based on TIZEN. + * + * Parts of this class are documented in the base interface class, AbstractFSNode. + */ +class TizenFilesystemNode : public AbstractFSNode { +public: + /** + * Creates a TizenFilesystemNode for a given path. + * + * @param path the path the new node should point to. + */ + TizenFilesystemNode(const Common::String &path); + + /** + * Creates a TizenFilesystemNode from the given Tizen internal path + * + * @param path the path the new node should point to. + */ + TizenFilesystemNode(SystemPath systemPath); + + Common::String getDisplayName() const { return _displayName; } + Common::String getName() const { return _displayName; } + Common::String getPath() const { return _path; } + + bool exists() const; + bool isDirectory() const; + bool isReadable() const; + bool isWritable() const; + + AbstractFSNode *getChild(const Common::String &n) const; + bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const; + AbstractFSNode *getParent() const; + + Common::SeekableReadStream *createReadStream(); + Common::WriteStream *createWriteStream(); + +protected: + TizenFilesystemNode(const Common::String &root, const Common::String &p); + void init(const Common::String &nodePath); + + Common::String _displayName; + Common::String _path; + String _unicodePath; + bool _isValid; + bool _isVirtualDir; + FileAttributes _attr; +}; + +#endif diff --git a/backends/platform/tizen/graphics.cpp b/backends/platform/tizen/graphics.cpp new file mode 100644 index 0000000000..bf255cd264 --- /dev/null +++ b/backends/platform/tizen/graphics.cpp @@ -0,0 +1,237 @@ +/* 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. + * + */ + +#include "graphics/fontman.h" + +#include "backends/platform/tizen/form.h" +#include "backends/platform/tizen/system.h" +#include "backends/platform/tizen/graphics.h" + +// +// TizenGraphicsManager +// +TizenGraphicsManager::TizenGraphicsManager(TizenAppForm *appForm) : + _appForm(appForm), + _eglDisplay(EGL_DEFAULT_DISPLAY), + _eglSurface(EGL_NO_SURFACE), + _eglConfig(NULL), + _eglContext(EGL_NO_CONTEXT), + _initState(true) { + assert(appForm != NULL); + _videoMode.fullscreen = true; +} + +TizenGraphicsManager::~TizenGraphicsManager() { + logEntered(); + + if (_eglDisplay != EGL_NO_DISPLAY) { + eglMakeCurrent(_eglDisplay, NULL, NULL, NULL); + if (_eglContext != EGL_NO_CONTEXT) { + eglDestroyContext(_eglDisplay, _eglContext); + } + } +} + +const Graphics::Font *TizenGraphicsManager::getFontOSD() { + return FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont); +} + +bool TizenGraphicsManager::moveMouse(int16 &x, int16 &y) { + int16 currentX = _cursorState.x; + int16 currentY = _cursorState.y; + + // save the current hardware coordinates + _cursorState.x = x; + _cursorState.y = y; + + // return x/y as game coordinates + adjustMousePosition(x, y); + + // convert current x/y to game coordinates + adjustMousePosition(currentX, currentY); + + // return whether game coordinates have changed + return (currentX != x || currentY != y); +} + +Common::List<Graphics::PixelFormat> TizenGraphicsManager::getSupportedFormats() const { + logEntered(); + + Common::List<Graphics::PixelFormat> res; + res.push_back(Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0)); + res.push_back(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); + res.push_back(Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)); + res.push_back(Graphics::PixelFormat::createFormatCLUT8()); + return res; +} + +bool TizenGraphicsManager::hasFeature(OSystem::Feature f) { + bool result = (f == OSystem::kFeatureFullscreenMode || + f == OSystem::kFeatureVirtualKeyboard || + OpenGLGraphicsManager::hasFeature(f)); + return result; +} + +void TizenGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) { + if (f == OSystem::kFeatureVirtualKeyboard && enable) { + _appForm->showKeypad(); + } else { + OpenGLGraphicsManager::setFeatureState(f, enable); + } +} + +void TizenGraphicsManager::setReady() { + _initState = false; +} + +void TizenGraphicsManager::updateScreen() { + if (_transactionMode == kTransactionNone) { + internUpdateScreen(); + } +} + +bool TizenGraphicsManager::loadEgl() { + logEntered(); + + EGLint numConfigs = 1; + EGLint eglConfigList[] = { + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, + EGL_NONE + }; + + EGLint eglContextList[] = { + EGL_CONTEXT_CLIENT_VERSION, 1, + EGL_NONE + }; + + eglBindAPI(EGL_OPENGL_ES_API); + + if (_eglDisplay) { + unloadGFXMode(); + } + + _eglDisplay = eglGetDisplay((EGLNativeDisplayType) EGL_DEFAULT_DISPLAY); + if (EGL_NO_DISPLAY == _eglDisplay) { + systemError("eglGetDisplay() failed"); + return false; + } + + if (EGL_FALSE == eglInitialize(_eglDisplay, NULL, NULL) || + EGL_SUCCESS != eglGetError()) { + systemError("eglInitialize() failed"); + return false; + } + + if (EGL_FALSE == eglChooseConfig(_eglDisplay, eglConfigList, &_eglConfig, 1, &numConfigs) || + EGL_SUCCESS != eglGetError()) { + systemError("eglChooseConfig() failed"); + return false; + } + + if (!numConfigs) { + systemError("eglChooseConfig() failed. Matching config does not exist \n"); + return false; + } + + _eglSurface = eglCreateWindowSurface(_eglDisplay, _eglConfig, (EGLNativeWindowType)_appForm, NULL); + if (EGL_NO_SURFACE == _eglSurface || EGL_SUCCESS != eglGetError()) { + systemError("eglCreateWindowSurface() failed. EGL_NO_SURFACE"); + return false; + } + + _eglContext = eglCreateContext(_eglDisplay, _eglConfig, EGL_NO_CONTEXT, eglContextList); + if (EGL_NO_CONTEXT == _eglContext || + EGL_SUCCESS != eglGetError()) { + systemError("eglCreateContext() failed"); + return false; + } + + if (false == eglMakeCurrent(_eglDisplay, _eglSurface, _eglSurface, _eglContext) || + EGL_SUCCESS != eglGetError()) { + systemError("eglMakeCurrent() failed"); + return false; + } + + logLeaving(); + return true; +} + +bool TizenGraphicsManager::loadGFXMode() { + logEntered(); + + if (!loadEgl()) { + unloadGFXMode(); + return false; + } + + int x, y, width, height; + _appForm->GetBounds(x, y, width, height); + _videoMode.overlayWidth = _videoMode.hardwareWidth = width; + _videoMode.overlayHeight = _videoMode.hardwareHeight = height; + _videoMode.scaleFactor = 4; // for proportional sized cursor in the launcher + + AppLog("screen size: %dx%d", _videoMode.hardwareWidth, _videoMode.hardwareHeight); + return OpenGLGraphicsManager::loadGFXMode(); +} + +void TizenGraphicsManager::loadTextures() { + logEntered(); + OpenGLGraphicsManager::loadTextures(); +} + +void TizenGraphicsManager::internUpdateScreen() { + if (!_initState) { + OpenGLGraphicsManager::internUpdateScreen(); + eglSwapBuffers(_eglDisplay, _eglSurface); + } +} + +void TizenGraphicsManager::unloadGFXMode() { + logEntered(); + + if (_eglDisplay != EGL_NO_DISPLAY) { + eglMakeCurrent(_eglDisplay, NULL, NULL, NULL); + + if (_eglContext != EGL_NO_CONTEXT) { + eglDestroyContext(_eglDisplay, _eglContext); + _eglContext = EGL_NO_CONTEXT; + } + + if (_eglSurface != EGL_NO_SURFACE) { + eglDestroySurface(_eglDisplay, _eglSurface); + _eglSurface = EGL_NO_SURFACE; + } + + eglTerminate(_eglDisplay); + _eglDisplay = EGL_NO_DISPLAY; + } + + _eglConfig = NULL; + OpenGLGraphicsManager::unloadGFXMode(); + logLeaving(); +} diff --git a/backends/platform/tizen/graphics.h b/backends/platform/tizen/graphics.h new file mode 100644 index 0000000000..27e5a6aaeb --- /dev/null +++ b/backends/platform/tizen/graphics.h @@ -0,0 +1,73 @@ +/* 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. + * + */ + +#ifndef TIZEN_GRAPHICS_H +#define TIZEN_GRAPHICS_H + +#include <FBase.h> +#include <FGraphics.h> +#include <FApp.h> +#include <FGraphicsOpengl.h> +#include <FSystem.h> +#include <FUi.h> + +#include "config.h" +#include "backends/graphics/opengl/opengl-graphics.h" +#include "graphics/font.h" +#include "backends/platform/tizen/form.h" + +using namespace Tizen::Graphics; +using namespace Tizen::Graphics::Opengl; +using namespace Tizen::App; + +class TizenGraphicsManager : public OpenGLGraphicsManager { +public: + TizenGraphicsManager(TizenAppForm *appForm); + virtual ~TizenGraphicsManager(); + + Common::List<Graphics::PixelFormat> getSupportedFormats() const; + bool hasFeature(OSystem::Feature f); + void updateScreen(); + void setFeatureState(OSystem::Feature f, bool enable); + void setReady(); + bool isReady() { return !_initState; } + const Graphics::Font *getFontOSD(); + bool moveMouse(int16 &x, int16 &y); + +private: + void internUpdateScreen(); + bool loadGFXMode(); + void loadTextures(); + void unloadGFXMode(); + void setInternalMousePosition(int x, int y) {} + void showSplash(); + + bool loadEgl(); + TizenAppForm *_appForm; + EGLDisplay _eglDisplay; + EGLSurface _eglSurface; + EGLConfig _eglConfig; + EGLContext _eglContext; + bool _initState; +}; + +#endif diff --git a/backends/platform/tizen/main.cpp b/backends/platform/tizen/main.cpp new file mode 100644 index 0000000000..b12cc3adc9 --- /dev/null +++ b/backends/platform/tizen/main.cpp @@ -0,0 +1,51 @@ +/* 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. + * + */ + +#include <FBase.h> +#include <FApp.h> +#include <FSystem.h> + +#include "backends/platform/tizen/application.h" + +using namespace Tizen::Base; +using namespace Tizen::Base::Collection; + +/** + * The entry function of tizen application called by the operating system. + */ +extern "C" _EXPORT_ int OspMain(int argc, char *pArgv[]) { + result r = E_SUCCESS; + + AppLog("Application started."); + ArrayList args(SingleObjectDeleter); + args.Construct(); + for (int i = 0; i < argc; i++) { + args.Add(new (std::nothrow) String(pArgv[i])); + } + + r = Tizen::App::UiApp::Execute(TizenScummVM::createInstance, &args); + TryLog(r == E_SUCCESS, "[%s] Application execution failed", GetErrorMessage(r)); + AppLog("Application finished."); + + return static_cast<int>(r); +} + diff --git a/backends/platform/tizen/missing.cpp b/backends/platform/tizen/missing.cpp new file mode 100644 index 0000000000..5ac55d0f6c --- /dev/null +++ b/backends/platform/tizen/missing.cpp @@ -0,0 +1,82 @@ +/* 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. + * + */ + +#include <FApp.h> +#include <FGraphics.h> +#include <FUi.h> +#include <FSystem.h> +#include <FBase.h> + +#include "backends/platform/tizen/portdefs.h" + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <ctype.h> + +#define BUF_SIZE 255 + +void systemError(const char *message); + +C_LINKAGE_BEGIN + +int __errno; // for overridden method in saves/default/default-saves.cpp + +void __assert_func(const char *file, int line, + const char *func, const char *err) { + char buffer[BUF_SIZE]; + snprintf(buffer, sizeof(buffer), "%s %d %s %s", file, line, func, err); + systemError(buffer); +} + +void stderr_fprintf(void *, const char *format, ...) { + va_list ap; + char buffer[BUF_SIZE]; + + va_start(ap, format); + vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + + AppLog(buffer); +} + +void stderr_vfprintf(void *, const char *format, va_list ap) { + char buffer[BUF_SIZE]; + vsnprintf(buffer, sizeof(buffer), format, ap); + AppLog(buffer); +} + +int printf(const char *format, ...) { + int result = 0; + va_list ap; + char buffer[BUF_SIZE]; + + va_start(ap, format); + result = vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + + AppLog(buffer); + + return result; +} + +C_LINKAGE_END diff --git a/backends/platform/tizen/portdefs.h b/backends/platform/tizen/portdefs.h new file mode 100644 index 0000000000..050ce7d1e0 --- /dev/null +++ b/backends/platform/tizen/portdefs.h @@ -0,0 +1,85 @@ +/* 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. + * + */ + +#ifndef PORT_DEFS_H +#define PORT_DEFS_H + +#include <assert.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <math.h> +#include <new> + +#define M_PI 3.14159265358979323846 + +#ifdef __cplusplus + #include <ctype.h> // causes a link error when building c programs + #define C_LINKAGE_BEGIN extern "C" { + #define C_LINKAGE_END } +#else + #define C_LINKAGE_BEGIN + #define C_LINKAGE_END +#endif + +// value missing from osp gl headers +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 + +C_LINKAGE_BEGIN + +// for libFLAC +#undef fseeko +#undef ftello +#define fseeko fseek +#define ftello ftell + +// overcome use of fprintf since newlib (1.2) does not +// support stderr/stdout (undefined reference to `_impure_ptr'). + +void stderr_fprintf(void *, const char *format, ...); +void stderr_vfprintf(void *, const char *format, va_list ap); + +#undef fprintf +#undef vfprintf +#undef stderr +#undef stdout +#undef stdin +#undef fputs +#undef fflush + +#define stderr (void *)0 +#define stdout (void *)1 +#define stdin (void *)2 +#define fputs(str, file) +#define fflush(file) +#define sscanf simple_sscanf +#define fprintf stderr_fprintf +#define vfprintf stderr_vfprintf + +int printf(const char *format, ...); +int simple_sscanf(const char *buffer, const char *format, ...); + +C_LINKAGE_END + +#endif diff --git a/backends/platform/tizen/sscanf.cpp b/backends/platform/tizen/sscanf.cpp new file mode 100644 index 0000000000..aa846698f6 --- /dev/null +++ b/backends/platform/tizen/sscanf.cpp @@ -0,0 +1,213 @@ +/* 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. + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> + +// +// simple sscanf replacement to match scummvm usage patterns +// + +bool scanInt(const char **in, va_list *ap, int max) { + // skip leading space characters + while (**in && **in == ' ') { + (*in)++; + } + + // number optionally preceeded with a + or - sign. + bool negate = false; + if (**in == '-') { + (*in)++; + negate = true; + } + + if (**in == '+') { + (*in)++; + } + + int *arg = va_arg(*ap, int*); + char *end; + long n = strtol(*in, &end, 10); + if (negate) { + n = -n; + } + + bool err = false; + if (end == *in || (max > 0 && (end - *in) > max)) { + err = true; + } else { + *arg = (int)n; + *in = end; + } + return err; +} + +bool scanHex(const char **in, va_list *ap) { + unsigned *arg = va_arg(*ap, unsigned*); + char *end; + long n = strtol(*in, &end, 16); + if (end == *in) { + return true; + } + + *in = end; + *arg = (unsigned) n; + return false; +} + +bool scanString(const char **in, va_list *ap) { + char *arg = va_arg(*ap, char*); + while (**in && **in != ' ' && **in != '\n' && **in != '\t') { + *arg = **in; + arg++; + (*in)++; + } + *arg = '\0'; + (*in)++; + return false; +} + +bool scanStringUntil(const char **in, va_list *ap, char c_end) { + char *arg = va_arg(*ap, char*); + while (**in && **in != c_end) { + *arg = **in; + *arg++; + (*in)++; + } + *arg = 0; + (*in)++; + return false; +} + +bool scanChar(const char **in, va_list *ap) { + char *arg = va_arg(*ap, char*); + if (**in) { + *arg = **in; + (*in)++; + } + return false; +} + +extern "C" int simple_sscanf(const char *input, const char *format, ...) { + va_list ap; + int result = 0; + const char *next = input; + + va_start(ap, format); + + while (*format) { + if (*format == '%') { + format++; + int max = 0; + while (isdigit(*format)) { + max = (max * 10) + (*format - '0'); + format++; + } + + bool err = false; + switch (*format++) { + case 'c': + err = scanChar(&next, &ap); + break; + case 'd': + case 'u': + err = scanInt(&next, &ap, max); + break; + case 'x': + err = scanHex(&next, &ap); + break; + case 's': + err = scanString(&next, &ap); + break; + case '[': + // assume %[^c] + if ('^' != *format) { + err = true; + } else { + format++; + if (*format && *(format+1) == ']') { + err = scanStringUntil(&next, &ap, *format); + format += 2; + } else { + err = true; + } + } + break; + default: + err = true; + break; + } + + if (err) { + break; + } else { + result++; + } + } else if (*format++ != *next++) { + // match input + break; + } + } + + va_end(ap); + return result; +} + +#if defined(TEST) +int main(int argc, char *pArgv[]) { + int x,y,xx,yy,h; + char buffer[100]; + unsigned u; + char c; + strcpy(buffer, "hello"); + char *b = buffer; + + if (simple_sscanf("BBX 00009 -1 +10 000", + "BBX %d %d %d %d", + &x, &y, &xx, &yy) != 4) { + printf("Failed\n"); + } else { + printf("Success %d %d %d %d\n", x, y, xx, yy); + } + + if (simple_sscanf("CAT 123x-10 0x100h 123456.AUD $ ", + "CAT %dx%d %xh %06u.AUD %c", + &x, &y, &h, &u, &c) != 5) { + printf("Failed\n"); + } else { + printf("Success %d %d %d %d '%c' \n", x, y, h, u, c); + } + + if (simple_sscanf("COPYRIGHT \"Copyright (c) 1984, 1987 Foo Systems Incorporated", + "COPYRIGHT \"%[^\"]", + b) != 1) { + printf("Failed\n"); + } else { + printf("Success %s\n", buffer); + } + + return 0; +} +#endif diff --git a/backends/platform/tizen/system.cpp b/backends/platform/tizen/system.cpp new file mode 100644 index 0000000000..54d92146e5 --- /dev/null +++ b/backends/platform/tizen/system.cpp @@ -0,0 +1,547 @@ +/* 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. + * + */ + +#include <FUiCtrlMessageBox.h> +#include <FLocales.h> + +#include "common/config-manager.h" +#include "common/file.h" +#include "engines/engine.h" +#include "graphics/font.h" +#include "graphics/fontman.h" +#include "graphics/fonts/bdf.h" +#include "backends/saves/default/default-saves.h" +#include "backends/events/default/default-events.h" +#include "backends/audiocd/default/default-audiocd.h" +#include "backends/mutex/mutex.h" +#include "backends/fs/fs-factory.h" +#include "backends/timer/tizen/timer.h" + +#include "backends/platform/tizen/form.h" +#include "backends/platform/tizen/system.h" +#include "backends/platform/tizen/graphics.h" +#include "backends/platform/tizen/audio.h" + +using namespace Tizen::Base; +using namespace Tizen::Base::Runtime; +using namespace Tizen::Locales; +using namespace Tizen::Ui; +using namespace Tizen::Ui::Controls; +using namespace Tizen::System; + +#define DEFAULT_CONFIG_FILE "scummvm.ini" +#define MUTEX_BUFFER_SIZE 5 + +// +// TizenFilesystemFactory +// +class TizenFilesystemFactory : public FilesystemFactory { + AbstractFSNode *makeRootFileNode() const; + AbstractFSNode *makeCurrentDirectoryFileNode() const; + AbstractFSNode *makeFileNodePath(const Common::String &path) const; +}; + +AbstractFSNode *TizenFilesystemFactory::makeRootFileNode() const { + return new TizenFilesystemNode("/"); +} + +AbstractFSNode *TizenFilesystemFactory::makeCurrentDirectoryFileNode() const { + return new TizenFilesystemNode("/"); +} + +AbstractFSNode *TizenFilesystemFactory::makeFileNodePath(const Common::String &path) const { + AppAssert(!path.empty()); + return new TizenFilesystemNode(path); +} + +// +// TizenSaveFileManager +// +struct TizenSaveFileManager : public DefaultSaveFileManager { + bool removeSavefile(const Common::String &filename); +}; + +bool TizenSaveFileManager::removeSavefile(const Common::String &filename) { + Common::String savePathName = getSavePath(); + + checkPath(Common::FSNode(savePathName)); + if (getError().getCode() != Common::kNoError) { + return false; + } + + // recreate FSNode since checkPath may have changed/created the directory + Common::FSNode savePath(savePathName); + Common::FSNode file = savePath.getChild(filename); + + String unicodeFileName; + StringUtil::Utf8ToString(file.getPath().c_str(), unicodeFileName); + + switch (Tizen::Io::File::Remove(unicodeFileName)) { + case E_SUCCESS: + return true; + + case E_ILLEGAL_ACCESS: + setError(Common::kWritePermissionDenied, "Search or write permission denied: " + + file.getName()); + break; + + default: + setError(Common::kPathDoesNotExist, "removeSavefile: '" + file.getName() + + "' does not exist or path is invalid"); + break; + } + + return false; +} + +// +// TizenMutexManager +// +struct TizenMutexManager : public MutexManager { + TizenMutexManager(); + ~TizenMutexManager(); + OSystem::MutexRef createMutex(); + void lockMutex(OSystem::MutexRef mutex); + void unlockMutex(OSystem::MutexRef mutex); + void deleteMutex(OSystem::MutexRef mutex); +private: + Mutex *_buffer[MUTEX_BUFFER_SIZE]; +}; + +TizenMutexManager::TizenMutexManager() { + for (int i = 0; i < MUTEX_BUFFER_SIZE; i++) { + _buffer[i] = NULL; + } +} + +TizenMutexManager::~TizenMutexManager() { + for (int i = 0; i < MUTEX_BUFFER_SIZE; i++) { + if (_buffer[i] != NULL) { + delete _buffer[i]; + } + } +} + +OSystem::MutexRef TizenMutexManager::createMutex() { + Mutex *mutex = new Mutex(); + mutex->Create(); + + for (int i = 0; i < MUTEX_BUFFER_SIZE; i++) { + if (_buffer[i] == NULL) { + _buffer[i] = mutex; + break; + } + } + + return (OSystem::MutexRef) mutex; +} + +void TizenMutexManager::lockMutex(OSystem::MutexRef mutex) { + Mutex *m = (Mutex *)mutex; + m->Acquire(); +} + +void TizenMutexManager::unlockMutex(OSystem::MutexRef mutex) { + Mutex *m = (Mutex *)mutex; + m->Release(); +} + +void TizenMutexManager::deleteMutex(OSystem::MutexRef mutex) { + Mutex *m = (Mutex *)mutex; + + for (int i = 0; i < MUTEX_BUFFER_SIZE; i++) { + if (_buffer[i] == m) { + _buffer[i] = NULL; + } + } + + delete m; +} + +// +// TizenEventManager +// +struct TizenEventManager : public DefaultEventManager { + TizenEventManager(Common::EventSource *boss); + void init(); + int shouldQuit() const; +}; + +TizenEventManager::TizenEventManager(Common::EventSource *boss) : + DefaultEventManager(boss) { +} + +void TizenEventManager::init() { + DefaultEventManager::init(); + + // theme and vkbd should have now loaded - clear the splash screen + TizenSystem *system = (TizenSystem *)g_system; + TizenGraphicsManager *graphics = system->getGraphics(); + if (graphics) { + graphics->setReady(); + } +} + +int TizenEventManager::shouldQuit() const { + TizenSystem *system = (TizenSystem *)g_system; + return DefaultEventManager::shouldQuit() || system->isClosing(); +} + +// +// TizenAppFrame - avoid drawing the misplaced UiTheme at startup +// +struct TizenAppFrame : Frame { + result OnDraw(void) { + logEntered(); + TizenAppForm *form = (TizenAppForm *)GetCurrentForm(); + if (form->isStarting()) { + Canvas *canvas = GetCanvasN(); + canvas->SetBackgroundColor(Color::GetColor(COLOR_ID_BLACK)); + canvas->Clear(); + delete canvas; + } + return E_SUCCESS; + } +}; + +// +// TizenSystem +// +TizenSystem::TizenSystem(TizenAppForm *appForm) : + _appForm(appForm), + _audioThread(0), + _epoch(0) { +} + +result TizenSystem::Construct(void) { + logEntered(); + + _fsFactory = new TizenFilesystemFactory(); + if (!_fsFactory) { + return E_OUT_OF_MEMORY; + } + + _resourcePath = fromString(App::GetInstance()->GetAppResourcePath()); + return E_SUCCESS; +} + +TizenSystem::~TizenSystem() { + logEntered(); +} + +result TizenSystem::initModules() { + logEntered(); + + _mutexManager = new TizenMutexManager(); + if (!_mutexManager) { + return E_OUT_OF_MEMORY; + } + + _timerManager = new TizenTimerManager(); + if (!_timerManager) { + return E_OUT_OF_MEMORY; + } + + _savefileManager = new TizenSaveFileManager(); + if (!_savefileManager) { + return E_OUT_OF_MEMORY; + } + + _graphicsManager = (GraphicsManager *)new TizenGraphicsManager(_appForm); + if (!_graphicsManager) { + return E_OUT_OF_MEMORY; + } + + // depends on _graphicsManager when ENABLE_VKEYBD enabled + _eventManager = new TizenEventManager(this); + if (!_eventManager) { + return E_OUT_OF_MEMORY; + } + + _audioThread = new AudioThread(); + if (!_audioThread) { + return E_OUT_OF_MEMORY; + } + + _mixer = _audioThread->Construct(this); + if (!_mixer) { + return E_OUT_OF_MEMORY; + } + + _audiocdManager = (AudioCDManager *)new DefaultAudioCDManager(); + if (!_audiocdManager) { + return E_OUT_OF_MEMORY; + } + + if (IsFailed(_audioThread->Start())) { + AppLog("Failed to start audio thread"); + return E_OUT_OF_MEMORY; + } + + logLeaving(); + return E_SUCCESS; +} + +void TizenSystem::initBackend() { + logEntered(); + + Common::String dataPath = fromString(App::GetInstance()->GetAppDataPath()); + + // use the mobile device theme + ConfMan.set("gui_theme", _resourcePath + "scummmodern"); + + // allow tizen virtual keypad pack to be found + ConfMan.set("vkeybdpath", _resourcePath + "vkeybd_bada"); + ConfMan.set("vkeybd_pack_name", "vkeybd_bada"); + + // set default save path to writable area + if (!ConfMan.hasKey("savepath")) { + ConfMan.set("savepath", dataPath); + } + + // default to no auto-save + if (!ConfMan.hasKey("autosave_period")) { + ConfMan.setInt("autosave_period", 0); + } + + ConfMan.registerDefault("fullscreen", true); + ConfMan.registerDefault("aspect_ratio", false); + ConfMan.setBool("confirm_exit", false); + + SystemTime::GetTicks(_epoch); + + if (E_SUCCESS != initModules()) { + AppLog("initModules failed"); + } else { + OSystem::initBackend(); + + // replace kBigGUIFont for the vkbd and on-screen messages + Common::String fontCacheFile = dataPath + "helvR24.fcc"; + TizenFilesystemNode file(fontCacheFile); + if (!file.exists()) { + Common::String bdfFile = _resourcePath + "fonts/helvR24.bdf"; + TizenFilesystemNode file(bdfFile); + if (file.exists()) { + Common::SeekableReadStream *stream = file.createReadStream(); + Common::File fontFile; + if (stream && fontFile.open(stream, bdfFile)) { + Graphics::BdfFont *font = Graphics::BdfFont::loadFont(fontFile); + Graphics::BdfFont::cacheFontData(*font, fontCacheFile); + FontMan.setFont(Graphics::FontManager::kBigGUIFont, font); + } + } + } else { + Common::SeekableReadStream *stream = file.createReadStream(); + Common::File fontFile; + if (stream && fontFile.open(stream, fontCacheFile)) { + Graphics::BdfFont *font = Graphics::BdfFont::loadFromCache(fontFile); + if (font) { + FontMan.setFont(Graphics::FontManager::kBigGUIFont, font); + } + } + } + } + logLeaving(); +} + +void TizenSystem::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) { + // allow translations.dat and game .DAT files to be found + s.addDirectory(_resourcePath, _resourcePath, priority); +} + +void TizenSystem::destroyBackend() { + closeAudio(); + + delete _graphicsManager; + _graphicsManager = NULL; + + delete _savefileManager; + _savefileManager = NULL; + + delete _fsFactory; + _fsFactory = NULL; + + delete _mixer; + _mixer = NULL; + + delete _audiocdManager; + _audiocdManager = NULL; + + delete _timerManager; + _timerManager = NULL; + + delete _eventManager; + _eventManager = NULL; + + delete _mutexManager; + _mutexManager = NULL; +} + +bool TizenSystem::pollEvent(Common::Event &event) { + return _appForm->pollEvent(event); +} + +uint32 TizenSystem::getMillis(bool skipRecord) { + long long result, ticks = 0; + SystemTime::GetTicks(ticks); + result = ticks - _epoch; + return result; +} + +void TizenSystem::delayMillis(uint msecs) { + if (!_appForm->isClosing()) { + Thread::Sleep(msecs); + } +} + +void TizenSystem::updateScreen() { + if (_graphicsManager != NULL) { + _graphicsManager->updateScreen(); + } +} + +void TizenSystem::getTimeAndDate(TimeDate &td) const { + DateTime currentTime; + + if (E_SUCCESS == SystemTime::GetCurrentTime(WALL_TIME, currentTime)) { + td.tm_sec = currentTime.GetSecond(); + td.tm_min = currentTime.GetMinute(); + td.tm_hour = currentTime.GetHour(); + td.tm_mday = currentTime.GetDay(); + td.tm_mon = currentTime.GetMonth(); + td.tm_year = currentTime.GetYear(); + + Calendar *calendar = Calendar::CreateInstanceN(CALENDAR_GREGORIAN); + calendar->SetTime(currentTime); + td.tm_wday = calendar->GetTimeField(TIME_FIELD_DAY_OF_WEEK) - 1; + delete calendar; + } +} + +void TizenSystem::fatalError() { + systemError("ScummVM: Fatal internal error."); +} + +void TizenSystem::exitSystem() { + if (_appForm) { + closeAudio(); + closeGraphics(); + _appForm->exitSystem(); + } +} + +void TizenSystem::logMessage(LogMessageType::Type type, const char *message) { + if (type == LogMessageType::kError) { + systemError(message); + } else { + AppLog(message); + } +} + +Common::SeekableReadStream *TizenSystem::createConfigReadStream() { + TizenFilesystemNode file(fromString(App::GetInstance()->GetAppDataPath()) + DEFAULT_CONFIG_FILE); + return file.createReadStream(); +} + +Common::WriteStream *TizenSystem::createConfigWriteStream() { + TizenFilesystemNode file(fromString(App::GetInstance()->GetAppDataPath()) + DEFAULT_CONFIG_FILE); + return file.createWriteStream(); +} + +void TizenSystem::closeAudio() { + if (_audioThread) { + _audioThread->Quit(); + _audioThread->Join(); + delete _audioThread; + _audioThread = NULL; + } +} + +void TizenSystem::closeGraphics() { + if (_graphicsManager) { + delete _graphicsManager; + _graphicsManager = NULL; + } +} + +void TizenSystem::setMute(bool on) { + // only change mute after eventManager init() has completed + if (_audioThread) { + TizenGraphicsManager *graphics = getGraphics(); + if (graphics && graphics->isReady()) { + _audioThread->setMute(on); + } + } +} + +// +// create the ScummVM system +// +TizenAppForm *systemStart(Tizen::App::Application *app) { + logEntered(); + + Frame *appFrame = new (std::nothrow) TizenAppFrame(); + if (!appFrame || appFrame->Construct() == E_FAILURE) { + AppLog("Failed to create appFrame"); + return NULL; + } + app->AddFrame(*appFrame); + + TizenAppForm *appForm = new TizenAppForm(); + if (!appForm) { + AppLog("Failed to create appForm"); + return NULL; + } + + if (E_SUCCESS != appForm->Construct() || + E_SUCCESS != appFrame->AddControl(*appForm)) { + delete appForm; + AppLog("Failed to construct appForm"); + return NULL; + } + + appFrame->SetCurrentForm(appForm); + logLeaving(); + return appForm; +} + +// +// display a fatal error notification +// +void systemError(const char *message) { + AppLog("Fatal system error: %s", message); + + if (strspn(message, "Config file buggy:") > 0) { + Tizen::Io::File::Remove(DEFAULT_CONFIG_FILE); + Application::GetInstance()->SendUserEvent(USER_MESSAGE_EXIT_ERR_CONFIG, NULL); + } else { + ArrayList *args = new ArrayList(); + args->Construct(); + args->Add(*(new String(message))); + Application::GetInstance()->SendUserEvent(USER_MESSAGE_EXIT_ERR, args); + } + + if (g_system) { + TizenSystem *system = (TizenSystem *)g_system; + system->exitSystem(); + } +} diff --git a/backends/platform/tizen/system.h b/backends/platform/tizen/system.h new file mode 100644 index 0000000000..b38940cc95 --- /dev/null +++ b/backends/platform/tizen/system.h @@ -0,0 +1,102 @@ +/* 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. + * + */ + +#ifndef TIZEN_SYSTEM_H +#define TIZEN_SYSTEM_H + +#include <FApp.h> +#include <FGraphics.h> +#include <FUi.h> +#include <FSystem.h> +#include <FBase.h> +#include <FIoFile.h> + +#include "config.h" +#include "common/scummsys.h" +#include "backends/modular-backend.h" + +#include "backends/platform/tizen/fs.h" +#include "backends/platform/tizen/form.h" +#include "backends/platform/tizen/audio.h" +#include "backends/platform/tizen/graphics.h" + +#if defined(_DEBUG) +#define logEntered() AppLog("%s entered (%s %d)", __FUNCTION__, __FILE__, __LINE__); +#define logLeaving() AppLog("%s leaving (%s %d)", __FUNCTION__, __FILE__, __LINE__); +#else +#define logEntered() +#define logLeaving() +#endif + +TizenAppForm *systemStart(Tizen::App::Application *app); +void systemError(const char *message); + +#define USER_MESSAGE_EXIT 1000 +#define USER_MESSAGE_EXIT_ERR 1001 +#define USER_MESSAGE_EXIT_ERR_CONFIG 1002 + +// +// TizenSystem +// +class TizenSystem : + public ModularBackend, + Common::EventSource { +public: + TizenSystem(TizenAppForm *appForm); + ~TizenSystem(); + + result Construct(); + void closeAudio(); + void closeGraphics(); + void destroyBackend(); + void setMute(bool on); + void exitSystem(); + bool isClosing() { return _appForm->isClosing(); } + + TizenGraphicsManager *getGraphics() { + return (TizenGraphicsManager *)_graphicsManager; + } + +private: + void initBackend(); + result initModules(); + + void updateScreen(); + bool pollEvent(Common::Event &event); + uint32 getMillis(bool skipRecord = false); + void delayMillis(uint msecs); + void getTimeAndDate(TimeDate &t) const; + void fatalError(); + void logMessage(LogMessageType::Type type, const char *message); + void addSysArchivesToSearchSet(Common::SearchSet &s, int priority); + + Common::EventSource *getDefaultEventSource() { return this; } + Common::SeekableReadStream *createConfigReadStream(); + Common::WriteStream *createConfigWriteStream(); + + TizenAppForm *_appForm; + AudioThread *_audioThread; + long long _epoch; + Common::String _resourcePath; +}; + +#endif diff --git a/backends/platform/tizen/tizen.mk b/backends/platform/tizen/tizen.mk new file mode 100644 index 0000000000..d8925b62dc --- /dev/null +++ b/backends/platform/tizen/tizen.mk @@ -0,0 +1,7 @@ +# port files built under eclipse + +MODULE := backends/platform/tizen + +$(EXECUTABLE): $(OBJS) + rm -f $@ + arm-linux-gnueabi-ar Tru $@ $(OBJS) |