Initial commit
171
backends/platform/psp/README.PSP
Normal 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)
|
||||
171
backends/platform/psp/README.PSP.in
Normal 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)
|
||||
144
backends/platform/psp/audio.cpp
Normal 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;
|
||||
}
|
||||
65
backends/platform/psp/audio.h
Normal 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 */
|
||||
18
backends/platform/psp/build-psp.sh
Normal 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
|
||||
|
||||
327
backends/platform/psp/cursor.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
81
backends/platform/psp/cursor.h
Normal 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 */
|
||||
206
backends/platform/psp/default_display_client.cpp
Normal 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
|
||||
}
|
||||
101
backends/platform/psp/default_display_client.h
Normal 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 */
|
||||
773
backends/platform/psp/display_client.cpp
Normal 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);
|
||||
}
|
||||
244
backends/platform/psp/display_client.h
Normal 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 */
|
||||
472
backends/platform/psp/display_manager.cpp
Normal 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;
|
||||
}
|
||||
167
backends/platform/psp/display_manager.h
Normal 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 */
|
||||
55
backends/platform/psp/dummy.cpp
Normal 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;
|
||||
}
|
||||
BIN
backends/platform/psp/icon0.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
320
backends/platform/psp/image_viewer.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
104
backends/platform/psp/image_viewer.h
Normal 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 */
|
||||
615
backends/platform/psp/input.cpp
Normal 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);
|
||||
}
|
||||
200
backends/platform/psp/input.h
Normal 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 */
|
||||
BIN
backends/platform/psp/kbd.zip
Normal file
BIN
backends/platform/psp/kbd/keys.xcf
Normal file
BIN
backends/platform/psp/kbd/keys4.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
backends/platform/psp/kbd/keys_c.xcf
Normal file
BIN
backends/platform/psp/kbd/keys_c4.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
backends/platform/psp/kbd/keys_s.xcf
Normal file
BIN
backends/platform/psp/kbd/keys_s4.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
backends/platform/psp/kbd/keys_s_c.xcf
Normal file
BIN
backends/platform/psp/kbd/keys_s_c4.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
backends/platform/psp/kbd/nums.xcf
Normal file
BIN
backends/platform/psp/kbd/nums4.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
backends/platform/psp/kbd/nums_s.xcf
Normal file
BIN
backends/platform/psp/kbd/nums_s4.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
9
backends/platform/psp/kbd/readme_kbd.txt
Normal 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.)
|
||||
BIN
backends/platform/psp/kbd/syms.xcf
Normal file
BIN
backends/platform/psp/kbd/syms4.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
backends/platform/psp/kbd/syms_s.xcf
Normal file
BIN
backends/platform/psp/kbd/syms_s4.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
403
backends/platform/psp/memory.cpp
Normal 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));
|
||||
}
|
||||
}
|
||||
123
backends/platform/psp/memory.h
Normal 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 */
|
||||
27
backends/platform/psp/module.mk
Normal 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)))
|
||||
516
backends/platform/psp/mp3.cpp
Normal 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
@@ -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
|
||||
442
backends/platform/psp/osys_psp.cpp
Normal 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";
|
||||
}
|
||||
147
backends/platform/psp/osys_psp.h
Normal 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 */
|
||||
BIN
backends/platform/psp/pic0.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
backends/platform/psp/pic1.png
Normal file
|
After Width: | Height: | Size: 154 KiB |
221
backends/platform/psp/png_loader.cpp
Normal 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;
|
||||
}
|
||||
72
backends/platform/psp/png_loader.h
Normal 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 */
|
||||
60
backends/platform/psp/portdefs.h
Normal 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 */
|
||||
339
backends/platform/psp/powerman.cpp
Normal 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
|
||||
}
|
||||
134
backends/platform/psp/powerman.h
Normal 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 */
|
||||
68
backends/platform/psp/psp.mk
Normal 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
|
||||
3
backends/platform/psp/psp.spec
Normal file
@@ -0,0 +1,3 @@
|
||||
%rename lib old_lib
|
||||
*lib:
|
||||
-lstdc++ -lpspprof -lpspvfpu -lpspdebug -lpspgu -lpspge -lpspdisplay -lpspctrl -lpsppower -lpsphprm -lpspaudio -lpspaudiocodec %(old_lib)
|
||||
198
backends/platform/psp/psp_main.cpp
Normal 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;
|
||||
}
|
||||
571
backends/platform/psp/pspkeyboard.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
107
backends/platform/psp/pspkeyboard.h
Normal 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 */
|
||||
188
backends/platform/psp/psppixelformat.cpp
Normal 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;
|
||||
}
|
||||
244
backends/platform/psp/psppixelformat.h
Normal 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 */
|
||||
84
backends/platform/psp/rtc.cpp
Normal 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];
|
||||
}
|
||||
46
backends/platform/psp/rtc.h
Normal 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
|
||||
760
backends/platform/psp/tests.cpp
Normal 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) */
|
||||
32
backends/platform/psp/tests.h
Normal 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_ */
|
||||
229
backends/platform/psp/thread.cpp
Normal 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
|
||||
}
|
||||
106
backends/platform/psp/thread.h
Normal 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 */
|
||||
149
backends/platform/psp/trace.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
126
backends/platform/psp/trace.h
Normal 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__
|
||||