Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

pspkeyboard.cpp

/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-1-1-1/backends/platform/psp/pspkeyboard.cpp $
 * $Id: pspkeyboard.cpp 48148 2010-02-27 17:02:58Z jvprat $
 *
 */

//#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/game4xx/scummvm-1.0.0rc1/"      /* path to kbd.zip */
#endif
//#define PSP_KB_DEBUG

#include <malloc.h>
#include "pspkernel.h"
#include <pspgu.h>
#include "png.h"
#include "pspkeyboard.h"
#include "common/keyboard.h"
#include "common/fs.h"
#include "common/unzip.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(F8),  C(F7),  C(F6),  C(F9),  C(F10) },
            { K('5'),  K('6'), K('7'), K('8'), K(0),   K(0)   },
            { C(F1),   C(F4),  C(F3),  C(F2),  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)   }
      }
};

// Read function for png library to be able to read from our SeekableReadStream
//
void pngReadStreamRead(png_structp png_ptr, png_bytep data, png_size_t length) {
      Common::SeekableReadStream *file;

      file = (Common::SeekableReadStream *)png_ptr->io_ptr;

      file->read(data, length);
}

// Constructor
PSPKeyboard::PSPKeyboard() {
      _init = false;                // we're not initialized yet
      _prevButtons = 0;       // Reset previous buttons
      _dirty = true;          // keyboard needs redrawing
      _mode = 0;              // charset selected. (0 - letters, 1 - uppercase 2 - numbers 3 - symbols)
      _oldCursor = kCenter;   // Center cursor by default
      _moved_x = 20;                // Default starting location
      _moved_y = 50;
      _moved = false;               // Keyboard wasn't moved recently
      _state = kInvisible;    // We start invisible
      _lastState = kInvisible;
}

// Destructor
PSPKeyboard::~PSPKeyboard() {
      if (!_init) return;

      int a;
      for (a = 0; a < guiStringsSize; a++) {
            free(_keyTextures[a].texture);
            free(_keyTextures[a].palette);
            _keyTextures[a].texture = NULL;
            _keyTextures[a].palette = NULL;
      }
      _init = false;
}

// 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"
};

// 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.
 */
