mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-26 12:16:20 +00:00
commit
a2f24e8960
19 changed files with 4606 additions and 2137 deletions
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
BIN
Data/Sys/bootloader.gct
Normal file
BIN
Data/Sys/bootloader.gct
Normal file
Binary file not shown.
|
@ -128,6 +128,7 @@
|
||||||
#define WII_SETTING "setting.txt"
|
#define WII_SETTING "setting.txt"
|
||||||
|
|
||||||
#define GECKO_CODE_HANDLER "codehandler.bin"
|
#define GECKO_CODE_HANDLER "codehandler.bin"
|
||||||
|
#define GCT_BOOTLOADER "bootloader.gct"
|
||||||
|
|
||||||
// Subdirs in Sys
|
// Subdirs in Sys
|
||||||
#define GC_SYS_DIR "GC"
|
#define GC_SYS_DIR "GC"
|
||||||
|
|
|
@ -26,13 +26,13 @@
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <ShlObj.h>
|
||||||
#include <Shlwapi.h>
|
#include <Shlwapi.h>
|
||||||
#include <commdlg.h> // for GetSaveFileName
|
#include <commdlg.h> // for GetSaveFileName
|
||||||
#include <direct.h> // getcwd
|
#include <direct.h> // getcwd
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
#include <objbase.h> // guid stuff
|
#include <objbase.h> // guid stuff
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#include <ShlObj.h>
|
|
||||||
#include <winerror.h>
|
#include <winerror.h>
|
||||||
#else
|
#else
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
@ -791,21 +791,22 @@ std::string GetExePath()
|
||||||
return dolphin_path;
|
return dolphin_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SLIPPITODO: refactor with c++17 std::filesystem?
|
|
||||||
std::string GetHomeDirectory()
|
std::string GetHomeDirectory()
|
||||||
{
|
{
|
||||||
std::string homeDir;
|
std::string homeDir;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
wchar_t* path = nullptr;
|
wchar_t* path = nullptr;
|
||||||
|
|
||||||
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, nullptr, &path))) {
|
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, nullptr, &path)))
|
||||||
|
{
|
||||||
char pathStr[MAX_PATH];
|
char pathStr[MAX_PATH];
|
||||||
wcstombs(pathStr, path, MAX_PATH);
|
wcstombs(pathStr, path, MAX_PATH);
|
||||||
|
|
||||||
homeDir = std::string(pathStr);
|
homeDir = std::string(pathStr);
|
||||||
CoTaskMemFree(path);
|
CoTaskMemFree(path);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
const char* home = getenv("USERPROFILE");
|
const char* home = getenv("USERPROFILE");
|
||||||
homeDir = std::string(home) + "\\Documents";
|
homeDir = std::string(home) + "\\Documents";
|
||||||
}
|
}
|
||||||
|
@ -853,13 +854,15 @@ std::string GetSysDirectory()
|
||||||
ASSERT_MSG(COMMON, !sysDir.empty(), "Sys directory has not been set");
|
ASSERT_MSG(COMMON, !sysDir.empty(), "Sys directory has not been set");
|
||||||
#else
|
#else
|
||||||
const char* home = getenv("HOME");
|
const char* home = getenv("HOME");
|
||||||
if (!home) home = getenv("PWD");
|
if (!home)
|
||||||
if (!home) home = "";
|
home = getenv("PWD");
|
||||||
|
if (!home)
|
||||||
|
home = "";
|
||||||
std::string home_path = std::string(home) + DIR_SEP;
|
std::string home_path = std::string(home) + DIR_SEP;
|
||||||
const char* config_home = getenv("XDG_CONFIG_HOME");
|
const char* config_home = getenv("XDG_CONFIG_HOME");
|
||||||
sysDir = std::string(config_home && config_home[0] == '/'
|
sysDir =
|
||||||
? config_home : (home_path + ".config"))
|
std::string(config_home && config_home[0] == '/' ? config_home : (home_path + ".config")) +
|
||||||
+ DIR_SEP DOLPHIN_DATA_DIR DIR_SEP "Sys" DIR_SEP;
|
DIR_SEP DOLPHIN_DATA_DIR DIR_SEP "Sys" DIR_SEP;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
INFO_LOG_FMT(COMMON, "GetSysDirectory: Setting to {}:", sysDir);
|
INFO_LOG_FMT(COMMON, "GetSysDirectory: Setting to {}:", sysDir);
|
||||||
|
|
|
@ -41,6 +41,7 @@ namespace fs = std::filesystem;
|
||||||
#include "Core/Config/SYSCONFSettings.h"
|
#include "Core/Config/SYSCONFSettings.h"
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/FifoPlayer/FifoPlayer.h"
|
#include "Core/FifoPlayer/FifoPlayer.h"
|
||||||
|
#include "Core/GeckoCode.h"
|
||||||
#include "Core/HLE/HLE.h"
|
#include "Core/HLE/HLE.h"
|
||||||
#include "Core/HW/DVD/DVDInterface.h"
|
#include "Core/HW/DVD/DVDInterface.h"
|
||||||
#include "Core/HW/EXI/EXI_DeviceIPL.h"
|
#include "Core/HW/EXI/EXI_DeviceIPL.h"
|
||||||
|
@ -537,6 +538,7 @@ bool CBoot::BootUp(std::unique_ptr<BootParameters> boot)
|
||||||
|
|
||||||
PatchEngine::LoadPatches();
|
PatchEngine::LoadPatches();
|
||||||
HLE::PatchFixedFunctions();
|
HLE::PatchFixedFunctions();
|
||||||
|
Gecko::RunCodeHandler();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
#include "VideoCommon/HiresTextures.h"
|
#include "VideoCommon/HiresTextures.h"
|
||||||
|
|
||||||
#include "DiscIO/Enums.h"
|
#include "DiscIO/Enums.h"
|
||||||
|
#include "DiscIO/Filesystem.h"
|
||||||
#include "DiscIO/Volume.h"
|
#include "DiscIO/Volume.h"
|
||||||
#include "DiscIO/VolumeWad.h"
|
#include "DiscIO/VolumeWad.h"
|
||||||
|
|
||||||
|
@ -959,7 +960,32 @@ bool SConfig::SetPathsAndGameMetadata(const BootParameters& boot)
|
||||||
const std::string region_dir = GetDirectoryForRegion(ToGameCubeRegion(m_region));
|
const std::string region_dir = GetDirectoryForRegion(ToGameCubeRegion(m_region));
|
||||||
m_strSRAM = File::GetUserPath(F_GCSRAM_IDX);
|
m_strSRAM = File::GetUserPath(F_GCSRAM_IDX);
|
||||||
m_strBootROM = GetBootROMPath(region_dir);
|
m_strBootROM = GetBootROMPath(region_dir);
|
||||||
m_strIsoPath = (boot.parameters.index() == 0) ? std::get<BootParameters::Disc>(boot.parameters).path : "";
|
m_strIsoPath =
|
||||||
|
(boot.parameters.index() == 0) ? std::get<BootParameters::Disc>(boot.parameters).path : "";
|
||||||
|
|
||||||
|
std::shared_ptr<DiscIO::Volume> volume = DiscIO::CreateVolume(m_strIsoPath);
|
||||||
|
|
||||||
|
if (m_game_id == "GALE01" || m_game_id == "GALJ01")
|
||||||
|
{
|
||||||
|
m_melee_version = Melee::Version::NTSC;
|
||||||
|
|
||||||
|
if (volume->GetLongNames()[DiscIO::Language::English].find("20XX") != std::string::npos)
|
||||||
|
m_melee_version = Melee::Version::TwentyXX;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// check for m-ex based build
|
||||||
|
if (volume->FileExists("MxDt.dat"))
|
||||||
|
{
|
||||||
|
m_melee_version = Melee::Version::MEX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (m_game_id == "GTME01")
|
||||||
|
{
|
||||||
|
m_melee_version = Melee::Version::UPTM;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO_LOG_FMT(BOOT, "Melee Version: {}", m_melee_version);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <future>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
@ -45,6 +46,18 @@ namespace SerialInterface
|
||||||
enum SIDevices : int;
|
enum SIDevices : int;
|
||||||
} // namespace SerialInterface
|
} // namespace SerialInterface
|
||||||
|
|
||||||
|
namespace Melee
|
||||||
|
{
|
||||||
|
enum class Version
|
||||||
|
{
|
||||||
|
NTSC,
|
||||||
|
TwentyXX,
|
||||||
|
UPTM,
|
||||||
|
MEX,
|
||||||
|
OTHER,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
struct BootParameters;
|
struct BootParameters;
|
||||||
|
|
||||||
// DSP Backend Types
|
// DSP Backend Types
|
||||||
|
@ -67,6 +80,8 @@ enum class GPUDeterminismMode
|
||||||
|
|
||||||
struct SConfig
|
struct SConfig
|
||||||
{
|
{
|
||||||
|
// Melee Version
|
||||||
|
Melee::Version m_melee_version;
|
||||||
// Wii Devices
|
// Wii Devices
|
||||||
bool m_WiiSDCard;
|
bool m_WiiSDCard;
|
||||||
bool m_WiiKeyboard;
|
bool m_WiiKeyboard;
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
#include "Core/PowerPC/MMU.h"
|
#include "Core/PowerPC/MMU.h"
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
|
|
||||||
|
#include "VideoCommon/OnScreenDisplay.h"
|
||||||
|
|
||||||
namespace Gecko
|
namespace Gecko
|
||||||
{
|
{
|
||||||
static constexpr u32 CODE_SIZE = 8;
|
static constexpr u32 CODE_SIZE = 8;
|
||||||
|
@ -68,6 +70,8 @@ void SetActiveCodes(const std::vector<GeckoCode>& gcodes)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(s_active_codes_lock);
|
std::lock_guard<std::mutex> lk(s_active_codes_lock);
|
||||||
|
|
||||||
|
DEBUG_LOG_FMT(ACTIONREPLAY, "Setting up active codes...");
|
||||||
|
|
||||||
s_active_codes.clear();
|
s_active_codes.clear();
|
||||||
if (SConfig::GetInstance().bEnableCheats)
|
if (SConfig::GetInstance().bEnableCheats)
|
||||||
{
|
{
|
||||||
|
@ -153,69 +157,84 @@ static Installation InstallCodeHandlerLocked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 codelist_base_address;
|
u32 codelist_base_address = INSTALLER_BASE_ADDRESS + static_cast<u32>(data.length()) - CODE_SIZE;
|
||||||
u32 codelist_end_address;
|
u32 codelist_end_address = INSTALLER_END_ADDRESS;
|
||||||
// Let the Gecko codehandler use free space from Melee's tournament mode region
|
|
||||||
if (SConfig::GetInstance().GetGameID() == "GALE01")
|
// Write a magic value to 'gameid' (codehandleronly does not actually read this).
|
||||||
|
PowerPC::HostWrite_U32(MAGIC_GAMEID, INSTALLER_BASE_ADDRESS);
|
||||||
|
|
||||||
|
// Install the custom bootloader to write gecko codes to the heaps
|
||||||
|
if (SConfig::GetInstance().m_melee_version == Melee::Version::NTSC ||
|
||||||
|
SConfig::GetInstance().m_melee_version == Melee::Version::MEX)
|
||||||
{
|
{
|
||||||
INFO_LOG(ACTIONREPLAY,
|
// Write GCT loader into memory which will eventually load the real GCT into the heap
|
||||||
"Detected GALE01 - using tournament mode region for Gecko codelist");
|
std::string bootloaderData;
|
||||||
|
std::string _bootloaderFilename = File::GetSysDirectory() + GCT_BOOTLOADER;
|
||||||
|
if (!File::ReadFileToString(_bootloaderFilename, bootloaderData))
|
||||||
|
{
|
||||||
|
OSD::AddMessage("bootloader.gct not found in Sys folder.", 30000, 0xFFFF0000);
|
||||||
|
ERROR_LOG_FMT(ACTIONREPLAY, "Could not enable cheats because bootloader.gct was missing.");
|
||||||
|
return Installation::Failed;
|
||||||
|
}
|
||||||
|
|
||||||
// Set codelist base to the tournament mode region
|
if (bootloaderData.length() > codelist_end_address - codelist_base_address)
|
||||||
codelist_base_address = 0x801910E0;
|
{
|
||||||
codelist_end_address = 0x8019AF4C;
|
OSD::AddMessage("Gecko bootloader too large.", 30000, 0xFFFF0000);
|
||||||
|
ERROR_LOG_FMT(SLIPPI, "Gecko bootloader too large");
|
||||||
|
return Installation::Failed;
|
||||||
|
}
|
||||||
|
|
||||||
// Patch codehandler to use tournament mode region
|
// Install bootloader gct
|
||||||
PowerPC::HostWrite_U32(0x3DE08019, 0x80001904); // lis r15, 0x8019
|
for (size_t i = 0; i < bootloaderData.length(); ++i)
|
||||||
PowerPC::HostWrite_U32(0x61EF10E0, 0x80001908); // ori r15, r15, 0x10e0
|
PowerPC::HostWrite_U8(bootloaderData[i], static_cast<u32>(codelist_base_address + i));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
codelist_base_address = INSTALLER_BASE_ADDRESS + static_cast<u32>(data.size()) - CODE_SIZE;
|
// Create GCT in memory
|
||||||
codelist_end_address = INSTALLER_END_ADDRESS;
|
PowerPC::HostWrite_U32(0x00d0c0de, codelist_base_address);
|
||||||
}
|
PowerPC::HostWrite_U32(0x00d0c0de, codelist_base_address + 4);
|
||||||
|
|
||||||
// Write a magic value to 'gameid' (codehandleronly does not actually read this).
|
// Each code is 8 bytes (2 words) wide. There is a starter code and an end code.
|
||||||
// This value will be read back and modified over time by HLE_Misc::GeckoCodeHandlerICacheFlush.
|
const u32 start_address = codelist_base_address + CODE_SIZE;
|
||||||
PowerPC::HostWrite_U32(MAGIC_GAMEID, INSTALLER_BASE_ADDRESS);
|
const u32 end_address = codelist_end_address - CODE_SIZE;
|
||||||
|
u32 next_address = start_address;
|
||||||
|
|
||||||
// Create GCT in memory
|
// NOTE: Only active codes are in the list
|
||||||
PowerPC::HostWrite_U32(0x00d0c0de, codelist_base_address);
|
for (const GeckoCode& active_code : s_active_codes)
|
||||||
PowerPC::HostWrite_U32(0x00d0c0de, codelist_base_address + 4);
|
|
||||||
|
|
||||||
// Each code is 8 bytes (2 words) wide. There is a starter code and an end code.
|
|
||||||
const u32 start_address = codelist_base_address + CODE_SIZE;
|
|
||||||
const u32 end_address = codelist_end_address - CODE_SIZE;
|
|
||||||
u32 next_address = start_address;
|
|
||||||
|
|
||||||
// NOTE: Only active codes are in the list
|
|
||||||
for (const GeckoCode& active_code : s_active_codes)
|
|
||||||
{
|
|
||||||
// If the code is not going to fit in the space we have left then we have to skip it
|
|
||||||
if (next_address + active_code.codes.size() * CODE_SIZE > end_address)
|
|
||||||
{
|
{
|
||||||
NOTICE_LOG_FMT(ACTIONREPLAY,
|
// If the code is not going to fit in the space we have left then we have to skip it
|
||||||
"Too many GeckoCodes! Ran out of storage space in Game RAM. Could "
|
if (next_address + active_code.codes.size() * CODE_SIZE > end_address)
|
||||||
"not write: \"{}\". Need {} bytes, only {} remain.",
|
{
|
||||||
active_code.name, active_code.codes.size() * CODE_SIZE,
|
OSD::AddMessage(
|
||||||
end_address - next_address);
|
fmt::format("Ran out of memory applying gecko codes. Too many codes enabled. "
|
||||||
continue;
|
"Need {} bytes, only {} remain.",
|
||||||
|
active_code.codes.size() * CODE_SIZE, end_address - next_address),
|
||||||
|
30000, 0xFFFF0000);
|
||||||
|
NOTICE_LOG_FMT(ACTIONREPLAY,
|
||||||
|
"Too many GeckoCodes! Ran out of storage space in Game RAM. Could "
|
||||||
|
"not write: \"{}\". Need {} bytes, only {} remain.",
|
||||||
|
active_code.name, active_code.codes.size() * CODE_SIZE,
|
||||||
|
end_address - next_address);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const GeckoCode::Code& code : active_code.codes)
|
||||||
|
{
|
||||||
|
PowerPC::HostWrite_U32(code.address, next_address);
|
||||||
|
PowerPC::HostWrite_U32(code.data, next_address + 4);
|
||||||
|
next_address += CODE_SIZE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const GeckoCode::Code& code : active_code.codes)
|
WARN_LOG_FMT(ACTIONREPLAY, "GeckoCodes: Using {} of {} bytes", next_address - start_address,
|
||||||
{
|
end_address - start_address);
|
||||||
PowerPC::HostWrite_U32(code.address, next_address);
|
|
||||||
PowerPC::HostWrite_U32(code.data, next_address + 4);
|
// Stop code. Tells the handler that this is the end of the list.
|
||||||
next_address += CODE_SIZE;
|
PowerPC::HostWrite_U32(0xF0000000, next_address);
|
||||||
}
|
PowerPC::HostWrite_U32(0x00000000, next_address + 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
WARN_LOG_FMT(ACTIONREPLAY, "GeckoCodes: Using {} of {} bytes", next_address - start_address,
|
// Write 0 to trampoline address, not sure why this is necessary
|
||||||
end_address - start_address);
|
|
||||||
|
|
||||||
// Stop code. Tells the handler that this is the end of the list.
|
|
||||||
PowerPC::HostWrite_U32(0xF0000000, next_address);
|
|
||||||
PowerPC::HostWrite_U32(0x00000000, next_address + 4);
|
|
||||||
PowerPC::HostWrite_U32(0, HLE_TRAMPOLINE_ADDRESS);
|
PowerPC::HostWrite_U32(0, HLE_TRAMPOLINE_ADDRESS);
|
||||||
|
|
||||||
// Turn on codes
|
// Turn on codes
|
||||||
|
@ -226,6 +245,7 @@ static Installation InstallCodeHandlerLocked()
|
||||||
{
|
{
|
||||||
PowerPC::ppcState.iCache.Invalidate(INSTALLER_BASE_ADDRESS + j);
|
PowerPC::ppcState.iCache.Invalidate(INSTALLER_BASE_ADDRESS + j);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Installation::Installed;
|
return Installation::Installed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,10 +316,73 @@ void RunCodeHandler()
|
||||||
}
|
}
|
||||||
DEBUG_LOG_FMT(ACTIONREPLAY,
|
DEBUG_LOG_FMT(ACTIONREPLAY,
|
||||||
"GeckoCodes: Initiating phantom branch-and-link. "
|
"GeckoCodes: Initiating phantom branch-and-link. "
|
||||||
"PC = {:#010x}, SP = {:#010x}, SFP = {:#010x}",
|
"PC = {:#010x}, SP = {:#010x}, SFP = {:#010x}\n",
|
||||||
PC, SP, SFP);
|
PC, SP, SFP);
|
||||||
LR = HLE_TRAMPOLINE_ADDRESS;
|
LR = HLE_TRAMPOLINE_ADDRESS;
|
||||||
PC = NPC = ENTRY_POINT;
|
PC = NPC = ENTRY_POINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 GetGctLength()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(s_active_codes_lock);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (const GeckoCode& active_code : s_active_codes)
|
||||||
|
{
|
||||||
|
if (active_code.enabled)
|
||||||
|
{
|
||||||
|
i += 8 * static_cast<int>(active_code.codes.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return i + 0x10; // 0x10 is the fixed size of the header and terminator
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> uint32ToVector(u32 num)
|
||||||
|
{
|
||||||
|
u8 byte0 = num >> 24;
|
||||||
|
u8 byte1 = (num & 0xFF0000) >> 16;
|
||||||
|
u8 byte2 = (num & 0xFF00) >> 8;
|
||||||
|
u8 byte3 = num & 0xFF;
|
||||||
|
|
||||||
|
return std::vector<u8>({byte0, byte1, byte2, byte3});
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendWordToBuffer(std::vector<u8>* buf, u32 word)
|
||||||
|
{
|
||||||
|
auto wordVector = uint32ToVector(word);
|
||||||
|
buf->insert(buf->end(), wordVector.begin(), wordVector.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> GenerateGct()
|
||||||
|
{
|
||||||
|
std::vector<u8> res;
|
||||||
|
|
||||||
|
// Write header
|
||||||
|
appendWordToBuffer(&res, 0x00d0c0de);
|
||||||
|
appendWordToBuffer(&res, 0x00d0c0de);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lk(s_active_codes_lock);
|
||||||
|
|
||||||
|
// Write codes
|
||||||
|
for (const GeckoCode& active_code : s_active_codes)
|
||||||
|
{
|
||||||
|
if (active_code.enabled)
|
||||||
|
{
|
||||||
|
for (const GeckoCode::Code& code : active_code.codes)
|
||||||
|
{
|
||||||
|
appendWordToBuffer(&res, code.address);
|
||||||
|
appendWordToBuffer(&res, code.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write footer
|
||||||
|
appendWordToBuffer(&res, 0xff000000);
|
||||||
|
appendWordToBuffer(&res, 0x00000000);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Gecko
|
} // namespace Gecko
|
||||||
|
|
|
@ -66,4 +66,7 @@ void RunCodeHandler();
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
void DoState(PointerWrap&);
|
void DoState(PointerWrap&);
|
||||||
|
|
||||||
|
u32 GetGctLength();
|
||||||
|
std::vector<u8> GenerateGct();
|
||||||
|
|
||||||
} // namespace Gecko
|
} // namespace Gecko
|
||||||
|
|
|
@ -20,11 +20,13 @@
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "Core/CoreTiming.h"
|
#include "Core/CoreTiming.h"
|
||||||
#include "Core/Debugger/Debugger_SymbolMap.h"
|
#include "Core/Debugger/Debugger_SymbolMap.h"
|
||||||
|
#include "Core/GeckoCode.h"
|
||||||
#include "Core/HW/EXI/EXI_DeviceSlippi.h"
|
#include "Core/HW/EXI/EXI_DeviceSlippi.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
#include "Core/HW/SystemTimers.h"
|
#include "Core/HW/SystemTimers.h"
|
||||||
#include "Core/Host.h"
|
#include "Core/Host.h"
|
||||||
#include "Core/NetPlayClient.h"
|
#include "Core/NetPlayClient.h"
|
||||||
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
#include "Core/Slippi/SlippiPlayback.h"
|
#include "Core/Slippi/SlippiPlayback.h"
|
||||||
#include "Core/Slippi/SlippiReplayComm.h"
|
#include "Core/Slippi/SlippiReplayComm.h"
|
||||||
#include "Core/State.h"
|
#include "Core/State.h"
|
||||||
|
@ -95,6 +97,18 @@ void appendHalfToBuffer(std::vector<u8>* buf, u16 word)
|
||||||
buf->insert(buf->end(), halfVector.begin(), halfVector.end());
|
buf->insert(buf->end(), halfVector.begin(), halfVector.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ConvertConnectCodeForGame(const std::string& input)
|
||||||
|
{
|
||||||
|
// Shift-Jis '#' symbol is two bytes (0x8194), followed by 0x00 null terminator
|
||||||
|
char fullWidthShiftJisHashtag[] = {-127, -108, 0}; // 0x81, 0x94, 0x00
|
||||||
|
std::string connectCode(input);
|
||||||
|
// SLIPPITODO:Not the best use of ReplaceAll. potential bug if more than one '#' found.
|
||||||
|
connectCode = ReplaceAll(connectCode, "#", std::string(fullWidthShiftJisHashtag));
|
||||||
|
connectCode.resize(CONNECT_CODE_LENGTH +
|
||||||
|
2); // fixed length + full width (two byte) hashtag +1, null terminator +1
|
||||||
|
return connectCode;
|
||||||
|
}
|
||||||
|
|
||||||
CEXISlippi::CEXISlippi()
|
CEXISlippi::CEXISlippi()
|
||||||
{
|
{
|
||||||
INFO_LOG(SLIPPI, "EXI SLIPPI Constructor called.");
|
INFO_LOG(SLIPPI, "EXI SLIPPI Constructor called.");
|
||||||
|
@ -1484,8 +1498,7 @@ bool CEXISlippi::isDisconnected()
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
auto status = slippi_netplay->GetSlippiConnectStatus();
|
auto status = slippi_netplay->GetSlippiConnectStatus();
|
||||||
return status != SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED ||
|
return status != SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED;
|
||||||
isConnectionStalled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tempTestCount = 0;
|
static int tempTestCount = 0;
|
||||||
|
@ -2100,16 +2113,6 @@ void CEXISlippi::prepareOnlineMatchState()
|
||||||
// Set p3/p4 player type to human
|
// Set p3/p4 player type to human
|
||||||
onlineMatchBlock[0x61 + 2 * 0x24] = 0;
|
onlineMatchBlock[0x61 + 2 * 0x24] = 0;
|
||||||
onlineMatchBlock[0x61 + 3 * 0x24] = 0;
|
onlineMatchBlock[0x61 + 3 * 0x24] = 0;
|
||||||
|
|
||||||
// Set alt color to light/dark costume for multiples of the same character on a team
|
|
||||||
int characterCount[26][3] = {0};
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
int charId = onlineMatchBlock[0x60 + i * 0x24];
|
|
||||||
int teamId = onlineMatchBlock[0x69 + i * 0x24];
|
|
||||||
onlineMatchBlock[0x67 + i * 0x24] = characterCount[charId][teamId];
|
|
||||||
characterCount[charId][teamId]++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overwrite stage
|
// Overwrite stage
|
||||||
|
@ -2133,7 +2136,7 @@ void CEXISlippi::prepareOnlineMatchState()
|
||||||
onlineMatchBlock[0x84]);
|
onlineMatchBlock[0x84]);
|
||||||
|
|
||||||
// Turn pause on in direct, off in everything else
|
// Turn pause on in direct, off in everything else
|
||||||
u8* gameBitField3 = (u8*)&onlineMatchBlock[2];
|
u8* gameBitField3 = static_cast<u8*>(&onlineMatchBlock[2]);
|
||||||
*gameBitField3 = lastSearch.mode >= directMode ? *gameBitField3 & 0xF7 : *gameBitField3 | 0x8;
|
*gameBitField3 = lastSearch.mode >= directMode ? *gameBitField3 & 0xF7 : *gameBitField3 | 0x8;
|
||||||
//*gameBitField3 = *gameBitField3 | 0x8;
|
//*gameBitField3 = *gameBitField3 | 0x8;
|
||||||
|
|
||||||
|
@ -2146,8 +2149,8 @@ void CEXISlippi::prepareOnlineMatchState()
|
||||||
else
|
else
|
||||||
rightTeamPlayers.push_back(i);
|
rightTeamPlayers.push_back(i);
|
||||||
}
|
}
|
||||||
auto leftTeamSize = leftTeamPlayers.size();
|
int leftTeamSize = static_cast<int>(leftTeamPlayers.size());
|
||||||
auto rightTeamSize = rightTeamPlayers.size();
|
int rightTeamSize = static_cast<int>(rightTeamPlayers.size());
|
||||||
leftTeamPlayers.resize(4, 0);
|
leftTeamPlayers.resize(4, 0);
|
||||||
rightTeamPlayers.resize(4, 0);
|
rightTeamPlayers.resize(4, 0);
|
||||||
leftTeamPlayers[3] = static_cast<u8>(leftTeamSize);
|
leftTeamPlayers[3] = static_cast<u8>(leftTeamSize);
|
||||||
|
@ -2211,6 +2214,21 @@ void CEXISlippi::prepareOnlineMatchState()
|
||||||
oppName = ConvertStringForGame(oppText, MAX_NAME_LENGTH * 2 + 1);
|
oppName = ConvertStringForGame(oppText, MAX_NAME_LENGTH * 2 + 1);
|
||||||
m_read_queue.insert(m_read_queue.end(), oppName.begin(), oppName.end());
|
m_read_queue.insert(m_read_queue.end(), oppName.begin(), oppName.end());
|
||||||
|
|
||||||
|
#ifdef LOCAL_TESTING
|
||||||
|
std::string defaultConnectCodes[] = {"PLYR#001", "PLYR#002", "PLYR#003", "PLYR#004"};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto playerInfo = matchmaking->GetPlayerInfo();
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
std::string connectCode = i < playerInfo.size() ? playerInfo[i].connect_code : "";
|
||||||
|
#ifdef LOCAL_TESTING
|
||||||
|
connectCode = defaultConnectCodes[i];
|
||||||
|
#endif
|
||||||
|
connectCode = ConvertConnectCodeForGame(connectCode);
|
||||||
|
m_read_queue.insert(m_read_queue.end(), connectCode.begin(), connectCode.end());
|
||||||
|
}
|
||||||
|
|
||||||
// Add error message if there is one
|
// Add error message if there is one
|
||||||
auto errorStr = !forcedError.empty() ? forcedError : matchmaking->GetErrorMessage();
|
auto errorStr = !forcedError.empty() ? forcedError : matchmaking->GetErrorMessage();
|
||||||
errorStr = ConvertStringForGame(errorStr, 120);
|
errorStr = ConvertStringForGame(errorStr, 120);
|
||||||
|
@ -2317,6 +2335,32 @@ void CEXISlippi::prepareFileLoad(u8* payload)
|
||||||
m_read_queue.insert(m_read_queue.end(), buf.begin(), buf.end());
|
m_read_queue.insert(m_read_queue.end(), buf.begin(), buf.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CEXISlippi::prepareGctLength()
|
||||||
|
{
|
||||||
|
m_read_queue.clear();
|
||||||
|
|
||||||
|
u32 size = Gecko::GetGctLength();
|
||||||
|
|
||||||
|
INFO_LOG(SLIPPI, "Getting gct size: %d", size);
|
||||||
|
|
||||||
|
// Write size to output
|
||||||
|
appendWordToBuffer(&m_read_queue, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXISlippi::prepareGctLoad(u8* payload)
|
||||||
|
{
|
||||||
|
m_read_queue.clear();
|
||||||
|
|
||||||
|
auto gct = Gecko::GenerateGct();
|
||||||
|
|
||||||
|
// This is the address where the codes will be written to
|
||||||
|
auto address = Common::swap32(&payload[0]);
|
||||||
|
|
||||||
|
INFO_LOG(SLIPPI, "Preparing to write gecko codes at: 0x%X", address);
|
||||||
|
|
||||||
|
m_read_queue.insert(m_read_queue.end(), gct.begin(), gct.end());
|
||||||
|
}
|
||||||
|
|
||||||
void CEXISlippi::handleChatMessage(u8* payload)
|
void CEXISlippi::handleChatMessage(u8* payload)
|
||||||
{
|
{
|
||||||
int messageId = payload[0];
|
int messageId = payload[0];
|
||||||
|
@ -2468,6 +2512,7 @@ void CEXISlippi::prepareNewSeed()
|
||||||
|
|
||||||
void CEXISlippi::handleReportGame(u8* payload)
|
void CEXISlippi::handleReportGame(u8* payload)
|
||||||
{
|
{
|
||||||
|
#ifndef LOCAL_TESTING
|
||||||
SlippiGameReporter::GameReport r;
|
SlippiGameReporter::GameReport r;
|
||||||
r.duration_frames = Common::swap32(&payload[0]);
|
r.duration_frames = Common::swap32(&payload[0]);
|
||||||
|
|
||||||
|
@ -2488,6 +2533,7 @@ void CEXISlippi::handleReportGame(u8* payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
game_reporter->StartReport(r);
|
game_reporter->StartReport(r);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXISlippi::DMAWrite(u32 _uAddr, u32 _uSize)
|
void CEXISlippi::DMAWrite(u32 _uAddr, u32 _uSize)
|
||||||
|
@ -2619,6 +2665,12 @@ void CEXISlippi::DMAWrite(u32 _uAddr, u32 _uSize)
|
||||||
case CMD_REPORT_GAME:
|
case CMD_REPORT_GAME:
|
||||||
handleReportGame(&memPtr[bufLoc + 1]);
|
handleReportGame(&memPtr[bufLoc + 1]);
|
||||||
break;
|
break;
|
||||||
|
case CMD_GCT_LENGTH:
|
||||||
|
prepareGctLength();
|
||||||
|
break;
|
||||||
|
case CMD_GCT_LOAD:
|
||||||
|
prepareGctLoad(&memPtr[bufLoc + 1]);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
writeToFileAsync(&memPtr[bufLoc], payloadLen + 1, "");
|
writeToFileAsync(&memPtr[bufLoc], payloadLen + 1, "");
|
||||||
SlippiSpectateServer::getInstance().write(&memPtr[bufLoc], payloadLen + 1);
|
SlippiSpectateServer::getInstance().write(&memPtr[bufLoc], payloadLen + 1);
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "Common/File.h"
|
#include "Common/File.h"
|
||||||
#include "Common/FileUtil.h"
|
#include "Common/FileUtil.h"
|
||||||
#include "Core/Slippi/SlippiGameFileLoader.h"
|
#include "Core/Slippi/SlippiGameFileLoader.h"
|
||||||
|
#include "Core/Slippi/SlippiGameReporter.h"
|
||||||
#include "Core/Slippi/SlippiMatchmaking.h"
|
#include "Core/Slippi/SlippiMatchmaking.h"
|
||||||
#include "Core/Slippi/SlippiNetplay.h"
|
#include "Core/Slippi/SlippiNetplay.h"
|
||||||
#include "Core/Slippi/SlippiPlayback.h"
|
#include "Core/Slippi/SlippiPlayback.h"
|
||||||
|
@ -17,7 +18,6 @@
|
||||||
#include "Core/Slippi/SlippiSavestate.h"
|
#include "Core/Slippi/SlippiSavestate.h"
|
||||||
#include "Core/Slippi/SlippiSpectate.h"
|
#include "Core/Slippi/SlippiSpectate.h"
|
||||||
#include "Core/Slippi/SlippiUser.h"
|
#include "Core/Slippi/SlippiUser.h"
|
||||||
#include "Core/Slippi/SlippiGameReporter.h"
|
|
||||||
#include "EXI_Device.h"
|
#include "EXI_Device.h"
|
||||||
|
|
||||||
#define ROLLBACK_MAX_FRAMES 7
|
#define ROLLBACK_MAX_FRAMES 7
|
||||||
|
@ -79,6 +79,8 @@ private:
|
||||||
CMD_LOG_MESSAGE = 0xD0,
|
CMD_LOG_MESSAGE = 0xD0,
|
||||||
CMD_FILE_LENGTH = 0xD1,
|
CMD_FILE_LENGTH = 0xD1,
|
||||||
CMD_FILE_LOAD = 0xD2,
|
CMD_FILE_LOAD = 0xD2,
|
||||||
|
CMD_GCT_LENGTH = 0xD3,
|
||||||
|
CMD_GCT_LOAD = 0xD4,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
@ -124,6 +126,8 @@ private:
|
||||||
{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_LENGTH, 0x40},
|
||||||
{CMD_FILE_LOAD, 0x40},
|
{CMD_FILE_LOAD, 0x40},
|
||||||
|
{CMD_GCT_LENGTH, 0x0},
|
||||||
|
{CMD_GCT_LOAD, 0x4},
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WriteMessage
|
struct WriteMessage
|
||||||
|
@ -174,7 +178,7 @@ private:
|
||||||
void prepareOnlineStatus();
|
void prepareOnlineStatus();
|
||||||
void handleConnectionCleanup();
|
void handleConnectionCleanup();
|
||||||
void prepareNewSeed();
|
void prepareNewSeed();
|
||||||
void handleReportGame(u8 *payload);
|
void handleReportGame(u8* payload);
|
||||||
|
|
||||||
// replay playback stuff
|
// replay playback stuff
|
||||||
void prepareGameInfo(u8* payload);
|
void prepareGameInfo(u8* payload);
|
||||||
|
@ -185,10 +189,12 @@ private:
|
||||||
void prepareIsFileReady();
|
void prepareIsFileReady();
|
||||||
|
|
||||||
// misc stuff
|
// misc stuff
|
||||||
void handleChatMessage(u8 *payload);
|
void handleChatMessage(u8* payload);
|
||||||
void logMessageFromGame(u8* payload);
|
void logMessageFromGame(u8* payload);
|
||||||
void prepareFileLength(u8* payload);
|
void prepareFileLength(u8* payload);
|
||||||
void prepareFileLoad(u8* payload);
|
void prepareFileLoad(u8* payload);
|
||||||
|
void prepareGctLength();
|
||||||
|
void prepareGctLoad(u8* payload);
|
||||||
int getCharColor(u8 charId, u8 teamId);
|
int getCharColor(u8 charId, u8 teamId);
|
||||||
|
|
||||||
void FileWriteThread(void);
|
void FileWriteThread(void);
|
||||||
|
@ -222,7 +228,7 @@ private:
|
||||||
std::string forcedError = "";
|
std::string forcedError = "";
|
||||||
|
|
||||||
// Used to determine when to detect when a new session has started
|
// Used to determine when to detect when a new session has started
|
||||||
bool is_play_session_active = false;
|
bool is_play_session_active = false;
|
||||||
|
|
||||||
// Frame skipping variables
|
// Frame skipping variables
|
||||||
int framesToSkip = 0;
|
int framesToSkip = 0;
|
||||||
|
|
|
@ -77,7 +77,7 @@ CoreTiming::EventType* et_AudioDMA;
|
||||||
CoreTiming::EventType* et_DSP;
|
CoreTiming::EventType* et_DSP;
|
||||||
CoreTiming::EventType* et_IPC_HLE;
|
CoreTiming::EventType* et_IPC_HLE;
|
||||||
// PatchEngine updates every 1/60th of a second by default
|
// PatchEngine updates every 1/60th of a second by default
|
||||||
CoreTiming::EventType* et_PatchEngine;
|
// CoreTiming::EventType* et_PatchEngine;
|
||||||
CoreTiming::EventType* et_Throttle;
|
CoreTiming::EventType* et_Throttle;
|
||||||
|
|
||||||
u32 s_cpu_core_clock = 486000000u; // 486 mhz (its not 485, stop bugging me!)
|
u32 s_cpu_core_clock = 486000000u; // 486 mhz (its not 485, stop bugging me!)
|
||||||
|
@ -163,7 +163,7 @@ void PatchEngineCallback(u64 userdata, s64 cycles_late)
|
||||||
cycles_pruned += next_schedule;
|
cycles_pruned += next_schedule;
|
||||||
}
|
}
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(next_schedule, et_PatchEngine, cycles_pruned);
|
// CoreTiming::ScheduleEvent(next_schedule, et_PatchEngine, cycles_pruned);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThrottleCallback(u64 last_time, s64 cyclesLate)
|
void ThrottleCallback(u64 last_time, s64 cyclesLate)
|
||||||
|
@ -326,7 +326,7 @@ void Init()
|
||||||
et_DSP = CoreTiming::RegisterEvent("DSPCallback", DSPCallback);
|
et_DSP = CoreTiming::RegisterEvent("DSPCallback", DSPCallback);
|
||||||
et_AudioDMA = CoreTiming::RegisterEvent("AudioDMACallback", AudioDMACallback);
|
et_AudioDMA = CoreTiming::RegisterEvent("AudioDMACallback", AudioDMACallback);
|
||||||
et_IPC_HLE = CoreTiming::RegisterEvent("IPC_HLE_UpdateCallback", IPC_HLE_UpdateCallback);
|
et_IPC_HLE = CoreTiming::RegisterEvent("IPC_HLE_UpdateCallback", IPC_HLE_UpdateCallback);
|
||||||
et_PatchEngine = CoreTiming::RegisterEvent("PatchEngine", PatchEngineCallback);
|
// et_PatchEngine = CoreTiming::RegisterEvent("PatchEngine", PatchEngineCallback);
|
||||||
et_Throttle = CoreTiming::RegisterEvent("Throttle", ThrottleCallback);
|
et_Throttle = CoreTiming::RegisterEvent("Throttle", ThrottleCallback);
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerHalfLine(), et_VI);
|
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerHalfLine(), et_VI);
|
||||||
|
@ -334,7 +334,7 @@ void Init()
|
||||||
CoreTiming::ScheduleEvent(s_audio_dma_period, et_AudioDMA);
|
CoreTiming::ScheduleEvent(s_audio_dma_period, et_AudioDMA);
|
||||||
CoreTiming::ScheduleEvent(0, et_Throttle, Common::Timer::GetTimeUs());
|
CoreTiming::ScheduleEvent(0, et_Throttle, Common::Timer::GetTimeUs());
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerField(), et_PatchEngine);
|
// CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerField(), et_PatchEngine);
|
||||||
|
|
||||||
if (SConfig::GetInstance().bWii)
|
if (SConfig::GetInstance().bWii)
|
||||||
CoreTiming::ScheduleEvent(s_ipc_hle_period, et_IPC_HLE);
|
CoreTiming::ScheduleEvent(s_ipc_hle_period, et_IPC_HLE);
|
||||||
|
|
|
@ -16,18 +16,6 @@
|
||||||
#include <json.hpp>
|
#include <json.hpp>
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
inline size_t receive(char* ptr, size_t size, size_t nmemb, void* rcvBuf)
|
|
||||||
{
|
|
||||||
size_t len = size * nmemb;
|
|
||||||
INFO_LOG(SLIPPI_ONLINE, "[User] Received data: %d", len);
|
|
||||||
|
|
||||||
std::string* buf = (std::string*)rcvBuf;
|
|
||||||
|
|
||||||
buf->insert(buf->end(), ptr, ptr + len);
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
SlippiGameReporter::SlippiGameReporter(SlippiUser* user)
|
SlippiGameReporter::SlippiGameReporter(SlippiUser* user)
|
||||||
{
|
{
|
||||||
CURL* curl = curl_easy_init();
|
CURL* curl = curl_easy_init();
|
||||||
|
@ -76,7 +64,7 @@ void SlippiGameReporter::StartReport(GameReport report)
|
||||||
|
|
||||||
void SlippiGameReporter::StartNewSession(std::vector<std::string> new_player_uids)
|
void SlippiGameReporter::StartNewSession(std::vector<std::string> new_player_uids)
|
||||||
{
|
{
|
||||||
this->player_uids = new_player_uids;
|
this->m_player_uids = new_player_uids;
|
||||||
gameIndex = 1;
|
gameIndex = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +96,7 @@ void SlippiGameReporter::ReportThreadHandler()
|
||||||
for (int i = 0; i < report.players.size(); i++)
|
for (int i = 0; i < report.players.size(); i++)
|
||||||
{
|
{
|
||||||
json p;
|
json p;
|
||||||
p["uid"] = player_uids[i];
|
p["uid"] = m_player_uids[i];
|
||||||
p["damage_done"] = report.players[i].damage_done;
|
p["damage_done"] = report.players[i].damage_done;
|
||||||
p["stocks_remaining"] = report.players[i].stocks_remaining;
|
p["stocks_remaining"] = report.players[i].stocks_remaining;
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ protected:
|
||||||
struct curl_slist* m_curl_header_list = nullptr;
|
struct curl_slist* m_curl_header_list = nullptr;
|
||||||
|
|
||||||
u32 gameIndex = 1;
|
u32 gameIndex = 1;
|
||||||
std::vector<std::string> player_uids;
|
std::vector<std::string> m_player_uids;
|
||||||
|
|
||||||
SlippiUser* m_user;
|
SlippiUser* m_user;
|
||||||
std::queue<GameReport> game_report_queue;
|
std::queue<GameReport> game_report_queue;
|
||||||
|
|
|
@ -67,13 +67,13 @@ SlippiNetplayClient::SlippiNetplayClient(std::vector<std::string> addrs, std::ve
|
||||||
|
|
||||||
this->isDecider = isDecider;
|
this->isDecider = isDecider;
|
||||||
this->m_remotePlayerCount = remotePlayerCount;
|
this->m_remotePlayerCount = remotePlayerCount;
|
||||||
this->playerIdx = playerIdx;
|
this->m_player_idx = playerIdx;
|
||||||
|
|
||||||
// Set up remote player data structures.
|
// Set up remote player data structures.
|
||||||
int j = 0;
|
int j = 0;
|
||||||
for (int i = 0; i < SLIPPI_REMOTE_PLAYER_MAX; i++, j++)
|
for (int i = 0; i < SLIPPI_REMOTE_PLAYER_MAX; i++, j++)
|
||||||
{
|
{
|
||||||
if (j == playerIdx)
|
if (j == m_player_idx)
|
||||||
j++;
|
j++;
|
||||||
this->matchInfo.remotePlayerSelections[i] = SlippiPlayerSelections();
|
this->matchInfo.remotePlayerSelections[i] = SlippiPlayerSelections();
|
||||||
this->matchInfo.remotePlayerSelections[i].playerIdx = j;
|
this->matchInfo.remotePlayerSelections[i].playerIdx = j;
|
||||||
|
@ -151,7 +151,7 @@ SlippiNetplayClient::SlippiNetplayClient(bool isDecider)
|
||||||
u8 SlippiNetplayClient::PlayerIdxFromPort(u8 port)
|
u8 SlippiNetplayClient::PlayerIdxFromPort(u8 port)
|
||||||
{
|
{
|
||||||
u8 p = port;
|
u8 p = port;
|
||||||
if (port > playerIdx)
|
if (port > m_player_idx)
|
||||||
{
|
{
|
||||||
p--;
|
p--;
|
||||||
}
|
}
|
||||||
|
@ -160,7 +160,7 @@ u8 SlippiNetplayClient::PlayerIdxFromPort(u8 port)
|
||||||
|
|
||||||
u8 SlippiNetplayClient::LocalPlayerPort()
|
u8 SlippiNetplayClient::LocalPlayerPort()
|
||||||
{
|
{
|
||||||
return this->playerIdx;
|
return this->m_player_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
// called from ---NETPLAY--- thread
|
// called from ---NETPLAY--- thread
|
||||||
|
@ -267,7 +267,7 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet, ENetPeer* peer)
|
||||||
sf::Packet spac;
|
sf::Packet spac;
|
||||||
spac << (NetPlay::MessageId)NetPlay::NP_MSG_SLIPPI_PAD_ACK;
|
spac << (NetPlay::MessageId)NetPlay::NP_MSG_SLIPPI_PAD_ACK;
|
||||||
spac << frame;
|
spac << frame;
|
||||||
spac << playerIdx;
|
spac << m_player_idx;
|
||||||
INFO_LOG(SLIPPI_ONLINE, "Sending ack packet for frame %d (player %d) to peer at %d:%d", frame,
|
INFO_LOG(SLIPPI_ONLINE, "Sending ack packet for frame %d (player %d) to peer at %d:%d", frame,
|
||||||
packetPlayerPort, peer->address.host, peer->address.port);
|
packetPlayerPort, peer->address.host, peer->address.port);
|
||||||
|
|
||||||
|
@ -835,7 +835,7 @@ void SlippiNetplayClient::SendSlippiPad(std::unique_ptr<SlippiPad> pad)
|
||||||
auto spac = std::make_unique<sf::Packet>();
|
auto spac = std::make_unique<sf::Packet>();
|
||||||
*spac << static_cast<NetPlay::MessageId>(NetPlay::NP_MSG_SLIPPI_PAD);
|
*spac << static_cast<NetPlay::MessageId>(NetPlay::NP_MSG_SLIPPI_PAD);
|
||||||
*spac << frame;
|
*spac << frame;
|
||||||
*spac << this->playerIdx;
|
*spac << this->m_player_idx;
|
||||||
// INFO_LOG(SLIPPI_ONLINE, "Sending a packet of inputs [%d]...", frame);
|
// INFO_LOG(SLIPPI_ONLINE, "Sending a packet of inputs [%d]...", frame);
|
||||||
for (auto it = localPadQueue.begin(); it != localPadQueue.end(); ++it)
|
for (auto it = localPadQueue.begin(); it != localPadQueue.end(); ++it)
|
||||||
{
|
{
|
||||||
|
@ -869,7 +869,7 @@ void SlippiNetplayClient::SendSlippiPad(std::unique_ptr<SlippiPad> pad)
|
||||||
void SlippiNetplayClient::SetMatchSelections(SlippiPlayerSelections& s)
|
void SlippiNetplayClient::SetMatchSelections(SlippiPlayerSelections& s)
|
||||||
{
|
{
|
||||||
matchInfo.localPlayerSelections.Merge(s);
|
matchInfo.localPlayerSelections.Merge(s);
|
||||||
matchInfo.localPlayerSelections.playerIdx = playerIdx;
|
matchInfo.localPlayerSelections.playerIdx = m_player_idx;
|
||||||
|
|
||||||
// Send packet containing selections
|
// Send packet containing selections
|
||||||
auto spac = std::make_unique<sf::Packet>();
|
auto spac = std::make_unique<sf::Packet>();
|
||||||
|
|
|
@ -192,7 +192,7 @@ protected:
|
||||||
bool isConnectionSelected = false;
|
bool isConnectionSelected = false;
|
||||||
bool isDecider = false;
|
bool isDecider = false;
|
||||||
bool hasGameStarted = false;
|
bool hasGameStarted = false;
|
||||||
u8 playerIdx = 0;
|
u8 m_player_idx = 0;
|
||||||
|
|
||||||
std::deque<std::unique_ptr<SlippiPad>> localPadQueue; // most recent inputs at start of deque
|
std::deque<std::unique_ptr<SlippiPad>> localPadQueue; // most recent inputs at start of deque
|
||||||
std::deque<std::unique_ptr<SlippiPad>>
|
std::deque<std::unique_ptr<SlippiPad>>
|
||||||
|
|
|
@ -85,6 +85,26 @@ std::map<Language, std::string> Volume::ReadWiiNames(const std::vector<char16_t>
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Volume::FileExists(std::string file_name)
|
||||||
|
{
|
||||||
|
std::vector<DiscIO::Partition> partitions = this->GetPartitions();
|
||||||
|
if (partitions.empty())
|
||||||
|
partitions.emplace_back(PARTITION_NONE);
|
||||||
|
|
||||||
|
for (const auto& partition : partitions)
|
||||||
|
{
|
||||||
|
const DiscIO::FileInfo& root_dir = this->GetFileSystem(partition)->GetRoot();
|
||||||
|
for (const auto& file_info : root_dir)
|
||||||
|
{
|
||||||
|
if (file_info.GetName() == file_name)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static std::unique_ptr<VolumeDisc> CreateDisc(std::unique_ptr<BlobReader>& reader)
|
static std::unique_ptr<VolumeDisc> CreateDisc(std::unique_ptr<BlobReader>& reader)
|
||||||
{
|
{
|
||||||
// Check for Wii
|
// Check for Wii
|
||||||
|
|
|
@ -152,6 +152,8 @@ public:
|
||||||
// The way the hash is calculated may change with updates to Dolphin.
|
// The way the hash is calculated may change with updates to Dolphin.
|
||||||
virtual std::array<u8, 20> GetSyncHash() const = 0;
|
virtual std::array<u8, 20> GetSyncHash() const = 0;
|
||||||
|
|
||||||
|
bool FileExists(std::string file_name);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template <u32 N>
|
template <u32 N>
|
||||||
std::string DecodeString(const char (&data)[N]) const
|
std::string DecodeString(const char (&data)[N]) const
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue