Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions source/abstract/File.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@

namespace Abstract::File {

u8 *Load(const char *path, size_t &size) {
std::filesystem::path Path(const char *path) {
char filepath[256];

if (path[0] == '/') {
path++;
}

snprintf(filepath, sizeof(filepath), "./%s", path);
std::ifstream file(filepath, std::ios::binary);
snprintf(filepath, sizeof(filepath), "%s", path);
return std::filesystem::path(filepath);
}

u8 *Load(const std::filesystem::path &path, size_t &size) {
std::ifstream file(path, std::ios::binary);
if (!file) {
PANIC("File with provided path %s was not loaded correctly!", path);
PANIC("File with provided path %s was not loaded correctly!", path.c_str());
}

file.seekg(0, std::ios::end);
Expand All @@ -27,6 +31,10 @@ u8 *Load(const char *path, size_t &size) {
return buffer;
}

u8 *Load(const char *path, size_t &size) {
return Load(Path(path), size);
}

void Append(const char *path, const char *data, size_t size) {
std::ofstream stream;
stream.open(path, std::ios::app | std::ios::binary);
Expand Down
4 changes: 4 additions & 0 deletions source/abstract/File.hh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

#include <Common.hh>

#include <filesystem>

namespace Abstract::File {

[[nodiscard]] std::filesystem::path Path(const char *path);
[[nodiscard]] u8 *Load(const std::filesystem::path &path, size_t &size);
[[nodiscard]] u8 *Load(const char *path, size_t &size);
void Append(const char *path, const char *data, size_t size);
int Remove(const char *path);
Expand Down
153 changes: 130 additions & 23 deletions source/host/KReplaySystem.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,11 @@

/// @brief Initializes the system.
void KReplaySystem::init() {
ASSERT(m_currentGhostFileName);
ASSERT(m_currentRawGhost);
ASSERT(m_currentGhost);

auto *sceneCreator = new Host::SceneCreatorDynamic;
m_sceneMgr = new EGG::SceneManager(sceneCreator);

System::RaceConfig::RegisterInitCallback(OnInit, nullptr);
Abstract::File::Remove("results.txt");

m_sceneMgr->changeScene(0);
}

/// @brief Executes a frame.
Expand All @@ -33,11 +27,27 @@ void KReplaySystem::calc() {
/// @details A run consists of replaying a ghost.
/// @return Whether the run was successful or not.
bool KReplaySystem::run() {
while (!calcEnd()) {
calc();
bool success = true;

while (true) {
if (m_ghostArgs.size() == 0) {
break;
}

std::filesystem::path nextPath = m_ghostArgs.front();
m_ghostArgs.pop();

if (std::filesystem::is_directory(nextPath)) {
success &= runDirectory(nextPath);
} else if (std::filesystem::is_regular_file(nextPath)) {
success &= runGhost(nextPath);
} else {
WARN("Not a valid path: %s", nextPath.string().c_str());
continue;
}
}

return success();
return success;
}

/// @brief Parses non-generic command line options.
Expand All @@ -60,19 +70,30 @@ void KReplaySystem::parseOptions(int argc, char **argv) {
case Host::EOption::Ghost: {
ASSERT(i + 1 < argc);

m_currentGhostFileName = argv[++i];
m_currentRawGhost = Abstract::File::Load(m_currentGhostFileName, m_currentRawGhostSize);

if (m_currentRawGhostSize < System::RKG_HEADER_SIZE ||
m_currentRawGhostSize > sizeof(System::RawGhostFile)) {
PANIC("File cannot be a ghost! Check the file size.");
// Every argument (until we see another flag) should be a ghost filename or directory
while (++i < argc) {
// Decrement i if it represents some other flag
if (Host::Option::CheckFlag(argv[i])) {
--i;
break;
}

std::filesystem::path filepath = Abstract::File::Path(argv[i]);

if (std::filesystem::is_directory(filepath) ||
std::filesystem::is_regular_file(filepath)) {
m_ghostArgs.push(filepath);
} else {
WARN("Unable to find %s. Skipping...", filepath.c_str());
}
}
} break;
case Host::EOption::Progress: {
if (i + 1 >= argc) {
PANIC("Expected progress interval argument!");
}

// Creating the raw ghost file validates it
System::RawGhostFile file = System::RawGhostFile(m_currentRawGhost);

m_currentGhost = new System::GhostFile(file);
ASSERT(m_currentGhost);
m_progressInterval = strtoul(argv[++i], nullptr, 0);
} break;
case Host::EOption::Invalid:
default:
Expand All @@ -96,8 +117,8 @@ void KReplaySystem::DestroyInstance() {
}

KReplaySystem::KReplaySystem()
: m_currentGhostFileName(nullptr), m_currentGhost(nullptr), m_currentRawGhost(nullptr),
m_currentRawGhostSize(0) {}
: m_progressInterval(0), m_replaysPlayed(0), m_replaysSynced(0), m_currentGhost(nullptr),
m_currentRawGhost(nullptr), m_currentRawGhostSize(0) {}

KReplaySystem::~KReplaySystem() {
if (s_instance) {
Expand Down Expand Up @@ -130,11 +151,97 @@ bool KReplaySystem::calcEnd() const {
/// @brief Reports failure to file.
/// @param msg The message to report.
void KReplaySystem::reportFail(const std::string &msg) const {
std::string report(m_currentGhostFileName);
std::string report(m_currentGhostPath.string());
report += "\n" + std::string(msg);
Abstract::File::Append("results.txt", report.c_str(), report.size());
}

bool KReplaySystem::runDirectory(const std::filesystem::path &dirPath) {
bool success = true;

for (const auto &dir_entry : std::filesystem::recursive_directory_iterator(dirPath)) {
const auto &entry_path = dir_entry.path();

// Skip directories
if (std::filesystem::is_regular_file(entry_path)) {
success &= runGhost(entry_path);
}
}

REPORT("Progress: %llu/%llu replays synced (Error rate: %.2f%)", m_replaysSynced,
m_replaysPlayed,
static_cast<f64>(m_replaysPlayed - m_replaysSynced) / m_replaysPlayed * 100);

return success;
}

bool KReplaySystem::runGhost(const std::filesystem::path &ghostPath) {
if (!ghostPath.has_extension()) {
return true;
}

if (ghostPath.extension() != ".rkg") {
WARN("Skipping %s", ghostPath.string().c_str());
return true;
}

loadGhost(ghostPath);

// Has the root scene been created?
if (!m_sceneMgr->currentScene()) {
m_sceneMgr->changeScene(0);
} else {
m_sceneMgr->createScene(2, m_sceneMgr->currentScene());
}

while (!calcEnd()) {
calc();
}

bool isSuccess = success();

if (!isSuccess) {
WARN("DESYNC! Ghost path: %s", ghostPath.string().c_str());
}

m_sceneMgr->currentScene()->heap()->enableAllocation();
m_sceneMgr->destroyScene(m_sceneMgr->currentScene());

delete m_currentRawGhost;
m_currentRawGhost = nullptr;
m_currentRawGhostSize = 0;

delete m_currentGhost;
m_currentGhost = nullptr;

++m_replaysPlayed;

if (isSuccess) {
++m_replaysSynced;
}

if (m_progressInterval > 0 && m_replaysPlayed % m_progressInterval == 0) {
REPORT("Progress: %llu/%llu replays synced (Error rate: %.2f%)", m_replaysSynced,
m_replaysPlayed,
static_cast<f64>(m_replaysPlayed - m_replaysSynced) / m_replaysPlayed * 100);
}

return isSuccess;
}

void KReplaySystem::loadGhost(const std::filesystem::path &ghostPath) {
m_currentGhostPath = ghostPath;
m_currentRawGhost = Abstract::File::Load(m_currentGhostPath, m_currentRawGhostSize);
if (m_currentRawGhostSize < System::RKG_HEADER_SIZE ||
m_currentRawGhostSize > System::RKG_UNCOMPRESSED_INPUT_DATA_SECTION_SIZE) {
PANIC("File cannot be a ghost! Check the file size. %llu", m_currentRawGhostSize);
}

// Creating the raw ghost file validates it
System::RawGhostFile file = System::RawGhostFile(m_currentRawGhost);
m_currentGhost = new System::GhostFile(file);
}

/// @brief Determines whether the simulation was a success or not.
/// @return Whether the simulation was a success or not.
bool KReplaySystem::success() const {
Expand Down
12 changes: 12 additions & 0 deletions source/host/KReplaySystem.hh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

#include <game/system/RaceConfig.hh>

#include <filesystem>
#include <queue>

/// @brief Kinoko system designed to execute replays.
class KReplaySystem : public KSystem {
public:
Expand All @@ -30,6 +33,10 @@ private:
bool calcEnd() const;
void reportFail(const std::string &msg) const;

bool runDirectory(const std::filesystem::path &dirPath);
bool runGhost(const std::filesystem::path &ghostPath);
void loadGhost(const std::filesystem::path &ghostPath);

bool success() const;
s32 getDesyncingTimerIdx() const;
DesyncingTimerPair getDesyncingTimer(s32 i) const;
Expand All @@ -38,6 +45,11 @@ private:

EGG::SceneManager *m_sceneMgr;

std::queue<std::filesystem::path> m_ghostArgs;
size_t m_progressInterval;
size_t m_replaysPlayed;
size_t m_replaysSynced;
std::filesystem::path m_currentGhostPath;
const char *m_currentGhostFileName;
const System::GhostFile *m_currentGhost;
const u8 *m_currentRawGhost;
Expand Down
7 changes: 7 additions & 0 deletions source/host/Option.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ std::optional<EOption> CheckFlag(const char *arg) {
return EOption::TargetFrame;
}

if (strcmp(verbose_arg, "progress") == 0) {
return EOption::Progress;
}

return EOption::Invalid;
} else {
switch (arg[1]) {
Expand All @@ -45,6 +49,9 @@ std::optional<EOption> CheckFlag(const char *arg) {
case 'F':
case 'f':
return EOption::TargetFrame;
case 'P':
case 'p':
return EOption::Progress;
default:
return EOption::Invalid;
}
Expand Down
1 change: 1 addition & 0 deletions source/host/Option.hh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ enum class EOption {
Ghost,
KRKG,
TargetFrame,
Progress,
};

namespace Option {
Expand Down