bool PSPKeyboard::processInput(Common::Event &event, SceCtrlData &pad, bool &usedInput) {
      usedInput = false;                  // Assume we don't use an input
      bool haveEvent = false;       // Whether we have an event for the event manager to process
      event.kbd.flags = 0;
      unsigned int keyDown;
      uint32 buttonsChanged = _prevButtons ^ pad.Buttons;

      if (!_init)                         // In case we never had init
            goto END;

      if (PRESSED(PSP_CTRL_SELECT)) {
            _lastState = _state;
            _state = kMove;               // Check for move or visible state
            usedInput = true;
            goto END;
      }

      if (_state == kInvisible)     // Return if we're invisible
            goto END;

      if (DOWN(PSP_DPAD | PSP_4BUTTONS | PSP_CTRL_LTRIGGER | PSP_CTRL_RTRIGGER | PSP_CTRL_START))
            usedInput = true;       // for now, we neutralize all button inputs

      // Check for moving the keyboard onscreen
      if (_state == kMove) {
            if (UNPRESSED(PSP_CTRL_SELECT)) {
                  _state = (_lastState == kInvisible) ? kDefault : kInvisible;                  // Back to previous state
                  _dirty = true;

                  if (_moved) {                       // We moved the keyboard. Keep the keyboard onscreen
                        _state = kDefault;
                        _moved = false;               // reset moved flag
                  }
            }
            else if (DOWN(PSP_DPAD)) {
                  _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);
            }
            usedInput = true; // We used up the input (select was held down)
            goto END;
      }


      // Handle 4 buttons + 2 triggers
      if (_state == kDefault || _state == kCornersSelected) {
            unsigned int changed;

            if (_state == kDefault) {     // Handle default state
                  changed = CHANGED(PSP_4BUTTONS);                // We only care about the 4 buttons
                  if (PRESSED(PSP_CTRL_LTRIGGER)) {               // Don't say we used up the input
                        _state = kLTriggerDown;
                        goto END;
                  }
                  else if (PRESSED(PSP_CTRL_RTRIGGER)) {          // Don't say we used up the input
                        _state = kRTriggerDown;
                        goto END;
                  }

                  if (DOWN(PSP_4BUTTONS))
                        usedInput = true;                               // Make sure these button presses don't get through
            } else { /* _state == kCornersSelected */
                  // We care about 4 buttons + triggers (for letter selection)
                  changed = CHANGED(PSP_4BUTTONS | PSP_CTRL_RTRIGGER | PSP_CTRL_LTRIGGER);

                  if (DOWN(PSP_4BUTTONS | PSP_CTRL_RTRIGGER | PSP_CTRL_LTRIGGER))
                        usedInput = true; // Make sure these button presses don't get through
            }

            if (changed) { //pressing a char select button -- both states

                  int innerChoice;

                  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  /* (UNPRESSED(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
                  usedInput = true;
            }
      }

      // Check for movement of cursor
      if (_state == kDefault || _state == kCornersSelected) {
            // Check if a dependent button is down
            if (_state == kDefault)
                  keyDown = DOWN(PSP_4BUTTONS);
            else /* if (_state == kCornersSelected) */
                  keyDown = DOWN(PSP_4BUTTONS | PSP_CTRL_RTRIGGER | PSP_CTRL_LTRIGGER);

            Cursor cursor = _oldCursor;

            // Find where the cursor is pointing
            if (keyDown == false) { // Don't allow movement if a nub-dependent key is down
                  cursor = kCenter;
                  _state = kDefault;

                  if (DOWN(PSP_DPAD)) {
                        _state = kCornersSelected;
                        usedInput = true; // Make sure the pressed d-pad is used up

                        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;
                  usedInput = true;       // We 'used up' the input
                  //goto END;                   // We didn't find an event
            }
      }

      // Deal with trigger states
      if (_state == kRTriggerDown) {
            usedInput = true;

            if (UNPRESSED(PSP_CTRL_RTRIGGER)) {
                  _dirty = true;

                  if(_mode > 1)
                        _mode = 0;
                  else
                        _mode = (_mode == 0) ? 1 : 0;

                  usedInput = true;
                  _state = kDefault;

                  goto END;
            }
      } else if (_state == kLTriggerDown) {
            usedInput = true;

            if (UNPRESSED(PSP_CTRL_LTRIGGER)) {
                  _dirty = true;

                  if(_mode < 2)
                        _mode = 2;
                  else
                        _mode = (_mode == 2) ? 3 : 2;

                  usedInput = true;
                  _state = kDefault;

                  goto END;
            }
      }

      // Handle start button: enter plus make keyboard invisible
      if (CHANGED(PSP_CTRL_START)) {
            event.kbd.ascii = '\n';
            event.kbd.keycode = Common::KEYCODE_RETURN;
            event.type = DOWN(PSP_CTRL_START) ? Common::EVENT_KEYDOWN : Common::EVENT_KEYUP;
            usedInput = true;                         // Don't let start get through
            haveEvent = true;
            if (UP(PSP_CTRL_START))
                  _state = kInvisible;                // Make us invisible if unpressed
      }

END:
      _prevButtons = pad.Buttons;
      return haveEvent;
}


/* move the position the keyboard is currently drawn at */
void PSPKeyboard::moveTo(const int newX, const int newY) {
      _moved_x = newX;
      _moved_y = newY;
}

/* move the position the keyboard is currently drawn at */
void PSPKeyboard::increaseKeyboardLocationX(int amount) {
      int newX = _moved_x + amount;

      if (newX > PSP_SCREEN_WIDTH - 5 || newX < 0 - 140)    // clamp
            return;
      _moved_x = newX;
}

/* move the position the keyboard is currently drawn at */
void PSPKeyboard::increaseKeyboardLocationY(int amount) {
      int newY = _moved_y + amount;

      if (newY > PSP_SCREEN_HEIGHT - 5 || newY < 0 - 140)   // clamp
            return;
      _moved_y = newY;
}

/* draw the keyboard at the current position */
void PSPKeyboard::render() {
      _dirty = false;
      int x, y;

      switch(_oldCursor) {
      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;
      }

      // Draw the background letters
      surface_draw_offset(&_keyTextures[_mode<<1], 0, 0, 0, 0,
                  _keyTextures[_mode<<1].texture_width,
                  _keyTextures[_mode<<1].texture_height);

      // Draw the current Highlighted Selector (orange bit)
      surface_draw_offset(&_keyTextures[(_mode<<1) + 1],
                  //Offset from the current draw position to render at
                  x * 43, y * 43,
                  //internal offset of the image
                  x * 64, y * 64,
                  //size to render (always the same)
                  64, 64);
}


//  Render the given surface at the current screen position offset by screenX, screenY
//  the surface will be internally offset by offsetX,offsetY. And the size of it to be drawn will be intWidth,intHeight
void PSPKeyboard::surface_draw_offset(struct gu_surface* surface, int screenX, int screenY, int offsetX, int offsetY, int intWidth, int intHeight) {
      sceGuAlphaFunc( GU_GREATER, 0, 0xff );
      sceGuEnable( GU_ALPHA_TEST );
      sceGuTexFunc(GU_TFX_BLEND,GU_TCC_RGBA);
      sceGuTexEnvColor(0xFF000000);
      sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 );
      sceGuEnable(GU_BLEND);
      if (surface->paletteSize == 256) {  // 8-bit
            sceGuClutMode(GU_PSM_8888, 0, 0xFF, 0);
            sceGuClutLoad(32, surface->palette); // upload 32*8 entries (256)
            sceGuTexMode(GU_PSM_T8, 0, 0, 0);   // 8-bit image
      } else if (surface->paletteSize == 16) {  // 4-bit
            sceGuClutMode(GU_PSM_8888, 0, 0x0F, 0);
            sceGuClutLoad(2, surface->palette); // upload 2*8 entries (16)
            sceGuTexMode(GU_PSM_T4, 0, 0, 0);   // 4-bit image
      } else {    // 32-bit
            sceGuTexMode(GU_PSM_8888,0,0,GU_FALSE);
      }
      sceGuTexImage(0,surface->surface_width, surface->surface_height,surface->surface_width, surface->texture);
      sceGuTexFunc(GU_TFX_MODULATE,GU_TCC_RGBA);

      Vertex* c_vertices = (Vertex*)sceGuGetMemory(2 * sizeof(Vertex));

      c_vertices[0].u         = offsetX;
      c_vertices[0].v         = offsetY;
      c_vertices[0].x         = _moved_x + screenX;
      c_vertices[0].y         = _moved_y + screenY;
      c_vertices[0].z         = 0;
      c_vertices[0].color     = 0xFFFFFFFF;

      c_vertices[1].u         = offsetX + intWidth;
      c_vertices[1].v         = offsetY + intHeight;
      c_vertices[1].x         = _moved_x + screenX + intWidth;
      c_vertices[1].y         = _moved_y + screenY + intHeight;
      c_vertices[1].z         = 0;
      c_vertices[1].color     = 0xFFFFFFFF;

      sceGuDrawArray(GU_SPRITES,GU_TEXTURE_32BITF|GU_COLOR_8888|GU_VERTEX_32BITF|GU_TRANSFORM_2D,2,0,c_vertices);

      sceGuDisable( GU_BLEND );
      sceGuDisable( GU_ALPHA_TEST );
}

