mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-22 10:19:01 +00:00
Fix formatting for External SlippiLib files
This commit is contained in:
parent
b192600c6c
commit
e6f721f5c0
2 changed files with 187 additions and 127 deletions
257
Externals/SlippiLib/SlippiGame.cpp
vendored
257
Externals/SlippiLib/SlippiGame.cpp
vendored
|
@ -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
|
||||||
|
|
57
Externals/SlippiLib/SlippiGame.h
vendored
57
Externals/SlippiLib/SlippiGame.h
vendored
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue