Merged from trunk.

svn-id: r49499
This commit is contained in:
Alejandro Marzini 2010-06-08 03:31:27 +00:00
commit 7ea78b1036
226 changed files with 11766 additions and 6185 deletions

View File

@ -166,6 +166,9 @@ ScummVM Team
Backend Teams
-------------
Android:
Angus Lees
Dreamcast:
Marcus Comstedt

View File

@ -48,6 +48,7 @@ Martin Kiewitz
Pawel Kolodziejski
Mutwin Kraus
Andrew Kurushin
Angus Lees
Claudio Matsuoka
Thomas Mayer
Neil Millstone

18
NEWS
View File

@ -2,12 +2,22 @@ For a more comprehensive changelog for the latest experimental SVN code, see:
http://scummvm.svn.sourceforge.net/viewvc/scummvm/?view=log
1.2.0 (????-??-??)
PSP port:
- Switched to new backend design which fixes minor graphical issues,
speeds things up, and provides 16-bit support.
New Ports:
- Added Android port.
General:
- Switched to the "fast" DOSBox OPL emulator.
- Fixed a crash in the rjp1 player code affecting the FOTAQ Amiga version.
Groovie:
- Added support for the Macintosh version of The 7th Guest.
- Added support for custom MT-32 instruments.
PSP port:
- Switched to new backend design which fixes minor graphical issues,
speeds things up, and provides 16-bit support.
- Enabled playback of MP3 files using the hardware decoder (ME). This means that
the port is now optimized for MP3 playback (as opposed to OGG).
1.1.2 (????-??-??)
Broken Sword 2
@ -17,7 +27,7 @@ For a more comprehensive changelog for the latest experimental SVN code, see:
1.1.1 (2010-05-02)
New Ports:
- Added Nintendo 64 Port. (Actually added in 1.1.0, but forgot to mention it. oops)
- Added Nintendo 64 port. (Actually added in 1.1.0, but forgot to mention it. oops)
General:
- Fixed several minor bugs here and there.

21
README
View File