/* load all the guibits that make up the OSK */
bool PSPKeyboard::load() {
      unsigned char *temp_texture = NULL;
      uint32 *temp_palette = NULL;
      int a;

      if (_init)
            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
#ifdef PSP_KB_DEBUG
      fprintf(stderr, "path = %s\n", node.getPath().c_str());
#endif

      Common::Archive *fileArchive = NULL;
      Common::Archive *zipArchive = NULL;
      Common::SeekableReadStream * file = 0;

      if (node.getChild("kbd").exists() && node.getChild("kbd").isDirectory()) {
#ifdef PSP_KB_DEBUG
            fprintf(stderr, "found directory ./kbd\n");
#endif
            fileArchive = new Common::FSDirectory(node.getChild("kbd"));
      }
      if (node.getChild("kbd.zip").exists()) {
#ifdef PSP_KB_DEBUG
            fprintf(stderr, "found kbd.zip\n");
#endif
            zipArchive  = Common::makeZipArchive(node.getChild("kbd.zip"));
      }

      // Loop through different png images
      for (a = 0; a < guiStringsSize; a++) {
            uint32 height = 0, width = 0, paletteSize = 0;

#ifdef PSP_KB_DEBUG
            fprintf(stderr, "load(): Opening %s.\n", _guiStrings[a]);
#endif

            // Look for the file in the kbd directory
            if (fileArchive && fileArchive->hasFile(_guiStrings[a])) {

#ifdef PSP_KB_DEBUG
                  fprintf(stderr, "load(): found it in kbd directory.\n");
#endif

                  file = fileArchive->createReadStreamForMember(_guiStrings[a]);
                  if (!file) {
                        fprintf(stderr, "load(): Can't open kbd/%s for keyboard. No keyboard will load.\n", _guiStrings[a]);
                        goto ERROR;
                  }
            }
            // We didn't find it. Look for it in the zip file
            else if (zipArchive && zipArchive->hasFile(_guiStrings[a])) {

#ifdef PSP_KB_DEBUG
                  fprintf(stderr, "load(): found it in kbd.zip.\n");
#endif

                  file = zipArchive->createReadStreamForMember(_guiStrings[a]);
                  if (!file) {
                        fprintf(stderr, "Can't open %s in kbd.zip for keyboard. No keyboard will load.\n", _guiStrings[a]);
                        goto ERROR;
                  }
            } else {    // Couldn't find the file
                  fprintf(stderr, "load(): Can't find %s for keyboard. No keyboard will load.\n", _guiStrings[a]);
                  goto ERROR;
            }

            if (get_png_image_size(file, &width, &height, &paletteSize) == 0) { // Check image size and palette size
                  // Allocate memory for image
#ifdef PSP_KB_DEBUG
                  fprintf(stderr, "load(): width=%d, height=%d, paletteSize=%d\n", width, height, paletteSize);
#endif

                  if (paletteSize) {      // 8 or 4-bit image
                        uint32 textureSize = 0;

                        if (paletteSize <= 16) { // 4 bit
                              paletteSize = 16;
                              textureSize = (width * height)>>1;
                        } else if (paletteSize <= 256){                 // 8-bit image
                              paletteSize = 256;
                              textureSize = width * height;
                        } else {
                              fprintf(stderr, "Error: palette of %d too big!\n", paletteSize);
                              goto ERROR;
                        }
#ifdef PSP_KB_DEBUG
                        fprintf(stderr, "load(): allocating %d bytes for texture and %d for palette\n", textureSize, paletteSize*4);
#endif
                        temp_texture = (u8 *)malloc(textureSize);
                        temp_palette = (uint32 *)memalign(16, paletteSize<<2);
                        memset(temp_palette, 0, paletteSize<<2);  // Set to 0 since we might only fill some of it
                  } else {                      // 32-bit image
                        temp_texture = (unsigned char *)malloc((width * height)<<2);
                  }

                  // Try to load the image
                  file->seek(0);    // Go back to start

                  if (load_png_image(file, temp_texture, temp_palette) != 0)
                        goto ERROR;
                  else {      // Success
#ifdef PSP_KB_DEBUG
                        fprintf(stderr, "Managed to load the image.\n");
#endif
                        // we need to store the texture in an image of width and heights of 2^n sizes
                        _keyTextures[a].texture_width       = width;                                  // original size
                        _keyTextures[a].texture_height      = height;
                        _keyTextures[a].surface_width       = convert_pow2(width);        // converted size
                        _keyTextures[a].surface_height      = convert_pow2(height);
                        _keyTextures[a].palette             = temp_palette;
                        _keyTextures[a].paletteSize   = paletteSize;

                        uint32 size;

                        if (paletteSize == 16)              // 4 bit
                              size = (_keyTextures[a].surface_width * _keyTextures[a].surface_height)>>1;
                        else if (paletteSize == 256)  // 8-bit
                              size = _keyTextures[a].surface_width * _keyTextures[a].surface_height;
                        else                                      // 32 bit
                              size = (_keyTextures[a].surface_width * _keyTextures[a].surface_height)<<2;

#ifdef PSP_KB_DEBUG
                        fprintf(stderr, "load(): perm texture width=%d, height=%d, size=%d\n", _keyTextures[a].surface_width, _keyTextures[a].surface_height, size);
#endif
                        _keyTextures[a].texture = (unsigned char *)memalign(16, size);    // Allocate memory

                        block_copy(&_keyTextures[a], temp_texture);                 // Copy temp texture to permanent texture

                        if (paletteSize == 16)        // 4-bit
                              flipNibbles(&_keyTextures[a]);

                        free(temp_texture);

                        delete file;
                  }
            }
            else
                  goto ERROR;
      } /* for loop */
      _init = true;

      delete fileArchive;
      delete zipArchive;

      return true;

ERROR:

      // Error .. Couldn't get png info from one of the needed files
      free(temp_texture);
      delete file;
      delete fileArchive;
      delete zipArchive;

      for (int b = 0; b < a; b++) {
            free(_keyTextures[b].texture);
            free(_keyTextures[b].palette);
            _keyTextures[b].texture = NULL;
      }
      _init = false;
      return false;
}

