From b192600c6cb15a33fdedc341aed1efe9caa4bb85 Mon Sep 17 00:00:00 2001 From: Nicholas Marcott <481161+bmarcott@users.noreply.github.com> Date: Fri, 1 Jan 2021 20:10:44 +0900 Subject: [PATCH 1/2] apply clang-format to Slippi folder --- Externals/SlippiLib/SlippiGame.cpp | 1019 +++++++++-------- Externals/SlippiLib/SlippiGame.h | 241 ++-- Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp | 942 ++++++++------- Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h | 326 +++--- .../Core/Core/Slippi/SlippiGameFileLoader.cpp | 16 +- .../Core/Core/Slippi/SlippiGameFileLoader.h | 2 +- Source/Core/Core/Slippi/SlippiMatchmaking.cpp | 33 +- Source/Core/Core/Slippi/SlippiMatchmaking.h | 6 +- Source/Core/Core/Slippi/SlippiNetplay.cpp | 121 +- Source/Core/Core/Slippi/SlippiNetplay.h | 36 +- Source/Core/Core/Slippi/SlippiPad.cpp | 2 +- Source/Core/Core/Slippi/SlippiPad.h | 1 - Source/Core/Core/Slippi/SlippiPlayback.cpp | 41 +- Source/Core/Core/Slippi/SlippiPlayback.h | 10 +- Source/Core/Core/Slippi/SlippiReplayComm.cpp | 15 +- Source/Core/Core/Slippi/SlippiReplayComm.h | 6 +- Source/Core/Core/Slippi/SlippiSavestate.cpp | 80 +- Source/Core/Core/Slippi/SlippiSavestate.h | 9 +- Source/Core/Core/Slippi/SlippiUser.cpp | 36 +- Source/Core/Core/Slippi/SlippiUser.h | 2 +- Source/Core/DolphinQt/Settings/SlippiPane.cpp | 15 +- 21 files changed, 1548 insertions(+), 1411 deletions(-) diff --git a/Externals/SlippiLib/SlippiGame.cpp b/Externals/SlippiLib/SlippiGame.cpp index b76d004970..31987774d7 100644 --- a/Externals/SlippiLib/SlippiGame.cpp +++ b/Externals/SlippiLib/SlippiGame.cpp @@ -1,560 +1,567 @@ -#include #include #include +#include #include "SlippiGame.h" namespace Slippi { - //********************************************************************** - //* Event Handlers - //********************************************************************** - //The read operators will read a value and increment the index so the next read will read in the correct location - uint8_t readByte(uint8_t* a, int& idx, uint32_t maxSize, uint8_t defaultValue) { - if (idx >= (int)maxSize) { - idx += 1; - return defaultValue; - } - - return a[idx++]; +//********************************************************************** +//* Event Handlers +//********************************************************************** +// The read operators will read a value and increment the index so the next read +// will read in the correct location +uint8_t readByte(uint8_t *a, int &idx, uint32_t maxSize, uint8_t defaultValue) { + if (idx >= (int)maxSize) { + idx += 1; + return defaultValue; } - uint16_t readHalf(uint8_t* a, int& idx, uint32_t maxSize, uint16_t defaultValue) { - if (idx >= (int)maxSize) { - idx += 2; - return defaultValue; - } + return a[idx++]; +} - uint16_t value = a[idx] << 8 | a[idx + 1]; +uint16_t readHalf(uint8_t *a, int &idx, uint32_t maxSize, + uint16_t defaultValue) { + if (idx >= (int)maxSize) { idx += 2; - return value; + return defaultValue; } - uint32_t readWord(uint8_t* a, int& idx, uint32_t maxSize, uint32_t defaultValue) { - if (idx >= (int)maxSize) { - idx += 4; - return defaultValue; - } + uint16_t value = a[idx] << 8 | a[idx + 1]; + idx += 2; + return value; +} - uint32_t value = a[idx] << 24 | a[idx + 1] << 16 | a[idx + 2] << 8 | a[idx + 3]; +uint32_t readWord(uint8_t *a, int &idx, uint32_t maxSize, + uint32_t defaultValue) { + if (idx >= (int)maxSize) { idx += 4; - return value; + return defaultValue; } - float readFloat(uint8_t* a, int& idx, uint32_t maxSize, float defaultValue) { - uint32_t bytes = readWord(a, idx, maxSize, *(uint32_t*)(&defaultValue)); - return *(float*)(&bytes); + uint32_t value = + a[idx] << 24 | a[idx + 1] << 16 | a[idx + 2] << 8 | a[idx + 3]; + idx += 4; + return value; +} + +float readFloat(uint8_t *a, int &idx, uint32_t maxSize, float defaultValue) { + uint32_t bytes = readWord(a, idx, maxSize, *(uint32_t *)(&defaultValue)); + return *(float *)(&bytes); +} + +void handleGameInit(Game *game, uint32_t maxSize) { + int idx = 0; + + // Read version number + for (int i = 0; i < 4; i++) { + game->version[i] = readByte(data, idx, maxSize, 0); } - void handleGameInit(Game* game, uint32_t maxSize) { - int idx = 0; + // Read entire game info header + for (int i = 0; i < GAME_INFO_HEADER_SIZE; i++) { + game->settings.header[i] = readWord(data, idx, maxSize, 0); + } - // Read version number - for (int i = 0; i < 4; i++) { - game->version[i] = readByte(data, idx, maxSize, 0); - } + // Load random seed + game->settings.randomSeed = readWord(data, idx, maxSize, 0); - // Read entire game info header - for (int i = 0; i < GAME_INFO_HEADER_SIZE; i++) { - game->settings.header[i] = readWord(data, idx, maxSize, 0); - } + // Read UCF toggle bytes + bool shouldRead = game->version[0] >= 1; + for (int i = 0; i < UCF_TOGGLE_SIZE; i++) { + uint32_t value = shouldRead ? readWord(data, idx, maxSize, 0) : 0; + game->settings.ucfToggles[i] = value; + } - // Load random seed - game->settings.randomSeed = readWord(data, idx, maxSize, 0); - - // Read UCF toggle bytes - bool shouldRead = game->version[0] >= 1; - for (int i = 0; i < UCF_TOGGLE_SIZE; i++) { - uint32_t value = shouldRead ? readWord(data, idx, maxSize, 0) : 0; - game->settings.ucfToggles[i] = value; - } - - // Read nametag for each player - std::array, 4> playerNametags; - for (int i = 0; i < 4; i++) { - for (int j = 0; j < NAMETAG_SIZE; j++) { - playerNametags[i][j] = readHalf(data, idx, maxSize, 0); - } - } - - // Read isPAL byte - game->settings.isPAL = readByte(data, idx, maxSize, 0); - - // Read isFrozenPS byte - game->settings.isFrozenPS = readByte(data, idx, maxSize, 0); - - // Pull header data into struct - int player1Pos = 24; // This is the index of the first players character info - std::array gameInfoHeader = game->settings.header; - for (int i = 0; i < 4; i++) { - // this is the position in the array that this player's character info is stored - int pos = player1Pos + (9 * i); - - uint32_t playerInfo = gameInfoHeader[pos]; - uint8_t playerType = (playerInfo & 0x00FF0000) >> 16; - if (playerType == 0x3) { - // Player type 3 is an empty slot - continue; - } - - PlayerSettings p; - - // Get player settings - p.controllerPort = i; - p.characterId = playerInfo >> 24; - p.playerType = playerType; - p.characterColor = playerInfo & 0xFF; - p.nametag = playerNametags[i]; - - //Add player settings to result - game->settings.players[i] = p; - } - - game->settings.stage = gameInfoHeader[3] & 0xFFFF; - - auto majorVersion = game->version[0]; - auto minorVersion = game->version[1]; - if (majorVersion > 3 || (majorVersion == 3 && minorVersion >= 1)) { - // After version 3.1.0 we added a dynamic gecko loading process. These - // are needed before starting the game. areSettingsLoaded will be set - // to true when they are received - game->areSettingsLoaded = false; - } - else if (majorVersion > 1 || (majorVersion == 1 && minorVersion >= 6)) { - // Indicate settings loaded immediately if after version 1.6.0 - // Sheik game info was added in this version and so we no longer - // need to wait - game->areSettingsLoaded = true; + // Read nametag for each player + std::array, 4> playerNametags; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < NAMETAG_SIZE; j++) { + playerNametags[i][j] = readHalf(data, idx, maxSize, 0); } } - void handleGeckoList(Game* game, uint32_t maxSize) { - game->settings.geckoCodes.clear(); - game->settings.geckoCodes.insert(game->settings.geckoCodes.end(), data, data + maxSize); + // Read isPAL byte + game->settings.isPAL = readByte(data, idx, maxSize, 0); - // File is good to load + // Read isFrozenPS byte + game->settings.isFrozenPS = readByte(data, idx, maxSize, 0); + + // Pull header data into struct + int player1Pos = 24; // This is the index of the first players character info + std::array gameInfoHeader = + game->settings.header; + for (int i = 0; i < 4; i++) { + // this is the position in the array that this player's character info is + // stored + int pos = player1Pos + (9 * i); + + uint32_t playerInfo = gameInfoHeader[pos]; + uint8_t playerType = (playerInfo & 0x00FF0000) >> 16; + if (playerType == 0x3) { + // Player type 3 is an empty slot + continue; + } + + PlayerSettings p; + + // Get player settings + p.controllerPort = i; + p.characterId = playerInfo >> 24; + p.playerType = playerType; + p.characterColor = playerInfo & 0xFF; + p.nametag = playerNametags[i]; + + // Add player settings to result + game->settings.players[i] = p; + } + + game->settings.stage = gameInfoHeader[3] & 0xFFFF; + + auto majorVersion = game->version[0]; + auto minorVersion = game->version[1]; + if (majorVersion > 3 || (majorVersion == 3 && minorVersion >= 1)) { + // After version 3.1.0 we added a dynamic gecko loading process. These + // are needed before starting the game. areSettingsLoaded will be set + // to true when they are received + game->areSettingsLoaded = false; + } else if (majorVersion > 1 || (majorVersion == 1 && minorVersion >= 6)) { + // Indicate settings loaded immediately if after version 1.6.0 + // Sheik game info was added in this version and so we no longer + // need to wait game->areSettingsLoaded = true; } +} - void handleFrameStart(Game* game, uint32_t maxSize) { - int idx = 0; +void handleGeckoList(Game *game, uint32_t maxSize) { + game->settings.geckoCodes.clear(); + game->settings.geckoCodes.insert(game->settings.geckoCodes.end(), data, + data + maxSize); - //Check frame count - int32_t frameCount = readWord(data, idx, maxSize, 0); - game->frameCount = frameCount; + // File is good to load + game->areSettingsLoaded = true; +} - auto frameUniquePtr = std::make_unique(); - FrameData* frame = frameUniquePtr.get(); +void handleFrameStart(Game *game, uint32_t maxSize) { + int idx = 0; - frame->frame = frameCount; - frame->randomSeedExists = true; - frame->randomSeed = readWord(data, idx, maxSize, 0); + // Check frame count + int32_t frameCount = readWord(data, idx, maxSize, 0); + game->frameCount = frameCount; - // Add frame to game. The frames are stored in multiple ways because - // for games with rollback, the same frame may be replayed multiple times + auto frameUniquePtr = std::make_unique(); + FrameData *frame = frameUniquePtr.get(); + + frame->frame = frameCount; + frame->randomSeedExists = true; + frame->randomSeed = readWord(data, idx, maxSize, 0); + + // Add frame to game. The frames are stored in multiple ways because + // for games with rollback, the same frame may be replayed multiple times + frame->numSinceStart = game->frames.size(); + game->frames.push_back(std::move(frameUniquePtr)); + game->framesByIndex[frameCount] = frame; +} + +void handlePreFrameUpdate(Game *game, uint32_t maxSize) { + int idx = 0; + + // Check frame count + int32_t frameCount = readWord(data, idx, maxSize, 0); + game->frameCount = frameCount; + + auto frameUniquePtr = std::make_unique(); + FrameData *frame = frameUniquePtr.get(); + bool isNewFrame = true; + + if (game->framesByIndex.count(frameCount)) { + // If this frame already exists, get the current frame + frame = game->frames.back().get(); + isNewFrame = false; + } + + frame->frame = frameCount; + + PlayerFrameData p; + + uint8_t playerSlot = readByte(data, idx, maxSize, 0); + uint8_t isFollower = readByte(data, idx, maxSize, 0); + + // Load random seed for player frame update + p.randomSeed = readWord(data, idx, maxSize, 0); + + // Load player data + p.animation = readHalf(data, idx, maxSize, 0); + p.locationX = readFloat(data, idx, maxSize, 0); + p.locationY = readFloat(data, idx, maxSize, 0); + p.facingDirection = readFloat(data, idx, maxSize, 0); + + // Controller information + p.joystickX = readFloat(data, idx, maxSize, 0); + p.joystickY = readFloat(data, idx, maxSize, 0); + p.cstickX = readFloat(data, idx, maxSize, 0); + p.cstickY = readFloat(data, idx, maxSize, 0); + p.trigger = readFloat(data, idx, maxSize, 0); + p.buttons = readWord(data, idx, maxSize, 0); + + // Raw controller information + p.physicalButtons = readHalf(data, idx, maxSize, 0); + p.lTrigger = readFloat(data, idx, maxSize, 0); + p.rTrigger = readFloat(data, idx, maxSize, 0); + + if (asmEvents[EVENT_PRE_FRAME_UPDATE] >= 59) { + p.joystickXRaw = readByte(data, idx, maxSize, 0); + } + + uint32_t noPercent = 0xFFFFFFFF; + p.percent = readFloat(data, idx, maxSize, *(float *)(&noPercent)); + + // Add player data to frame + std::unordered_map *target; + target = isFollower ? &frame->followers : &frame->players; + + // Set the player data for the player or follower + target->operator[](playerSlot) = p; + + // Add frame to game + if (isNewFrame) { frame->numSinceStart = game->frames.size(); game->frames.push_back(std::move(frameUniquePtr)); game->framesByIndex[frameCount] = frame; } +} - void handlePreFrameUpdate(Game* game, uint32_t maxSize) { - int idx = 0; +void handlePostFrameUpdate(Game *game, uint32_t maxSize) { + int idx = 0; - //Check frame count - int32_t frameCount = readWord(data, idx, maxSize, 0); - game->frameCount = frameCount; + // Check frame count + int32_t frameCount = readWord(data, idx, maxSize, 0); - auto frameUniquePtr = std::make_unique(); - FrameData* frame = frameUniquePtr.get(); - bool isNewFrame = true; - - if (game->framesByIndex.count(frameCount)) { - // If this frame already exists, get the current frame - frame = game->frames.back().get(); - isNewFrame = false; - } - - frame->frame = frameCount; - - PlayerFrameData p; - - uint8_t playerSlot = readByte(data, idx, maxSize, 0); - uint8_t isFollower = readByte(data, idx, maxSize, 0); - - //Load random seed for player frame update - p.randomSeed = readWord(data, idx, maxSize, 0); - - //Load player data - p.animation = readHalf(data, idx, maxSize, 0); - p.locationX = readFloat(data, idx, maxSize, 0); - p.locationY = readFloat(data, idx, maxSize, 0); - p.facingDirection = readFloat(data, idx, maxSize, 0); - - //Controller information - p.joystickX = readFloat(data, idx, maxSize, 0); - p.joystickY = readFloat(data, idx, maxSize, 0); - p.cstickX = readFloat(data, idx, maxSize, 0); - p.cstickY = readFloat(data, idx, maxSize, 0); - p.trigger = readFloat(data, idx, maxSize, 0); - p.buttons = readWord(data, idx, maxSize, 0); - - //Raw controller information - p.physicalButtons = readHalf(data, idx, maxSize, 0); - p.lTrigger = readFloat(data, idx, maxSize, 0); - p.rTrigger = readFloat(data, idx, maxSize, 0); - - if (asmEvents[EVENT_PRE_FRAME_UPDATE] >= 59) { - p.joystickXRaw = readByte(data, idx, maxSize, 0); - } - - uint32_t noPercent = 0xFFFFFFFF; - p.percent = readFloat(data, idx, maxSize, *(float*)(&noPercent)); - - // Add player data to frame - std::unordered_map* target; - target = isFollower ? &frame->followers : &frame->players; - - // Set the player data for the player or follower - target->operator[](playerSlot) = p; - - // Add frame to game - if (isNewFrame) { - frame->numSinceStart = game->frames.size(); - game->frames.push_back(std::move(frameUniquePtr)); - game->framesByIndex[frameCount] = frame; - } + FrameData *frame; + if (game->framesByIndex.count(frameCount)) { + // If this frame already exists, get the current frame + frame = game->frames.back().get(); } - void handlePostFrameUpdate(Game* game, uint32_t maxSize) { - int idx = 0; + // As soon as a post frame update happens, we know we have received all the + // inputs This is used to determine if a frame is ready to be used for a + // replay (for mirroring) + frame->inputsFullyFetched = true; - //Check frame count - int32_t frameCount = readWord(data, idx, maxSize, 0); + uint8_t playerSlot = readByte(data, idx, maxSize, 0); + uint8_t isFollower = readByte(data, idx, maxSize, 0); - FrameData* frame; - if (game->framesByIndex.count(frameCount)) { - // If this frame already exists, get the current frame - frame = game->frames.back().get(); - } + PlayerFrameData *p = + isFollower ? &frame->followers[playerSlot] : &frame->players[playerSlot]; - // As soon as a post frame update happens, we know we have received all the inputs - // This is used to determine if a frame is ready to be used for a replay (for mirroring) - frame->inputsFullyFetched = true; + p->internalCharacterId = readByte(data, idx, maxSize, 0); - uint8_t playerSlot = readByte(data, idx, maxSize, 0); - uint8_t isFollower = readByte(data, idx, maxSize, 0); + // Check if a player started as sheik and update + if (frameCount == GAME_FIRST_FRAME && + p->internalCharacterId == GAME_SHEIK_INTERNAL_ID) { + game->settings.players[playerSlot].characterId = GAME_SHEIK_EXTERNAL_ID; + } - PlayerFrameData* p = isFollower ? &frame->followers[playerSlot] : &frame->players[playerSlot]; - - p->internalCharacterId = readByte(data, idx, maxSize, 0); - - // Check if a player started as sheik and update - if (frameCount == GAME_FIRST_FRAME && p->internalCharacterId == GAME_SHEIK_INTERNAL_ID) { - game->settings.players[playerSlot].characterId = GAME_SHEIK_EXTERNAL_ID; - } - - // Set settings loaded if this is the last character - if (frameCount == GAME_FIRST_FRAME) { - uint8_t lastPlayerIndex = 0; - for (auto it = frame->players.begin(); it != frame->players.end(); ++it) { - if (it->first <= lastPlayerIndex) { - continue; - } - - lastPlayerIndex = it->first; + // Set settings loaded if this is the last character + if (frameCount == GAME_FIRST_FRAME) { + uint8_t lastPlayerIndex = 0; + for (auto it = frame->players.begin(); it != frame->players.end(); ++it) { + if (it->first <= lastPlayerIndex) { + continue; } - if (playerSlot >= lastPlayerIndex) { - game->areSettingsLoaded = true; - } - } - } - - void handleGameEnd(Game* game, uint32_t maxSize) { - int idx = 0; - - game->winCondition = readByte(data, idx, maxSize, 0); - } - - // This function gets the position where the raw data starts - int getRawDataPosition(std::ifstream* f) { - char buffer[2]; - f->seekg(0, std::ios::beg); - f->read(buffer, 2); - - if (buffer[0] == 0x36) { - return 0; + lastPlayerIndex = it->first; } - if (buffer[0] != '{') { - // TODO: Do something here to cause an error - return 0; + if (playerSlot >= lastPlayerIndex) { + game->areSettingsLoaded = true; } - - // 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 - return 15; - } - - uint32_t getRawDataLength(std::ifstream* f, int position, int fileSize) { - if (position == 0) { - return fileSize; - } - - char buffer[4]; - f->seekg(position - 4, std::ios::beg); - f->read(buffer, 4); - - uint8_t* byteBuf = (uint8_t*)&buffer[0]; - uint32_t length = byteBuf[0] << 24 | byteBuf[1] << 16 | byteBuf[2] << 8 | byteBuf[3]; - return length; - } - - std::unordered_map getMessageSizes(std::ifstream* f, int position) { - char buffer[2]; - f->seekg(position, std::ios::beg); - f->read(buffer, 2); - if (buffer[0] != EVENT_PAYLOAD_SIZES) { - return {}; - } - - int payloadLength = buffer[1]; - std::unordered_map messageSizes = { - { EVENT_PAYLOAD_SIZES, payloadLength } - }; - - - std::vector messageSizesBuffer(payloadLength - 1); - f->read(&messageSizesBuffer[0], payloadLength - 1); - for (int i = 0; i < payloadLength - 1; i += 3) { - uint8_t command = messageSizesBuffer[i]; - - // Extract the bytes in u8s. Without this the chars don't or together well - uint8_t byte1 = messageSizesBuffer[i + 1]; - uint8_t byte2 = messageSizesBuffer[i + 2]; - - uint16_t size = byte1 << 8 | byte2; - messageSizes[command] = size; - } - - return messageSizes; - } - - void SlippiGame::processData() { - if (isProcessingComplete) { - // If we have finished processing this file, return - return; - } - - // This function will process as much data as possible - int startPos = (int)file->tellg(); - file->seekg(startPos); - if (startPos == 0) { - file->seekg(0, std::ios::end); - int len = (int)file->tellg(); - if (len < 2) { - // If we can't read message sizes payload size yet, return - return; - } - - int rawDataPos = getRawDataPosition(file.get()); - int rawDataLen = len - rawDataPos; - if (rawDataLen < 2) { - // 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 - file->seekg(0); - return; - } - - startPos = rawDataPos; - - char buffer[2]; - file->seekg(startPos); - file->read(buffer, 2); - file->seekg(startPos); - auto messageSizesSize = (int)buffer[1]; - if (rawDataLen < messageSizesSize) { - // If we haven't received the full payload sizes message, return - // Reset to begining so that the startPos condition will be hit again - file->seekg(0); - return; - } - - asmEvents = getMessageSizes(file.get(), rawDataPos); - } - - // Read everything to the end - file->seekg(0, std::ios::end); - int endPos = (int)file->tellg(); - int sizeToRead = endPos - startPos; - file->seekg(startPos); - //log << "Size to read: " << sizeToRead << "\n"; - //log << "Start Pos: " << startPos << "\n"; - //log << "End Pos: " << endPos << "\n\n"; - if (sizeToRead <= 0) { - return; - } - - std::vector newData(sizeToRead); - file->read(&newData[0], sizeToRead); - - int newDataPos = 0; - while (newDataPos < sizeToRead) { - auto command = newData[newDataPos]; - auto payloadSize = asmEvents[command]; - - //char buff[100]; - //snprintf(buff, sizeof(buff), "%x", command); - //log << "Command: " << buff << " | Payload Size: " << payloadSize << "\n"; - - auto remainingLen = sizeToRead - newDataPos; - if (remainingLen < ((int)payloadSize + 1)) { - // Here we don't have enough data to read the whole payload - // Will be processed after getting more data (hopefully) - file->seekg(-remainingLen, std::ios::cur); - return; - } - - data = (uint8_t*)&newData[newDataPos + 1]; - - uint8_t isSplitComplete = false; - uint32_t outerPayloadSize = payloadSize; - - // Handle a split message, combining in until we possess the entire message - if (command == EVENT_SPLIT_MESSAGE) { - if (shouldResetSplitMessageBuf) - { - splitMessageBuf.clear(); - shouldResetSplitMessageBuf = false; - } - - int _ = 0; - uint16_t blockSize = readHalf(&data[SPLIT_MESSAGE_INTERNAL_DATA_LEN], _, payloadSize, 0); - splitMessageBuf.insert(splitMessageBuf.end(), data, data + blockSize); - - isSplitComplete = data[SPLIT_MESSAGE_INTERNAL_DATA_LEN + 3]; - if (isSplitComplete) - { - // Transform this message into a different message - command = data[SPLIT_MESSAGE_INTERNAL_DATA_LEN + 2]; - data = &splitMessageBuf[0]; - payloadSize = asmEvents[command]; - shouldResetSplitMessageBuf = true; - } - } - - switch (command) { - case EVENT_GAME_INIT: - handleGameInit(game.get(), payloadSize); - break; - case EVENT_GECKO_LIST: - handleGeckoList(game.get(), payloadSize); - break; - case EVENT_FRAME_START: - handleFrameStart(game.get(), payloadSize); - break; - case EVENT_PRE_FRAME_UPDATE: - handlePreFrameUpdate(game.get(), payloadSize); - break; - case EVENT_POST_FRAME_UPDATE: - handlePostFrameUpdate(game.get(), payloadSize); - break; - case EVENT_GAME_END: - handleGameEnd(game.get(), payloadSize); - isProcessingComplete = true; - break; - case 0x55: - // This is sort of a hack to prevent this functioning - // from processing the metadata as raw data. 0x55 is 'U' - // which is the first character after the raw data in the - // ubjson file format - //log.close(); - isProcessingComplete = true; - file->seekg(-remainingLen, std::ios::cur); - return; - } - - payloadSize = isSplitComplete ? outerPayloadSize : payloadSize; - newDataPos += payloadSize + 1; - } - } - - std::unique_ptr SlippiGame::FromFile(std::string path) { - auto result = std::make_unique(); - result->game = std::make_unique(); - result->path = path; - -#ifdef _WIN32 - // On Windows, we need to convert paths to std::wstring to deal with UTF-8 - std::wstring convertedPath = std::wstring_convert>().from_bytes(path); - result->file = std::make_unique(convertedPath, std::ios::in | std::ios::binary); -#else - result->file = std::make_unique(path, std::ios::in | std::ios::binary); -#endif - - //result->log.open("log.txt"); - if (!result->file->is_open()) { - return nullptr; - } - - //int fileLength = (int)file.tellg(); - //int rawDataPos = getRawDataPosition(&file); - //uint32_t rawDataLength = getRawDataLength(&file, rawDataPos, fileLength); - //asmEvents = getMessageSizes(&file, rawDataPos); - - //std::vector rawData(rawDataLength); - //file.seekg(rawDataPos, std::ios::beg); - //file.read(&rawData[0], rawDataLength); - - //SlippiGame* result = processFile((uint8_t*)&rawData[0], rawDataLength); - - return std::move(result); - } - - bool SlippiGame::IsProcessingComplete() { - return isProcessingComplete; - } - - bool SlippiGame::AreSettingsLoaded() { - processData(); - return game->areSettingsLoaded; - } - - bool SlippiGame::DoesFrameExist(int32_t frame) { - processData(); - return (bool)game->framesByIndex.count(frame); - } - - std::array SlippiGame::GetVersion() - { - return game->version; - } - - FrameData* SlippiGame::GetFrame(int32_t frame) { - // Get the frame we want - return game->framesByIndex.at(frame); - } - - FrameData* SlippiGame::GetFrameAt(uint32_t pos) { - if (pos >= game->frames.size()) { - return nullptr; - } - - // Get the frame we want - return game->frames[pos].get(); - } - - int32_t SlippiGame::GetLatestIndex() { - processData(); - return game->frameCount; - } - - GameSettings* SlippiGame::GetSettings() { - processData(); - return &game->settings; - } - - bool SlippiGame::DoesPlayerExist(int8_t port) { - return game->settings.players.find(port) != game->settings.players.end(); } } + +void handleGameEnd(Game *game, uint32_t maxSize) { + int idx = 0; + + game->winCondition = readByte(data, idx, maxSize, 0); +} + +// This function gets the position where the raw data starts +int getRawDataPosition(std::ifstream *f) { + char buffer[2]; + f->seekg(0, std::ios::beg); + f->read(buffer, 2); + + if (buffer[0] == 0x36) { + return 0; + } + + if (buffer[0] != '{') { + // TODO: Do something here to cause an error + return 0; + } + + // 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 + return 15; +} + +uint32_t getRawDataLength(std::ifstream *f, int position, int fileSize) { + if (position == 0) { + return fileSize; + } + + char buffer[4]; + f->seekg(position - 4, std::ios::beg); + f->read(buffer, 4); + + uint8_t *byteBuf = (uint8_t *)&buffer[0]; + uint32_t length = + byteBuf[0] << 24 | byteBuf[1] << 16 | byteBuf[2] << 8 | byteBuf[3]; + return length; +} + +std::unordered_map getMessageSizes(std::ifstream *f, + int position) { + char buffer[2]; + f->seekg(position, std::ios::beg); + f->read(buffer, 2); + if (buffer[0] != EVENT_PAYLOAD_SIZES) { + return {}; + } + + int payloadLength = buffer[1]; + std::unordered_map messageSizes = { + {EVENT_PAYLOAD_SIZES, payloadLength}}; + + std::vector messageSizesBuffer(payloadLength - 1); + f->read(&messageSizesBuffer[0], payloadLength - 1); + for (int i = 0; i < payloadLength - 1; i += 3) { + uint8_t command = messageSizesBuffer[i]; + + // Extract the bytes in u8s. Without this the chars don't or together well + uint8_t byte1 = messageSizesBuffer[i + 1]; + uint8_t byte2 = messageSizesBuffer[i + 2]; + + uint16_t size = byte1 << 8 | byte2; + messageSizes[command] = size; + } + + return messageSizes; +} + +void SlippiGame::processData() { + if (isProcessingComplete) { + // If we have finished processing this file, return + return; + } + + // This function will process as much data as possible + int startPos = (int)file->tellg(); + file->seekg(startPos); + if (startPos == 0) { + file->seekg(0, std::ios::end); + int len = (int)file->tellg(); + if (len < 2) { + // If we can't read message sizes payload size yet, return + return; + } + + int rawDataPos = getRawDataPosition(file.get()); + int rawDataLen = len - rawDataPos; + if (rawDataLen < 2) { + // 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 + file->seekg(0); + return; + } + + startPos = rawDataPos; + + char buffer[2]; + file->seekg(startPos); + file->read(buffer, 2); + file->seekg(startPos); + auto messageSizesSize = (int)buffer[1]; + if (rawDataLen < messageSizesSize) { + // If we haven't received the full payload sizes message, return + // Reset to begining so that the startPos condition will be hit again + file->seekg(0); + return; + } + + asmEvents = getMessageSizes(file.get(), rawDataPos); + } + + // Read everything to the end + file->seekg(0, std::ios::end); + int endPos = (int)file->tellg(); + int sizeToRead = endPos - startPos; + file->seekg(startPos); + // log << "Size to read: " << sizeToRead << "\n"; + // log << "Start Pos: " << startPos << "\n"; + // log << "End Pos: " << endPos << "\n\n"; + if (sizeToRead <= 0) { + return; + } + + std::vector newData(sizeToRead); + file->read(&newData[0], sizeToRead); + + int newDataPos = 0; + while (newDataPos < sizeToRead) { + auto command = newData[newDataPos]; + auto payloadSize = asmEvents[command]; + + // char buff[100]; + // snprintf(buff, sizeof(buff), "%x", command); + // log << "Command: " << buff << " | Payload Size: " << payloadSize << "\n"; + + auto remainingLen = sizeToRead - newDataPos; + if (remainingLen < ((int)payloadSize + 1)) { + // Here we don't have enough data to read the whole payload + // Will be processed after getting more data (hopefully) + file->seekg(-remainingLen, std::ios::cur); + return; + } + + data = (uint8_t *)&newData[newDataPos + 1]; + + uint8_t isSplitComplete = false; + uint32_t outerPayloadSize = payloadSize; + + // Handle a split message, combining in until we possess the entire message + if (command == EVENT_SPLIT_MESSAGE) { + if (shouldResetSplitMessageBuf) { + splitMessageBuf.clear(); + shouldResetSplitMessageBuf = false; + } + + int _ = 0; + uint16_t blockSize = + readHalf(&data[SPLIT_MESSAGE_INTERNAL_DATA_LEN], _, payloadSize, 0); + splitMessageBuf.insert(splitMessageBuf.end(), data, data + blockSize); + + isSplitComplete = data[SPLIT_MESSAGE_INTERNAL_DATA_LEN + 3]; + if (isSplitComplete) { + // Transform this message into a different message + command = data[SPLIT_MESSAGE_INTERNAL_DATA_LEN + 2]; + data = &splitMessageBuf[0]; + payloadSize = asmEvents[command]; + shouldResetSplitMessageBuf = true; + } + } + + switch (command) { + case EVENT_GAME_INIT: + handleGameInit(game.get(), payloadSize); + break; + case EVENT_GECKO_LIST: + handleGeckoList(game.get(), payloadSize); + break; + case EVENT_FRAME_START: + handleFrameStart(game.get(), payloadSize); + break; + case EVENT_PRE_FRAME_UPDATE: + handlePreFrameUpdate(game.get(), payloadSize); + break; + case EVENT_POST_FRAME_UPDATE: + handlePostFrameUpdate(game.get(), payloadSize); + break; + case EVENT_GAME_END: + handleGameEnd(game.get(), payloadSize); + isProcessingComplete = true; + break; + case 0x55: + // This is sort of a hack to prevent this functioning + // from processing the metadata as raw data. 0x55 is 'U' + // which is the first character after the raw data in the + // ubjson file format + // log.close(); + isProcessingComplete = true; + file->seekg(-remainingLen, std::ios::cur); + return; + } + + payloadSize = isSplitComplete ? outerPayloadSize : payloadSize; + newDataPos += payloadSize + 1; + } +} + +std::unique_ptr SlippiGame::FromFile(std::string path) { + auto result = std::make_unique(); + result->game = std::make_unique(); + result->path = path; + +#ifdef _WIN32 + // On Windows, we need to convert paths to std::wstring to deal with UTF-8 + std::wstring convertedPath = + std::wstring_convert>().from_bytes(path); + result->file = std::make_unique( + convertedPath, std::ios::in | std::ios::binary); +#else + result->file = + std::make_unique(path, std::ios::in | std::ios::binary); +#endif + + // result->log.open("log.txt"); + if (!result->file->is_open()) { + return nullptr; + } + + // int fileLength = (int)file.tellg(); + // int rawDataPos = getRawDataPosition(&file); + // uint32_t rawDataLength = getRawDataLength(&file, rawDataPos, fileLength); + // asmEvents = getMessageSizes(&file, rawDataPos); + + // std::vector rawData(rawDataLength); + // file.seekg(rawDataPos, std::ios::beg); + // file.read(&rawData[0], rawDataLength); + + // SlippiGame* result = processFile((uint8_t*)&rawData[0], rawDataLength); + + return std::move(result); +} + +bool SlippiGame::IsProcessingComplete() { return isProcessingComplete; } + +bool SlippiGame::AreSettingsLoaded() { + processData(); + return game->areSettingsLoaded; +} + +bool SlippiGame::DoesFrameExist(int32_t frame) { + processData(); + return (bool)game->framesByIndex.count(frame); +} + +std::array SlippiGame::GetVersion() { return game->version; } + +FrameData *SlippiGame::GetFrame(int32_t frame) { + // Get the frame we want + return game->framesByIndex.at(frame); +} + +FrameData *SlippiGame::GetFrameAt(uint32_t pos) { + if (pos >= game->frames.size()) { + return nullptr; + } + + // Get the frame we want + return game->frames[pos].get(); +} + +int32_t SlippiGame::GetLatestIndex() { + processData(); + return game->frameCount; +} + +GameSettings *SlippiGame::GetSettings() { + processData(); + return &game->settings; +} + +bool SlippiGame::DoesPlayerExist(int8_t port) { + return game->settings.players.find(port) != game->settings.players.end(); +} +} // namespace Slippi diff --git a/Externals/SlippiLib/SlippiGame.h b/Externals/SlippiLib/SlippiGame.h index de76ea75c7..bb352ec3ae 100644 --- a/Externals/SlippiLib/SlippiGame.h +++ b/Externals/SlippiLib/SlippiGame.h @@ -1,143 +1,146 @@ #pragma once -#include #include -#include -#include -#include #include +#include #include +#include +#include +#include namespace Slippi { - const uint8_t EVENT_SPLIT_MESSAGE = 0x10; - const uint8_t EVENT_PAYLOAD_SIZES = 0x35; - const uint8_t EVENT_GAME_INIT = 0x36; - const uint8_t EVENT_PRE_FRAME_UPDATE = 0x37; - const uint8_t EVENT_POST_FRAME_UPDATE = 0x38; - const uint8_t EVENT_GAME_END = 0x39; - 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 UCF_TOGGLE_SIZE = 8; - const uint8_t NAMETAG_SIZE = 8; - const int32_t GAME_FIRST_FRAME = -123; - const int32_t PLAYBACK_FIRST_SAVE = -122; - const uint8_t GAME_SHEIK_INTERNAL_ID = 0x7; - const uint8_t GAME_SHEIK_EXTERNAL_ID = 0x13; +const uint8_t EVENT_SPLIT_MESSAGE = 0x10; +const uint8_t EVENT_PAYLOAD_SIZES = 0x35; +const uint8_t EVENT_GAME_INIT = 0x36; +const uint8_t EVENT_PRE_FRAME_UPDATE = 0x37; +const uint8_t EVENT_POST_FRAME_UPDATE = 0x38; +const uint8_t EVENT_GAME_END = 0x39; +const uint8_t EVENT_FRAME_START = 0x3A; +const uint8_t EVENT_GECKO_LIST = 0x3D; - const uint32_t SPLIT_MESSAGE_INTERNAL_DATA_LEN = 512; +const uint8_t GAME_INFO_HEADER_SIZE = 78; +const uint8_t UCF_TOGGLE_SIZE = 8; +const uint8_t NAMETAG_SIZE = 8; +const int32_t GAME_FIRST_FRAME = -123; +const int32_t PLAYBACK_FIRST_SAVE = -122; +const uint8_t GAME_SHEIK_INTERNAL_ID = 0x7; +const uint8_t GAME_SHEIK_EXTERNAL_ID = 0x13; - static uint8_t* data; +const uint32_t SPLIT_MESSAGE_INTERNAL_DATA_LEN = 512; - typedef struct { - // Every player update has its own rng seed because it might change in between players - uint32_t randomSeed; +static uint8_t *data; - uint8_t internalCharacterId; - uint16_t animation; - float locationX; - float locationY; - float facingDirection; - uint8_t stocks; - float percent; - float shieldSize; - uint8_t lastMoveHitId; - uint8_t comboCount; - uint8_t lastHitBy; +typedef struct { + // Every player update has its own rng seed because it might change in between + // players + uint32_t randomSeed; - //Controller information - float joystickX; - float joystickY; - float cstickX; - float cstickY; - float trigger; - uint32_t buttons; //This will include multiple "buttons" pressed on special buttons. For example I think pressing z sets 3 bits + uint8_t internalCharacterId; + uint16_t animation; + float locationX; + float locationY; + float facingDirection; + uint8_t stocks; + float percent; + float shieldSize; + uint8_t lastMoveHitId; + uint8_t comboCount; + uint8_t lastHitBy; - //This is extra controller information - uint16_t physicalButtons; //A better representation of what a player is actually pressing - float lTrigger; - float rTrigger; + // Controller information + float joystickX; + float joystickY; + float cstickX; + float cstickY; + float trigger; + uint32_t buttons; // This will include multiple "buttons" pressed on special + // buttons. For example I think pressing z sets 3 bits - uint8_t joystickXRaw; - } PlayerFrameData; + // This is extra controller information + uint16_t physicalButtons; // A better representation of what a player is + // actually pressing + float lTrigger; + float rTrigger; - typedef struct FrameData { - int32_t frame; - uint32_t numSinceStart; - bool randomSeedExists = false; - uint32_t randomSeed; - bool inputsFullyFetched = false; - std::unordered_map players; - std::unordered_map followers; - } FrameData; + uint8_t joystickXRaw; +} PlayerFrameData; - typedef struct { - //Static data - uint8_t characterId; - uint8_t characterColor; - uint8_t playerType; - uint8_t controllerPort; - std::array nametag; - } PlayerSettings; +typedef struct FrameData { + int32_t frame; + uint32_t numSinceStart; + bool randomSeedExists = false; + uint32_t randomSeed; + bool inputsFullyFetched = false; + std::unordered_map players; + std::unordered_map followers; +} FrameData; - typedef struct { - uint16_t stage; //Stage ID - uint32_t randomSeed; - std::array header; - std::array ucfToggles; - std::unordered_map players; - uint8_t isPAL; - uint8_t isFrozenPS; - std::vector geckoCodes; - } GameSettings; +typedef struct { + // Static data + uint8_t characterId; + uint8_t characterColor; + uint8_t playerType; + uint8_t controllerPort; + std::array nametag; +} PlayerSettings; - typedef struct Game { - std::array version; - std::unordered_map framesByIndex; - std::vector> frames; - GameSettings settings; - bool areSettingsLoaded = false; +typedef struct { + uint16_t stage; // Stage ID + uint32_t randomSeed; + std::array header; + std::array ucfToggles; + std::unordered_map players; + uint8_t isPAL; + uint8_t isFrozenPS; + std::vector geckoCodes; +} GameSettings; - int32_t frameCount; // Current/last frame count +typedef struct Game { + std::array version; + std::unordered_map framesByIndex; + std::vector> frames; + GameSettings settings; + bool areSettingsLoaded = false; - //From OnGameEnd event - uint8_t winCondition; - } Game; + int32_t frameCount; // Current/last frame count - // TODO: This shouldn't be static. Doesn't matter too much atm because we always - // TODO: only read one file at a time - static std::unordered_map asmEvents = { - { EVENT_GAME_INIT, 320 }, - { EVENT_PRE_FRAME_UPDATE, 58 }, - { EVENT_POST_FRAME_UPDATE, 33 }, - { EVENT_GAME_END, 1 }, - { EVENT_FRAME_START, 8 } - }; + // From OnGameEnd event + uint8_t winCondition; +} Game; - class SlippiGame - { - public: - static std::unique_ptr FromFile(std::string path); - bool AreSettingsLoaded(); - bool DoesFrameExist(int32_t frame); - std::array GetVersion(); - FrameData* GetFrame(int32_t frame); - FrameData* GetFrameAt(uint32_t pos); - int32_t GetLatestIndex(); - GameSettings* GetSettings(); - bool DoesPlayerExist(int8_t port); - bool IsProcessingComplete(); - private: - std::unique_ptr game; - std::unique_ptr file; - std::vector rawData; - std::string path; - std::ofstream log; - std::vector splitMessageBuf; - bool shouldResetSplitMessageBuf = false; +// TODO: This shouldn't be static. Doesn't matter too much atm because we always +// TODO: only read one file at a time +static std::unordered_map asmEvents = { + {EVENT_GAME_INIT, 320}, + {EVENT_PRE_FRAME_UPDATE, 58}, + {EVENT_POST_FRAME_UPDATE, 33}, + {EVENT_GAME_END, 1}, + {EVENT_FRAME_START, 8}}; - bool isProcessingComplete = false; - void processData(); - }; -} +class SlippiGame { +public: + static std::unique_ptr FromFile(std::string path); + bool AreSettingsLoaded(); + bool DoesFrameExist(int32_t frame); + std::array GetVersion(); + FrameData *GetFrame(int32_t frame); + FrameData *GetFrameAt(uint32_t pos); + int32_t GetLatestIndex(); + GameSettings *GetSettings(); + bool DoesPlayerExist(int8_t port); + bool IsProcessingComplete(); + +private: + std::unique_ptr game; + std::unique_ptr file; + std::vector rawData; + std::string path; + std::ofstream log; + std::vector splitMessageBuf; + bool shouldResetSplitMessageBuf = false; + + bool isProcessingComplete = false; + void processData(); +}; +} // namespace Slippi diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp index a60668d0cb..68045d06d4 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp @@ -2,10 +2,9 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. - -#include -#include // std::move #include +#include +#include // std::move #include "Common/CommonPaths.h" #include "Common/CommonTypes.h" @@ -17,17 +16,17 @@ #include "Common/Thread.h" #include "Common/Version.h" -#include "Core/Core.h" #include "Core/ConfigManager.h" +#include "Core/Core.h" #include "Core/CoreTiming.h" #include "Core/Debugger/Debugger_SymbolMap.h" -#include "Core/Host.h" #include "Core/HW/EXI/EXI_DeviceSlippi.h" #include "Core/HW/Memmap.h" #include "Core/HW/SystemTimers.h" +#include "Core/Host.h" #include "Core/NetPlayClient.h" -#include "Core/Slippi/SlippiReplayComm.h" #include "Core/Slippi/SlippiPlayback.h" +#include "Core/Slippi/SlippiReplayComm.h" #include "Core/State.h" #define FRAME_INTERVAL 900 @@ -43,12 +42,13 @@ extern std::unique_ptr g_replayComm; bool isLocalConnected = false; #endif -namespace ExpansionInterface { - +namespace ExpansionInterface +{ static std::unordered_map slippi_names; static std::unordered_map slippi_connect_codes; -template bool isFutureReady(std::future& t) +template +bool isFutureReady(std::future& t) { return t.wait_for(std::chrono::seconds(0)) == std::future_status::ready; } @@ -58,7 +58,7 @@ std::vector uint16ToVector(u16 num) u8 byte0 = num >> 8; u8 byte1 = num & 0xFF; - return std::vector({ byte0, byte1 }); + return std::vector({byte0, byte1}); } std::vector uint32ToVector(u32 num) @@ -68,7 +68,7 @@ std::vector uint32ToVector(u32 num) u8 byte2 = (num & 0xFF00) >> 8; u8 byte3 = num & 0xFF; - return std::vector({ byte0, byte1, byte2, byte3 }); + return std::vector({byte0, byte1, byte2, byte3}); } std::vector int32ToVector(int32_t num) @@ -78,7 +78,7 @@ std::vector int32ToVector(int32_t num) u8 byte2 = (num & 0xFF00) >> 8; u8 byte3 = num & 0xFF; - return std::vector({ byte0, byte1, byte2, byte3 }); + return std::vector({byte0, byte1, byte2, byte3}); } void appendWordToBuffer(std::vector* buf, u32 word) @@ -120,60 +120,76 @@ CEXISlippi::CEXISlippi() // MnMaAll.usd std::string origStr; std::string modifiedStr; - File::ReadFileToString("C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\MnMaAll.usd", origStr); - File::ReadFileToString("C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\MnMaAll-new.usd", - modifiedStr); + File::ReadFileToString( + "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\MnMaAll.usd", origStr); + File::ReadFileToString( + "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\MnMaAll-new.usd", modifiedStr); std::vector orig(origStr.begin(), origStr.end()); std::vector modified(modifiedStr.begin(), modifiedStr.end()); auto diff = processDiff(orig, modified); - File::WriteStringToFile(diff, "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\MnMaAll.usd.diff"); + File::WriteStringToFile( + diff, "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\MnMaAll.usd.diff"); File::WriteStringToFile(diff, "C:\\Dolphin\\IshiiDev\\Sys\\GameFiles\\GALE01\\MnMaAll.usd.diff"); // MnExtAll.usd - File::ReadFileToString("C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\CSS\\MnExtAll.usd", origStr); - File::ReadFileToString("C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\CSS\\MnExtAll-new.usd", modifiedStr); + File::ReadFileToString("C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\CSS\\MnExtAll.usd", + origStr); + File::ReadFileToString( + "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\CSS\\MnExtAll-new.usd", modifiedStr); orig = std::vector(origStr.begin(), origStr.end()); modified = std::vector(modifiedStr.begin(), modifiedStr.end()); diff = processDiff(orig, modified); - File::WriteStringToFile(diff, "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\CSS\\MnExtAll.usd.diff"); + File::WriteStringToFile( + diff, "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\CSS\\MnExtAll.usd.diff"); File::WriteStringToFile(diff, "C:\\Dolphin\\IshiiDev\\Sys\\GameFiles\\GALE01\\MnExtAll.usd.diff"); // SdMenu.usd - File::ReadFileToString("C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\SdMenu.usd", origStr); - File::ReadFileToString("C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\SdMenu-new.usd", modifiedStr); + File::ReadFileToString("C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\SdMenu.usd", + origStr); + File::ReadFileToString( + "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\SdMenu-new.usd", modifiedStr); orig = std::vector(origStr.begin(), origStr.end()); modified = std::vector(modifiedStr.begin(), modifiedStr.end()); diff = processDiff(orig, modified); - File::WriteStringToFile(diff, "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\SdMenu.usd.diff"); + File::WriteStringToFile( + diff, "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\SdMenu.usd.diff"); File::WriteStringToFile(diff, "C:\\Dolphin\\IshiiDev\\Sys\\GameFiles\\GALE01\\SdMenu.usd.diff"); // Japanese Files // MnMaAll.dat - File::ReadFileToString("C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\MnMaAll.dat", origStr); - File::ReadFileToString("C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\MnMaAll-new.dat", - modifiedStr); + File::ReadFileToString( + "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\MnMaAll.dat", origStr); + File::ReadFileToString( + "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\MnMaAll-new.dat", modifiedStr); orig = std::vector(origStr.begin(), origStr.end()); modified = std::vector(modifiedStr.begin(), modifiedStr.end()); diff = processDiff(orig, modified); - File::WriteStringToFile(diff, "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\MnMaAll.dat.diff"); + File::WriteStringToFile( + diff, "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\MnMaAll.dat.diff"); File::WriteStringToFile(diff, "C:\\Dolphin\\IshiiDev\\Sys\\GameFiles\\GALE01\\MnMaAll.dat.diff"); // MnExtAll.dat - File::ReadFileToString("C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\CSS\\MnExtAll.dat", origStr); - File::ReadFileToString("C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\CSS\\MnExtAll-new.dat", modifiedStr); + File::ReadFileToString("C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\CSS\\MnExtAll.dat", + origStr); + File::ReadFileToString( + "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\CSS\\MnExtAll-new.dat", modifiedStr); orig = std::vector(origStr.begin(), origStr.end()); modified = std::vector(modifiedStr.begin(), modifiedStr.end()); diff = processDiff(orig, modified); - File::WriteStringToFile(diff, "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\CSS\\MnExtAll.dat.diff"); + File::WriteStringToFile( + diff, "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\CSS\\MnExtAll.dat.diff"); File::WriteStringToFile(diff, "C:\\Dolphin\\IshiiDev\\Sys\\GameFiles\\GALE01\\MnExtAll.dat.diff"); // SdMenu.dat - File::ReadFileToString("C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\SdMenu.dat", origStr); - File::ReadFileToString("C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\SdMenu-new.dat", modifiedStr); + File::ReadFileToString("C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\SdMenu.dat", + origStr); + File::ReadFileToString( + "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\SdMenu-new.dat", modifiedStr); orig = std::vector(origStr.begin(), origStr.end()); modified = std::vector(modifiedStr.begin(), modifiedStr.end()); diff = processDiff(orig, modified); - File::WriteStringToFile(diff, "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\SdMenu.dat.diff"); + File::WriteStringToFile( + diff, "C:\\Users\\Jas\\Documents\\Melee\\Textures\\Slippi\\MainMenu\\SdMenu.dat.diff"); File::WriteStringToFile(diff, "C:\\Dolphin\\IshiiDev\\Sys\\GameFiles\\GALE01\\SdMenu.dat.diff"); // TEMP - Restore orig @@ -257,7 +273,7 @@ std::unordered_map CEXISlippi::getNetplayNames() std::vector CEXISlippi::generateMetadata() { - std::vector metadata({ 'U', 8, 'm', 'e', 't', 'a', 'd', 'a', 't', 'a', '{' }); + std::vector metadata({'U', 8, 'm', 'e', 't', 'a', 'd', 'a', 't', 'a', '{'}); // TODO: Abstract out UBJSON functions to make this cleaner @@ -265,17 +281,18 @@ std::vector CEXISlippi::generateMetadata() u8 dateTimeStrLength = sizeof "2011-10-08T07:07:09Z"; std::vector dateTimeBuf(dateTimeStrLength); strftime(&dateTimeBuf[0], dateTimeStrLength, "%FT%TZ", gmtime(&gameStartTime)); - dateTimeBuf.pop_back(); // Removes the \0 from the back of string - metadata.insert(metadata.end(), { 'U', 7, 's', 't', 'a', 'r', 't', 'A', 't', 'S', 'U', (u8)dateTimeBuf.size() }); + dateTimeBuf.pop_back(); // Removes the \0 from the back of string + metadata.insert(metadata.end(), + {'U', 7, 's', 't', 'a', 'r', 't', 'A', 't', 'S', 'U', (u8)dateTimeBuf.size()}); metadata.insert(metadata.end(), dateTimeBuf.begin(), dateTimeBuf.end()); // Add game duration std::vector lastFrameToWrite = int32ToVector(lastFrame); - metadata.insert(metadata.end(), { 'U', 9, 'l', 'a', 's', 't', 'F', 'r', 'a', 'm', 'e', 'l' }); + metadata.insert(metadata.end(), {'U', 9, 'l', 'a', 's', 't', 'F', 'r', 'a', 'm', 'e', 'l'}); metadata.insert(metadata.end(), lastFrameToWrite.begin(), lastFrameToWrite.end()); // Add players elements to metadata, one per player index - metadata.insert(metadata.end(), { 'U', 7, 'p', 'l', 'a', 'y', 'e', 'r', 's', '{' }); + metadata.insert(metadata.end(), {'U', 7, 'p', 'l', 'a', 'y', 'e', 'r', 's', '{'}); auto playerNames = getNetplayNames(); @@ -291,13 +308,13 @@ std::vector CEXISlippi::generateMetadata() metadata.push_back('{'); // Add names element for this player - metadata.insert(metadata.end(), { 'U', 5, 'n', 'a', 'm', 'e', 's', '{' }); + metadata.insert(metadata.end(), {'U', 5, 'n', 'a', 'm', 'e', 's', '{'}); if (playerNames.count(playerIndex)) { auto playerName = playerNames[playerIndex]; // Add netplay element for this player name - metadata.insert(metadata.end(), { 'U', 7, 'n', 'e', 't', 'p', 'l', 'a', 'y', 'S', 'U' }); + metadata.insert(metadata.end(), {'U', 7, 'n', 'e', 't', 'p', 'l', 'a', 'y', 'S', 'U'}); metadata.push_back((u8)playerName.length()); metadata.insert(metadata.end(), playerName.begin(), playerName.end()); } @@ -306,15 +323,16 @@ std::vector CEXISlippi::generateMetadata() { auto connectCode = slippi_connect_codes[playerIndex]; // Add connection code element for this player name - metadata.insert(metadata.end(), { 'U', 4, 'c', 'o', 'd', 'e', 'S', 'U' }); + metadata.insert(metadata.end(), {'U', 4, 'c', 'o', 'd', 'e', 'S', 'U'}); metadata.push_back((u8)connectCode.length()); metadata.insert(metadata.end(), connectCode.begin(), connectCode.end()); } - metadata.push_back('}'); // close names + metadata.push_back('}'); // close names // Add character element for this player - metadata.insert(metadata.end(), { 'U', 10, 'c', 'h', 'a', 'r', 'a', 'c', 't', 'e', 'r', 's', '{' }); + metadata.insert(metadata.end(), + {'U', 10, 'c', 'h', 'a', 'r', 'a', 'c', 't', 'e', 'r', 's', '{'}); for (auto it2 = playerCharacterUsage.begin(); it2 != playerCharacterUsage.end(); ++it2) { metadata.push_back('U'); @@ -326,15 +344,15 @@ std::vector CEXISlippi::generateMetadata() std::vector frameCount = uint32ToVector(it2->second); metadata.insert(metadata.end(), frameCount.begin(), frameCount.end()); } - metadata.push_back('}'); // close characters + metadata.push_back('}'); // close characters - metadata.push_back('}'); // close player + metadata.push_back('}'); // close player } metadata.push_back('}'); // Indicate this was played on dolphin - metadata.insert(metadata.end(), - { 'U', 8, 'p', 'l', 'a', 'y', 'e', 'd', 'O', 'n', 'S', 'U', 7, 'd', 'o', 'l', 'p', 'h', 'i', 'n' }); + metadata.insert(metadata.end(), {'U', 8, 'p', 'l', 'a', 'y', 'e', 'd', 'O', 'n', + 'S', 'U', 7, 'd', 'o', 'l', 'p', 'h', 'i', 'n'}); metadata.push_back('}'); return metadata; @@ -407,7 +425,7 @@ void CEXISlippi::writeToFile(std::unique_ptr msg) // Start ubjson file and prepare the "raw" element that game // data output will be dumped into. The size of the raw output will // be initialized to 0 until all of the data has been received - std::vector headerBytes({ '{', 'U', 3, 'r', 'a', 'w', '[', '$', 'U', '#', 'l', 0, 0, 0, 0 }); + std::vector headerBytes({'{', 'U', 3, 'r', 'a', 'w', '[', '$', 'U', '#', 'l', 0, 0, 0, 0}); dataToWrite.insert(dataToWrite.end(), headerBytes.begin(), headerBytes.end()); // Used to keep track of how many bytes have been written to the file @@ -540,11 +558,11 @@ void CEXISlippi::createNewFile() if (!m_file) { PanicAlertT("Could not create .slp replay file [%s].\n\n" - "The replay folder's path might be invalid, or you might " - "not have permission to write to it.\n\n" - "You can change the replay folder in Config > GameCube > " - "Slippi Replay Settings.", - filepath.c_str()); + "The replay folder's path might be invalid, or you might " + "not have permission to write to it.\n\n" + "You can change the replay folder in Config > GameCube > " + "Slippi Replay Settings.", + filepath.c_str()); } } @@ -594,13 +612,14 @@ void CEXISlippi::prepareGameInfo(u8* payload) // Prepare playback savestate payload playbackSavestatePayload.clear(); - appendWordToBuffer(&playbackSavestatePayload, 0); // This space will be used to set frame index + appendWordToBuffer(&playbackSavestatePayload, 0); // This space will be used to set frame index int bkpPos = 0; while ((*(u32*)(&payload[bkpPos * 8])) != 0) { bkpPos += 1; } - playbackSavestatePayload.insert(playbackSavestatePayload.end(), payload, payload + (bkpPos * 8 + 4)); + playbackSavestatePayload.insert(playbackSavestatePayload.end(), payload, + payload + (bkpPos * 8 + 4)); Slippi::GameSettings* settings = m_current_game->GetSettings(); @@ -621,7 +640,7 @@ void CEXISlippi::prepareGameInfo(u8* payload) // This is kinda dumb but we need to handle the case where a player transforms // into sheik/zelda immediately. This info is not stored in the game info header // and so let's overwrite those values - 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 gameInfoHeader = settings->header; for (int i = 0; i < 4; i++) { @@ -721,7 +740,7 @@ void CEXISlippi::prepareGameInfo(u8* payload) // Initialize replay related threads if not viewing rollback versions of relays if (replayCommSettings.rollbackDisplayMethod == "off" && - (replayCommSettings.mode == "normal" || replayCommSettings.mode == "queue")) + (replayCommSettings.mode == "normal" || replayCommSettings.mode == "queue")) { g_playbackStatus->startThreads(); } @@ -732,283 +751,318 @@ void CEXISlippi::prepareGeckoList() // TODO: How do I move this somewhere else? // This contains all of the codes required to play legacy replays (UCF, PAL, Frz Stadium) static std::vector defaultCodeList = { - 0xC2, 0x0C, 0x9A, 0x44, 0x00, 0x00, 0x00, 0x2F, // #External/UCF + Arduino Toggle UI/UCF/UCF 0.74 - // Dashback - Check for Toggle.asm - 0xD0, 0x1F, 0x00, 0x2C, 0x88, 0x9F, 0x06, 0x18, 0x38, 0x62, 0xF2, 0x28, 0x7C, 0x63, 0x20, 0xAE, 0x2C, 0x03, - 0x00, 0x01, 0x41, 0x82, 0x00, 0x14, 0x38, 0x62, 0xF2, 0x2C, 0x7C, 0x63, 0x20, 0xAE, 0x2C, 0x03, 0x00, 0x01, - 0x40, 0x82, 0x01, 0x50, 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x04, 0x94, 0x21, 0xFF, 0x50, 0xBE, 0x81, - 0x00, 0x08, 0x48, 0x00, 0x01, 0x21, 0x7F, 0xC8, 0x02, 0xA6, 0xC0, 0x3F, 0x08, 0x94, 0xC0, 0x5E, 0x00, 0x00, - 0xFC, 0x01, 0x10, 0x40, 0x40, 0x82, 0x01, 0x18, 0x80, 0x8D, 0xAE, 0xB4, 0xC0, 0x3F, 0x06, 0x20, 0xFC, 0x20, - 0x0A, 0x10, 0xC0, 0x44, 0x00, 0x3C, 0xFC, 0x01, 0x10, 0x40, 0x41, 0x80, 0x01, 0x00, 0x88, 0x7F, 0x06, 0x70, - 0x2C, 0x03, 0x00, 0x02, 0x40, 0x80, 0x00, 0xF4, 0x88, 0x7F, 0x22, 0x1F, 0x54, 0x60, 0x07, 0x39, 0x40, 0x82, - 0x00, 0xE8, 0x3C, 0x60, 0x80, 0x4C, 0x60, 0x63, 0x1F, 0x78, 0x8B, 0xA3, 0x00, 0x01, 0x38, 0x7D, 0xFF, 0xFE, - 0x88, 0x9F, 0x06, 0x18, 0x48, 0x00, 0x00, 0x8D, 0x7C, 0x7C, 0x1B, 0x78, 0x7F, 0xA3, 0xEB, 0x78, 0x88, 0x9F, - 0x06, 0x18, 0x48, 0x00, 0x00, 0x7D, 0x7C, 0x7C, 0x18, 0x50, 0x7C, 0x63, 0x19, 0xD6, 0x2C, 0x03, 0x15, 0xF9, - 0x40, 0x81, 0x00, 0xB0, 0x38, 0x00, 0x00, 0x01, 0x90, 0x1F, 0x23, 0x58, 0x90, 0x1F, 0x23, 0x40, 0x80, 0x9F, - 0x00, 0x04, 0x2C, 0x04, 0x00, 0x0A, 0x40, 0xA2, 0x00, 0x98, 0x88, 0x7F, 0x00, 0x0C, 0x38, 0x80, 0x00, 0x01, - 0x3D, 0x80, 0x80, 0x03, 0x61, 0x8C, 0x41, 0x8C, 0x7D, 0x89, 0x03, 0xA6, 0x4E, 0x80, 0x04, 0x21, 0x2C, 0x03, - 0x00, 0x00, 0x41, 0x82, 0x00, 0x78, 0x80, 0x83, 0x00, 0x2C, 0x80, 0x84, 0x1E, 0xCC, 0xC0, 0x3F, 0x00, 0x2C, - 0xD0, 0x24, 0x00, 0x18, 0xC0, 0x5E, 0x00, 0x04, 0xFC, 0x01, 0x10, 0x40, 0x41, 0x81, 0x00, 0x0C, 0x38, 0x60, - 0x00, 0x80, 0x48, 0x00, 0x00, 0x08, 0x38, 0x60, 0x00, 0x7F, 0x98, 0x64, 0x00, 0x06, 0x48, 0x00, 0x00, 0x48, - 0x7C, 0x85, 0x23, 0x78, 0x38, 0x63, 0xFF, 0xFF, 0x2C, 0x03, 0x00, 0x00, 0x40, 0x80, 0x00, 0x08, 0x38, 0x63, - 0x00, 0x05, 0x3C, 0x80, 0x80, 0x46, 0x60, 0x84, 0xB1, 0x08, 0x1C, 0x63, 0x00, 0x30, 0x7C, 0x84, 0x1A, 0x14, - 0x1C, 0x65, 0x00, 0x0C, 0x7C, 0x84, 0x1A, 0x14, 0x88, 0x64, 0x00, 0x02, 0x7C, 0x63, 0x07, 0x74, 0x4E, 0x80, - 0x00, 0x20, 0x4E, 0x80, 0x00, 0x21, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBA, 0x81, 0x00, 0x08, - 0x80, 0x01, 0x00, 0xB4, 0x38, 0x21, 0x00, 0xB0, 0x7C, 0x08, 0x03, 0xA6, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x09, - 0x98, 0xA4, 0x00, 0x00, 0x00, 0x2B, // #External/UCF + Arduino Toggle UI/UCF/UCF - // 0.74 Shield Drop - Check for Toggle.asm - 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x04, 0x94, 0x21, 0xFF, 0x50, 0xBE, 0x81, 0x00, 0x08, 0x7C, 0x7E, - 0x1B, 0x78, 0x83, 0xFE, 0x00, 0x2C, 0x48, 0x00, 0x01, 0x01, 0x7F, 0xA8, 0x02, 0xA6, 0x88, 0x9F, 0x06, 0x18, - 0x38, 0x62, 0xF2, 0x28, 0x7C, 0x63, 0x20, 0xAE, 0x2C, 0x03, 0x00, 0x01, 0x41, 0x82, 0x00, 0x14, 0x38, 0x62, - 0xF2, 0x30, 0x7C, 0x63, 0x20, 0xAE, 0x2C, 0x03, 0x00, 0x01, 0x40, 0x82, 0x00, 0xF8, 0xC0, 0x3F, 0x06, 0x3C, - 0x80, 0x6D, 0xAE, 0xB4, 0xC0, 0x03, 0x03, 0x14, 0xFC, 0x01, 0x00, 0x40, 0x40, 0x81, 0x00, 0xE4, 0xC0, 0x3F, - 0x06, 0x20, 0x48, 0x00, 0x00, 0x71, 0xD0, 0x21, 0x00, 0x90, 0xC0, 0x3F, 0x06, 0x24, 0x48, 0x00, 0x00, 0x65, - 0xC0, 0x41, 0x00, 0x90, 0xEC, 0x42, 0x00, 0xB2, 0xEC, 0x21, 0x00, 0x72, 0xEC, 0x21, 0x10, 0x2A, 0xC0, 0x5D, - 0x00, 0x0C, 0xFC, 0x01, 0x10, 0x40, 0x41, 0x80, 0x00, 0xB4, 0x88, 0x9F, 0x06, 0x70, 0x2C, 0x04, 0x00, 0x03, - 0x40, 0x81, 0x00, 0xA8, 0xC0, 0x1D, 0x00, 0x10, 0xC0, 0x3F, 0x06, 0x24, 0xFC, 0x00, 0x08, 0x40, 0x40, 0x80, - 0x00, 0x98, 0xBA, 0x81, 0x00, 0x08, 0x80, 0x01, 0x00, 0xB4, 0x38, 0x21, 0x00, 0xB0, 0x7C, 0x08, 0x03, 0xA6, - 0x80, 0x61, 0x00, 0x1C, 0x83, 0xE1, 0x00, 0x14, 0x38, 0x21, 0x00, 0x18, 0x38, 0x63, 0x00, 0x08, 0x7C, 0x68, - 0x03, 0xA6, 0x4E, 0x80, 0x00, 0x20, 0xFC, 0x00, 0x0A, 0x10, 0xC0, 0x3D, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x72, - 0xC0, 0x3D, 0x00, 0x04, 0xEC, 0x00, 0x08, 0x28, 0xFC, 0x00, 0x00, 0x1E, 0xD8, 0x01, 0x00, 0x80, 0x80, 0x61, - 0x00, 0x84, 0x38, 0x63, 0x00, 0x02, 0x3C, 0x00, 0x43, 0x30, 0xC8, 0x5D, 0x00, 0x14, 0x6C, 0x63, 0x80, 0x00, - 0x90, 0x01, 0x00, 0x80, 0x90, 0x61, 0x00, 0x84, 0xC8, 0x21, 0x00, 0x80, 0xEC, 0x01, 0x10, 0x28, 0xC0, 0x3D, - 0x00, 0x00, 0xEC, 0x20, 0x08, 0x24, 0x4E, 0x80, 0x00, 0x20, 0x4E, 0x80, 0x00, 0x21, 0x42, 0xA0, 0x00, 0x00, - 0x37, 0x27, 0x00, 0x00, 0x43, 0x30, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0xBF, 0x4C, 0xCC, 0xCD, 0x43, 0x30, - 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x7F, 0xC3, 0xF3, 0x78, 0x7F, 0xE4, 0xFB, 0x78, 0xBA, 0x81, 0x00, 0x08, - 0x80, 0x01, 0x00, 0xB4, 0x38, 0x21, 0x00, 0xB0, 0x7C, 0x08, 0x03, 0xA6, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xC2, 0x16, 0xE7, 0x50, 0x00, 0x00, 0x00, - 0x33, // #Common/StaticPatches/ToggledStaticOverwrites.asm - 0x88, 0x62, 0xF2, 0x34, 0x2C, 0x03, 0x00, 0x00, 0x41, 0x82, 0x00, 0x14, 0x48, 0x00, 0x00, 0x75, 0x7C, 0x68, - 0x02, 0xA6, 0x48, 0x00, 0x01, 0x3D, 0x48, 0x00, 0x00, 0x14, 0x48, 0x00, 0x00, 0x95, 0x7C, 0x68, 0x02, 0xA6, - 0x48, 0x00, 0x01, 0x2D, 0x48, 0x00, 0x00, 0x04, 0x88, 0x62, 0xF2, 0x38, 0x2C, 0x03, 0x00, 0x00, 0x41, 0x82, - 0x00, 0x14, 0x48, 0x00, 0x00, 0xB9, 0x7C, 0x68, 0x02, 0xA6, 0x48, 0x00, 0x01, 0x11, 0x48, 0x00, 0x00, 0x10, - 0x48, 0x00, 0x00, 0xC9, 0x7C, 0x68, 0x02, 0xA6, 0x48, 0x00, 0x01, 0x01, 0x88, 0x62, 0xF2, 0x3C, 0x2C, 0x03, - 0x00, 0x00, 0x41, 0x82, 0x00, 0x14, 0x48, 0x00, 0x00, 0xD1, 0x7C, 0x68, 0x02, 0xA6, 0x48, 0x00, 0x00, 0xE9, - 0x48, 0x00, 0x01, 0x04, 0x48, 0x00, 0x00, 0xD1, 0x7C, 0x68, 0x02, 0xA6, 0x48, 0x00, 0x00, 0xD9, 0x48, 0x00, - 0x00, 0xF4, 0x4E, 0x80, 0x00, 0x21, 0x80, 0x3C, 0xE4, 0xD4, 0x00, 0x24, 0x04, 0x64, 0x80, 0x07, 0x96, 0xE0, - 0x60, 0x00, 0x00, 0x00, 0x80, 0x2B, 0x7E, 0x54, 0x48, 0x00, 0x00, 0x88, 0x80, 0x2B, 0x80, 0x8C, 0x48, 0x00, - 0x00, 0x84, 0x80, 0x12, 0x39, 0xA8, 0x60, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x4E, 0x80, 0x00, 0x21, - 0x80, 0x3C, 0xE4, 0xD4, 0x00, 0x20, 0x00, 0x00, 0x80, 0x07, 0x96, 0xE0, 0x3A, 0x40, 0x00, 0x01, 0x80, 0x2B, - 0x7E, 0x54, 0x88, 0x7F, 0x22, 0x40, 0x80, 0x2B, 0x80, 0x8C, 0x2C, 0x03, 0x00, 0x02, 0x80, 0x10, 0xFC, 0x48, - 0x90, 0x05, 0x21, 0xDC, 0x80, 0x10, 0xFB, 0x68, 0x90, 0x05, 0x21, 0xDC, 0x80, 0x12, 0x39, 0xA8, 0x90, 0x1F, - 0x1A, 0x5C, 0xFF, 0xFF, 0xFF, 0xFF, 0x4E, 0x80, 0x00, 0x21, 0x80, 0x1D, 0x46, 0x10, 0x48, 0x00, 0x00, 0x4C, - 0x80, 0x1D, 0x47, 0x24, 0x48, 0x00, 0x00, 0x3C, 0x80, 0x1D, 0x46, 0x0C, 0x80, 0x9F, 0x00, 0xEC, 0xFF, 0xFF, - 0xFF, 0xFF, 0x4E, 0x80, 0x00, 0x21, 0x80, 0x1D, 0x46, 0x10, 0x38, 0x83, 0x7F, 0x9C, 0x80, 0x1D, 0x47, 0x24, - 0x88, 0x1B, 0x00, 0xC4, 0x80, 0x1D, 0x46, 0x0C, 0x3C, 0x60, 0x80, 0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0x4E, 0x80, - 0x00, 0x21, 0x80, 0x1D, 0x45, 0xFC, 0x48, 0x00, 0x09, 0xDC, 0xFF, 0xFF, 0xFF, 0xFF, 0x4E, 0x80, 0x00, 0x21, - 0x80, 0x1D, 0x45, 0xFC, 0x40, 0x80, 0x09, 0xDC, 0xFF, 0xFF, 0xFF, 0xFF, 0x38, 0xA3, 0xFF, 0xFC, 0x84, 0x65, - 0x00, 0x04, 0x2C, 0x03, 0xFF, 0xFF, 0x41, 0x82, 0x00, 0x10, 0x84, 0x85, 0x00, 0x04, 0x90, 0x83, 0x00, 0x00, - 0x4B, 0xFF, 0xFF, 0xEC, 0x4E, 0x80, 0x00, 0x20, 0x3C, 0x60, 0x80, 0x00, 0x3C, 0x80, 0x00, 0x3B, 0x60, 0x84, - 0x72, 0x2C, 0x3D, 0x80, 0x80, 0x32, 0x61, 0x8C, 0x8F, 0x50, 0x7D, 0x89, 0x03, 0xA6, 0x4E, 0x80, 0x04, 0x21, - 0x3C, 0x60, 0x80, 0x17, 0x3C, 0x80, 0x80, 0x17, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x1D, 0x14, 0xC8, 0x00, 0x00, - 0x00, 0x04, // #Common/Preload Stadium - // Transformations/Handlers/Init - // isLoaded Bool.asm - 0x88, 0x62, 0xF2, 0x38, 0x2C, 0x03, 0x00, 0x00, 0x41, 0x82, 0x00, 0x0C, 0x38, 0x60, 0x00, 0x00, 0x98, 0x7F, - 0x00, 0xF0, 0x3B, 0xA0, 0x00, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x1D, 0x45, 0xEC, - 0x00, 0x00, 0x00, 0x1B, // #Common/Preload Stadium - // Transformations/Handlers/Load - // Transformation.asm - 0x88, 0x62, 0xF2, 0x38, 0x2C, 0x03, 0x00, 0x00, 0x41, 0x82, 0x00, 0xC4, 0x88, 0x7F, 0x00, 0xF0, 0x2C, 0x03, - 0x00, 0x00, 0x40, 0x82, 0x00, 0xB8, 0x38, 0x60, 0x00, 0x04, 0x3D, 0x80, 0x80, 0x38, 0x61, 0x8C, 0x05, 0x80, - 0x7D, 0x89, 0x03, 0xA6, 0x4E, 0x80, 0x04, 0x21, 0x54, 0x60, 0x10, 0x3A, 0xA8, 0x7F, 0x00, 0xE2, 0x3C, 0x80, - 0x80, 0x3B, 0x60, 0x84, 0x7F, 0x9C, 0x7C, 0x84, 0x00, 0x2E, 0x7C, 0x03, 0x20, 0x00, 0x41, 0x82, 0xFF, 0xD4, - 0x90, 0x9F, 0x00, 0xEC, 0x2C, 0x04, 0x00, 0x03, 0x40, 0x82, 0x00, 0x0C, 0x38, 0x80, 0x00, 0x00, 0x48, 0x00, - 0x00, 0x34, 0x2C, 0x04, 0x00, 0x04, 0x40, 0x82, 0x00, 0x0C, 0x38, 0x80, 0x00, 0x01, 0x48, 0x00, 0x00, 0x24, - 0x2C, 0x04, 0x00, 0x09, 0x40, 0x82, 0x00, 0x0C, 0x38, 0x80, 0x00, 0x02, 0x48, 0x00, 0x00, 0x14, 0x2C, 0x04, - 0x00, 0x06, 0x40, 0x82, 0x00, 0x00, 0x38, 0x80, 0x00, 0x03, 0x48, 0x00, 0x00, 0x04, 0x3C, 0x60, 0x80, 0x3E, - 0x60, 0x63, 0x12, 0x48, 0x54, 0x80, 0x10, 0x3A, 0x7C, 0x63, 0x02, 0x14, 0x80, 0x63, 0x03, 0xD8, 0x80, 0x9F, - 0x00, 0xCC, 0x38, 0xBF, 0x00, 0xC8, 0x3C, 0xC0, 0x80, 0x1D, 0x60, 0xC6, 0x42, 0x20, 0x38, 0xE0, 0x00, 0x00, - 0x3D, 0x80, 0x80, 0x01, 0x61, 0x8C, 0x65, 0x80, 0x7D, 0x89, 0x03, 0xA6, 0x4E, 0x80, 0x04, 0x21, 0x38, 0x60, - 0x00, 0x01, 0x98, 0x7F, 0x00, 0xF0, 0x80, 0x7F, 0x00, 0xD8, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xC2, 0x1D, 0x4F, 0x14, 0x00, 0x00, 0x00, 0x04, // #Common/Preload - // Stadium - // Transformations/Handlers/Reset - // isLoaded.asm - 0x88, 0x62, 0xF2, 0x38, 0x2C, 0x03, 0x00, 0x00, 0x41, 0x82, 0x00, 0x0C, 0x38, 0x60, 0x00, 0x00, 0x98, 0x7F, - 0x00, 0xF0, 0x80, 0x6D, 0xB2, 0xD8, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x06, 0x8F, 0x30, - 0x00, 0x00, 0x00, 0x9D, // #Common/PAL/Handlers/Character DAT - // Patcher.asm - 0x88, 0x62, 0xF2, 0x34, 0x2C, 0x03, 0x00, 0x00, 0x41, 0x82, 0x04, 0xD4, 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, - 0x00, 0x04, 0x94, 0x21, 0xFF, 0x50, 0xBE, 0x81, 0x00, 0x08, 0x83, 0xFE, 0x01, 0x0C, 0x83, 0xFF, 0x00, 0x08, - 0x3B, 0xFF, 0xFF, 0xE0, 0x80, 0x7D, 0x00, 0x00, 0x2C, 0x03, 0x00, 0x1B, 0x40, 0x80, 0x04, 0x9C, 0x48, 0x00, - 0x00, 0x71, 0x48, 0x00, 0x00, 0xA9, 0x48, 0x00, 0x00, 0xB9, 0x48, 0x00, 0x01, 0x51, 0x48, 0x00, 0x01, 0x79, - 0x48, 0x00, 0x01, 0x79, 0x48, 0x00, 0x02, 0x29, 0x48, 0x00, 0x02, 0x39, 0x48, 0x00, 0x02, 0x81, 0x48, 0x00, - 0x02, 0xF9, 0x48, 0x00, 0x03, 0x11, 0x48, 0x00, 0x03, 0x11, 0x48, 0x00, 0x03, 0x11, 0x48, 0x00, 0x03, 0x11, - 0x48, 0x00, 0x03, 0x21, 0x48, 0x00, 0x03, 0x21, 0x48, 0x00, 0x03, 0x89, 0x48, 0x00, 0x03, 0x89, 0x48, 0x00, - 0x03, 0x91, 0x48, 0x00, 0x03, 0x91, 0x48, 0x00, 0x03, 0xA9, 0x48, 0x00, 0x03, 0xA9, 0x48, 0x00, 0x03, 0xB9, - 0x48, 0x00, 0x03, 0xB9, 0x48, 0x00, 0x03, 0xC9, 0x48, 0x00, 0x03, 0xC9, 0x48, 0x00, 0x03, 0xC9, 0x48, 0x00, - 0x04, 0x29, 0x7C, 0x88, 0x02, 0xA6, 0x1C, 0x63, 0x00, 0x04, 0x7C, 0x84, 0x1A, 0x14, 0x80, 0xA4, 0x00, 0x00, - 0x54, 0xA5, 0x01, 0xBA, 0x7C, 0xA4, 0x2A, 0x14, 0x80, 0x65, 0x00, 0x00, 0x80, 0x85, 0x00, 0x04, 0x2C, 0x03, - 0x00, 0xFF, 0x41, 0x82, 0x00, 0x14, 0x7C, 0x63, 0xFA, 0x14, 0x90, 0x83, 0x00, 0x00, 0x38, 0xA5, 0x00, 0x08, - 0x4B, 0xFF, 0xFF, 0xE4, 0x48, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x33, 0x44, 0x3F, 0x54, 0x7A, 0xE1, 0x00, 0x00, - 0x33, 0x60, 0x42, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x37, 0x9C, 0x42, 0x92, 0x00, 0x00, - 0x00, 0x00, 0x39, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x0C, 0x40, 0x86, 0x66, 0x66, 0x00, 0x00, - 0x39, 0x10, 0x3D, 0xEA, 0x0E, 0xA1, 0x00, 0x00, 0x39, 0x28, 0x41, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x04, - 0x2C, 0x01, 0x48, 0x0C, 0x00, 0x00, 0x47, 0x20, 0x1B, 0x96, 0x80, 0x13, 0x00, 0x00, 0x47, 0x34, 0x1B, 0x96, - 0x80, 0x13, 0x00, 0x00, 0x47, 0x3C, 0x04, 0x00, 0x00, 0x09, 0x00, 0x00, 0x4A, 0x40, 0x2C, 0x00, 0x68, 0x11, - 0x00, 0x00, 0x4A, 0x4C, 0x28, 0x1B, 0x00, 0x13, 0x00, 0x00, 0x4A, 0x50, 0x0D, 0x00, 0x01, 0x0B, 0x00, 0x00, - 0x4A, 0x54, 0x2C, 0x80, 0x68, 0x11, 0x00, 0x00, 0x4A, 0x60, 0x28, 0x1B, 0x00, 0x13, 0x00, 0x00, 0x4A, 0x64, - 0x0D, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x4B, 0x24, 0x2C, 0x00, 0x68, 0x0D, 0x00, 0x00, 0x4B, 0x30, 0x0F, 0x10, - 0x40, 0x13, 0x00, 0x00, 0x4B, 0x38, 0x2C, 0x80, 0x38, 0x0D, 0x00, 0x00, 0x4B, 0x44, 0x0F, 0x10, 0x40, 0x13, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x38, 0x0C, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x4E, 0xF8, 0x2C, 0x00, - 0x38, 0x03, 0x00, 0x00, 0x4F, 0x08, 0x0F, 0x80, 0x00, 0x0B, 0x00, 0x00, 0x4F, 0x0C, 0x2C, 0x80, 0x20, 0x03, - 0x00, 0x00, 0x4F, 0x1C, 0x0F, 0x80, 0x00, 0x0B, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, - 0x4D, 0x10, 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x70, 0x42, 0x94, 0x00, 0x00, 0x00, 0x00, 0x4D, 0xD4, - 0x41, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4D, 0xE0, 0x41, 0x90, 0x00, 0x00, 0x00, 0x00, 0x83, 0xAC, 0x2C, 0x00, - 0x00, 0x09, 0x00, 0x00, 0x83, 0xB8, 0x34, 0x8C, 0x80, 0x11, 0x00, 0x00, 0x84, 0x00, 0x34, 0x8C, 0x80, 0x11, - 0x00, 0x00, 0x84, 0x30, 0x05, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x84, 0x38, 0x04, 0x1A, 0x05, 0x00, 0x00, 0x00, - 0x84, 0x44, 0x05, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x84, 0xDC, 0x05, 0x78, 0x05, 0x78, 0x00, 0x00, 0x85, 0xB8, - 0x10, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x85, 0xC0, 0x03, 0xE8, 0x01, 0xF4, 0x00, 0x00, 0x85, 0xCC, 0x10, 0x00, - 0x01, 0x0B, 0x00, 0x00, 0x85, 0xD4, 0x03, 0x84, 0x03, 0xE8, 0x00, 0x00, 0x85, 0xE0, 0x10, 0x00, 0x01, 0x0B, - 0x00, 0x00, 0x88, 0x18, 0x0B, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x88, 0x2C, 0x0B, 0x00, 0x01, 0x0B, 0x00, 0x00, - 0x88, 0xF8, 0x04, 0x1A, 0x0B, 0xB8, 0x00, 0x00, 0x89, 0x3C, 0x04, 0x1A, 0x0B, 0xB8, 0x00, 0x00, 0x89, 0x80, - 0x04, 0x1A, 0x0B, 0xB8, 0x00, 0x00, 0x89, 0xE0, 0x04, 0xFE, 0xF7, 0x04, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, - 0x36, 0xCC, 0x42, 0xEC, 0x00, 0x00, 0x00, 0x00, 0x37, 0xC4, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x00, 0x00, 0x34, 0x68, 0x3F, 0x66, 0x66, 0x66, 0x00, 0x00, 0x39, 0xD8, 0x44, 0x0C, 0x00, 0x00, 0x00, 0x00, - 0x3A, 0x44, 0xB4, 0x99, 0x00, 0x11, 0x00, 0x00, 0x3A, 0x48, 0x1B, 0x8C, 0x00, 0x8F, 0x00, 0x00, 0x3A, 0x58, - 0xB4, 0x99, 0x00, 0x11, 0x00, 0x00, 0x3A, 0x5C, 0x1B, 0x8C, 0x00, 0x8F, 0x00, 0x00, 0x3A, 0x6C, 0xB4, 0x99, - 0x00, 0x11, 0x00, 0x00, 0x3A, 0x70, 0x1B, 0x8C, 0x00, 0x8F, 0x00, 0x00, 0x3B, 0x30, 0x44, 0x0C, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x45, 0xC8, 0x2C, 0x01, 0x50, 0x10, 0x00, 0x00, 0x45, 0xD4, 0x2D, 0x19, - 0x80, 0x13, 0x00, 0x00, 0x45, 0xDC, 0x2C, 0x80, 0xB0, 0x10, 0x00, 0x00, 0x45, 0xE8, 0x2D, 0x19, 0x80, 0x13, - 0x00, 0x00, 0x49, 0xC4, 0x2C, 0x00, 0x68, 0x0A, 0x00, 0x00, 0x49, 0xD0, 0x28, 0x1B, 0x80, 0x13, 0x00, 0x00, - 0x49, 0xD8, 0x2C, 0x80, 0x78, 0x0A, 0x00, 0x00, 0x49, 0xE4, 0x28, 0x1B, 0x80, 0x13, 0x00, 0x00, 0x49, 0xF0, - 0x2C, 0x00, 0x68, 0x08, 0x00, 0x00, 0x49, 0xFC, 0x23, 0x1B, 0x80, 0x13, 0x00, 0x00, 0x4A, 0x04, 0x2C, 0x80, - 0x78, 0x08, 0x00, 0x00, 0x4A, 0x10, 0x23, 0x1B, 0x80, 0x13, 0x00, 0x00, 0x5C, 0x98, 0x1E, 0x0C, 0x80, 0x80, - 0x00, 0x00, 0x5C, 0xF4, 0xB4, 0x80, 0x0C, 0x90, 0x00, 0x00, 0x5D, 0x08, 0xB4, 0x80, 0x0C, 0x90, 0x00, 0x00, - 0x00, 0xFF, 0x00, 0x00, 0x3A, 0x1C, 0xB4, 0x94, 0x00, 0x13, 0x00, 0x00, 0x3A, 0x64, 0x2C, 0x00, 0x00, 0x15, - 0x00, 0x00, 0x3A, 0x70, 0xB4, 0x92, 0x80, 0x13, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, - 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x64, 0x7C, 0xB4, 0x9A, 0x40, 0x17, 0x00, 0x00, 0x64, 0x80, - 0x64, 0x00, 0x10, 0x97, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x33, 0xE4, 0x42, 0xDE, - 0x00, 0x00, 0x00, 0x00, 0x45, 0x28, 0x2C, 0x01, 0x30, 0x11, 0x00, 0x00, 0x45, 0x34, 0xB4, 0x98, 0x80, 0x13, - 0x00, 0x00, 0x45, 0x3C, 0x2C, 0x81, 0x30, 0x11, 0x00, 0x00, 0x45, 0x48, 0xB4, 0x98, 0x80, 0x13, 0x00, 0x00, - 0x45, 0x50, 0x2D, 0x00, 0x20, 0x11, 0x00, 0x00, 0x45, 0x5C, 0xB4, 0x98, 0x80, 0x13, 0x00, 0x00, 0x45, 0xF8, - 0x2C, 0x01, 0x30, 0x0F, 0x00, 0x00, 0x46, 0x08, 0x0F, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x46, 0x0C, 0x2C, 0x81, - 0x28, 0x0F, 0x00, 0x00, 0x46, 0x1C, 0x0F, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x4A, 0xEC, 0x2C, 0x00, 0x70, 0x03, - 0x00, 0x00, 0x4B, 0x00, 0x2C, 0x80, 0x38, 0x03, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, - 0x48, 0x5C, 0x2C, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x37, 0xB0, - 0x3F, 0x59, 0x99, 0x9A, 0x00, 0x00, 0x37, 0xCC, 0x42, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x55, 0x20, 0x87, 0x11, - 0x80, 0x13, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x3B, 0x8C, 0x44, 0x0C, 0x00, 0x00, - 0x00, 0x00, 0x3D, 0x0C, 0x44, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, - 0x50, 0xE4, 0xB4, 0x99, 0x00, 0x13, 0x00, 0x00, 0x50, 0xF8, 0xB4, 0x99, 0x00, 0x13, 0x00, 0x00, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x4E, 0xB0, 0x02, 0xBC, 0xFF, 0x38, 0x00, 0x00, - 0x4E, 0xBC, 0x14, 0x00, 0x01, 0x23, 0x00, 0x00, 0x4E, 0xC4, 0x03, 0x84, 0x01, 0xF4, 0x00, 0x00, 0x4E, 0xD0, - 0x14, 0x00, 0x01, 0x23, 0x00, 0x00, 0x4E, 0xD8, 0x04, 0x4C, 0x04, 0xB0, 0x00, 0x00, 0x4E, 0xE4, 0x14, 0x00, - 0x01, 0x23, 0x00, 0x00, 0x50, 0x5C, 0x2C, 0x00, 0x68, 0x15, 0x00, 0x00, 0x50, 0x6C, 0x14, 0x08, 0x01, 0x23, - 0x00, 0x00, 0x50, 0x70, 0x2C, 0x80, 0x60, 0x15, 0x00, 0x00, 0x50, 0x80, 0x14, 0x08, 0x01, 0x23, 0x00, 0x00, - 0x50, 0x84, 0x2D, 0x00, 0x20, 0x15, 0x00, 0x00, 0x50, 0x94, 0x14, 0x08, 0x01, 0x23, 0x00, 0x00, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0xFF, 0xBA, 0x81, 0x00, 0x08, 0x80, 0x01, 0x00, 0xB4, 0x38, 0x21, 0x00, 0xB0, 0x7C, 0x08, - 0x03, 0xA6, 0x3C, 0x60, 0x80, 0x3C, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x2F, 0x9A, 0x3C, - 0x00, 0x00, 0x00, 0x08, // #Common/PAL/Handlers/PAL Stock Icons.asm - 0x88, 0x62, 0xF2, 0x34, 0x2C, 0x03, 0x00, 0x00, 0x41, 0x82, 0x00, 0x30, 0x48, 0x00, 0x00, 0x21, 0x7C, 0x88, - 0x02, 0xA6, 0x80, 0x64, 0x00, 0x00, 0x90, 0x7D, 0x00, 0x2C, 0x90, 0x7D, 0x00, 0x30, 0x80, 0x64, 0x00, 0x04, - 0x90, 0x7D, 0x00, 0x3C, 0x48, 0x00, 0x00, 0x10, 0x4E, 0x80, 0x00, 0x21, 0x3F, 0x59, 0x99, 0x9A, 0xC1, 0xA8, - 0x00, 0x00, 0x80, 0x1D, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x10, 0xFC, 0x44, 0x00, 0x00, 0x00, - 0x04, // #Common/PAL/Handlers/DK - // Up B/Aerial Up B.asm - 0x88, 0x82, 0xF2, 0x34, 0x2C, 0x04, 0x00, 0x00, 0x41, 0x82, 0x00, 0x10, 0x3C, 0x00, 0x80, 0x11, 0x60, 0x00, - 0x00, 0x74, 0x48, 0x00, 0x00, 0x08, 0x38, 0x03, 0xD7, 0x74, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x10, 0xFB, 0x64, - 0x00, 0x00, 0x00, 0x04, // #Common/PAL/Handlers/DK Up B/Grounded - // Up B.asm - 0x88, 0x82, 0xF2, 0x34, 0x2C, 0x04, 0x00, 0x00, 0x41, 0x82, 0x00, 0x10, 0x3C, 0x00, 0x80, 0x11, 0x60, 0x00, - 0x00, 0x74, 0x48, 0x00, 0x00, 0x08, 0x38, 0x03, 0xD7, 0x74, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 // Termination sequence + 0xC2, 0x0C, 0x9A, 0x44, 0x00, 0x00, 0x00, 0x2F, // #External/UCF + Arduino Toggle UI/UCF/UCF + // 0.74 Dashback - Check for Toggle.asm + 0xD0, 0x1F, 0x00, 0x2C, 0x88, 0x9F, 0x06, 0x18, 0x38, 0x62, 0xF2, 0x28, 0x7C, 0x63, 0x20, + 0xAE, 0x2C, 0x03, 0x00, 0x01, 0x41, 0x82, 0x00, 0x14, 0x38, 0x62, 0xF2, 0x2C, 0x7C, 0x63, + 0x20, 0xAE, 0x2C, 0x03, 0x00, 0x01, 0x40, 0x82, 0x01, 0x50, 0x7C, 0x08, 0x02, 0xA6, 0x90, + 0x01, 0x00, 0x04, 0x94, 0x21, 0xFF, 0x50, 0xBE, 0x81, 0x00, 0x08, 0x48, 0x00, 0x01, 0x21, + 0x7F, 0xC8, 0x02, 0xA6, 0xC0, 0x3F, 0x08, 0x94, 0xC0, 0x5E, 0x00, 0x00, 0xFC, 0x01, 0x10, + 0x40, 0x40, 0x82, 0x01, 0x18, 0x80, 0x8D, 0xAE, 0xB4, 0xC0, 0x3F, 0x06, 0x20, 0xFC, 0x20, + 0x0A, 0x10, 0xC0, 0x44, 0x00, 0x3C, 0xFC, 0x01, 0x10, 0x40, 0x41, 0x80, 0x01, 0x00, 0x88, + 0x7F, 0x06, 0x70, 0x2C, 0x03, 0x00, 0x02, 0x40, 0x80, 0x00, 0xF4, 0x88, 0x7F, 0x22, 0x1F, + 0x54, 0x60, 0x07, 0x39, 0x40, 0x82, 0x00, 0xE8, 0x3C, 0x60, 0x80, 0x4C, 0x60, 0x63, 0x1F, + 0x78, 0x8B, 0xA3, 0x00, 0x01, 0x38, 0x7D, 0xFF, 0xFE, 0x88, 0x9F, 0x06, 0x18, 0x48, 0x00, + 0x00, 0x8D, 0x7C, 0x7C, 0x1B, 0x78, 0x7F, 0xA3, 0xEB, 0x78, 0x88, 0x9F, 0x06, 0x18, 0x48, + 0x00, 0x00, 0x7D, 0x7C, 0x7C, 0x18, 0x50, 0x7C, 0x63, 0x19, 0xD6, 0x2C, 0x03, 0x15, 0xF9, + 0x40, 0x81, 0x00, 0xB0, 0x38, 0x00, 0x00, 0x01, 0x90, 0x1F, 0x23, 0x58, 0x90, 0x1F, 0x23, + 0x40, 0x80, 0x9F, 0x00, 0x04, 0x2C, 0x04, 0x00, 0x0A, 0x40, 0xA2, 0x00, 0x98, 0x88, 0x7F, + 0x00, 0x0C, 0x38, 0x80, 0x00, 0x01, 0x3D, 0x80, 0x80, 0x03, 0x61, 0x8C, 0x41, 0x8C, 0x7D, + 0x89, 0x03, 0xA6, 0x4E, 0x80, 0x04, 0x21, 0x2C, 0x03, 0x00, 0x00, 0x41, 0x82, 0x00, 0x78, + 0x80, 0x83, 0x00, 0x2C, 0x80, 0x84, 0x1E, 0xCC, 0xC0, 0x3F, 0x00, 0x2C, 0xD0, 0x24, 0x00, + 0x18, 0xC0, 0x5E, 0x00, 0x04, 0xFC, 0x01, 0x10, 0x40, 0x41, 0x81, 0x00, 0x0C, 0x38, 0x60, + 0x00, 0x80, 0x48, 0x00, 0x00, 0x08, 0x38, 0x60, 0x00, 0x7F, 0x98, 0x64, 0x00, 0x06, 0x48, + 0x00, 0x00, 0x48, 0x7C, 0x85, 0x23, 0x78, 0x38, 0x63, 0xFF, 0xFF, 0x2C, 0x03, 0x00, 0x00, + 0x40, 0x80, 0x00, 0x08, 0x38, 0x63, 0x00, 0x05, 0x3C, 0x80, 0x80, 0x46, 0x60, 0x84, 0xB1, + 0x08, 0x1C, 0x63, 0x00, 0x30, 0x7C, 0x84, 0x1A, 0x14, 0x1C, 0x65, 0x00, 0x0C, 0x7C, 0x84, + 0x1A, 0x14, 0x88, 0x64, 0x00, 0x02, 0x7C, 0x63, 0x07, 0x74, 0x4E, 0x80, 0x00, 0x20, 0x4E, + 0x80, 0x00, 0x21, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBA, 0x81, 0x00, 0x08, + 0x80, 0x01, 0x00, 0xB4, 0x38, 0x21, 0x00, 0xB0, 0x7C, 0x08, 0x03, 0xA6, 0x00, 0x00, 0x00, + 0x00, 0xC2, 0x09, 0x98, 0xA4, 0x00, 0x00, 0x00, + 0x2B, // #External/UCF + Arduino Toggle UI/UCF/UCF + // 0.74 Shield Drop - Check for Toggle.asm + 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x04, 0x94, 0x21, 0xFF, 0x50, 0xBE, 0x81, 0x00, + 0x08, 0x7C, 0x7E, 0x1B, 0x78, 0x83, 0xFE, 0x00, 0x2C, 0x48, 0x00, 0x01, 0x01, 0x7F, 0xA8, + 0x02, 0xA6, 0x88, 0x9F, 0x06, 0x18, 0x38, 0x62, 0xF2, 0x28, 0x7C, 0x63, 0x20, 0xAE, 0x2C, + 0x03, 0x00, 0x01, 0x41, 0x82, 0x00, 0x14, 0x38, 0x62, 0xF2, 0x30, 0x7C, 0x63, 0x20, 0xAE, + 0x2C, 0x03, 0x00, 0x01, 0x40, 0x82, 0x00, 0xF8, 0xC0, 0x3F, 0x06, 0x3C, 0x80, 0x6D, 0xAE, + 0xB4, 0xC0, 0x03, 0x03, 0x14, 0xFC, 0x01, 0x00, 0x40, 0x40, 0x81, 0x00, 0xE4, 0xC0, 0x3F, + 0x06, 0x20, 0x48, 0x00, 0x00, 0x71, 0xD0, 0x21, 0x00, 0x90, 0xC0, 0x3F, 0x06, 0x24, 0x48, + 0x00, 0x00, 0x65, 0xC0, 0x41, 0x00, 0x90, 0xEC, 0x42, 0x00, 0xB2, 0xEC, 0x21, 0x00, 0x72, + 0xEC, 0x21, 0x10, 0x2A, 0xC0, 0x5D, 0x00, 0x0C, 0xFC, 0x01, 0x10, 0x40, 0x41, 0x80, 0x00, + 0xB4, 0x88, 0x9F, 0x06, 0x70, 0x2C, 0x04, 0x00, 0x03, 0x40, 0x81, 0x00, 0xA8, 0xC0, 0x1D, + 0x00, 0x10, 0xC0, 0x3F, 0x06, 0x24, 0xFC, 0x00, 0x08, 0x40, 0x40, 0x80, 0x00, 0x98, 0xBA, + 0x81, 0x00, 0x08, 0x80, 0x01, 0x00, 0xB4, 0x38, 0x21, 0x00, 0xB0, 0x7C, 0x08, 0x03, 0xA6, + 0x80, 0x61, 0x00, 0x1C, 0x83, 0xE1, 0x00, 0x14, 0x38, 0x21, 0x00, 0x18, 0x38, 0x63, 0x00, + 0x08, 0x7C, 0x68, 0x03, 0xA6, 0x4E, 0x80, 0x00, 0x20, 0xFC, 0x00, 0x0A, 0x10, 0xC0, 0x3D, + 0x00, 0x00, 0xEC, 0x00, 0x00, 0x72, 0xC0, 0x3D, 0x00, 0x04, 0xEC, 0x00, 0x08, 0x28, 0xFC, + 0x00, 0x00, 0x1E, 0xD8, 0x01, 0x00, 0x80, 0x80, 0x61, 0x00, 0x84, 0x38, 0x63, 0x00, 0x02, + 0x3C, 0x00, 0x43, 0x30, 0xC8, 0x5D, 0x00, 0x14, 0x6C, 0x63, 0x80, 0x00, 0x90, 0x01, 0x00, + 0x80, 0x90, 0x61, 0x00, 0x84, 0xC8, 0x21, 0x00, 0x80, 0xEC, 0x01, 0x10, 0x28, 0xC0, 0x3D, + 0x00, 0x00, 0xEC, 0x20, 0x08, 0x24, 0x4E, 0x80, 0x00, 0x20, 0x4E, 0x80, 0x00, 0x21, 0x42, + 0xA0, 0x00, 0x00, 0x37, 0x27, 0x00, 0x00, 0x43, 0x30, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, + 0xBF, 0x4C, 0xCC, 0xCD, 0x43, 0x30, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x7F, 0xC3, 0xF3, + 0x78, 0x7F, 0xE4, 0xFB, 0x78, 0xBA, 0x81, 0x00, 0x08, 0x80, 0x01, 0x00, 0xB4, 0x38, 0x21, + 0x00, 0xB0, 0x7C, 0x08, 0x03, 0xA6, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC2, + 0x16, 0xE7, 0x50, 0x00, 0x00, 0x00, + 0x33, // #Common/StaticPatches/ToggledStaticOverwrites.asm + 0x88, 0x62, 0xF2, 0x34, 0x2C, 0x03, 0x00, 0x00, 0x41, 0x82, 0x00, 0x14, 0x48, 0x00, 0x00, + 0x75, 0x7C, 0x68, 0x02, 0xA6, 0x48, 0x00, 0x01, 0x3D, 0x48, 0x00, 0x00, 0x14, 0x48, 0x00, + 0x00, 0x95, 0x7C, 0x68, 0x02, 0xA6, 0x48, 0x00, 0x01, 0x2D, 0x48, 0x00, 0x00, 0x04, 0x88, + 0x62, 0xF2, 0x38, 0x2C, 0x03, 0x00, 0x00, 0x41, 0x82, 0x00, 0x14, 0x48, 0x00, 0x00, 0xB9, + 0x7C, 0x68, 0x02, 0xA6, 0x48, 0x00, 0x01, 0x11, 0x48, 0x00, 0x00, 0x10, 0x48, 0x00, 0x00, + 0xC9, 0x7C, 0x68, 0x02, 0xA6, 0x48, 0x00, 0x01, 0x01, 0x88, 0x62, 0xF2, 0x3C, 0x2C, 0x03, + 0x00, 0x00, 0x41, 0x82, 0x00, 0x14, 0x48, 0x00, 0x00, 0xD1, 0x7C, 0x68, 0x02, 0xA6, 0x48, + 0x00, 0x00, 0xE9, 0x48, 0x00, 0x01, 0x04, 0x48, 0x00, 0x00, 0xD1, 0x7C, 0x68, 0x02, 0xA6, + 0x48, 0x00, 0x00, 0xD9, 0x48, 0x00, 0x00, 0xF4, 0x4E, 0x80, 0x00, 0x21, 0x80, 0x3C, 0xE4, + 0xD4, 0x00, 0x24, 0x04, 0x64, 0x80, 0x07, 0x96, 0xE0, 0x60, 0x00, 0x00, 0x00, 0x80, 0x2B, + 0x7E, 0x54, 0x48, 0x00, 0x00, 0x88, 0x80, 0x2B, 0x80, 0x8C, 0x48, 0x00, 0x00, 0x84, 0x80, + 0x12, 0x39, 0xA8, 0x60, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x4E, 0x80, 0x00, 0x21, + 0x80, 0x3C, 0xE4, 0xD4, 0x00, 0x20, 0x00, 0x00, 0x80, 0x07, 0x96, 0xE0, 0x3A, 0x40, 0x00, + 0x01, 0x80, 0x2B, 0x7E, 0x54, 0x88, 0x7F, 0x22, 0x40, 0x80, 0x2B, 0x80, 0x8C, 0x2C, 0x03, + 0x00, 0x02, 0x80, 0x10, 0xFC, 0x48, 0x90, 0x05, 0x21, 0xDC, 0x80, 0x10, 0xFB, 0x68, 0x90, + 0x05, 0x21, 0xDC, 0x80, 0x12, 0x39, 0xA8, 0x90, 0x1F, 0x1A, 0x5C, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4E, 0x80, 0x00, 0x21, 0x80, 0x1D, 0x46, 0x10, 0x48, 0x00, 0x00, 0x4C, 0x80, 0x1D, 0x47, + 0x24, 0x48, 0x00, 0x00, 0x3C, 0x80, 0x1D, 0x46, 0x0C, 0x80, 0x9F, 0x00, 0xEC, 0xFF, 0xFF, + 0xFF, 0xFF, 0x4E, 0x80, 0x00, 0x21, 0x80, 0x1D, 0x46, 0x10, 0x38, 0x83, 0x7F, 0x9C, 0x80, + 0x1D, 0x47, 0x24, 0x88, 0x1B, 0x00, 0xC4, 0x80, 0x1D, 0x46, 0x0C, 0x3C, 0x60, 0x80, 0x3B, + 0xFF, 0xFF, 0xFF, 0xFF, 0x4E, 0x80, 0x00, 0x21, 0x80, 0x1D, 0x45, 0xFC, 0x48, 0x00, 0x09, + 0xDC, 0xFF, 0xFF, 0xFF, 0xFF, 0x4E, 0x80, 0x00, 0x21, 0x80, 0x1D, 0x45, 0xFC, 0x40, 0x80, + 0x09, 0xDC, 0xFF, 0xFF, 0xFF, 0xFF, 0x38, 0xA3, 0xFF, 0xFC, 0x84, 0x65, 0x00, 0x04, 0x2C, + 0x03, 0xFF, 0xFF, 0x41, 0x82, 0x00, 0x10, 0x84, 0x85, 0x00, 0x04, 0x90, 0x83, 0x00, 0x00, + 0x4B, 0xFF, 0xFF, 0xEC, 0x4E, 0x80, 0x00, 0x20, 0x3C, 0x60, 0x80, 0x00, 0x3C, 0x80, 0x00, + 0x3B, 0x60, 0x84, 0x72, 0x2C, 0x3D, 0x80, 0x80, 0x32, 0x61, 0x8C, 0x8F, 0x50, 0x7D, 0x89, + 0x03, 0xA6, 0x4E, 0x80, 0x04, 0x21, 0x3C, 0x60, 0x80, 0x17, 0x3C, 0x80, 0x80, 0x17, 0x00, + 0x00, 0x00, 0x00, 0xC2, 0x1D, 0x14, 0xC8, 0x00, 0x00, 0x00, + 0x04, // #Common/Preload Stadium + // Transformations/Handlers/Init + // isLoaded Bool.asm + 0x88, 0x62, 0xF2, 0x38, 0x2C, 0x03, 0x00, 0x00, 0x41, 0x82, 0x00, 0x0C, 0x38, 0x60, 0x00, + 0x00, 0x98, 0x7F, 0x00, 0xF0, 0x3B, 0xA0, 0x00, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC2, 0x1D, 0x45, 0xEC, 0x00, 0x00, 0x00, 0x1B, // #Common/Preload Stadium + // Transformations/Handlers/Load + // Transformation.asm + 0x88, 0x62, 0xF2, 0x38, 0x2C, 0x03, 0x00, 0x00, 0x41, 0x82, 0x00, 0xC4, 0x88, 0x7F, 0x00, + 0xF0, 0x2C, 0x03, 0x00, 0x00, 0x40, 0x82, 0x00, 0xB8, 0x38, 0x60, 0x00, 0x04, 0x3D, 0x80, + 0x80, 0x38, 0x61, 0x8C, 0x05, 0x80, 0x7D, 0x89, 0x03, 0xA6, 0x4E, 0x80, 0x04, 0x21, 0x54, + 0x60, 0x10, 0x3A, 0xA8, 0x7F, 0x00, 0xE2, 0x3C, 0x80, 0x80, 0x3B, 0x60, 0x84, 0x7F, 0x9C, + 0x7C, 0x84, 0x00, 0x2E, 0x7C, 0x03, 0x20, 0x00, 0x41, 0x82, 0xFF, 0xD4, 0x90, 0x9F, 0x00, + 0xEC, 0x2C, 0x04, 0x00, 0x03, 0x40, 0x82, 0x00, 0x0C, 0x38, 0x80, 0x00, 0x00, 0x48, 0x00, + 0x00, 0x34, 0x2C, 0x04, 0x00, 0x04, 0x40, 0x82, 0x00, 0x0C, 0x38, 0x80, 0x00, 0x01, 0x48, + 0x00, 0x00, 0x24, 0x2C, 0x04, 0x00, 0x09, 0x40, 0x82, 0x00, 0x0C, 0x38, 0x80, 0x00, 0x02, + 0x48, 0x00, 0x00, 0x14, 0x2C, 0x04, 0x00, 0x06, 0x40, 0x82, 0x00, 0x00, 0x38, 0x80, 0x00, + 0x03, 0x48, 0x00, 0x00, 0x04, 0x3C, 0x60, 0x80, 0x3E, 0x60, 0x63, 0x12, 0x48, 0x54, 0x80, + 0x10, 0x3A, 0x7C, 0x63, 0x02, 0x14, 0x80, 0x63, 0x03, 0xD8, 0x80, 0x9F, 0x00, 0xCC, 0x38, + 0xBF, 0x00, 0xC8, 0x3C, 0xC0, 0x80, 0x1D, 0x60, 0xC6, 0x42, 0x20, 0x38, 0xE0, 0x00, 0x00, + 0x3D, 0x80, 0x80, 0x01, 0x61, 0x8C, 0x65, 0x80, 0x7D, 0x89, 0x03, 0xA6, 0x4E, 0x80, 0x04, + 0x21, 0x38, 0x60, 0x00, 0x01, 0x98, 0x7F, 0x00, 0xF0, 0x80, 0x7F, 0x00, 0xD8, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x1D, 0x4F, 0x14, 0x00, 0x00, 0x00, + 0x04, // #Common/Preload + // Stadium + // Transformations/Handlers/Reset + // isLoaded.asm + 0x88, 0x62, 0xF2, 0x38, 0x2C, 0x03, 0x00, 0x00, 0x41, 0x82, 0x00, 0x0C, 0x38, 0x60, 0x00, + 0x00, 0x98, 0x7F, 0x00, 0xF0, 0x80, 0x6D, 0xB2, 0xD8, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC2, 0x06, 0x8F, 0x30, 0x00, 0x00, 0x00, 0x9D, // #Common/PAL/Handlers/Character + // DAT Patcher.asm + 0x88, 0x62, 0xF2, 0x34, 0x2C, 0x03, 0x00, 0x00, 0x41, 0x82, 0x04, 0xD4, 0x7C, 0x08, 0x02, + 0xA6, 0x90, 0x01, 0x00, 0x04, 0x94, 0x21, 0xFF, 0x50, 0xBE, 0x81, 0x00, 0x08, 0x83, 0xFE, + 0x01, 0x0C, 0x83, 0xFF, 0x00, 0x08, 0x3B, 0xFF, 0xFF, 0xE0, 0x80, 0x7D, 0x00, 0x00, 0x2C, + 0x03, 0x00, 0x1B, 0x40, 0x80, 0x04, 0x9C, 0x48, 0x00, 0x00, 0x71, 0x48, 0x00, 0x00, 0xA9, + 0x48, 0x00, 0x00, 0xB9, 0x48, 0x00, 0x01, 0x51, 0x48, 0x00, 0x01, 0x79, 0x48, 0x00, 0x01, + 0x79, 0x48, 0x00, 0x02, 0x29, 0x48, 0x00, 0x02, 0x39, 0x48, 0x00, 0x02, 0x81, 0x48, 0x00, + 0x02, 0xF9, 0x48, 0x00, 0x03, 0x11, 0x48, 0x00, 0x03, 0x11, 0x48, 0x00, 0x03, 0x11, 0x48, + 0x00, 0x03, 0x11, 0x48, 0x00, 0x03, 0x21, 0x48, 0x00, 0x03, 0x21, 0x48, 0x00, 0x03, 0x89, + 0x48, 0x00, 0x03, 0x89, 0x48, 0x00, 0x03, 0x91, 0x48, 0x00, 0x03, 0x91, 0x48, 0x00, 0x03, + 0xA9, 0x48, 0x00, 0x03, 0xA9, 0x48, 0x00, 0x03, 0xB9, 0x48, 0x00, 0x03, 0xB9, 0x48, 0x00, + 0x03, 0xC9, 0x48, 0x00, 0x03, 0xC9, 0x48, 0x00, 0x03, 0xC9, 0x48, 0x00, 0x04, 0x29, 0x7C, + 0x88, 0x02, 0xA6, 0x1C, 0x63, 0x00, 0x04, 0x7C, 0x84, 0x1A, 0x14, 0x80, 0xA4, 0x00, 0x00, + 0x54, 0xA5, 0x01, 0xBA, 0x7C, 0xA4, 0x2A, 0x14, 0x80, 0x65, 0x00, 0x00, 0x80, 0x85, 0x00, + 0x04, 0x2C, 0x03, 0x00, 0xFF, 0x41, 0x82, 0x00, 0x14, 0x7C, 0x63, 0xFA, 0x14, 0x90, 0x83, + 0x00, 0x00, 0x38, 0xA5, 0x00, 0x08, 0x4B, 0xFF, 0xFF, 0xE4, 0x48, 0x00, 0x03, 0xF0, 0x00, + 0x00, 0x33, 0x44, 0x3F, 0x54, 0x7A, 0xE1, 0x00, 0x00, 0x33, 0x60, 0x42, 0xC4, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x37, 0x9C, 0x42, 0x92, 0x00, 0x00, 0x00, 0x00, 0x39, + 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x0C, 0x40, 0x86, 0x66, 0x66, 0x00, 0x00, + 0x39, 0x10, 0x3D, 0xEA, 0x0E, 0xA1, 0x00, 0x00, 0x39, 0x28, 0x41, 0xA0, 0x00, 0x00, 0x00, + 0x00, 0x3C, 0x04, 0x2C, 0x01, 0x48, 0x0C, 0x00, 0x00, 0x47, 0x20, 0x1B, 0x96, 0x80, 0x13, + 0x00, 0x00, 0x47, 0x34, 0x1B, 0x96, 0x80, 0x13, 0x00, 0x00, 0x47, 0x3C, 0x04, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x4A, 0x40, 0x2C, 0x00, 0x68, 0x11, 0x00, 0x00, 0x4A, 0x4C, 0x28, 0x1B, + 0x00, 0x13, 0x00, 0x00, 0x4A, 0x50, 0x0D, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x4A, 0x54, 0x2C, + 0x80, 0x68, 0x11, 0x00, 0x00, 0x4A, 0x60, 0x28, 0x1B, 0x00, 0x13, 0x00, 0x00, 0x4A, 0x64, + 0x0D, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x4B, 0x24, 0x2C, 0x00, 0x68, 0x0D, 0x00, 0x00, 0x4B, + 0x30, 0x0F, 0x10, 0x40, 0x13, 0x00, 0x00, 0x4B, 0x38, 0x2C, 0x80, 0x38, 0x0D, 0x00, 0x00, + 0x4B, 0x44, 0x0F, 0x10, 0x40, 0x13, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x38, 0x0C, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x4E, 0xF8, 0x2C, 0x00, 0x38, 0x03, 0x00, 0x00, 0x4F, 0x08, + 0x0F, 0x80, 0x00, 0x0B, 0x00, 0x00, 0x4F, 0x0C, 0x2C, 0x80, 0x20, 0x03, 0x00, 0x00, 0x4F, + 0x1C, 0x0F, 0x80, 0x00, 0x0B, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x4D, 0x10, 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x70, 0x42, 0x94, 0x00, 0x00, 0x00, + 0x00, 0x4D, 0xD4, 0x41, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4D, 0xE0, 0x41, 0x90, 0x00, 0x00, + 0x00, 0x00, 0x83, 0xAC, 0x2C, 0x00, 0x00, 0x09, 0x00, 0x00, 0x83, 0xB8, 0x34, 0x8C, 0x80, + 0x11, 0x00, 0x00, 0x84, 0x00, 0x34, 0x8C, 0x80, 0x11, 0x00, 0x00, 0x84, 0x30, 0x05, 0x00, + 0x00, 0x8B, 0x00, 0x00, 0x84, 0x38, 0x04, 0x1A, 0x05, 0x00, 0x00, 0x00, 0x84, 0x44, 0x05, + 0x00, 0x00, 0x8B, 0x00, 0x00, 0x84, 0xDC, 0x05, 0x78, 0x05, 0x78, 0x00, 0x00, 0x85, 0xB8, + 0x10, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x85, 0xC0, 0x03, 0xE8, 0x01, 0xF4, 0x00, 0x00, 0x85, + 0xCC, 0x10, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x85, 0xD4, 0x03, 0x84, 0x03, 0xE8, 0x00, 0x00, + 0x85, 0xE0, 0x10, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x88, 0x18, 0x0B, 0x00, 0x01, 0x0B, 0x00, + 0x00, 0x88, 0x2C, 0x0B, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x88, 0xF8, 0x04, 0x1A, 0x0B, 0xB8, + 0x00, 0x00, 0x89, 0x3C, 0x04, 0x1A, 0x0B, 0xB8, 0x00, 0x00, 0x89, 0x80, 0x04, 0x1A, 0x0B, + 0xB8, 0x00, 0x00, 0x89, 0xE0, 0x04, 0xFE, 0xF7, 0x04, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x36, 0xCC, 0x42, 0xEC, 0x00, 0x00, 0x00, 0x00, 0x37, 0xC4, 0x0C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0x00, 0x34, 0x68, 0x3F, 0x66, 0x66, 0x66, 0x00, 0x00, 0x39, 0xD8, + 0x44, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x44, 0xB4, 0x99, 0x00, 0x11, 0x00, 0x00, 0x3A, + 0x48, 0x1B, 0x8C, 0x00, 0x8F, 0x00, 0x00, 0x3A, 0x58, 0xB4, 0x99, 0x00, 0x11, 0x00, 0x00, + 0x3A, 0x5C, 0x1B, 0x8C, 0x00, 0x8F, 0x00, 0x00, 0x3A, 0x6C, 0xB4, 0x99, 0x00, 0x11, 0x00, + 0x00, 0x3A, 0x70, 0x1B, 0x8C, 0x00, 0x8F, 0x00, 0x00, 0x3B, 0x30, 0x44, 0x0C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x45, 0xC8, 0x2C, 0x01, 0x50, 0x10, 0x00, 0x00, 0x45, + 0xD4, 0x2D, 0x19, 0x80, 0x13, 0x00, 0x00, 0x45, 0xDC, 0x2C, 0x80, 0xB0, 0x10, 0x00, 0x00, + 0x45, 0xE8, 0x2D, 0x19, 0x80, 0x13, 0x00, 0x00, 0x49, 0xC4, 0x2C, 0x00, 0x68, 0x0A, 0x00, + 0x00, 0x49, 0xD0, 0x28, 0x1B, 0x80, 0x13, 0x00, 0x00, 0x49, 0xD8, 0x2C, 0x80, 0x78, 0x0A, + 0x00, 0x00, 0x49, 0xE4, 0x28, 0x1B, 0x80, 0x13, 0x00, 0x00, 0x49, 0xF0, 0x2C, 0x00, 0x68, + 0x08, 0x00, 0x00, 0x49, 0xFC, 0x23, 0x1B, 0x80, 0x13, 0x00, 0x00, 0x4A, 0x04, 0x2C, 0x80, + 0x78, 0x08, 0x00, 0x00, 0x4A, 0x10, 0x23, 0x1B, 0x80, 0x13, 0x00, 0x00, 0x5C, 0x98, 0x1E, + 0x0C, 0x80, 0x80, 0x00, 0x00, 0x5C, 0xF4, 0xB4, 0x80, 0x0C, 0x90, 0x00, 0x00, 0x5D, 0x08, + 0xB4, 0x80, 0x0C, 0x90, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x3A, 0x1C, 0xB4, 0x94, 0x00, + 0x13, 0x00, 0x00, 0x3A, 0x64, 0x2C, 0x00, 0x00, 0x15, 0x00, 0x00, 0x3A, 0x70, 0xB4, 0x92, + 0x80, 0x13, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0x00, 0x64, 0x7C, 0xB4, 0x9A, 0x40, 0x17, 0x00, 0x00, 0x64, 0x80, + 0x64, 0x00, 0x10, 0x97, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x33, + 0xE4, 0x42, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x45, 0x28, 0x2C, 0x01, 0x30, 0x11, 0x00, 0x00, + 0x45, 0x34, 0xB4, 0x98, 0x80, 0x13, 0x00, 0x00, 0x45, 0x3C, 0x2C, 0x81, 0x30, 0x11, 0x00, + 0x00, 0x45, 0x48, 0xB4, 0x98, 0x80, 0x13, 0x00, 0x00, 0x45, 0x50, 0x2D, 0x00, 0x20, 0x11, + 0x00, 0x00, 0x45, 0x5C, 0xB4, 0x98, 0x80, 0x13, 0x00, 0x00, 0x45, 0xF8, 0x2C, 0x01, 0x30, + 0x0F, 0x00, 0x00, 0x46, 0x08, 0x0F, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x46, 0x0C, 0x2C, 0x81, + 0x28, 0x0F, 0x00, 0x00, 0x46, 0x1C, 0x0F, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x4A, 0xEC, 0x2C, + 0x00, 0x70, 0x03, 0x00, 0x00, 0x4B, 0x00, 0x2C, 0x80, 0x38, 0x03, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x48, 0x5C, 0x2C, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x37, 0xB0, 0x3F, 0x59, 0x99, 0x9A, 0x00, 0x00, + 0x37, 0xCC, 0x42, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x55, 0x20, 0x87, 0x11, 0x80, 0x13, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x3B, 0x8C, 0x44, 0x0C, 0x00, 0x00, + 0x00, 0x00, 0x3D, 0x0C, 0x44, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x50, 0xE4, 0xB4, 0x99, 0x00, 0x13, 0x00, 0x00, 0x50, 0xF8, 0xB4, 0x99, + 0x00, 0x13, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0x4E, 0xB0, 0x02, 0xBC, 0xFF, 0x38, 0x00, 0x00, 0x4E, 0xBC, 0x14, 0x00, 0x01, 0x23, + 0x00, 0x00, 0x4E, 0xC4, 0x03, 0x84, 0x01, 0xF4, 0x00, 0x00, 0x4E, 0xD0, 0x14, 0x00, 0x01, + 0x23, 0x00, 0x00, 0x4E, 0xD8, 0x04, 0x4C, 0x04, 0xB0, 0x00, 0x00, 0x4E, 0xE4, 0x14, 0x00, + 0x01, 0x23, 0x00, 0x00, 0x50, 0x5C, 0x2C, 0x00, 0x68, 0x15, 0x00, 0x00, 0x50, 0x6C, 0x14, + 0x08, 0x01, 0x23, 0x00, 0x00, 0x50, 0x70, 0x2C, 0x80, 0x60, 0x15, 0x00, 0x00, 0x50, 0x80, + 0x14, 0x08, 0x01, 0x23, 0x00, 0x00, 0x50, 0x84, 0x2D, 0x00, 0x20, 0x15, 0x00, 0x00, 0x50, + 0x94, 0x14, 0x08, 0x01, 0x23, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xBA, 0x81, + 0x00, 0x08, 0x80, 0x01, 0x00, 0xB4, 0x38, 0x21, 0x00, 0xB0, 0x7C, 0x08, 0x03, 0xA6, 0x3C, + 0x60, 0x80, 0x3C, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x2F, 0x9A, 0x3C, + 0x00, 0x00, 0x00, 0x08, // #Common/PAL/Handlers/PAL Stock Icons.asm + 0x88, 0x62, 0xF2, 0x34, 0x2C, 0x03, 0x00, 0x00, 0x41, 0x82, 0x00, 0x30, 0x48, 0x00, 0x00, + 0x21, 0x7C, 0x88, 0x02, 0xA6, 0x80, 0x64, 0x00, 0x00, 0x90, 0x7D, 0x00, 0x2C, 0x90, 0x7D, + 0x00, 0x30, 0x80, 0x64, 0x00, 0x04, 0x90, 0x7D, 0x00, 0x3C, 0x48, 0x00, 0x00, 0x10, 0x4E, + 0x80, 0x00, 0x21, 0x3F, 0x59, 0x99, 0x9A, 0xC1, 0xA8, 0x00, 0x00, 0x80, 0x1D, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x00, 0xC2, 0x10, 0xFC, 0x44, 0x00, 0x00, 0x00, + 0x04, // #Common/PAL/Handlers/DK + // Up B/Aerial Up B.asm + 0x88, 0x82, 0xF2, 0x34, 0x2C, 0x04, 0x00, 0x00, 0x41, 0x82, 0x00, 0x10, 0x3C, 0x00, 0x80, + 0x11, 0x60, 0x00, 0x00, 0x74, 0x48, 0x00, 0x00, 0x08, 0x38, 0x03, 0xD7, 0x74, 0x00, 0x00, + 0x00, 0x00, 0xC2, 0x10, 0xFB, 0x64, 0x00, 0x00, 0x00, 0x04, // #Common/PAL/Handlers/DK Up + // B/Grounded Up B.asm + 0x88, 0x82, 0xF2, 0x34, 0x2C, 0x04, 0x00, 0x00, 0x41, 0x82, 0x00, 0x10, 0x3C, 0x00, 0x80, + 0x11, 0x60, 0x00, 0x00, 0x74, 0x48, 0x00, 0x00, 0x08, 0x38, 0x03, 0xD7, 0x74, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Termination sequence }; static std::unordered_map staticBlacklist = { - {0x8008d698, true}, // Recording/GetLCancelStatus/GetLCancelStatus.asm - {0x8006c324, true}, // Recording/GetLCancelStatus/ResetLCancelStatus.asm - {0x800679bc, true}, // Recording/ExtendPlayerBlock.asm - {0x802fef88, true}, // Recording/FlushFrameBuffer.asm - {0x80005604, true}, // Recording/IsVSMode.asm - {0x8016d30c, true}, // Recording/SendGameEnd.asm - {0x8016e74c, true}, // Recording/SendGameInfo.asm - {0x8006c5d8, true}, // Recording/SendGamePostFrame.asm - {0x8006b0dc, true}, // Recording/SendGamePreFrame.asm - {0x803219ec, true}, // 3.4.0: Recording/FlushFrameBuffer.asm (Have to keep old ones for backward compatibility) - {0x8006da34, true}, // 3.4.0: Recording/SendGamePostFrame.asm - {0x8016d884, true}, // 3.7.0: Recording/SendGameEnd.asm + {0x8008d698, true}, // Recording/GetLCancelStatus/GetLCancelStatus.asm + {0x8006c324, true}, // Recording/GetLCancelStatus/ResetLCancelStatus.asm + {0x800679bc, true}, // Recording/ExtendPlayerBlock.asm + {0x802fef88, true}, // Recording/FlushFrameBuffer.asm + {0x80005604, true}, // Recording/IsVSMode.asm + {0x8016d30c, true}, // Recording/SendGameEnd.asm + {0x8016e74c, true}, // Recording/SendGameInfo.asm + {0x8006c5d8, true}, // Recording/SendGamePostFrame.asm + {0x8006b0dc, true}, // Recording/SendGamePreFrame.asm + {0x803219ec, true}, // 3.4.0: Recording/FlushFrameBuffer.asm (Have to keep old ones for + // backward compatibility) + {0x8006da34, true}, // 3.4.0: Recording/SendGamePostFrame.asm + {0x8016d884, true}, // 3.7.0: Recording/SendGameEnd.asm - {0x8021aae4, true}, // Binary/FasterMeleeSettings/DisableFdTransitions.bin - {0x801cbb90, true}, // Binary/FasterMeleeSettings/LaglessFod.bin - {0x801CC8AC, true}, // Binary/FasterMeleeSettings/LaglessFod.bin - {0x801CBE9C, true}, // Binary/FasterMeleeSettings/LaglessFod.bin - {0x801CBEF0, true}, // Binary/FasterMeleeSettings/LaglessFod.bin - {0x801CBF54, true}, // Binary/FasterMeleeSettings/LaglessFod.bin - {0x80390838, true}, // Binary/FasterMeleeSettings/LaglessFod.bin - {0x801CD250, true}, // Binary/FasterMeleeSettings/LaglessFod.bin - {0x801CCDCC, true}, // Binary/FasterMeleeSettings/LaglessFod.bin - {0x801C26B0, true}, // Binary/FasterMeleeSettings/RandomStageMusic.bin - {0x803761ec, true}, // Binary/NormalLagReduction.bin - {0x800198a4, true}, // Binary/PerformanceLagReduction.bin - {0x80019620, true}, // Binary/PerformanceLagReduction.bin - {0x801A5054, true}, // Binary/PerformanceLagReduction.bin - {0x80397878, true}, // Binary/OsReportPrintOnCrash.bin - {0x801A4DA0, true}, // Binary/LagReduction/PD.bin - {0x801A4DB4, true}, // Binary/LagReduction/PD.bin - {0x80019860, true}, // Binary/LagReduction/PD.bin - {0x801A4C24, true}, // Binary/LagReduction/PD+VB.bin - {0x8001985C, true}, // Binary/LagReduction/PD+VB.bin - {0x80019860, true}, // Binary/LagReduction/PD+VB.bin - {0x80376200, true}, // Binary/LagReduction/PD+VB.bin - {0x801A5018, true}, // Binary/LagReduction/PD+VB.bin - {0x80218D68, true}, // Binary/LagReduction/PD+VB.bin + {0x8021aae4, true}, // Binary/FasterMeleeSettings/DisableFdTransitions.bin + {0x801cbb90, true}, // Binary/FasterMeleeSettings/LaglessFod.bin + {0x801CC8AC, true}, // Binary/FasterMeleeSettings/LaglessFod.bin + {0x801CBE9C, true}, // Binary/FasterMeleeSettings/LaglessFod.bin + {0x801CBEF0, true}, // Binary/FasterMeleeSettings/LaglessFod.bin + {0x801CBF54, true}, // Binary/FasterMeleeSettings/LaglessFod.bin + {0x80390838, true}, // Binary/FasterMeleeSettings/LaglessFod.bin + {0x801CD250, true}, // Binary/FasterMeleeSettings/LaglessFod.bin + {0x801CCDCC, true}, // Binary/FasterMeleeSettings/LaglessFod.bin + {0x801C26B0, true}, // Binary/FasterMeleeSettings/RandomStageMusic.bin + {0x803761ec, true}, // Binary/NormalLagReduction.bin + {0x800198a4, true}, // Binary/PerformanceLagReduction.bin + {0x80019620, true}, // Binary/PerformanceLagReduction.bin + {0x801A5054, true}, // Binary/PerformanceLagReduction.bin + {0x80397878, true}, // Binary/OsReportPrintOnCrash.bin + {0x801A4DA0, true}, // Binary/LagReduction/PD.bin + {0x801A4DB4, true}, // Binary/LagReduction/PD.bin + {0x80019860, true}, // Binary/LagReduction/PD.bin + {0x801A4C24, true}, // Binary/LagReduction/PD+VB.bin + {0x8001985C, true}, // Binary/LagReduction/PD+VB.bin + {0x80019860, true}, // Binary/LagReduction/PD+VB.bin + {0x80376200, true}, // Binary/LagReduction/PD+VB.bin + {0x801A5018, true}, // Binary/LagReduction/PD+VB.bin + {0x80218D68, true}, // Binary/LagReduction/PD+VB.bin - {0x800055f0, true}, // Common/EXITransferBuffer.asm - {0x800055f8, true}, // Common/GetIsFollower.asm - {0x800055fc, true}, // Common/Gecko/ProcessCodeList.asm - {0x8016d294, true}, // Common/IncrementFrameIndex.asm + {0x800055f0, true}, // Common/EXITransferBuffer.asm + {0x800055f8, true}, // Common/GetIsFollower.asm + {0x800055fc, true}, // Common/Gecko/ProcessCodeList.asm + {0x8016d294, true}, // Common/IncrementFrameIndex.asm - {0x801a5b14, true}, // External/Salty Runback/Salty Runback.asm - {0x801a4570, true}, // External/LagReduction/ForceHD/480pDeflickerOff.asm - {0x802fccd8, true}, // External/Hide Nametag When Invisible/Hide Nametag When Invisible.asm + {0x801a5b14, true}, // External/Salty Runback/Salty Runback.asm + {0x801a4570, true}, // External/LagReduction/ForceHD/480pDeflickerOff.asm + {0x802fccd8, true}, // External/Hide Nametag When Invisible/Hide Nametag When Invisible.asm - {0x804ddb30, - true}, // External/Widescreen/Adjust Offscreen Scissor/Fix Bubble Positions/Adjust Corner Value 1.asm - {0x804ddb34, - true}, // External/Widescreen/Adjust Offscreen Scissor/Fix Bubble Positions/Adjust Corner Value 2.asm - {0x804ddb2c, true}, // External/Widescreen/Adjust Offscreen Scissor/Fix Bubble Positions/Extend Negative - // Vertical Bound.asm - {0x804ddb28, true}, // External/Widescreen/Adjust Offscreen Scissor/Fix Bubble Positions/Extend Positive - // Vertical Bound.asm - {0x804ddb4c, true}, // External/Widescreen/Adjust Offscreen Scissor/Fix Bubble Positions/Widen Bubble Region.asm - {0x804ddb58, true}, // External/Widescreen/Adjust Offscreen Scissor/Adjust Bubble Zoom.asm - {0x80086b24, true}, // External/Widescreen/Adjust Offscreen Scissor/Draw High Poly Models.asm - {0x80030C7C, true}, // External/Widescreen/Adjust Offscreen Scissor/Left Camera Bound.asm - {0x80030C88, true}, // External/Widescreen/Adjust Offscreen Scissor/Right Camera Bound.asm - {0x802fcfc4, true}, // External/Widescreen/Nametag Fixes/Adjust Nametag Background X Scale.asm - {0x804ddb84, true}, // External/Widescreen/Nametag Fixes/Adjust Nametag Text X Scale.asm - {0x803BB05C, true}, // External/Widescreen/Fix Screen Flash.asm - {0x8036A4A8, true}, // External/Widescreen/Overwrite CObj Values.asm - {0x800C0148, true}, // External/FlashRedFailedLCancel/ChangeColor.asm - {0x8008D690, true}, // External/FlashRedFailedLCancel/TriggerColor.asm + {0x804ddb30, true}, // External/Widescreen/Adjust Offscreen Scissor/Fix Bubble + // Positions/Adjust Corner Value 1.asm + {0x804ddb34, true}, // External/Widescreen/Adjust Offscreen Scissor/Fix Bubble + // Positions/Adjust Corner Value 2.asm + {0x804ddb2c, true}, // External/Widescreen/Adjust Offscreen Scissor/Fix Bubble + // Positions/Extend Negative Vertical Bound.asm + {0x804ddb28, true}, // External/Widescreen/Adjust Offscreen Scissor/Fix Bubble + // Positions/Extend Positive Vertical Bound.asm + {0x804ddb4c, true}, // External/Widescreen/Adjust Offscreen Scissor/Fix Bubble + // Positions/Widen Bubble Region.asm + {0x804ddb58, true}, // External/Widescreen/Adjust Offscreen Scissor/Adjust Bubble Zoom.asm + {0x80086b24, true}, // External/Widescreen/Adjust Offscreen Scissor/Draw High Poly Models.asm + {0x80030C7C, true}, // External/Widescreen/Adjust Offscreen Scissor/Left Camera Bound.asm + {0x80030C88, true}, // External/Widescreen/Adjust Offscreen Scissor/Right Camera Bound.asm + {0x802fcfc4, + true}, // External/Widescreen/Nametag Fixes/Adjust Nametag Background X Scale.asm + {0x804ddb84, true}, // External/Widescreen/Nametag Fixes/Adjust Nametag Text X Scale.asm + {0x803BB05C, true}, // External/Widescreen/Fix Screen Flash.asm + {0x8036A4A8, true}, // External/Widescreen/Overwrite CObj Values.asm + {0x800C0148, true}, // External/FlashRedFailedLCancel/ChangeColor.asm + {0x8008D690, true}, // External/FlashRedFailedLCancel/TriggerColor.asm - {0x8006A880, true}, // Online/Core/BrawlOffscreenDamage.asm - {0x801A4DB4, true}, // Online/Core/ForceEngineOnRollback.asm - {0x8016D310, true}, // Online/Core/HandleLRAS.asm - {0x8034DED8, true}, // Online/Core/HandleRumble.asm - {0x8016E748, true}, // Online/Core/InitOnlinePlay.asm - {0x8016e904, true}, // Online/Core/InitPause.asm - {0x801a5014, true}, // Online/Core/LoopEngineForRollback.asm - {0x801a4de4, true}, // Online/Core/StartEngineLoop.asm - {0x80376A28, true}, // Online/Core/TriggerSendInput.asm - {0x801a4cb4, true}, // Online/Core/EXIFileLoad/AllocBuffer.asm - {0x800163fc, true}, // Online/Core/EXIFileLoad/GetFileSize.asm - {0x800166b8, true}, // Online/Core/EXIFileLoad/TransferFile.asm - {0x80019260, true}, // Online/Core/Hacks/ForceNoDiskCrash.asm - {0x80376304, true}, // Online/Core/Hacks/ForceNoVideoAssert.asm - {0x80321d70, true}, // Online/Core/Hacks/PreventCharacterCrowdChants.asm - {0x80019608, true}, // Online/Core/Hacks/PreventPadAlarmDuringRollback.asm - {0x8038D224, true}, // Online/Core/Sound/AssignSoundInstanceId.asm - {0x80088224, true}, // Online/Core/Sound/NoDestroyVoice.asm - {0x800882B0, true}, // Online/Core/Sound/NoDestroyVoice2.asm - {0x8038D0B0, true}, // Online/Core/Sound/PreventDuplicateSounds.asm - {0x803775b8, true}, // Online/Logging/LogInputOnCopy.asm - {0x8016e9b4, true}, // Online/Menus/InGame/InitInGame.asm - {0x80185050, true}, // Online/Menus/VSScreen/HideStageDisplay/PreventEarlyR3Overwrite.asm - {0x80184b1c, true}, // Online/Menus/VSScreen/HideStageText/SkipStageNumberShow.asm - {0x801A45BC, true}, // Online/Slippi Online Scene/main.asm - {0x801BFA20, true}, // Online/Slippi Online Scene/boot.asm + {0x8006A880, true}, // Online/Core/BrawlOffscreenDamage.asm + {0x801A4DB4, true}, // Online/Core/ForceEngineOnRollback.asm + {0x8016D310, true}, // Online/Core/HandleLRAS.asm + {0x8034DED8, true}, // Online/Core/HandleRumble.asm + {0x8016E748, true}, // Online/Core/InitOnlinePlay.asm + {0x8016e904, true}, // Online/Core/InitPause.asm + {0x801a5014, true}, // Online/Core/LoopEngineForRollback.asm + {0x801a4de4, true}, // Online/Core/StartEngineLoop.asm + {0x80376A28, true}, // Online/Core/TriggerSendInput.asm + {0x801a4cb4, true}, // Online/Core/EXIFileLoad/AllocBuffer.asm + {0x800163fc, true}, // Online/Core/EXIFileLoad/GetFileSize.asm + {0x800166b8, true}, // Online/Core/EXIFileLoad/TransferFile.asm + {0x80019260, true}, // Online/Core/Hacks/ForceNoDiskCrash.asm + {0x80376304, true}, // Online/Core/Hacks/ForceNoVideoAssert.asm + {0x80321d70, true}, // Online/Core/Hacks/PreventCharacterCrowdChants.asm + {0x80019608, true}, // Online/Core/Hacks/PreventPadAlarmDuringRollback.asm + {0x8038D224, true}, // Online/Core/Sound/AssignSoundInstanceId.asm + {0x80088224, true}, // Online/Core/Sound/NoDestroyVoice.asm + {0x800882B0, true}, // Online/Core/Sound/NoDestroyVoice2.asm + {0x8038D0B0, true}, // Online/Core/Sound/PreventDuplicateSounds.asm + {0x803775b8, true}, // Online/Logging/LogInputOnCopy.asm + {0x8016e9b4, true}, // Online/Menus/InGame/InitInGame.asm + {0x80185050, true}, // Online/Menus/VSScreen/HideStageDisplay/PreventEarlyR3Overwrite.asm + {0x80184b1c, true}, // Online/Menus/VSScreen/HideStageText/SkipStageNumberShow.asm + {0x801A45BC, true}, // Online/Slippi Online Scene/main.asm + {0x801BFA20, true}, // Online/Slippi Online Scene/boot.asm }; std::unordered_map blacklist; @@ -1020,8 +1074,8 @@ void CEXISlippi::prepareGeckoList() // Some codes should only be blacklisted when not displaying rollbacks, these are codes // that are required for things to not break when using Slippi savestates. Perhaps this // should be handled by actually applying these codes in the playback ASM instead? not sure - blacklist[0x8038add0] = true; // Online/Core/PreventFileAlarms/PreventMusicAlarm.asm - blacklist[0x80023FFC] = true; // Online/Core/PreventFileAlarms/MuteMusic.asm + blacklist[0x8038add0] = true; // Online/Core/PreventFileAlarms/PreventMusicAlarm.asm + blacklist[0x80023FFC] = true; // Online/Core/PreventFileAlarms/MuteMusic.asm } geckoList.clear(); @@ -1040,16 +1094,18 @@ void CEXISlippi::prepareGeckoList() while (idx < source.size()) { u8 codeType = source[idx] & 0xFE; - u32 address = source[idx] << 24 | source[idx + 1] << 16 | source[idx + 2] << 8 | source[idx + 3]; + u32 address = + source[idx] << 24 | source[idx + 1] << 16 | source[idx + 2] << 8 | source[idx + 3]; address = (address & 0x01FFFFFF) | 0x80000000; - u32 codeOffset = 8; // Default code offset. Most codes are this length + u32 codeOffset = 8; // Default code offset. Most codes are this length switch (codeType) { case 0xC0: case 0xC2: { - u32 lineCount = source[idx + 4] << 24 | source[idx + 5] << 16 | source[idx + 6] << 8 | source[idx + 7]; + u32 lineCount = + source[idx + 4] << 24 | source[idx + 5] << 16 | source[idx + 6] << 8 | source[idx + 7]; codeOffset = 8 + (lineCount * 8); break; } @@ -1058,8 +1114,10 @@ void CEXISlippi::prepareGeckoList() break; case 0x06: { - u32 byteLen = source[idx + 4] << 24 | source[idx + 5] << 16 | source[idx + 6] << 8 | source[idx + 7]; - codeOffset = 8 + ((byteLen + 7) & 0xFFFFFFF8); // Round up to next 8 bytes and add the first 8 bytes + u32 byteLen = + source[idx + 4] << 24 | source[idx + 5] << 16 | source[idx + 6] << 8 | source[idx + 7]; + codeOffset = + 8 + ((byteLen + 7) & 0xFFFFFFF8); // Round up to next 8 bytes and add the first 8 bytes break; } } @@ -1070,15 +1128,15 @@ void CEXISlippi::prepareGeckoList() if (blacklist.count(address)) continue; - INFO_LOG(SLIPPI, "Codetype [%x] Inserting section: %d - %d (%x, %d)", codeType, idx - codeOffset, idx, address, - codeOffset); + INFO_LOG(SLIPPI, "Codetype [%x] Inserting section: %d - %d (%x, %d)", codeType, + idx - codeOffset, idx, address, codeOffset); // If not blacklisted, add code to return vector geckoList.insert(geckoList.end(), source.begin() + (idx - codeOffset), source.begin() + idx); } // Add the termination sequence - geckoList.insert(geckoList.end(), { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); + geckoList.insert(geckoList.end(), {0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); } void CEXISlippi::prepareCharacterFrameData(Slippi::FrameData* frame, u8 port, u8 isFollower) @@ -1100,27 +1158,27 @@ void CEXISlippi::prepareCharacterFrameData(Slippi::FrameData* frame, u8 port, u8 // Get data for this player Slippi::PlayerFrameData data = source[port]; - // log << frameIndex << "\t" << port << "\t" << data.locationX << "\t" << data.locationY << "\t" << - // data.animation + // log << frameIndex << "\t" << port << "\t" << data.locationX << "\t" << data.locationY << "\t" + // << data.animation // << "\n"; - // WARN_LOG(EXPANSIONINTERFACE, "[Frame %d] [Player %d] Positions: %f | %f", frameIndex, port, data.locationX, - // data.locationY); + // WARN_LOG(EXPANSIONINTERFACE, "[Frame %d] [Player %d] Positions: %f | %f", frameIndex, port, + // data.locationX, data.locationY); // Add all of the inputs in order appendWordToBuffer(&m_read_queue, data.randomSeed); - appendWordToBuffer(&m_read_queue, *(u32*)& data.joystickX); - appendWordToBuffer(&m_read_queue, *(u32*)& data.joystickY); - appendWordToBuffer(&m_read_queue, *(u32*)& data.cstickX); - appendWordToBuffer(&m_read_queue, *(u32*)& data.cstickY); - appendWordToBuffer(&m_read_queue, *(u32*)& data.trigger); + appendWordToBuffer(&m_read_queue, *(u32*)&data.joystickX); + appendWordToBuffer(&m_read_queue, *(u32*)&data.joystickY); + appendWordToBuffer(&m_read_queue, *(u32*)&data.cstickX); + appendWordToBuffer(&m_read_queue, *(u32*)&data.cstickY); + appendWordToBuffer(&m_read_queue, *(u32*)&data.trigger); appendWordToBuffer(&m_read_queue, data.buttons); - appendWordToBuffer(&m_read_queue, *(u32*)& data.locationX); - appendWordToBuffer(&m_read_queue, *(u32*)& data.locationY); - appendWordToBuffer(&m_read_queue, *(u32*)& data.facingDirection); + appendWordToBuffer(&m_read_queue, *(u32*)&data.locationX); + appendWordToBuffer(&m_read_queue, *(u32*)&data.locationY); + appendWordToBuffer(&m_read_queue, *(u32*)&data.facingDirection); appendWordToBuffer(&m_read_queue, (u32)data.animation); m_read_queue.push_back(data.joystickXRaw); - appendWordToBuffer(&m_read_queue, *(u32*)& data.percent); + appendWordToBuffer(&m_read_queue, *(u32*)&data.percent); // NOTE TO DEV: If you add data here, make sure to increase the size above } @@ -1249,13 +1307,14 @@ void CEXISlippi::prepareFrameData(u8* payload) if (requestResultCode == FRAME_RESP_TERMINATE) { - ERROR_LOG(EXPANSIONINTERFACE, "Game should terminate on frame %d [%X]", frameIndex, frameIndex); + ERROR_LOG(EXPANSIONINTERFACE, "Game should terminate on frame %d [%X]", frameIndex, + frameIndex); } return; } - u8 rollbackCode = 0; // 0 = not rollback, 1 = rollback, perhaps other options in the future? + u8 rollbackCode = 0; // 0 = not rollback, 1 = rollback, perhaps other options in the future? // Increment frame index if greater if (frameIndex > g_playbackStatus->currentPlaybackFrame) @@ -1271,7 +1330,7 @@ void CEXISlippi::prepareFrameData(u8* payload) if (shouldFFW) { WARN_LOG(SLIPPI, "[Frame %d] FFW frame, behind by: %d frames.", frameIndex, - g_playbackStatus->lastFrame - frameIndex); + g_playbackStatus->lastFrame - frameIndex); g_playbackStatus->lastFFWFrame = frameIndex; } @@ -1312,7 +1371,7 @@ void CEXISlippi::prepareFrameData(u8* payload) // Add frame rng seed to be restored at priority 0 u8 rngResult = frame->randomSeedExists ? 1 : 0; m_read_queue.push_back(rngResult); - appendWordToBuffer(&m_read_queue, *(u32*)& frame->randomSeed); + appendWordToBuffer(&m_read_queue, *(u32*)&frame->randomSeed); // Add frame data for every character for (u8 port = 0; port < 4; port++) @@ -1429,8 +1488,10 @@ void CEXISlippi::handleOnlineInputs(u8* payload) bool CEXISlippi::shouldSkipOnlineFrame(s32 frame) { auto status = slippi_netplay->GetSlippiConnectStatus(); - bool connectionFailed = status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_FAILED; - bool connectionDisconnected = status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_DISCONNECTED; + bool connectionFailed = + status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_FAILED; + bool connectionDisconnected = + status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_DISCONNECTED; if (connectionFailed || connectionDisconnected) { // If connection failed just continue the game @@ -1454,8 +1515,9 @@ bool CEXISlippi::shouldSkipOnlineFrame(s32 frame) isConnectionStalled = true; } - WARN_LOG(SLIPPI_ONLINE, "Halting for one frame due to rollback limit (frame: %d | latest: %d)...", frame, - latestRemoteFrame); + WARN_LOG(SLIPPI_ONLINE, + "Halting for one frame due to rollback limit (frame: %d | latest: %d)...", frame, + latestRemoteFrame); return true; } @@ -1463,10 +1525,10 @@ bool CEXISlippi::shouldSkipOnlineFrame(s32 frame) // Return true if we are over 60% of a frame ahead of our opponent. Currently limiting how // often this happens because I'm worried about jittery data causing a lot of unneccesary delays. - // Only skip once for a given frame because our time detection method doesn't take into consideration - // waiting for a frame. Also it's less jarring and it happens often enough that it will smoothly - // get to the right place - auto isTimeSyncFrame = frame % SLIPPI_ONLINE_LOCKSTEP_INTERVAL; // Only time sync every 30 frames + // Only skip once for a given frame because our time detection method doesn't take into + // consideration waiting for a frame. Also it's less jarring and it happens often enough that it + // will smoothly get to the right place + auto isTimeSyncFrame = frame % SLIPPI_ONLINE_LOCKSTEP_INTERVAL; // Only time sync every 30 frames if (isTimeSyncFrame == 0 && !isCurrentlySkipping) { auto offsetUs = slippi_netplay->CalcTimeOffsetUs(); @@ -1476,12 +1538,13 @@ bool CEXISlippi::shouldSkipOnlineFrame(s32 frame) { isCurrentlySkipping = true; - int maxSkipFrames = frame <= 120 ? 5 : 1; // On early frames, support skipping more frames + int maxSkipFrames = frame <= 120 ? 5 : 1; // On early frames, support skipping more frames framesToSkip = ((offsetUs - 10000) / 16683) + 1; - framesToSkip = framesToSkip > maxSkipFrames ? maxSkipFrames : framesToSkip; // Only skip 5 frames max + framesToSkip = + framesToSkip > maxSkipFrames ? maxSkipFrames : framesToSkip; // Only skip 5 frames max - WARN_LOG(SLIPPI_ONLINE, "Halting on frame %d due to time sync. Offset: %d us. Frames: %d...", frame, - offsetUs, framesToSkip); + WARN_LOG(SLIPPI_ONLINE, "Halting on frame %d due to time sync. Offset: %d us. Frames: %d...", + frame, offsetUs, framesToSkip); } } @@ -1516,15 +1579,16 @@ void CEXISlippi::prepareOpponentInputs(u8* payload) { m_read_queue.clear(); - u8 frameResult = 1; // Indicates to continue frame + u8 frameResult = 1; // Indicates to continue frame auto state = slippi_netplay->GetSlippiConnectStatus(); - if (state != SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED || isConnectionStalled) + if (state != SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED || + isConnectionStalled) { - frameResult = 3; // Indicates we have disconnected + frameResult = 3; // Indicates we have disconnected } - m_read_queue.push_back(frameResult); // Indicate a continue frame + m_read_queue.push_back(frameResult); // Indicate a continue frame int32_t frame = payload[0] << 24 | payload[1] << 16 | payload[2] << 8 | payload[3]; @@ -1536,7 +1600,7 @@ void CEXISlippi::prepareOpponentInputs(u8* payload) // add latest frame we are transfering to begining of return buf int32_t latestFrame = offset > 0 ? frame : result->latestFrame; - appendWordToBuffer(&m_read_queue, *(u32*)& latestFrame); + appendWordToBuffer(&m_read_queue, *(u32*)&latestFrame); // copy pad data over auto txStart = result->data.begin() + offset; @@ -1548,8 +1612,9 @@ void CEXISlippi::prepareOpponentInputs(u8* payload) m_read_queue.insert(m_read_queue.end(), tx.begin(), tx.end()); - // ERROR_LOG(SLIPPI_ONLINE, "EXI: [%d] %X %X %X %X %X %X %X %X", latestFrame, m_read_queue[5], m_read_queue[6], - // m_read_queue[7], m_read_queue[8], m_read_queue[9], m_read_queue[10], m_read_queue[11], m_read_queue[12]); + // ERROR_LOG(SLIPPI_ONLINE, "EXI: [%d] %X %X %X %X %X %X %X %X", latestFrame, m_read_queue[5], + // m_read_queue[6], m_read_queue[7], m_read_queue[8], m_read_queue[9], m_read_queue[10], + // m_read_queue[11], m_read_queue[12]); } void CEXISlippi::handleCaptureSavestate(u8* payload) @@ -1585,7 +1650,7 @@ void CEXISlippi::handleCaptureSavestate(u8* payload) u32 timeDiff = (u32)(Common::Timer::GetTimeUs() - startTime); INFO_LOG(SLIPPI_ONLINE, "SLIPPI ONLINE: Captured savestate for frame %d in: %f ms", frame, - ((double)timeDiff) / 1000); + ((double)timeDiff) / 1000); } void CEXISlippi::handleLoadSavestate(u8* payload) @@ -1609,7 +1674,8 @@ void CEXISlippi::handleLoadSavestate(u8* payload) int idx = 0; while (Common::swap32(preserveArr[idx]) != 0) { - SlippiSavestate::PreserveBlock p = { Common::swap32(preserveArr[idx]), Common::swap32(preserveArr[idx + 1]) }; + SlippiSavestate::PreserveBlock p = {Common::swap32(preserveArr[idx]), + Common::swap32(preserveArr[idx + 1])}; blocks.push_back(p); idx += 2; } @@ -1626,7 +1692,8 @@ void CEXISlippi::handleLoadSavestate(u8* payload) activeSavestates.clear(); u32 timeDiff = (u32)(Common::Timer::GetTimeUs() - startTime); - INFO_LOG(SLIPPI_ONLINE, "SLIPPI ONLINE: Loaded savestate for frame %d in: %f ms", frame, ((double)timeDiff) / 1000); + INFO_LOG(SLIPPI_ONLINE, "SLIPPI ONLINE: Loaded savestate for frame %d in: %f ms", frame, + ((double)timeDiff) / 1000); } void CEXISlippi::startFindMatch(u8* payload) @@ -1645,9 +1712,9 @@ void CEXISlippi::startFindMatch(u8* payload) // Store this search so we know what was queued for lastSearch = search; - // While we do have another condition that checks characters after being connected, it's nice to give - // someone an early error before they even queue so that they wont enter the queue and make someone - // else get force removed from queue and have to requeue + // While we do have another condition that checks characters after being connected, it's nice to + // give someone an early error before they even queue so that they wont enter the queue and make + // someone else get force removed from queue and have to requeue auto directMode = SlippiMatchmaking::OnlinePlayMode::DIRECT; if (search.mode != directMode && localSelections.characterId >= 26) { @@ -1672,34 +1739,38 @@ void CEXISlippi::startFindMatch(u8* payload) void CEXISlippi::prepareOnlineMatchState() { - // This match block is a VS match with P1 Red Falco vs P2 Red Bowser on Battlefield. The proper values will - // be overwritten + // This match block is a VS match with P1 Red Falco vs P2 Red Bowser on Battlefield. The proper + // values will be overwritten static std::vector onlineMatchBlock = { - 0x32, 0x01, 0x86, 0x4C, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x6E, 0x00, 0x1F, 0x00, 0x00, - 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, - 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, - 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x05, 0x00, 0x04, 0x01, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, - 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, - 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x1A, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, - 0x40, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, - 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x1A, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, - 0x40, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, - 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x21, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, - 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, - 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x21, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, - 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, - 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, + 0x32, 0x01, 0x86, 0x4C, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x6E, 0x00, + 0x1F, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x3F, + 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x00, 0x78, 0x00, 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x05, 0x00, 0x04, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, + 0x80, 0x00, 0x00, 0x1A, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, + 0x40, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, + 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x1A, 0x03, 0x04, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, 0x40, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, + 0x21, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, 0x40, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, + 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x21, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, + 0x00, 0x78, 0x00, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, }; m_read_queue.clear(); auto errorState = SlippiMatchmaking::ProcessState::ERROR_ENCOUNTERED; - SlippiMatchmaking::ProcessState mmState = !forcedError.empty() ? errorState : matchmaking->GetMatchmakeState(); + SlippiMatchmaking::ProcessState mmState = + !forcedError.empty() ? errorState : matchmaking->GetMatchmakeState(); #ifdef LOCAL_TESTING if (localSelections.isCharacterSelected || isLocalConnected) @@ -1709,7 +1780,7 @@ void CEXISlippi::prepareOnlineMatchState() } #endif - m_read_queue.push_back(mmState); // Matchmaking State + m_read_queue.push_back(mmState); // Matchmaking State u8 localPlayerReady = localSelections.isCharacterSelected; u8 remotePlayerReady = 0; @@ -1735,7 +1806,8 @@ void CEXISlippi::prepareOnlineMatchState() bool isConnected = true; #else auto status = slippi_netplay->GetSlippiConnectStatus(); - bool isConnected = status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED; + bool isConnected = + status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED; #endif if (isConnected) @@ -1768,10 +1840,10 @@ void CEXISlippi::prepareOnlineMatchState() slippi_netplay = nullptr; } - m_read_queue.push_back(localPlayerReady); // Local player ready - m_read_queue.push_back(remotePlayerReady); // Remote player ready - m_read_queue.push_back(localPlayerIndex); // Local player index - m_read_queue.push_back(remotePlayerIndex); // Remote player index + m_read_queue.push_back(localPlayerReady); // Local player ready + m_read_queue.push_back(remotePlayerReady); // Remote player ready + m_read_queue.push_back(localPlayerIndex); // Local player index + m_read_queue.push_back(remotePlayerIndex); // Remote player index u32 rngOffset = 0; std::string p1Name = ""; @@ -1815,8 +1887,8 @@ void CEXISlippi::prepareOnlineMatchState() onlineMatchBlock[0x63 + remotePlayerIndex * 0x24] = rps.characterColor; // Make one character lighter if same character, same color - bool isSheikVsZelda = - lps.characterId == 0x12 && rps.characterId == 0x13 || lps.characterId == 0x13 && rps.characterId == 0x12; + bool isSheikVsZelda = lps.characterId == 0x12 && rps.characterId == 0x13 || + lps.characterId == 0x13 && rps.characterId == 0x12; bool charMatch = lps.characterId == rps.characterId || isSheikVsZelda; bool colMatch = lps.characterColor == rps.characterColor; @@ -1837,20 +1909,21 @@ void CEXISlippi::prepareOnlineMatchState() // u32 *timer = (u32 *)&onlineMatchBlock[0x10]; //*timer = Common::swap32(seconds * 60); - u16* stage = (u16*)& onlineMatchBlock[0xE]; + u16* stage = (u16*)&onlineMatchBlock[0xE]; *stage = Common::swap16(stageId); // Set rng offset rngOffset = isDecider ? lps.rngOffset : rps.rngOffset; WARN_LOG(SLIPPI_ONLINE, "Rng Offset: 0x%x", rngOffset); - WARN_LOG(SLIPPI_ONLINE, "P1 Char: 0x%X, P2 Char: 0x%X", onlineMatchBlock[0x60], onlineMatchBlock[0x84]); + WARN_LOG(SLIPPI_ONLINE, "P1 Char: 0x%X, P2 Char: 0x%X", onlineMatchBlock[0x60], + onlineMatchBlock[0x84]); // Set player names p1Name = isDecider ? lps.playerName : rps.playerName; p2Name = isDecider ? rps.playerName : lps.playerName; // Turn pause on in direct, off in everything else - u8* gameBitField3 = (u8*)& onlineMatchBlock[2]; + u8* gameBitField3 = (u8*)&onlineMatchBlock[2]; *gameBitField3 = lastSearch.mode == directMode ? *gameBitField3 & 0xF7 : *gameBitField3 | 0x8; } @@ -1882,12 +1955,12 @@ u16 CEXISlippi::getRandomStage() static u16 selectedStage; static std::vector stages = { - 0x2, // FoD - 0x3, // Pokemon - 0x8, // Yoshi's Story - 0x1C, // Dream Land - 0x1F, // Battlefield - 0x20, // Final Destination + 0x2, // FoD + 0x3, // Pokemon + 0x8, // Yoshi's Story + 0x1C, // Dream Land + 0x1F, // Battlefield + 0x20, // Final Destination }; // Reset stage pool if it's empty @@ -1953,7 +2026,7 @@ void CEXISlippi::prepareFileLength(u8* payload) { m_read_queue.clear(); - std::string fileName((char*)& payload[0]); + std::string fileName((char*)&payload[0]); std::string contents; u32 size = gameFileLoader->LoadFile(fileName, contents); @@ -1968,7 +2041,7 @@ void CEXISlippi::prepareFileLoad(u8* payload) { m_read_queue.clear(); - std::string fileName((char*)& payload[0]); + std::string fileName((char*)&payload[0]); std::string contents; u32 size = gameFileLoader->LoadFile(fileName, contents); @@ -1985,12 +2058,12 @@ void CEXISlippi::logMessageFromGame(u8* payload) if (payload[0] == 0) { // The first byte indicates whether to log the time or not - GENERIC_LOG(Common::Log::SLIPPI, (Common::Log::LOG_LEVELS)payload[1], "%s", (char*)& payload[2]); + GENERIC_LOG(Common::Log::SLIPPI, (Common::Log::LOG_LEVELS)payload[1], "%s", (char*)&payload[2]); } else { - GENERIC_LOG(Common::Log::SLIPPI, (Common::Log::LOG_LEVELS)payload[1], "%s: %llu", (char*)& payload[2], - Common::Timer::GetTimeUs()); + GENERIC_LOG(Common::Log::SLIPPI, (Common::Log::LOG_LEVELS)payload[1], "%s: %llu", + (char*)&payload[2], Common::Timer::GetTimeUs()); } } @@ -2015,8 +2088,8 @@ void CEXISlippi::handleLogOutRequest() void CEXISlippi::handleUpdateAppRequest() { #ifdef __APPLE__ - CriticalAlertT( - "Automatic updates are not available for macOS, please get the latest update from slippi.gg/netplay."); + CriticalAlertT("Automatic updates are not available for macOS, please get the latest update from " + "slippi.gg/netplay."); #else Host_LowerWindow(); user->UpdateApp(); @@ -2049,14 +2122,15 @@ void CEXISlippi::prepareOnlineStatus() // Write connect code (10 bytes) std::string connectCode = userInfo.connectCode; - char shiftJisHashtag[] = { '\x81', '\x94', '\x00' }; + char shiftJisHashtag[] = {'\x81', '\x94', '\x00'}; connectCode.resize(CONNECT_CODE_LENGTH); connectCode = ReplaceAll(connectCode, "#", shiftJisHashtag); auto codeBuf = connectCode.c_str(); m_read_queue.insert(m_read_queue.end(), codeBuf, codeBuf + CONNECT_CODE_LENGTH + 2); } -void doConnectionCleanup(std::unique_ptr mm, std::unique_ptr nc) +void doConnectionCleanup(std::unique_ptr mm, + std::unique_ptr nc) { if (mm) mm.reset(); @@ -2112,16 +2186,17 @@ void CEXISlippi::DMAWrite(u32 _uAddr, u32 _uSize) u8 byte = memPtr[0]; if (byte == CMD_RECEIVE_COMMANDS) { - time(&gameStartTime); // Store game start time + time(&gameStartTime); // Store game start time u8 receiveCommandsLen = memPtr[1]; configureCommands(&memPtr[1], receiveCommandsLen); writeToFileAsync(&memPtr[0], receiveCommandsLen + 1, "create"); bufLoc += receiveCommandsLen + 1; } - INFO_LOG(EXPANSIONINTERFACE, "EXI SLIPPI DMAWrite: addr: 0x%08x size: %d, bufLoc:[%02x %02x %02x %02x %02x]", - _uAddr, _uSize, memPtr[bufLoc], memPtr[bufLoc + 1], memPtr[bufLoc + 2], memPtr[bufLoc + 3], - memPtr[bufLoc + 4]); + INFO_LOG(EXPANSIONINTERFACE, + "EXI SLIPPI DMAWrite: addr: 0x%08x size: %d, bufLoc:[%02x %02x %02x %02x %02x]", _uAddr, + _uSize, memPtr[bufLoc], memPtr[bufLoc + 1], memPtr[bufLoc + 2], memPtr[bufLoc + 3], + memPtr[bufLoc + 4]); while (bufLoc < _uSize) { @@ -2215,11 +2290,12 @@ void CEXISlippi::DMARead(u32 addr, u32 size) return; } - m_read_queue.resize(size, 0); // Resize response array to make sure it's all full/allocated + m_read_queue.resize(size, 0); // Resize response array to make sure it's all full/allocated auto queueAddr = &m_read_queue[0]; - INFO_LOG(EXPANSIONINTERFACE, "EXI SLIPPI DMARead: addr: 0x%08x size: %d, startResp: [%02x %02x %02x %02x %02x]", - addr, size, queueAddr[0], queueAddr[1], queueAddr[2], queueAddr[3], queueAddr[4]); + INFO_LOG(EXPANSIONINTERFACE, + "EXI SLIPPI DMARead: addr: 0x%08x size: %d, startResp: [%02x %02x %02x %02x %02x]", addr, + size, queueAddr[0], queueAddr[1], queueAddr[2], queueAddr[3], queueAddr[4]); // Copy buffer data to memory Memory::CopyToEmu(addr, queueAddr, size); @@ -2230,5 +2306,7 @@ bool CEXISlippi::IsPresent() const return true; } -void CEXISlippi::TransferByte(u8& byte) {} +void CEXISlippi::TransferByte(u8& byte) +{ } +} // namespace ExpansionInterface diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h index b0681e6bc8..4f5ad9493d 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h @@ -7,9 +7,8 @@ #include #include "Common/CommonTypes.h" -#include "Common/FileUtil.h" #include "Common/File.h" -#include "EXI_Device.h" +#include "Common/FileUtil.h" #include "Core/Slippi/SlippiGameFileLoader.h" #include "Core/Slippi/SlippiMatchmaking.h" #include "Core/Slippi/SlippiNetplay.h" @@ -17,6 +16,7 @@ #include "Core/Slippi/SlippiReplayComm.h" #include "Core/Slippi/SlippiSavestate.h" #include "Core/Slippi/SlippiUser.h" +#include "EXI_Device.h" #define ROLLBACK_MAX_FRAMES 7 #define MAX_NAME_LENGTH 15 @@ -24,65 +24,65 @@ namespace ExpansionInterface { - // Emulated Slippi device used to receive and respond to in-game messages - class CEXISlippi : public IEXIDevice +// Emulated Slippi device used to receive and respond to in-game messages +class CEXISlippi : public IEXIDevice +{ +public: + CEXISlippi(); + virtual ~CEXISlippi(); + + void DMAWrite(u32 _uAddr, u32 _uSize) override; + void DMARead(u32 addr, u32 size) override; + + bool IsPresent() const override; + +private: + enum { - public: - CEXISlippi(); - virtual ~CEXISlippi(); + CMD_UNKNOWN = 0x0, - void DMAWrite(u32 _uAddr, u32 _uSize) override; - void DMARead(u32 addr, u32 size) override; + // Recording + CMD_RECEIVE_COMMANDS = 0x35, + CMD_RECEIVE_GAME_INFO = 0x36, + CMD_RECEIVE_POST_FRAME_UPDATE = 0x38, + CMD_RECEIVE_GAME_END = 0x39, - bool IsPresent() const override; + // Playback + CMD_PREPARE_REPLAY = 0x75, + CMD_READ_FRAME = 0x76, + CMD_GET_LOCATION = 0x77, + CMD_IS_FILE_READY = 0x88, + CMD_IS_STOCK_STEAL = 0x89, + CMD_GET_GECKO_CODES = 0x8A, - private: - enum - { - CMD_UNKNOWN = 0x0, + // Online + CMD_ONLINE_INPUTS = 0xB0, + CMD_CAPTURE_SAVESTATE = 0xB1, + CMD_LOAD_SAVESTATE = 0xB2, + CMD_GET_MATCH_STATE = 0xB3, + CMD_FIND_OPPONENT = 0xB4, + CMD_SET_MATCH_SELECTIONS = 0xB5, + CMD_OPEN_LOGIN = 0xB6, + CMD_LOGOUT = 0xB7, + CMD_UPDATE = 0xB8, + CMD_GET_ONLINE_STATUS = 0xB9, + CMD_CLEANUP_CONNECTION = 0xBA, - // Recording - CMD_RECEIVE_COMMANDS = 0x35, - CMD_RECEIVE_GAME_INFO = 0x36, - CMD_RECEIVE_POST_FRAME_UPDATE = 0x38, - CMD_RECEIVE_GAME_END = 0x39, + // Misc + CMD_LOG_MESSAGE = 0xD0, + CMD_FILE_LENGTH = 0xD1, + CMD_FILE_LOAD = 0xD2, + }; - // Playback - CMD_PREPARE_REPLAY = 0x75, - CMD_READ_FRAME = 0x76, - CMD_GET_LOCATION = 0x77, - CMD_IS_FILE_READY = 0x88, - CMD_IS_STOCK_STEAL = 0x89, - CMD_GET_GECKO_CODES = 0x8A, + enum + { + FRAME_RESP_WAIT = 0, + FRAME_RESP_CONTINUE = 1, + FRAME_RESP_TERMINATE = 2, + FRAME_RESP_FASTFORWARD = 3, + }; - // Online - CMD_ONLINE_INPUTS = 0xB0, - CMD_CAPTURE_SAVESTATE = 0xB1, - CMD_LOAD_SAVESTATE = 0xB2, - CMD_GET_MATCH_STATE = 0xB3, - CMD_FIND_OPPONENT = 0xB4, - CMD_SET_MATCH_SELECTIONS = 0xB5, - CMD_OPEN_LOGIN = 0xB6, - CMD_LOGOUT = 0xB7, - CMD_UPDATE = 0xB8, - CMD_GET_ONLINE_STATUS = 0xB9, - CMD_CLEANUP_CONNECTION = 0xBA, - - // Misc - CMD_LOG_MESSAGE = 0xD0, - CMD_FILE_LENGTH = 0xD1, - CMD_FILE_LOAD = 0xD2, - }; - - enum - { - FRAME_RESP_WAIT = 0, - FRAME_RESP_CONTINUE = 1, - FRAME_RESP_TERMINATE = 2, - FRAME_RESP_FASTFORWARD = 3, - }; - - std::unordered_map payloadSizes = { + std::unordered_map payloadSizes = { // The actual size of this command will be sent in one byte // after the command is received. The other receive command IDs // and sizes will be received immediately following @@ -111,115 +111,115 @@ namespace ExpansionInterface {CMD_CLEANUP_CONNECTION, 0}, // Misc - {CMD_LOG_MESSAGE, 0xFFFF}, // Variable size... will only work if by itself + {CMD_LOG_MESSAGE, 0xFFFF}, // Variable size... will only work if by itself {CMD_FILE_LENGTH, 0x40}, {CMD_FILE_LOAD, 0x40}, - }; - - struct WriteMessage - { - std::vector data; - std::string operation; - }; - - // .slp File creation stuff - u32 writtenByteCount = 0; - - // vars for metadata generation - time_t gameStartTime; - s32 lastFrame; - std::unordered_map> characterUsage; - - void updateMetadataFields(u8* payload, u32 length); - void configureCommands(u8* payload, u8 length); - void writeToFileAsync(u8* payload, u32 length, std::string fileOption); - void writeToFile(std::unique_ptr msg); - std::vector generateMetadata(); - void createNewFile(); - void closeFile(); - std::string generateFileName(); - bool checkFrameFullyFetched(s32 frameIndex); - bool shouldFFWFrame(s32 frameIndex); - - // std::ofstream log; - - File::IOFile m_file; - std::vector m_payload; - - // online play stuff - u16 getRandomStage(); - void handleOnlineInputs(u8* payload); - void prepareOpponentInputs(u8* payload); - void handleSendInputs(u8* payload); - void handleCaptureSavestate(u8* payload); - void handleLoadSavestate(u8* payload); - void startFindMatch(u8* payload); - void prepareOnlineMatchState(); - void setMatchSelections(u8* payload); - bool shouldSkipOnlineFrame(s32 frame); - void handleLogInRequest(); - void handleLogOutRequest(); - void handleUpdateAppRequest(); - void prepareOnlineStatus(); - void handleConnectionCleanup(); - - // replay playback stuff - void prepareGameInfo(u8* payload); - void prepareGeckoList(); - void prepareCharacterFrameData(Slippi::FrameData* frame, u8 port, u8 isFollower); - void prepareFrameData(u8* payload); - void prepareIsStockSteal(u8* payload); - void prepareIsFileReady(); - - // misc stuff - void logMessageFromGame(u8* payload); - void prepareFileLength(u8* payload); - void prepareFileLoad(u8* payload); - - void FileWriteThread(void); - - std::queue> fileWriteQueue; - bool writeThreadRunning = false; - std::thread m_fileWriteThread; - - std::unordered_map getNetplayNames(); - - std::vector playbackSavestatePayload; - std::vector geckoList; - - u32 stallFrameCount = 0; - bool isConnectionStalled = false; - - std::vector m_read_queue; - std::unique_ptr m_current_game = nullptr; - SlippiMatchmaking::MatchSearchSettings lastSearch; - - std::vector stagePool; - - u32 frameSeqIdx = 0; - - bool isEnetInitialized = false; - - std::default_random_engine generator; - - std::string forcedError = ""; - - // Frame skipping variables - int framesToSkip = 0; - bool isCurrentlySkipping = false; - - protected: - void TransferByte(u8& byte) override; - - private: - SlippiPlayerSelections localSelections; - - std::unique_ptr user; - std::unique_ptr gameFileLoader; - std::unique_ptr slippi_netplay; - std::unique_ptr matchmaking; - - std::map> activeSavestates; - std::deque> availableSavestates; }; -} + + struct WriteMessage + { + std::vector data; + std::string operation; + }; + + // .slp File creation stuff + u32 writtenByteCount = 0; + + // vars for metadata generation + time_t gameStartTime; + s32 lastFrame; + std::unordered_map> characterUsage; + + void updateMetadataFields(u8* payload, u32 length); + void configureCommands(u8* payload, u8 length); + void writeToFileAsync(u8* payload, u32 length, std::string fileOption); + void writeToFile(std::unique_ptr msg); + std::vector generateMetadata(); + void createNewFile(); + void closeFile(); + std::string generateFileName(); + bool checkFrameFullyFetched(s32 frameIndex); + bool shouldFFWFrame(s32 frameIndex); + + // std::ofstream log; + + File::IOFile m_file; + std::vector m_payload; + + // online play stuff + u16 getRandomStage(); + void handleOnlineInputs(u8* payload); + void prepareOpponentInputs(u8* payload); + void handleSendInputs(u8* payload); + void handleCaptureSavestate(u8* payload); + void handleLoadSavestate(u8* payload); + void startFindMatch(u8* payload); + void prepareOnlineMatchState(); + void setMatchSelections(u8* payload); + bool shouldSkipOnlineFrame(s32 frame); + void handleLogInRequest(); + void handleLogOutRequest(); + void handleUpdateAppRequest(); + void prepareOnlineStatus(); + void handleConnectionCleanup(); + + // replay playback stuff + void prepareGameInfo(u8* payload); + void prepareGeckoList(); + void prepareCharacterFrameData(Slippi::FrameData* frame, u8 port, u8 isFollower); + void prepareFrameData(u8* payload); + void prepareIsStockSteal(u8* payload); + void prepareIsFileReady(); + + // misc stuff + void logMessageFromGame(u8* payload); + void prepareFileLength(u8* payload); + void prepareFileLoad(u8* payload); + + void FileWriteThread(void); + + std::queue> fileWriteQueue; + bool writeThreadRunning = false; + std::thread m_fileWriteThread; + + std::unordered_map getNetplayNames(); + + std::vector playbackSavestatePayload; + std::vector geckoList; + + u32 stallFrameCount = 0; + bool isConnectionStalled = false; + + std::vector m_read_queue; + std::unique_ptr m_current_game = nullptr; + SlippiMatchmaking::MatchSearchSettings lastSearch; + + std::vector stagePool; + + u32 frameSeqIdx = 0; + + bool isEnetInitialized = false; + + std::default_random_engine generator; + + std::string forcedError = ""; + + // Frame skipping variables + int framesToSkip = 0; + bool isCurrentlySkipping = false; + +protected: + void TransferByte(u8& byte) override; + +private: + SlippiPlayerSelections localSelections; + + std::unique_ptr user; + std::unique_ptr gameFileLoader; + std::unique_ptr slippi_netplay; + std::unique_ptr matchmaking; + + std::map> activeSavestates; + std::deque> availableSavestates; +}; +} // namespace ExpansionInterface diff --git a/Source/Core/Core/Slippi/SlippiGameFileLoader.cpp b/Source/Core/Core/Slippi/SlippiGameFileLoader.cpp index e1ce3fc849..0e4bdd936a 100644 --- a/Source/Core/Core/Slippi/SlippiGameFileLoader.cpp +++ b/Source/Core/Core/Slippi/SlippiGameFileLoader.cpp @@ -1,18 +1,18 @@ #include "SlippiGameFileLoader.h" -#include "Common/Logging/Log.h" -#include "Common/FileUtil.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/Core.h" #include "Core/ConfigManager.h" +#include "Core/Core.h" +#include "Core/HW/DVD/DVDThread.h" std::string getFilePath(std::string fileName) { std::string dirPath = File::GetSysDirectory(); - std::string filePath = dirPath + "GameFiles/GALE01/" + fileName; // TODO: Handle other games? + std::string filePath = dirPath + "GameFiles/GALE01/" + fileName; // TODO: Handle other games? if (File::Exists(filePath)) { @@ -49,8 +49,10 @@ u32 SlippiGameFileLoader::LoadFile(std::string fileName, std::string& data) std::string 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 (gameFilePath.substr(gameFilePath.length() - 5) == ".diff" && Core::GetState() == Core::State::Running) + // If the file was a diff file and the game is running, load the main file from ISO and apply + // patch + if (gameFilePath.substr(gameFilePath.length() - 5) == ".diff" && + Core::GetState() == Core::State::Running) { std::vector buf; INFO_LOG(SLIPPI, "Will process diff"); diff --git a/Source/Core/Core/Slippi/SlippiGameFileLoader.h b/Source/Core/Core/Slippi/SlippiGameFileLoader.h index 5bc5f25360..d066ab9236 100644 --- a/Source/Core/Core/Slippi/SlippiGameFileLoader.h +++ b/Source/Core/Core/Slippi/SlippiGameFileLoader.h @@ -1,10 +1,10 @@ #pragma once -#include "Common/CommonTypes.h" #include #include #include #include +#include "Common/CommonTypes.h" class SlippiGameFileLoader { diff --git a/Source/Core/Core/Slippi/SlippiMatchmaking.cpp b/Source/Core/Core/Slippi/SlippiMatchmaking.cpp index bc4d075fff..5e005c38a2 100644 --- a/Source/Core/Core/Slippi/SlippiMatchmaking.cpp +++ b/Source/Core/Core/Slippi/SlippiMatchmaking.cpp @@ -1,11 +1,11 @@ #include "SlippiMatchmaking.h" +#include +#include #include "Common/Common.h" -#include "Common/Version.h" #include "Common/ENetUtil.h" #include "Common/Logging/Log.h" #include "Common/StringUtil.h" -#include -#include +#include "Common/Version.h" class MmMessageType { @@ -28,7 +28,8 @@ SlippiMatchmaking::SlippiMatchmaking(SlippiUser* user) m_client = 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()); } @@ -111,7 +112,8 @@ int SlippiMatchmaking::receiveMessage(json& msg, int timeoutMs) case ENET_EVENT_TYPE_RECEIVE: { std::vector 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()); INFO_LOG(SLIPPI_ONLINE, "[Matchmaking] Received: %s", str.c_str()); @@ -281,13 +283,13 @@ void SlippiMatchmaking::startMatchmaking() std::vector connectCodeBuf; connectCodeBuf.insert(connectCodeBuf.end(), m_searchSettings.connectCode.begin(), - m_searchSettings.connectCode.end()); + m_searchSettings.connectCode.end()); // Send message to server to create ticket json request; request["type"] = MmMessageType::CREATE_TICKET; - request["user"] = { {"uid", userInfo.uid}, {"playKey", userInfo.playKey} }; - request["search"] = { {"mode", m_searchSettings.mode}, {"connectCode", connectCodeBuf} }; + request["user"] = {{"uid", userInfo.uid}, {"playKey", userInfo.playKey}}; + request["search"] = {{"mode", m_searchSettings.mode}, {"connectCode", connectCodeBuf}}; request["appVersion"] = Common::scm_slippi_semver_str; sendMessage(request); @@ -296,7 +298,8 @@ void SlippiMatchmaking::startMatchmaking() int rcvRes = receiveMessage(response, 5000); 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_errorMsg = "Failed to join mm queue"; return; @@ -364,7 +367,8 @@ void SlippiMatchmaking::handleMatchmaking() if (latestVersion != "") { // 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"); @@ -382,7 +386,8 @@ void SlippiMatchmaking::handleMatchmaking() terminateMmConnection(); 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() @@ -390,7 +395,8 @@ void SlippiMatchmaking::handleConnecting() std::vector ipParts = SplitString(m_oppIp, ':'); // Is host is now used to specify who the decider is - auto client = std::make_unique(ipParts[0], std::stoi(ipParts[1]), m_hostPort, m_isHost); + auto client = std::make_unique(ipParts[0], std::stoi(ipParts[1]), m_hostPort, + m_isHost); while (!m_netplayClient) { @@ -408,7 +414,8 @@ void SlippiMatchmaking::handleConnecting() } 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 m_netplayClient = nullptr; diff --git a/Source/Core/Core/Slippi/SlippiMatchmaking.h b/Source/Core/Core/Slippi/SlippiMatchmaking.h index f36d083dcb..b5f9fd0f11 100644 --- a/Source/Core/Core/Slippi/SlippiMatchmaking.h +++ b/Source/Core/Core/Slippi/SlippiMatchmaking.h @@ -6,9 +6,9 @@ #include "Core/Slippi/SlippiUser.h" #include +#include #include #include -#include #include using json = nlohmann::json; @@ -50,8 +50,8 @@ public: std::string GetErrorMessage(); protected: - const std::string MM_HOST_DEV = "35.197.121.196"; // Dev host - const std::string MM_HOST_PROD = "35.247.98.48"; // Production host + const std::string MM_HOST_DEV = "35.197.121.196"; // Dev host + const std::string MM_HOST_PROD = "35.247.98.48"; // Production host const u16 MM_PORT = 43113; std::string MM_HOST = ""; diff --git a/Source/Core/Core/Slippi/SlippiNetplay.cpp b/Source/Core/Core/Slippi/SlippiNetplay.cpp index aee1f36fb8..f5201c7080 100644 --- a/Source/Core/Core/Slippi/SlippiNetplay.cpp +++ b/Source/Core/Core/Slippi/SlippiNetplay.cpp @@ -11,10 +11,10 @@ #include "Common/MD5.h" #include "Common/MsgHandler.h" #include "Common/Timer.h" -#include "Core/ConfigManager.h" -#include "Core/NetPlayProto.h" -#include "Core/Core.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/SI.h" //#include "Core/HW/SI_DeviceGCController.h" @@ -22,16 +22,16 @@ #include "Core/HW/WiimoteEmu/WiimoteEmu.h" #include "Core/HW/WiimoteReal/WiimoteReal.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 #include #include #include #include #include +#include "Core/Movie.h" +#include "InputCommon/GCAdapter.h" +#include "VideoCommon/OnScreenDisplay.h" +#include "VideoCommon/VideoConfig.h" static std::mutex pad_mutex; static std::mutex ack_mutex; @@ -62,15 +62,14 @@ SlippiNetplayClient::~SlippiNetplayClient() } // called from ---SLIPPI EXI--- thread -SlippiNetplayClient::SlippiNetplayClient(const std::string& address, const u16 remotePort, const u16 localPort, - bool isDecider) +SlippiNetplayClient::SlippiNetplayClient(const std::string& address, const u16 remotePort, + const u16 localPort, bool isDecider) #ifdef _WIN32 - : m_qos_handle(nullptr) - , m_qos_flow_id(0) + : m_qos_handle(nullptr), m_qos_flow_id(0) #endif { WARN_LOG(SLIPPI_ONLINE, "Initializing Slippi Netplay for port: %d, with host: %s", localPort, - isDecider ? "true" : "false"); + isDecider ? "true" : "false"); this->isDecider = isDecider; @@ -78,9 +77,10 @@ SlippiNetplayClient::SlippiNetplayClient(const std::string& address, const u16 r ENetAddress* localAddr = nullptr; ENetAddress localAddrDef; - // It is important to be able to set the local port to listen on even in a client connection because - // not doing so will break hole punching, the host is expecting traffic to come from a specific ip/port - // and if the port does not match what it is expecting, it will not get through the NAT on some routers + // It is important to be able to set the local port to listen on even in a client connection + // because not doing so will break hole punching, the host is expecting traffic to come from a + // 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) { INFO_LOG(SLIPPI_ONLINE, "Setting up local address"); @@ -91,7 +91,8 @@ SlippiNetplayClient::SlippiNetplayClient(const std::string& address, const u16 r 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); if (m_client == nullptr) @@ -126,7 +127,8 @@ SlippiNetplayClient::SlippiNetplayClient(bool isDecider) unsigned int SlippiNetplayClient::OnData(sf::Packet& packet) { NetPlay::MessageId mid = 0; - if (!(packet >> mid)) { + if (!(packet >> mid)) + { ERROR_LOG(SLIPPI_ONLINE, "Received empty netplay packet"); return 0; } @@ -136,7 +138,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet) case NetPlay::NP_MSG_SLIPPI_PAD: { int32_t frame; - if (!(packet >> frame)) { + if (!(packet >> frame)) + { ERROR_LOG(SLIPPI_ONLINE, "Netplay packet too small to read frame count"); break; } @@ -151,8 +154,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet) auto timing = lastFrameTiming; if (!hasGameStarted) { - // Handle case where opponent starts sending inputs before our game has reached frame 1. This will - // continuously say frame 0 is now to prevent opp from getting too far ahead + // Handle case where opponent starts sending inputs before our game has reached frame 1. This + // will continuously say frame 0 is now to prevent opp from getting too far ahead timing.frame = 0; timing.timeUs = curTime; } @@ -161,8 +164,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet) s64 frameDiffOffsetUs = 16683 * (timing.frame - frame); s64 timeOffsetUs = opponentSendTimeUs - timing.timeUs + frameDiffOffsetUs; - INFO_LOG(SLIPPI_ONLINE, "[Offset] Opp Frame: %d, My Frame: %d. Time offset: %lld", frame, timing.frame, - timeOffsetUs); + INFO_LOG(SLIPPI_ONLINE, "[Offset] Opp Frame: %d, My Frame: %d. Time offset: %lld", frame, + timing.frame, timeOffsetUs); // Add this offset to circular buffer for use later if (frameOffsetData.buf.size() < SLIPPI_ONLINE_LOCKSTEP_INTERVAL) @@ -173,7 +176,7 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet) frameOffsetData.idx = (frameOffsetData.idx + 1) % SLIPPI_ONLINE_LOCKSTEP_INTERVAL; { - std::lock_guard lk(pad_mutex); // TODO: Is this the correct lock? + std::lock_guard lk(pad_mutex); // TODO: Is this the correct lock? auto packetData = (u8*)packet.getData(); @@ -183,18 +186,20 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet) int inputsToCopy = frame - headFrame; // 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"); break; } for (int i = inputsToCopy - 1; i >= 0; i--) { - auto pad = std::make_unique(frame - i, &packetData[5 + i * SLIPPI_PAD_DATA_SIZE]); + auto pad = + std::make_unique(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, - pad->padBuf[0], pad->padBuf[1], pad->padBuf[2], pad->padBuf[3], pad->padBuf[4], pad->padBuf[5], - pad->padBuf[6], pad->padBuf[7]); + pad->padBuf[0], pad->padBuf[1], pad->padBuf[2], pad->padBuf[3], pad->padBuf[4], + pad->padBuf[5], pad->padBuf[6], pad->padBuf[7]); remotePadQueue.push_front(std::move(pad)); } @@ -210,11 +215,12 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet) case NetPlay::NP_MSG_SLIPPI_PAD_ACK: { - std::lock_guard lk(ack_mutex); // Trying to fix rare crash on ackTimers.count + std::lock_guard lk(ack_mutex); // Trying to fix rare crash on ackTimers.count // Store last frame acked int32_t frame; - if (!(packet >> frame)) { + if (!(packet >> frame)) + { ERROR_LOG(SLIPPI_ONLINE, "Ack packet too small to read frame"); break; } @@ -239,8 +245,9 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet) pingUs = Common::Timer::GetTimeUs() - sendTime; if (g_ActiveConfig.bShowNetPlayPing && frame % SLIPPI_PING_DISPLAY_INTERVAL == 0) { - OSD::AddTypedMessage(OSD::MessageType::NetPlayPing, StringFromFormat("Ping: %u", pingUs / 1000), - OSD::Duration::NORMAL, OSD::Color::CYAN); + OSD::AddTypedMessage(OSD::MessageType::NetPlayPing, + StringFromFormat("Ping: %u", pingUs / 1000), OSD::Duration::NORMAL, + OSD::Color::CYAN); } } 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 // 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 hasGameStarted = false; } @@ -267,7 +275,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet) 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; } break; @@ -290,7 +299,8 @@ void SlippiNetplayClient::writeToPacket(sf::Packet& packet, SlippiPlayerSelectio packet << s.connectCode; } -std::unique_ptr SlippiNetplayClient::readSelectionsFromPacket(sf::Packet& packet) +std::unique_ptr +SlippiNetplayClient::readSelectionsFromPacket(sf::Packet& packet) { auto s = std::make_unique(); @@ -405,26 +415,27 @@ void SlippiNetplayClient::ThreadFunc() bool qos_success = false; #ifdef _WIN32 - QOS_VERSION ver = { 1, 0 }; + QOS_VERSION ver = {1, 0}; if (Config::Get(Config::NETPLAY_ENABLE_QOS) && QOSCreateHandle(&ver, &m_qos_handle)) { // from win32.c - struct sockaddr_in sin = { 0 }; + struct sockaddr_in sin = {0}; sin.sin_family = AF_INET; sin.sin_port = ENET_HOST_TO_NET_16(m_server->host->address.port); sin.sin_addr.s_addr = m_server->host->address.host; if (QOSAddSocketToFlow(m_qos_handle, m_server->host->socket, reinterpret_cast(&sin), - // this is 0x38 - QOSTrafficTypeControl, QOS_NON_ADAPTIVE_FLOW, &m_qos_flow_id)) + // this is 0x38 + QOSTrafficTypeControl, QOS_NON_ADAPTIVE_FLOW, &m_qos_flow_id)) { DWORD dscp = 0x2e; // this will fail if we're not admin // 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; } @@ -441,7 +452,8 @@ void SlippiNetplayClient::ThreadFunc() // https://www.tucny.com/Home/dscp-tos // ef is better than cs7 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 @@ -468,13 +480,13 @@ void SlippiNetplayClient::ThreadFunc() break; case ENET_EVENT_TYPE_DISCONNECT: ERROR_LOG(SLIPPI_ONLINE, "[Netplay] Disconnected Event detected: %s", - netEvent.peer == m_server ? "same client" : "diff client"); + netEvent.peer == m_server ? "same client" : "diff client"); // If the disconnect event doesn't come from the client we are actually listening to, // it can be safely ignored if (netEvent.peer == m_server) { - m_do_loop.Clear(); // Stop the loop, will trigger a disconnect + m_do_loop.Clear(); // Stop the loop, will trigger a disconnect } break; default: @@ -550,8 +562,10 @@ void SlippiNetplayClient::SendConnectionSelected() void SlippiNetplayClient::SendSlippiPad(std::unique_ptr pad) { auto status = slippiConnectStatus; - bool connectionFailed = status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_FAILED; - bool connectionDisconnected = status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_DISCONNECTED; + bool connectionFailed = + status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_FAILED; + bool connectionDisconnected = + status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_DISCONNECTED; if (connectionFailed || connectionDisconnected) { return; @@ -559,8 +573,9 @@ void SlippiNetplayClient::SendSlippiPad(std::unique_ptr pad) // if (pad && isDecider) //{ - // ERROR_LOG(SLIPPI_ONLINE, "[%d] %X %X %X %X %X %X %X %X", pad->frame, pad->padBuf[0], pad->padBuf[1], - // pad->padBuf[2], pad->padBuf[3], pad->padBuf[4], pad->padBuf[5], pad->padBuf[6], pad->padBuf[7]); + // ERROR_LOG(SLIPPI_ONLINE, "[%d] %X %X %X %X %X %X %X %X", pad->frame, pad->padBuf[0], + // pad->padBuf[1], pad->padBuf[2], pad->padBuf[3], pad->padBuf[4], pad->padBuf[5], + // pad->padBuf[6], pad->padBuf[7]); //} if (pad) @@ -590,10 +605,10 @@ void SlippiNetplayClient::SendSlippiPad(std::unique_ptr pad) INFO_LOG(SLIPPI_ONLINE, "Sending a packet of inputs [%d]...", frame); 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], - (*it)->padBuf[1], (*it)->padBuf[2], (*it)->padBuf[3], (*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 + INFO_LOG(SLIPPI_ONLINE, "Send [%d] -> %02X %02X %02X %02X %02X %02X %02X %02X", (*it)->frame, + (*it)->padBuf[0], (*it)->padBuf[1], (*it)->padBuf[2], (*it)->padBuf[3], + (*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 } SendAsync(std::move(spac)); @@ -626,7 +641,7 @@ void SlippiNetplayClient::SetMatchSelections(SlippiPlayerSelections& s) std::unique_ptr SlippiNetplayClient::GetSlippiRemotePad(int32_t curFrame) { - std::lock_guard lk(pad_mutex); // TODO: Is this the correct lock? + std::lock_guard lk(pad_mutex); // TODO: Is this the correct lock? std::unique_ptr padOutput = std::make_unique(); @@ -677,7 +692,7 @@ std::string SlippiNetplayClient::GetOpponentName() int32_t SlippiNetplayClient::GetSlippiLatestRemoteFrame() { - std::lock_guard lk(pad_mutex); // TODO: Is this the correct lock? + std::lock_guard lk(pad_mutex); // TODO: Is this the correct lock? if (remotePadQueue.empty()) { @@ -713,7 +728,7 @@ s32 SlippiNetplayClient::CalcTimeOffsetUs() int count = end - offset; if (count <= 0) { - return 0; // What do I return here? + return 0; // What do I return here? } return sum / count; diff --git a/Source/Core/Core/Slippi/SlippiNetplay.h b/Source/Core/Core/Slippi/SlippiNetplay.h index ccad38188e..ef640325c3 100644 --- a/Source/Core/Core/Slippi/SlippiNetplay.h +++ b/Source/Core/Core/Slippi/SlippiNetplay.h @@ -4,6 +4,16 @@ #pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "Common/CommonTypes.h" #include "Common/Event.h" #include "Common/Timer.h" @@ -11,22 +21,13 @@ #include "Core/NetPlayProto.h" #include "Core/Slippi/SlippiPad.h" #include "InputCommon/GCPadStatus.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #ifdef _WIN32 #include #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 struct SlippiRemotePadOutput @@ -103,8 +104,9 @@ public: void ThreadFunc(); void SendAsync(std::unique_ptr packet); - SlippiNetplayClient(bool isDecider); // Make a dummy client - SlippiNetplayClient(const std::string& address, const u16 remotePort, const u16 localPort, bool isDecider); + SlippiNetplayClient(bool isDecider); // Make a dummy client + SlippiNetplayClient(const std::string& address, const u16 remotePort, const u16 localPort, + bool isDecider); ~SlippiNetplayClient(); // Slippi Online @@ -149,8 +151,8 @@ protected: std::thread m_thread; std::string m_selected_game; - Common::Flag m_is_running{ false }; - Common::Flag m_do_loop{ true }; + Common::Flag m_is_running{false}; + Common::Flag m_do_loop{true}; unsigned int m_minimum_buffer_size = 6; @@ -176,8 +178,8 @@ protected: bool hasGameStarted = false; FrameTiming lastFrameTiming; u64 pingUs; - std::deque> localPadQueue; // most recent inputs at start of deque - std::deque> remotePadQueue; // most recent inputs at start of deque + std::deque> localPadQueue; // most recent inputs at start of deque + std::deque> remotePadQueue; // most recent inputs at start of deque std::queue ackTimers; SlippiConnectStatus slippiConnectStatus = SlippiConnectStatus::NET_CONNECT_STATUS_UNSET; SlippiMatchInfo matchInfo; diff --git a/Source/Core/Core/Slippi/SlippiPad.cpp b/Source/Core/Core/Slippi/SlippiPad.cpp index 96cdd86823..df0c329227 100644 --- a/Source/Core/Core/Slippi/SlippiPad.cpp +++ b/Source/Core/Core/Slippi/SlippiPad.cpp @@ -5,7 +5,7 @@ #include // 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) { diff --git a/Source/Core/Core/Slippi/SlippiPad.h b/Source/Core/Core/Slippi/SlippiPad.h index 101c82e4d9..8b7d2171a8 100644 --- a/Source/Core/Core/Slippi/SlippiPad.h +++ b/Source/Core/Core/Slippi/SlippiPad.h @@ -15,4 +15,3 @@ public: int32_t frame; u8 padBuf[SLIPPI_PAD_FULL_SIZE]; }; - diff --git a/Source/Core/Core/Slippi/SlippiPlayback.cpp b/Source/Core/Core/Slippi/SlippiPlayback.cpp index 140c8b2ff4..e918abfb2d 100644 --- a/Source/Core/Core/Slippi/SlippiPlayback.cpp +++ b/Source/Core/Core/Slippi/SlippiPlayback.cpp @@ -86,8 +86,7 @@ void SlippiPlaybackStatus::prepareSlippiPlayback(s32& frameIndex) // TODO: figure out why sometimes playback frame increments past targetFrameNum if (inSlippiPlayback && frameIndex >= targetFrameNum) { - INFO_LOG(SLIPPI, "Reached frame %d. Target was %d. Unblocking", frameIndex, - targetFrameNum); + INFO_LOG(SLIPPI, "Reached frame %d. Target was %d. Unblocking", frameIndex, targetFrameNum); cv_waitingForTargetFrame.notify_one(); } } @@ -101,7 +100,7 @@ void SlippiPlaybackStatus::resetPlayback() if (m_savestateThread.joinable()) m_savestateThread.detach(); - condVar.notify_one(); // Will allow thread to kill itself + condVar.notify_one(); // Will allow thread to kill itself futureDiffs.clear(); futureDiffs.rehash(0); } @@ -136,7 +135,8 @@ void SlippiPlaybackStatus::SavestateThread() { // Wait to hit one of the intervals // 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); if (!shouldRunThreads) @@ -169,11 +169,13 @@ void SlippiPlaybackStatus::SavestateThread() void SlippiPlaybackStatus::seekToFrame() { - if (seekMtx.try_lock()) { + if (seekMtx.try_lock()) + { if (targetFrameNum < Slippi::PLAYBACK_FIRST_SAVE) targetFrameNum = Slippi::PLAYBACK_FIRST_SAVE; - if (targetFrameNum > lastFrame) { + if (targetFrameNum > lastFrame) + { targetFrameNum = lastFrame; } @@ -186,8 +188,10 @@ void SlippiPlaybackStatus::seekToFrame() if (prevState != Core::State::Paused) Core::SetState(Core::State::Paused); - s32 closestStateFrame = targetFrameNum - emod(targetFrameNum - Slippi::PLAYBACK_FIRST_SAVE, FRAME_INTERVAL); - bool isLoadingStateOptimal = targetFrameNum < currentPlaybackFrame || closestStateFrame > currentPlaybackFrame; + s32 closestStateFrame = + targetFrameNum - emod(targetFrameNum - Slippi::PLAYBACK_FIRST_SAVE, FRAME_INTERVAL); + bool isLoadingStateOptimal = + targetFrameNum < currentPlaybackFrame || closestStateFrame > currentPlaybackFrame; if (isLoadingStateOptimal) { @@ -206,7 +210,7 @@ void SlippiPlaybackStatus::seekToFrame() { s32 closestActualStateFrame = closestStateFrame - FRAME_INTERVAL; while (closestActualStateFrame > Slippi::PLAYBACK_FIRST_SAVE && - futureDiffs.count(closestActualStateFrame) == 0) + futureDiffs.count(closestActualStateFrame) == 0) closestActualStateFrame -= FRAME_INTERVAL; loadState(closestActualStateFrame); } @@ -214,10 +218,11 @@ void SlippiPlaybackStatus::seekToFrame() { s32 closestActualStateFrame = closestStateFrame - FRAME_INTERVAL; while (closestActualStateFrame > currentPlaybackFrame && - futureDiffs.count(closestActualStateFrame) == 0) + futureDiffs.count(closestActualStateFrame) == 0) 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) loadState(closestActualStateFrame); } @@ -234,18 +239,22 @@ void SlippiPlaybackStatus::seekToFrame() 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; targetFrameNum = INT_MAX; Core::SetState(prevState); seekMtx.unlock(); - } else { + } + else + { INFO_LOG(SLIPPI, "Already seeking. Ignoring this call"); } } // Set isHardFFW and update OC settings to speed up the FFW -void SlippiPlaybackStatus::setHardFFW(bool enable) { +void SlippiPlaybackStatus::setHardFFW(bool enable) +{ if (enable) { SConfig::GetInstance().m_OCEnable = true; @@ -260,7 +269,6 @@ void SlippiPlaybackStatus::setHardFFW(bool enable) { isHardFFW = enable; } - void SlippiPlaybackStatus::loadState(s32 closestStateFrame) { if (closestStateFrame == Slippi::PLAYBACK_FIRST_SAVE) @@ -268,7 +276,8 @@ void SlippiPlaybackStatus::loadState(s32 closestStateFrame) else { 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 stateToLoad(stateString.begin(), stateString.end()); State::LoadFromBuffer(stateToLoad); } diff --git a/Source/Core/Core/Slippi/SlippiPlayback.h b/Source/Core/Core/Slippi/SlippiPlayback.h index dfb6fc3d72..8870076a60 100644 --- a/Source/Core/Core/Slippi/SlippiPlayback.h +++ b/Source/Core/Core/Slippi/SlippiPlayback.h @@ -5,12 +5,12 @@ #include #include +#include #include #include -#include -#include "Core/ConfigManager.h" #include "../../Common/CommonTypes.h" +#include "Core/ConfigManager.h" class SlippiPlaybackStatus { @@ -48,9 +48,9 @@ private: void updateWatchSettingsStartEnd(); std::unordered_map> - futureDiffs; // State diffs keyed by frameIndex, processed async - std::vector iState; // The initial state - std::vector cState; // The current (latest) state + futureDiffs; // State diffs keyed by frameIndex, processed async + std::vector iState; // The initial state + std::vector cState; // The current (latest) state open_vcdiff::VCDiffDecoder decoder; open_vcdiff::VCDiffEncoder* encoder = NULL; diff --git a/Source/Core/Core/Slippi/SlippiReplayComm.cpp b/Source/Core/Core/Slippi/SlippiReplayComm.cpp index cc5e7d16e0..1559e0e982 100644 --- a/Source/Core/Core/Slippi/SlippiReplayComm.cpp +++ b/Source/Core/Core/Slippi/SlippiReplayComm.cpp @@ -1,6 +1,6 @@ +#include "SlippiReplayComm.h" #include #include -#include "SlippiReplayComm.h" #include "Common/CommonPaths.h" #include "Common/FileUtil.h" #include "Common/Logging/LogManager.h" @@ -18,7 +18,8 @@ static inline void ltrim(std::string& s) // trim from end (in place) static inline void rtrim(std::string& s) { - s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end()); + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), + s.end()); } // trim from both ends (in place) @@ -31,11 +32,13 @@ static inline void trim(std::string& s) SlippiReplayComm::SlippiReplayComm() { INFO_LOG(EXPANSIONINTERFACE, "SlippiReplayComm: Using playback config path: %s", - SConfig::GetInstance().m_strSlippiInput.c_str()); + SConfig::GetInstance().m_strSlippiInput.c_str()); configFilePath = SConfig::GetInstance().m_strSlippiInput.c_str(); } -SlippiReplayComm::~SlippiReplayComm() {} +SlippiReplayComm::~SlippiReplayComm() +{ +} SlippiReplayComm::CommSettings SlippiReplayComm::getSettings() { @@ -177,8 +180,8 @@ void SlippiReplayComm::loadFile() { 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 - // the file is not readable yet? + // Reset in the case of read error. this fixes a race condition where file mod time changes + // but the file is not readable yet? configLastLoadModTime = 0; } diff --git a/Source/Core/Core/Slippi/SlippiReplayComm.h b/Source/Core/Core/Slippi/SlippiReplayComm.h index dd82ed75bc..dc4266c19f 100644 --- a/Source/Core/Core/Slippi/SlippiReplayComm.h +++ b/Source/Core/Core/Slippi/SlippiReplayComm.h @@ -1,10 +1,10 @@ #pragma once +#include #include +#include #include #include -#include -#include #include @@ -32,7 +32,7 @@ public: int endFrame = INT_MAX; bool outputOverlayFiles; bool isRealTimeMode; - std::string rollbackDisplayMethod; // off, normal, visible + std::string rollbackDisplayMethod; // off, normal, visible std::string commandId; std::queue queue; } CommSettings; diff --git a/Source/Core/Core/Slippi/SlippiSavestate.cpp b/Source/Core/Core/Slippi/SlippiSavestate.cpp index 57d7a2bd72..66b02428f3 100644 --- a/Source/Core/Core/Slippi/SlippiSavestate.cpp +++ b/Source/Core/Core/Slippi/SlippiSavestate.cpp @@ -1,4 +1,5 @@ #include "SlippiSavestate.h" +#include #include "Common/CommonFuncs.h" #include "Common/MemoryUtil.h" #include "Core/HW/AudioInterface.h" @@ -11,7 +12,6 @@ #include "Core/HW/ProcessorInterface.h" #include "Core/HW/SI/SI.h" #include "Core/HW/VideoInterface.h" -#include SlippiSavestate::SlippiSavestate() { @@ -47,52 +47,52 @@ bool cmpFn(SlippiSavestate::PreserveBlock pb1, SlippiSavestate::PreserveBlock pb void SlippiSavestate::initBackupLocs() { static std::vector fullBackupRegions = { - {0x80005520, 0x80005940, nullptr}, // Data Sections 0 and 1 - {0x803b7240, 0x804DEC00, nullptr}, // Data Sections 2-7 and in between sections including BSS + {0x80005520, 0x80005940, nullptr}, // Data Sections 0 and 1 + {0x803b7240, 0x804DEC00, nullptr}, // Data Sections 2-7 and in between sections including BSS // Full Unknown Region: [804fec00 - 80BD5C40) // https://docs.google.com/spreadsheets/d/16ccNK_qGrtPfx4U25w7OWIDMZ-NxN1WNBmyQhaDxnEg/edit?usp=sharing - {0x8065c000, 0x8071b000, nullptr}, // Unknown Region Pt1 - {0x80bb0000, 0x811AD5A0, nullptr}, // Unknown Region Pt2, Heap [80bd5c40 - 811AD5A0) + {0x8065c000, 0x8071b000, nullptr}, // Unknown Region Pt1 + {0x80bb0000, 0x811AD5A0, nullptr}, // Unknown Region Pt2, Heap [80bd5c40 - 811AD5A0) }; static std::vector excludeSections = { - // Sound stuff - {0x804031A0, 0x24}, // [804031A0 - 804031C4) - {0x80407FB4, 0x34C}, // [80407FB4 - 80408300) - {0x80433C64, 0x1EE80}, // [80433C64 - 80452AE4) - {0x804A8D78, 0x17A68}, // [804A8D78 - 804C07E0) - {0x804C28E0, 0x399C}, // [804C28E0 - 804C627C) - {0x804D7474, 0x8}, // [804D7474 - 804D747C) - {0x804D74F0, 0x50}, // [804D74F0 - 804D7540) - {0x804D7548, 0x4}, // [804D7548 - 804D754C) - {0x804D7558, 0x24}, // [804D7558 - 804D757C) - {0x804D7580, 0xC}, // [804D7580 - 804D758C) - {0x804D759C, 0x4}, // [804D759C - 804D75A0) - {0x804D7720, 0x4}, // [804D7720 - 804D7724) - {0x804D7744, 0x4}, // [804D7744 - 804D7748) - {0x804D774C, 0x8}, // [804D774C - 804D7754) - {0x804D7758, 0x8}, // [804D7758 - 804D7760) - {0x804D7788, 0x10}, // [804D7788 - 804D7798) - {0x804D77C8, 0x4}, // [804D77C8 - 804D77CC) - {0x804D77D0, 0x4}, // [804D77D0 - 804D77D4) - {0x804D77E0, 0x4}, // [804D77E0 - 804D77E4) - {0x804DE358, 0x80}, // [804DE358 - 804DE3D8) - {0x804DE800, 0x70}, // [804DE800 - 804DE870) + // Sound stuff + {0x804031A0, 0x24}, // [804031A0 - 804031C4) + {0x80407FB4, 0x34C}, // [80407FB4 - 80408300) + {0x80433C64, 0x1EE80}, // [80433C64 - 80452AE4) + {0x804A8D78, 0x17A68}, // [804A8D78 - 804C07E0) + {0x804C28E0, 0x399C}, // [804C28E0 - 804C627C) + {0x804D7474, 0x8}, // [804D7474 - 804D747C) + {0x804D74F0, 0x50}, // [804D74F0 - 804D7540) + {0x804D7548, 0x4}, // [804D7548 - 804D754C) + {0x804D7558, 0x24}, // [804D7558 - 804D757C) + {0x804D7580, 0xC}, // [804D7580 - 804D758C) + {0x804D759C, 0x4}, // [804D759C - 804D75A0) + {0x804D7720, 0x4}, // [804D7720 - 804D7724) + {0x804D7744, 0x4}, // [804D7744 - 804D7748) + {0x804D774C, 0x8}, // [804D774C - 804D7754) + {0x804D7758, 0x8}, // [804D7758 - 804D7760) + {0x804D7788, 0x10}, // [804D7788 - 804D7798) + {0x804D77C8, 0x4}, // [804D77C8 - 804D77CC) + {0x804D77D0, 0x4}, // [804D77D0 - 804D77D4) + {0x804D77E0, 0x4}, // [804D77E0 - 804D77E4) + {0x804DE358, 0x80}, // [804DE358 - 804DE3D8) + {0x804DE800, 0x70}, // [804DE800 - 804DE870) - // The following need to be added to the ranges proper - {0x804d6030, 0x4}, // ??? - {0x804d603c, 0x4}, // ??? - {0x804d7218, 0x4}, // ??? - {0x804d7228, 0x8}, // ??? - {0x804d7740, 0x4}, // ??? - {0x804d7754, 0x4}, // ??? - {0x804d77bc, 0x4}, // ??? - {0x804de7f0, 0x10}, // ??? + // The following need to be added to the ranges proper + {0x804d6030, 0x4}, // ??? + {0x804d603c, 0x4}, // ??? + {0x804d7218, 0x4}, // ??? + {0x804d7228, 0x8}, // ??? + {0x804d7740, 0x4}, // ??? + {0x804d7754, 0x4}, // ??? + {0x804d77bc, 0x4}, // ??? + {0x804de7f0, 0x10}, // ??? - // Camera Blocks, Temporarily added here - //{0x80452c7c, 0x2B0}, // Cam Block 1, including gaps - //{0x806e516c, 0xA8}, // Cam Block 2, including gaps + // Camera Blocks, Temporarily added here + //{0x80452c7c, 0x2B0}, // Cam Block 1, including gaps + //{0x806e516c, 0xA8}, // Cam Block 2, including gaps }; static std::vector processedLocs = {}; @@ -146,7 +146,7 @@ void SlippiSavestate::initBackupLocs() // Add split section after exclusion 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); } diff --git a/Source/Core/Core/Slippi/SlippiSavestate.h b/Source/Core/Core/Slippi/SlippiSavestate.h index 284df2cf14..30001129ff 100644 --- a/Source/Core/Core/Slippi/SlippiSavestate.h +++ b/Source/Core/Core/Slippi/SlippiSavestate.h @@ -1,8 +1,8 @@ #pragma once +#include #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" -#include class PointerWrap; @@ -14,7 +14,10 @@ public: u32 address; 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(); @@ -46,7 +49,7 @@ private: { std::size_t operator()(const PreserveBlock& node) const { - return node.address ^ node.length; // TODO: This is probably a bad hash + return node.address ^ node.length; // TODO: This is probably a bad hash } }; diff --git a/Source/Core/Core/Slippi/SlippiUser.cpp b/Source/Core/Core/Slippi/SlippiUser.cpp index 4074bd1da8..6858d384ff 100644 --- a/Source/Core/Core/Slippi/SlippiUser.cpp +++ b/Source/Core/Core/Slippi/SlippiUser.cpp @@ -33,7 +33,7 @@ static void system_hidden(const char* cmd) memset(&p_info, 0, sizeof(p_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); 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 = - 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 both files exist we just log they exist and take no further action if (File::Exists(userFilePath)) { - INFO_LOG(SLIPPI_ONLINE, - "Found both .json.txt and .json file for user data. Using .json and ignoring the .json.txt"); + INFO_LOG(SLIPPI_ONLINE, "Found both .json.txt and .json file for user data. Using .json " + "and ignoring the .json.txt"); } // If only the .txt file exists move the contents to a json file and log if it fails 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) { 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; @@ -149,7 +152,8 @@ void SlippiUser::OpenLogInPage() std::string path = getUserFilePath(); #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, "/", "\\"); #endif @@ -169,7 +173,7 @@ void SlippiUser::OpenLogInPage() #elif defined(__APPLE__) std::string command = "open \"" + fullUrl + "\""; #else - std::string command = "xdg-open \"" + fullUrl + "\""; // Linux + std::string command = "xdg-open \"" + fullUrl + "\""; // Linux #endif RunSystemCommand(command); @@ -181,12 +185,15 @@ void SlippiUser::UpdateApp() auto isoPath = SConfig::GetInstance().m_strIsoPath; std::string path = File::GetExeDirectory() + "/dolphin-slippi-tools.exe"; - 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 ..."; + 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 ..."; // std::string command = - // "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 + "\""; + // "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 + "\""; WARN_LOG(SLIPPI, "Executing app update command: %s", command.c_str()); RunSystemCommand(command); #elif defined(__APPLE__) @@ -263,7 +270,8 @@ void SlippiUser::FileListenThread() std::string SlippiUser::getUserFilePath() { #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) std::string userFilePath = File::GetExeDirectory() + DIR_SEP + "user.json"; #else diff --git a/Source/Core/Core/Slippi/SlippiUser.h b/Source/Core/Core/Slippi/SlippiUser.h index f12b66f774..6b350bf019 100644 --- a/Source/Core/Core/Slippi/SlippiUser.h +++ b/Source/Core/Core/Slippi/SlippiUser.h @@ -1,12 +1,12 @@ #pragma once -#include "Common/CommonTypes.h" #include #include #include #include #include #include +#include "Common/CommonTypes.h" class SlippiUser { diff --git a/Source/Core/DolphinQt/Settings/SlippiPane.cpp b/Source/Core/DolphinQt/Settings/SlippiPane.cpp index 5ae78ba8ef..4998841c28 100644 --- a/Source/Core/DolphinQt/Settings/SlippiPane.cpp +++ b/Source/Core/DolphinQt/Settings/SlippiPane.cpp @@ -94,22 +94,23 @@ void SlippiPane::CreateLayout() connect(delay_spin, qOverload(&QSpinBox::valueChanged), this, [](int delay) { SConfig::GetInstance().m_slippiOnlineDelay = delay; }); #else - //Playback Settings + // Playback Settings auto* playback_settings = new QGroupBox(tr("Playback Settings")); auto* playback_settings_layout = new QVBoxLayout(); playback_settings->setLayout(playback_settings_layout); layout->addWidget(playback_settings); auto* enable_playback_seek_checkbox = new QCheckBox(tr("Enable Seekbar")); - char seekbarTooltip[] = "

