mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-28 13:16:41 +00:00
compiles, working, playable matches. connect code history feature works
This commit is contained in:
parent
90c4c8ebfb
commit
de49277949
13 changed files with 8894 additions and 1437 deletions
BIN
Data/Sys/GameFiles/GALE01/slpCSS.dat
Normal file
BIN
Data/Sys/GameFiles/GALE01/slpCSS.dat
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load diff
5527
Data/Sys/GameSettings/GALJ01r2.ini
Normal file
5527
Data/Sys/GameSettings/GALJ01r2.ini
Normal file
File diff suppressed because it is too large
Load diff
|
@ -930,6 +930,7 @@ static void RebuildUserDirectories(unsigned int dir_index)
|
|||
s_user_paths[F_GCSRAM_IDX] = s_user_paths[D_GCUSER_IDX] + GC_SRAM;
|
||||
s_user_paths[F_WIISDCARD_IDX] = s_user_paths[D_WIIROOT_IDX] + DIR_SEP WII_SDCARD;
|
||||
s_user_paths[F_USERJSON_IDX] = s_user_paths[D_USER_IDX] + "user.json";
|
||||
s_user_paths[F_DIRECTCODESJSON_IDX] = s_user_paths[D_USER_IDX] + "directcodes.json";
|
||||
|
||||
s_user_paths[D_MEMORYWATCHER_IDX] = s_user_paths[D_USER_IDX] + MEMORYWATCHER_DIR DIR_SEP;
|
||||
s_user_paths[F_MEMORYWATCHERLOCATIONS_IDX] =
|
||||
|
|
|
@ -72,6 +72,7 @@ enum
|
|||
F_MEMORYWATCHERSOCKET_IDX,
|
||||
F_WIISDCARD_IDX,
|
||||
F_USERJSON_IDX,
|
||||
F_DIRECTCODESJSON_IDX,
|
||||
F_DUALSHOCKUDPCLIENTCONFIG_IDX,
|
||||
NUM_PATH_INDICES
|
||||
};
|
||||
|
|
|
@ -488,9 +488,10 @@ add_library(core
|
|||
Slippi/SlippiSpectate.h
|
||||
Slippi/SlippiUser.cpp
|
||||
Slippi/SlippiUser.h
|
||||
Slippi/SlippiGameReporter.cpp
|
||||
Slippi/SlippiGameReporter.h
|
||||
Slippi/SlippiPremadeText.h
|
||||
Slippi/SlippiGameReporter.cpp
|
||||
Slippi/SlippiGameReporter.h
|
||||
Slippi/SlippiDirectCodes.cpp
|
||||
Slippi/SlippiPremadeText.h
|
||||
)
|
||||
|
||||
if(_M_X86)
|
||||
|
|
|
@ -65,7 +65,7 @@ public:
|
|||
// For savestates. storing it here seemed cleaner than requiring each implementation to report its
|
||||
// type. I know this class is set up like an interface, but no code requires it to be strictly
|
||||
// such.
|
||||
TEXIDevices m_device_type;
|
||||
TEXIDevices m_device_type{EXIDEVICE_NONE};
|
||||
|
||||
private:
|
||||
// Byte transfer function for this device
|
||||
|
|
|
@ -121,6 +121,8 @@ CEXISlippi::CEXISlippi()
|
|||
gameFileLoader = std::make_unique<SlippiGameFileLoader>();
|
||||
game_reporter = std::make_unique<SlippiGameReporter>(user.get());
|
||||
g_replayComm = std::make_unique<SlippiReplayComm>();
|
||||
directCodes = std::make_unique<SlippiDirectCodes>("direct-codes.json");
|
||||
teamsCodes = std::make_unique<SlippiDirectCodes>("teams-codes.json");
|
||||
|
||||
generator = std::default_random_engine(Common::Timer::GetTimeMs());
|
||||
|
||||
|
@ -1830,6 +1832,20 @@ void CEXISlippi::startFindMatch(u8* payload)
|
|||
shiftJisCode.insert(shiftJisCode.begin(), &payload[1], &payload[1] + 18);
|
||||
shiftJisCode.erase(std::find(shiftJisCode.begin(), shiftJisCode.end(), 0x00), shiftJisCode.end());
|
||||
|
||||
// Log the direct code to file.
|
||||
if (search.mode == SlippiMatchmaking::DIRECT)
|
||||
{
|
||||
// Make sure to convert to UTF8, otherwise json library will fail when
|
||||
// calling dump().
|
||||
std::string utf8Code = SHIFTJISToUTF8(shiftJisCode);
|
||||
directCodes->AddOrUpdateCode(utf8Code);
|
||||
}
|
||||
else if (search.mode == SlippiMatchmaking::TEAMS)
|
||||
{
|
||||
std::string utf8Code = SHIFTJISToUTF8(shiftJisCode);
|
||||
teamsCodes->AddOrUpdateCode(utf8Code);
|
||||
}
|
||||
|
||||
// TODO: Make this work so we dont have to pass shiftJis to mm server
|
||||
// search.connectCode = SHIFTJISToUTF8(shiftJisCode).c_str();
|
||||
search.connectCode = shiftJisCode;
|
||||
|
@ -1874,6 +1890,123 @@ void CEXISlippi::startFindMatch(u8* payload)
|
|||
#endif
|
||||
}
|
||||
|
||||
bool CEXISlippi::doesTagMatchInput(u8* input, u8 inputLen, std::string tag)
|
||||
{
|
||||
auto jisTag = UTF8ToSHIFTJIS(tag);
|
||||
|
||||
// Check if this tag matches what has been input so far
|
||||
bool isMatch = true;
|
||||
for (int i = 0; i < inputLen; i++)
|
||||
{
|
||||
// ERROR_LOG(SLIPPI_ONLINE, "Entered: %X%X. History: %X%X", input[i * 3], input[i * 3 + 1],
|
||||
// (u8)jisTag[i * 2],
|
||||
// (u8)jisTag[i * 2 + 1]);
|
||||
if (input[i * 3] != (u8)jisTag[i * 2] || input[i * 3 + 1] != (u8)jisTag[i * 2 + 1])
|
||||
{
|
||||
isMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return isMatch;
|
||||
}
|
||||
|
||||
void CEXISlippi::handleNameEntryLoad(u8* payload)
|
||||
{
|
||||
u8 inputLen = payload[24];
|
||||
u32 initialIndex = payload[25] << 24 | payload[26] << 16 | payload[27] << 8 | payload[28];
|
||||
u8 scrollDirection = payload[29];
|
||||
u8 curMode = payload[30];
|
||||
|
||||
auto codeHistory = directCodes.get();
|
||||
if (curMode == SlippiMatchmaking::TEAMS)
|
||||
{
|
||||
codeHistory = teamsCodes.get();
|
||||
}
|
||||
|
||||
// Adjust index
|
||||
u32 curIndex = initialIndex;
|
||||
if (scrollDirection == 1)
|
||||
{
|
||||
curIndex++;
|
||||
}
|
||||
else if (scrollDirection == 2)
|
||||
{
|
||||
curIndex = curIndex > 0 ? curIndex - 1 : curIndex;
|
||||
}
|
||||
else if (scrollDirection == 3)
|
||||
{
|
||||
curIndex = 0;
|
||||
}
|
||||
|
||||
// Scroll to next tag that
|
||||
std::string tagAtIndex = "1";
|
||||
while (curIndex >= 0 && curIndex < (u32)codeHistory->length())
|
||||
{
|
||||
tagAtIndex = codeHistory->get(curIndex);
|
||||
|
||||
// Break if we have found a tag that matches
|
||||
if (doesTagMatchInput(payload, inputLen, tagAtIndex))
|
||||
break;
|
||||
|
||||
curIndex = scrollDirection == 2 ? curIndex - 1 : curIndex + 1;
|
||||
}
|
||||
|
||||
INFO_LOG(SLIPPI_ONLINE, "Idx: %d, InitIdx: %d, Scroll: %d. Len: %d", curIndex, initialIndex,
|
||||
scrollDirection, inputLen);
|
||||
|
||||
tagAtIndex = codeHistory->get(curIndex);
|
||||
if (tagAtIndex == "1")
|
||||
{
|
||||
// If we failed to find a tag at the current index, try the initial index again.
|
||||
// If the initial index matches the filter, preserve that suggestion. Without
|
||||
// this logic, the suggestion would get cleared
|
||||
auto initialTag = codeHistory->get(initialIndex);
|
||||
if (doesTagMatchInput(payload, inputLen, initialTag))
|
||||
{
|
||||
tagAtIndex = initialTag;
|
||||
curIndex = initialIndex;
|
||||
}
|
||||
}
|
||||
|
||||
INFO_LOG(SLIPPI_ONLINE, "Retrieved tag: %s", tagAtIndex.c_str());
|
||||
std::string jisCode;
|
||||
m_read_queue.clear();
|
||||
|
||||
if (tagAtIndex == "1")
|
||||
{
|
||||
m_read_queue.push_back(0);
|
||||
m_read_queue.insert(m_read_queue.end(), payload, payload + 3 * inputLen);
|
||||
m_read_queue.insert(m_read_queue.end(), 3 * (8 - inputLen), 0);
|
||||
m_read_queue.push_back(inputLen);
|
||||
appendWordToBuffer(&m_read_queue, initialIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// Indicate we have a suggestion
|
||||
m_read_queue.push_back(1);
|
||||
|
||||
// Convert to tag to shift jis and write to response
|
||||
jisCode = UTF8ToSHIFTJIS(tagAtIndex);
|
||||
|
||||
// Write out connect code into buffer, injection null terminator after each letter
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
for (int j = i * 2; j < i * 2 + 2; j++)
|
||||
{
|
||||
m_read_queue.push_back(j < jisCode.length() ? jisCode[j] : 0);
|
||||
}
|
||||
|
||||
m_read_queue.push_back(0x0);
|
||||
}
|
||||
|
||||
INFO_LOG(SLIPPI_ONLINE, "New Idx: %d. Jis Code length: %d", curIndex, (u8)(jisCode.length() / 2));
|
||||
|
||||
// Write length of tag
|
||||
m_read_queue.push_back(static_cast<u8>(jisCode.length() / 2));
|
||||
appendWordToBuffer(&m_read_queue, curIndex);
|
||||
}
|
||||
|
||||
void CEXISlippi::prepareOnlineMatchState()
|
||||
{
|
||||
// This match block is a VS match with P1 Red Falco vs P2 Red Bowser vs P3 Young Link vs P4 Young
|
||||
|
@ -2133,7 +2266,7 @@ void CEXISlippi::prepareOnlineMatchState()
|
|||
|
||||
// Overwrite stage information. Make sure everyone loads the same stage
|
||||
u16 stageId = 0x1F; // Default to battlefield if there was no selection
|
||||
for (auto selections : orderedSelections)
|
||||
for (const auto& selections : orderedSelections)
|
||||
{
|
||||
if (!selections.isStageSelected)
|
||||
continue;
|
||||
|
@ -2780,6 +2913,9 @@ void CEXISlippi::DMAWrite(u32 _uAddr, u32 _uSize)
|
|||
case CMD_FILE_LENGTH:
|
||||
prepareFileLength(&memPtr[bufLoc + 1]);
|
||||
break;
|
||||
case CMD_FETCH_CODE_SUGGESTION:
|
||||
handleNameEntryLoad(&memPtr[bufLoc + 1]);
|
||||
break;
|
||||
case CMD_FILE_LOAD:
|
||||
prepareFileLoad(&memPtr[bufLoc + 1]);
|
||||
break;
|
||||
|
@ -2839,7 +2975,7 @@ void CEXISlippi::DMARead(u32 addr, u32 size)
|
|||
{
|
||||
if (m_read_queue.empty())
|
||||
{
|
||||
INFO_LOG(EXPANSIONINTERFACE, "EXI SLIPPI DMARead: Empty");
|
||||
ERROR_LOG(SLIPPI, "EXI SLIPPI DMARead: Empty");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "Common/CommonTypes.h"
|
||||
#include "Common/File.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Core/Slippi/SlippiDirectCodes.h"
|
||||
#include "Core/Slippi/SlippiGameFileLoader.h"
|
||||
#include "Core/Slippi/SlippiGameReporter.h"
|
||||
#include "Core/Slippi/SlippiMatchmaking.h"
|
||||
|
@ -74,6 +75,7 @@ private:
|
|||
CMD_SEND_CHAT_MESSAGE = 0xBB,
|
||||
CMD_GET_NEW_SEED = 0xBC,
|
||||
CMD_REPORT_GAME = 0xBD,
|
||||
CMD_FETCH_CODE_SUGGESTION = 0xBE,
|
||||
|
||||
// Misc
|
||||
CMD_LOG_MESSAGE = 0xD0,
|
||||
|
@ -124,6 +126,7 @@ private:
|
|||
{CMD_CLEANUP_CONNECTION, 0},
|
||||
{CMD_GET_NEW_SEED, 0},
|
||||
{CMD_REPORT_GAME, 16},
|
||||
{CMD_FETCH_CODE_SUGGESTION, 31},
|
||||
|
||||
// Misc
|
||||
{CMD_LOG_MESSAGE, 0xFFFF}, // Variable size... will only work if by itself
|
||||
|
@ -174,6 +177,8 @@ private:
|
|||
void handleSendInputs(u8* payload);
|
||||
void handleCaptureSavestate(u8* payload);
|
||||
void handleLoadSavestate(u8* payload);
|
||||
void handleNameEntryAutoComplete(u8* payload);
|
||||
void handleNameEntryLoad(u8* payload);
|
||||
void startFindMatch(u8* payload);
|
||||
void prepareOnlineMatchState();
|
||||
void setMatchSelections(u8* payload);
|
||||
|
@ -204,6 +209,8 @@ private:
|
|||
void prepareDelayResponse();
|
||||
void preparePremadeTextLength(u8* payload);
|
||||
void preparePremadeTextLoad(u8* payload);
|
||||
bool doesTagMatchInput(u8* input, u8 inputLen, std::string tag);
|
||||
|
||||
std::vector<u8> loadPremadeText(u8* payload);
|
||||
int getCharColor(u8 charId, u8 teamId);
|
||||
|
||||
|
@ -259,6 +266,8 @@ private:
|
|||
std::unique_ptr<SlippiNetplayClient> slippi_netplay;
|
||||
std::unique_ptr<SlippiMatchmaking> matchmaking;
|
||||
std::unique_ptr<SlippiGameReporter> game_reporter;
|
||||
std::unique_ptr<SlippiDirectCodes> directCodes;
|
||||
std::unique_ptr<SlippiDirectCodes> teamsCodes;
|
||||
|
||||
std::map<s32, std::unique_ptr<SlippiSavestate>> activeSavestates;
|
||||
std::deque<std::unique_ptr<SlippiSavestate>> availableSavestates;
|
||||
|
|
241
Source/Core/Core/Slippi/SlippiDirectCodes.cpp
Normal file
241
Source/Core/Core/Slippi/SlippiDirectCodes.cpp
Normal file
|
@ -0,0 +1,241 @@
|
|||
#include "SlippiDirectCodes.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "AtlBase.h"
|
||||
#include "AtlConv.h"
|
||||
#endif
|
||||
|
||||
#include "Common/CommonPaths.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/Thread.h"
|
||||
|
||||
#include "Core/ConfigManager.h"
|
||||
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
#include <time.h>
|
||||
|
||||
#include <json.hpp>
|
||||
using json = nlohmann::json;
|
||||
|
||||
SlippiDirectCodes::SlippiDirectCodes(std::string fileName)
|
||||
{
|
||||
m_fileName = fileName;
|
||||
|
||||
// Prevent additional file reads, if we've already loaded data to memory.
|
||||
// if (directCodeInfos.empty())
|
||||
ReadFile();
|
||||
Sort();
|
||||
}
|
||||
|
||||
SlippiDirectCodes::~SlippiDirectCodes()
|
||||
{
|
||||
// Add additional cleanup behavior here? Just added something
|
||||
// So compiler wouldn't nag.
|
||||
return;
|
||||
}
|
||||
|
||||
void SlippiDirectCodes::ReadFile()
|
||||
{
|
||||
std::string directCodesFilePath = getCodesFilePath();
|
||||
|
||||
INFO_LOG(SLIPPI_ONLINE, "Looking for direct codes file at %s", directCodesFilePath.c_str());
|
||||
|
||||
if (!File::Exists(directCodesFilePath))
|
||||
{
|
||||
// Attempt to create empty file with array as parent json item.
|
||||
if (File::CreateEmptyFile(directCodesFilePath))
|
||||
{
|
||||
File::WriteStringToFile("[\n]", directCodesFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(SLIPPI_ONLINE, "Was unable to create %s", directCodesFilePath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
std::string directCodesFileContents;
|
||||
File::ReadFileToString(directCodesFilePath, directCodesFileContents);
|
||||
|
||||
directCodeInfos = parseFile(directCodesFileContents);
|
||||
}
|
||||
|
||||
void SlippiDirectCodes::AddOrUpdateCode(std::string code)
|
||||
{
|
||||
WARN_LOG(SLIPPI_ONLINE, "Attempting to add or update direct code: %s", code.c_str());
|
||||
|
||||
time_t curTime;
|
||||
time(&curTime);
|
||||
u8 dateTimeStrLength = sizeof "20171015T095717";
|
||||
std::vector<char> dateTimeBuf(dateTimeStrLength);
|
||||
strftime(&dateTimeBuf[0], dateTimeStrLength, "%Y%m%dT%H%M%S", localtime(&curTime));
|
||||
std::string timestamp(&dateTimeBuf[0]);
|
||||
|
||||
bool found = false;
|
||||
for (auto it = directCodeInfos.begin(); it != directCodeInfos.end(); ++it)
|
||||
{
|
||||
if (it->connectCode == code)
|
||||
{
|
||||
found = true;
|
||||
it->lastPlayed = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
CodeInfo newDirectCode = {code, timestamp, false};
|
||||
directCodeInfos.push_back(newDirectCode);
|
||||
}
|
||||
|
||||
// TODO: Maybe remove from here?
|
||||
// Or start a thread that is periodically called, if file writes will happen enough.
|
||||
WriteFile();
|
||||
}
|
||||
|
||||
void SlippiDirectCodes::Sort(u8 sortByProperty)
|
||||
{
|
||||
switch (sortByProperty)
|
||||
{
|
||||
case SORT_BY_TIME:
|
||||
std::sort(
|
||||
directCodeInfos.begin(), directCodeInfos.end(),
|
||||
[](const CodeInfo a, const CodeInfo b) -> bool { return a.lastPlayed > b.lastPlayed; });
|
||||
break;
|
||||
|
||||
case SORT_BY_NAME:
|
||||
std::sort(
|
||||
directCodeInfos.begin(), directCodeInfos.end(),
|
||||
[](const CodeInfo a, const CodeInfo b) -> bool { return a.connectCode < b.connectCode; });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string SlippiDirectCodes::Autocomplete(std::string startText)
|
||||
{
|
||||
// Pre-sort direct codes.
|
||||
Sort();
|
||||
|
||||
// Find first entry in our sorted vector that starts with the given text.
|
||||
for (auto it = directCodeInfos.begin(); it != directCodeInfos.end(); it++)
|
||||
{
|
||||
if (it->connectCode.rfind(startText, 0) == 0)
|
||||
{
|
||||
return it->connectCode;
|
||||
}
|
||||
}
|
||||
|
||||
return startText;
|
||||
}
|
||||
|
||||
std::string SlippiDirectCodes::get(int index)
|
||||
{
|
||||
Sort();
|
||||
|
||||
if (index < directCodeInfos.size() && index >= 0)
|
||||
{
|
||||
return directCodeInfos.at(index).connectCode;
|
||||
}
|
||||
|
||||
INFO_LOG(SLIPPI_ONLINE, "Out of bounds name entry index %d", index);
|
||||
|
||||
return (index >= directCodeInfos.size()) ? "1" : "";
|
||||
}
|
||||
|
||||
int SlippiDirectCodes::length()
|
||||
{
|
||||
return (int)directCodeInfos.size();
|
||||
}
|
||||
|
||||
void SlippiDirectCodes::WriteFile()
|
||||
{
|
||||
std::string directCodesFilePath = getCodesFilePath();
|
||||
|
||||
// Outer empty array.
|
||||
json fileData = json::array();
|
||||
|
||||
// Inner contents.
|
||||
json directCodeData = json::object();
|
||||
|
||||
// TODO Define constants for string literals.
|
||||
for (auto it = directCodeInfos.begin(); it != directCodeInfos.end(); ++it)
|
||||
{
|
||||
directCodeData["connectCode"] = it->connectCode;
|
||||
directCodeData["lastPlayed"] = it->lastPlayed;
|
||||
directCodeData["isFavorite"] = it->isFavorite;
|
||||
|
||||
fileData.emplace_back(directCodeData);
|
||||
}
|
||||
|
||||
File::WriteStringToFile(fileData.dump(), directCodesFilePath);
|
||||
}
|
||||
|
||||
std::string SlippiDirectCodes::getCodesFilePath()
|
||||
{
|
||||
std::string fileName = m_fileName + ".json";
|
||||
|
||||
// TODO: Move to User dir
|
||||
#if defined(__APPLE__)
|
||||
std::string directCodesPath =
|
||||
File::GetBundleDirectory() + "/Contents/Resources" + DIR_SEP + m_fileName;
|
||||
#elif defined(_WIN32)
|
||||
std::string directCodesPath = File::GetExeDirectory() + DIR_SEP + m_fileName;
|
||||
#else
|
||||
std::string directCodesPath = File::GetUserPath(D_USER_IDX) + m_fileName;
|
||||
// directCodesPath.pop_back();
|
||||
#endif
|
||||
return directCodesPath;
|
||||
}
|
||||
|
||||
inline std::string readString(json obj, std::string key)
|
||||
{
|
||||
auto item = obj.find(key);
|
||||
if (item == obj.end() || item.value().is_null())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return obj[key];
|
||||
}
|
||||
|
||||
inline bool readBool(json obj, std::string key)
|
||||
{
|
||||
auto item = obj.find(key);
|
||||
if (item == obj.end() || item.value().is_null())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return obj[key];
|
||||
}
|
||||
|
||||
std::vector<SlippiDirectCodes::CodeInfo> SlippiDirectCodes::parseFile(std::string fileContents)
|
||||
{
|
||||
std::vector<SlippiDirectCodes::CodeInfo> directCodes;
|
||||
|
||||
json res = json::parse(fileContents, nullptr, false);
|
||||
// Unlike the user.json, the encapsulating type should be an array.
|
||||
if (res.is_discarded() || !res.is_array())
|
||||
{
|
||||
WARN_LOG(SLIPPI_ONLINE, "Malformed json in direct codes file.");
|
||||
return directCodes;
|
||||
}
|
||||
|
||||
// Retrieve all saved direct codes and related info
|
||||
for (auto it = res.begin(); it != res.end(); ++it)
|
||||
{
|
||||
if (it.value().is_object())
|
||||
{
|
||||
CodeInfo curDirectCode;
|
||||
curDirectCode.connectCode = readString(*it, "connectCode");
|
||||
curDirectCode.lastPlayed = readString(*it, "lastPlayed");
|
||||
curDirectCode.isFavorite = readBool(*it, "favorite");
|
||||
|
||||
directCodes.push_back(curDirectCode);
|
||||
}
|
||||
}
|
||||
|
||||
return directCodes;
|
||||
}
|
39
Source/Core/Core/Slippi/SlippiDirectCodes.h
Normal file
39
Source/Core/Core/Slippi/SlippiDirectCodes.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class SlippiDirectCodes
|
||||
{
|
||||
public:
|
||||
static const uint8_t SORT_BY_TIME = 1;
|
||||
static const uint8_t SORT_BY_FAVORITE = 2;
|
||||
static const uint8_t SORT_BY_NAME = 3;
|
||||
|
||||
struct CodeInfo
|
||||
{
|
||||
std::string connectCode = "";
|
||||
std::string lastPlayed = "";
|
||||
bool isFavorite = false;
|
||||
};
|
||||
|
||||
SlippiDirectCodes(std::string fileName);
|
||||
~SlippiDirectCodes();
|
||||
|
||||
void ReadFile();
|
||||
void AddOrUpdateCode(std::string code);
|
||||
std::string get(int index);
|
||||
int length();
|
||||
void Sort(u8 sortByProperty = SlippiDirectCodes::SORT_BY_TIME);
|
||||
std::string Autocomplete(std::string startText);
|
||||
|
||||
protected:
|
||||
void WriteFile();
|
||||
std::string getCodesFilePath();
|
||||
std::vector<CodeInfo> parseFile(std::string fileContents);
|
||||
std::vector<CodeInfo> directCodeInfos;
|
||||
std::string m_fileName;
|
||||
};
|
|
@ -42,19 +42,19 @@ struct SlippiRemotePadOutput
|
|||
class SlippiPlayerSelections
|
||||
{
|
||||
public:
|
||||
u8 playerIdx = 0;
|
||||
u8 characterId = 0;
|
||||
u8 characterColor = 0;
|
||||
u8 teamId = 0;
|
||||
u8 playerIdx{};
|
||||
u8 characterId{};
|
||||
u8 characterColor{};
|
||||
u8 teamId{};
|
||||
|
||||
bool isCharacterSelected = false;
|
||||
|
||||
u16 stageId = 0;
|
||||
u16 stageId{};
|
||||
bool isStageSelected = false;
|
||||
|
||||
u32 rngOffset = 0;
|
||||
u32 rngOffset{};
|
||||
|
||||
int messageId;
|
||||
int messageId{};
|
||||
|
||||
void Merge(SlippiPlayerSelections& s)
|
||||
{
|
||||
|
|
|
@ -269,7 +269,6 @@ private:
|
|||
COMMON_CHARACTER = 0x20,
|
||||
SPECIAL_CHARACTER = 0x40,
|
||||
CUSTOM_NULL = 0x99,
|
||||
|
||||
};
|
||||
|
||||
std::vector<std::tuple<TEXT_OP_CODE, std::vector<u16>>> OPCODES;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue