Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,171 @@
ScummVM-PSP 2026.1.1git README
==============================================================================
Installation
============
- Copy the relevant game datafiles to your memory stick (location doesn't matter).
- Install ScummVM like any other homebrew.
- Run ScummVM and use the launcher to add games and run them.
- Press Start to return to the launcher and play another game.
Controls
========
Right trigger - Modifier key (see below for uses)
Left trigger - ESC (Usually skips cutscenes. Depends on game)
Analog - Mouse movement
Right trigger + Analog - Fine mouse movement
D-Pad - Arrow keys (useful mostly in SCI and AGI games)
D-Pad + Right Trigger - Diagonal arrow keys (it's hard to input diagonals on some PSPs)
Triangle - Enter (useful for some dialogs)
Cross - Left Mouse Button (usually the main button)
Circle - Right Mouse Button (secondary button in some games)
Square - '.' (skip dialogue in some games e.g. Scumm)
Right trigger + Square - Spacebar (useful in Gobli*ns and SCI games)
Right trigger + Start - F5 (Main Menu in some games)
Select - Show/Hide Virtual Keyboard. Hold down to move keyboard onscreen (with D-Pad).
Right trigger + Select - Show Image Viewer (see below)
Start - Global Menu. Allows you to 'Return To Launcher' to play another game
Virtual Keyboard Mode
=====================
Start - Enter key. Also exits virtual keyboard mode
Select - Exit the virtual keyboard mode
Right trigger - Input letters: lowercase/uppercase (press to toggle)
Left trigger - Input numbers/symbols (press to toggle)
D-Pad - Select square of characters (up, down, left or right)
Buttons/Triggers - Choose a specific character in the square. The four center characters are chosen
by the button in the corresponding position. The 2 top characters are chosen by the
triggers.
Analog - Moves in a direction (left/right/up/down) (Useful to keep moving
while typing in AGI games among other things)
Image Viewer
============
For your convenience, I've included a simple image viewer in the PSP port.
You can view anything you want while playing a game.
There are a few simple rules to follow:
- Images must be of PNG format. If you have images in another format, many
graphics utilities will convert them for you.
- Images must be named psp_image1.png, psp_image2.png etc. This is to make
sure there's no possible conflict between image files and game files.
- Images must be placed in the game directories. When using the image viewer,
only the images of the particular game being played will be available for viewing.
- Don't place any images in the ScummVM directory, or you won't be able to see
the images in the game directories.
- There's no guarantee that you'll be able to view your image. This is because
big images take a lot of memory (more than the size of the image on disk). If there
isn't enough memory left to show the image, ScummVM will tell you so. Try to make the
image smaller by either shrinking it or reducing the colors to 256 color palette mode.
Image Viewer Controls:
=====================
Left/Right - previous/next image (e.g. go from psp_image1.png to psp_image2.png)
Up/down - zoom in/out
Analog - move around the image
Triggers, Start: - exit image viewer
1st Person Game Mode (Can be ignored by most users)
====================
This is a special mode built for 1st person games like Lands of Lore. If you don't have these games you can
safely ignore this mode. To enter or leave this mode, use the combo:
Right Trigger + Left Trigger + Square
Some buttons have been switched around to make these games more playable:
Square - Is the modifier key instead of Right Trigger.
Left/Right Trigger - Strafe left/right
D-Pad Left/Right - Turn left/right
Square + D-Pad - F1/F2/F3/F4
Square + Select - Image Viewer
Square + Start - Esc (shows game menu)
Notes
=====
- Notice that you can switch between games! This is much faster than quitting
and having to start ScummVM all over again. Go to the global menu and choose 'Return To Launcher'.
(Some games may not have the Return To Launcher option available yet.)
- The PSP version of ScummVM uses the Media Engine to accelerate decoding of MP3 files. This means
that if you have the choice of compressing using Ogg files or MP3 files, choose MP3 -- the game
will generally run faster.
- Plugin files are NOT interchangeable between ScummVM versions! You must copy ALL the
plugin files that come with every version of ScummVM. They sit in the /plugin
subdirectory. If you get a crash upon startup, try deleting all the existing
plugin files in the plugin directory and copying the new ones over again.
- This README may be outdated, for more up-to-date instructions and notes see
the PSP Port Wiki: https://wiki.scummvm.org/index.php/PlayStation_Portable
Frequently Asked Questions
==========================
Q: Scummvm crashes upon starting up!
A: See the first note above.
Q: Pressing select doesn't make the virtual keyboard show up on screen!
A: You need to make sure that the kbd.zip file is in the same directory as the ScummVM executable.
Q: What do I need to run the games?
A: A PSP that can run homebrew and the necessary datafiles for the game you want to play.
Q: Can I run game X with this?
A: You can find the list of supported games on the compatibility page
at https://www.scummvm.org
Note that ScummVM is NOT an emulator. The supported games engines have been painstakingly rewritten.
It's not easy to add support for a game you want that currently isn't supported.
Q: My Monkey Island 1 doesn't have any music, what gives?
A: If your version of Monkey Island came on a CD then it has the music
as CD Audio tracks. You need to rip those to MP3/Ogg (MP3 is preferred), naming them track1.mp3 track2.mp3
etc. and copy them to the same directory as the game datafiles for music to work.
Q: Game X crashes, or feature Y doesn't work. Can you fix this?
A: Possibly.
Because of the large amount of games ScummVM supports we obviously haven't
played them all start-to-finish on the PSP, so it's possible there
are bugs or issues that we're not aware of.
When you encounter such a bug, please use the "Bug Tracker" you find linked
on the ScummVM website, and mention all relevant info i.e. that you're
using the PSP version, which ScummVM version it is, if the problem exists
in a recent PC version, a detailed description of the problem,
and if at all possible a nearby savegame. This will make it much easier
for us to reproduce (and hopefully fix) the problem.
Building the source code
========================
To build ScummVM for PSP you need:
- ScummVM source code (git clone https://github.com/scummvm/scummvm.git)
- PSP toolchain (svn co svn://svn.pspdev.org/psp/trunk/psptoolchain)
- PSPSDK (svn co svn://svn.pspdev.org/psp/trunk/pspsdk)
Note: This usually gets installed by the PSP toolchain,
so you don't have to do it manually.
- zlib (svn co svn://svn.pspdev.org/psp/trunk/zlib)
- libPNG (svn co svn://svn.pspdev.org/psp/trunk/libpng)
- libmad(*) (svn co svn://svn.pspdev.org/psp/trunk/libmad)
- libTremor(*) (svn co svn://svn.pspdev.org/psp/trunk/libTremor)
(*) = optional
Once you've installed these libraries (read their README.PSP for instructions),
create a subdirectory in your ScummVM folder called 'builds/psp'. Then, in this folder, type
'../../configure --host=psp --enable-plugins --default-dynamic'. If everything is installed
correctly, ScummVM will inform you as it finds the right components. Finally type 'make' to build.
Port Authors
============
Joost Peters (joostp@scummvm.org)
Paolo Costabel (paoloc@pacbell.net)
Thomas Mayer (tommybear@internode.on.net)
Yotam Barnoy (yotambarnoy@gmail.com)

View File

@@ -0,0 +1,171 @@
ScummVM-PSP @VERSION@ README
==============================================================================
Installation
============
- Copy the relevant game datafiles to your memory stick (location doesn't matter).
- Install ScummVM like any other homebrew.
- Run ScummVM and use the launcher to add games and run them.
- Press Start to return to the launcher and play another game.
Controls
========
Right trigger - Modifier key (see below for uses)
Left trigger - ESC (Usually skips cutscenes. Depends on game)
Analog - Mouse movement
Right trigger + Analog - Fine mouse movement
D-Pad - Arrow keys (useful mostly in SCI and AGI games)
D-Pad + Right Trigger - Diagonal arrow keys (it's hard to input diagonals on some PSPs)
Triangle - Enter (useful for some dialogs)
Cross - Left Mouse Button (usually the main button)
Circle - Right Mouse Button (secondary button in some games)
Square - '.' (skip dialogue in some games e.g. Scumm)
Right trigger + Square - Spacebar (useful in Gobli*ns and SCI games)
Right trigger + Start - F5 (Main Menu in some games)
Select - Show/Hide Virtual Keyboard. Hold down to move keyboard onscreen (with D-Pad).
Right trigger + Select - Show Image Viewer (see below)
Start - Global Menu. Allows you to 'Return To Launcher' to play another game
Virtual Keyboard Mode
=====================
Start - Enter key. Also exits virtual keyboard mode
Select - Exit the virtual keyboard mode
Right trigger - Input letters: lowercase/uppercase (press to toggle)
Left trigger - Input numbers/symbols (press to toggle)
D-Pad - Select square of characters (up, down, left or right)
Buttons/Triggers - Choose a specific character in the square. The four center characters are chosen
by the button in the corresponding position. The 2 top characters are chosen by the
triggers.
Analog - Moves in a direction (left/right/up/down) (Useful to keep moving
while typing in AGI games among other things)
Image Viewer
============
For your convenience, I've included a simple image viewer in the PSP port.
You can view anything you want while playing a game.
There are a few simple rules to follow:
- Images must be of PNG format. If you have images in another format, many
graphics utilities will convert them for you.
- Images must be named psp_image1.png, psp_image2.png etc. This is to make
sure there's no possible conflict between image files and game files.
- Images must be placed in the game directories. When using the image viewer,
only the images of the particular game being played will be available for viewing.
- Don't place any images in the ScummVM directory, or you won't be able to see
the images in the game directories.
- There's no guarantee that you'll be able to view your image. This is because
big images take a lot of memory (more than the size of the image on disk). If there
isn't enough memory left to show the image, ScummVM will tell you so. Try to make the
image smaller by either shrinking it or reducing the colors to 256 color palette mode.
Image Viewer Controls:
=====================
Left/Right - previous/next image (e.g. go from psp_image1.png to psp_image2.png)
Up/down - zoom in/out
Analog - move around the image
Triggers, Start: - exit image viewer
1st Person Game Mode (Can be ignored by most users)
====================
This is a special mode built for 1st person games like Lands of Lore. If you don't have these games you can
safely ignore this mode. To enter or leave this mode, use the combo:
Right Trigger + Left Trigger + Square
Some buttons have been switched around to make these games more playable:
Square - Is the modifier key instead of Right Trigger.
Left/Right Trigger - Strafe left/right
D-Pad Left/Right - Turn left/right
Square + D-Pad - F1/F2/F3/F4
Square + Select - Image Viewer
Square + Start - Esc (shows game menu)
Notes
=====
- Notice that you can switch between games! This is much faster than quitting
and having to start ScummVM all over again. Go to the global menu and choose 'Return To Launcher'.
(Some games may not have the Return To Launcher option available yet.)
- The PSP version of ScummVM uses the Media Engine to accelerate decoding of MP3 files. This means
that if you have the choice of compressing using Ogg files or MP3 files, choose MP3 -- the game
will generally run faster.
- Plugin files are NOT interchangeable between ScummVM versions! You must copy ALL the
plugin files that come with every version of ScummVM. They sit in the /plugin
subdirectory. If you get a crash upon startup, try deleting all the existing
plugin files in the plugin directory and copying the new ones over again.
- This README may be outdated, for more up-to-date instructions and notes see
the PSP Port Wiki: https://wiki.scummvm.org/index.php/PlayStation_Portable
Frequently Asked Questions
==========================
Q: Scummvm crashes upon starting up!
A: See the first note above.
Q: Pressing select doesn't make the virtual keyboard show up on screen!
A: You need to make sure that the kbd.zip file is in the same directory as the ScummVM executable.
Q: What do I need to run the games?
A: A PSP that can run homebrew and the necessary datafiles for the game you want to play.
Q: Can I run game X with this?
A: You can find the list of supported games on the compatibility page
at https://www.scummvm.org
Note that ScummVM is NOT an emulator. The supported games engines have been painstakingly rewritten.
It's not easy to add support for a game you want that currently isn't supported.
Q: My Monkey Island 1 doesn't have any music, what gives?
A: If your version of Monkey Island came on a CD then it has the music
as CD Audio tracks. You need to rip those to MP3/Ogg (MP3 is preferred), naming them track1.mp3 track2.mp3
etc. and copy them to the same directory as the game datafiles for music to work.
Q: Game X crashes, or feature Y doesn't work. Can you fix this?
A: Possibly.
Because of the large amount of games ScummVM supports we obviously haven't
played them all start-to-finish on the PSP, so it's possible there
are bugs or issues that we're not aware of.
When you encounter such a bug, please use the "Bug Tracker" you find linked
on the ScummVM website, and mention all relevant info i.e. that you're
using the PSP version, which ScummVM version it is, if the problem exists
in a recent PC version, a detailed description of the problem,
and if at all possible a nearby savegame. This will make it much easier
for us to reproduce (and hopefully fix) the problem.
Building the source code
========================
To build ScummVM for PSP you need:
- ScummVM source code (git clone https://github.com/scummvm/scummvm.git)
- PSP toolchain (svn co svn://svn.pspdev.org/psp/trunk/psptoolchain)
- PSPSDK (svn co svn://svn.pspdev.org/psp/trunk/pspsdk)
Note: This usually gets installed by the PSP toolchain,
so you don't have to do it manually.
- zlib (svn co svn://svn.pspdev.org/psp/trunk/zlib)
- libPNG (svn co svn://svn.pspdev.org/psp/trunk/libpng)
- libmad(*) (svn co svn://svn.pspdev.org/psp/trunk/libmad)
- libTremor(*) (svn co svn://svn.pspdev.org/psp/trunk/libTremor)
(*) = optional
Once you've installed these libraries (read their README.PSP for instructions),
create a subdirectory in your ScummVM folder called 'builds/psp'. Then, in this folder, type
'../../configure --host=psp --enable-plugins --default-dynamic'. If everything is installed
correctly, ScummVM will inform you as it finds the right components. Finally type 'make' to build.
Port Authors
============
Joost Peters (joostp@scummvm.org)
Paolo Costabel (paoloc@pacbell.net)
Thomas Mayer (tommybear@internode.on.net)
Yotam Barnoy (yotambarnoy@gmail.com)

View File

@@ -0,0 +1,144 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include <pspthreadman.h>
#include <pspaudio.h>
#include "common/scummsys.h"
#include "backends/platform/psp/audio.h"
//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
//#define __PSP_DEBUG_PRINT__ /* For debug printouts */
#include "backends/platform/psp/trace.h"
bool PspAudio::open(uint32 freq, uint32 numOfChannels, uint32 numOfSamples, callbackFunc callback, void *userData) {
DEBUG_ENTER_FUNC();
if (_init) {
PSP_ERROR("audio device already initialized\n");
return true;
}
PSP_DEBUG_PRINT("freq[%d], numOfChannels[%d], numOfSamples[%d], callback[%p], userData[%x]\n",
freq, numOfChannels, numOfSamples, callback, (uint32)userData);
numOfSamples = PSP_AUDIO_SAMPLE_ALIGN(numOfSamples);
uint32 bufLen = numOfSamples * numOfChannels * NUM_BUFFERS * sizeof(uint16);
PSP_DEBUG_PRINT("total buffer size[%d]\n", bufLen);
_buffers[0] = (byte *)memalign(64, bufLen);
if (!_buffers[0]) {
PSP_ERROR("failed to allocate memory for audio buffers\n");
return false;
}
memset(_buffers[0], 0, bufLen); // clean the buffer
// Fill in the rest of the buffer pointers
byte *pBuffer = _buffers[0];
for (int i = 1; i < NUM_BUFFERS; i++) {
pBuffer += numOfSamples * numOfChannels * sizeof(uint16);
_buffers[i] = pBuffer;
}
// Reserve a HW channel for our audio
_pspChannel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, numOfSamples, numOfChannels == 2 ? PSP_AUDIO_FORMAT_STEREO : PSP_AUDIO_FORMAT_MONO);
if (_pspChannel < 0) {
PSP_ERROR("failed to reserve audio channel\n");
return false;
}
PSP_DEBUG_PRINT("reserved channel[%d] for audio\n", _pspChannel);
// Save our data
_numOfChannels = numOfChannels;
_numOfSamples = numOfSamples;
_bufferSize = numOfSamples * numOfChannels * sizeof(uint16); // should be the right size to send the app
_callback = callback;
_userData = userData;
_bufferToFill = 0;
_bufferToPlay = 0;
_init = true;
_paused = true; // start in paused mode
threadCreateAndStart("audioThread", PRIORITY_AUDIO_THREAD, STACK_AUDIO_THREAD); // start the consumer thread
return true;
}
// The real thread function
void PspAudio::threadFunction() {
assert(_callback);
PSP_DEBUG_PRINT_FUNC("audio thread started\n");
while (_init) { // Keep looping so long as we haven't been told to stop
if (_paused)
PSP_DEBUG_PRINT("audio thread paused\n");
while (_paused) { // delay until we stop pausing
PspThread::delayMicros(100000); // 100ms
if (!_paused)
PSP_DEBUG_PRINT("audio thread unpaused\n");
}
PSP_DEBUG_PRINT("filling buffer[%d]\n", _bufferToFill);
_callback(_userData, _buffers[_bufferToFill], _bufferSize); // ask mixer to fill in data
nextBuffer(_bufferToFill);
PSP_DEBUG_PRINT("playing buffer[%d].\n", _bufferToPlay);
playBuffer();
nextBuffer(_bufferToPlay);
} // while _init
// destroy everything
free(_buffers[0]);
sceAudioChRelease(_pspChannel);
PSP_DEBUG_PRINT("audio thread exiting. ****************************\n");
}
// Much faster than using %, especially with conditional moves (MIPS)
inline void PspAudio::nextBuffer(int &bufferIdx) {
DEBUG_ENTER_FUNC();
bufferIdx++;
if (bufferIdx >= NUM_BUFFERS)
bufferIdx = 0;
}
// Don't do it with blocking
inline bool PspAudio::playBuffer() {
DEBUG_ENTER_FUNC();
int ret;
if (_numOfChannels == 1)
ret = sceAudioOutputBlocking(_pspChannel, PSP_AUDIO_VOLUME_MAX, _buffers[_bufferToPlay]);
else
ret = sceAudioOutputPannedBlocking(_pspChannel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, _buffers[_bufferToPlay]);
if (ret < 0) {
PSP_ERROR("failed to output audio. Error[%d]\n", ret);
return false;
}
return true;
}
void PspAudio::close() {
PSP_DEBUG_PRINT("close has been called ***************\n");
_init = false;
}

View File

@@ -0,0 +1,65 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PSP_AUDIO_H
#define PSP_AUDIO_H
#include "backends/platform/psp/thread.h"
class PspAudio : public PspThreadable {
public:
enum {
NUM_BUFFERS = 2,
FREQUENCY = 44100 /* only frequency we allow */
};
typedef void (* callbackFunc)(void *userData, byte *samples, int len); // audio callback to call
PspAudio() : _pspChannel(0),
_numOfChannels(0), _numOfSamples(0), _callback(0),
_bufferToPlay(0), _bufferToFill(0),
_init(false), _paused(true) {
for (int i=0; i<NUM_BUFFERS; i++)
_buffers[i] = 0;
}
~PspAudio() { close(); }
bool playBuffer();
void nextBuffer(int &bufferIdx);
bool open(uint32 freq, uint32 numOfChannels, uint32 numOfSamples, callbackFunc callback, void *userData);
void close();
uint32 getFrequency() { return FREQUENCY; }
void pause() { _paused = true; }
void unpause() { _paused = false; }
virtual void threadFunction(); // actual audio thread
private:
int _pspChannel; // chosen hardware output channel
uint32 _numOfChannels; // 1 for mono; 2 for stereo
uint32 _numOfSamples;
callbackFunc _callback; // the callback to call between outputting audio
void *_userData; // userData to send with callback
byte *_buffers[NUM_BUFFERS];
int _bufferToPlay; // the next buffer to output
int _bufferToFill;
int _bufferSize;
bool _init; // flag for initialization
bool _paused;
};
#endif /* PSP_AUDIO_H */

View File

@@ -0,0 +1,18 @@
#!/bin/bash
export PATH=/opt/toolchains/psp/bin:$PATH
export PSPDEV=/opt/toolchains/psp
CXXFLAGS="-isystem /opt/toolchains/psp/include"
export CXXFLAGS
export LDFLAGS=-L/opt/toolchains/psp/lib
make distclean
./configure --host=psp --disable-debug --enable-plugins --default-dynamic --enable-release
make -j4
VERSION=`make print-distversion`
DISTS=`make print-dists`
mkdir scummvm-$VERSION
cp -r $DISTS EBOOT.PBP plugins scummvm-$VERSION
mkdir scummvm-$VERSION/kbd
cp -r backends/platform/psp/kbd/*.png scummvm-$VERSION/kbd
zip -r9 scummvm-$VERSION-psp.zip scummvm-$VERSION

View File

@@ -0,0 +1,327 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/scummsys.h"
#include "backends/platform/psp/psppixelformat.h"
#include "backends/platform/psp/display_client.h"
#include "backends/platform/psp/default_display_client.h"
#include "backends/platform/psp/cursor.h"
//#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */
//#define __PSP_DEBUG_PRINT__
#include "backends/platform/psp/trace.h"
void Cursor::init() {
DEBUG_ENTER_FUNC();
_renderer.setBuffer(&_buffer); // We do this explicitly
_renderer.setPalette(&_screenPalette); // because we want to choose screenpalette by default
_renderer.setUseGlobalScaler(true);
setRendererModePalettized(true); // Assume we start in 8bit mode
// Default modes
_palette.setPixelFormats(PSPPixelFormat::Type_5551, PSPPixelFormat::Type_Palette_8bit); // default
_screenPalette.setPixelFormats(PSPPixelFormat::Type_5551, PSPPixelFormat::Type_Palette_8bit);
_buffer.setPixelFormat(PSPPixelFormat::Type_5551);
}
void Cursor::deallocate() {
DEBUG_ENTER_FUNC();
_buffer.deallocate();
_palette.deallocate();
_screenPalette.deallocate();
}
void Cursor::setCursorPalette(const byte *colors, uint start, uint num) {
DEBUG_ENTER_FUNC();
if (!_palette.isAllocated()) {
_palette.allocate();
}
// Workaround: This is wrong, but we seem to not be getting setScreenPalette
if (!_screenPalette.isAllocated()) {
_screenPalette.allocate();
}
_palette.setPartial(colors, start, num);
setDirty();
}
void Cursor::setScreenPalette(const byte *colors, uint start, uint num) {
DEBUG_ENTER_FUNC();
if (!_screenPalette.isAllocated()) {
_screenPalette.allocate();
}
_screenPalette.setPartial(colors, start, num);
setDirty();
}
void Cursor::setKeyColor(uint32 color) {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("new color[%u], old color[%u]\n", color, _keyColor);
// If it's a different color, undo the last keycolor
if (_buffer.hasPalette() && color != _keyColor) {
if (_screenPalette.isAllocated())
_screenPalette.setColorPositionAlpha(_keyColor, true);
if (_palette.isAllocated())
_palette.setColorPositionAlpha(_keyColor, true);
}
// Don't need anything special for 16-bit
_keyColor = color;
}
void Cursor::clearKeyColor() {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("keyColor[%d]\n", _keyColor);
// We need 2 mechanisms: one for palettized and one for 16 bit
if (_buffer.hasPalette()) {
if (_screenPalette.isAllocated())
_screenPalette.setColorPositionAlpha(_keyColor, false); // set keycolor to 0
if (_palette.isAllocated())
_palette.setColorPositionAlpha(_keyColor, false);
} else { // 16bit
_renderer.setKeyColor(_keyColor);
}
setDirty();
}
void Cursor::enableCursorPalette(bool enable) {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("enable[%s]\n", enable ? "true" : "false");
_useCursorPalette = enable;
if (enable)
_renderer.setPalette(&_palette); // very important that we do this switch
else
_renderer.setPalette(&_screenPalette);
setDirty();
}
inline void Cursor::setSize(uint32 width, uint32 height) {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("width[%u], height[%u]\n", width, height);
_buffer.setSize(width, height, Buffer::kSizeByTextureSize); // we'll use texture size for mouse
_renderer.setDrawWholeBuffer(); // We need to let the renderer know how much to draw
}
void Cursor::copyFromArray(const byte *array) {
DEBUG_ENTER_FUNC();
if (!_buffer.isAllocated()) {
_buffer.allocate();
}
_buffer.copyFromArray(array, _buffer.getSourceWidthInBytes()); // pitch is source width
setDirty();
// debug
//PSP_DEBUG_DO(_buffer.print(0xF));
}
void Cursor::setHotspot(int32 x, int32 y) {
DEBUG_ENTER_FUNC();
_hotspotX = x;
_hotspotY = y;
updateRendererOffset(); // Important
PSP_DEBUG_PRINT("hotspotX[%d], hotspotY[%d]\n", x, y);
}
// Returns true if change in x or y
bool Cursor::increaseXY(int32 incX, int32 incY) {
DEBUG_ENTER_FUNC();
int32 oldX = _x, oldY = _y;
// adjust for screen resolution
adjustXYForScreenSize(incX, incY);
_x += incX;
_y += incY;
// Clamp mouse
if (_x < 0)
_x = 0;
if (_y < 0)
_y = 0;
if (_x >= (int)_mouseLimitWidth)
_x = (int)_mouseLimitWidth - 1;
if (_y >= (int)_mouseLimitHeight)
_y = (int)_mouseLimitHeight - 1;
PSP_DEBUG_PRINT("X[%d], Y[%d]\n", _x, _y);
if (oldX != _x || oldY != _y) {
updateRendererOffset();
setDirty();
return true;
}
return false;
}
// Set limits on the movement of the cursor ie. screen size
void Cursor::setLimits(uint32 width, uint32 height) {
#define PSP_SCREEN_WIDTH 480
#define PSP_SCREEN_HEIGHT 272
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("width[%u], height[%u]\n", width, height);
_mouseLimitWidth = width;
_mouseLimitHeight = height;
}
// Adjust X,Y movement for the screen size to keep it consistent
inline void Cursor::adjustXYForScreenSize(int32 &x, int32 &y) {
DEBUG_ENTER_FUNC();
// We have our speed calibrated for the y axis at 480x272. The idea is to adjust this for other
// resolutions
int32 newX = x, newY = y;
if (_mouseLimitWidth >= 600) { // multiply by 2
newX *= 2;
newY *= 2;
} else if (_mouseLimitWidth >= 480) { // multiply by 1.5
newX = newX + (newX / 2);
newY = newY + (newY / 2);
}
}
// This is only called when we have a new screen
void Cursor::setScreenPaletteScummvmPixelFormat(const Graphics::PixelFormat *format) {
DEBUG_ENTER_FUNC();
PSPPixelFormat::Type bufferType = PSPPixelFormat::Type_Unknown;
PSPPixelFormat::Type paletteType = PSPPixelFormat::Type_Unknown;
bool swapRedBlue = false;
// Convert Scummvm Pixel Format to PSPPixelFormat
PSPPixelFormat::convertFromScummvmPixelFormat(format, bufferType, paletteType, swapRedBlue);
if (paletteType == PSPPixelFormat::Type_None) {
//_screenPalette.deallocate(); // leave palette for default CLUT8
setRendererModePalettized(false); // use 16-bit mechanism
} else { // We have a palette
_screenPalette.setPixelFormats(paletteType, bufferType);
_palette.setPixelFormats(paletteType, bufferType);
setRendererModePalettized(true); // use palettized mechanism
}
}
// This is called many many times
void Cursor::setSizeAndScummvmPixelFormat(uint32 width, uint32 height, const Graphics::PixelFormat *format) {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("useCursorPalette[%s]\n", _useCursorPalette ? "true" : "false");
uint32 oldBufferSize = 0;
if (_buffer.isAllocated())
oldBufferSize = _buffer.getSizeInBytes();
setSize(width, height);
PSPPixelFormat::Type bufferType = PSPPixelFormat::Type_Unknown;
PSPPixelFormat::Type paletteType = PSPPixelFormat::Type_Unknown;
bool swapRedBlue = false;
PSPPixelFormat::convertFromScummvmPixelFormat(format, bufferType, paletteType, swapRedBlue);
PSP_DEBUG_PRINT("bufferType[%u], paletteType[%u]\n", bufferType, paletteType);
// Check if we need to set new pixel format
if (_buffer.getPixelFormat() != bufferType) {
PSP_DEBUG_PRINT("new buffer pixel format[%u] is different from [%u]. Setting it.\n", bufferType, _buffer.getPixelFormat());
_buffer.setPixelFormat(bufferType);
}
// Check if we need to reallocate
if (_buffer.getSizeInBytes() != oldBufferSize) {
_buffer.allocate();
PSP_DEBUG_PRINT("reallocating buffer. new size: width[%u], height[%u]\n", width, height);
}
PSP_DEBUG_PRINT("palette pixel format[%u]\n", paletteType);
if (paletteType == PSPPixelFormat::Type_None) {
setRendererModePalettized(false); // use non-palettized mechanism
if (format) {
if (format->aBits() == 0)
_fakeAlpha = true; // we are treating e.g. 555 as 5551
else
_fakeAlpha = false; // we have a genuine alpha channel
}
} else { // We have a palette
_palette.setPixelFormats(paletteType, bufferType);
setRendererModePalettized(true); // use palettized mechanism
}
// debug
// PSP_DEBUG_DO(_palette.print(10));
// PSP_DEBUG_DO(_screenPalette.print(10));
}
void Cursor::setXY(int x, int y) {
DEBUG_ENTER_FUNC();
_x = x;
_y = y;
updateRendererOffset(); // Very important to let renderer know things changed
setDirty();
}
inline void Cursor::updateRendererOffset() {
DEBUG_ENTER_FUNC();
_renderer.setOffsetOnScreen(_x - _hotspotX, _y - _hotspotY);
}
inline void Cursor::setRendererModePalettized(bool palettized) {
if (palettized) { // We have a palette. Use blending
_renderer.setAlphaBlending(true);
_renderer.setAlphaReverse(false);
_renderer.setColorTest(false);
} else { // 16 bits, no palette
// Color test is an easy way for the hardware to make our keycolor
// transparent.
_renderer.setColorTest(true);
// Alpha blending is not strictly required, but makes the cursor look
// much better
_renderer.setAlphaBlending(true);
// Pixel formats without alpha (5650) are considered to have their alpha set.
// Since pixel formats like 555 are treated as 5551 on PSP, we reverse
// the alpha format for them so that 0 alpha is 1.
if (_buffer.getPixelFormat() != PSPPixelFormat::Type_5650 && _fakeAlpha)
_renderer.setAlphaReverse(true);
else
_renderer.setAlphaReverse(false);
}
}

View File

@@ -0,0 +1,81 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MOUSE_H
#define MOUSE_H
#include "backends/platform/psp/default_display_client.h"
class Cursor : public DefaultDisplayClient {
private:
int _hotspotX, _hotspotY;
uint32 _keyColor;
int _cursorTargetScale;
bool _useCursorPalette;
bool _hasCursorPalette;
uint32 _mouseLimitWidth;
uint32 _mouseLimitHeight;
int32 _x, _y;
Palette _screenPalette; // separate palette for screen. default 'palette' is cursor palette.
bool _fakeAlpha; // true if treating e.g. 555 as 5551, false if there's a genuine alpha channel
void updateRendererOffset();
public:
Cursor() : _hotspotX(0), _hotspotY(0), _keyColor(0), _cursorTargetScale(0),
_useCursorPalette(false), _hasCursorPalette(false), _mouseLimitWidth(0),
_mouseLimitHeight(0), _x(0), _y(0), _fakeAlpha(false) { }
virtual ~Cursor() { deallocate(); }
void setKeyColor(uint32 color);
void setCursorTargetScale(int scale) { _cursorTargetScale = scale; }
void setScreenPalette(const byte *colors, uint start, uint num);
void copyFromArray(const byte *array);
Palette &palette() { return _palette; }
Buffer &buffer() { return _buffer; }
void setCursorPalette(const byte *colors, uint start, uint num);
void enableCursorPalette(bool enable);
bool isCursorPaletteEnabled() const { return _useCursorPalette; }
void setLimits(uint32 width, uint32 height);
void setXY(int x, int y);
int32 getX() const { return _x; }
int32 getY() const { return _y; }
bool increaseXY(int32 incX, int32 incY); // returns true if there's a change in x or y
void adjustXYForScreenSize(int32 &x, int32 &y);
void init();
void setHotspot(int32 x, int32 y);
void setScreenPaletteScummvmPixelFormat(const Graphics::PixelFormat *format);
void setSizeAndScummvmPixelFormat(uint32 widht, uint32 height, const Graphics::PixelFormat *format);
void clearKeyColor();
void useGlobalScaler(bool val) { _renderer.setUseGlobalScaler(val); }
bool allocate();
void deallocate();
private:
void setSize(uint32 width, uint32 height);
void getPixelFormatsFromScummvmPixelFormat(const Graphics::PixelFormat *format,
PSPPixelFormat::Type &bufferFormat,
PSPPixelFormat::Type &paletteFormat,
uint32 &numOfEntries);
void setRendererModePalettized(bool palettized);
};
#endif /* MOUSE_H */

View File

@@ -0,0 +1,206 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/scummsys.h"
#include "backends/platform/psp/psppixelformat.h"
#include "backends/platform/psp/display_client.h"
#include "backends/platform/psp/default_display_client.h"
//#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */
//#define __PSP_DEBUG_PRINT__
#include "backends/platform/psp/trace.h"
// Class DefaultDisplayClient ---------------------------------------------
bool DefaultDisplayClient::allocate(bool bufferInVram /* = false */, bool paletteInVram /* = false */) {
DEBUG_ENTER_FUNC();
if (!_buffer.allocate(bufferInVram)) {
PSP_ERROR("Couldn't allocate buffer.\n");
return false;
}
if (_buffer.hasPalette()) {
PSP_DEBUG_PRINT("_palette[%p]\n", &_palette);
if (!_palette.allocate()) {
PSP_ERROR("Couldn't allocate palette.\n");
return false;
}
}
return true;
}
void DefaultDisplayClient::deallocate() {
_buffer.deallocate();
if (_buffer.hasPalette())
_palette.deallocate();
}
void DefaultDisplayClient::clearBuffer() {
DEBUG_ENTER_FUNC();
_buffer.clear();
setDirty();
}
inline void DefaultDisplayClient::clearPalette() {
DEBUG_ENTER_FUNC();
_palette.clear();
setDirty();
}
void DefaultDisplayClient::init() {
DEBUG_ENTER_FUNC();
_renderer.setBuffer(&_buffer);
_renderer.setPalette(&_palette);
}
void DefaultDisplayClient::copyFromRect(const byte *buf, int pitch, int destX, int destY, int recWidth, int recHeight) {
DEBUG_ENTER_FUNC();
_buffer.copyFromRect(buf, pitch, destX, destY, recWidth, recHeight);
setDirty();
}
void DefaultDisplayClient::copyToArray(byte *dst, int pitch) {
DEBUG_ENTER_FUNC();
_buffer.copyToArray(dst, pitch);
}
// Class Overlay -------------------------------------------------------
void Overlay::init() {
DEBUG_ENTER_FUNC();
DefaultDisplayClient::init();
_renderer.setAlphaBlending(true);
_renderer.setColorTest(false);
_renderer.setUseGlobalScaler(false);
_renderer.setFullScreen(true); // speeds up render slightly
}
void Overlay::setBytesPerPixel(uint32 size) {
DEBUG_ENTER_FUNC();
switch (size) {
case 1:
_buffer.setPixelFormat(PSPPixelFormat::Type_Palette_8bit);
_palette.setPixelFormats(PSPPixelFormat::Type_4444, PSPPixelFormat::Type_Palette_8bit);
break;
case 2:
_buffer.setPixelFormat(PSPPixelFormat::Type_4444);
break;
case 4:
_buffer.setPixelFormat(PSPPixelFormat::Type_8888);
break;
}
}
void Overlay::setSize(uint32 width, uint32 height) {
DEBUG_ENTER_FUNC();
_buffer.setSize(width, height, Buffer::kSizeBySourceSize);
_renderer.setDrawWholeBuffer(); // We need to let the renderer know how much to draw
}
void Overlay::copyToArray(void *buf, int pitch) {
DEBUG_ENTER_FUNC();
_buffer.copyToArray((byte *)buf, pitch);
}
void Overlay::copyFromRect(const void *buf, int pitch, int x, int y, int w, int h) {
DEBUG_ENTER_FUNC();
_buffer.copyFromRect((const byte *)buf, pitch, x, y, w, h);
// debug
//_buffer.print(0xFF);
setDirty();
}
bool Overlay::allocate() {
DEBUG_ENTER_FUNC();
bool ret = DefaultDisplayClient::allocate(true, false); // buffer in VRAM
return ret;
}
// Class Screen -----------------------------------------------------------
void Screen::init() {
DEBUG_ENTER_FUNC();
DefaultDisplayClient::init();
_renderer.setAlphaBlending(false);
_renderer.setColorTest(false);
_renderer.setUseGlobalScaler(true);
_renderer.setFullScreen(true);
}
void Screen::setShakePos(int shakeXOffset, int shakeYOffset) {
_shakeXOffset = shakeXOffset;
_shakeYOffset = shakeYOffset;
_renderer.setOffsetOnScreen(shakeXOffset, shakeYOffset);
setDirty();
}
void Screen::setSize(uint32 width, uint32 height) {
DEBUG_ENTER_FUNC();
_buffer.setSize(width, height, Buffer::kSizeBySourceSize);
_renderer.setDrawWholeBuffer(); // We need to let the renderer know how much to draw
}
void Screen::setScummvmPixelFormat(const Graphics::PixelFormat *format) {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("format[%p], _buffer[%p], _palette[%p]\n", format, &_buffer, &_palette);
if (!format) {
memset(&_pixelFormat, 0, sizeof(_pixelFormat));
_pixelFormat.bytesPerPixel = 1; // default
} else {
_pixelFormat = *format;
}
PSPPixelFormat::Type bufferFormat, paletteFormat;
bool swapRedBlue = false;
PSPPixelFormat::convertFromScummvmPixelFormat(format, bufferFormat, paletteFormat, swapRedBlue);
_buffer.setPixelFormat(bufferFormat, swapRedBlue);
_palette.setPixelFormats(paletteFormat, bufferFormat, swapRedBlue);
}
Graphics::Surface *Screen::lockAndGetForEditing() {
DEBUG_ENTER_FUNC();
_frameBuffer.init(_buffer.getSourceWidth(), _buffer.getSourceHeight(), _buffer.getBytesPerPixel() * _buffer.getWidth(),
_buffer.getPixels(), _pixelFormat);
// We'll set to dirty once we unlock the screen
return &_frameBuffer;
}
bool Screen::allocate() {
DEBUG_ENTER_FUNC();
return DefaultDisplayClient::allocate(true, false); // buffer in VRAM
}

View File

@@ -0,0 +1,101 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PSP_DEF_DISPLAY_CLIENT_H
#define PSP_DEF_DISPLAY_CLIENT_H
/**
* Default display client that is useful for most purposes.
*/
class DefaultDisplayClient : public DisplayClient {
public:
DefaultDisplayClient() : _visible(false), _dirty(true) {}
bool isVisible() const { return _visible; }
void setVisible(bool v) { _visible = v; setDirty(); }
Buffer &buffer() { return _buffer; }
Palette &palette() { return _palette; }
void init();
bool allocate(bool bufferInVram = false, bool paletteInVram = false);
void deallocate();
void clearBuffer();
void clearPalette();
void render() { _renderer.render(); }
uint32 getWidth() const { return _buffer.getSourceWidth(); }
uint32 getHeight() const { return _buffer.getSourceHeight(); }
void setPartialPalette(const byte *colors, uint start, uint num) { setDirty(); return _palette.setPartial(colors, start, num); }
void getPartialPalette(byte *colors, uint start, uint num) const {
return _palette.getPartial(colors, start, num);
}
void copyFromRect(const byte *buf, int pitch, int destX, int destY, int recWidth, int recHeight);
void copyToArray(byte *dst, int pitch);
void setDirty() { _dirty = true; }
void setClean() { _dirty = false; }
bool isDirty() const { return _dirty; }
protected:
Buffer _buffer;
Palette _palette;
GuRenderer _renderer;
bool _visible;
bool _dirty;
};
/**
* Screen overlay class.
*/
class Overlay : public DefaultDisplayClient {
public:
void init();
bool allocate();
void setBytesPerPixel(uint32 size);
void setSize(uint32 width, uint32 height);
void copyToArray(void *buf, int pitch);
void copyFromRect(const void *buf, int pitch, int x, int y, int w, int h);
};
/**
* Screen class.
*/
class Screen : public DefaultDisplayClient {
public:
Screen() : _shakeXOffset(0), _shakeYOffset(0) {
memset(&_pixelFormat, 0, sizeof(_pixelFormat));
memset(&_frameBuffer, 0, sizeof(_frameBuffer));
}
void init();
bool allocate();
void setShakePos(int shakeXOffset, int shakeYOffset);
void setScummvmPixelFormat(const Graphics::PixelFormat *format);
const Graphics::PixelFormat &getScummvmPixelFormat() const { return _pixelFormat; }
Graphics::Surface *lockAndGetForEditing();
void unlock() { setDirty(); } // set dirty here because of changes
void setSize(uint32 width, uint32 height);
private:
uint32 _shakeXOffset;
uint32 _shakeYOffset;
Graphics::PixelFormat _pixelFormat;
Graphics::Surface _frameBuffer;
};
#endif /* PSP_DEF_DISPLAY_CLIENT_H */

View File

@@ -0,0 +1,773 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Disable printf override in common/forbidden.h to avoid
// clashes with pspdebug.h from the PSP SDK.
// That header file uses
// __attribute__((format(printf,1,2)));
// which gets messed up by our override mechanism; this could
// be avoided by either changing the PSP SDK to use the equally
// legal and valid
// __attribute__((format(__printf__,1,2)));
// or by refining our printf override to use a varadic macro
// (which then wouldn't be portable, though).
// Anyway, for now we just disable the printf override globally
// for the PSP port
#define FORBIDDEN_SYMBOL_EXCEPTION_printf
#include <pspgu.h>
#include <pspkerneltypes.h>
#include <pspdisplay.h>
#include <psputils.h>
#include "common/scummsys.h"
#include "backends/platform/psp/psppixelformat.h"
#include "backends/platform/psp/display_client.h"
#include "backends/platform/psp/display_manager.h"
#define PSP_INCLUDE_SWAP
#include "backends/platform/psp/memory.h"
//#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */
//#define __PSP_DEBUG_PRINT__
#include "backends/platform/psp/trace.h"
#define PSP_BUFFER_WIDTH (512)
#define PSP_SCREEN_WIDTH 480
#define PSP_SCREEN_HEIGHT 272
#define PSP_FRAME_SIZE (PSP_BUFFER_WIDTH * PSP_SCREEN_HEIGHT)
DisplayManager *GuRenderer::_displayManager = 0;
// class Palette ------------------------------------------------------------
//
void Palette::clear() {
DEBUG_ENTER_FUNC();
if (_values && _numOfEntries)
memset(_values, 0, getSizeInBytes());
PSP_DEBUG_PRINT("_values[%p]\n", _values);
}
// Used to clear the specific keycolor
//
void Palette::setColorPositionAlpha(uint32 position, bool alpha) {
DEBUG_ENTER_FUNC();
assert(_values);
assert(position < _numOfEntries);
PSP_DEBUG_PRINT("position[%d], numofEntries[%u], bpp[%u], values[%p]\n", position, _numOfEntries,
_pixelFormat.bitsPerPixel, _values);
if (_numOfEntries <= 16)
position &= 0xF;
else if (_numOfEntries <= 256)
position &= 0xFF;
switch (_pixelFormat.bitsPerPixel) {
case 16: {
uint16 *shortVal = (uint16 *) & _values[_pixelFormat.pixelsToBytes(position)];
*shortVal = _pixelFormat.setColorAlpha((uint32) * shortVal, alpha ? 255 : 0);
}
break;
case 32: {
uint32 *wordVal = (uint32 *) & _values[_pixelFormat.pixelsToBytes(position)];
*wordVal = _pixelFormat.setColorAlpha((uint32) * wordVal, alpha ? 255 : 0);
}
break;
default:
PSP_ERROR("Incorrect bits per pixel value[%u]\n", _pixelFormat.bitsPerPixel);
}
}
// Set some of the palette to color values in array
// By default, ScummVm doesn't support alpha values in palettes
void Palette::setPartial(const byte *colors, uint start, uint num, bool supportsAlpha /* = false */) {
DEBUG_ENTER_FUNC();
assert(_values);
assert(_numOfEntries);
const byte *src = colors;
if (start + num > _numOfEntries) // Check boundary
num = _numOfEntries - start;
if (_pixelFormat.bitsPerPixel == 16) {
uint16 *palette = (uint16 *)_values;
palette += start;
for (uint32 i = 0; i < num; ++i) {
byte alphaVal = supportsAlpha ? src[3] : 0xFF;
*palette = (uint16)_pixelFormat.rgbaToColor(src[0], src[1], src[2], alphaVal);
src += 3;
palette++;
}
} else if (_pixelFormat.bitsPerPixel == 32) {
uint32 *palette = (uint32 *)_values;
palette += start;
for (uint32 i = 0; i < num; ++i) {
byte alphaVal = supportsAlpha ? src[3] : 0xFF;
*palette = _pixelFormat.rgbaToColor(src[0], src[1], src[2], alphaVal);
src += 3;
palette++;
}
}
}
// Sets pixel format and number of entries by the buffer's pixel format */
void Palette::setPixelFormats(PSPPixelFormat::Type paletteType, PSPPixelFormat::Type bufferType, bool swapRedBlue /* = false */) {
DEBUG_ENTER_FUNC();
if (paletteType == PSPPixelFormat::Type_Unknown)
PSP_ERROR("Unknown paletteType[%u]\n", paletteType);
switch (bufferType) {
case PSPPixelFormat::Type_Palette_8bit:
_numOfEntries = 256;
break;
case PSPPixelFormat::Type_Palette_4bit:
_numOfEntries = 16;
break;
case PSPPixelFormat::Type_Unknown:
case PSPPixelFormat::Type_None:
PSP_ERROR("Unhandled bufferType[%u]\n", bufferType);
break;
default: // No palette
_numOfEntries = 0;
break;
}
_pixelFormat.set(paletteType, swapRedBlue);
}
bool Palette::allocate() {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("_numOfEntries[%u]\n", _numOfEntries);
PSP_DEBUG_PRINT("_pixelFormat: format[%u], bpp[%u]\n", _pixelFormat.format, _pixelFormat.bitsPerPixel);
if (_values) {
free(CACHED(_values));
_values = 0;
}
// We allocate on 64bytes to get a cache line, and round up to 64bytes to get the full line
uint32 amountInBytes = getSizeInBytes();
if (amountInBytes < 64)
amountInBytes = 64;
_values = (byte *)memalign(64, amountInBytes);
// Use uncached memory
GuRenderer::cacheInvalidate(_values, amountInBytes);
_values = UNCACHED(_values);
if (!_values) {
PSP_ERROR("Couldn't allocate palette.\n");
return false;
}
PSP_DEBUG_PRINT("_values[%p]\n", _values);
clear();
return true;
}
void Palette::deallocate() {
DEBUG_ENTER_FUNC();
free(CACHED(_values));
_values = 0;
_numOfEntries = 0;
}
// Copy some of the palette to an array of colors
//
void Palette::getPartial(byte *colors, uint start, uint num) const {
DEBUG_ENTER_FUNC();
assert(_values);
assert(_numOfEntries);
uint32 r, g, b, a;
if (start + num > _numOfEntries) // Check boundary
num = _numOfEntries - start;
if (_pixelFormat.bitsPerPixel == 16) {
uint16 *palette = (uint16 *)_values;
palette += start;
for (uint32 i = start; i < start + num; i++) {
_pixelFormat.colorToRgba(*palette, r, g, b, a);
*colors++ = (byte)r;
*colors++ = (byte)g;
*colors++ = (byte)b;
palette++;
}
} else if (_pixelFormat.bitsPerPixel == 32) {
uint32 *palette = (uint32 *)_values;
palette += start;
for (uint32 i = start; i < start + num; i++) {
_pixelFormat.colorToRgba(*palette, r, g, b, a);
*colors++ = (byte)r;
*colors++ = (byte)g;
*colors++ = (byte)b;
palette++;
}
}
}
void Palette::setSingleColorRGBA(uint32 num, byte r, byte g, byte b, byte a) {
// DEBUG_ENTER_FUNC();
uint16 *shortValues;
uint32 *wordValues;
assert(_values);
assert(num < _numOfEntries);
switch (_pixelFormat.bitsPerPixel) {
case 16:
shortValues = (uint16 *)_values;
shortValues[num] = _pixelFormat.rgbaToColor(r, g, b, a);
break;
case 32:
wordValues = (uint32 *)_values;
wordValues[num] = _pixelFormat.rgbaToColor(r, g, b, a);
break;
default:
PSP_ERROR("Incorrect bitsPerPixel[%d]\n", _pixelFormat.bitsPerPixel);
break;
}
}
// Print to screen
void Palette::print(uint32 numToPrint /* = 0 */) {
if (_numOfEntries > 0) {
assert(_values);
if (numToPrint > _numOfEntries || numToPrint == 0)
numToPrint = _numOfEntries;
PSP_INFO_PRINT("cursor palette:\n");
for (unsigned int i = 0; i < numToPrint; i++) {
byte *pcolor = &_values[_pixelFormat.pixelsToBytes(i)];
uint32 color = _pixelFormat.getColorValueAt(pcolor);
PSP_INFO_PRINT("[%u=%x] ", i, color);
}
PSP_INFO_PRINT("\n");
}
}
uint32 Palette::getRawColorAt(uint32 position) const {
byte *pcolor = &_values[_pixelFormat.pixelsToBytes(position)];
uint32 color = _pixelFormat.getColorValueAt(pcolor);
return color;
}
uint32 Palette::getRGBAColorAt(uint32 position) const {
uint32 color = getRawColorAt(position);
uint32 r, g, b, a;
_pixelFormat.colorToRgba(color, r, g, b, a);
return (a << 24 | b << 16 | g << 8 | r);
}
// class Buffer ---------------------------------------------------
void Buffer::setPixelFormat(PSPPixelFormat::Type type, bool swapRedBlue) {
if (type == PSPPixelFormat::Type_None ||
type == PSPPixelFormat::Type_Unknown)
PSP_ERROR("Unhandled buffer format[%u]\n", type);
_pixelFormat.set(type, swapRedBlue);
}
bool Buffer::hasPalette() {
if (_pixelFormat.format == PSPPixelFormat::Type_Palette_8bit ||
_pixelFormat.format == PSPPixelFormat::Type_Palette_4bit)
return true;
return false;
}
/* pitch is in bytes */
void Buffer::copyFromArray(const byte *buffer, int pitch) {
DEBUG_ENTER_FUNC();
// We use sourceSize because outside, they won't know what the true size is
copyFromRect(buffer, pitch, 0, 0, _sourceSize.width, _sourceSize.height);
}
/* pitch is in bytes */
void Buffer::copyFromRect(const byte *buf, uint32 pitch, int destX, int destY, uint32 recWidth, uint32 recHeight) {
// Removed silly clipping code
DEBUG_ENTER_FUNC();
assert(_pixels);
if (recWidth > _sourceSize.width - destX) {
recWidth = _sourceSize.width - destX;
}
if (recHeight > _sourceSize.height - destY) {
recHeight = _sourceSize.height - destY;
}
if (recWidth <= 0 || recHeight <= 0) {
return;
}
byte *dst = _pixels + _pixelFormat.pixelsToBytes((destY * _width) + destX);
uint32 recWidthInBytes = _pixelFormat.pixelsToBytes(recWidth);
uint32 realWidthInBytes = _pixelFormat.pixelsToBytes(_width);
if (pitch == realWidthInBytes && pitch == recWidthInBytes) {
//memcpy(dst, buf, _pixelFormat.pixelsToBytes(recHeight * recWidth));
if (_pixelFormat.swapRB)
PspMemorySwap::fastSwap(dst, buf, _pixelFormat.pixelsToBytes(recHeight * recWidth), _pixelFormat);
else
PspMemory::fastCopy(dst, buf, _pixelFormat.pixelsToBytes(recHeight * recWidth));
} else {
do {
if (_pixelFormat.swapRB)
PspMemorySwap::fastSwap(dst, buf, recWidthInBytes, _pixelFormat);
else
PspMemory::fastCopy(dst, buf, recWidthInBytes);
buf += pitch;
dst += realWidthInBytes;
} while (--recHeight);
}
}
/* pitch is in bytes */
void Buffer::copyToArray(byte *dst, int pitch) {
DEBUG_ENTER_FUNC();
assert(_pixels);
uint32 h = _height;
byte *src = _pixels;
uint32 sourceWidthInBytes = _pixelFormat.pixelsToBytes(_sourceSize.width);
uint32 realWidthInBytes = _pixelFormat.pixelsToBytes(_width);
do {
//memcpy(dst, src, sourceWidthInBytes);
if (_pixelFormat.swapRB)
PspMemorySwap::fastSwap(dst, src, sourceWidthInBytes, _pixelFormat);
else
PspMemory::fastCopy(dst, src, sourceWidthInBytes);
src += realWidthInBytes;
dst += pitch;
} while (--h);
}
void Buffer::setSize(uint32 width, uint32 height, HowToSize textureOrSource/*=kSizeByTextureSize*/) {
DEBUG_ENTER_FUNC();
// We can size the buffer either by texture size (multiple of 2^n) or source size.
// At higher sizes, increasing the texture size to 2^n is a waste of space. At these sizes kSizeBySourceSize should be used.
_sourceSize.width = width;
_sourceSize.height = height;
_textureSize.width = scaleUpToPowerOfTwo(width); // can only scale up to 512
_textureSize.height = scaleUpToPowerOfTwo(height);
if (textureOrSource == kSizeByTextureSize) {
_width = _textureSize.width;
_height = _textureSize.height;
} else { // sizeBySourceSize
_width = _sourceSize.width;
_height = _sourceSize.height;
// adjust allocated width to be divisible by 32.
// The GU can only handle multiples of 16 bytes. A 4 bit image x 32 will give us 16 bytes
// We don't necessarily know the depth of the pixels here. So just make it divisible by 32.
uint32 checkDiv = _width & 31;
if (checkDiv)
_width += 32 - checkDiv;
}
PSP_DEBUG_PRINT("width[%u], height[%u], texW[%u], texH[%u], sourceW[%d], sourceH[%d] %s\n", _width, _height, _textureSize.width, _textureSize.height, _sourceSize.width, _sourceSize.height, textureOrSource ? "size by source" : "size by texture");
}
// Scale a dimension (width/height) up to power of 2 for the texture
// Will only go up to 512 since that's the maximum PSP texture size
uint32 Buffer::scaleUpToPowerOfTwo(uint32 size) {
uint32 textureDimension = 16;
while (size > textureDimension && textureDimension < 512)
textureDimension <<= 1;
PSP_DEBUG_PRINT("size[%u]. power of 2[%u]\n", size, textureDimension);
return textureDimension;
}
bool Buffer::allocate(bool inVram/*=false*/) {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("_width[%u], _height[%u]\n", _width, _height);
PSP_DEBUG_PRINT("_pixelFormat: format[%u], bpp[%u]\n", _pixelFormat.format, _pixelFormat.bitsPerPixel);
if (_pixels) {
if (VramAllocator::isAddressInVram(_pixels)) // Check if in VRAM
VramAllocator::instance().deallocate(_pixels);
else // not in VRAM
free(CACHED(_pixels));
_pixels = 0;
}
uint32 size = getSizeInBytes();
if (inVram) {
_pixels = (byte *)VramAllocator::instance().allocate(size);
}
if (!_pixels) { // Either we are not in vram or we didn't manage to allocate in vram
// Align to 64 bytes. All normal buffer sizes are multiples of 64 anyway
_pixels = (byte *)memalign(64, size);
}
if (!_pixels) {
PSP_ERROR("couldn't allocate buffer.\n");
return false;
}
// Use uncached memory
GuRenderer::cacheInvalidate(_pixels, size);
_pixels = UNCACHED(_pixels);
clear();
return true;
}
void Buffer::deallocate() {
DEBUG_ENTER_FUNC();
if (!_pixels)
return;
if (VramAllocator::isAddressInVram(_pixels)) // Check if in VRAM
VramAllocator::instance().deallocate(_pixels);
else
free(CACHED(_pixels));
_pixels = 0;
}
void Buffer::clear() {
DEBUG_ENTER_FUNC();
if (_pixels)
memset(_pixels, 0, getSizeInBytes());
}
/* Convert 4 bit images to match weird PSP format */
void Buffer::flipNibbles() {
DEBUG_ENTER_FUNC();
if (_pixelFormat.bitsPerPixel != 4)
return;
assert(_pixels);
uint32 *dest = (uint32 *)_pixels;
for (uint32 i = 0; i < getSourceHeight(); i++) {
for (uint32 j = 0; j < (getWidth() >> 3); j++) { // /8 because we do it in 32bit chunks
uint32 val = *dest;
*dest++ = ((val >> 4) & 0x0F0F0F0F) | ((val << 4) & 0xF0F0F0F0);
}
}
}
// Print buffer contents to screen (only source size is printed out)
void Buffer::print(uint32 mask, uint32 numToPrint /*=0*/) {
assert(_pixels);
if (numToPrint > _sourceSize.width * _sourceSize.height || numToPrint == 0)
numToPrint = _sourceSize.width * _sourceSize.height;
PSP_INFO_PRINT("buffer: \n");
PSP_INFO_PRINT("width[%u], height[%u]\n\n", _sourceSize.width, _sourceSize.height);
for (unsigned int i = 0; i < _sourceSize.height; i++) {
for (unsigned int j = 0; j < _sourceSize.width; j++) {
if (numToPrint <= 0) // check if done
break;
byte *pcolor = &_pixels[_pixelFormat.pixelsToBytes((i * _width) + j)];
uint32 color = _pixelFormat.getColorValueAt(pcolor);
//if (color != 0) PSP_INFO_PRINT("[%x] ", color);
PSP_INFO_PRINT("[%x] ", mask & color);
numToPrint--;
}
PSP_INFO_PRINT("\n");
}
PSP_INFO_PRINT("\n");
}
// class GuRenderer -------------------------------------------------
//#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */
//#define __PSP_DEBUG_PRINT__
#include "backends/platform/psp/trace.h"
void GuRenderer::render() {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("Buffer[%p] Palette[%p]\n", _buffer->getPixels(), _palette->getRawValues());
guProgramDrawBehavior();
if (_buffer->hasPalette())
guLoadPalette();
guProgramTextureFormat();
// Loop over patches of 512x512 pixel textures and draw them
for (uint32 j = 0; j < _buffer->getSourceHeight(); j += 512) {
_textureLoadOffset.y = j;
for (uint32 i = 0; i < _buffer->getSourceWidth(); i += 512) {
_textureLoadOffset.x = i;
guLoadTexture();
Vertex *vertices = guGetVertices();
fillVertices(vertices);
guDrawVertices(vertices);
}
}
}
inline void GuRenderer::guProgramDrawBehavior() {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("blending[%s] colorTest[%s] reverseAlpha[%s] keyColor[%u]\n",
_blending ? "on" : "off", _colorTest ? "on" : "off",
_alphaReverse ? "on" : "off", _keyColor);
if (_blending) {
sceGuEnable(GU_BLEND);
if (_alphaReverse) // Reverse the alpha value (ie. 0 is 1) easier to do in some cases
sceGuBlendFunc(GU_ADD, GU_ONE_MINUS_SRC_ALPHA, GU_SRC_ALPHA, 0, 0);
else // Normal alpha values
sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
} else
sceGuDisable(GU_BLEND);
if (_colorTest) {
sceGuEnable(GU_COLOR_TEST);
sceGuColorFunc(GU_NOTEQUAL, // show only colors not equal to this color
_keyColor,
0x00ffffff); // match everything but alpha
} else
sceGuDisable(GU_COLOR_TEST);
}
inline void GuRenderer::guLoadPalette() {
DEBUG_ENTER_FUNC();
uint32 mask;
if (_buffer->getBitsPerPixel() == 4)
mask = 0x0F;
else if (_buffer->getBitsPerPixel() == 8)
mask = 0xFF;
else
assert(0); /* error */
PSP_DEBUG_PRINT("numOfEntries[%d]\n", _palette->getNumOfEntries());
PSP_DEBUG_PRINT("bpp[%d], pixelformat[%d], mask[%x]\n", _buffer->getBitsPerPixel(), _palette->getPixelFormat(), mask);
sceGuClutMode(convertToGuPixelFormat(_palette->getPixelFormat()), 0, mask, 0);
sceGuClutLoad(_palette->getNumOfEntries() >> 3, // it's in batches of 8 for some reason
_palette->getRawValues());
}
inline void GuRenderer::guProgramTextureFormat() {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("pixelFormat[%d]\n", _buffer->getPixelFormat());
sceGuTexMode(convertToGuPixelFormat(_buffer->getPixelFormat()), 0, 0, 0);
}
inline uint32 GuRenderer::convertToGuPixelFormat(PSPPixelFormat::Type format) {
DEBUG_ENTER_FUNC();
uint32 guFormat = 0;
switch (format) {
case PSPPixelFormat::Type_4444:
guFormat = GU_PSM_4444;
break;
case PSPPixelFormat::Type_5551:
guFormat = GU_PSM_5551;
break;
case PSPPixelFormat::Type_5650:
guFormat = GU_PSM_5650;
break;
case PSPPixelFormat::Type_8888:
case PSPPixelFormat::Type_8888_RGBA:
guFormat = GU_PSM_8888;
break;
case PSPPixelFormat::Type_Palette_8bit:
guFormat = GU_PSM_T8;
break;
case PSPPixelFormat::Type_Palette_4bit:
guFormat = GU_PSM_T4;
break;
default:
break;
}
PSP_DEBUG_PRINT("Pixelformat[%d], guFormat[%d]\n", format, guFormat);
return guFormat;
}
inline void GuRenderer::guLoadTexture() {
DEBUG_ENTER_FUNC();
byte *startPoint = _buffer->getPixels();
if (_textureLoadOffset.x)
startPoint += _buffer->_pixelFormat.pixelsToBytes(_textureLoadOffset.x);
if (_textureLoadOffset.y)
startPoint += _buffer->getWidthInBytes() * _textureLoadOffset.y;
sceGuTexImage(0,
_buffer->getTextureWidth(), // texture width (must be power of 2)
_buffer->getTextureHeight(), // texture height (must be power of 2)
_buffer->getWidth(), // width of a line of the image (to get to the next line)
startPoint); // where to start reading
}
inline Vertex *GuRenderer::guGetVertices() {
DEBUG_ENTER_FUNC();
Vertex *ret = (Vertex *)sceGuGetMemory(2 * sizeof(Vertex));
return ret;
}
// Fills the vertices. Most of the logic is here.
void GuRenderer::fillVertices(Vertex *vertices) {
DEBUG_ENTER_FUNC();
uint32 outputWidth = _displayManager->getOutputWidth();
uint32 outputHeight = _displayManager->getOutputHeight();
// Texture adjustments for eliminating half-pixel artifacts from scaling
// Not necessary if we don't scale
float textureFix = 0.0f;
if (_useGlobalScaler &&
(_displayManager->getScaleX() != 1.0f || _displayManager->getScaleY() != 1.0f))
textureFix = 0.5f;
// These coordinates describe an area within the texture. ie. we already loaded a square of texture,
// now the coordinates within it are 0 to the edge of the area of the texture we want to draw
float textureStartX = textureFix + _offsetInBuffer.x;
float textureStartY = textureFix + _offsetInBuffer.y;
int textureLeftX = _drawSize.width - _textureLoadOffset.x;
if (textureLeftX > 512)
textureLeftX = 512;
int textureLeftY = _drawSize.height - _textureLoadOffset.y;
if (textureLeftY > 512)
textureLeftY = 512;
float textureEndX = -textureFix + _offsetInBuffer.x + textureLeftX;
float textureEndY = -textureFix + _offsetInBuffer.y + textureLeftY;
// For scaling to the final image size, calculate the gaps on both sides
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 = scaleSourceToOutput(true, _offsetOnScreen.x);
float scaledOffsetOnScreenY = scaleSourceToOutput(false, _offsetOnScreen.y);
float imageStartX = gapX + scaledOffsetOnScreenX + (scaleSourceToOutput(true, stretch(true, _textureLoadOffset.x)));
float imageStartY = gapY + scaledOffsetOnScreenY + (scaleSourceToOutput(false, stretch(false, _textureLoadOffset.y)));
float imageEndX, imageEndY;
imageEndX = imageStartX + scaleSourceToOutput(true, stretch(true, textureLeftX));
imageEndY = imageStartY + scaleSourceToOutput(false, stretch(false, textureLeftY));
vertices[0].u = textureStartX;
vertices[0].v = textureStartY;
vertices[1].u = textureEndX;
vertices[1].v = textureEndY;
vertices[0].x = imageStartX;
vertices[0].y = imageStartY;
vertices[0].z = 0;
vertices[1].x = imageEndX;
vertices[1].y = imageEndY;
vertices[1].z = 0;
PSP_DEBUG_PRINT("TextureStart: X[%f] Y[%f] TextureEnd: X[%.1f] Y[%.1f]\n", textureStartX, textureStartY, textureEndX, textureEndY);
PSP_DEBUG_PRINT("ImageStart: X[%f] Y[%f] ImageEnd: X[%.1f] Y[%.1f]\n", imageStartX, imageStartY, imageEndX, imageEndY);
}
/* Scale the input X/Y offset to appear in proper position on the screen */
inline float GuRenderer::scaleSourceToOutput(bool x, float offset) {
float result;
if (!_useGlobalScaler)
result = offset;
else if (!offset)
result = 0.0f;
else
result = x ? offset * _displayManager->getScaleX() : offset * _displayManager->getScaleY();
return result;
}
/* Scale the input X/Y offset to appear in proper position on the screen */
inline float GuRenderer::stretch(bool x, float size) {
if (!_stretch)
return size;
return (x ? size * _stretchX : size * _stretchY);
}
inline void GuRenderer::guDrawVertices(Vertex *vertices) {
DEBUG_ENTER_FUNC();
// This function shouldn't need changing. The '32' here refers to floating point vertices.
sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_TRANSFORM_2D, 2, 0, vertices);
}
void GuRenderer::cacheInvalidate(void *pointer, uint32 size) {
sceKernelDcacheWritebackInvalidateRange(pointer, size);
}

View File

@@ -0,0 +1,244 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PSP_GRAPHICS_H
#define PSP_GRAPHICS_H
#include "common/singleton.h"
#include "graphics/surface.h"
#include "common/system.h"
#include "backends/platform/psp/memory.h"
#include "backends/platform/psp/psppixelformat.h"
#define MAX_TEXTURE_SIZE 512
class DisplayManager;
class GuRenderer;
/**
* Interface to inherit for all display clients
* We deliberately avoid virtual functions for speed.
*/
class DisplayClient { // Abstract class
public:
DisplayClient() {}
bool isVisible() { return true; }
bool isDirty() { return true; }
void setClean() {}
void render() {}
virtual ~DisplayClient() {}
};
/**
* Vertex used for GU rendering
*/
struct Vertex {
float u, v;
float x, y, z;
};
struct Point {
int x;
int y;
Point() : x(0), y(0) {}
};
/**
* Dimensions struct for simplification
*/
struct Dimensions {
uint32 width;
uint32 height;
Dimensions() : width(0), height(0) {}
};
/**
* Universal PSP Palette class
* Use this in any class that wishes to draw to the PSP screen.
* Use together with GuRenderer
*/
class Palette {
public:
Palette() : _values(0), _numOfEntries(0) {}
virtual ~Palette() { deallocate(); }
bool allocate();
void deallocate();
void clear();
void setPixelFormats(PSPPixelFormat::Type paletteType, PSPPixelFormat::Type bufferType, bool swapRedBlue = false);
void setNumOfEntries(uint32 num) { _numOfEntries = num; }
uint32 getNumOfEntries() const { return _numOfEntries; }
uint32 getSizeInBytes() const { return _pixelFormat.pixelsToBytes(_numOfEntries); }
void set(byte *values) { setPartial(values, 0, _numOfEntries); }
void setPartial(const byte *colors, uint start, uint num, bool supportsAlpha = false);
void getPartial(byte *colors, uint start, uint num) const;
uint32 getRawColorAt(uint32 position) const;
uint32 getRGBAColorAt(uint32 position) const;
void setSingleColorRGBA(uint32 num, byte r, byte g, byte b, byte a);
void setColorPositionAlpha(uint32 position, bool alpha);
const byte *getRawValues() const { return _values; }
byte *getRawValues() { return _values; }
bool isAllocated() const { return (_values != 0); }
PSPPixelFormat::Type getPixelFormat() const { return _pixelFormat.format; }
void print(uint32 numToPrint = 0); // print to screen
protected:
byte *_values; ///< array of palette data
uint32 _numOfEntries; ///< number of palette entries
PSPPixelFormat _pixelFormat; ///< pixel format of the palette data
};
/**
* Universal PSP buffer/texture object
* Use this in any class that wishes to draw to the PSP screen.
* Use together with GuRenderer
*/
class Buffer {
public:
enum HowToSize {
kSizeByTextureSize, // buffer size is determined by power of 2 roundup for texture
kSizeBySourceSize // buffer size is determined by source size
};
Buffer() : _pixels(0), _width(0), _height(0) {}
virtual ~Buffer() { deallocate(); }
// setters
void setSize(uint32 width, uint32 height, HowToSize textureOrSource = kSizeByTextureSize);
void setBitsPerPixel(uint32 bits) { _pixelFormat.bitsPerPixel = bits; }
void setBytesPerPixel(uint32 bytes) { setBitsPerPixel(bytes << 3); }
void setPixelFormat(PSPPixelFormat::Type type, bool swapRedBlue = false);
// getters
uint32 getWidth() const { return _width; }
uint32 getWidthInBytes() const { return _pixelFormat.pixelsToBytes(getWidth()); }
uint32 getHeight() const { return _height; }
uint32 getSourceWidth() const { return _sourceSize.width; }
uint32 getSourceWidthInBytes() const { return _pixelFormat.pixelsToBytes(_sourceSize.width); }
uint32 getSourceHeight() const { return _sourceSize.height; }
uint32 getTextureWidth() const { return _textureSize.width; }
uint32 getTextureHeight() const { return _textureSize.height; }
PSPPixelFormat::Type getPixelFormat() const { return _pixelFormat.format; }
uint32 getBitsPerPixel() const { return _pixelFormat.bitsPerPixel; }
uint32 getBytesPerPixel() const { return getBitsPerPixel() >> 3; } /* won't work for 4-bit */
const byte *getPixels() const { return _pixels; }
byte *getPixels() { return _pixels; }
uint32 getSizeInBytes() const { return _pixelFormat.pixelsToBytes(_width * _height); }
bool hasPalette();
void copyFromArray(const byte *buffer, int pitch);
void copyFromRect(const byte *buf, uint32 pitch, int destX, int destY, uint32 recWidth, uint32 recHeight);
void copyToArray(byte *dst, int pitch);
bool allocate(bool inVram = false);
void deallocate();
bool isAllocated() const { return (_pixels != 0) ; }
void clear();
void flipNibbles(); // To handle peculiarities of PSP's 4 bit textures
static uint32 scaleUpToPowerOfTwo(uint32 size);
void print(uint32 mask, uint32 numToPrint = 0);
protected:
friend class GuRenderer;
byte *_pixels;
uint32 _width; ///< True allocated width
uint32 _height; ///< True allocated height
Dimensions _textureSize; ///< Size rounded up to power of 2. Used for drawing
Dimensions _sourceSize; ///< Original size of the buffer
PSPPixelFormat _pixelFormat; ///< Format of the buffer
};
/**
* Universal rendering class for PSP
* Use this if you want to draw to the screen.
* Needs to be supplied with a Buffer and a Palette
*/
class GuRenderer {
public:
// Constructors
GuRenderer() : _useGlobalScaler(false), _buffer(0), _palette(0),
_blending(false), _alphaReverse(false), _colorTest(false),
_keyColor(0), _fullScreen(false), _stretch(false), _stretchX(1.0f), _stretchY(1.0f) {}
GuRenderer(Buffer *buffer, Palette *palette) :
_useGlobalScaler(false), _buffer(buffer), _palette(palette),
_blending(false), _alphaReverse(false), _colorTest(false),
_keyColor(0), _fullScreen(false), _stretch(false), _stretchX(1.0f), _stretchY(1.0f) {}
static void setDisplayManager(DisplayManager *dm) { _displayManager = dm; } // Called by the Display Manager
// Setters
void setDrawSize(uint32 width, uint32 height) { // How big of an area to draw
_drawSize.width = width;
_drawSize.height = height;
}
void setDrawWholeBuffer() { // Draw the full size of the current buffer
assert(_buffer);
_drawSize.width = _buffer->getSourceWidth();
_drawSize.height = _buffer->getSourceHeight();
}
void setBuffer(Buffer *buffer) { _buffer = buffer; }
void setPalette(Palette *palette) { _palette = palette; }
void setOffsetOnScreen(int x, int y) { _offsetOnScreen.x = x; _offsetOnScreen.y = y; }
void setOffsetInBuffer(uint32 x, uint32 y) { _offsetInBuffer.x = x; _offsetInBuffer.y = y; }
void setColorTest(bool value) { _colorTest = value; }
void setKeyColor(uint32 value) { _keyColor = _buffer->_pixelFormat.convertTo32BitColor(value); }
void setAlphaBlending(bool value) { _blending = value; }
void setAlphaReverse(bool value) { _alphaReverse = value; }
void setFullScreen(bool value) { _fullScreen = value; } // Shortcut for rendering
void setUseGlobalScaler(bool value) { _useGlobalScaler = value; } // Scale to screen
void setStretch(bool active) { _stretch = active; }
void setStretchXY(float x, float y) { _stretchX = x; _stretchY = y; }
static void cacheInvalidate(void *pointer, uint32 size);
void render(); // Default rendering function. This should be good enough for most purposes
protected:
// Gu functions
void fillVertices(Vertex *vertices); // Fill in vertices with coordinates
void guProgramDrawBehavior();
Vertex *guGetVertices();
void guLoadTexture();
void guLoadPalette();
void guProgramTextureFormat();
void guProgramTextureBitDepth();
void guDrawVertices(Vertex *vertices);
uint32 convertToGuPixelFormat(PSPPixelFormat::Type format);
float scaleSourceToOutput(bool x, float offset);
float stretch(bool x, float size);
friend class MasterGuRenderer;
Point _textureLoadOffset; ///> For rendering textures > 512 pixels
Point _offsetOnScreen; ///> Where on screen to draw
Point _offsetInBuffer; ///> Where in the texture to draw
bool _useGlobalScaler; ///> Scale to the output size on screen
Buffer *_buffer;
Palette *_palette;
static DisplayManager *_displayManager;
Dimensions _drawSize; ///> Actual size to draw out of the Buffer
bool _blending;
bool _alphaReverse; ///> 0 counts as full alpha
bool _colorTest;
uint32 _keyColor; ///> Color to test against for color test. in 32 bits.
bool _fullScreen; ///> Speeds up for fullscreen rendering
bool _stretch; ///> Whether zooming is activated
float _stretchX, _stretchY;
};
#endif /* PSP_SCREEN_H */

View File

@@ -0,0 +1,472 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include <pspgu.h>
#include <pspdisplay.h>
#include <pspthreadman.h>
#include "common/scummsys.h"
#include "backends/base-backend.h"
#include "backends/platform/psp/psppixelformat.h"
#include "backends/platform/psp/display_client.h"
#include "backends/platform/psp/default_display_client.h"
#include "backends/platform/psp/cursor.h"
#include "backends/platform/psp/pspkeyboard.h"
#include "backends/platform/psp/image_viewer.h"
#include "common/config-manager.h"
#define USE_DISPLAY_CALLBACK // to use callback for finishing the render
#include "backends/platform/psp/display_manager.h"
#define PSP_BUFFER_WIDTH (512)
#define PSP_SCREEN_WIDTH 480
#define PSP_SCREEN_HEIGHT 272
#define PSP_FRAME_SIZE (PSP_BUFFER_WIDTH * PSP_SCREEN_HEIGHT)
//#define ENABLE_RENDER_MEASURE /* how long it takes to render a frame */
//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
//#define __PSP_DEBUG_PRINT__ /* For debug printouts */
#include "backends/platform/psp/trace.h"
uint32 __attribute__((aligned(16))) MasterGuRenderer::_displayList[2048];
const OSystem::GraphicsMode DisplayManager::_supportedModes[] = {
{ "Original Resolution", "Original Resolution", ORIGINAL_RESOLUTION },
{ "Fit to Screen", "Fit to Screen", FIT_TO_SCREEN },
{ "Stretch to Screen", "Stretch to Screen", STRETCH_TO_SCREEN },
{0, 0, 0}
};
// Class VramAllocator -----------------------------------
namespace Common {
DECLARE_SINGLETON(VramAllocator);
}
//#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */
//#define __PSP_DEBUG_PRINT__
#include "backends/platform/psp/trace.h"
void *VramAllocator::allocate(int32 size, bool smallAllocation /* = false */) {
DEBUG_ENTER_FUNC();
assert(size > 0);
byte *lastAddress = smallAllocation ? (byte *)VRAM_SMALL_ADDRESS : (byte *)VRAM_START_ADDRESS;
Common::List<Allocation>::iterator i;
// Find a block that fits, starting from the beginning
for (i = _allocList.begin(); i != _allocList.end(); ++i) {
byte *currAddress = (*i).address;
if (currAddress - lastAddress >= size) // We found a match
break;
if ((*i).getEnd() > lastAddress)
lastAddress = (byte *)(*i).getEnd();
}
if (lastAddress + size > (byte *)VRAM_END_ADDRESS) {
PSP_DEBUG_PRINT("No space for allocation of %d bytes. %d bytes already allocated.\n",
size, _bytesAllocated);
return NULL;
}
_allocList.insert(i, Allocation(lastAddress, size));
_bytesAllocated += size;
PSP_DEBUG_PRINT("Allocated in VRAM, size %u at %p.\n", size, lastAddress);
PSP_DEBUG_PRINT("Total allocated %u, remaining %u.\n", _bytesAllocated, (2 * 1024 * 1024) - _bytesAllocated);
return lastAddress;
}
// Deallocate a block from VRAM
void VramAllocator::deallocate(void *address) {
DEBUG_ENTER_FUNC();
address = (byte *)CACHED(address); // Make sure all addresses are the same
Common::List<Allocation>::iterator i;
// Find the Allocator to deallocate
for (i = _allocList.begin(); i != _allocList.end(); ++i) {
if ((*i).address == address) {
PSP_DEBUG_PRINT("Deallocated address[%p], size[%u]\n", (*i).address, (*i).size);
_bytesAllocated -= (*i).size;
_allocList.erase(i);
return;
}
}
PSP_DEBUG_PRINT("Address[%p] not allocated.\n", address);
}
// Class MasterGuRenderer ----------------------------------------------
void MasterGuRenderer::setupCallbackThread() {
DEBUG_ENTER_FUNC();
// start the thread that updates the display
threadCreateAndStart("DisplayCbThread", PRIORITY_DISPLAY_THREAD, STACK_DISPLAY_THREAD);
}
// this function gets called by PspThread when starting the new thread
void MasterGuRenderer::threadFunction() {
DEBUG_ENTER_FUNC();
// Create the callback. It should always get the pointer to MasterGuRenderer
_callbackId = sceKernelCreateCallback("Display Callback", guCallback, this);
if (_callbackId < 0) {
PSP_ERROR("failed to create display callback\n");
}
PSP_DEBUG_PRINT("created callback. Going to sleep\n");
sceKernelSleepThreadCB(); // sleep until we get a callback
}
// Sleep on the render mutex if the rendering thread hasn't finished its work
//
void MasterGuRenderer::sleepUntilRenderFinished() {
if (!isRenderFinished()) {
_renderSema.take(); // sleep on the semaphore
_renderSema.give();
PSP_DEBUG_PRINT("slept on the rendering semaphore\n");
}
}
// This callback is called when the render is finished. It swaps the buffers
int MasterGuRenderer::guCallback(int, int, void *__this) {
MasterGuRenderer *_this = (MasterGuRenderer *)__this;
sceGuSync(0, 0); // make sure we wait for GU to finish
sceDisplayWaitVblankStartCB(); // wait for v-blank without eating main thread cycles
sceGuSwapBuffers(); // swap the back and front buffers
_this->_renderFinished = true; // Only this thread can set the variable to true
_this->_renderSema.give(); // Release render semaphore
return 0;
}
void MasterGuRenderer::guInit() {
DEBUG_ENTER_FUNC();
sceGuInit();
sceGuStart(0, _displayList);
guProgramDisplayBufferSizes();
sceGuOffset(2048 - (PSP_SCREEN_WIDTH / 2), 2048 - (PSP_SCREEN_HEIGHT / 2));
sceGuViewport(2048, 2048, PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT);
sceGuDepthRange(0xC350, 0x2710);
sceGuDisable(GU_DEPTH_TEST); // We'll use depth buffer area
sceGuDepthMask(GU_TRUE); // Prevent writes to depth buffer
sceGuScissor(0, 0, PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT);
sceGuEnable(GU_SCISSOR_TEST);
sceGuFrontFace(GU_CW);
sceGuEnable(GU_TEXTURE_2D);
sceGuClear(GU_COLOR_BUFFER_BIT | GU_DEPTH_BUFFER_BIT);
sceGuFinish();
sceGuSync(0, 0);
sceDisplayWaitVblankStart();
sceGuDisplay(1);
}
void MasterGuRenderer::guProgramDisplayBufferSizes() {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("Outputbits[%u]\n", GuRenderer::_displayManager->getOutputBitsPerPixel());
switch (GuRenderer::_displayManager->getOutputBitsPerPixel()) {
case 16:
sceGuDrawBuffer(GU_PSM_4444, (void *)0, PSP_BUFFER_WIDTH);
sceGuDispBuffer(PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT, (void *)(PSP_FRAME_SIZE * sizeof(uint16)), PSP_BUFFER_WIDTH);
sceGuDepthBuffer((void *)(PSP_FRAME_SIZE * sizeof(uint16) * 2), PSP_BUFFER_WIDTH);
VramAllocator::instance().allocate(PSP_FRAME_SIZE * sizeof(uint16) * 2);
break;
case 32:
sceGuDrawBuffer(GU_PSM_8888, (void *)0, PSP_BUFFER_WIDTH);
sceGuDispBuffer(PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT, (void *)(PSP_FRAME_SIZE * sizeof(uint32)), PSP_BUFFER_WIDTH);
sceGuDepthBuffer((void *)(PSP_FRAME_SIZE * sizeof(uint32) * 2), PSP_BUFFER_WIDTH);
VramAllocator::instance().allocate(PSP_FRAME_SIZE * sizeof(uint32) * 2);
break;
}
}
// These are GU commands that should always stay the same
inline void MasterGuRenderer::guPreRender() {
DEBUG_ENTER_FUNC();
#ifdef USE_DISPLAY_CALLBACK
_renderSema.take(); // Take the semaphore to prevent writes
// to the palette/screen before we're done
_renderFinished = false; // set to synchronize with callback thread
#endif
#ifdef ENABLE_RENDER_MEASURE
_lastRenderTime = g_system->getMillis();
#endif /* ENABLE_RENDER_MEASURE */
sceGuStart(0, _displayList);
sceGuClearColor(0xFF000000);
sceGuClear(GU_COLOR_BUFFER_BIT);
sceGuAmbientColor(0xFFFFFFFF);
sceGuColor(0xFFFFFFFF);
sceGuTexOffset(0, 0);
sceGuTexFilter(GU_LINEAR, GU_LINEAR);
sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); // Also good enough for all purposes
sceGuAlphaFunc(GU_GREATER, 0, 0xFF); // Also good enough for all purposes
}
inline void MasterGuRenderer::guPostRender() {
DEBUG_ENTER_FUNC();
sceGuFinish();
#ifdef USE_DISPLAY_CALLBACK
if (_callbackId < 0)
PSP_ERROR("bad callbackId[%d]\n", _callbackId);
else
sceKernelNotifyCallback(_callbackId, 0); // notify the callback. Nothing extra to pass
#else
sceGuSync(0, 0);
#ifdef ENABLE_RENDER_MEASURE
uint32 now = g_system->getMillis();
PSP_INFO_PRINT("Render took %d milliseconds\n", now - _lastRenderTime);
#endif /* ENABLE_RENDER_MEASURE */
sceDisplayWaitVblankStart();
sceGuSwapBuffers();
_renderFinished = true;
#endif /* !USE_DISPLAY_CALLBACK */
}
void MasterGuRenderer::guShutDown() {
sceGuTerm();
}
// Class DisplayManager -----------------------------------------------------
DisplayManager::~DisplayManager() {
_masterGuRenderer.guShutDown();
}
void DisplayManager::init() {
DEBUG_ENTER_FUNC();
_displayParams.outputBitsPerPixel = 32; // can be changed to produce 16-bit output
GuRenderer::setDisplayManager(this);
_screen->init();
_overlay->init();
_cursor->init();
_masterGuRenderer.guInit(); // start up the renderer
#ifdef USE_DISPLAY_CALLBACK
_masterGuRenderer.setupCallbackThread();
#endif
// Init overlay since we never change the size
_overlay->deallocate();
_overlay->setBytesPerPixel(sizeof(uint16));
_overlay->setSize(PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT);
_overlay->allocate();
}
void DisplayManager::setSizeAndPixelFormat(uint width, uint height, const Graphics::PixelFormat *format) {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("w[%u], h[%u], pformat[%p]\n", width, height, format);
_screen->deallocate();
_screen->setScummvmPixelFormat(format);
_screen->setSize(width, height);
_screen->allocate();
_cursor->setScreenPaletteScummvmPixelFormat(format);
_displayParams.screenSource.width = width;
_displayParams.screenSource.height = height;
calculateScaleParams();
}
bool DisplayManager::setGraphicsMode(int mode) {
DEBUG_ENTER_FUNC();
_graphicsMode = mode;
calculateScaleParams();
return true;
}
void DisplayManager::calculateScaleParams() {
if (!_displayParams.screenSource.width || !_displayParams.screenSource.height)
return; // we can't calculate anything without these
switch (_graphicsMode) {
case ORIGINAL_RESOLUTION:
// check if we can fit the original resolution inside the screen
if ((_displayParams.screenSource.width <= PSP_SCREEN_WIDTH) &&
(_displayParams.screenSource.height <= PSP_SCREEN_HEIGHT)) {
_displayParams.screenOutput.width = _displayParams.screenSource.width;
_displayParams.screenOutput.height = _displayParams.screenSource.height;
break;
}
// else revert to fit to screen
case FIT_TO_SCREEN: { // maximize the height while keeping aspect ratio
float aspectRatio;
if (ConfMan.getBool("aspect_ratio")) {
aspectRatio = 4.0f / 3.0f;
} else {
aspectRatio = (float)_displayParams.screenSource.width / (float)_displayParams.screenSource.height;
}
_displayParams.screenOutput.height = PSP_SCREEN_HEIGHT; // always full height
_displayParams.screenOutput.width = (uint32)(PSP_SCREEN_HEIGHT * aspectRatio);
if (_displayParams.screenOutput.width > PSP_SCREEN_WIDTH) { // shrink if wider than screen
_displayParams.screenOutput.height = (uint32)(((float)PSP_SCREEN_HEIGHT * (float)PSP_SCREEN_WIDTH) / (float)_displayParams.screenOutput.width);
_displayParams.screenOutput.width = PSP_SCREEN_WIDTH;
}
}
break;
case STRETCH_TO_SCREEN: // we simply stretch to the whole screen
_displayParams.screenOutput.width = PSP_SCREEN_WIDTH;
_displayParams.screenOutput.height = PSP_SCREEN_HEIGHT;
break;
default:
PSP_ERROR("Unsupported graphics mode[%d].\n", _graphicsMode);
}
// calculate scale factors for X and Y
_displayParams.scaleX = ((float)_displayParams.screenOutput.width) / _displayParams.screenSource.width;
_displayParams.scaleY = ((float)_displayParams.screenOutput.height) / _displayParams.screenSource.height;
}
void DisplayManager::waitUntilRenderFinished() {
#ifdef USE_DISPLAY_CALLBACK
_masterGuRenderer.sleepUntilRenderFinished();
#endif /* USE_DISPLAY_CALLBACK */
}
// return true if we really rendered or no dirty. False otherwise
bool DisplayManager::renderAll() {
DEBUG_ENTER_FUNC();
#ifdef USE_DISPLAY_CALLBACK
if (!_masterGuRenderer.isRenderFinished()) {
PSP_DEBUG_PRINT("Callback render not finished.\n");
return false; // didn't render
}
#endif /* USE_DISPLAY_CALLBACK */
// This is cheaper than checking time, so we do it first
// Any one of these being dirty causes everything to draw
if (!_screen->isDirty() &&
!_overlay->isDirty() &&
!_cursor->isDirty() &&
!_keyboard->isDirty() &&
!_imageViewer->isDirty()) {
PSP_DEBUG_PRINT("Nothing dirty\n");
return true; // nothing to render
}
if (!isTimeToUpdate())
return false; // didn't render
PSP_DEBUG_PRINT("dirty: screen[%s], overlay[%s], cursor[%s], keyboard[%s], imageViewer[%s]\n",
_screen->isDirty() ? "true" : "false",
_overlay->isDirty() ? "true" : "false",
_cursor->isDirty() ? "true" : "false",
_keyboard->isDirty() ? "true" : "false",
_imageViewer->isDirty() ? "true" : "false"
);
_masterGuRenderer.guPreRender(); // Set up rendering
_screen->render();
_screen->setClean(); // clean out dirty bit
if (_imageViewer->isVisible())
_imageViewer->render();
_imageViewer->setClean();
if (_overlay->isVisible())
_overlay->render();
_overlay->setClean();
if (_cursor->isVisible())
_cursor->render();
_cursor->setClean();
if (_keyboard->isVisible())
_keyboard->render();
_keyboard->setClean();
_masterGuRenderer.guPostRender();
return true; // rendered successfully
}
inline bool DisplayManager::isTimeToUpdate() {
#define MAX_FPS 60 // was 30
uint32 now = g_system->getMillis();
if (now - _lastUpdateTime < (1000 / MAX_FPS))
return false;
_lastUpdateTime = now;
return true;
}
Common::List<Graphics::PixelFormat> DisplayManager::getSupportedPixelFormats() const {
Common::List<Graphics::PixelFormat> list;
// In order of preference
list.push_back(PSPPixelFormat::convertToScummvmPixelFormat(PSPPixelFormat::Type_5650));
list.push_back(PSPPixelFormat::convertToScummvmPixelFormat(PSPPixelFormat::Type_5551));
list.push_back(PSPPixelFormat::convertToScummvmPixelFormat(PSPPixelFormat::Type_4444));
list.push_back(Graphics::PixelFormat::createFormatCLUT8());
return list;
}

View File

@@ -0,0 +1,167 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PSP_DISPLAY_MAN_H
#define PSP_DISPLAY_MAN_H
#include "backends/platform/psp/thread.h"
#include "common/list.h"
#define UNCACHED(x) ((byte *)(((uint32)(x)) | 0x40000000)) /* make an uncached access */
#define CACHED(x) ((byte *)(((uint32)(x)) & 0xBFFFFFFF)) /* make an uncached access into a cached one */
/**
* Class that allocates memory in the VRAM
*/
class VramAllocator : public Common::Singleton<VramAllocator> {
public:
VramAllocator() : _bytesAllocated(0) {}
void *allocate(int32 size, bool smallAllocation = false); // smallAllocation e.g. palettes
void deallocate(void *pointer);
static inline bool isAddressInVram(void *address) {
if ((uint32)(CACHED(address)) >= VRAM_START_ADDRESS && (uint32)(CACHED(address)) < VRAM_END_ADDRESS)
return true;
return false;
}
private:
/**
* Used to allocate in VRAM
*/
struct Allocation {
byte *address;
uint32 size;
void *getEnd() { return address + size; }
Allocation(void *Address, uint32 Size) : address((byte *)Address), size(Size) {}
Allocation() : address(0), size(0) {}
};
enum {
VRAM_START_ADDRESS = 0x04000000,
VRAM_END_ADDRESS = 0x04200000,
VRAM_SMALL_ADDRESS = VRAM_END_ADDRESS - (4 * 1024) // 4K in the end for small allocations
};
Common::List <Allocation> _allocList; // List of allocations
uint32 _bytesAllocated;
};
/**
* Class used only by DisplayManager to start/stop GU rendering
*/
class MasterGuRenderer : public PspThreadable {
public:
MasterGuRenderer() : _lastRenderTime(0), _renderFinished(true),
_renderSema(1, 1), _callbackId(-1) {}
void guInit();
void guPreRender();
void guPostRender();
void guShutDown();
bool isRenderFinished() { return _renderFinished; }
void sleepUntilRenderFinished();
void setupCallbackThread();
private:
virtual void threadFunction(); // for the display callback thread
static uint32 _displayList[];
uint32 _lastRenderTime; // For measuring rendering time
void guProgramDisplayBufferSizes();
static int guCallback(int, int, void *__this); // for the display callback
bool _renderFinished; // for sync with render callback
PspSemaphore _renderSema; // semaphore for syncing
int _callbackId; // to keep track of render callback
};
class Screen;
class Overlay;
class Cursor;
class PSPKeyboard;
class ImageViewer;
/**
* Class that manages all display clients
*/
class DisplayManager {
public:
enum GraphicsModeID { ///> Possible output formats onscreen
ORIGINAL_RESOLUTION,
FIT_TO_SCREEN,
STRETCH_TO_SCREEN
};
DisplayManager() : _screen(0), _cursor(0), _overlay(0), _keyboard(0),
_imageViewer(0), _lastUpdateTime(0), _graphicsMode(0) {}
~DisplayManager();
void init();
bool renderAll(); // return true if rendered or nothing dirty. False otherwise
void waitUntilRenderFinished();
bool setGraphicsMode(int mode);
int getGraphicsMode() const { return _graphicsMode; }
uint32 getDefaultGraphicsMode() const { return FIT_TO_SCREEN; }
const OSystem::GraphicsMode* getSupportedGraphicsModes() const { return _supportedModes; }
// Setters for pointers
void setScreen(Screen *screen) { _screen = screen; }
void setCursor(Cursor *cursor) { _cursor = cursor; }
void setOverlay(Overlay *overlay) { _overlay = overlay; }
void setKeyboard(PSPKeyboard *keyboard) { _keyboard = keyboard; }
void setImageViewer(ImageViewer *imageViewer) { _imageViewer = imageViewer; }
void setSizeAndPixelFormat(uint width, uint height, const Graphics::PixelFormat *format);
// Getters
float getScaleX() const { return _displayParams.scaleX; }
float getScaleY() const { return _displayParams.scaleY; }
uint32 getOutputWidth() const { return _displayParams.screenOutput.width; }
uint32 getOutputHeight() const { return _displayParams.screenOutput.height; }
uint32 getOutputBitsPerPixel() const { return _displayParams.outputBitsPerPixel; }
Common::List<Graphics::PixelFormat> getSupportedPixelFormats() const;
private:
struct GlobalDisplayParams {
Dimensions screenOutput;
Dimensions screenSource;
float scaleX;
float scaleY;
uint32 outputBitsPerPixel; // How many bits end up on-screen
GlobalDisplayParams() : scaleX(0.0f), scaleY(0.0f), outputBitsPerPixel(0) {}
};
// Pointers to DisplayClients
Screen *_screen;
Cursor *_cursor;
Overlay *_overlay;
PSPKeyboard *_keyboard;
ImageViewer *_imageViewer;
MasterGuRenderer _masterGuRenderer;
uint32 _lastUpdateTime; // For limiting FPS
int _graphicsMode;
GlobalDisplayParams _displayParams;
static const OSystem::GraphicsMode _supportedModes[];
void calculateScaleParams(); // calculates scaling factor
bool isTimeToUpdate(); // should we update the screen now
};
#endif /* PSP_DISPLAY_MAN_H */

View File

@@ -0,0 +1,55 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
//#include "common/scummsys.h"
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <png.h>
#include <sys/socket.h>
//void userWriteFn(png_structp png_ptr, png_bytep data, png_size_t length) {
//}
//void userFlushFn(png_structp png_ptr) {
//}
// Dummy functions are pulled in so that we don't need to build the plugins with certain libs
int dummyFunc() {
// For Broken Sword 2.5
volatile int i;
i = clock();
rename("dummyA", "dummyB");
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_set_write_fn(png_ptr, NULL, NULL, NULL);
png_infop info_ptr;
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
png_destroy_write_struct(&png_ptr, &info_ptr);
// For lua's usage of libc: very heavy usage so it pulls in sockets?
setsockopt(0, 0, 0, NULL, 0);
getsockopt(0, 0, 0, NULL, NULL);
return i;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,320 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Disable symbol overrides so that we can use system headers.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "common/scummsys.h"
#include "common/str.h"
#include "common/stream.h"
#include "common/archive.h"
#include "common/events.h"
#include "common/ptr.h"
#include "gui/message.h"
#include "engines/engine.h"
#include "backends/platform/psp/input.h"
#include "backends/platform/psp/display_manager.h"
#include "backends/platform/psp/display_client.h"
#include "backends/platform/psp/image_viewer.h"
#include "backends/platform/psp/png_loader.h"
#include "backends/platform/psp/thread.h"
static const char *imageName = "psp_image";
#define PSP_SCREEN_HEIGHT 272
#define PSP_SCREEN_WIDTH 480
bool ImageViewer::load(int imageNum) {
if (_init)
unload();
// build string
Common::Path specificImageName(Common::String::format("%s%d.png",imageName, imageNum));
// search for image file
if (!SearchMan.hasFile(specificImageName)) {
PSP_ERROR("file %s not found\n", specificImageName.toString(Common::Path::kNativeSeparator).c_str());
return false;
}
Common::ScopedPtr<Common::SeekableReadStream> file(SearchMan.createReadStreamForMember(specificImageName));
_buffer = new Buffer();
_palette = new Palette();
_renderer = new GuRenderer();
assert(_buffer);
assert(_palette);
assert(_renderer);
// Load a PNG into our buffer and palette. Size it by the actual size of the image
PngLoader image(*file, *_buffer, *_palette, Buffer::kSizeBySourceSize);
PngLoader::Status status = image.allocate(); // allocate the buffers for the file
char error[100];
if (status == PngLoader::BAD_FILE) {
Common::sprintf_s(error, "Cannot display %s. Not a proper PNG file", specificImageName.toString(Common::Path::kNativeSeparator).c_str());
GUI::TimedMessageDialog dialog(Common::U32String(error), 4000);
dialog.runModal();
return false;
} else if (status == PngLoader::OUT_OF_MEMORY) {
Common::sprintf_s(error, "Out of memory loading %s. Try making the image smaller", specificImageName.toString(Common::Path::kNativeSeparator).c_str());
GUI::TimedMessageDialog dialog(Common::U32String(error), 4000);
dialog.runModal();
return false;
}
// try to load the image file
if (!image.load()) {
Common::sprintf_s(error, "Cannot display %s. Not a proper PNG file", specificImageName.toString(Common::Path::kNativeSeparator).c_str());
GUI::TimedMessageDialog dialog(Common::U32String(error), 4000);
dialog.runModal();
return false;
}
setConstantRendererOptions();
setFullScreenImageParams(); // prepare renderer for full screen view
_imageNum = imageNum; // now we can say we displayed this image
_init = true;
return true;
}
void ImageViewer::setConstantRendererOptions() {
_renderer->setBuffer(_buffer);
_renderer->setPalette(_palette);
_renderer->setAlphaBlending(false);
_renderer->setColorTest(false);
_renderer->setUseGlobalScaler(false);
_renderer->setStretch(true);
_renderer->setOffsetInBuffer(0, 0);
_renderer->setDrawWholeBuffer();
}
void ImageViewer::unload() {
_init = false;
delete _buffer;
delete _palette;
delete _renderer;
_buffer = 0;
_palette = 0;
_renderer = 0;
}
void ImageViewer::resetOnEngineDone() {
_imageNum = 0;
}
void ImageViewer::setVisible(bool visible) {
DEBUG_ENTER_FUNC();
if (_visible == visible)
return;
// from here on, we're making the loader visible
if (visible && g_engine) { // we can only run the image viewer when there's an engine
_pauseToken = g_engine->pauseEngine();
load(_imageNum ? _imageNum : 1); // load the 1st image or the current
}
if (visible && _init) { // we managed to load
_visible = true;
setViewerButtons(true);
{ // so dialog goes out of scope, destroying all allocations
GUI::TimedMessageDialog dialog(Common::U32String("Image Viewer"), 1000);
dialog.runModal();
}
runLoop(); // only listen to viewer events
} else { // we were asked to make invisible or failed to load
_visible = false;
unload();
setViewerButtons(false);
if (g_engine && g_engine->isPaused())
_pauseToken.clear();
}
setDirty();
}
// This is the only way we can truly pause the games
// Sad but true.
void ImageViewer::runLoop() {
while (_visible) {
Common::Event event;
PspThread::delayMillis(30);
_inputHandler->getAllInputs(event);
_displayManager->renderAll();
}
}
void ImageViewer::setViewerButtons(bool active) {
_inputHandler->setImageViewerMode(active);
}
void ImageViewer::loadNextImage() {
if (!load(_imageNum+1)) { // try to load the next image
if (!load(_imageNum)) // we failed, so reload the current image
setVisible(false); // just hide
}
setDirty();
}
void ImageViewer::loadLastImage() {
if (_imageNum - 1 > 0) {
if (!load(_imageNum-1))
if (!load(_imageNum))
setVisible(false); // we can't even show the old image so hide
}
setDirty();
}
void ImageViewer::setFullScreenImageParams() {
// we try to fit the image fullscreen at least in one dimension
uint32 width = _buffer->getSourceWidth();
uint32 height = _buffer->getSourceHeight();
_centerX = PSP_SCREEN_WIDTH / 2.0f;
_centerY = PSP_SCREEN_HEIGHT / 2.0f;
// see if we fit width wise
if (PSP_SCREEN_HEIGHT >= (int)((height * PSP_SCREEN_WIDTH) / (float)width)) {
setZoom(PSP_SCREEN_WIDTH / (float)width);
} else {
setZoom(PSP_SCREEN_HEIGHT / (float)height);
}
}
void ImageViewer::render() {
if (_init) {
assert(_buffer);
assert(_renderer);
// move the image slightly. Note that we count on the renderer's timing
switch (_movement) {
case EVENT_MOVE_LEFT:
moveImageX(-_visibleWidth / 100.0f);
break;
case EVENT_MOVE_UP:
moveImageY(-_visibleHeight / 100.0f);
break;
case EVENT_MOVE_RIGHT:
moveImageX(_visibleWidth / 100.0f);
break;
case EVENT_MOVE_DOWN:
moveImageY(_visibleHeight / 100.0f);
break;
default:
break;
}
_renderer->render();
}
}
void ImageViewer::modifyZoom(bool up) {
float factor = _zoomFactor;
if (up)
factor += 0.1f;
else // down
factor -= 0.1f;
setZoom(factor);
}
void ImageViewer::setZoom(float value) {
if (value <= 0.0f) // don't want 0 or negative zoom
return;
_zoomFactor = value;
_renderer->setStretchXY(value, value);
setOffsetParams();
}
void ImageViewer::moveImageX(float val) {
float newVal = _centerX + val;
if (newVal - (_visibleWidth / 2) > PSP_SCREEN_WIDTH - 4 || newVal + (_visibleWidth / 2) < 4)
return;
_centerX = newVal;
setOffsetParams();
}
void ImageViewer::moveImageY(float val) {
float newVal = _centerY + val;
if (newVal - (_visibleHeight / 2) > PSP_SCREEN_HEIGHT - 4 || newVal + (_visibleHeight / 2) < 4)
return;
_centerY = newVal;
setOffsetParams();
}
// Set the renderer with the proper offset on the screen
//
void ImageViewer::setOffsetParams() {
_visibleWidth = _zoomFactor * _buffer->getSourceWidth();
_visibleHeight = _zoomFactor * _buffer->getSourceHeight();
int offsetX = _centerX - (int)(_visibleWidth * 0.5f);
int offsetY = _centerY - (int)(_visibleHeight * 0.5f);
_renderer->setOffsetOnScreen(offsetX, offsetY);
setDirty();
}
// Handler events coming in from the inputHandler
//
void ImageViewer::handleEvent(uint32 event) {
DEBUG_ENTER_FUNC();
switch (event) {
case EVENT_HIDE:
setVisible(false);
break;
case EVENT_SHOW:
setVisible(true);
break;
case EVENT_ZOOM_IN:
modifyZoom(true);
break;
case EVENT_ZOOM_OUT:
modifyZoom(false);
break;
case EVENT_MOVE_LEFT:
case EVENT_MOVE_UP:
case EVENT_MOVE_RIGHT:
case EVENT_MOVE_DOWN:
case EVENT_MOVE_STOP:
_movement = (Event)event;
break;
case EVENT_NEXT_IMAGE:
loadNextImage();
break;
case EVENT_LAST_IMAGE:
loadLastImage();
break;
default:
PSP_ERROR("Unknown event %d\n", event);
break;
}
}

View File

@@ -0,0 +1,104 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PSP_IMAGE_VIEWER_H
#define PSP_IMAGE_VIEWER_H
#include "engines/engine.h" // for PauseToken
class InputHandler;
class ImageViewer : public DisplayClient {
public:
enum Event {
EVENT_NONE = -1,
EVENT_HIDE = 0,
EVENT_SHOW = 1,
EVENT_ZOOM_IN,
EVENT_ZOOM_OUT,
EVENT_MOVE_LEFT,
EVENT_MOVE_UP,
EVENT_MOVE_RIGHT,
EVENT_MOVE_DOWN,
EVENT_MOVE_STOP,
EVENT_NEXT_IMAGE,
EVENT_LAST_IMAGE,
};
private:
Buffer *_buffer;
Palette *_palette;
GuRenderer *_renderer;
bool _visible;
bool _dirty;
bool _init;
uint32 _imageNum; // current image number
float _zoomFactor; // how much we're zooming in/out on the image
float _visibleHeight, _visibleWidth;
float _centerX, _centerY;
Event _movement;
PauseToken _pauseToken;
InputHandler *_inputHandler;
DisplayManager *_displayManager;
void setFullScreenImageParams();
void loadNextImage();
void loadLastImage();
void setViewerButtons(bool active);
void setConstantRendererOptions();
void moveImageX(float val);
void moveImageY(float val);
bool load(int imageNum);
void unload();
void runLoop(); // to get total pausing we have to do our own loop
void setZoom(float value);
void setOffsetParams();
void modifyZoom(bool up); // up or down
void setVisible(bool visible);
public:
ImageViewer() : _buffer(0), _palette(0), _visible(false),
_dirty(false), _init(false), _imageNum(0),
_zoomFactor(0.0f), _visibleHeight(0.0f), _visibleWidth(0.0f),
_centerX(0.0f), _centerY(0.0f), _movement(EVENT_MOVE_STOP),
_inputHandler(0), _displayManager(0) {}
~ImageViewer() { unload(); } // deallocate images
bool load();
void render();
bool isVisible() { return _visible; }
bool isDirty() { return _dirty; }
void setDirty() { _dirty = true; }
void setClean() { if (!_visible) // otherwise we want to keep rendering
_dirty = false;
}
void resetOnEngineDone();
void handleEvent(uint32 event);
// pointer setters
void setInputHandler(InputHandler *inputHandler) { _inputHandler = inputHandler; }
void setDisplayManager(DisplayManager *displayManager) { _displayManager = displayManager; }
};
#endif /* PSP_IMAGE_VIEWER_H */

View File

@@ -0,0 +1,615 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include <pspctrl.h>
#include "gui/message.h"
#include "backends/platform/psp/input.h"
#include "common/config-manager.h"
//#define __PSP_DEBUG_FUNCS__ /* Uncomment for debugging the stack */
//#define __PSP_DEBUG_PRINT__ /* Uncomment for debug prints */
#include "backends/platform/psp/trace.h"
// Defines for working with PSP buttons
#define DOWN(x) ((pad.Buttons & (x)) == (x))
#define UP(x) (!(pad.Buttons & (x)))
#define PSP_DPAD (PSP_CTRL_DOWN|PSP_CTRL_UP|PSP_CTRL_LEFT|PSP_CTRL_RIGHT)
#define PSP_4BUTTONS (PSP_CTRL_CROSS | PSP_CTRL_CIRCLE | PSP_CTRL_TRIANGLE | PSP_CTRL_SQUARE)
#define PSP_TRIGGERS (PSP_CTRL_LTRIGGER | PSP_CTRL_RTRIGGER)
#define PSP_ALL_BUTTONS (PSP_DPAD | PSP_4BUTTONS | PSP_TRIGGERS | PSP_CTRL_START | PSP_CTRL_SELECT)
#define PAD_CHECK_TIME 13 // was 53
Button::Button() {
clear();
}
inline void Button::clear() {
_key = Common::KEYCODE_INVALID;
_ascii = 0;
_flag = 0;
_pspEventDown.clear();
_pspEventUp.clear();
}
inline bool Button::getEvent(Common::Event &event, PspEvent &pspEvent, bool down) {
if (down) {
if (!_pspEventDown.isEmpty())
pspEvent = _pspEventDown;
} else { // up
if (!_pspEventUp.isEmpty())
pspEvent = _pspEventUp;
}
if (_key != Common::KEYCODE_INVALID) {
event.type = down ? Common::EVENT_KEYDOWN : Common::EVENT_KEYUP;
event.kbd.keycode = _key;
event.kbd.ascii = _ascii;
event.kbd.flags |= _flag;
return true;
} else if (_flag) { // handle flag only events
event.type = down ? Common::EVENT_KEYDOWN : Common::EVENT_KEYUP;
event.kbd.flags |= down ? _flag : 0;
return true;
}
return false;
}
void Button::setPspEvent(PspEventType typeDown, uint32 dataDown, PspEventType typeUp, uint32 dataUp) {
_pspEventDown.type = typeDown;
_pspEventDown.data = dataDown;
_pspEventUp.type = typeUp;
_pspEventUp.data = dataUp;
}
// Translates bitfields to our constants
// We put combined bitfields first to make sure we pick up diagonals
const uint32 ButtonPad::_buttonMap[] = {
PSP_CTRL_UP | PSP_CTRL_LEFT,
PSP_CTRL_UP | PSP_CTRL_RIGHT,
PSP_CTRL_DOWN | PSP_CTRL_RIGHT,
PSP_CTRL_DOWN | PSP_CTRL_LEFT,
PSP_CTRL_RIGHT, PSP_CTRL_DOWN, PSP_CTRL_LEFT, PSP_CTRL_UP,
PSP_CTRL_CROSS, PSP_CTRL_CIRCLE, PSP_CTRL_TRIANGLE, PSP_CTRL_SQUARE,
PSP_CTRL_LTRIGGER, PSP_CTRL_RTRIGGER, PSP_CTRL_START, PSP_CTRL_SELECT
};
ButtonPad::ButtonPad() : _prevButtonState(0), _shifted(UNSHIFTED), _padMode(PAD_MODE_NORMAL),
_comboMode(false), _combosEnabled(true) {
for (int i = UNSHIFTED; i < SHIFTED_MODE_LAST; i++)
_buttonsChanged[i] = 0;
clearButtons();
}
void ButtonPad::clearButtons() {
for (int i = BTN_UP_LEFT; i < BTN_LAST; i++) {
_button[i][UNSHIFTED].clear();
_button[i][SHIFTED].clear();
}
}
void ButtonPad::initButtons() {
switch (_padMode) {
case PAD_MODE_NORMAL:
initButtonsNormalMode();
break;
case PAD_MODE_LOL:
initButtonsLolMode();
break;
default:
break;
}
}
void ButtonPad::initButtonsNormalMode() {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("initializing buttons for normal mode\n");
clearButtons();
// Dpad
_button[BTN_UP_LEFT][UNSHIFTED].setKey(Common::KEYCODE_KP7, '7');
_button[BTN_LEFT][SHIFTED].setKey(Common::KEYCODE_KP7, '7'); // same as up_left
_button[BTN_UP][UNSHIFTED].setKey(Common::KEYCODE_KP8, '8');
_button[BTN_UP_RIGHT][UNSHIFTED].setKey(Common::KEYCODE_KP9, '9');
_button[BTN_UP][SHIFTED].setKey(Common::KEYCODE_KP9, '9'); // same as up_right
_button[BTN_LEFT][UNSHIFTED].setKey(Common::KEYCODE_KP4, '4');
_button[BTN_RIGHT][UNSHIFTED].setKey(Common::KEYCODE_KP6, '6');
_button[BTN_DOWN_LEFT][UNSHIFTED].setKey(Common::KEYCODE_KP1, '1');
_button[BTN_DOWN][SHIFTED].setKey(Common::KEYCODE_KP1, '1'); // same as down_left
_button[BTN_DOWN][UNSHIFTED].setKey(Common::KEYCODE_KP2, '2');
_button[BTN_DOWN_RIGHT][UNSHIFTED].setKey(Common::KEYCODE_KP3, '3');
_button[BTN_RIGHT][SHIFTED].setKey(Common::KEYCODE_KP3, '3'); // same as down_right
// Other buttons
_button[BTN_CROSS][UNSHIFTED].setPspEvent(PSP_EVENT_LBUTTON, true, PSP_EVENT_LBUTTON, false);
_button[BTN_CIRCLE][UNSHIFTED].setPspEvent(PSP_EVENT_RBUTTON, true, PSP_EVENT_RBUTTON, false);
_button[BTN_TRIANGLE][UNSHIFTED].setKey(Common::KEYCODE_RETURN, '\r');
_button[BTN_SQUARE][UNSHIFTED].setKey(Common::KEYCODE_PERIOD, '.');
_button[BTN_SQUARE][SHIFTED].setKey(Common::KEYCODE_SPACE, ' ');
_button[BTN_LTRIGGER][UNSHIFTED].setKey(Common::KEYCODE_ESCAPE, 27);
_button[BTN_RTRIGGER][SHIFTED].setPspEvent(PSP_EVENT_SHIFT, true, PSP_EVENT_SHIFT, false);
_button[BTN_RTRIGGER][UNSHIFTED].setPspEvent(PSP_EVENT_SHIFT, true, PSP_EVENT_SHIFT, false);
_button[BTN_RTRIGGER][SHIFTED].setKey(Common::KEYCODE_INVALID, 0, Common::KBD_SHIFT);
_button[BTN_RTRIGGER][UNSHIFTED].setKey(Common::KEYCODE_INVALID, 0, Common::KBD_SHIFT);
_button[BTN_START][SHIFTED].setKey(Common::KEYCODE_F5, Common::ASCII_F5);
_button[BTN_START][UNSHIFTED].setKey(Common::KEYCODE_F5, Common::ASCII_F5, Common::KBD_CTRL);
_button[BTN_SELECT][UNSHIFTED].setPspEvent(PSP_EVENT_SHOW_VIRTUAL_KB, true, PSP_EVENT_NONE, 0);
_button[BTN_SELECT][SHIFTED].setPspEvent(PSP_EVENT_IMAGE_VIEWER, true, PSP_EVENT_NONE, 0);
}
void ButtonPad::initButtonsLolMode() {
DEBUG_ENTER_FUNC();
initButtonsNormalMode(); // set normal button configuration
PSP_DEBUG_PRINT("initializing buttons for LOL mode\n");
// Square is our new shift button
_button[BTN_SQUARE][UNSHIFTED].clear();
_button[BTN_SQUARE][UNSHIFTED].setPspEvent(PSP_EVENT_SHIFT, true, PSP_EVENT_SHIFT, false);
_button[BTN_SQUARE][SHIFTED].clear();
_button[BTN_SQUARE][SHIFTED].setPspEvent(PSP_EVENT_SHIFT, true, PSP_EVENT_SHIFT, false);
// Dpad
_button[BTN_LEFT][UNSHIFTED].clear();
_button[BTN_LEFT][UNSHIFTED].setKey(Common::KEYCODE_KP7, '7');
_button[BTN_LEFT][SHIFTED].clear();
_button[BTN_LEFT][SHIFTED].setKey(Common::KEYCODE_F1, Common::ASCII_F1);
_button[BTN_UP][SHIFTED].clear();
_button[BTN_UP][SHIFTED].setKey(Common::KEYCODE_F2, Common::ASCII_F2);
_button[BTN_RIGHT][UNSHIFTED].clear();
_button[BTN_RIGHT][UNSHIFTED].setKey(Common::KEYCODE_KP9, '9');
_button[BTN_RIGHT][SHIFTED].clear();
_button[BTN_RIGHT][SHIFTED].setKey(Common::KEYCODE_F3, Common::ASCII_F3);
_button[BTN_DOWN][SHIFTED].clear();
_button[BTN_DOWN][SHIFTED].setKey(Common::KEYCODE_F4, Common::ASCII_F4);
// Buttons
_button[BTN_LTRIGGER][UNSHIFTED].clear();
_button[BTN_LTRIGGER][SHIFTED].clear();
_button[BTN_LTRIGGER][UNSHIFTED].setKey(Common::KEYCODE_KP4, '4'); // Triggers turn
_button[BTN_RTRIGGER][UNSHIFTED].clear();
_button[BTN_RTRIGGER][SHIFTED].clear();
_button[BTN_RTRIGGER][UNSHIFTED].setKey(Common::KEYCODE_KP6, '6');
_button[BTN_START][SHIFTED].clear();
_button[BTN_START][SHIFTED].setKey(Common::KEYCODE_ESCAPE, 27);
}
bool ButtonPad::getEvent(Common::Event &event, PspEvent &pspEvent, SceCtrlData &pad) {
DEBUG_ENTER_FUNC();
//PSP_DEBUG_PRINT("buttons[%x]\n", pad.Buttons);
uint32 curButtonState = PSP_ALL_BUTTONS & pad.Buttons; // we only care about these
if (_combosEnabled)
modifyButtonsForCombos(pad); // change buttons for combos
return getEventFromButtonState(event, pspEvent, curButtonState);
}
bool ButtonPad::getEventFromButtonState(Common::Event &event, PspEvent &pspEvent, uint32 buttonState) {
DEBUG_ENTER_FUNC();
_buttonsChanged[_shifted] |= buttonState ^ _prevButtonState; // add any buttons that changed
_prevButtonState = buttonState;
for (int shiftState = UNSHIFTED; shiftState < SHIFTED_MODE_LAST; shiftState++) {
if (_buttonsChanged[shiftState]) { // any button to address?
PSP_DEBUG_PRINT("found changed buttons\n");
ButtonType buttonType = BTN_LAST;
bool buttonDown = false; // normally we release a button (as in when we're in a different shiftmode)
for (int i = BTN_UP_LEFT; i < BTN_LAST; i++) {
uint32 buttonCode = _buttonMap[i];
if ((_buttonsChanged[shiftState] & buttonCode) == buttonCode) { // check for this changed button
buttonType = (ButtonType)i; // we know which button changed
_buttonsChanged[shiftState] &= ~buttonCode; // save the fact that we treated this button
if (shiftState == _shifted)
buttonDown = buttonState & buttonCode ? true : false; // pressed or released?
PSP_DEBUG_PRINT("button[%i] pressed\n", i);
break;
}
}
assert (buttonType < BTN_LAST);
bool haveEvent = _button[buttonType][shiftState].getEvent(event, pspEvent, buttonDown);
if (haveEvent)
PSP_DEBUG_PRINT("have event. key[%d] flag[%x] %s\n", event.kbd.ascii, event.kbd.flags, buttonDown ? "down" : "up");
return haveEvent;
}
}
return false;
}
void ButtonPad::modifyButtonsForCombos(SceCtrlData &pad) {
if (DOWN(PSP_CTRL_RTRIGGER | PSP_CTRL_LTRIGGER)) {
if (!_comboMode) { // we're entering combo mode
PSP_DEBUG_PRINT("entering combo mode\n");
_button[BTN_SQUARE][UNSHIFTED].clear();
_button[BTN_SQUARE][SHIFTED].clear();
_button[BTN_DOWN][SHIFTED].clear();
_button[BTN_DOWN][UNSHIFTED].clear();
_button[BTN_UP][SHIFTED].clear();
_button[BTN_UP][UNSHIFTED].clear();
_button[BTN_SQUARE][UNSHIFTED].setPspEvent(PSP_EVENT_MODE_SWITCH, true, PSP_EVENT_NONE, true);
_button[BTN_SQUARE][SHIFTED].setPspEvent(PSP_EVENT_MODE_SWITCH, true, PSP_EVENT_NONE, true);
_button[BTN_DOWN][UNSHIFTED].setPspEvent(PSP_EVENT_CHANGE_SPEED, false, PSP_EVENT_NONE, true);
_button[BTN_DOWN][SHIFTED].setPspEvent(PSP_EVENT_CHANGE_SPEED, false, PSP_EVENT_NONE, true);
_button[BTN_UP][UNSHIFTED].setPspEvent(PSP_EVENT_CHANGE_SPEED, true, PSP_EVENT_NONE, true);
_button[BTN_UP][SHIFTED].setPspEvent(PSP_EVENT_CHANGE_SPEED, true, PSP_EVENT_NONE, true);
_comboMode = true;
}
} else { // no combo buttons are pressed now
if (_comboMode) { // we have been running in combo mode
initButtons(); // reset the button configuration
_comboMode = false;
}
}
}
bool Nub::getEvent(Common::Event &event, PspEvent &pspEvent, SceCtrlData &pad) {
DEBUG_ENTER_FUNC();
if (_dpadMode) { // Convert the nub to a D-Pad
uint32 buttonState;
translateToDpadState(pad.Lx, pad.Ly, buttonState);
return _buttonPad.getEventFromButtonState(event, pspEvent, buttonState);
}
int32 analogStepX = pad.Lx; // Goes up to 255.
int32 analogStepY = pad.Ly;
analogStepX = modifyNubAxisMotion(analogStepX);
analogStepY = modifyNubAxisMotion(analogStepY);
if (analogStepX == 0)
_hiresX = 0;
if (analogStepY == 0)
_hiresY = 0;
int32 speedFactor = 25;
switch (ConfMan.getInt("kbdmouse_speed")) {
// 0.25 keyboard pointer speed
case 0:
speedFactor = 100;
break;
// 0.5 speed
case 1:
speedFactor = 50;
break;
// 0.75 speed
case 2:
speedFactor = 33;
break;
// 1.0 speed
case 3:
speedFactor = 25;
break;
// 1.25 speed
case 4:
speedFactor = 20;
break;
// 1.5 speed
case 5:
speedFactor = 17;
break;
// 1.75 speed
case 6:
speedFactor = 14;
break;
// 2.0 speed
case 7:
speedFactor = 12;
break;
default:
speedFactor = 25;
}
// the larger the factor, the slower the cursor will move
int32 additionalFactor = 16;
if (_shifted) {
additionalFactor = 192;
}
int32 factor = (speedFactor * additionalFactor) / 25;
// hi-res cumulative analog delta for sub-pixel cursor positioning
_hiresX += (analogStepX * 1024) / factor;
_hiresY += (analogStepY * 1024) / factor;
analogStepX = (_hiresX / 1024);
analogStepY = (_hiresY / 1024);
// keep track of remainder for true sub-pixel cursor position
_hiresX %= 1024;
_hiresY %= 1024;
int32 oldX = _cursor->getX();
int32 oldY = _cursor->getY();
if (analogStepX != 0 || analogStepY != 0) {
PSP_DEBUG_PRINT("raw x[%d], y[%d]\n", analogStepX, analogStepY);
_cursor->increaseXY(analogStepX, 0);
_cursor->increaseXY(0, analogStepY);
int32 newX = _cursor->getX();
int32 newY = _cursor->getY();
if ((oldX != newX) || (oldY != newY)) {
event.type = Common::EVENT_MOUSEMOVE;
event.mouse.x = newX;
event.mouse.y = newY;
PSP_DEBUG_PRINT("Nub event. X[%d], Y[%d]\n", newX, newY);
return true;
}
}
return false;
}
void Nub::translateToDpadState(int dpadX, int dpadY, uint32 &buttonState) {
#define MIN_NUB_POSITION 70
buttonState = 0;
if (dpadX > 127 + MIN_NUB_POSITION)
buttonState |= PSP_CTRL_RIGHT;
else if (dpadX < 127 - MIN_NUB_POSITION)
buttonState |= PSP_CTRL_LEFT;
if (dpadY > 127 + MIN_NUB_POSITION)
buttonState |= PSP_CTRL_DOWN;
else if (dpadY < 127 - MIN_NUB_POSITION)
buttonState |= PSP_CTRL_UP;
}
inline int32 Nub::modifyNubAxisMotion(int32 input) {
DEBUG_ENTER_FUNC();
int MIN_NUB_MOTION = 10 * ConfMan.getInt("joystick_deadzone");
input -= 128; // Center on 0.
if (input < -MIN_NUB_MOTION - 1)
input += MIN_NUB_MOTION + 1; // reduce the velocity
else if (input > MIN_NUB_MOTION)
input -= MIN_NUB_MOTION; // same
else // between these points, dampen the response to 0
input = 0;
if (input != 0) { // scaled deadzone
input = (input * 128)/(128 - MIN_NUB_MOTION);
}
return input;
}
inline bool Nub::isButtonDown() {
if (_dpadMode) // only relevant in dpad mode
return _buttonPad.isButtonDown();
return false;
}
const char *InputHandler::_padModeText[] = {
"Normal Button Mode",
"1st Person RPG Button Mode"
};
void InputHandler::init() {
sceCtrlSetSamplingCycle(0); // set sampling to vsync. n = n usecs
sceCtrlSetSamplingMode(1); // analog
_buttonPad.initButtons();
_nub.init();
}
bool InputHandler::getAllInputs(Common::Event &event) {
DEBUG_ENTER_FUNC();
uint32 time = g_system->getMillis(); // may not be necessary with read
if (time - _lastPadCheckTime < PAD_CHECK_TIME) {
return false;
}
_lastPadCheckTime = time;
SceCtrlData pad;
sceCtrlPeekBufferPositive(&pad, 1); // Peek doesn't sleep. Read sleeps the thread
bool haveEvent;
//memset(&event, 0, sizeof(event));
haveEvent = getEvent(event, pad);
if (haveEvent) {
PSP_DEBUG_PRINT("Have event[%s]. Type[%d]\n", haveEvent ? "true" : "false", event.type);
}
return haveEvent;
}
bool InputHandler::getEvent(Common::Event &event, SceCtrlData &pad) {
DEBUG_ENTER_FUNC();
PspEvent pspEvent;
bool haveEvent = false;
if (_keyboard->isVisible()) {
haveEvent = _keyboard->processInput(event, pspEvent, pad);
} else { // only process buttonpad if keyboard invisible
haveEvent = _buttonPad.getEvent(event, pspEvent, pad);
}
if (!haveEvent && pspEvent.isEmpty())
haveEvent = _nub.getEvent(event, pspEvent, pad);
// handle any pending PSP events
if (!haveEvent && pspEvent.isEmpty()) {
if (!_pendingPspEvent.isEmpty()) {
pspEvent = _pendingPspEvent;
_pendingPspEvent.clear();
}
}
// handle any PSP events we might have
if (!pspEvent.isEmpty())
haveEvent |= handlePspEvent(event, pspEvent); // overrides any event we might have
return haveEvent;
}
bool InputHandler::handlePspEvent(Common::Event &event, PspEvent &pspEvent) {
bool haveEvent = false;
PSP_DEBUG_PRINT("have pspEvent[%d] data[%d]\n", pspEvent.type, pspEvent.data);
switch (pspEvent.type) {
case PSP_EVENT_SHIFT:
handleShiftEvent((ShiftMode)pspEvent.data);
break;
case PSP_EVENT_SHOW_VIRTUAL_KB:
_keyboard->setVisible((bool)pspEvent.data);
if ((pspEvent.data && _keyboard->isVisible()) || !pspEvent.data) // don't change mode if keyboard didn't load
_nub.setDpadMode((bool)pspEvent.data); // set nub to keypad/regular mode
break;
case PSP_EVENT_LBUTTON:
haveEvent = true;
if (pspEvent.data) // down
handleMouseEvent(event, Common::EVENT_LBUTTONDOWN, "LButtonDown");
else
handleMouseEvent(event, Common::EVENT_LBUTTONUP, "LButtonUp");
break;
case PSP_EVENT_RBUTTON:
haveEvent = true;
if (pspEvent.data) // down
handleMouseEvent(event, Common::EVENT_RBUTTONDOWN, "RButtonDown");
else
handleMouseEvent(event, Common::EVENT_RBUTTONUP, "RButtonUp");
break;
case PSP_EVENT_MODE_SWITCH:
handleModeSwitchEvent();
break;
/*case PSP_EVENT_CHANGE_SPEED:
handleSpeedChange(pspEvent.data);
break;*/
case PSP_EVENT_IMAGE_VIEWER:
_imageViewer->handleEvent(pspEvent.data);
break;
case PSP_EVENT_IMAGE_VIEWER_SET_BUTTONS:
setImageViewerMode(pspEvent.data);
break;
default:
PSP_ERROR("Unhandled PSP Event[%d]\n", pspEvent.type);
break;
}
return haveEvent;
}
void InputHandler::handleMouseEvent(Common::Event &event, Common::EventType type, const char *string) {
event.type = type;
event.mouse.x = _cursor->getX();
event.mouse.y = _cursor->getY();
PSP_DEBUG_PRINT("%s event, x[%d], y[%d]\n", string, event.mouse.x, event.mouse.y);
}
void InputHandler::handleShiftEvent(ShiftMode shifted) {
_buttonPad.setShifted(shifted);
_nub.setShifted(shifted);
}
void InputHandler::handleModeSwitchEvent() {
// check if we can't switch modes right now
if (_buttonPad.isButtonDown() || _nub.isButtonDown()) { // can't switch yet
PSP_DEBUG_PRINT("postponing mode switch event\n");
_pendingPspEvent.type = PSP_EVENT_MODE_SWITCH; // queue it to be done later
} else { // we can switch
PSP_DEBUG_PRINT("mode switch event\n");
_padMode = (PspPadMode)(_padMode + 1);
if (_padMode >= PAD_MODE_LAST)
_padMode = PAD_MODE_NORMAL;
GUI::TimedMessageDialog dialog(Common::U32String(_padModeText[_padMode]), 1500);
dialog.runModal();
_buttonPad.setPadMode(_padMode);
_buttonPad.initButtons();
}
}
/*
void InputHandler::handleSpeedChange(bool up) {
char *dialogMsg;
if (up) {
dialogMsg = "
GUI::TimedMessageDialog dialog(_padModeText[_padMode], 1500);
dialog.runModal();
}*/
void InputHandler::setImageViewerMode(bool active) {
if (_buttonPad.isButtonDown() || _nub.isButtonDown()) { // can't switch yet
PSP_DEBUG_PRINT("postponing image viewer on event\n");
_pendingPspEvent.type = PSP_EVENT_IMAGE_VIEWER_SET_BUTTONS; // queue it to be done later
_pendingPspEvent.data = active;
} else if (active) {
_nub.setDpadMode(true);
_buttonPad.enableCombos(false); // disable combos
setButtonsForImageViewer();
} else { // deactivate
_nub.setDpadMode(false);
_nub.init();
_buttonPad.enableCombos(true); // re-enable combos
_buttonPad.initButtons();
}
}
void InputHandler::setButtonsForImageViewer() {
DEBUG_ENTER_FUNC();
// Dpad
_buttonPad.clearButtons();
_buttonPad.getButton(ButtonPad::BTN_UP, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_ZOOM_IN,
PSP_EVENT_NONE, false);
_buttonPad.getButton(ButtonPad::BTN_DOWN, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_ZOOM_OUT,
PSP_EVENT_NONE, false);
_buttonPad.getButton(ButtonPad::BTN_LEFT, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_LAST_IMAGE,
PSP_EVENT_NONE, false);
_buttonPad.getButton(ButtonPad::BTN_RIGHT, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_NEXT_IMAGE,
PSP_EVENT_NONE, false);
_buttonPad.getButton(ButtonPad::BTN_LTRIGGER, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_HIDE,
PSP_EVENT_NONE, false);
_buttonPad.getButton(ButtonPad::BTN_RTRIGGER, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_HIDE,
PSP_EVENT_NONE, false);
_buttonPad.getButton(ButtonPad::BTN_START, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_HIDE,
PSP_EVENT_NONE, false);
_buttonPad.getButton(ButtonPad::BTN_SELECT, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_HIDE,
PSP_EVENT_NONE, false);
//Nub
_nub.getPad().clearButtons();
_nub.getPad().getButton(ButtonPad::BTN_UP, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_MOVE_UP,
PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_MOVE_STOP);
_nub.getPad().getButton(ButtonPad::BTN_DOWN, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_MOVE_DOWN,
PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_MOVE_STOP);
_nub.getPad().getButton(ButtonPad::BTN_LEFT, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_MOVE_LEFT,
PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_MOVE_STOP);
_nub.getPad().getButton(ButtonPad::BTN_RIGHT, UNSHIFTED).setPspEvent(PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_MOVE_RIGHT,
PSP_EVENT_IMAGE_VIEWER, ImageViewer::EVENT_MOVE_STOP);
}

View File

@@ -0,0 +1,200 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PSP_INPUT_H
#define PSP_INPUT_H
#include "common/scummsys.h"
#include "common/events.h"
#include "backends/platform/psp/pspkeyboard.h"
#include "backends/platform/psp/cursor.h"
#include "backends/platform/psp/image_viewer.h"
#include <pspctrl.h>
enum PspEventType {
PSP_EVENT_NONE = 0,
PSP_EVENT_SHIFT,
PSP_EVENT_SHOW_VIRTUAL_KB,
PSP_EVENT_LBUTTON,
PSP_EVENT_RBUTTON,
PSP_EVENT_MODE_SWITCH,
PSP_EVENT_CHANGE_SPEED,
PSP_EVENT_IMAGE_VIEWER,
PSP_EVENT_IMAGE_VIEWER_SET_BUTTONS,
PSP_EVENT_LAST
};
struct PspEvent {
PspEventType type;
uint32 data;
PspEvent() { clear(); }
void clear() {
type = PSP_EVENT_NONE;
data = 0;
}
bool isEmpty() { return type == PSP_EVENT_NONE; }
};
enum PspPadMode {
PAD_MODE_NORMAL,
PAD_MODE_LOL,
PAD_MODE_LAST
};
enum ShiftMode {
UNSHIFTED = 0,
SHIFTED = 1,
SHIFTED_MODE_LAST
};
class Button {
private:
Common::KeyCode _key;
uint32 _ascii;
uint32 _flag;
PspEvent _pspEventDown; // event when we press
PspEvent _pspEventUp; // event when we release
public:
Button();
void clear();
bool getEvent(Common::Event &event, PspEvent &pspEvent, bool buttonDown);
void setKey(Common::KeyCode key, uint32 ascii = 0, uint32 flag = 0) { _key = key; _ascii = ascii; _flag = flag; }
void setPspEvent(PspEventType typeDown, uint32 dataDown, PspEventType typeUp, uint32 dataUp);
};
class ButtonPad {
public:
enum ButtonType { // must match the buttonMap
BTN_UP_LEFT,
BTN_UP_RIGHT,
BTN_DOWN_RIGHT,
BTN_DOWN_LEFT,
BTN_RIGHT,
BTN_DOWN,
BTN_LEFT,
BTN_UP,
BTN_CROSS,
BTN_CIRCLE,
BTN_TRIANGLE,
BTN_SQUARE,
BTN_LTRIGGER,
BTN_RTRIGGER,
BTN_START,
BTN_SELECT,
BTN_LAST
};
private:
Button _button[BTN_LAST][SHIFTED_MODE_LAST];
uint32 _buttonsChanged[SHIFTED_MODE_LAST]; // normal and shifted
uint32 _prevButtonState;
ShiftMode _shifted;
PspPadMode _padMode;
bool _comboMode; // are we in the middle of combos
bool _combosEnabled; // can we do combos
static const uint32 _buttonMap[]; // maps the buttons to their values
void initButtonsNormalMode();
void initButtonsLolMode();
void modifyButtonsForCombos(SceCtrlData &pad);
public:
ButtonPad();
void initButtons(); // set the buttons to the mode that's selected
void clearButtons(); // empty the buttons of all events
bool getEvent(Common::Event &event, PspEvent &pspEvent, SceCtrlData &pad);
bool getEventFromButtonState(Common::Event &event, PspEvent &pspEvent, uint32 buttonState);
void setShifted(ShiftMode shifted) { _shifted = shifted; }
void setPadMode(PspPadMode mode) { _padMode = mode; }
bool isButtonDown() { return _prevButtonState; }
void enableCombos(bool value) { _combosEnabled = value; }
Button &getButton(ButtonType type, ShiftMode mode) { return _button[type][mode]; }
};
class Nub {
private:
Cursor *_cursor; // to enable changing/getting cursor position
ShiftMode _shifted;
int32 _hiresX; // to accumulate analog X over many frames
int32 _hiresY; // to accumulate analog Y over many frames
bool _dpadMode;
ButtonPad _buttonPad; // private buttonpad for dpad mode
int32 modifyNubAxisMotion(int32 input);
void translateToDpadState(int dpadX, int dpadY, uint32 &buttonState); // convert nub data to dpad data
public:
Nub() : _shifted(UNSHIFTED), _dpadMode(false), _hiresX(0), _hiresY(0) { }
void init() { _buttonPad.initButtons(); }
void setCursor(Cursor *cursor) { _cursor = cursor; }
// setters
void setDpadMode(bool active) { _dpadMode = active; }
void setShifted(ShiftMode shifted) { _shifted = shifted; }
// getters
bool isButtonDown();
bool getEvent(Common::Event &event, PspEvent &pspEvent, SceCtrlData &pad);
ButtonPad &getPad() { return _buttonPad; }
};
class InputHandler {
public:
InputHandler() : _keyboard(0), _cursor(0), _imageViewer(0), _padMode(PAD_MODE_NORMAL),
_lastPadCheckTime(0) {}
// pointer setters
void setKeyboard(PSPKeyboard *keyboard) { _keyboard = keyboard; }
void setCursor(Cursor *cursor) { _cursor = cursor; _nub.setCursor(cursor); }
void setImageViewer(ImageViewer *imageViewer) { _imageViewer = imageViewer; }
void init();
bool getAllInputs(Common::Event &event);
void setImageViewerMode(bool active);
private:
Nub _nub;
ButtonPad _buttonPad;
// Pointers to relevant other classes
PSPKeyboard *_keyboard;
Cursor *_cursor;
ImageViewer *_imageViewer;
PspPadMode _padMode; // whice mode we're in
PspEvent _pendingPspEvent; // an event that can't be handled yet
uint32 _lastPadCheckTime;
static const char *_padModeText[];
bool getEvent(Common::Event &event, SceCtrlData &pad);
bool handlePspEvent(Common::Event &event, PspEvent &pspEvent);
void handleMouseEvent(Common::Event &event, Common::EventType type, const char *string);
void handleShiftEvent(ShiftMode shifted);
void handleModeSwitchEvent();
void setButtonsForImageViewer();
};
#endif /* PSP_INPUT_H */

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,9 @@
Readme file for keyboard
-------------------------
This keyboard started out as the Danzeff keyboard, but was modified by Bluddy for efficiency and reduced memory consumption.
Image Modification
------------------
The .xcf files are GIMP 2.0 files which were used to build the images. From these files, 32-bit PNG files were created (GIMP doesn't support 8 or 4 bit files properly i.e. letter anti-aliasing becomes messed up).
The program pngquant.exe which is part of libpng was then used to reduce the image from 32-bits to 4-bits using the command "pngquant 256 pngname.png".
Finally, for the selection images only (*_s.png), Irfanview (www.irfanview.com) was used to save the images and select the transparent background color. GIMP was not used for this purpose as its default transparent color is black. (Irfanview also doesn't fully support alpha bits for 8 or 4-bit PNG images, but it was useful in setting the background to transparent in this particular case.)

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,403 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/scummsys.h"
#include "common/singleton.h"
#include "backends/platform/psp/psppixelformat.h"
#define PSP_INCLUDE_SWAP
#include "backends/platform/psp/memory.h"
// Class Copier --------------------------------------------------------------------------
//#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */
//#define __PSP_DEBUG_PRINT__
#include "backends/platform/psp/trace.h"
//#define TEST_MEMORY_COPY
void PspMemory::copy(byte *dst, const byte *src, uint32 bytes) {
DEBUG_ENTER_FUNC();
#ifdef TEST_MEMORY_COPY
uint32 debugBytes = bytes;
const byte *debugDst = dst, *debugSrc = src;
#endif
PSP_DEBUG_PRINT("copy(): dst[%p], src[%p], bytes[%d]\n", dst, src, bytes);
// align the destination pointer first
uint32 prefixDst = (((uint32)dst) & 0x3);
if (prefixDst) {
prefixDst = 4 - prefixDst; // prefix only if we have address % 4 != 0
PSP_DEBUG_PRINT("prefixDst[%d]\n", prefixDst);
bytes -= prefixDst; // remember we assume bytes >= 4
if (bytes < MIN_AMOUNT_FOR_COMPLEX_COPY) { // check if it's worthwhile to continue
copy8(dst, src, bytes + prefixDst);
#ifdef TEST_MEMORY_COPY
testCopy(debugDst, debugSrc, debugBytes);
#endif
return;
}
while (prefixDst--) {
*dst++ = *src++;
}
}
// check the source pointer alignment now
uint32 alignSrc = (((uint32)src) & 0x3);
if (alignSrc) { // we'll need to realign our reads
copy32Misaligned((uint32 *)dst, src, bytes, alignSrc);
} else {
copy32Aligned((uint32 *)dst, (const uint32 *)src, bytes);
}
#ifdef TEST_MEMORY_COPY
testCopy(debugDst, debugSrc, debugBytes);
#endif
}
void PspMemory::copy32Aligned(uint32 *dst32, const uint32 *src32, uint32 bytes) {
PSP_DEBUG_PRINT("copy32Aligned(): dst32[%p], src32[%p], bytes[%d]\n", dst32, src32, bytes);
int words8 = bytes >> 5;
// try blocks of 8 words at a time
if (words8) {
while (words8--) {
uint32 a, b, c, d;
a = src32[0];
b = src32[1];
c = src32[2];
d = src32[3];
dst32[0] = a;
dst32[1] = b;
dst32[2] = c;
dst32[3] = d;
a = src32[4];
b = src32[5];
c = src32[6];
d = src32[7];
dst32[4] = a;
dst32[5] = b;
dst32[6] = c;
dst32[7] = d;
dst32 += 8;
src32 += 8;
}
}
int words4 = (bytes & 0x1F) >> 4;
// try blocks of 4 words at a time
if (words4) {
uint32 a, b, c, d;
a = src32[0];
b = src32[1];
c = src32[2];
d = src32[3];
dst32[0] = a;
dst32[1] = b;
dst32[2] = c;
dst32[3] = d;
dst32 += 4;
src32 += 4;
}
int bytesLeft = (bytes & 0xF); // only look at bytes left after we did the above
int wordsLeft = bytesLeft >> 2;
// now just do single words
while (wordsLeft) {
*dst32++ = *src32++;
wordsLeft--;
}
bytesLeft = bytes & 0x3; // get remaining bytes
PSP_DEBUG_PRINT("bytesLeft[%d]\n", bytesLeft);
byte *dst = (byte *)dst32;
const byte *src = (const byte *)src32;
while (bytesLeft--) {
*dst++ = *src++;
}
}
// More challenging -- need to shift
// Assume dst is aligned
void PspMemory::copy32Misaligned(uint32 *dst32, const byte *src, uint32 bytes, uint32 alignSrc) {
PSP_DEBUG_PRINT("copy32Misaligned: dst32[%p], src[%p], bytes[%d], alignSrc[%d]\n", dst32, src, bytes, alignSrc);
uint32 *src32 = (uint32 *)(((uint32)src) & 0xFFFFFFFC); // remove misalignment
uint32 shiftValue, lastShiftValue;
switch (alignSrc) {
case 1:
shiftValue = 8;
lastShiftValue = 24;
break;
case 2:
shiftValue = 16;
lastShiftValue = 16;
break;
default: /* 3 */
shiftValue = 24;
lastShiftValue = 8;
break;
}
uint32 dstWord, srcWord;
// Try to do groups of 4 words
uint32 words4 = bytes >> 4;
srcWord = *src32; // preload 1st word so we read ahead
for (; words4; words4--) {
dstWord = srcWord >> shiftValue;
srcWord = src32[1];
dstWord |= srcWord << lastShiftValue;
dst32[0] = dstWord;
dstWord = srcWord >> shiftValue;
srcWord = src32[2];
dstWord |= srcWord << lastShiftValue;
dst32[1] = dstWord;
dstWord = srcWord >> shiftValue;
srcWord = src32[3];
dstWord |= srcWord << lastShiftValue;
dst32[2] = dstWord;
dstWord = srcWord >> shiftValue;
srcWord = src32[4];
dstWord |= srcWord << lastShiftValue;
dst32[3] = dstWord;
src32 += 4;
dst32 += 4;
}
uint32 words = (bytes & 0xF) >> 2; // now get remaining words
// we read one word ahead of what we write
// setup the first read
for (; words ;words--) {
dstWord = srcWord >> shiftValue;
srcWord = src32[1]; // we still go one ahead
src32++;
dstWord |= srcWord << lastShiftValue;
*dst32++ = dstWord;
}
uint32 bytesLeft = bytes & 3; // and remaining bytes
if (bytesLeft) {
byte *dst8 = (byte *)dst32;
byte *src8 = ((byte *)src32) + ((uint32)src & 0x3); // get exact location we should be at
for(; bytesLeft; bytesLeft--) {
*dst8++ = *src8++;
}
}
}
void PspMemory::testCopy(const byte *debugDst, const byte *debugSrc, uint32 debugBytes) {
bool mismatch = false;
PSP_INFO_PRINT("testing fastCopy...");
for (uint32 i = 0; i < debugBytes; i++) {
if (debugDst[i] != debugSrc[i]) {
if (!mismatch) {
PSP_INFO_PRINT("**** mismatch in copy! ****\n");
PSP_INFO_PRINT("dst[%p], src[%p], bytes[%u]\n", debugDst, debugSrc, debugBytes);
mismatch = true;
}
PSP_INFO_PRINT("[%d]%x!=%x ", i, debugSrc[i], debugDst[i]);
}
}
if (mismatch) {
PSP_INFO_PRINT("\n");
} else {
PSP_INFO_PRINT("ok\n");
}
}
//
// used to swap red and blue
void PspMemorySwap::swap(uint16 *dst16, const uint16 *src16, uint32 bytes, PSPPixelFormat &format) {
DEBUG_ENTER_FUNC();
#ifdef TEST_MEMORY_COPY
uint32 debugBytes = bytes;
const uint16 *debugDst = dst16, *debugSrc = src16;
#endif
// align the destination pointer first
uint32 prefixDst = (((uint32)dst16) & 0x3); // for swap, we can only have 2 or 0 as our prefix
if (prefixDst) {
bytes -= prefixDst; // remember we assume bytes > 4
*dst16++ = format.swapRedBlue16(*src16++);
if (bytes < MIN_AMOUNT_FOR_COMPLEX_COPY) { // check if it's worthwhile to continue
swap16(dst16, src16, bytes, format);
#ifdef TEST_MEMORY_COPY
testSwap(debugDst, debugSrc, debugBytes, format);
#endif
return;
}
}
// check the source pointer alignment now
uint32 alignSrc = (((uint32)src16) & 0x3);
if (alignSrc) { // we'll need to realign our reads
PSP_DEBUG_PRINT("misaligned copy of %u bytes from %p to %p\n", bytes, src16, dst16);
swap32Misaligned((uint32 *)dst16, src16, bytes, format);
} else {
swap32Aligned((uint32 *)dst16, (const uint32 *)src16, bytes, format);
}
#ifdef TEST_MEMORY_COPY
testSwap(debugDst, debugSrc, debugBytes, format);
#endif
}
void PspMemorySwap::testSwap(const uint16 *debugDst, const uint16 *debugSrc, uint32 debugBytes, PSPPixelFormat &format) {
bool mismatch = false;
PSP_INFO_PRINT("testing fastSwap...");
uint32 shorts = debugBytes >> 1;
for (uint32 i = 0; i < shorts; i++) {
if (debugDst[i] != format.swapRedBlue16(debugSrc[i])) {
if (!mismatch) {
PSP_INFO_PRINT("**** mismatch in swap! ****\n");
PSP_INFO_PRINT("dst[%p], src[%p], bytes[%u]\n", debugDst, debugSrc, debugBytes);
mismatch = true;
}
PSP_INFO_PRINT("[%d]%x!=%x ", i<<1, format.swapRedBlue16(debugSrc[i]), debugDst[i]);
}
}
if (mismatch) {
PSP_INFO_PRINT("\n");
} else {
PSP_INFO_PRINT("ok\n");
}
}
void PspMemorySwap::swap32Aligned(uint32 *dst32, const uint32 *src32, uint32 bytes, PSPPixelFormat &format) {
DEBUG_ENTER_FUNC();
int words4 = bytes >> 4;
// try blocks of 4 words at a time
while (words4--) {
uint32 a, b, c, d;
a = format.swapRedBlue32(src32[0]);
b = format.swapRedBlue32(src32[1]);
c = format.swapRedBlue32(src32[2]);
d = format.swapRedBlue32(src32[3]);
dst32[0] = a;
dst32[1] = b;
dst32[2] = c;
dst32[3] = d;
dst32 += 4;
src32 += 4;
}
uint32 bytesLeft = bytes & 0xF;
uint32 words = bytesLeft >> 2;
// now just do words
while (words--) {
*dst32++ = format.swapRedBlue32(*src32++);
}
bytesLeft = bytes & 0x3;
if (bytesLeft) { // for swap, can only be 1 short left
*((uint16 *)dst32) = format.swapRedBlue16(*((const uint16 *)src32));
}
}
// More challenging -- need to shift
// We assume dst is aligned
void PspMemorySwap::swap32Misaligned(uint32 *dst32, const uint16 *src16, uint32 bytes, PSPPixelFormat &format) {
DEBUG_ENTER_FUNC();
const uint32 shiftValue = 16;
uint32 *src32 = (uint32 *)(((uint32)src16) & 0xFFFFFFFC); // remove misalignment
// Try to do groups of 4 words
uint32 words4 = bytes >> 4;
uint32 srcWord = src32[0]; // preload
while (words4--) {
uint32 dstWord = srcWord >> shiftValue;
srcWord = src32[1];
dstWord |= srcWord << shiftValue;
dst32[0] = format.swapRedBlue32(dstWord);
dstWord = srcWord >> shiftValue;
srcWord = src32[2];
dstWord |= srcWord << shiftValue;
dst32[1] = format.swapRedBlue32(dstWord);
dstWord = srcWord >> shiftValue;
srcWord = src32[3];
dstWord |= srcWord << shiftValue;
dst32[2] = format.swapRedBlue32(dstWord);
dstWord = srcWord >> shiftValue;
srcWord = src32[4];
dstWord |= srcWord << shiftValue;
dst32[3] = format.swapRedBlue32(dstWord);
src32 += 4;
dst32 += 4;
}
uint32 words = (bytes & 0xF) >> 2;
// we read one word ahead of what we write
// setup the first read
if (words) {
//srcWord = *src32++; // don't need this. already loaded
src32++; // we already have the value loaded in
while (words--) {
uint32 dstWord = srcWord >> shiftValue;
srcWord = *src32++;
dstWord |= srcWord << shiftValue;
*dst32++ = format.swapRedBlue32(dstWord);
}
}
uint32 bytesLeft = bytes & 3;
if (bytesLeft) { // for swap, can only be 1 short left
*((uint16 *)dst32) = format.swapRedBlue16((uint16)(srcWord >> shiftValue));
}
}

View File

@@ -0,0 +1,123 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PSP_MEMORY_H
#define PSP_MEMORY_H
#define MIN_AMOUNT_FOR_COMPLEX_COPY 8
#define MIN_AMOUNT_FOR_MISALIGNED_COPY 8
//#define __PSP_DEBUG_PRINT__
//#include "backends/platform/psp/trace.h"
// These instructions don't generate automatically but are faster then copying byte by byte
inline void lwl_copy(byte *dst, const byte *src) {
uint32 data;
asm volatile ("lwr %0,0(%1)\n\t"
"lwl %0,3(%1)\n\t"
: "=&r" (data) : "r" (src), "m" (*src));
asm volatile ("swr %1,0(%2)\n\t"
"swl %1,3(%2)\n\t"
: "=m" (*dst) : "r" (data), "r" (dst));
}
/**
* Class that does memory copying and swapping if needed
*/
class PspMemory {
private:
static void testCopy(const byte *debugDst, const byte *debugSrc, uint32 debugBytes);
static void copy(byte *dst, const byte *src, uint32 bytes);
static void copy32Aligned(uint32 *dst32, const uint32 *src32, uint32 bytes);
static void copy32Misaligned(uint32 *dst32, const byte *src, uint32 bytes, uint32 alignSrc);
static inline void copy8(byte *dst, const byte *src, int32 bytes) {
//PSP_DEBUG_PRINT("copy8 called with dst[%p], src[%p], bytes[%d]\n", dst, src, bytes);
uint32 words = bytes >> 2;
for (; words; words--) {
lwl_copy(dst, src);
dst += 4;
src += 4;
}
uint32 bytesLeft = bytes & 0x3;
for (; bytesLeft; bytesLeft--) {
*dst++ = *src++;
}
}
public:
// This is the interface to the outside world
static void *fastCopy(void *dstv, const void *srcv, int32 bytes) {
byte *dst = (byte *)dstv;
const byte *src = (const byte *)srcv;
if (bytes < MIN_AMOUNT_FOR_COMPLEX_COPY) {
copy8(dst, src, bytes);
} else { // go to more powerful copy
copy(dst, src, bytes);
}
return dstv;
}
};
inline void *psp_memcpy(void *dst, const void *src, int32 bytes) {
return PspMemory::fastCopy(dst, src, bytes);
}
#endif /* PSP_MEMORY_H */
#if defined(PSP_INCLUDE_SWAP) && !defined(PSP_MEMORY_SWAP_H)
#define PSP_MEMORY_SWAP_H
//#include "backends/platform/psp/psppixelformat.h"
class PspMemorySwap {
private:
static void testSwap(const uint16 *debugDst, const uint16 *debugSrc, uint32 debugBytes, PSPPixelFormat &format);
static void swap(uint16 *dst16, const uint16 *src16, uint32 bytes, PSPPixelFormat &format);
static void swap32Aligned(uint32 *dst32, const uint32 *src32, uint32 bytes, PSPPixelFormat &format);
static void swap32Misaligned(uint32 *dst32, const uint16 *src16, uint32 bytes, PSPPixelFormat &format);
// For swapping, we know that we have multiples of 16 bits
static void swap16(uint16 *dst16, const uint16 *src16, uint32 bytes, PSPPixelFormat &format) {
PSP_DEBUG_PRINT("swap16 called with dst16[%p], src16[%p], bytes[%d]\n", dst16, src16, bytes);
uint32 shorts = bytes >> 1;
while (shorts--) {
*dst16++ = format.swapRedBlue16(*src16++);
}
}
public:
static void fastSwap(byte *dst, const byte *src, uint32 bytes, PSPPixelFormat &format) {
if (bytes < MIN_AMOUNT_FOR_COMPLEX_COPY * 2) {
swap16((uint16 *)dst, (const uint16 *)src, bytes, format);
} else { // go to more powerful copy
swap((uint16 *)dst, (const uint16 *)src, bytes, format);
}
}
};
#endif /* PSP_INCLUDE_SWAP */

View File

@@ -0,0 +1,27 @@
MODULE := backends/platform/psp
MODULE_OBJS := powerman.o \
psp_main.o \
osys_psp.o \
psppixelformat.o \
memory.o \
display_manager.o \
display_client.o \
default_display_client.o \
input.o \
cursor.o \
trace.o \
pspkeyboard.o \
audio.o \
thread.o \
rtc.o \
mp3.o \
png_loader.o \
image_viewer.o \
tests.o \
dummy.o
# We don't use rules.mk but rather manually update OBJS and MODULE_DIRS.
MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS))
OBJS := $(MODULE_OBJS) $(OBJS)
MODULE_DIRS += $(sort $(dir $(MODULE_OBJS)))

View File

@@ -0,0 +1,516 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Disable printf override in common/forbidden.h to avoid
// clashes with pspdebug.h from the PSP SDK.
// That header file uses
// __attribute__((format(printf,1,2)));
// which gets messed up by our override mechanism; this could
// be avoided by either changing the PSP SDK to use the equally
// legal and valid
// __attribute__((format(__printf__,1,2)));
// or by refining our printf override to use a varadic macro
// (which then wouldn't be portable, though).
// Anyway, for now we just disable the printf override globally
// for the PSP port
#define FORBIDDEN_SYMBOL_EXCEPTION_printf
#include "common/debug.h"
#include "common/stream.h"
#include "common/util.h"
#include "common/singleton.h"
#include "common/mutex.h"
#include "common/textconsole.h"
#include "audio/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
// Arranged in groups of 3 (layers), starting with MPEG-1 and ending with MPEG 2.5
static uint32 mp3SamplesPerFrame[9] = {384, 1152, 1152, 384, 1152, 576, 384, 1152, 576};
// The numbering below doesn't correspond to the way they are in the header
enum {
MPEG_VER1 = 0,
MPEG_VER1_HEADER = 0x3,
MPEG_VER2 = 1,
MPEG_VER2_HEADER = 0x2,
MPEG_VER2_5 = 2,
MPEG_VER2_5_HEADER = 0x0
};
#define HEADER_GET_MPEG_VERSION(x) ((((x)[1])>>3) & 0x3)
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("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("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. ME cannot start.\n");
_decoderFail = true;
return false;
}
}
PSP_DEBUG_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){ // TODO: how do we unload?
/* 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, 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
findValidHeader(); // get a first header so we can read basic stuff
_sampleRate = _header.samplerate; // copy it before it gets destroyed
_stereo = (MAD_NCHANNELS(&_header) == 2);
while (_state != MP3_STATE_EOS)
findValidHeader(); // get a first header so we can read basic stuff
_length = Timestamp(mad_timer_count(_totalTime, MAD_UNITS_MILLISECONDS), getRate());
deinitStream();
_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
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
}
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(); // search for next valid header
while (_state == MP3_STATE_READY) { // not a real 'while'. Just for easy flow
_stream.error = MAD_ERROR_NONE;
uint32 frame_size = _stream.next_frame - _stream.this_frame;
updatePcmLength(); // Retrieve the number of PCM samples.
// We seem to change this, so it needs to be dynamic
PSP_DEBUG_PRINT("MP3 frame size[%d]. pcmLength[%d]\n", frame_size, _pcmLength);
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] = _pcmLength * 2; // x2 for stereo, though this one's not so important
// 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_INFO_PRINT("failed to decode MP3 data in ME. sceAudiocodecDecode returned 0x%x\n", ret);
}
#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
_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;
}
inline void Mp3PspStream::updatePcmLength() {
uint32 mpegVer = HEADER_GET_MPEG_VERSION(_stream.this_frame); // sadly, MAD can't do this for us
PSP_DEBUG_PRINT("mpeg ver[%x]\n", mpegVer);
switch (mpegVer) {
case MPEG_VER1_HEADER:
mpegVer = MPEG_VER1;
break;
case MPEG_VER2_HEADER:
mpegVer = MPEG_VER2;
break;
case MPEG_VER2_5_HEADER:
mpegVer = MPEG_VER2_5;
break;
default:
PSP_ERROR("Unknown MPEG version %x\n", mpegVer);
break;
}
PSP_DEBUG_PRINT("layer[%d]\n", _header.layer);
_pcmLength = mp3SamplesPerFrame[(mpegVer * 3) + _header.layer - 1];
}
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);
// Important to release and re-init the ME
releaseStreamME();
initStreamME();
// Check if we need to rewind
if (_state != MP3_STATE_READY || mad_timer_compare(destination, _totalTime) < 0) {
initStream();
}
// 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
}
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

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

@@ -0,0 +1,119 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SOUND_MP3_PSP_H
#define SOUND_MP3_PSP_H
#include "common/ptr.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 1152 * 2 /* x2 for stereo */
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::DisposablePtr<Common::SeekableReadStream> _inStream;
uint32 _pcmLength; // how many pcm samples we have for this type of file (x2 this for stereo)
uint _posInFrame; // position in frame
State _state; // what state the stream is in
Timestamp _length;
uint32 _sampleRate;
bool _stereo;
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();
void updatePcmLength();
// 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 _stereo; }
int getRate() const { return _sampleRate; }
bool seek(const Timestamp &where);
Timestamp getLength() const { return _length; }
};
} // End of namespace Audio
#endif // #ifndef SOUND_MP3_PSP_H

View File

@@ -0,0 +1,442 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include <pspuser.h>
#include <pspgu.h>
#include <pspdisplay.h>
#include <time.h>
#include <zlib.h>
#include "common/config-manager.h"
#include "common/events.h"
#include "common/scummsys.h"
#include "backends/platform/psp/psppixelformat.h"
#include "backends/platform/psp/osys_psp.h"
#include "backends/platform/psp/powerman.h"
#include "backends/platform/psp/rtc.h"
#include "backends/saves/default/default-saves.h"
#include "backends/timer/psp/timer.h"
#include "graphics/surface.h"
#include "audio/mixer_intern.h"
//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
//#define __PSP_DEBUG_PRINT__ /* For debug printouts */
#include "backends/platform/psp/trace.h"
#define SAMPLES_PER_SEC 44100
OSystem_PSP::~OSystem_PSP() {}
#define PSP_SCREEN_WIDTH 480
#define PSP_SCREEN_HEIGHT 272
#define PSP_DEFAULT_SAVE_PATH "ms0:/scummvm_savegames"
void OSystem_PSP::initBackend() {
DEBUG_ENTER_FUNC();
ConfMan.registerDefault("aspect_ratio", false);
ConfMan.registerDefault("gfx_mode", "Fit to Screen");
ConfMan.registerDefault("kbdmouse_speed", 3);
ConfMan.registerDefault("joystick_deadzone", 3);
// Instantiate real time clock
PspRtc::instance();
_cursor.enableCursorPalette(false);
_cursor.setXY(PSP_SCREEN_WIDTH >> 1, PSP_SCREEN_HEIGHT >> 1); // Mouse in the middle of the screen
// Set pointers for display manager
_displayManager.setCursor(&_cursor);
_displayManager.setScreen(&_screen);
_displayManager.setOverlay(&_overlay);
_displayManager.setKeyboard(&_keyboard);
_displayManager.setImageViewer(&_imageViewer);
_displayManager.init();
// Set pointers for input handler
_inputHandler.setCursor(&_cursor);
_inputHandler.setKeyboard(&_keyboard);
_inputHandler.setImageViewer(&_imageViewer);
_inputHandler.init();
// Set pointers for image viewer
_imageViewer.setInputHandler(&_inputHandler);
_imageViewer.setDisplayManager(&_displayManager);
_savefileManager = new DefaultSaveFileManager(PSP_DEFAULT_SAVE_PATH);
_timerManager = new PspTimerManager();
PSP_DEBUG_PRINT("calling keyboard.load()\n");
_keyboard.load(); // Load virtual keyboard files into memory
setupMixer();
EventsBaseBackend::initBackend();
}
// Let's us know an engine
void OSystem_PSP::engineDone() {
// for now, all we need is to reset the image number on the viewer
_imageViewer.resetOnEngineDone();
}
bool OSystem_PSP::hasFeature(Feature f) {
return (f == kFeatureOverlaySupportsAlpha || f == kFeatureCursorPalette || f == kFeatureCursorAlpha ||
f == kFeatureKbdMouseSpeed || f == kFeatureJoystickDeadzone);
}
void OSystem_PSP::setFeatureState(Feature f, bool enable) {
if (f == kFeatureCursorPalette) {
_pendingUpdate = false;
_cursor.enableCursorPalette(enable);
}
}
bool OSystem_PSP::getFeatureState(Feature f) {
if (f == kFeatureCursorPalette) {
return _cursor.isCursorPaletteEnabled();
}
return false;
}
const OSystem::GraphicsMode* OSystem_PSP::getSupportedGraphicsModes() const {
return _displayManager.getSupportedGraphicsModes();
}
int OSystem_PSP::getDefaultGraphicsMode() const {
DEBUG_ENTER_FUNC();
return _displayManager.getDefaultGraphicsMode();
}
bool OSystem_PSP::setGraphicsMode(int mode, uint flags) {
DEBUG_ENTER_FUNC();
_displayManager.waitUntilRenderFinished();
_pendingUpdate = false;
return _displayManager.setGraphicsMode(mode);
}
int OSystem_PSP::getGraphicsMode() const {
DEBUG_ENTER_FUNC();
return _displayManager.getGraphicsMode();
}
#ifdef USE_RGB_COLOR
Graphics::PixelFormat OSystem_PSP::getScreenFormat() const {
return _screen.getScummvmPixelFormat();
}
Common::List<Graphics::PixelFormat> OSystem_PSP::getSupportedFormats() const {
return _displayManager.getSupportedPixelFormats();
}
#endif
void OSystem_PSP::initSize(uint width, uint height, const Graphics::PixelFormat *format) {
DEBUG_ENTER_FUNC();
_displayManager.waitUntilRenderFinished();
_pendingUpdate = false;
_displayManager.setSizeAndPixelFormat(width, height, format);
_cursor.setVisible(false);
_cursor.setLimits(_screen.getWidth(), _screen.getHeight());
}
int16 OSystem_PSP::getWidth() {
DEBUG_ENTER_FUNC();
return (int16)_screen.getWidth();
}
int16 OSystem_PSP::getHeight() {
DEBUG_ENTER_FUNC();
return (int16)_screen.getHeight();
}
void OSystem_PSP::setPalette(const byte *colors, uint start, uint num) {
DEBUG_ENTER_FUNC();
_displayManager.waitUntilRenderFinished();
_pendingUpdate = false;
_screen.setPartialPalette(colors, start, num);
_cursor.setScreenPalette(colors, start, num);
_cursor.clearKeyColor();
}
void OSystem_PSP::setCursorPalette(const byte *colors, uint start, uint num) {
DEBUG_ENTER_FUNC();
_displayManager.waitUntilRenderFinished();
_pendingUpdate = false;
_cursor.setCursorPalette(colors, start, num);
_cursor.enableCursorPalette(true);
_cursor.clearKeyColor(); // Do we need this?
}
void OSystem_PSP::copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) {
DEBUG_ENTER_FUNC();
_displayManager.waitUntilRenderFinished();
_pendingUpdate = false;
_screen.copyFromRect((const byte *)buf, pitch, x, y, w, h);
}
Graphics::Surface *OSystem_PSP::lockScreen() {
DEBUG_ENTER_FUNC();
_displayManager.waitUntilRenderFinished();
_pendingUpdate = false;
return _screen.lockAndGetForEditing();
}
void OSystem_PSP::unlockScreen() {
DEBUG_ENTER_FUNC();
_pendingUpdate = false;
// The screen is always completely updated anyway, so we don't have to force a full update here.
_screen.unlock();
}
void OSystem_PSP::updateScreen() {
DEBUG_ENTER_FUNC();
_pendingUpdate = !_displayManager.renderAll(); // if we didn't update, we have a pending update
}
void OSystem_PSP::setShakePos(int shakeXOffset, int shakeYOffset) {
DEBUG_ENTER_FUNC();
_displayManager.waitUntilRenderFinished();
_pendingUpdate = false;
_screen.setShakePos(shakeXOffset, shakeYOffset);
}
void OSystem_PSP::showOverlay(bool inGUI) {
DEBUG_ENTER_FUNC();
_pendingUpdate = false;
_overlay.setVisible(true);
if (inGUI) {
_cursor.setLimits(_overlay.getWidth(), _overlay.getHeight());
_cursor.useGlobalScaler(false); // mouse with overlay is 1:1
}
}
void OSystem_PSP::hideOverlay() {
DEBUG_ENTER_FUNC();
_pendingUpdate = false;
_overlay.setVisible(false);
_cursor.setLimits(_screen.getWidth(), _screen.getHeight());
_cursor.useGlobalScaler(true); // mouse needs to be scaled with screen
}
bool OSystem_PSP::isOverlayVisible() const {
DEBUG_ENTER_FUNC();
return _overlay.isVisible();
}
void OSystem_PSP::clearOverlay() {
DEBUG_ENTER_FUNC();
_displayManager.waitUntilRenderFinished();
_pendingUpdate = false;
_overlay.clearBuffer();
}
void OSystem_PSP::grabOverlay(Graphics::Surface &surface) {
DEBUG_ENTER_FUNC();
assert(surface.w >= _overlay.getWidth());
assert(surface.h >= _overlay.getHeight());
assert(surface.format.bytesPerPixel == 2);
_overlay.copyToArray(surface.getPixels(), surface.pitch);
}
void OSystem_PSP::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) {
DEBUG_ENTER_FUNC();
_displayManager.waitUntilRenderFinished();
_pendingUpdate = false;
_overlay.copyFromRect(buf, pitch, x, y, w, h);
}
int16 OSystem_PSP::getOverlayWidth() const {
return (int16)_overlay.getWidth();
}
int16 OSystem_PSP::getOverlayHeight() const {
return (int16)_overlay.getHeight();
}
void OSystem_PSP::grabPalette(byte *colors, uint start, uint num) const {
DEBUG_ENTER_FUNC();
_screen.getPartialPalette(colors, start, num);
}
bool OSystem_PSP::showMouse(bool v) {
DEBUG_ENTER_FUNC();
_pendingUpdate = false;
PSP_DEBUG_PRINT("%s\n", v ? "true" : "false");
bool last = _cursor.isVisible();
_cursor.setVisible(v);
return last;
}
void OSystem_PSP::warpMouse(int x, int y) {
DEBUG_ENTER_FUNC();
_displayManager.waitUntilRenderFinished();
_pendingUpdate = false;
_cursor.setXY(x, y);
}
void OSystem_PSP::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
DEBUG_ENTER_FUNC();
if (mask)
PSP_DEBUG_PRINT("OSystem_PSP::setMouseCursor: Masks are not supported");
_displayManager.waitUntilRenderFinished();
_pendingUpdate = false;
PSP_DEBUG_PRINT("pbuf[%p], w[%u], h[%u], hotspot:X[%d], Y[%d], keycolor[%d], scale[%d], pformat[%p]\n", buf, w, h, hotspotX, hotspotY, keycolor, !dontScale, format);
if (format) {
PSP_DEBUG_PRINT("format: bpp[%d], rLoss[%d], gLoss[%d], bLoss[%d], aLoss[%d], rShift[%d], gShift[%d], bShift[%d], aShift[%d]\n", format->bytesPerPixel, format->rLoss, format->gLoss, format->bLoss, format->aLoss, format->rShift, format->gShift, format->bShift, format->aShift);
}
_cursor.setKeyColor(keycolor);
// TODO: The old target scale was saved but never used. Should the new
// "do not scale" logic be implemented?
//_cursor.setCursorTargetScale(cursorTargetScale);
_cursor.setSizeAndScummvmPixelFormat(w, h, format);
_cursor.setHotspot(hotspotX, hotspotY);
_cursor.clearKeyColor();
_cursor.copyFromArray((const byte *)buf);
}
bool OSystem_PSP::pollEvent(Common::Event &event) {
// If we're polling for events, we should check for pausing the engine
// Pausing the engine is a necessary fix for games that use the timer for music synchronization
// recovering many hours later causes the game to crash. We're polling without mutexes since it's not critical to
// get it right now.
PowerMan.pollPauseEngine();
// A hack:
// Check if we have a pending update that we missed for some reason (FPS throttling for example)
// Time between event polls is usually 5-10ms, so waiting for 4 calls before checking to update the screen should be fine
if (_pendingUpdate) {
_pendingUpdateCounter++;
if (_pendingUpdateCounter >= 4) {
PSP_DEBUG_PRINT("servicing pending update\n");
updateScreen();
if (!_pendingUpdate) // we handled the update
_pendingUpdateCounter = 0;
}
} else
_pendingUpdateCounter = 0; // reset the counter, no pending
return _inputHandler.getAllInputs(event);
}
uint32 OSystem_PSP::getMillis(bool skipRecord) {
return PspRtc::instance().getMillis();
}
void OSystem_PSP::delayMillis(uint msecs) {
PspThread::delayMillis(msecs);
}
Common::MutexInternal *OSystem_PSP::createMutex(void) {
return new PspMutex(true); // start with a full mutex
}
void OSystem_PSP::mixCallback(void *sys, byte *samples, int len) {
OSystem_PSP *this_ = (OSystem_PSP *)sys;
assert(this_);
if (this_->_mixer)
this_->_mixer->mixCallback(samples, len);
}
void OSystem_PSP::setupMixer(void) {
// Determine the desired output sampling frequency.
uint32 samplesPerSec = 0;
if (ConfMan.hasKey("output_rate"))
samplesPerSec = ConfMan.getInt("output_rate");
if (samplesPerSec <= 0)
samplesPerSec = SAMPLES_PER_SEC;
// Determine the sample buffer size. We want it to store enough data for
// at least 1/16th of a second (though at most 8192 samples). Note
// that it must be a power of two. So e.g. at 22050 Hz, we request a
// sample buffer size of 2048.
uint32 samples = 8192;
while (samples * 16 > samplesPerSec * 2)
samples >>= 1;
assert(!_mixer);
if (!_audio.open(samplesPerSec, 2, samples, mixCallback, this)) {
PSP_ERROR("failed to open audio\n");
return;
}
samplesPerSec = _audio.getFrequency(); // may have been changed by audio system
_mixer = new Audio::MixerImpl(samplesPerSec);
assert(_mixer);
_mixer->setReady(true);
_audio.unpause();
}
void OSystem_PSP::quit() {
_audio.close();
sceKernelExitGame();
}
void OSystem_PSP::logMessage(LogMessageType::Type type, const char *message) {
FILE *output = 0;
if (type == LogMessageType::kInfo || type == LogMessageType::kDebug)
output = stdout;
else
output = stderr;
fputs(message, output);
fflush(output);
if (type == LogMessageType::kError)
PspDebugTrace(false, "%s", message); // write to file
}
void OSystem_PSP::getTimeAndDate(TimeDate &td, bool skipRecord) const {
time_t curTime = time(0);
struct tm t = *localtime(&curTime);
td.tm_sec = t.tm_sec;
td.tm_min = t.tm_min;
td.tm_hour = t.tm_hour;
td.tm_mday = t.tm_mday;
td.tm_mon = t.tm_mon;
td.tm_year = t.tm_year;
td.tm_wday = t.tm_wday;
}
Common::Path OSystem_PSP::getDefaultConfigFileName() {
return "ms0:/scummvm.ini";
}

View File

@@ -0,0 +1,147 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef OSYS_PSP_H
#define OSYS_PSP_H
#include "common/scummsys.h"
#include "graphics/surface.h"
#include "graphics/paletteman.h"
#include "audio/mixer_intern.h"
#include "backends/base-backend.h"
#include "backends/fs/psp/psp-fs-factory.h"
#include "backends/platform/psp/display_client.h"
#include "backends/platform/psp/default_display_client.h"
#include "backends/platform/psp/cursor.h"
#include "backends/platform/psp/pspkeyboard.h"
#include "backends/platform/psp/image_viewer.h"
#include "backends/platform/psp/display_manager.h"
#include "backends/platform/psp/input.h"
#include "backends/platform/psp/audio.h"
#include "backends/platform/psp/thread.h"
class OSystem_PSP : public EventsBaseBackend, public PaletteManager {
private:
Audio::MixerImpl *_mixer;
bool _pendingUpdate; // save an update we couldn't perform
uint32 _pendingUpdateCounter; // prevent checking for pending update too often, in a cheap way
// All needed sub-members
Screen _screen;
Overlay _overlay;
Cursor _cursor;
DisplayManager _displayManager;
PSPKeyboard _keyboard;
InputHandler _inputHandler;
PspAudio _audio;
ImageViewer _imageViewer;
public:
OSystem_PSP() : _mixer(0), _pendingUpdate(false), _pendingUpdateCounter(0) {}
~OSystem_PSP();
static OSystem *instance();
void initBackend();
// Feature related
bool hasFeature(Feature f);
void setFeatureState(Feature f, bool enable);
bool getFeatureState(Feature f);
// Graphics related
const GraphicsMode *getSupportedGraphicsModes() const;
int getDefaultGraphicsMode() const;
bool setGraphicsMode(int mode, uint flags);
int getGraphicsMode() const;
#ifdef USE_RGB_COLOR
virtual Graphics::PixelFormat getScreenFormat() const;
virtual Common::List<Graphics::PixelFormat> getSupportedFormats() const;
#endif
// Screen size
void initSize(uint width, uint height, const Graphics::PixelFormat *format);
int16 getWidth();
int16 getHeight();
// Palette related
PaletteManager *getPaletteManager() { return this; }
protected:
// PaletteManager API
void setPalette(const byte *colors, uint start, uint num);
void grabPalette(byte *colors, uint start, uint num) const;
public:
void setCursorPalette(const byte *colors, uint start, uint num);
// Screen related
void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h);
Graphics::Surface *lockScreen();
void unlockScreen();
void updateScreen();
void setShakePos(int shakeXOffset, int shakeYOffset);
// Overlay related
void showOverlay(bool inGUI);
void hideOverlay();
bool isOverlayVisible() const;
void clearOverlay();
void grabOverlay(Graphics::Surface &surface);
void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h);
int16 getOverlayHeight() const;
int16 getOverlayWidth() const;
Graphics::PixelFormat getOverlayFormat() const { return Graphics::PixelFormat(2, 4, 4, 4, 4, 0, 4, 8, 12); }
// Mouse related
bool showMouse(bool visible);
void warpMouse(int x, int y);
void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask);
// Events and input
bool pollEvent(Common::Event &event);
bool processInput(Common::Event &event);
// Time
uint32 getMillis(bool skipRecord = false);
void delayMillis(uint msecs);
// Mutex
Common::MutexInternal *createMutex(void);
// Sound
static void mixCallback(void *sys, byte *samples, int len);
void setupMixer(void);
Audio::Mixer *getMixer() { return _mixer; }
// Misc
FilesystemFactory *getFilesystemFactory() { return &PSPFilesystemFactory::instance(); }
void getTimeAndDate(TimeDate &td, bool skipRecord = false) const;
virtual void engineDone();
void quit();
void logMessage(LogMessageType::Type type, const char *message);
virtual Common::Path getDefaultConfigFileName();
};
#endif /* OSYS_PSP_H */

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

View File

@@ -0,0 +1,221 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Disable symbol overrides so that we can use system headers.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "common/scummsys.h"
#include "common/stream.h"
#include "backends/platform/psp/psppixelformat.h"
#include "backends/platform/psp/display_client.h"
#include "backends/platform/psp/png_loader.h"
//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
//#define __PSP_DEBUG_PRINT__ /* For debug printouts */
#include "backends/platform/psp/trace.h"
PngLoader::~PngLoader() {
if (!_pngPtr) {
return;
}
png_destroy_read_struct(&_pngPtr, &_infoPtr, nullptr);
}
PngLoader::Status PngLoader::allocate() {
DEBUG_ENTER_FUNC();
if (!findImageDimensions()) {
PSP_ERROR("failed to get image dimensions\n");
return BAD_FILE;
}
_buffer->setSize(_width, _height, _sizeBy);
uint32 bitsPerPixel = _bitDepth;
if (_paletteSize) { // 8 or 4-bit image
if (bitsPerPixel == 4) {
_buffer->setPixelFormat(PSPPixelFormat::Type_Palette_4bit);
_palette->setPixelFormats(PSPPixelFormat::Type_4444, PSPPixelFormat::Type_Palette_4bit);
_paletteSize = 16; // round up
} else if (bitsPerPixel == 8) { // 8-bit image
_buffer->setPixelFormat(PSPPixelFormat::Type_Palette_8bit);
_palette->setPixelFormats(PSPPixelFormat::Type_4444, PSPPixelFormat::Type_Palette_8bit);
_paletteSize = 256; // round up
} else {
PSP_ERROR("too many bits per pixel[%d] for a palette\n", bitsPerPixel);
return BAD_FILE;
}
} else { // 32-bit image
_buffer->setPixelFormat(PSPPixelFormat::Type_8888);
}
if (!_buffer->allocate()) {
PSP_ERROR("failed to allocate buffer\n");
return OUT_OF_MEMORY;
}
if (_buffer->hasPalette() && !_palette->allocate()) {
PSP_ERROR("failed to allocate palette\n");
return OUT_OF_MEMORY;
}
return OK;
}
bool PngLoader::load() {
DEBUG_ENTER_FUNC();
// Try to really load the image
if (!loadImageIntoBuffer()) {
PSP_DEBUG_PRINT("failed to load image\n");
return false;
}
PSP_DEBUG_PRINT("succeeded in loading image\n");
if (_bitDepth == 4) // 4-bit
_buffer->flipNibbles(); // required because of PNG 4-bit format
return true;
}
void PngLoader::warningFn(png_structp png_ptr, png_const_charp warning_msg) {
// ignore PNG warnings
PSP_ERROR("Got PNG warning: %s\n", warning_msg);
}
void PngLoader::errorFn(png_structp png_ptr, png_const_charp error_msg) {
// ignore PNG warnings
PSP_ERROR("Got PNG error: %s\n", error_msg);
abort();
}
// Read function for png library to be able to read from our SeekableReadStream
//
void PngLoader::libReadFunc(png_structp pngPtr, png_bytep data, png_size_t length) {
Common::SeekableReadStream &file = *(Common::SeekableReadStream *)png_get_io_ptr(pngPtr);
uint32 ret = file.read(data, length);
assert(ret == length);
}
bool PngLoader::basicImageLoad() {
DEBUG_ENTER_FUNC();
_pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!_pngPtr)
return false;
png_set_error_fn(_pngPtr, (png_voidp) nullptr, (png_error_ptr) errorFn, warningFn);
_infoPtr = png_create_info_struct(_pngPtr);
if (!_infoPtr) {
return false;
}
// Set the png lib to use our read function
png_set_read_fn(_pngPtr, &_file, libReadFunc);
png_read_info(_pngPtr, _infoPtr);
int interlaceType;
png_get_IHDR(_pngPtr, _infoPtr, (png_uint_32 *)&_width, (png_uint_32 *)&_height, &_bitDepth,
&_colorType, &interlaceType, nullptr, nullptr);
_channels = png_get_channels(_pngPtr, _infoPtr);
if (_colorType & PNG_COLOR_MASK_PALETTE) {
int paletteSize = 0;
png_colorp palettePtr = nullptr;
png_uint_32 ret = png_get_PLTE(_pngPtr, _infoPtr, &palettePtr, &paletteSize);
assert(ret == PNG_INFO_PLTE);
_paletteSize = paletteSize;
}
return true;
}
/* Get the width and height of a png image */
bool PngLoader::findImageDimensions() {
DEBUG_ENTER_FUNC();
bool status = basicImageLoad();
PSP_DEBUG_PRINT("width[%d], height[%d], paletteSize[%d], bitDepth[%d], channels[%d], rowBytes[%d]\n", _width, _height, _paletteSize, _bitDepth, _channels, png_get_rowbytes(_pngPtr, _infoPtr));
return status;
}
//
// Load a texture from a png image
//
bool PngLoader::loadImageIntoBuffer() {
DEBUG_ENTER_FUNC();
// Everything has already been set up in allocate
assert(_pngPtr);
png_set_strip_16(_pngPtr); // Strip off 16 bit channels in case they occur
if (_paletteSize) {
// Copy the palette
png_colorp srcPal;
int numPalette;
png_get_PLTE(_pngPtr, _infoPtr, &srcPal, &numPalette);
png_bytep transAlpha;
int numTrans;
png_color_16p transColor;
png_get_tRNS(_pngPtr, _infoPtr, &transAlpha, &numTrans, &transColor);
for (int i = 0; i < numPalette; i++) {
unsigned char alphaVal = (i < numTrans) ? transAlpha[i] : 0xFF; // Load alpha if it's there
_palette->setSingleColorRGBA(i, srcPal->red, srcPal->green, srcPal->blue, alphaVal);
srcPal++;
}
} else { // Not a palettized image
if (_colorType == PNG_COLOR_TYPE_GRAY && _bitDepth < 8)
png_set_expand_gray_1_2_4_to_8(_pngPtr); // Round up grayscale images
if (png_get_valid(_pngPtr, _infoPtr, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(_pngPtr); // Convert trans channel to alpha for 32 bits
png_set_add_alpha(_pngPtr, 0xff, PNG_FILLER_AFTER); // Filler for alpha if none exists
}
uint32 rowBytes = png_get_rowbytes(_pngPtr, _infoPtr);
// there seems to be a bug in libpng where it doesn't increase the rowbytes or the
// channel even after we add the alpha channel
if (_channels == 3 && (rowBytes / _width) == 3) {
_channels = 4;
rowBytes = _width * _channels;
}
PSP_DEBUG_PRINT("rowBytes[%d], channels[%d]\n", rowBytes, _channels);
unsigned char *line = (unsigned char*) malloc(rowBytes);
if (!line) {
PSP_ERROR("Couldn't allocate line\n");
return false;
}
for (size_t y = 0; y < _height; y++) {
png_read_row(_pngPtr, line, nullptr);
_buffer->copyFromRect(line, rowBytes, 0, y, _width, 1); // Copy into buffer
}
free(line);
png_read_end(_pngPtr, _infoPtr);
return true;
}

View File

@@ -0,0 +1,72 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PSP_PNG_IMAGE_H
#define PSP_PNG_IMAGE_H
#include <png.h>
class PngLoader {
private:
bool basicImageLoad(); // common operation
bool findImageDimensions(); // find dimensions of a given PNG file
bool loadImageIntoBuffer();
static void warningFn(png_structp png_ptr, png_const_charp warning_msg);
static void errorFn(png_structp png_ptr, png_const_charp warning_msg);
static void libReadFunc(png_structp pngPtr, png_bytep data, png_size_t length);
Common::SeekableReadStream &_file;
Buffer *_buffer;
Palette *_palette;
uint32 _width;
uint32 _height;
uint32 _paletteSize;
Buffer::HowToSize _sizeBy;
// PNG lib values
int _bitDepth;
png_structp _pngPtr;
png_infop _infoPtr;
int _colorType;
uint32 _channels;
public:
enum Status {
OK,
OUT_OF_MEMORY,
BAD_FILE
};
PngLoader(Common::SeekableReadStream &file, Buffer &buffer, Palette &palette,
Buffer::HowToSize sizeBy = Buffer::kSizeByTextureSize) :
_file(file), _buffer(&buffer), _palette(&palette),
_width(0), _height(0), _paletteSize(0),
_bitDepth(0), _sizeBy(sizeBy), _pngPtr(0),
_infoPtr(0), _colorType(0), _channels(0) {}
~PngLoader();
PngLoader::Status allocate();
bool load();
};
#endif /* PSP_PNG_IMAGE_H */

View File

@@ -0,0 +1,60 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PORTDEFS_H
#define PORTDEFS_H
// FIXME: This file is only used when building using the file
// backends/platform/psp/Makefile, but not when building using configure
// && make. So either -DNONSTANDARD_PORT needs to be added to the PSP
// configure rules, or it should be removed from the aforementioned
// Makefile.
#include <malloc.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stddef.h>
#include <assert.h>
#include <ctype.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <new>
#include <limits>
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspdisplay.h>
#include <pspctrl.h>
#include "trace.h"
//#define BREAKPOINT asm("break\n")
//#define printf pspDebugScreenPrintf
#endif /* PORTDEFS_H */

View File

@@ -0,0 +1,339 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include <psppower.h>
#include <pspthreadman.h>
#include "backends/platform/psp/powerman.h"
#include "engines/engine.h"
//#define __PSP_DEBUG_FUNCS__ /* can put this locally too */
//#define __PSP_DEBUG_PRINT__
#include "backends/platform/psp/trace.h"
namespace Common {
DECLARE_SINGLETON(PowerManager);
}
// Function to debug the Power Manager (we have no output to screen)
inline void PowerManager::debugPM() {
PSP_DEBUG_PRINT("PM status[%d]. Listcount[%d]. CriticalCount[%d]. ThreadId[%x]. Error[%d]\n",
_PMStatus, _listCounter, _criticalCounter, sceKernelGetThreadId(), _error);
}
/*******************************************
*
* Constructor
*
********************************************/
PowerManager::PowerManager() : _pauseFlag(false), _pauseFlagOld(false), _pauseClientState(UNPAUSED),
_suspendFlag(false), _flagMutex(true), _listMutex(true),
_criticalCounter(0), _listCounter(0), _error(0), _PMStatus(kInitDone) {}
/*******************************************
*
* Function to register to be notified when suspend/resume time comes
*
********************************************/
bool PowerManager::registerForSuspend(Suspendable *item) {
DEBUG_ENTER_FUNC();
// Register in list
debugPM();
_listMutex.lock();
_suspendList.push_front(item);
_listCounter++;
_listMutex.unlock();
debugPM();
return true;
}
/*******************************************
*
* Function to unregister to be notified when suspend/resume time comes
*
********************************************/
bool PowerManager::unregisterForSuspend(Suspendable *item) {
DEBUG_ENTER_FUNC();
debugPM();
// Unregister from stream list
_listMutex.lock();
_suspendList.remove(item);
_listCounter--;
_listMutex.unlock();
debugPM();
return true;
}
/*******************************************
*
* Destructor
*
********************************************/
PowerManager::~PowerManager() {
_PMStatus = kDestroyPM;
}
/*******************************************
*
* Unsafe function to poll for a pause event (first stage of suspending)
* Only for pausing the engine, which doesn't need high synchronization ie. we don't care if it misreads
* the flag a couple of times since there is NO mutex protection (for performance reasons).
* Polling the engine happens regularly.
* On the other hand, we don't know if there will be ANY polling which prevents us from using proper events.
*
********************************************/
void PowerManager::pollPauseEngine() {
DEBUG_ENTER_FUNC();
bool pause = _pauseFlag; // We copy so as not to have multiple values
if (pause != _pauseFlagOld) {
if (g_engine) { // Check to see if we have an engine
if (pause && _pauseClientState == UNPAUSED) {
_pauseClientState = PAUSING; // Tell PM we're in the middle of pausing
PSP_DEBUG_PRINT_FUNC("Pausing engine\n");
_pauseToken = g_engine->pauseEngine();
_pauseClientState = PAUSED; // Tell PM we're done pausing
} else if (!pause && _pauseClientState == PAUSED) {
PSP_DEBUG_PRINT_FUNC("Unpausing for resume\n");
_pauseToken.clear();
_pauseClientState = UNPAUSED; // Tell PM we're unpaused
}
}
_pauseFlagOld = pause;
}
}
/*******************************************
*
* Function to block on a suspend, then start a non-suspendable critical section
* Use this for large or REALLY critical critical-sections.
* Make sure to call endCriticalSection or the PSP won't suspend.
* returns true if blocked, false if not blocked
********************************************/
bool PowerManager::beginCriticalSection() {
DEBUG_ENTER_FUNC();
bool ret = false;
_flagMutex.lock();
// Check the access flag
if (_suspendFlag) {
ret = true;
PSP_DEBUG_PRINT("I got blocked. ThreadId[%x]\n", sceKernelGetThreadId());
debugPM();
_threadSleep.wait(_flagMutex);
PSP_DEBUG_PRINT_FUNC("I got released. ThreadId[%x]\n", sceKernelGetThreadId());
debugPM();
}
// Now prevent the PM from suspending until we're done
_criticalCounter++;
_flagMutex.unlock();
return ret;
}
// returns success = true
void PowerManager::endCriticalSection() {
DEBUG_ENTER_FUNC();
_flagMutex.lock();
// We're done with our critical section
_criticalCounter--;
if (_criticalCounter <= 0) {
if (_suspendFlag) { // If the PM is sleeping, this flag must be set
PSP_DEBUG_PRINT_FUNC("PM is asleep. Waking it up.\n");
debugPM();
_pmSleep.releaseAll();
PSP_DEBUG_PRINT_FUNC("Woke up the PM\n");
debugPM();
}
if (_criticalCounter < 0) { // Check for bad usage of critical sections
PSP_ERROR("Critical counter[%d]!!!\n", _criticalCounter);
debugPM();
}
}
_flagMutex.unlock();
}
/*******************************************
*
* Callback function to be called to put every Suspendable to suspend
*
********************************************/
void PowerManager::suspend() {
DEBUG_ENTER_FUNC();
if (_pauseFlag)
return; // Very important - make sure we only suspend once
scePowerLock(0); // Also critical to make sure PSP doesn't suspend before we're done
// The first stage of suspend is pausing the engine if possible. We don't want to cause files
// to block, or we might not get the engine to pause. On the other hand, we might wait for polling
// and it'll never happen. We also want to do this w/o mutexes (for speed) which is ok in this case.
_pauseFlag = true;
_PMStatus = kWaitForClientPause;
// Now we wait, giving the engine thread some time to find our flag.
for (int i = 0; i < 10 && _pauseClientState == UNPAUSED; i++)
PspThread::delayMicros(50000); // We wait 50 msec x 10 times = 0.5 seconds
if (_pauseClientState == PAUSING) { // Our event has been acknowledged. Let's wait until the client is done.
_PMStatus = kWaitForClientToFinishPausing;
while (_pauseClientState != PAUSED)
PspThread::delayMicros(50000); // We wait 50 msec at a time
}
// It's possible that the polling thread missed our pause event, but there's
// nothing we can do about that.
// We can't know if there's polling going on or not.
// It's usually not a critical thing anyway.
_PMStatus = kGettingFlagMutexSuspend;
// Now we set the suspend flag to true to cause reading threads to block
_flagMutex.lock();
_PMStatus = kGotFlagMutexSuspend;
_suspendFlag = true;
// Check if anyone is in a critical section. If so, we'll wait for them
if (_criticalCounter > 0) {
_PMStatus = kWaitCritSectionSuspend;
_pmSleep.wait(_flagMutex);
_PMStatus = kDoneWaitingCritSectionSuspend;
}
_flagMutex.unlock();
_PMStatus = kGettingListMutexSuspend;
// Loop over list, calling suspend()
_listMutex.lock();
_PMStatus = kIteratingListSuspend;
// Iterate
Common::List<Suspendable *>::iterator i;
for (i = _suspendList.begin(); i != _suspendList.end(); ++i) {
(*i)->suspend();
}
_PMStatus = kDoneIteratingListSuspend;
_listMutex.unlock();
_PMStatus = kDoneSuspend;
scePowerUnlock(0); // Allow the PSP to go to sleep now
_PMStatus = kDonePowerUnlock;
}
/*******************************************
*
* Callback function to resume every Suspendable
*
********************************************/
void PowerManager::resume() {
DEBUG_ENTER_FUNC();
_PMStatus = kBeginResume;
// Make sure we can't get another suspend
scePowerLock(0);
_PMStatus = kCheckingPauseFlag;
if (!_pauseFlag)
return; // Make sure we can only resume once
_PMStatus = kGettingListMutexResume;
// First we notify our Suspendables. Loop over list, calling resume()
_listMutex.lock();
_PMStatus = kIteratingListResume;
// Iterate
Common::List<Suspendable *>::iterator i = _suspendList.begin();
for (; i != _suspendList.end(); ++i) {
(*i)->resume();
}
_PMStatus = kDoneIteratingListResume;
_listMutex.unlock();
_PMStatus = kGettingFlagMutexResume;
// Now we set the suspend flag to false
_flagMutex.lock();
_PMStatus = kGotFlagMutexResume;
_suspendFlag = false;
_PMStatus = kSignalSuspendedThreadsResume;
// Signal the threads to wake up
_threadSleep.releaseAll();
_PMStatus = kDoneSignallingSuspendedThreadsResume;
_flagMutex.unlock();
_PMStatus = kDoneResume;
_pauseFlag = false; // Signal engine to unpause -- no mutex needed
scePowerUnlock(0); // Allow new suspends
}

View File

@@ -0,0 +1,134 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef POWERMAN_H
#define POWERMAN_H
#include "backends/platform/psp/thread.h"
#include "common/singleton.h"
#include "common/list.h"
#include "engines/engine.h" // for PauseToken
/*
* Implement this class (interface) if you want to use PowerManager's suspend callback functionality
*
*/
class Suspendable {
public:
virtual ~Suspendable() {}
virtual int suspend() = 0;
virtual int resume() = 0;
};
/******************************************************************************************************
*
* This class will call a Suspendable when the PSP goes to suspend/resumes. It also provides the ability to block
* a thread when the PSP is going to suspend/suspending, and to wake it up when the PSP is resumed.
* This ability is very useful for managing the PSPIoStream class, but may be found useful by other classes as well.
*
*******************************************************************************************************/
class PowerManager: public Common::Singleton<PowerManager> {
public:
int blockOnSuspend(); /* block if suspending */
bool beginCriticalSection(); /* Use a critical section to block (if suspend was already pressed) */
void endCriticalSection(); /* and to prevent the PSP from suspending in a particular section */
bool registerForSuspend(Suspendable *item); /* register to be called to suspend/resume */
bool unregisterForSuspend(Suspendable *item); /* remove from suspend/resume list */
void suspend(); /* callback to have all items in list suspend */
void resume(); /* callback to have all items in list resume */
// Functions for pausing the engine
void pollPauseEngine(); /* Poll whether the engine should be paused */
enum {
Error = -1,
NotBlocked = 0,
Blocked = 1
};
enum PauseState {
UNPAUSED = 0,
PAUSING,
PAUSED
};
private:
friend class Common::Singleton<PowerManager>;
PowerManager();
~PowerManager();
Common::List<Suspendable *> _suspendList; // list to register in
volatile bool _pauseFlag; // For pausing, which is before suspending
volatile bool _pauseFlagOld; // Save the last state of the flag while polling
volatile PauseState _pauseClientState; // Pause state of the target
PauseToken _pauseToken;
volatile bool _suspendFlag; // protected variable
PspMutex _flagMutex; // mutex to access access flag
PspMutex _listMutex; // mutex to access Suspendable list
PspCondition _threadSleep; // signal to synchronize accessing threads
PspCondition _pmSleep; // signal to wake up the PM from a critical section
volatile int _criticalCounter; // Counter of how many threads are in a critical section
int _error; // error code - PM can't talk to us. For debugging
volatile int _PMStatus; // What the PM is doing. for debugging
// States for PM to be in (used for debugging)
enum PMState {
kInitDone = 1,
kDestroyPM = 2,
kWaitForClientPause = 3,
kWaitForClientToFinishPausing = 4,
kGettingFlagMutexSuspend = 5,
kGotFlagMutexSuspend = 6,
kWaitCritSectionSuspend = 7,
kDoneWaitingCritSectionSuspend = 8,
kGettingListMutexSuspend = 9,
kIteratingListSuspend = 10,
kDoneIteratingListSuspend = 11,
kDoneSuspend = 12,
kDonePowerUnlock,
kBeginResume,
kCheckingPauseFlag,
kGettingListMutexResume,
kIteratingListResume,
kDoneIteratingListResume,
kGettingFlagMutexResume,
kGotFlagMutexResume,
kSignalSuspendedThreadsResume,
kDoneSignallingSuspendedThreadsResume,
kDoneResume
};
volatile int _listCounter; /* How many people are in the list - just for debugging */
void debugPM(); /* print info about the PM */
public:
int getPMStatus() const { return _PMStatus; }
};
// For easy access
#define PowerMan PowerManager::instance()
#endif /* POWERMAN_H */

View File

@@ -0,0 +1,68 @@
PSP_EXE_STRIPPED := scummvm_stripped$(EXEEXT)
PSP_EBOOT = EBOOT.PBP
PSP_EBOOT_SFO = param.sfo
PSP_EBOOT_TITLE = ScummVM-PSP
DATE = $(shell date +%Y%m%d)
DIST_EXECUTABLES=$(PSP_EBOOT) $(PLUGINS)
DIST_FILES_PLATFORM=$(srcdir)/backends/platform/psp/README.PSP
MKSFO = mksfoex -d MEMSIZE=1
PACK_PBP = pack-pbp
all: pack_pbp
clean: psp_clean
$(PSP_EXE_STRIPPED): $(EXECUTABLE)
$(STRIP) $< -o $@
$(PSP_EBOOT_SFO): $(EXECUTABLE)
$(MKSFO) '$(PSP_EBOOT_TITLE) r$(VER_REV) ($(DATE))' $@
psp_clean:
$(RM) $(PSP_EXE_STRIPPED) $(PSP_EBOOT) $(PSP_EBOOT_SFO)
psp_fixup_elf: $(PSP_EXE_STRIPPED)
psp-fixup-imports $<
pack_pbp: psp_fixup_elf $(PSP_EBOOT_SFO)
$(PACK_PBP) $(PSP_EBOOT) \
$(PSP_EBOOT_SFO) \
$(srcdir)/backends/platform/psp/icon0.png \
NULL \
$(srcdir)/backends/platform/psp/pic0.png \
$(srcdir)/backends/platform/psp/pic1.png \
NULL \
$(PSP_EXE_STRIPPED) \
NULL
psp_release: pack_pbp $(PLUGINS)
rm -rf ./psp_release
mkdir -p ./psp_release/scummvm/doc
cp $(PSP_EBOOT) ./psp_release/scummvm/
cp $(DIST_FILES_THEMES) ./psp_release/scummvm/
ifdef DIST_FILES_ENGINEDATA
cp $(DIST_FILES_ENGINEDATA) ./psp_release/scummvm/
endif
ifdef DIST_FILES_SOUNDFONTS
cp $(DIST_FILES_SOUNDFONTS) ./psp_release/scummvm/
endif
ifdef DIST_FILES_NETWORKING
cp $(DIST_FILES_NETWORKING) ./psp_release/scummvm/
endif
ifdef DIST_FILES_VKEYBD
cp $(DIST_FILES_VKEYBD) ./psp_release/scummvm/
else
cp $(srcdir)/backends/platform/psp/kbd.zip ./psp_release/scummvm/
endif
ifdef DYNAMIC_MODULES
mkdir -p ./psp_release/scummvm/plugins
cp $(PLUGINS) ./psp_release/scummvm/plugins/
endif
cp $(DIST_FILES_DOCS) ./psp_release/scummvm/doc/
cp $(srcdir)/backends/platform/psp/README.PSP ./psp_release/scummvm/doc/
scummvm_psp.zip: psp_release
cd ./psp_release && zip -r ../scummvm_psp.zip . && cd ..
.PHONY: psp_fixup_elf pack_pbp psp_release scummvm_psp.zip

View File

@@ -0,0 +1,3 @@
%rename lib old_lib
*lib:
-lstdc++ -lpspprof -lpspvfpu -lpspdebug -lpspgu -lpspge -lpspdisplay -lpspctrl -lpsppower -lpsphprm -lpspaudio -lpspaudiocodec %(old_lib)

View File

@@ -0,0 +1,198 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Disable printf override in common/forbidden.h to avoid
// clashes with pspdebug.h from the PSP SDK.
// That header file uses
// __attribute__((format(printf,1,2)));
// which gets messed up by our override mechanism; this could
// be avoided by either changing the PSP SDK to use the equally
// legal and valid
// __attribute__((format(__printf__,1,2)));
// or by refining our printf override to use a varadic macro
// (which then wouldn't be portable, though).
// Anyway, for now we just disable the printf override globally
// for the PSP port
#define FORBIDDEN_SYMBOL_EXCEPTION_printf
#define USERSPACE_ONLY //don't use kernel mode features
#ifndef USERSPACE_ONLY
#include <pspkernel.h>
#include <pspdebug.h>
#else
#include <pspuser.h>
#endif
#include <psppower.h>
#include "common/system.h"
#include "engines/engine.h"
#include "base/main.h"
#include "base/plugins.h"
#include "backends/platform/psp/powerman.h"
#include "backends/platform/psp/thread.h"
#include "backends/plugins/psp/psp-provider.h"
#include "backends/platform/psp/psppixelformat.h"
#include "backends/platform/psp/osys_psp.h"
#include "backends/platform/psp/tests.h" /* for unit/speed tests */
#include "backends/platform/psp/trace.h"
#ifdef ENABLE_PROFILING
#include <pspprof.h>
#endif
/**
* Define the module info section
*
* 2nd arg must 0x1000 so __init is executed in
* kernelmode for our loaderInit function
*/
#ifndef USERSPACE_ONLY
PSP_MODULE_INFO("SCUMMVM-PSP", 0x1000, 1, 1);
#else
PSP_MODULE_INFO("SCUMMVM-PSP", 0, 1, 1);
#endif
/**
* THREAD_ATTR_USER causes the thread that the startup
* code (crt0.c) starts this program in to be in usermode
* even though the module was started in kernelmode
*/
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);
#ifndef USERSPACE_ONLY
void MyExceptionHandler(PspDebugRegBlock *regs) {
/* Do normal initial dump, setup screen etc */
pspDebugScreenInit();
pspDebugScreenSetBackColor(0x00FF0000);
pspDebugScreenSetTextColor(0xFFFFFFFF);
pspDebugScreenClear();
pspDebugScreenPrintf("Exception Details:\n");
pspDebugDumpException(regs);
while (1) ;
}
/**
* Function that is called from _init in kernelmode before the
* main thread is started in usermode.
*/
__attribute__((constructor))
void loaderInit() {
pspKernelSetKernelPC();
pspDebugInstallErrorHandler(MyExceptionHandler);
}
#endif
/* Exit callback */
int exit_callback(void) {
#ifdef ENABLE_PROFILING
gprof_cleanup();
#endif
sceKernelExitGame();
return 0;
}
/* Function for handling suspend/resume */
int power_callback(int , int powerinfo, void *) {
if (powerinfo & PSP_POWER_CB_POWER_SWITCH || powerinfo & PSP_POWER_CB_SUSPENDING) {
PowerMan.suspend();
} else if (powerinfo & PSP_POWER_CB_RESUME_COMPLETE) {
PowerMan.resume();
}
return 0;
}
/* Callback thread */
int CallbackThread(SceSize /*size*/, void *arg) {
int cbid;
cbid = sceKernelCreateCallback("Exit Callback", (SceKernelCallbackFunction)exit_callback, NULL);
sceKernelRegisterExitCallback(cbid);
/* Set up callbacks for PSPIoStream */
cbid = sceKernelCreateCallback("Power Callback", (SceKernelCallbackFunction)power_callback, 0);
if (cbid >= 0) {
if (scePowerRegisterCallback(-1, cbid) < 0) {
PSP_ERROR("Couldn't register callback for power_callback\n");
}
} else {
PSP_ERROR("Couldn't create a callback for power_callback\n");
}
sceKernelSleepThreadCB();
return 0;
}
/* Sets up the callback thread and returns its thread id */
int SetupCallbacks(void) {
int thid = sceKernelCreateThread("power_thread", CallbackThread, PRIORITY_POWER_THREAD, STACK_POWER_THREAD, THREAD_ATTR_USER, 0);
if (thid >= 0) {
sceKernelStartThread(thid, 0, 0);
}
return thid;
}
#undef main
int main(void) {
//change clock rate to 333mhz
scePowerSetClockFrequency(333, 333, 166);
PowerManager::instance(); // Setup power manager
SetupCallbacks();
static const char *argv[] = { "scummvm", NULL };
static int argc = sizeof(argv) / sizeof(char *) - 1;
g_system = new OSystem_PSP();
assert(g_system);
#ifdef DYNAMIC_MODULES
PluginManager::instance().addPluginProvider(new PSPPluginProvider());
#endif
/* unit/speed tests */
#if defined(PSP_ENABLE_UNIT_TESTS) || defined(PSP_ENABLE_SPEED_TESTS)
PSP_INFO_PRINT("running tests\n");
psp_tests();
sceKernelSleepThread(); // that's it. That's all we're doing
#endif
int res = scummvm_main(argc, argv);
g_system->quit(); // TODO: Consider removing / replacing this!
PowerManager::destroy(); // get rid of PowerManager
sceKernelSleepThread();
return res;
}

View File

@@ -0,0 +1,571 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Disable symbol overrides so that we can use system headers.
#define FORBIDDEN_SYMBOL_ALLOW_ALL
//#define PSP_KB_SHELL /* Need a hack to properly load the keyboard from the PSP shell */
#ifdef PSP_KB_SHELL
#define PSP_KB_SHELL_PATH "ms0:/psp/game5xx/scummvm-solid/" /* path to kbd.zip */
#endif
#include <malloc.h>
#include <pspkernel.h>
#include "backends/platform/psp/psppixelformat.h"
#include "backends/platform/psp/pspkeyboard.h"
#include "backends/platform/psp/png_loader.h"
#include "backends/platform/psp/input.h"
#include "common/keyboard.h"
#include "common/fs.h"
#include "common/compression/unzip.h"
//#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */
//#define __PSP_DEBUG_PRINT__
#include "backends/platform/psp/trace.h"
#define PSP_SCREEN_WIDTH 480
#define PSP_SCREEN_HEIGHT 272
#define K(x) ((short)(Common::KEYCODE_INVALID + (x)))
#define C(x) ((short)(Common::KEYCODE_##x))
// Layout of the keyboard: Order for the boxes is clockwise and then middle:
// 1
// 4 5 2
// 3
// and order of letters is clockwise in each box, plus 2 top letters:
// e f
// a
// d b
// c
// K(x) is used for ascii values. C(x) is used for keys without ascii values
short PSPKeyboard::_modeChar[MODE_COUNT][5][6] = {
{ //standard letters
{ K('a'), K('b'), K('c'), K('d'), K('f'), K('g') },
{ K('h'), K('i'), K('l'), K('m'), K('j'), K('k') },
{ K('o'), K('n'), K('r'), K('s'), K('p'), K('q') },
{ K('u'), K('v'), K('w'), K('y'), K('x'), K('z') },
{ K('\b'), K('t'), K(' '), K('e'), K(0), K(0) }
},
{ //capital letters
{ K('A'), K('B'), K('C'), K('D'), K('F'), K('G') },
{ K('H'), K('I'), K('L'), K('M'), K('J'), K('K') },
{ K('O'), K('N'), K('R'), K('S'), K('P'), K('Q') },
{ K('U'), K('V'), K('W'), K('Y'), K('X'), K('Z') },
{ K('\b'), K('T'), K(' '), K('E'), K(0), K(0) }
},
{ //numbers
{ K('1'), K('2'), K('3'), K('4'), K(0), K(0) },
{ C(F5), C(F6), C(F7), C(F8), C(F9), C(F10) },
{ K('5'), K('6'), K('7'), K('8'), K(0), K(0) },
{ C(F1), C(F2), C(F3), C(F4), K(0), K(0) },
{ K('\b'), K('0'), K(' '), K('9'), K(0), K(0) }
},
{ //symbols
{ K('!'), K(')'), K('?'), K('('), K('<'), K('>') },
{ K('+'), K('/'), K('='), K('\\'), K('\''), K('"') },
{ K(':'), K(']'), K(';'), K('['), K('@'), K('#') },
{ K('-'), K('}'), K('_'), K('{'), K('*'), K('$') },
{ K('\b'), K('.'), K(' '), K(','), K(0), K(0) }
}
};
// Array with file names
const char *PSPKeyboard::_guiStrings[] = {
"keys4.png", "keys_s4.png",
"keys_c4.png", "keys_s_c4.png",
"nums4.png", "nums_s4.png",
"syms4.png", "syms_s4.png"
};
// Constructor
PSPKeyboard::PSPKeyboard() {
DEBUG_ENTER_FUNC();
_init = false; // we're not initialized yet
_prevButtons = 0; // Reset previous buttons
_dirty = false; // keyboard needs redrawing
_mode = 0; // charset selected. (0: letters, 1: uppercase 2: numbers 3: symbols)
_oldCursor = kCenter; // Center cursor by default
_movedX = 20; // Default starting location
_movedY = 50;
_moved = false; // Keyboard wasn't moved recently
_state = kInvisible; // We start invisible
_lastState = kInvisible;
// Constant renderer settings
_renderer.setAlphaBlending(true);
_renderer.setColorTest(false);
_renderer.setUseGlobalScaler(false);
}
// Destructor
PSPKeyboard::~PSPKeyboard() {
DEBUG_ENTER_FUNC();
if (!_init) {
return;
}
for (int i = 0; i < guiStringsSize; i++) {
_buffers[i].deallocate();
_palettes[i].deallocate();
}
_init = false;
}
void PSPKeyboard::setVisible(bool val) {
if (val && _state == kInvisible && _init) { // Check also that were loaded correctly
_lastState = _state;
_state = kMove;
} else if (!val && _state != kInvisible) {
_lastState = _state;
_state = kInvisible;
}
setDirty();
}
/* move the position the keyboard is currently drawn at */
void PSPKeyboard::moveTo(const int newX, const int newY) {
DEBUG_ENTER_FUNC();
_movedX = newX;
_movedY = newY;
setDirty();
}
/* move the position the keyboard is currently drawn at */
void PSPKeyboard::increaseKeyboardLocationX(int amount) {
DEBUG_ENTER_FUNC();
int newX = _movedX + amount;
if (newX > PSP_SCREEN_WIDTH - 5 || newX < 0 - 140) { // clamp
return;
}
_movedX = newX;
setDirty();
}
/* move the position the keyboard is currently drawn at */
void PSPKeyboard::increaseKeyboardLocationY(int amount) {
DEBUG_ENTER_FUNC();
int newY = _movedY + amount;
if (newY > PSP_SCREEN_HEIGHT - 5 || newY < 0 - 140) { // clamp
return;
}
_movedY = newY;
setDirty();
}
/* draw the keyboard at the current position */
void PSPKeyboard::render() {
DEBUG_ENTER_FUNC();
unsigned int currentBuffer = _mode << 1;
// Draw the background letters
// Set renderer to current buffer & palette
_renderer.setBuffer(&_buffers[currentBuffer]);
_renderer.setPalette(&_palettes[currentBuffer]);
_renderer.setOffsetOnScreen(_movedX, _movedY);
_renderer.setOffsetInBuffer(0, 0);
_renderer.setDrawWholeBuffer();
_renderer.render();
// Get X and Y coordinates for the orange block
int x, y;
convertCursorToXY(_oldCursor, x, y);
const int OrangeBlockSize = 64;
const int GrayBlockSize = 43;
// Draw the current Highlighted Selector (orange block)
_renderer.setBuffer(&_buffers[currentBuffer + 1]);
_renderer.setPalette(&_palettes[currentBuffer + 1]);
_renderer.setOffsetOnScreen(_movedX + (x * GrayBlockSize), _movedY + (y * GrayBlockSize));
_renderer.setOffsetInBuffer(x * OrangeBlockSize, y * OrangeBlockSize);
_renderer.setDrawSize(OrangeBlockSize, OrangeBlockSize);
_renderer.render();
}
inline void PSPKeyboard::convertCursorToXY(CursorDirections cur, int &x, int &y) {
switch (cur) {
case kUp:
x = 1;
y = 0;
break;
case kRight:
x = 2;
y = 1;
break;
case kDown:
x = 1;
y = 2;
break;
case kLeft:
x = 0;
y = 1;
break;
default:
x = 1;
y = 1;
break;
}
}
/* load the keyboard into memory */
bool PSPKeyboard::load() {
DEBUG_ENTER_FUNC();
if (_init) {
PSP_DEBUG_PRINT("keyboard already loaded into memory\n");
return true;
}
// For the shell, we must use a hack
#ifdef PSP_KB_SHELL
Common::FSNode node(PSP_KB_SHELL_PATH);
#else /* normal mode */
Common::FSNode node("."); // Look in current directory
#endif
PSP_DEBUG_PRINT("path[%s]\n", node.getPath().c_str());
Common::Archive *fileArchive = NULL;
Common::Archive *zipArchive = NULL;
Common::SeekableReadStream * file = 0;
if (node.getChild("kbd").exists() && node.getChild("kbd").isDirectory()) {
PSP_DEBUG_PRINT("found directory ./kbd\n");
fileArchive = new Common::FSDirectory(node.getChild("kbd"));
}
if (node.getChild("kbd.zip").exists()) {
PSP_DEBUG_PRINT("found kbd.zip\n");
zipArchive = Common::makeZipArchive(node.getChild("kbd.zip"));
}
int i;
// Loop through all png images
for (i = 0; i < guiStringsSize; i++) {
PSP_DEBUG_PRINT("Opening %s.\n", _guiStrings[i]);
// Look for the file in the kbd directory
if (fileArchive && fileArchive->hasFile(_guiStrings[i])) {
PSP_DEBUG_PRINT("found it in kbd directory.\n");
file = fileArchive->createReadStreamForMember(_guiStrings[i]);
if (!file) {
PSP_ERROR("Can't open kbd/%s for keyboard. No keyboard will load.\n", _guiStrings[i]);
goto ERROR;
}
}
// We didn't find it. Look for it in the zip file
else if (zipArchive && zipArchive->hasFile(_guiStrings[i])) {
PSP_DEBUG_PRINT("found it in kbd.zip.\n");
file = zipArchive->createReadStreamForMember(_guiStrings[i]);
if (!file) {
PSP_ERROR("Can't open %s in kbd.zip for keyboard. No keyboard will load.\n", _guiStrings[i]);
goto ERROR;
}
} else { // Couldn't find the file
PSP_ERROR("Can't find %s for keyboard. No keyboard will load.\n", _guiStrings[i]);
goto ERROR;
}
PngLoader image(*file, _buffers[i], _palettes[i]);
if (image.allocate() != PngLoader::OK) {
PSP_ERROR("Failed to allocate memory for keyboard image %s\n", _guiStrings[i]);
goto ERROR;
}
if (!image.load()) {
PSP_ERROR("Failed to load image from file %s\n", _guiStrings[i]);
goto ERROR;
}
delete file;
} /* for loop */
_init = true;
delete fileArchive;
delete zipArchive;
return true;
ERROR:
delete file;
delete fileArchive;
delete zipArchive;
for (int j = 0; j < i; j++) {
_buffers[j].deallocate();
_palettes[j].deallocate();
}
_init = false;
return false;
}
// Defines for working with PSP buttons
#define CHANGED(x) (_buttonsChanged & (x))
#define PRESSED(x) ((_buttonsChanged & (x)) && (pad.Buttons & (x)))
#define UNPRESSED(x) ((_buttonsChanged & (x)) && !(pad.Buttons & (x)))
#define DOWN(x) (pad.Buttons & (x))
#define UP(x) (!(pad.Buttons & (x)))
#define PSP_DPAD (PSP_CTRL_DOWN|PSP_CTRL_UP|PSP_CTRL_LEFT|PSP_CTRL_RIGHT)
#define PSP_4BUTTONS (PSP_CTRL_CROSS | PSP_CTRL_CIRCLE | PSP_CTRL_TRIANGLE | PSP_CTRL_SQUARE)
/*
* Attempts to read a character from the controller
* Uses the state machine.
* returns whether we have an event
*/
bool PSPKeyboard::processInput(Common::Event &event, PspEvent &pspEvent, SceCtrlData &pad) {
DEBUG_ENTER_FUNC();
bool haveEvent = false; // Whether we have an event for the event manager to process
bool havePspEvent = false;
event.kbd.flags = 0;
_buttonsChanged = _prevButtons ^ pad.Buttons;
if (!_init) // In case we never had init
return false;
if (_state == kInvisible) // Return if we're invisible
return false;
if (_state != kMove && PRESSED(PSP_CTRL_SELECT)) {
_lastState = _state;
_state = kMove; // Check for move or visible state
} else if (CHANGED(PSP_CTRL_START)) { // Handle start button: enter, make KB invisible
event.kbd.ascii = '\r';
event.kbd.keycode = Common::KEYCODE_RETURN;
event.type = DOWN(PSP_CTRL_START) ? Common::EVENT_KEYDOWN : Common::EVENT_KEYUP;
haveEvent = true;
_dirty = true;
if (UP(PSP_CTRL_START))
havePspEvent = true;
}
// Check for being in state of moving the keyboard onscreen or pressing select
else if (_state == kMove)
havePspEvent = handleMoveState(pad);
else if (_state == kDefault)
haveEvent = handleDefaultState(event, pad);
else if (_state == kCornersSelected)
haveEvent = handleCornersSelectedState(event, pad);
else if (_state == kRTriggerDown)
handleRTriggerDownState(pad); // Deal with trigger states
else if (_state == kLTriggerDown)
handleLTriggerDownState(pad); // Deal with trigger states
if (havePspEvent) {
pspEvent.type = PSP_EVENT_SHOW_VIRTUAL_KB; // tell the input handler we're off
pspEvent.data = false;
}
_prevButtons = pad.Buttons;
return haveEvent;
}
bool PSPKeyboard::handleMoveState(SceCtrlData &pad) {
DEBUG_ENTER_FUNC();
if (UP(PSP_CTRL_SELECT)) {
// Toggle between visible and invisible
_state = (_lastState == kInvisible) ? kDefault : kInvisible;
_dirty = true;
if (_moved) { // We moved the keyboard. Keep the keyboard onscreen anyway
_state = kDefault;
_moved = false; // reset moved flag
}
if (_state == kInvisible) {
return true; // we become invisible
}
} else if (DOWN(PSP_DPAD)) { // How we move the KB onscreen
_moved = true;
_dirty = true;
if (DOWN(PSP_CTRL_DOWN))
increaseKeyboardLocationY(5);
else if (DOWN(PSP_CTRL_UP))
increaseKeyboardLocationY(-5);
else if (DOWN(PSP_CTRL_LEFT))
increaseKeyboardLocationX(-5);
else /* DOWN(PSP_CTRL_RIGHT) */
increaseKeyboardLocationX(5);
}
return false;
}
bool PSPKeyboard::handleDefaultState(Common::Event &event, SceCtrlData &pad) {
DEBUG_ENTER_FUNC();
bool haveEvent = false;
if (PRESSED(PSP_CTRL_LTRIGGER)) // Don't say we used up the input
_state = kLTriggerDown;
else if (PRESSED(PSP_CTRL_RTRIGGER)) // Don't say we used up the input
_state = kRTriggerDown;
else if (CHANGED(PSP_4BUTTONS)) // We only care about the 4 buttons
haveEvent = getInputChoice(event, pad);
else if (!DOWN(PSP_4BUTTONS)) // Must be up to move cursor
getCursorMovement(pad);
return haveEvent;
}
bool PSPKeyboard::handleCornersSelectedState(Common::Event &event, SceCtrlData &pad) {
DEBUG_ENTER_FUNC();
// We care about 4 buttons + triggers (for letter selection)
bool haveEvent = false;
if (CHANGED(PSP_4BUTTONS | PSP_CTRL_RTRIGGER | PSP_CTRL_LTRIGGER))
haveEvent = getInputChoice(event, pad);
if (!DOWN(PSP_4BUTTONS | PSP_CTRL_RTRIGGER | PSP_CTRL_LTRIGGER)) // Must be up to move cursor
getCursorMovement(pad);
return haveEvent;
}
bool PSPKeyboard::getInputChoice(Common::Event &event, SceCtrlData &pad) {
DEBUG_ENTER_FUNC();
int innerChoice;
bool haveEvent = false;
if (UNPRESSED(PSP_CTRL_TRIANGLE)) {
innerChoice = 0;
event.type = Common::EVENT_KEYUP; // We give priority to key_up
} else if (UNPRESSED(PSP_CTRL_CIRCLE)) {
innerChoice = 1;
event.type = Common::EVENT_KEYUP; // We give priority to key_up
} else if (UNPRESSED(PSP_CTRL_CROSS)) {
innerChoice = 2;
event.type = Common::EVENT_KEYUP; // We give priority to key_up
} else if (UNPRESSED(PSP_CTRL_SQUARE)) {
innerChoice = 3;
event.type = Common::EVENT_KEYUP; // We give priority to key_up
} else if (UNPRESSED(PSP_CTRL_LTRIGGER) && _state == kCornersSelected) {
innerChoice = 4;
event.type = Common::EVENT_KEYUP; // We give priority to key_up
} else if (UNPRESSED(PSP_CTRL_RTRIGGER) && _state == kCornersSelected) {
innerChoice = 5;
event.type = Common::EVENT_KEYUP; // We give priority to key_up
} else if (PRESSED(PSP_CTRL_TRIANGLE)) {
innerChoice = 0;
event.type = Common::EVENT_KEYDOWN;
} else if (PRESSED(PSP_CTRL_CIRCLE)) {
innerChoice = 1;
event.type = Common::EVENT_KEYDOWN;
} else if (PRESSED(PSP_CTRL_CROSS)) {
innerChoice = 2;
event.type = Common::EVENT_KEYDOWN;
} else if (PRESSED(PSP_CTRL_SQUARE)) {
innerChoice = 3;
event.type = Common::EVENT_KEYDOWN;
} else if (PRESSED(PSP_CTRL_LTRIGGER) && _state == kCornersSelected) {
innerChoice = 4;
event.type = Common::EVENT_KEYDOWN; // We give priority to key_up
} else { /* (PRESSED(PSP_CTRL_RTRIGGER)) && _state == kCornersSelected */
innerChoice = 5;
event.type = Common::EVENT_KEYDOWN; // We give priority to key_up
}
#define IS_UPPERCASE(x) ((x) >= (unsigned short)'A' && (x) <= (unsigned short)'Z')
#define TO_LOWER(x) ((x) += 'a'-'A')
//Now grab the value out of the array
short choice = _modeChar[_mode][_oldCursor][innerChoice];
event.kbd.ascii = choice <= 255 ? choice : 0;
// Handle upper-case which is missing in Common::KeyCode
if (IS_UPPERCASE(choice)) {
event.kbd.keycode = (Common::KeyCode) TO_LOWER(choice);
event.kbd.flags = Common::KBD_SHIFT;
} else
event.kbd.keycode = (Common::KeyCode) choice;
haveEvent = (choice != Common::KEYCODE_INVALID) ? true : false; // We have an event/don't if it's invalid
return haveEvent;
}
void PSPKeyboard::getCursorMovement(SceCtrlData &pad) {
DEBUG_ENTER_FUNC();
CursorDirections cursor;
// Find where the cursor is pointing
cursor = kCenter;
_state = kDefault;
if (DOWN(PSP_DPAD)) {
_state = kCornersSelected;
if (DOWN(PSP_CTRL_UP))
cursor = kUp;
else if (DOWN(PSP_CTRL_RIGHT))
cursor = kRight;
else if (DOWN(PSP_CTRL_DOWN))
cursor = kDown;
else if (DOWN(PSP_CTRL_LEFT))
cursor = kLeft;
}
if (cursor != _oldCursor) { //If we've moved, update dirty and return
_dirty = true;
_oldCursor = cursor;
}
}
void PSPKeyboard::handleLTriggerDownState(SceCtrlData &pad) {
DEBUG_ENTER_FUNC();
if (UNPRESSED(PSP_CTRL_LTRIGGER)) {
_dirty = true;
if (_mode < 2)
_mode = 2;
else
_mode = (_mode == 2) ? 3 : 2;
_state = kDefault;
}
}
void PSPKeyboard::handleRTriggerDownState(SceCtrlData &pad) {
DEBUG_ENTER_FUNC();
if (UNPRESSED(PSP_CTRL_RTRIGGER)) {
_dirty = true;
if (_mode > 1)
_mode = 0;
else
_mode = (_mode == 0) ? 1 : 0;
_state = kDefault;
}
}

View File

@@ -0,0 +1,107 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PSPKEYBOARD_H
#define PSPKEYBOARD_H
#include "common/events.h"
#include "common/stream.h"
#include "backends/platform/psp/display_client.h"
//#include "backends/platform/psp/input.h"
#include <pspctrl.h>
//number of modes
#define MODE_COUNT 4
#define guiStringsSize 8 /* size of guistrings array */
class PspEvent;
class PSPKeyboard : public DisplayClient {
private:
enum State {
kInvisible,
kDefault,
kCornersSelected,
kLTriggerDown,
kRTriggerDown,
kMove
};
public:
PSPKeyboard();
~PSPKeyboard();
bool load(); // Load keyboard into memory
bool isInit() const { return _init; } // Check for initialization
bool isDirty() const { return _dirty; } // Check if needs redrawing
void setDirty() { _dirty = true; }
void setClean() { _dirty = false; }
bool isVisible() const { return _state != kInvisible; } // Check if visible
void setVisible(bool val);
bool processInput(Common::Event &event, PspEvent &pspEvent, SceCtrlData &pad); // Process input
void moveTo(const int newX, const int newY); // Move keyboard
void render(); // Draw the keyboard onscreen
private:
enum CursorDirections {
kUp = 0,
kRight,
kDown,
kLeft,
kCenter
};
Buffer _buffers[guiStringsSize];
Palette _palettes[guiStringsSize];
GuRenderer _renderer;
void increaseKeyboardLocationX(int amount); // Move keyboard onscreen
void increaseKeyboardLocationY(int amount);
void convertCursorToXY(CursorDirections cur, int &x, int &y);
bool handleMoveState(SceCtrlData &pad);
bool handleDefaultState(Common::Event &event, SceCtrlData &pad);
bool handleCornersSelectedState(Common::Event &event, SceCtrlData &pad);
bool getInputChoice(Common::Event &event, SceCtrlData &pad);
void getCursorMovement(SceCtrlData &pad);
void handleRTriggerDownState(SceCtrlData &pad);
void handleLTriggerDownState(SceCtrlData &pad);
static short _modeChar[MODE_COUNT][5][6];
static const char *_guiStrings[];
bool _init;
uint32 _prevButtons; // A bit pattern.
uint32 _buttonsChanged;
bool _dirty; // keyboard needs redrawing
int _mode; // charset selected. (0 - letters or 1 - numbers)
int _movedX; // location we've moved the KB to onscreen
int _movedY;
bool _moved; // whether the keyboard was moved
State _state; // State of keyboard Keyboard state machine
State _lastState;
CursorDirections _oldCursor; // Point to place of last cursor
};
#endif /* PSPKEYBOARD_H */

View File

@@ -0,0 +1,188 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/scummsys.h"
#include "backends/platform/psp/psppixelformat.h"
//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
//#define __PSP_DEBUG_PRINT__ /* For debug printouts */
#include "backends/platform/psp/trace.h"
// class PSPPixelFormat --------------------------------------
void PSPPixelFormat::set(Type type, bool swap /* = false */) {
DEBUG_ENTER_FUNC();
PSP_DEBUG_PRINT("type = %d\n", type);
format = type;
swapRB = swap;
switch (type) {
case Type_4444:
case Type_5551:
case Type_5650:
bitsPerPixel = 16;
break;
case Type_8888:
case Type_8888_RGBA:
bitsPerPixel = 32;
break;
case Type_Palette_8bit:
bitsPerPixel = 8;
break;
case Type_Palette_4bit:
bitsPerPixel = 4;
break;
case Type_None:
bitsPerPixel = 0;
break;
default: // This is an error, but let's continue anyway
PSP_ERROR("Unhandled value of pixel type[%d]\n", type);
bitsPerPixel = 16;
break;
}
PSP_DEBUG_PRINT("bitsPerPixel[%u]\n", bitsPerPixel);
}
// Convert from ScummVM general PixelFormat to our pixel format
// For buffer and palette.
void PSPPixelFormat::convertFromScummvmPixelFormat(const Graphics::PixelFormat *pf,
PSPPixelFormat::Type &bufferType,
PSPPixelFormat::Type &paletteType,
bool &swapRedBlue) {
swapRedBlue = false; // no red-blue swap by default
PSPPixelFormat::Type *target = nullptr; // which one we'll be filling
if (!pf) { // Default, pf is NULL
bufferType = Type_Palette_8bit;
paletteType = Type_5551;
} else { // We have a pf
if (pf->bytesPerPixel == 1) {
bufferType = Type_Palette_8bit;
target = &paletteType; // The type describes the palette
} else if (pf->bytesPerPixel == 2 || pf->bytesPerPixel == 4) {
paletteType = Type_None;
target = &bufferType; // The type describes the buffer
} else {
PSP_ERROR("Unknown bpp[%u] in pixeltype. Reverting to 8bpp\n", pf->bytesPerPixel);
bufferType = Type_Palette_8bit;
target = &paletteType; // The type describes the palette
}
// Find out the exact type of the target
if (pf->rLoss == 3 && pf->bLoss == 3) {
if (pf->gLoss == 3)
*target = Type_5551;
else
*target = Type_5650;
} else if (pf->rLoss == 4 && pf->gLoss == 4 && pf->bLoss == 4) {
*target = Type_4444;
} else if (pf->gLoss == 0 && pf->gShift == 8) {
*target = Type_8888;
} else if (pf->gLoss == 0 && pf->gShift == 16) {
*target = Type_8888_RGBA;
} else if ((pf->gLoss == 0 && pf->gShift == 0) ||
(pf->gLoss == 8 && pf->gShift == 0)) { // Default CLUT8 can have weird values
*target = Type_5551;
} else {
PSP_ERROR("Unknown Scummvm pixel format.\n");
PSP_ERROR("\trLoss[%d], gLoss[%d], bLoss[%d], aLoss[%d]\n\trShift[%d], gShift[%d], bShift[%d], aShift[%d]\n",
pf->rLoss, pf->gLoss, pf->bLoss, pf->aLoss,
pf->rShift, pf->gShift, pf->bShift, pf->aShift);
*target = Type_Unknown;
}
if (pf->rShift != 0) {// We allow backend swap of red and blue
swapRedBlue = true;
PSP_DEBUG_PRINT("detected red/blue swap\n");
}
}
}
Graphics::PixelFormat PSPPixelFormat::convertToScummvmPixelFormat(PSPPixelFormat::Type type) {
Graphics::PixelFormat pf;
switch (type) {
case Type_4444:
pf.bytesPerPixel = 2;
pf.aLoss = 4;
pf.rLoss = 4;
pf.gLoss = 4;
pf.bLoss = 4;
pf.aShift = 12;
pf.rShift = 0;
pf.gShift = 4;
pf.bShift = 8;
break;
case Type_5551:
pf.bytesPerPixel = 2;
pf.aLoss = 7;
pf.rLoss = 3;
pf.gLoss = 3;
pf.bLoss = 3;
pf.aShift = 15;
pf.rShift = 0;
pf.gShift = 5;
pf.bShift = 10;
break;
case Type_5650:
pf.bytesPerPixel = 2;
pf.aLoss = 8;
pf.rLoss = 3;
pf.gLoss = 2;
pf.bLoss = 3;
pf.aShift = 0;
pf.rShift = 0;
pf.gShift = 5;
pf.bShift = 11;
break;
case Type_8888:
case Type_8888_RGBA:
pf.bytesPerPixel = 4;
pf.aLoss = 0;
pf.rLoss = 0;
pf.gLoss = 0;
pf.bLoss = 0;
pf.aShift = 24;
pf.rShift = 0;
pf.gShift = 8;
pf.bShift = 16;
break;
default:
PSP_ERROR("Unhandled PSPPixelFormat[%u]\n", type);
break;
}
return pf;
}
uint32 PSPPixelFormat::convertTo32BitColor(uint32 color) const {
DEBUG_ENTER_FUNC();
uint32 r, g, b, a, output;
colorToRgba(color, r, g, b, a);
output = ((b << 16) | (g << 8) | (r << 0) | (a << 24));
PSP_DEBUG_PRINT_FUNC("input color[%x], output[%x]\n", color, output);
return output;
}

View File

@@ -0,0 +1,244 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PSP_PIXEL_FORMAT_H
#define PSP_PIXEL_FORMAT_H
#include "graphics/pixelformat.h"
#include "backends/platform/psp/trace.h"
/**
* Specialized PixelFormat class
* Supports only those formats which the PSP allows, including 4 bit palettes.
* Also provides accurate color conversion (needed for color masking)
* As well as swapping of red and blue channels (needed by HE games, for example)
*/
struct PSPPixelFormat {
enum Type {
Type_None,
Type_4444,
Type_5551,
Type_5650,
Type_8888,
Type_8888_RGBA,
Type_Palette_8bit,
Type_Palette_4bit,
Type_Unknown
};
Type format;
uint32 bitsPerPixel; ///< Must match bpp of selected type
bool swapRB; ///< Swap red and blue values when reading and writing
PSPPixelFormat() : format(Type_Unknown), bitsPerPixel(0), swapRB(false) {}
void set(Type type, bool swap = false);
static void convertFromScummvmPixelFormat(const Graphics::PixelFormat *pf,
PSPPixelFormat::Type &bufferType,
PSPPixelFormat::Type &paletteType,
bool &swapRedBlue);
static Graphics::PixelFormat convertToScummvmPixelFormat(PSPPixelFormat::Type type);
uint32 convertTo32BitColor(uint32 color) const;
inline uint32 rgbaToColor(uint32 r, uint32 g, uint32 b, uint32 a) const {
uint32 color;
switch (format) {
case Type_4444:
color = (((b >> 4) << 8) | ((g >> 4) << 4) | ((r >> 4) << 0) | ((a >> 4) << 12));
break;
case Type_5551:
color = (((b >> 3) << 10) | ((g >> 3) << 5) | ((r >> 3) << 0) | ((a >> 7) << 15));
break;
case Type_5650:
color = (((b >> 3) << 11) | ((g >> 2) << 5) | ((r >> 3) << 0));
break;
case Type_8888:
case Type_8888_RGBA:
color = (((b >> 0) << 16) | ((g >> 0) << 8) | ((r >> 0) << 0) | ((a >> 0) << 24));
break;
default:
color = 0;
break;
}
return color;
}
inline void colorToRgba(uint32 color, uint32 &r, uint32 &g, uint32 &b, uint32 &a) const {
switch (format) {
case Type_4444:
a = (color >> 12) & 0xF; // Interpolate to get true colors
b = (color >> 8) & 0xF;
g = (color >> 4) & 0xF;
r = (color >> 0) & 0xF;
a = a << 4 | a;
b = b << 4 | b;
g = g << 4 | g;
r = r << 4 | r;
break;
case Type_5551:
a = (color >> 15) ? 0xFF : 0;
b = (color >> 10) & 0x1F;
g = (color >> 5) & 0x1F;
r = (color >> 0) & 0x1F;
b = b << 3 | b >> 2;
g = g << 3 | g >> 2;
r = r << 3 | r >> 2;
break;
case Type_5650:
a = 0xFF;
b = (color >> 11) & 0x1F;
g = (color >> 5) & 0x3F;
r = (color >> 0) & 0x1F;
b = b << 3 | b >> 2;
g = g << 2 | g >> 4;
r = r << 3 | r >> 2;
break;
case Type_8888:
case Type_8888_RGBA:
a = (color >> 24) & 0xFF;
b = (color >> 16) & 0xFF;
g = (color >> 8) & 0xFF;
r = (color >> 0) & 0xFF;
break;
default:
a = b = g = r = 0;
break;
}
}
inline uint32 setColorAlpha(uint32 color, byte alpha) {
switch (format) {
case Type_4444:
color = (color & 0x0FFF) | (((uint32)alpha >> 4) << 12);
break;
case Type_5551:
color = (color & 0x7FFF) | (((uint32)alpha >> 7) << 15);
break;
case Type_8888:
case Type_8888_RGBA:
color = (color & 0x00FFFFFF) | ((uint32)alpha << 24);
break;
case Type_5650:
default:
break;
}
return color;
}
inline uint32 pixelsToBytes(uint32 pixels) const {
switch (bitsPerPixel) {
case 4:
pixels >>= 1;
break;
case 16:
pixels <<= 1;
break;
case 32:
pixels <<= 2;
break;
case 8:
break;
default:
PSP_ERROR("Incorrect bitsPerPixel value[%u]. pixels[%u]\n", bitsPerPixel, pixels);
break;
}
return pixels;
}
inline uint16 swapRedBlue16(uint16 color) const {
uint16 output;
switch (format) {
case Type_4444:
output = (color & 0xf0f0) | ((color & 0x000f) << 8) | ((color & 0x0f00) >> 8);
break;
case Type_5551:
output = (color & 0x83e0) | ((color & 0x001f) << 10) | ((color & 0x7c00) >> 10);
break;
case Type_5650:
output = (color & 0x07e0) | ((color & 0x001f) << 11) | ((color & 0xf800) >> 11);
break;
default:
PSP_ERROR("invalid format[%u] for swapping\n", format);
output = 0;
break;
}
return output;
}
inline uint32 swapRedBlue32(uint32 color) const {
uint32 output;
switch (format) {
case Type_4444:
output = (color & 0xf0f0f0f0) |
((color & 0x000f000f) << 8) | ((color & 0x0f000f00) >> 8);
break;
case Type_5551:
output = (color & 0x83e083e0) |
((color & 0x001f001f) << 10) | ((color & 0x7c007c00) >> 10);
break;
case Type_5650:
output = (color & 0x07e007e0) |
((color & 0x001f001f) << 11) | ((color & 0xf800f800) >> 11);
break;
case Type_8888:
output = (color & 0xff00ff00) |
((color & 0x000000ff) << 16) | ((color & 0x00ff0000) >> 16);
break;
case Type_8888_RGBA:
output = ((color & 0x000000ff) << 24) | ((color & 0x0000ff00) << 8) |
((color & 0x00ff0000) >> 8) | ((color & 0xff000000) >> 24);
break;
default:
PSP_ERROR("invalid format[%u] for swapping\n", format);
output = 0;
break;
}
return output;
}
// Return whatever color we point at
inline uint32 getColorValueAt(byte *pointer) const {
uint32 result;
switch (bitsPerPixel) {
case 4: // We can't distinguish a 4 bit color with a pointer
case 8:
result = *pointer;
break;
case 16:
result = *(uint16 *)pointer;
break;
case 32:
result = *(uint32 *)pointer;
break;
default:
result = 0;
PSP_ERROR("Incorrect bitsPerPixel value[%u].\n", bitsPerPixel);
break;
}
return result;
}
};
#endif /* PSP_PIXEL_FORMAT_H */

View File

@@ -0,0 +1,84 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include <time.h>
#include <psptypes.h>
#include <psprtc.h>
#include "common/scummsys.h"
#include "backends/platform/psp/rtc.h"
//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
//#define __PSP_DEBUG_PRINT__ /* For debug printouts */
#include "backends/platform/psp/trace.h"
// Class PspRtc ---------------------------------------------------------------
namespace Common {
DECLARE_SINGLETON(PspRtc);
}
void PspRtc::init() { // init our starting ticks
uint32 ticks[2];
sceRtcGetCurrentTick((u64 *)ticks);
_startMillis = ticks[0]/1000;
_startMicros = ticks[0];
//_lastMillis = ticks[0]/1000; //debug - only when we don't subtract startMillis
}
#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(bool skipRecord) {
uint32 ticks[2];
sceRtcGetCurrentTick((u64 *)ticks); // can introduce weird thread delays
uint32 millis = ticks[0]/1000;
millis -= _startMillis; // get ms since start of program
if ((int)_lastMillis - (int)millis > MS_LOOP_CHECK) { // we must have looped around
if (_looped == false) { // check to make sure threads do this once
_looped = true;
_milliOffset += MS_LOOP_AROUND; // add the needed offset
PSP_DEBUG_PRINT("looping around. last ms[%d], curr ms[%d]\n", _lastMillis, millis);
}
} else {
_looped = false;
}
_lastMillis = millis;
return millis + _milliOffset;
}
uint32 PspRtc::getMicros() {
uint32 ticks[2];
sceRtcGetCurrentTick((u64 *)ticks);
ticks[0] -= _startMicros;
return ticks[0];
}

View File

@@ -0,0 +1,46 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _PSP_RTC_H_
#define _PSP_RTC_H_
#include "common/singleton.h"
class PspRtc : public Common::Singleton<PspRtc> {
private:
uint32 _startMillis;
uint32 _startMicros;
uint32 _lastMillis;
uint32 _milliOffset; // to prevent looping around of millis
bool _looped; // make sure we only loop once - for threading
public:
PspRtc()
: _startMillis(0), _startMicros(0),
_lastMillis(0), _milliOffset(0),
_looped(false) {
init();
}
void init();
uint32 getMillis(bool skipRecord = false);
uint32 getMicros();
};
#endif

View File

@@ -0,0 +1,760 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// PSP speed and unit tests. Activate in tests.h
// You may also want to build without any engines.
#include "backends/platform/psp/tests.h"
#if defined(PSP_ENABLE_UNIT_TESTS) || defined(PSP_ENABLE_SPEED_TESTS)
#include "common/scummsys.h"
#include <pspiofilemgr_fcntl.h>
#include <pspiofilemgr_stat.h>
#include <pspiofilemgr.h>
#include <pspthreadman.h>
#include <pspsdk.h>
#include <psprtc.h>
#include <stdlib.h>
#include <stdio.h>
#include <psputils.h>
#include "backends/platform/psp/rtc.h"
#include "backends/platform/psp/thread.h"
#include "backends/platform/psp/memory.h"
#include "common/stream.h"
#include "common/file.h"
#include "common/fs.h"
#define UNCACHED(x) ((byte *)(((uint32)(x)) | 0x40000000)) /* make an uncached access */
#define CACHED(x) ((byte *)(((uint32)(x)) & 0xBFFFFFFF)) /* make an uncached access into a cached one */
//#define __PSP_DEBUG_FUNCS__
//#define __PSP_DEBUG_PRINT__
// Results: (333Mhz/222Mhz)
// Getting a tick: 1-2 us
// Getting a time structure: 9/14us
// ie. using a tick and just dividing by 1000 saves us time.
#include "backends/platform/psp/trace.h"
class PspSpeedTests {
public:
void tickSpeed();
void getMicrosSpeed();
void seekSpeed();
void msReadSpeed();
void threadFunctionsSpeed();
void semaphoreSpeed();
static int threadFunc(SceSize args, void *argp);
void semaphoreManyThreadSpeed();
void fastCopySpeed();
private:
enum {
MEMCPY_BUFFER_SIZE = 8192
};
static PspSemaphore _sem; // semaphore
void readAndTime(uint32 bytes, char *buffer, FILE *file);
void seekAndTime(int bytes, int origin, FILE *file);
void fastCopySpecificSize(byte *dst, byte *src, uint32 bytes);
void fastCopyDifferentSizes(byte *dst, byte *src);
int getThreadIdSpeed();
void getPrioritySpeed();
void changePrioritySpeed(int id, int priority);
};
PspSemaphore PspSpeedTests::_sem(0);
void PspSpeedTests::tickSpeed() {
uint32 ticksPerSecond = sceRtcGetTickResolution();
PSP_INFO_PRINT("ticksPerSecond[%d]\n", ticksPerSecond);
uint32 currentTicks1[2];
uint32 currentTicks2[2];
sceRtcGetCurrentTick((u64 *)currentTicks1);
sceRtcGetCurrentTick((u64 *)currentTicks2);
PSP_INFO_PRINT("current tick[%x %x][%u %u]\n", currentTicks1[0], currentTicks1[1], currentTicks1[0], currentTicks1[1]);
PSP_INFO_PRINT("current tick[%x %x][%u %u]\n", currentTicks2[0], currentTicks2[1], currentTicks2[0], currentTicks2[1]);
pspTime time;
sceRtcSetTick(&time, (u64 *)currentTicks2);
PSP_INFO_PRINT("current tick in time, year[%d] month[%d] day[%d] hour[%d] minutes[%d] seconds[%d] us[%d]\n", time.year, time.month, time.day, time.hour, time.minutes, time.seconds, time.microseconds);
pspTime time1;
pspTime time2;
sceRtcGetCurrentClockLocalTime(&time1);
sceRtcGetCurrentClockLocalTime(&time2);
PSP_INFO_PRINT("time1, year[%d] month[%d] day[%d] hour[%d] minutes[%d] seconds[%d] us[%d]\n", time1.year, time1.month, time1.day, time1.hour, time1.minutes, time1.seconds, time1.microseconds);
PSP_INFO_PRINT("time2, year[%d] month[%d] day[%d] hour[%d] minutes[%d] seconds[%d] us[%d]\n", time2.year, time2.month, time2.day, time2.hour, time2.minutes, time2.seconds, time2.microseconds);
}
void PspSpeedTests::getMicrosSpeed() {
uint32 time1, time2, time3, time4;
time1 = PspRtc::instance().getMicros();
time2 = PspRtc::instance().getMicros();
time3 = PspRtc::instance().getMicros();
time4 = PspRtc::instance().getMicros();
PSP_INFO_PRINT("getMicros() times: %d, %d, %d\n", time4-time3, time3-time2, time2-time1);
}
void PspSpeedTests::readAndTime(uint32 bytes, char *buffer, FILE *file) {
uint32 time1 = PspRtc::instance().getMicros();
// test minimal read
fread(buffer, bytes, 1, file);
uint32 time2 = PspRtc::instance().getMicros();
PSP_INFO_PRINT("Reading %d byte takes %dus\n", bytes, time2-time1);
}
/*
333MHz/222MHz
Reading 1 byte takes 2590us / 3167
Reading 10 byte takes 8us / 9
Reading 50 byte takes 8us / 11
Reading 100 byte takes 8us / 11
Reading 1000 byte takes 915us / 1131
Reading 2000 byte takes 1806us / 2,284
Reading 3000 byte takes 2697us / 3,374
Reading 5000 byte takes 4551us / 5,544
Reading 6000 byte takes 5356us / 6,676
Reading 7000 byte takes 6800us / 8,358
Reading 8000 byte takes 6794us / 8,454
Reading 9000 byte takes 6782us / 8,563
Reading 10000 byte takes 8497us / 10,631
Reading 30000 byte takes 25995us / 32,473
Reading 80000 byte takes 68457us / 85,291
Reading 100000 byte takes 85103us / 106,163
*/
// Function to test the impact of MS reads
// These tests can't be done from shell - the cache screws them up
void PspSpeedTests::msReadSpeed() {
FILE *file;
file = fopen("ms0:/psp/music/track1.mp3", "r");
char *buffer = (char *)malloc(2 * 1024 * 1024);
readAndTime(1, buffer, file);
readAndTime(10, buffer, file);
readAndTime(50, buffer, file);
readAndTime(100, buffer, file);
readAndTime(1000, buffer, file);
readAndTime(2000, buffer, file);
readAndTime(3000, buffer, file);
readAndTime(5000, buffer, file);
readAndTime(6000, buffer, file);
readAndTime(7000, buffer, file);
readAndTime(8000, buffer, file);
readAndTime(9000, buffer, file);
readAndTime(10000, buffer, file);
readAndTime(30000, buffer, file);
readAndTime(50000, buffer, file);
readAndTime(80000, buffer, file);
readAndTime(100000, buffer, file);
fclose(file);
free(buffer);
}
void PspSpeedTests::seekAndTime(int bytes, int origin, FILE *file) {
char buffer[1000];
uint32 time1 = PspRtc::instance().getMicros();
// test minimal read
fseek(file, bytes, origin);
uint32 time2 = PspRtc::instance().getMicros();
PSP_INFO_PRINT("Seeking %d byte from %d took %dus\n", bytes, origin, time2-time1);
time1 = PspRtc::instance().getMicros();
// test minimal read
fread(buffer, 1000, 1, file);
time2 = PspRtc::instance().getMicros();
PSP_INFO_PRINT("Reading 1000 bytes took %dus\n", time2-time1);
}
/*
333MHz
Seeking 0 byte from 0 took 946us
Reading 1000 bytes took 1781us
Seeking 5 byte from 0 took 6us
Reading 1000 bytes took 19us
Seeking 1000 byte from 0 took 5us
Reading 1000 bytes took 913us
Seeking 100 byte from 0 took 955us
Reading 1000 bytes took 906us
Seeking 10000 byte from 0 took 963us
Reading 1000 bytes took 905us
Seeking -5 byte from 1 took 1022us
Reading 1000 bytes took 949us
Seeking -100 byte from 1 took 1040us
Reading 1000 bytes took 907us
Seeking 100 byte from 1 took 1044us
Reading 1000 bytes took 930us
Seeking 0 byte from 2 took 7211us
Reading 1000 bytes took 80us
Seeking 10000 byte from 2 took 3636us
Reading 1000 bytes took 110us
*/
void PspSpeedTests::seekSpeed() {
FILE *file;
file = fopen("ms0:/psp/music/track1.mp3", "r");
seekAndTime(0, SEEK_SET, file);
seekAndTime(5, SEEK_SET, file);
seekAndTime(1000, SEEK_SET, file);
seekAndTime(100, SEEK_SET, file);
seekAndTime(10000, SEEK_SET, file);
seekAndTime(-5, SEEK_CUR, file);
seekAndTime(-100, SEEK_CUR, file);
seekAndTime(100, SEEK_CUR, file);
seekAndTime(0, SEEK_END, file);
seekAndTime(-10000, SEEK_END, file);
fclose(file);
}
// 222: 5-7us
int PspSpeedTests::getThreadIdSpeed() {
uint32 time1 = PspRtc::instance().getMicros();
int threadId = sceKernelGetThreadId();
uint32 time2 = PspRtc::instance().getMicros();
PSP_INFO_PRINT("Getting thread ID %d took %dus\n", threadId, time2-time1);
return threadId;
}
// 222: 4-5us
void PspSpeedTests::getPrioritySpeed() {
uint32 time1 = PspRtc::instance().getMicros();
int priority = sceKernelGetThreadCurrentPriority();
uint32 time2 = PspRtc::instance().getMicros();
PSP_INFO_PRINT("Getting thread priority %d took %dus\n", priority, time2-time1);
}
// 222: 9-10us
void PspSpeedTests::changePrioritySpeed(int id, int priority) {
uint32 time1 = PspRtc::instance().getMicros();
sceKernelChangeThreadPriority(id, priority);
uint32 time2 = PspRtc::instance().getMicros();
PSP_INFO_PRINT("Changing thread priority to %d for id %d took %dus\n", priority, id, time2-time1);
}
void PspSpeedTests::threadFunctionsSpeed() {
// very unscientific -- just ballpark
int id;
id = getThreadIdSpeed();
getThreadIdSpeed();
getPrioritySpeed();
getPrioritySpeed();
changePrioritySpeed(id, 30);
changePrioritySpeed(id, 35);
changePrioritySpeed(id, 25);
// test context switch time
for (int i=0; i<10; i++) {
uint time1 = PspRtc::instance().getMicros();
PspThread::delayMicros(0);
uint time2 = PspRtc::instance().getMicros();
PSP_INFO_PRINT("poll %d. context switch Time = %dus\n", i, time2-time1); // 10-15us
}
}
void PspSpeedTests::semaphoreSpeed() {
PspSemaphore sem(1);
uint32 time1 = PspRtc::instance().getMicros();
sem.take();
uint32 time2 = PspRtc::instance().getMicros();
PSP_INFO_PRINT("taking semaphore took %d us\n", time2-time1); // 10us
uint32 time3 = PspRtc::instance().getMicros();
sem.give();
uint32 time4 = PspRtc::instance().getMicros();
PSP_INFO_PRINT("releasing semaphore took %d us\n", time4-time3); //10us-55us
}
int PspSpeedTests::threadFunc(SceSize args, void *argp) {
PSP_INFO_PRINT("thread %x created.\n", sceKernelGetThreadId());
_sem.take();
PSP_INFO_PRINT("grabbed semaphore. Quitting thread\n");
return 0;
}
void PspSpeedTests::semaphoreManyThreadSpeed() {
// create 4 threads
for (int i=0; i<4; i++) {
int thid = sceKernelCreateThread("my_thread", PspSpeedTests::threadFunc, 0x18, 0x10000, THREAD_ATTR_USER, NULL);
sceKernelStartThread(thid, 0, 0);
}
PSP_INFO_PRINT("main thread. created threads\n");
uint32 threads = _sem.numOfWaitingThreads();
while (threads < 4) {
threads = _sem.numOfWaitingThreads();
PSP_INFO_PRINT("main thread: waiting threads[%d]\n", threads);
}
PSP_INFO_PRINT("main: semaphore value[%d]\n", _sem.getValue());
PSP_INFO_PRINT("main thread: waiting threads[%d]\n", _sem.numOfWaitingThreads());
_sem.give(4);
}
void PspSpeedTests::fastCopySpecificSize(byte *dst, byte *src, uint32 bytes) {
uint32 time1, time2;
uint32 fastcopyTime, memcpyTime;
const int iterations = 2000;
int intc;
intc = pspSdkDisableInterrupts();
time1 = PspRtc::instance().getMicros();
for (int i=0; i<iterations; i++) {
PspMemory::fastCopy(dst, src, bytes);
}
time2 = PspRtc::instance().getMicros();
pspSdkEnableInterrupts(intc);
fastcopyTime = time2-time1;
intc = pspSdkDisableInterrupts();
time1 = PspRtc::instance().getMicros();
for (int i=0; i<iterations; i++) {
memcpy(dst, src, bytes);
}
time2 = PspRtc::instance().getMicros();
pspSdkEnableInterrupts(intc);
memcpyTime = time2-time1;
PSP_INFO_PRINT("%d bytes. memcpy[%d], fastcopy[%d]\n", bytes, memcpyTime, fastcopyTime);
}
void PspSpeedTests::fastCopyDifferentSizes(byte *dst, byte *src) {
PSP_INFO_PRINT("\nsrc[%p], dst[%p]\n", src, dst);
fastCopySpecificSize(dst, src, 1);
fastCopySpecificSize(dst, src, 2);
fastCopySpecificSize(dst, src, 3);
fastCopySpecificSize(dst, src, 4);
fastCopySpecificSize(dst, src, 5);
fastCopySpecificSize(dst, src, 8);
fastCopySpecificSize(dst, src, 10);
fastCopySpecificSize(dst, src, 16);
fastCopySpecificSize(dst, src, 32);
fastCopySpecificSize(dst, src, 50);
fastCopySpecificSize(dst, src, 100);
fastCopySpecificSize(dst, src, 500);
fastCopySpecificSize(dst, src, 1024);
fastCopySpecificSize(dst, src, 2048);
}
void PspSpeedTests::fastCopySpeed() {
PSP_INFO_PRINT("running fastCopy speed test\n");
uint32 *bufferSrc32 = (uint32 *)memalign(16, MEMCPY_BUFFER_SIZE);
uint32 *bufferDst32 = (uint32 *)memalign(16, MEMCPY_BUFFER_SIZE);
// fill buffer 1
for (int i=0; i<MEMCPY_BUFFER_SIZE/4; i++)
bufferSrc32[i] = i | (((MEMCPY_BUFFER_SIZE/4)-i)<<16);
// print buffer
for (int i=0; i<50; i++)
PSP_INFO_PRINT("%x ", bufferSrc32[i]);
PSP_INFO_PRINT("\n");
byte *bufferSrc = ((byte *)bufferSrc32);
byte *bufferDst = ((byte *)bufferDst32);
PSP_INFO_PRINT("\n\ndst and src cached: -----------------\n");
fastCopyDifferentSizes(bufferDst, bufferSrc);
fastCopyDifferentSizes(bufferDst+1, bufferSrc+1);
fastCopyDifferentSizes(bufferDst, bufferSrc+1);
fastCopyDifferentSizes(bufferDst+1, bufferSrc);
PSP_INFO_PRINT("\n\ndst cached, src uncached: -----------------\n");
bufferSrc = UNCACHED(bufferSrc);
fastCopyDifferentSizes(bufferDst, bufferSrc);
fastCopyDifferentSizes(bufferDst+1, bufferSrc+1);
fastCopyDifferentSizes(bufferDst, bufferSrc+1);
fastCopyDifferentSizes(bufferDst+1, bufferSrc);
PSP_INFO_PRINT("\n\ndst uncached, src uncached: --------------\n");
bufferDst = UNCACHED(bufferDst);
fastCopyDifferentSizes(bufferDst, bufferSrc);
fastCopyDifferentSizes(bufferDst+1, bufferSrc+1);
fastCopyDifferentSizes(bufferDst, bufferSrc+1);
fastCopyDifferentSizes(bufferDst+1, bufferSrc);
PSP_INFO_PRINT("\n\ndst uncached, src cached: -------------------\n");
bufferSrc = CACHED(bufferSrc);
fastCopyDifferentSizes(bufferDst, bufferSrc);
fastCopyDifferentSizes(bufferDst+1, bufferSrc+1);
fastCopyDifferentSizes(bufferDst, bufferSrc+1);
fastCopyDifferentSizes(bufferDst+1, bufferSrc);
free(bufferSrc32);
free(bufferDst32);
}
//-------Unit Tests -------------------------------
class PspUnitTests {
public:
void testFastCopy();
bool testFileSystem();
private:
enum {
MEMCPY_BUFFER_SIZE = 8192
};
void fastCopySpecificSize(byte *dst, byte *src, uint32 bytes, bool swap = false);
void fastCopyDifferentSizes(byte *dst, byte *src, bool swap = false);
};
void PspUnitTests::testFastCopy() {
PSP_INFO_PRINT("running fastcopy unit test ***********\n");
PSP_INFO_PRINT("this test requires the test flag to be on in fastCopy\n\n");
uint32 *bufferSrc32 = (uint32 *)memalign(16, MEMCPY_BUFFER_SIZE);
uint32 *bufferDst32 = (uint32 *)memalign(16, MEMCPY_BUFFER_SIZE);
// fill buffer 1
for (int i=0; i<MEMCPY_BUFFER_SIZE/4; i++)
bufferSrc32[i] = i | (((MEMCPY_BUFFER_SIZE/4)-i)<<16);
// print buffer
for (int i=0; i<50; i++)
PSP_INFO_PRINT("%x ", bufferSrc32[i]);
PSP_INFO_PRINT("\n");
byte *bufferSrc = ((byte *)bufferSrc32);
byte *bufferDst = ((byte *)bufferDst32);
fastCopyDifferentSizes(bufferDst, bufferSrc, true);
fastCopyDifferentSizes(bufferDst+1, bufferSrc+1);
fastCopyDifferentSizes(bufferDst+2, bufferSrc+2, true);
fastCopyDifferentSizes(bufferDst+3, bufferSrc+3);
fastCopyDifferentSizes(bufferDst, bufferSrc+1);
fastCopyDifferentSizes(bufferDst, bufferSrc+2, true);
fastCopyDifferentSizes(bufferDst+2, bufferSrc, true);
fastCopyDifferentSizes(bufferDst, bufferSrc+3);
fastCopyDifferentSizes(bufferDst+1, bufferSrc+2);
fastCopyDifferentSizes(bufferDst+1, bufferSrc+3);
fastCopyDifferentSizes(bufferDst+2, bufferSrc+1);
fastCopyDifferentSizes(bufferDst+2, bufferSrc+3);
fastCopyDifferentSizes(bufferDst+3, bufferSrc+1);
fastCopyDifferentSizes(bufferDst+3, bufferSrc+2);
free(bufferSrc32);
free(bufferDst32);
}
void PspUnitTests::fastCopyDifferentSizes(byte *dst, byte *src, bool swap) {
fastCopySpecificSize(dst, src, 1);
fastCopySpecificSize(dst, src, 2, swap);
fastCopySpecificSize(dst, src, 4, swap);
fastCopySpecificSize(dst, src, 6, swap);
fastCopySpecificSize(dst, src, 8, swap);
fastCopySpecificSize(dst, src, 9);
fastCopySpecificSize(dst, src, 10, swap);
fastCopySpecificSize(dst, src, 11);
fastCopySpecificSize(dst, src, 12, swap);
fastCopySpecificSize(dst, src, 13);
fastCopySpecificSize(dst, src, 14, swap);
fastCopySpecificSize(dst, src, 15);
fastCopySpecificSize(dst, src, 16, swap);
fastCopySpecificSize(dst, src, 17);
fastCopySpecificSize(dst, src, 18, swap);
fastCopySpecificSize(dst, src, 19);
fastCopySpecificSize(dst, src, 20, swap);
fastCopySpecificSize(dst, src, 32, swap);
fastCopySpecificSize(dst, src, 33);
fastCopySpecificSize(dst, src, 34, swap);
fastCopySpecificSize(dst, src, 35);
fastCopySpecificSize(dst, src, 36, swap);
fastCopySpecificSize(dst, src, 50, swap);
fastCopySpecificSize(dst, src, 100, swap);
fastCopySpecificSize(dst, src, 500, swap);
fastCopySpecificSize(dst, src, 1000, swap);
}
void PspUnitTests::fastCopySpecificSize(byte *dst, byte *src, uint32 bytes, bool swap) {
memset(dst, 0, bytes);
PspMemory::fastCopy(dst, src, bytes);
if (swap) { // test swap also
memset(dst, 0, bytes);
// pixelformat for swap
PSPPixelFormat format;
format.set(PSPPixelFormat::Type_4444, true);
PspMemory::fastSwap(dst, src, bytes, format);
}
}
// This function leaks. For now I don't care
bool PspUnitTests::testFileSystem() {
// create memory
const uint32 BufSize = 32 * 1024;
char* buffer = new char[BufSize];
int i;
Common::WriteStream *wrStream;
Common::SeekableReadStream *rdStream;
PSP_INFO_PRINT("testing fileSystem...\n");
// fill buffer
for (i=0; i<(int)BufSize; i += 4) {
buffer[i] = 'A';
buffer[i + 1] = 'B';
buffer[i + 2] = 'C';
buffer[i + 3] = 'D';
}
// create a file
const char *path = "./file.test";
Common::FSNode file(path);
PSP_INFO_PRINT("creating write stream...\n");
wrStream = file.createWriteStream();
if (!wrStream) {
PSP_ERROR("%s couldn't be created.\n", path);
delete[] buffer;
return false;
}
// write contents
char* index = buffer;
int32 totalLength = BufSize;
int32 curLength = 50;
PSP_INFO_PRINT("writing...\n");
while(totalLength - curLength > 0) {
if ((int)wrStream->write(index, curLength) != curLength) {
PSP_ERROR("couldn't write %d bytes\n", curLength);
delete[] buffer;
delete wrStream;
return false;
}
totalLength -= curLength;
index += curLength;
//curLength *= 2;
//PSP_INFO_PRINT("write\n");
}
// write the rest
if ((int)wrStream->write(index, totalLength) != totalLength) {
PSP_ERROR("couldn't write %d bytes\n", curLength);
delete[] buffer;
delete wrStream;
return false;
}
delete wrStream;
PSP_INFO_PRINT("reading...\n");
rdStream = file.createReadStream();
if (!rdStream) {
PSP_ERROR("%s couldn't be created.\n", path);
delete[] buffer;
return false;
}
// seek to beginning
if (!rdStream->seek(0, SEEK_SET)) {
PSP_ERROR("couldn't seek to the beginning after writing the file\n");
delete[] buffer;
delete rdStream;
return false;
}
// read the contents
char *readBuffer = new char[BufSize + 4]();
index = readBuffer;
while (rdStream->read(index, 100) == 100) {
index += 100;
}
if (!rdStream->eos()) {
PSP_ERROR("didn't find EOS at end of stream\n");
delete[] buffer;
delete rdStream;
delete[] readBuffer;
return false;
}
// compare
for (i=0; i<(int)BufSize; i++)
if (buffer[i] != readBuffer[i]) {
PSP_ERROR("reading/writing mistake at %x. Got %x instead of %x\n", i, readBuffer[i], buffer[i]);
delete[] buffer;
delete rdStream;
delete[] readBuffer;
return false;
}
// Check for exceeding limit
for (i=0; i<4; i++) {
if (readBuffer[BufSize + i]) {
PSP_ERROR("read exceeded limits. %d = %x\n", BufSize + i, readBuffer[BufSize + i]);
}
}
delete[] buffer;
delete rdStream;
delete[] readBuffer;
PSP_INFO_PRINT("writing...\n");
wrStream = file.createWriteStream();
if (!wrStream) {
PSP_ERROR("%s couldn't be created.\n", path);
return false;
}
const char *phrase = "Jello is really fabulous";
uint32 phraseLen = strlen(phrase);
int ret;
if ((ret = wrStream->write(phrase, phraseLen)) != (int)phraseLen) {
PSP_ERROR("couldn't write phrase. Got %d instead of %d\n", ret, phraseLen);
delete wrStream;
return false;
}
PSP_INFO_PRINT("reading...\n");
delete wrStream;
rdStream = file.createReadStream();
if (!rdStream) {
PSP_ERROR("%s couldn't be created.\n", path);
return false;
}
char *readPhrase = new char[phraseLen + 2]();
if ((ret = rdStream->read(readPhrase, phraseLen) != phraseLen)) {
PSP_ERROR("read error on phrase. Got %d instead of %d\n", ret, phraseLen);
delete rdStream;
delete[] readPhrase;
return false;
}
for (i=0; i<(int)phraseLen; i++) {
if (readPhrase[i] != phrase[i]) {
PSP_ERROR("bad read/write in phrase. At %d, %x != %x\n", i, readPhrase[i], phrase[i]);
delete rdStream;
delete[] readPhrase;
return false;
}
}
// check for exceeding
if (readPhrase[i] != 0) {
PSP_ERROR("found excessive copy in phrase. %c at %d\n", readPhrase[i], i);
delete rdStream;
delete[] readPhrase;
return false;
}
PSP_INFO_PRINT("trying to read end...\n");
// seek to end
if (!rdStream->seek(0, SEEK_END)) {
PSP_ERROR("couldn't seek to end for append\n");
delete rdStream;
delete[] readPhrase;
return false;
};
// try to read
if (rdStream->read(readPhrase, 2) || !rdStream->eos()) {
PSP_ERROR("was able to read at end of file\n");
delete rdStream;
delete[] readPhrase;
return false;
}
delete rdStream;
delete[] readPhrase;
PSP_INFO_PRINT("ok\n");
return true;
}
void psp_tests() {
PSP_INFO_PRINT("in tests\n");
#ifdef PSP_ENABLE_SPEED_TESTS
// Speed tests
PspSpeedTests speedTests;
speedTests.tickSpeed();
speedTests.getMicrosSpeed();
speedTests.msReadSpeed();
speedTests.seekSpeed();
speedTests.msReadSpeed();
speedTests.threadFunctionsSpeed();
speedTests.semaphoreSpeed();
speedTests.semaphoreManyThreadSpeed();
speedTests.fastCopySpeed();
#endif
#ifdef PSP_ENABLE_UNIT_TESTS
// Unit tests
PspUnitTests unitTests;
//unitTests.testFastCopy();
unitTests.testFileSystem();
#endif
}
#endif /* (PSP_ENABLE_UNIT_TESTS) || defined(PSP_ENABLE_SPEED_TESTS) */

View File

@@ -0,0 +1,32 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _PSP_TESTS_H_
#define _PSP_TESTS_H_
//#define PSP_ENABLE_UNIT_TESTS // run unit tests
//#define PSP_ENABLE_SPEED_TESTS // run speed tests
#if defined(PSP_ENABLE_UNIT_TESTS) || defined(PSP_ENABLE_SPEED_TESTS)
void psp_tests();
#endif
#endif /* _PSP_TESTS_H_ */

View File

@@ -0,0 +1,229 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include <pspthreadman.h>
#include "backends/platform/psp/thread.h"
#include "backends/platform/psp/trace.h"
// Class PspThreadable --------------------------------------------------
// Inherit this to create C++ threads easily
bool PspThreadable::threadCreateAndStart(const char *threadName, int priority, int stackSize, bool useVfpu /*= false*/) {
DEBUG_ENTER_FUNC();
if (_threadId != -1) {
PSP_ERROR("thread already created!\n");
return false;
}
_threadId = sceKernelCreateThread(threadName, __threadCallback, priority, stackSize, THREAD_ATTR_USER, 0); // add VFPU support
if (_threadId < 0) {
PSP_ERROR("failed to create %s thread. Error code %d\n", threadName, _threadId);
return false;
}
// We want to pass the pointer to this, but we'll have to take address of this so use a little trick
PspThreadable *_this = this;
if (sceKernelStartThread(_threadId, sizeof(uint32 *), &_this) < 0) {
PSP_ERROR("failed to start %s thread id[%d]\n", threadName, _threadId);
return false;
}
PSP_DEBUG_PRINT("Started %s thread with id[%x]\n", threadName, _threadId);
return true;
}
// Callback function to be called by PSP kernel
int PspThreadable::__threadCallback(SceSize, void *__this) {
DEBUG_ENTER_FUNC();
PspThreadable *_this = *(PspThreadable **)__this; // Dereference the copied value which was 'this'
_this->threadFunction(); // call the virtual function
return 0;
}
// PspThread class
// Utilities to access general thread functions
void PspThread::delayMillis(uint32 ms) {
sceKernelDelayThread(ms * 1000);
}
void PspThread::delayMicros(uint32 us) {
sceKernelDelayThread(us);
}
// Class PspSemaphore ------------------------------------------------
//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */
//#define __PSP_DEBUG_PRINT__ /* For debug printouts */
#include "backends/platform/psp/trace.h"
PspSemaphore::PspSemaphore(int initialValue, int maxValue/*=255*/) {
DEBUG_ENTER_FUNC();
_handle = 0;
_handle = (uint32)sceKernelCreateSema("ScummVM Sema", 0 /* attr */,
initialValue, maxValue,
0 /*option*/);
if (!_handle)
PSP_ERROR("failed to create semaphore.\n");
}
PspSemaphore::~PspSemaphore() {
DEBUG_ENTER_FUNC();
if (_handle)
if (sceKernelDeleteSema((SceUID)_handle) < 0)
PSP_ERROR("failed to delete semaphore.\n");
}
int PspSemaphore::numOfWaitingThreads() {
DEBUG_ENTER_FUNC();
SceKernelSemaInfo info;
info.numWaitThreads = 0;
if (sceKernelReferSemaStatus((SceUID)_handle, &info) < 0)
PSP_ERROR("failed to retrieve semaphore info for handle %d\n", _handle);
return info.numWaitThreads;
}
int PspSemaphore::getValue() {
DEBUG_ENTER_FUNC();
SceKernelSemaInfo info;
info.currentCount = 0;
if (sceKernelReferSemaStatus((SceUID)_handle, &info) < 0)
PSP_ERROR("failed to retrieve semaphore info for handle %d\n", _handle);
return info.currentCount;
}
bool PspSemaphore::pollForValue(int value) {
DEBUG_ENTER_FUNC();
if (sceKernelPollSema((SceUID)_handle, value) < 0)
return false;
return true;
}
// false: timeout or error
bool PspSemaphore::takeWithTimeOut(uint timeOut) {
DEBUG_ENTER_FUNC();
uint *pTimeOut = 0;
if (timeOut)
pTimeOut = &timeOut;
if (sceKernelWaitSema(_handle, 1, pTimeOut) < 0) // we always wait for 1
return false;
return true;
}
bool PspSemaphore::give(int num /*=1*/) {
DEBUG_ENTER_FUNC();
if (sceKernelSignalSema((SceUID)_handle, num) < 0)
return false;
return true;
}
// Class PspMutex ------------------------------------------------------------
bool PspMutex::lock() {
DEBUG_ENTER_FUNC();
int threadId = sceKernelGetThreadId();
bool ret = true;
if (_ownerId == threadId) {
_recursiveCount++;
} else {
ret = _semaphore.take();
_ownerId = threadId;
_recursiveCount = 0;
}
return ret;
}
bool PspMutex::unlock() {
DEBUG_ENTER_FUNC();
int threadId = sceKernelGetThreadId();
bool ret = true;
if (_ownerId != threadId) {
PSP_ERROR("attempt to unlock mutex by thread[%x] as opposed to owner[%x]\n",
threadId, _ownerId);
return false;
}
if (_recursiveCount) {
_recursiveCount--;
} else {
_ownerId = 0;
ret = _semaphore.give(1);
}
return ret;
}
// Class PspCondition -------------------------------------------------
// Release all threads waiting on the condition
void PspCondition::releaseAll() {
_mutex.lock();
if (_waitingThreads > _signaledThreads) { // we have signals to issue
int numWaiting = _waitingThreads - _signaledThreads; // threads we haven't signaled
_signaledThreads = _waitingThreads;
_waitSem.give(numWaiting);
_mutex.unlock();
for (int i=0; i<numWaiting; i++) // wait for threads to tell us they're awake
_doneSem.take();
} else {
_mutex.unlock();
}
}
// Mutex must be taken before entering wait
void PspCondition::wait(PspMutex &externalMutex) {
_mutex.lock();
_waitingThreads++;
_mutex.unlock();
externalMutex.unlock(); // must unlock external mutex
_waitSem.take(); // sleep on the wait semaphore
// let the signaling thread know we're done
_mutex.lock();
if (_signaledThreads > 0 ) {
_doneSem.give(); // let the thread know
_signaledThreads--;
}
_waitingThreads--;
_mutex.unlock();
externalMutex.lock(); // must lock external mutex here for continuation
}

View File

@@ -0,0 +1,106 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PSP_THREAD_H
#define PSP_THREAD_H
#include <pspthreadman.h>
#include "common/mutex.h"
// class to inherit for creating threads
class PspThreadable {
protected:
int _threadId;
virtual void threadFunction() = 0; // this function will be called when the thread starts
public:
PspThreadable() : _threadId(-1) {} // constructor
virtual ~PspThreadable() {} // destructor
static int __threadCallback(SceSize, void *__this); // used to get called by sceKernelStartThread() Don't override
bool threadCreateAndStart(const char *threadName, int priority, int stackSize, bool useVfpu = false);
};
// class for thread utils
class PspThread {
public:
// static functions
static void delayMillis(uint32 ms); // delay the current thread
static void delayMicros(uint32 us);
};
class PspSemaphore {
private:
uint32 _handle;
public:
PspSemaphore(int initialValue, int maxValue=255);
~PspSemaphore();
bool take() { return takeWithTimeOut(0); }
bool takeWithTimeOut(uint timeOut);
bool give(int num=1);
bool pollForValue(int value); // check for a certain value
int numOfWaitingThreads();
int getValue();
};
class PspMutex : public Common::MutexInternal {
private:
PspSemaphore _semaphore;
int _recursiveCount;
int _ownerId;
public:
PspMutex(bool initialValue) : _semaphore(initialValue ? 1 : 0, 255), _recursiveCount(0), _ownerId(0) {} // initial, max value
bool lock();
bool unlock();
bool poll() { return _semaphore.pollForValue(1); }
int numOfWaitingThreads() { return _semaphore.numOfWaitingThreads(); }
bool getValue() { return (bool)_semaphore.getValue(); }
};
class PspCondition {
private:
PspMutex _mutex;
int _waitingThreads;
int _signaledThreads;
PspSemaphore _waitSem;
PspSemaphore _doneSem;
public:
PspCondition() : _mutex(true), _waitingThreads(0), _signaledThreads(0),
_waitSem(0), _doneSem(0) {}
void wait(PspMutex &externalMutex);
void releaseAll();
};
enum ThreadPriority {
PRIORITY_MAIN_THREAD = 36,
PRIORITY_TIMER_THREAD = 30,
PRIORITY_AUDIO_THREAD = 25, // must be higher than timer or we get stuttering
PRIORITY_POWER_THREAD = 20, // quite a light thread
PRIORITY_DISPLAY_THREAD = 17 // very light thread for callbacks only
};
enum StackSizes {
STACK_DEFAULT = 4 * 1024,
STACK_AUDIO_THREAD = 16 * 1024,
STACK_TIMER_THREAD = 32 * 1024,
STACK_DISPLAY_THREAD = 2 * 1024,
STACK_POWER_THREAD = 4 * 1024
};
#endif /* PSP_THREADS_H */

View File

@@ -0,0 +1,149 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Disable printf override in common/forbidden.h to avoid
// clashes with pspdebug.h from the PSP SDK.
// That header file uses
// __attribute__((format(printf,1,2)));
// which gets messed up by our override mechanism; this could
// be avoided by either changing the PSP SDK to use the equally
// legal and valid
// __attribute__((format(__printf__,1,2)));
// or by refining our printf override to use a varadic macro
// (which then wouldn't be portable, though).
// Anyway, for now we just disable the printf override globally
// for the PSP port
#define FORBIDDEN_SYMBOL_EXCEPTION_printf
#include <pspkernel.h>
#include <pspdebug.h>
#include <stdarg.h>
#include <stdio.h>
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "common/scummsys.h"
int psp_debug_indent = 0;
bool firstWriteToFile = true;
void PspDebugTrace(bool alsoToScreen, const char *format, ...) {
va_list opt;
char buffer[2048];
int bufsz;
FILE *fd = 0;
va_start(opt, format);
bufsz = vsnprintf(buffer, (size_t) sizeof(buffer), format, opt);
va_end(opt);
if (firstWriteToFile) {
fd = fopen("SCUMMTRACE.TXT", "wb"); // erase the file the first time we write
firstWriteToFile = false;
} else {
fd = fopen("SCUMMTRACE.TXT", "ab");
}
if (fd == 0)
return;
fwrite(buffer, 1, bufsz, fd);
fclose(fd);
if (alsoToScreen)
fprintf(stderr, buffer);
}
// Assembly functions to get the Return Address register and the Stack pointer register
#define GET_RET(retAddr) \
asm volatile ("move %0,$ra\n\t" \
: "=&r" (retAddr) : )
#define GET_SP(stackPtr) \
asm volatile ("move %0,$sp\n\t" \
: "=&r" (stackPtr) : )
// Function to retrieve a backtrace for the MIPS processor
// This is not trivial since the MIPS doesn't use a frame pointer.
// Takes the number of levels wanted above the calling function (included) and an array of void *
//
void mipsBacktrace(uint32 levels, void **addresses) {
// get the current return address
byte *retAddr;
byte *stackPointer;
GET_RET(retAddr);
GET_SP(stackPointer);
char string[100];
if (!levels)
return;
memset(addresses, 0, sizeof(void *) * levels);
uint32 curLevel = 0;
const uint32 SP_SUBTRACT = 0x27bd8000; // The instruction to subtract from the SP looks like this
const uint32 SP_SUB_HIGH_MASK = 0xffff8000; // The mask to check for the subtract SP instruction
const uint32 SP_SUB_LOW_MASK = 0x0000ffff; // The mask that gives us how much was subtracted
// make sure we go out of the stack of this current level
// we already have the return address for this level from the register
if (curLevel < levels) {
void *thisFunc = (void *)mipsBacktrace;
for (uint32 *seekPtr = (uint32 *)thisFunc; ; seekPtr++) {
if ((*seekPtr & SP_SUB_HIGH_MASK) == SP_SUBTRACT) {
// we found the $sp subtraction at the beginning of the function
int16 subAmount = (int16)((*seekPtr) & SP_SUB_LOW_MASK);
//sprintf(string, "found local $sp sub at %p. Data[%x]. Sub amount %d\n", seekPtr, *seekPtr, subAmount);
//fputs(string, stderr);
stackPointer -= subAmount;
byte *testRetAddr = (byte *)*((uint32 *)(stackPointer - 4));
if (testRetAddr != retAddr) {
sprintf(string, "mismatch in testretAddr.\n");
fputs(string, stderr);
}
break;
}
}
}
// keep scanning while more levels are requested
while (curLevel < levels) {
// now scan backwards from the return address to find the size of the stack
for(uint32 *seekPtr = (uint32 *)retAddr; ; seekPtr--) {
if (((*seekPtr) & SP_SUB_HIGH_MASK) == SP_SUBTRACT) {
// we found the $sp subtraction at the beginning of the function
int16 subAmount = (int16)((*seekPtr) & SP_SUB_LOW_MASK);
//sprintf(string, "found $sp sub at %p. Data[%x]. Sub amount %d\n", seekPtr, *seekPtr, subAmount);
//fputs(string, stderr);
stackPointer -= subAmount;
retAddr = (byte *)*((uint32 *)(stackPointer - 4));
if (retAddr < (byte *)0x8900000 || retAddr > (byte *)0xC900000) {
sprintf(string, "invalid retAddr %p\n", retAddr);
fputs(string, stderr);
return;
}
//sprintf(string, "retAddr[%p]\n", retAddr);
//fputs(string, stderr);
addresses[curLevel++] = retAddr;
break;
}
}
}
}

View File

@@ -0,0 +1,126 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// This section can only be included once
#ifndef PSP_TRACE_H
#define PSP_TRACE_H
#include "common/str.h"
#define __PSP_PRINT_TO_FILE_AND_SCREEN__
/* Choose to print to file/screen/both */
#ifdef __PSP_PRINT_TO_FILE__
#define __PSP_PRINT__(format,...) PspDebugTrace(false, format, ## __VA_ARGS__)
#elif defined(__PSP_PRINT_TO_FILE_AND_SCREEN__)
#define __PSP_PRINT__(format,...) PspDebugTrace(true, format, ## __VA_ARGS__)
#else /* default - print to screen */
#define __PSP_PRINT__(format,...) fprintf(stderr, format, ## __VA_ARGS__)
#endif /* PSP_PRINT_TO_FILE/SCREEN */
/* Error function - always print to file as well */
#define PSP_ERROR(format,...) PspDebugTrace(true, "Error in %s: " format, __PRETTY_FUNCTION__, ## __VA_ARGS__)
/* Do the indent */
#define __PSP_INDENT__ for(int _i=psp_debug_indent; _i>0; _i--) \
__PSP_PRINT__( " ")
/* always print */
#define PSP_INFO_PRINT(format,...) __PSP_PRINT__(format, ## __VA_ARGS__)
/* always print, with indent */
#define PSP_INFO_PRINT_INDENT(format,...) { __PSP_INDENT__; \
__PSP_PRINT__(format, ## __VA_ARGS__); }
void PspDebugTrace(bool alsoToScreen, const char *format, ...);
void mipsBacktrace(uint32 levels, void **addresses);
extern int psp_debug_indent;
// We use this class to print out function calls on the stack in an easy way.
//
class PSPStackDebugFuncs {
Common::String _name;
public:
PSPStackDebugFuncs(const char *name) : _name(name) {
PSP_INFO_PRINT_INDENT("++ %s\n", _name.c_str());
psp_debug_indent++;
}
~PSPStackDebugFuncs() {
psp_debug_indent--;
if (psp_debug_indent < 0)
PSP_ERROR("debug indent < 0\n");
PSP_INFO_PRINT_INDENT("-- %s\n", _name.c_str());
}
};
#endif /* PSP_TRACE_H */
// From here on, we allow multiple redefinitions
// Use these defines for debugging
//#define __PSP_PRINT_TO_FILE__
//#define __PSP_PRINT_TO_FILE_AND_SCREEN__
//#define __PSP_DEBUG_FUNCS__ /* can put this locally too */
//#define __PSP_DEBUG_PRINT__
#undef PSP_DEBUG_PRINT
#undef PSP_DEBUG_PRINT_FUNC
#undef PSP_DEBUG_PRINT_SAMELN
#undef PSP_DEBUG_DO
#undef DEBUG_ENTER_FUNC
#undef DEBUG_EXIT_FUNC
#ifdef __PSP_DEBUG_PRINT__
/* printf with indents */
#define PSP_DEBUG_PRINT_SAMELN(format,...) __PSP_PRINT__(format, ## __VA_ARGS__)
#define PSP_DEBUG_PRINT(format,...) PSP_INFO_PRINT_INDENT(format, ## __VA_ARGS__)
#define PSP_DEBUG_PRINT_FUNC(format,...) { __PSP_INDENT__; \
__PSP_PRINT__("In %s: " format, __PRETTY_FUNCTION__, ## __VA_ARGS__); }
#define PSP_DEBUG_DO(x) (x)
#else /* no debug print */
#define PSP_DEBUG_PRINT_SAMELN(format,...)
#define PSP_DEBUG_PRINT(format,...)
#define PSP_DEBUG_PRINT_FUNC(format,...)
#define PSP_DEBUG_DO(x)
#endif /* __PSP_DEBUG_PRINT__ */
/* We don't need anything but this line at the beginning of each function to debug function calls */
/* Debugging function calls */
#ifdef __PSP_DEBUG_FUNCS__
#define DEBUG_ENTER_FUNC() volatile PSPStackDebugFuncs __foo(__PRETTY_FUNCTION__)
#else /* Don't debug function calls */
#define DEBUG_ENTER_FUNC()
#endif /* __PSP_DEBUG_FUNCS__ */
// Undef the main defines for next time
#undef __PSP_PRINT_TO_FILE__
#undef __PSP_PRINT_TO_FILE_AND_SCREEN__
#undef __PSP_DEBUG_FUNCS__
#undef __PSP_DEBUG_PRINT__