diff --git a/Data/Sys/GameFiles/GALE01/MnSlChr.dat.diff b/Data/Sys/GameFiles/GALE01/MnSlChr.dat.diff index 3b642123f7..f528a9646c 100644 Binary files a/Data/Sys/GameFiles/GALE01/MnSlChr.dat.diff and b/Data/Sys/GameFiles/GALE01/MnSlChr.dat.diff differ diff --git a/Data/Sys/GameFiles/GALE01/MnSlChr.usd.diff b/Data/Sys/GameFiles/GALE01/MnSlChr.usd.diff index 1e0ec9013c..a04cf06cb9 100644 Binary files a/Data/Sys/GameFiles/GALE01/MnSlChr.usd.diff and b/Data/Sys/GameFiles/GALE01/MnSlChr.usd.diff differ diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index e0cca848f6..5a835d1be2 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -490,6 +490,7 @@ add_library(core Slippi/SlippiUser.h Slippi/SlippiGameReporter.cpp Slippi/SlippiGameReporter.h + Slippi/SlippiPremadeText.h ) if(_M_X86) diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp index 0b4dd33693..16f0deb4b7 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp @@ -29,6 +29,7 @@ #include "Core/PowerPC/PowerPC.h" #include "Core/Slippi/SlippiMatchmaking.h" #include "Core/Slippi/SlippiPlayback.h" +#include "Core/Slippi/SlippiPremadeText.h" #include "Core/Slippi/SlippiReplayComm.h" #include "Core/State.h" @@ -2022,7 +2023,7 @@ void CEXISlippi::prepareOnlineMatchState() #ifdef LOCAL_TESTING localPlayerIndex = 0; - chatMessageId = localChatMessageId; + sentChatMessageId = localChatMessageId; chatMessagePlayerIdx = 0; localChatMessageId = 0; // in CSS p1 is always current player and p2 is opponent @@ -2042,8 +2043,15 @@ void CEXISlippi::prepareOnlineMatchState() chatMessageId = remoteMessageSelection.messageId; chatMessagePlayerIdx = remoteMessageSelection.playerIdx; sentChatMessageId = slippi_netplay->GetSlippiRemoteSentChatMessage(); + // If connection is 1v1 set index 0 for local and 1 for remote + if ((matchmaking && matchmaking->RemotePlayerCount() == 1) || !matchmaking) + { + chatMessagePlayerIdx = sentChatMessageId > 0 ? localPlayerIndex : remotePlayerIndex; + } // in CSS p1 is always current player and p2 is opponent localPlayerName = p1Name = userInfo.display_name; + INFO_LOG_FMT(SLIPPI, "chatMessagePlayerIdx {} {}", chatMessagePlayerIdx, + matchmaking ? matchmaking->RemotePlayerCount() : 0); } std::vector leftTeamPlayers = {}; @@ -2407,6 +2415,58 @@ void CEXISlippi::prepareGctLoad(u8* payload) m_read_queue.insert(m_read_queue.end(), gct.begin(), gct.end()); } +std::vector CEXISlippi::loadPremadeText(u8* payload) +{ + u8 textId = payload[0]; + std::vector premadeTextData; + auto spt = SlippiPremadeText(); + + if (textId >= SlippiPremadeText::SPT_CHAT_P1 && textId <= SlippiPremadeText::SPT_CHAT_P4) + { + auto port = textId - 1; + std::string playerName; + if (matchmaking) + playerName = matchmaking->GetPlayerName(port); +#ifdef LOCAL_TESTING + std::string defaultNames[] = {"Player 1", "lol u lost 2 dk", "Player 3", "Player 4"}; + playerName = defaultNames[port]; +#endif + + u8 paramId = payload[1] == 0x83 ? + 0x88 : + payload[1]; // TODO: Figure out what the hell is going on and fix this + + auto chatMessage = spt.premadeTextsParams[paramId]; + std::string param = ReplaceAll(chatMessage.c_str(), " ", ""); + playerName = ReplaceAll(playerName.c_str(), " ", ""); + premadeTextData = spt.GetPremadeTextData(textId, playerName.c_str(), param.c_str()); + } + else + { + premadeTextData = spt.GetPremadeTextData(textId); + } + + return premadeTextData; +} + +void CEXISlippi::preparePremadeTextLength(u8* payload) +{ + std::vector premadeTextData = loadPremadeText(payload); + + m_read_queue.clear(); + // Write size to output + appendWordToBuffer(&m_read_queue, static_cast(premadeTextData.size())); +} + +void CEXISlippi::preparePremadeTextLoad(u8* payload) +{ + std::vector premadeTextData = loadPremadeText(payload); + + m_read_queue.clear(); + // Write data to output + m_read_queue.insert(m_read_queue.end(), premadeTextData.begin(), premadeTextData.end()); +} + void CEXISlippi::handleChatMessage(u8* payload) { int messageId = payload[0]; @@ -2697,6 +2757,12 @@ void CEXISlippi::DMAWrite(u32 _uAddr, u32 _uSize) case CMD_FILE_LOAD: prepareFileLoad(&memPtr[bufLoc + 1]); break; + case CMD_PREMADE_TEXT_LENGTH: + preparePremadeTextLength(&memPtr[bufLoc + 1]); + break; + case CMD_PREMADE_TEXT_LOAD: + preparePremadeTextLoad(&memPtr[bufLoc + 1]); + break; case CMD_OPEN_LOGIN: handleLogInRequest(); break; diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h index 90173c6a06..1f7efcbfc2 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h @@ -82,6 +82,8 @@ private: CMD_GCT_LENGTH = 0xD3, CMD_GCT_LOAD = 0xD4, CMD_GET_DELAY = 0xD5, + CMD_PREMADE_TEXT_LENGTH = 0xE1, + CMD_PREMADE_TEXT_LOAD = 0xE2, }; enum @@ -130,6 +132,8 @@ private: {CMD_GCT_LENGTH, 0x0}, {CMD_GCT_LOAD, 0x4}, {CMD_GET_DELAY, 0x0}, + {CMD_PREMADE_TEXT_LENGTH, 0x0}, + {CMD_PREMADE_TEXT_LOAD, 0x4}, }; struct WriteMessage @@ -198,6 +202,10 @@ private: void prepareGctLength(); void prepareGctLoad(u8* payload); void prepareDelayResponse(); + void preparePremadeTextLength(u8* payload); + void preparePremadeTextLoad(u8* payload); + std::vector loadPremadeText(u8* payload); + int getCharColor(u8 charId, u8 teamId); void FileWriteThread(void); diff --git a/Source/Core/Core/Slippi/SlippiPremadeText.h b/Source/Core/Core/Slippi/SlippiPremadeText.h new file mode 100644 index 0000000000..c3908b9643 --- /dev/null +++ b/Source/Core/Core/Slippi/SlippiPremadeText.h @@ -0,0 +1,679 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/Logging/Log.h" + +using namespace std; + +class SlippiPremadeText +{ +public: + enum + { + SPT_CHAT_P1 = 0x1, + SPT_CHAT_P2 = 0x2, + SPT_CHAT_P3 = 0x3, + SPT_CHAT_P4 = 0x4, + SPT_LOGOUT = 0x5, + + CHAT_MSG_U_PAD_LEFT = 0x81, + CHAT_MSG_U_PAD_RIGHT = 0x82, + CHAT_MSG_U_PAD_DOWN = 0x84, + CHAT_MSG_U_PAD_UP = 0x88, + + CHAT_MSG_L_PAD_LEFT = 0x11, + CHAT_MSG_L_PAD_RIGHT = 0x12, + CHAT_MSG_L_PAD_DOWN = 0x14, + CHAT_MSG_L_PAD_UP = 0x18, + + CHAT_MSG_R_PAD_LEFT = 0x21, + CHAT_MSG_R_PAD_RIGHT = 0x22, + CHAT_MSG_R_PAD_DOWN = 0x24, + CHAT_MSG_R_PAD_UP = 0x28, + + CHAT_MSG_D_PAD_LEFT = 0x41, + CHAT_MSG_D_PAD_RIGHT = 0x42, + CHAT_MSG_D_PAD_DOWN = 0x44, + CHAT_MSG_D_PAD_UP = 0x48, + }; + + unordered_map premadeTextsParams = { + + {CHAT_MSG_U_PAD_UP, "ggs"}, + {CHAT_MSG_U_PAD_LEFT, "one more"}, + {CHAT_MSG_U_PAD_RIGHT, "brb"}, + {CHAT_MSG_U_PAD_DOWN, "good luck"}, + + {CHAT_MSG_L_PAD_UP, "well played"}, + {CHAT_MSG_L_PAD_LEFT, "that was fun"}, + {CHAT_MSG_L_PAD_RIGHT, "thanks"}, + {CHAT_MSG_L_PAD_DOWN, "too good"}, + + {CHAT_MSG_R_PAD_UP, "oof"}, + {CHAT_MSG_R_PAD_LEFT, "my b"}, + {CHAT_MSG_R_PAD_RIGHT, "lol"}, + {CHAT_MSG_R_PAD_DOWN, "wow"}, + + {CHAT_MSG_D_PAD_UP, "okay"}, + {CHAT_MSG_D_PAD_LEFT, "thinking"}, + {CHAT_MSG_D_PAD_RIGHT, "lets play again later"}, + {CHAT_MSG_D_PAD_DOWN, "bad connection"}, + }; + + unordered_map premadeTexts = { + {SPT_CHAT_P1, "%s-%s"}, + {SPT_CHAT_P2, "%s-%s"}, + {SPT_CHAT_P3, "%s-%s"}, + {SPT_CHAT_P4, "%s-%s"}, + {SPT_LOGOUT, "AreYouSure?"}, + }; + + // TODO: use va_list to handle any no. or args + string GetPremadeTextString(u8 textId) { return premadeTexts[textId]; } + + vector GetPremadeTextData(u8 textId, ...) + { + string format = GetPremadeTextString(textId); + char str[400]; + va_list args; + va_start(args, textId); + vsprintf(str, format.c_str(), args); + va_end(args); + // INFO_LOG(SLIPPI, "%s", str); + + vector data = {}; + vector empty = {}; + + vector matches = vector(); + + // NOTE: This code is converted from HSDRaw C# code + // Fuck Regex, current cpp version does not support positive lookaheads to match this pattern + // "((?<=<).+?(?=>))|((?<=>*)([^>]+?)(?=<) Good ol' fashioned nested loop :) + auto splitted = split(str, ">"); + for (int i = 0; i < splitted.size(); i++) + { + auto splitted2 = split(splitted[i], "<"); + for (int j = 0; j < splitted2.size(); j++) + { + if (splitted2[j].length() > 0) + matches.push_back(splitted2[j]); + } + } + + string match; + for (int m = 0; m < matches.size(); m++) + { + match = matches[m]; + + auto splittedMatches = split(match, ","); + if (splittedMatches.size() == 0) + continue; + string firstMatch = splittedMatches[0]; + + pair> key = findCodeKey(firstMatch); + if (key.first != TEXT_OP_CODE::CUSTOM_NULL) + { + if (splittedMatches.size() - 1 != strlen(key.second.second.c_str())) + return empty; + + data.push_back((u8)key.first); + + string res; + string res2; + for (int j = 0; j < strlen(key.second.second.c_str()); j++) + { + switch (key.second.second.c_str()[j]) + { + case 'b': + res = splittedMatches[j + 1]; + trim(res); + if ((u8)atoi(res.c_str())) + data.push_back((u8)atoi(res.c_str())); + else + data.push_back(0); + break; + case 's': + res2 = splittedMatches[j + 1]; + trim(res2); + u16 sht = (u16)atoi(res2.c_str()); + if (sht) + { + data.push_back((u8)(sht >> 8)); + data.push_back((u8)(sht & 0xFF)); + } + else + { + data.push_back(0); + data.push_back(0); + } + break; + } + } + } + else + { + // process string otherwise + + if (splittedMatches.size() >= 2 && firstMatch == "CHR") + { + string res3 = splittedMatches[1]; + trim(res3); + u16 ch = (u16)atoi(res3.c_str()); + if (ch) + { + u16 sht = (u16)(((u16)TEXT_OP_CODE::SPECIAL_CHARACTER << 8) | ch); + u8 r = (u8)(sht >> 8); + u8 r2 = (u8)(sht & 0xFF); + data.push_back(r); + data.push_back(r2); + } + } + else + { + for (int c = 0; c < strlen(firstMatch.c_str()); c++) + { + char chr = firstMatch[c]; + + // Yup, fuck strchr and cpp too, I'm not in the mood to spend 4 more hours researching + // how to get Japanese characters properly working with a map, so I put everything on an + // int array in hex + int pos = -1; + for (int ccc = 0; ccc < 287; ccc++) + { + if ((char)CHAR_MAP[ccc] == chr) + { + pos = ccc; + break; + } + } + + if (pos >= 0) + { + u16 sht = (u16)(((u16)TEXT_OP_CODE::COMMON_CHARACTER << 8) | pos); + u8 r = (u8)(sht >> 8); + u8 r2 = (u8)(sht & 0xFF); + // INFO_LOG(SLIPPI, "%x %x %x %c", sht, r, r2, chr); + + data.push_back(r); + data.push_back(r2); + } + else + return empty; + } + } + } + } + + // INFO_LOG(SLIPPI, "DATA:"); + // for(int i=0;i>> OPCODES; + unordered_map> CODES = { + {TEXT_OP_CODE::CENTERED, pair("CENTER", "")}, + {TEXT_OP_CODE::RESET_CENTERED, pair("/CENTER", "")}, + {TEXT_OP_CODE::CLEAR_COLOR, pair("/COLOR", "")}, + {TEXT_OP_CODE::COLOR, pair("COLOR", "bbb")}, + {TEXT_OP_CODE::END, pair("END", "")}, + {TEXT_OP_CODE::FITTING, pair("FIT", "")}, + {TEXT_OP_CODE::KERNING, pair("KERN", "")}, + {TEXT_OP_CODE::LEFT_ALIGNED, pair("LEFT", "")}, + {TEXT_OP_CODE::LINE_BREAK, pair("BR", "")}, + {TEXT_OP_CODE::NO_FITTING, pair("/FIT", "")}, + {TEXT_OP_CODE::NO_KERNING, pair("/KERN", "")}, + {TEXT_OP_CODE::OFFSET, pair("OFFSET", "ss")}, + {TEXT_OP_CODE::RESET, pair("RESET", "")}, + {TEXT_OP_CODE::RESET_LEFT_ALIGN, pair("/LEFT", "")}, + {TEXT_OP_CODE::RESET_RIGHT_ALIGN, pair("/RIGHT", "")}, + {TEXT_OP_CODE::RESET_SCALING, pair("/SCALE", "")}, + {TEXT_OP_CODE::RESET_TEXTBOX, pair("/TEXTBOX", "")}, + {TEXT_OP_CODE::RIGHT_ALIGNED, pair("/RIGHT", "")}, + {TEXT_OP_CODE::SCALING, pair("SCALE", "bbbb")}, + {TEXT_OP_CODE::SET_TEXTBOX, pair("TEXTBOX", "ss")}, + {TEXT_OP_CODE::UNKNOWN_02, pair("UNK02", "")}, + {TEXT_OP_CODE::UNKNOWN_04, pair("UNK04", "")}, + {TEXT_OP_CODE::UNKNOWN_05, pair("UNK05", "s")}, + {TEXT_OP_CODE::UNKNOWN_06, pair("UNK06", "ss")}, + {TEXT_OP_CODE::UNKNOWN_08, pair("UNK08", "")}, + {TEXT_OP_CODE::UNKNOWN_09, pair("UNK09", "")}, + {TEXT_OP_CODE::SPACE, pair("S", "")}, + }; + + pair> findCodeKey(string p) + { + unordered_map>::iterator it; + + for (it = CODES.begin(); it != CODES.end(); it++) + { + if (it->second.first == p) + { + return *it; + } + } + return pair>(TEXT_OP_CODE::CUSTOM_NULL, + pair("", "")); + } + + vector>> DeserializeCodes(vector data) + { + vector>> d = vector>>(); + + for (int i = 0; i < data.size();) + { + auto opcode = (TEXT_OP_CODE)data[i++]; + vector param = vector(0); + + int textCode = (u8)opcode; + + if ((textCode >> 4) == 2) + param = vector{(u16)(((textCode << 8) | (data[i++] & 0xFF)) & 0xFFF)}; + else if ((textCode >> 4) == 4) + param = vector{(u16)(((textCode << 8) | (data[i++] & 0xFF)) & 0xFFF)}; + else if (!CODES.count(opcode)) + { + ERROR_LOG_FMT(SLIPPI, "Opcode Not Supported!"); + } + else + { + pair code = CODES[opcode]; + auto p = code.second.c_str(); + param = vector(strlen(p)); + for (int j = 0; j < param.size(); j++) + { + switch (p[j]) + { + case 'b': + param[j] = (u16)(data[i++] & 0xFF); + break; + case 's': + param[j] = (u16)(((data[i++] & 0xFF) << 8) | (data[i++] & 0xFF)); + break; + } + } + } + + pair> c = pair>(opcode, param); + d.push_back(c); + + if (opcode == TEXT_OP_CODE::END) + break; + } + + return d; + } + + // https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring + // trim from start (in place) + static inline void ltrim(std::string& s) + { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { return !std::isspace(ch); })); + } + + // trim from end (in place) + static inline void rtrim(std::string& s) + { + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), + s.end()); + } + + // trim from both ends (in place) + static inline void trim(std::string& s) + { + ltrim(s); + rtrim(s); + } + + vector split(const string& str, const string& delim) + { + vector tokens; + size_t prev = 0, pos = 0; + do + { + pos = str.find(delim, prev); + if (pos == string::npos) + pos = str.length(); + string token = str.substr(prev, pos - prev); + if (!token.empty()) + tokens.push_back(token); + prev = pos + delim.length(); + } while (pos < str.length() && prev < str.length()); + return tokens; + } + + // region CharMAPS + int CHAR_MAP[287] = { + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + 'A', + 'B', + 'C', + 'D', + 'E', + 'F', + 'G', + 'H', + 'I', + 'J', + 'K', + 'L', + 'M', + 'N', + 'O', + 'P', + 'Q', + 'R', + 'S', + 'T', + 'U', + 'V', + 'W', + 'X', + 'Y', + 'Z', + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 0x0079 /*'y'*/, + 0x007a /*'z'*/, + 0x3041 /*'ぁ'*/, + 0x3042 /*'あ'*/, + 0x3043 /*'ぃ'*/, + 0x3044 /*'い'*/, + 0x3045 /*'ぅ'*/, + 0x3046 /*'う'*/, + 0x3047 /*'ぇ'*/, + 0x3048 /*'え'*/, + 0x3049 /*'ぉ'*/, + 0x304a /*'お'*/, + 0x304b /*'か'*/, + 0x304c /*'が'*/, + 0x304d /*'き'*/, + 0x304e /*'ぎ'*/, + 0x304f /*'く'*/, + 0x3050 /*'ぐ'*/, + 0x3051 /*'け'*/, + 0x3052 /*'げ'*/, + 0x3053 /*'こ'*/, + 0x3054 /*'ご'*/, + 0x3055 /*'さ'*/, + 0x3056 /*'ざ'*/, + 0x3057 /*'し'*/, + 0x3058 /*'じ'*/, + 0x3059 /*'す'*/, + 0x305a /*'ず'*/, + 0x305b /*'せ'*/, + 0x305c /*'ぜ'*/, + 0x305d /*'そ'*/, + 0x305e /*'ぞ'*/, + 0x305f /*'た'*/, + 0x3060 /*'だ'*/, + 0x3061 /*'ち'*/, + 0x3062 /*'ぢ'*/, + 0x3063 /*'っ'*/, + 0x3064 /*'つ'*/, + 0x3065 /*'づ'*/, + 0x3066 /*'て'*/, + 0x3067 /*'で'*/, + 0x3068 /*'と'*/, + 0x3069 /*'ど'*/, + 0x306a /*'な'*/, + 0x306b /*'に'*/, + 0x306c /*'ぬ'*/, + 0x306d /*'ね'*/, + 0x306e /*'の'*/, + 0x306f /*'は'*/, + 0x3070 /*'ば'*/, + 0x3071 /*'ぱ'*/, + 0x3072 /*'ひ'*/, + 0x3073 /*'び'*/, + 0x3074 /*'ぴ'*/, + 0x3075 /*'ふ'*/, + 0x3076 /*'ぶ'*/, + 0x3077 /*'ぷ'*/, + 0x3078 /*'へ'*/, + 0x3079 /*'べ'*/, + 0x307a /*'ぺ'*/, + 0x307b /*'ほ'*/, + 0x307c /*'ぼ'*/, + 0x307d /*'ぽ'*/, + 0x307e /*'ま'*/, + 0x307f /*'み'*/, + 0x3080 /*'む'*/, + 0x3081 /*'め'*/, + 0x3082 /*'も'*/, + 0x3083 /*'ゃ'*/, + 0x3084 /*'や'*/, + 0x3085 /*'ゅ'*/, + 0x3086 /*'ゆ'*/, + 0x3087 /*'ょ'*/, + 0x3088 /*'よ'*/, + 0x3089 /*'ら'*/, + 0x308a /*'り'*/, + 0x308b /*'る'*/, + 0x308c /*'れ'*/, + 0x308d /*'ろ'*/, + 0x308e /*'ゎ'*/, + 0x308f /*'わ'*/, + 0x3092 /*'を'*/, + 0x3093 /*'ん'*/, + 0x30a1 /*'ァ'*/, + 0x30a2 /*'ア'*/, + 0x30a3 /*'ィ'*/, + 0x30a4 /*'イ'*/, + 0x30a5 /*'ゥ'*/, + 0x30a6 /*'ウ'*/, + 0x30a7 /*'ェ'*/, + 0x30a8 /*'エ'*/, + 0x30a9 /*'ォ'*/, + 0x30aa /*'オ'*/, + 0x30ab /*'カ'*/, + 0x30ac /*'ガ'*/, + 0x30ad /*'キ'*/, + 0x30ae /*'ギ'*/, + 0x30af /*'ク'*/, + 0x30b0 /*'グ'*/, + 0x30b1 /*'ケ'*/, + 0x30b2 /*'ゲ'*/, + 0x30b3 /*'コ'*/, + 0x30b4 /*'ゴ'*/, + 0x30b5 /*'サ'*/, + 0x30b6 /*'ザ'*/, + 0x30b7 /*'シ'*/, + 0x30b8 /*'ジ'*/, + 0x30b9 /*'ス'*/, + 0x30ba /*'ズ'*/, + 0x30bb /*'セ'*/, + 0x30bc /*'ゼ'*/, + 0x30bd /*'ソ'*/, + 0x30be /*'ゾ'*/, + 0x30bf /*'タ'*/, + 0x30c0 /*'ダ'*/, + 0x30c1 /*'チ'*/, + 0x30c2 /*'ヂ'*/, + 0x30c3 /*'ッ'*/, + 0x30c4 /*'ツ'*/, + 0x30c5 /*'ヅ'*/, + 0x30c6 /*'テ'*/, + 0x30c7 /*'デ'*/, + 0x30c8 /*'ト'*/, + 0x30c9 /*'ド'*/, + 0x30ca /*'ナ'*/, + 0x30cb /*'ニ'*/, + 0x30cc /*'ヌ'*/, + 0x30cd /*'ネ'*/, + 0x30ce /*'ノ'*/, + 0x30cf /*'ハ'*/, + 0x30d0 /*'バ'*/, + 0x30d1 /*'パ'*/, + 0x30d2 /*'ヒ'*/, + 0x30d3 /*'ビ'*/, + 0x30d4 /*'ピ'*/, + 0x30d5 /*'フ'*/, + 0x30d6 /*'ブ'*/, + 0x30d7 /*'プ'*/, + 0x30d8 /*'ヘ'*/, + 0x30d9 /*'ベ'*/, + 0x30da /*'ペ'*/, + 0x30db /*'ホ'*/, + 0x30dc /*'ボ'*/, + 0x30dd /*'ポ'*/, + 0x30de /*'マ'*/, + 0x30df /*'ミ'*/, + 0x30e0 /*'ム'*/, + 0x30e1 /*'メ'*/, + 0x30e2 /*'モ'*/, + 0x30e3 /*'ャ'*/, + 0x30e4 /*'ヤ'*/, + 0x30e5 /*'ュ'*/, + 0x30e6 /*'ユ'*/, + 0x30e7 /*'ョ'*/, + 0x30e8 /*'ヨ'*/, + 0x30e9 /*'ラ'*/, + 0x30ea /*'リ'*/, + 0x30eb /*'ル'*/, + 0x30ec /*'レ'*/, + 0x30ed /*'ロ'*/, + 0x30ee /*'ヮ'*/, + 0x30ef /*'ワ'*/, + 0x30f2 /*'ヲ'*/, + 0x30f3 /*'ン'*/, + 0x30f4 /*'ヴ'*/, + 0x30f5 /*'ヵ'*/, + 0x30f6 /*'ヶ'*/, + 0x3000 /*' '*/, + 0x3001 /*'、'*/, + 0x3002 /*'。'*/, + 0x002c /*','*/, + 0x002e /*'.'*/, + 0x2022 /*'•'*/, + 0x002c /*','*/, + 0x003b /*';'*/, + 0x003f /*'?'*/, + 0x0021 /*'!'*/, + 0x005e /*'^'*/, + 0x005f /*'_'*/, + 0x2014 /*'—'*/, + 0x002f /*'/'*/, + 0x007e /*'~'*/, + 0x007c /*'|'*/, + 0x0027 /*'\''*/, + 0x0022 /*'"'*/, + 0x0028 /*'('*/, + 0x0029 /*')'*/, + 0x005b /*'['*/, + 0x005d /*']'*/, + 0x007b /*'{'*/, + 0x007d /*'}'*/, + 0x002b /*'+'*/, + '-', + 0x00d7 /*'×'*/, + 0x003d /*'='*/, + 0x003c /*'<'*/, + 0x003e /*'>'*/, + 0x00a5 /*'¥'*/, + 0x0024 /*'$'*/, + 0x0025 /*'%'*/, + 0x0023 /*'#'*/, + 0x0026 /*'&'*/, + 0x002a /*'*'*/, + 0x0040 /*'@'*/, + 0x6271 /*'扱'*/, + 0x62bc /*'押'*/, + 0x8ecd /*'軍'*/, + 0x6e90 /*'源'*/, + 0x500b /*'個'*/, + 0x8fbc /*'込'*/, + 0x6307 /*'指'*/, + 0x793a /*'示'*/, + 0x53d6 /*'取'*/, + 0x66f8 /*'書'*/, + 0x8a73 /*'詳'*/, + 0x4eba /*'人'*/, + 0x751f /*'生'*/, + 0x8aac /*'説'*/, + 0x4f53 /*'体'*/, + 0x56e3 /*'団'*/, + 0x96fb /*'電'*/, + 0x8aad /*'読'*/, + 0x767a /*'発'*/, + 0x629c /*'抜'*/, + 0x9591 /*'閑'*/, + 0x672c /*'本'*/, + 0x660e /*'明'*/, + }; +};