// Copy texture from regular size image to power of 2 size image
//
void PSPKeyboard::block_copy(gu_surface* surface, u8 *texture) {
      u32 stride = 0, width = 0;

      switch(surface->paletteSize) {
      case 16:    // 4-bit
            width = surface->texture_width >> 1;
            stride = (surface->surface_width - surface->texture_width)>>1;
            break;
      case 256:   // 8-bit
            width = surface->texture_width;
            stride = surface->surface_width - surface->texture_width;
            break;
      case 0:           // 32-bit
            width = surface->texture_width << 2;
            stride = (surface->surface_width - surface->texture_width)<<2;
            break;
      default:
            fprintf(stderr, "Error in block_copy: bad value in paletteSize = %d\n", surface->paletteSize);
            return;
      }

      u8 *src  = texture;
      u8 *dest = surface->texture;

      for (unsigned int y = 0; y < surface->texture_height; y++) {
            memcpy(dest, src, width);
            dest += width;
            src += width;

            // skip at the end of each line
            if (stride > 0) {
                  dest += stride;
            }
      }
}

// Convert 4 bit images to match weird PSP format
//
void PSPKeyboard::flipNibbles(gu_surface* surface) {
      u32 *dest = (u32 *)surface->texture;

      for (u32 y = 0; y < surface->texture_height; y++) {
            for (u32 x = 0; x < (surface->surface_width >> 3); x++) {
                  u32 val = *dest;
                  *dest++ = ((val >> 4) & 0x0F0F0F0F) | ((val << 4) & 0xF0F0F0F0);
            }
      }

}


