Fix formatting for External SlippiLib files

This commit is contained in:
Nicholas Marcott 2021-01-04 16:43:41 +09:00
commit e6f721f5c0
2 changed files with 187 additions and 127 deletions

View file

@ -4,15 +4,17 @@
#include "SlippiGame.h" #include "SlippiGame.h"
namespace Slippi { namespace Slippi
{
//********************************************************************** //**********************************************************************
//* Event Handlers //* Event Handlers
//********************************************************************** //**********************************************************************
// The read operators will read a value and increment the index so the next read // The read operators will read a value and increment the index so the next read
// will read in the correct location // will read in the correct location
uint8_t readByte(uint8_t *a, int &idx, uint32_t maxSize, uint8_t defaultValue) { uint8_t readByte(uint8_t* a, int& idx, uint32_t maxSize, uint8_t defaultValue)
if (idx >= (int)maxSize) { {
if (idx >= (int)maxSize)
{
idx += 1; idx += 1;
return defaultValue; return defaultValue;
} }
@ -20,9 +22,10 @@ uint8_t readByte(uint8_t *a, int &idx, uint32_t maxSize, uint8_t defaultValue) {
return a[idx++]; return a[idx++];
} }
uint16_t readHalf(uint8_t *a, int &idx, uint32_t maxSize, uint16_t readHalf(uint8_t* a, int& idx, uint32_t maxSize, uint16_t defaultValue)
uint16_t defaultValue) { {
if (idx >= (int)maxSize) { if (idx >= (int)maxSize)
{
idx += 2; idx += 2;
return defaultValue; return defaultValue;
} }
@ -32,34 +35,38 @@ uint16_t readHalf(uint8_t *a, int &idx, uint32_t maxSize,
return value; return value;
} }
uint32_t readWord(uint8_t *a, int &idx, uint32_t maxSize, uint32_t readWord(uint8_t* a, int& idx, uint32_t maxSize, uint32_t defaultValue)
uint32_t defaultValue) { {
if (idx >= (int)maxSize) { if (idx >= (int)maxSize)
{
idx += 4; idx += 4;
return defaultValue; return defaultValue;
} }
uint32_t value = uint32_t value = a[idx] << 24 | a[idx + 1] << 16 | a[idx + 2] << 8 | a[idx + 3];
a[idx] << 24 | a[idx + 1] << 16 | a[idx + 2] << 8 | a[idx + 3];
idx += 4; idx += 4;
return value; return value;
} }
float readFloat(uint8_t *a, int &idx, uint32_t maxSize, float defaultValue) { float readFloat(uint8_t* a, int& idx, uint32_t maxSize, float defaultValue)
uint32_t bytes = readWord(a, idx, maxSize, *(uint32_t *)(&defaultValue)); {
return *(float *)(&bytes); 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; int idx = 0;
// Read version number // Read version number
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++)
{
game->version[i] = readByte(data, idx, maxSize, 0); game->version[i] = readByte(data, idx, maxSize, 0);
} }
// Read entire game info header // Read entire game info header
for (int i = 0; i < GAME_INFO_HEADER_SIZE; i++) { for (int i = 0; i < GAME_INFO_HEADER_SIZE; i++)
{
game->settings.header[i] = readWord(data, idx, maxSize, 0); game->settings.header[i] = readWord(data, idx, maxSize, 0);
} }
@ -68,15 +75,18 @@ void handleGameInit(Game *game, uint32_t maxSize) {
// Read UCF toggle bytes // Read UCF toggle bytes
bool shouldRead = game->version[0] >= 1; bool shouldRead = game->version[0] >= 1;
for (int i = 0; i < UCF_TOGGLE_SIZE; i++) { for (int i = 0; i < UCF_TOGGLE_SIZE; i++)
{
uint32_t value = shouldRead ? readWord(data, idx, maxSize, 0) : 0; uint32_t value = shouldRead ? readWord(data, idx, maxSize, 0) : 0;
game->settings.ucfToggles[i] = value; game->settings.ucfToggles[i] = value;
} }
// Read nametag for each player // Read nametag for each player
std::array<std::array<uint16_t, NAMETAG_SIZE>, 4> playerNametags; std::array<std::array<uint16_t, NAMETAG_SIZE>, 4> playerNametags;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++)
for (int j = 0; j < NAMETAG_SIZE; j++) { {
for (int j = 0; j < NAMETAG_SIZE; j++)
{
playerNametags[i][j] = readHalf(data, idx, maxSize, 0); playerNametags[i][j] = readHalf(data, idx, maxSize, 0);
} }
} }
@ -88,17 +98,18 @@ void handleGameInit(Game *game, uint32_t maxSize) {
game->settings.isFrozenPS = readByte(data, idx, maxSize, 0); game->settings.isFrozenPS = readByte(data, idx, maxSize, 0);
// Pull header data into struct // Pull header data into struct
int player1Pos = 24; // This is the index of the first players character info int player1Pos = 24; // This is the index of the first players character info
std::array<uint32_t, Slippi::GAME_INFO_HEADER_SIZE> gameInfoHeader = std::array<uint32_t, Slippi::GAME_INFO_HEADER_SIZE> gameInfoHeader = game->settings.header;
game->settings.header; for (int i = 0; i < 4; i++)
for (int i = 0; i < 4; i++) { {
// this is the position in the array that this player's character info is // this is the position in the array that this player's character info is
// stored // stored
int pos = player1Pos + (9 * i); int pos = player1Pos + (9 * i);
uint32_t playerInfo = gameInfoHeader[pos]; uint32_t playerInfo = gameInfoHeader[pos];
uint8_t playerType = (playerInfo & 0x00FF0000) >> 16; uint8_t playerType = (playerInfo & 0x00FF0000) >> 16;
if (playerType == 0x3) { if (playerType == 0x3)
{
// Player type 3 is an empty slot // Player type 3 is an empty slot
continue; continue;
} }
@ -120,12 +131,15 @@ void handleGameInit(Game *game, uint32_t maxSize) {
auto majorVersion = game->version[0]; auto majorVersion = game->version[0];
auto minorVersion = game->version[1]; auto minorVersion = game->version[1];
if (majorVersion > 3 || (majorVersion == 3 && minorVersion >= 1)) { if (majorVersion > 3 || (majorVersion == 3 && minorVersion >= 1))
{
// After version 3.1.0 we added a dynamic gecko loading process. These // After version 3.1.0 we added a dynamic gecko loading process. These
// are needed before starting the game. areSettingsLoaded will be set // are needed before starting the game. areSettingsLoaded will be set
// to true when they are received // to true when they are received
game->areSettingsLoaded = false; game->areSettingsLoaded = false;
} else if (majorVersion > 1 || (majorVersion == 1 && minorVersion >= 6)) { }
else if (majorVersion > 1 || (majorVersion == 1 && minorVersion >= 6))
{
// Indicate settings loaded immediately if after version 1.6.0 // Indicate settings loaded immediately if after version 1.6.0
// Sheik game info was added in this version and so we no longer // Sheik game info was added in this version and so we no longer
// need to wait // need to wait
@ -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.clear();
game->settings.geckoCodes.insert(game->settings.geckoCodes.end(), data, game->settings.geckoCodes.insert(game->settings.geckoCodes.end(), data, data + maxSize);
data + maxSize);
// File is good to load // File is good to load
game->areSettingsLoaded = true; game->areSettingsLoaded = true;
} }
void handleFrameStart(Game *game, uint32_t maxSize) { void handleFrameStart(Game* game, uint32_t maxSize)
{
int idx = 0; int idx = 0;
// Check frame count // Check frame count
@ -150,7 +165,7 @@ void handleFrameStart(Game *game, uint32_t maxSize) {
game->frameCount = frameCount; game->frameCount = frameCount;
auto frameUniquePtr = std::make_unique<FrameData>(); auto frameUniquePtr = std::make_unique<FrameData>();
FrameData *frame = frameUniquePtr.get(); FrameData* frame = frameUniquePtr.get();
frame->frame = frameCount; frame->frame = frameCount;
frame->randomSeedExists = true; frame->randomSeedExists = true;
@ -163,7 +178,8 @@ void handleFrameStart(Game *game, uint32_t maxSize) {
game->framesByIndex[frameCount] = frame; game->framesByIndex[frameCount] = frame;
} }
void handlePreFrameUpdate(Game *game, uint32_t maxSize) { void handlePreFrameUpdate(Game* game, uint32_t maxSize)
{
int idx = 0; int idx = 0;
// Check frame count // Check frame count
@ -171,10 +187,11 @@ void handlePreFrameUpdate(Game *game, uint32_t maxSize) {
game->frameCount = frameCount; game->frameCount = frameCount;
auto frameUniquePtr = std::make_unique<FrameData>(); auto frameUniquePtr = std::make_unique<FrameData>();
FrameData *frame = frameUniquePtr.get(); FrameData* frame = frameUniquePtr.get();
bool isNewFrame = true; bool isNewFrame = true;
if (game->framesByIndex.count(frameCount)) { if (game->framesByIndex.count(frameCount))
{
// If this frame already exists, get the current frame // If this frame already exists, get the current frame
frame = game->frames.back().get(); frame = game->frames.back().get();
isNewFrame = false; isNewFrame = false;
@ -209,36 +226,40 @@ void handlePreFrameUpdate(Game *game, uint32_t maxSize) {
p.lTrigger = readFloat(data, idx, maxSize, 0); p.lTrigger = readFloat(data, idx, maxSize, 0);
p.rTrigger = readFloat(data, idx, maxSize, 0); p.rTrigger = readFloat(data, idx, maxSize, 0);
if (asmEvents[EVENT_PRE_FRAME_UPDATE] >= 59) { if (asmEvents[EVENT_PRE_FRAME_UPDATE] >= 59)
{
p.joystickXRaw = readByte(data, idx, maxSize, 0); p.joystickXRaw = readByte(data, idx, maxSize, 0);
} }
uint32_t noPercent = 0xFFFFFFFF; 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 // Add player data to frame
std::unordered_map<uint8_t, PlayerFrameData> *target; std::unordered_map<uint8_t, PlayerFrameData>* target;
target = isFollower ? &frame->followers : &frame->players; target = isFollower ? &frame->followers : &frame->players;
// Set the player data for the player or follower // Set the player data for the player or follower
target->operator[](playerSlot) = p; target->operator[](playerSlot) = p;
// Add frame to game // Add frame to game
if (isNewFrame) { if (isNewFrame)
{
frame->numSinceStart = game->frames.size(); frame->numSinceStart = game->frames.size();
game->frames.push_back(std::move(frameUniquePtr)); game->frames.push_back(std::move(frameUniquePtr));
game->framesByIndex[frameCount] = frame; game->framesByIndex[frameCount] = frame;
} }
} }
void handlePostFrameUpdate(Game *game, uint32_t maxSize) { void handlePostFrameUpdate(Game* game, uint32_t maxSize)
{
int idx = 0; int idx = 0;
// Check frame count // Check frame count
int32_t frameCount = readWord(data, idx, maxSize, 0); int32_t frameCount = readWord(data, idx, maxSize, 0);
FrameData *frame; FrameData* frame;
if (game->framesByIndex.count(frameCount)) { if (game->framesByIndex.count(frameCount))
{
// If this frame already exists, get the current frame // If this frame already exists, get the current frame
frame = game->frames.back().get(); frame = game->frames.back().get();
} }
@ -251,51 +272,58 @@ void handlePostFrameUpdate(Game *game, uint32_t maxSize) {
uint8_t playerSlot = readByte(data, idx, maxSize, 0); uint8_t playerSlot = readByte(data, idx, maxSize, 0);
uint8_t isFollower = readByte(data, idx, maxSize, 0); uint8_t isFollower = readByte(data, idx, maxSize, 0);
PlayerFrameData *p = PlayerFrameData* p = isFollower ? &frame->followers[playerSlot] : &frame->players[playerSlot];
isFollower ? &frame->followers[playerSlot] : &frame->players[playerSlot];
p->internalCharacterId = readByte(data, idx, maxSize, 0); p->internalCharacterId = readByte(data, idx, maxSize, 0);
// Check if a player started as sheik and update // Check if a player started as sheik and update
if (frameCount == GAME_FIRST_FRAME && if (frameCount == GAME_FIRST_FRAME && p->internalCharacterId == GAME_SHEIK_INTERNAL_ID)
p->internalCharacterId == GAME_SHEIK_INTERNAL_ID) { {
game->settings.players[playerSlot].characterId = GAME_SHEIK_EXTERNAL_ID; game->settings.players[playerSlot].characterId = GAME_SHEIK_EXTERNAL_ID;
} }
// Set settings loaded if this is the last character // Set settings loaded if this is the last character
if (frameCount == GAME_FIRST_FRAME) { if (frameCount == GAME_FIRST_FRAME)
{
uint8_t lastPlayerIndex = 0; uint8_t lastPlayerIndex = 0;
for (auto it = frame->players.begin(); it != frame->players.end(); ++it) { for (auto it = frame->players.begin(); it != frame->players.end(); ++it)
if (it->first <= lastPlayerIndex) { {
if (it->first <= lastPlayerIndex)
{
continue; continue;
} }
lastPlayerIndex = it->first; lastPlayerIndex = it->first;
} }
if (playerSlot >= lastPlayerIndex) { if (playerSlot >= lastPlayerIndex)
{
game->areSettingsLoaded = true; game->areSettingsLoaded = true;
} }
} }
} }
void handleGameEnd(Game *game, uint32_t maxSize) { void handleGameEnd(Game* game, uint32_t maxSize)
{
int idx = 0; int idx = 0;
game->winCondition = readByte(data, idx, maxSize, 0); game->winCondition = readByte(data, idx, maxSize, 0);
} }
// This function gets the position where the raw data starts // This function gets the position where the raw data starts
int getRawDataPosition(std::ifstream *f) { int getRawDataPosition(std::ifstream* f)
{
char buffer[2]; char buffer[2];
f->seekg(0, std::ios::beg); f->seekg(0, std::ios::beg);
f->read(buffer, 2); f->read(buffer, 2);
if (buffer[0] == 0x36) { if (buffer[0] == 0x36)
{
return 0; return 0;
} }
if (buffer[0] != '{') { if (buffer[0] != '{')
{
// TODO: Do something here to cause an error // TODO: Do something here to cause an error
return 0; return 0;
} }
@ -306,8 +334,10 @@ int getRawDataPosition(std::ifstream *f) {
return 15; return 15;
} }
uint32_t getRawDataLength(std::ifstream *f, int position, int fileSize) { uint32_t getRawDataLength(std::ifstream* f, int position, int fileSize)
if (position == 0) { {
if (position == 0)
{
return fileSize; return fileSize;
} }
@ -315,28 +345,28 @@ uint32_t getRawDataLength(std::ifstream *f, int position, int fileSize) {
f->seekg(position - 4, std::ios::beg); f->seekg(position - 4, std::ios::beg);
f->read(buffer, 4); f->read(buffer, 4);
uint8_t *byteBuf = (uint8_t *)&buffer[0]; uint8_t* byteBuf = (uint8_t*)&buffer[0];
uint32_t length = uint32_t length = byteBuf[0] << 24 | byteBuf[1] << 16 | byteBuf[2] << 8 | byteBuf[3];
byteBuf[0] << 24 | byteBuf[1] << 16 | byteBuf[2] << 8 | byteBuf[3];
return length; return length;
} }
std::unordered_map<uint8_t, uint32_t> getMessageSizes(std::ifstream *f, std::unordered_map<uint8_t, uint32_t> getMessageSizes(std::ifstream* f, int position)
int position) { {
char buffer[2]; char buffer[2];
f->seekg(position, std::ios::beg); f->seekg(position, std::ios::beg);
f->read(buffer, 2); f->read(buffer, 2);
if (buffer[0] != EVENT_PAYLOAD_SIZES) { if (buffer[0] != EVENT_PAYLOAD_SIZES)
{
return {}; return {};
} }
int payloadLength = buffer[1]; int payloadLength = buffer[1];
std::unordered_map<uint8_t, uint32_t> messageSizes = { std::unordered_map<uint8_t, uint32_t> messageSizes = {{EVENT_PAYLOAD_SIZES, payloadLength}};
{EVENT_PAYLOAD_SIZES, payloadLength}};
std::vector<char> messageSizesBuffer(payloadLength - 1); std::vector<char> messageSizesBuffer(payloadLength - 1);
f->read(&messageSizesBuffer[0], payloadLength - 1); f->read(&messageSizesBuffer[0], payloadLength - 1);
for (int i = 0; i < payloadLength - 1; i += 3) { for (int i = 0; i < payloadLength - 1; i += 3)
{
uint8_t command = messageSizesBuffer[i]; uint8_t command = messageSizesBuffer[i];
// Extract the bytes in u8s. Without this the chars don't or together well // Extract the bytes in u8s. Without this the chars don't or together well
@ -350,8 +380,10 @@ std::unordered_map<uint8_t, uint32_t> getMessageSizes(std::ifstream *f,
return messageSizes; return messageSizes;
} }
void SlippiGame::processData() { void SlippiGame::processData()
if (isProcessingComplete) { {
if (isProcessingComplete)
{
// If we have finished processing this file, return // If we have finished processing this file, return
return; return;
} }
@ -359,17 +391,20 @@ void SlippiGame::processData() {
// This function will process as much data as possible // This function will process as much data as possible
int startPos = (int)file->tellg(); int startPos = (int)file->tellg();
file->seekg(startPos); file->seekg(startPos);
if (startPos == 0) { if (startPos == 0)
{
file->seekg(0, std::ios::end); file->seekg(0, std::ios::end);
int len = (int)file->tellg(); int len = (int)file->tellg();
if (len < 2) { if (len < 2)
{
// If we can't read message sizes payload size yet, return // If we can't read message sizes payload size yet, return
return; return;
} }
int rawDataPos = getRawDataPosition(file.get()); int rawDataPos = getRawDataPosition(file.get());
int rawDataLen = len - rawDataPos; int rawDataLen = len - rawDataPos;
if (rawDataLen < 2) { if (rawDataLen < 2)
{
// If we don't have enough raw data yet to read the replay file, return // If we don't have enough raw data yet to read the replay file, return
// Reset to begining so that the startPos condition will be hit again // Reset to begining so that the startPos condition will be hit again
file->seekg(0); file->seekg(0);
@ -383,7 +418,8 @@ void SlippiGame::processData() {
file->read(buffer, 2); file->read(buffer, 2);
file->seekg(startPos); file->seekg(startPos);
auto messageSizesSize = (int)buffer[1]; auto messageSizesSize = (int)buffer[1];
if (rawDataLen < messageSizesSize) { if (rawDataLen < messageSizesSize)
{
// If we haven't received the full payload sizes message, return // If we haven't received the full payload sizes message, return
// Reset to begining so that the startPos condition will be hit again // Reset to begining so that the startPos condition will be hit again
file->seekg(0); file->seekg(0);
@ -401,7 +437,8 @@ void SlippiGame::processData() {
// log << "Size to read: " << sizeToRead << "\n"; // log << "Size to read: " << sizeToRead << "\n";
// log << "Start Pos: " << startPos << "\n"; // log << "Start Pos: " << startPos << "\n";
// log << "End Pos: " << endPos << "\n\n"; // log << "End Pos: " << endPos << "\n\n";
if (sizeToRead <= 0) { if (sizeToRead <= 0)
{
return; return;
} }
@ -409,7 +446,8 @@ void SlippiGame::processData() {
file->read(&newData[0], sizeToRead); file->read(&newData[0], sizeToRead);
int newDataPos = 0; int newDataPos = 0;
while (newDataPos < sizeToRead) { while (newDataPos < sizeToRead)
{
auto command = newData[newDataPos]; auto command = newData[newDataPos];
auto payloadSize = asmEvents[command]; auto payloadSize = asmEvents[command];
@ -418,32 +456,35 @@ void SlippiGame::processData() {
// log << "Command: " << buff << " | Payload Size: " << payloadSize << "\n"; // log << "Command: " << buff << " | Payload Size: " << payloadSize << "\n";
auto remainingLen = sizeToRead - newDataPos; auto remainingLen = sizeToRead - newDataPos;
if (remainingLen < ((int)payloadSize + 1)) { if (remainingLen < ((int)payloadSize + 1))
{
// Here we don't have enough data to read the whole payload // Here we don't have enough data to read the whole payload
// Will be processed after getting more data (hopefully) // Will be processed after getting more data (hopefully)
file->seekg(-remainingLen, std::ios::cur); file->seekg(-remainingLen, std::ios::cur);
return; return;
} }
data = (uint8_t *)&newData[newDataPos + 1]; data = (uint8_t*)&newData[newDataPos + 1];
uint8_t isSplitComplete = false; uint8_t isSplitComplete = false;
uint32_t outerPayloadSize = payloadSize; uint32_t outerPayloadSize = payloadSize;
// Handle a split message, combining in until we possess the entire message // Handle a split message, combining in until we possess the entire message
if (command == EVENT_SPLIT_MESSAGE) { if (command == EVENT_SPLIT_MESSAGE)
if (shouldResetSplitMessageBuf) { {
if (shouldResetSplitMessageBuf)
{
splitMessageBuf.clear(); splitMessageBuf.clear();
shouldResetSplitMessageBuf = false; shouldResetSplitMessageBuf = false;
} }
int _ = 0; int _ = 0;
uint16_t blockSize = uint16_t blockSize = readHalf(&data[SPLIT_MESSAGE_INTERNAL_DATA_LEN], _, payloadSize, 0);
readHalf(&data[SPLIT_MESSAGE_INTERNAL_DATA_LEN], _, payloadSize, 0);
splitMessageBuf.insert(splitMessageBuf.end(), data, data + blockSize); splitMessageBuf.insert(splitMessageBuf.end(), data, data + blockSize);
isSplitComplete = data[SPLIT_MESSAGE_INTERNAL_DATA_LEN + 3]; isSplitComplete = data[SPLIT_MESSAGE_INTERNAL_DATA_LEN + 3];
if (isSplitComplete) { if (isSplitComplete)
{
// Transform this message into a different message // Transform this message into a different message
command = data[SPLIT_MESSAGE_INTERNAL_DATA_LEN + 2]; command = data[SPLIT_MESSAGE_INTERNAL_DATA_LEN + 2];
data = &splitMessageBuf[0]; data = &splitMessageBuf[0];
@ -452,7 +493,8 @@ void SlippiGame::processData() {
} }
} }
switch (command) { switch (command)
{
case EVENT_GAME_INIT: case EVENT_GAME_INIT:
handleGameInit(game.get(), payloadSize); handleGameInit(game.get(), payloadSize);
break; break;
@ -488,24 +530,23 @@ void SlippiGame::processData() {
} }
} }
std::unique_ptr<SlippiGame> SlippiGame::FromFile(std::string path) { std::unique_ptr<SlippiGame> SlippiGame::FromFile(std::string path)
{
auto result = std::make_unique<SlippiGame>(); auto result = std::make_unique<SlippiGame>();
result->game = std::make_unique<Game>(); result->game = std::make_unique<Game>();
result->path = path; result->path = path;
#ifdef _WIN32 #ifdef _WIN32
// On Windows, we need to convert paths to std::wstring to deal with UTF-8 // On Windows, we need to convert paths to std::wstring to deal with UTF-8
std::wstring convertedPath = std::wstring convertedPath = std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(path);
std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(path); result->file = std::make_unique<std::ifstream>(convertedPath, std::ios::in | std::ios::binary);
result->file = std::make_unique<std::ifstream>(
convertedPath, std::ios::in | std::ios::binary);
#else #else
result->file = result->file = std::make_unique<std::ifstream>(path, std::ios::in | std::ios::binary);
std::make_unique<std::ifstream>(path, std::ios::in | std::ios::binary);
#endif #endif
// result->log.open("log.txt"); // result->log.open("log.txt");
if (!result->file->is_open()) { if (!result->file->is_open())
{
return nullptr; return nullptr;
} }
@ -523,27 +564,38 @@ std::unique_ptr<SlippiGame> SlippiGame::FromFile(std::string path) {
return std::move(result); return std::move(result);
} }
bool SlippiGame::IsProcessingComplete() { return isProcessingComplete; } bool SlippiGame::IsProcessingComplete()
{
return isProcessingComplete;
}
bool SlippiGame::AreSettingsLoaded() { bool SlippiGame::AreSettingsLoaded()
{
processData(); processData();
return game->areSettingsLoaded; return game->areSettingsLoaded;
} }
bool SlippiGame::DoesFrameExist(int32_t frame) { bool SlippiGame::DoesFrameExist(int32_t frame)
{
processData(); processData();
return (bool)game->framesByIndex.count(frame); return (bool)game->framesByIndex.count(frame);
} }
std::array<uint8_t, 4> SlippiGame::GetVersion() { return game->version; } std::array<uint8_t, 4> SlippiGame::GetVersion()
{
return game->version;
}
FrameData *SlippiGame::GetFrame(int32_t frame) { FrameData* SlippiGame::GetFrame(int32_t frame)
{
// Get the frame we want // Get the frame we want
return game->framesByIndex.at(frame); return game->framesByIndex.at(frame);
} }
FrameData *SlippiGame::GetFrameAt(uint32_t pos) { FrameData* SlippiGame::GetFrameAt(uint32_t pos)
if (pos >= game->frames.size()) { {
if (pos >= game->frames.size())
{
return nullptr; return nullptr;
} }
@ -551,17 +603,20 @@ FrameData *SlippiGame::GetFrameAt(uint32_t pos) {
return game->frames[pos].get(); return game->frames[pos].get();
} }
int32_t SlippiGame::GetLatestIndex() { int32_t SlippiGame::GetLatestIndex()
{
processData(); processData();
return game->frameCount; return game->frameCount;
} }
GameSettings *SlippiGame::GetSettings() { GameSettings* SlippiGame::GetSettings()
{
processData(); processData();
return &game->settings; return &game->settings;
} }
bool SlippiGame::DoesPlayerExist(int8_t port) { bool SlippiGame::DoesPlayerExist(int8_t port)
{
return game->settings.players.find(port) != game->settings.players.end(); return game->settings.players.find(port) != game->settings.players.end();
} }
} // namespace Slippi } // namespace Slippi

View file

@ -8,8 +8,8 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
namespace Slippi { namespace Slippi
{
const uint8_t EVENT_SPLIT_MESSAGE = 0x10; const uint8_t EVENT_SPLIT_MESSAGE = 0x10;
const uint8_t EVENT_PAYLOAD_SIZES = 0x35; const uint8_t EVENT_PAYLOAD_SIZES = 0x35;
const uint8_t EVENT_GAME_INIT = 0x36; 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; 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 // Every player update has its own rng seed because it might change in between
// players // players
uint32_t randomSeed; uint32_t randomSeed;
@ -54,19 +55,20 @@ typedef struct {
float cstickX; float cstickX;
float cstickY; float cstickY;
float trigger; float trigger;
uint32_t buttons; // This will include multiple "buttons" pressed on special uint32_t buttons; // This will include multiple "buttons" pressed on special
// buttons. For example I think pressing z sets 3 bits // buttons. For example I think pressing z sets 3 bits
// This is extra controller information // This is extra controller information
uint16_t physicalButtons; // A better representation of what a player is uint16_t physicalButtons; // A better representation of what a player is
// actually pressing // actually pressing
float lTrigger; float lTrigger;
float rTrigger; float rTrigger;
uint8_t joystickXRaw; uint8_t joystickXRaw;
} PlayerFrameData; } PlayerFrameData;
typedef struct FrameData { typedef struct FrameData
{
int32_t frame; int32_t frame;
uint32_t numSinceStart; uint32_t numSinceStart;
bool randomSeedExists = false; bool randomSeedExists = false;
@ -76,7 +78,8 @@ typedef struct FrameData {
std::unordered_map<uint8_t, PlayerFrameData> followers; std::unordered_map<uint8_t, PlayerFrameData> followers;
} FrameData; } FrameData;
typedef struct { typedef struct
{
// Static data // Static data
uint8_t characterId; uint8_t characterId;
uint8_t characterColor; uint8_t characterColor;
@ -85,8 +88,9 @@ typedef struct {
std::array<uint16_t, NAMETAG_SIZE> nametag; std::array<uint16_t, NAMETAG_SIZE> nametag;
} PlayerSettings; } PlayerSettings;
typedef struct { typedef struct
uint16_t stage; // Stage ID {
uint16_t stage; // Stage ID
uint32_t randomSeed; uint32_t randomSeed;
std::array<uint32_t, GAME_INFO_HEADER_SIZE> header; std::array<uint32_t, GAME_INFO_HEADER_SIZE> header;
std::array<uint32_t, UCF_TOGGLE_SIZE> ucfToggles; std::array<uint32_t, UCF_TOGGLE_SIZE> ucfToggles;
@ -96,14 +100,15 @@ typedef struct {
std::vector<uint8_t> geckoCodes; std::vector<uint8_t> geckoCodes;
} GameSettings; } GameSettings;
typedef struct Game { typedef struct Game
{
std::array<uint8_t, 4> version; std::array<uint8_t, 4> version;
std::unordered_map<int32_t, FrameData *> framesByIndex; std::unordered_map<int32_t, FrameData*> framesByIndex;
std::vector<std::unique_ptr<FrameData>> frames; std::vector<std::unique_ptr<FrameData>> frames;
GameSettings settings; GameSettings settings;
bool areSettingsLoaded = false; bool areSettingsLoaded = false;
int32_t frameCount; // Current/last frame count int32_t frameCount; // Current/last frame count
// From OnGameEnd event // From OnGameEnd event
uint8_t winCondition; uint8_t winCondition;
@ -111,23 +116,23 @@ typedef struct Game {
// TODO: This shouldn't be static. Doesn't matter too much atm because we always // TODO: This shouldn't be static. Doesn't matter too much atm because we always
// TODO: only read one file at a time // TODO: only read one file at a time
static std::unordered_map<uint8_t, uint32_t> asmEvents = { static std::unordered_map<uint8_t, uint32_t> asmEvents = {{EVENT_GAME_INIT, 320},
{EVENT_GAME_INIT, 320}, {EVENT_PRE_FRAME_UPDATE, 58},
{EVENT_PRE_FRAME_UPDATE, 58}, {EVENT_POST_FRAME_UPDATE, 33},
{EVENT_POST_FRAME_UPDATE, 33}, {EVENT_GAME_END, 1},
{EVENT_GAME_END, 1}, {EVENT_FRAME_START, 8}};
{EVENT_FRAME_START, 8}};
class SlippiGame { class SlippiGame
{
public: public:
static std::unique_ptr<SlippiGame> FromFile(std::string path); static std::unique_ptr<SlippiGame> FromFile(std::string path);
bool AreSettingsLoaded(); bool AreSettingsLoaded();
bool DoesFrameExist(int32_t frame); bool DoesFrameExist(int32_t frame);
std::array<uint8_t, 4> GetVersion(); std::array<uint8_t, 4> GetVersion();
FrameData *GetFrame(int32_t frame); FrameData* GetFrame(int32_t frame);
FrameData *GetFrameAt(uint32_t pos); FrameData* GetFrameAt(uint32_t pos);
int32_t GetLatestIndex(); int32_t GetLatestIndex();
GameSettings *GetSettings(); GameSettings* GetSettings();
bool DoesPlayerExist(int8_t port); bool DoesPlayerExist(int8_t port);
bool IsProcessingComplete(); bool IsProcessingComplete();
@ -143,4 +148,4 @@ private:
bool isProcessingComplete = false; bool isProcessingComplete = false;
void processData(); void processData();
}; };
} // namespace Slippi } // namespace Slippi