CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/Dialog/PSPOskDialog.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include "ppsspp_config.h"18#include <algorithm>1920#include "Common/Data/Text/I18n.h"21#include "Common/Math/math_util.h"22#include "Common/Data/Encoding/Utf8.h"23#include "Common/Serialize/SerializeFuncs.h"24#include "Common/System/Request.h"25#include "Common/Serialize/Serializer.h"2627#include "Core/Dialog/PSPOskDialog.h"28#include "Core/Util/PPGeDraw.h"29#include "Core/HLE/sceCtrl.h"30#include "Core/HLE/sceUtility.h"31#include "Core/HW/Display.h"32#include "Core/Config.h"33#include "Core/Reporting.h"34#include "GPU/GPUState.h"3536#ifndef _WIN3237#include <ctype.h>38#include <math.h>39#endif4041// These are rough, it seems to take a long time to init, and probably depends on threads.42// TODO: This takes like 700ms on a PSP but that's annoyingly long.43const static int OSK_INIT_DELAY_US = 300000;44const static int OSK_SHUTDOWN_DELAY_US = 40000;4546static std::map<std::string, std::pair<std::string, int>, std::less<>> languageMapping;4748const uint8_t numKeyCols[OSK_KEYBOARD_COUNT] = {12, 12, 13, 13, 12, 12, 12, 12, 12};49const uint8_t numKeyRows[OSK_KEYBOARD_COUNT] = {4, 4, 6, 6, 5, 4, 4, 4, 4};5051// Japanese (Kana) diacritics52static const wchar_t diacritics[2][103] =53{54{L"かがきぎくぐけげこごさざしじすずせぜそぞただちぢつづてでとどはばぱばひびぴびふぶぷぶへべぺべほぼぽぼウヴカガキギクグケゲコゴサザシジスズセゼソゾタダチヂツヅテデトドハバパバヒビピビフブプブヘベペベホボポボ"},55{L"はぱばぱひぴびぴふぷぶぷへぺべぺほぽぼぽハパバパヒピビピフプブプヘペベペホポボポ"}56};5758// Korean (Hangul) consonant59static const wchar_t kor_cons[] = L"ㄱㄲㄴㄷㄸㄹㅁㅂㅃㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎ";6061// Korean (Hangul) vowels, Some vowels are not used, they will be spaces62static const wchar_t kor_vowel[] = L"ㅏㅐㅑㅒㅓㅔㅕㅖㅗ ㅛㅜ ㅠㅡ ㅣ";6364// Korean (Hangul) vowel Combination key65const 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};6667// Korean (Hangul) last consonant(diacritics)68static const wchar_t kor_lcons[] = L"ㄱㄲㄳㄴㄵㄶㄷㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅄㅅㅆㅇㅈㅊㅋㅌㅍㅎ";6970// Korean (Hangul) last consonant Combination key71const 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};7273// Korean (Hangul) last consonant Separation key74const 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};7576static const char16_t oskKeys[OSK_KEYBOARD_COUNT][6][14] =77{78{79// Latin Lowercase80{u"1234567890-+"},81{u"qwertyuiop[]"},82{u"asdfghjkl;@~"},83{u"zxcvbnm,./?\\"},84},85{86// Latin Uppercase87{u"!@#$%^&*()_+"},88{u"QWERTYUIOP{}"},89{u"ASDFGHJKL:\"`"},90{u"ZXCVBNM<>/?|"},91},92{93// Hiragana94{u"あかさたなはまやらわぁゃっ"},95{u"いきしちにひみ り ぃ "},96{u"うくすつぬふむゆるをぅゅ゛"},97{u"えけせてねへめ れ ぇ ゜"},98{u"おこそとのほもよろんぉょー"},99{u"・。、「」『』〜 "},100},101{102// Katakana103{u"アカサタナハマヤラワァャッ"},104{u"イキシチニヒミ リ ィ "},105{u"ウクスツヌフムユルヲゥュ゛"},106{u"エケセテネヘメ レ ェ ゜"},107{u"オコソトノホモヨロンォョー"},108{u"・。、「」『』〜 "},109},110{111// Korean(Hangul)112{u"1234567890-+"},113{u"ㅃㅉㄸㄲㅆ!@#$%^&"},114{u"ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ[]"},115{u"ㅁㄴㅇㄹㅎㅗㅓㅏㅣ;@~"},116{u"ㅋㅌㅊㅍㅠㅜㅡ<>/?|"},117},118{119// Russian Lowercase120{u"1234567890-+"},121{u"йцукенгшщзхъ"},122{u"фывапролджэё"},123{u"ячсмитьбю/?|"},124},125{126// Russian Uppercase127{u"!@#$%^&*()_+"},128{u"ЙЦУКЕНГШЩЗХЪ"},129{u"ФЫВАПРОЛДЖЭЁ"},130{u"ЯЧСМИТЬБЮ/?|"},131},132{133// Latin Full-width Lowercase134{ u"1234567890-+" },135{ u"qwertyuiop[]" },136{ u"asdfghjkl;@~" },137{ u"zxcvbnm,./?¥¥" },138},139{140// Latin Full-width Uppercase141{ u"!@#$%^&*()_+" },142{ u"QWERTYUIOP{}" },143{ u"ASDFGHJKL:¥”‘" },144{ u"ZXCVBNM<>/?|" },145},146};147148// This isn't a complete representation of these flags, it just helps ensure we show the right keyboards.149static const int allowedInputFlagsMap[OSK_KEYBOARD_COUNT] = {150PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT,151PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL,152PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HIRAGANA,153PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_KATAKANA | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HALF_KATAKANA,154PSP_UTILITY_OSK_INPUTTYPE_KOREAN,155PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE,156PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE,157PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_DIGIT,158PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL,159};160static const int defaultInputFlagsMap[OSK_KEYBOARD_COUNT] = {161PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT,162PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL,163PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HIRAGANA,164PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_KATAKANA | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HALF_KATAKANA,165PSP_UTILITY_OSK_INPUTTYPE_KOREAN,166PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE,167PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE,168PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_DIGIT,169PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE | PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL,170};171172PSPOskDialog::PSPOskDialog(UtilityDialogType type) : PSPDialog(type) {173// This can break all kinds of stuff, changing the decimal point in sprintf for example.174// Not sure what the intended effect is so commented out for now.175// setlocale(LC_ALL, "");176}177178PSPOskDialog::~PSPOskDialog() {179}180181void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, const PSPPointer<u16_le>& em_address)182{183if (!em_address.IsValid())184{185_string.clear();186return;187}188189const size_t maxLength = 2047;190char stringBuffer[maxLength + 1];191char *string = stringBuffer;192193u16_le *input = &em_address[0];194int c;195while ((c = *input++) != 0 && string < stringBuffer + maxLength)196{197if (c < 0x80)198*string++ = c;199else if (c < 0x800) {200*string++ = 0xC0 | (c >> 6);201*string++ = 0x80 | (c & 0x3F);202} else {203*string++ = 0xE0 | (c >> 12);204*string++ = 0x80 | ((c >> 6) & 0x3F);205*string++ = 0x80 | (c & 0x3F);206}207}208*string++ = '\0';209_string = stringBuffer;210}211212void GetWideStringFromPSPPointer(std::u16string& _string, const PSPPointer<u16_le>& em_address)213{214if (!em_address.IsValid())215{216_string.clear();217return;218}219220const size_t maxLength = 2047;221char16_t stringBuffer[maxLength + 1];222char16_t *string = stringBuffer;223224u16_le *input = &em_address[0];225int c;226while ((c = *input++) != 0 && string < stringBuffer + maxLength)227*string++ = c;228*string++ = '\0';229_string = stringBuffer;230}231232void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, const char16_t *input)233{234char stringBuffer[2048];235char *string = stringBuffer;236237int c;238while ((c = *input++) != 0)239{240if (c < 0x80)241*string++ = c;242else if (c < 0x800) {243*string++ = 0xC0 | (c >> 6);244*string++ = 0x80 | (c & 0x3F);245} else {246*string++ = 0xE0 | (c >> 12);247*string++ = 0x80 | ((c >> 6) & 0x3F);248*string++ = 0x80 | (c & 0x3F);249}250}251*string++ = '\0';252_string = stringBuffer;253}254255static void FindValidKeyboard(s32 inputType, int direction, OskKeyboardLanguage &lang, OskKeyboardDisplay &disp) {256OskKeyboardLanguage origLang = lang;257OskKeyboardDisplay origDisp = disp;258259if (inputType == 0) {260return;261}262// We use direction = 0 for default, but we actually move "forward".263const int *matchMap = allowedInputFlagsMap;264if (direction == 0) {265direction = 1;266matchMap = defaultInputFlagsMap;267}268269// TODO: Limit by allowed keyboards properly... this is just an approximation.270int tries = OSK_LANGUAGE_COUNT * 2;271while (!(inputType & matchMap[disp]) && tries > 0) {272if ((--tries % 2) == 0) {273lang = (OskKeyboardLanguage)((OSK_LANGUAGE_COUNT + lang + direction) % OSK_LANGUAGE_COUNT);274disp = OskKeyboardCases[lang][LOWERCASE];275} else {276disp = OskKeyboardCases[lang][UPPERCASE];277}278}279280if (tries == 0) {281// In case of error, let's just fall back to allowing all.282lang = origLang;283disp = origDisp;284}285}286287static bool IsKeyboardShiftValid(s32 inputType, OskKeyboardLanguage lang, OskKeyboardDisplay disp) {288// Swap disp and check if it's valid.289if (disp == OskKeyboardCases[lang][UPPERCASE])290disp = OskKeyboardCases[lang][LOWERCASE];291else292disp = OskKeyboardCases[lang][UPPERCASE];293294return inputType == 0 || (inputType & allowedInputFlagsMap[disp]) != 0;295}296297int PSPOskDialog::Init(u32 oskPtr) {298// Ignore if already running299if (GetStatus() != SCE_UTILITY_STATUS_NONE) {300ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid status");301return SCE_ERROR_UTILITY_INVALID_STATUS;302}303// Seems like this should crash?304if (!Memory::IsValidAddress(oskPtr)) {305ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid params (%08x)", oskPtr);306return -1;307}308309oskParams = oskPtr;310if (oskParams->base.size != sizeof(SceUtilityOskParams))311{312ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid size %d", oskParams->base.size);313return SCE_ERROR_UTILITY_INVALID_PARAM_SIZE;314}315// Also seems to crash.316if (!oskParams->fields.IsValid())317{318ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: invalid field data (%08x)", oskParams->fields.ptr);319return -1;320}321322if (oskParams->unk_60 != 0)323WARN_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: unknown param is non-zero (%08x)", oskParams->unk_60);324if (oskParams->fieldCount != 1)325WARN_LOG_REPORT(Log::sceUtility, "sceUtilityOskInitStart: unsupported field count %d", oskParams->fieldCount);326327ChangeStatusInit(OSK_INIT_DELAY_US);328selectedChar = 0;329currentKeyboardLanguage = OSK_LANGUAGE_ENGLISH;330currentKeyboard = OSK_KEYBOARD_LATIN_LOWERCASE;331FindValidKeyboard(oskParams->fields[0].inputtype, 0, currentKeyboardLanguage, currentKeyboard);332333ConvertUCS2ToUTF8(oskDesc, oskParams->fields[0].desc);334ConvertUCS2ToUTF8(oskIntext, oskParams->fields[0].intext);335ConvertUCS2ToUTF8(oskOuttext, oskParams->fields[0].outtext);336337i_level = 0;338339inputChars.clear();340341if (oskParams->fields[0].intext.IsValid()) {342auto src = oskParams->fields[0].intext;343int c;344while ((c = *src++) != 0)345inputChars += c;346}347348languageMapping = g_Config.GetLangValuesMapping();349350// Eat any keys pressed before the dialog inited.351UpdateButtons();352InitCommon();353354std::lock_guard<std::mutex> guard(nativeMutex_);355nativeStatus_ = PSPOskNativeStatus::IDLE;356357StartFade(true);358return 0;359}360361std::u16string PSPOskDialog::CombinationKorean(bool isInput)362{363std::u16string string;364365isCombinated = true;366367int selectedRow = selectedChar / numKeyCols[currentKeyboard];368int selectedCol = selectedChar % numKeyCols[currentKeyboard];369370if (inputChars.size() == 0) {371wchar_t sw = oskKeys[currentKeyboard][selectedRow][selectedCol];372373if (inputChars.size() < FieldMaxLength()) {374string += sw;375376i_value[0] = GetIndex(kor_cons, sw);377378if(i_value[0] != -1 && isInput == true)379i_level = 1;380} else {381isCombinated = false;382}383} else {384for(u32 i = 0; i < inputChars.size(); i++) {385if(i + 1 == inputChars.size()) {386wchar_t sw = oskKeys[currentKeyboard][selectedRow][selectedCol];387388if(i_level == 0) {389string += inputChars[i];390if (inputChars.size() < FieldMaxLength()) {391string += sw;392393i_value[0] = GetIndex(kor_cons, sw);394395if(i_value[0] != -1 && isInput == true)396i_level = 1;397} else {398isCombinated = false;399}400} else if(i_level == 1) {401i_value[1] = GetIndex(kor_vowel, sw);402403if(i_value[1] == -1) {404string += inputChars[i];405if (inputChars.size() < FieldMaxLength()) {406string += sw;407408if(isInput == true) {409i_value[0] = GetIndex(kor_cons, sw);410411if(i_value[0] != -1)412i_level = 1;413else414i_level = 0;415}416} else {417isCombinated = false;418}419} else {420u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C;421string += code;422423if(isInput == true) {424i_level = 2;425}426}427} else if(i_level == 2) {428int tmp = GetIndex(kor_vowel, sw);429if(tmp != -1) {430int tmp2 = -1;431for(size_t j = 0; j < sizeof(kor_vowelCom) / 4; j+=3) {432if(kor_vowelCom[j] == tmp && kor_vowelCom[j + 1] == i_value[1]) {433tmp2 = kor_vowelCom[j + 2];434break;435}436}437if(tmp2 != -1) {438if(isInput == true) {439i_value[1] = tmp2;440}441442u16 code = 0xAC00 + i_value[0] * 0x24C + tmp2 * 0x1C;443444string += code;445} else {446string += inputChars[i];447if (inputChars.size() < FieldMaxLength()) {448string += sw;449450if(isInput == true) {451i_level = 0;452}453} else {454isCombinated = false;455}456}457} else {458int tmp2 = GetIndex(kor_lcons, sw);459460if (tmp2 == -1) {461string += inputChars[i];462if (inputChars.size() < FieldMaxLength()) {463string += sw;464465if (isInput == true) {466i_value[0] = GetIndex(kor_cons, sw);467468if(i_value[0] != -1)469i_level = 1;470else471i_level = 0;472}473} else {474isCombinated = false;475}476} else {477u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + tmp2 + 1;478479string += code;480481if (isInput == true) {482i_level = 3;483i_value[2] = tmp2;484}485}486}487} else if(i_level == 3) {488int tmp = GetIndex(kor_lcons, sw);489if(tmp != -1) {490int tmp2 = -1;491for(size_t j = 0; j < sizeof(kor_lconsCom) / 4; j+=3) {492if(kor_lconsCom[j] == tmp && kor_lconsCom[j + 1] == i_value[2]) {493tmp2 = kor_lconsCom[j + 2];494break;495}496}497if(tmp2 != -1) {498if(isInput == true) {499i_value[2] = tmp2;500}501502u16 code = 0xAC00 + i_value[0] * 0x24C + tmp2 * 0x1C + i_value[2] + 1;503504string += code;505} else {506string += inputChars[i];507if (inputChars.size() < FieldMaxLength()) {508string += sw;509510if(isInput == true) {511i_value[0] = GetIndex(kor_cons, sw);512513if(i_value[0] != -1)514i_level = 1;515else516i_level = 0;517}518} else {519isCombinated = false;520}521}522} else {523int tmp3 = GetIndex(kor_vowel, sw);524if (tmp3 == -1) {525string += inputChars[i];526if (inputChars.size() < FieldMaxLength()) {527string += sw;528529if(isInput == true) {530i_value[0] = GetIndex(kor_cons, sw);531532if(i_value[0] != -1)533i_level = 1;534else535i_level = 0;536}537} else {538isCombinated = false;539}540} else {541if (inputChars.size() < FieldMaxLength()) {542int tmp2 = -1;543for(size_t j = 0; j < sizeof(kor_lconsSpr) / 4; j+=3) {544if(kor_lconsSpr[j] == i_value[2]) {545tmp2 = (int)j;546break;547}548}549if(tmp2 != -1) {550u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + kor_lconsSpr[tmp2 + 1];551string += code;552553code = 0xAC00 + kor_lconsSpr[tmp2 + 2] * 0x24C + tmp3 * 0x1C;554string += code;555556if(isInput == true) {557i_value[0] = kor_lconsSpr[tmp2 + 2];558i_value[1] = tmp3;559i_level = 2;560}561} else {562int tmp4 = GetIndex(kor_cons, kor_lcons[i_value[2]]);563564if (tmp4 != -1) {565u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C;566567string += code;568569code = 0xAC00 + tmp4 * 0x24C + tmp3 * 0x1C;570571string += code;572573if(isInput == true) {574i_value[0] = tmp4;575i_value[1] = tmp3;576i_level = 2;577}578} else {579string += inputChars[i];580string += sw;581582if(isInput == true) {583i_level = 0;584}585}586}587} else {588string += inputChars[i];589isCombinated = false;590}591}592}593}594} else {595string += inputChars[i];596}597}598}599600return string;601}602603std::u16string PSPOskDialog::CombinationString(bool isInput)604{605std::u16string string;606607isCombinated = false;608609int selectedRow = selectedChar / numKeyCols[currentKeyboard];610int selectedCol = selectedChar % numKeyCols[currentKeyboard];611612if(currentKeyboard == OSK_KEYBOARD_KOREAN)613{614string = CombinationKorean(isInput);615}616else617{618if(isInput == true)619{620i_level = 0;621}622623if(oskKeys[currentKeyboard][selectedRow][selectedCol] == L'゛')624{625for(u32 i = 0; i < inputChars.size(); i++)626{627if(i + 1 == inputChars.size())628{629for(u32 j = 0; j < wcslen(diacritics[0]); j+=2)630{631if(inputChars[i] == diacritics[0][j])632{633string += diacritics[0][j + 1];634isCombinated = true;635break;636}637}638639if(isCombinated == false)640{641string += inputChars[i];642}643}644else645{646string += inputChars[i];647}648}649}650else if(oskKeys[currentKeyboard][selectedRow][selectedCol] == L'゜')651{652for(u32 i = 0; i < inputChars.size(); i++)653{654if(i + 1 == inputChars.size())655{656for(u32 j = 0; j < wcslen(diacritics[1]); j+=2)657{658if(inputChars[i] == diacritics[1][j])659{660string += diacritics[1][j + 1];661isCombinated = true;662break;663}664}665666if(isCombinated == false)667{668string += inputChars[i];669}670}671else672{673string += inputChars[i];674}675}676}677else678{679for(u32 i = 0; i < inputChars.size(); i++)680{681string += inputChars[i];682}683684if (string.size() < FieldMaxLength())685{686string += oskKeys[currentKeyboard][selectedRow][selectedCol];687}688isCombinated = true;689}690}691692return string;693}694695void PSPOskDialog::RemoveKorean()696{697if(i_level == 1)698{699i_level = 0;700}701else if(i_level == 2)702{703int tmp = -1;704for(size_t i = 2; i < sizeof(kor_vowelCom) / 4; i+=3)705{706if(kor_vowelCom[i] == i_value[1])707{708tmp = kor_vowelCom[i - 1];709break;710}711}712713if(tmp != -1)714{715i_value[1] = tmp;716u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C;717inputChars += code;718}719else720{721i_level = 1;722inputChars += kor_cons[i_value[0]];723}724}725else if(i_level == 3)726{727int tmp = -1;728for(size_t i = 2; i < sizeof(kor_lconsCom) / 4; i+=3)729{730if(kor_lconsCom[i] == i_value[2])731{732tmp = kor_lconsCom[i - 1];733break;734}735}736737if(tmp != -1)738{739i_value[2] = tmp;740u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C + i_value[2] + 1;741inputChars += code;742}743else744{745i_level = 2;746u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C;747inputChars += code;748}749}750}751752int PSPOskDialog::GetIndex(const wchar_t* src, wchar_t ch)753{754for(int i = 0, end = (int)wcslen(src); i < end; i++)755{756if(src[i] == ch)757{758return i;759}760}761762return -1;763}764765u32 PSPOskDialog::FieldMaxLength()766{767if ((oskParams->fields[0].outtextlimit > oskParams->fields[0].outtextlength - 1) || oskParams->fields[0].outtextlimit == 0)768return oskParams->fields[0].outtextlength - 1;769return oskParams->fields[0].outtextlimit;770}771772void PSPOskDialog::RenderKeyboard()773{774// Sanity check that a valid keyboard is selected.775if ((int)currentKeyboard < 0 || (int)currentKeyboard >= OSK_KEYBOARD_COUNT) {776return;777}778779int selectedRow = selectedChar / numKeyCols[currentKeyboard];780int selectedCol = selectedChar % numKeyCols[currentKeyboard];781782char16_t temp[2];783temp[1] = '\0';784785std::string buffer;786787static const u32 FIELDDRAWMAX = 16;788u32 limit = FieldMaxLength();789u32 drawLimit = std::min(FIELDDRAWMAX, limit); // Field drew length limit.790791const float keyboardLeftSide = (480.0f - (24.0f * numKeyCols[currentKeyboard])) / 2.0f;792const float characterWidth = 12.0f;793float previewLeftSide = (480.0f - (12.0f * drawLimit)) / 2.0f;794float title = (480.0f - (0.5f * drawLimit)) / 2.0f;795796PPGeStyle descStyle = FadedStyle(PPGeAlign::BOX_CENTER, 0.5f);797PPGeDrawText(oskDesc.c_str(), title, 20, descStyle);798799PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_HCENTER, 0.5f);800801PPGeStyle keyStyle = FadedStyle(PPGeAlign::BOX_HCENTER, 0.6f);802PPGeStyle selectedKeyStyle = FadedStyle(PPGeAlign::BOX_HCENTER, 0.6f);803selectedKeyStyle.color = CalcFadedColor(0xFF3060FF);804805std::u16string result;806807result = CombinationString(false);808809u32 drawIndex = (u32)(result.size() > drawLimit ? result.size() - drawLimit : 0);810drawIndex = result.size() == limit + 1 ? drawIndex - 1 : drawIndex; // When the length reached limit, the last character don't fade in and out.811for (u32 i = 0; i < drawLimit; ++i, ++drawIndex)812{813if (drawIndex + 1 < result.size())814{815temp[0] = result[drawIndex];816ConvertUCS2ToUTF8(buffer, temp);817PPGeDrawText(buffer.c_str(), previewLeftSide + (i * characterWidth), 40.0f, textStyle);818}819else820{821if (drawIndex + 1 == result.size())822{823temp[0] = result[drawIndex];824825if(isCombinated == true)826{827float animStep = (float)(__DisplayGetNumVblanks() % 40) / 20.0f;828// Fade in and out the next character so they know it's not part of the string yet.829u32 alpha = (0.5f - (cosf(animStep * M_PI) / 2.0f)) * 128 + 127;830PPGeStyle animStyle = textStyle;831animStyle.color = CalcFadedColor((alpha << 24) | 0x00FFFFFF);832833ConvertUCS2ToUTF8(buffer, temp);834835PPGeDrawText(buffer.c_str(), previewLeftSide + (i * characterWidth), 40.0f, animStyle);836837// Also draw the underline for the same reason.838PPGeDrawText("_", previewLeftSide + (i * characterWidth), 40.0f, textStyle);839}840else841{842ConvertUCS2ToUTF8(buffer, temp);843PPGeDrawText(buffer.c_str(), previewLeftSide + (i * characterWidth), 40.0f, textStyle);844}845}846else847{848PPGeDrawText("_", previewLeftSide + (i * characterWidth), 40.0f, textStyle);849}850}851}852853for (int row = 0; row < numKeyRows[currentKeyboard]; ++row)854{855for (int col = 0; col < numKeyCols[currentKeyboard]; ++col)856{857temp[0] = oskKeys[currentKeyboard][row][col];858859ConvertUCS2ToUTF8(buffer, temp);860861if (selectedRow == row && col == selectedCol) {862PPGeDrawText(buffer.c_str(), keyboardLeftSide + (25.0f * col) + characterWidth / 2.0, 70.0f + (25.0f * row), selectedKeyStyle);863PPGeDrawText("_", keyboardLeftSide + (25.0f * col) + characterWidth / 2.0, 70.0f + (25.0f * row), keyStyle);864} else {865PPGeDrawText(buffer.c_str(), keyboardLeftSide + (25.0f * col) + characterWidth / 2.0, 70.0f + (25.0f * row), keyStyle);866}867}868}869}870871// TODO: Why does this have a 2 button press lag/delay when872// re-opening the dialog box? I don't get it.873int PSPOskDialog::NativeKeyboard() {874if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) {875return SCE_ERROR_UTILITY_INVALID_STATUS;876}877878#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID)879bool beginInputBox = false;880if (nativeStatus_ == PSPOskNativeStatus::IDLE) {881std::lock_guard<std::mutex> guard(nativeMutex_);882if (nativeStatus_ == PSPOskNativeStatus::IDLE) {883nativeStatus_ = PSPOskNativeStatus::WAITING;884beginInputBox = true;885}886}887888if (beginInputBox) {889std::u16string titleText;890GetWideStringFromPSPPointer(titleText, oskParams->fields[0].desc);891892std::u16string defaultText;893GetWideStringFromPSPPointer(defaultText, oskParams->fields[0].intext);894895if (defaultText.empty())896defaultText.assign(u"VALUE");897898// There's already ConvertUCS2ToUTF8 in this file. Should we use that instead of the global ones?899System_InputBoxGetString(NON_EPHEMERAL_TOKEN, ::ConvertUCS2ToUTF8(titleText), ::ConvertUCS2ToUTF8(defaultText), false,900[&](const std::string &value, int) {901// Success callback902std::lock_guard<std::mutex> guard(nativeMutex_);903if (nativeStatus_ != PSPOskNativeStatus::WAITING) {904return;905}906nativeValue_ = value;907nativeStatus_ = PSPOskNativeStatus::SUCCESS;908},909[&]() {910// Failure callback911std::lock_guard<std::mutex> guard(nativeMutex_);912if (nativeStatus_ != PSPOskNativeStatus::WAITING) {913return;914}915nativeValue_ = "";916nativeStatus_ = PSPOskNativeStatus::FAILURE;917}918);919} else if (nativeStatus_ == PSPOskNativeStatus::SUCCESS) {920inputChars = ConvertUTF8ToUCS2(nativeValue_);921nativeValue_.clear();922923u32 maxLength = FieldMaxLength();924if (inputChars.length() > maxLength) {925ERROR_LOG(Log::sceUtility, "NativeKeyboard: input text too long(%d characters/glyphs max), truncating to game-requested length.", maxLength);926inputChars.erase(maxLength, std::string::npos);927}928ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);929nativeStatus_ = PSPOskNativeStatus::DONE;930} else if (nativeStatus_ == PSPOskNativeStatus::FAILURE) {931ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);932nativeStatus_ = PSPOskNativeStatus::DONE;933}934#endif935936u16_le *outText = oskParams->fields[0].outtext;937938size_t end = oskParams->fields[0].outtextlength;939if (end > inputChars.size())940end = inputChars.size() + 1;941// Only write the bytes of the output and the null terminator, don't write the rest.942for (size_t i = 0; i < end; ++i) {943u16 value = 0;944if (i < FieldMaxLength() && i < inputChars.size())945value = inputChars[i];946outText[i] = value;947}948949oskParams->base.result = 0;950oskParams->fields[0].result = PSP_UTILITY_OSK_RESULT_CHANGED;951952return 0;953}954955int PSPOskDialog::Update(int animSpeed) {956if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) {957return SCE_ERROR_UTILITY_INVALID_STATUS;958}959960int cancelButton = GetCancelButton();961int confirmButton = GetConfirmButton();962963static int cancelBtnFramesHeld = 0;964static int confirmBtnFramesHeld = 0;965static int leftBtnFramesHeld = 0;966static int upBtnFramesHeld = 0;967static int downBtnFramesHeld = 0;968static int rightBtnFramesHeld = 0;969const int framesHeldThreshold = 10;970const int framesHeldRepeatRate = 5;971972UpdateButtons();973UpdateCommon();974int selectedRow = selectedChar / numKeyCols[currentKeyboard];975int selectedExtra = selectedChar % numKeyCols[currentKeyboard];976977#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)978// Windows: Fall back to the OSK/continue normally if we're in fullscreen.979// The dialog box doesn't work right if in fullscreen.980if (System_GetPropertyBool(SYSPROP_HAS_KEYBOARD)) {981if (g_Config.bBypassOSKWithKeyboard && !g_Config.UseFullScreen())982return NativeKeyboard();983}984#endif985986UpdateFade(animSpeed);987988StartDraw();989PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x63636363));990RenderKeyboard();991992auto di = GetI18NCategory(I18NCat::DIALOG);993994PPGeStyle actionStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.5f);995PPGeStyle guideStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.6f);996997PPGeDrawImage(ImageID("I_SQUARE"), 365, 222, 16, 16, guideStyle);998PPGeDrawText(di->T("Space"), 390, 222, actionStyle);9991000if (GetConfirmButton() != CTRL_CIRCLE) {1001PPGeDrawImage(ImageID("I_CROSS"), 45, 222, 16, 16, guideStyle);1002PPGeDrawImage(ImageID("I_CIRCLE"), 45, 247, 16, 16, guideStyle);1003} else {1004PPGeDrawImage(ImageID("I_CIRCLE"), 45, 222, 16, 16, guideStyle);1005PPGeDrawImage(ImageID("I_CROSS"), 45, 247, 16, 16, guideStyle);1006}10071008PPGeDrawText(di->T("Select"), 75, 222, actionStyle);1009PPGeDrawText(di->T("Delete"), 75, 247, actionStyle);10101011PPGeDrawText("Start", 135, 220, guideStyle);1012PPGeDrawText(di->T("Finish"), 185, 222, actionStyle);10131014auto lookupLangName = [&](int direction) {1015// First, find the valid one...1016OskKeyboardLanguage lang = (OskKeyboardLanguage)((OSK_LANGUAGE_COUNT + currentKeyboardLanguage + direction) % OSK_LANGUAGE_COUNT);1017OskKeyboardDisplay disp = OskKeyboardCases[lang][LOWERCASE];1018FindValidKeyboard(oskParams->fields[0].inputtype, direction, lang, disp);10191020if (lang == currentKeyboardLanguage) {1021return (const char *)nullptr;1022}10231024// Now, let's grab the name.1025const char *countryCode = OskKeyboardNames[lang].c_str();1026const char *language = languageMapping[countryCode].first.c_str();10271028// It seems like this is a "fake" country code for extra keyboard purposes.1029if (!strcmp(countryCode, "English Full-width"))1030language = "English Full-width";10311032return language;1033};10341035if (OskKeyboardNames[currentKeyboardLanguage] != "ko_KR" && IsKeyboardShiftValid(oskParams->fields[0].inputtype, currentKeyboardLanguage, currentKeyboard)) {1036PPGeDrawText("Select", 135, 245, guideStyle);1037PPGeDrawText(di->T("Shift"), 185, 247, actionStyle);1038}10391040const char *prevLang = lookupLangName(-1);1041if (prevLang) {1042PPGeDrawText("L", 235, 220, guideStyle);1043PPGeDrawText(prevLang, 255, 222, actionStyle);1044}10451046const char *nextLang = lookupLangName(1);1047if (nextLang) {1048PPGeDrawText("R", 235, 245, guideStyle);1049PPGeDrawText(nextLang, 255, 247, actionStyle);1050}10511052if (IsButtonPressed(CTRL_UP) || IsButtonHeld(CTRL_UP, upBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {1053selectedChar -= numKeyCols[currentKeyboard];1054} else if (IsButtonPressed(CTRL_DOWN) || IsButtonHeld(CTRL_DOWN, downBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {1055selectedChar += numKeyCols[currentKeyboard];1056} else if (IsButtonPressed(CTRL_LEFT) || IsButtonHeld(CTRL_LEFT, leftBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {1057selectedChar--;1058if (((selectedChar + numKeyCols[currentKeyboard]) % numKeyCols[currentKeyboard]) == numKeyCols[currentKeyboard] - 1)1059selectedChar += numKeyCols[currentKeyboard];1060} else if (IsButtonPressed(CTRL_RIGHT) || IsButtonHeld(CTRL_RIGHT, rightBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {1061selectedChar++;1062if ((selectedChar % numKeyCols[currentKeyboard]) == 0)1063selectedChar -= numKeyCols[currentKeyboard];1064}10651066selectedChar = (selectedChar + (numKeyCols[currentKeyboard] * numKeyRows[currentKeyboard])) % (numKeyCols[currentKeyboard] * numKeyRows[currentKeyboard]);10671068if (IsButtonPressed(confirmButton) || IsButtonHeld(confirmButton, confirmBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {1069inputChars = CombinationString(true);1070} else if (IsButtonPressed(CTRL_SELECT)) {1071// Select now swaps case.1072if (IsKeyboardShiftValid(oskParams->fields[0].inputtype, currentKeyboardLanguage, currentKeyboard)) {1073if (currentKeyboard == OskKeyboardCases[currentKeyboardLanguage][UPPERCASE])1074currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE];1075else1076currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][UPPERCASE];1077}10781079if (selectedRow >= numKeyRows[currentKeyboard]) {1080selectedRow = numKeyRows[currentKeyboard] - 1;1081}10821083if (selectedExtra >= numKeyCols[currentKeyboard]) {1084selectedExtra = numKeyCols[currentKeyboard] - 1;1085}10861087selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra;1088} else if (IsButtonPressed(CTRL_RTRIGGER)) {1089// RTRIGGER now cycles languages forward.1090currentKeyboardLanguage = (OskKeyboardLanguage)((currentKeyboardLanguage + 1) % OSK_LANGUAGE_COUNT);1091currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE];1092FindValidKeyboard(oskParams->fields[0].inputtype, 1, currentKeyboardLanguage, currentKeyboard);10931094if (selectedRow >= numKeyRows[currentKeyboard]) {1095selectedRow = numKeyRows[currentKeyboard] - 1;1096}10971098if (selectedExtra >= numKeyCols[currentKeyboard]) {1099selectedExtra = numKeyCols[currentKeyboard] - 1;1100}11011102selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra;1103} else if (IsButtonPressed(CTRL_LTRIGGER)) {1104// LTRIGGER now cycles languages backward.1105if (currentKeyboardLanguage - 1 >= 0)1106currentKeyboardLanguage = (OskKeyboardLanguage)((currentKeyboardLanguage - 1) % OSK_LANGUAGE_COUNT);1107else1108currentKeyboardLanguage = (OskKeyboardLanguage)(OSK_LANGUAGE_COUNT - 1);1109currentKeyboard = OskKeyboardCases[currentKeyboardLanguage][LOWERCASE];1110FindValidKeyboard(oskParams->fields[0].inputtype, -1, currentKeyboardLanguage, currentKeyboard);11111112if (selectedRow >= numKeyRows[currentKeyboard]) {1113selectedRow = numKeyRows[currentKeyboard] - 1;1114}11151116if (selectedExtra >= numKeyCols[currentKeyboard]) {1117selectedExtra = numKeyCols[currentKeyboard] - 1;1118}11191120selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra;1121} else if (IsButtonPressed(cancelButton) || IsButtonHeld(cancelButton, cancelBtnFramesHeld, framesHeldThreshold, framesHeldRepeatRate)) {1122if (inputChars.size() > 0) {1123inputChars.resize(inputChars.size() - 1);1124if (i_level != 0) {1125RemoveKorean();1126}1127}1128} else if (IsButtonPressed(CTRL_START)) {1129StartFade(false);1130} else if (IsButtonPressed(CTRL_SQUARE) && inputChars.size() < FieldMaxLength()) {1131// Use a regular space if the current keyboard isn't Japanese nor full-width English1132if (currentKeyboardLanguage != OSK_LANGUAGE_JAPANESE && currentKeyboardLanguage != OSK_LANGUAGE_ENGLISH_FW)1133inputChars += u" ";1134else1135inputChars += u" ";1136}11371138EndDraw();11391140u16_le *outText = oskParams->fields[0].outtext;1141size_t end = oskParams->fields[0].outtextlength;1142// Only write the bytes of the output and the null terminator, don't write the rest.1143if (end > inputChars.size())1144end = inputChars.size() + 1;1145for (size_t i = 0; i < end; ++i)1146{1147u16 value = 0;1148if (i < FieldMaxLength() && i < inputChars.size())1149value = inputChars[i];1150outText[i] = value;1151}11521153oskParams->base.result = 0;1154oskParams->fields[0].result = PSP_UTILITY_OSK_RESULT_CHANGED;1155return 0;1156}11571158int PSPOskDialog::Shutdown(bool force)1159{1160if (GetStatus() != SCE_UTILITY_STATUS_FINISHED && !force)1161return SCE_ERROR_UTILITY_INVALID_STATUS;11621163PSPDialog::Shutdown(force);1164if (!force) {1165ChangeStatusShutdown(OSK_SHUTDOWN_DELAY_US);1166}1167nativeStatus_ = PSPOskNativeStatus::IDLE;11681169return 0;1170}11711172void PSPOskDialog::DoState(PointerWrap &p)1173{1174PSPDialog::DoState(p);11751176auto s = p.Section("PSPOskDialog", 1, 2);1177if (!s)1178return;11791180// TODO: Should we save currentKeyboard/currentKeyboardLanguage?11811182Do(p, oskParams);1183Do(p, oskDesc);1184Do(p, oskIntext);1185Do(p, oskOuttext);1186Do(p, selectedChar);1187if (s >= 2) {1188Do(p, inputChars);1189} else {1190// Discard the wstring.1191std::wstring wstr;1192Do(p, wstr);1193}1194// Don't need to save state native status or value.1195}11961197pspUtilityDialogCommon *PSPOskDialog::GetCommonParam()1198{1199return &oskParams->base;1200}120112021203