static void user_warning_fn(png_structp png_ptr, png_const_charp warning_msg) {
      // ignore PNG warnings
}

/* Get the width and height of a png image */
int PSPKeyboard::get_png_image_size(Common::SeekableReadStream *file, uint32 *png_width, uint32 *png_height, u32 *paletteSize) {
      png_structp png_ptr;
      png_infop info_ptr;
      unsigned int sig_read = 0;
      png_uint_32 width, height;
      int bit_depth, color_type, interlace_type;

      png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
      if (png_ptr == NULL) {
            return -1;
      }
      png_set_error_fn(png_ptr, (png_voidp) NULL, (png_error_ptr) NULL, user_warning_fn);
      info_ptr = png_create_info_struct(png_ptr);
      if (info_ptr == NULL) {
            png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
            return -1;
      }
      // Set the png lib to use our read function
      png_set_read_fn(png_ptr, (void *)file, pngReadStreamRead);

      png_set_sig_bytes(png_ptr, sig_read);
      png_read_info(png_ptr, info_ptr);
      png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL);
      if (color_type & PNG_COLOR_MASK_PALETTE)
            *paletteSize = info_ptr->num_palette;
      else
            *paletteSize = 0;

      png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);

      *png_width = width;
      *png_height = height;
      return 0;
}

