From 10389dc3f18a41ba84dd0fea5f95af9419918e59 Mon Sep 17 00:00:00 2001 From: RedPanda4552 Date: Mon, 1 Jan 2024 18:17:33 -0500 Subject: [PATCH] Pad: Add Pop'n Music controller type --- 3rdparty/include/IconsPromptFont.h | 11 +- bin/resources/fonts/promptfont.otf | Bin 202604 -> 211172 bytes pcsx2-qt/CMakeLists.txt | 1 + .../Settings/ControllerBindingWidget_Popn.ui | 531 +++++++++++++++++ .../Settings/ControllerBindingWidgets.cpp | 25 + pcsx2-qt/Settings/ControllerBindingWidgets.h | 17 + pcsx2-qt/Translations.cpp | 8 +- pcsx2-qt/pcsx2-qt.vcxproj | 3 +- pcsx2-qt/pcsx2-qt.vcxproj.filters | 3 + .../resources/icons/black/svg/Popn-line.svg | 20 + .../resources/icons/white/svg/Popn-line.svg | 13 + pcsx2-qt/resources/images/Popn.svg | 183 ++++++ pcsx2-qt/resources/resources.qrc | 3 + pcsx2/CMakeLists.txt | 2 + pcsx2/ImGui/ImGuiManager.cpp | 2 +- pcsx2/SIO/Pad/Pad.cpp | 5 + pcsx2/SIO/Pad/PadPopn.cpp | 556 ++++++++++++++++++ pcsx2/SIO/Pad/PadPopn.h | 103 ++++ pcsx2/SIO/Pad/PadTypes.h | 1 + pcsx2/pcsx2.vcxproj | 2 + pcsx2/pcsx2.vcxproj.filters | 6 + 21 files changed, 1488 insertions(+), 7 deletions(-) create mode 100644 pcsx2-qt/Settings/ControllerBindingWidget_Popn.ui create mode 100644 pcsx2-qt/resources/icons/black/svg/Popn-line.svg create mode 100644 pcsx2-qt/resources/icons/white/svg/Popn-line.svg create mode 100644 pcsx2-qt/resources/images/Popn.svg create mode 100644 pcsx2/SIO/Pad/PadPopn.cpp create mode 100644 pcsx2/SIO/Pad/PadPopn.h diff --git a/3rdparty/include/IconsPromptFont.h b/3rdparty/include/IconsPromptFont.h index cd158abb34..17fe5def7e 100644 --- a/3rdparty/include/IconsPromptFont.h +++ b/3rdparty/include/IconsPromptFont.h @@ -329,4 +329,13 @@ #define ICON_PF_SHOPPING_CART "\xF0\x9F\x9B\x92" #define ICON_PF_ACHIEVEMENTS_PROGRESSION "\xE2\x8E\xB2" #define ICON_PF_ACHIEVEMENTS_MISSABLE "\xE2\x8E\xB3" -#define ICON_PF_ACHIEVEMENTS_WIN "\xE2\x8E\xB4" \ No newline at end of file +#define ICON_PF_ACHIEVEMENTS_WIN "\xE2\x8E\xB4" +#define ICON_PF_POPN_BLUE_LEFT "\xE2\x8B\x80" +#define ICON_PF_POPN_BLUE_RIGHT "\xE2\x8B\x81" +#define ICON_PF_POPN_GREEN_LEFT "\xE2\x8B\x82" +#define ICON_PF_POPN_GREEN_RIGHT "\xE2\x8B\x83" +#define ICON_PF_POPN_YELLOW_LEFT "\xE2\x8B\x84" +#define ICON_PF_POPN_YELLOW_RIGHT "\xE2\x8B\x85" +#define ICON_PF_POPN_WHITE_LEFT "\xE2\x8B\x86" +#define ICON_PF_POPN_WHITE_RIGHT "\xE2\x8B\x87" +#define ICON_PF_POPN_RED "\xE2\x8B\x88" \ No newline at end of file diff --git a/bin/resources/fonts/promptfont.otf b/bin/resources/fonts/promptfont.otf index bddd1c2cc35dca46ed4914f9a4f0dc184f6156af..5bf2041c16148117a23f533226ff5ae7c9f7058e 100644 GIT binary patch delta 56335 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 VWM#2FHFBv)|1#25tXH{2{sEr;*3kd} diff --git a/pcsx2-qt/CMakeLists.txt b/pcsx2-qt/CMakeLists.txt index 9287189559..245077a89d 100644 --- a/pcsx2-qt/CMakeLists.txt +++ b/pcsx2-qt/CMakeLists.txt @@ -64,6 +64,7 @@ target_sources(pcsx2-qt PRIVATE Settings/ControllerBindingWidget.ui Settings/ControllerBindingWidget_DualShock2.ui Settings/ControllerBindingWidget_Guitar.ui + Settings/ControllerBindingWidget_Popn.ui Settings/ControllerBindingWidgets.cpp Settings/ControllerBindingWidgets.h Settings/ControllerLEDSettingsDialog.ui diff --git a/pcsx2-qt/Settings/ControllerBindingWidget_Popn.ui b/pcsx2-qt/Settings/ControllerBindingWidget_Popn.ui new file mode 100644 index 0000000000..cfa5ee8f82 --- /dev/null +++ b/pcsx2-qt/Settings/ControllerBindingWidget_Popn.ui @@ -0,0 +1,531 @@ + + + ControllerBindingWidget_Popn + + + + 0 + 0 + 1232 + 644 + + + + + 0 + 0 + + + + + 1100 + 500 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 400 + 266 + + + + + + + :/images/Popn.svg + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + Select + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Yellow (Left) + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Yellow (Right) + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Blue (Right) + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Blue (Left) + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Start + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + + + + + Red + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Green (Right) + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 150 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + White (Left) + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 150 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Green (Left) + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 150 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + White (Right) + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 150 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + + + + InputBindingWidget + QPushButton +
Settings/InputBindingWidget.h
+
+
+ + + + +
diff --git a/pcsx2-qt/Settings/ControllerBindingWidgets.cpp b/pcsx2-qt/Settings/ControllerBindingWidgets.cpp index 5d356a19e8..6d545b6b9b 100644 --- a/pcsx2-qt/Settings/ControllerBindingWidgets.cpp +++ b/pcsx2-qt/Settings/ControllerBindingWidgets.cpp @@ -108,6 +108,10 @@ void ControllerBindingWidget::onTypeChanged() { m_bindings_widget = ControllerBindingWidget_Guitar::createInstance(this); } + else if (cinfo->type == Pad::ControllerType::Popn) + { + m_bindings_widget = ControllerBindingWidget_Popn::createInstance(this); + } else { m_bindings_widget = new ControllerBindingWidget_Base(this); @@ -897,6 +901,27 @@ ControllerBindingWidget_Base* ControllerBindingWidget_Guitar::createInstance(Con return new ControllerBindingWidget_Guitar(parent); } +ControllerBindingWidget_Popn::ControllerBindingWidget_Popn(ControllerBindingWidget* parent) + : ControllerBindingWidget_Base(parent) +{ + m_ui.setupUi(this); + initBindingWidgets(); +} + +ControllerBindingWidget_Popn::~ControllerBindingWidget_Popn() +{ +} + +QIcon ControllerBindingWidget_Popn::getIcon() const +{ + return QIcon::fromTheme("Popn-line"); +} + +ControllerBindingWidget_Base* ControllerBindingWidget_Popn::createInstance(ControllerBindingWidget* parent) +{ + return new ControllerBindingWidget_Popn(parent); +} + ////////////////////////////////////////////////////////////////////////// USBDeviceWidget::USBDeviceWidget(QWidget* parent, ControllerSettingsWindow* dialog, u32 port) diff --git a/pcsx2-qt/Settings/ControllerBindingWidgets.h b/pcsx2-qt/Settings/ControllerBindingWidgets.h index 84125b1af6..be11c6c7a1 100644 --- a/pcsx2-qt/Settings/ControllerBindingWidgets.h +++ b/pcsx2-qt/Settings/ControllerBindingWidgets.h @@ -12,6 +12,7 @@ #include "ui_ControllerBindingWidget.h" #include "ui_ControllerBindingWidget_DualShock2.h" #include "ui_ControllerBindingWidget_Guitar.h" +#include "ui_ControllerBindingWidget_Popn.h" #include "ui_ControllerMacroWidget.h" #include "ui_ControllerMacroEditWidget.h" #include "ui_USBDeviceWidget.h" @@ -199,6 +200,22 @@ private: Ui::ControllerBindingWidget_Guitar m_ui; }; +class ControllerBindingWidget_Popn final : public ControllerBindingWidget_Base +{ + Q_OBJECT + +public: + ControllerBindingWidget_Popn(ControllerBindingWidget* parent); + ~ControllerBindingWidget_Popn(); + + QIcon getIcon() const override; + + static ControllerBindingWidget_Base* createInstance(ControllerBindingWidget* parent); + +private: + Ui::ControllerBindingWidget_Popn m_ui; +}; + ////////////////////////////////////////////////////////////////////////// class USBDeviceWidget final : public QWidget diff --git a/pcsx2-qt/Translations.cpp b/pcsx2-qt/Translations.cpp index 0bde59d84a..4e06fcaddf 100644 --- a/pcsx2-qt/Translations.cpp +++ b/pcsx2-qt/Translations.cpp @@ -360,22 +360,22 @@ static constexpr const QtHost::GlyphInfo s_glyph_info[] = { { "ja-JP", "msgothic.ttc", nullptr, "ヒラギノ角ゴシック W3.ttc", // auto update by update_glyph_ranges.py with pcsx2-qt_ja-JP.ts - u"​​……□□△△◯◯✕✕、。々々「』〜〜ああいいううええおせそそたちっぬのばびびぶぶへべほぼまやょろわわをんァイウチッツテニネロワワンン・ー一一上下不与世世両両並並中中乗乗了了予予事事互互交交今介他他付付代以仮仮件件任任伸伸似似位低体体何何作作使使例例供供係係保保信信修修個個倍倍借借値値停停側側傍傍備備像像優優元元先光入入全全公公共共具典内内再再冗冗処処出出分切列列初初判別利利制刷則則削削前前副副割割力力加加効効動動勧勧化化区区十十半半南単印印即即去去参参及及反収取受古古可台右右号号各各合合同名向向否否含含告告周周味味命命品品商商問問善善四四回回囲囲固固国国圧在地地均均垂垂型型域域基基報報場場境境増増壊壊声声変変外外多多大大太太央央失失奇奇奨奨好好妙妙妨妨始始子子字存安安完完定定実実容容寄寄密密対対射射尊尊小小少少尾尾展展履履岐岐崩崩左左巨巨己己帰帰常常幅幅平平序序度座延延式式引引弱弱張張強強当当形形彩彩影影役役待待後後得得御御復復微微心心必必応応忠忠念念急急性性悪悪情情想想意意感感態態慣慣成成戻戻所所手手承承投投択択抱抱押押拒拒招招拡拡拳拳持持指指振振挿挿捗捗排排接接推推描提換換揮揮援援揺揺損損撮撮操操支支改改放放敗敗数数整整文文料料断断新新方方既既日日早早明明昨昨時時曲曲更更書書替最有有望望期期未本条条析析果果格格桁桁案案械械検検極極楽楽概概構構標標権権橙橙機機欄欄欠次止正歴歴殊残毎毎比比水水求求汎汎決決況況法法波波注注浪浪浮浮消消深深済済減減渡渡港港湾湾満満源源準準滑滑点点無無照照牲牲特特犠犠状状獲獲率率現現理理璧璧環環生生用用由由申申画画番番異異発登白白的的目目直直相相省省知知短短破破確確示示禁禁秒秒移移程程種種稿稿空空立立端端競競第第算算管管範範精精索索細細終終組組経経結結絞絞統統続続維維緑緒線線編編縦縦縮縮績績繰繰置置義義者者肢肢能能自自致致般般良良色色荷荷落落蔵蔵行行表表衰衰装装補補製製複複要要見見規規視視覚覚覧覧観観角角解解言言計計記記設設許許訳訳証証試試該詳認認語語誤誤説読調調識識警警象象負負責責費貼資資質質赤赤起起超超足足跡跡転転軸軸軽軽辞辞込込近近返返追追送送逆逆途途通通速速連連進進遅遅遊遊過過適適選選避避部部配配重量鉄鉄録録長長閉閉開開間間関関閾閾防防降降限限除除陽陽隅隅際際集集離難電電青青非非面面韓韓音音響響頂頂順順領領頻頼題題類類飾飾香香駄駄高高鮮鮮黄黄!!>?~~" + u"​​……□□△△◯◯✕✕、。々々「』〜〜ああいいううええおせそそたちっぬのばびびぶぶへべほぼまもややょろわわをんァイウチッツテニネロワワンヴ・ー一一上下不与世世両両並並中中丸丸乗乗了了予予事事互互交交今介他他付付代以仮仮件件任任伸伸似似位低体体何何作作使使例例供供依依価価係係保保信信修修個個倍倍借借値値停停側側傍傍備備像像優優元元先光入入全全公公共共具典内内再再冗冗処処出出分切列列初初判別利利制刷則則削削前前副副割割力力加加効効動動勧勧化化区区十十半半南単印印即即厳厳去去参参及及反反取受古古可台右右号号各各合合同名向向含含告告周周味味命命品品商商問問善善四四回回囲囲固固国国圧在地地均均垂垂型型域域基基報報場場境境増増壊壊声声変変外外多多大大太太央央失失奇奇奨奨好好妙妙妨妨始始子子字存安安完完定定実実容容寄寄密密対対専専射射尊尊小小少少尾尾展展履履岐岐崩崩左左巨巨差差己己帰帰常常幅幅平平序序度座延延式式引引弱弱張張強強当当形形彩彩影影役役待待後後得得御御復復微微心心必必応応忠忠念念思思急急性性悪悪情情想想意意感感態態慣慣成成戦戦戻戻所所手手扱扱承承抜抜択択抱抱押押招招拡拡拳拳持持指指振振挿挿捗捗排排接接推推描提換換援援揺揺損損撮撮操操支支改改放放敗敗数数整整文文料料断断新新方方既既日日早早昇昇明明昨昨時時普普曲曲更更書書替最有有望望期期未本条条析析果果格格桁桁案案械械検検極極楽楽概概構構標標権権橙橙機機欄欄欠次止正歴歴殊残母毎比比水水求求汎汎決決況況法法波波注注浪浪浮浮消消深深済済減減渡渡港港湾湾満満源源準準滑滑演演点点無無照照牲牲特特犠犠状状独独獲獲率率現現理理璧璧環環生生用用由由申申画画留留番番異異発登白白的的目目直直相相省省知知短短破破確確示示禁禁秒秒移移程程種種空空立立端端競競第第策策算算管管節節範範精精索索細細終終組組経経結結絞絞統統続続維維緑緒線線編編縦縦縮縮績績繰繰置置義義習習者者肢肢能能自自致致般般良良色色英英荷荷落落蔵蔵行行表表衰衰装装補補製製複複要要見見規規視視覚覚覧覧観観角角解解言言計計記記設設許許訳訳証証評評試試該詳認認語語誤誤説読調調識識警警象象負負責責費貼資資質質赤赤起起超超足足跡跡転転軸軸軽軽辞辞込込近近返返述述追追送送逆逆途途通通速速連連進進遅遅遊遊過過適適選選避避部部配配重量鉄鉄録録長長閉閉開開間間関関閾閾防防降降限限除除陽陽隅隅際際集集雑雑難難電電青青静静非非面面韓韓音音響響頂頂順順領領頻頼題題類類飾飾香香駄駄高高鮮鮮黄黄️️!!%%()::>?~~" }, { "ko-KR", "malgun.ttf", nullptr, "AppleSDGothicNeo.ttc", // auto update by update_glyph_ranges.py with pcsx2-qt_ko-KR.ts - u"“”……←↓□□△△◯◯✕✕んん茶茶가각간간갈갈감값강강같같개개거거건건걸걸검겁것것게게겟겠겨격견견결결경경계계고곡곱곱공공과과관관교교구국군군권권규규균균그극근근글글금급긍긍기기긴긴길길김깁깃깃깅깅깊깊까까깝깝깨깨꺼꺼께께꾸꾸꿉꿉끄끄끊끊끔끔끝끝나나난난날날남남낭낮내내낸낸냅냅너너널널넣네넬넬넷넷노녹높놓누누눈눈눌눌뉴뉴느느는는늘늘능능니니닌닌님닙닛닛다다단단닫달담담당당대대댑댑더더덜덜덤덤덩덩덮덮데덱덴덴델델도독돌돌동동됐됐되되된된될될됨됩두두둘둘뒤뒤듀듀듈듈드득든든들들듬듭등등디디딩딩때때떠떠떤떤떨떨또또뛰뛰뛸뛸뜁뜁뜨뜨라락란란랍랍랑랑래랙랜랜램램랫랫량량러럭런런럴럴럼럽렀렀렇렉렌렌렛렛려력련련렬렬렸령로록롤롤롭롭롯롯료료루루룸룸류류률률르르른른를를름릅리릭린린릴릴림립릿릿링링마막만만많많말말맞맞매매맨맨맵맵맺맺머머먼먼멀멀멈멈멋멋메메며며면면명명모목몬몬못못몽몽무무문문물물뮬뮬므므미믹민민밀밀밉밉밍밍및및바바반반받밝밥밥방방배백밴밴뱃뱃버버번번벌벌범법벗벗베벡벤벤벨벨벼벽변변별별병병보복본본볼볼부부분분불불붙붙뷰뷰브브블블비빅빈빈빌빌빙빙빛빛빠빠빨빨사삭산산살살삼삽상상새색샘샘생생샷샷서서선선설설성성세섹센센셀셀셈셉셋셋션션셰셰소속손손솔솔송송쇄쇄쇠쇠쇼쇼수숙순순숨숨숫숫쉬쉬슈슈스스슬슬습습시식신신실실심십싱싱싶싶쌍쌍쓰쓰쓸쓸씬씬아아안안않않알알암압았앙애액앤앤앨앨앵앵야약양양어어언언얻얼업없었었에에엔엔여역연연열열영영예예오오온온올올옵옵와와완완왑왑왔왔외외왼왼요요용용우우운운울울움웁웃웃워워원원월월웠웠웨웨웹웹위위유유율율으으은은을을음음응응의의이이인인일읽임입있있자작잘잘잠잡장장재재잿잿저적전전절절점접정정제제젠젠젤젤젯젯져져조족존존종종좋좌죄죄주주준준줄줄줍줍중중즈즉즐즐즘즘증증지직진진질질짐집짝짝짧짧째째쪽쪽찌찍차착참참창찾채책챕챕처척천천철철첨첩첫첫청청체체쳐쳐초초총총촬촬최최추축출출춤춥충충춰춰취취츠측치칙칠칠침칩칭칭카카칸칸칼칼캐캐캔캔캘캘캠캡커커컨컨컬컬컴컴케케켓켓켜켜켬켬코코콘콘콜콜콩콩쿳쿳퀀퀀퀴퀴큐큐크크큰큰클클큽큽키키킨킨킬킬킵킵킹킹타타탈탈탐탐태택탠탠탬탭터터턴턴털털텀텀테텍텐텐텔텔템템토토톨톨톱톱통통투투트특튼튼틀틀티틱팀팀팅팅파파팔팔팝팝패패팻팻퍼퍼페펙편편평평포포폴폴폼폼표표푸푸풀풀품품퓨퓨프프픈픈플플피픽핀핀필필핑핑하하한한할할함합핫핫항항해핵했행향향허허헌헌헤헤현현형형호혹혼혼홈홈홍홍화확환환활활황황회획횟횟횡횡효효후후훨훨휠휠휴휴흐흐흔흔희희히히힌힌" + u"“”……←↓□□△△◯◯✕✕んん茶茶가각간간갈갈감값강강같같개개거거건건걸걸검겁것것게게겟겠격격견견결결경경계계고곡곱곱공공과과관관교교구국군군권권귀귀규규균균그극근근글글금급긍긍기기긴긴길길김깁깃깃깅깅깊깊까까깝깝깨깨꺼꺼께께꾸꾸꿉꿉끄끄끊끊끔끔끝끝나나난난날날남남낭낮내내낸낸냅냅너너널널넣네넬넬넷넷노녹높놓누누눈눈눌눌뉴뉴느느는는늘늘능능니닉닌닌님닙닛닛다다단단닫달담담당당대대댑댑더더덜덜덤덤덩덩덮덮데덱덴덴델델도독돌돌동동됐됐되되된된될될됨됩두두둘둘뒤뒤듀듀듈듈드득든든들들듬듭등등디디딩딩때때떠떠떤떤떨떨또또뛰뛰뛸뛸뜁뜁뜨뜨라락란란랍랍랑랑래랙랜랜램램랫랫량량러럭런런럴럴럼럽렀렀렇렉렌렌렛렛려력련련렬렬렸령로록롤롤롭롭롯롯료료루루룸룸류류률률르르른른를를름릅리릭린린릴릴림립릿릿링링마막만만많많말말맞맞매매맨맨맵맵맺맺머머먼먼멀멀멈멈멋멋메메며며면면명명모목몬몬못못무무문문물물뮬뮬므므미믹민민밀밀밉밉밍밍및및바바반반받밝밥밥방방배백밴밴뱃뱃버버번번벌벌범법벗벗베벡벤벤벨벨벼벽변변별별병병보복본본볼볼부부분분불불붙붙뷰뷰브브블블비빅빈빈빌빌빙빙빛빛빠빠빨빨사삭산산살살삼삽상상새색샘샘생생샷샷서서선선설설성성세섹센센셀셀셈셉셋셋션션셰셰소속손손솔솔송송쇄쇄쇠쇠쇼쇼수숙순순숨숨숫숫슈슈스스슬슬습습시식신신실실심십싱싱싶싶쌍쌍쓰쓰쓸쓸씬씬아악안안않않알알암압았앙앞앞애액앤앤앨앨앵앵야약양양어어언언얻얼업없었었에에엔엔여역연연열열영영예예오오온온올올옵옵와와완완왑왑왔왔외외왼왼요요용용우우운운울울움웁웃웃워워원원월월웠웠웨웨웹웹위위유유율율으으은은을을음음응응의의이이인인일읽임입있있자작잘잘잠잡장장재재잿잿저적전전절절점접정정제제젠젠젤젤젯젯져져조족존존종종좋좌죄죄주주준준줄줄줍줍중중즈즉즐즐즘즘증증지직진진질질짐집짝짝짧짧째째쪽쪽찌찍차착참참창찾채책챕챕처척천천철철첨첩첫첫청청체체쳐쳐초초총총촬촬최최추축출출춤춥충충춰춰취취츠측치칙칠칠침칩칭칭카카칸칸칼칼캐캐캔캔캘캘캠캡커커컨컨컬컬컴컴케케켓켓켜켜켬켬코코콘콘콜콜콩콩쿼쿼퀀퀀퀴퀴큐큐크크큰큰클클큽큽키키킨킨킬킬킵킵킹킹타타탈탈탐탐태택탠탠탬탭터터턴턴털털텀텁테텍텐텐텔텔템템토토톨톨톱톱통통투투트특튼튼틀틀티틱팀팀팅팅파파팔팔팝팝패패팻팻퍼퍼페펙편편평평포포폴폴폼폼표표푸푸풀풀품품퓨퓨프프픈픈플플피픽핀핀필필핑핑하하한한할할함합핫핫항항해핵했행향향허허헌헌헤헤현현형형호혹혼혼홈홈홍홍화확환환활활황황회획횟횟횡횡효효후후훨훨휠휠휴휴흐흐흔흔희희히히힌힌" }, { "zh-CN", "msyh.ttc", nullptr, "Hiragino Sans GB.ttc", // auto update by update_glyph_ranges.py with pcsx2-qt_zh-CN.ts - u"‘’□□△△○○、。一丁三下不与且且世世丢丢两严个个中中串串为主么义之之乎乎乐乐乘乘也也了了事二于于互互五五亚些交交产产享享亮亮亲亲人人什什仅仅今介仍从他他代以们们件价任任份份仿仿伍伍休休优优会会传传估估伸伸似似但但位住佑佑体体何何余余作作你你佳佳使使例例供供依依侧侧便便俄俄保保信信修修倍倍倒倒借借值值倾倾假假偏偏做做停停储储像像儿儿允允元元充充先光克克免免入入全全公六兰共关关兵兹兼兼内内册再写写冲决况况冻冻净净准准减减几凡凭凭出击函函刀刀刃刃分切列列则创初初删删利利别别到到制刷刹刹前前剪剪副副力力功务动助势势勾勿包包化北匹区十十升升半半协协单单南南占卡卫卫印印即即卸卸历历压压原原去去参参叉及双反发发取变叠叠口古另另只只可台史右号司各各合吉同后向向吗吗否否含含启启呈呈告告员员味味命命和和哈哈响响哪哪唤唤商商善善器器四四回回因因团团围围固固国图圈圈在在地地场场址址均均坏坐块块坛坛垂垂型型域域基基堆堆堪堪塔塔填填增增士士声声处处备备复复外外多多够够大大天太失失头头夹夹奇奇奏奏套套奥奥女女奶奶好好如如始始威威娜娜娱娱婴婴媒媒子子字存它它安安完完宏宏官官定定宝实害害家家容容宽宿寄寄密密富富寸对导导封封射射将将小小少少尔尔尘尘尚尚尝尝就就尺尺尼尾局局层层屏屏展展属属崩崩工左巨巨巫巫差差己已巴巴希希带帧帮帮常常幅幅幕幕干平并并幸幸幻幻序序库底度度延延建建开开异弃弊弊式式引引张张弹强归当录录形形彩彩影影径待很很律律得得循循微微德德心心必忆志志忙忙快快忽忽态态性性怪怪恢恢息息恶恶您您悬悬情情惑惑惩惩惯惯想想愉愉意意感感慢慢戏我或或战战截截戳戳户户所扁扇扇手手才才打打托托执执扩扩扫扬扳扳找找技技抑抑抖抗护护拆拆拉拉拒拒拟拟拥拦择择括括拳拳持挂指指按按挑挑挡挡挪挪振振捕捕损损换换据据捷捷掌掌排排接接控掩描提插插握握搜搜摇摇摔摔摘摘撕撕撤撤播播操擎支支收收改改放放故故效效敌敌敏敏散散数数整整文文斜斜断断斯新方方旁旁旋旋无无日日旧旧时时明明易易星映昨昨是是显显晕晕普普晰晰暂暂暗暗曲曲更更替最有有服服望望期期未未本本术术机机杂权杆杆束束条条来来板板极极果果枪枪柄柄某某染染查查栅栅标栈栏栏校校样根格格框框案案档档桥桥梦梦检检棕棕榜榜槽槽模模横横橙橙次欢欧欧止步死死殊殊段段每每比比毫毫水水求求汇汇污污沙沙没没油油法法波波注注泻泻洋洋洲洲活活流流浅浅测测浏浏浪浪浮浮海海消消淡淡深深混混添添清清港港渲渲游游湖湖湾湾溃溃源源溢溢滑滑满满滤滤演演潜潜澳澳激激火火灯灰灵灵点点烈烈热热焦焦然然煞煞照照爆爆片版牌牌牙牙物物特特状状独独狼狼猎猎猩猩率率王王玛玛玩玩环现班班理理瑞瑞甚甚生生用用由由电电画画畅畅界界留留略略疤疤登登白百的的皇皇监监盖盘目目直直相相省省看看真眠着着瞄瞄矢矢知知短短石石码码破破础础硬硬确确碌碌碎碎磁磁示示神神禁禁离离种种秒秒积称移移程程稍稍稳稳空空突突窗窗立立站站竞竞端端符符第第等等答答筛筛签签简简算算管管类类粉粉粘粘精精糊糊系系素素索索紫紫繁繁纠纠红红级级纳纳纵纵纹纹线线组组细终经经绑绑结结绕绕绘给络绝统统继继绪绪续续维维绿缀缓缓编编缘缘缩缩缺缺网网罗罗罚罚置置美美翻翻考考者者而而耗耗耳耳联联肩肩胖胖能能腊腊自自至致舍舍航航良良色色节节芬芬若若英英范范荐荐荒荒荷荷莱莱获获菜菜萄萄著著葡葡蓝蓝藏藏虑虑虚虚融融行行街街衡衡补补表表被被裁裂装装西西要要覆覆见观规规视视览觉角角解解触触言言警警计计认认让让议议记记许许论论设访证证识识诊诊译译试试话话询询该详语语误误说说请诸读读调调谍谍象象豹豹负负败账质质贴贴费费赖赖赛赛赫赫起起超超越越足足跃跃跟跟跨跨路路跳跳踏踏踪踪身身车车轨轨转转轮软轴轴轻轻载载较较辅辅辑辑输输辨辨边边达达过过迎迎运近返返还这进进连迟述述追追退适逆逆选选逐逐递递通通速造遇遇道道避避那那邻邻部部都都配配醒醒采采释释里量金金针针钟钟钮钮铁铁铺铺链链销锁锐锐错错键锯镜镜长长门门闭问闲闲间间阅阅队队防防阴阵附陆降降限限除除险险随隐隔隔隙隙障障雄雄集集零零雾雾需需震震静静非非靠靠面面韩韩音音页顶项须顿顿预预频频题题颜额颠颠风风饱饱馈馈首首香香马马驱驱驾驾验验骑骑骤骤高高鬼鬼魂魂魔魔麦麦黄黄黑黑默默鼓鼓鼠鼠齐齐齿齿,,::??" + u"‘’□□△△○○、。一丁三下不与且且世世丢丢两严个个中中串串为主么义之之乎乎乐乐乘乘也也了了事二于于互互五五亚些交交产产享享亮亮亲亲人人什什仅仅今介仍从他他代以们们件价任任份份伍伍休休众优会会传传估估伸伸似似但但位住佑佑体体何何余余作作你你佳佳使使例例供供依依侧侧俄俄保保信信修修倍倍倒倒借借值值倾倾假假偏偏做做停停储储像像儿儿允允元元充充先光克克免免入入全全公六兰共关关兵兹兼兼内内册再写写冲决况况冻冻净净准准减减几凡凭凭出击函函刀刀刃刃分切列列则创初初删删利利别别到到制刷刹刹前前剪剪副副力力功务动助势势勾勿包包化北匹区十十升升半半协协单单南南占卡卫卫印印即即卸卸历历压压原原去去参参又及双反发发取变叠叠口古另另只只可台史右号司各各合吉同后向向吗吗否否含含启启呈呈告告员员味味命命和和哈哈响响哪哪唤唤商商善善器器四四回回因因团团围围固固国图圈圈在在地地场场址址均均坏坐块块坛坛垂垂型型域域基基堆堆堪堪塔塔填填增增士士声声处处备备复复外外多多够够大大天太失失头头夹夹奇奇奏奏套套奥奥女女奶奶好好如如始始威威娜娜娱娱婴婴媒媒子子字存它它安安完完宏宏官官定定宝实害害家家容容宽宿寄寄密密富富寸对导导封封射射将将小小少少尔尔尘尘尚尚尝尝就就尺尺尼尾局局层层屏屏展展属属崩崩工左巨巨巫巫差差己已巴巴希希带帧帮帮常常幅幅幕幕干平并并幸幸幻幻序序库底度度延延建建开开异弃弊弊式式引引张张弹强归当录录形形彩彩影影径待很很律律得得循循微微德德心心必忆志志忙忙快快忽忽态态性性怪怪总总恢恢息息恶恶您您悬悬情情惑惑惯惯想想愉愉意意感感慢慢戏我或或战战截截戳戳户户所扁扇扇手手才才打打托托执执扩扩扫扬扳扳找找技技抑抑抖抗护护拆拆拉拉拟拟拥拦择择括括拳拳持挂指指按按挑挑挡挡挪挪振振捕捕损损换换据据捷捷掌掌排排接接控掩描提插插握握搜搜摇摇摔摔摘摘撕撕撤撤播播操擎支支收收改改放放故故效效敌敌敏敏散散数数整整文文斜斜断断斯新方方旁旁旋旋无无既既日日旧旧时时明明易易星映昨昨是是显显晕晕普普晰晰暂暂暗暗曲曲更更替最有有服服望望期期未未本本术术机机杂权杆杆束束条条来来板板极极果果枪枪柄柄某某染染查查栅栅标栈栏栏树树校校样根格格框框案案档档桥桥桩桩梦梦检检棕棕榜榜槽槽模模横横橙橙次欢欧欧止步死死殊殊段段每每比比毫毫水水求求汇汇污污沙沙没没油油法法波波注注泻泻洋洋洲洲活活流流浅浅测测浏浏浪浪浮浮海海消消淡淡深深混混添添清清港港渲渲游游湖湖湾湾溃溃源源溢溢滑滑满满滤滤演演潜潜澳澳激激火火灯灰灵灵点点烈烈热热焦焦然然煞煞照照爆爆片版牌牌牙牙物物特特状状独独狼狼猩猩率率王王玛玛玩玩环现班班球球理理瑕瑕瑞瑞甚甚生生用用由由电电画画畅畅界界留留略略疤疤疵疵登登白百的的皇皇监监盖盘目目直直相相省省看看真眠着着瞄瞄矢矢知知短短石石码码破破础础硬硬确确碌碌碎碎磁磁示示神神禁禁离离种种秒秒称称移移程程稍稍稳稳空空突突窗窗立立站站竞竞端端符符第第等等答答筛筛签签简简算算管管类类粉粉粘粘精精糊糊系系素素索索紫紫纠纠红红级级纳纳纵纵纹纹线线组组细终经经绑绑结结绕绕绘给络络统统继继绪绪续续维维绿缀缓缓编编缘缘缩缩缺缺网网罗罗置置美美翻翻考考者者而而耗耗耳耳联联肩肩胖胖能能腊腊臂臂自自至致舍舍航航良良色色节节芬芬若若英英范范荐荐荒荒荷荷莱莱获获菜菜萄萄著著葡葡蓝蓝藏藏虑虑虚虚融融行行街街衡衡补补表表被被裁裂装装西西要要覆覆见观规规视视览觉角角解解触触言言警警计计认认让让议议记记许许论论设访证证识识诊诊译译试试话话询询该详语语误误说说请诸读读调调象象负负败账质质贴贴费费赖赖赛赛赫赫起起超超越越足足跃跃跟跟跤跤跨跨路路跳跳踏踏踪踪身身车车轨轨转转轮软轴轴轻轻载载较较辅辅辑辑输输辨辨边边达达过过迎迎运近返返还这进远连迟述述追追退适逆逆选选逐逐递递通通速速遇遇道道避避那那邻邻部部都都配配醒醒采采释释里量金金针针钟钟钮钮铁铁铺铺链链销锁锐锐错错键锯镜镜长长门门闭问闲闲间间阅阅队队防防阴阵附陆降降限限除除险险随隐隔隔隙隙障障雄雄集集零零雾雾需需震震静静非非靠靠面面韩韩音音页顶项须顿顿预预频频题题颜额颠颠颤颤风风饱饱馈馈首首香香马马驱驱验验骑骑骤骤高高鬼鬼魂魂魔魔麦麦黄黄黑黑默默鼓鼓鼠鼠齐齐齿齿️️,,::??" }, { "zh-TW", "msyh.ttc", nullptr, "Hiragino Sans GB.ttc", // auto update by update_glyph_ranges.py with pcsx2-qt_zh-TW.ts - u"□□△△○○、。『』一丁三下不不且且世世丟丟並並中中串串主主之之乎乎乘乙也也乾乾了了事二于于互互五五些些亞亞交交享享亮亮人人什什今介仍仍他他代以件件任任份份伍伍休休估估伸伸伺伺似似但佇位住佑佑佔何作作你你佳佳併併使使來來例例供供依依便便係係俄俄保保修修個個倍倍們倒借借值值假假偏偏做做停停側偵備備傳傳傾傾僅僅像像價價儘儘優優儲儲允允元元充充先光克克免免兒兒入入內兩公六共共兵典冊冊再再凍凍凡凡出出函函刀刀刃刃分切列列初初別別利刪到到制制則則前剎剛剛剪剪副副力力功加助助動動務務勢勢勾勿包包化北匯匯匹匹區十升升半半協協南南卡卡印印即即原原去去參參叉及反反取受口古另另只叫可可史右司司各各合吉同后向向否否含含呈呈告告味味呼命和和哈哈員員哪哪商商問問啓啓啟啟善善喚喚單單嗎嗎嘉嘉嘗嘗器器嚮嚮嚴嚴四四回回因因固固圈圈國國圍圍圖圖團團在在地地址址均均垂垂型型域埠執執基基堆堆堪堪場場塊塊塔塔填填塵塵增增壇壇壓壓壞壞士士外外多多夠夠夢夢大大天太失失夾夾奇奇奏奏套套奧奧女女奶奶好好如如始始威威娛娜媒媒嬰嬰子子字存它它安安完完宏宏官官定定害害家家容容宿宿密密富富實實寫寬寶寶寸寸封封射射將專對小少少尚尚就就尺尺尼尼尾尾屏屏展展層層屬屬崩崩工左巨巨巫巫差差己已巴巴希希帶帶常常幀幀幅幅幕幕幫幫平平幸幸幻幻幾幾序序底底度座庫庫延延建建弊弊式式引引張張強強彈彈彙彙形形彩彩影影待待很很律後徑徑得得從從復循微微德德心心必必忙忙快快忽忽性性怪怪恢恢息息您您情情惑惑惡惡想想愉愉意意感感態態慢慣慮慮憑憑憶憶應應懲懲懸懸成我或或截截戰戰戲戳戶戶所扁扇扇手手才才打打扳扳找找技技抑抑抖抗拆拆拉拉拒拒括括拳拳持持指指按按挑挑挪挪振振捕捕捨捨捷捷掃掃掌掌排排掛掛採採接接控掩描提插插揚換握握援援損損搖搖搜搜摔摔摘摘撕撕撤撤播播擁擁擇擇擊擋操擎據據擬擬擴擴擷擷攔攔支支收收改改放放故故效效敏敏敗敗整敵數數文文料料斜斜斯新斷斷方方於於旁旁旋旋日日明明易易星映昨昨是是時時普普晰晰暈暈暗暗暢暢暫暫曲曲更更替最會會有有服服望望期期未未本本束束板板析析果果柄柄某某染染查查柵柵校校核根格格框框案案桿桿條條棄棄棕棕極極榜榜槍槍槽槽樂樂標標模模樣樣橋橋橙橙機機橫橫檔檔檢檢檯檯欄欄權權次次歐歐歡步歷歸死死殊殊段段每每比比毫毫水水求求污污決決沒沒沙沙油油況況法法波波注注洋洋洲洲活活流流浪浪浮浮海海消消淡淡淨淨深深混混淺淺清清減減測測港港渲渲游游湊湊湖湖源源準準溢溢滑滑滿滿演演潛潛潰潰澳澳激激濾濾瀉瀉瀏瀏灣灣火火灰灰為為烈烈無無焦焦然然煞煞照照熱熱燈燈爆爆爲爲爾爾片版牌牌牙牙物物特特狀狀狼狼猩猩獨獨獲獲獵獵率率王王玩玩班班現現理理瑞瑞瑪瑪環環甚甚生生產產用用由由界界留留略略畫畫異異當當疊疊疤疤登百的的皇皇盜盜盡盡監盤目目直直相相省省看看真眠瞄瞄知知短短石石破破硬硬碌碌碎碎碟碟確確碼碼磁磁礎礎示示神神禁禁秒秒移移程程稍稍種種稱稱積積穩穩空空突突窗窗立立站站端端競競符符第第等等答答算算管管節節範範篩篩簡簡簿簿籤籤粉粉精精糊糊系系糾糾紅紅紋紋納納級級素素索索紫紫細細終終組組結結絕絕給給統統經經綠綠維維網網綴綴緒緒線線緣緣編緩縮縮縱縱繁繁織織繞繞繪繫繼繼續續缺缺置置罰罰羅羅美美義義翻翻考考者者而而耗耗耳耳聯聯聲聲肩肩胖胖能能臘臘自自至致臺臺與與舊舊舍舍航航良良色色芬芬芽芽若若英英茲茲荒荒荷荷菜菜萄萄萊萊著著葡葡蓋蓋薦薦藍藍藏藏蘭蘭處處虛虛號號融融螢螢行行術術街街衛衛衝衝衡衡表表被被裁裂補裝裡裡製製複複西西要要覆覆見見規規視視親親覺覺覽覽觀觀角角解解觸觸言言計計訊訊託記訪訪設設許許診註詢詢試試話詳誌認語語誤誤說說調調請請論論諜諜諸諸證證識識警警譯議護護讀讀變變讓讓象象豹豹負負費貼資資賓賓質質賬賬賴賴賽賽赫赫起起超超越越足足跟跟跨跨路路跳跳踏踏蹤蹤躍躍身身車車軌軌軟軟軸軸較較載載輔輕輪輪輯輯輸輸轉轉迎迎近近返返述述迴迴追追退送逆逆逐逐這通速造連連進進遇遇遊運過過道達遞遞適適遲遲選選避避還還邊邊那那部部都都鄰鄰配配醒醒重量金金針針鈕鈕銳銳銷銷鋪鋪鋸鋸錄錄錯錯鍵鍵鎖鎖鏈鏈鏡鏡鐘鐘鐵鐵長長門門閉閉開開閑閑間間閘閘閱閱關關防防附附降降限限陣除陰陰陸陸隊隊隔隔隙隙際障隨隨險險隱隱雄雄集集雙雙雜雜離離零零電電需需震震霧霧靈靈靜靜非非靠靠面面韓韓音音響響頁頂項順須須預預頓頓頭頭頻頻題額顏顏顛顛類類顯顯風風飽飽餘餘饋饋首首香香馬馬駕駕騎騎驅驅驗驗驟驟體體高高髮髮鬼鬼魂魂魔魔麥麥麼麼黃黃黑黑點點鼓鼓鼠鼠齊齊齒齒,,::??" + u"□□△△○○、。『』一丁三下不不且且世世丟丟並並中中串串主主之之乎乎乘乙也也乾乾了了事二于于互互五五些些亞亞交交享享亮亮人人什什今介仍仍他他代以件件任任份份休休估估伸伸伺伺似似但佇位住佔何作作你你佳佳併併使使來來例例供供依依係係俄俄保保修修個個倍倍們倒借借值值假假偏偏做做停停側偵備備傳傳傾傾僅僅像像價價儘儘優優儲儲允允元元充充先光克克免免兒兒入入內兩公六共共兵典冊冊再再凍凍出出函函刀刀分切列列初初別別利刪到到制制則則前剎剪剪副副力力功加助助動動務務勢勢勾勾包包化化匯匯匹匹區十升升半半協協南南卡卡印印即即原原去去參參叉及反反取受口口另另只叫可可史右司司各各合吉同后向向否否含含呈呈告告味味呼命和和哈哈員員哪哪商商問問啓啓啟啟善善喚喚單單嗎嗎嘉嘉嘗嘗器器嚮嚮嚴嚴四四回回因因固固圈圈國國圍圍圖圖團團在在地地址址均均垂垂型型域埠執執基基堆堆堪堪場場塊塊塔塔填填塵塵增增壇壇壓壓壞壞士士外外多多夠夠大大天太失失夾夾奇奇奏奏套套奧奧奶奶好好如如始始威威娛娜媒媒嬰嬰子子字存它它安安完完宏宏官官定定害害家家容容宿宿密密富富察察實實寫寬寶寶寸寸封封射射將專對小少少尚尚就就尺尺尼尼尾尾屏屏展展層層屬屬崩崩工左巨巨差差己已巴巴希希帳帳帶帶常常幀幀幅幅幕幕幫幫平平幸幸幾幾序序底底度座庫庫延延建建弊弊式式引引張張強強彈彈彙彙形形彩彩影影待待很很律後徑徑得得從從復循微微德德心心必必快快忽忽性性怪怪恢恢息息您您情情想想愉愉意意感感態態慢慣慮慮憑憑憶憶應應成我或或截截戰戰戲戳戶戶所扁扇扇手手才才打打扳扳找找技技抑抑抖抗拆拆拉拉括括拳拳持持指指按按挑挑挪挪振振捕捕捨捨捷捷掃掃排排掛掛採採接接控掩描提插插揚換援援損損搖搖搜搜撕撕撤撤播播擁擁擇擇擊擋操擎據據擬擬擴擴擷擷攔攔支支收收改改放放故故效效敏敏敗敗整敵數數文文料料斜斜斯新斷斷方方於於旁旁旋旋日日明明易易星映昨昨是是時時普普晰晰暈暈暗暗暢暢暫暫曲曲更更替最會會有有服服望望期期未未本本束束板板析析果果柄柄某某染染查查柵柵校校核根格格框框案案桿桿條條棄棄棕棕極極榜榜槍槍槽槽樂樂標標模模樣樣橋橋橙橙機機橫橫檔檔檢檢檯檯欄欄權權次次歐歐歡步歷歸死死殊殊段段每每比比毫毫水水求求污污決決沒沒沙沙油油況況法法波波注注洲洲活活流流浮浮消消淡淡淨淨深深混混淺淺清清減減測測港港渲渲游游湊湊湖湖源源準準溢溢滑滑滿滿演演潛潛潰潰澳澳激激濾濾瀉瀉瀏瀏灣灣灰灰為為烈烈無無焦焦然然照照熱熱爲爲爾爾片版牌牌牙牙特特狀狀猩猩獨獨獲獲率率王王玩玩班班現現理理瑞瑞瑪瑪環環甚甚生生產產用用由由界界留留略略畫畫異異當當疊疊登百的的皇皇盡盡監盤目目直直相相省省看看真眠瞄瞄知知短短石石破破硬硬碎碎碟碟確確碼碼磁磁礎礎示示禁禁秒秒移移程程稍稍種種稱稱穩穩空空突突窗窗立立站站端端符符第第等等答答算算管管節節範範篩篩簡簡簿簿籤籤粉粉精精糊糊系系糾糾紅紅紋紋納納級級素素索索紫紫細細終終組組結結給給統統經經綠綠維維網網綴綴緒緒線線緣緣編緩縮縮縱縱織織繞繞繪繫繼繼續續缺缺置置羅羅美美義義翻翻考考者者而而耗耗耳耳聯聯聲聲肩肩胖胖能能臘臘自自至致臺臺與與舊舊舍舍航航良良色色芬芬芽芽若若英英茲茲荒荒荷荷菜菜萄萄萊萊著著葡葡蓋蓋薦薦藍藍藏藏蘭蘭處處虛虛號號融融螢螢行行衝衝衡衡表表被被裁裂補裝裡裡製製複複西西要要覆覆見見規規視視親親覺覺覽覽觀觀角角解解觸觸言言計計訊訊託記訪訪設設許許診註詢詢試試話詳誌認語語誤誤說說調調請請論論諸諸證證識識警警譯議護護讀讀變變讓讓象象負負費貼資資賓賓質質賬賬賴賴赫赫起起超超越越足足跟跟跨跨路路跳跳踏踏蹤蹤躍躍身身車車軌軌軟軟軸軸較較載載輔輕輪輪輯輯輸輸轉轉迎迎近近返返述述迴迴追追退送逆逆逐逐這通速速連連進進遇遇遊運過過道達遞遞適適遲遲選選避避還還邊邊那那部部都都鄰鄰配配醒醒重量金金針針鈕鈕銳銳銷銷鋪鋪鋸鋸錄錄錯錯鍵鍵鎖鎖鏈鏈鏡鏡鐘鐘鐵鐵長長門門閉閉開開閑閑間間閘閘閱閱關關防防附附降降限限陣除陰陰陸陸隊隊隔隔隙隙際障隨隨險險隱隱雄雄集集雙雙雜雜離離零零電電需需震震霧霧靈靈靜靜非非靠靠面面韓韓音音響響頂頂項順須須預預頓頓頭頭頻頻題額顏顏顛顛類類顯顯風風飽飽餘餘饋饋首首香香馬馬驅驅驗驗驟驟體體高高髮髮鬼鬼魂魂魔魔麥麥麼麼黃黃黑黑點點鼓鼓鼠鼠齊齊齒齒️️,,::??" }, }; // clang-format on diff --git a/pcsx2-qt/pcsx2-qt.vcxproj b/pcsx2-qt/pcsx2-qt.vcxproj index c00807db68..5b1578d2b1 100644 --- a/pcsx2-qt/pcsx2-qt.vcxproj +++ b/pcsx2-qt/pcsx2-qt.vcxproj @@ -419,9 +419,10 @@ Document + - + \ No newline at end of file diff --git a/pcsx2-qt/pcsx2-qt.vcxproj.filters b/pcsx2-qt/pcsx2-qt.vcxproj.filters index 7665f894c9..0574cf5a6c 100644 --- a/pcsx2-qt/pcsx2-qt.vcxproj.filters +++ b/pcsx2-qt/pcsx2-qt.vcxproj.filters @@ -638,6 +638,9 @@ Settings + + Settings + diff --git a/pcsx2-qt/resources/icons/black/svg/Popn-line.svg b/pcsx2-qt/resources/icons/black/svg/Popn-line.svg new file mode 100644 index 0000000000..d0a40f7d47 --- /dev/null +++ b/pcsx2-qt/resources/icons/black/svg/Popn-line.svg @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/pcsx2-qt/resources/icons/white/svg/Popn-line.svg b/pcsx2-qt/resources/icons/white/svg/Popn-line.svg new file mode 100644 index 0000000000..8b613ea02e --- /dev/null +++ b/pcsx2-qt/resources/icons/white/svg/Popn-line.svg @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/pcsx2-qt/resources/images/Popn.svg b/pcsx2-qt/resources/images/Popn.svg new file mode 100644 index 0000000000..d2be19357a --- /dev/null +++ b/pcsx2-qt/resources/images/Popn.svg @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT + START + + + \ No newline at end of file diff --git a/pcsx2-qt/resources/resources.qrc b/pcsx2-qt/resources/resources.qrc index 50261c8ae5..56ab17faba 100644 --- a/pcsx2-qt/resources/resources.qrc +++ b/pcsx2-qt/resources/resources.qrc @@ -68,6 +68,7 @@ icons/black/svg/pause-line.svg icons/black/svg/play-line.svg icons/black/svg/plus-line.svg + icons/black/svg/Popn-line.svg icons/black/svg/price-tag-3-line.svg icons/black/svg/printer-line.svg icons/black/svg/refresh-line.svg @@ -161,6 +162,7 @@ icons/white/svg/pause-line.svg icons/white/svg/play-line.svg icons/white/svg/plus-line.svg + icons/white/svg/Popn-line.svg icons/white/svg/price-tag-3-line.svg icons/white/svg/printer-line.svg icons/white/svg/refresh-line.svg @@ -186,5 +188,6 @@ images/DualShock_2.svg images/GT_Force.svg images/Guitar.svg + images/Popn.svg diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 21b8ad1c02..475a3d3cf9 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -442,6 +442,7 @@ set(pcsx2PADSources SIO/Pad/PadBase.cpp SIO/Pad/PadDualshock2.cpp SIO/Pad/PadGuitar.cpp + SIO/Pad/PadPopn.cpp SIO/Pad/PadNotConnected.cpp ) set(pcsx2PADHeaders @@ -449,6 +450,7 @@ set(pcsx2PADHeaders SIO/Pad/PadBase.h SIO/Pad/PadDualshock2.h SIO/Pad/PadGuitar.h + SIO/Pad/PadPopn.h SIO/Pad/PadNotConnected.h SIO/Pad/PadTypes.h ) diff --git a/pcsx2/ImGui/ImGuiManager.cpp b/pcsx2/ImGui/ImGuiManager.cpp index 12caddc46a..35774925a6 100644 --- a/pcsx2/ImGui/ImGuiManager.cpp +++ b/pcsx2/ImGui/ImGuiManager.cpp @@ -485,7 +485,7 @@ bool ImGuiManager::AddIconFonts(float size) { // clang-format off static constexpr ImWchar range_fa[] = { 0xf002,0xf002,0xf005,0xf005,0xf007,0xf007,0xf00c,0xf00e,0xf011,0xf011,0xf013,0xf013,0xf017,0xf017,0xf019,0xf019,0xf01c,0xf01c,0xf021,0xf021,0xf023,0xf023,0xf025,0xf025,0xf027,0xf028,0xf02e,0xf02e,0xf030,0xf030,0xf03a,0xf03a,0xf03d,0xf03d,0xf04a,0xf04c,0xf04e,0xf04e,0xf050,0xf050,0xf052,0xf052,0xf059,0xf059,0xf05e,0xf05e,0xf063,0xf063,0xf065,0xf065,0xf067,0xf067,0xf06a,0xf06a,0xf071,0xf071,0xf077,0xf078,0xf07b,0xf07c,0xf084,0xf085,0xf091,0xf091,0xf0ac,0xf0ad,0xf0b0,0xf0b0,0xf0c5,0xf0c5,0xf0c7,0xf0c9,0xf0cb,0xf0cb,0xf0d0,0xf0d0,0xf0dc,0xf0dc,0xf0e2,0xf0e2,0xf0eb,0xf0eb,0xf0f1,0xf0f1,0xf0f3,0xf0f3,0xf0fe,0xf0fe,0xf110,0xf110,0xf119,0xf119,0xf11b,0xf11c,0xf121,0xf121,0xf133,0xf133,0xf140,0xf140,0xf144,0xf144,0xf14a,0xf14a,0xf15b,0xf15b,0xf15d,0xf15d,0xf188,0xf188,0xf191,0xf192,0xf1c9,0xf1c9,0xf1dd,0xf1de,0xf1e6,0xf1e6,0xf1ea,0xf1eb,0xf1f8,0xf1f8,0xf1fc,0xf1fc,0xf242,0xf242,0xf245,0xf245,0xf26c,0xf26c,0xf279,0xf279,0xf2d0,0xf2d0,0xf2db,0xf2db,0xf2f2,0xf2f2,0xf2f5,0xf2f5,0xf302,0xf302,0xf3c1,0xf3c1,0xf3fd,0xf3fd,0xf410,0xf410,0xf466,0xf466,0xf500,0xf500,0xf517,0xf517,0xf51f,0xf51f,0xf543,0xf543,0xf545,0xf545,0xf547,0xf548,0xf552,0xf552,0xf56d,0xf56d,0xf5a2,0xf5a2,0xf5e7,0xf5e7,0xf65d,0xf65e,0xf6a9,0xf6a9,0xf756,0xf756,0xf7c2,0xf7c2,0xf807,0xf807,0xf815,0xf815,0xf818,0xf818,0xf84c,0xf84c,0xf8cc,0xf8cc,0xf8d9,0xf8d9,0x0,0x0 }; - static constexpr ImWchar range_pf[] = { 0x2198,0x2199,0x219e,0x21a1,0x21b0,0x21b3,0x21ba,0x21c3,0x21d0,0x21d4,0x21dc,0x21dd,0x21e0,0x21e3,0x21f3,0x21f3,0x21f7,0x21f8,0x21fa,0x21fb,0x227a,0x227d,0x23b2,0x23b4,0x23f4,0x23f7,0x2427,0x243a,0x243c,0x243c,0x2443,0x2443,0x2460,0x246b,0x24f5,0x24fd,0x24ff,0x24ff,0x278a,0x278e,0xe001,0xe001,0xff21,0xff3a,0x0,0x0 }; + static constexpr ImWchar range_pf[] = { 0x2198,0x2199,0x219e,0x21a1,0x21b0,0x21b3,0x21ba,0x21c3,0x21d0,0x21d4,0x21dc,0x21dd,0x21e0,0x21e3,0x21f3,0x21f3,0x21f7,0x21f8,0x21fa,0x21fb,0x227a,0x227d,0x22c0,0x22c8,0x23b2,0x23b4,0x23f4,0x23f7,0x2427,0x243a,0x243c,0x243c,0x2443,0x2443,0x2460,0x246b,0x24f5,0x24fd,0x24ff,0x24ff,0x278a,0x278e,0xe001,0xe001,0xff21,0xff3a,0x0,0x0 }; // clang-format on { diff --git a/pcsx2/SIO/Pad/Pad.cpp b/pcsx2/SIO/Pad/Pad.cpp index eed6af4bab..77c9ac8428 100644 --- a/pcsx2/SIO/Pad/Pad.cpp +++ b/pcsx2/SIO/Pad/Pad.cpp @@ -6,6 +6,7 @@ #include "SIO/Pad/Pad.h" #include "SIO/Pad/PadDualshock2.h" #include "SIO/Pad/PadGuitar.h" +#include "SIO/Pad/PadPopn.h" #include "SIO/Pad/PadNotConnected.h" #include "SIO/Sio.h" @@ -243,6 +244,7 @@ static const Pad::ControllerInfo* s_controller_info[] = { &PadNotConnected::ControllerInfo, &PadDualshock2::ControllerInfo, &PadGuitar::ControllerInfo, + &PadPopn::ControllerInfo, }; const Pad::ControllerInfo* Pad::GetControllerInfo(Pad::ControllerType type) @@ -480,6 +482,9 @@ PadBase* Pad::CreatePad(u8 unifiedSlot, ControllerType controllerType, size_t ej case ControllerType::Guitar: s_controllers[unifiedSlot] = std::make_unique(unifiedSlot, ejectTicks); break; + case ControllerType::Popn: + s_controllers[unifiedSlot] = std::make_unique(unifiedSlot, ejectTicks); + break; default: s_controllers[unifiedSlot] = std::make_unique(unifiedSlot, ejectTicks); break; diff --git a/pcsx2/SIO/Pad/PadPopn.cpp b/pcsx2/SIO/Pad/PadPopn.cpp new file mode 100644 index 0000000000..f3b0847ea6 --- /dev/null +++ b/pcsx2/SIO/Pad/PadPopn.cpp @@ -0,0 +1,556 @@ +// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team +// SPDX-License-Identifier: LGPL-3.0+ + +#include "SIO/Pad/PadPopn.h" +#include "SIO/Pad/Pad.h" +#include "SIO/Sio.h" +#include "SIO/Sio0.h" + +#include "Common.h" +#include "Input/InputManager.h" +#include "Host.h" + +#include "IconsPromptFont.h" + +static const InputBindingInfo s_bindings[] = { + // clang-format off + {"YellowL", TRANSLATE_NOOP("Pad", "Yellow (Left)"), ICON_PF_POPN_YELLOW_LEFT, InputBindingInfo::Type::Button, PadPopn::Inputs::PAD_YELLOW_LEFT, GenericInputBinding::Circle}, + {"YellowR", TRANSLATE_NOOP("Pad", "Yellow (Right)"), ICON_PF_POPN_YELLOW_RIGHT, InputBindingInfo::Type::Button, PadPopn::Inputs::PAD_YELLOW_RIGHT, GenericInputBinding::DPadUp}, + {"BlueL", TRANSLATE_NOOP("Pad", "Blue (Left)"), ICON_PF_POPN_BLUE_LEFT, InputBindingInfo::Type::Button, PadPopn::Inputs::PAD_BLUE_LEFT, GenericInputBinding::Cross}, + {"BlueR", TRANSLATE_NOOP("Pad", "Blue (Right)"), ICON_PF_POPN_BLUE_RIGHT, InputBindingInfo::Type::Button, PadPopn::Inputs::PAD_BLUE_RIGHT, GenericInputBinding::Square}, + {"WhiteL", TRANSLATE_NOOP("Pad", "White (Left)"), ICON_PF_POPN_WHITE_LEFT, InputBindingInfo::Type::Button, PadPopn::Inputs::PAD_WHITE_LEFT, GenericInputBinding::Triangle}, + {"WhiteR", TRANSLATE_NOOP("Pad", "White (Right)"), ICON_PF_POPN_WHITE_RIGHT, InputBindingInfo::Type::Button, PadPopn::Inputs::PAD_WHITE_RIGHT, GenericInputBinding::L2}, + {"GreenL", TRANSLATE_NOOP("Pad", "Green (Left)"), ICON_PF_POPN_GREEN_LEFT, InputBindingInfo::Type::Button, PadPopn::Inputs::PAD_GREEN_LEFT, GenericInputBinding::R1}, + {"GreenR", TRANSLATE_NOOP("Pad", "Green (Right)"), ICON_PF_POPN_GREEN_RIGHT, InputBindingInfo::Type::Button, PadPopn::Inputs::PAD_GREEN_RIGHT, GenericInputBinding::R2}, + {"Red", TRANSLATE_NOOP("Pad", "Red"), ICON_PF_POPN_RED, InputBindingInfo::Type::Button, PadPopn::Inputs::PAD_RED, GenericInputBinding::L1}, + {"Start", TRANSLATE_NOOP("Pad", "Start"), ICON_PF_START, InputBindingInfo::Type::Button, PadPopn::Inputs::PAD_START, GenericInputBinding::Start}, + {"Select", TRANSLATE_NOOP("Pad", "Select"), ICON_PF_SELECT_SHARE, InputBindingInfo::Type::Button, PadPopn::Inputs::PAD_SELECT, GenericInputBinding::Select}, + // clang-format on +}; + +const Pad::ControllerInfo PadPopn::ControllerInfo = {Pad::ControllerType::Popn, "Popn", + TRANSLATE_NOOP("Pad", "Pop'n Music"), ICON_PF_GAMEPAD, s_bindings, {}, Pad::VibrationCapabilities::NoVibration}; + +void PadPopn::ConfigLog() +{ + const auto [port, slot] = sioConvertPadToPortAndSlot(unifiedSlot); + + std::string_view pressureStr; + + switch (this->responseBytes) + { + case static_cast(Pad::ResponseBytes::DUALSHOCK2): + pressureStr = "D+A+P"; + break; + case static_cast(Pad::ResponseBytes::ANALOG): + pressureStr = "D+A"; + break; + case static_cast(Pad::ResponseBytes::DIGITAL): + pressureStr = "D"; + break; + default: + pressureStr = "U"; + break; + } + + // AL: Analog Light (is it turned on right now) + // AB: Analog Button (is it useable or is it locked in its current state) + // RB: Response Bytes (what data is included in the controller's responses - D = Digital, A = Analog, P = Pressure) + Console.WriteLn(fmt::format("[Pad] Pop'n Config Finished - P{0}/S{1} - AL: {2} - AB: {3} - RB: {4} (0x{5:08X})", + port + 1, + slot + 1, + (this->analogLight ? "On" : "Off"), + (this->analogLocked ? "Locked" : "Usable"), + pressureStr, + this->responseBytes)); +} + +u8 PadPopn::Mystery(u8 commandByte) +{ + switch (commandBytesReceived) + { + case 5: + return 0x02; + case 8: + return 0x5a; + default: + return 0x00; + } +} + +u8 PadPopn::ButtonQuery(u8 commandByte) +{ + switch (this->currentMode) + { + case Pad::Mode::DUALSHOCK2: + case Pad::Mode::ANALOG: + switch (commandBytesReceived) + { + case 3: + case 4: + return 0xff; + case 5: + return 0x03; + case 8: + g_Sio0.SetAcknowledge(false); + return 0x5a; + default: + return 0x00; + } + default: + switch (commandBytesReceived) + { + case 8: + g_Sio0.SetAcknowledge(false); + return 0x00; + default: + return 0x00; + } + } +} + +u8 PadPopn::Poll(u8 commandByte) +{ + const u32 buttons = GetButtons(); + u8 largeMotor = 0x00; + u8 smallMotor = 0x00; + + switch (commandBytesReceived) + { + case 3: + return (buttons >> 8) & 0xff; + case 4: + // PS1 mode: If the controller is still in digital mode, it is time to stop acknowledging. + if (this->currentMode == Pad::Mode::DIGITAL) + { + g_Sio0.SetAcknowledge(false); + } + + return buttons & 0xff; + } + + Console.Warning("%s(%02X) Did not reach a valid return path! Returning zero as a failsafe!", __FUNCTION__, commandByte); + return 0x00; +} + +u8 PadPopn::Config(u8 commandByte) +{ + if (commandBytesReceived == 3) + { + if (commandByte) + { + if (!this->isInConfig) + { + this->isInConfig = true; + } + else + { + Console.Warning("%s(%02X) Unexpected enter while already in config mode", __FUNCTION__, commandByte); + } + } + else + { + if (this->isInConfig) + { + this->isInConfig = false; + this->ConfigLog(); + } + else + { + Console.Warning("%s(%02X) Unexpected exit while not in config mode", __FUNCTION__, commandByte); + } + } + } + + // PS1 mode: Config mode would have been triggered by a prior byte in this command sequence; + // if we are now in config mode, check the current mode and if this is the last byte. If so, + // don't acknowledge. + if (this->isInConfig) + { + if ((this->currentMode == Pad::Mode::DIGITAL && this->commandBytesReceived == 4) || (this->currentMode == Pad::Mode::ANALOG && this->commandBytesReceived == 8)) + { + g_Sio0.SetAcknowledge(false); + } + } + + return 0x00; +} + +// Changes the mode of the controller between digital and analog, and adjusts the analog LED accordingly. +u8 PadPopn::ModeSwitch(u8 commandByte) +{ + switch (commandBytesReceived) + { + case 3: + this->analogLight = commandByte; + + if (this->analogLight) + { + this->currentMode = Pad::Mode::ANALOG; + } + else + { + this->currentMode = Pad::Mode::DIGITAL; + } + + break; + case 4: + this->analogLocked = (commandByte == 0x03); + break; + case 8: + g_Sio0.SetAcknowledge(false); + break; + default: + break; + } + + return 0x00; +} + +u8 PadPopn::StatusInfo(u8 commandByte) +{ + switch (commandBytesReceived) + { + case 3: + return static_cast(Pad::PhysicalType::STANDARD); + case 4: + return 0x02; + case 5: + return this->analogLight; + case 6: + return 0x02; + case 7: + return 0x01; + case 8: + g_Sio0.SetAcknowledge(false); + return 0x00; + default: + return 0x00; + } +} + +u8 PadPopn::Constant1(u8 commandByte) +{ + switch (commandBytesReceived) + { + case 3: + commandStage = commandByte != 0; + return 0x00; + case 5: + return 0x01; + case 6: + if (commandStage) + { + return 0x01; + } + else + { + return 0x02; + } + case 7: + if (commandStage) + { + return 0x01; + } + else + { + return 0x00; + } + case 8: + g_Sio0.SetAcknowledge(false); + return (commandStage ? 0x14 : 0x0a); + default: + return 0x00; + } +} + +u8 PadPopn::Constant2(u8 commandByte) +{ + switch (commandBytesReceived) + { + case 5: + return 0x02; + case 7: + return 0x01; + case 8: + g_Sio0.SetAcknowledge(false); + return 0x00; + default: + return 0x00; + } +} + +u8 PadPopn::Constant3(u8 commandByte) +{ + switch (commandBytesReceived) + { + case 3: + commandStage = (commandByte != 0); + return 0x00; + case 6: + if (commandStage) + { + return 0x07; + } + else + { + return 0x04; + } + case 8: + g_Sio0.SetAcknowledge(false); + return 0x00; + default: + return 0x00; + } +} + +// Set which byte of the poll command will correspond to a motor's power level. +// In all known cases, games never rearrange the motors. We've hard coded pad polls +// to always use the first vibration byte as small motor, and the second as big motor. +// There is no reason to rearrange these. Games never rearrange these. If someone does +// try to rearrange these, they should suffer. +// +// The return values for cases 3 and 4 are just to notify the pad module of what the mapping was, prior to this command. +u8 PadPopn::VibrationMap(u8 commandByte) +{ + return 0xff; +} + +u8 PadPopn::ResponseBytes(u8 commandByte) +{ + switch (commandBytesReceived) + { + case 3: + this->responseBytes = commandByte; + return 0x00; + case 4: + this->responseBytes |= (commandByte << 8); + return 0x00; + case 5: + this->responseBytes |= (commandByte << 16); + + switch (static_cast(this->responseBytes)) + { + case Pad::ResponseBytes::ANALOG: + this->analogLight = true; + this->currentMode = Pad::Mode::ANALOG; + break; + case Pad::ResponseBytes::DUALSHOCK2: + this->analogLight = true; + this->currentMode = Pad::Mode::DUALSHOCK2; + break; + default: + this->analogLight = false; + this->currentMode = Pad::Mode::DIGITAL; + break; + } + + return 0x00; + case 8: + return 0x5a; + default: + return 0x00; + } +} + +PadPopn::PadPopn(u8 unifiedSlot, size_t ejectTicks) + : PadBase(unifiedSlot, ejectTicks) +{ + currentMode = Pad::Mode::DIGITAL; +} + +PadPopn::~PadPopn() = default; + +Pad::ControllerType PadPopn::GetType() const +{ + return Pad::ControllerType::Popn; +} + +const Pad::ControllerInfo& PadPopn::GetInfo() const +{ + return ControllerInfo; +} + +void PadPopn::Set(u32 index, float value) +{ + if (index > Inputs::LENGTH) + { + return; + } + + // Since we reordered the buttons for better UI, we need to remap them here. + static constexpr std::array bitmaskMapping = {{ + 5, // PAD_YELLOW_LEFT + 12, // PAD_YELLOW_RIGHT + 6, // PAD_BLUE_LEFT + 7, // PAD_BLUE_RIGHT + 4, // PAD_WHITE_LEFT + 0, // PAD_WHITE_RIGHT + 3, // PAD_GREEN_LEFT + 1, // PAD_GREEN_RIGHT + 2, // PAD_RED + 11, // PAD_START + 8, // PAD_SELECT + }}; + + this->rawInputs[index] = static_cast(std::clamp(value * 255.0f, 0.0f, 255.0f)); + + if (value) + { + this->buttons &= ~(1u << bitmaskMapping[index]); + } + else + { + this->buttons |= (1u << bitmaskMapping[index]); + } +} + +void PadPopn::SetRawAnalogs(const std::tuple left, const std::tuple right) +{ +} + +void PadPopn::SetAxisScale(float deadzone, float scale) +{ +} + +float PadPopn::GetVibrationScale(u32 motor) const +{ + return 0; +} + +void PadPopn::SetVibrationScale(u32 motor, float scale) +{ +} + +float PadPopn::GetPressureModifier() const +{ + return 0; +} + +void PadPopn::SetPressureModifier(float mod) +{ +} + +void PadPopn::SetButtonDeadzone(float deadzone) +{ +} + +void PadPopn::SetAnalogInvertL(bool x, bool y) +{ +} + +void PadPopn::SetAnalogInvertR(bool x, bool y) +{ +} + +u8 PadPopn::GetRawInput(u32 index) const +{ + return rawInputs[index]; +} + +std::tuple PadPopn::GetRawLeftAnalog() const +{ + return std::tuple{0x7f, 0x7f}; +} + +std::tuple PadPopn::GetRawRightAnalog() const +{ + return std::tuple{0x7f, 0x7f}; +} + +u32 PadPopn::GetButtons() const +{ + // A quirk of the Pop'n controller, the "D-Pad" left right and down buttons are always pressed. + // Likely this was a simple way to identify their controllers by always reporting this button combination, + // since it was both impossible for a normal pad to do and quite impractical to imitate without + // some level of hardware tampering. This also meant they could just have the pad identify as a seemingly + // normal PS1 digital pad to work with the standard pad libraries across the board. + return buttons & ~(0xE000); +} + +u8 PadPopn::GetPressure(u32 index) const +{ + return 0; +} + +bool PadPopn::Freeze(StateWrapper& sw) +{ + if (!PadBase::Freeze(sw) || !sw.DoMarker("PadPopn")) + return false; + + // Private PadPopn members + sw.Do(&analogLight); + sw.Do(&analogLocked); + sw.Do(&analogPressed); + sw.Do(&commandStage); + sw.Do(&responseBytes); + return !sw.HasError(); +} + +u8 PadPopn::SendCommandByte(u8 commandByte) +{ + u8 ret = 0; + + switch (this->commandBytesReceived) + { + case 0: + ret = 0x00; + break; + case 1: + this->currentCommand = static_cast(commandByte); + + if (this->currentCommand != Pad::Command::POLL && this->currentCommand != Pad::Command::CONFIG && !this->isInConfig) + { + Console.Warning("%s(%02X) Config-only command was sent to a pad outside of config mode!", __FUNCTION__, commandByte); + } + + ret = this->isInConfig ? static_cast(Pad::Mode::CONFIG) : static_cast(this->currentMode); + break; + case 2: + ret = 0x5a; + break; + default: + switch (this->currentCommand) + { + case Pad::Command::MYSTERY: + ret = Mystery(commandByte); + break; + case Pad::Command::BUTTON_QUERY: + ret = ButtonQuery(commandByte); + break; + case Pad::Command::POLL: + ret = Poll(commandByte); + break; + case Pad::Command::CONFIG: + ret = Config(commandByte); + break; + case Pad::Command::MODE_SWITCH: + ret = ModeSwitch(commandByte); + break; + case Pad::Command::STATUS_INFO: + ret = StatusInfo(commandByte); + break; + case Pad::Command::CONST_1: + ret = Constant1(commandByte); + break; + case Pad::Command::CONST_2: + ret = Constant2(commandByte); + break; + case Pad::Command::CONST_3: + ret = Constant3(commandByte); + break; + case Pad::Command::VIBRATION_MAP: + ret = VibrationMap(commandByte); + break; + case Pad::Command::RESPONSE_BYTES: + ret = ResponseBytes(commandByte); + break; + default: + ret = 0x00; + break; + } + } + + this->commandBytesReceived++; + return ret; +} diff --git a/pcsx2/SIO/Pad/PadPopn.h b/pcsx2/SIO/Pad/PadPopn.h new file mode 100644 index 0000000000..e42ee6c5b1 --- /dev/null +++ b/pcsx2/SIO/Pad/PadPopn.h @@ -0,0 +1,103 @@ +// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team +// SPDX-License-Identifier: LGPL-3.0+ + +#pragma once + +#include "SIO/Pad/PadBase.h" + +#include + +// FIXME: For the brave soul. No one on earth seems to have a (functioning) real Pop'N controller. +// Those who do don't seem to have a PS2 setup which they can run the pad ID homebrew on. +// We are going with old information yanked out of Lilypad for this one, which basically means we +// imitate a DS2, and then three buttons are always pressed down. +// +// If any brave challengers wish to make this cleaner or more slim, track one of these things down +// and figure out exactly what the inputs are and how they correlate. +class PadPopn final : public PadBase +{ +public: + enum Inputs + { + PAD_YELLOW_LEFT, + PAD_YELLOW_RIGHT, + PAD_BLUE_LEFT, + PAD_BLUE_RIGHT, + PAD_WHITE_LEFT, + PAD_WHITE_RIGHT, + PAD_GREEN_LEFT, + PAD_GREEN_RIGHT, + PAD_RED, + PAD_START, + PAD_SELECT, + LENGTH, + }; + + static constexpr u8 VIBRATION_MOTORS = 2; + +private: + struct Analogs + { + u8 lx = Pad::ANALOG_NEUTRAL_POSITION; + u8 ly = Pad::ANALOG_NEUTRAL_POSITION; + u8 rx = Pad::ANALOG_NEUTRAL_POSITION; + u8 ry = Pad::ANALOG_NEUTRAL_POSITION; + bool lxInvert = false; + bool lyInvert = false; + bool rxInvert = false; + bool ryInvert = false; + }; + + u32 buttons = 0xffffffffu; + Analogs analogs; + bool analogLight = false; + bool analogLocked = false; + // Analog button can be held without changing its state. + // We track here if it is currently held down, to avoid flipping in + // and out of analog mode every frame. + bool analogPressed = false; + bool commandStage = false; + u32 responseBytes = 0; + + void ConfigLog(); + + u8 Mystery(u8 commandByte); + u8 ButtonQuery(u8 commandByte); + u8 Poll(u8 commandByte); + u8 Config(u8 commandByte); + u8 ModeSwitch(u8 commandByte); + u8 StatusInfo(u8 commandByte); + u8 Constant1(u8 commandByte); + u8 Constant2(u8 commandByte); + u8 Constant3(u8 commandByte); + u8 VibrationMap(u8 commandByte); + u8 ResponseBytes(u8 commandByte); + +public: + PadPopn(u8 unifiedSlot, size_t ejectTicks); + ~PadPopn() override; + + Pad::ControllerType GetType() const override; + const Pad::ControllerInfo& GetInfo() const override; + void Set(u32 index, float value) override; + void SetRawAnalogs(const std::tuple left, const std::tuple right) override; + void SetAxisScale(float deadzone, float scale) override; + float GetVibrationScale(u32 motor) const override; + void SetVibrationScale(u32 motor, float scale) override; + float GetPressureModifier() const override; + void SetPressureModifier(float mod) override; + void SetButtonDeadzone(float deadzone) override; + void SetAnalogInvertL(bool x, bool y) override; + void SetAnalogInvertR(bool x, bool y) override; + u8 GetRawInput(u32 index) const override; + std::tuple GetRawLeftAnalog() const override; + std::tuple GetRawRightAnalog() const override; + u32 GetButtons() const override; + u8 GetPressure(u32 index) const override; + + bool Freeze(StateWrapper& sw) override; + + u8 SendCommandByte(u8 commandByte) override; + + static const Pad::ControllerInfo ControllerInfo; +}; diff --git a/pcsx2/SIO/Pad/PadTypes.h b/pcsx2/SIO/Pad/PadTypes.h index ef089ca384..0a15006397 100644 --- a/pcsx2/SIO/Pad/PadTypes.h +++ b/pcsx2/SIO/Pad/PadTypes.h @@ -64,6 +64,7 @@ namespace Pad NotConnected, DualShock2, Guitar, + Popn, Count }; diff --git a/pcsx2/pcsx2.vcxproj b/pcsx2/pcsx2.vcxproj index aeb183f1fb..5e10c212e5 100644 --- a/pcsx2/pcsx2.vcxproj +++ b/pcsx2/pcsx2.vcxproj @@ -248,6 +248,7 @@ + @@ -596,6 +597,7 @@ + diff --git a/pcsx2/pcsx2.vcxproj.filters b/pcsx2/pcsx2.vcxproj.filters index 45e0e4b9e0..fc14843f48 100644 --- a/pcsx2/pcsx2.vcxproj.filters +++ b/pcsx2/pcsx2.vcxproj.filters @@ -1407,6 +1407,9 @@ System\Ps2\Iop\SIO\PAD + + System\Ps2\Iop\SIO\PAD + @@ -2323,6 +2326,9 @@ System\Ps2\Iop\SIO\PAD + + System\Ps2\Iop\SIO\PAD +