1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 // Disable symbol overrides so that we can use system headers.
24 #define FORBIDDEN_SYMBOL_ALLOW_ALL
25
26 //#define PSP_KB_SHELL /* Need a hack to properly load the keyboard from the PSP shell */
27
28 #ifdef PSP_KB_SHELL
29 #define PSP_KB_SHELL_PATH "ms0:/psp/game5xx/scummvm-solid/" /* path to kbd.zip */
30 #endif
31
32
33 #include <malloc.h>
34 #include <pspkernel.h>
35
36 #include "backends/platform/psp/psppixelformat.h"
37 #include "backends/platform/psp/pspkeyboard.h"
38 #include "backends/platform/psp/png_loader.h"
39 #include "backends/platform/psp/input.h"
40 #include "common/keyboard.h"
41 #include "common/fs.h"
42 #include "common/unzip.h"
43
44 //#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */
45 //#define __PSP_DEBUG_PRINT__
46
47 #include "backends/platform/psp/trace.h"
48
49 #define PSP_SCREEN_WIDTH 480
50 #define PSP_SCREEN_HEIGHT 272
51 #define K(x) ((short)(Common::KEYCODE_INVALID + (x)))
52 #define C(x) ((short)(Common::KEYCODE_##x))
53
54 // Layout of the keyboard: Order for the boxes is clockwise and then middle:
55 // 1
56 // 4 5 2
57 // 3
58 // and order of letters is clockwise in each box, plus 2 top letters:
59 // e f
60 // a
61 // d b
62 // c
63 // K(x) is used for ascii values. C(x) is used for keys without ascii values
64 short PSPKeyboard::_modeChar[MODE_COUNT][5][6] = {
65 { //standard letters
66 { K('a'), K('b'), K('c'), K('d'), K('f'), K('g') },
67 { K('h'), K('i'), K('l'), K('m'), K('j'), K('k') },
68 { K('o'), K('n'), K('r'), K('s'), K('p'), K('q') },
69 { K('u'), K('v'), K('w'), K('y'), K('x'), K('z') },
70 { K('\b'), K('t'), K(' '), K('e'), K(0), K(0) }
71 },
72 { //capital letters
73 { K('A'), K('B'), K('C'), K('D'), K('F'), K('G') },
74 { K('H'), K('I'), K('L'), K('M'), K('J'), K('K') },
75 { K('O'), K('N'), K('R'), K('S'), K('P'), K('Q') },
76 { K('U'), K('V'), K('W'), K('Y'), K('X'), K('Z') },
77 { K('\b'), K('T'), K(' '), K('E'), K(0), K(0) }
78 },
79 { //numbers
80 { K('1'), K('2'), K('3'), K('4'), K(0), K(0) },
81 { C(F5), C(F6), C(F7), C(F8), C(F9), C(F10) },
82 { K('5'), K('6'), K('7'), K('8'), K(0), K(0) },
83 { C(F1), C(F2), C(F3), C(F4), K(0), K(0) },
84 { K('\b'), K('0'), K(' '), K('9'), K(0), K(0) }
85 },
86 { //symbols
87 { K('!'), K(')'), K('?'), K('('), K('<'), K('>') },
88 { K('+'), K('/'), K('='), K('\\'), K('\''), K('"') },
89 { K(':'), K(']'), K(';'), K('['), K('@'), K('#') },
90 { K('-'), K('}'), K('_'), K('{'), K('*'), K('$') },
91 { K('\b'), K('.'), K(' '), K(','), K(0), K(0) }
92 }
93 };
94
95 // Array with file names
96 const char *PSPKeyboard::_guiStrings[] = {
97 "keys4.png", "keys_s4.png",
98 "keys_c4.png", "keys_s_c4.png",
99 "nums4.png", "nums_s4.png",
100 "syms4.png", "syms_s4.png"
101 };
102
103 // Constructor
PSPKeyboard()104 PSPKeyboard::PSPKeyboard() {
105 DEBUG_ENTER_FUNC();
106
107 _init = false; // we're not initialized yet
108 _prevButtons = 0; // Reset previous buttons
109 _dirty = false; // keyboard needs redrawing
110 _mode = 0; // charset selected. (0: letters, 1: uppercase 2: numbers 3: symbols)
111 _oldCursor = kCenter; // Center cursor by default
112 _movedX = 20; // Default starting location
113 _movedY = 50;
114 _moved = false; // Keyboard wasn't moved recently
115 _state = kInvisible; // We start invisible
116 _lastState = kInvisible;
117
118 // Constant renderer settings
119 _renderer.setAlphaBlending(true);
120 _renderer.setColorTest(false);
121 _renderer.setUseGlobalScaler(false);
122 }
123
124 // Destructor
~PSPKeyboard()125 PSPKeyboard::~PSPKeyboard() {
126 DEBUG_ENTER_FUNC();
127
128 if (!_init) {
129 return;
130 }
131
132 for (int i = 0; i < guiStringsSize; i++) {
133 _buffers[i].deallocate();
134 _palettes[i].deallocate();
135 }
136 _init = false;
137 }
138
setVisible(bool val)139 void PSPKeyboard::setVisible(bool val) {
140 if (val && _state == kInvisible && _init) { // Check also that were loaded correctly
141 _lastState = _state;
142 _state = kMove;
143 } else if (!val && _state != kInvisible) {
144 _lastState = _state;
145 _state = kInvisible;
146 }
147 setDirty();
148 }
149
150 /* move the position the keyboard is currently drawn at */
moveTo(const int newX,const int newY)151 void PSPKeyboard::moveTo(const int newX, const int newY) {
152 DEBUG_ENTER_FUNC();
153
154 _movedX = newX;
155 _movedY = newY;
156 setDirty();
157 }
158
159 /* move the position the keyboard is currently drawn at */
increaseKeyboardLocationX(int amount)160 void PSPKeyboard::increaseKeyboardLocationX(int amount) {
161 DEBUG_ENTER_FUNC();
162
163 int newX = _movedX + amount;
164
165 if (newX > PSP_SCREEN_WIDTH - 5 || newX < 0 - 140) { // clamp
166 return;
167 }
168 _movedX = newX;
169 setDirty();
170 }
171
172 /* move the position the keyboard is currently drawn at */
increaseKeyboardLocationY(int amount)173 void PSPKeyboard::increaseKeyboardLocationY(int amount) {
174 DEBUG_ENTER_FUNC();
175
176 int newY = _movedY + amount;
177
178 if (newY > PSP_SCREEN_HEIGHT - 5 || newY < 0 - 140) { // clamp
179 return;
180 }
181 _movedY = newY;
182 setDirty();
183 }
184
185 /* draw the keyboard at the current position */
render()186 void PSPKeyboard::render() {
187 DEBUG_ENTER_FUNC();
188
189 unsigned int currentBuffer = _mode << 1;
190
191 // Draw the background letters
192 // Set renderer to current buffer & palette
193 _renderer.setBuffer(&_buffers[currentBuffer]);
194 _renderer.setPalette(&_palettes[currentBuffer]);
195 _renderer.setOffsetOnScreen(_movedX, _movedY);
196 _renderer.setOffsetInBuffer(0, 0);
197 _renderer.setDrawWholeBuffer();
198 _renderer.render();
199
200 // Get X and Y coordinates for the orange block
201 int x, y;
202 convertCursorToXY(_oldCursor, x, y);
203
204 const int OrangeBlockSize = 64;
205 const int GrayBlockSize = 43;
206
207 // Draw the current Highlighted Selector (orange block)
208 _renderer.setBuffer(&_buffers[currentBuffer + 1]);
209 _renderer.setPalette(&_palettes[currentBuffer + 1]);
210 _renderer.setOffsetOnScreen(_movedX + (x * GrayBlockSize), _movedY + (y * GrayBlockSize));
211 _renderer.setOffsetInBuffer(x * OrangeBlockSize, y * OrangeBlockSize);
212 _renderer.setDrawSize(OrangeBlockSize, OrangeBlockSize);
213 _renderer.render();
214 }
215
convertCursorToXY(CursorDirections cur,int & x,int & y)216 inline void PSPKeyboard::convertCursorToXY(CursorDirections cur, int &x, int &y) {
217 switch (cur) {
218 case kUp:
219 x = 1;
220 y = 0;
221 break;
222 case kRight:
223 x = 2;
224 y = 1;
225 break;
226 case kDown:
227 x = 1;
228 y = 2;
229 break;
230 case kLeft:
231 x = 0;
232 y = 1;
233 break;
234 default:
235 x = 1;
236 y = 1;
237 break;
238 }
239 }
240
241 /* load the keyboard into memory */
load()242 bool PSPKeyboard::load() {
243 DEBUG_ENTER_FUNC();
244
245 if (_init) {
246 PSP_DEBUG_PRINT("keyboard already loaded into memory\n");
247 return true;
248 }
249
250 // For the shell, we must use a hack
251 #ifdef PSP_KB_SHELL
252 Common::FSNode node(PSP_KB_SHELL_PATH);
253 #else /* normal mode */
254 Common::FSNode node("."); // Look in current directory
255 #endif
256 PSP_DEBUG_PRINT("path[%s]\n", node.getPath().c_str());
257
258 Common::Archive *fileArchive = NULL;
259 Common::Archive *zipArchive = NULL;
260 Common::SeekableReadStream * file = 0;
261
262 if (node.getChild("kbd").exists() && node.getChild("kbd").isDirectory()) {
263 PSP_DEBUG_PRINT("found directory ./kbd\n");
264 fileArchive = new Common::FSDirectory(node.getChild("kbd"));
265 }
266 if (node.getChild("kbd.zip").exists()) {
267 PSP_DEBUG_PRINT("found kbd.zip\n");
268 zipArchive = Common::makeZipArchive(node.getChild("kbd.zip"));
269 }
270
271 int i;
272
273 // Loop through all png images
274 for (i = 0; i < guiStringsSize; i++) {
275 PSP_DEBUG_PRINT("Opening %s.\n", _guiStrings[i]);
276
277 // Look for the file in the kbd directory
278 if (fileArchive && fileArchive->hasFile(_guiStrings[i])) {
279 PSP_DEBUG_PRINT("found it in kbd directory.\n");
280
281 file = fileArchive->createReadStreamForMember(_guiStrings[i]);
282 if (!file) {
283 PSP_ERROR("Can't open kbd/%s for keyboard. No keyboard will load.\n", _guiStrings[i]);
284 goto ERROR;
285 }
286 }
287 // We didn't find it. Look for it in the zip file
288 else if (zipArchive && zipArchive->hasFile(_guiStrings[i])) {
289 PSP_DEBUG_PRINT("found it in kbd.zip.\n");
290
291 file = zipArchive->createReadStreamForMember(_guiStrings[i]);
292 if (!file) {
293 PSP_ERROR("Can't open %s in kbd.zip for keyboard. No keyboard will load.\n", _guiStrings[i]);
294 goto ERROR;
295 }
296 } else { // Couldn't find the file
297 PSP_ERROR("Can't find %s for keyboard. No keyboard will load.\n", _guiStrings[i]);
298 goto ERROR;
299 }
300
301 PngLoader image(*file, _buffers[i], _palettes[i]);
302
303 if (image.allocate() != PngLoader::OK) {
304 PSP_ERROR("Failed to allocate memory for keyboard image %s\n", _guiStrings[i]);
305 goto ERROR;
306 }
307 if (!image.load()) {
308 PSP_ERROR("Failed to load image from file %s\n", _guiStrings[i]);
309 goto ERROR;
310 }
311
312 delete file;
313 } /* for loop */
314
315 _init = true;
316
317 delete fileArchive;
318 delete zipArchive;
319
320 return true;
321
322 ERROR:
323
324 delete file;
325 delete fileArchive;
326 delete zipArchive;
327
328 for (int j = 0; j < i; j++) {
329 _buffers[j].deallocate();
330 _palettes[j].deallocate();
331 }
332 _init = false;
333
334 return false;
335 }
336
337 // Defines for working with PSP buttons
338 #define CHANGED(x) (_buttonsChanged & (x))
339 #define PRESSED(x) ((_buttonsChanged & (x)) && (pad.Buttons & (x)))
340 #define UNPRESSED(x) ((_buttonsChanged & (x)) && !(pad.Buttons & (x)))
341 #define DOWN(x) (pad.Buttons & (x))
342 #define UP(x) (!(pad.Buttons & (x)))
343 #define PSP_DPAD (PSP_CTRL_DOWN|PSP_CTRL_UP|PSP_CTRL_LEFT|PSP_CTRL_RIGHT)
344 #define PSP_4BUTTONS (PSP_CTRL_CROSS | PSP_CTRL_CIRCLE | PSP_CTRL_TRIANGLE | PSP_CTRL_SQUARE)
345
346 /*
347 * Attempts to read a character from the controller
348 * Uses the state machine.
349 * returns whether we have an event
350 */
processInput(Common::Event & event,PspEvent & pspEvent,SceCtrlData & pad)351 bool PSPKeyboard::processInput(Common::Event &event, PspEvent &pspEvent, SceCtrlData &pad) {
352 DEBUG_ENTER_FUNC();
353
354 bool haveEvent = false; // Whether we have an event for the event manager to process
355 bool havePspEvent = false;
356 event.kbd.flags = 0;
357
358 _buttonsChanged = _prevButtons ^ pad.Buttons;
359
360 if (!_init) // In case we never had init
361 return false;
362 if (_state == kInvisible) // Return if we're invisible
363 return false;
364 if (_state != kMove && PRESSED(PSP_CTRL_SELECT)) {
365 _lastState = _state;
366 _state = kMove; // Check for move or visible state
367 } else if (CHANGED(PSP_CTRL_START)) { // Handle start button: enter, make KB invisible
368 event.kbd.ascii = '\r';
369 event.kbd.keycode = Common::KEYCODE_RETURN;
370 event.type = DOWN(PSP_CTRL_START) ? Common::EVENT_KEYDOWN : Common::EVENT_KEYUP;
371 haveEvent = true;
372 _dirty = true;
373 if (UP(PSP_CTRL_START))
374 havePspEvent = true;
375 }
376 // Check for being in state of moving the keyboard onscreen or pressing select
377 else if (_state == kMove)
378 havePspEvent = handleMoveState(pad);
379 else if (_state == kDefault)
380 haveEvent = handleDefaultState(event, pad);
381 else if (_state == kCornersSelected)
382 haveEvent = handleCornersSelectedState(event, pad);
383 else if (_state == kRTriggerDown)
384 handleRTriggerDownState(pad); // Deal with trigger states
385 else if (_state == kLTriggerDown)
386 handleLTriggerDownState(pad); // Deal with trigger states
387
388 if (havePspEvent) {
389 pspEvent.type = PSP_EVENT_SHOW_VIRTUAL_KB; // tell the input handler we're off
390 pspEvent.data = false;
391 }
392 _prevButtons = pad.Buttons;
393
394 return haveEvent;
395 }
396
handleMoveState(SceCtrlData & pad)397 bool PSPKeyboard::handleMoveState(SceCtrlData &pad) {
398 DEBUG_ENTER_FUNC();
399 if (UP(PSP_CTRL_SELECT)) {
400 // Toggle between visible and invisible
401 _state = (_lastState == kInvisible) ? kDefault : kInvisible;
402 _dirty = true;
403
404 if (_moved) { // We moved the keyboard. Keep the keyboard onscreen anyway
405 _state = kDefault;
406 _moved = false; // reset moved flag
407 }
408 if (_state == kInvisible) {
409 return true; // we become invisible
410 }
411 } else if (DOWN(PSP_DPAD)) { // How we move the KB onscreen
412 _moved = true;
413 _dirty = true;
414
415 if (DOWN(PSP_CTRL_DOWN))
416 increaseKeyboardLocationY(5);
417 else if (DOWN(PSP_CTRL_UP))
418 increaseKeyboardLocationY(-5);
419 else if (DOWN(PSP_CTRL_LEFT))
420 increaseKeyboardLocationX(-5);
421 else /* DOWN(PSP_CTRL_RIGHT) */
422 increaseKeyboardLocationX(5);
423 }
424 return false;
425 }
426
handleDefaultState(Common::Event & event,SceCtrlData & pad)427 bool PSPKeyboard::handleDefaultState(Common::Event &event, SceCtrlData &pad) {
428 DEBUG_ENTER_FUNC();
429 bool haveEvent = false;
430
431 if (PRESSED(PSP_CTRL_LTRIGGER)) // Don't say we used up the input
432 _state = kLTriggerDown;
433 else if (PRESSED(PSP_CTRL_RTRIGGER)) // Don't say we used up the input
434 _state = kRTriggerDown;
435 else if (CHANGED(PSP_4BUTTONS)) // We only care about the 4 buttons
436 haveEvent = getInputChoice(event, pad);
437 else if (!DOWN(PSP_4BUTTONS)) // Must be up to move cursor
438 getCursorMovement(pad);
439
440 return haveEvent;
441 }
442
handleCornersSelectedState(Common::Event & event,SceCtrlData & pad)443 bool PSPKeyboard::handleCornersSelectedState(Common::Event &event, SceCtrlData &pad) {
444 DEBUG_ENTER_FUNC();
445 // We care about 4 buttons + triggers (for letter selection)
446 bool haveEvent = false;
447
448 if (CHANGED(PSP_4BUTTONS | PSP_CTRL_RTRIGGER | PSP_CTRL_LTRIGGER))
449 haveEvent = getInputChoice(event, pad);
450 if (!DOWN(PSP_4BUTTONS | PSP_CTRL_RTRIGGER | PSP_CTRL_LTRIGGER)) // Must be up to move cursor
451 getCursorMovement(pad);
452
453 return haveEvent;
454 }
455
getInputChoice(Common::Event & event,SceCtrlData & pad)456 bool PSPKeyboard::getInputChoice(Common::Event &event, SceCtrlData &pad) {
457 DEBUG_ENTER_FUNC();
458 int innerChoice;
459 bool haveEvent = false;
460
461 if (UNPRESSED(PSP_CTRL_TRIANGLE)) {
462 innerChoice = 0;
463 event.type = Common::EVENT_KEYUP; // We give priority to key_up
464 } else if (UNPRESSED(PSP_CTRL_CIRCLE)) {
465 innerChoice = 1;
466 event.type = Common::EVENT_KEYUP; // We give priority to key_up
467 } else if (UNPRESSED(PSP_CTRL_CROSS)) {
468 innerChoice = 2;
469 event.type = Common::EVENT_KEYUP; // We give priority to key_up
470 } else if (UNPRESSED(PSP_CTRL_SQUARE)) {
471 innerChoice = 3;
472 event.type = Common::EVENT_KEYUP; // We give priority to key_up
473 } else if (UNPRESSED(PSP_CTRL_LTRIGGER) && _state == kCornersSelected) {
474 innerChoice = 4;
475 event.type = Common::EVENT_KEYUP; // We give priority to key_up
476 } else if (UNPRESSED(PSP_CTRL_RTRIGGER) && _state == kCornersSelected) {
477 innerChoice = 5;
478 event.type = Common::EVENT_KEYUP; // We give priority to key_up
479 } else if (PRESSED(PSP_CTRL_TRIANGLE)) {
480 innerChoice = 0;
481 event.type = Common::EVENT_KEYDOWN;
482 } else if (PRESSED(PSP_CTRL_CIRCLE)) {
483 innerChoice = 1;
484 event.type = Common::EVENT_KEYDOWN;
485 } else if (PRESSED(PSP_CTRL_CROSS)) {
486 innerChoice = 2;
487 event.type = Common::EVENT_KEYDOWN;
488 } else if (PRESSED(PSP_CTRL_SQUARE)) {
489 innerChoice = 3;
490 event.type = Common::EVENT_KEYDOWN;
491 } else if (PRESSED(PSP_CTRL_LTRIGGER) && _state == kCornersSelected) {
492 innerChoice = 4;
493 event.type = Common::EVENT_KEYDOWN; // We give priority to key_up
494 } else { /* (PRESSED(PSP_CTRL_RTRIGGER)) && _state == kCornersSelected */
495 innerChoice = 5;
496 event.type = Common::EVENT_KEYDOWN; // We give priority to key_up
497 }
498
499 #define IS_UPPERCASE(x) ((x) >= (unsigned short)'A' && (x) <= (unsigned short)'Z')
500 #define TO_LOWER(x) ((x) += 'a'-'A')
501
502 //Now grab the value out of the array
503 short choice = _modeChar[_mode][_oldCursor][innerChoice];
504
505 event.kbd.ascii = choice <= 255 ? choice : 0;
506
507 // Handle upper-case which is missing in Common::KeyCode
508 if (IS_UPPERCASE(choice)) {
509 event.kbd.keycode = (Common::KeyCode) TO_LOWER(choice);
510 event.kbd.flags = Common::KBD_SHIFT;
511 } else
512 event.kbd.keycode = (Common::KeyCode) choice;
513
514 haveEvent = (choice != Common::KEYCODE_INVALID) ? true : false; // We have an event/don't if it's invalid
515
516 return haveEvent;
517 }
518
getCursorMovement(SceCtrlData & pad)519 void PSPKeyboard::getCursorMovement(SceCtrlData &pad) {
520 DEBUG_ENTER_FUNC();
521 CursorDirections cursor;
522
523 // Find where the cursor is pointing
524 cursor = kCenter;
525 _state = kDefault;
526
527 if (DOWN(PSP_DPAD)) {
528 _state = kCornersSelected;
529
530 if (DOWN(PSP_CTRL_UP))
531 cursor = kUp;
532 else if (DOWN(PSP_CTRL_RIGHT))
533 cursor = kRight;
534 else if (DOWN(PSP_CTRL_DOWN))
535 cursor = kDown;
536 else if (DOWN(PSP_CTRL_LEFT))
537 cursor = kLeft;
538 }
539
540 if (cursor != _oldCursor) { //If we've moved, update dirty and return
541 _dirty = true;
542 _oldCursor = cursor;
543 }
544 }
545
handleLTriggerDownState(SceCtrlData & pad)546 void PSPKeyboard::handleLTriggerDownState(SceCtrlData &pad) {
547 DEBUG_ENTER_FUNC();
548 if (UNPRESSED(PSP_CTRL_LTRIGGER)) {
549 _dirty = true;
550
551 if (_mode < 2)
552 _mode = 2;
553 else
554 _mode = (_mode == 2) ? 3 : 2;
555
556 _state = kDefault;
557 }
558 }
559
handleRTriggerDownState(SceCtrlData & pad)560 void PSPKeyboard::handleRTriggerDownState(SceCtrlData &pad) {
561 DEBUG_ENTER_FUNC();
562 if (UNPRESSED(PSP_CTRL_RTRIGGER)) {
563 _dirty = true;
564
565 if (_mode > 1)
566 _mode = 0;
567 else
568 _mode = (_mode == 0) ? 1 : 0;
569
570 _state = kDefault;
571 }
572 }
573