@ -81,9 +81,10 @@ You can find a thorough list with details on which games are supported
and how well on the compatibility page. ScummVM is continually
improving, so check back often.
Among the systems on which you can play those games are Windows, Linux,
Mac OS X, Dreamcast, PocketPC, PalmOS, AmigaOS, BeOS, OS/2, PSP, PS2,
SymbianOS/EPOC, iPhone and many more.
Among the systems on which you can play those games are regular desktop
computers (running Windows, Linux, Mac OS X, ...), game consoles
(Dreamcast, Nintendo DS & Wii, PS2, PSP, ...), smartphones (Android,
iPhone, PocketPC, Symbian ...) and more.
At this time ScummVM should be considered beta software, and is still
under heavy development. Be aware that whilst we attempt to make sure
@ -843,21 +844,25 @@ contact us!
Supported platforms include (but are not limited to):
UNIX (Linux, Solaris, IRIX, *BSD)
UNIX (Linux, Solaris, IRIX, *BSD, ...)
Windows
Windows CE and Windows Mobile (including Smartphones and PocketPCs)
Mac OS X
AmigaOS
Android
BeOS
Dreamcast
iPhone (also includes the iPod Touch)
GP2x
iPhone (also includes iPod Touch and iPad)
Maemo (Nokia Internet tablets 770, N800, N810, N900)
Nintendo 64
Nintendo DS
Nintendo Wii
OS/2
PalmOS
PlayStation 2
PlayStation Portable
RISC OS
Symbian
Maemo (Nokia Internet tablets 770, N800, N810, N900)
The Dreamcast port does not support The Curse of Monkey Island, nor The
Dig. The PalmOS port does not support The Curse of Monkey Island,
@ -865,6 +870,8 @@ Beneath a Steel Sky, nor either Simon the Sorcerer 1 or 2. The Dig will
only work on some Palm devices (those with a large dynamic heap). The
Nintendo DS port does not support Full Throttle, The Dig, or The Curse
of Monkey Island.
For more platform specific limitations, please refer to our Wiki:
http://wiki.scummvm.org/index.php/Platforms
In the Macintosh port, the right mouse button is emulated via Cmd-Click
(that is, you click the mouse button while holding the

View File

@ -24,6 +24,8 @@
*/
#ifdef __PSP__
#include <pspiofilemgr_stat.h>
#include <pspiofilemgr.h>
#include <SDL/SDL_thread.h>
#include <SDL/SDL_mutex.h>
@ -32,28 +34,48 @@
#include <errno.h>
//#define __PSP_PRINT_TO_FILE__
//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
#define MIN2(a,b) ((a < b) ? a : b)
#define MIN3(a,b,c) ( (a < b) ? (a < c ? a : c) : (b < c ? b : c) )
//#define __PSP_PRINT_TO_FILE__ /* For debugging suspend stuff, we have no screen output */
//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
//#define __PSP_DEBUG_PRINT__ /* For debug printouts */
#include "backends/platform/psp/trace.h"
//#define DEBUG_BUFFERS /* to see the contents of the buffers being read */
#ifdef DEBUG_BUFFERS
void printBuffer(byte *ptr, uint32 len) {
uint32 printLen = len <= 10 ? len : 10;
for (int i = 0; i < printLen; i++) {
PSP_INFO_PRINT("%x ", ptr[i]);
}
if (len > 10) {
PSP_INFO_PRINT("... ");
for (int i = len - 10; i < len; i++)
PSP_INFO_PRINT("%x ", ptr[i]);
}
PSP_INFO_PRINT("\n");
}
#endif
PSPIoStream::PSPIoStream(const Common::String &path, bool writeMode)
: StdioStream((void *)1), _path(path), _writeMode(writeMode) {
: StdioStream((void *)1), _path(path), _writeMode(writeMode),
_ferror(false), _pos(0),
_physicalPos(0), _fileSize(0), _inCache(false), _eos(false),
_cacheStartOffset(-1), _cache(0),
_errorSuspend(0), _errorSource(0),
_errorPos(0), _errorHandle(0), _suspendCount(0) {
DEBUG_ENTER_FUNC();
assert(!path.empty());
// assert(!path.empty()); // do we need this?
_handle = (void *)0; // Need to do this since base class asserts not 0.
_ferror = false;
_feof = false;
_pos = 0;
/* for error checking */
_errorSuspend = 0;
_errorSource = 0;
_errorPos = 0;
_errorHandle = 0;
_suspendCount = 0;
}
PSPIoStream::~PSPIoStream() {
@ -63,9 +85,12 @@ PSPIoStream::~PSPIoStream() {
PSP_DEBUG_PRINT_FUNC("Suspended\n");
PowerMan.unregisterSuspend(this); // Unregister with powermanager to be suspended
// Must do this before fclose() or resume() will reopen.
// Must do this before fclose() or resume() will reopen.
fclose((FILE *)_handle); // We don't need a critical section(?). Worst case, the handle gets closed on its own
fclose((FILE *)_handle); // We don't need a critical section. Worst case, the handle gets closed on its own
if (_cache)
free(_cache);
PowerMan.endCriticalSection();
}
@ -82,6 +107,17 @@ void *PSPIoStream::open() {
_handle = fopen(_path.c_str(), _writeMode ? "wb" : "rb"); // open
if (_handle) {
// Get the file size. This way is much faster than going to the end of the file and back
SceIoStat stat;
sceIoGetstat(_path.c_str(), &stat);
_fileSize = *((uint32 *)(void *)&stat.st_size); // 4GB file is big enough for us
PSP_DEBUG_PRINT("%s filesize = %d\n", _path.c_str(), _fileSize);
// Allocate the cache
_cache = (char *)memalign(64, CACHE_SIZE);
}
PowerMan.registerSuspend(this); // Register with the powermanager to be suspended
PowerMan.endCriticalSection();
@ -91,100 +127,183 @@ void *PSPIoStream::open() {
bool PSPIoStream::err() const {
DEBUG_ENTER_FUNC();
if (_ferror)
PSP_ERROR("mem_ferror[%d], source[%d], suspend error[%d], pos[%d], _errorPos[%d], _errorHandle[%p], suspendCount[%d]\n",
_ferror, _errorSource, _errorSuspend, _pos, _errorPos, _errorHandle, _suspendCount);
if (_ferror) // We dump since no printing to screen with suspend
PSP_ERROR("mem_ferror[%d], source[%d], suspend error[%d], pos[%d], \
_errorPos[%d], _errorHandle[%p], suspendCount[%d]\n",
_ferror, _errorSource, _errorSuspend, _pos,
_errorPos, _errorHandle, _suspendCount);
return _ferror;
}
void PSPIoStream::clearErr() {
_ferror = false; // Remove regular error bit
_ferror = false;
}
bool PSPIoStream::eos() const {
return _feof;
return _eos;
}
int32 PSPIoStream::pos() const {
return _pos;
}
int32 PSPIoStream::size() const {
DEBUG_ENTER_FUNC();
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSP_DEBUG_PRINT_FUNC("Suspended\n");
fseek((FILE *)_handle, 0, SEEK_END);
int32 length = ftell((FILE *)_handle);
fseek((FILE *)_handle, _pos, SEEK_SET);
if (_pos < 0 || length < 0) { // Check for errors
_errorSource = 2;
PSP_ERROR("pos[%d] or length[%d] < 0!\n", _pos, length);
_ferror = true;
length = -1; // If our oldPos is bad, we want length to be bad too to signal
clearerr((FILE *)_handle);
}
PowerMan.endCriticalSection();
return length;
return _fileSize;
}
bool PSPIoStream::seek(int32 offs, int whence) {
DEBUG_ENTER_FUNC();
// Check if we can access the file
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSP_DEBUG_PRINT_FUNC("Suspended\n");
int ret = fseek((FILE *)_handle, offs, whence);
if (ret != 0) {
_ferror = true;
PSP_ERROR("fseek returned with [%d], non-zero\n", ret);
clearerr((FILE *)_handle);
_feof = feof((FILE *)_handle);
_errorSource = 3;
} else { // everything ok
_feof = false; // Reset eof flag since we know it was ok
PSP_DEBUG_PRINT_FUNC("offset[0x%x], whence[%d], _pos[0x%x], _physPos[0x%x]\n", offs, whence, _pos, _physicalPos);
_eos = false;
int32 posToSearchFor = 0;
switch (whence) {
case SEEK_CUR:
posToSearchFor = _pos;
break;
case SEEK_END:
posToSearchFor = _fileSize; // unsure. Does it take us here or to EOS - 1?
break;
}
_pos = ftell((FILE *)_handle); // update pos
PowerMan.endCriticalSection();
return (ret == 0);
posToSearchFor += offs;
// Check for bad values
if (posToSearchFor < 0) {
_ferror = true;
return false;
}
if (posToSearchFor > _fileSize) {
_ferror = true;
_eos = true;
return false;
}
// See if we can find it in cache
if (isOffsetInCache(posToSearchFor)) {
PSP_DEBUG_PRINT("seek offset[0x%x] found in cache. Cache starts[0x%x]\n", posToSearchFor, _cacheStartOffset);
_inCache = true;
} else { // not in cache
_inCache = false;
}
_pos = posToSearchFor;
return true;
}
uint32 PSPIoStream::read(void *ptr, uint32 len) {
DEBUG_ENTER_FUNC();
// Check if we can access the file
PSP_DEBUG_PRINT_FUNC("filename[%s], len[0x%x], ptr[%p]\n", _path.c_str(), len, ptr);
if (_ferror || _eos)
return 0;
byte *destPtr = (byte *)ptr;
uint32 lenFromFile = len; // how much we read from the actual file
uint32 lenFromCache = 0; // how much we read from cache
uint32 lenRemainingInFile = _fileSize - _pos;
if (lenFromFile > lenRemainingInFile) {
lenFromFile = lenRemainingInFile;
_eos = true;
}
// Are we in cache?
if (_inCache && isCacheValid()) {
uint32 offsetInCache = _pos - _cacheStartOffset;
// We can read at most what's in the cache or the remaining size of the file
lenFromCache = MIN2(lenFromFile, CACHE_SIZE - offsetInCache); // unsure
PSP_DEBUG_PRINT("reading 0x%x bytes from cache to %p. pos[0x%x] physPos[0x%x] cacheStart[0x%x]\n", lenFromCache, destPtr, _pos, _physicalPos, _cacheStartOffset);
memcpy(destPtr, &_cache[offsetInCache], lenFromCache);
_pos += lenFromCache;
if (lenFromCache < lenFromFile) { // there's more to copy from the file
lenFromFile -= lenFromCache;
lenRemainingInFile -= lenFromCache; // since we moved pos
destPtr += lenFromCache;
} else { // we're done
#ifdef DEBUG_BUFFERS
printBuffer((byte *)ptr, len);
#endif
return lenFromCache; // how much we actually read
}
}
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSP_DEBUG_PRINT_FUNC("Suspended\n");
PSP_DEBUG_PRINT_FUNC("filename[%s], len[%d]\n", _path.c_str(), len);
size_t ret = fread((byte *)ptr, 1, len, (FILE *)_handle);
_pos += ret; // Update pos
if (ret != len) { // Check for eof
_feof = feof((FILE *)_handle);
if (!_feof) { // It wasn't an eof. Must be an error
synchronizePhysicalPos(); // we need to update our physical position
if (lenFromFile <= MIN_READ_SIZE) { // We load the cache in case the read is small enough
// This optimization is based on the principle that reading 1 byte is as expensive as 1000 bytes
uint32 lenToCopyToCache = MIN2((uint32)MIN_READ_SIZE, lenRemainingInFile); // at most remaining file size
PSP_DEBUG_PRINT("filling cache with 0x%x bytes from physicalPos[0x%x]. cacheStart[0x%x], pos[0x%x], fileSize[0x%x]\n", lenToCopyToCache, _physicalPos, _cacheStartOffset, _pos, _fileSize);
size_t ret = fread(_cache, 1, lenToCopyToCache, (FILE *)_handle);
if (ret != lenToCopyToCache) {
PSP_ERROR("in filling cache, failed to get 0x%x bytes. Only got 0x%x\n", lenToCopyToCache, ret);
_ferror = true;
clearerr((FILE *)_handle);
_pos = ftell((FILE *)_handle); // Update our position
_errorSource = 4;
PSP_ERROR("fread returned ret[%d] instead of len[%d]\n", ret, len);
}
_cacheStartOffset = _physicalPos;
_inCache = true;
_physicalPos += ret;
PSP_DEBUG_PRINT("copying 0x%x bytes from cache to %p\n", lenFromFile, destPtr);
// Copy to the destination buffer from cache
memcpy(destPtr, _cache, lenFromFile);
_pos += lenFromFile;
} else { // Too big for cache. No caching
PSP_DEBUG_PRINT("reading 0x%x bytes from file to %p. Pos[0x%x], physPos[0x%x]\n", lenFromFile, destPtr, _pos, _physicalPos);
size_t ret = fread(destPtr, 1, lenFromFile, (FILE *)_handle);
_physicalPos += ret; // Update pos
_pos = _physicalPos;
if (ret != lenFromFile) { // error
PSP_ERROR("fread returned [0x%x] instead of len[0x%x]\n", ret, lenFromFile);
_ferror = true;
clearerr((FILE *)_handle);
_errorSource = 4;
}
_inCache = false;
}
PowerMan.endCriticalSection();
return ret;
#ifdef DEBUG_BUFFERS
printBuffer((byte *)ptr, len);
#endif
return lenFromCache + lenFromFile; // total of what was copied
}
// TODO: Test if seeking backwards/forwards has any effect on performance
inline bool PSPIoStream::synchronizePhysicalPos() {
if (_pos != _physicalPos) {
if (fseek((FILE *)_handle, _pos - _physicalPos, SEEK_CUR) != 0)
return false;
_physicalPos = _pos;
}
return true;
}
inline bool PSPIoStream::isOffsetInCache(uint32 offset) {
if (_cacheStartOffset != -1 &&
offset >= (uint32)_cacheStartOffset &&
offset < (uint32)(_cacheStartOffset + CACHE_SIZE))
return true;
return false;
}
uint32 PSPIoStream::write(const void *ptr, uint32 len) {
@ -193,18 +312,30 @@ uint32 PSPIoStream::write(const void *ptr, uint32 len) {
if (PowerMan.beginCriticalSection() == PowerManager::Blocked)
PSP_DEBUG_PRINT_FUNC("Suspended\n");
PSP_DEBUG_PRINT_FUNC("filename[%s], len[%d]\n", _path.c_str(), len);
PSP_DEBUG_PRINT_FUNC("filename[%s], len[0x%x]\n", _path.c_str(), len);
if (_ferror)
return 0;
_eos = false; // we can't have eos with write
synchronizePhysicalPos();
size_t ret = fwrite(ptr, 1, len, (FILE *)_handle);
_pos += ret;
// If we're making the file bigger, adjust the size
if (_physicalPos + (int)ret > _fileSize)
_fileSize = _physicalPos + ret;
_physicalPos += ret;
_pos = _physicalPos;
_inCache = false;
_cacheStartOffset = -1; // invalidate cache
if (ret != len) { // Set error
_ferror = true;
clearerr((FILE *)_handle);
_pos = ftell((FILE *)_handle); // Update pos
_errorSource = 5;
PSP_ERROR("fwrite returned[%d] instead of len[%d]\n", ret, len);
PSP_ERROR("fwrite returned[0x%x] instead of len[0x%x]\n", ret, len);
}
PowerMan.endCriticalSection();
@ -224,7 +355,7 @@ bool PSPIoStream::flush() {
_ferror = true;
clearerr((FILE *)_handle);
_errorSource = 6;
PSP_ERROR("fflush returned ret[%u]\n", ret);
PSP_ERROR("fflush returned ret[%d]\n", ret);
}
PowerMan.endCriticalSection();
@ -286,6 +417,9 @@ int PSPIoStream::resume() {
// Resume our previous position
if (_handle > 0 && _pos > 0) {
ret = fseek((FILE *)_handle, _pos, SEEK_SET);
_physicalPos = _pos;
_inCache = false;
if (ret != 0) { // Check for problem
_errorSuspend = ResumeError;

View File

@ -35,25 +35,39 @@
*/
class PSPIoStream : public StdioStream, public Suspendable {
protected:
Common::String _path; /* Need to maintain for reopening after suspend */
bool _writeMode; /* "" */
int _pos; /* "" */
mutable int _ferror; /* Save file ferror */
mutable bool _feof; /* and eof */
Common::String _path;
int _fileSize;
bool _writeMode; // for resuming in the right mode
int _physicalPos; // position in the real file
int _pos; // position. Sometimes virtual
bool _inCache; // whether we're in cache (virtual) mode
bool _eos; // EOS flag
enum {
SuspendError = 2,
ResumeError = 3
};
int _errorSuspend;
enum {
CACHE_SIZE = 1024,
MIN_READ_SIZE = 1024 // reading less than 1024 takes exactly the same time as 1024
};
// For caching
char *_cache;
int _cacheStartOffset; // starting offset of the cache. -1 when cache is invalid
mutable int _ferror; // file error state
int _errorSuspend; // for debugging
mutable int _errorSource;
// Error checking
int _errorPos;
void * _errorHandle;
int _suspendCount;
bool synchronizePhysicalPos(); // synchronize the physical and virtual positions
bool isOffsetInCache(uint32 pos); // check if an offset is found in cache
bool isCacheValid() { return _cacheStartOffset != -1; }
public:
/**

View File

@ -195,7 +195,6 @@ bool SdlGraphicsManager::hasFeature(OSystem::Feature f) {
return
(f == OSystem::kFeatureFullscreenMode) ||
(f == OSystem::kFeatureAspectRatioCorrection) ||
(f == OSystem::kFeatureAutoComputeDirtyRects) ||
(f == OSystem::kFeatureCursorHasPalette) ||
(f == OSystem::kFeatureIconifyWindow);
}

View File

@ -0,0 +1,84 @@
Building the ScummVM Android port
=================================
You will need these things to build:
1. Android EGL headers and library
2. Android SDK
3. An arm-android-eabi GCC toolchain
In the example commands, we are going to build against the Android 1.5
native ABI (but using the Android 1.6 SDK tools). Other version
combinations might/should be possible with a bit of tweaking.
In detail:
1. Android EGL headers and library
You can build these from the full Android source, but it is far easier
to just download the 3 Android EGL headers from here:
http://android.git.kernel.org/?p=platform/frameworks/base.git;a=tree;f=opengl/include/EGL;hb=HEAD
(copy them to a directory called "EGL" somewhere)
... and grab libEGL.so off an existing phone/emulator:
adb pull /system/lib/libEGL.so /tmp
2. Android SDK
Download and install somewhere.
3. arm-android-eabi GCC toolchain
You have several choices for toolchains:
- Use Google arm-eabi prebuilt toolchain.
This is shipped with both the Android source release and Android NDK.
The problem is that "arm-eabi-gcc" can't actually link anything
successfully without extra command line flags. To use this with the
ScummVM configure/build environment you will need to create a family
of shell wrapper scripts that convert "arm-android-eabi-foo" to
"arm-eabi-foo -mandroid".
For example, I use this script:
#!/bin/sh
exec arm-eabi-${0##*-} -mandroid -DANDROID "$@"
... and create a family of symlinks/hardlinks pointing to it called
arm-android-eabi-gcc, arm-android-eabi-g++, etc. For tools that don't
take a "-mandroid" argument - like arm-eabi-strip - I bypass the shell
wrapper and just create an arm-android-eabi-strip symlink to the tool
directly.
- Build your own arm-android-eabi toolchain from GCC source.
This is lots of fun. I suggest my Android openembedded patches, see:
http://wiki.github.com/anguslees/openembedded-android/
(You just need to have lots of disk space and type a few commands)
If you get stuck, ask
Alternatively, do a websearch - there are several other cross-compile
toolchains around.
Building ScummVM
================
export ANDROID_SDK=<root of Android SDK>
PATH=$ANDROID_SDK/platforms/android-1.6/tools:$ANDROID_SDK/tools:$PATH
# You also want to ensure your arm-android-eabi toolchain is in your $PATH
export ANDROID_TOP=<root of built Android source>
EGL_INC="-I<location of EGL/ header directory>"
EGL_LIBS="-L<location of libEGL.so>"
CPPFLAGS="$EGL_INC" \
LDFLAGS="-g $EGL_LIBS" \
./configure --backend=android --host=android --enable-zlib #and any other flags
make scummvm.apk
This will build a "monolithic" ScummVM package, with the engines
statically linked in. If you want to build separate engine packages,
like on the market, add "--enable-plugins --default-dynamic" to
configure and also make scummvm-engine-scumm.apk, etc.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,52 @@
# Android specific build targets
AAPT = aapt
DX = dx
APKBUILDER = apkbuilder
ADB = adb -e
ANDROID_JAR = $(ANDROID_SDK)/platforms/android-1.6/android.jar
JAVAC ?= javac
JAVACFLAGS = -source 1.5 -target 1.5
# FIXME: find/mark plugin entry points and add all this back again:
#LDFLAGS += -Wl,--gc-sections
#CXXFLAGS += -ffunction-sections -fdata-sections -fvisibility=hidden -fvisibility-inlines-hidden
scummvm.apk: build.tmp/libscummvm.so resources.ap_ classes.dex
# Package installer won't delete old libscummvm.so on upgrade so
# replace it with a zero size file
$(INSTALL) -d build.stage/common/lib/armeabi
touch build.stage/common/lib/armeabi/libscummvm.so
# We now handle the library unpacking ourselves from mylib/
$(INSTALL) -d build.stage/common/mylib/armeabi
$(INSTALL) -c -m 644 build.tmp/libscummvm.so build.stage/common/mylib/armeabi/
$(STRIP) build.stage/common/mylib/armeabi/libscummvm.so
# "-nf lib/armeabi/libscummvm.so" builds bogus paths?
$(APKBUILDER) $@ -z resources.ap_ -f classes.dex -rf build.stage/common || { $(RM) $@; exit 1; }
scummvm-engine-%.apk: plugins/lib%.so build.tmp/%/resources.ap_ build.tmp/plugins/classes.dex
$(INSTALL) -d build.stage/$*/apk/mylib/armeabi/
$(INSTALL) -c -m 644 plugins/lib$*.so build.stage/$*/apk/mylib/armeabi/
$(STRIP) build.stage/$*/apk/mylib/armeabi/lib$*.so
$(APKBUILDER) $@ -z build.tmp/$*/resources.ap_ -f build.tmp/plugins/classes.dex -rf build.stage/$*/apk || { $(RM) $@; exit 1; }
release/%.apk: %.apk
@$(MKDIR) -p $(@D)
@$(RM) $@
$(CP) $< $@.tmp
# remove debugging signature
zip -d $@.tmp META-INF/\*
jarsigner $(JARSIGNER_FLAGS) $@.tmp release
zipalign 4 $@.tmp $@
$(RM) $@.tmp
androidrelease: release/scummvm.apk $(patsubst plugins/lib%.so,release/scummvm-engine-%.apk,$(PLUGINS))
androidtest: scummvm.apk scummvm-engine-scumm.apk scummvm-engine-kyra.apk
@set -e; for apk in $^; do \
echo $(ADB) install -r $$apk; \
$(ADB) install -r $$apk; \
done
$(ADB) shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n org.inodes.gus.scummvm/.Unpacker
.PHONY: androidrelease androidtest

View File

@ -0,0 +1,414 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#if defined(ANDROID)
#include <jni.h>
#include <sys/types.h>
#include <unistd.h>
#include "common/str.h"
#include "common/stream.h"
#include "common/util.h"
#include "common/archive.h"
#include "common/debug.h"
#include "backends/platform/android/asset-archive.h"
extern JNIEnv* JNU_GetEnv();
// Must match android.content.res.AssetManager.ACCESS_*
const jint ACCESS_UNKNOWN = 0;
const jint ACCESS_RANDOM = 1;
// This might be useful to someone else. Assumes markSupported() == true.
class JavaInputStream : public Common::SeekableReadStream {
public:
JavaInputStream(JNIEnv* env, jobject is);
virtual ~JavaInputStream();
virtual bool eos() const { return _eos; }
virtual bool err() const { return _err; }
virtual void clearErr() { _eos = _err = false; }
virtual uint32 read(void *dataPtr, uint32 dataSize);
virtual int32 pos() const { return _pos; }
virtual int32 size() const { return _len; }
virtual bool seek(int32 offset, int whence = SEEK_SET);
private:
void close(JNIEnv* env);
jmethodID MID_mark;
jmethodID MID_available;
jmethodID MID_close;
jmethodID MID_read;
jmethodID MID_reset;
jmethodID MID_skip;
jobject _input_stream;
jsize _buflen;
jbyteArray _buf;
uint32 _pos;
jint _len;
bool _eos;
bool _err;
};
JavaInputStream::JavaInputStream(JNIEnv* env, jobject is) :
_eos(false), _err(false), _pos(0)
{
_input_stream = env->NewGlobalRef(is);
_buflen = 8192;
_buf = static_cast<jbyteArray>(env->NewGlobalRef(env->NewByteArray(_buflen)));
jclass cls = env->GetObjectClass(_input_stream);
MID_mark = env->GetMethodID(cls, "mark", "(I)V");
assert(MID_mark);
MID_available = env->GetMethodID(cls, "available", "()I");
assert(MID_mark);
MID_close = env->GetMethodID(cls, "close", "()V");
assert(MID_close);
MID_read = env->GetMethodID(cls, "read", "([BII)I");
assert(MID_read);
MID_reset = env->GetMethodID(cls, "reset", "()V");
assert(MID_reset);
MID_skip = env->GetMethodID(cls, "skip", "(J)J");
assert(MID_skip);
// Mark start of stream, so we can reset back to it.
// readlimit is set to something bigger than anything we might
// want to seek within.
env->CallVoidMethod(_input_stream, MID_mark, 10*1024*1024);
_len = env->CallIntMethod(_input_stream, MID_available);
}
JavaInputStream::~JavaInputStream() {
JNIEnv* env = JNU_GetEnv();
close(env);
env->DeleteGlobalRef(_buf);
env->DeleteGlobalRef(_input_stream);
}
void JavaInputStream::close(JNIEnv* env) {
env->CallVoidMethod(_input_stream, MID_close);
if (env->ExceptionCheck())
env->ExceptionClear();
}
uint32 JavaInputStream::read(void *dataPtr, uint32 dataSize) {
JNIEnv* env = JNU_GetEnv();
if (_buflen < dataSize) {
_buflen = dataSize;
env->DeleteGlobalRef(_buf);
_buf = static_cast<jbyteArray>(env->NewGlobalRef(env->NewByteArray(_buflen)));
}
jint ret = env->CallIntMethod(_input_stream, MID_read, _buf, 0, dataSize);
if (env->ExceptionCheck()) {
warning("Exception during JavaInputStream::read(%p, %d)",
dataPtr, dataSize);
env->ExceptionDescribe();
env->ExceptionClear();
_err = true;
ret = -1;
} else if (ret == -1) {
_eos = true;
ret = 0;
} else {
env->GetByteArrayRegion(_buf, 0, ret, static_cast<jbyte*>(dataPtr));
_pos += ret;
}
return ret;
}
bool JavaInputStream::seek(int32 offset, int whence) {
JNIEnv* env = JNU_GetEnv();
uint32 newpos;
switch (whence) {
case SEEK_SET:
newpos = offset;
break;
case SEEK_CUR:
newpos = _pos + offset;
break;
case SEEK_END:
newpos = _len + offset;
break;
default:
debug("Unknown 'whence' arg %d", whence);
return false;
}
jlong skip_bytes;
if (newpos > _pos) {
skip_bytes = newpos - _pos;
} else {
// Can't skip backwards, so jump back to start and skip from there.
env->CallVoidMethod(_input_stream, MID_reset);
if (env->ExceptionCheck()) {
warning("Failed to rewind to start of asset stream");
env->ExceptionDescribe();
env->ExceptionClear();
return false;
}
_pos = 0;
skip_bytes = newpos;
}
while (skip_bytes > 0) {
jlong ret = env->CallLongMethod(_input_stream, MID_skip, skip_bytes);
if (env->ExceptionCheck()) {
warning("Failed to skip %ld bytes into asset stream",
static_cast<long>(skip_bytes));
env->ExceptionDescribe();
env->ExceptionClear();
return false;
} else if (ret == 0) {
warning("InputStream->skip(%ld) didn't skip any bytes. Aborting seek.",
static_cast<long>(skip_bytes));
return false; // No point looping forever...
}
_pos += ret;
skip_bytes -= ret;
}
_eos = false;
return true;
}
// Must match android.content.res.AssetFileDescriptor.UNKNOWN_LENGTH
const jlong UNKNOWN_LENGTH = -1;
// Reading directly from a fd is so much more efficient, that it is
// worth optimising for.
class AssetFdReadStream : public Common::SeekableReadStream {
public:
AssetFdReadStream(JNIEnv* env, jobject assetfd);
virtual ~AssetFdReadStream();
virtual bool eos() const { return _eos; }
virtual bool err() const { return _err; }
virtual void clearErr() { _eos = _err = false; }
virtual uint32 read(void *dataPtr, uint32 dataSize);
virtual int32 pos() const { return _pos; }
virtual int32 size() const { return _declared_len; }
virtual bool seek(int32 offset, int whence = SEEK_SET);
private:
void close(JNIEnv* env);
int _fd;
jmethodID MID_close;
jobject _assetfd;
jlong _start_off;
jlong _declared_len;
uint32 _pos;
bool _eos;
bool _err;
};
AssetFdReadStream::AssetFdReadStream(JNIEnv* env, jobject assetfd) :
_eos(false), _err(false), _pos(0)
{
_assetfd = env->NewGlobalRef(assetfd);
jclass cls = env->GetObjectClass(_assetfd);
MID_close = env->GetMethodID(cls, "close", "()V");
assert(MID_close);
jmethodID MID_getStartOffset =
env->GetMethodID(cls, "getStartOffset", "()J");
assert(MID_getStartOffset);
_start_off = env->CallLongMethod(_assetfd, MID_getStartOffset);
jmethodID MID_getDeclaredLength =
env->GetMethodID(cls, "getDeclaredLength", "()J");
assert(MID_getDeclaredLength);
_declared_len = env->CallLongMethod(_assetfd, MID_getDeclaredLength);
jmethodID MID_getFileDescriptor =
env->GetMethodID(cls, "getFileDescriptor", "()Ljava/io/FileDescriptor;");
assert(MID_getFileDescriptor);
jobject javafd = env->CallObjectMethod(_assetfd, MID_getFileDescriptor);
assert(javafd);
jclass fd_cls = env->GetObjectClass(javafd);
jfieldID FID_descriptor = env->GetFieldID(fd_cls, "descriptor", "I");
assert(FID_descriptor);
_fd = env->GetIntField(javafd, FID_descriptor);
}
AssetFdReadStream::~AssetFdReadStream() {
JNIEnv* env = JNU_GetEnv();
env->CallVoidMethod(_assetfd, MID_close);
if (env->ExceptionCheck())
env->ExceptionClear();
env->DeleteGlobalRef(_assetfd);
}
uint32 AssetFdReadStream::read(void *dataPtr, uint32 dataSize) {
if (_declared_len != UNKNOWN_LENGTH) {
jlong cap = _declared_len - _pos;
if (dataSize > cap)
dataSize = cap;
}
int ret = ::read(_fd, dataPtr, dataSize);
if (ret == 0)
_eos = true;
else if (ret == -1)
_err = true;
else
_pos += ret;
return ret;
}
bool AssetFdReadStream::seek(int32 offset, int whence) {
if (whence == SEEK_SET) {
if (_declared_len != UNKNOWN_LENGTH && offset > _declared_len)
offset = _declared_len;
offset += _start_off;
} else if (whence == SEEK_END && _declared_len != UNKNOWN_LENGTH) {
whence = SEEK_SET;
offset = _start_off + _declared_len + offset;
}
int ret = lseek(_fd, offset, whence);
if (ret == -1)
return false;
_pos = ret - _start_off;
_eos = false;
return true;
}
AndroidAssetArchive::AndroidAssetArchive(jobject am) {
JNIEnv* env = JNU_GetEnv();
_am = env->NewGlobalRef(am);
jclass cls = env->GetObjectClass(_am);
MID_open = env->GetMethodID(cls, "open",
"(Ljava/lang/String;I)Ljava/io/InputStream;");
assert(MID_open);
MID_openFd = env->GetMethodID(cls, "openFd",
"(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
assert(MID_openFd);
MID_list = env->GetMethodID(cls, "list",
"(Ljava/lang/String;)[Ljava/lang/String;");
assert(MID_list);
}
AndroidAssetArchive::~AndroidAssetArchive() {
JNIEnv* env = JNU_GetEnv();
env->DeleteGlobalRef(_am);
}
bool AndroidAssetArchive::hasFile(const Common::String &name) {
JNIEnv* env = JNU_GetEnv();
jstring path = env->NewStringUTF(name.c_str());
jobject result = env->CallObjectMethod(_am, MID_open, path, ACCESS_UNKNOWN);
if (env->ExceptionCheck()) {
// Assume FileNotFoundException
//warning("Error while calling AssetManager->open(%s)", name.c_str());
//env->ExceptionDescribe();
env->ExceptionClear();
env->DeleteLocalRef(path);
return false;
}
env->DeleteLocalRef(result);
env->DeleteLocalRef(path);
return true;
}
int AndroidAssetArchive::listMembers(Common::ArchiveMemberList &member_list) {
JNIEnv* env = JNU_GetEnv();
Common::List<Common::String> dirlist;
dirlist.push_back("");
int count = 0;
while (!dirlist.empty()) {
const Common::String dir = dirlist.back();
dirlist.pop_back();
jstring jpath = env->NewStringUTF(dir.c_str());
jobjectArray jpathlist = static_cast<jobjectArray>(env->CallObjectMethod(_am, MID_list, jpath));
if (env->ExceptionCheck()) {
warning("Error while calling AssetManager->list(%s). Ignoring.",
dir.c_str());
env->ExceptionDescribe();
env->ExceptionClear();
continue; // May as well keep going ...
}
env->DeleteLocalRef(jpath);
for (jsize i = 0; i < env->GetArrayLength(jpathlist); ++i) {
jstring elem = (jstring)env->GetObjectArrayElement(jpathlist, i);
const char* p = env->GetStringUTFChars(elem, NULL);
Common::String thispath = dir;
if (!thispath.empty())
thispath += "/";
thispath += p;
// Assume files have a . in them, and directories don't
if (strchr(p, '.')) {
member_list.push_back(getMember(thispath));
++count;
} else
dirlist.push_back(thispath);
env->ReleaseStringUTFChars(elem, p);
env->DeleteLocalRef(elem);
}
env->DeleteLocalRef(jpathlist);
}
return count;
}
Common::ArchiveMemberPtr AndroidAssetArchive::getMember(const Common::String &name) {
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
}
Common::SeekableReadStream *AndroidAssetArchive::createReadStreamForMember(const Common::String &path) const {
JNIEnv* env = JNU_GetEnv();
jstring jpath = env->NewStringUTF(path.c_str());
// Try openFd() first ...
jobject afd = env->CallObjectMethod(_am, MID_openFd, jpath);
if (env->ExceptionCheck())
env->ExceptionClear();
else if (afd != NULL) {
// success :)
env->DeleteLocalRef(jpath);
return new AssetFdReadStream(env, afd);
}
// ... and fallback to normal open() if that doesn't work
jobject is = env->CallObjectMethod(_am, MID_open, jpath, ACCESS_RANDOM);
if (env->ExceptionCheck()) {
// Assume FileNotFoundException
//warning("Error opening %s", path.c_str());
//env->ExceptionDescribe();
env->ExceptionClear();
env->DeleteLocalRef(jpath);
return NULL;
}
return new JavaInputStream(env, is);
}
#endif

View File

@ -0,0 +1,53 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#if defined(ANDROID)
#include <jni.h>
#include "common/str.h"
#include "common/stream.h"
#include "common/util.h"
#include "common/archive.h"
class AndroidAssetArchive : public Common::Archive {
public:
AndroidAssetArchive(jobject am);
virtual ~AndroidAssetArchive();
virtual bool hasFile(const Common::String &name);
virtual int listMembers(Common::ArchiveMemberList &list);
virtual Common::ArchiveMemberPtr getMember(const Common::String &name);
virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
private:
jmethodID MID_open;
jmethodID MID_openFd;
jmethodID MID_list;
jobject _am;
};
#endif

View File

@ -0,0 +1,85 @@
MODULE := backends/platform/android
MODULE_OBJS := \
android.o asset-archive.o video.o
MODULE_DIRS += \
backends/platform/android/
# We don't use the rules.mk here on purpose
OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) $(OBJS)
JAVA_SRC = \
$(MODULE)/org/inodes/gus/scummvm/ScummVM.java \
$(MODULE)/org/inodes/gus/scummvm/ScummVMApplication.java \
$(MODULE)/org/inodes/gus/scummvm/ScummVMActivity.java \
$(MODULE)/org/inodes/gus/scummvm/EditableSurfaceView.java \
$(MODULE)/org/inodes/gus/scummvm/Unpacker.java \
$(MODULE)/org/inodes/gus/scummvm/Manifest.java \
$(MODULE)/org/inodes/gus/scummvm/R.java
JAVA_PLUGIN_SRC = \
$(MODULE)/org/inodes/gus/scummvm/PluginProvider.java
RESOURCES = \
$(srcdir)/dists/android/res/values/strings.xml \
$(srcdir)/dists/android/res/layout/main.xml \
$(srcdir)/dists/android/res/layout/splash.xml \
$(srcdir)/dists/android/res/drawable/gradient.xml \
$(srcdir)/dists/android/res/drawable/scummvm.png \
$(srcdir)/dists/android/res/drawable/scummvm_big.png
ASSETS = $(DIST_FILES_ENGINEDATA) $(DIST_FILES_THEMES)
PLUGIN_RESOURCES = \
$(srcdir)/dists/android/res/values/strings.xml \
$(srcdir)/dists/android/res/drawable/scummvm.png
# These must be incremented for each market upload
#ANDROID_VERSIONCODE = 6 Specified in dists/android/AndroidManifest.xml.in
ANDROID_PLUGIN_VERSIONCODE = 6
# This library contains scummvm proper
build.tmp/libscummvm.so: $(OBJS)
@$(MKDIR) -p $(@D)
$(CXX) $(PLUGIN_LDFLAGS) -shared $(LDFLAGS) -Wl,-soname,$(@F) -Wl,--no-undefined -o $@ $(PRE_OBJS_FLAGS) $(OBJS) $(POST_OBJS_FLAGS) $(LIBS)
backends/platform/android/org/inodes/gus/scummvm/R.java backends/platform/android/org/inodes/gus/scummvm/Manifest.java: $(srcdir)/dists/android/AndroidManifest.xml $(filter %.xml,$(RESOURCES)) $(ANDROID_JAR)
$(AAPT) package -m -J backends/platform/android -M $< -S $(srcdir)/dists/android/res -I $(ANDROID_JAR)
build.tmp/classes/%.class: $(srcdir)/backends/platform/android/%.java $(srcdir)/backends/platform/android/org/inodes/gus/scummvm/R.java
@$(MKDIR) -p $(@D)
$(JAVAC) $(JAVACFLAGS) -cp $(srcdir)/backends/platform/android -d build.tmp/classes -bootclasspath $(ANDROID_JAR) $<
build.tmp/classes.plugin/%.class: $(srcdir)/backends/platform/android/%.java
@$(MKDIR) -p $(@D)
$(JAVAC) $(JAVACFLAGS) -cp $(srcdir)/backends/platform/android -d build.tmp/classes.plugin -bootclasspath $(ANDROID_JAR) $<
classes.dex: $(JAVA_SRC:backends/platform/android/%.java=build.tmp/classes/%.class)
$(DX) --dex --output=$@ build.tmp/classes
build.tmp/plugins/classes.dex: $(JAVA_PLUGIN_SRC:backends/platform/android/%.java=build.tmp/classes.plugin/%.class)
@$(MKDIR) -p $(@D)
$(DX) --dex --output=$@ build.tmp/classes.plugin
resources.ap_: $(srcdir)/dists/android/AndroidManifest.xml $(RESOURCES) $(ASSETS) $(ANDROID_JAR) $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA)
$(INSTALL) -d build.tmp/assets/
$(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) build.tmp/assets/
$(AAPT) package -f -M $< -S $(srcdir)/dists/android/res -A build.tmp/assets -I $(ANDROID_JAR) -F $@
build.tmp/%/resources.ap_: build.tmp/%/AndroidManifest.xml build.stage/%/res/values/strings.xml build.stage/%/res/drawable/scummvm.png $(ANDROID_JAR)
$(AAPT) package -f -M $< -S build.stage/$*/res -I $(ANDROID_JAR) -F $@
build.tmp/%/AndroidManifest.xml build.stage/%/res/values/strings.xml: dists/android/mkmanifest.pl configure dists/android/AndroidManifest.xml
dists/android/mkmanifest.pl --id=$* --configure=configure \
--version-name=$(VERSION) \
--version-code=$(ANDROID_PLUGIN_VERSIONCODE) \
--stringres=build.stage/$*/res/values/strings.xml \
--manifest=build.tmp/$*/AndroidManifest.xml \
--master-manifest=dists/android/AndroidManifest.xml \
--unpacklib=mylib/armeabi/lib$*.so
build.stage/%/res/drawable/scummvm.png: dists/android/res/drawable/scummvm.png
@$(MKDIR) -p $(@D)
$(CP) $< $@

View File

@ -0,0 +1,59 @@
package org.inodes.gus.scummvm;
import android.content.Context;
import android.text.InputType;
import android.util.AttributeSet;
import android.view.SurfaceView;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
public class EditableSurfaceView extends SurfaceView {
public EditableSurfaceView(Context context) {
super(context);
}
public EditableSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EditableSurfaceView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onCheckIsTextEditor() {
return true;
}
private class MyInputConnection extends BaseInputConnection {
public MyInputConnection() {
super(EditableSurfaceView.this, false);
}
@Override
public boolean performEditorAction(int actionCode) {
if (actionCode == EditorInfo.IME_ACTION_DONE) {
InputMethodManager imm = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getWindowToken(), 0);
}
return super.performEditorAction(actionCode); // Sends enter key
}
}
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
outAttrs.initialCapsMode = 0;
outAttrs.initialSelEnd = outAttrs.initialSelStart = -1;
outAttrs.inputType = (InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_VARIATION_NORMAL |
InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
outAttrs.imeOptions = (EditorInfo.IME_ACTION_DONE |
EditorInfo.IME_FLAG_NO_EXTRACT_UI);
return new MyInputConnection();
}
}

View File

@ -0,0 +1,330 @@
package org.inodes.gus.scummvm;
import android.view.KeyEvent;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class Event {
// Common::EventType enum.
// Must be kept in sync with common/events.h
public final static int EVENT_INVALID = 0;
public final static int EVENT_KEYDOWN = 1;
public final static int EVENT_KEYUP = 2;
public final static int EVENT_MOUSEMOVE = 3;
public final static int EVENT_LBUTTONDOWN = 4;
public final static int EVENT_LBUTTONUP = 5;
public final static int EVENT_RBUTTONDOWN = 6;
public final static int EVENT_RBUTTONUP = 7;
public final static int EVENT_WHEELUP = 8;
public final static int EVENT_WHEELDOWN = 9;
public final static int EVENT_QUIT = 10;
public final static int EVENT_SCREEN_CHANGED = 11;
public final static int EVENT_PREDICTIVE_DIALOG = 12;
public final static int EVENT_MBUTTONDOWN = 13;
public final static int EVENT_MBUTTONUP = 14;
public final static int EVENT_MAINMENU = 15;
public final static int EVENT_RTL = 16;
// common/keyboard.h
public final static int ASCII_F1 = 315;
public final static int ASCII_F2 = 316;
public final static int ASCII_F3 = 317;
public final static int ASCII_F4 = 318;
public final static int ASCII_F5 = 319;
public final static int ASCII_F6 = 320;
public final static int ASCII_F7 = 321;
public final static int ASCII_F8 = 322;
public final static int ASCII_F9 = 323;
public final static int ASCII_F10 = 324;
public final static int ASCII_F11 = 325;
public final static int ASCII_F12 = 326;
public final static int KBD_CTRL = 1 << 0;
public final static int KBD_ALT = 1 << 1;
public final static int KBD_SHIFT = 1 << 2;
public final static int KEYCODE_INVALID = 0;
public final static int KEYCODE_BACKSPACE = 8;
public final static int KEYCODE_TAB = 9;
public final static int KEYCODE_CLEAR = 12;
public final static int KEYCODE_RETURN = 13;
public final static int KEYCODE_PAUSE = 19;
public final static int KEYCODE_ESCAPE = 27;
public final static int KEYCODE_SPACE = 32;
public final static int KEYCODE_EXCLAIM = 33;
public final static int KEYCODE_QUOTEDBL = 34;
public final static int KEYCODE_HASH = 35;
public final static int KEYCODE_DOLLAR = 36;
public final static int KEYCODE_AMPERSAND = 38;
public final static int KEYCODE_QUOTE = 39;
public final static int KEYCODE_LEFTPAREN = 40;
public final static int KEYCODE_RIGHTPAREN = 41;
public final static int KEYCODE_ASTERISK = 42;
public final static int KEYCODE_PLUS = 43;
public final static int KEYCODE_COMMA = 44;
public final static int KEYCODE_MINUS = 45;
public final static int KEYCODE_PERIOD = 46;
public final static int KEYCODE_SLASH = 47;
public final static int KEYCODE_0 = 48;
public final static int KEYCODE_1 = 49;
public final static int KEYCODE_2 = 50;
public final static int KEYCODE_3 = 51;
public final static int KEYCODE_4 = 52;
public final static int KEYCODE_5 = 53;
public final static int KEYCODE_6 = 54;
public final static int KEYCODE_7 = 55;
public final static int KEYCODE_8 = 56;
public final static int KEYCODE_9 = 57;
public final static int KEYCODE_COLON = 58;
public final static int KEYCODE_SEMICOLON = 59;
public final static int KEYCODE_LESS = 60;
public final static int KEYCODE_EQUALS = 61;
public final static int KEYCODE_GREATER = 62;
public final static int KEYCODE_QUESTION = 63;
public final static int KEYCODE_AT = 64;
public final static int KEYCODE_LEFTBRACKET = 91;
public final static int KEYCODE_BACKSLASH = 92;
public final static int KEYCODE_RIGHTBRACKET = 93;
public final static int KEYCODE_CARET = 94;
public final static int KEYCODE_UNDERSCORE = 95;
public final static int KEYCODE_BACKQUOTE = 96;
public final static int KEYCODE_a = 97;
public final static int KEYCODE_b = 98;
public final static int KEYCODE_c = 99;
public final static int KEYCODE_d = 100;
public final static int KEYCODE_e = 101;
public final static int KEYCODE_f = 102;
public final static int KEYCODE_g = 103;
public final static int KEYCODE_h = 104;
public final static int KEYCODE_i = 105;
public final static int KEYCODE_j = 106;
public final static int KEYCODE_k = 107;
public final static int KEYCODE_l = 108;
public final static int KEYCODE_m = 109;
public final static int KEYCODE_n = 110;
public final static int KEYCODE_o = 111;
public final static int KEYCODE_p = 112;
public final static int KEYCODE_q = 113;
public final static int KEYCODE_r = 114;
public final static int KEYCODE_s = 115;
public final static int KEYCODE_t = 116;
public final static int KEYCODE_u = 117;
public final static int KEYCODE_v = 118;
public final static int KEYCODE_w = 119;
public final static int KEYCODE_x = 120;
public final static int KEYCODE_y = 121;
public final static int KEYCODE_z = 122;
public final static int KEYCODE_DELETE = 127;
// Numeric keypad
public final static int KEYCODE_KP0 = 256;
public final static int KEYCODE_KP1 = 257;
public final static int KEYCODE_KP2 = 258;
public final static int KEYCODE_KP3 = 259;
public final static int KEYCODE_KP4 = 260;
public final static int KEYCODE_KP5 = 261;
public final static int KEYCODE_KP6 = 262;
public final static int KEYCODE_KP7 = 263;
public final static int KEYCODE_KP8 = 264;
public final static int KEYCODE_KP9 = 265;
public final static int KEYCODE_KP_PERIOD = 266;
public final static int KEYCODE_KP_DIVIDE = 267;
public final static int KEYCODE_KP_MULTIPLY = 268;
public final static int KEYCODE_KP_MINUS = 269;
public final static int KEYCODE_KP_PLUS = 270;
public final static int KEYCODE_KP_ENTER = 271;
public final static int KEYCODE_KP_EQUALS = 272;
// Arrows + Home/End pad
public final static int KEYCODE_UP = 273;
public final static int KEYCODE_DOWN = 274;
public final static int KEYCODE_RIGHT = 275;
public final static int KEYCODE_LEFT = 276;
public final static int KEYCODE_INSERT = 277;
public final static int KEYCODE_HOME = 278;
public final static int KEYCODE_END = 279;
public final static int KEYCODE_PAGEUP = 280;
public final static int KEYCODE_PAGEDOWN = 281;
// Function keys
public final static int KEYCODE_F1 = 282;
public final static int KEYCODE_F2 = 283;
public final static int KEYCODE_F3 = 284;
public final static int KEYCODE_F4 = 285;
public final static int KEYCODE_F5 = 286;
public final static int KEYCODE_F6 = 287;
public final static int KEYCODE_F7 = 288;
public final static int KEYCODE_F8 = 289;
public final static int KEYCODE_F9 = 290;
public final static int KEYCODE_F10 = 291;
public final static int KEYCODE_F11 = 292;
public final static int KEYCODE_F12 = 293;
public final static int KEYCODE_F13 = 294;
public final static int KEYCODE_F14 = 295;
public final static int KEYCODE_F15 = 296;
// Key state modifier keys
public final static int KEYCODE_NUMLOCK = 300;
public final static int KEYCODE_CAPSLOCK = 301;
public final static int KEYCODE_SCROLLOCK = 302;
public final static int KEYCODE_RSHIFT = 303;
public final static int KEYCODE_LSHIFT = 304;
public final static int KEYCODE_RCTRL = 305;
public final static int KEYCODE_LCTRL = 306;
public final static int KEYCODE_RALT = 307;
public final static int KEYCODE_LALT = 308;
public final static int KEYCODE_RMETA = 309;
public final static int KEYCODE_LMETA = 310;
public final static int KEYCODE_LSUPER = 311; // Left "Windows" key
public final static int KEYCODE_RSUPER = 312; // Right "Windows" key
public final static int KEYCODE_MODE = 313; // "Alt Gr" key
public final static int KEYCODE_COMPOSE = 314; // Multi-key compose key
// Miscellaneous function keys
public final static int KEYCODE_HELP = 315;
public final static int KEYCODE_PRINT = 316;
public final static int KEYCODE_SYSREQ = 317;
public final static int KEYCODE_BREAK = 318;
public final static int KEYCODE_MENU = 319;
public final static int KEYCODE_POWER = 320; // Power Macintosh power key
public final static int KEYCODE_EURO = 321; // Some european keyboards
public final static int KEYCODE_UNDO = 322; // Atari keyboard has Undo
// Android KeyEvent keycode -> ScummVM keycode
public final static Map<Integer, Integer> androidKeyMap;
static {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
map.put(KeyEvent.KEYCODE_DEL, KEYCODE_BACKSPACE);
map.put(KeyEvent.KEYCODE_TAB, KEYCODE_TAB);
map.put(KeyEvent.KEYCODE_CLEAR, KEYCODE_CLEAR);
map.put(KeyEvent.KEYCODE_ENTER, KEYCODE_RETURN);
//map.put(??, KEYCODE_PAUSE);
map.put(KeyEvent.KEYCODE_BACK, KEYCODE_ESCAPE);
map.put(KeyEvent.KEYCODE_SPACE, KEYCODE_SPACE);
//map.put(??, KEYCODE_EXCLAIM);
//map.put(??, KEYCODE_QUOTEDBL);
map.put(KeyEvent.KEYCODE_POUND, KEYCODE_HASH);
//map.put(??, KEYCODE_DOLLAR);
//map.put(??, KEYCODE_AMPERSAND);
map.put(KeyEvent.KEYCODE_APOSTROPHE, KEYCODE_QUOTE);
//map.put(??, KEYCODE_LEFTPAREN);
//map.put(??, KEYCODE_RIGHTPAREN);
//map.put(??, KEYCODE_ASTERISK);
map.put(KeyEvent.KEYCODE_PLUS, KEYCODE_PLUS);
map.put(KeyEvent.KEYCODE_COMMA, KEYCODE_COMMA);
map.put(KeyEvent.KEYCODE_MINUS, KEYCODE_MINUS);
map.put(KeyEvent.KEYCODE_PERIOD, KEYCODE_PERIOD);
map.put(KeyEvent.KEYCODE_SLASH, KEYCODE_SLASH);
map.put(KeyEvent.KEYCODE_0, KEYCODE_0);
map.put(KeyEvent.KEYCODE_1, KEYCODE_1);
map.put(KeyEvent.KEYCODE_2, KEYCODE_2);
map.put(KeyEvent.KEYCODE_3, KEYCODE_3);
map.put(KeyEvent.KEYCODE_4, KEYCODE_4);
map.put(KeyEvent.KEYCODE_5, KEYCODE_5);
map.put(KeyEvent.KEYCODE_6, KEYCODE_6);
map.put(KeyEvent.KEYCODE_7, KEYCODE_7);
map.put(KeyEvent.KEYCODE_8, KEYCODE_8);
map.put(KeyEvent.KEYCODE_9, KEYCODE_9);
//map.put(??, KEYCODE_COLON);
map.put(KeyEvent.KEYCODE_SEMICOLON, KEYCODE_SEMICOLON);
//map.put(??, KEYCODE_LESS);
map.put(KeyEvent.KEYCODE_EQUALS, KEYCODE_EQUALS);
//map.put(??, KEYCODE_GREATER);
//map.put(??, KEYCODE_QUESTION);
map.put(KeyEvent.KEYCODE_AT, KEYCODE_AT);
map.put(KeyEvent.KEYCODE_LEFT_BRACKET, KEYCODE_LEFTBRACKET);
map.put(KeyEvent.KEYCODE_BACKSLASH, KEYCODE_BACKSLASH);
map.put(KeyEvent.KEYCODE_RIGHT_BRACKET, KEYCODE_RIGHTBRACKET);
//map.put(??, KEYCODE_CARET);
//map.put(??, KEYCODE_UNDERSCORE);
//map.put(??, KEYCODE_BACKQUOTE);
map.put(KeyEvent.KEYCODE_A, KEYCODE_a);
map.put(KeyEvent.KEYCODE_B, KEYCODE_b);
map.put(KeyEvent.KEYCODE_C, KEYCODE_c);
map.put(KeyEvent.KEYCODE_D, KEYCODE_d);
map.put(KeyEvent.KEYCODE_E, KEYCODE_e);
map.put(KeyEvent.KEYCODE_F, KEYCODE_f);
map.put(KeyEvent.KEYCODE_G, KEYCODE_g);
map.put(KeyEvent.KEYCODE_H, KEYCODE_h);
map.put(KeyEvent.KEYCODE_I, KEYCODE_i);
map.put(KeyEvent.KEYCODE_J, KEYCODE_j);
map.put(KeyEvent.KEYCODE_K, KEYCODE_k);
map.put(KeyEvent.KEYCODE_L, KEYCODE_l);
map.put(KeyEvent.KEYCODE_M, KEYCODE_m);
map.put(KeyEvent.KEYCODE_N, KEYCODE_n);
map.put(KeyEvent.KEYCODE_O, KEYCODE_o);
map.put(KeyEvent.KEYCODE_P, KEYCODE_p);
map.put(KeyEvent.KEYCODE_Q, KEYCODE_q);
map.put(KeyEvent.KEYCODE_R, KEYCODE_r);
map.put(KeyEvent.KEYCODE_S, KEYCODE_s);
map.put(KeyEvent.KEYCODE_T, KEYCODE_t);
map.put(KeyEvent.KEYCODE_U, KEYCODE_u);
map.put(KeyEvent.KEYCODE_V, KEYCODE_v);
map.put(KeyEvent.KEYCODE_W, KEYCODE_w);
map.put(KeyEvent.KEYCODE_X, KEYCODE_x);
map.put(KeyEvent.KEYCODE_Y, KEYCODE_y);
map.put(KeyEvent.KEYCODE_Z, KEYCODE_z);
//map.put(KeyEvent.KEYCODE_DEL, KEYCODE_DELETE); use BACKSPACE instead
//map.put(??, KEYCODE_KP_*);
map.put(KeyEvent.KEYCODE_DPAD_UP, KEYCODE_UP);
map.put(KeyEvent.KEYCODE_DPAD_DOWN, KEYCODE_DOWN);
map.put(KeyEvent.KEYCODE_DPAD_RIGHT, KEYCODE_RIGHT);
map.put(KeyEvent.KEYCODE_DPAD_LEFT, KEYCODE_LEFT);
//map.put(??, KEYCODE_INSERT);
//map.put(??, KEYCODE_HOME);
//map.put(??, KEYCODE_END);
//map.put(??, KEYCODE_PAGEUP);
//map.put(??, KEYCODE_PAGEDOWN);
//map.put(??, KEYCODE_F{1-15});
map.put(KeyEvent.KEYCODE_NUM, KEYCODE_NUMLOCK);
//map.put(??, KEYCODE_CAPSLOCK);
//map.put(??, KEYCODE_SCROLLLOCK);
map.put(KeyEvent.KEYCODE_SHIFT_RIGHT, KEYCODE_RSHIFT);
map.put(KeyEvent.KEYCODE_SHIFT_LEFT, KEYCODE_LSHIFT);
//map.put(??, KEYCODE_RCTRL);
//map.put(??, KEYCODE_LCTRL);
map.put(KeyEvent.KEYCODE_ALT_RIGHT, KEYCODE_RALT);
map.put(KeyEvent.KEYCODE_ALT_LEFT, KEYCODE_LALT);
// ?? META, SUPER
// ?? MODE, COMPOSE
// ?? HELP, PRINT, SYSREQ, BREAK, EURO, UNDO
map.put(KeyEvent.KEYCODE_MENU, KEYCODE_MENU);
map.put(KeyEvent.KEYCODE_POWER, KEYCODE_POWER);
androidKeyMap = Collections.unmodifiableMap(map);
}
public int type;
public boolean synthetic;
public int kbd_keycode;
public int kbd_ascii;
public int kbd_flags;
public int mouse_x;
public int mouse_y;
public boolean mouse_relative; // Used for trackball events
public Event() {
type = EVENT_INVALID;
synthetic = false;
}
public Event(int type) {
this.type = type;
synthetic = false;
}
public static Event KeyboardEvent(int type, int keycode, int ascii,
int flags) {
Event e = new Event();
e.type = type;
e.kbd_keycode = keycode;
e.kbd_ascii = ascii;
e.kbd_flags = flags;
return e;
}
public static Event MouseEvent(int type, int x, int y) {
Event e = new Event();
e.type = type;
e.mouse_x = x;
e.mouse_y = y;
return e;
}
}

View File

@ -0,0 +1,52 @@
package org.inodes.gus.scummvm;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import java.util.ArrayList;
public class PluginProvider extends BroadcastReceiver {
public final static String META_UNPACK_LIB =
"org.inodes.gus.scummvm.meta.UNPACK_LIB";
public void onReceive(Context context, Intent intent) {
if (!intent.getAction().equals(ScummVMApplication.ACTION_PLUGIN_QUERY))
return;
Bundle extras = getResultExtras(true);
final ActivityInfo info;
try {
info = context.getPackageManager()
.getReceiverInfo(new ComponentName(context, this.getClass()),
PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
Log.e(this.toString(), "Error finding my own info?", e);
return;
}
String mylib = info.metaData.getString(META_UNPACK_LIB);
if (mylib != null) {
ArrayList<String> all_libs =
extras.getStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS);
all_libs.add(new Uri.Builder()
.scheme("plugin")
.authority(context.getPackageName())
.path(mylib)
.toString());
extras.putStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS,
all_libs);
}
setResultExtras(extras);
}
}

View File

@ -0,0 +1,317 @@
package org.inodes.gus.scummvm;
import android.content.Context;
import android.content.res.AssetManager;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import java.io.File;
import java.util.concurrent.Semaphore;
// At least in Android 2.1, eglCreateWindowSurface() requires an
// EGLNativeWindowSurface object, which is hidden deep in the bowels
// of libui. Until EGL is properly exposed, it's probably safer to
// use the Java versions of most EGL functions :(
public class ScummVM implements SurfaceHolder.Callback {
private final static String LOG_TAG = "ScummVM.java";
private final int AUDIO_FRAME_SIZE = 2 * 2; // bytes. 16bit audio * stereo
public static class AudioSetupException extends Exception {}
private long nativeScummVM; // native code hangs itself here
boolean scummVMRunning = false;
private native void create(AssetManager am);
public ScummVM(Context context) {
create(context.getAssets()); // Init C++ code, set nativeScummVM
}
private native void nativeDestroy();
public synchronized void destroy() {
if (nativeScummVM != 0) {
nativeDestroy();
nativeScummVM = 0;
}
}
protected void finalize() {
destroy();
}
// Surface creation:
// GUI thread: create surface, release lock
// ScummVM thread: acquire lock (block), read surface
//
// Surface deletion:
// GUI thread: post event, acquire lock (block), return
// ScummVM thread: read event, free surface, release lock
//
// In other words, ScummVM thread does this:
// acquire lock
// setup surface
// when SCREEN_CHANGED arrives:
// destroy surface
// release lock
// back to acquire lock
static final int configSpec[] = {
EGL10.EGL_RED_SIZE, 5,
EGL10.EGL_GREEN_SIZE, 5,
EGL10.EGL_BLUE_SIZE, 5,
EGL10.EGL_DEPTH_SIZE, 0,
EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
EGL10.EGL_NONE,
};
EGL10 egl;
EGLDisplay eglDisplay = EGL10.EGL_NO_DISPLAY;
EGLConfig eglConfig;
EGLContext eglContext = EGL10.EGL_NO_CONTEXT;
EGLSurface eglSurface = EGL10.EGL_NO_SURFACE;
Semaphore surfaceLock = new Semaphore(0, true);
SurfaceHolder nativeSurface;
public void surfaceCreated(SurfaceHolder holder) {
nativeSurface = holder;
surfaceLock.release();
}
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
// Disabled while I debug GL problems
//pushEvent(new Event(Event.EVENT_SCREEN_CHANGED));
}
public void surfaceDestroyed(SurfaceHolder holder) {
pushEvent(new Event(Event.EVENT_SCREEN_CHANGED));
try {
surfaceLock.acquire();
} catch (InterruptedException e) {
Log.e(this.toString(),
"Interrupted while waiting for surface lock", e);
}
}
// Called by ScummVM thread (from initBackend)
private void createScummVMGLContext() {
egl = (EGL10)EGLContext.getEGL();
eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
int[] version = new int[2];
egl.eglInitialize(eglDisplay, version);
int[] num_config = new int[1];
egl.eglChooseConfig(eglDisplay, configSpec, null, 0, num_config);
final int numConfigs = num_config[0];
if (numConfigs <= 0)
throw new IllegalArgumentException("No configs match configSpec");
EGLConfig[] configs = new EGLConfig[numConfigs];
egl.eglChooseConfig(eglDisplay, configSpec, configs, numConfigs,
num_config);
eglConfig = configs[0];
eglContext = egl.eglCreateContext(eglDisplay, eglConfig,
EGL10.EGL_NO_CONTEXT, null);
}
// Called by ScummVM thread
protected void setupScummVMSurface() {
try {
surfaceLock.acquire();
} catch (InterruptedException e) {
Log.e(this.toString(),
"Interrupted while waiting for surface lock", e);
return;
}
eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig,
nativeSurface, null);
egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
}
// Called by ScummVM thread
protected void destroyScummVMSurface() {
if (eglSurface != null) {
egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
egl.eglDestroySurface(eglDisplay, eglSurface);
eglSurface = EGL10.EGL_NO_SURFACE;
}
surfaceLock.release();
}
public void setSurface(SurfaceHolder holder) {
holder.addCallback(this);
}
// Set scummvm config options
final public native static void loadConfigFile(String path);
final public native static void setConfMan(String key, int value);
final public native static void setConfMan(String key, String value);
// Feed an event to ScummVM. Safe to call from other threads.
final public native void pushEvent(Event e);
final private native void audioMixCallback(byte[] buf);
// Runs the actual ScummVM program and returns when it does.
// This should not be called from multiple threads simultaneously...
final public native int scummVMMain(String[] argv);
// Callbacks from C++ peer instance
//protected GraphicsMode[] getSupportedGraphicsModes() {}
protected void displayMessageOnOSD(String msg) {}
protected void setWindowCaption(String caption) {}
protected void showVirtualKeyboard(boolean enable) {}
protected String[] getSysArchives() { return new String[0]; }
protected String[] getPluginDirectories() { return new String[0]; }
protected void initBackend() throws AudioSetupException {
createScummVMGLContext();
initAudio();
}
private static class AudioThread extends Thread {
final private int buf_size;
private boolean is_paused = false;
final private ScummVM scummvm;
final private AudioTrack audio_track;
AudioThread(ScummVM scummvm, AudioTrack audio_track, int buf_size) {
super("AudioThread");
this.scummvm = scummvm;
this.audio_track = audio_track;
this.buf_size = buf_size;
setPriority(Thread.MAX_PRIORITY);
setDaemon(true);
}
public void pauseAudio() {
synchronized (this) {
is_paused = true;
}
audio_track.pause();
}
public void resumeAudio() {
synchronized (this) {
is_paused = false;
notifyAll();
}
audio_track.play();
}
public void run() {
byte[] buf = new byte[buf_size];
audio_track.play();
int offset = 0;
try {
while (true) {
synchronized (this) {
while (is_paused)
wait();
}
if (offset == buf.length) {
// Grab new audio data
scummvm.audioMixCallback(buf);
offset = 0;
}
int len = buf.length - offset;
int ret = audio_track.write(buf, offset, len);
if (ret < 0) {
Log.w(LOG_TAG, String.format(
"AudioTrack.write(%dB) returned error %d",
buf.length, ret));
break;
} else if (ret != len) {
Log.w(LOG_TAG, String.format(
"Short audio write. Wrote %dB, not %dB",
ret, buf.length));
// Buffer is full, so yield cpu for a while
Thread.sleep(100);
}
offset += ret;
}
} catch (InterruptedException e) {
Log.e(this.toString(), "Audio thread interrupted", e);
}
}
}
private AudioThread audio_thread;
final public int audioSampleRate() {
return AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);
}
private void initAudio() throws AudioSetupException {
int sample_rate = audioSampleRate();
int buf_size =
AudioTrack.getMinBufferSize(sample_rate,
AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT);
if (buf_size < 0) {
int guess = AUDIO_FRAME_SIZE * sample_rate / 100; // 10ms of audio
Log.w(LOG_TAG, String.format(
"Unable to get min audio buffer size (error %d). Guessing %dB.",
buf_size, guess));
buf_size = guess;
}
Log.d(LOG_TAG, String.format("Using %dB buffer for %dHZ audio",
buf_size, sample_rate));
AudioTrack audio_track =
new AudioTrack(AudioManager.STREAM_MUSIC,
sample_rate,
AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT,
buf_size,
AudioTrack.MODE_STREAM);
if (audio_track.getState() != AudioTrack.STATE_INITIALIZED) {
Log.e(LOG_TAG, "Error initialising Android audio system.");
throw new AudioSetupException();
}
audio_thread = new AudioThread(this, audio_track, buf_size);
audio_thread.start();
}
public void pause() {
audio_thread.pauseAudio();
// TODO: need to pause engine too
}
public void resume() {
// TODO: need to resume engine too
audio_thread.resumeAudio();
}
static {
// For grabbing with gdb...
final boolean sleep_for_debugger = false;
if (sleep_for_debugger) {
try {
Thread.sleep(20*1000);
} catch (InterruptedException e) {
}
}
//System.loadLibrary("scummvm");
File cache_dir = ScummVMApplication.getLastCacheDir();
String libname = System.mapLibraryName("scummvm");
File libpath = new File(cache_dir, libname);
System.load(libpath.getPath());
}
}

View File

@ -0,0 +1,446 @@
package org.inodes.gus.scummvm;
import android.app.AlertDialog;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.res.Configuration;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.Toast;
import java.io.IOException;
public class ScummVMActivity extends Activity {
private boolean _do_right_click;
private boolean _last_click_was_right;
// game pixels to move per trackball/dpad event.
// FIXME: replace this with proper mouse acceleration
private final static int TRACKBALL_SCALE = 2;
private class MyScummVM extends ScummVM {
private boolean scummvmRunning = false;
public MyScummVM() {
super(ScummVMActivity.this);
}
@Override
protected void initBackend() throws ScummVM.AudioSetupException {
synchronized (this) {
scummvmRunning = true;
notifyAll();
}
super.initBackend();
}
public void waitUntilRunning() throws InterruptedException {
synchronized (this) {
while (!scummvmRunning)
wait();
}
}
@Override
protected void displayMessageOnOSD(String msg) {
Log.i(this.toString(), "OSD: " + msg);
Toast.makeText(ScummVMActivity.this, msg, Toast.LENGTH_LONG).show();
}
@Override
protected void setWindowCaption(final String caption) {
runOnUiThread(new Runnable() {
public void run() {
setTitle(caption);
}
});
}
@Override
protected String[] getPluginDirectories() {
String[] dirs = new String[1];
dirs[0] = ScummVMApplication.getLastCacheDir().getPath();
return dirs;
}
@Override
protected void showVirtualKeyboard(final boolean enable) {
if (getResources().getConfiguration().keyboard ==
Configuration.KEYBOARD_NOKEYS) {
runOnUiThread(new Runnable() {
public void run() {
showKeyboard(enable);
}
});
}
}
}
private MyScummVM scummvm;
private Thread scummvm_thread;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_do_right_click = false;
setVolumeControlStream(AudioManager.STREAM_MUSIC);
setContentView(R.layout.main);
takeKeyEvents(true);
// This is a common enough error that we should warn about it
// explicitly.
if (!Environment.getExternalStorageDirectory().canRead()) {
new AlertDialog.Builder(this)
.setTitle(R.string.no_sdcard_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.no_sdcard)
.setNegativeButton(R.string.quit,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
finish();
}
})
.show();
return;
}
SurfaceView main_surface = (SurfaceView)findViewById(R.id.main_surface);
main_surface.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
return onTouchEvent(event);
}
});
main_surface.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int code, KeyEvent ev) {
return onKeyDown(code, ev);
}
});
main_surface.requestFocus();
// Start ScummVM
scummvm = new MyScummVM();
scummvm_thread = new Thread(new Runnable() {
public void run() {
try {
runScummVM();
} catch (Exception e) {
Log.e("ScummVM", "Fatal error in ScummVM thread", e);
new AlertDialog.Builder(ScummVMActivity.this)
.setTitle("Error")
.setMessage(e.toString())
.setIcon(android.R.drawable.ic_dialog_alert)
.show();
finish();
}
}
}, "ScummVM");
scummvm_thread.start();
// Block UI thread until ScummVM has started. In particular,
// this means that surface and event callbacks should be safe
// after this point.
try {
scummvm.waitUntilRunning();
} catch (InterruptedException e) {
Log.e(this.toString(),
"Interrupted while waiting for ScummVM.initBackend", e);
finish();
}
scummvm.setSurface(main_surface.getHolder());
}
// Runs in another thread
private void runScummVM() throws IOException {
getFilesDir().mkdirs();
String[] args = {
"ScummVM-lib",
"--config=" + getFileStreamPath("scummvmrc").getPath(),
"--path=" + Environment.getExternalStorageDirectory().getPath(),
"--gui-theme=scummmodern",
"--savepath=" + getDir("saves", 0).getPath(),
};
int ret = scummvm.scummVMMain(args);
// On exit, tear everything down for a fresh
// restart next time.
System.exit(ret);
}
private boolean was_paused = false;
@Override
public void onPause() {
if (scummvm != null) {
was_paused = true;
scummvm.pause();
}
super.onPause();
}
@Override
public void onResume() {
super.onResume();
if (scummvm != null && was_paused)
scummvm.resume();
was_paused = false;
}
@Override
public void onStop() {
if (scummvm != null) {
scummvm.pushEvent(new Event(Event.EVENT_QUIT));
try {
scummvm_thread.join(1000); // 1s timeout
} catch (InterruptedException e) {
Log.i(this.toString(),
"Error while joining ScummVM thread", e);
}
}
super.onStop();
}
static final int MSG_MENU_LONG_PRESS = 1;
private final Handler keycodeMenuTimeoutHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_MENU_LONG_PRESS) {
InputMethodManager imm = (InputMethodManager)
getSystemService(INPUT_METHOD_SERVICE);
if (imm != null)
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
}
};
@Override
public boolean onKeyUp(int keyCode, KeyEvent kevent) {
return onKeyDown(keyCode, kevent);
}
@Override
public boolean onKeyMultiple(int keyCode, int repeatCount,
KeyEvent kevent) {
return onKeyDown(keyCode, kevent);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent kevent) {
// Filter out "special" keys
switch (keyCode) {
case KeyEvent.KEYCODE_MENU:
// Have to reimplement hold-down-menu-brings-up-softkeybd
// ourselves, since we are otherwise hijacking the menu
// key :(
// See com.android.internal.policy.impl.PhoneWindow.onKeyDownPanel()
// for the usual Android implementation of this feature.
if (kevent.getRepeatCount() > 0)
// Ignore keyrepeat for menu
return false;
boolean timeout_fired = false;
if (getResources().getConfiguration().keyboard ==
Configuration.KEYBOARD_NOKEYS) {
timeout_fired = !keycodeMenuTimeoutHandler.hasMessages(MSG_MENU_LONG_PRESS);
keycodeMenuTimeoutHandler.removeMessages(MSG_MENU_LONG_PRESS);
if (kevent.getAction() == KeyEvent.ACTION_DOWN) {
keycodeMenuTimeoutHandler.sendMessageDelayed(
keycodeMenuTimeoutHandler.obtainMessage(MSG_MENU_LONG_PRESS),
ViewConfiguration.getLongPressTimeout());
return true;
}
}
if (kevent.getAction() == KeyEvent.ACTION_UP) {
if (!timeout_fired)
scummvm.pushEvent(new Event(Event.EVENT_MAINMENU));
return true;
}
return false;
case KeyEvent.KEYCODE_CAMERA:
case KeyEvent.KEYCODE_SEARCH:
_do_right_click = (kevent.getAction() == KeyEvent.ACTION_DOWN);
return true;
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT: {
// HTC Hero doesn't seem to generate
// MotionEvent.ACTION_DOWN events on trackball press :(
// We'll have to just fake one here.
// Some other handsets lack a trackball, so the DPAD is
// the only way of moving the cursor.
int motion_action;
// FIXME: this logic is a mess.
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
switch (kevent.getAction()) {
case KeyEvent.ACTION_DOWN:
motion_action = MotionEvent.ACTION_DOWN;
break;
case KeyEvent.ACTION_UP:
motion_action = MotionEvent.ACTION_UP;
break;
default: // ACTION_MULTIPLE
return false;
}
} else
motion_action = MotionEvent.ACTION_MOVE;
Event e = new Event(getEventType(motion_action));
e.mouse_x = 0;
e.mouse_y = 0;
e.mouse_relative = true;
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
e.mouse_y = -TRACKBALL_SCALE;
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
e.mouse_y = TRACKBALL_SCALE;
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
e.mouse_x = -TRACKBALL_SCALE;
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
e.mouse_x = TRACKBALL_SCALE;
break;
}
scummvm.pushEvent(e);
return true;
}
case KeyEvent.KEYCODE_BACK:
// skip isSystem() check and fall through to main code
break;
default:
if (kevent.isSystem())
return false;
}
// FIXME: what do I need to do for composed characters?
Event e = new Event();
switch (kevent.getAction()) {
case KeyEvent.ACTION_DOWN:
e.type = Event.EVENT_KEYDOWN;
e.synthetic = false;
break;
case KeyEvent.ACTION_UP:
e.type = Event.EVENT_KEYUP;
e.synthetic = false;
break;
case KeyEvent.ACTION_MULTIPLE:
// e.type is handled below
e.synthetic = true;
break;
default:
return false;
}
e.kbd_keycode = Event.androidKeyMap.containsKey(keyCode) ?
Event.androidKeyMap.get(keyCode) : Event.KEYCODE_INVALID;
e.kbd_ascii = kevent.getUnicodeChar();
if (e.kbd_ascii == 0)
e.kbd_ascii = e.kbd_keycode; // scummvm keycodes are mostly ascii
e.kbd_flags = 0;
if (kevent.isAltPressed())
e.kbd_flags |= Event.KBD_ALT;
if (kevent.isSymPressed()) // no ctrl key in android, so use sym (?)
e.kbd_flags |= Event.KBD_CTRL;
if (kevent.isShiftPressed()) {
if (keyCode >= KeyEvent.KEYCODE_0 &&
keyCode <= KeyEvent.KEYCODE_9) {
// Shift+number -> convert to F* key
int offset = keyCode == KeyEvent.KEYCODE_0 ?
10 : keyCode - KeyEvent.KEYCODE_1; // turn 0 into 10
e.kbd_keycode = Event.KEYCODE_F1 + offset;
e.kbd_ascii = Event.ASCII_F1 + offset;
} else
e.kbd_flags |= Event.KBD_SHIFT;
}
if (kevent.getAction() == KeyEvent.ACTION_MULTIPLE) {
for (int i = 0; i <= kevent.getRepeatCount(); i++) {
e.type = Event.EVENT_KEYDOWN;
scummvm.pushEvent(e);
e.type = Event.EVENT_KEYUP;
scummvm.pushEvent(e);
}
} else
scummvm.pushEvent(e);
return true;
}
private int getEventType(int action) {
switch (action) {
case MotionEvent.ACTION_DOWN:
_last_click_was_right = _do_right_click;
return _last_click_was_right ?
Event.EVENT_RBUTTONDOWN : Event.EVENT_LBUTTONDOWN;
case MotionEvent.ACTION_UP:
return _last_click_was_right ?
Event.EVENT_RBUTTONUP : Event.EVENT_LBUTTONUP;
case MotionEvent.ACTION_MOVE:
return Event.EVENT_MOUSEMOVE;
default:
return Event.EVENT_INVALID;
}
}
@Override
public boolean onTrackballEvent(MotionEvent event) {
int type = getEventType(event.getAction());
if (type == Event.EVENT_INVALID)
return false;
Event e = new Event(type);
e.mouse_x =
(int)(event.getX() * event.getXPrecision()) * TRACKBALL_SCALE;
e.mouse_y =
(int)(event.getY() * event.getYPrecision()) * TRACKBALL_SCALE;
e.mouse_relative = true;
scummvm.pushEvent(e);
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int type = getEventType(event.getAction());
if (type == Event.EVENT_INVALID)
return false;
Event e = new Event(type);
e.mouse_x = (int)event.getX();
e.mouse_y = (int)event.getY();
e.mouse_relative = false;
scummvm.pushEvent(e);
return true;
}
private void showKeyboard(boolean show) {
SurfaceView main_surface = (SurfaceView)findViewById(R.id.main_surface);
InputMethodManager imm = (InputMethodManager)
getSystemService(INPUT_METHOD_SERVICE);
if (show)
imm.showSoftInput(main_surface, InputMethodManager.SHOW_IMPLICIT);
else
imm.hideSoftInputFromWindow(main_surface.getWindowToken(),
InputMethodManager.HIDE_IMPLICIT_ONLY);
}
}