// Load a texture from a png image
//
int PSPKeyboard::load_png_image(Common::SeekableReadStream *file, unsigned char *ImageBuffer, uint32 *palette) {
      png_structp png_ptr;
      png_infop info_ptr;
      unsigned int sig_read = 0;
      png_uint_32 width, height;
      int bit_depth, color_type, interlace_type;
      size_t x, y;

      png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
      if (png_ptr == NULL) {
            fprintf(stderr, "load_png_image(): Error: couldn't create read struct to load keyboard.\n");
            return -1;
      }
      // Use dummy error function
      png_set_error_fn(png_ptr, (png_voidp) NULL, (png_error_ptr) NULL, user_warning_fn);

      info_ptr = png_create_info_struct(png_ptr);
      if (info_ptr == NULL) {
            png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
            return -1;
      }

      // Set the png lib to use our read function
      png_set_read_fn(png_ptr, (void *)file, pngReadStreamRead);

      png_set_sig_bytes(png_ptr, sig_read);
      png_read_info(png_ptr, info_ptr);
      png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL);

      // Strip off 16 bit channels. Not really needed but whatever
      png_set_strip_16(png_ptr);

      if (color_type == PNG_COLOR_TYPE_PALETTE) {
            // We copy the palette
            uint32 *dstPal = palette;
            png_colorp srcPal = info_ptr->palette;
            for(int i=0; i < info_ptr->num_palette; i++) {
                  unsigned char alphaVal = (i < info_ptr->num_trans) ? info_ptr->trans[i] : 0xFF;     // Load alpha if it's there
                  *dstPal++ = GU_ARGB(alphaVal, srcPal->red, srcPal->green, srcPal->blue);
                  srcPal++;
            }

            unsigned char *line = (unsigned char*) malloc(info_ptr->rowbytes);
            if (!line) {
                  png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
                  return -1;
            }

            for (y = 0; y < height; y++) {
                  png_read_row(png_ptr, line, png_bytep_NULL);
                  memcpy(&ImageBuffer[y * info_ptr->rowbytes], line, info_ptr->rowbytes);
            }

            free(line);
      } else {    // Not a palettized image
            if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_gray_1_2_4_to_8(png_ptr);
            // Convert trans channel to alpha for 32 bits
            if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr);
            png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);

            u32* line = (u32*) malloc(width<<2);
            if (!line) {
                  png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
                  return -1;
            }

            u32* Image = (u32 *)ImageBuffer;
            for (y = 0; y < height; y++) {
                  png_read_row(png_ptr, (u8*) line, png_bytep_NULL);
                  for (x = 0; x < width; x++) {
                        Image[y*width + x] = line[x];
                  }
            }

            free(line);
      }

      png_read_end(png_ptr, info_ptr);
      png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);

      return 0;
}

// Function to get the lowest power of 2 higher than our image size
//
uint32 PSPKeyboard::convert_pow2(uint32 size) {
      uint32 pow_counter = 0;

      for (; pow_counter < 32; pow_counter++) {
            // Find the first value which is higher
            if ((size >> pow_counter) == 0) {
                  // take already good values into account
                  if (((uint32) 1 << pow_counter) != size)
                        return ((uint32)1 << pow_counter);
                  else
                        return ((uint32)1 << (pow_counter-1));
            }
      }
      return 0;
}


Generated by  Doxygen 1.6.0   Back to index