mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-10 03:40:25 +00:00
Merged from trunk.
svn-id: r49499
This commit is contained in:
commit
7ea78b1036
3
AUTHORS
3
AUTHORS
@ -166,6 +166,9 @@ ScummVM Team
|
||||
|
||||
Backend Teams
|
||||
-------------
|
||||
Android:
|
||||
Angus Lees
|
||||
|
||||
Dreamcast:
|
||||
Marcus Comstedt
|
||||
|
||||
|
@ -48,6 +48,7 @@ Martin Kiewitz
|
||||
Pawel Kolodziejski
|
||||
Mutwin Kraus
|
||||
Andrew Kurushin
|
||||
Angus Lees
|
||||
Claudio Matsuoka
|
||||
Thomas Mayer
|
||||
Neil Millstone
|
||||
|
18
NEWS
18
NEWS
@ -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
21
README
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
|
84
backends/platform/android/README.build
Normal file
84
backends/platform/android/README.build
Normal 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.
|
1413
backends/platform/android/android.cpp
Normal file
1413
backends/platform/android/android.cpp
Normal file
File diff suppressed because it is too large
Load Diff
52
backends/platform/android/android.mk
Normal file
52
backends/platform/android/android.mk
Normal 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
|
414
backends/platform/android/asset-archive.cpp
Normal file
414
backends/platform/android/asset-archive.cpp
Normal 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
|
53
backends/platform/android/asset-archive.h
Normal file
53
backends/platform/android/asset-archive.h
Normal 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
|
85
backends/platform/android/module.mk
Normal file
85
backends/platform/android/module.mk
Normal 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) $< $@
|
@ -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();
|
||||
}
|
||||
}
|
330
backends/platform/android/org/inodes/gus/scummvm/Event.java
Normal file
330
backends/platform/android/org/inodes/gus/scummvm/Event.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
317
backends/platform/android/org/inodes/gus/scummvm/ScummVM.java
Normal file
317
backends/platform/android/org/inodes/gus/scummvm/ScummVM.java
Normal 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());
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
370
backends/platform/android/org/inodes/gus/scummvm/Unpacker.java
Normal file
370
backends/platform/android/org/inodes/gus/scummvm/Unpacker.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
135
backends/platform/android/scummvm-android-themeengine.patch
Normal file
135
backends/platform/android/scummvm-android-themeengine.patch
Normal 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. */
|
@ -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);
|
||||
|
6
backends/platform/ds/arm9/dist/readme_ds.txt
vendored
6
backends/platform/ds/arm9/dist/readme_ds.txt
vendored
@ -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
|
||||
|
@ -75,7 +75,7 @@ else
|
||||
ifdef DS_BUILD_K
|
||||
|
||||
else
|
||||
USE_MAD = 1
|
||||
# USE_MAD = 1
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
@ -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");
|
||||
|
@ -33,6 +33,18 @@
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
typedef struct sTransferSoundData {
|
||||
//---------------------------------------------------------------------------------
|
||||
const void *data;
|
||||
u32 len;
|
||||
u32 rate;
|
||||
u8 vol;
|
||||
u8 pan;
|
||||
u8 format;
|
||||
u8 PADDING;
|
||||
} TransferSoundData, * pTransferSoundData;
|
||||
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
echo Quick script to make building all the time less painful.
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -43,8 +43,6 @@
|
||||
SoftKeyboard* _keyboardView;
|
||||
CALayer* _screenLayer;
|
||||
|
||||
int _fullWidth;
|
||||
int _fullHeight;
|
||||
int _widthOffset;
|
||||
int _heightOffset;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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/
|
||||
|
487
backends/platform/psp/mp3.cpp
Normal file
487
backends/platform/psp/mp3.cpp
Normal 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
121
backends/platform/psp/mp3.h
Normal 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
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
43
configure
vendored
@ -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
169
dists/android/mkmanifest.pl
Normal 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;
|
7
dists/android/res/drawable/gradient.xml
Normal file
7
dists/android/res/drawable/gradient.xml
Normal 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>
|
10
dists/android/res/layout/main.xml
Normal file
10
dists/android/res/layout/main.xml
Normal 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"
|
||||
/>
|
19
dists/android/res/layout/splash.xml
Normal file
19
dists/android/res/layout/splash.xml
Normal 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>
|
22
dists/android/res/values/strings.xml
Normal file
22
dists/android/res/values/strings.xml
Normal 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>
|
@ -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
13
dists/os2/readme.os2
Normal 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
BIN
dists/os2/scummvm.ico
Normal file
Binary file not shown.
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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]);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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[] = {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -23,8 +23,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "groovie/groovie.h"
|
||||
#include "groovie/graphics.h"
|
||||
#include "groovie/groovie.h"
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Groovie {
|
||||
|
||||
|
@ -26,6 +26,8 @@
|
||||
#ifndef GROOVIE_GRAPHICS_H
|
||||
#define GROOVIE_GRAPHICS_H
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Groovie {
|
||||
|
||||
class GroovieEngine;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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);
|
||||
|
@ -23,8 +23,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "groovie/groovie.h"
|
||||
#include "groovie/player.h"
|
||||
#include "groovie/groovie.h"
|
||||
|
||||
namespace Groovie {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)();
|
||||
|
@ -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"
|
||||
|
@ -28,6 +28,10 @@
|
||||
|
||||
#include "groovie/player.h"
|
||||
|
||||
namespace Common {
|
||||
class ReadStream;
|
||||
}
|
||||
|
||||
namespace Groovie {
|
||||
|
||||
class VDXPlayer : public VideoPlayer {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -66,8 +66,8 @@ public:
|
||||
|
||||
class FabDecompressor {
|
||||
private:
|
||||
int _bitsLeft;
|
||||
uint32 _bitBuffer;
|
||||
int _bitsLeft;
|
||||
uint32 _bitBuffer;
|
||||
const byte *_srcData, *_srcP;
|
||||
int _srcSize;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user