Enables video player style controls while watching Slippi replays. Uses more cpu resources and can be stuttery. " \ - "Space: Pause/Play " \ - "Left/Right: Jump 5 seconds back/forward" \ - "Shift + Left/Right: Jump 20 seconds back/forward" \ - "Period (while paused): Advance one frame"; + char seekbarTooltip[] = "

Enables video player style controls while " + "watching Slippi replays. Uses more cpu resources and can be stuttery. " + "Space: Pause/Play " + "Left/Right: Jump 5 seconds back/forward" + "Shift + Left/Right: Jump 20 seconds back/forward" + "Period (while paused): Advance one frame"; enable_playback_seek_checkbox->setToolTip(tr(seekbarTooltip)); playback_settings_layout->addWidget(enable_playback_seek_checkbox); enable_playback_seek_checkbox->setChecked(SConfig::GetInstance().m_slippiEnableSeek); connect(enable_playback_seek_checkbox, &QCheckBox::toggled, this, - [](bool checked) { SConfig::GetInstance().m_slippiEnableSeek = checked; }); + [](bool checked) { SConfig::GetInstance().m_slippiEnableSeek = checked; }); #endif } From e6f721f5c0b985a6e9aa76dd4ccf37330f9918c2 Mon Sep 17 00:00:00 2001 From: Nicholas Marcott <481161+bmarcott@users.noreply.github.com> Date: Mon, 4 Jan 2021 16:43:41 +0900 Subject: [PATCH 2/2] Fix formatting for External SlippiLib files --- Externals/SlippiLib/SlippiGame.cpp | 257 +++++++++++++++++------------ Externals/SlippiLib/SlippiGame.h | 57 ++++--- 2 files changed, 187 insertions(+), 127 deletions(-) diff --git a/Externals/SlippiLib/SlippiGame.cpp b/Externals/SlippiLib/SlippiGame.cpp index 31987774d7..497f9bf1d5 100644 --- a/Externals/SlippiLib/SlippiGame.cpp +++ b/Externals/SlippiLib/SlippiGame.cpp @@ -4,15 +4,17 @@ #include "SlippiGame.h" -namespace Slippi { - +namespace Slippi +{ //********************************************************************** //* Event Handlers //********************************************************************** // The read operators will read a value and increment the index so the next read // will read in the correct location -uint8_t readByte(uint8_t *a, int &idx, uint32_t maxSize, uint8_t defaultValue) { - if (idx >= (int)maxSize) { +uint8_t readByte(uint8_t* a, int& idx, uint32_t maxSize, uint8_t defaultValue) +{ + if (idx >= (int)maxSize) + { idx += 1; return defaultValue; } @@ -20,9 +22,10 @@ uint8_t readByte(uint8_t *a, int &idx, uint32_t maxSize, uint8_t defaultValue) { return a[idx++]; } -uint16_t readHalf(uint8_t *a, int &idx, uint32_t maxSize, - uint16_t defaultValue) { - if (idx >= (int)maxSize) { +uint16_t readHalf(uint8_t* a, int& idx, uint32_t maxSize, uint16_t defaultValue) +{ + if (idx >= (int)maxSize) + { idx += 2; return defaultValue; } @@ -32,34 +35,38 @@ uint16_t readHalf(uint8_t *a, int &idx, uint32_t maxSize, return value; } -uint32_t readWord(uint8_t *a, int &idx, uint32_t maxSize, - uint32_t defaultValue) { - if (idx >= (int)maxSize) { +uint32_t readWord(uint8_t* a, int& idx, uint32_t maxSize, uint32_t defaultValue) +{ + if (idx >= (int)maxSize) + { idx += 4; return defaultValue; } - 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; return value; } -float readFloat(uint8_t *a, int &idx, uint32_t maxSize, float defaultValue) { - uint32_t bytes = readWord(a, idx, maxSize, *(uint32_t *)(&defaultValue)); - return *(float *)(&bytes); +float readFloat(uint8_t* a, int& idx, uint32_t maxSize, float defaultValue) +{ + uint32_t bytes = readWord(a, idx, maxSize, *(uint32_t*)(&defaultValue)); + return *(float*)(&bytes); } -void handleGameInit(Game *game, uint32_t maxSize) { +void handleGameInit(Game* game, uint32_t maxSize) +{ int idx = 0; // 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); } // 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); } @@ -68,15 +75,18 @@ void handleGameInit(Game *game, uint32_t maxSize) { // Read UCF toggle bytes 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; game->settings.ucfToggles[i] = value; } // Read nametag for each player std::array, 4> playerNametags; - for (int i = 0; i < 4; i++) { - for (int j = 0; j < NAMETAG_SIZE; j++) { + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < NAMETAG_SIZE; j++) + { playerNametags[i][j] = readHalf(data, idx, maxSize, 0); } } @@ -88,17 +98,18 @@ void handleGameInit(Game *game, uint32_t maxSize) { game->settings.isFrozenPS = readByte(data, idx, maxSize, 0); // Pull header data into struct - int player1Pos = 24; // This is the index of the first players character info - std::array gameInfoHeader = - game->settings.header; - for (int i = 0; i < 4; i++) { + int player1Pos = 24; // This is the index of the first players character info + std::array gameInfoHeader = game->settings.header; + for (int i = 0; i < 4; i++) + { // this is the position in the array that this player's character info is // stored int pos = player1Pos + (9 * i); uint32_t playerInfo = gameInfoHeader[pos]; uint8_t playerType = (playerInfo & 0x00FF0000) >> 16; - if (playerType == 0x3) { + if (playerType == 0x3) + { // Player type 3 is an empty slot continue; } @@ -120,12 +131,15 @@ void handleGameInit(Game *game, uint32_t maxSize) { auto majorVersion = game->version[0]; 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 // are needed before starting the game. areSettingsLoaded will be set // to true when they are received 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 // Sheik game info was added in this version and so we no longer // need to wait @@ -133,16 +147,17 @@ void handleGameInit(Game *game, uint32_t maxSize) { } } -void handleGeckoList(Game *game, uint32_t maxSize) { +void handleGeckoList(Game* game, uint32_t maxSize) +{ 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 game->areSettingsLoaded = true; } -void handleFrameStart(Game *game, uint32_t maxSize) { +void handleFrameStart(Game* game, uint32_t maxSize) +{ int idx = 0; // Check frame count @@ -150,7 +165,7 @@ void handleFrameStart(Game *game, uint32_t maxSize) { game->frameCount = frameCount; auto frameUniquePtr = std::make_unique(); - FrameData *frame = frameUniquePtr.get(); + FrameData* frame = frameUniquePtr.get(); frame->frame = frameCount; frame->randomSeedExists = true; @@ -163,7 +178,8 @@ void handleFrameStart(Game *game, uint32_t maxSize) { game->framesByIndex[frameCount] = frame; } -void handlePreFrameUpdate(Game *game, uint32_t maxSize) { +void handlePreFrameUpdate(Game* game, uint32_t maxSize) +{ int idx = 0; // Check frame count @@ -171,10 +187,11 @@ void handlePreFrameUpdate(Game *game, uint32_t maxSize) { game->frameCount = frameCount; auto frameUniquePtr = std::make_unique(); - FrameData *frame = frameUniquePtr.get(); + FrameData* frame = frameUniquePtr.get(); bool isNewFrame = true; - if (game->framesByIndex.count(frameCount)) { + if (game->framesByIndex.count(frameCount)) + { // If this frame already exists, get the current frame frame = game->frames.back().get(); isNewFrame = false; @@ -209,36 +226,40 @@ void handlePreFrameUpdate(Game *game, uint32_t maxSize) { p.lTrigger = 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); } uint32_t noPercent = 0xFFFFFFFF; - p.percent = readFloat(data, idx, maxSize, *(float *)(&noPercent)); + p.percent = readFloat(data, idx, maxSize, *(float*)(&noPercent)); // Add player data to frame - std::unordered_map *target; + std::unordered_map* target; target = isFollower ? &frame->followers : &frame->players; // Set the player data for the player or follower target->operator[](playerSlot) = p; // Add frame to game - if (isNewFrame) { + if (isNewFrame) + { frame->numSinceStart = game->frames.size(); game->frames.push_back(std::move(frameUniquePtr)); game->framesByIndex[frameCount] = frame; } } -void handlePostFrameUpdate(Game *game, uint32_t maxSize) { +void handlePostFrameUpdate(Game* game, uint32_t maxSize) +{ int idx = 0; // Check frame count int32_t frameCount = readWord(data, idx, maxSize, 0); - FrameData *frame; - if (game->framesByIndex.count(frameCount)) { + FrameData* frame; + if (game->framesByIndex.count(frameCount)) + { // If this frame already exists, get the current frame frame = game->frames.back().get(); } @@ -251,51 +272,58 @@ void handlePostFrameUpdate(Game *game, uint32_t maxSize) { uint8_t playerSlot = readByte(data, idx, maxSize, 0); uint8_t isFollower = readByte(data, idx, maxSize, 0); - PlayerFrameData *p = - isFollower ? &frame->followers[playerSlot] : &frame->players[playerSlot]; + PlayerFrameData* p = isFollower ? &frame->followers[playerSlot] : &frame->players[playerSlot]; p->internalCharacterId = readByte(data, idx, maxSize, 0); // 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; } // Set settings loaded if this is the last character - if (frameCount == GAME_FIRST_FRAME) { + if (frameCount == GAME_FIRST_FRAME) + { uint8_t lastPlayerIndex = 0; - for (auto it = frame->players.begin(); it != frame->players.end(); ++it) { - if (it->first <= lastPlayerIndex) { + for (auto it = frame->players.begin(); it != frame->players.end(); ++it) + { + if (it->first <= lastPlayerIndex) + { continue; } lastPlayerIndex = it->first; } - if (playerSlot >= lastPlayerIndex) { + if (playerSlot >= lastPlayerIndex) + { game->areSettingsLoaded = true; } } } -void handleGameEnd(Game *game, uint32_t maxSize) { +void handleGameEnd(Game* game, uint32_t maxSize) +{ int idx = 0; game->winCondition = readByte(data, idx, maxSize, 0); } // This function gets the position where the raw data starts -int getRawDataPosition(std::ifstream *f) { +int getRawDataPosition(std::ifstream* f) +{ char buffer[2]; f->seekg(0, std::ios::beg); f->read(buffer, 2); - if (buffer[0] == 0x36) { + if (buffer[0] == 0x36) + { return 0; } - if (buffer[0] != '{') { + if (buffer[0] != '{') + { // TODO: Do something here to cause an error return 0; } @@ -306,8 +334,10 @@ int getRawDataPosition(std::ifstream *f) { return 15; } -uint32_t getRawDataLength(std::ifstream *f, int position, int fileSize) { - if (position == 0) { +uint32_t getRawDataLength(std::ifstream* f, int position, int fileSize) +{ + if (position == 0) + { return fileSize; } @@ -315,28 +345,28 @@ uint32_t getRawDataLength(std::ifstream *f, int position, int fileSize) { f->seekg(position - 4, std::ios::beg); f->read(buffer, 4); - uint8_t *byteBuf = (uint8_t *)&buffer[0]; - uint32_t length = - byteBuf[0] << 24 | byteBuf[1] << 16 | byteBuf[2] << 8 | byteBuf[3]; + uint8_t* byteBuf = (uint8_t*)&buffer[0]; + uint32_t length = byteBuf[0] << 24 | byteBuf[1] << 16 | byteBuf[2] << 8 | byteBuf[3]; return length; } -std::unordered_map getMessageSizes(std::ifstream *f, - int position) { +std::unordered_map getMessageSizes(std::ifstream* f, int position) +{ char buffer[2]; f->seekg(position, std::ios::beg); f->read(buffer, 2); - if (buffer[0] != EVENT_PAYLOAD_SIZES) { + if (buffer[0] != EVENT_PAYLOAD_SIZES) + { return {}; } int payloadLength = buffer[1]; - std::unordered_map messageSizes = { - {EVENT_PAYLOAD_SIZES, payloadLength}}; + std::unordered_map messageSizes = {{EVENT_PAYLOAD_SIZES, payloadLength}}; std::vector messageSizesBuffer(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]; // Extract the bytes in u8s. Without this the chars don't or together well @@ -350,8 +380,10 @@ std::unordered_map getMessageSizes(std::ifstream *f, return messageSizes; } -void SlippiGame::processData() { - if (isProcessingComplete) { +void SlippiGame::processData() +{ + if (isProcessingComplete) + { // If we have finished processing this file, return return; } @@ -359,17 +391,20 @@ void SlippiGame::processData() { // This function will process as much data as possible int startPos = (int)file->tellg(); file->seekg(startPos); - if (startPos == 0) { + if (startPos == 0) + { file->seekg(0, std::ios::end); int len = (int)file->tellg(); - if (len < 2) { + if (len < 2) + { // If we can't read message sizes payload size yet, return return; } int rawDataPos = getRawDataPosition(file.get()); 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 // Reset to begining so that the startPos condition will be hit again file->seekg(0); @@ -383,7 +418,8 @@ void SlippiGame::processData() { file->read(buffer, 2); file->seekg(startPos); auto messageSizesSize = (int)buffer[1]; - if (rawDataLen < messageSizesSize) { + if (rawDataLen < messageSizesSize) + { // If we haven't received the full payload sizes message, return // Reset to begining so that the startPos condition will be hit again file->seekg(0); @@ -401,7 +437,8 @@ void SlippiGame::processData() { // log << "Size to read: " << sizeToRead << "\n"; // log << "Start Pos: " << startPos << "\n"; // log << "End Pos: " << endPos << "\n\n"; - if (sizeToRead <= 0) { + if (sizeToRead <= 0) + { return; } @@ -409,7 +446,8 @@ void SlippiGame::processData() { file->read(&newData[0], sizeToRead); int newDataPos = 0; - while (newDataPos < sizeToRead) { + while (newDataPos < sizeToRead) + { auto command = newData[newDataPos]; auto payloadSize = asmEvents[command]; @@ -418,32 +456,35 @@ void SlippiGame::processData() { // log << "Command: " << buff << " | Payload Size: " << payloadSize << "\n"; 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 // Will be processed after getting more data (hopefully) file->seekg(-remainingLen, std::ios::cur); return; } - data = (uint8_t *)&newData[newDataPos + 1]; + data = (uint8_t*)&newData[newDataPos + 1]; uint8_t isSplitComplete = false; uint32_t outerPayloadSize = payloadSize; // Handle a split message, combining in until we possess the entire message - if (command == EVENT_SPLIT_MESSAGE) { - if (shouldResetSplitMessageBuf) { + if (command == EVENT_SPLIT_MESSAGE) + { + if (shouldResetSplitMessageBuf) + { splitMessageBuf.clear(); shouldResetSplitMessageBuf = false; } int _ = 0; - uint16_t blockSize = - readHalf(&data[SPLIT_MESSAGE_INTERNAL_DATA_LEN], _, payloadSize, 0); + uint16_t blockSize = readHalf(&data[SPLIT_MESSAGE_INTERNAL_DATA_LEN], _, payloadSize, 0); splitMessageBuf.insert(splitMessageBuf.end(), data, data + blockSize); isSplitComplete = data[SPLIT_MESSAGE_INTERNAL_DATA_LEN + 3]; - if (isSplitComplete) { + if (isSplitComplete) + { // Transform this message into a different message command = data[SPLIT_MESSAGE_INTERNAL_DATA_LEN + 2]; data = &splitMessageBuf[0]; @@ -452,7 +493,8 @@ void SlippiGame::processData() { } } - switch (command) { + switch (command) + { case EVENT_GAME_INIT: handleGameInit(game.get(), payloadSize); break; @@ -488,24 +530,23 @@ void SlippiGame::processData() { } } -std::unique_ptr SlippiGame::FromFile(std::string path) { +std::unique_ptr SlippiGame::FromFile(std::string path) +{ auto result = std::make_unique(); result->game = std::make_unique(); result->path = path; #ifdef _WIN32 // On Windows, we need to convert paths to std::wstring to deal with UTF-8 - std::wstring convertedPath = - std::wstring_convert>().from_bytes(path); - result->file = std::make_unique( - convertedPath, std::ios::in | std::ios::binary); + std::wstring convertedPath = std::wstring_convert>().from_bytes(path); + result->file = std::make_unique(convertedPath, std::ios::in | std::ios::binary); #else - result->file = - std::make_unique(path, std::ios::in | std::ios::binary); + result->file = std::make_unique(path, std::ios::in | std::ios::binary); #endif // result->log.open("log.txt"); - if (!result->file->is_open()) { + if (!result->file->is_open()) + { return nullptr; } @@ -523,27 +564,38 @@ std::unique_ptr SlippiGame::FromFile(std::string path) { return std::move(result); } -bool SlippiGame::IsProcessingComplete() { return isProcessingComplete; } +bool SlippiGame::IsProcessingComplete() +{ + return isProcessingComplete; +} -bool SlippiGame::AreSettingsLoaded() { +bool SlippiGame::AreSettingsLoaded() +{ processData(); return game->areSettingsLoaded; } -bool SlippiGame::DoesFrameExist(int32_t frame) { +bool SlippiGame::DoesFrameExist(int32_t frame) +{ processData(); return (bool)game->framesByIndex.count(frame); } -std::array SlippiGame::GetVersion() { return game->version; } +std::array SlippiGame::GetVersion() +{ + return game->version; +} -FrameData *SlippiGame::GetFrame(int32_t frame) { +FrameData* SlippiGame::GetFrame(int32_t frame) +{ // Get the frame we want return game->framesByIndex.at(frame); } -FrameData *SlippiGame::GetFrameAt(uint32_t pos) { - if (pos >= game->frames.size()) { +FrameData* SlippiGame::GetFrameAt(uint32_t pos) +{ + if (pos >= game->frames.size()) + { return nullptr; } @@ -551,17 +603,20 @@ FrameData *SlippiGame::GetFrameAt(uint32_t pos) { return game->frames[pos].get(); } -int32_t SlippiGame::GetLatestIndex() { +int32_t SlippiGame::GetLatestIndex() +{ processData(); return game->frameCount; } -GameSettings *SlippiGame::GetSettings() { +GameSettings* SlippiGame::GetSettings() +{ processData(); return &game->settings; } -bool SlippiGame::DoesPlayerExist(int8_t port) { +bool SlippiGame::DoesPlayerExist(int8_t port) +{ return game->settings.players.find(port) != game->settings.players.end(); } -} // namespace Slippi +} // namespace Slippi diff --git a/Externals/SlippiLib/SlippiGame.h b/Externals/SlippiLib/SlippiGame.h index bb352ec3ae..f585cb3cd8 100644 --- a/Externals/SlippiLib/SlippiGame.h +++ b/Externals/SlippiLib/SlippiGame.h @@ -8,8 +8,8 @@ #include #include -namespace Slippi { - +namespace Slippi +{ const uint8_t EVENT_SPLIT_MESSAGE = 0x10; const uint8_t EVENT_PAYLOAD_SIZES = 0x35; const uint8_t EVENT_GAME_INIT = 0x36; @@ -29,9 +29,10 @@ const uint8_t GAME_SHEIK_EXTERNAL_ID = 0x13; 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 uint32_t randomSeed; @@ -54,19 +55,20 @@ typedef struct { float cstickX; float cstickY; 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 - 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 rTrigger; uint8_t joystickXRaw; } PlayerFrameData; -typedef struct FrameData { +typedef struct FrameData +{ int32_t frame; uint32_t numSinceStart; bool randomSeedExists = false; @@ -76,7 +78,8 @@ typedef struct FrameData { std::unordered_map followers; } FrameData; -typedef struct { +typedef struct +{ // Static data uint8_t characterId; uint8_t characterColor; @@ -85,8 +88,9 @@ typedef struct { std::array nametag; } PlayerSettings; -typedef struct { - uint16_t stage; // Stage ID +typedef struct +{ + uint16_t stage; // Stage ID uint32_t randomSeed; std::array header; std::array ucfToggles; @@ -96,14 +100,15 @@ typedef struct { std::vector geckoCodes; } GameSettings; -typedef struct Game { +typedef struct Game +{ std::array version; - std::unordered_map framesByIndex; + std::unordered_map framesByIndex; std::vector> frames; GameSettings settings; bool areSettingsLoaded = false; - int32_t frameCount; // Current/last frame count + int32_t frameCount; // Current/last frame count // From OnGameEnd event uint8_t winCondition; @@ -111,23 +116,23 @@ typedef struct Game { // TODO: This shouldn't be static. Doesn't matter too much atm because we always // TODO: only read one file at a time -static std::unordered_map asmEvents = { - {EVENT_GAME_INIT, 320}, - {EVENT_PRE_FRAME_UPDATE, 58}, - {EVENT_POST_FRAME_UPDATE, 33}, - {EVENT_GAME_END, 1}, - {EVENT_FRAME_START, 8}}; +static std::unordered_map asmEvents = {{EVENT_GAME_INIT, 320}, + {EVENT_PRE_FRAME_UPDATE, 58}, + {EVENT_POST_FRAME_UPDATE, 33}, + {EVENT_GAME_END, 1}, + {EVENT_FRAME_START, 8}}; -class SlippiGame { +class SlippiGame +{ public: static std::unique_ptr FromFile(std::string path); bool AreSettingsLoaded(); bool DoesFrameExist(int32_t frame); std::array GetVersion(); - FrameData *GetFrame(int32_t frame); - FrameData *GetFrameAt(uint32_t pos); + FrameData* GetFrame(int32_t frame); + FrameData* GetFrameAt(uint32_t pos); int32_t GetLatestIndex(); - GameSettings *GetSettings(); + GameSettings* GetSettings(); bool DoesPlayerExist(int8_t port); bool IsProcessingComplete(); @@ -143,4 +148,4 @@ private: bool isProcessingComplete = false; void processData(); }; -} // namespace Slippi +} // namespace Slippi