View File

@ -0,0 +1,29 @@
package org.inodes.gus.scummvm;
import android.app.Application;
import java.io.File;
public class ScummVMApplication extends Application {
public final static String ACTION_PLUGIN_QUERY = "org.inodes.gus.scummvm.action.PLUGIN_QUERY";
public final static String EXTRA_UNPACK_LIBS = "org.inodes.gus.scummvm.extra.UNPACK_LIBS";
private static File cache_dir;
@Override
public void onCreate() {
super.onCreate();
// This is still on /data :(
cache_dir = getCacheDir();
// This is mounted noexec :(
//cache_dir = new File(Environment.getExternalStorageDirectory(),
// "/.ScummVM.tmp");
// This is owned by download manager and requires special
// permissions to access :(
//cache_dir = Environment.getDownloadCacheDirectory();
}
public static File getLastCacheDir() {
return cache_dir;
}
}

View File

@ -0,0 +1,370 @@
package org.inodes.gus.scummvm;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.ProgressBar;
import java.io.IOException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipFile;
import java.util.zip.ZipEntry;
public class Unpacker extends Activity {
private final static String META_NEXT_ACTIVITY =
"org.inodes.gus.unpacker.nextActivity";
private ProgressBar mProgress;
private File mUnpackDest; // location to unpack into
private AsyncTask<String, Integer, Void> mUnpacker;
private final static int REQUEST_MARKET = 1;
private static class UnpackJob {
public ZipFile zipfile;
public Set<String> paths;
public UnpackJob(ZipFile zipfile, Set<String> paths) {
this.zipfile = zipfile;
this.paths = paths;
}
public long UnpackSize() {
long size = 0;
for (String path: paths) {
ZipEntry entry = zipfile.getEntry(path);
if (entry != null) size += entry.getSize();
}
return size;
}
}
private class UnpackTask extends AsyncTask<String, Integer, Void> {
@Override
protected void onProgressUpdate(Integer... progress) {
mProgress.setIndeterminate(false);
mProgress.setMax(progress[1]);
mProgress.setProgress(progress[0]);
mProgress.postInvalidate();
}
@Override
protected void onPostExecute(Void result) {
Bundle md = getMetaData();
String nextActivity = md.getString(META_NEXT_ACTIVITY);
if (nextActivity != null) {
final ComponentName cn =
ComponentName.unflattenFromString(nextActivity);
if (cn != null) {
final Intent origIntent = getIntent();
Intent intent = new Intent();
intent.setPackage(origIntent.getPackage());
intent.setComponent(cn);
if (origIntent.getExtras() != null)
intent.putExtras(origIntent.getExtras());
intent.putExtra(Intent.EXTRA_INTENT, origIntent);
intent.setDataAndType(origIntent.getData(),
origIntent.getType());
//intent.fillIn(getIntent(), 0);
intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
Log.i(this.toString(),
"Starting next activity with intent " + intent);
startActivity(intent);
} else {
Log.w(this.toString(),
"Unable to extract a component name from " + nextActivity);
}
}
finish();
}
@Override
protected Void doInBackground(String... all_libs) {
// This will contain all unpack jobs
Map<String, UnpackJob> unpack_jobs =
new HashMap<String, UnpackJob>(all_libs.length);
// This will contain all unpack filenames (so we can
// detect stale files in the unpack directory)
Set<String> all_files = new HashSet<String>(all_libs.length);
for (String lib: all_libs) {
final Uri uri = Uri.parse(lib);
final String pkg = uri.getAuthority();
final String path = uri.getPath().substring(1); // skip first /
all_files.add(new File(path).getName());
UnpackJob job = unpack_jobs.get(pkg);
if (job == null) {
try {
// getPackageResourcePath is hidden in Context,
// but exposed in ContextWrapper...
ContextWrapper context =
new ContextWrapper(createPackageContext(pkg, 0));
ZipFile zipfile =
new ZipFile(context.getPackageResourcePath());
job = new UnpackJob(zipfile, new HashSet<String>(1));
} catch (PackageManager.NameNotFoundException e) {
Log.e(this.toString(), "Package " + pkg +
" not found", e);
continue;
} catch (IOException e) {
// FIXME: show some sort of GUI error dialog
Log.e(this.toString(),
"Error opening ZIP for package " + pkg, e);
continue;
}
unpack_jobs.put(pkg, job);
}
job.paths.add(path);
}
// Delete stale filenames from mUnpackDest
for (File file: mUnpackDest.listFiles()) {
if (!all_files.contains(file.getName())) {
Log.i(this.toString(),
"Deleting stale cached file " + file);
file.delete();
}
}
int total_size = 0;
for (UnpackJob job: unpack_jobs.values())
total_size += job.UnpackSize();
publishProgress(0, total_size);
mUnpackDest.mkdirs();
int progress = 0;
for (UnpackJob job: unpack_jobs.values()) {
try {
ZipFile zipfile = job.zipfile;
for (String path: job.paths) {
ZipEntry zipentry = zipfile.getEntry(path);
if (zipentry == null)
throw new FileNotFoundException(
"Couldn't find " + path + " in zip");
File dest = new File(mUnpackDest, new File(path).getName());
if (dest.exists() &&
dest.lastModified() == zipentry.getTime() &&
dest.length() == zipentry.getSize()) {
// Already unpacked
progress += zipentry.getSize();
} else {
if (dest.exists())
Log.d(this.toString(),
"Replacing " + dest.getPath() +
" old.mtime=" + dest.lastModified() +
" new.mtime=" + zipentry.getTime() +
" old.size=" + dest.length() +
" new.size=" + zipentry.getSize());
else
Log.i(this.toString(),
"Extracting " + zipentry.getName() +
" from " + zipfile.getName() +
" to " + dest.getPath());
long next_update = progress;
InputStream in = zipfile.getInputStream(zipentry);
OutputStream out = new FileOutputStream(dest);
int len;
byte[] buffer = new byte[4096];
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
progress += len;
if (progress >= next_update) {
publishProgress(progress, total_size);
// Arbitrary limit of 2% update steps
next_update += total_size / 50;
}
}
in.close();
out.close();
dest.setLastModified(zipentry.getTime());
}
publishProgress(progress, total_size);
}
zipfile.close();
} catch (IOException e) {
// FIXME: show some sort of GUI error dialog
Log.e(this.toString(), "Error unpacking plugin", e);
}
}
if (progress != total_size)
Log.d(this.toString(), "Ended with progress " + progress +
" != total size " + total_size);
setResult(RESULT_OK);
return null;
}
}
private class PluginBroadcastReciever extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (!intent.getAction()
.equals(ScummVMApplication.ACTION_PLUGIN_QUERY)) {
Log.e(this.toString(),
"Received unexpected action " + intent.getAction());
return;
}
Bundle extras = getResultExtras(false);
if (extras == null) {
// Nothing for us to do.
Unpacker.this.setResult(RESULT_OK);
finish();
}
ArrayList<String> unpack_libs =
extras.getStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS);
if (unpack_libs != null && !unpack_libs.isEmpty()) {
final String[] libs =
unpack_libs.toArray(new String[unpack_libs.size()]);
mUnpacker = new UnpackTask().execute(libs);
}
}
}
private void initPlugins() {
Bundle extras = new Bundle(1);
ArrayList<String> unpack_libs = new ArrayList<String>(1);
// This is the common ScummVM code (not really a "plugin" as such)
unpack_libs.add(new Uri.Builder()
.scheme("plugin")
.authority(getPackageName())
.path("mylib/armeabi/libscummvm.so")
.toString());
extras.putStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS,
unpack_libs);
Intent intent = new Intent(ScummVMApplication.ACTION_PLUGIN_QUERY);
sendOrderedBroadcast(intent, Manifest.permission.SCUMMVM_PLUGIN,
new PluginBroadcastReciever(),
null, RESULT_OK, null, extras);
}
@Override
public void onCreate(Bundle b) {
super.onCreate(b);
mUnpackDest = ScummVMApplication.getLastCacheDir();
setContentView(R.layout.splash);
mProgress = (ProgressBar)findViewById(R.id.progress);
setResult(RESULT_CANCELED);
tryUnpack();
}
private void tryUnpack() {
Intent intent = new Intent(ScummVMApplication.ACTION_PLUGIN_QUERY);
List<ResolveInfo> plugins = getPackageManager()
.queryBroadcastReceivers(intent, 0);
if (plugins.isEmpty()) {
// No plugins installed
AlertDialog.Builder alert = new AlertDialog.Builder(this)
.setTitle(R.string.no_plugins_title)
.setMessage(R.string.no_plugins_found)
.setIcon(android.R.drawable.ic_dialog_alert)
.setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
finish();
}
})
.setNegativeButton(R.string.quit,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
final Uri uri = Uri.parse("market://search?q=ScummVM plugin");
final Intent market_intent = new Intent(Intent.ACTION_VIEW, uri);
if (getPackageManager().resolveActivity(market_intent, 0) != null) {
alert.setPositiveButton(R.string.to_market,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
try {
startActivityForResult(market_intent,
REQUEST_MARKET);
} catch (ActivityNotFoundException e) {
Log.e(this.toString(),
"Error starting market", e);
}
}
});
}
alert.show();
} else {
// Already have at least one plugin installed
initPlugins();
}
}
@Override
public void onStop() {
if (mUnpacker != null)
mUnpacker.cancel(true);
super.onStop();
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
switch (requestCode) {
case REQUEST_MARKET:
if (resultCode != RESULT_OK)
Log.w(this.toString(), "Market returned " + resultCode);
tryUnpack();
break;
}
}
private Bundle getMetaData() {
try {
ActivityInfo ai = getPackageManager()
.getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
return ai.metaData;
} catch (PackageManager.NameNotFoundException e) {
Log.w(this.toString(), "Unable to find my own meta-data", e);
return new Bundle();
}
}
}

