Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/Dialog/PSPOskDialog.cpp
3186 views
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include "ppsspp_config.h"
19
20
#include <cctype>
21
#include <cmath>
22
#include <algorithm>
23
24
#include "Common/Data/Text/I18n.h"
25
#include "Common/Math/math_util.h"
26
#include "Common/Data/Encoding/Utf8.h"
27
#include "Common/Serialize/SerializeFuncs.h"
28
#include "Common/System/Request.h"
29
#include "Common/Serialize/Serializer.h"
30
31
#include "Core/Dialog/PSPOskDialog.h"
32
#include "Core/Util/PPGeDraw.h"
33
#include "Core/HLE/sceCtrl.h"
34
#include "Core/HLE/ErrorCodes.h"
35
#include "Core/HLE/sceUtility.h"
36
#include "Core/HW/Display.h"
37
#include "Core/Config.h"
38
#include "Core/Reporting.h"
39
40
// These are rough, it seems to take a long time to init, and probably depends on threads.
41
// TODO: This takes like 700ms on a PSP but that's annoyingly long.
42
const static int OSK_INIT_DELAY_US = 300000;
43
const static int OSK_SHUTDOWN_DELAY_US = 40000;
44
45
static std::map<std::string, std::pair<std::string, int>, std::less<>> languageMapping;
46
47
const uint8_t numKeyCols[OSK_KEYBOARD_COUNT] = {12, 12, 13, 13, 12, 12, 12, 12, 12};
48
const uint8_t numKeyRows[OSK_KEYBOARD_COUNT] = {4, 4, 6, 6, 5, 4, 4, 4, 4};
49
50
// Korean (Hangul) vowel Combination key
51
static const uint8_t kor_vowelCom[21] = { 0,8,9,1,8,10,20,8,11,4,13,14,5,13,15,20,13,16,20,18,19 };
52
53
// Korean (Hangul) last consonant Combination key
54
static const uint8_t kor_lconsCom[33] = { 18,0,2,21,3,4,26,3,5,0,7,8,15,7,9,16,7,10,18,7,11,24,7,12,25,7,13,26,7,14,18,16,17 };
55
56
// Korean (Hangul) last consonant Separation key
57
static const uint8_t kor_lconsSpr[33] = { 2,1,9,4,4,12,5,4,18,8,8,0,9,8,6,10,8,7,11,8,9,12,8,16,13,8,17,14,8,18,17,17,9 };
58
59
static const std::string_view OskKeyboardNames[] =
60
{
61
"en_US",
62
"ja_JP",
63
"ko_KR",
64
"ru_RU",
65
"English Full-width",
66
};
67
68
// This isn't a complete representation of these flags, it just helps ensure we show the right keyboards.
69
static const int allowedInputFlagsMap[OSK_KEYBOARD_COUNT] = {
70
PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT,
71
PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL,
72
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HIRAGANA,
73
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_KATAKANA | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HALF_KATAKANA,
74
PSP_UTILITY_OSK_INPUTTYPE_KOREAN,
75
PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE,
76
PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE,
77
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_DIGIT,
78
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL,
79
};
80
static const int defaultInputFlagsMap[OSK_KEYBOARD_COUNT] = {
81
PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT,
82
PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL,
83
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HIRAGANA,
84
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_KATAKANA | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HALF_KATAKANA,
85
PSP_UTILITY_OSK_INPUTTYPE_KOREAN,
86
PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE,
87
PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE,
88
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_DIGIT,
89
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL,
90
};
91
92
PSPOskDialog::PSPOskDialog(UtilityDialogType type) : PSPDialog(type) {
93
// This can break all kinds of stuff, changing the decimal point in sprintf for example.
94
// Not sure what the intended effect is so commented out for now.
95
// setlocale(LC_ALL, "");
96
}
97
98
PSPOskDialog::~PSPOskDialog() {
99
}
100
101
void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, const PSPPointer<u16_le>& em_address)
102
{
103
if (!em_address.IsValid())
104
{
105
_string.clear();
106
return;
107
}
108
109
const size_t maxLength = 2047;
110
char stringBuffer[maxLength + 1];
111
char *string = stringBuffer;
112
113
const u16_le *input = &em_address[0];
114
int c;
115
while ((c = *input++) != 0 && string < stringBuffer + maxLength)
116
{
117
if (c < 0x80)
118
*string++ = c;
119
else if (c < 0x800) {
120
*string++ = 0xC0 | (c >> 6);
121
*string++ = 0x80 | (c & 0x3F);
122
} else {
123
*string++ = 0xE0 | (c >> 12);
124
*string++ = 0x80 | ((c >> 6) & 0x3F);
125
*string++ = 0x80 | (c & 0x3F);
126
}
127
}
128
*string++ = '\0';
129
_string = stringBuffer;
130
}
131
132
void GetWideStringFromPSPPointer(std::u16string& _string, const PSPPointer<u16_le>& em_address)
133
{
134
if (!em_address.IsValid())
135
{
136
_string.clear();
137
return;
138
}
139
140
const size_t maxLength = 2047;
141
char16_t stringBuffer[maxLength + 1];
142
char16_t *string = stringBuffer;
143
144
const u16_le *input = &em_address[0];
145
int c;
146
while ((c = *input++) != 0 && string < stringBuffer + maxLength)
147
*string++ = c;
148
*string++ = '\0';
149
_string = stringBuffer;
150
}
151
152
void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, const char16_t *input)
153
{
154
char stringBuffer[2048];
155
char *string = stringBuffer;
156
157
int c;
158
while ((c = *input++) != 0)
159
{
160
if (c < 0x80)
161
*string++ = c;
162
else if (c < 0x800) {
163
*string++ = 0xC0 | (c >> 6);
164
*string++ = 0x80 | (c & 0x3F);
165
} else {
166
*string++ = 0xE0 | (c >> 12);
167
*string++ = 0x80 | ((c >> 6) & 0x3F);
168
*string++ = 0x80 | (c & 0x3F);
169
}
170
}
171
*string++ = '\0';
172
_string = stringBuffer;
173
}
174
175
static void FindValidKeyboard(s32 inputType, int direction, OskKeyboardLanguage &lang, OskKeyboardDisplay &disp) {
176
OskKeyboardLanguage origLang = lang;
177
OskKeyboardDisplay origDisp = disp;
178
179
if (inputType == 0) {
180
return;
181
}
182
// We use direction = 0 for default, but we actually move "forward".
183
const int *matchMap = allowedInputFlagsMap;
184
if (direction == 0) {
185
direction = 1;
186
matchMap = defaultInputFlagsMap;
187
}
188
189
// TODO: Limit by allowed keyboards properly... this is just an approximation.
190
int tries = OSK_LANGUAGE_COUNT * 2;
191
while (!(inputType & matchMap[disp]) && tries > 0) {
192
if ((--tries % 2) == 0) {
193
lang = (OskKeyboardLanguage)((OSK_LANGUAGE_COUNT + lang + direction) % OSK_LANGUAGE_COUNT);
194
disp = OskKeyboardCases[lang][LOWERCASE];
195
} else {
196
disp = OskKeyboardCases[lang][UPPERCASE];
197
}
198
}
199
200
if (tries == 0) {
201
// In case of error, let's just fall back to allowing all.
202
lang = origLang;
203
disp = origDisp;
204
}
205
}
206
207
static bool IsKeyboardShiftValid(s32 inputType, OskKeyboardLanguage lang, OskKeyboardDisplay disp) {
208
// Swap disp and check if it's valid.
209
if (disp == OskKeyboardCases[lang][UPPERCASE])
210
disp = OskKeyboardCases[lang][LOWERCASE];
211
else
212
disp = OskKeyboardCases[lang][UPPERCASE];
213
214
return inputType == 0 || (inputType & allowedInputFlagsMap[disp]) != 0;
215
}
216
217
int PSPOskDialog::Init(u32 oskPtr) {
218
// Ignore if already running
219
if (GetStatus() != SCE_UTILITY_STATUS_NONE) {
220
ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid status");
221
return SCE_ERROR_UTILITY_INVALID_STATUS;
222
}
223
// Seems like this should crash?
224
if (!Memory::IsValidAddress(oskPtr)) {
225
ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid params (%08x)", oskPtr);
226
return -1;
227
}
228
229
oskParams = oskPtr;
230
if (oskParams->base.size != sizeof(SceUtilityOskParams))
231
{
232
ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid size %d", oskParams->base.size);
233
return SCE_ERROR_UTILITY_INVALID_PARAM_SIZE;
234
}
235
// Also seems to crash.
236
if (!oskParams->fields.IsValid())
237
{
238
ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid field data (%08x)", oskParams->fields.ptr);
239
return -1;
240
}
241
242
if (oskParams->unk_60 != 0)
243
WARN_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: unknown param is non-zero (%08x)", oskParams->unk_60);
244
if (oskParams->fieldCount != 1)
245
WARN_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: unsupported field count %d", oskParams->fieldCount);
246
247
ChangeStatusInit(OSK_INIT_DELAY_US);
248
selectedChar = 0;
249
currentKeyboardLanguage = OSK_LANGUAGE_ENGLISH;
250
currentKeyboard = OSK_KEYBOARD_LATIN_LOWERCASE;
251
FindValidKeyboard(oskParams->fields[0].inputtype, 0, currentKeyboardLanguage, currentKeyboard);
252
253
ConvertUCS2ToUTF8(oskDesc, oskParams->fields[0].desc);
254
ConvertUCS2ToUTF8(oskIntext, oskParams->fields[0].intext);
255
ConvertUCS2ToUTF8(oskOuttext, oskParams->fields[0].outtext);
256
257
i_level = 0;
258
259
inputChars.clear();
260
261
if (oskParams->fields[0].intext.IsValid()) {
262
auto src = oskParams->fields[0].intext;
263
int c;
264
while ((c = *src++) != 0)
265
inputChars += c;
266
}
267
268
languageMapping = g_Config.GetLangValuesMapping();
269
270
// Eat any keys pressed before the dialog inited.
271
UpdateButtons();
272
InitCommon();
273
274
std::lock_guard<std::mutex> guard(nativeMutex_);
275
nativeStatus_ = PSPOskNativeStatus::IDLE;
276
277
StartFade(true);
278
return 0;
279
}
280
281
std::u16string PSPOskDialog::CombinationKorean(bool isInput)
282
{
283
std::u16string string;
284
285
isCombinated = true;
286
287
int selectedRow = selectedChar / numKeyCols[currentKeyboard];
288
int selectedCol = selectedChar % numKeyCols[currentKeyboard];
289
290
if (inputChars.size() == 0) {
291
wchar_t sw = OskKeyAt(currentKeyboard, selectedRow, selectedCol);
292
293
if (inputChars.size() < FieldMaxLength()) {
294
string += sw;
295
296
i_value[0] = GetIndex(KorCons(), sw);
297
298
if(i_value[0] != -1 && isInput == true)
299
i_level = 1;
300
} else {
301
isCombinated = false;
302
}
303
} else {
304
for(u32 i = 0; i < inputChars.size(); i++) {
305
if(i + 1 == inputChars.size()) {
306
wchar_t sw = OskKeyAt(currentKeyboard, selectedRow, selectedCol);
307
308
if(i_level == 0) {
309
string += inputChars[i];
310
if (inputChars.size() < FieldMaxLength()) {
311
string += sw;
312
313
i_value[0] = GetIndex(KorCons(), sw);
314
315
if(i_value[0] != -1 && isInput == true)
316
i_level = 1;
317
} else {
318
isCombinated = false;
319
}
320
} else if(i_level == 1) {
321
i_value[1] = GetIndex(KorVowel(), sw);
322
323
if(i_value[1] == -1) {
324
string += inputChars[i];
325
if (inputChars.size() < FieldMaxLength()) {
326
string += sw;
327
328
if(isInput == true) {
329
i_value[0] = GetIndex(KorCons(), sw);
330
331
if(i_value[0] != -1)
332
i_level = 1;
333
else
334
i_level = 0;
335
}
336
} else {
337
isCombinated = false;
338
}
339
} else {
340
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C;
341
string += code;
342
343
if(isInput == true) {
344
i_level = 2;
345
}
346
}
347
} else if(i_level == 2) {
348
int tmp = GetIndex(KorVowel(), sw);
349
if(tmp != -1) {
350
int tmp2 = -1;
351
for(size_t j = 0; j < sizeof(kor_vowelCom) / 4; j+=3) {
352
if(kor_vowelCom[j] == tmp && kor_vowelCom[j + 1] == i_value[1]) {
353
tmp2 = kor_vowelCom[j + 2];
354
break;
355
}
356
}
357
if(tmp2 != -1) {
358
if(isInput == true) {
359
i_value[1] = tmp2;
360
}
361
362
u16 code = 0xAC00 + i_value[0] * 0x24C + tmp2 * 0x1C;
363
364
string += code;
365
} else {
366
string += inputChars[i];
367
if (inputChars.size() < FieldMaxLength()) {
368
string += sw;
369
370
if(isInput == true) {
371
i_level = 0;
372
}
373
} else {
374
isCombinated = false;
375
}
376
}
377
} else {
378
int tmp2 = GetIndex(KorLCons(), sw);
379
380
if (tmp2 == -1) {
381
string += inputChars[i];
382
if (inputChars.size() < FieldMaxLength()) {
383
string += sw;
384
385
if (isInput == true) {
386
i_value[0] = GetIndex(KorCons(), sw);
387
388
if(i_value[0] != -1)
389
i_level = 1;
390
else
391
i_level = 0;
392
}
393
} else {
394
isCombinated = false;
395
}
396
} else {
397
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + tmp2 + 1;
398
399
string += code;
400
401
if (isInput == true) {
402
i_level = 3;
403
i_value[2] = tmp2;
404
}
405
}
406
}
407
} else if(i_level == 3) {
408
int tmp = GetIndex(KorLCons(), sw);
409
if(tmp != -1) {
410
int tmp2 = -1;
411
for(size_t j = 0; j < sizeof(kor_lconsCom) / 4; j+=3) {
412
if(kor_lconsCom[j] == tmp && kor_lconsCom[j + 1] == i_value[2]) {
413
tmp2 = kor_lconsCom[j + 2];
414
break;
415
}
416
}
417
if(tmp2 != -1) {
418
if(isInput == true) {
419
i_value[2] = tmp2;
420
}
421
422
u16 code = 0xAC00 + i_value[0] * 0x24C + tmp2 * 0x1C + i_value[2] + 1;
423
424
string += code;
425
} else {
426
string += inputChars[i];
427
if (inputChars.size() < FieldMaxLength()) {
428
string += sw;
429
430
if(isInput == true) {
431
i_value[0] = GetIndex(KorCons(), sw);
432
433
if(i_value[0] != -1)
434
i_level = 1;
435
else
436
i_level = 0;
437
}
438
} else {
439
isCombinated = false;
440
}
441
}
442
} else {
443
int tmp3 = GetIndex(KorVowel(), sw);
444
if (tmp3 == -1) {
445
string += inputChars[i];
446
if (inputChars.size() < FieldMaxLength()) {
447
string += sw;
448
449
if(isInput == true) {
450
i_value[0] = GetIndex(KorCons(), sw);
451
452
if(i_value[0] != -1)
453
i_level = 1;
454
else
455
i_level = 0;
456
}
457
} else {
458
isCombinated = false;
459
}
460
} else {
461
if (inputChars.size() < FieldMaxLength()) {
462
int tmp2 = -1;
463
for(size_t j = 0; j < sizeof(kor_lconsSpr) / 4; j+=3) {
464
if(kor_lconsSpr[j] == i_value[2]) {
465
tmp2 = (int)j;
466
break;
467
}
468
}
469
if(tmp2 != -1) {
470
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + kor_lconsSpr[tmp2 + 1];
471
string += code;
472
473
code = 0xAC00 + kor_lconsSpr[tmp2 + 2] * 0x24C + tmp3 * 0x1C;
474
string += code;
475
476
if(isInput == true) {
477
i_value[0] = kor_lconsSpr[tmp2 + 2];
478
i_value[1] = tmp3;
479
i_level = 2;
480
}
481
} else {
482
int tmp4 = GetIndex(KorCons(), KorLCons()[i_value[2]]);
483
484
if (tmp4 != -1) {
485
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C;
486
487
string += code;
488
489
code = 0xAC00 + tmp4 * 0x24C + tmp3 * 0x1C;
490
491
string += code;
492
493
if(isInput == true) {
494
i_value[0] = tmp4;
495
i_value[1] = tmp3;
496
i_level = 2;
497
}
498
} else {
499
string += inputChars[i];
500
string += sw;
501
502
if(isInput == true) {
503
i_level = 0;
504
}
505
}
506
}
507
} else {
508
string += inputChars[i];
509
isCombinated = false;
510
}
511
}
512
}
513
}
514
} else {
515
string += inputChars[i];
516
}
517
}
518
}
519
520
return string;
521
}
522
523
std::u16string PSPOskDialog::CombinationString(bool isInput)
524
{
525
std::u16string string;
526
527
isCombinated = false;
528
529
int selectedRow = selectedChar / numKeyCols[currentKeyboard];
530
int selectedCol = selectedChar % numKeyCols[currentKeyboard];
531
532
if(currentKeyboard == OSK_KEYBOARD_KOREAN)
533
{
534
string = CombinationKorean(isInput);
535
}
536
else
537
{
538
if(isInput == true)
539
{
540
i_level = 0;
541
}
542
543
if(OskKeyAt(currentKeyboard, selectedRow, selectedCol) == L'゛')
544
{
545
for(u32 i = 0; i < inputChars.size(); i++)
546
{
547
if(i + 1 == inputChars.size())
548
{
549
for(u32 j = 0; j < wcslen(JapDiacritics(0)); j+=2)
550
{
551
if(inputChars[i] == JapDiacritics(0)[j])
552
{
553
string += JapDiacritics(0)[j + 1];
554
isCombinated = true;
555
break;
556
}
557
}
558
559
if(isCombinated == false)
560
{
561
string += inputChars[i];
562
}
563
}
564
else
565
{
566
string += inputChars[i];
567
}
568
}
569
}
570
else if(OskKeyAt(currentKeyboard, selectedRow, selectedCol) == L'゜')
571
{
572
for(u32 i = 0; i < inputChars.size(); i++)
573
{
574
if(i + 1 == inputChars.size())
575
{
576
for(u32 j = 0; j < wcslen(JapDiacritics(1)); j+=2)
577
{
578
if(inputChars[i] == JapDiacritics(1)[j])
579
{
580
string += JapDiacritics(1)[j + 1];
581
isCombinated = true;
582
break;
583
}
584
}
585
586
if(isCombinated == false)
587
{
588
string += inputChars[i];
589
}
590
}
591
else
592
{
593
string += inputChars[i];
594
}
595
}
596
}
597
else
598
{
599
for(u32 i = 0; i < inputChars.size(); i++)
600
{
601
string += inputChars[i];
602
}
603
604
if (string.size() < FieldMaxLength())
605
{
606
string += OskKeyAt(currentKeyboard, selectedRow, selectedCol);
607
}
608
isCombinated = true;
609
}
610
}
611
612
return string;
613
}
614
615
void PSPOskDialog::RemoveKorean()
616
{
617
if(i_level == 1)
618
{
619
i_level = 0;
620
}
621
else if(i_level == 2)
622
{
623
int tmp = -1;
624
for(size_t i = 2; i < sizeof(kor_vowelCom) / 4; i+=3)
625
{
626
if(kor_vowelCom[i] == i_value[1])
627
{
628
tmp = kor_vowelCom[i - 1];
629
break;
630
}
631
}
632
633
if(tmp != -1)
634
{
635
i_value[1] = tmp;
636
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C;
637
inputChars += code;
638
}
639
else
640
{
641
i_level = 1;
642
inputChars += KorCons()[i_value[0]];
643
}
644
}
645
else if(i_level == 3)
646
{
647
int tmp = -1;
648
for(size_t i = 2; i < sizeof(kor_lconsCom) / 4; i+=3)
649
{
650
if(kor_lconsCom[i] == i_value[2])
651
{
652
tmp = kor_lconsCom[i - 1];
653
break;
654
}
655
}
656
657
if(tmp != -1)
658
{
659
i_value[2] = tmp;
660
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + i_value[2] + 1;
661
inputChars += code;
662
}
663
else
664
{
665
i_level = 2;
666
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C;
667
inputChars += code;
668
}
669
}
670
}
671
672
int PSPOskDialog::GetIndex(const wchar_t* src, wchar_t ch)
673
{
674
for(int i = 0, end = (int)wcslen(src); i < end; i++)
675
{
676
if(src[i] == ch)
677
{
678
return i;
679
}
680
}
681
682
return -1;
683
}
684
685
u32 PSPOskDialog::FieldMaxLength()
686
{
687
if ((oskParams->fields[0].outtextlimit > oskParams->fields[0].outtextlength - 1) || oskParams->fields[0].outtextlimit == 0)
688
return oskParams->fields[0].outtextlength - 1;
689
return oskParams->fields[0].outtextlimit;
690
}
691
692
void PSPOskDialog::RenderKeyboard()
693
{
694
// Sanity check that a valid keyboard is selected.
695
if ((int)currentKeyboard < 0 || (int)currentKeyboard >= OSK_KEYBOARD_COUNT) {
696
return;
697
}
698
699
int selectedRow = selectedChar / numKeyCols[currentKeyboard];
700
int selectedCol = selectedChar % numKeyCols[currentKeyboard];
701
702
char16_t temp[2];
703
temp[1] = '\0';
704
705
std::string buffer;
706
707
static const u32 FIELDDRAWMAX = 16;
708
u32 limit = FieldMaxLength();
709
u32 drawLimit = std::min(FIELDDRAWMAX, limit); // Field drew length limit.
710
711
const float keyboardLeftSide = (480.0f - (24.0f * numKeyCols[currentKeyboard])) / 2.0f;
712
const float characterWidth = 12.0f;
713
float previewLeftSide = (480.0f - (12.0f * drawLimit)) / 2.0f;
714
float title = (480.0f - (0.5f * drawLimit)) / 2.0f;
715
716
PPGeStyle descStyle = FadedStyle(PPGeAlign::BOX_CENTER, 0.5f);
717
PPGeDrawText(oskDesc, title, 20, descStyle);
718
719
PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_HCENTER, 0.5f);
720
721
PPGeStyle keyStyle = FadedStyle(PPGeAlign::BOX_HCENTER, 0.6f);
722
PPGeStyle selectedKeyStyle = FadedStyle(PPGeAlign::BOX_HCENTER, 0.6f);
723
selectedKeyStyle.color = CalcFadedColor(0xFF3060FF);
724
725
std::u16string result;
726
727
result = CombinationString(false);
728
729
u32 drawIndex = (u32)(result.size() > drawLimit ? result.size() - drawLimit : 0);
730
drawIndex = result.size() == limit + 1 ? drawIndex - 1 : drawIndex; // When the length reached limit, the last character don't fade in and out.
731
for (u32 i = 0; i < drawLimit; ++i, ++drawIndex)
732
{
733
if (drawIndex + 1 < result.size())
734
{
735
temp[0] = result[drawIndex];
736
ConvertUCS2ToUTF8(buffer, temp);
737
PPGeDrawText(buffer, previewLeftSide + (i * characterWidth), 40.0f, textStyle);
738
}
739
else
740
{
741
if (drawIndex + 1 == result.size())
742
{
743
temp[0] = result[drawIndex];
744
745
if(isCombinated == true)
746
{
747
float animStep = (float)(__DisplayGetNumVblanks() % 40) / 20.0f;
748
// Fade in and out the next character so they know it's not part of the string yet.
749
u32 alpha = (0.5f - (cosf(animStep * M_PI) / 2.0f)) * 128 + 127;
750
PPGeStyle animStyle = textStyle;
751
animStyle.color = CalcFadedColor((alpha << 24) | 0x00FFFFFF);
752
753
ConvertUCS2ToUTF8(buffer, temp);
754
755
PPGeDrawText(buffer, previewLeftSide + (i * characterWidth), 40.0f, animStyle);
756
757
// Also draw the underline for the same reason.
758
PPGeDrawText("_", previewLeftSide + (i * characterWidth), 40.0f, textStyle);
759
}
760
else
761
{
762
ConvertUCS2ToUTF8(buffer, temp);
763
PPGeDrawText(buffer, previewLeftSide + (i * characterWidth), 40.0f, textStyle);
764
}
765
}
766
else
767
{
768
PPGeDrawText("_", previewLeftSide + (i * characterWidth), 40.0f, textStyle);
769
}
770
}
771
}
772
773
for (int row = 0; row < numKeyRows[currentKeyboard]; ++row)
774
{
775
for (int col = 0; col < numKeyCols[currentKeyboard]; ++col)
776
{
777
temp[0] = OskKeyAt(currentKeyboard, row, col);
778
779
ConvertUCS2ToUTF8(buffer, temp);
780
781
if (selectedRow == row && col == selectedCol) {
782
PPGeDrawText(buffer, keyboardLeftSide + (25.0f * col) + characterWidth / 2.0, 70.0f + (25.0f * row), selectedKeyStyle);
783
PPGeDrawText("_", keyboardLeftSide + (25.0f * col) + characterWidth / 2.0, 70.0f + (25.0f * row), keyStyle);
784
} else {
785
PPGeDrawText(buffer, keyboardLeftSide + (25.0f * col) + characterWidth / 2.0, 70.0f + (25.0f * row), keyStyle);
786
}
787
}
788
}
789
}
790
791
// TODO: Why does this have a 2 button press lag/delay when
792
// re-opening the dialog box? I don't get it.
793
int PSPOskDialog::NativeKeyboard() {
794
if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) {
795
return SCE_ERROR_UTILITY_INVALID_STATUS;
796
}
797
798
#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID)
799
bool beginInputBox = false;
800
if (nativeStatus_ == PSPOskNativeStatus::IDLE) {
801
std::lock_guard<std::mutex> guard(nativeMutex_);
802
if (nativeStatus_ == PSPOskNativeStatus::IDLE) {
803
nativeStatus_ = PSPOskNativeStatus::WAITING;
804
beginInputBox = true;
805
}
806
}
807
808
if (beginInputBox) {
809
std::u16string titleText;
810
GetWideStringFromPSPPointer(titleText, oskParams->fields[0].desc);
811
812
std::u16string defaultText;
813
GetWideStringFromPSPPointer(defaultText, oskParams->fields[0].intext);
814
815
// There's already ConvertUCS2ToUTF8 in this file. Should we use that instead of the global ones?
816
System_InputBoxGetString(NON_EPHEMERAL_TOKEN, ::ConvertUCS2ToUTF8(titleText), ::ConvertUCS2ToUTF8(defaultText), false,
817
[&](const std::string &value, int) {
818
// Success callback
819
std::lock_guard<std::mutex> guard(nativeMutex_);
820
if (nativeStatus_ != PSPOskNativeStatus::WAITING) {
821
return;
822
}
823
nativeValue_ = value;
824
nativeStatus_ = PSPOskNativeStatus::SUCCESS;
825
},
826
[&]() {
827
// Failure callback
828
std::lock_guard<std::mutex> guard(nativeMutex_);
829
if (nativeStatus_ != PSPOskNativeStatus::WAITING) {
830
return;
831
}
832
nativeValue_ = "";
833
nativeStatus_ = PSPOskNativeStatus::FAILURE;
834
}
835
);
836
} else if (nativeStatus_ == PSPOskNativeStatus::SUCCESS) {
837
inputChars = ConvertUTF8ToUCS2(nativeValue_);
838
nativeValue_.clear();
839
840
u32 maxLength = FieldMaxLength();
841
if (inputChars.length() > maxLength) {
842
ERROR_LOG(Log::sceUtility, "NativeKeyboard: input text too long(%d characters/glyphs max), truncating to game-requested length.", maxLength);
843
inputChars.erase(maxLength, std::string::npos);
844
}
845
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);
846
nativeStatus_ = PSPOskNativeStatus::DONE;
847
} else if (nativeStatus_ == PSPOskNativeStatus::FAILURE) {
848
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);
849
nativeStatus_ = PSPOskNativeStatus::DONE;
850
}
851
#endif
852
853
u16_le *outText = oskParams->fields[0].outtext;
854
855
size_t end = oskParams->fields[0].outtextlength;
856
if (end > inputChars.size())
857
end = inputChars.size() + 1;
858
// Only write the bytes of the output and the null terminator, don't write the rest.
859
for (size_t i = 0; i < end; ++i) {
860
u16 value = 0;
861
if (i < FieldMaxLength() && i < inputChars.size())
862
value = inputChars[i];
863
outText[i] = value;
864
}
865
866
oskParams->base.result = 0;
867
oskParams->fields[0].result = PSP_UTILITY_OSK_RESULT_CHANGED;
868
869
return 0;
870
}
871
872
int PSPOskDialog::Update(int animSpeed) {
873
if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) {
874
return SCE_ERROR_UTILITY_INVALID_STATUS;
875
}
876
877
int cancelButton = GetCancelButton();
878
int confirmButton = GetConfirmButton();
879
880
static int cancelBtnFramesHeld = 0;
881
static int confirmBtnFramesHeld = 0;
882
static int leftBtnFramesHeld = 0;
883
static int upBtnFramesHeld = 0;
884
static int downBtnFramesHeld = 0;
885
static int rightBtnFramesHeld = 0;
886
const int framesHeldThreshold = 10;
887
const int framesHeldRepeatRate = 5;
888
889
UpdateButtons();
890
UpdateCommon();
891
int selectedRow = selectedChar / numKeyCols[currentKeyboard];
892
int selectedExtra = selectedChar % numKeyCols[currentKeyboard];
893
894
#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
895
// Windows: Fall back to the OSK/continue normally if we're in fullscreen.
896
// The dialog box doesn't work right if in fullscreen.
897
if (System_GetPropertyBool(SYSPROP_HAS_KEYBOARD)) {
898
if (g_Config.bBypassOSKWithKeyboard && !g_Config.UseFullScreen())
899
return NativeKeyboard();
900
}
901
#endif
902
903
UpdateFade(animSpeed);
904
905
StartDraw();
906
PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x63636363));
907
RenderKeyboard();
908
909
auto di = GetI18NCategory(I18NCat::DIALOG);
910
911
PPGeStyle actionStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.5f);
912
PPGeStyle guideStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.6f);
913
914
PPGeDrawImage(ImageID("I_SQUARE"), 365, 222, 16, 16, guideStyle);
915
PPGeDrawText(di->T("Space"), 390, 222, actionStyle);
916
917
if (GetConfirmButton() != CTRL_CIRCLE) {
918
PPGeDrawImage(ImageID("I_CROSS"), 45, 222, 16, 16, guideStyle);
919
PPGeDrawImage(ImageID("I_CIRCLE"), 45, 247, 16, 16, guideStyle);
920
} else {
921
PPGeDrawImage(ImageID("I_CIRCLE"), 45, 222, 16, 16, guideStyle);
922
PPGeDrawImage(ImageID("I_CROSS"), 45, 247, 16, 16, guideStyle);
923
}
924
925
PPGeDrawText(di->T("Select"), 75, 222, actionStyle);
926
PPGeDrawText(di->T("Delete"), 75, 247, actionStyle);
927
928
PPGeDrawText("Start", 135, 220, guideStyle);
929
PPGeDrawText(di->T("Finish"), 185, 222, actionStyle);
930
931
auto lookupLangName = [&](int direction) {
932
// First, find the valid one...
933
OskKeyboardLanguage lang = (OskKeyboardLanguage)((OSK_LANGUAGE_COUNT + currentKeyboardLanguage + direction) % OSK_LANGUAGE_COUNT);
934
OskKeyboardDisplay disp = OskKeyboardCases[lang][LOWERCASE];
935
FindValidKeyboard(oskParams->fields[0].inputtype, direction, lang, disp);
936
937
if (lang == currentKeyboardLanguage) {
938
return (const char *)nullptr;
939
}
940
941
// Now, let's grab the name.
942
std::string countryCode(OskKeyboardNames[lang]);
943
const char *language = languageMapping[countryCode].first.c_str();
944
945
// It seems like this is a "fake" country code for extra keyboard purposes.
946
if (countryCode == "English Full-width")
947
language = "English Full-width";
948
949
return language;
950
};
951
952
if (OskKeyboardNames[currentKeyboardLanguage] != "ko_KR" && IsKeyboardShiftValid(oskParams->fields[0].inputtype, currentKeyboardLanguage, currentKeyboard)) {
953
PPGeDrawText("Select", 135, 245, guideStyle);
954
PPGeDrawText(di->T("Shift"), 185, 247, actionStyle);
955
}
956
957
const char *prevLang = lookupLangName(-1);
958
if (prevLang) {
959
PPGeDrawText("L", 235, 220, guideStyle);
960
PPGeDrawText(prevLang, 255, 222, actionStyle);
961
}
962
963
const char *nextLang = lookupLangName(1);
964
if (nextLang) {
965
PPGeDrawText("R", 235, 245, guideStyle);
966
PPGeDrawText(nextLang, 255, 247, actionStyle);
967
}
968
969
if (IsButtonPressed(CTRL_UP) || IsButtonHeld(CTRL_UP, upBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {
970
selectedChar -= numKeyCols[currentKeyboard];
971
} else if (IsButtonPressed(CTRL_DOWN) || IsButtonHeld(CTRL_DOWN, downBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {
972
selectedChar += numKeyCols[currentKeyboard];
973
} else if (IsButtonPressed(CTRL_LEFT) || IsButtonHeld(CTRL_LEFT, leftBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {
974
selectedChar--;
975
if (((selectedChar + numKeyCols[currentKeyboard]) % numKeyCols[currentKeyboard]) == numKeyCols[currentKeyboard] - 1)
976
selectedChar += numKeyCols[currentKeyboard];
977
} else if (IsButtonPressed(CTRL_RIGHT) || IsButtonHeld(CTRL_RIGHT, rightBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {
978
selectedChar++;
979
if ((selectedChar % numKeyCols[currentKeyboard]) == 0)
980
selectedChar -= numKeyCols[currentKeyboard];
981
}
982
983
selectedChar = (selectedChar + (numKeyCols[currentKeyboard] * numKeyRows[currentKeyboard])) % (numKeyCols[currentKeyboard] * numKeyRows[currentKeyboard]);
984
985
if (IsButtonPressed(confirmButton) || IsButtonHeld(confirmButton, confirmBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {
986
inputChars = CombinationString(true);
987
} else if (IsButtonPressed(CTRL_SELECT)) {
988
// Select now swaps case.
989
if (IsKeyboardShiftValid(oskParams->fields[0].inputtype, currentKeyboardLanguage, currentKeyboard)) {
990
if (currentKeyboard == OskKeyboardCases[currentKeyboardLanguage][UPPERCASE])
991
currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE];
992
else
993
currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][UPPERCASE];
994
}
995
996
if (selectedRow >= numKeyRows[currentKeyboard]) {
997
selectedRow = numKeyRows[currentKeyboard] - 1;
998
}
999
1000
if (selectedExtra >= numKeyCols[currentKeyboard]) {
1001
selectedExtra = numKeyCols[currentKeyboard] - 1;
1002
}
1003
1004
selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra;
1005
} else if (IsButtonPressed(CTRL_RTRIGGER)) {
1006
// RTRIGGER now cycles languages forward.
1007
currentKeyboardLanguage = (OskKeyboardLanguage)((currentKeyboardLanguage + 1) % OSK_LANGUAGE_COUNT);
1008
currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE];
1009
FindValidKeyboard(oskParams->fields[0].inputtype, 1, currentKeyboardLanguage, currentKeyboard);
1010
1011
if (selectedRow >= numKeyRows[currentKeyboard]) {
1012
selectedRow = numKeyRows[currentKeyboard] - 1;
1013
}
1014
1015
if (selectedExtra >= numKeyCols[currentKeyboard]) {
1016
selectedExtra = numKeyCols[currentKeyboard] - 1;
1017
}
1018
1019
selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra;
1020
} else if (IsButtonPressed(CTRL_LTRIGGER)) {
1021
// LTRIGGER now cycles languages backward.
1022
if (currentKeyboardLanguage - 1 >= 0)
1023
currentKeyboardLanguage = (OskKeyboardLanguage)((currentKeyboardLanguage - 1) % OSK_LANGUAGE_COUNT);
1024
else
1025
currentKeyboardLanguage = (OskKeyboardLanguage)(OSK_LANGUAGE_COUNT - 1);
1026
currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE];
1027
FindValidKeyboard(oskParams->fields[0].inputtype, -1, currentKeyboardLanguage, currentKeyboard);
1028
1029
if (selectedRow >= numKeyRows[currentKeyboard]) {
1030
selectedRow = numKeyRows[currentKeyboard] - 1;
1031
}
1032
1033
if (selectedExtra >= numKeyCols[currentKeyboard]) {
1034
selectedExtra = numKeyCols[currentKeyboard] - 1;
1035
}
1036
1037
selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra;
1038
} else if (IsButtonPressed(cancelButton) || IsButtonHeld(cancelButton, cancelBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {
1039
if (inputChars.size() > 0) {
1040
inputChars.resize(inputChars.size() - 1);
1041
if (i_level != 0) {
1042
RemoveKorean();
1043
}
1044
}
1045
} else if (IsButtonPressed(CTRL_START)) {
1046
StartFade(false);
1047
} else if (IsButtonPressed(CTRL_SQUARE) && inputChars.size() < FieldMaxLength()) {
1048
// Use a regular space if the current keyboard isn't Japanese nor full-width English
1049
if (currentKeyboardLanguage != OSK_LANGUAGE_JAPANESE && currentKeyboardLanguage != OSK_LANGUAGE_ENGLISH_FW)
1050
inputChars += u" ";
1051
else
1052
inputChars += u" ";
1053
}
1054
1055
EndDraw();
1056
1057
u16_le *outText = oskParams->fields[0].outtext;
1058
size_t end = oskParams->fields[0].outtextlength;
1059
// Only write the bytes of the output and the null terminator, don't write the rest.
1060
if (end > inputChars.size())
1061
end = inputChars.size() + 1;
1062
for (size_t i = 0; i < end; ++i)
1063
{
1064
u16 value = 0;
1065
if (i < FieldMaxLength() && i < inputChars.size())
1066
value = inputChars[i];
1067
outText[i] = value;
1068
}
1069
1070
oskParams->base.result = 0;
1071
oskParams->fields[0].result = PSP_UTILITY_OSK_RESULT_CHANGED;
1072
return 0;
1073
}
1074
1075
int PSPOskDialog::Shutdown(bool force)
1076
{
1077
if (GetStatus() != SCE_UTILITY_STATUS_FINISHED && !force)
1078
return SCE_ERROR_UTILITY_INVALID_STATUS;
1079
1080
PSPDialog::Shutdown(force);
1081
if (!force) {
1082
ChangeStatusShutdown(OSK_SHUTDOWN_DELAY_US);
1083
}
1084
nativeStatus_ = PSPOskNativeStatus::IDLE;
1085
1086
return 0;
1087
}
1088
1089
void PSPOskDialog::DoState(PointerWrap &p)
1090
{
1091
PSPDialog::DoState(p);
1092
1093
auto s = p.Section("PSPOskDialog", 1, 2);
1094
if (!s)
1095
return;
1096
1097
// TODO: Should we save currentKeyboard/currentKeyboardLanguage?
1098
1099
Do(p, oskParams);
1100
Do(p, oskDesc);
1101
Do(p, oskIntext);
1102
Do(p, oskOuttext);
1103
Do(p, selectedChar);
1104
if (s >= 2) {
1105
Do(p, inputChars);
1106
} else {
1107
// Discard the wstring.
1108
std::wstring wstr;
1109
Do(p, wstr);
1110
}
1111
// Don't need to save state native status or value.
1112
}
1113
1114
pspUtilityDialogCommon *PSPOskDialog::GetCommonParam()
1115
{
1116
return &oskParams->base;
1117
}
1118
1119