From 1b73f68a44031fc694ddffcbc0e349e41414701d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 4 Jan 2025 11:28:58 +0100 Subject: [PATCH] Make PSPOskConstants.cpp not binary anymore --- .gitattributes | 2 +- Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 6 + Core/Dialog/PSPOskConstants.cpp | 118 ++ Core/Dialog/PSPOskConstants.h | 16 + Core/Dialog/PSPOskDialog.cpp | 2320 +++++++++++++++---------------- Core/Dialog/PSPOskDialog.h | 18 +- 7 files changed, 1267 insertions(+), 1215 deletions(-) create mode 100644 Core/Dialog/PSPOskConstants.cpp create mode 100644 Core/Dialog/PSPOskConstants.h diff --git a/.gitattributes b/.gitattributes index 531cac20f9..1b2fed5acc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -11,4 +11,4 @@ *.xml text # To avoid mucking up the utf-8 characters. -Core/Dialog/PSPOskDialog.cpp binary \ No newline at end of file +Core/Dialog/PSPOskConstants.cpp binary diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 3b4dc630e8..c8d1d26165 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -564,6 +564,7 @@ + @@ -1174,6 +1175,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 8dcc3c8b93..328d352e3b 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1315,6 +1315,9 @@ HLE\Libraries + + Dialog + @@ -2109,6 +2112,9 @@ HLE\Libraries + + Dialog + diff --git a/Core/Dialog/PSPOskConstants.cpp b/Core/Dialog/PSPOskConstants.cpp new file mode 100644 index 0000000000..d298f8c4c4 --- /dev/null +++ b/Core/Dialog/PSPOskConstants.cpp @@ -0,0 +1,118 @@ +// Copyright (c) 2012- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include + +#include "Core/Dialog/PSPOskConstants.h" + +// Japanese (Kana) diacritics +const wchar_t diacritics[2][103] = +{ + {L"かがきぎくぐけげこごさざしじすずせぜそぞただちぢつづてでとどはばぱばひびぴびふぶぷぶへべぺべほぼぽぼウヴカガキギクグケゲコゴサザシジスズセゼソゾタダチヂツヅテデトドハバパバヒビピビフブプブヘベペベホボポボ"}, + {L"はぱばぱひぴびぴふぷぶぷへぺべぺほぽぼぽハパバパヒピビピフプブプヘペベペホポボポ"} +}; + +// Korean (Hangul) consonant +const wchar_t kor_cons[20] = L"ㄱㄲㄴㄷㄸㄹㅁㅂㅃㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎ"; + +// Korean (Hangul) vowels, Some vowels are not used, they will be spaces +const wchar_t kor_vowel[22] = L"ㅏㅐㅑㅒㅓㅔㅕㅖㅗ ㅛㅜ ㅠㅡ ㅣ"; + +// Korean (Hangul) vowel Combination key +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}; + +// Korean (Hangul) last consonant(diacritics) +const wchar_t kor_lcons[28] = L"ㄱㄲㄳㄴㄵㄶㄷㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅄㅅㅆㅇㅈㅊㅋㅌㅍㅎ"; + +// Korean (Hangul) last consonant Combination key +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}; + +// Korean (Hangul) last consonant Separation key +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}; + +const char16_t oskKeys[OSK_KEYBOARD_COUNT][6][14] = +{ + { + // Latin Lowercase + {u"1234567890-+"}, + {u"qwertyuiop[]"}, + {u"asdfghjkl;@~"}, + {u"zxcvbnm,./?\\"}, + }, + { + // Latin Uppercase + {u"!@#$%^&*()_+"}, + {u"QWERTYUIOP{}"}, + {u"ASDFGHJKL:\"`"}, + {u"ZXCVBNM<>/?|"}, + }, + { + // Hiragana + {u"あかさたなはまやらわぁゃっ"}, + {u"いきしちにひみ り ぃ  "}, + {u"うくすつぬふむゆるをぅゅ゛"}, + {u"えけせてねへめ れ ぇ ゜"}, + {u"おこそとのほもよろんぉょー"}, + {u"・。、「」『』〜 "}, + }, + { + // Katakana + {u"アカサタナハマヤラワァャッ"}, + {u"イキシチニヒミ リ ィ  "}, + {u"ウクスツヌフムユルヲゥュ゛"}, + {u"エケセテネヘメ レ ェ ゜"}, + {u"オコソトノホモヨロンォョー"}, + {u"・。、「」『』〜 "}, + }, + { + // Korean(Hangul) + {u"1234567890-+"}, + {u"ㅃㅉㄸㄲㅆ!@#$%^&"}, + {u"ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ[]"}, + {u"ㅁㄴㅇㄹㅎㅗㅓㅏㅣ;@~"}, + {u"ㅋㅌㅊㅍㅠㅜㅡ<>/?|"}, + }, + { + // Russian Lowercase + {u"1234567890-+"}, + {u"йцукенгшщзхъ"}, + {u"фывапролджэё"}, + {u"ячсмитьбю/?|"}, + }, + { + // Russian Uppercase + {u"!@#$%^&*()_+"}, + {u"ЙЦУКЕНГШЩЗХЪ"}, + {u"ФЫВАПРОЛДЖЭЁ"}, + {u"ЯЧСМИТЬБЮ/?|"}, + }, + { + // Latin Full-width Lowercase + { u"1234567890-+" }, + { u"qwertyuiop[]" }, + { u"asdfghjkl;@~" }, + { u"zxcvbnm,./?¥¥" }, + }, + { + // Latin Full-width Uppercase + { u"!@#$%^&*()_+" }, + { u"QWERTYUIOP{}" }, + { u"ASDFGHJKL:¥”‘" }, + { u"ZXCVBNM<>/?|" }, + }, +}; + diff --git a/Core/Dialog/PSPOskConstants.h b/Core/Dialog/PSPOskConstants.h new file mode 100644 index 0000000000..d36ce85199 --- /dev/null +++ b/Core/Dialog/PSPOskConstants.h @@ -0,0 +1,16 @@ +#pragma once + +// Internal enum, not from PSP. +enum OskKeyboardDisplay { + OSK_KEYBOARD_LATIN_LOWERCASE, + OSK_KEYBOARD_LATIN_UPPERCASE, + OSK_KEYBOARD_HIRAGANA, + OSK_KEYBOARD_KATAKANA, + OSK_KEYBOARD_KOREAN, + OSK_KEYBOARD_RUSSIAN_LOWERCASE, + OSK_KEYBOARD_RUSSIAN_UPPERCASE, + OSK_KEYBOARD_LATIN_FW_LOWERCASE, + OSK_KEYBOARD_LATIN_FW_UPPERCASE, + // TODO: Something to do native? + OSK_KEYBOARD_COUNT +}; diff --git a/Core/Dialog/PSPOskDialog.cpp b/Core/Dialog/PSPOskDialog.cpp index 9f9b376668..b7d6773883 100755 --- a/Core/Dialog/PSPOskDialog.cpp +++ b/Core/Dialog/PSPOskDialog.cpp @@ -1,1197 +1,1123 @@ -// Copyright (c) 2012- PPSSPP Project. - -// 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, version 2.0 or later versions. - -// 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 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official git repository and contact information can be found at -// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. - -#include "ppsspp_config.h" - -#include -#include -#include -#include "Common/Data/Text/I18n.h" -#include "Common/Math/math_util.h" -#include "Common/Data/Encoding/Utf8.h" -#include "Common/Serialize/SerializeFuncs.h" -#include "Common/System/Request.h" -#include "Common/Serialize/Serializer.h" - -#include "Core/Dialog/PSPOskDialog.h" -#include "Core/Util/PPGeDraw.h" -#include "Core/HLE/sceCtrl.h" -#include "Core/HLE/sceUtility.h" -#include "Core/HW/Display.h" -#include "Core/Config.h" -#include "Core/Reporting.h" - -// These are rough, it seems to take a long time to init, and probably depends on threads. -// TODO: This takes like 700ms on a PSP but that's annoyingly long. -const static int OSK_INIT_DELAY_US = 300000; -const static int OSK_SHUTDOWN_DELAY_US = 40000; - -static std::map, std::less<>> languageMapping; - -const uint8_t numKeyCols[OSK_KEYBOARD_COUNT] = {12, 12, 13, 13, 12, 12, 12, 12, 12}; -const uint8_t numKeyRows[OSK_KEYBOARD_COUNT] = {4, 4, 6, 6, 5, 4, 4, 4, 4}; - -// Japanese (Kana) diacritics -static const wchar_t diacritics[2][103] = -{ - {L"かがきぎくぐけげこごさざしじすずせぜそぞただちぢつづてでとどはばぱばひびぴびふぶぷぶへべぺべほぼぽぼウヴカガキギクグケゲコゴサザシジスズセゼソゾタダチヂツヅテデトドハバパバヒビピビフブプブヘベペベホボポボ"}, - {L"はぱばぱひぴびぴふぷぶぷへぺべぺほぽぼぽハパバパヒピビピフプブプヘペベペホポボポ"} -}; - -// Korean (Hangul) consonant -static const wchar_t kor_cons[] = L"ㄱㄲㄴㄷㄸㄹㅁㅂㅃㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎ"; - -// Korean (Hangul) vowels, Some vowels are not used, they will be spaces -static const wchar_t kor_vowel[] = L"ㅏㅐㅑㅒㅓㅔㅕㅖㅗ ㅛㅜ ㅠㅡ ㅣ"; - -// Korean (Hangul) vowel Combination key -const uint8_t kor_vowelCom[] = {0,8,9,1,8,10,20,8,11,4,13,14,5,13,15,20,13,16,20,18,19}; - -// Korean (Hangul) last consonant(diacritics) -static const wchar_t kor_lcons[] = L"ㄱㄲㄳㄴㄵㄶㄷㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅄㅅㅆㅇㅈㅊㅋㅌㅍㅎ"; - -// Korean (Hangul) last consonant Combination key -const uint8_t kor_lconsCom[] = {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}; - -// Korean (Hangul) last consonant Separation key -const uint8_t kor_lconsSpr[] = {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}; - -static const char16_t oskKeys[OSK_KEYBOARD_COUNT][6][14] = -{ - { - // Latin Lowercase - {u"1234567890-+"}, - {u"qwertyuiop[]"}, - {u"asdfghjkl;@~"}, - {u"zxcvbnm,./?\\"}, - }, - { - // Latin Uppercase - {u"!@#$%^&*()_+"}, - {u"QWERTYUIOP{}"}, - {u"ASDFGHJKL:\"`"}, - {u"ZXCVBNM<>/?|"}, - }, - { - // Hiragana - {u"あかさたなはまやらわぁゃっ"}, - {u"いきしちにひみ り ぃ  "}, - {u"うくすつぬふむゆるをぅゅ゛"}, - {u"えけせてねへめ れ ぇ ゜"}, - {u"おこそとのほもよろんぉょー"}, - {u"・。、「」『』〜 "}, - }, - { - // Katakana - {u"アカサタナハマヤラワァャッ"}, - {u"イキシチニヒミ リ ィ  "}, - {u"ウクスツヌフムユルヲゥュ゛"}, - {u"エケセテネヘメ レ ェ ゜"}, - {u"オコソトノホモヨロンォョー"}, - {u"・。、「」『』〜 "}, - }, - { - // Korean(Hangul) - {u"1234567890-+"}, - {u"ㅃㅉㄸㄲㅆ!@#$%^&"}, - {u"ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ[]"}, - {u"ㅁㄴㅇㄹㅎㅗㅓㅏㅣ;@~"}, - {u"ㅋㅌㅊㅍㅠㅜㅡ<>/?|"}, - }, - { - // Russian Lowercase - {u"1234567890-+"}, - {u"йцукенгшщзхъ"}, - {u"фывапролджэё"}, - {u"ячсмитьбю/?|"}, - }, - { - // Russian Uppercase - {u"!@#$%^&*()_+"}, - {u"ЙЦУКЕНГШЩЗХЪ"}, - {u"ФЫВАПРОЛДЖЭЁ"}, - {u"ЯЧСМИТЬБЮ/?|"}, - }, - { - // Latin Full-width Lowercase - { u"1234567890-+" }, - { u"qwertyuiop[]" }, - { u"asdfghjkl;@~" }, - { u"zxcvbnm,./?¥¥" }, - }, - { - // Latin Full-width Uppercase - { u"!@#$%^&*()_+" }, - { u"QWERTYUIOP{}" }, - { u"ASDFGHJKL:¥”‘" }, - { u"ZXCVBNM<>/?|" }, - }, -}; - -// This isn't a complete representation of these flags, it just helps ensure we show the right keyboards. -static const int allowedInputFlagsMap[OSK_KEYBOARD_COUNT] = { - PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT, - PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL, - PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HIRAGANA, - PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_KATAKANA | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HALF_KATAKANA, - PSP_UTILITY_OSK_INPUTTYPE_KOREAN, - PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE, - PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE, - PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_DIGIT, - PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL, -}; -static const int defaultInputFlagsMap[OSK_KEYBOARD_COUNT] = { - PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT, - PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL, - PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HIRAGANA, - PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_KATAKANA | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HALF_KATAKANA, - PSP_UTILITY_OSK_INPUTTYPE_KOREAN, - PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE, - PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE, - PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_DIGIT, - PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL, -}; - -PSPOskDialog::PSPOskDialog(UtilityDialogType type) : PSPDialog(type) { - // This can break all kinds of stuff, changing the decimal point in sprintf for example. - // Not sure what the intended effect is so commented out for now. - // setlocale(LC_ALL, ""); -} - -PSPOskDialog::~PSPOskDialog() { -} - -void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, const PSPPointer& em_address) -{ - if (!em_address.IsValid()) - { - _string.clear(); - return; - } - - const size_t maxLength = 2047; - char stringBuffer[maxLength + 1]; - char *string = stringBuffer; - - u16_le *input = &em_address[0]; - int c; - while ((c = *input++) != 0 && string < stringBuffer + maxLength) - { - if (c < 0x80) - *string++ = c; - else if (c < 0x800) { - *string++ = 0xC0 | (c >> 6); - *string++ = 0x80 | (c & 0x3F); - } else { - *string++ = 0xE0 | (c >> 12); - *string++ = 0x80 | ((c >> 6) & 0x3F); - *string++ = 0x80 | (c & 0x3F); - } - } - *string++ = '\0'; - _string = stringBuffer; -} - -void GetWideStringFromPSPPointer(std::u16string& _string, const PSPPointer& em_address) -{ - if (!em_address.IsValid()) - { - _string.clear(); - return; - } - - const size_t maxLength = 2047; - char16_t stringBuffer[maxLength + 1]; - char16_t *string = stringBuffer; - - u16_le *input = &em_address[0]; - int c; - while ((c = *input++) != 0 && string < stringBuffer + maxLength) - *string++ = c; - *string++ = '\0'; - _string = stringBuffer; -} - -void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, const char16_t *input) -{ - char stringBuffer[2048]; - char *string = stringBuffer; - - int c; - while ((c = *input++) != 0) - { - if (c < 0x80) - *string++ = c; - else if (c < 0x800) { - *string++ = 0xC0 | (c >> 6); - *string++ = 0x80 | (c & 0x3F); - } else { - *string++ = 0xE0 | (c >> 12); - *string++ = 0x80 | ((c >> 6) & 0x3F); - *string++ = 0x80 | (c & 0x3F); - } - } - *string++ = '\0'; - _string = stringBuffer; -} - -static void FindValidKeyboard(s32 inputType, int direction, OskKeyboardLanguage &lang, OskKeyboardDisplay &disp) { - OskKeyboardLanguage origLang = lang; - OskKeyboardDisplay origDisp = disp; - - if (inputType == 0) { - return; - } - // We use direction = 0 for default, but we actually move "forward". - const int *matchMap = allowedInputFlagsMap; - if (direction == 0) { - direction = 1; - matchMap = defaultInputFlagsMap; - } - - // TODO: Limit by allowed keyboards properly... this is just an approximation. - int tries = OSK_LANGUAGE_COUNT * 2; - while (!(inputType & matchMap[disp]) && tries > 0) { - if ((--tries % 2) == 0) { - lang = (OskKeyboardLanguage)((OSK_LANGUAGE_COUNT + lang + direction) % OSK_LANGUAGE_COUNT); - disp = OskKeyboardCases[lang][LOWERCASE]; - } else { - disp = OskKeyboardCases[lang][UPPERCASE]; - } - } - - if (tries == 0) { - // In case of error, let's just fall back to allowing all. - lang = origLang; - disp = origDisp; - } -} - -static bool IsKeyboardShiftValid(s32 inputType, OskKeyboardLanguage lang, OskKeyboardDisplay disp) { - // Swap disp and check if it's valid. - if (disp == OskKeyboardCases[lang][UPPERCASE]) - disp = OskKeyboardCases[lang][LOWERCASE]; - else - disp = OskKeyboardCases[lang][UPPERCASE]; - - return inputType == 0 || (inputType & allowedInputFlagsMap[disp]) != 0; -} - -int PSPOskDialog::Init(u32 oskPtr) { - // Ignore if already running - if (GetStatus() != SCE_UTILITY_STATUS_NONE) { - ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid status"); - return SCE_ERROR_UTILITY_INVALID_STATUS; - } - // Seems like this should crash? - if (!Memory::IsValidAddress(oskPtr)) { - ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid params (%08x)", oskPtr); - return -1; - } - - oskParams = oskPtr; - if (oskParams->base.size != sizeof(SceUtilityOskParams)) - { - ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid size %d", oskParams->base.size); - return SCE_ERROR_UTILITY_INVALID_PARAM_SIZE; - } - // Also seems to crash. - if (!oskParams->fields.IsValid()) - { - ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid field data (%08x)", oskParams->fields.ptr); - return -1; - } - - if (oskParams->unk_60 != 0) - WARN_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: unknown param is non-zero (%08x)", oskParams->unk_60); - if (oskParams->fieldCount != 1) - WARN_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: unsupported field count %d", oskParams->fieldCount); - - ChangeStatusInit(OSK_INIT_DELAY_US); - selectedChar = 0; - currentKeyboardLanguage = OSK_LANGUAGE_ENGLISH; - currentKeyboard = OSK_KEYBOARD_LATIN_LOWERCASE; - FindValidKeyboard(oskParams->fields[0].inputtype, 0, currentKeyboardLanguage, currentKeyboard); - - ConvertUCS2ToUTF8(oskDesc, oskParams->fields[0].desc); - ConvertUCS2ToUTF8(oskIntext, oskParams->fields[0].intext); - ConvertUCS2ToUTF8(oskOuttext, oskParams->fields[0].outtext); - - i_level = 0; - - inputChars.clear(); - - if (oskParams->fields[0].intext.IsValid()) { - auto src = oskParams->fields[0].intext; - int c; - while ((c = *src++) != 0) - inputChars += c; - } - - languageMapping = g_Config.GetLangValuesMapping(); - - // Eat any keys pressed before the dialog inited. - UpdateButtons(); - InitCommon(); - - std::lock_guard guard(nativeMutex_); - nativeStatus_ = PSPOskNativeStatus::IDLE; - - StartFade(true); - return 0; -} - -std::u16string PSPOskDialog::CombinationKorean(bool isInput) -{ - std::u16string string; - - isCombinated = true; - - int selectedRow = selectedChar / numKeyCols[currentKeyboard]; - int selectedCol = selectedChar % numKeyCols[currentKeyboard]; - - if (inputChars.size() == 0) { - wchar_t sw = oskKeys[currentKeyboard][selectedRow][selectedCol]; - - if (inputChars.size() < FieldMaxLength()) { - string += sw; - - i_value[0] = GetIndex(kor_cons, sw); - - if(i_value[0] != -1 && isInput == true) - i_level = 1; - } else { - isCombinated = false; - } - } else { - for(u32 i = 0; i < inputChars.size(); i++) { - if(i + 1 == inputChars.size()) { - wchar_t sw = oskKeys[currentKeyboard][selectedRow][selectedCol]; - - if(i_level == 0) { - string += inputChars[i]; - if (inputChars.size() < FieldMaxLength()) { - string += sw; - - i_value[0] = GetIndex(kor_cons, sw); - - if(i_value[0] != -1 && isInput == true) - i_level = 1; - } else { - isCombinated = false; - } - } else if(i_level == 1) { - i_value[1] = GetIndex(kor_vowel, sw); - - if(i_value[1] == -1) { - string += inputChars[i]; - if (inputChars.size() < FieldMaxLength()) { - string += sw; - - if(isInput == true) { - i_value[0] = GetIndex(kor_cons, sw); - - if(i_value[0] != -1) - i_level = 1; - else - i_level = 0; - } - } else { - isCombinated = false; - } - } else { - u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C; - string += code; - - if(isInput == true) { - i_level = 2; - } - } - } else if(i_level == 2) { - int tmp = GetIndex(kor_vowel, sw); - if(tmp != -1) { - int tmp2 = -1; - for(size_t j = 0; j < sizeof(kor_vowelCom) / 4; j+=3) { - if(kor_vowelCom[j] == tmp && kor_vowelCom[j + 1] == i_value[1]) { - tmp2 = kor_vowelCom[j + 2]; - break; - } - } - if(tmp2 != -1) { - if(isInput == true) { - i_value[1] = tmp2; - } - - u16 code = 0xAC00 + i_value[0] * 0x24C + tmp2 * 0x1C; - - string += code; - } else { - string += inputChars[i]; - if (inputChars.size() < FieldMaxLength()) { - string += sw; - - if(isInput == true) { - i_level = 0; - } - } else { - isCombinated = false; - } - } - } else { - int tmp2 = GetIndex(kor_lcons, sw); - - if (tmp2 == -1) { - string += inputChars[i]; - if (inputChars.size() < FieldMaxLength()) { - string += sw; - - if (isInput == true) { - i_value[0] = GetIndex(kor_cons, sw); - - if(i_value[0] != -1) - i_level = 1; - else - i_level = 0; - } - } else { - isCombinated = false; - } - } else { - u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + tmp2 + 1; - - string += code; - - if (isInput == true) { - i_level = 3; - i_value[2] = tmp2; - } - } - } - } else if(i_level == 3) { - int tmp = GetIndex(kor_lcons, sw); - if(tmp != -1) { - int tmp2 = -1; - for(size_t j = 0; j < sizeof(kor_lconsCom) / 4; j+=3) { - if(kor_lconsCom[j] == tmp && kor_lconsCom[j + 1] == i_value[2]) { - tmp2 = kor_lconsCom[j + 2]; - break; - } - } - if(tmp2 != -1) { - if(isInput == true) { - i_value[2] = tmp2; - } - - u16 code = 0xAC00 + i_value[0] * 0x24C + tmp2 * 0x1C + i_value[2] + 1; - - string += code; - } else { - string += inputChars[i]; - if (inputChars.size() < FieldMaxLength()) { - string += sw; - - if(isInput == true) { - i_value[0] = GetIndex(kor_cons, sw); - - if(i_value[0] != -1) - i_level = 1; - else - i_level = 0; - } - } else { - isCombinated = false; - } - } - } else { - int tmp3 = GetIndex(kor_vowel, sw); - if (tmp3 == -1) { - string += inputChars[i]; - if (inputChars.size() < FieldMaxLength()) { - string += sw; - - if(isInput == true) { - i_value[0] = GetIndex(kor_cons, sw); - - if(i_value[0] != -1) - i_level = 1; - else - i_level = 0; - } - } else { - isCombinated = false; - } - } else { - if (inputChars.size() < FieldMaxLength()) { - int tmp2 = -1; - for(size_t j = 0; j < sizeof(kor_lconsSpr) / 4; j+=3) { - if(kor_lconsSpr[j] == i_value[2]) { - tmp2 = (int)j; - break; - } - } - if(tmp2 != -1) { - u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + kor_lconsSpr[tmp2 + 1]; - string += code; - - code = 0xAC00 + kor_lconsSpr[tmp2 + 2] * 0x24C + tmp3 * 0x1C; - string += code; - - if(isInput == true) { - i_value[0] = kor_lconsSpr[tmp2 + 2]; - i_value[1] = tmp3; - i_level = 2; - } - } else { - int tmp4 = GetIndex(kor_cons, kor_lcons[i_value[2]]); - - if (tmp4 != -1) { - u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C; - - string += code; - - code = 0xAC00 + tmp4 * 0x24C + tmp3 * 0x1C; - - string += code; - - if(isInput == true) { - i_value[0] = tmp4; - i_value[1] = tmp3; - i_level = 2; - } - } else { - string += inputChars[i]; - string += sw; - - if(isInput == true) { - i_level = 0; - } - } - } - } else { - string += inputChars[i]; - isCombinated = false; - } - } - } - } - } else { - string += inputChars[i]; - } - } - } - - return string; -} - -std::u16string PSPOskDialog::CombinationString(bool isInput) -{ - std::u16string string; - - isCombinated = false; - - int selectedRow = selectedChar / numKeyCols[currentKeyboard]; - int selectedCol = selectedChar % numKeyCols[currentKeyboard]; - - if(currentKeyboard == OSK_KEYBOARD_KOREAN) - { - string = CombinationKorean(isInput); - } - else - { - if(isInput == true) - { - i_level = 0; - } - - if(oskKeys[currentKeyboard][selectedRow][selectedCol] == L'゛') - { - for(u32 i = 0; i < inputChars.size(); i++) - { - if(i + 1 == inputChars.size()) - { - for(u32 j = 0; j < wcslen(diacritics[0]); j+=2) - { - if(inputChars[i] == diacritics[0][j]) - { - string += diacritics[0][j + 1]; - isCombinated = true; - break; - } - } - - if(isCombinated == false) - { - string += inputChars[i]; - } - } - else - { - string += inputChars[i]; - } - } - } - else if(oskKeys[currentKeyboard][selectedRow][selectedCol] == L'゜') - { - for(u32 i = 0; i < inputChars.size(); i++) - { - if(i + 1 == inputChars.size()) - { - for(u32 j = 0; j < wcslen(diacritics[1]); j+=2) - { - if(inputChars[i] == diacritics[1][j]) - { - string += diacritics[1][j + 1]; - isCombinated = true; - break; - } - } - - if(isCombinated == false) - { - string += inputChars[i]; - } - } - else - { - string += inputChars[i]; - } - } - } - else - { - for(u32 i = 0; i < inputChars.size(); i++) - { - string += inputChars[i]; - } - - if (string.size() < FieldMaxLength()) - { - string += oskKeys[currentKeyboard][selectedRow][selectedCol]; - } - isCombinated = true; - } - } - - return string; -} - -void PSPOskDialog::RemoveKorean() -{ - if(i_level == 1) - { - i_level = 0; - } - else if(i_level == 2) - { - int tmp = -1; - for(size_t i = 2; i < sizeof(kor_vowelCom) / 4; i+=3) - { - if(kor_vowelCom[i] == i_value[1]) - { - tmp = kor_vowelCom[i - 1]; - break; - } - } - - if(tmp != -1) - { - i_value[1] = tmp; - u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C; - inputChars += code; - } - else - { - i_level = 1; - inputChars += kor_cons[i_value[0]]; - } - } - else if(i_level == 3) - { - int tmp = -1; - for(size_t i = 2; i < sizeof(kor_lconsCom) / 4; i+=3) - { - if(kor_lconsCom[i] == i_value[2]) - { - tmp = kor_lconsCom[i - 1]; - break; - } - } - - if(tmp != -1) - { - i_value[2] = tmp; - u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + i_value[2] + 1; - inputChars += code; - } - else - { - i_level = 2; - u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C; - inputChars += code; - } - } -} - -int PSPOskDialog::GetIndex(const wchar_t* src, wchar_t ch) -{ - for(int i = 0, end = (int)wcslen(src); i < end; i++) - { - if(src[i] == ch) - { - return i; - } - } - - return -1; -} - -u32 PSPOskDialog::FieldMaxLength() -{ - if ((oskParams->fields[0].outtextlimit > oskParams->fields[0].outtextlength - 1) || oskParams->fields[0].outtextlimit == 0) - return oskParams->fields[0].outtextlength - 1; - return oskParams->fields[0].outtextlimit; -} - -void PSPOskDialog::RenderKeyboard() -{ - // Sanity check that a valid keyboard is selected. - if ((int)currentKeyboard < 0 || (int)currentKeyboard >= OSK_KEYBOARD_COUNT) { - return; - } - - int selectedRow = selectedChar / numKeyCols[currentKeyboard]; - int selectedCol = selectedChar % numKeyCols[currentKeyboard]; - - char16_t temp[2]; - temp[1] = '\0'; - - std::string buffer; - - static const u32 FIELDDRAWMAX = 16; - u32 limit = FieldMaxLength(); - u32 drawLimit = std::min(FIELDDRAWMAX, limit); // Field drew length limit. - - const float keyboardLeftSide = (480.0f - (24.0f * numKeyCols[currentKeyboard])) / 2.0f; - const float characterWidth = 12.0f; - float previewLeftSide = (480.0f - (12.0f * drawLimit)) / 2.0f; - float title = (480.0f - (0.5f * drawLimit)) / 2.0f; - - PPGeStyle descStyle = FadedStyle(PPGeAlign::BOX_CENTER, 0.5f); - PPGeDrawText(oskDesc.c_str(), title, 20, descStyle); - - PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_HCENTER, 0.5f); - - PPGeStyle keyStyle = FadedStyle(PPGeAlign::BOX_HCENTER, 0.6f); - PPGeStyle selectedKeyStyle = FadedStyle(PPGeAlign::BOX_HCENTER, 0.6f); - selectedKeyStyle.color = CalcFadedColor(0xFF3060FF); - - std::u16string result; - - result = CombinationString(false); - - u32 drawIndex = (u32)(result.size() > drawLimit ? result.size() - drawLimit : 0); - drawIndex = result.size() == limit + 1 ? drawIndex - 1 : drawIndex; // When the length reached limit, the last character don't fade in and out. - for (u32 i = 0; i < drawLimit; ++i, ++drawIndex) - { - if (drawIndex + 1 < result.size()) - { - temp[0] = result[drawIndex]; - ConvertUCS2ToUTF8(buffer, temp); - PPGeDrawText(buffer.c_str(), previewLeftSide + (i * characterWidth), 40.0f, textStyle); - } - else - { - if (drawIndex + 1 == result.size()) - { - temp[0] = result[drawIndex]; - - if(isCombinated == true) - { - float animStep = (float)(__DisplayGetNumVblanks() % 40) / 20.0f; - // Fade in and out the next character so they know it's not part of the string yet. - u32 alpha = (0.5f - (cosf(animStep * M_PI) / 2.0f)) * 128 + 127; - PPGeStyle animStyle = textStyle; - animStyle.color = CalcFadedColor((alpha << 24) | 0x00FFFFFF); - - ConvertUCS2ToUTF8(buffer, temp); - - PPGeDrawText(buffer.c_str(), previewLeftSide + (i * characterWidth), 40.0f, animStyle); - - // Also draw the underline for the same reason. - PPGeDrawText("_", previewLeftSide + (i * characterWidth), 40.0f, textStyle); - } - else - { - ConvertUCS2ToUTF8(buffer, temp); - PPGeDrawText(buffer.c_str(), previewLeftSide + (i * characterWidth), 40.0f, textStyle); - } - } - else - { - PPGeDrawText("_", previewLeftSide + (i * characterWidth), 40.0f, textStyle); - } - } - } - - for (int row = 0; row < numKeyRows[currentKeyboard]; ++row) - { - for (int col = 0; col < numKeyCols[currentKeyboard]; ++col) - { - temp[0] = oskKeys[currentKeyboard][row][col]; - - ConvertUCS2ToUTF8(buffer, temp); - - if (selectedRow == row && col == selectedCol) { - PPGeDrawText(buffer.c_str(), keyboardLeftSide + (25.0f * col) + characterWidth / 2.0, 70.0f + (25.0f * row), selectedKeyStyle); - PPGeDrawText("_", keyboardLeftSide + (25.0f * col) + characterWidth / 2.0, 70.0f + (25.0f * row), keyStyle); - } else { - PPGeDrawText(buffer.c_str(), keyboardLeftSide + (25.0f * col) + characterWidth / 2.0, 70.0f + (25.0f * row), keyStyle); - } - } - } -} - -// TODO: Why does this have a 2 button press lag/delay when -// re-opening the dialog box? I don't get it. -int PSPOskDialog::NativeKeyboard() { - if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) { - return SCE_ERROR_UTILITY_INVALID_STATUS; - } - -#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) - bool beginInputBox = false; - if (nativeStatus_ == PSPOskNativeStatus::IDLE) { - std::lock_guard guard(nativeMutex_); - if (nativeStatus_ == PSPOskNativeStatus::IDLE) { - nativeStatus_ = PSPOskNativeStatus::WAITING; - beginInputBox = true; - } - } - - if (beginInputBox) { - std::u16string titleText; - GetWideStringFromPSPPointer(titleText, oskParams->fields[0].desc); - - std::u16string defaultText; - GetWideStringFromPSPPointer(defaultText, oskParams->fields[0].intext); - - if (defaultText.empty()) - defaultText.assign(u"VALUE"); - - // There's already ConvertUCS2ToUTF8 in this file. Should we use that instead of the global ones? - System_InputBoxGetString(NON_EPHEMERAL_TOKEN, ::ConvertUCS2ToUTF8(titleText), ::ConvertUCS2ToUTF8(defaultText), false, - [&](const std::string &value, int) { - // Success callback - std::lock_guard guard(nativeMutex_); - if (nativeStatus_ != PSPOskNativeStatus::WAITING) { - return; - } - nativeValue_ = value; - nativeStatus_ = PSPOskNativeStatus::SUCCESS; - }, - [&]() { - // Failure callback - std::lock_guard guard(nativeMutex_); - if (nativeStatus_ != PSPOskNativeStatus::WAITING) { - return; - } - nativeValue_ = ""; - nativeStatus_ = PSPOskNativeStatus::FAILURE; - } - ); - } else if (nativeStatus_ == PSPOskNativeStatus::SUCCESS) { - inputChars = ConvertUTF8ToUCS2(nativeValue_); - nativeValue_.clear(); - - u32 maxLength = FieldMaxLength(); - if (inputChars.length() > maxLength) { - ERROR_LOG(Log::sceUtility, "NativeKeyboard: input text too long(%d characters/glyphs max), truncating to game-requested length.", maxLength); - inputChars.erase(maxLength, std::string::npos); - } - ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0); - nativeStatus_ = PSPOskNativeStatus::DONE; - } else if (nativeStatus_ == PSPOskNativeStatus::FAILURE) { - ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0); - nativeStatus_ = PSPOskNativeStatus::DONE; - } -#endif - - u16_le *outText = oskParams->fields[0].outtext; - - size_t end = oskParams->fields[0].outtextlength; - if (end > inputChars.size()) - end = inputChars.size() + 1; - // Only write the bytes of the output and the null terminator, don't write the rest. - for (size_t i = 0; i < end; ++i) { - u16 value = 0; - if (i < FieldMaxLength() && i < inputChars.size()) - value = inputChars[i]; - outText[i] = value; - } - - oskParams->base.result = 0; - oskParams->fields[0].result = PSP_UTILITY_OSK_RESULT_CHANGED; - - return 0; -} - -int PSPOskDialog::Update(int animSpeed) { - if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) { - return SCE_ERROR_UTILITY_INVALID_STATUS; - } - - int cancelButton = GetCancelButton(); - int confirmButton = GetConfirmButton(); - - static int cancelBtnFramesHeld = 0; - static int confirmBtnFramesHeld = 0; - static int leftBtnFramesHeld = 0; - static int upBtnFramesHeld = 0; - static int downBtnFramesHeld = 0; - static int rightBtnFramesHeld = 0; - const int framesHeldThreshold = 10; - const int framesHeldRepeatRate = 5; - - UpdateButtons(); - UpdateCommon(); - int selectedRow = selectedChar / numKeyCols[currentKeyboard]; - int selectedExtra = selectedChar % numKeyCols[currentKeyboard]; - -#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH) - // Windows: Fall back to the OSK/continue normally if we're in fullscreen. - // The dialog box doesn't work right if in fullscreen. - if (System_GetPropertyBool(SYSPROP_HAS_KEYBOARD)) { - if (g_Config.bBypassOSKWithKeyboard && !g_Config.UseFullScreen()) - return NativeKeyboard(); - } -#endif - - UpdateFade(animSpeed); - - StartDraw(); - PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x63636363)); - RenderKeyboard(); - - auto di = GetI18NCategory(I18NCat::DIALOG); - - PPGeStyle actionStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.5f); - PPGeStyle guideStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.6f); - - PPGeDrawImage(ImageID("I_SQUARE"), 365, 222, 16, 16, guideStyle); - PPGeDrawText(di->T("Space"), 390, 222, actionStyle); - - if (GetConfirmButton() != CTRL_CIRCLE) { - PPGeDrawImage(ImageID("I_CROSS"), 45, 222, 16, 16, guideStyle); - PPGeDrawImage(ImageID("I_CIRCLE"), 45, 247, 16, 16, guideStyle); - } else { - PPGeDrawImage(ImageID("I_CIRCLE"), 45, 222, 16, 16, guideStyle); - PPGeDrawImage(ImageID("I_CROSS"), 45, 247, 16, 16, guideStyle); - } - - PPGeDrawText(di->T("Select"), 75, 222, actionStyle); - PPGeDrawText(di->T("Delete"), 75, 247, actionStyle); - - PPGeDrawText("Start", 135, 220, guideStyle); - PPGeDrawText(di->T("Finish"), 185, 222, actionStyle); - - auto lookupLangName = [&](int direction) { - // First, find the valid one... - OskKeyboardLanguage lang = (OskKeyboardLanguage)((OSK_LANGUAGE_COUNT + currentKeyboardLanguage + direction) % OSK_LANGUAGE_COUNT); - OskKeyboardDisplay disp = OskKeyboardCases[lang][LOWERCASE]; - FindValidKeyboard(oskParams->fields[0].inputtype, direction, lang, disp); - - if (lang == currentKeyboardLanguage) { - return (const char *)nullptr; - } - - // Now, let's grab the name. - const char *countryCode = OskKeyboardNames[lang].c_str(); - const char *language = languageMapping[countryCode].first.c_str(); - - // It seems like this is a "fake" country code for extra keyboard purposes. - if (!strcmp(countryCode, "English Full-width")) - language = "English Full-width"; - - return language; - }; - - if (OskKeyboardNames[currentKeyboardLanguage] != "ko_KR" && IsKeyboardShiftValid(oskParams->fields[0].inputtype, currentKeyboardLanguage, currentKeyboard)) { - PPGeDrawText("Select", 135, 245, guideStyle); - PPGeDrawText(di->T("Shift"), 185, 247, actionStyle); - } - - const char *prevLang = lookupLangName(-1); - if (prevLang) { - PPGeDrawText("L", 235, 220, guideStyle); - PPGeDrawText(prevLang, 255, 222, actionStyle); - } - - const char *nextLang = lookupLangName(1); - if (nextLang) { - PPGeDrawText("R", 235, 245, guideStyle); - PPGeDrawText(nextLang, 255, 247, actionStyle); - } - - if (IsButtonPressed(CTRL_UP) || IsButtonHeld(CTRL_UP, upBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) { - selectedChar -= numKeyCols[currentKeyboard]; - } else if (IsButtonPressed(CTRL_DOWN) || IsButtonHeld(CTRL_DOWN, downBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) { - selectedChar += numKeyCols[currentKeyboard]; - } else if (IsButtonPressed(CTRL_LEFT) || IsButtonHeld(CTRL_LEFT, leftBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) { - selectedChar--; - if (((selectedChar + numKeyCols[currentKeyboard]) % numKeyCols[currentKeyboard]) == numKeyCols[currentKeyboard] - 1) - selectedChar += numKeyCols[currentKeyboard]; - } else if (IsButtonPressed(CTRL_RIGHT) || IsButtonHeld(CTRL_RIGHT, rightBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) { - selectedChar++; - if ((selectedChar % numKeyCols[currentKeyboard]) == 0) - selectedChar -= numKeyCols[currentKeyboard]; - } - - selectedChar = (selectedChar + (numKeyCols[currentKeyboard] * numKeyRows[currentKeyboard])) % (numKeyCols[currentKeyboard] * numKeyRows[currentKeyboard]); - - if (IsButtonPressed(confirmButton) || IsButtonHeld(confirmButton, confirmBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) { - inputChars = CombinationString(true); - } else if (IsButtonPressed(CTRL_SELECT)) { - // Select now swaps case. - if (IsKeyboardShiftValid(oskParams->fields[0].inputtype, currentKeyboardLanguage, currentKeyboard)) { - if (currentKeyboard == OskKeyboardCases[currentKeyboardLanguage][UPPERCASE]) - currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE]; - else - currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][UPPERCASE]; - } - - if (selectedRow >= numKeyRows[currentKeyboard]) { - selectedRow = numKeyRows[currentKeyboard] - 1; - } - - if (selectedExtra >= numKeyCols[currentKeyboard]) { - selectedExtra = numKeyCols[currentKeyboard] - 1; - } - - selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra; - } else if (IsButtonPressed(CTRL_RTRIGGER)) { - // RTRIGGER now cycles languages forward. - currentKeyboardLanguage = (OskKeyboardLanguage)((currentKeyboardLanguage + 1) % OSK_LANGUAGE_COUNT); - currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE]; - FindValidKeyboard(oskParams->fields[0].inputtype, 1, currentKeyboardLanguage, currentKeyboard); - - if (selectedRow >= numKeyRows[currentKeyboard]) { - selectedRow = numKeyRows[currentKeyboard] - 1; - } - - if (selectedExtra >= numKeyCols[currentKeyboard]) { - selectedExtra = numKeyCols[currentKeyboard] - 1; - } - - selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra; - } else if (IsButtonPressed(CTRL_LTRIGGER)) { - // LTRIGGER now cycles languages backward. - if (currentKeyboardLanguage - 1 >= 0) - currentKeyboardLanguage = (OskKeyboardLanguage)((currentKeyboardLanguage - 1) % OSK_LANGUAGE_COUNT); - else - currentKeyboardLanguage = (OskKeyboardLanguage)(OSK_LANGUAGE_COUNT - 1); - currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE]; - FindValidKeyboard(oskParams->fields[0].inputtype, -1, currentKeyboardLanguage, currentKeyboard); - - if (selectedRow >= numKeyRows[currentKeyboard]) { - selectedRow = numKeyRows[currentKeyboard] - 1; - } - - if (selectedExtra >= numKeyCols[currentKeyboard]) { - selectedExtra = numKeyCols[currentKeyboard] - 1; - } - - selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra; - } else if (IsButtonPressed(cancelButton) || IsButtonHeld(cancelButton, cancelBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) { - if (inputChars.size() > 0) { - inputChars.resize(inputChars.size() - 1); - if (i_level != 0) { - RemoveKorean(); - } - } - } else if (IsButtonPressed(CTRL_START)) { - StartFade(false); - } else if (IsButtonPressed(CTRL_SQUARE) && inputChars.size() < FieldMaxLength()) { - // Use a regular space if the current keyboard isn't Japanese nor full-width English - if (currentKeyboardLanguage != OSK_LANGUAGE_JAPANESE && currentKeyboardLanguage != OSK_LANGUAGE_ENGLISH_FW) - inputChars += u" "; - else - inputChars += u" "; - } - - EndDraw(); - - u16_le *outText = oskParams->fields[0].outtext; - size_t end = oskParams->fields[0].outtextlength; - // Only write the bytes of the output and the null terminator, don't write the rest. - if (end > inputChars.size()) - end = inputChars.size() + 1; - for (size_t i = 0; i < end; ++i) - { - u16 value = 0; - if (i < FieldMaxLength() && i < inputChars.size()) - value = inputChars[i]; - outText[i] = value; - } - - oskParams->base.result = 0; - oskParams->fields[0].result = PSP_UTILITY_OSK_RESULT_CHANGED; - return 0; -} - -int PSPOskDialog::Shutdown(bool force) -{ - if (GetStatus() != SCE_UTILITY_STATUS_FINISHED && !force) - return SCE_ERROR_UTILITY_INVALID_STATUS; - - PSPDialog::Shutdown(force); - if (!force) { - ChangeStatusShutdown(OSK_SHUTDOWN_DELAY_US); - } - nativeStatus_ = PSPOskNativeStatus::IDLE; - - return 0; -} - -void PSPOskDialog::DoState(PointerWrap &p) -{ - PSPDialog::DoState(p); - - auto s = p.Section("PSPOskDialog", 1, 2); - if (!s) - return; - - // TODO: Should we save currentKeyboard/currentKeyboardLanguage? - - Do(p, oskParams); - Do(p, oskDesc); - Do(p, oskIntext); - Do(p, oskOuttext); - Do(p, selectedChar); - if (s >= 2) { - Do(p, inputChars); - } else { - // Discard the wstring. - std::wstring wstr; - Do(p, wstr); - } - // Don't need to save state native status or value. -} - -pspUtilityDialogCommon *PSPOskDialog::GetCommonParam() -{ - return &oskParams->base; -} +// Copyright (c) 2012- PPSSPP Project. + +// 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, version 2.0 or later versions. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "ppsspp_config.h" + +#include +#include +#include +#include "Common/Data/Text/I18n.h" +#include "Common/Math/math_util.h" +#include "Common/Data/Encoding/Utf8.h" +#include "Common/Serialize/SerializeFuncs.h" +#include "Common/System/Request.h" +#include "Common/Serialize/Serializer.h" + +#include "Core/Dialog/PSPOskDialog.h" +#include "Core/Util/PPGeDraw.h" +#include "Core/HLE/sceCtrl.h" +#include "Core/HLE/sceUtility.h" +#include "Core/HW/Display.h" +#include "Core/Config.h" +#include "Core/Reporting.h" + +// These are rough, it seems to take a long time to init, and probably depends on threads. +// TODO: This takes like 700ms on a PSP but that's annoyingly long. +const static int OSK_INIT_DELAY_US = 300000; +const static int OSK_SHUTDOWN_DELAY_US = 40000; + +static std::map, std::less<>> languageMapping; + +const uint8_t numKeyCols[OSK_KEYBOARD_COUNT] = {12, 12, 13, 13, 12, 12, 12, 12, 12}; +const uint8_t numKeyRows[OSK_KEYBOARD_COUNT] = {4, 4, 6, 6, 5, 4, 4, 4, 4}; + +// Japanese (Kana) diacritics +extern const wchar_t diacritics[2][103]; + +// Korean (Hangul) consonant +extern const wchar_t kor_cons[20]; + +// Korean (Hangul) vowels, Some vowels are not used, they will be spaces +extern const wchar_t kor_vowel[22]; + +// Korean (Hangul) vowel Combination key +extern const uint8_t kor_vowelCom[21]; + +// Korean (Hangul) last consonant(diacritics) +extern const wchar_t kor_lcons[28]; + +// Korean (Hangul) last consonant Combination key +extern const uint8_t kor_lconsCom[33]; + +// Korean (Hangul) last consonant Separation key +extern const uint8_t kor_lconsSpr[33]; + +extern const char16_t oskKeys[OSK_KEYBOARD_COUNT][6][14]; + +// This isn't a complete representation of these flags, it just helps ensure we show the right keyboards. +static const int allowedInputFlagsMap[OSK_KEYBOARD_COUNT] = { + PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT, + PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HIRAGANA, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_KATAKANA | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HALF_KATAKANA, + PSP_UTILITY_OSK_INPUTTYPE_KOREAN, + PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE, + PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_DIGIT, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL, +}; +static const int defaultInputFlagsMap[OSK_KEYBOARD_COUNT] = { + PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT, + PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HIRAGANA, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_KATAKANA | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HALF_KATAKANA, + PSP_UTILITY_OSK_INPUTTYPE_KOREAN, + PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE, + PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_DIGIT, + PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL, +}; + +PSPOskDialog::PSPOskDialog(UtilityDialogType type) : PSPDialog(type) { + // This can break all kinds of stuff, changing the decimal point in sprintf for example. + // Not sure what the intended effect is so commented out for now. + // setlocale(LC_ALL, ""); +} + +PSPOskDialog::~PSPOskDialog() { +} + +void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, const PSPPointer& em_address) +{ + if (!em_address.IsValid()) + { + _string.clear(); + return; + } + + const size_t maxLength = 2047; + char stringBuffer[maxLength + 1]; + char *string = stringBuffer; + + u16_le *input = &em_address[0]; + int c; + while ((c = *input++) != 0 && string < stringBuffer + maxLength) + { + if (c < 0x80) + *string++ = c; + else if (c < 0x800) { + *string++ = 0xC0 | (c >> 6); + *string++ = 0x80 | (c & 0x3F); + } else { + *string++ = 0xE0 | (c >> 12); + *string++ = 0x80 | ((c >> 6) & 0x3F); + *string++ = 0x80 | (c & 0x3F); + } + } + *string++ = '\0'; + _string = stringBuffer; +} + +void GetWideStringFromPSPPointer(std::u16string& _string, const PSPPointer& em_address) +{ + if (!em_address.IsValid()) + { + _string.clear(); + return; + } + + const size_t maxLength = 2047; + char16_t stringBuffer[maxLength + 1]; + char16_t *string = stringBuffer; + + u16_le *input = &em_address[0]; + int c; + while ((c = *input++) != 0 && string < stringBuffer + maxLength) + *string++ = c; + *string++ = '\0'; + _string = stringBuffer; +} + +void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, const char16_t *input) +{ + char stringBuffer[2048]; + char *string = stringBuffer; + + int c; + while ((c = *input++) != 0) + { + if (c < 0x80) + *string++ = c; + else if (c < 0x800) { + *string++ = 0xC0 | (c >> 6); + *string++ = 0x80 | (c & 0x3F); + } else { + *string++ = 0xE0 | (c >> 12); + *string++ = 0x80 | ((c >> 6) & 0x3F); + *string++ = 0x80 | (c & 0x3F); + } + } + *string++ = '\0'; + _string = stringBuffer; +} + +static void FindValidKeyboard(s32 inputType, int direction, OskKeyboardLanguage &lang, OskKeyboardDisplay &disp) { + OskKeyboardLanguage origLang = lang; + OskKeyboardDisplay origDisp = disp; + + if (inputType == 0) { + return; + } + // We use direction = 0 for default, but we actually move "forward". + const int *matchMap = allowedInputFlagsMap; + if (direction == 0) { + direction = 1; + matchMap = defaultInputFlagsMap; + } + + // TODO: Limit by allowed keyboards properly... this is just an approximation. + int tries = OSK_LANGUAGE_COUNT * 2; + while (!(inputType & matchMap[disp]) && tries > 0) { + if ((--tries % 2) == 0) { + lang = (OskKeyboardLanguage)((OSK_LANGUAGE_COUNT + lang + direction) % OSK_LANGUAGE_COUNT); + disp = OskKeyboardCases[lang][LOWERCASE]; + } else { + disp = OskKeyboardCases[lang][UPPERCASE]; + } + } + + if (tries == 0) { + // In case of error, let's just fall back to allowing all. + lang = origLang; + disp = origDisp; + } +} + +static bool IsKeyboardShiftValid(s32 inputType, OskKeyboardLanguage lang, OskKeyboardDisplay disp) { + // Swap disp and check if it's valid. + if (disp == OskKeyboardCases[lang][UPPERCASE]) + disp = OskKeyboardCases[lang][LOWERCASE]; + else + disp = OskKeyboardCases[lang][UPPERCASE]; + + return inputType == 0 || (inputType & allowedInputFlagsMap[disp]) != 0; +} + +int PSPOskDialog::Init(u32 oskPtr) { + // Ignore if already running + if (GetStatus() != SCE_UTILITY_STATUS_NONE) { + ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid status"); + return SCE_ERROR_UTILITY_INVALID_STATUS; + } + // Seems like this should crash? + if (!Memory::IsValidAddress(oskPtr)) { + ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid params (%08x)", oskPtr); + return -1; + } + + oskParams = oskPtr; + if (oskParams->base.size != sizeof(SceUtilityOskParams)) + { + ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid size %d", oskParams->base.size); + return SCE_ERROR_UTILITY_INVALID_PARAM_SIZE; + } + // Also seems to crash. + if (!oskParams->fields.IsValid()) + { + ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid field data (%08x)", oskParams->fields.ptr); + return -1; + } + + if (oskParams->unk_60 != 0) + WARN_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: unknown param is non-zero (%08x)", oskParams->unk_60); + if (oskParams->fieldCount != 1) + WARN_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: unsupported field count %d", oskParams->fieldCount); + + ChangeStatusInit(OSK_INIT_DELAY_US); + selectedChar = 0; + currentKeyboardLanguage = OSK_LANGUAGE_ENGLISH; + currentKeyboard = OSK_KEYBOARD_LATIN_LOWERCASE; + FindValidKeyboard(oskParams->fields[0].inputtype, 0, currentKeyboardLanguage, currentKeyboard); + + ConvertUCS2ToUTF8(oskDesc, oskParams->fields[0].desc); + ConvertUCS2ToUTF8(oskIntext, oskParams->fields[0].intext); + ConvertUCS2ToUTF8(oskOuttext, oskParams->fields[0].outtext); + + i_level = 0; + + inputChars.clear(); + + if (oskParams->fields[0].intext.IsValid()) { + auto src = oskParams->fields[0].intext; + int c; + while ((c = *src++) != 0) + inputChars += c; + } + + languageMapping = g_Config.GetLangValuesMapping(); + + // Eat any keys pressed before the dialog inited. + UpdateButtons(); + InitCommon(); + + std::lock_guard guard(nativeMutex_); + nativeStatus_ = PSPOskNativeStatus::IDLE; + + StartFade(true); + return 0; +} + +std::u16string PSPOskDialog::CombinationKorean(bool isInput) +{ + std::u16string string; + + isCombinated = true; + + int selectedRow = selectedChar / numKeyCols[currentKeyboard]; + int selectedCol = selectedChar % numKeyCols[currentKeyboard]; + + if (inputChars.size() == 0) { + wchar_t sw = oskKeys[currentKeyboard][selectedRow][selectedCol]; + + if (inputChars.size() < FieldMaxLength()) { + string += sw; + + i_value[0] = GetIndex(kor_cons, sw); + + if(i_value[0] != -1 && isInput == true) + i_level = 1; + } else { + isCombinated = false; + } + } else { + for(u32 i = 0; i < inputChars.size(); i++) { + if(i + 1 == inputChars.size()) { + wchar_t sw = oskKeys[currentKeyboard][selectedRow][selectedCol]; + + if(i_level == 0) { + string += inputChars[i]; + if (inputChars.size() < FieldMaxLength()) { + string += sw; + + i_value[0] = GetIndex(kor_cons, sw); + + if(i_value[0] != -1 && isInput == true) + i_level = 1; + } else { + isCombinated = false; + } + } else if(i_level == 1) { + i_value[1] = GetIndex(kor_vowel, sw); + + if(i_value[1] == -1) { + string += inputChars[i]; + if (inputChars.size() < FieldMaxLength()) { + string += sw; + + if(isInput == true) { + i_value[0] = GetIndex(kor_cons, sw); + + if(i_value[0] != -1) + i_level = 1; + else + i_level = 0; + } + } else { + isCombinated = false; + } + } else { + u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C; + string += code; + + if(isInput == true) { + i_level = 2; + } + } + } else if(i_level == 2) { + int tmp = GetIndex(kor_vowel, sw); + if(tmp != -1) { + int tmp2 = -1; + for(size_t j = 0; j < sizeof(kor_vowelCom) / 4; j+=3) { + if(kor_vowelCom[j] == tmp && kor_vowelCom[j + 1] == i_value[1]) { + tmp2 = kor_vowelCom[j + 2]; + break; + } + } + if(tmp2 != -1) { + if(isInput == true) { + i_value[1] = tmp2; + } + + u16 code = 0xAC00 + i_value[0] * 0x24C + tmp2 * 0x1C; + + string += code; + } else { + string += inputChars[i]; + if (inputChars.size() < FieldMaxLength()) { + string += sw; + + if(isInput == true) { + i_level = 0; + } + } else { + isCombinated = false; + } + } + } else { + int tmp2 = GetIndex(kor_lcons, sw); + + if (tmp2 == -1) { + string += inputChars[i]; + if (inputChars.size() < FieldMaxLength()) { + string += sw; + + if (isInput == true) { + i_value[0] = GetIndex(kor_cons, sw); + + if(i_value[0] != -1) + i_level = 1; + else + i_level = 0; + } + } else { + isCombinated = false; + } + } else { + u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + tmp2 + 1; + + string += code; + + if (isInput == true) { + i_level = 3; + i_value[2] = tmp2; + } + } + } + } else if(i_level == 3) { + int tmp = GetIndex(kor_lcons, sw); + if(tmp != -1) { + int tmp2 = -1; + for(size_t j = 0; j < sizeof(kor_lconsCom) / 4; j+=3) { + if(kor_lconsCom[j] == tmp && kor_lconsCom[j + 1] == i_value[2]) { + tmp2 = kor_lconsCom[j + 2]; + break; + } + } + if(tmp2 != -1) { + if(isInput == true) { + i_value[2] = tmp2; + } + + u16 code = 0xAC00 + i_value[0] * 0x24C + tmp2 * 0x1C + i_value[2] + 1; + + string += code; + } else { + string += inputChars[i]; + if (inputChars.size() < FieldMaxLength()) { + string += sw; + + if(isInput == true) { + i_value[0] = GetIndex(kor_cons, sw); + + if(i_value[0] != -1) + i_level = 1; + else + i_level = 0; + } + } else { + isCombinated = false; + } + } + } else { + int tmp3 = GetIndex(kor_vowel, sw); + if (tmp3 == -1) { + string += inputChars[i]; + if (inputChars.size() < FieldMaxLength()) { + string += sw; + + if(isInput == true) { + i_value[0] = GetIndex(kor_cons, sw); + + if(i_value[0] != -1) + i_level = 1; + else + i_level = 0; + } + } else { + isCombinated = false; + } + } else { + if (inputChars.size() < FieldMaxLength()) { + int tmp2 = -1; + for(size_t j = 0; j < sizeof(kor_lconsSpr) / 4; j+=3) { + if(kor_lconsSpr[j] == i_value[2]) { + tmp2 = (int)j; + break; + } + } + if(tmp2 != -1) { + u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + kor_lconsSpr[tmp2 + 1]; + string += code; + + code = 0xAC00 + kor_lconsSpr[tmp2 + 2] * 0x24C + tmp3 * 0x1C; + string += code; + + if(isInput == true) { + i_value[0] = kor_lconsSpr[tmp2 + 2]; + i_value[1] = tmp3; + i_level = 2; + } + } else { + int tmp4 = GetIndex(kor_cons, kor_lcons[i_value[2]]); + + if (tmp4 != -1) { + u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C; + + string += code; + + code = 0xAC00 + tmp4 * 0x24C + tmp3 * 0x1C; + + string += code; + + if(isInput == true) { + i_value[0] = tmp4; + i_value[1] = tmp3; + i_level = 2; + } + } else { + string += inputChars[i]; + string += sw; + + if(isInput == true) { + i_level = 0; + } + } + } + } else { + string += inputChars[i]; + isCombinated = false; + } + } + } + } + } else { + string += inputChars[i]; + } + } + } + + return string; +} + +std::u16string PSPOskDialog::CombinationString(bool isInput) +{ + std::u16string string; + + isCombinated = false; + + int selectedRow = selectedChar / numKeyCols[currentKeyboard]; + int selectedCol = selectedChar % numKeyCols[currentKeyboard]; + + if(currentKeyboard == OSK_KEYBOARD_KOREAN) + { + string = CombinationKorean(isInput); + } + else + { + if(isInput == true) + { + i_level = 0; + } + + if(oskKeys[currentKeyboard][selectedRow][selectedCol] == L'゛') + { + for(u32 i = 0; i < inputChars.size(); i++) + { + if(i + 1 == inputChars.size()) + { + for(u32 j = 0; j < wcslen(diacritics[0]); j+=2) + { + if(inputChars[i] == diacritics[0][j]) + { + string += diacritics[0][j + 1]; + isCombinated = true; + break; + } + } + + if(isCombinated == false) + { + string += inputChars[i]; + } + } + else + { + string += inputChars[i]; + } + } + } + else if(oskKeys[currentKeyboard][selectedRow][selectedCol] == L'゜') + { + for(u32 i = 0; i < inputChars.size(); i++) + { + if(i + 1 == inputChars.size()) + { + for(u32 j = 0; j < wcslen(diacritics[1]); j+=2) + { + if(inputChars[i] == diacritics[1][j]) + { + string += diacritics[1][j + 1]; + isCombinated = true; + break; + } + } + + if(isCombinated == false) + { + string += inputChars[i]; + } + } + else + { + string += inputChars[i]; + } + } + } + else + { + for(u32 i = 0; i < inputChars.size(); i++) + { + string += inputChars[i]; + } + + if (string.size() < FieldMaxLength()) + { + string += oskKeys[currentKeyboard][selectedRow][selectedCol]; + } + isCombinated = true; + } + } + + return string; +} + +void PSPOskDialog::RemoveKorean() +{ + if(i_level == 1) + { + i_level = 0; + } + else if(i_level == 2) + { + int tmp = -1; + for(size_t i = 2; i < sizeof(kor_vowelCom) / 4; i+=3) + { + if(kor_vowelCom[i] == i_value[1]) + { + tmp = kor_vowelCom[i - 1]; + break; + } + } + + if(tmp != -1) + { + i_value[1] = tmp; + u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C; + inputChars += code; + } + else + { + i_level = 1; + inputChars += kor_cons[i_value[0]]; + } + } + else if(i_level == 3) + { + int tmp = -1; + for(size_t i = 2; i < sizeof(kor_lconsCom) / 4; i+=3) + { + if(kor_lconsCom[i] == i_value[2]) + { + tmp = kor_lconsCom[i - 1]; + break; + } + } + + if(tmp != -1) + { + i_value[2] = tmp; + u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + i_value[2] + 1; + inputChars += code; + } + else + { + i_level = 2; + u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C; + inputChars += code; + } + } +} + +int PSPOskDialog::GetIndex(const wchar_t* src, wchar_t ch) +{ + for(int i = 0, end = (int)wcslen(src); i < end; i++) + { + if(src[i] == ch) + { + return i; + } + } + + return -1; +} + +u32 PSPOskDialog::FieldMaxLength() +{ + if ((oskParams->fields[0].outtextlimit > oskParams->fields[0].outtextlength - 1) || oskParams->fields[0].outtextlimit == 0) + return oskParams->fields[0].outtextlength - 1; + return oskParams->fields[0].outtextlimit; +} + +void PSPOskDialog::RenderKeyboard() +{ + // Sanity check that a valid keyboard is selected. + if ((int)currentKeyboard < 0 || (int)currentKeyboard >= OSK_KEYBOARD_COUNT) { + return; + } + + int selectedRow = selectedChar / numKeyCols[currentKeyboard]; + int selectedCol = selectedChar % numKeyCols[currentKeyboard]; + + char16_t temp[2]; + temp[1] = '\0'; + + std::string buffer; + + static const u32 FIELDDRAWMAX = 16; + u32 limit = FieldMaxLength(); + u32 drawLimit = std::min(FIELDDRAWMAX, limit); // Field drew length limit. + + const float keyboardLeftSide = (480.0f - (24.0f * numKeyCols[currentKeyboard])) / 2.0f; + const float characterWidth = 12.0f; + float previewLeftSide = (480.0f - (12.0f * drawLimit)) / 2.0f; + float title = (480.0f - (0.5f * drawLimit)) / 2.0f; + + PPGeStyle descStyle = FadedStyle(PPGeAlign::BOX_CENTER, 0.5f); + PPGeDrawText(oskDesc.c_str(), title, 20, descStyle); + + PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_HCENTER, 0.5f); + + PPGeStyle keyStyle = FadedStyle(PPGeAlign::BOX_HCENTER, 0.6f); + PPGeStyle selectedKeyStyle = FadedStyle(PPGeAlign::BOX_HCENTER, 0.6f); + selectedKeyStyle.color = CalcFadedColor(0xFF3060FF); + + std::u16string result; + + result = CombinationString(false); + + u32 drawIndex = (u32)(result.size() > drawLimit ? result.size() - drawLimit : 0); + drawIndex = result.size() == limit + 1 ? drawIndex - 1 : drawIndex; // When the length reached limit, the last character don't fade in and out. + for (u32 i = 0; i < drawLimit; ++i, ++drawIndex) + { + if (drawIndex + 1 < result.size()) + { + temp[0] = result[drawIndex]; + ConvertUCS2ToUTF8(buffer, temp); + PPGeDrawText(buffer.c_str(), previewLeftSide + (i * characterWidth), 40.0f, textStyle); + } + else + { + if (drawIndex + 1 == result.size()) + { + temp[0] = result[drawIndex]; + + if(isCombinated == true) + { + float animStep = (float)(__DisplayGetNumVblanks() % 40) / 20.0f; + // Fade in and out the next character so they know it's not part of the string yet. + u32 alpha = (0.5f - (cosf(animStep * M_PI) / 2.0f)) * 128 + 127; + PPGeStyle animStyle = textStyle; + animStyle.color = CalcFadedColor((alpha << 24) | 0x00FFFFFF); + + ConvertUCS2ToUTF8(buffer, temp); + + PPGeDrawText(buffer.c_str(), previewLeftSide + (i * characterWidth), 40.0f, animStyle); + + // Also draw the underline for the same reason. + PPGeDrawText("_", previewLeftSide + (i * characterWidth), 40.0f, textStyle); + } + else + { + ConvertUCS2ToUTF8(buffer, temp); + PPGeDrawText(buffer.c_str(), previewLeftSide + (i * characterWidth), 40.0f, textStyle); + } + } + else + { + PPGeDrawText("_", previewLeftSide + (i * characterWidth), 40.0f, textStyle); + } + } + } + + for (int row = 0; row < numKeyRows[currentKeyboard]; ++row) + { + for (int col = 0; col < numKeyCols[currentKeyboard]; ++col) + { + temp[0] = oskKeys[currentKeyboard][row][col]; + + ConvertUCS2ToUTF8(buffer, temp); + + if (selectedRow == row && col == selectedCol) { + PPGeDrawText(buffer.c_str(), keyboardLeftSide + (25.0f * col) + characterWidth / 2.0, 70.0f + (25.0f * row), selectedKeyStyle); + PPGeDrawText("_", keyboardLeftSide + (25.0f * col) + characterWidth / 2.0, 70.0f + (25.0f * row), keyStyle); + } else { + PPGeDrawText(buffer.c_str(), keyboardLeftSide + (25.0f * col) + characterWidth / 2.0, 70.0f + (25.0f * row), keyStyle); + } + } + } +} + +// TODO: Why does this have a 2 button press lag/delay when +// re-opening the dialog box? I don't get it. +int PSPOskDialog::NativeKeyboard() { + if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) { + return SCE_ERROR_UTILITY_INVALID_STATUS; + } + +#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) + bool beginInputBox = false; + if (nativeStatus_ == PSPOskNativeStatus::IDLE) { + std::lock_guard guard(nativeMutex_); + if (nativeStatus_ == PSPOskNativeStatus::IDLE) { + nativeStatus_ = PSPOskNativeStatus::WAITING; + beginInputBox = true; + } + } + + if (beginInputBox) { + std::u16string titleText; + GetWideStringFromPSPPointer(titleText, oskParams->fields[0].desc); + + std::u16string defaultText; + GetWideStringFromPSPPointer(defaultText, oskParams->fields[0].intext); + + if (defaultText.empty()) + defaultText.assign(u"VALUE"); + + // There's already ConvertUCS2ToUTF8 in this file. Should we use that instead of the global ones? + System_InputBoxGetString(NON_EPHEMERAL_TOKEN, ::ConvertUCS2ToUTF8(titleText), ::ConvertUCS2ToUTF8(defaultText), false, + [&](const std::string &value, int) { + // Success callback + std::lock_guard guard(nativeMutex_); + if (nativeStatus_ != PSPOskNativeStatus::WAITING) { + return; + } + nativeValue_ = value; + nativeStatus_ = PSPOskNativeStatus::SUCCESS; + }, + [&]() { + // Failure callback + std::lock_guard guard(nativeMutex_); + if (nativeStatus_ != PSPOskNativeStatus::WAITING) { + return; + } + nativeValue_ = ""; + nativeStatus_ = PSPOskNativeStatus::FAILURE; + } + ); + } else if (nativeStatus_ == PSPOskNativeStatus::SUCCESS) { + inputChars = ConvertUTF8ToUCS2(nativeValue_); + nativeValue_.clear(); + + u32 maxLength = FieldMaxLength(); + if (inputChars.length() > maxLength) { + ERROR_LOG(Log::sceUtility, "NativeKeyboard: input text too long(%d characters/glyphs max), truncating to game-requested length.", maxLength); + inputChars.erase(maxLength, std::string::npos); + } + ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0); + nativeStatus_ = PSPOskNativeStatus::DONE; + } else if (nativeStatus_ == PSPOskNativeStatus::FAILURE) { + ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0); + nativeStatus_ = PSPOskNativeStatus::DONE; + } +#endif + + u16_le *outText = oskParams->fields[0].outtext; + + size_t end = oskParams->fields[0].outtextlength; + if (end > inputChars.size()) + end = inputChars.size() + 1; + // Only write the bytes of the output and the null terminator, don't write the rest. + for (size_t i = 0; i < end; ++i) { + u16 value = 0; + if (i < FieldMaxLength() && i < inputChars.size()) + value = inputChars[i]; + outText[i] = value; + } + + oskParams->base.result = 0; + oskParams->fields[0].result = PSP_UTILITY_OSK_RESULT_CHANGED; + + return 0; +} + +int PSPOskDialog::Update(int animSpeed) { + if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) { + return SCE_ERROR_UTILITY_INVALID_STATUS; + } + + int cancelButton = GetCancelButton(); + int confirmButton = GetConfirmButton(); + + static int cancelBtnFramesHeld = 0; + static int confirmBtnFramesHeld = 0; + static int leftBtnFramesHeld = 0; + static int upBtnFramesHeld = 0; + static int downBtnFramesHeld = 0; + static int rightBtnFramesHeld = 0; + const int framesHeldThreshold = 10; + const int framesHeldRepeatRate = 5; + + UpdateButtons(); + UpdateCommon(); + int selectedRow = selectedChar / numKeyCols[currentKeyboard]; + int selectedExtra = selectedChar % numKeyCols[currentKeyboard]; + +#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH) + // Windows: Fall back to the OSK/continue normally if we're in fullscreen. + // The dialog box doesn't work right if in fullscreen. + if (System_GetPropertyBool(SYSPROP_HAS_KEYBOARD)) { + if (g_Config.bBypassOSKWithKeyboard && !g_Config.UseFullScreen()) + return NativeKeyboard(); + } +#endif + + UpdateFade(animSpeed); + + StartDraw(); + PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x63636363)); + RenderKeyboard(); + + auto di = GetI18NCategory(I18NCat::DIALOG); + + PPGeStyle actionStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.5f); + PPGeStyle guideStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.6f); + + PPGeDrawImage(ImageID("I_SQUARE"), 365, 222, 16, 16, guideStyle); + PPGeDrawText(di->T("Space"), 390, 222, actionStyle); + + if (GetConfirmButton() != CTRL_CIRCLE) { + PPGeDrawImage(ImageID("I_CROSS"), 45, 222, 16, 16, guideStyle); + PPGeDrawImage(ImageID("I_CIRCLE"), 45, 247, 16, 16, guideStyle); + } else { + PPGeDrawImage(ImageID("I_CIRCLE"), 45, 222, 16, 16, guideStyle); + PPGeDrawImage(ImageID("I_CROSS"), 45, 247, 16, 16, guideStyle); + } + + PPGeDrawText(di->T("Select"), 75, 222, actionStyle); + PPGeDrawText(di->T("Delete"), 75, 247, actionStyle); + + PPGeDrawText("Start", 135, 220, guideStyle); + PPGeDrawText(di->T("Finish"), 185, 222, actionStyle); + + auto lookupLangName = [&](int direction) { + // First, find the valid one... + OskKeyboardLanguage lang = (OskKeyboardLanguage)((OSK_LANGUAGE_COUNT + currentKeyboardLanguage + direction) % OSK_LANGUAGE_COUNT); + OskKeyboardDisplay disp = OskKeyboardCases[lang][LOWERCASE]; + FindValidKeyboard(oskParams->fields[0].inputtype, direction, lang, disp); + + if (lang == currentKeyboardLanguage) { + return (const char *)nullptr; + } + + // Now, let's grab the name. + const char *countryCode = OskKeyboardNames[lang].c_str(); + const char *language = languageMapping[countryCode].first.c_str(); + + // It seems like this is a "fake" country code for extra keyboard purposes. + if (!strcmp(countryCode, "English Full-width")) + language = "English Full-width"; + + return language; + }; + + if (OskKeyboardNames[currentKeyboardLanguage] != "ko_KR" && IsKeyboardShiftValid(oskParams->fields[0].inputtype, currentKeyboardLanguage, currentKeyboard)) { + PPGeDrawText("Select", 135, 245, guideStyle); + PPGeDrawText(di->T("Shift"), 185, 247, actionStyle); + } + + const char *prevLang = lookupLangName(-1); + if (prevLang) { + PPGeDrawText("L", 235, 220, guideStyle); + PPGeDrawText(prevLang, 255, 222, actionStyle); + } + + const char *nextLang = lookupLangName(1); + if (nextLang) { + PPGeDrawText("R", 235, 245, guideStyle); + PPGeDrawText(nextLang, 255, 247, actionStyle); + } + + if (IsButtonPressed(CTRL_UP) || IsButtonHeld(CTRL_UP, upBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) { + selectedChar -= numKeyCols[currentKeyboard]; + } else if (IsButtonPressed(CTRL_DOWN) || IsButtonHeld(CTRL_DOWN, downBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) { + selectedChar += numKeyCols[currentKeyboard]; + } else if (IsButtonPressed(CTRL_LEFT) || IsButtonHeld(CTRL_LEFT, leftBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) { + selectedChar--; + if (((selectedChar + numKeyCols[currentKeyboard]) % numKeyCols[currentKeyboard]) == numKeyCols[currentKeyboard] - 1) + selectedChar += numKeyCols[currentKeyboard]; + } else if (IsButtonPressed(CTRL_RIGHT) || IsButtonHeld(CTRL_RIGHT, rightBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) { + selectedChar++; + if ((selectedChar % numKeyCols[currentKeyboard]) == 0) + selectedChar -= numKeyCols[currentKeyboard]; + } + + selectedChar = (selectedChar + (numKeyCols[currentKeyboard] * numKeyRows[currentKeyboard])) % (numKeyCols[currentKeyboard] * numKeyRows[currentKeyboard]); + + if (IsButtonPressed(confirmButton) || IsButtonHeld(confirmButton, confirmBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) { + inputChars = CombinationString(true); + } else if (IsButtonPressed(CTRL_SELECT)) { + // Select now swaps case. + if (IsKeyboardShiftValid(oskParams->fields[0].inputtype, currentKeyboardLanguage, currentKeyboard)) { + if (currentKeyboard == OskKeyboardCases[currentKeyboardLanguage][UPPERCASE]) + currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE]; + else + currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][UPPERCASE]; + } + + if (selectedRow >= numKeyRows[currentKeyboard]) { + selectedRow = numKeyRows[currentKeyboard] - 1; + } + + if (selectedExtra >= numKeyCols[currentKeyboard]) { + selectedExtra = numKeyCols[currentKeyboard] - 1; + } + + selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra; + } else if (IsButtonPressed(CTRL_RTRIGGER)) { + // RTRIGGER now cycles languages forward. + currentKeyboardLanguage = (OskKeyboardLanguage)((currentKeyboardLanguage + 1) % OSK_LANGUAGE_COUNT); + currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE]; + FindValidKeyboard(oskParams->fields[0].inputtype, 1, currentKeyboardLanguage, currentKeyboard); + + if (selectedRow >= numKeyRows[currentKeyboard]) { + selectedRow = numKeyRows[currentKeyboard] - 1; + } + + if (selectedExtra >= numKeyCols[currentKeyboard]) { + selectedExtra = numKeyCols[currentKeyboard] - 1; + } + + selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra; + } else if (IsButtonPressed(CTRL_LTRIGGER)) { + // LTRIGGER now cycles languages backward. + if (currentKeyboardLanguage - 1 >= 0) + currentKeyboardLanguage = (OskKeyboardLanguage)((currentKeyboardLanguage - 1) % OSK_LANGUAGE_COUNT); + else + currentKeyboardLanguage = (OskKeyboardLanguage)(OSK_LANGUAGE_COUNT - 1); + currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE]; + FindValidKeyboard(oskParams->fields[0].inputtype, -1, currentKeyboardLanguage, currentKeyboard); + + if (selectedRow >= numKeyRows[currentKeyboard]) { + selectedRow = numKeyRows[currentKeyboard] - 1; + } + + if (selectedExtra >= numKeyCols[currentKeyboard]) { + selectedExtra = numKeyCols[currentKeyboard] - 1; + } + + selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra; + } else if (IsButtonPressed(cancelButton) || IsButtonHeld(cancelButton, cancelBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) { + if (inputChars.size() > 0) { + inputChars.resize(inputChars.size() - 1); + if (i_level != 0) { + RemoveKorean(); + } + } + } else if (IsButtonPressed(CTRL_START)) { + StartFade(false); + } else if (IsButtonPressed(CTRL_SQUARE) && inputChars.size() < FieldMaxLength()) { + // Use a regular space if the current keyboard isn't Japanese nor full-width English + if (currentKeyboardLanguage != OSK_LANGUAGE_JAPANESE && currentKeyboardLanguage != OSK_LANGUAGE_ENGLISH_FW) + inputChars += u" "; + else + inputChars += u" "; + } + + EndDraw(); + + u16_le *outText = oskParams->fields[0].outtext; + size_t end = oskParams->fields[0].outtextlength; + // Only write the bytes of the output and the null terminator, don't write the rest. + if (end > inputChars.size()) + end = inputChars.size() + 1; + for (size_t i = 0; i < end; ++i) + { + u16 value = 0; + if (i < FieldMaxLength() && i < inputChars.size()) + value = inputChars[i]; + outText[i] = value; + } + + oskParams->base.result = 0; + oskParams->fields[0].result = PSP_UTILITY_OSK_RESULT_CHANGED; + return 0; +} + +int PSPOskDialog::Shutdown(bool force) +{ + if (GetStatus() != SCE_UTILITY_STATUS_FINISHED && !force) + return SCE_ERROR_UTILITY_INVALID_STATUS; + + PSPDialog::Shutdown(force); + if (!force) { + ChangeStatusShutdown(OSK_SHUTDOWN_DELAY_US); + } + nativeStatus_ = PSPOskNativeStatus::IDLE; + + return 0; +} + +void PSPOskDialog::DoState(PointerWrap &p) +{ + PSPDialog::DoState(p); + + auto s = p.Section("PSPOskDialog", 1, 2); + if (!s) + return; + + // TODO: Should we save currentKeyboard/currentKeyboardLanguage? + + Do(p, oskParams); + Do(p, oskDesc); + Do(p, oskIntext); + Do(p, oskOuttext); + Do(p, selectedChar); + if (s >= 2) { + Do(p, inputChars); + } else { + // Discard the wstring. + std::wstring wstr; + Do(p, wstr); + } + // Don't need to save state native status or value. +} + +pspUtilityDialogCommon *PSPOskDialog::GetCommonParam() +{ + return &oskParams->base; +} diff --git a/Core/Dialog/PSPOskDialog.h b/Core/Dialog/PSPOskDialog.h index 0639c6226a..64a593a33b 100644 --- a/Core/Dialog/PSPOskDialog.h +++ b/Core/Dialog/PSPOskDialog.h @@ -23,7 +23,7 @@ #include "Core/Dialog/PSPDialog.h" #include "Core/MemMap.h" #include "Common/CommonTypes.h" - +#include "Core/Dialog/PSPOskConstants.h" /** * Enumeration for input language @@ -150,22 +150,6 @@ struct SceUtilityOskParams s32_le unk_60; }; -// Internal enum, not from PSP. -enum OskKeyboardDisplay -{ - OSK_KEYBOARD_LATIN_LOWERCASE, - OSK_KEYBOARD_LATIN_UPPERCASE, - OSK_KEYBOARD_HIRAGANA, - OSK_KEYBOARD_KATAKANA, - OSK_KEYBOARD_KOREAN, - OSK_KEYBOARD_RUSSIAN_LOWERCASE, - OSK_KEYBOARD_RUSSIAN_UPPERCASE, - OSK_KEYBOARD_LATIN_FW_LOWERCASE, - OSK_KEYBOARD_LATIN_FW_UPPERCASE, - // TODO: Something to do native? - OSK_KEYBOARD_COUNT -}; - // Internal enum, not from PSP. enum OskKeyboardLanguage {