View File

@ -0,0 +1,135 @@
diff -r 884e66fd1b9c gui/ThemeEngine.cpp
--- a/gui/ThemeEngine.cpp Tue Apr 13 09:30:52 2010 +1000
+++ b/gui/ThemeEngine.cpp Fri May 28 23:24:43 2010 +1000
@@ -390,21 +390,19 @@
// Try to create a Common::Archive with the files of the theme.
if (!_themeArchive && !_themeFile.empty()) {
- Common::FSNode node(_themeFile);
- if (node.getName().hasSuffix(".zip") && !node.isDirectory()) {
+ Common::ArchiveMemberPtr member = SearchMan.getMember(_themeFile);
+ if (member && member->getName().hasSuffix(".zip")) {
#ifdef USE_ZLIB
- Common::Archive *zipArchive = Common::makeZipArchive(node);
+ Common::Archive *zipArchive = Common::makeZipArchive(member->createReadStream());
if (!zipArchive) {
- warning("Failed to open Zip archive '%s'.", node.getPath().c_str());
+ warning("Failed to open Zip archive '%s'.", member->getDisplayName().c_str());
}
_themeArchive = zipArchive;
#else
warning("Trying to load theme '%s' in a Zip archive without zLib support", _themeFile.c_str());
return false;
#endif
- } else if (node.isDirectory()) {
- _themeArchive = new Common::FSDirectory(node);
}
}
@@ -1436,6 +1434,30 @@
return tok.empty();
}
+bool ThemeEngine::themeConfigUsable(const Common::ArchiveMember &member, Common::String &themeName) {
+ Common::File stream;
+ bool foundHeader = false;
+
+ if (member.getName().hasSuffix(".zip")) {
+#ifdef USE_ZLIB
+ Common::Archive *zipArchive = Common::makeZipArchive(member.createReadStream());
+
+ if (zipArchive && zipArchive->hasFile("THEMERC")) {
+ stream.open("THEMERC", *zipArchive);
+ }
+
+ delete zipArchive;
+#endif
+ }
+
+ if (stream.isOpen()) {
+ Common::String stxHeader = stream.readLine();
+ foundHeader = themeConfigParseHeader(stxHeader, themeName);
+ }
+
+ return foundHeader;
+}
+
bool ThemeEngine::themeConfigUsable(const Common::FSNode &node, Common::String &themeName) {
Common::File stream;
bool foundHeader = false;
@@ -1493,10 +1515,6 @@
if (ConfMan.hasKey("themepath"))
listUsableThemes(Common::FSNode(ConfMan.get("themepath")), list);
-#ifdef DATA_PATH
- listUsableThemes(Common::FSNode(DATA_PATH), list);
-#endif
-
#if defined(MACOSX) || defined(IPHONE)
CFURLRef resourceUrl = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
if (resourceUrl) {
@@ -1509,10 +1527,7 @@
}
#endif
- if (ConfMan.hasKey("extrapath"))
- listUsableThemes(Common::FSNode(ConfMan.get("extrapath")), list);
-
- listUsableThemes(Common::FSNode("."), list, 1);
+ listUsableThemes(SearchMan, list);
// Now we need to strip all duplicates
// TODO: It might not be the best idea to strip duplicates. The user might
@@ -1531,6 +1546,34 @@
output.clear();
}
+void ThemeEngine::listUsableThemes(Common::Archive &archive, Common::List<ThemeDescriptor> &list) {
+ ThemeDescriptor td;
+
+#ifdef USE_ZLIB
+ Common::ArchiveMemberList fileList;
+ archive.listMatchingMembers(fileList, "*.zip");
+ for (Common::ArchiveMemberList::iterator i = fileList.begin();
+ i != fileList.end(); ++i) {
+ td.name.clear();
+ if (themeConfigUsable(**i, td.name)) {
+ td.filename = (*i)->getName();
+ td.id = (*i)->getDisplayName();
+
+ // If the name of the node object also contains
+ // the ".zip" suffix, we will strip it.
+ if (td.id.hasSuffix(".zip")) {
+ for (int j = 0; j < 4; ++j)
+ td.id.deleteLastChar();
+ }
+
+ list.push_back(td);
+ }
+ }
+
+ fileList.clear();
+#endif
+}
+
void ThemeEngine::listUsableThemes(const Common::FSNode &node, Common::List<ThemeDescriptor> &list, int depth) {
if (!node.exists() || !node.isReadable() || !node.isDirectory())
return;
diff -r 884e66fd1b9c gui/ThemeEngine.h
--- a/gui/ThemeEngine.h Tue Apr 13 09:30:52 2010 +1000
+++ b/gui/ThemeEngine.h Fri May 28 23:24:43 2010 +1000
@@ -560,11 +560,13 @@
static void listUsableThemes(Common::List<ThemeDescriptor> &list);
private:
static bool themeConfigUsable(const Common::FSNode &node, Common::String &themeName);
+ static bool themeConfigUsable(const Common::ArchiveMember &member, Common::String &themeName);
static bool themeConfigParseHeader(Common::String header, Common::String &themeName);
static Common::String getThemeFile(const Common::String &id);
static Common::String getThemeId(const Common::String &filename);
static void listUsableThemes(const Common::FSNode &node, Common::List<ThemeDescriptor> &list, int depth = -1);
+ static void listUsableThemes(Common::Archive &archive, Common::List<ThemeDescriptor> &list);
protected:
OSystem *_system; /** Global system object. */

View File

@ -38,7 +38,7 @@
#include <system.h>
#include <stdlib.h>
#include <string.h>
//#include <registers_alt.h> // not needed in current libnds
#include <registers_alt.h> // Needed for SOUND_CR
#include <NDS/scummvm_ipc.h>
//////////////////////////////////////////////////////////////////////
#ifdef USE_DEBUGGER
@ -590,7 +590,7 @@ int main(int argc, char ** argv) {
IPC->reset = false;
fifoInit();
//fifoInit();
for (int r = 0; r < 8; r++) {
IPC->adpcm.arm7Buffer[r] = (u8 *) malloc(512);

View File

@ -41,6 +41,10 @@ Visit the main ScummVM website <http://www.scummvm.org>
What's New?
------------------------------------------------------------------------
ScummVM DS 1.1.1
* Bugfix release: no new features
ScummVM DS 1.1.0
* New games are supported in this stable build: Return to Zork, Rodney's
@ -319,7 +323,7 @@ CANNOT DO THIS.
------------------------------------------------------------------------
I'm glad you asked. Here is a list of the compatible games in version
1.1.0. Demo versions of the games listed should work too.
1.1.1. Demo versions of the games listed should work too.
Flight of the Amazon Queen, Beneath a Steel Sky, and Lure of the
Temptress have generously been released as freeware by the original

View File

@ -75,7 +75,7 @@ else
ifdef DS_BUILD_K
else
USE_MAD = 1
# USE_MAD = 1
endif
endif
endif

View File

@ -103,6 +103,7 @@
#include "profiler/cyg-profile.h"
#endif
#include "backends/fs/ds/ds-fs.h"
#include "base/version.h"
#include "engine.h"
extern "C" void OurIntrMain(void);
@ -701,7 +702,7 @@ void displayMode8Bit() {
consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, 2, 0, true);
consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, 2, 0, true, true);
// Set this again because consoleinit resets it
videoSetMode(MODE_5_2D | (consoleEnable? DISPLAY_BG0_ACTIVE: 0) | DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP);
@ -939,7 +940,7 @@ void displayMode16Bit() {
SUB_BG0_CR = BG_MAP_BASE(4) | BG_TILE_BASE(0);
SUB_BG0_Y0 = 0;
consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, 4, 0, false);
consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, 4, 0, false, true);
// consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(4), (u16*)CHAR_BASE_BLOCK_SUB(0), 16);
for (int r = 0; r < 32 * 32; r++) {
@ -3162,7 +3163,7 @@ int main(void) {
consolePrintf("-------------------------------\n");
consolePrintf("ScummVM DS\n");
consolePrintf("Ported by Neil Millstone\n");
consolePrintf("Version 1.1.0 ");
consolePrintf("Version %s ", gScummVMVersion);
#if defined(DS_BUILD_A)
consolePrintf("build A\n");
consolePrintf("Lucasarts SCUMM games (SCUMM)\n");

View File

@ -33,6 +33,18 @@
//////////////////////////////////////////////////////////////////////
typedef struct sTransferSoundData {
//---------------------------------------------------------------------------------
const void *data;
u32 len;
u32 rate;
u8 vol;
u8 pan;
u8 format;
u8 PADDING;
} TransferSoundData, * pTransferSoundData;
//---------------------------------------------------------------------------------

View File

@ -121,19 +121,6 @@ public:
virtual Audio::Mixer *getMixer();
// Poll CD status
// Returns true if cd audio is playing
bool pollCD();
// Play CD audio track
void playCD(int track, int num_loops, int start_frame, int duration);
// Stop CD audio track
void stopCD();
// Update CD audio status
void updateCD();
// Quit
void quit();
@ -163,8 +150,6 @@ public:
bool setGraphicsMode(int mode);
int getGraphicsMode() const;
bool openCD(int drive);
bool hasFeature(Feature f);
void setFeatureState(Feature f, bool enable);
bool getFeatureState(Feature f);
@ -206,15 +191,6 @@ protected:
bool _overlayVisible;
Graphics::PixelFormat _overlayFormat;
// CD Audio
SDL_CD *_cdrom;
int _cdTrack, _cdNumLoops, _cdStartFrame, _cdDuration;
uint32 _cdEndTime, _cdStopTime;
enum {
DF_WANT_RECT_OPTIM = 1 << 0
};
enum {
kTransactionNone = 0,
kTransactionActive = 1,
@ -255,7 +231,6 @@ protected:
Graphics::Surface _framebuffer;
/** Current video mode flags (see DF_* constants) */
uint32 _modeFlags;
bool _modeChanged;
int _screenChangeCount;
@ -272,9 +247,6 @@ protected:
// Dirty rect management
SDL_Rect _dirtyRectList[NUM_DIRTY_RECT];
int _numDirtyRects;
uint32 *_dirtyChecksums;
bool _cksumValid;
int _cksumNum;
// Keyboard mouse emulation. Disabled by fingolfin 2004-12-18.
// I am keeping the rest of the code in for now, since the joystick
@ -371,9 +343,6 @@ protected:
Common::TimerManager *_timer;
protected:
void addDirtyRgnAuto(const byte *buf);
void makeChecksums(const byte *buf);
virtual void addDirtyRect(int x, int y, int w, int h, bool realCoordinates = false);
void drawMouse();

View File

@ -193,13 +193,11 @@ void OSystem_GP2X::initBackend() {
memset(&_videoMode, 0, sizeof(_videoMode));
memset(&_transactionDetails, 0, sizeof(_transactionDetails));
_cksumValid = false;
_videoMode.mode = GFX_NORMAL;
_videoMode.scaleFactor = 1;
_scalerProc = Normal1x;
_videoMode.aspectRatioCorrection = ConfMan.getBool("aspect_ratio");
_scalerType = 0;
_modeFlags = 0;
_adjustZoomOnMouse = false;
ConfMan.setBool("FM_low_quality", true);
@ -246,7 +244,7 @@ OSystem_GP2X::OSystem_GP2X()
_hwscreen(0), _screen(0), _tmpscreen(0),
_overlayVisible(false),
_overlayscreen(0), _tmpscreen2(0),
_cdrom(0), _scalerProc(0), _modeChanged(false), _screenChangeCount(0), _dirtyChecksums(0),
_scalerProc(0), _modeChanged(false), _screenChangeCount(0),
_mouseVisible(false), _mouseNeedsRedraw(false), _mouseData(0), _mouseSurface(0),
_mouseOrigSurface(0), _cursorTargetScale(1), _cursorPaletteDisabled(true),
_joystick(0),
@ -281,7 +279,6 @@ OSystem_GP2X::~OSystem_GP2X() {
SDL_RemoveTimer(_timerID);
closeMixer();
free(_dirtyChecksums);
free(_currentPalette);
free(_cursorPalette);
free(_mouseData);
@ -380,7 +377,6 @@ bool OSystem_GP2X::hasFeature(Feature f) {
return
(f == kFeatureFullscreenMode) ||
(f == kFeatureAspectRatioCorrection) ||
(f == kFeatureAutoComputeDirtyRects) ||
(f == kFeatureCursorHasPalette);
}
@ -391,12 +387,6 @@ void OSystem_GP2X::setFeatureState(Feature f, bool enable) {
case kFeatureAspectRatioCorrection:
setAspectRatioCorrection(enable);
break;
case kFeatureAutoComputeDirtyRects:
if (enable)
_modeFlags |= DF_WANT_RECT_OPTIM;
else
_modeFlags &= ~DF_WANT_RECT_OPTIM;
break;
case kFeatureDisableKeyFiltering:
// TODO: Extend as more support for this is added to engines.
return;
@ -413,8 +403,6 @@ bool OSystem_GP2X::getFeatureState(Feature f) {
return false;
case kFeatureAspectRatioCorrection:
return _videoMode.aspectRatioCorrection;
case kFeatureAutoComputeDirtyRects:
return _modeFlags & DF_WANT_RECT_OPTIM;
default:
return false;
}
@ -431,7 +419,6 @@ void OSystem_GP2X::quit() {
SDL_RemoveTimer(_timerID);
closeMixer();
free(_dirtyChecksums);
free(_currentPalette);
free(_cursorPalette);
free(_mouseData);
@ -650,26 +637,3 @@ Audio::Mixer *OSystem_GP2X::getMixer() {
assert(_mixer);
return _mixer;
}
#pragma mark -
#pragma mark --- CD Audio ---
#pragma mark -
bool OSystem_GP2X::openCD(int drive) {
return (_cdrom = NULL);
}
void OSystem_GP2X::stopCD() {
}
void OSystem_GP2X::playCD(int track, int num_loops, int start_frame, int duration) {
return;
}
bool OSystem_GP2X::pollCD() {
return false;
}
void OSystem_GP2X::updateCD() {
return;
}

View File

@ -270,12 +270,7 @@ void OSystem_GP2X::initSize(uint w, uint h, const Graphics::PixelFormat *format)
_videoMode.screenWidth = w;
_videoMode.screenHeight = h;
_cksumNum = (w * h / (8 * 8));
_transactionDetails.sizeChanged = true;
free(_dirtyChecksums);
_dirtyChecksums = (uint32 *)calloc(_cksumNum * 2, sizeof(uint32));
}
int OSystem_GP2X::effectiveScreenHeight() const {
@ -724,41 +719,32 @@ void OSystem_GP2X::copyRectToScreen(const byte *src, int pitch, int x, int y, in
assert(h > 0 && y + h <= _videoMode.screenHeight);
assert(w > 0 && x + w <= _videoMode.screenWidth);
if (IS_ALIGNED(src, 4) && pitch == _videoMode.screenWidth && x == 0 && y == 0 &&
w == _videoMode.screenWidth && h == _videoMode.screenHeight && _modeFlags & DF_WANT_RECT_OPTIM) {
/* Special, optimized case for full screen updates.
* It tries to determine what areas were actually changed,
* and just updates those, on the actual display. */
addDirtyRgnAuto(src);
} else {
/* Clip the coordinates */
if (x < 0) {
w += x;
src -= x;
x = 0;
}
if (y < 0) {
h += y;
src -= y * pitch;
y = 0;
}
if (w > _videoMode.screenWidth - x) {
w = _videoMode.screenWidth - x;
}
if (h > _videoMode.screenHeight - y) {
h = _videoMode.screenHeight - y;
}
if (w <= 0 || h <= 0)
return;
_cksumValid = false;
addDirtyRect(x, y, w, h);
/* Clip the coordinates */
if (x < 0) {
w += x;
src -= x;
x = 0;
}
if (y < 0) {
h += y;
src -= y * pitch;
y = 0;
}
if (w > _videoMode.screenWidth - x) {
w = _videoMode.screenWidth - x;
}
if (h > _videoMode.screenHeight - y) {
h = _videoMode.screenHeight - y;
}
if (w <= 0 || h <= 0)
return;
addDirtyRect(x, y, w, h);
// Try to lock the screen surface
if (SDL_LockSurface(_screen) == -1)
error("SDL_LockSurface failed: %s", SDL_GetError());
@ -885,88 +871,6 @@ void OSystem_GP2X::addDirtyRect(int x, int y, int w, int h, bool realCoordinates
}
}
void OSystem_GP2X::makeChecksums(const byte *buf) {
assert(buf);
uint32 *sums = _dirtyChecksums;
uint x,y;
const uint last_x = (uint)_videoMode.screenWidth / 8;
const uint last_y = (uint)_videoMode.screenHeight / 8;
const uint BASE = 65521; /* largest prime smaller than 65536 */
/* the 8x8 blocks in buf are enumerated starting in the top left corner and
* reading each line at a time from left to right */
for (y = 0; y != last_y; y++, buf += _videoMode.screenWidth * (8 - 1))
for (x = 0; x != last_x; x++, buf += 8) {
// Adler32 checksum algorithm (from RFC1950, used by gzip and zlib).
// This computes the Adler32 checksum of a 8x8 pixel block. Note
// that we can do the modulo operation (which is the slowest part)
// of the algorithm) at the end, instead of doing each iteration,
// since we only have 64 iterations in total - and thus s1 and
// s2 can't overflow anyway.
uint32 s1 = 1;
uint32 s2 = 0;
const byte *ptr = buf;
for (int subY = 0; subY < 8; subY++) {
for (int subX = 0; subX < 8; subX++) {
s1 += ptr[subX];
s2 += s1;
}
ptr += _videoMode.screenWidth;
}
s1 %= BASE;
s2 %= BASE;
/* output the checksum for this block */
*sums++ = (s2 << 16) + s1;
}
}
void OSystem_GP2X::addDirtyRgnAuto(const byte *buf) {
assert(buf);
assert(IS_ALIGNED(buf, 4));
/* generate a table of the checksums */
makeChecksums(buf);
if (!_cksumValid) {
_forceFull = true;
_cksumValid = true;
}
/* go through the checksum list, compare it with the previous checksums,
and add all dirty rectangles to a list. try to combine small rectangles
into bigger ones in a simple way */
if (!_forceFull) {
int x, y, w;
uint32 *ck = _dirtyChecksums;
for (y = 0; y != _videoMode.screenHeight / 8; y++) {
for (x = 0; x != _videoMode.screenWidth / 8; x++, ck++) {
if (ck[0] != ck[_cksumNum]) {
/* found a dirty 8x8 block, now go as far to the right as possible,
and at the same time, unmark the dirty status by setting old to new. */
w=0;
do {
ck[w + _cksumNum] = ck[w];
w++;
} while (x + w != _videoMode.screenWidth / 8 && ck[w] != ck[w + _cksumNum]);
addDirtyRect(x * 8, y * 8, w * 8, 8);
if (_forceFull)
goto get_out;
}
}
}
} else {
get_out:;
/* Copy old checksums to new */
memcpy(_dirtyChecksums + _cksumNum, _dirtyChecksums, _cksumNum * sizeof(uint32));
}
}
int16 OSystem_GP2X::getHeight() {
return _videoMode.screenHeight;
}
@ -1175,7 +1079,6 @@ void OSystem_GP2X::copyRectToOverlay(const OverlayColor *buf, int pitch, int x,
return;
// Mark the modified region as dirty
_cksumValid = false;
addDirtyRect(x, y, w, h);
if (SDL_LockSurface(_overlayscreen) == -1)
@ -1502,7 +1405,6 @@ void OSystem_GP2X::drawMouse() {
SDL_Rect zoomdst;
SDL_Rect dst;
int scale;
int width, height;
int hotX, hotY;
int tmpScreenWidth, tmpScreenHeight;
@ -1523,16 +1425,12 @@ void OSystem_GP2X::drawMouse() {
if (!_overlayVisible) {
scale = _videoMode.scaleFactor;
width = _videoMode.screenWidth;
height = _videoMode.screenHeight;
dst.w = _mouseCurState.vW;
dst.h = _mouseCurState.vH;
hotX = _mouseCurState.vHotX;
hotY = _mouseCurState.vHotY;
} else {
scale = 1;
width = _videoMode.overlayWidth;
height = _videoMode.overlayHeight;
dst.w = _mouseCurState.rW;
dst.h = _mouseCurState.rH;
hotX = _mouseCurState.rHotX;

View File

@ -13,7 +13,7 @@ export ASFLAGS=-mfloat-abi=soft
cd ../../../..
echo Building ScummVM for GP2X.
echo Building ScummVM for GP2X Wiz.
make
echo Build for GP2X - SDL - complete - Please check build logs.
echo Build for GP2X Wiz - complete - Please check build logs.

View File

@ -35,10 +35,12 @@ loc=`dirname "$f"`
cp $loc/../lib/libz.so.1.2.3 ./scummvm-wiz-`date '+%Y-%m-%d'`/scummvm/lib/libz.so.1
cp $loc/../lib/libvorbisidec.so.1.0.2 ./scummvm-wiz-`date '+%Y-%m-%d'`/scummvm/lib/libvorbisidec.so.1
echo Making Stripped exe.
echo Making Stripped Binary.
arm-open2x-linux-strip ./scummvm-wiz-`date '+%Y-%m-%d'`/scummvm/scummvm.wiz
echo Making Stripped Plugins.
arm-open2x-linux-strip ./scummvm-wiz-`date '+%Y-%m-%d'`/scummvm/plugins/*
echo Building ZIP bundle.
if [ -f /usr/bin/zip ]
then

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
echo Quick script to make building all the time less painful.

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
echo Quick script to make running configure all the time less painful
echo and let all the build work be done from the backend/build folder.

View File

@ -30,6 +30,7 @@
#include "backends/platform/gp2xwiz/gp2xwiz-sdl.h"
#include "backends/platform/gp2xwiz/gp2xwiz-hw.h"
#include "graphics/scaler/aspect.h"
#include "common/util.h"
#include "common/events.h"

View File

@ -127,12 +127,7 @@ void OSystem_GP2XWIZ::initSize(uint w, uint h) {
toggleMouseGrab();
}
_cksumNum = (w * h / (8 * 8));
_transactionDetails.sizeChanged = true;
free(_dirtyChecksums);
_dirtyChecksums = (uint32 *)calloc(_cksumNum * 2, sizeof(uint32));
}
bool OSystem_GP2XWIZ::loadGFXMode() {
@ -154,7 +149,6 @@ void OSystem_GP2XWIZ::drawMouse() {
SDL_Rect dst;
int scale;
int width, height;
int hotX, hotY;
if (_videoMode.mode == GFX_HALF && !_overlayVisible){
@ -167,16 +161,12 @@ void OSystem_GP2XWIZ::drawMouse() {
if (!_overlayVisible) {
scale = _videoMode.scaleFactor;
width = _videoMode.screenWidth;
height = _videoMode.screenHeight;
dst.w = _mouseCurState.vW;
dst.h = _mouseCurState.vH;
hotX = _mouseCurState.vHotX;
hotY = _mouseCurState.vHotY;
} else {
scale = 1;
width = _videoMode.overlayWidth;
height = _videoMode.overlayHeight;
dst.w = _mouseCurState.rW;
dst.h = _mouseCurState.rH;
hotX = _mouseCurState.rHotX;

View File

@ -72,6 +72,7 @@ void iPhone_updateScreenRect(unsigned short* screen, int x1, int y1, int x2, int
void iPhone_initSurface(int width, int height);
bool iPhone_fetchEvent(int *outEvent, float *outX, float *outY);
const char* iPhone_getDocumentsDir();
bool iPhone_isHighResDevice();
#ifdef __cplusplus
}

View File

@ -43,8 +43,6 @@
SoftKeyboard* _keyboardView;
CALayer* _screenLayer;
int _fullWidth;
int _fullHeight;
int _widthOffset;
int _heightOffset;

View File

@ -29,6 +29,8 @@
static iPhoneView *sharedInstance = nil;
static int _width = 0;
static int _height = 0;
static int _fullWidth;
static int _fullHeight;
static CGRect _screenRect;
static char* _textureBuffer = 0;
static int _textureWidth = 0;
@ -42,6 +44,10 @@ static UITouch* _secondTouch = NULL;
// static long lastTick = 0;
// static int frames = 0;
bool iPhone_isHighResDevice() {
return _fullHeight > 480;
}
void iPhone_updateScreen() {
if (!_needsScreenUpdate) {
_needsScreenUpdate = 1;

View File

@ -57,7 +57,7 @@ OSystem_IPHONE::OSystem_IPHONE() :
_overlayVisible(false), _overlayBuffer(NULL), _fullscreen(NULL),
_mouseHeight(0), _mouseWidth(0), _mouseBuf(NULL), _lastMouseTap(0),
_secondaryTapped(false), _lastSecondaryTap(0), _screenOrientation(kScreenOrientationFlippedLandscape),
_needEventRestPeriod(false), _mouseClickAndDragEnabled(false), _touchpadModeEnabled(true),
_needEventRestPeriod(false), _mouseClickAndDragEnabled(false),
_gestureStartX(-1), _gestureStartY(-1), _fullScreenIsDirty(false), _fullScreenOverlayIsDirty(false),
_mouseDirty(false), _timeSuspended(0), _lastDragPosX(-1), _lastDragPosY(-1), _screenChangeCount(0)
@ -65,6 +65,7 @@ OSystem_IPHONE::OSystem_IPHONE() :
_queuedInputEvent.type = (Common::EventType)0;
_lastDrawnMouseRect = Common::Rect(0, 0, 0, 0);
_touchpadModeEnabled = !iPhone_isHighResDevice();
_fsFactory = new POSIXFilesystemFactory();
}

View File

@ -86,7 +86,7 @@ int16 OSystem_IPHONE::getWidth() {
}
void OSystem_IPHONE::setPalette(const byte *colors, uint start, uint num) {
//printf("setPalette()\n");
assert(start + num <= 256);
const byte *b = colors;
for (uint i = start; i < start + num; ++i) {
@ -98,7 +98,14 @@ void OSystem_IPHONE::setPalette(const byte *colors, uint start, uint num) {
}
void OSystem_IPHONE::grabPalette(byte *colors, uint start, uint num) {
//printf("grabPalette()\n");
assert(start + num <= 256);
byte *b = colors;
for (uint i = start; i < start + num; ++i) {
Graphics::colorToRGB<Graphics::ColorMasks<565> >(_palette[i], b[0], b[1], b[2]);
b[3] = 0xFF;
b += 4;
}
}
void OSystem_IPHONE::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) {

View File

@ -128,12 +128,7 @@ void OSystem_LINUXMOTO::initSize(uint w, uint h) {
toggleMouseGrab();
}
_cksumNum = (w * h / (8 * 8));
_transactionDetails.sizeChanged = true;
free(_dirtyChecksums);
_dirtyChecksums = (uint32 *)calloc(_cksumNum * 2, sizeof(uint32));
}
bool OSystem_LINUXMOTO::loadGFXMode() {
@ -173,7 +168,6 @@ void OSystem_LINUXMOTO::drawMouse() {
SDL_Rect dst;
int scale;
int width, height;
int hotX, hotY;
if (_videoMode.mode == GFX_HALF && !_overlayVisible) {
@ -186,16 +180,12 @@ void OSystem_LINUXMOTO::drawMouse() {
if (!_overlayVisible) {
scale = _videoMode.scaleFactor;
width = _videoMode.screenWidth;
height = _videoMode.screenHeight;
dst.w = _mouseCurState.vW;
dst.h = _mouseCurState.vH;
hotX = _mouseCurState.vHotX;
hotY = _mouseCurState.vHotY;
} else {
scale = 1;
width = _videoMode.overlayWidth;
height = _videoMode.overlayHeight;
dst.w = _mouseCurState.rW;
dst.h = _mouseCurState.rH;
hotX = _mouseCurState.rHotX;

View File

@ -624,23 +624,6 @@ void OSystem_PS2::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x,
_screen->setMouseOverlay(buf, w, h, hotspot_x, hotspot_y, keycolor);
}
bool OSystem_PS2::openCD(int drive) {
return false;
}
bool OSystem_PS2::pollCD(void) {
return false;
}
void OSystem_PS2::playCD(int track, int num_loops, int start_frame, int duration) {
}
void OSystem_PS2::stopCD(void) {
}
void OSystem_PS2::updateCD(void) {
}
void OSystem_PS2::showOverlay(void) {
_screen->showOverlay();
}

View File

@ -97,12 +97,6 @@ public:
virtual Audio::Mixer *getMixer();
virtual bool openCD(int drive);
virtual bool pollCD();
virtual void playCD(int track, int num_loops, int start_frame, int duration);
virtual void stopCD();
virtual void updateCD();
virtual MutexRef createMutex(void);
virtual void lockMutex(MutexRef mutex);
virtual void unlockMutex(MutexRef mutex);

View File

@ -129,7 +129,8 @@ SDLFLAGS := $(shell $(PSPBIN)/sdl-config --cflags)
SDLLIBS := $(shell $(PSPBIN)/sdl-config --libs)
# PSP LIBS
PSPLIBS = -lpspprof -lpspvfpu -lpspdebug -lpspgu -lpspge -lpspdisplay -lpspctrl -lpspsdk \
-lpsputility -lpspuser -lpsppower -lpsphprm -lpspsdk -lpsprtc -lpspaudio -lpspkernel
-lpsputility -lpspuser -lpsppower -lpsphprm -lpspsdk -lpsprtc -lpspaudio -lpspaudiocodec \
-lpspkernel
# Add in PSPSDK includes and libraries.
CXXFLAGS += $(SDLFLAGS)
@ -149,7 +150,8 @@ OBJS := powerman.o \
psploader.o \
pspkeyboard.o \
audio.o \
thread.o
thread.o \
mp3.o
# Include common Scummvm makefile
include $(srcdir)/Makefile.common

View File

@ -686,17 +686,18 @@ void GuRenderer::fillVertices(Vertex *vertices) {
uint32 gapX = _useGlobalScaler ? (PSP_SCREEN_WIDTH - outputWidth) >> 1 : 0;
uint32 gapY = _useGlobalScaler ? (PSP_SCREEN_HEIGHT - outputHeight) >> 1 : 0;
// Save scaled offset on screen
float scaledOffsetOnScreenX = scaleSourceToOutputX(_offsetOnScreen.x);
float scaledOffsetOnScreenY = scaleSourceToOutputY(_offsetOnScreen.y);
float imageStartX, imageStartY, imageEndX, imageEndY;
imageStartX = gapX + (scaleSourceToOutputX(_maxTextureOffset.x));
imageStartY = gapY;
imageStartX += scaleSourceToOutputX(_offsetOnScreen.x);
imageStartY += scaleSourceToOutputY(_offsetOnScreen.y);
imageStartX = gapX + scaledOffsetOnScreenX + (scaleSourceToOutputX(_maxTextureOffset.x));
imageStartY = gapY + scaledOffsetOnScreenY;
if (_fullScreen) { // shortcut
imageEndX = PSP_SCREEN_WIDTH - gapX;
imageEndY = PSP_SCREEN_HEIGHT - gapY;
imageEndX = PSP_SCREEN_WIDTH - gapX + scaledOffsetOnScreenX;
imageEndY = PSP_SCREEN_HEIGHT - gapY + scaledOffsetOnScreenY; // needed for screen shake
} else { /* !fullScreen */
imageEndX = imageStartX + scaleSourceToOutputX(_drawSize.width);
imageEndY = imageStartY + scaleSourceToOutputY(_drawSize.height);

View File

@ -14,7 +14,8 @@ MODULE_OBJS := powerman.o \
psploader.o \
pspkeyboard.o \
audio.o \
thread.o
thread.o \
mp3.o
MODULE_DIRS += \
backends/platform/psp/

View File

@ -0,0 +1,487 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "common/debug.h"
#include "common/stream.h"
#include "common/util.h"
#include "common/singleton.h"
#include "common/mutex.h"
#include "sound/audiostream.h"
#include <pspaudiocodec.h>
#include <psputility_modules.h>
#include <pspthreadman.h>
#include <pspsysmem.h>
#include <pspmodulemgr.h>
#include <psputility_avmodules.h>
#include <mad.h>
#include "backends/platform/psp/mp3.h"
//#define DISABLE_PSP_MP3 // to make us use the regular MAD decoder instead
//#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */
//#define __PSP_DEBUG_PRINT__
#include "backends/platform/psp/trace.h"
//#define PRINT_BUFFERS /* to debug MP3 buffers */
namespace Audio {
class Mp3PspStream;
bool Mp3PspStream::_decoderInit = false; // has the decoder been initialized
#ifdef DISABLE_PSP_MP3
bool Mp3PspStream::_decoderFail = true; // pretend the decoder failed
#else
bool Mp3PspStream::_decoderFail = false; // has the decoder failed to load
#endif
bool Mp3PspStream::initDecoder() {
DEBUG_ENTER_FUNC();
if (_decoderInit) {
PSP_ERROR("Already initialized!");
return true;
}
// Based on PSP firmware version, we need to do different things to do Media Engine processing
uint32 firmware = sceKernelDevkitVersion();
PSP_DEBUG_PRINT("Firmware version 0x%x\n", firmware);
if (firmware == 0x01050001){
if (!loadStartAudioModule((char *)(void *)"flash0:/kd/me_for_vsh.prx",
PSP_MEMORY_PARTITION_KERNEL)) {
PSP_ERROR("failed to load me_for_vsh.prx. ME cannot start.\n");
_decoderFail = true;
return false;
}
if (!loadStartAudioModule((char *)(void *)"flash0:/kd/audiocodec.prx", PSP_MEMORY_PARTITION_KERNEL)) {
PSP_ERROR("failed to load audiocodec.prx. ME cannot start.\n");
_decoderFail = true;
return false;
}
} else {
if (sceUtilityLoadAvModule(PSP_AV_MODULE_AVCODEC) < 0) {
PSP_ERROR("failed to load AVCODEC module.\n");
_decoderFail = true;
return false;
}
}
PSP_INFO_PRINT("Using PSP's ME for MP3\n"); // important to know this is happening
_decoderInit = true;
return true;
}
bool Mp3PspStream::stopDecoder() {
DEBUG_ENTER_FUNC();
if (!_decoderInit)
return true;
// Based on PSP firmware version, we need to do different things to do Media Engine processing
if (sceKernelDevkitVersion() == 0x01050001){
/* if (!unloadAudioModule("flash0:/kd/me_for_vsh.prx", PSP_MEMORY_PARTITION_KERNEL) ||
!unloadAudioModule("flash0:/kd/audiocodec.prx", PSP_MEMORY_PARTITION_KERNEL) {
PSP_ERROR("failed to unload audio module\n");
return false;
}
*/
}else{
if (sceUtilityUnloadModule(PSP_MODULE_AV_AVCODEC) < 0) {
PSP_ERROR("failed to unload avcodec module\n");
return false;
}
}
_decoderInit = false;
return true;
}
//Load a PSP audio module
bool Mp3PspStream::loadStartAudioModule(const char *modname, int partition){
DEBUG_ENTER_FUNC();
SceKernelLMOption option;
SceUID modid;
memset(&option, 0, sizeof(option));
option.size = sizeof(option);
option.mpidtext = partition;
option.mpiddata = partition;
option.position = 0;
option.access = 1;
modid = sceKernelLoadModule(modname, 0, &option);
if (modid < 0) {
PSP_ERROR("Failed to load module %s. Got error 0x%x\n", modname, modid);
return false;
}
int ret = sceKernelStartModule(modid, 0, NULL, NULL, NULL);
if (ret < 0) {
PSP_ERROR("Failed to start module %s. Got error 0x%x\n", modname, ret);
return false;
}
return true;
}
// TODO: make parallel function for unloading the 1.50 modules
Mp3PspStream::Mp3PspStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) :
_inStream(inStream),
_disposeAfterUse(dispose),
_pcmLength(0),
_posInFrame(0),
_state(MP3_STATE_INIT),
_length(0, 1000),
_sampleRate(0),
_totalTime(mad_timer_zero) {
DEBUG_ENTER_FUNC();
assert(_decoderInit); // must be initialized by now
// let's leave the buffer guard -- who knows, it may be good?
memset(_buf, 0, sizeof(_buf));
memset(_codecInBuffer, 0, sizeof(_codecInBuffer));
initStream(); // init needed stuff for the stream
while (_state != MP3_STATE_EOS)
findValidHeader(); // get a first header so we can read basic stuff
_sampleRate = _header.samplerate; // copy it before it gets destroyed
_length = Timestamp(mad_timer_count(_totalTime, MAD_UNITS_MILLISECONDS), getRate());
//initStreamME(); // init the stuff needed for the ME to work
deinitStream();
//releaseStreamME();
_state = MP3_STATE_INIT;
}
int Mp3PspStream::initStream() {
DEBUG_ENTER_FUNC();
if (_state != MP3_STATE_INIT)
deinitStream();
// Init MAD
mad_stream_init(&_stream);
mad_header_init(&_header);
// Reset the stream data
_inStream->seek(0, SEEK_SET);
_totalTime = mad_timer_zero;
_posInFrame = 0;
// Update state
_state = MP3_STATE_READY;
// Read the first few sample bytes into the buffer
readMP3DataIntoBuffer();
return true;
}
bool Mp3PspStream::initStreamME() {
// The following will eventually go into the thread
sceAudiocodecReleaseEDRAM(_codecParams); // do we need this?
memset(_codecParams, 0, sizeof(_codecParams));
// Init the MP3 hardware
int ret = 0;
ret = sceAudiocodecCheckNeedMem(_codecParams, 0x1002);
if (ret < 0) {
PSP_ERROR("failed to init MP3 ME module. sceAudiocodecCheckNeedMem returned 0x%x.\n", ret);
return false;
}
PSP_DEBUG_PRINT("sceAudiocodecCheckNeedMem returned %d\n", ret);
ret = sceAudiocodecGetEDRAM(_codecParams, 0x1002);
if (ret < 0) {
PSP_ERROR("failed to init MP3 ME module. sceAudiocodecGetEDRAM returned 0x%x.\n", ret);
return false;
}
PSP_DEBUG_PRINT("sceAudioCodecGetEDRAM returned %d\n", ret);
PSP_DEBUG_PRINT("samplerate[%d]\n", _sampleRate);
_codecParams[10] = _sampleRate;
ret = sceAudiocodecInit(_codecParams, 0x1002);
if (ret < 0) {
PSP_ERROR("failed to init MP3 ME module. sceAudiocodecInit returned 0x%x.\n", ret);
return false;
}
return true;
}
Mp3PspStream::~Mp3PspStream() {
DEBUG_ENTER_FUNC();
deinitStream();
releaseStreamME(); // free the memory used for this stream
if (_disposeAfterUse == DisposeAfterUse::YES)
delete _inStream;
}
void Mp3PspStream::deinitStream() {
DEBUG_ENTER_FUNC();
if (_state == MP3_STATE_INIT)
return;
// Deinit MAD
mad_header_finish(&_header);
mad_stream_finish(&_stream);
_state = MP3_STATE_EOS;
}
void Mp3PspStream::releaseStreamME() {
sceAudiocodecReleaseEDRAM(_codecParams);
}
void Mp3PspStream::decodeMP3Data() {
DEBUG_ENTER_FUNC();
do {
if (_state == MP3_STATE_INIT) {
initStream();
initStreamME();
}
if (_state == MP3_STATE_EOS)
return;
findValidHeader(); // seach for next valid header
while (_state == MP3_STATE_READY) {
_stream.error = MAD_ERROR_NONE;
uint32 frame_size = _stream.next_frame - _stream.this_frame;
uint32 samplesPerFrame = _header.layer == MAD_LAYER_III ? 576 : 1152; // Varies by layer
// calculate frame size -- try
//uint32 calc_frame_size = ((144 * _header.bitrate) / 22050) + (_header.flags & MAD_FLAG_PADDING ? 1 : 0);
// Get stereo/mono
uint32 multFactor = 1;
if (_header.mode != MAD_MODE_SINGLE_CHANNEL) // mono - x2 for 16bit
multFactor *= 2; // stereo - x4 for 16bit
PSP_DEBUG_PRINT("MP3 frame size[%d]. Samples[%d]. Multfactor[%d] pad[%d]\n", frame_size, samplesPerFrame, multFactor, _header.flags & MAD_FLAG_PADDING);
memcpy(_codecInBuffer, _stream.this_frame, frame_size); // we need it aligned
// set up parameters for ME
_codecParams[6] = (unsigned long)_codecInBuffer;
_codecParams[8] = (unsigned long)_pcmSamples;
_codecParams[7] = frame_size;
_codecParams[9] = samplesPerFrame * multFactor; // x2 for stereo
// debug
#ifdef PRINT_BUFFERS
PSP_DEBUG_PRINT("mp3 frame:\n");
for (int i=0; i < (int)frame_size; i++) {
PSP_DEBUG_PRINT_SAMELN("%x ", _codecInBuffer[i]);
}
PSP_DEBUG_PRINT("\n");
#endif
// Decode the next frame
// This function blocks. We'll want to put it in a thread
int ret = sceAudiocodecDecode(_codecParams, 0x1002);
if (ret < 0) {
PSP_ERROR("failed to decode MP3 data in ME. sceAudiocodecDecode returned 0x%x\n", ret);
// handle error here
}
#ifdef PRINT_BUFFERS
PSP_DEBUG_PRINT("PCM frame:\n");
for (int i=0; i < (int)_codecParams[9]; i+=2) { // changed from i+=2
PSP_DEBUG_PRINT_SAMELN("%d ", (int16)_pcmSamples[i]);
}
PSP_DEBUG_PRINT("\n");
#endif
_pcmLength = samplesPerFrame;
_posInFrame = 0;
break;
}
} while (_state != MP3_STATE_EOS && _stream.error == MAD_ERROR_BUFLEN);
if (_stream.error != MAD_ERROR_NONE) // catch EOS
_state = MP3_STATE_EOS;
}
void Mp3PspStream::readMP3DataIntoBuffer() {
DEBUG_ENTER_FUNC();
uint32 remaining = 0;
// Give up immediately if we already used up all data in the stream
if (_inStream->eos()) {
_state = MP3_STATE_EOS;
return;
}
if (_stream.next_frame) {
// If there is still data in the MAD stream, we need to preserve it.
// Note that we use memmove, as we are reusing the same buffer,
// and hence the data regions we copy from and to may overlap.
remaining = _stream.bufend - _stream.next_frame;
assert(remaining < BUFFER_SIZE); // Paranoia check
memmove(_buf, _stream.next_frame, remaining); // TODO: may want another buffer
}
// Try to read the next block
uint32 size = _inStream->read(_buf + remaining, BUFFER_SIZE - remaining);
if (size <= 0) {
_state = MP3_STATE_EOS;
return;
}
// Feed the data we just read into the stream decoder
_stream.error = MAD_ERROR_NONE;
mad_stream_buffer(&_stream, _buf, size + remaining); // just setup the pointers
}
bool Mp3PspStream::seek(const Timestamp &where) {
DEBUG_ENTER_FUNC();
if (where == _length) {
_state = MP3_STATE_EOS;
return true;
} else if (where > _length) {
return false;
}
const uint32 time = where.msecs();
mad_timer_t destination;
mad_timer_set(&destination, time / 1000, time % 1000, 1000);
// Check if we need to rewind
if (_state != MP3_STATE_READY || mad_timer_compare(destination, _totalTime) < 0) {
initStream();
initStreamME();
}
// The ME will need clear data no matter what once we seek?
//if (mad_timer_compare(destination, _totalTime) > 0 && _state != MP3_STATE_EOS)
// initStreamME();
// Skip ahead
while (mad_timer_compare(destination, _totalTime) > 0 && _state != MP3_STATE_EOS)
findValidHeader();
return (_state != MP3_STATE_EOS);
}
// Seek in the stream, finding the next valid header
void Mp3PspStream::findValidHeader() {
DEBUG_ENTER_FUNC();
if (_state != MP3_STATE_READY)
return;
// If necessary, load more data into the stream decoder
if (_stream.error == MAD_ERROR_BUFLEN)
readMP3DataIntoBuffer();
while (_state != MP3_STATE_EOS) {
_stream.error = MAD_ERROR_NONE;
// Decode the next header.
if (mad_header_decode(&_header, &_stream) == -1) {
if (_stream.error == MAD_ERROR_BUFLEN) {
readMP3DataIntoBuffer(); // Read more data
continue;
} else if (MAD_RECOVERABLE(_stream.error)) {
debug(6, "MP3PSPStream: Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream));
continue;
} else {
warning("MP3PSPStream: Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream));
break;
}
}
// Sum up the total playback time so far
mad_timer_add(&_totalTime, _header.duration);
break;
}
if (_stream.error != MAD_ERROR_NONE)
_state = MP3_STATE_EOS;
}
int Mp3PspStream::readBuffer(int16 *buffer, const int numSamples) {
DEBUG_ENTER_FUNC();
int samples = 0;
#ifdef PRINT_BUFFERS
int16 *debugBuffer = buffer;
#endif
// Keep going as long as we have input available
while (samples < numSamples && _state != MP3_STATE_EOS) {
const int len = MIN(numSamples, samples + (int)(_pcmLength - _posInFrame) * MAD_NCHANNELS(&_header));
while (samples < len) {
*buffer++ = _pcmSamples[_posInFrame << 1];
samples++;
if (MAD_NCHANNELS(&_header) == 2) {
*buffer++ = _pcmSamples[(_posInFrame << 1) + 1];
samples++;
}
_posInFrame++; // always skip an extra sample since ME always outputs stereo
}
//memcpy(buffer, &_pcmSamples[_posInFrame], len << 1); // 16 bits
//_posInFrame += len; // next time we start from the middle
if (_posInFrame >= _pcmLength) {
// We used up all PCM data in the current frame -- read & decode more
decodeMP3Data();
}
}
#ifdef PRINT_BUFFERS
PSP_INFO_PRINT("buffer:\n");
for (int i = 0; i<numSamples; i++)
PSP_INFO_PRINT("%d ", debugBuffer[i]);
PSP_INFO_PRINT("\n\n");
#endif
return samples;
}
} // End of namespace Audio

121
backends/platform/psp/mp3.h Normal file
View File

@ -0,0 +1,121 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#ifndef SOUND_MP3_PSP_H
#define SOUND_MP3_PSP_H
#include "common/types.h"
#include "common/scummsys.h"
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class AudioStream;
class SeekableAudioStream;
class Mp3PspStream : public SeekableAudioStream {
protected:
enum State {
MP3_STATE_INIT, // Need to init the decoder
MP3_STATE_READY, // ready for processing data
MP3_STATE_EOS // end of data reached (may need to loop)
};
#define MAX_SAMPLES_PER_FRAME 2048 * 2
int16 _pcmSamples[MAX_SAMPLES_PER_FRAME] __attribute__((aligned(64))); // samples to output PCM data into
byte _codecInBuffer[3072] __attribute__((aligned(64))); // the codec always needs alignment
unsigned long _codecParams[65]__attribute__((aligned(64))); // TODO: change to struct
Common::SeekableReadStream *_inStream;
DisposeAfterUse::Flag _disposeAfterUse;
uint32 _pcmLength; // how many pcm samples we have (/2 for mono)
uint _posInFrame; // position in frame
State _state; // what state the stream is in
Timestamp _length;
uint32 _sampleRate;
mad_timer_t _totalTime;
mad_stream _stream; //
mad_header _header; // This is all we need from libmad
static bool _decoderInit; // has the decoder been initialized
static bool _decoderFail; // has the decoder failed to load
enum {
BUFFER_SIZE = 5 * 8192
};
// This buffer contains a slab of input data
byte _buf[BUFFER_SIZE + MAD_BUFFER_GUARD];
void decodeMP3Data();
void readMP3DataIntoBuffer();
static bool loadStartAudioModule(const char *modname, int partition);
int initStream();
void findValidHeader();
void deinitStream();
// to init and uninit ME decoder
static bool initDecoder();
static bool stopDecoder();
// ME functions for stream
bool initStreamME();
void releaseStreamME();
public:
Mp3PspStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose);
~Mp3PspStream();
// This function avoids having to create streams when it's not possible
static inline bool isOkToCreateStream() {
if (_decoderFail) // fatal failure
return false;
if (!_decoderInit) // if we're not initialized
if (!initDecoder()) // check if we failed init
return false;
return true;
}
int readBuffer(int16 *buffer, const int numSamples);
bool endOfData() const { return _state == MP3_STATE_EOS; }
bool isStereo() const { return MAD_NCHANNELS(&_header) == 2; }
int getRate() const { return _header.samplerate; }
bool seek(const Timestamp &where);
Timestamp getLength() const { return _length; }
};
} // End of namespace Audio
#endif // #ifndef SOUND_MP3_PSP_H

View File

@ -37,7 +37,6 @@
#include "backends/platform/psp/psppixelformat.h"
#include "backends/platform/psp/osys_psp.h"
#include "backends/platform/psp/powerman.h"
#include "backends/platform/psp/thread.h"
#include "backends/saves/psp/psp-saves.h"
#include "backends/timer/default/default-timer.h"
@ -300,7 +299,7 @@ bool OSystem_PSP::pollEvent(Common::Event &event) {
}
uint32 OSystem_PSP::getMillis() {
return PspThread::getMillis();
return _pspRtc.getMillis();
}
void OSystem_PSP::delayMillis(uint msecs) {

View File

@ -40,6 +40,7 @@
#include "backends/platform/psp/input.h"
#include "backends/platform/psp/audio.h"
#include "backends/timer/psp/timer.h"
#include "backends/platform/psp/thread.h"
#include <SDL.h>
@ -59,6 +60,7 @@ private:
InputHandler _inputHandler;
PspAudio _audio;
PspTimer _pspTimer;
PspRtc _pspRtc;
void initSDL();

View File

@ -1,3 +1,3 @@
%rename lib old_lib
*lib:
%(old_lib) -lz -lstdc++ -lc -lm -lpspprof -lpspvfpu -lpspdebug -lpspgu -lpspge -lpspdisplay -lpspctrl -lpspsdk -lpsputility -lpspuser -lpsppower -lpsphprm -lpsprtc -lpspaudio -lpspkernel
%(old_lib) -lz -lstdc++ -lc -lm -lpspprof -lpspvfpu -lpspdebug -lpspgu -lpspge -lpspdisplay -lpspctrl -lpspsdk -lpsputility -lpspuser -lpsppower -lpsphprm -lpsprtc -lpspaudio -lpspaudiocodec -lpspkernel

View File

@ -29,6 +29,7 @@
#include <pspthreadman.h>
#include "backends/platform/psp/thread.h"
#include "backends/platform/psp/trace.h"
void PspThread::delayMillis(uint32 ms) {
sceKernelDelayThread(ms * 1000);
@ -38,15 +39,49 @@ void PspThread::delayMicros(uint32 us) {
sceKernelDelayThread(us);
}
uint32 PspThread::getMillis() {
void PspRtc::init() { // init our starting ticks
uint32 ticks[2];
sceRtcGetCurrentTick((u64 *)ticks);
return (ticks[0]/1000);
_startMillis = ticks[0]/1000;
_startMicros = ticks[0];
//_lastMillis = ticks[0]/1000; //debug - only when we don't subtract startMillis
}
uint32 PspThread::getMicros() {
#define MS_LOOP_AROUND 4294967 /* We loop every 2^32 / 1000 = 71 minutes */
#define MS_LOOP_CHECK 60000 /* Threading can cause weird mixups without this */
// Note that after we fill up 32 bits ie 50 days we'll loop back to 0, which may cause
// unpredictable results
uint32 PspRtc::getMillis() {
uint32 ticks[2];
sceRtcGetCurrentTick((u64 *)ticks); // can introduce weird thread delays
uint32 millis = ticks[0]/1000;
millis -= _startMillis; // get ms since start of program
if ((int)_lastMillis - (int)millis > MS_LOOP_CHECK) { // we must have looped around
if (_looped == false) { // check to make sure threads do this once
_looped = true;
_milliOffset += MS_LOOP_AROUND; // add the needed offset
PSP_DEBUG_PRINT("looping around. last ms[%d], curr ms[%d]\n", _lastMillis, millis);
}
} else {
_looped = false;
}
_lastMillis = millis;
return millis + _milliOffset;
}
uint32 PspRtc::getMicros() {
uint32 ticks[2];
sceRtcGetCurrentTick((u64 *)ticks);
ticks[0] -= _startMicros;
return ticks[0];
}

View File

@ -32,8 +32,20 @@ class PspThread {
public:
static void delayMillis(uint32 ms);
static void delayMicros(uint32 us);
static uint32 getMillis();
static uint32 getMicros();
};
class PspRtc {
private:
uint32 _startMillis;
uint32 _startMicros;
uint32 _lastMillis;
uint32 _milliOffset; // to prevent looping around of millis
bool _looped; // make sure we only loop once
public:
PspRtc() : _startMillis(0), _startMicros(0), _lastMillis(0), _milliOffset(0), _looped(false) { init(); }
void init();
uint32 getMillis();
uint32 getMicros();
};
enum ThreadPriority {

View File

@ -30,7 +30,6 @@
bool OSystem_SDL_SamsungTV::hasFeature(Feature f) {
return
(f == kFeatureAspectRatioCorrection) ||
(f == kFeatureAutoComputeDirtyRects) ||
(f == kFeatureCursorHasPalette);
}
@ -39,12 +38,6 @@ void OSystem_SDL_SamsungTV::setFeatureState(Feature f, bool enable) {
case kFeatureAspectRatioCorrection:
setAspectRatioCorrection(enable);
break;
case kFeatureAutoComputeDirtyRects:
if (enable)
_modeFlags |= DF_WANT_RECT_OPTIM;
else
_modeFlags &= ~DF_WANT_RECT_OPTIM;
break;
default:
break;
}
@ -56,8 +49,6 @@ bool OSystem_SDL_SamsungTV::getFeatureState(Feature f) {
switch (f) {
case kFeatureAspectRatioCorrection:
return _videoMode.aspectRatioCorrection;
case kFeatureAutoComputeDirtyRects:
return _modeFlags & DF_WANT_RECT_OPTIM;
default:
return false;
}

View File

@ -83,7 +83,6 @@ bool OSystem_SDL_Symbian::hasFeature(Feature f) {
switch (f) {
case kFeatureFullscreenMode:
case kFeatureAspectRatioCorrection:
case kFeatureAutoComputeDirtyRects:
case kFeatureCursorHasPalette:
#ifdef USE_VIBRA_SE_PXXX
case kFeatureVibration:

View File

@ -926,7 +926,7 @@ const OSystem::GraphicsMode *OSystem_WINCE3::getSupportedGraphicsModes() const {
}
bool OSystem_WINCE3::hasFeature(Feature f) {
return (f == kFeatureAutoComputeDirtyRects || f == kFeatureVirtualKeyboard);
return (f == kFeatureVirtualKeyboard);
}
void OSystem_WINCE3::setFeatureState(Feature f, bool enable) {
@ -1151,14 +1151,12 @@ bool OSystem_WINCE3::update_scalers() {
_scaleFactorYm = 1;
_scaleFactorYd = 1;
_scalerProc = DownscaleHorizByThreeQuarters;
_modeFlags = 0;
} else {
_scaleFactorXm = 1;
_scaleFactorXd = 1;
_scaleFactorYm = 1;
_scaleFactorYd = 1;
_scalerProc = Normal1x;
_modeFlags = 0;
}
} else if ( _orientationLandscape && (_videoMode.screenWidth == 320 || !_videoMode.screenWidth)) {
if (!_panelVisible && !_hasSmartphoneResolution && !_overlayVisible && _canBeAspectScaled) {
@ -1167,7 +1165,6 @@ bool OSystem_WINCE3::update_scalers() {
_scaleFactorYm = 6;
_scaleFactorYd = 5;
_scalerProc = Normal1xAspect;
_modeFlags = 0;
_videoMode.aspectRatioCorrection = true;
} else {
_scaleFactorXm = 1;
@ -1175,7 +1172,6 @@ bool OSystem_WINCE3::update_scalers() {
_scaleFactorYm = 1;
_scaleFactorYd = 1;
_scalerProc = Normal1x;
_modeFlags = 0;
}
} else if (_videoMode.screenWidth == 640 && !(isOzone() && (getScreenWidth() >= 640 || getScreenHeight() >= 640))) {
_scaleFactorXm = 1;
@ -1183,14 +1179,12 @@ bool OSystem_WINCE3::update_scalers() {
_scaleFactorYm = 1;
_scaleFactorYd = 2;
_scalerProc = DownscaleAllByHalf;
_modeFlags = 0;
} else if (_videoMode.screenWidth == 640 && (isOzone() && (getScreenWidth() >= 640 || getScreenHeight() >= 640))) {
_scaleFactorXm = 1;
_scaleFactorXd = 1;
_scaleFactorYm = 1;
_scaleFactorYd = 1;
_scalerProc = Normal1x;
_modeFlags = 0;
}
return true;
@ -1203,7 +1197,6 @@ bool OSystem_WINCE3::update_scalers() {
_scaleFactorYm = 12;
_scaleFactorYd = 5;
_scalerProc = Normal2xAspect;
_modeFlags = 0;
_videoMode.aspectRatioCorrection = true;
} else if ( (_panelVisible || _overlayVisible) && _canBeAspectScaled ) {
_scaleFactorXm = 2;
@ -1211,7 +1204,6 @@ bool OSystem_WINCE3::update_scalers() {
_scaleFactorYm = 2;
_scaleFactorYd = 1;
_scalerProc = Normal2x;
_modeFlags = 0;
}
return true;
}
@ -1232,7 +1224,6 @@ bool OSystem_WINCE3::update_scalers() {
_scaleFactorYm = 7;
_scaleFactorYd = 8;
_scalerProc = SmartphoneLandscape;
_modeFlags = 0;
initZones();
return true;
}
@ -1824,7 +1815,6 @@ void OSystem_WINCE3::copyRectToOverlay(const OverlayColor *buf, int pitch, int x
return;
// Mark the modified region as dirty
_cksumValid = false;
addDirtyRect(x, y, w, h);
undrawMouse();
@ -1851,41 +1841,32 @@ void OSystem_WINCE3::copyRectToScreen(const byte *src, int pitch, int x, int y,
Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends
if (((long)src & 3) == 0 && pitch == _videoMode.screenWidth && x == 0 && y == 0 &&
w == _videoMode.screenWidth && h == _videoMode.screenHeight && _modeFlags & DF_WANT_RECT_OPTIM) {
/* Special, optimized case for full screen updates.
* It tries to determine what areas were actually changed,
* and just updates those, on the actual display. */
addDirtyRgnAuto(src);
} else {
/* Clip the coordinates */
if (x < 0) {
w += x;
src -= x;
x = 0;
}
if (y < 0) {
h += y;
src -= y * pitch;
y = 0;
}
if (w > _videoMode.screenWidth - x) {
w = _videoMode.screenWidth - x;
}
if (h > _videoMode.screenHeight - y) {
h = _videoMode.screenHeight - y;
}
if (w <= 0 || h <= 0)
return;
_cksumValid = false;
addDirtyRect(x, y, w, h);
/* Clip the coordinates */
if (x < 0) {
w += x;
src -= x;
x = 0;
}
if (y < 0) {
h += y;
src -= y * pitch;
y = 0;
}
if (w > _videoMode.screenWidth - x) {
w = _videoMode.screenWidth - x;
}
if (h > _videoMode.screenHeight - y) {
h = _videoMode.screenHeight - y;
}
if (w <= 0 || h <= 0)
return;
addDirtyRect(x, y, w, h);
undrawMouse();
// Try to lock the screen surface

View File

@ -51,7 +51,7 @@ static const char USAGE_STRING[] =
;
// DONT FIXME: DO NOT ORDER ALPHABETICALLY, THIS IS ORDERED BY IMPORTANCE/CATEGORY! :)
#if defined(PALMOS_MODE) || defined(__SYMBIAN32__) || defined(__GP32__)
#if defined(PALMOS_MODE) || defined(__SYMBIAN32__) || defined(__GP32__) || defined(ANDROID)
static const char HELP_STRING[] = "NoUsageString"; // save more data segment space
#else
static const char HELP_STRING[] =
@ -948,7 +948,7 @@ Common::Error processSettings(Common::String &command, Common::StringMap &settin
// environment variable. This is weaker than a --savepath on the
// command line, but overrides the default savepath, hence it is
// handled here, just before the command line gets parsed.
#if !defined(MACOS_CARBON) && !defined(_WIN32_WCE) && !defined(PALMOS_MODE) && !defined(__GP32__)
#if !defined(MACOS_CARBON) && !defined(_WIN32_WCE) && !defined(PALMOS_MODE) && !defined(__GP32__) && !defined(ANDROID)
if (!settings.contains("savepath")) {
const char *dir = getenv("SCUMMVM_SAVEPATH");
if (dir && *dir && strlen(dir) < MAXPATHLEN) {

View File

@ -222,6 +222,12 @@ public:
typedef const char * const_iterator;
iterator begin() {
// Since the user could potentially
// change the string via the returned
// iterator we have to assure we are
// pointing to a unique storage.
makeUnique();
return _str;
}

View File

@ -156,7 +156,7 @@ public:
class ReadStream : virtual public Stream {
public:
/**
* Returns true if a read failed because the stream has been reached.
* Returns true if a read failed because the stream end has been reached.
* This flag is cleared by clearErr().
* For a SeekableReadStream, it is also cleared by a successful seek.
*/

View File

@ -150,18 +150,6 @@ public:
*/
kFeatureVirtualKeyboard,
/**
* This flag is a bit more obscure: it gives a hint to the backend that
* the frontend code is very inefficient in doing screen updates. So
* the frontend might do a lot of fullscreen blits even though only a
* tiny portion of the actual screen data changed. In that case, it
* might pay off for the backend to compute which parts actually changed,
* and then only mark those as dirty.
* Implementing this is purely optional, and no harm should arise
* when not doing so (except for decreased speed in said frontends).
*/
kFeatureAutoComputeDirtyRects,
/**
* This flag determines whether or not the cursor can have its own palette.
* It is currently used only by some Macintosh versions of Humongous

View File

@ -43,6 +43,10 @@ extern bool isSmartphone();
#define fputs(str, file) DS::std_fwrite(str, strlen(str), 1, file)
#endif
#ifdef ANDROID
#include <android/log.h>
#endif
namespace Common {
static OutputFormatter s_errorOutputFormatter = 0;
@ -71,7 +75,9 @@ void warning(const char *s, ...) {
vsnprintf(buf, STRINGBUFLEN, s, va);
va_end(va);
#if !defined (__SYMBIAN32__)
#if defined( ANDROID )
__android_log_write(ANDROID_LOG_WARN, "ScummVM", buf);
#elif !defined (__SYMBIAN32__)
fputs("WARNING: ", stderr);
fputs(buf, stderr);
fputs("!\n", stderr);
@ -141,6 +147,10 @@ void NORETURN_PRE error(const char *s, ...) {
#endif
#endif
#ifdef ANDROID
__android_log_assert("Fatal error", "ScummVM", "%s", buf_output);
#endif
#ifdef PALMOS_MODE
extern void PalmFatalError(const char *err);
PalmFatalError(buf_output);

View File

@ -1433,11 +1433,11 @@ Common::SeekableReadStream *ZipArchive::createReadStreamForMember(const Common::
unz_file_info fileInfo;
unzOpenCurrentFile(_zipFile);
unzGetCurrentFileInfo(_zipFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
byte *buffer = (byte *)calloc(fileInfo.uncompressed_size+1, 1);
byte *buffer = (byte *)malloc(fileInfo.uncompressed_size);
assert(buffer);
unzReadCurrentFile(_zipFile, buffer, fileInfo.uncompressed_size);
unzCloseCurrentFile(_zipFile);
return new Common::MemoryReadStream(buffer, fileInfo.uncompressed_size+1, DisposeAfterUse::YES);
return new Common::MemoryReadStream(buffer, fileInfo.uncompressed_size, DisposeAfterUse::YES);
// FIXME: instead of reading all into a memory stream, we could
// instead create a new ZipStream class. But then we have to be

43
configure vendored
View File

@ -999,6 +999,11 @@ wince)
_host_cpu=arm
_host_alias=arm-wince-mingw32ce
;;
android)
_host_os=android
_host_cpu=arm
_host_alias=arm-android-eabi
;;
*)
if test -n "$_host"; then
guessed_host=`$_srcdir/config.sub $_host`
@ -1077,6 +1082,12 @@ psp)
exit 1
fi
;;
android)
if test -z "$ANDROID_SDK"; then
echo "Please set ANDROID_SDK in your environment. export ANDROID_SDK=<path to Android SDK>"
exit 1
fi
;;
*)
;;
esac
@ -1399,6 +1410,11 @@ case $_host_os in
DEFINES="$DEFINES -D_WIN32_WCE=300 -D__ARM__ -D_ARM_ -DUNICODE -DFPM_DEFAULT -DNONSTANDARD_PORT"
DEFINES="$DEFINES -DWIN32 -Dcdecl= -D__cdecl__="
;;
android)
DEFINES="$DEFINES -DUNIX"
CXXFLAGS="$CXXFLAGS -Os -msoft-float -mtune=xscale -march=armv5te -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5TE__"
add_line_to_config_mk "ANDROID_SDK = $ANDROID_SDK"
;;
# given this is a shell script assume some type of unix
*)
echo "WARNING: could not establish system type, assuming unix like"
@ -1647,6 +1663,19 @@ if test -n "$_host"; then
_mt32emu="no"
_port_mk="backends/platform/wince/wince.mk"
;;
android)
DEFINES="$DEFINES -DANDROID -DUNIX -DUSE_ARM_SMUSH_ASM"
_endian=little
_need_memalign=yes
add_line_to_config_mk 'USE_ARM_SOUND_ASM = 1'
add_line_to_config_mk 'USE_ARM_SMUSH_ASM = 1'
add_line_to_config_mk 'USE_ARM_GFX_ASM = 1'
add_line_to_config_mk 'USE_ARM_SCALER_ASM = 1'
add_line_to_config_mk 'USE_ARM_COSTUME_ASM = 1'
_backend="android"
_port_mk="backends/platform/android/android.mk"
_build_hq_scalers="no"
;;
*)
echo "WARNING: Unknown target, continuing with auto-detected values"
;;
@ -1825,7 +1854,7 @@ POST_OBJS_FLAGS := -Wl,-no-whole-archive
LIBS += -ldl
'
;;
linux*)
linux*|android)
_def_plugin='
#define PLUGIN_PREFIX "lib"
#define PLUGIN_SUFFIX ".so"
@ -2432,6 +2461,14 @@ case $_backend in
INCLUDES="$INCLUDES "'-I$(srcdir) -I$(srcdir)/backends/platform/wince -I$(srcdir)/engines -I$(srcdir)/backends/platform/wince/missing/gcc -I$(srcdir)/backends/platform/wince/CEgui -I$(srcdir)/backends/platform/wince/CEkeys'
LIBS="$LIBS -static -lSDL"
;;
android)
# -lgcc is carefully placed here - we want to catch
# all toolchain symbols in *our* libraries rather
# than pick up anything unhygenic from the Android libs.
LIBS="$LIBS -lgcc -lstdc++ -llog -lGLESv1_CM -lEGL"
DEFINES="$DEFINES -D__ANDROID__ -DANDROID_BACKEND -DREDUCE_MEMORY_USAGE"
add_line_to_config_mk 'PLUGIN_LDFLAGS += $(LDFLAGS) -Wl,-shared,-Bsymbolic'
;;
*)
echo "support for $_backend backend not implemented in configure script yet"
exit 1
@ -2447,7 +2484,7 @@ if test "$have_gcc" = yes ; then
case $_host_os in
# newlib-based system include files suppress non-C89 function
# declarations under __STRICT_ANSI__
mingw* | dreamcast | wii | gamecube | psp | wince | amigaos*)
mingw* | dreamcast | wii | gamecube | psp | wince | amigaos* | android)
CXXFLAGS="$CXXFLAGS -W -Wno-unused-parameter"
;;
*)
@ -2468,7 +2505,7 @@ fi;
# Some platforms use certain GNU extensions in header files
case $_host_os in
gamecube | psp | wii)
gamecube | psp | wii | android)
;;
*)
CXXFLAGS="$CXXFLAGS -pedantic"

169
dists/android/mkmanifest.pl Normal file
View File

@ -0,0 +1,169 @@
#!/usr/bin/perl
use File::Basename qw(dirname);
use File::Path qw(mkpath);
use IO::File;
use XML::Writer;
use XML::Parser;
use Getopt::Long;
use warnings;
use strict;
use constant ANDROID => 'http://schemas.android.com/apk/res/android';
my $id;
my $package_versionName;
my $package_versionCode;
my $configure = 'configure';
my $stringres = 'res/string/values.xml';
my $manifest = 'AndroidManifest.xml';
my $master_manifest;
my @unpack_libs;
GetOptions('id=s' => \$id,
'version-name=s' => \$package_versionName,
'version-code=i' => \$package_versionCode,
'configure=s' => \$configure,
'stringres=s' => \$stringres,
'manifest=s' => \$manifest,
'master-manifest=s' => \$master_manifest,
'unpacklib=s' => \@unpack_libs,
) or die;
die "Missing required arg"
unless $id and $package_versionName and $package_versionCode;
sub grope_engine_info {
my $configure = shift;
my @ret;
while (<$configure>) {
m/^add_engine \s+ (\w+) \s+ "(.*?)" \s+ \w+ (?:\s+ "([\w\s]*)")?/x
or next;
my $subengines = $3 || '';
my %info = (id => $1, name => $2,
subengines => [split / /, $subengines]);
push @ret, \%info;
}
return @ret;
}
sub read_constraints {
my $manifest = shift;
my @constraints;
my $parser = new XML::Parser Handlers => {
Start => sub {
my $expat = shift;
my $elem = shift;
return if $elem !~
/^(uses-configuration|supports-screens|uses-sdk)$/;
my @constraint = ($elem);
while (@_) {
my $attr = shift;
my $value = shift;
$attr = [ANDROID, $attr] if $attr =~ s/^android://;
push @constraint, $attr, $value;
}
push @constraints, \@constraint;
},
};
$parser->parse($manifest);
return @constraints;
}
sub print_stringres {
my $output = shift;
my $info = shift;
my $writer = new XML::Writer(OUTPUT => $output, ENCODING => 'utf-8',
DATA_MODE => 1, DATA_INDENT => 2);
$writer->xmlDecl();
$writer->startTag('resources');
while (my ($k,$v) = each %$info) {
$writer->dataElement('string', $v, name => $k);
}
$writer->endTag('resources');
$writer->end();
}
sub print_manifest {
my $output = shift;
my $info = shift;
my $constraints = shift;
my $writer = new XML::Writer(OUTPUT => $output, ENCODING => 'utf-8',
DATA_MODE => 1, DATA_INDENT => 2,
NAMESPACES => 1,
PREFIX_MAP => {ANDROID, 'android'});
$writer->xmlDecl();
$writer->startTag(
'manifest',
'package' => "org.inodes.gus.scummvm.plugin.$info->{name}",
[ANDROID, 'versionCode'] => $package_versionCode,
[ANDROID, 'versionName'] => $package_versionName,
);
$writer->startTag(
'application',
[ANDROID, 'label'] => '@string/app_name',
[ANDROID, 'description'] => '@string/app_desc',
[ANDROID, 'icon'] => '@drawable/scummvm',
);
$writer->startTag(
'receiver',
[ANDROID, 'name'] => 'org.inodes.gus.scummvm.PluginProvider',
[ANDROID, 'process'] => 'org.inodes.gus.scummvm');
$writer->startTag('intent-filter');
$writer->emptyTag('action', [ANDROID, 'name'] =>
'org.inodes.gus.scummvm.action.PLUGIN_QUERY');
$writer->emptyTag('category', [ANDROID, 'name'] =>
'android.intent.category.INFO');
$writer->endTag('intent-filter');
$writer->emptyTag(
'meta-data',
[ANDROID, 'name'] => 'org.inodes.gus.scummvm.meta.UNPACK_LIB',
[ANDROID, 'value'] => $_)
for @{$info->{unpack_libs}};
$writer->endTag('receiver');
$writer->endTag('application');
$writer->emptyTag('uses-permission', [ANDROID, 'name'] =>
'org.inodes.gus.scummvm.permission.SCUMMVM_PLUGIN');
$writer->emptyTag(@$_) foreach @$constraints;
$writer->endTag('manifest');
$writer->end();
}
my %engines;
for my $engine (grope_engine_info(new IO::File $configure, 'r')) {
$engines{$engine->{id}} = $engine;
}
my @games = ($id, @{$engines{$id}{subengines}});
my $games_desc = join('; ', map $engines{$_}{name}, @games);
my @constraints = read_constraints(new IO::File $master_manifest, 'r');
print "Writing $stringres ...\n";
mkpath(dirname($stringres));
print_stringres(IO::File->new($stringres, 'w'),
{app_name => qq{ScummVM plugin: "$id"},
app_desc => "Game engine for: $games_desc",
});
print "Writing $manifest ...\n";
mkpath(dirname($manifest));
print_manifest(IO::File->new($manifest, 'w'),
{name => $id, unpack_libs => \@unpack_libs}, \@constraints);
exit 0;

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#e9bb8b"
android:endColor="#d16e09"
android:angle="315" />
</shape>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<org.inodes.gus.scummvm.EditableSurfaceView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:id="@+id/main_surface"
android:gravity="center"
android:keepScreenOn="true"
android:focusable="true"
android:focusableInTouchMode="true"
/>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:gravity="center"
android:background="@drawable/gradient"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:src="@drawable/scummvm_big" />
<ProgressBar android:id="@+id/progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="300dip"
android:layout_height="wrap_content"
android:padding="20dip"/>
</LinearLayout>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">ScummVM</string>
<string name="app_desc">Graphic adventure game engine</string>
<string name="quit">Quit</string>
<string name="scummvm_perm_plugin_label">ScummVM plugin</string>
<string name="scummvm_perm_plugin_desc">Allows the application to
provide a ScummVM loadable plugin: code that will be executed in the
ScummVM application. Malicious plugins may do anything ScummVM
itself could do: write to your SD card, delete your savegames,
change the ScummVM background to puce, replace menu labels with rude
words, etc.</string>
<string name="no_sdcard_title">No SD card?</string>
<string name="no_sdcard">Unable to read your SD card. This usually
means you still have it mounted on your PC. Unmount, reinsert,
whatever and then try again.</string>
<string name="no_plugins_title">No plugins found</string>
<string name="no_plugins_found">ScummVM requires at least one <i>game
engine</i> to be useful. Engines are available as separate plugin
packages, from wherever you found ScummVM.</string>
<string name="to_market">To Market</string>
</resources>

View File

@ -735,7 +735,6 @@
DF09418A0F63CB26002D821E /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAAAFFB0F0112DF003E9390 /* detection.cpp */; };
DF09418B0F63CB26002D821E /* thumbnail_intern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAAB0010F011392003E9390 /* thumbnail_intern.cpp */; };
DF09418C0F63CB26002D821E /* dither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFB900F485D890006E566 /* dither.cpp */; };
DF0941910F63CB26002D821E /* video_player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBA70F485D950006E566 /* video_player.cpp */; };
DF0941920F63CB26002D821E /* debug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBD10F485DFB0006E566 /* debug.cpp */; };
DF0941930F63CB26002D821E /* GuiManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBD50F485E360006E566 /* GuiManager.cpp */; };
DF0941940F63CB26002D821E /* posix-saves.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBF80F4860A60006E566 /* posix-saves.cpp */; };
@ -875,7 +874,6 @@
DF2EC51910E64EE600765801 /* wave6581.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2EC51710E64EE600765801 /* wave6581.cpp */; };
DF2EC51A10E64EE600765801 /* wave6581.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2EC51710E64EE600765801 /* wave6581.cpp */; };
DF2FFB930F485D890006E566 /* dither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFB900F485D890006E566 /* dither.cpp */; };
DF2FFBB70F485D950006E566 /* video_player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBA70F485D950006E566 /* video_player.cpp */; };
DF2FFBD30F485DFB0006E566 /* debug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBD10F485DFB0006E566 /* debug.cpp */; };
DF2FFBD90F485E360006E566 /* GuiManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBD50F485E360006E566 /* GuiManager.cpp */; };
DF2FFBFC0F4860A60006E566 /* posix-saves.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBF80F4860A60006E566 /* posix-saves.cpp */; };
@ -1915,6 +1913,33 @@
DFAAAFFC0F0112DF003E9390 /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAAAFFB0F0112DF003E9390 /* detection.cpp */; };
DFAAB0020F011392003E9390 /* thumbnail_intern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAAB0010F011392003E9390 /* thumbnail_intern.cpp */; };
DFAAD23D0F50120E00C3A4E2 /* console.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAAD2390F50120E00C3A4E2 /* console.cpp */; };
DFB0576811B753AF0015AE65 /* mpeg_player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576211B753AF0015AE65 /* mpeg_player.cpp */; };
DFB0576911B753AF0015AE65 /* qt_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576411B753AF0015AE65 /* qt_decoder.cpp */; };
DFB0576A11B753AF0015AE65 /* video_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576611B753AF0015AE65 /* video_decoder.cpp */; };
DFB0576B11B753AF0015AE65 /* mpeg_player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576211B753AF0015AE65 /* mpeg_player.cpp */; };
DFB0576C11B753AF0015AE65 /* qt_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576411B753AF0015AE65 /* qt_decoder.cpp */; };
DFB0576D11B753AF0015AE65 /* video_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576611B753AF0015AE65 /* video_decoder.cpp */; };
DFB0576E11B753AF0015AE65 /* mpeg_player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576211B753AF0015AE65 /* mpeg_player.cpp */; };
DFB0576F11B753AF0015AE65 /* qt_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576411B753AF0015AE65 /* qt_decoder.cpp */; };
DFB0577011B753AF0015AE65 /* video_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0576611B753AF0015AE65 /* video_decoder.cpp */; };
DFB0577611B753DA0015AE65 /* rational.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577411B753DA0015AE65 /* rational.cpp */; };
DFB0577711B753DA0015AE65 /* rational.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577411B753DA0015AE65 /* rational.cpp */; };
DFB0577811B753DA0015AE65 /* rational.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577411B753DA0015AE65 /* rational.cpp */; };
DFB0578011B7541F0015AE65 /* resource_audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577D11B7541F0015AE65 /* resource_audio.cpp */; };
DFB0578111B7541F0015AE65 /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577E11B7541F0015AE65 /* util.cpp */; };
DFB0578211B7541F0015AE65 /* resource_audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577D11B7541F0015AE65 /* resource_audio.cpp */; };
DFB0578311B7541F0015AE65 /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577E11B7541F0015AE65 /* util.cpp */; };
DFB0578411B7541F0015AE65 /* resource_audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577D11B7541F0015AE65 /* resource_audio.cpp */; };
DFB0578511B7541F0015AE65 /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0577E11B7541F0015AE65 /* util.cpp */; };
DFB0578A11B754570015AE65 /* maciconbar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0578811B754570015AE65 /* maciconbar.cpp */; };
DFB0578B11B754570015AE65 /* maciconbar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0578811B754570015AE65 /* maciconbar.cpp */; };
DFB0578C11B754570015AE65 /* maciconbar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0578811B754570015AE65 /* maciconbar.cpp */; };
DFB0579111B7547D0015AE65 /* pict.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0578F11B7547D0015AE65 /* pict.cpp */; };
DFB0579211B7547D0015AE65 /* pict.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0578F11B7547D0015AE65 /* pict.cpp */; };
DFB0579311B7547D0015AE65 /* pict.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0578F11B7547D0015AE65 /* pict.cpp */; };
DFB0579811B7549C0015AE65 /* cinepak.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0579611B7549C0015AE65 /* cinepak.cpp */; };
DFB0579911B7549C0015AE65 /* cinepak.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0579611B7549C0015AE65 /* cinepak.cpp */; };
DFB0579A11B7549C0015AE65 /* cinepak.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFB0579611B7549C0015AE65 /* cinepak.cpp */; };
DFC831210F48AF19005EF03C /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFC8301A0F48AF18005EF03C /* detection.cpp */; };
DFC831230F48AF19005EF03C /* game.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFC8301D0F48AF18005EF03C /* game.cpp */; };
DFC831240F48AF19005EF03C /* gc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFC8301E0F48AF18005EF03C /* gc.cpp */; };
@ -2796,7 +2821,6 @@
DFF95C1F0FB22D5700A3EC78 /* detection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAAAFFB0F0112DF003E9390 /* detection.cpp */; };
DFF95C200FB22D5700A3EC78 /* thumbnail_intern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAAB0010F011392003E9390 /* thumbnail_intern.cpp */; };
DFF95C210FB22D5700A3EC78 /* dither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFB900F485D890006E566 /* dither.cpp */; };
DFF95C260FB22D5700A3EC78 /* video_player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBA70F485D950006E566 /* video_player.cpp */; };
DFF95C270FB22D5700A3EC78 /* debug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBD10F485DFB0006E566 /* debug.cpp */; };
DFF95C280FB22D5700A3EC78 /* GuiManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBD50F485E360006E566 /* GuiManager.cpp */; };
DFF95C290FB22D5700A3EC78 /* posix-saves.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF2FFBF80F4860A60006E566 /* posix-saves.cpp */; };
@ -2985,8 +3009,6 @@
DF2FFB900F485D890006E566 /* dither.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dither.cpp; sourceTree = "<group>"; };
DF2FFB910F485D890006E566 /* dither.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dither.h; sourceTree = "<group>"; };
DF2FFB920F485D890006E566 /* pixelformat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pixelformat.h; sourceTree = "<group>"; };
DF2FFBA70F485D950006E566 /* video_player.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = video_player.cpp; sourceTree = "<group>"; };
DF2FFBA80F485D950006E566 /* video_player.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = video_player.h; sourceTree = "<group>"; };
DF2FFBD10F485DFB0006E566 /* debug.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debug.cpp; sourceTree = "<group>"; };
DF2FFBD20F485DFB0006E566 /* debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = debug.h; sourceTree = "<group>"; };
DF2FFBD50F485E360006E566 /* GuiManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GuiManager.cpp; sourceTree = "<group>"; };
@ -4358,6 +4380,24 @@
DFAAB0010F011392003E9390 /* thumbnail_intern.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thumbnail_intern.cpp; sourceTree = "<group>"; };
DFAAD2390F50120E00C3A4E2 /* console.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = console.cpp; sourceTree = "<group>"; };
DFAAD23A0F50120E00C3A4E2 /* console.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = console.h; sourceTree = "<group>"; };
DFB0576211B753AF0015AE65 /* mpeg_player.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mpeg_player.cpp; sourceTree = "<group>"; };
DFB0576311B753AF0015AE65 /* mpeg_player.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mpeg_player.h; sourceTree = "<group>"; };
DFB0576411B753AF0015AE65 /* qt_decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = qt_decoder.cpp; sourceTree = "<group>"; };
DFB0576511B753AF0015AE65 /* qt_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = qt_decoder.h; sourceTree = "<group>"; };
DFB0576611B753AF0015AE65 /* video_decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = video_decoder.cpp; sourceTree = "<group>"; };
DFB0576711B753AF0015AE65 /* video_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = video_decoder.h; sourceTree = "<group>"; };
DFB0577311B753DA0015AE65 /* debug-channels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "debug-channels.h"; sourceTree = "<group>"; };
DFB0577411B753DA0015AE65 /* rational.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rational.cpp; sourceTree = "<group>"; };
DFB0577511B753DA0015AE65 /* rational.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rational.h; sourceTree = "<group>"; };
DFB0577D11B7541F0015AE65 /* resource_audio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = resource_audio.cpp; sourceTree = "<group>"; };
DFB0577E11B7541F0015AE65 /* util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = util.cpp; sourceTree = "<group>"; };
DFB0577F11B7541F0015AE65 /* util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = util.h; sourceTree = "<group>"; };
DFB0578811B754570015AE65 /* maciconbar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = maciconbar.cpp; sourceTree = "<group>"; };
DFB0578911B754570015AE65 /* maciconbar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = maciconbar.h; sourceTree = "<group>"; };
DFB0578F11B7547D0015AE65 /* pict.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pict.cpp; sourceTree = "<group>"; };
DFB0579011B7547D0015AE65 /* pict.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pict.h; sourceTree = "<group>"; };
DFB0579611B7549C0015AE65 /* cinepak.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cinepak.cpp; sourceTree = "<group>"; };
DFB0579711B7549C0015AE65 /* cinepak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cinepak.h; sourceTree = "<group>"; };
DFC8301A0F48AF18005EF03C /* detection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = detection.cpp; sourceTree = "<group>"; };
DFC8301D0F48AF18005EF03C /* game.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = game.cpp; sourceTree = "<group>"; };
DFC8301E0F48AF18005EF03C /* gc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gc.cpp; sourceTree = "<group>"; };
@ -4776,6 +4816,12 @@
DF2FFB940F485D950006E566 /* video */ = {
isa = PBXGroup;
children = (
DFB0576211B753AF0015AE65 /* mpeg_player.cpp */,
DFB0576311B753AF0015AE65 /* mpeg_player.h */,
DFB0576411B753AF0015AE65 /* qt_decoder.cpp */,
DFB0576511B753AF0015AE65 /* qt_decoder.h */,
DFB0576611B753AF0015AE65 /* video_decoder.cpp */,
DFB0576711B753AF0015AE65 /* video_decoder.h */,
DF90EABF10B023F300C8F93F /* codecs */,
DF90EAB610B023D100C8F93F /* avi_decoder.cpp */,
DF90EAB710B023D100C8F93F /* avi_decoder.h */,
@ -4786,8 +4832,6 @@
DF6118630FE3A9410042AD3F /* flic_decoder.h */,
DF6118640FE3A9410042AD3F /* smk_decoder.cpp */,
DF6118650FE3A9410042AD3F /* smk_decoder.h */,
DF2FFBA70F485D950006E566 /* video_player.cpp */,
DF2FFBA80F485D950006E566 /* video_player.h */,
);
path = video;
sourceTree = "<group>";
@ -4884,6 +4928,8 @@
DF45B175116628A5009B85CC /* graphics */ = {
isa = PBXGroup;
children = (
DFB0578811B754570015AE65 /* maciconbar.cpp */,
DFB0578911B754570015AE65 /* maciconbar.h */,
DF9B924F118E46A00069C19D /* fontsjis.cpp */,
DF9B9250118E46A00069C19D /* fontsjis.h */,
DF45B176116628A5009B85CC /* animate.cpp */,
@ -6388,6 +6434,8 @@
DF90EABF10B023F300C8F93F /* codecs */ = {
isa = PBXGroup;
children = (
DFB0579611B7549C0015AE65 /* cinepak.cpp */,
DFB0579711B7549C0015AE65 /* cinepak.h */,
DFCDC6FC11662AD700A7D2A0 /* msrle.cpp */,
DFCDC6FD11662AD700A7D2A0 /* msrle.h */,
DF90EAC010B023F400C8F93F /* codec.h */,
@ -6414,6 +6462,9 @@
DFC830190F48AF18005EF03C /* sci */ = {
isa = PBXGroup;
children = (
DFB0577D11B7541F0015AE65 /* resource_audio.cpp */,
DFB0577E11B7541F0015AE65 /* util.cpp */,
DFB0577F11B7541F0015AE65 /* util.h */,
DF45B175116628A5009B85CC /* graphics */,
DF45B1A5116628A5009B85CC /* parser */,
DF45B1AB116628A5009B85CC /* sound */,
@ -6685,6 +6736,9 @@
DFE473950D81F4E800B6D1FB /* common */ = {
isa = PBXGroup;
children = (
DFB0577311B753DA0015AE65 /* debug-channels.h */,
DFB0577411B753DA0015AE65 /* rational.cpp */,
DFB0577511B753DA0015AE65 /* rational.h */,
DF9B9261118E46FE0069C19D /* error.cpp */,
DFEC5D0A1166C5CF00C90552 /* random.cpp */,
DFEC5D0B1166C5CF00C90552 /* random.h */,
@ -6767,6 +6821,8 @@
DFE477520D81F4E900B6D1FB /* graphics */ = {
isa = PBXGroup;
children = (
DFB0578F11B7547D0015AE65 /* pict.cpp */,
DFB0579011B7547D0015AE65 /* pict.h */,
DF6BF4C010529DA50069811F /* conversion.cpp */,
DF6BF4C110529DA50069811F /* conversion.h */,
DF6BF4C210529DA50069811F /* jpeg.cpp */,
@ -7834,7 +7890,6 @@
DFAAAFFC0F0112DF003E9390 /* detection.cpp in Sources */,
DFAAB0020F011392003E9390 /* thumbnail_intern.cpp in Sources */,
DF2FFB930F485D890006E566 /* dither.cpp in Sources */,
DF2FFBB70F485D950006E566 /* video_player.cpp in Sources */,
DF2FFBD30F485DFB0006E566 /* debug.cpp in Sources */,
DF2FFBD90F485E360006E566 /* GuiManager.cpp in Sources */,
DF2FFBFC0F4860A60006E566 /* posix-saves.cpp in Sources */,
@ -8055,6 +8110,15 @@
DF9B9249118E46730069C19D /* error.cpp in Sources */,
DF9B9254118E46A00069C19D /* fontsjis.cpp in Sources */,
DF9B9263118E46FE0069C19D /* error.cpp in Sources */,
DFB0576B11B753AF0015AE65 /* mpeg_player.cpp in Sources */,
DFB0576C11B753AF0015AE65 /* qt_decoder.cpp in Sources */,
DFB0576D11B753AF0015AE65 /* video_decoder.cpp in Sources */,
DFB0577711B753DA0015AE65 /* rational.cpp in Sources */,
DFB0578211B7541F0015AE65 /* resource_audio.cpp in Sources */,
DFB0578311B7541F0015AE65 /* util.cpp in Sources */,
DFB0578B11B754570015AE65 /* maciconbar.cpp in Sources */,
DFB0579211B7547D0015AE65 /* pict.cpp in Sources */,
DFB0579911B7549C0015AE65 /* cinepak.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -8781,7 +8845,6 @@
DF09418A0F63CB26002D821E /* detection.cpp in Sources */,
DF09418B0F63CB26002D821E /* thumbnail_intern.cpp in Sources */,
DF09418C0F63CB26002D821E /* dither.cpp in Sources */,
DF0941910F63CB26002D821E /* video_player.cpp in Sources */,
DF0941920F63CB26002D821E /* debug.cpp in Sources */,
DF0941930F63CB26002D821E /* GuiManager.cpp in Sources */,
DF0941940F63CB26002D821E /* posix-saves.cpp in Sources */,
@ -9006,6 +9069,15 @@
DF9B924A118E46730069C19D /* error.cpp in Sources */,
DF9B9256118E46A00069C19D /* fontsjis.cpp in Sources */,
DF9B9264118E46FE0069C19D /* error.cpp in Sources */,
DFB0576E11B753AF0015AE65 /* mpeg_player.cpp in Sources */,
DFB0576F11B753AF0015AE65 /* qt_decoder.cpp in Sources */,
DFB0577011B753AF0015AE65 /* video_decoder.cpp in Sources */,
DFB0577811B753DA0015AE65 /* rational.cpp in Sources */,
DFB0578411B7541F0015AE65 /* resource_audio.cpp in Sources */,
DFB0578511B7541F0015AE65 /* util.cpp in Sources */,
DFB0578C11B754570015AE65 /* maciconbar.cpp in Sources */,
DFB0579311B7547D0015AE65 /* pict.cpp in Sources */,
DFB0579A11B7549C0015AE65 /* cinepak.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -9737,7 +9809,6 @@
DFF95C1F0FB22D5700A3EC78 /* detection.cpp in Sources */,
DFF95C200FB22D5700A3EC78 /* thumbnail_intern.cpp in Sources */,
DFF95C210FB22D5700A3EC78 /* dither.cpp in Sources */,
DFF95C260FB22D5700A3EC78 /* video_player.cpp in Sources */,
DFF95C270FB22D5700A3EC78 /* debug.cpp in Sources */,
DFF95C280FB22D5700A3EC78 /* GuiManager.cpp in Sources */,
DFF95C290FB22D5700A3EC78 /* posix-saves.cpp in Sources */,
@ -9958,6 +10029,15 @@
DF9B9248118E46730069C19D /* error.cpp in Sources */,
DF9B9252118E46A00069C19D /* fontsjis.cpp in Sources */,
DF9B9262118E46FE0069C19D /* error.cpp in Sources */,
DFB0576811B753AF0015AE65 /* mpeg_player.cpp in Sources */,
DFB0576911B753AF0015AE65 /* qt_decoder.cpp in Sources */,
DFB0576A11B753AF0015AE65 /* video_decoder.cpp in Sources */,
DFB0577611B753DA0015AE65 /* rational.cpp in Sources */,
DFB0578011B7541F0015AE65 /* resource_audio.cpp in Sources */,
DFB0578111B7541F0015AE65 /* util.cpp in Sources */,
DFB0578A11B754570015AE65 /* maciconbar.cpp in Sources */,
DFB0579111B7547D0015AE65 /* pict.cpp in Sources */,
DFB0579811B7549C0015AE65 /* cinepak.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -10061,6 +10141,8 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
FRAMEWORK_SEARCH_PATHS = "";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_ENABLE_CPP_EXCEPTIONS = NO;
GCC_ENABLE_CPP_RTTI = NO;
GCC_INPUT_FILETYPE = automatic;
GCC_PREPROCESSOR_DEFINITIONS = (
XCODE,
@ -10128,6 +10210,8 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
FRAMEWORK_SEARCH_PATHS = "";
GCC_ENABLE_CPP_EXCEPTIONS = NO;
GCC_ENABLE_CPP_RTTI = NO;
GCC_INPUT_FILETYPE = automatic;
GCC_PREPROCESSOR_DEFINITIONS = (
XCODE,

13
dists/os2/readme.os2 Normal file
View File

@ -0,0 +1,13 @@
This is an OS/2 & eComStation build of ScummVM 1.1.1
To run, it requires:
* The libc063 runtime dll, available from ftp://ftp.netlabs.org/pub/gcc/libc-0.6.3-csd3.zip
* The gcc444 runtime dll, available from http://download.smedley.info/gcc444.zip
* The SDL dll's available from ftp://ftp.netlabs.org/pub/sdl/sdl-1.2.10-bin-20080804.zip
All feedback is appreciated!
Thanks!
Paul Smedley
5th May, 2010

BIN
dists/os2/scummvm.ico Normal file

Binary file not shown.

View File

@ -35,8 +35,9 @@
#include "gui/GuiManager.h"
#include "gui/launcher.h"
#include "gui/ListWidget.h"
#include "gui/ThemeEval.h"
#include "gui/options.h"
#include "gui/saveload.h"
#include "gui/ThemeEval.h"
#include "engines/dialogs.h"
#include "engines/engine.h"
@ -49,16 +50,17 @@
using GUI::CommandSender;
using GUI::StaticTextWidget;
enum {
kSaveCmd = 'SAVE',
kLoadCmd = 'LOAD',
kPlayCmd = 'PLAY',
kOptionsCmd = 'OPTN',
kHelpCmd = 'HELP',
kAboutCmd = 'ABOU',
kQuitCmd = 'QUIT',
kRTLCmd = 'RTL ',
kChooseCmd = 'CHOS'
class ConfigDialog : public GUI::OptionsDialog {
protected:
#ifdef SMALL_SCREEN_DEVICE
GUI::Dialog *_keysDialog;
#endif
public:
ConfigDialog(bool subtitleControls);
~ConfigDialog();
virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
};
MainMenuDialog::MainMenuDialog(Engine *engine)
@ -95,6 +97,12 @@ MainMenuDialog::MainMenuDialog(Engine *engine)
new GUI::ButtonWidget(this, "GlobalMenu.Options", "Options", kOptionsCmd, 'O');
// The help button is disabled by default.
// To enable "Help", an engine needs to use a subclass of MainMenuDialog
// (at least for now, we might change how this works in the future).
_helpButton = new GUI::ButtonWidget(this, "GlobalMenu.Help", "Help", kHelpCmd, 'H');
_helpButton->setEnabled(false);
new GUI::ButtonWidget(this, "GlobalMenu.About", "About", kAboutCmd, 'A');
_rtlButton = new GUI::ButtonWidget(this, "GlobalMenu.RTL", "Return to Launcher", kRTLCmd, 'R');
@ -135,6 +143,9 @@ void MainMenuDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
case kAboutCmd:
_aboutDialog->runModal();
break;
case kHelpCmd:
// Not handled here -- needs to be handled by a subclass (for now)
break;
case kRTLCmd: {
Common::Event eventRTL;
eventRTL.type = Common::EVENT_RTL;
@ -263,13 +274,13 @@ enum {
// "" as value for the domain, and in fact provide a somewhat better user
// experience at the same time.
ConfigDialog::ConfigDialog(bool subtitleControls)
: GUI::OptionsDialog("", "ScummConfig") {
: GUI::OptionsDialog("", "GlobalConfig") {
//
// Sound controllers
//
addVolumeControls(this, "ScummConfig.");
addVolumeControls(this, "GlobalConfig.");
setVolumeSettingsState(true); // could disable controls by GUI options
//
@ -278,7 +289,7 @@ ConfigDialog::ConfigDialog(bool subtitleControls)
if (subtitleControls) {
// Global talkspeed range of 0-255
addSubtitleControls(this, "ScummConfig.", 255);
addSubtitleControls(this, "GlobalConfig.", 255);
setSubtitleSettingsState(true); // could disable controls by GUI options
}
@ -286,11 +297,11 @@ ConfigDialog::ConfigDialog(bool subtitleControls)
// Add the buttons
//
new GUI::ButtonWidget(this, "ScummConfig.Ok", "OK", GUI::kOKCmd, 'O');
new GUI::ButtonWidget(this, "ScummConfig.Cancel", "Cancel", GUI::kCloseCmd, 'C');
new GUI::ButtonWidget(this, "GlobalConfig.Ok", "OK", GUI::kOKCmd, 'O');
new GUI::ButtonWidget(this, "GlobalConfig.Cancel", "Cancel", GUI::kCloseCmd, 'C');
#ifdef SMALL_SCREEN_DEVICE
new GUI::ButtonWidget(this, "ScummConfig.Keys", "Keys", kKeysCmd, 'K');
new GUI::ButtonWidget(this, "GlobalConfig.Keys", "Keys", kKeysCmd, 'K');
_keysDialog = NULL;
#endif
}

View File

@ -27,7 +27,6 @@
#include "common/str.h"
#include "gui/dialog.h"
#include "gui/options.h"
class Engine;
@ -38,6 +37,19 @@ namespace GUI {
}
class MainMenuDialog : public GUI::Dialog {
public:
enum {
kSaveCmd = 'SAVE',
kLoadCmd = 'LOAD',
kPlayCmd = 'PLAY',
kOptionsCmd = 'OPTN',
kHelpCmd = 'HELP',
kAboutCmd = 'ABOU',
kQuitCmd = 'QUIT',
kRTLCmd = 'RTL ',
kChooseCmd = 'CHOS'
};
public:
MainMenuDialog(Engine *engine);
~MainMenuDialog();
@ -51,29 +63,20 @@ protected:
void load();
protected:
Engine *_engine;
Engine *_engine;
GUI::GraphicsWidget *_logo;
GUI::ButtonWidget *_rtlButton;
GUI::ButtonWidget *_loadButton;
GUI::ButtonWidget *_saveButton;
GUI::Dialog *_aboutDialog;
GUI::Dialog *_optionsDialog;
GUI::SaveLoadChooser *_loadDialog;
GUI::SaveLoadChooser *_saveDialog;
};
GUI::GraphicsWidget *_logo;
class ConfigDialog : public GUI::OptionsDialog {
protected:
#ifdef SMALL_SCREEN_DEVICE
GUI::Dialog *_keysDialog;
#endif
GUI::ButtonWidget *_rtlButton;
GUI::ButtonWidget *_loadButton;
GUI::ButtonWidget *_saveButton;
GUI::ButtonWidget *_helpButton;
public:
ConfigDialog(bool subtitleControls);
~ConfigDialog();
GUI::Dialog *_aboutDialog;
GUI::Dialog *_optionsDialog;
virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
GUI::SaveLoadChooser *_loadDialog;
GUI::SaveLoadChooser *_saveDialog;
};
#endif

View File

@ -26,11 +26,7 @@
#ifndef GROOVIE_CELL_H
#define GROOVIE_CELL_H
#include "common/file.h"
#include "common/util.h"
#include "groovie/cell.h"
#include "groovie/groovie.h"
#include "common/textconsole.h"
#define BOARDSIZE 7
#define CELL_CLEAR 0

View File

@ -26,9 +26,8 @@
#ifndef GROOVIE_CURSOR_H
#define GROOVIE_CURSOR_H
#include "common/system.h"
#include "common/array.h"
#include "common/file.h"
#include "common/system.h"
namespace Common {
class MacResManager;

View File

@ -24,15 +24,17 @@
*/
#include "groovie/debug.h"
#include "groovie/graphics.h"
#include "groovie/groovie.h"
#include "groovie/script.h"
#include "common/debug-channels.h"
#include "common/system.h"
namespace Groovie {
Debugger::Debugger(GroovieEngine *vm) :
_vm (vm), _script(_vm->_script), _syst(_vm->_system) {
_vm(vm), _script(_vm->_script) {
// Register the debugger comands
DCmd_Register("step", WRAP_METHOD(Debugger, cmd_step));
@ -136,7 +138,7 @@ bool Debugger::cmd_playref(int argc, const char **argv) {
bool Debugger::cmd_dumppal(int argc, const char **argv) {
uint16 i;
byte palettedump[256 * 4];
_syst->grabPalette(palettedump, 0, 256);
_vm->_system->grabPalette(palettedump, 0, 256);
for (i = 0; i < 256; i++) {
DebugPrintf("%3d: %3d,%3d,%3d,%3d\n", i, palettedump[(i * 4)], palettedump[(i * 4) + 1], palettedump[(i * 4) + 2], palettedump[(i * 4) + 3]);

View File

@ -27,12 +27,11 @@
#define GROOVIE_DEBUG_H
#include "gui/debugger.h"
#include "engines/engine.h"
namespace Groovie {
class Script;
class GroovieEngine;
class Script;
class Debugger : public GUI::Debugger {
public:
@ -42,7 +41,6 @@ public:
private:
GroovieEngine *_vm;
Script *_script;
OSystem *_syst;
int getNumber(const char *arg);

View File

@ -23,12 +23,12 @@
*
*/
#include "common/savefile.h"
#include "groovie/groovie.h"
#include "groovie/detection.h"
#include "groovie/saveload.h"
#include "common/system.h"
namespace Groovie {
static const PlainGameDescriptor groovieGames[] = {

View File

@ -23,106 +23,105 @@
*
*/
#include "common/file.h"
#include "graphics/surface.h"
#include "groovie/font.h"
namespace Groovie {
Font::Font(OSystem *syst) :
_syst(syst), _sphinxfnt(NULL) {
T7GFont::T7GFont() : _maxHeight(0), _maxWidth(0), _glyphs(0) {
}
Common::File fontfile;
if (!fontfile.open("sphinx.fnt")) {
error("Groovie::Font: Couldn't open sphinx.fnt");
T7GFont::~T7GFont() {
delete[] _glyphs;
}
bool T7GFont::load(Common::SeekableReadStream &stream) {
// Read the mapping of characters to glyphs
if (stream.read(_mapChar2Glyph, 128) < 128) {
error("Groovie::T7GFont: Couldn't read the character to glyph map");
return false;
}
uint16 fontfilesize = fontfile.size();
_sphinxfnt = fontfile.readStream(fontfilesize);
fontfile.close();
}
Font::~Font() {
delete _sphinxfnt;
}
// Calculate the number of glyphs
byte numGlyphs = 0;
for (int i = 0; i < 128; i++)
if (_mapChar2Glyph[i] >= numGlyphs)
numGlyphs = _mapChar2Glyph[i] + 1;
void Font::printstring(const char *messagein) {
uint16 totalwidth = 0, currxoffset, i;
// Read the glyph offsets
uint16 *glyphOffsets = new uint16[numGlyphs];
for (int i = 0; i < numGlyphs; i++)
glyphOffsets[i] = stream.readUint16LE();
char message[15];
memset(message, 0, 15);
if (stream.eos()) {
error("Groovie::T7GFont: Couldn't read the glyph offsets");
return false;
}
// Clear the top bar
Common::Rect topbar(640, 80);
Graphics::Surface *gamescreen;
gamescreen = _syst->lockScreen();
gamescreen->fillRect(topbar, 0);
_syst->unlockScreen();
// Allocate the glyph data
delete[] _glyphs;
_glyphs = new Glyph[numGlyphs];
for (i = 0; i < 14; i++) {
char chartocopy = messagein[i];
if (chartocopy <= 0x00 || chartocopy == 0x24) {
break;
// Read the glyphs
_maxHeight = _maxWidth = 0;
for (int i = 0; (i < numGlyphs) && !stream.eos(); i++) {
// Verify we're at the expected stream position
if (stream.pos() != glyphOffsets[i]) {
error("Groovie::T7GFont: Glyph %d starts at %d but the current "
"offset is %d", i, glyphOffsets[i], stream.pos());
return false;
}
message[i] = chartocopy;
// Read the glyph information
Glyph *g = &_glyphs[i];
g->width = stream.readByte();
g->julia = stream.readByte();
// Read the pixels data into a dynamic array (we don't know its length)
Common::Array<byte> data;
data.reserve(300);
byte b = stream.readByte();
while (!stream.eos() && (b != 0xFF)) {
data.push_back(b);
b = stream.readByte();
}
// Verify the pixel data size
assert (data.size() % g->width == 0);
g->height = data.size() / g->width;
// Copy the pixel data into the definitive static array
g->pixels = new byte[data.size()];
memcpy(g->pixels, data.begin(), data.size());
// Update the max values
if (g->width > _maxWidth)
_maxWidth = g->width;
if (g->height > _maxHeight)
_maxHeight = g->height;
}
Common::rtrim(message);
for (i = 0; i < strlen(message); i++) {
totalwidth += letterwidth(message[i]);
}
currxoffset = (640 - totalwidth) / 2;
char *currpos = message;
while (*(currpos) != 0) {
currxoffset += printletter(*(currpos++), currxoffset);
delete[] glyphOffsets;
return true;
}
void T7GFont::drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint32 color) const {
// We ignore the color, as the font is already colored
const Glyph *glyph = getGlyph(chr);
const byte *src = glyph->pixels;
byte *target = (byte *)dst->getBasePtr(x, y);
for (int i = 0; i < glyph->height; i++) {
memcpy(target, src, glyph->width);
src += glyph->width;
target += dst->pitch;
}
}
uint16 Font::letteroffset(char letter) {
uint16 offset;
offset = letter;
_sphinxfnt->seek(offset);
offset = _sphinxfnt->readByte() * 2 + 128;
_sphinxfnt->seek(offset);
offset = _sphinxfnt->readUint16LE();
return offset;
}
const T7GFont::Glyph *T7GFont::getGlyph(byte chr) const {
assert (chr < 128);
uint8 Font::letterwidth(char letter) {
uint16 offset = letteroffset(letter);
_sphinxfnt->seek(offset);
return _sphinxfnt->readByte();
}
uint8 Font::letterheight(char letter) {
uint16 offset, width, julia, data, counter = 0;
offset = letteroffset(letter);
_sphinxfnt->seek(offset);
width = _sphinxfnt->readByte();
julia = _sphinxfnt->readByte();
data = _sphinxfnt->readByte();
while (data != 0xFF) {
data = _sphinxfnt->readByte();
counter++;
}
if (counter % width != 0) assert("font file corrupt");
return counter / width;
}
uint8 Font::printletter(char letter, uint16 xoffset) {
uint16 offset, width, height, julia;
offset = letteroffset(letter);
height = letterheight(letter);
_sphinxfnt->seek(offset);
width = _sphinxfnt->readByte();
julia = _sphinxfnt->readByte();
byte *data = new byte[width * height];
_sphinxfnt->read(data, width * height);
_syst->copyRectToScreen(data, width, xoffset, 16, width, height);
delete[] data;
return width;
byte numGlyph = _mapChar2Glyph[chr];
return &_glyphs[numGlyph];
}
} // End of Groovie namespace

View File

@ -27,24 +27,38 @@
#define GROOVIE_FONT_H
#include "common/stream.h"
#include "common/system.h"
#include "graphics/font.h"
namespace Groovie {
class Font {
class T7GFont : public Graphics::Font {
public:
Font(OSystem *syst);
~Font();
void printstring(const char *messagein);
T7GFont();
~T7GFont();
bool load(Common::SeekableReadStream &stream);
int getFontHeight() const { return _maxHeight; }
int getMaxCharWidth() const { return _maxWidth; }
int getCharWidth(byte chr) const { return getGlyph(chr)->width; }
void drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint32 color) const;
private:
OSystem *_syst;
Common::MemoryReadStream *_sphinxfnt;
int _maxHeight, _maxWidth;
uint16 letteroffset(char letter);
uint8 letterwidth(char letter);
uint8 letterheight(char letter);
uint8 printletter(char letter, uint16 xoffset);
struct Glyph {
Glyph() : pixels(0) {}
~Glyph() { delete[] pixels; }
byte width;
byte height;
byte julia;
byte *pixels;
};
byte _mapChar2Glyph[128];
Glyph *_glyphs;
const Glyph *getGlyph(byte chr) const;
};
} // End of Groovie namespace

View File

@ -23,8 +23,9 @@
*
*/
#include "groovie/groovie.h"
#include "groovie/graphics.h"
#include "groovie/groovie.h"
#include "common/system.h"
namespace Groovie {

View File

@ -26,6 +26,8 @@
#ifndef GROOVIE_GRAPHICS_H
#define GROOVIE_GRAPHICS_H
#include "graphics/surface.h"
namespace Groovie {
class GroovieEngine;

View File

@ -23,27 +23,30 @@
*
*/
#include "groovie/groovie.h"
#include "groovie/cursor.h"
#include "groovie/detection.h"
#include "groovie/graphics.h"
#include "groovie/music.h"
#include "groovie/resource.h"
#include "groovie/roq.h"
#include "groovie/vdx.h"
#include "common/config-manager.h"
#include "common/debug-channels.h"
#include "common/events.h"
#include "common/macresman.h"
#include "engines/util.h"
#include "graphics/fontman.h"
#include "sound/mixer.h"
#include "groovie/groovie.h"
#include "groovie/detection.h"
#include "groovie/music.h"
#include "groovie/roq.h"
#include "groovie/vdx.h"
namespace Groovie {
GroovieEngine::GroovieEngine(OSystem *syst, const GroovieGameDescription *gd) :
Engine(syst), _gameDescription(gd), _debugger(NULL), _script(NULL),
_resMan(NULL), _grvCursorMan(NULL), _videoPlayer(NULL), _musicPlayer(NULL),
_graphicsMan(NULL), _macResFork(NULL), _waitingForInput(false) {
_graphicsMan(NULL), _macResFork(NULL), _waitingForInput(false), _font(NULL) {
// Adding the default directories
const Common::FSNode gameDataDir(ConfMan.get("path"));
@ -104,12 +107,26 @@ Common::Error GroovieEngine::run() {
_graphicsMan = new GraphicsMan(this);
// Create the resource and cursor managers and the video player
// Prepare the font too
switch (_gameDescription->version) {
case kGroovieT7G:
if (_gameDescription->desc.platform == Common::kPlatformMacintosh) {
_macResFork = new Common::MacResManager();
if (!_macResFork->open(_gameDescription->desc.filesDescriptions[0].fileName))
error("Could not open %s as a resource fork", _gameDescription->desc.filesDescriptions[0].fileName);
// The Macintosh release used system fonts. We use GUI fonts.
_font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
} else {
Common::File fontfile;
if (!fontfile.open("sphinx.fnt")) {
error("Couldn't open sphinx.fnt");
return Common::kNoGameDataFoundError;
} else if (!_sphinxFont.load(fontfile)) {
error("Error loading sphinx.fnt");
return Common::kUnknownError;
}
fontfile.close();
_font = &_sphinxFont;
}
_resMan = new ResMan_t7g(_macResFork);

View File

@ -26,15 +26,11 @@
#ifndef GROOVIE_H
#define GROOVIE_H
#include "engines/engine.h"
#include "graphics/surface.h"
#include "groovie/cursor.h"
#include "groovie/debug.h"
#include "groovie/graphics.h"
#include "groovie/player.h"
#include "groovie/resource.h"
#include "groovie/script.h"
#include "groovie/font.h"
#include "engines/engine.h"
#include "graphics/pixelformat.h"
namespace Common {
class MacResManager;
@ -57,7 +53,12 @@ namespace Common {
*/
namespace Groovie {
class GraphicsMan;
class GrvCursorMan;
class MusicPlayer;
class ResMan;
class Script;
class VideoPlayer;
enum DebugLevels {
kGroovieDebugAll = 1 << 0,
@ -106,6 +107,7 @@ public:
VideoPlayer *_videoPlayer;
MusicPlayer *_musicPlayer;
GraphicsMan *_graphicsMan;
const Graphics::Font *_font;
Common::MacResManager *_macResFork;
@ -113,6 +115,7 @@ private:
const GroovieGameDescription *_gameDescription;
Debugger *_debugger;
bool _waitingForInput;
T7GFont _sphinxFont;
};
} // End of namespace Groovie

View File

@ -23,13 +23,14 @@
*
*/
#include "groovie/lzss.h"
#include "groovie/music.h"
#include "groovie/groovie.h"
#include "groovie/resource.h"
#include "common/config-manager.h"
#include "common/macresman.h"
#include "sound/audiocd.h"
#include "sound/midiparser.h"
namespace Groovie {

View File

@ -26,14 +26,16 @@
#ifndef GROOVIE_MUSIC_H
#define GROOVIE_MUSIC_H
#include "groovie/groovie.h"
#include "sound/mididrv.h"
#include "sound/midiparser.h"
#include "common/array.h"
#include "common/mutex.h"
#include "sound/mididrv.h"
class MidiParser;
namespace Groovie {
class GroovieEngine;
class MusicPlayer {
public:
MusicPlayer(GroovieEngine *vm);

View File

@ -23,8 +23,8 @@
*
*/
#include "groovie/groovie.h"
#include "groovie/player.h"
#include "groovie/groovie.h"
namespace Groovie {

View File

@ -26,8 +26,8 @@
#include "common/archive.h"
#include "common/macresman.h"
#include "groovie/groovie.h"
#include "groovie/resource.h"
#include "groovie/groovie.h"
namespace Groovie {

View File

@ -26,8 +26,9 @@
// ROQ video player based on this specification by Dr. Tim Ferguson:
// http://www.csse.monash.edu.au/~timf/videocodec/idroq.txt
#include "groovie/groovie.h"
#include "groovie/roq.h"
#include "groovie/graphics.h"
#include "groovie/groovie.h"
#include "graphics/jpeg.h"

View File

@ -23,18 +23,19 @@
*
*/
#include "groovie/debug.h"
#include "groovie/music.h"
#include "groovie/script.h"
#include "groovie/groovie.h"
#include "groovie/cell.h"
#include "groovie/cursor.h"
#include "groovie/graphics.h"
#include "groovie/groovie.h"
#include "groovie/music.h"
#include "groovie/player.h"
#include "groovie/resource.h"
#include "groovie/saveload.h"
#include "common/archive.h"
#include "common/config-manager.h"
#include "common/debug-channels.h"
#include "common/endian.h"
#include "common/events.h"
#include "common/EventRecorder.h"
#include "common/macresman.h"
@ -63,9 +64,8 @@ static void debugScript(int level, bool nl, const char *s, ...) {
}
Script::Script(GroovieEngine *vm, EngineVersion version) :
_code(NULL), _savedCode(NULL), _stacktop(0),
_debugger(NULL), _vm(vm),
_videoFile(NULL), _videoRef(0), _font(NULL), _staufsMove(NULL) {
_code(NULL), _savedCode(NULL), _stacktop(0), _debugger(NULL), _vm(vm),
_videoFile(NULL), _videoRef(0), _staufsMove(NULL) {
// Initialize the opcode set depending on the engine version
switch (version) {
case kGroovieT7G:
@ -112,7 +112,6 @@ Script::~Script() {
delete[] _code;
delete[] _savedCode;
delete _font;
delete _videoFile;
}
@ -429,6 +428,22 @@ void Script::savegame(uint slot) {
_saveNames[slot] = save;
}
void Script::printString(Graphics::Surface *surface, const char *str) {
char message[15];
memset(message, 0, 15);
// Preprocess the string
for (int i = 0; i < 14; i++) {
if (str[i] <= 0x00 || str[i] == 0x24)
break;
message[i] = str[i];
}
Common::rtrim(message);
// Draw the string
_vm->_font->drawString(surface, message, 0, 16, 640, 0xE2, Graphics::kTextAlignCenter);
}
// OPCODES
void Script::o_invalid() {
@ -1249,11 +1264,16 @@ void Script::o_printstring() {
stringstorage[counter] = 0;
// Load the font if required
if (!_font) {
_font = new Font(_vm->_system);
}
_font->printstring(stringstorage);
Common::Rect topbar(640, 80);
Graphics::Surface *gamescreen = _vm->_system->lockScreen();
// Clear the top bar
gamescreen->fillRect(topbar, 0);
// Draw the string
printString(gamescreen, stringstorage);
_vm->_system->unlockScreen();
}
void Script::o_hotspot_slot() {
@ -1273,11 +1293,15 @@ void Script::o_hotspot_slot() {
return;
}
// Load the font if required
if (!_font) {
_font = new Font(_vm->_system);
}
_font->printstring(_saveNames[slot].c_str());
Common::Rect topbar(640, 80);
Graphics::Surface *gamescreen = _vm->_system->lockScreen();
// Clear the top bar
gamescreen->fillRect(topbar, 0);
printString(gamescreen, _saveNames[slot].c_str());
_vm->_system->unlockScreen();
// Save the currently highlighted slot
_hotspotSlot = slot;

View File

@ -26,12 +26,16 @@
#ifndef GROOVIE_SCRIPT_H
#define GROOVIE_SCRIPT_H
#include "common/file.h"
#include "common/random.h"
#include "common/rect.h"
#include "groovie/font.h"
#include "groovie/cell.h"
namespace Common {
class SeekableReadStream;
}
namespace Graphics {
class Surface;
}
namespace Groovie {
@ -40,8 +44,9 @@ enum EngineVersion {
kGroovieV2
};
class GroovieEngine;
class CellGame;
class Debugger;
class GroovieEngine;
class Script {
friend class Debugger;
@ -112,7 +117,6 @@ private:
uint16 _hotspotSlot;
// Video
Font *_font;
Common::SeekableReadStream *_videoFile;
uint32 _videoRef;
uint16 _bitflags;
@ -140,6 +144,7 @@ private:
void loadgame(uint slot);
void savegame(uint slot);
bool playvideofromref(uint32 fileref);
void printString(Graphics::Surface *surface, const char *str);
// Opcodes
typedef void (Script::*OpcodeFunc)();

View File

@ -23,9 +23,10 @@
*
*/
#include "groovie/vdx.h"
#include "groovie/graphics.h"
#include "groovie/groovie.h"
#include "groovie/lzss.h"
#include "groovie/vdx.h"
#include "common/debug-channels.h"
#include "sound/mixer.h"

View File

@ -28,6 +28,10 @@
#include "groovie/player.h"
namespace Common {
class ReadStream;
}
namespace Groovie {
class VDXPlayer : public VideoPlayer {

View File

@ -26,181 +26,463 @@
#include "m4/assets.h"
#include "m4/animation.h"
#include "m4/compression.h"
#include "m4/mads_scene.h"
namespace M4 {
// TODO: this code needs cleanup
Animation::Animation(MadsM4Engine *vm) {
_vm = vm;
_playing = false;
MadsAnimation::MadsAnimation(MadsM4Engine *vm, MadsView *view): Animation(vm), _view(view) {
_font = NULL;
_resetFlag = false;
_freeFlag = false;
_skipLoad = false;
_unkIndex = -1;
_messageCtr= 0;
_field12 = 0;
_currentFrame = 0;
_oldFrameEntry = 0;
_nextFrameTimer = _madsVm->_currentTimer;
}
void Animation::loadFullScreen(const char *filename) {
_vm->_palette->deleteAllRanges();
load(filename);
MadsAnimation::~MadsAnimation() {
for (uint i = 0; i < _messages.size(); ++i) {
if (_messages[i].kernelMsgIndex >= 0)
_view->_kernelMessages.remove(_messages[i].kernelMsgIndex);
}
// Further deletion logic
if (_field12) {
_view->_spriteSlots.deleteSprites(_spriteListIndexes[_spriteListIndex]);
}
delete _font;
}
void Animation::load(const char *filename) {
MadsPack anim(filename, _vm);
/**
* Initialises and loads the data of an animation
*/
void MadsAnimation::initialise(const Common::String &filename, uint16 flags, M4Surface *interfaceSurface, M4Surface *sceneSurface) {
MadsPack anim(filename.c_str(), _vm);
bool madsRes = filename[0] == '*';
char buffer[20];
int streamIndex = 1;
// Chunk 1: header
// header
// TODO: there are some unknown fields here, plus we don't read
// the entire chunk
Common::SeekableReadStream *animStream = anim.getItemStream(0);
Common::SeekableReadStream *spriteSeriesStream;
//printf("Chunk 0, size %i\n", animStream->size());
_seriesCount = animStream->readUint16LE();
_frameCount = animStream->readUint16LE();
_frameEntryCount = animStream->readUint16LE();
// Unknown
for (int i = 0; i < 43; i++)
animStream->readByte();
int spriteListCount = animStream->readUint16LE();
int miscEntriesCount = animStream->readUint16LE();
int frameEntryCount = animStream->readUint16LE();
int messagesCount = animStream->readUint16LE();
animStream->skip(1);
_flags = animStream->readByte();
_spriteSeriesNames = new Common::String[_seriesCount];
printf("%i sprite series\n", _seriesCount);
animStream->skip(2);
_animMode = animStream->readUint16LE();
_roomNumber = animStream->readUint16LE();
animStream->skip(2);
_field12 = animStream->readUint16LE() != 0;
_spriteListIndex = animStream->readUint16LE();
_scrollX = animStream->readUint16LE();
_scrollY = animStream->readSint16LE();
animStream->skip(10);
animStream->read(buffer, 13);
_interfaceFile = Common::String(buffer, 13);
// TODO: for now, we only load the first sprite series
if (_seriesCount > 1)
printf("TODO: Anim has %i sprite series, for now, we only load the first one\n", _seriesCount);
_seriesCount = 1; // TODO
for (int i = 0; i < _seriesCount; i++) {
for (int i = 0; i < 10; ++i) {
animStream->read(buffer, 13);
_spriteSeriesNames[i] = Common::String(buffer);
//printf("%03d: %s\n", i, _spriteSeriesNames[i].c_str());
_spriteSetNames[i] = Common::String(buffer, 13);
}
spriteSeriesStream = _vm->res()->get(_spriteSeriesNames[i].c_str());
_spriteSeries = new SpriteAsset(_vm, spriteSeriesStream,
spriteSeriesStream->size(), _spriteSeriesNames[i].c_str());
_vm->res()->toss(_spriteSeriesNames[i].c_str());
animStream->skip(81);
animStream->read(buffer, 13);
_lbmFilename = Common::String(buffer, 13);
animStream->read(buffer, 13);
_spritesFilename = Common::String(buffer, 13);
animStream->skip(48);
animStream->read(buffer, 13);
_soundName = Common::String(buffer, 13);
animStream->skip(26);
animStream->read(buffer, 13);
Common::String fontResource(buffer, 13);
// Adjust the palette of the sprites in the sprite series
// so that they can be displayed on screen correctly
RGBList *palData = new RGBList(_spriteSeries->getColorCount(), _spriteSeries->getPalette(), true);
_vm->_palette->addRange(palData);
if (_animMode == 4)
flags |= 0x4000;
if (flags & 0x100)
loadInterface(interfaceSurface, sceneSurface);
for (int k = 0; k < _spriteSeries->getCount(); k++) {
M4Sprite *spr = _spriteSeries->getFrame(k);
spr->translate(palData); // sprite pixel translation
// Initialise the reference list
for (int i = 0; i < spriteListCount; ++i)
_spriteListIndexes.push_back(-1);
delete animStream;
if (messagesCount > 0) {
// Chunk 2
// Following is a list of any messages for the animation
animStream = anim.getItemStream(streamIndex++);
for (int i = 0; i < messagesCount; ++i) {
AnimMessage rec;
animStream->read(rec.msg, 70);
rec.pos.x = animStream->readUint16LE();
rec.pos.y = animStream->readUint16LE();
animStream->readUint16LE();
rec.rgb1.r = animStream->readByte();
rec.rgb1.g = animStream->readByte();
rec.rgb1.b = animStream->readByte();
rec.rgb2.r = animStream->readByte();
rec.rgb2.g = animStream->readByte();
rec.rgb2.b = animStream->readByte();
rec.kernelMsgIndex = animStream->readUint16LE();
animStream->skip(6);
rec.startFrame = animStream->readUint16LE();
rec.endFrame = animStream->readUint16LE();
animStream->readUint16LE();
_messages.push_back(rec);
}
delete animStream;
}
if (frameEntryCount > 0) {
// Chunk 3: animation frame info
animStream = anim.getItemStream(streamIndex++);
for (int i = 0; i < frameEntryCount; i++) {
AnimFrameEntry rec;
rec.frameNumber = animStream->readUint16LE();
rec.seqIndex = animStream->readByte();
rec.spriteSlot.spriteListIndex = animStream->readByte();
rec.spriteSlot.frameNumber = animStream->readUint16LE();
rec.spriteSlot.xp = animStream->readUint16LE();
rec.spriteSlot.yp = animStream->readUint16LE();
rec.spriteSlot.depth = animStream->readByte();
rec.spriteSlot.scale = (int8)animStream->readByte();
_frameEntries.push_back(rec);
}
delete animStream;
}
if (miscEntriesCount > 0) {
// Chunk 4: Misc Data
animStream = anim.getItemStream(streamIndex);
for (int i = 0; i < miscEntriesCount; ++i) {
AnimMiscEntry rec;
rec.soundNum = animStream->readByte();
animStream->skip(1);
rec.numTicks = animStream->readUint16LE();
rec.posAdjust.x = animStream->readUint16LE();
rec.posAdjust.y = animStream->readUint16LE();
animStream->readUint16LE();
_miscEntries.push_back(rec);
}
delete animStream;
}
// If the animation specifies a font, then load it for access
if (_flags & ANIM_CUSTOM_FONT) {
Common::String fontName;
if (madsRes)
fontName += "*";
fontName += fontResource;
_font = _vm->_font->getFont(fontName);
}
// Load all the sprite sets for the animation
for (int i = 0; i < spriteListCount; ++i) {
if (_field12 && (i == _spriteListIndex))
// Skip over field, since it's manually loaded
continue;
_spriteListIndexes[i] = _view->_spriteSlots.addSprites(_spriteSetNames[i].c_str());
}
if (_field12) {
Common::String resName;
if (madsRes)
resName += "*";
resName += _spriteSetNames[_spriteListIndex];
_spriteListIndexes[_spriteListIndex] = _view->_spriteSlots.addSprites(resName.c_str());
}
// TODO: Unknown section about handling sprite set list combined with messages size
// TODO: The original has two separate loops for the loop below based on _animMode == 4. Is it
// perhaps that in that mode the sprite frames has a different format..?
// Remap the sprite list index fields from the initial value to the indexes of the loaded
// sprite sets for the animation
for (uint i = 0; i < _frameEntries.size(); ++i) {
int idx = _frameEntries[i].spriteSlot.spriteListIndex;
_frameEntries[i].spriteSlot.spriteListIndex = _spriteListIndexes[idx];
}
}
/**
* Loads an animation file for display
*/
void MadsAnimation::load(const Common::String &filename, int abortTimers) {
initialise(filename, 0, NULL, NULL);
_messageCtr = 0;
_skipLoad = true;
/* TODO: figure out extra stuff in this routine
if (_field12) {
_unkIndex = -1;
int listIndex = _spriteListIndexes[_spriteListIndex];
SpriteAsset &spriteSet = _view->_spriteSlots.getSprite(listIndex);
..?..
}
*/
// Initialise miscellaneous fields
_currentFrame = 0;
_oldFrameEntry = 0;
_nextFrameTimer = _madsVm->_currentTimer;
_abortTimers = abortTimers;
_abortMode = _madsVm->scene()->_abortTimersMode2;
for (int i = 0; i < 3; ++i)
_actionNouns[i] = _madsVm->scene()->actionNouns[i];
// Initialise kernel message list
for (uint i = 0; i < _messages.size(); ++i)
_messages[i].kernelMsgIndex = -1;
}
void MadsAnimation::update() {
if (_field12) {
int spriteListIndex = _spriteListIndexes[_spriteListIndex];
int newIndex = -1;
for (uint idx = _oldFrameEntry; idx < _frameEntries.size(); ++idx) {
if (_frameEntries[idx].frameNumber > _currentFrame)
break;
if (_frameEntries[idx].spriteSlot.spriteListIndex == spriteListIndex)
newIndex = _frameEntries[idx].spriteSlot.frameNumber;
}
if (newIndex >= 0)
load1(newIndex);
}
// If it's not time for the next frame, then exit
if (_madsVm->_currentTimer < _nextFrameTimer)
return;
// Loop checks for any prior animation sprite slots to be expired
for (int slotIndex = 0; slotIndex < _view->_spriteSlots.startIndex; ++slotIndex) {
if (_view->_spriteSlots[slotIndex].seqIndex >= 0x80) {
// Flag the frame as animation sprite slot
_view->_spriteSlots[slotIndex].spriteType = EXPIRED_SPRITE;
}
}
//printf("End pos: %i\n", animStream->pos());
delete animStream;
// ------------------
// Chunk 2: anim info
AnimationFrame frame;
animStream = anim.getItemStream(1);
//printf("Chunk 1, size %i\n", animStream->size());
_frameEntries = new AnimationFrame[_frameEntryCount];
for (int i = 0; i < _frameEntryCount; i++) {
frame.animFrameIndex = animStream->readUint16LE();
frame.u = animStream->readByte();
frame.seriesIndex = animStream->readByte();
frame.seriesFrameIndex = animStream->readUint16LE();
frame.x = animStream->readUint16LE();
frame.y = animStream->readUint16LE();
frame.v = animStream->readByte();
frame.w = animStream->readByte();
_frameEntries[i] = frame;
/*
printf(
"animFrameIndex = %4d, "
"u = %3d, "
"seriesIndex = %3d, "
"seriesFrameIndex = %6d, "
"x = %3d, "
"y = %3d, "
"v = %3d, "
"w = %3d\n",
frame.animFrameIndex,
frame.u,
frame.seriesIndex,
frame.seriesFrameIndex,
frame.x,
frame.y,
frame.v,
frame.w
);
*/
}
//printf("End pos: %i\n", animStream->pos());
delete animStream;
// Chunk 3: unknown (seems to be sound data?)
// TODO
}
Animation::~Animation() {
//delete[] _spriteSeriesNames;
//delete[] _spriteSeries;
//delete[] _frameEntries;
}
void Animation::start() {
_curFrame = 0;
_curFrameEntry = 0;
//for (int i = 0; i < _seriesCount; i++) {
//_spriteSeries[i] = new SpriteSeries((char*)_spriteSeriesNames[i].c_str());
//}
_playing = true;
updateAnim();
}
bool Animation::updateAnim() {
if (!_playing)
return true;
// Get the scene background surface
M4Surface *bg = _vm->_scene->getBackgroundSurface();
while (_frameEntries[_curFrameEntry].animFrameIndex == _curFrame) {
AnimationFrame *frame = &_frameEntries[_curFrameEntry];
int seriesFrameIndex = (frame->seriesFrameIndex & 0x7FFF) - 1;
// Write the sprite onto the screen
M4Sprite *spr = _spriteSeries->getFrame(seriesFrameIndex);
// FIXME: correct x, y
spr->copyTo(bg, frame->x, frame->y, (int)spr->getTransparentColor());
// HACK: wait a bit
g_system->delayMillis(100);
//printf("_curFrameEntry = %d\n", _curFrameEntry);
_curFrameEntry++;
// Validate the current frame
if (_currentFrame >= (int)_miscEntries.size()) {
// Is the animation allowed to be repeated?
if (_resetFlag) {
_currentFrame = 0;
_oldFrameEntry = 0;
} else {
_freeFlag = true;
return;
}
}
//printf("_curFrame = %d\n", _curFrame);
// Handle starting any sound for this frame
AnimMiscEntry &misc = _miscEntries[_currentFrame];
if (misc.soundNum)
_vm->_sound->playSound(misc.soundNum);
_curFrame++;
if (_curFrame >= _frameCount) // anim done
stop();
bool screenChanged = false;
return _curFrame >= _frameCount;
// Handle any scrolling of the screen surface
if ((_scrollX != 0) || (_scrollY != 0)) {
_view->_bgSurface->scrollX(_scrollX);
_view->_bgSurface->scrollY(_scrollY);
screenChanged = true;
}
// Handle any offset adjustment for sprites as of this frame
if (_view->_posAdjust.x != misc.posAdjust.x) {
misc.posAdjust.x = _view->_posAdjust.x;
screenChanged = true;
}
if (_view->_posAdjust.y != misc.posAdjust.y) {
misc.posAdjust.y = _view->_posAdjust.y;
screenChanged = true;
}
if (screenChanged) {
// Signal the entire screen needs refreshing
_view->_spriteSlots.fullRefresh();
}
int spriteSlotsMax = _view->_spriteSlots.startIndex;
// Main frame animation loop - frames get animated by being placed, as necessary, into the
// main sprite slot array
while ((uint)_oldFrameEntry < _frameEntries.size()) {
if (_frameEntries[_oldFrameEntry].frameNumber > _currentFrame)
break;
else if (_frameEntries[_oldFrameEntry].frameNumber == _currentFrame) {
// Found the correct frame
int spriteSlotIndex = 0;
int index = 0;
for (;;) {
if ((spriteSlotIndex == 0) && (index < spriteSlotsMax)) {
int seqIndex = _frameEntries[_oldFrameEntry].seqIndex - _view->_spriteSlots[index].seqIndex;
if (seqIndex == 0x80) {
if (_view->_spriteSlots[index] == _frameEntries[_oldFrameEntry].spriteSlot) {
_view->_spriteSlots[index].spriteType = SPRITE_ZERO;
spriteSlotIndex = -1;
}
}
++index;
continue;
}
if (spriteSlotIndex == 0) {
int slotIndex = _view->_spriteSlots.getIndex();
MadsSpriteSlot &slot = _view->_spriteSlots[slotIndex];
slot.copy(_frameEntries[_oldFrameEntry].spriteSlot);
slot.seqIndex = _frameEntries[_oldFrameEntry].seqIndex + 0x80;
SpriteAsset &spriteSet = _view->_spriteSlots.getSprite(
_view->_spriteSlots[slotIndex].spriteListIndex);
slot.spriteType = spriteSet.isBackground() ? BACKGROUND_SPRITE : FOREGROUND_SPRITE;
}
break;
}
}
++_oldFrameEntry;
}
// Handle the display of any messages
for (uint idx = 0; idx < _messages.size(); ++idx) {
if (_messages[idx].kernelMsgIndex >= 0) {
// Handle currently active message
if ((_currentFrame < _messages[idx].startFrame) || (_currentFrame > _messages[idx].endFrame)) {
_view->_kernelMessages.remove(_messages[idx].kernelMsgIndex);
_messages[idx].kernelMsgIndex = -1;
--_messageCtr;
}
} else if ((_currentFrame >= _messages[idx].startFrame) && (_currentFrame <= _messages[idx].endFrame)) {
// Start displaying the message
AnimMessage &me = _messages[idx];
// The colour index to use is dependant on how many messages are currently on-screen
uint8 colIndex;
switch (_messageCtr) {
case 1:
colIndex = 252;
break;
case 2:
colIndex = 16;
break;
default:
colIndex = 250;
break;
}
_vm->_palette->setEntry(colIndex, me.rgb1.r, me.rgb1.g, me.rgb1.b);
_vm->_palette->setEntry(colIndex + 1, me.rgb2.r, me.rgb2.g, me.rgb2.b);
// Add a kernel message to display the given text
me.kernelMsgIndex = _view->_kernelMessages.add(me.pos, colIndex * 101, 0, 0, INDEFINITE_TIMEOUT, me.msg);
++_messageCtr;
}
}
// Move to the next frame
_currentFrame++;
if (_currentFrame >= (int)_miscEntries.size()) {
// Animation is complete
if (_abortTimers != 0) {
_view->_abortTimers = _abortTimers;
_view->_abortTimersMode = _abortMode;
if (_abortMode != ABORTMODE_1) {
// Copy the noun list
for (int i = 0; i < 3; ++i)
_madsVm->scene()->actionNouns[i] = _actionNouns[i];
}
}
}
int frameNum = MIN(_currentFrame, (int)_miscEntries.size() - 1);
_nextFrameTimer = _madsVm->_currentTimer + _miscEntries[frameNum].numTicks;
}
void Animation::stop() {
_playing = false;
void MadsAnimation::setCurrentFrame(int frameNumber) {
_currentFrame = frameNumber;
_oldFrameEntry = 0;
_freeFlag = false;
}
for (int i = 0; i < _seriesCount; i++) {
// TODO: cleanup
//delete _spriteSeries[i];
//_spriteSeries[i] = NULL;
void MadsAnimation::load1(int frameNumber) {
if (_skipLoad)
return;
Common::Point pt;
int listIndex = _spriteListIndexes[_spriteListIndex];
SpriteAsset &spriteSet = _view->_spriteSlots.getSprite(listIndex);
if (_unkIndex < 0) {
M4Surface *frame = spriteSet.getFrame(0);
pt.x = frame->bounds().left;
pt.y = frame->bounds().top;
} else {
pt.x = _unkList[_unkIndex].x;
pt.y = _unkList[_unkIndex].y;
_unkIndex = 1 - _unkIndex;
}
if (proc1(spriteSet, pt, frameNumber))
error("proc1 failure");
}
bool MadsAnimation::proc1(SpriteAsset &spriteSet, const Common::Point &pt, int frameNumber) {
return 0;
}
void MadsAnimation::loadInterface(M4Surface *&interfaceSurface, M4Surface *&depthSurface) {
if (_animMode <= 2) {
MadsSceneResources sceneResources;
sceneResources.load(_roomNumber, _interfaceFile.c_str(), 0, depthSurface, interfaceSurface);
} else if (_animMode == 4) {
// Load a scene interface
interfaceSurface->madsLoadInterface(_interfaceFile);
} else {
// This mode allocates two large surfaces for the animation
// TODO: Are these ever properly freed?
error("Anim mode %d - need to check free logic", _animMode);
assert(!interfaceSurface);
assert(!depthSurface);
depthSurface = new M4Surface(MADS_SURFACE_WIDTH, MADS_SCREEN_HEIGHT);
interfaceSurface = new M4Surface(MADS_SURFACE_WIDTH, MADS_SCREEN_HEIGHT);
depthSurface->clear();
interfaceSurface->clear();
}
}

View File

@ -29,39 +29,92 @@
#include "m4/m4.h"
#include "m4/graphics.h"
#include "m4/assets.h"
#include "m4/mads_views.h"
#include "common/array.h"
namespace M4 {
struct AnimationFrame {
uint16 animFrameIndex;
byte u;
byte seriesIndex;
uint16 seriesFrameIndex;
uint16 x, y;
byte v, w;
class MadsView;
class SpriteSlotSubset;
class AnimMessage {
public:
char msg[70];
Common::Point pos;
RGB8 rgb1, rgb2;
int kernelMsgIndex;
int startFrame, endFrame;
};
class Animation {
public:
Animation(MadsM4Engine *vm);
~Animation();
class AnimFrameEntry {
public:
int frameNumber;
int seqIndex;
SpriteSlotSubset spriteSlot;
};
void load(const char *filename);
void loadFullScreen(const char *filename);
void start();
bool updateAnim();
void stop();
class AnimMiscEntry {
public:
int soundNum;
int numTicks;
Common::Point posAdjust;
};
private:
bool _playing;
MadsM4Engine *_vm;
int _seriesCount;
int _frameCount;
int _frameEntryCount;
AnimationFrame *_frameEntries;
Common::String *_spriteSeriesNames;
SpriteAsset *_spriteSeries;
int _curFrame, _curFrameEntry;
#define ANIM_SPRITE_SET_SIZE 50
enum MadsAnimationFlags {ANIM_CUSTOM_FONT = 0x20, ANIM_HAS_SOUND = 0x8000};
class MadsAnimation: public Animation {
private:
MadsView *_view;
int _spriteListCount;
Common::Array<AnimMessage> _messages;
Common::Array<AnimFrameEntry> _frameEntries;
Common::Array<AnimMiscEntry> _miscEntries;
Font *_font;
uint8 _flags;
int _animMode;
int _roomNumber;
bool _field12;
int _spriteListIndex;
int _scrollX;
int _scrollY;
Common::String _interfaceFile;
Common::String _spriteSetNames[10];
Common::String _lbmFilename;
Common::String _spritesFilename;
Common::String _soundName;
Common::Array<int> _spriteListIndexes;
int _currentFrame, _oldFrameEntry;
bool _resetFlag;
bool _freeFlag;
bool _skipLoad;
int _unkIndex;
Common::Point _unkList[2];
uint32 _nextFrameTimer;
int _messageCtr;
int _abortTimers;
AbortTimerMode _abortMode;
uint16 _actionNouns[3];
void load1(int frameNumber);
bool proc1(SpriteAsset &spriteSet, const Common::Point &pt, int frameNumber);
void loadInterface(M4Surface *&interfaceSurface, M4Surface *&depthSurface);
public:
MadsAnimation(MadsM4Engine *vm, MadsView *view);
virtual ~MadsAnimation();
virtual void initialise(const Common::String &filename, uint16 flags, M4Surface *interfaceSurface, M4Surface *sceneSurface);
virtual void load(const Common::String &filename, int abortTimers);
virtual void update();
virtual void setCurrentFrame(int frameNumber);
bool freeFlag() const { return _freeFlag; }
int roomNumber() const { return _roomNumber; }
};
} // End of namespace M4

View File

@ -30,13 +30,13 @@
namespace M4 {
BaseAsset::BaseAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : _vm(vm) {
BaseAsset::BaseAsset(MadsM4Engine *vm) : _vm(vm) {
}
BaseAsset::~BaseAsset() {
}
MachineAsset::MachineAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) {
MachineAsset::MachineAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) {
uint32 stateCount = stream->readUint32LE();
for (uint32 curState = 0; curState < stateCount; curState++) {
uint32 stateOffset = stream->readUint32LE();
@ -61,7 +61,7 @@ uint32 MachineAsset::getStateOffset(uint32 state) {
return _stateTable[state];
}
SequenceAsset::SequenceAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) {
SequenceAsset::SequenceAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) {
_localVarCount = stream->readUint32LE();
_codeSize = size - 4;
_code = new byte[_codeSize];
@ -78,7 +78,7 @@ void SequenceAsset::getCode(byte *&code, uint32 &codeSize) {
}
DataAsset::DataAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) {
DataAsset::DataAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) {
_recCount = stream->readUint32LE();
_recSize = stream->readUint32LE();
@ -98,7 +98,8 @@ long *DataAsset::getRow(int index) {
return &_data[_recSize * index];
}
SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream) : BaseAsset(vm, stream, size, name) {
SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream) :
BaseAsset(vm) {
_stream = stream;
_palInterface = NULL;
_paletteData = NULL;
@ -110,6 +111,20 @@ SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, i
}
}
SpriteAsset::SpriteAsset(MadsM4Engine *vm, const char *name): BaseAsset(vm) {
_stream = vm->res()->get(name);
_palInterface = NULL;
_paletteData = NULL;
if (_vm->isM4()) {
loadM4SpriteAsset(vm, _stream, true);
} else {
loadMadsSpriteAsset(vm, _stream);
}
vm->res()->toss(name);
}
SpriteAsset::~SpriteAsset() {
if (_palInterface) {
// Internally stored palette translation data, so release it
@ -195,11 +210,12 @@ void SpriteAsset::loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStre
_maxHeight = 0;
Common::SeekableReadStream *spriteStream = sprite.getItemStream(0);
_assetType = spriteStream->readUint16LE();
for (int i = 0; i < 18; i++) {
spriteStream->readUint16LE();
}
_mode = spriteStream->readByte();
spriteStream->skip(1);
int type1 = spriteStream->readUint16LE();
int type2 = spriteStream->readUint16LE();
_isBackground = (type1 != 0) && (type2 < 4);
spriteStream->skip(32);
_frameCount = spriteStream->readUint16LE();
// we skip the rest of the data
delete spriteStream;

View File

@ -49,7 +49,7 @@ class Palette;
class BaseAsset {
public:
BaseAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name);
BaseAsset(MadsM4Engine *vm);
~BaseAsset();
const Common::String getName() const { return _name; }
protected:
@ -103,6 +103,7 @@ struct SpriteAssetFrame {
class SpriteAsset : public BaseAsset {
public:
SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream = false);
SpriteAsset(MadsM4Engine *vm, const char *name);
~SpriteAsset();
void loadM4SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, bool asStream);
void loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream);
@ -113,7 +114,7 @@ public:
int32 getFrameHeight(int index);
int32 getMaxFrameWidth() const { return _maxWidth; }
int32 getMaxFrameHeight() const { return _maxHeight; }
uint16 getAssetType() const { return _assetType; }
bool isBackground() const { return _isBackground; }
M4Sprite *getFrame(int frameIndex);
void loadStreamingFrame(M4Sprite *frame, int frameIndex, int destX, int destY);
RGB8* getPalette() { return _palette; }
@ -136,7 +137,8 @@ protected:
uint32 _frameStartOffset;
// MADS sprite set fields
uint16 _assetType;
uint8 _mode;
bool _isBackground;
int32 parseSprite(bool isBigEndian = false);
void loadFrameHeader(SpriteAssetFrame &frameHeader, bool isBigEndian = false);

View File

@ -66,8 +66,8 @@ public:
class FabDecompressor {
private:
int _bitsLeft;
uint32 _bitBuffer;
int _bitsLeft;
uint32 _bitBuffer;
const byte *_srcData, *_srcP;
int _srcSize;

View File

@ -47,7 +47,6 @@ Console::Console(MadsM4Engine *vm) : GUI::Debugger() {
DCmd_Register("start_conv", WRAP_METHOD(Console, cmdStartConversation));
DCmd_Register("textview", WRAP_METHOD(Console, cmdShowTextview));
DCmd_Register("animview", WRAP_METHOD(Console, cmdShowAnimview));
DCmd_Register("anim", WRAP_METHOD(Console, cmdPlayAnimation));
}
Console::~Console() {
@ -247,33 +246,6 @@ bool Console::cmdShowAnimview(int argc, const char **argv) {
return false;
}
bool Console::cmdPlayAnimation(int argc, const char **argv) {
View *view = _vm->_viewManager->getView(VIEWID_SCENE);
if (view == NULL) {
DebugPrintf("The scene view isn't currently active\n");
} else if (argc != 2 && argc != 3) {
DebugPrintf("Usage: %s <anim resource (*.aa)> <fullscreen>\n", argv[0]);
DebugPrintf("If fullscreen is 1, the screen palette is replaced with the palette of the animation\n");
} else {
char resourceName[20];
strncpy(resourceName, argv[1], 15);
resourceName[15] = '\0';
if (!strchr(resourceName, '.'))
strcat(resourceName, ".AA");
_vm->_viewManager->moveToFront(view);
if (argc == 3 && atoi(argv[2]) == 1)
_vm->_animation->loadFullScreen(resourceName);
else
_vm->_animation->load(resourceName);
_vm->_animation->start();
view->restore(0, 0, view->width(), view->height());
return false;
}
return true;
}
/*--------------------------------------------------------------------------*/
MadsConsole::MadsConsole(MadsEngine *vm): Console(vm) {
@ -282,6 +254,7 @@ MadsConsole::MadsConsole(MadsEngine *vm): Console(vm) {
DCmd_Register("object", WRAP_METHOD(MadsConsole, cmdObject));
DCmd_Register("message", WRAP_METHOD(MadsConsole, cmdMessage));
DCmd_Register("scene_info", WRAP_METHOD(MadsConsole, cmdSceneInfo));
DCmd_Register("anim", WRAP_METHOD(MadsConsole, cmdPlayAnimation));
}
bool MadsConsole::cmdObject(int argc, const char **argv) {
@ -386,6 +359,33 @@ bool MadsConsole::cmdSceneInfo(int argc, const char **argv) {
return true;
}
bool MadsConsole::cmdPlayAnimation(int argc, const char **argv) {
View *view = _vm->_viewManager->getView(VIEWID_SCENE);
if (view == NULL) {
DebugPrintf("The scene view isn't currently active\n");
} else if (argc != 2 && argc != 3) {
DebugPrintf("Usage: %s <anim resource (*.aa)> <fullscreen>\n", argv[0]);
DebugPrintf("If fullscreen is 1, the screen palette is replaced with the palette of the animation\n");
} else {
char resourceName[20];
strncpy(resourceName, argv[1], 15);
resourceName[15] = '\0';
if (!strchr(resourceName, '.'))
strcat(resourceName, ".AA");
_vm->_viewManager->moveToFront(view);
if (argc == 3 && atoi(argv[2]) == 1)
_madsVm->_palette->deleteAllRanges();
_madsVm->scene()->_sceneAnimation->load(resourceName, 0);
view->restore(0, 0, view->width(), view->height());
return false;
}
return true;
}
/*--------------------------------------------------------------------------*/
M4Console::M4Console(M4Engine *vm): Console(vm) {

View File

@ -50,7 +50,6 @@ private:
bool cmdStartConversation(int argc, const char **argv);
bool cmdShowTextview(int argc, const char **argv);
bool cmdShowAnimview(int argc, const char **argv);
bool cmdPlayAnimation(int argc, const char **argv);
public:
Console(MadsM4Engine *vm);
@ -64,6 +63,8 @@ private:
bool cmdObject(int argc, const char **argv);
bool cmdMessage(int argc, const char **argv);
bool cmdSceneInfo(int argc, const char **argv);
bool cmdPlayAnimation(int argc, const char **argv);
public:
MadsConsole(MadsEngine *vm);
virtual ~MadsConsole() {}

Some files were not shown because too many files have changed in this diff Show More