Merge pull request #65 from bmarcott/nmarcott_format

apply clang-format to all .cpp and .h files containing the word slippi
This commit is contained in:
David Liu 2021-01-19 18:26:07 -05:00 committed by GitHub
commit a557edb283
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1616 additions and 1419 deletions

View file

@ -1,26 +1,31 @@
#include <string>
#include <codecvt> #include <codecvt>
#include <locale> #include <locale>
#include <string>
#include "SlippiGame.h" #include "SlippiGame.h"
namespace Slippi { namespace Slippi
{
//********************************************************************** //**********************************************************************
//* Event Handlers //* Event Handlers
//********************************************************************** //**********************************************************************
//The read operators will read a value and increment the index so the next read will read in the correct location // The read operators will read a value and increment the index so the next read
uint8_t readByte(uint8_t* a, int& idx, uint32_t maxSize, uint8_t defaultValue) { // will read in the correct location
if (idx >= (int)maxSize) { uint8_t readByte(uint8_t* a, int& idx, uint32_t maxSize, uint8_t defaultValue)
{
if (idx >= (int)maxSize)
{
idx += 1; idx += 1;
return defaultValue; return defaultValue;
} }
return a[idx++]; return a[idx++];
} }
uint16_t readHalf(uint8_t* a, int& idx, uint32_t maxSize, uint16_t defaultValue) { uint16_t readHalf(uint8_t* a, int& idx, uint32_t maxSize, uint16_t defaultValue)
if (idx >= (int)maxSize) { {
if (idx >= (int)maxSize)
{
idx += 2; idx += 2;
return defaultValue; return defaultValue;
} }
@ -28,10 +33,12 @@ namespace Slippi {
uint16_t value = a[idx] << 8 | a[idx + 1]; uint16_t value = a[idx] << 8 | a[idx + 1];
idx += 2; idx += 2;
return value; return value;
} }
uint32_t readWord(uint8_t* a, int& idx, uint32_t maxSize, uint32_t defaultValue) { uint32_t readWord(uint8_t* a, int& idx, uint32_t maxSize, uint32_t defaultValue)
if (idx >= (int)maxSize) { {
if (idx >= (int)maxSize)
{
idx += 4; idx += 4;
return defaultValue; return defaultValue;
} }
@ -39,23 +46,27 @@ namespace Slippi {
uint32_t value = a[idx] << 24 | a[idx + 1] << 16 | a[idx + 2] << 8 | a[idx + 3]; uint32_t value = a[idx] << 24 | a[idx + 1] << 16 | a[idx + 2] << 8 | a[idx + 3];
idx += 4; idx += 4;
return value; return value;
} }
float readFloat(uint8_t* a, int& idx, uint32_t maxSize, float defaultValue) { float readFloat(uint8_t* a, int& idx, uint32_t maxSize, float defaultValue)
{
uint32_t bytes = readWord(a, idx, maxSize, *(uint32_t*)(&defaultValue)); uint32_t bytes = readWord(a, idx, maxSize, *(uint32_t*)(&defaultValue));
return *(float*)(&bytes); return *(float*)(&bytes);
} }
void handleGameInit(Game* game, uint32_t maxSize) { void handleGameInit(Game* game, uint32_t maxSize)
{
int idx = 0; int idx = 0;
// Read version number // Read version number
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++)
{
game->version[i] = readByte(data, idx, maxSize, 0); game->version[i] = readByte(data, idx, maxSize, 0);
} }
// Read entire game info header // Read entire game info header
for (int i = 0; i < GAME_INFO_HEADER_SIZE; i++) { for (int i = 0; i < GAME_INFO_HEADER_SIZE; i++)
{
game->settings.header[i] = readWord(data, idx, maxSize, 0); game->settings.header[i] = readWord(data, idx, maxSize, 0);
} }
@ -64,15 +75,18 @@ namespace Slippi {
// Read UCF toggle bytes // Read UCF toggle bytes
bool shouldRead = game->version[0] >= 1; bool shouldRead = game->version[0] >= 1;
for (int i = 0; i < UCF_TOGGLE_SIZE; i++) { for (int i = 0; i < UCF_TOGGLE_SIZE; i++)
{
uint32_t value = shouldRead ? readWord(data, idx, maxSize, 0) : 0; uint32_t value = shouldRead ? readWord(data, idx, maxSize, 0) : 0;
game->settings.ucfToggles[i] = value; game->settings.ucfToggles[i] = value;
} }
// Read nametag for each player // Read nametag for each player
std::array<std::array<uint16_t, NAMETAG_SIZE>, 4> playerNametags; std::array<std::array<uint16_t, NAMETAG_SIZE>, 4> playerNametags;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++)
for (int j = 0; j < NAMETAG_SIZE; j++) { {
for (int j = 0; j < NAMETAG_SIZE; j++)
{
playerNametags[i][j] = readHalf(data, idx, maxSize, 0); playerNametags[i][j] = readHalf(data, idx, maxSize, 0);
} }
} }
@ -86,13 +100,16 @@ namespace Slippi {
// Pull header data into struct // Pull header data into struct
int player1Pos = 24; // This is the index of the first players character info int player1Pos = 24; // This is the index of the first players character info
std::array<uint32_t, Slippi::GAME_INFO_HEADER_SIZE> gameInfoHeader = game->settings.header; std::array<uint32_t, Slippi::GAME_INFO_HEADER_SIZE> gameInfoHeader = game->settings.header;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++)
// this is the position in the array that this player's character info is stored {
// this is the position in the array that this player's character info is
// stored
int pos = player1Pos + (9 * i); int pos = player1Pos + (9 * i);
uint32_t playerInfo = gameInfoHeader[pos]; uint32_t playerInfo = gameInfoHeader[pos];
uint8_t playerType = (playerInfo & 0x00FF0000) >> 16; uint8_t playerType = (playerInfo & 0x00FF0000) >> 16;
if (playerType == 0x3) { if (playerType == 0x3)
{
// Player type 3 is an empty slot // Player type 3 is an empty slot
continue; continue;
} }
@ -106,7 +123,7 @@ namespace Slippi {
p.characterColor = playerInfo & 0xFF; p.characterColor = playerInfo & 0xFF;
p.nametag = playerNametags[i]; p.nametag = playerNametags[i];
//Add player settings to result // Add player settings to result
game->settings.players[i] = p; game->settings.players[i] = p;
} }
@ -114,32 +131,36 @@ namespace Slippi {
auto majorVersion = game->version[0]; auto majorVersion = game->version[0];
auto minorVersion = game->version[1]; auto minorVersion = game->version[1];
if (majorVersion > 3 || (majorVersion == 3 && minorVersion >= 1)) { if (majorVersion > 3 || (majorVersion == 3 && minorVersion >= 1))
{
// After version 3.1.0 we added a dynamic gecko loading process. These // After version 3.1.0 we added a dynamic gecko loading process. These
// are needed before starting the game. areSettingsLoaded will be set // are needed before starting the game. areSettingsLoaded will be set
// to true when they are received // to true when they are received
game->areSettingsLoaded = false; game->areSettingsLoaded = false;
} }
else if (majorVersion > 1 || (majorVersion == 1 && minorVersion >= 6)) { else if (majorVersion > 1 || (majorVersion == 1 && minorVersion >= 6))
{
// Indicate settings loaded immediately if after version 1.6.0 // Indicate settings loaded immediately if after version 1.6.0
// Sheik game info was added in this version and so we no longer // Sheik game info was added in this version and so we no longer
// need to wait // need to wait
game->areSettingsLoaded = true; game->areSettingsLoaded = true;
} }
} }
void handleGeckoList(Game* game, uint32_t maxSize) { void handleGeckoList(Game* game, uint32_t maxSize)
{
game->settings.geckoCodes.clear(); game->settings.geckoCodes.clear();
game->settings.geckoCodes.insert(game->settings.geckoCodes.end(), data, data + maxSize); game->settings.geckoCodes.insert(game->settings.geckoCodes.end(), data, data + maxSize);
// File is good to load // File is good to load
game->areSettingsLoaded = true; game->areSettingsLoaded = true;
} }
void handleFrameStart(Game* game, uint32_t maxSize) { void handleFrameStart(Game* game, uint32_t maxSize)
{
int idx = 0; int idx = 0;
//Check frame count // Check frame count
int32_t frameCount = readWord(data, idx, maxSize, 0); int32_t frameCount = readWord(data, idx, maxSize, 0);
game->frameCount = frameCount; game->frameCount = frameCount;
@ -155,12 +176,13 @@ namespace Slippi {
frame->numSinceStart = game->frames.size(); frame->numSinceStart = game->frames.size();
game->frames.push_back(std::move(frameUniquePtr)); game->frames.push_back(std::move(frameUniquePtr));
game->framesByIndex[frameCount] = frame; game->framesByIndex[frameCount] = frame;
} }
void handlePreFrameUpdate(Game* game, uint32_t maxSize) { void handlePreFrameUpdate(Game* game, uint32_t maxSize)
{
int idx = 0; int idx = 0;
//Check frame count // Check frame count
int32_t frameCount = readWord(data, idx, maxSize, 0); int32_t frameCount = readWord(data, idx, maxSize, 0);
game->frameCount = frameCount; game->frameCount = frameCount;
@ -168,7 +190,8 @@ namespace Slippi {
FrameData* frame = frameUniquePtr.get(); FrameData* frame = frameUniquePtr.get();
bool isNewFrame = true; bool isNewFrame = true;
if (game->framesByIndex.count(frameCount)) { if (game->framesByIndex.count(frameCount))
{
// If this frame already exists, get the current frame // If this frame already exists, get the current frame
frame = game->frames.back().get(); frame = game->frames.back().get();
isNewFrame = false; isNewFrame = false;
@ -181,16 +204,16 @@ namespace Slippi {
uint8_t playerSlot = readByte(data, idx, maxSize, 0); uint8_t playerSlot = readByte(data, idx, maxSize, 0);
uint8_t isFollower = readByte(data, idx, maxSize, 0); uint8_t isFollower = readByte(data, idx, maxSize, 0);
//Load random seed for player frame update // Load random seed for player frame update
p.randomSeed = readWord(data, idx, maxSize, 0); p.randomSeed = readWord(data, idx, maxSize, 0);
//Load player data // Load player data
p.animation = readHalf(data, idx, maxSize, 0); p.animation = readHalf(data, idx, maxSize, 0);
p.locationX = readFloat(data, idx, maxSize, 0); p.locationX = readFloat(data, idx, maxSize, 0);
p.locationY = readFloat(data, idx, maxSize, 0); p.locationY = readFloat(data, idx, maxSize, 0);
p.facingDirection = readFloat(data, idx, maxSize, 0); p.facingDirection = readFloat(data, idx, maxSize, 0);
//Controller information // Controller information
p.joystickX = readFloat(data, idx, maxSize, 0); p.joystickX = readFloat(data, idx, maxSize, 0);
p.joystickY = readFloat(data, idx, maxSize, 0); p.joystickY = readFloat(data, idx, maxSize, 0);
p.cstickX = readFloat(data, idx, maxSize, 0); p.cstickX = readFloat(data, idx, maxSize, 0);
@ -198,12 +221,13 @@ namespace Slippi {
p.trigger = readFloat(data, idx, maxSize, 0); p.trigger = readFloat(data, idx, maxSize, 0);
p.buttons = readWord(data, idx, maxSize, 0); p.buttons = readWord(data, idx, maxSize, 0);
//Raw controller information // Raw controller information
p.physicalButtons = readHalf(data, idx, maxSize, 0); p.physicalButtons = readHalf(data, idx, maxSize, 0);
p.lTrigger = readFloat(data, idx, maxSize, 0); p.lTrigger = readFloat(data, idx, maxSize, 0);
p.rTrigger = readFloat(data, idx, maxSize, 0); p.rTrigger = readFloat(data, idx, maxSize, 0);
if (asmEvents[EVENT_PRE_FRAME_UPDATE] >= 59) { if (asmEvents[EVENT_PRE_FRAME_UPDATE] >= 59)
{
p.joystickXRaw = readByte(data, idx, maxSize, 0); p.joystickXRaw = readByte(data, idx, maxSize, 0);
} }
@ -218,27 +242,31 @@ namespace Slippi {
target->operator[](playerSlot) = p; target->operator[](playerSlot) = p;
// Add frame to game // Add frame to game
if (isNewFrame) { if (isNewFrame)
{
frame->numSinceStart = game->frames.size(); frame->numSinceStart = game->frames.size();
game->frames.push_back(std::move(frameUniquePtr)); game->frames.push_back(std::move(frameUniquePtr));
game->framesByIndex[frameCount] = frame; game->framesByIndex[frameCount] = frame;
} }
} }
void handlePostFrameUpdate(Game* game, uint32_t maxSize) { void handlePostFrameUpdate(Game* game, uint32_t maxSize)
{
int idx = 0; int idx = 0;
//Check frame count // Check frame count
int32_t frameCount = readWord(data, idx, maxSize, 0); int32_t frameCount = readWord(data, idx, maxSize, 0);
FrameData* frame; FrameData* frame;
if (game->framesByIndex.count(frameCount)) { if (game->framesByIndex.count(frameCount))
{
// If this frame already exists, get the current frame // If this frame already exists, get the current frame
frame = game->frames.back().get(); frame = game->frames.back().get();
} }
// As soon as a post frame update happens, we know we have received all the inputs // As soon as a post frame update happens, we know we have received all the
// This is used to determine if a frame is ready to be used for a replay (for mirroring) // inputs This is used to determine if a frame is ready to be used for a
// replay (for mirroring)
frame->inputsFullyFetched = true; frame->inputsFullyFetched = true;
uint8_t playerSlot = readByte(data, idx, maxSize, 0); uint8_t playerSlot = readByte(data, idx, maxSize, 0);
@ -249,55 +277,67 @@ namespace Slippi {
p->internalCharacterId = readByte(data, idx, maxSize, 0); p->internalCharacterId = readByte(data, idx, maxSize, 0);
// Check if a player started as sheik and update // Check if a player started as sheik and update
if (frameCount == GAME_FIRST_FRAME && p->internalCharacterId == GAME_SHEIK_INTERNAL_ID) { if (frameCount == GAME_FIRST_FRAME && p->internalCharacterId == GAME_SHEIK_INTERNAL_ID)
{
game->settings.players[playerSlot].characterId = GAME_SHEIK_EXTERNAL_ID; game->settings.players[playerSlot].characterId = GAME_SHEIK_EXTERNAL_ID;
} }
// Set settings loaded if this is the last character // Set settings loaded if this is the last character
if (frameCount == GAME_FIRST_FRAME) { if (frameCount == GAME_FIRST_FRAME)
{
uint8_t lastPlayerIndex = 0; uint8_t lastPlayerIndex = 0;
for (auto it = frame->players.begin(); it != frame->players.end(); ++it) { for (auto it = frame->players.begin(); it != frame->players.end(); ++it)
if (it->first <= lastPlayerIndex) { {
if (it->first <= lastPlayerIndex)
{
continue; continue;
} }
lastPlayerIndex = it->first; lastPlayerIndex = it->first;
} }
if (playerSlot >= lastPlayerIndex) { if (playerSlot >= lastPlayerIndex)
{
game->areSettingsLoaded = true; game->areSettingsLoaded = true;
} }
} }
} }
void handleGameEnd(Game* game, uint32_t maxSize) { void handleGameEnd(Game* game, uint32_t maxSize)
{
int idx = 0; int idx = 0;
game->winCondition = readByte(data, idx, maxSize, 0); game->winCondition = readByte(data, idx, maxSize, 0);
} }
// This function gets the position where the raw data starts // This function gets the position where the raw data starts
int getRawDataPosition(std::ifstream* f) { int getRawDataPosition(std::ifstream* f)
{
char buffer[2]; char buffer[2];
f->seekg(0, std::ios::beg); f->seekg(0, std::ios::beg);
f->read(buffer, 2); f->read(buffer, 2);
if (buffer[0] == 0x36) { if (buffer[0] == 0x36)
{
return 0; return 0;
} }
if (buffer[0] != '{') { if (buffer[0] != '{')
{
// TODO: Do something here to cause an error // TODO: Do something here to cause an error
return 0; return 0;
} }
// TODO: Read ubjson file to find the "raw" element and return the start of it // TODO: Read ubjson file to find the "raw" element and return the start of it
// TODO: For now since raw is the first element the data will always start at 15 // TODO: For now since raw is the first element the data will always start at
// 15
return 15; return 15;
} }
uint32_t getRawDataLength(std::ifstream* f, int position, int fileSize) { uint32_t getRawDataLength(std::ifstream* f, int position, int fileSize)
if (position == 0) { {
if (position == 0)
{
return fileSize; return fileSize;
} }
@ -308,25 +348,25 @@ namespace Slippi {
uint8_t* byteBuf = (uint8_t*)&buffer[0]; uint8_t* byteBuf = (uint8_t*)&buffer[0];
uint32_t length = byteBuf[0] << 24 | byteBuf[1] << 16 | byteBuf[2] << 8 | byteBuf[3]; uint32_t length = byteBuf[0] << 24 | byteBuf[1] << 16 | byteBuf[2] << 8 | byteBuf[3];
return length; return length;
} }
std::unordered_map<uint8_t, uint32_t> getMessageSizes(std::ifstream* f, int position) { std::unordered_map<uint8_t, uint32_t> getMessageSizes(std::ifstream* f, int position)
{
char buffer[2]; char buffer[2];
f->seekg(position, std::ios::beg); f->seekg(position, std::ios::beg);
f->read(buffer, 2); f->read(buffer, 2);
if (buffer[0] != EVENT_PAYLOAD_SIZES) { if (buffer[0] != EVENT_PAYLOAD_SIZES)
{
return {}; return {};
} }
int payloadLength = buffer[1]; int payloadLength = buffer[1];
std::unordered_map<uint8_t, uint32_t> messageSizes = { std::unordered_map<uint8_t, uint32_t> messageSizes = {{EVENT_PAYLOAD_SIZES, payloadLength}};
{ EVENT_PAYLOAD_SIZES, payloadLength }
};
std::vector<char> messageSizesBuffer(payloadLength - 1); std::vector<char> messageSizesBuffer(payloadLength - 1);
f->read(&messageSizesBuffer[0], payloadLength - 1); f->read(&messageSizesBuffer[0], payloadLength - 1);
for (int i = 0; i < payloadLength - 1; i += 3) { for (int i = 0; i < payloadLength - 1; i += 3)
{
uint8_t command = messageSizesBuffer[i]; uint8_t command = messageSizesBuffer[i];
// Extract the bytes in u8s. Without this the chars don't or together well // Extract the bytes in u8s. Without this the chars don't or together well
@ -338,10 +378,12 @@ namespace Slippi {
} }
return messageSizes; return messageSizes;
} }
void SlippiGame::processData() { void SlippiGame::processData()
if (isProcessingComplete) { {
if (isProcessingComplete)
{
// If we have finished processing this file, return // If we have finished processing this file, return
return; return;
} }
@ -349,17 +391,20 @@ namespace Slippi {
// This function will process as much data as possible // This function will process as much data as possible
int startPos = (int)file->tellg(); int startPos = (int)file->tellg();
file->seekg(startPos); file->seekg(startPos);
if (startPos == 0) { if (startPos == 0)
{
file->seekg(0, std::ios::end); file->seekg(0, std::ios::end);
int len = (int)file->tellg(); int len = (int)file->tellg();
if (len < 2) { if (len < 2)
{
// If we can't read message sizes payload size yet, return // If we can't read message sizes payload size yet, return
return; return;
} }
int rawDataPos = getRawDataPosition(file.get()); int rawDataPos = getRawDataPosition(file.get());
int rawDataLen = len - rawDataPos; int rawDataLen = len - rawDataPos;
if (rawDataLen < 2) { if (rawDataLen < 2)
{
// If we don't have enough raw data yet to read the replay file, return // If we don't have enough raw data yet to read the replay file, return
// Reset to begining so that the startPos condition will be hit again // Reset to begining so that the startPos condition will be hit again
file->seekg(0); file->seekg(0);
@ -373,7 +418,8 @@ namespace Slippi {
file->read(buffer, 2); file->read(buffer, 2);
file->seekg(startPos); file->seekg(startPos);
auto messageSizesSize = (int)buffer[1]; auto messageSizesSize = (int)buffer[1];
if (rawDataLen < messageSizesSize) { if (rawDataLen < messageSizesSize)
{
// If we haven't received the full payload sizes message, return // If we haven't received the full payload sizes message, return
// Reset to begining so that the startPos condition will be hit again // Reset to begining so that the startPos condition will be hit again
file->seekg(0); file->seekg(0);
@ -388,10 +434,11 @@ namespace Slippi {
int endPos = (int)file->tellg(); int endPos = (int)file->tellg();
int sizeToRead = endPos - startPos; int sizeToRead = endPos - startPos;
file->seekg(startPos); file->seekg(startPos);
//log << "Size to read: " << sizeToRead << "\n"; // log << "Size to read: " << sizeToRead << "\n";
//log << "Start Pos: " << startPos << "\n"; // log << "Start Pos: " << startPos << "\n";
//log << "End Pos: " << endPos << "\n\n"; // log << "End Pos: " << endPos << "\n\n";
if (sizeToRead <= 0) { if (sizeToRead <= 0)
{
return; return;
} }
@ -399,16 +446,18 @@ namespace Slippi {
file->read(&newData[0], sizeToRead); file->read(&newData[0], sizeToRead);
int newDataPos = 0; int newDataPos = 0;
while (newDataPos < sizeToRead) { while (newDataPos < sizeToRead)
{
auto command = newData[newDataPos]; auto command = newData[newDataPos];
auto payloadSize = asmEvents[command]; auto payloadSize = asmEvents[command];
//char buff[100]; // char buff[100];
//snprintf(buff, sizeof(buff), "%x", command); // snprintf(buff, sizeof(buff), "%x", command);
//log << "Command: " << buff << " | Payload Size: " << payloadSize << "\n"; // log << "Command: " << buff << " | Payload Size: " << payloadSize << "\n";
auto remainingLen = sizeToRead - newDataPos; auto remainingLen = sizeToRead - newDataPos;
if (remainingLen < ((int)payloadSize + 1)) { if (remainingLen < ((int)payloadSize + 1))
{
// Here we don't have enough data to read the whole payload // Here we don't have enough data to read the whole payload
// Will be processed after getting more data (hopefully) // Will be processed after getting more data (hopefully)
file->seekg(-remainingLen, std::ios::cur); file->seekg(-remainingLen, std::ios::cur);
@ -421,7 +470,8 @@ namespace Slippi {
uint32_t outerPayloadSize = payloadSize; uint32_t outerPayloadSize = payloadSize;
// Handle a split message, combining in until we possess the entire message // Handle a split message, combining in until we possess the entire message
if (command == EVENT_SPLIT_MESSAGE) { if (command == EVENT_SPLIT_MESSAGE)
{
if (shouldResetSplitMessageBuf) if (shouldResetSplitMessageBuf)
{ {
splitMessageBuf.clear(); splitMessageBuf.clear();
@ -443,7 +493,8 @@ namespace Slippi {
} }
} }
switch (command) { switch (command)
{
case EVENT_GAME_INIT: case EVENT_GAME_INIT:
handleGameInit(game.get(), payloadSize); handleGameInit(game.get(), payloadSize);
break; break;
@ -468,7 +519,7 @@ namespace Slippi {
// from processing the metadata as raw data. 0x55 is 'U' // from processing the metadata as raw data. 0x55 is 'U'
// which is the first character after the raw data in the // which is the first character after the raw data in the
// ubjson file format // ubjson file format
//log.close(); // log.close();
isProcessingComplete = true; isProcessingComplete = true;
file->seekg(-remainingLen, std::ios::cur); file->seekg(-remainingLen, std::ios::cur);
return; return;
@ -477,9 +528,10 @@ namespace Slippi {
payloadSize = isSplitComplete ? outerPayloadSize : payloadSize; payloadSize = isSplitComplete ? outerPayloadSize : payloadSize;
newDataPos += payloadSize + 1; newDataPos += payloadSize + 1;
} }
} }
std::unique_ptr<SlippiGame> SlippiGame::FromFile(std::string path) { std::unique_ptr<SlippiGame> SlippiGame::FromFile(std::string path)
{
auto result = std::make_unique<SlippiGame>(); auto result = std::make_unique<SlippiGame>();
result->game = std::make_unique<Game>(); result->game = std::make_unique<Game>();
result->path = path; result->path = path;
@ -492,69 +544,79 @@ namespace Slippi {
result->file = std::make_unique<std::ifstream>(path, std::ios::in | std::ios::binary); result->file = std::make_unique<std::ifstream>(path, std::ios::in | std::ios::binary);
#endif #endif
//result->log.open("log.txt"); // result->log.open("log.txt");
if (!result->file->is_open()) { if (!result->file->is_open())
{
return nullptr; return nullptr;
} }
//int fileLength = (int)file.tellg(); // int fileLength = (int)file.tellg();
//int rawDataPos = getRawDataPosition(&file); // int rawDataPos = getRawDataPosition(&file);
//uint32_t rawDataLength = getRawDataLength(&file, rawDataPos, fileLength); // uint32_t rawDataLength = getRawDataLength(&file, rawDataPos, fileLength);
//asmEvents = getMessageSizes(&file, rawDataPos); // asmEvents = getMessageSizes(&file, rawDataPos);
//std::vector<char> rawData(rawDataLength); // std::vector<char> rawData(rawDataLength);
//file.seekg(rawDataPos, std::ios::beg); // file.seekg(rawDataPos, std::ios::beg);
//file.read(&rawData[0], rawDataLength); // file.read(&rawData[0], rawDataLength);
//SlippiGame* result = processFile((uint8_t*)&rawData[0], rawDataLength); // SlippiGame* result = processFile((uint8_t*)&rawData[0], rawDataLength);
return std::move(result); return std::move(result);
} }
bool SlippiGame::IsProcessingComplete() { bool SlippiGame::IsProcessingComplete()
{
return isProcessingComplete; return isProcessingComplete;
} }
bool SlippiGame::AreSettingsLoaded() { bool SlippiGame::AreSettingsLoaded()
{
processData(); processData();
return game->areSettingsLoaded; return game->areSettingsLoaded;
} }
bool SlippiGame::DoesFrameExist(int32_t frame) { bool SlippiGame::DoesFrameExist(int32_t frame)
{
processData(); processData();
return (bool)game->framesByIndex.count(frame); return (bool)game->framesByIndex.count(frame);
} }
std::array<uint8_t, 4> SlippiGame::GetVersion() std::array<uint8_t, 4> SlippiGame::GetVersion()
{ {
return game->version; return game->version;
} }
FrameData* SlippiGame::GetFrame(int32_t frame) { FrameData* SlippiGame::GetFrame(int32_t frame)
{
// Get the frame we want // Get the frame we want
return game->framesByIndex.at(frame); return game->framesByIndex.at(frame);
} }
FrameData* SlippiGame::GetFrameAt(uint32_t pos) { FrameData* SlippiGame::GetFrameAt(uint32_t pos)
if (pos >= game->frames.size()) { {
if (pos >= game->frames.size())
{
return nullptr; return nullptr;
} }
// Get the frame we want // Get the frame we want
return game->frames[pos].get(); return game->frames[pos].get();
} }
int32_t SlippiGame::GetLatestIndex() { int32_t SlippiGame::GetLatestIndex()
{
processData(); processData();
return game->frameCount; return game->frameCount;
} }
GameSettings* SlippiGame::GetSettings() { GameSettings* SlippiGame::GetSettings()
{
processData(); processData();
return &game->settings; return &game->settings;
}
bool SlippiGame::DoesPlayerExist(int8_t port) {
return game->settings.players.find(port) != game->settings.players.end();
}
} }
bool SlippiGame::DoesPlayerExist(int8_t port)
{
return game->settings.players.find(port) != game->settings.players.end();
}
} // namespace Slippi

View file

@ -1,37 +1,40 @@
#pragma once #pragma once
#include <string>
#include <array> #include <array>
#include <vector>
#include <unordered_map>
#include <iostream>
#include <fstream> #include <fstream>
#include <iostream>
#include <memory> #include <memory>
#include <string>
#include <unordered_map>
#include <vector>
namespace Slippi { namespace Slippi
const uint8_t EVENT_SPLIT_MESSAGE = 0x10; {
const uint8_t EVENT_PAYLOAD_SIZES = 0x35; const uint8_t EVENT_SPLIT_MESSAGE = 0x10;
const uint8_t EVENT_GAME_INIT = 0x36; const uint8_t EVENT_PAYLOAD_SIZES = 0x35;
const uint8_t EVENT_PRE_FRAME_UPDATE = 0x37; const uint8_t EVENT_GAME_INIT = 0x36;
const uint8_t EVENT_POST_FRAME_UPDATE = 0x38; const uint8_t EVENT_PRE_FRAME_UPDATE = 0x37;
const uint8_t EVENT_GAME_END = 0x39; const uint8_t EVENT_POST_FRAME_UPDATE = 0x38;
const uint8_t EVENT_FRAME_START = 0x3A; const uint8_t EVENT_GAME_END = 0x39;
const uint8_t EVENT_GECKO_LIST = 0x3D; const uint8_t EVENT_FRAME_START = 0x3A;
const uint8_t EVENT_GECKO_LIST = 0x3D;
const uint8_t GAME_INFO_HEADER_SIZE = 78; const uint8_t GAME_INFO_HEADER_SIZE = 78;
const uint8_t UCF_TOGGLE_SIZE = 8; const uint8_t UCF_TOGGLE_SIZE = 8;
const uint8_t NAMETAG_SIZE = 8; const uint8_t NAMETAG_SIZE = 8;
const int32_t GAME_FIRST_FRAME = -123; const int32_t GAME_FIRST_FRAME = -123;
const int32_t PLAYBACK_FIRST_SAVE = -122; const int32_t PLAYBACK_FIRST_SAVE = -122;
const uint8_t GAME_SHEIK_INTERNAL_ID = 0x7; const uint8_t GAME_SHEIK_INTERNAL_ID = 0x7;
const uint8_t GAME_SHEIK_EXTERNAL_ID = 0x13; const uint8_t GAME_SHEIK_EXTERNAL_ID = 0x13;
const uint32_t SPLIT_MESSAGE_INTERNAL_DATA_LEN = 512; const uint32_t SPLIT_MESSAGE_INTERNAL_DATA_LEN = 512;
static uint8_t* data; static uint8_t* data;
typedef struct { typedef struct
// Every player update has its own rng seed because it might change in between players {
// Every player update has its own rng seed because it might change in between
// players
uint32_t randomSeed; uint32_t randomSeed;
uint8_t internalCharacterId; uint8_t internalCharacterId;
@ -46,23 +49,26 @@ namespace Slippi {
uint8_t comboCount; uint8_t comboCount;
uint8_t lastHitBy; uint8_t lastHitBy;
//Controller information // Controller information
float joystickX; float joystickX;
float joystickY; float joystickY;
float cstickX; float cstickX;
float cstickY; float cstickY;
float trigger; float trigger;
uint32_t buttons; //This will include multiple "buttons" pressed on special buttons. For example I think pressing z sets 3 bits uint32_t buttons; // This will include multiple "buttons" pressed on special
// buttons. For example I think pressing z sets 3 bits
//This is extra controller information // This is extra controller information
uint16_t physicalButtons; //A better representation of what a player is actually pressing uint16_t physicalButtons; // A better representation of what a player is
// actually pressing
float lTrigger; float lTrigger;
float rTrigger; float rTrigger;
uint8_t joystickXRaw; uint8_t joystickXRaw;
} PlayerFrameData; } PlayerFrameData;
typedef struct FrameData { typedef struct FrameData
{
int32_t frame; int32_t frame;
uint32_t numSinceStart; uint32_t numSinceStart;
bool randomSeedExists = false; bool randomSeedExists = false;
@ -70,19 +76,21 @@ namespace Slippi {
bool inputsFullyFetched = false; bool inputsFullyFetched = false;
std::unordered_map<uint8_t, PlayerFrameData> players; std::unordered_map<uint8_t, PlayerFrameData> players;
std::unordered_map<uint8_t, PlayerFrameData> followers; std::unordered_map<uint8_t, PlayerFrameData> followers;
} FrameData; } FrameData;
typedef struct { typedef struct
//Static data {
// Static data
uint8_t characterId; uint8_t characterId;
uint8_t characterColor; uint8_t characterColor;
uint8_t playerType; uint8_t playerType;
uint8_t controllerPort; uint8_t controllerPort;
std::array<uint16_t, NAMETAG_SIZE> nametag; std::array<uint16_t, NAMETAG_SIZE> nametag;
} PlayerSettings; } PlayerSettings;
typedef struct { typedef struct
uint16_t stage; //Stage ID {
uint16_t stage; // Stage ID
uint32_t randomSeed; uint32_t randomSeed;
std::array<uint32_t, GAME_INFO_HEADER_SIZE> header; std::array<uint32_t, GAME_INFO_HEADER_SIZE> header;
std::array<uint32_t, UCF_TOGGLE_SIZE> ucfToggles; std::array<uint32_t, UCF_TOGGLE_SIZE> ucfToggles;
@ -90,9 +98,10 @@ namespace Slippi {
uint8_t isPAL; uint8_t isPAL;
uint8_t isFrozenPS; uint8_t isFrozenPS;
std::vector<uint8_t> geckoCodes; std::vector<uint8_t> geckoCodes;
} GameSettings; } GameSettings;
typedef struct Game { typedef struct Game
{
std::array<uint8_t, 4> version; std::array<uint8_t, 4> version;
std::unordered_map<int32_t, FrameData*> framesByIndex; std::unordered_map<int32_t, FrameData*> framesByIndex;
std::vector<std::unique_ptr<FrameData>> frames; std::vector<std::unique_ptr<FrameData>> frames;
@ -101,23 +110,21 @@ namespace Slippi {
int32_t frameCount; // Current/last frame count int32_t frameCount; // Current/last frame count
//From OnGameEnd event // From OnGameEnd event
uint8_t winCondition; uint8_t winCondition;
} Game; } Game;
// TODO: This shouldn't be static. Doesn't matter too much atm because we always // TODO: This shouldn't be static. Doesn't matter too much atm because we always
// TODO: only read one file at a time // TODO: only read one file at a time
static std::unordered_map<uint8_t, uint32_t> asmEvents = { static std::unordered_map<uint8_t, uint32_t> asmEvents = {{EVENT_GAME_INIT, 320},
{ EVENT_GAME_INIT, 320 }, {EVENT_PRE_FRAME_UPDATE, 58},
{ EVENT_PRE_FRAME_UPDATE, 58 }, {EVENT_POST_FRAME_UPDATE, 33},
{ EVENT_POST_FRAME_UPDATE, 33 }, {EVENT_GAME_END, 1},
{ EVENT_GAME_END, 1 }, {EVENT_FRAME_START, 8}};
{ EVENT_FRAME_START, 8 }
};
class SlippiGame class SlippiGame
{ {
public: public:
static std::unique_ptr<SlippiGame> FromFile(std::string path); static std::unique_ptr<SlippiGame> FromFile(std::string path);
bool AreSettingsLoaded(); bool AreSettingsLoaded();
bool DoesFrameExist(int32_t frame); bool DoesFrameExist(int32_t frame);
@ -128,7 +135,8 @@ namespace Slippi {
GameSettings* GetSettings(); GameSettings* GetSettings();
bool DoesPlayerExist(int8_t port); bool DoesPlayerExist(int8_t port);
bool IsProcessingComplete(); bool IsProcessingComplete();
private:
private:
std::unique_ptr<Game> game; std::unique_ptr<Game> game;
std::unique_ptr<std::ifstream> file; std::unique_ptr<std::ifstream> file;
std::vector<uint8_t> rawData; std::vector<uint8_t> rawData;
@ -139,5 +147,5 @@ namespace Slippi {
bool isProcessingComplete = false; bool isProcessingComplete = false;
void processData(); void processData();
}; };
} } // namespace Slippi

File diff suppressed because it is too large Load diff

View file

@ -7,9 +7,8 @@
#include <SlippiGame.h> #include <SlippiGame.h>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "Common/File.h" #include "Common/File.h"
#include "EXI_Device.h" #include "Common/FileUtil.h"
#include "Core/Slippi/SlippiGameFileLoader.h" #include "Core/Slippi/SlippiGameFileLoader.h"
#include "Core/Slippi/SlippiMatchmaking.h" #include "Core/Slippi/SlippiMatchmaking.h"
#include "Core/Slippi/SlippiNetplay.h" #include "Core/Slippi/SlippiNetplay.h"
@ -17,6 +16,7 @@
#include "Core/Slippi/SlippiReplayComm.h" #include "Core/Slippi/SlippiReplayComm.h"
#include "Core/Slippi/SlippiSavestate.h" #include "Core/Slippi/SlippiSavestate.h"
#include "Core/Slippi/SlippiUser.h" #include "Core/Slippi/SlippiUser.h"
#include "EXI_Device.h"
#define ROLLBACK_MAX_FRAMES 7 #define ROLLBACK_MAX_FRAMES 7
#define MAX_NAME_LENGTH 15 #define MAX_NAME_LENGTH 15
@ -24,10 +24,10 @@
namespace ExpansionInterface namespace ExpansionInterface
{ {
// Emulated Slippi device used to receive and respond to in-game messages // Emulated Slippi device used to receive and respond to in-game messages
class CEXISlippi : public IEXIDevice class CEXISlippi : public IEXIDevice
{ {
public: public:
CEXISlippi(); CEXISlippi();
virtual ~CEXISlippi(); virtual ~CEXISlippi();
@ -36,7 +36,7 @@ namespace ExpansionInterface
bool IsPresent() const override; bool IsPresent() const override;
private: private:
enum enum
{ {
CMD_UNKNOWN = 0x0, CMD_UNKNOWN = 0x0,
@ -208,10 +208,10 @@ namespace ExpansionInterface
int framesToSkip = 0; int framesToSkip = 0;
bool isCurrentlySkipping = false; bool isCurrentlySkipping = false;
protected: protected:
void TransferByte(u8& byte) override; void TransferByte(u8& byte) override;
private: private:
SlippiPlayerSelections localSelections; SlippiPlayerSelections localSelections;
std::unique_ptr<SlippiUser> user; std::unique_ptr<SlippiUser> user;
@ -221,5 +221,5 @@ namespace ExpansionInterface
std::map<s32, std::unique_ptr<SlippiSavestate>> activeSavestates; std::map<s32, std::unique_ptr<SlippiSavestate>> activeSavestates;
std::deque<std::unique_ptr<SlippiSavestate>> availableSavestates; std::deque<std::unique_ptr<SlippiSavestate>> availableSavestates;
}; };
} } // namespace ExpansionInterface

View file

@ -1,12 +1,12 @@
#include "SlippiGameFileLoader.h" #include "SlippiGameFileLoader.h"
#include "Common/Logging/Log.h"
#include "Common/FileUtil.h"
#include "Common/File.h" #include "Common/File.h"
#include "Core/HW/DVD/DVDThread.h" #include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Core/Boot/Boot.h" #include "Core/Boot/Boot.h"
#include "Core/Core.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/HW/DVD/DVDThread.h"
std::string getFilePath(std::string fileName) std::string getFilePath(std::string fileName)
{ {
@ -49,8 +49,10 @@ u32 SlippiGameFileLoader::LoadFile(std::string fileName, std::string& data)
std::string fileContents; std::string fileContents;
File::ReadFileToString(gameFilePath, fileContents); File::ReadFileToString(gameFilePath, fileContents);
// If the file was a diff file and the game is running, load the main file from ISO and apply patch // If the file was a diff file and the game is running, load the main file from ISO and apply
if (gameFilePath.substr(gameFilePath.length() - 5) == ".diff" && Core::GetState() == Core::State::Running) // patch
if (gameFilePath.substr(gameFilePath.length() - 5) == ".diff" &&
Core::GetState() == Core::State::Running)
{ {
std::vector<u8> buf; std::vector<u8> buf;
INFO_LOG(SLIPPI, "Will process diff"); INFO_LOG(SLIPPI, "Will process diff");

View file

@ -1,10 +1,10 @@
#pragma once #pragma once
#include "Common/CommonTypes.h"
#include <open-vcdiff/src/google/vcdecoder.h> #include <open-vcdiff/src/google/vcdecoder.h>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include "Common/CommonTypes.h"
class SlippiGameFileLoader class SlippiGameFileLoader
{ {

View file

@ -1,11 +1,11 @@
#include "SlippiMatchmaking.h" #include "SlippiMatchmaking.h"
#include <string>
#include <vector>
#include "Common/Common.h" #include "Common/Common.h"
#include "Common/Version.h"
#include "Common/ENetUtil.h" #include "Common/ENetUtil.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
#include <string> #include "Common/Version.h"
#include <vector>
class MmMessageType class MmMessageType
{ {
@ -28,7 +28,8 @@ SlippiMatchmaking::SlippiMatchmaking(SlippiUser* user)
m_client = nullptr; m_client = nullptr;
m_server = nullptr; m_server = nullptr;
MM_HOST = Common::scm_slippi_semver_str.find("dev") == std::string::npos ? MM_HOST_PROD : MM_HOST_DEV; MM_HOST =
Common::scm_slippi_semver_str.find("dev") == std::string::npos ? MM_HOST_PROD : MM_HOST_DEV;
generator = std::default_random_engine(Common::Timer::GetTimeMs()); generator = std::default_random_engine(Common::Timer::GetTimeMs());
} }
@ -111,7 +112,8 @@ int SlippiMatchmaking::receiveMessage(json& msg, int timeoutMs)
case ENET_EVENT_TYPE_RECEIVE: case ENET_EVENT_TYPE_RECEIVE:
{ {
std::vector<u8> buf; std::vector<u8> buf;
buf.insert(buf.end(), netEvent.packet->data, netEvent.packet->data + netEvent.packet->dataLength); buf.insert(buf.end(), netEvent.packet->data,
netEvent.packet->data + netEvent.packet->dataLength);
std::string str(buf.begin(), buf.end()); std::string str(buf.begin(), buf.end());
INFO_LOG(SLIPPI_ONLINE, "[Matchmaking] Received: %s", str.c_str()); INFO_LOG(SLIPPI_ONLINE, "[Matchmaking] Received: %s", str.c_str());
@ -286,8 +288,8 @@ void SlippiMatchmaking::startMatchmaking()
// Send message to server to create ticket // Send message to server to create ticket
json request; json request;
request["type"] = MmMessageType::CREATE_TICKET; request["type"] = MmMessageType::CREATE_TICKET;
request["user"] = { {"uid", userInfo.uid}, {"playKey", userInfo.playKey} }; request["user"] = {{"uid", userInfo.uid}, {"playKey", userInfo.playKey}};
request["search"] = { {"mode", m_searchSettings.mode}, {"connectCode", connectCodeBuf} }; request["search"] = {{"mode", m_searchSettings.mode}, {"connectCode", connectCodeBuf}};
request["appVersion"] = Common::scm_slippi_semver_str; request["appVersion"] = Common::scm_slippi_semver_str;
sendMessage(request); sendMessage(request);
@ -296,7 +298,8 @@ void SlippiMatchmaking::startMatchmaking()
int rcvRes = receiveMessage(response, 5000); int rcvRes = receiveMessage(response, 5000);
if (rcvRes != 0) if (rcvRes != 0)
{ {
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Did not receive response from server for create ticket"); ERROR_LOG(SLIPPI_ONLINE,
"[Matchmaking] Did not receive response from server for create ticket");
m_state = ProcessState::ERROR_ENCOUNTERED; m_state = ProcessState::ERROR_ENCOUNTERED;
m_errorMsg = "Failed to join mm queue"; m_errorMsg = "Failed to join mm queue";
return; return;
@ -364,7 +367,8 @@ void SlippiMatchmaking::handleMatchmaking()
if (latestVersion != "") if (latestVersion != "")
{ {
// Update file to get new version number when the mm server tells us our version is outdated // Update file to get new version number when the mm server tells us our version is outdated
m_user->OverwriteLatestVersion(latestVersion); // Force latest version for people whose file updates dont work m_user->OverwriteLatestVersion(
latestVersion); // Force latest version for people whose file updates dont work
} }
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Received error from server for get ticket"); ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Received error from server for get ticket");
@ -382,7 +386,8 @@ void SlippiMatchmaking::handleMatchmaking()
terminateMmConnection(); terminateMmConnection();
m_state = ProcessState::OPPONENT_CONNECTING; m_state = ProcessState::OPPONENT_CONNECTING;
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Opponent found. isDecider: %s", m_isHost ? "true" : "false"); ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Opponent found. isDecider: %s",
m_isHost ? "true" : "false");
} }
void SlippiMatchmaking::handleConnecting() void SlippiMatchmaking::handleConnecting()
@ -390,7 +395,8 @@ void SlippiMatchmaking::handleConnecting()
std::vector<std::string> ipParts = SplitString(m_oppIp, ':'); std::vector<std::string> ipParts = SplitString(m_oppIp, ':');
// Is host is now used to specify who the decider is // Is host is now used to specify who the decider is
auto client = std::make_unique<SlippiNetplayClient>(ipParts[0], std::stoi(ipParts[1]), m_hostPort, m_isHost); auto client = std::make_unique<SlippiNetplayClient>(ipParts[0], std::stoi(ipParts[1]), m_hostPort,
m_isHost);
while (!m_netplayClient) while (!m_netplayClient)
{ {
@ -408,7 +414,8 @@ void SlippiMatchmaking::handleConnecting()
} }
else if (status != SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED) else if (status != SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED)
{ {
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Connection attempt failed, looking for someone else."); ERROR_LOG(SLIPPI_ONLINE,
"[Matchmaking] Connection attempt failed, looking for someone else.");
// Return to the start to get a new ticket to find someone else we can hopefully connect with // Return to the start to get a new ticket to find someone else we can hopefully connect with
m_netplayClient = nullptr; m_netplayClient = nullptr;

View file

@ -6,9 +6,9 @@
#include "Core/Slippi/SlippiUser.h" #include "Core/Slippi/SlippiUser.h"
#include <enet/enet.h> #include <enet/enet.h>
#include <random>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <random>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
using json = nlohmann::json; using json = nlohmann::json;

View file

@ -11,10 +11,10 @@
#include "Common/MD5.h" #include "Common/MD5.h"
#include "Common/MsgHandler.h" #include "Common/MsgHandler.h"
#include "Common/Timer.h" #include "Common/Timer.h"
#include "Core/ConfigManager.h"
#include "Core/NetPlayProto.h"
#include "Core/Core.h"
#include "Core/Config/NetplaySettings.h" #include "Core/Config/NetplaySettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/NetPlayProto.h"
//#include "Core/HW/EXI_DeviceIPL.h" //#include "Core/HW/EXI_DeviceIPL.h"
//#include "Core/HW/SI.h" //#include "Core/HW/SI.h"
//#include "Core/HW/SI_DeviceGCController.h" //#include "Core/HW/SI_DeviceGCController.h"
@ -22,16 +22,16 @@
#include "Core/HW/WiimoteEmu/WiimoteEmu.h" #include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h" #include "Core/HW/WiimoteReal/WiimoteReal.h"
//#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb_bt_emu.h" //#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb_bt_emu.h"
#include "Core/Movie.h"
#include "InputCommon/GCAdapter.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/VideoConfig.h"
#include <SlippiGame.h> #include <SlippiGame.h>
#include <algorithm> #include <algorithm>
#include <fstream> #include <fstream>
#include <mbedtls/md5.h> #include <mbedtls/md5.h>
#include <memory> #include <memory>
#include <thread> #include <thread>
#include "Core/Movie.h"
#include "InputCommon/GCAdapter.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/VideoConfig.h"
static std::mutex pad_mutex; static std::mutex pad_mutex;
static std::mutex ack_mutex; static std::mutex ack_mutex;
@ -62,11 +62,10 @@ SlippiNetplayClient::~SlippiNetplayClient()
} }
// called from ---SLIPPI EXI--- thread // called from ---SLIPPI EXI--- thread
SlippiNetplayClient::SlippiNetplayClient(const std::string& address, const u16 remotePort, const u16 localPort, SlippiNetplayClient::SlippiNetplayClient(const std::string& address, const u16 remotePort,
bool isDecider) const u16 localPort, bool isDecider)
#ifdef _WIN32 #ifdef _WIN32
: m_qos_handle(nullptr) : m_qos_handle(nullptr), m_qos_flow_id(0)
, m_qos_flow_id(0)
#endif #endif
{ {
WARN_LOG(SLIPPI_ONLINE, "Initializing Slippi Netplay for port: %d, with host: %s", localPort, WARN_LOG(SLIPPI_ONLINE, "Initializing Slippi Netplay for port: %d, with host: %s", localPort,
@ -78,9 +77,10 @@ SlippiNetplayClient::SlippiNetplayClient(const std::string& address, const u16 r
ENetAddress* localAddr = nullptr; ENetAddress* localAddr = nullptr;
ENetAddress localAddrDef; ENetAddress localAddrDef;
// It is important to be able to set the local port to listen on even in a client connection because // It is important to be able to set the local port to listen on even in a client connection
// not doing so will break hole punching, the host is expecting traffic to come from a specific ip/port // because not doing so will break hole punching, the host is expecting traffic to come from a
// and if the port does not match what it is expecting, it will not get through the NAT on some routers // specific ip/port and if the port does not match what it is expecting, it will not get through
// the NAT on some routers
if (localPort > 0) if (localPort > 0)
{ {
INFO_LOG(SLIPPI_ONLINE, "Setting up local address"); INFO_LOG(SLIPPI_ONLINE, "Setting up local address");
@ -91,7 +91,8 @@ SlippiNetplayClient::SlippiNetplayClient(const std::string& address, const u16 r
localAddr = &localAddrDef; localAddr = &localAddrDef;
} }
// TODO: Figure out how to use a local port when not hosting without accepting incoming connections // TODO: Figure out how to use a local port when not hosting without accepting incoming
// connections
m_client = enet_host_create(localAddr, 2, 3, 0, 0); m_client = enet_host_create(localAddr, 2, 3, 0, 0);
if (m_client == nullptr) if (m_client == nullptr)
@ -126,7 +127,8 @@ SlippiNetplayClient::SlippiNetplayClient(bool isDecider)
unsigned int SlippiNetplayClient::OnData(sf::Packet& packet) unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
{ {
NetPlay::MessageId mid = 0; NetPlay::MessageId mid = 0;
if (!(packet >> mid)) { if (!(packet >> mid))
{
ERROR_LOG(SLIPPI_ONLINE, "Received empty netplay packet"); ERROR_LOG(SLIPPI_ONLINE, "Received empty netplay packet");
return 0; return 0;
} }
@ -136,7 +138,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
case NetPlay::NP_MSG_SLIPPI_PAD: case NetPlay::NP_MSG_SLIPPI_PAD:
{ {
int32_t frame; int32_t frame;
if (!(packet >> frame)) { if (!(packet >> frame))
{
ERROR_LOG(SLIPPI_ONLINE, "Netplay packet too small to read frame count"); ERROR_LOG(SLIPPI_ONLINE, "Netplay packet too small to read frame count");
break; break;
} }
@ -151,8 +154,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
auto timing = lastFrameTiming; auto timing = lastFrameTiming;
if (!hasGameStarted) if (!hasGameStarted)
{ {
// Handle case where opponent starts sending inputs before our game has reached frame 1. This will // Handle case where opponent starts sending inputs before our game has reached frame 1. This
// continuously say frame 0 is now to prevent opp from getting too far ahead // will continuously say frame 0 is now to prevent opp from getting too far ahead
timing.frame = 0; timing.frame = 0;
timing.timeUs = curTime; timing.timeUs = curTime;
} }
@ -161,8 +164,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
s64 frameDiffOffsetUs = 16683 * (timing.frame - frame); s64 frameDiffOffsetUs = 16683 * (timing.frame - frame);
s64 timeOffsetUs = opponentSendTimeUs - timing.timeUs + frameDiffOffsetUs; s64 timeOffsetUs = opponentSendTimeUs - timing.timeUs + frameDiffOffsetUs;
INFO_LOG(SLIPPI_ONLINE, "[Offset] Opp Frame: %d, My Frame: %d. Time offset: %lld", frame, timing.frame, INFO_LOG(SLIPPI_ONLINE, "[Offset] Opp Frame: %d, My Frame: %d. Time offset: %lld", frame,
timeOffsetUs); timing.frame, timeOffsetUs);
// Add this offset to circular buffer for use later // Add this offset to circular buffer for use later
if (frameOffsetData.buf.size() < SLIPPI_ONLINE_LOCKSTEP_INTERVAL) if (frameOffsetData.buf.size() < SLIPPI_ONLINE_LOCKSTEP_INTERVAL)
@ -183,18 +186,20 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
int inputsToCopy = frame - headFrame; int inputsToCopy = frame - headFrame;
// Check that the packet actually contains the data it claims to // Check that the packet actually contains the data it claims to
if ((5 + inputsToCopy * SLIPPI_PAD_DATA_SIZE) > (int)packet.getDataSize()) { if ((5 + inputsToCopy * SLIPPI_PAD_DATA_SIZE) > (int)packet.getDataSize())
{
ERROR_LOG(SLIPPI_ONLINE, "Netplay packet too small to read pad buffer"); ERROR_LOG(SLIPPI_ONLINE, "Netplay packet too small to read pad buffer");
break; break;
} }
for (int i = inputsToCopy - 1; i >= 0; i--) for (int i = inputsToCopy - 1; i >= 0; i--)
{ {
auto pad = std::make_unique<SlippiPad>(frame - i, &packetData[5 + i * SLIPPI_PAD_DATA_SIZE]); auto pad =
std::make_unique<SlippiPad>(frame - i, &packetData[5 + i * SLIPPI_PAD_DATA_SIZE]);
INFO_LOG(SLIPPI_ONLINE, "Rcv [%d] -> %02X %02X %02X %02X %02X %02X %02X %02X", pad->frame, INFO_LOG(SLIPPI_ONLINE, "Rcv [%d] -> %02X %02X %02X %02X %02X %02X %02X %02X", pad->frame,
pad->padBuf[0], pad->padBuf[1], pad->padBuf[2], pad->padBuf[3], pad->padBuf[4], pad->padBuf[5], pad->padBuf[0], pad->padBuf[1], pad->padBuf[2], pad->padBuf[3], pad->padBuf[4],
pad->padBuf[6], pad->padBuf[7]); pad->padBuf[5], pad->padBuf[6], pad->padBuf[7]);
remotePadQueue.push_front(std::move(pad)); remotePadQueue.push_front(std::move(pad));
} }
@ -214,7 +219,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
// Store last frame acked // Store last frame acked
int32_t frame; int32_t frame;
if (!(packet >> frame)) { if (!(packet >> frame))
{
ERROR_LOG(SLIPPI_ONLINE, "Ack packet too small to read frame"); ERROR_LOG(SLIPPI_ONLINE, "Ack packet too small to read frame");
break; break;
} }
@ -239,8 +245,9 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
pingUs = Common::Timer::GetTimeUs() - sendTime; pingUs = Common::Timer::GetTimeUs() - sendTime;
if (g_ActiveConfig.bShowNetPlayPing && frame % SLIPPI_PING_DISPLAY_INTERVAL == 0) if (g_ActiveConfig.bShowNetPlayPing && frame % SLIPPI_PING_DISPLAY_INTERVAL == 0)
{ {
OSD::AddTypedMessage(OSD::MessageType::NetPlayPing, StringFromFormat("Ping: %u", pingUs / 1000), OSD::AddTypedMessage(OSD::MessageType::NetPlayPing,
OSD::Duration::NORMAL, OSD::Color::CYAN); StringFromFormat("Ping: %u", pingUs / 1000), OSD::Duration::NORMAL,
OSD::Color::CYAN);
} }
} }
break; break;
@ -259,7 +266,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
// This might be a good place to reset some logic? Game can't start until we receive this msg // This might be a good place to reset some logic? Game can't start until we receive this msg
// so this should ensure that everything is initialized before the game starts // so this should ensure that everything is initialized before the game starts
// TODO: This could cause issues in the case of a desync? If this is ever received mid-game, bad things // TODO: This could cause issues in the case of a desync? If this is ever received mid-game, bad
// things
// TODO: will happen. Consider improving this // TODO: will happen. Consider improving this
hasGameStarted = false; hasGameStarted = false;
} }
@ -267,7 +275,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
case NetPlay::NP_MSG_SLIPPI_CONN_SELECTED: case NetPlay::NP_MSG_SLIPPI_CONN_SELECTED:
{ {
// Currently this is unused but the intent is to support two-way simultaneous connection attempts // Currently this is unused but the intent is to support two-way simultaneous connection
// attempts
isConnectionSelected = true; isConnectionSelected = true;
} }
break; break;
@ -290,7 +299,8 @@ void SlippiNetplayClient::writeToPacket(sf::Packet& packet, SlippiPlayerSelectio
packet << s.connectCode; packet << s.connectCode;
} }
std::unique_ptr<SlippiPlayerSelections> SlippiNetplayClient::readSelectionsFromPacket(sf::Packet& packet) std::unique_ptr<SlippiPlayerSelections>
SlippiNetplayClient::readSelectionsFromPacket(sf::Packet& packet)
{ {
auto s = std::make_unique<SlippiPlayerSelections>(); auto s = std::make_unique<SlippiPlayerSelections>();
@ -405,12 +415,12 @@ void SlippiNetplayClient::ThreadFunc()
bool qos_success = false; bool qos_success = false;
#ifdef _WIN32 #ifdef _WIN32
QOS_VERSION ver = { 1, 0 }; QOS_VERSION ver = {1, 0};
if (Config::Get(Config::NETPLAY_ENABLE_QOS) && QOSCreateHandle(&ver, &m_qos_handle)) if (Config::Get(Config::NETPLAY_ENABLE_QOS) && QOSCreateHandle(&ver, &m_qos_handle))
{ {
// from win32.c // from win32.c
struct sockaddr_in sin = { 0 }; struct sockaddr_in sin = {0};
sin.sin_family = AF_INET; sin.sin_family = AF_INET;
sin.sin_port = ENET_HOST_TO_NET_16(m_server->host->address.port); sin.sin_port = ENET_HOST_TO_NET_16(m_server->host->address.port);
@ -424,7 +434,8 @@ void SlippiNetplayClient::ThreadFunc()
// this will fail if we're not admin // this will fail if we're not admin
// sets DSCP to the same as linux (0x2e) // sets DSCP to the same as linux (0x2e)
QOSSetFlow(m_qos_handle, m_qos_flow_id, QOSSetOutgoingDSCPValue, sizeof(DWORD), &dscp, 0, nullptr); QOSSetFlow(m_qos_handle, m_qos_flow_id, QOSSetOutgoingDSCPValue, sizeof(DWORD), &dscp, 0,
nullptr);
qos_success = true; qos_success = true;
} }
@ -441,7 +452,8 @@ void SlippiNetplayClient::ThreadFunc()
// https://www.tucny.com/Home/dscp-tos // https://www.tucny.com/Home/dscp-tos
// ef is better than cs7 // ef is better than cs7
int tos_val = 0xb8; int tos_val = 0xb8;
qos_success = setsockopt(m_server->host->socket, IPPROTO_IP, IP_TOS, &tos_val, sizeof(tos_val)) == 0; qos_success =
setsockopt(m_server->host->socket, IPPROTO_IP, IP_TOS, &tos_val, sizeof(tos_val)) == 0;
} }
#endif #endif
@ -550,8 +562,10 @@ void SlippiNetplayClient::SendConnectionSelected()
void SlippiNetplayClient::SendSlippiPad(std::unique_ptr<SlippiPad> pad) void SlippiNetplayClient::SendSlippiPad(std::unique_ptr<SlippiPad> pad)
{ {
auto status = slippiConnectStatus; auto status = slippiConnectStatus;
bool connectionFailed = status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_FAILED; bool connectionFailed =
bool connectionDisconnected = status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_DISCONNECTED; status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_FAILED;
bool connectionDisconnected =
status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_DISCONNECTED;
if (connectionFailed || connectionDisconnected) if (connectionFailed || connectionDisconnected)
{ {
return; return;
@ -559,8 +573,9 @@ void SlippiNetplayClient::SendSlippiPad(std::unique_ptr<SlippiPad> pad)
// if (pad && isDecider) // if (pad && isDecider)
//{ //{
// ERROR_LOG(SLIPPI_ONLINE, "[%d] %X %X %X %X %X %X %X %X", pad->frame, pad->padBuf[0], pad->padBuf[1], // ERROR_LOG(SLIPPI_ONLINE, "[%d] %X %X %X %X %X %X %X %X", pad->frame, pad->padBuf[0],
// pad->padBuf[2], pad->padBuf[3], pad->padBuf[4], pad->padBuf[5], pad->padBuf[6], pad->padBuf[7]); // pad->padBuf[1], pad->padBuf[2], pad->padBuf[3], pad->padBuf[4], pad->padBuf[5],
// pad->padBuf[6], pad->padBuf[7]);
//} //}
if (pad) if (pad)
@ -590,9 +605,9 @@ void SlippiNetplayClient::SendSlippiPad(std::unique_ptr<SlippiPad> pad)
INFO_LOG(SLIPPI_ONLINE, "Sending a packet of inputs [%d]...", frame); INFO_LOG(SLIPPI_ONLINE, "Sending a packet of inputs [%d]...", frame);
for (auto it = localPadQueue.begin(); it != localPadQueue.end(); ++it) for (auto it = localPadQueue.begin(); it != localPadQueue.end(); ++it)
{ {
INFO_LOG(SLIPPI_ONLINE, "Send [%d] -> %02X %02X %02X %02X %02X %02X %02X %02X", (*it)->frame, (*it)->padBuf[0], INFO_LOG(SLIPPI_ONLINE, "Send [%d] -> %02X %02X %02X %02X %02X %02X %02X %02X", (*it)->frame,
(*it)->padBuf[1], (*it)->padBuf[2], (*it)->padBuf[3], (*it)->padBuf[4], (*it)->padBuf[5], (*it)->padBuf[0], (*it)->padBuf[1], (*it)->padBuf[2], (*it)->padBuf[3],
(*it)->padBuf[6], (*it)->padBuf[7]); (*it)->padBuf[4], (*it)->padBuf[5], (*it)->padBuf[6], (*it)->padBuf[7]);
spac->append((*it)->padBuf, SLIPPI_PAD_DATA_SIZE); // only transfer 8 bytes per pad spac->append((*it)->padBuf, SLIPPI_PAD_DATA_SIZE); // only transfer 8 bytes per pad
} }

View file

@ -4,6 +4,16 @@
#pragma once #pragma once
#include <SFML/Network/Packet.hpp>
#include <array>
#include <deque>
#include <map>
#include <memory>
#include <mutex>
#include <queue>
#include <string>
#include <thread>
#include <vector>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Event.h" #include "Common/Event.h"
#include "Common/Timer.h" #include "Common/Timer.h"
@ -11,22 +21,13 @@
#include "Core/NetPlayProto.h" #include "Core/NetPlayProto.h"
#include "Core/Slippi/SlippiPad.h" #include "Core/Slippi/SlippiPad.h"
#include "InputCommon/GCPadStatus.h" #include "InputCommon/GCPadStatus.h"
#include <SFML/Network/Packet.hpp>
#include <array>
#include <deque>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include <queue>
#ifdef _WIN32 #ifdef _WIN32
#include <Qos2.h> #include <Qos2.h>
#endif #endif
#define SLIPPI_ONLINE_LOCKSTEP_INTERVAL 30 // Number of frames to wait before attempting to time-sync #define SLIPPI_ONLINE_LOCKSTEP_INTERVAL \
30 // Number of frames to wait before attempting to time-sync
#define SLIPPI_PING_DISPLAY_INTERVAL 60 #define SLIPPI_PING_DISPLAY_INTERVAL 60
struct SlippiRemotePadOutput struct SlippiRemotePadOutput
@ -104,7 +105,8 @@ public:
void SendAsync(std::unique_ptr<sf::Packet> packet); void SendAsync(std::unique_ptr<sf::Packet> packet);
SlippiNetplayClient(bool isDecider); // Make a dummy client SlippiNetplayClient(bool isDecider); // Make a dummy client
SlippiNetplayClient(const std::string& address, const u16 remotePort, const u16 localPort, bool isDecider); SlippiNetplayClient(const std::string& address, const u16 remotePort, const u16 localPort,
bool isDecider);
~SlippiNetplayClient(); ~SlippiNetplayClient();
// Slippi Online // Slippi Online
@ -149,8 +151,8 @@ protected:
std::thread m_thread; std::thread m_thread;
std::string m_selected_game; std::string m_selected_game;
Common::Flag m_is_running{ false }; Common::Flag m_is_running{false};
Common::Flag m_do_loop{ true }; Common::Flag m_do_loop{true};
unsigned int m_minimum_buffer_size = 6; unsigned int m_minimum_buffer_size = 6;

View file

@ -5,7 +5,7 @@
#include <string.h> #include <string.h>
// TODO: Confirm the default and padding values are right // TODO: Confirm the default and padding values are right
static u8 emptyPad[SLIPPI_PAD_FULL_SIZE] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static u8 emptyPad[SLIPPI_PAD_FULL_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
SlippiPad::SlippiPad(int32_t frame) SlippiPad::SlippiPad(int32_t frame)
{ {

View file

@ -15,4 +15,3 @@ public:
int32_t frame; int32_t frame;
u8 padBuf[SLIPPI_PAD_FULL_SIZE]; u8 padBuf[SLIPPI_PAD_FULL_SIZE];
}; };

View file

@ -86,8 +86,7 @@ void SlippiPlaybackStatus::prepareSlippiPlayback(s32& frameIndex)
// TODO: figure out why sometimes playback frame increments past targetFrameNum // TODO: figure out why sometimes playback frame increments past targetFrameNum
if (inSlippiPlayback && frameIndex >= targetFrameNum) if (inSlippiPlayback && frameIndex >= targetFrameNum)
{ {
INFO_LOG(SLIPPI, "Reached frame %d. Target was %d. Unblocking", frameIndex, INFO_LOG(SLIPPI, "Reached frame %d. Target was %d. Unblocking", frameIndex, targetFrameNum);
targetFrameNum);
cv_waitingForTargetFrame.notify_one(); cv_waitingForTargetFrame.notify_one();
} }
} }
@ -136,7 +135,8 @@ void SlippiPlaybackStatus::SavestateThread()
{ {
// Wait to hit one of the intervals // Wait to hit one of the intervals
// Possible while rewinding that we hit this wait again. // Possible while rewinding that we hit this wait again.
while (shouldRunThreads && (currentPlaybackFrame - Slippi::PLAYBACK_FIRST_SAVE) % FRAME_INTERVAL != 0) while (shouldRunThreads &&
(currentPlaybackFrame - Slippi::PLAYBACK_FIRST_SAVE) % FRAME_INTERVAL != 0)
condVar.wait(intervalLock); condVar.wait(intervalLock);
if (!shouldRunThreads) if (!shouldRunThreads)
@ -169,11 +169,13 @@ void SlippiPlaybackStatus::SavestateThread()
void SlippiPlaybackStatus::seekToFrame() void SlippiPlaybackStatus::seekToFrame()
{ {
if (seekMtx.try_lock()) { if (seekMtx.try_lock())
{
if (targetFrameNum < Slippi::PLAYBACK_FIRST_SAVE) if (targetFrameNum < Slippi::PLAYBACK_FIRST_SAVE)
targetFrameNum = Slippi::PLAYBACK_FIRST_SAVE; targetFrameNum = Slippi::PLAYBACK_FIRST_SAVE;
if (targetFrameNum > lastFrame) { if (targetFrameNum > lastFrame)
{
targetFrameNum = lastFrame; targetFrameNum = lastFrame;
} }
@ -186,8 +188,10 @@ void SlippiPlaybackStatus::seekToFrame()
if (prevState != Core::State::Paused) if (prevState != Core::State::Paused)
Core::SetState(Core::State::Paused); Core::SetState(Core::State::Paused);
s32 closestStateFrame = targetFrameNum - emod(targetFrameNum - Slippi::PLAYBACK_FIRST_SAVE, FRAME_INTERVAL); s32 closestStateFrame =
bool isLoadingStateOptimal = targetFrameNum < currentPlaybackFrame || closestStateFrame > currentPlaybackFrame; targetFrameNum - emod(targetFrameNum - Slippi::PLAYBACK_FIRST_SAVE, FRAME_INTERVAL);
bool isLoadingStateOptimal =
targetFrameNum < currentPlaybackFrame || closestStateFrame > currentPlaybackFrame;
if (isLoadingStateOptimal) if (isLoadingStateOptimal)
{ {
@ -217,7 +221,8 @@ void SlippiPlaybackStatus::seekToFrame()
futureDiffs.count(closestActualStateFrame) == 0) futureDiffs.count(closestActualStateFrame) == 0)
closestActualStateFrame -= FRAME_INTERVAL; closestActualStateFrame -= FRAME_INTERVAL;
// only load a savestate if we find one past our current frame since we are seeking forwards // only load a savestate if we find one past our current frame since we are seeking
// forwards
if (closestActualStateFrame > currentPlaybackFrame) if (closestActualStateFrame > currentPlaybackFrame)
loadState(closestActualStateFrame); loadState(closestActualStateFrame);
} }
@ -234,18 +239,22 @@ void SlippiPlaybackStatus::seekToFrame()
setHardFFW(false); setHardFFW(false);
} }
// We've reached the frame we want. Reset targetFrameNum and release mutex so another seek can be performed // We've reached the frame we want. Reset targetFrameNum and release mutex so another seek can
// be performed
g_playbackStatus->currentPlaybackFrame = targetFrameNum; g_playbackStatus->currentPlaybackFrame = targetFrameNum;
targetFrameNum = INT_MAX; targetFrameNum = INT_MAX;
Core::SetState(prevState); Core::SetState(prevState);
seekMtx.unlock(); seekMtx.unlock();
} else { }
else
{
INFO_LOG(SLIPPI, "Already seeking. Ignoring this call"); INFO_LOG(SLIPPI, "Already seeking. Ignoring this call");
} }
} }
// Set isHardFFW and update OC settings to speed up the FFW // Set isHardFFW and update OC settings to speed up the FFW
void SlippiPlaybackStatus::setHardFFW(bool enable) { void SlippiPlaybackStatus::setHardFFW(bool enable)
{
if (enable) if (enable)
{ {
SConfig::GetInstance().m_OCEnable = true; SConfig::GetInstance().m_OCEnable = true;
@ -260,7 +269,6 @@ void SlippiPlaybackStatus::setHardFFW(bool enable) {
isHardFFW = enable; isHardFFW = enable;
} }
void SlippiPlaybackStatus::loadState(s32 closestStateFrame) void SlippiPlaybackStatus::loadState(s32 closestStateFrame)
{ {
if (closestStateFrame == Slippi::PLAYBACK_FIRST_SAVE) if (closestStateFrame == Slippi::PLAYBACK_FIRST_SAVE)
@ -268,7 +276,8 @@ void SlippiPlaybackStatus::loadState(s32 closestStateFrame)
else else
{ {
std::string stateString; std::string stateString;
decoder.Decode((char*)iState.data(), iState.size(), futureDiffs[closestStateFrame].get(), &stateString); decoder.Decode((char*)iState.data(), iState.size(), futureDiffs[closestStateFrame].get(),
&stateString);
std::vector<u8> stateToLoad(stateString.begin(), stateString.end()); std::vector<u8> stateToLoad(stateString.begin(), stateString.end());
State::LoadFromBuffer(stateToLoad); State::LoadFromBuffer(stateToLoad);
} }

View file

@ -5,12 +5,12 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <SlippiLib/SlippiGame.h>
#include <open-vcdiff/src/google/vcdecoder.h> #include <open-vcdiff/src/google/vcdecoder.h>
#include <open-vcdiff/src/google/vcencoder.h> #include <open-vcdiff/src/google/vcencoder.h>
#include <SlippiLib/SlippiGame.h>
#include "Core/ConfigManager.h"
#include "../../Common/CommonTypes.h" #include "../../Common/CommonTypes.h"
#include "Core/ConfigManager.h"
class SlippiPlaybackStatus class SlippiPlaybackStatus
{ {

View file

@ -1,6 +1,6 @@
#include "SlippiReplayComm.h"
#include <cctype> #include <cctype>
#include <memory> #include <memory>
#include "SlippiReplayComm.h"
#include "Common/CommonPaths.h" #include "Common/CommonPaths.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/Logging/LogManager.h" #include "Common/Logging/LogManager.h"
@ -18,7 +18,8 @@ static inline void ltrim(std::string& s)
// trim from end (in place) // trim from end (in place)
static inline void rtrim(std::string& s) 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()); s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(),
s.end());
} }
// trim from both ends (in place) // trim from both ends (in place)
@ -35,7 +36,9 @@ SlippiReplayComm::SlippiReplayComm()
configFilePath = SConfig::GetInstance().m_strSlippiInput.c_str(); configFilePath = SConfig::GetInstance().m_strSlippiInput.c_str();
} }
SlippiReplayComm::~SlippiReplayComm() {} SlippiReplayComm::~SlippiReplayComm()
{
}
SlippiReplayComm::CommSettings SlippiReplayComm::getSettings() SlippiReplayComm::CommSettings SlippiReplayComm::getSettings()
{ {
@ -177,8 +180,8 @@ void SlippiReplayComm::loadFile()
{ {
WARN_LOG(EXPANSIONINTERFACE, "Comm file load error detected. Check file format"); WARN_LOG(EXPANSIONINTERFACE, "Comm file load error detected. Check file format");
// Reset in the case of read error. this fixes a race condition where file mod time changes but // Reset in the case of read error. this fixes a race condition where file mod time changes
// the file is not readable yet? // but the file is not readable yet?
configLastLoadModTime = 0; configLastLoadModTime = 0;
} }

View file

@ -1,10 +1,10 @@
#pragma once #pragma once
#include <SlippiGame.h>
#include <limits.h> #include <limits.h>
#include <nlohmann/json.hpp>
#include <queue> #include <queue>
#include <string> #include <string>
#include <nlohmann/json.hpp>
#include <SlippiGame.h>
#include <Common/CommonTypes.h> #include <Common/CommonTypes.h>

View file

@ -1,4 +1,5 @@
#include "SlippiSavestate.h" #include "SlippiSavestate.h"
#include <vector>
#include "Common/CommonFuncs.h" #include "Common/CommonFuncs.h"
#include "Common/MemoryUtil.h" #include "Common/MemoryUtil.h"
#include "Core/HW/AudioInterface.h" #include "Core/HW/AudioInterface.h"
@ -11,7 +12,6 @@
#include "Core/HW/ProcessorInterface.h" #include "Core/HW/ProcessorInterface.h"
#include "Core/HW/SI/SI.h" #include "Core/HW/SI/SI.h"
#include "Core/HW/VideoInterface.h" #include "Core/HW/VideoInterface.h"
#include <vector>
SlippiSavestate::SlippiSavestate() SlippiSavestate::SlippiSavestate()
{ {
@ -146,7 +146,7 @@ void SlippiSavestate::initBackupLocs()
// Add split section after exclusion // Add split section after exclusion
if (backupLocs[idx].endAddress > ipb.address + ipb.length) if (backupLocs[idx].endAddress > ipb.address + ipb.length)
{ {
ssBackupLoc newLoc = { ipb.address + ipb.length, backupLocs[idx].endAddress, nullptr }; ssBackupLoc newLoc = {ipb.address + ipb.length, backupLocs[idx].endAddress, nullptr};
backupLocs.insert(backupLocs.begin() + idx + 1, newLoc); backupLocs.insert(backupLocs.begin() + idx + 1, newLoc);
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include <unordered_map>
#include "Common/ChunkFile.h" #include "Common/ChunkFile.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include <unordered_map>
class PointerWrap; class PointerWrap;
@ -14,7 +14,10 @@ public:
u32 address; u32 address;
u32 length; u32 length;
bool operator==(const PreserveBlock& p) const { return address == p.address && length == p.length; } bool operator==(const PreserveBlock& p) const
{
return address == p.address && length == p.length;
}
}; };
SlippiSavestate(); SlippiSavestate();

View file

@ -33,7 +33,7 @@ static void system_hidden(const char* cmd)
memset(&p_info, 0, sizeof(p_info)); memset(&p_info, 0, sizeof(p_info));
s_info.cb = sizeof(s_info); s_info.cb = sizeof(s_info);
wchar_t utf16cmd[MAX_SYSTEM_PROGRAM] = { 0 }; wchar_t utf16cmd[MAX_SYSTEM_PROGRAM] = {0};
MultiByteToWideChar(CP_UTF8, 0, cmd, -1, utf16cmd, MAX_SYSTEM_PROGRAM); MultiByteToWideChar(CP_UTF8, 0, cmd, -1, utf16cmd, MAX_SYSTEM_PROGRAM);
if (CreateProcessW(NULL, utf16cmd, NULL, NULL, 0, CREATE_NO_WINDOW, NULL, NULL, &s_info, &p_info)) if (CreateProcessW(NULL, utf16cmd, NULL, NULL, 0, CREATE_NO_WINDOW, NULL, NULL, &s_info, &p_info))
{ {
@ -110,19 +110,21 @@ bool SlippiUser::AttemptLogin()
{ {
std::string userFilePathTxt = std::string userFilePathTxt =
userFilePath + ".txt"; // Put the filename here in its own scope because we don't really need it elsewhere userFilePath +
".txt"; // Put the filename here in its own scope because we don't really need it elsewhere
if (File::Exists(userFilePathTxt)) if (File::Exists(userFilePathTxt))
{ {
// If both files exist we just log they exist and take no further action // If both files exist we just log they exist and take no further action
if (File::Exists(userFilePath)) if (File::Exists(userFilePath))
{ {
INFO_LOG(SLIPPI_ONLINE, INFO_LOG(SLIPPI_ONLINE, "Found both .json.txt and .json file for user data. Using .json "
"Found both .json.txt and .json file for user data. Using .json and ignoring the .json.txt"); "and ignoring the .json.txt");
} }
// If only the .txt file exists move the contents to a json file and log if it fails // If only the .txt file exists move the contents to a json file and log if it fails
else if (!File::Rename(userFilePathTxt, userFilePath)) else if (!File::Rename(userFilePathTxt, userFilePath))
{ {
WARN_LOG(SLIPPI_ONLINE, "Could not move file %s to %s", userFilePathTxt.c_str(), userFilePath.c_str()); WARN_LOG(SLIPPI_ONLINE, "Could not move file %s to %s", userFilePathTxt.c_str(),
userFilePath.c_str());
} }
} }
} }
@ -137,7 +139,8 @@ bool SlippiUser::AttemptLogin()
if (isLoggedIn) if (isLoggedIn)
{ {
overwriteFromServer(); overwriteFromServer();
WARN_LOG(SLIPPI_ONLINE, "Found user %s (%s)", userInfo.displayName.c_str(), userInfo.uid.c_str()); WARN_LOG(SLIPPI_ONLINE, "Found user %s (%s)", userInfo.displayName.c_str(),
userInfo.uid.c_str());
} }
return isLoggedIn; return isLoggedIn;
@ -149,7 +152,8 @@ void SlippiUser::OpenLogInPage()
std::string path = getUserFilePath(); std::string path = getUserFilePath();
#ifdef _WIN32 #ifdef _WIN32
// On windows, sometimes the path can have backslashes and slashes mixed, convert all to backslashes // On windows, sometimes the path can have backslashes and slashes mixed, convert all to
// backslashes
path = ReplaceAll(path, "\\", "\\"); path = ReplaceAll(path, "\\", "\\");
path = ReplaceAll(path, "/", "\\"); path = ReplaceAll(path, "/", "\\");
#endif #endif
@ -181,12 +185,15 @@ void SlippiUser::UpdateApp()
auto isoPath = SConfig::GetInstance().m_strIsoPath; auto isoPath = SConfig::GetInstance().m_strIsoPath;
std::string path = File::GetExeDirectory() + "/dolphin-slippi-tools.exe"; std::string path = File::GetExeDirectory() + "/dolphin-slippi-tools.exe";
std::string echoMsg = "echo Starting update process. If nothing happen after a few " std::string echoMsg =
"echo Starting update process. If nothing happen after a few "
"minutes, you may need to update manually from https://slippi.gg/netplay ..."; "minutes, you may need to update manually from https://slippi.gg/netplay ...";
// std::string command = // std::string command =
// "start /b cmd /c " + echoMsg + " && \"" + path + "\" app-update -launch -iso \"" + isoPath + "\""; // "start /b cmd /c " + echoMsg + " && \"" + path + "\" app-update -launch -iso \"" + isoPath +
std::string command = "start /b cmd /c " + echoMsg + " && \"" + path + "\" app-update -launch -iso \"" + isoPath + // "\"";
"\" -version \"" + Common::scm_slippi_semver_str + "\""; std::string command = "start /b cmd /c " + echoMsg + " && \"" + path +
"\" app-update -launch -iso \"" + isoPath + "\" -version \"" +
Common::scm_slippi_semver_str + "\"";
WARN_LOG(SLIPPI, "Executing app update command: %s", command.c_str()); WARN_LOG(SLIPPI, "Executing app update command: %s", command.c_str());
RunSystemCommand(command); RunSystemCommand(command);
#elif defined(__APPLE__) #elif defined(__APPLE__)
@ -263,7 +270,8 @@ void SlippiUser::FileListenThread()
std::string SlippiUser::getUserFilePath() std::string SlippiUser::getUserFilePath()
{ {
#if defined(__APPLE__) #if defined(__APPLE__)
std::string userFilePath = File::GetBundleDirectory() + "/Contents/Resources" + DIR_SEP + "user.json"; std::string userFilePath =
File::GetBundleDirectory() + "/Contents/Resources" + DIR_SEP + "user.json";
#elif defined(_WIN32) #elif defined(_WIN32)
std::string userFilePath = File::GetExeDirectory() + DIR_SEP + "user.json"; std::string userFilePath = File::GetExeDirectory() + DIR_SEP + "user.json";
#else #else

View file

@ -1,12 +1,12 @@
#pragma once #pragma once
#include "Common/CommonTypes.h"
#include <atomic> #include <atomic>
#include <curl/curl.h> #include <curl/curl.h>
#include <string> #include <string>
#include <thread> #include <thread>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include "Common/CommonTypes.h"
class SlippiUser class SlippiUser
{ {

View file

@ -94,17 +94,18 @@ void SlippiPane::CreateLayout()
connect(delay_spin, qOverload<int>(&QSpinBox::valueChanged), this, connect(delay_spin, qOverload<int>(&QSpinBox::valueChanged), this,
[](int delay) { SConfig::GetInstance().m_slippiOnlineDelay = delay; }); [](int delay) { SConfig::GetInstance().m_slippiOnlineDelay = delay; });
#else #else
//Playback Settings // Playback Settings
auto* playback_settings = new QGroupBox(tr("Playback Settings")); auto* playback_settings = new QGroupBox(tr("Playback Settings"));
auto* playback_settings_layout = new QVBoxLayout(); auto* playback_settings_layout = new QVBoxLayout();
playback_settings->setLayout(playback_settings_layout); playback_settings->setLayout(playback_settings_layout);
layout->addWidget(playback_settings); layout->addWidget(playback_settings);
auto* enable_playback_seek_checkbox = new QCheckBox(tr("Enable Seekbar")); auto* enable_playback_seek_checkbox = new QCheckBox(tr("Enable Seekbar"));
char seekbarTooltip[] = "<html><head/><body><p>Enables video player style controls while watching Slippi replays. Uses more cpu resources and can be stuttery. " \ char seekbarTooltip[] = "<html><head/><body><p>Enables video player style controls while "
"Space: Pause/Play " \ "watching Slippi replays. Uses more cpu resources and can be stuttery. "
"Left/Right: Jump 5 seconds back/forward" \ "Space: Pause/Play "
"Shift + Left/Right: Jump 20 seconds back/forward" \ "Left/Right: Jump 5 seconds back/forward"
"Shift + Left/Right: Jump 20 seconds back/forward"
"Period (while paused): Advance one frame"; "Period (while paused): Advance one frame";
enable_playback_seek_checkbox->setToolTip(tr(seekbarTooltip)); enable_playback_seek_checkbox->setToolTip(tr(seekbarTooltip));
playback_settings_layout->addWidget(enable_playback_seek_checkbox); playback_settings_layout->addWidget(enable_playback_seek_checkbox);