diff --git a/Source/Client/Replay.cpp b/Source/Client/Replay.cpp index 7a387509..b8c79425 100644 --- a/Source/Client/Replay.cpp +++ b/Source/Client/Replay.cpp @@ -2,7 +2,9 @@ #include "Contents/Configuration/ServiceConfiguration.h" #include +#include #include +#include #include #include @@ -440,6 +442,118 @@ std::vector Replay::ReadSection(int id) { return data; } +// OFFSET: 0x00B84E40 +// FUNCTION: fn_19322 +std::uint32_t Replay::ParseMetadata(const std::vector& data) { + if (data.empty()) { + return 0xFFFFFFFF; + } + + const unsigned char* buffer = data.data(); + const std::uint32_t size = static_cast(data.size()); + std::uint32_t offset = 6; + + std::uint16_t signature = 0; + std::memcpy(&signature, buffer, sizeof(signature)); + + if (signature != 0x3B6) { + return 0xFFFFFFFF; + } + + if (size > 6) { + do { + std::uint16_t id = 0; + std::uint32_t bytes = 0; + std::memcpy(&id, buffer + offset, sizeof(id)); + std::memcpy(&bytes, buffer + offset + 2, sizeof(bytes)); + offset += 6; + + switch (id) { + case 0x3B7: + return offset; + case 0x3C0: + std::memcpy(&this->header.closingSignature, buffer + offset, bytes); + offset += bytes; + + if (this->header.signature != this->header.closingSignature) { + std::uint32_t fileOffset = this->fileOffset; + ReplayRecordHeader record{}; + int count = static_cast(this->header.records) - 1; + + if (count > 0) { + do { + std::fseek(this->file, static_cast(fileOffset), SEEK_SET); + std::fread(&record, sizeof(record), 1, this->file); + fileOffset += record.bytes + sizeof(record); + --count; + } while (count != 0); + } + + this->header.duration = record.tick; + } + break; + case 0x3C1: + std::memcpy(&this->header.service, buffer + offset, bytes); + offset += bytes; + break; + case 0x3C2: + std::memcpy(&this->header.serviceType, buffer + offset, bytes); + offset += bytes; + break; + case 0x3C3: + std::memcpy(&this->header.unknown_0014, buffer + offset, bytes); + offset += bytes; + break; + case 0x3C4: + std::memcpy(this->header.characterName, buffer + offset, bytes); + offset += bytes; + break; + case 0x3C5: + std::memcpy(this->header.mapName, buffer + offset, bytes); + offset += bytes; + break; + case 0x3C6: + std::memcpy(&this->header.accountId, buffer + offset, bytes); + offset += bytes; + break; + case 0x3C7: + std::memcpy(&this->header.cellX, buffer + offset, bytes); + offset += bytes; + break; + case 0x3C8: + std::memcpy(&this->header.cellY, buffer + offset, bytes); + offset += bytes; + break; + case 0x3C9: + std::memcpy(&this->header.unknown_0078, buffer + offset, bytes); + offset += bytes; + break; + case 0x3CA: + std::memcpy(&this->header.duration, buffer + offset, bytes); + offset += bytes; + break; + case 0x3CB: + std::memcpy(&this->header.records, buffer + offset, bytes); + offset += bytes; + break; + case 0x3CC: + std::memcpy(&this->header.fileOffset, buffer + offset, bytes); + offset += bytes; + break; + case 0x3CD: + std::memcpy(&this->header.options, buffer + offset, bytes); + offset += bytes; + break; + default: + offset += bytes; + break; + } + } while (offset < size); + } + + return offset; +} + // OFFSET: 0x00B85350 // FUNCTION: fn_19323 bool Replay::ReadRecord(unsigned char* buffer, std::uint16_t& id, bool force) { @@ -501,6 +615,58 @@ void Replay::Rewind() { this->fileOffset = this->sections[this->FindSection(1)].fileOffset; } +// OFFSET: 0x00B83EC0 +// FUNCTION: fn_19311 +void ReplayFileList::Clear() { + this->selected = -1; + this->files.erase(this->files.begin(), this->files.end()); +} + +// OFFSET: 0x00B84B60 +// FUNCTION: fn_19321 +void ReplayFileList::Refresh() { + this->Clear(); + + char directory[260] = {}; + GetCurrentDirectoryA(sizeof(directory), directory); + + std::string pattern(directory, std::strlen(directory)); + pattern.append("\\Replay\\*.rrf", 13); + + WIN32_FIND_DATAA found; + HANDLE search = FindFirstFileA(pattern.c_str(), &found); + + if (search != INVALID_HANDLE_VALUE) { + BOOL more = TRUE; + + while (more) { + if ((found.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + const std::size_t length = std::strlen(found.cFileName); + const char* extension = found.cFileName + length - 4; + + if (extension[0] == '.' && extension[1] == 'r' && extension[2] == 'r' && extension[3] == 'f') { + ReplayFileEntry entry = {}; + std::memcpy(entry.name, found.cFileName, length); + entry.name[length] = '\0'; + entry.unknown_0000 = 0; + + if (length == 23) { + char number[6] = {}; + std::memcpy(number, entry.name + 15, 4); + entry.unknown_0000 = std::atoi(number); + } + + this->files.push_back(entry); + } + } + + more = FindNextFileA(search, &found); + } + } + + FindClose(search); +} + // OFFSET: 0x00B84300 // FUNCTION: fn_19313 const char* ReplayFileList::FileName(int index) const { diff --git a/Source/Client/Replay.h b/Source/Client/Replay.h index 2f60cca4..9513fda5 100644 --- a/Source/Client/Replay.h +++ b/Source/Client/Replay.h @@ -118,6 +118,7 @@ public: bool WriteSection(int id, std::vector& bytes, int event); int WriteHeader(int event); std::vector BuildMetadata(); + std::uint32_t ParseMetadata(const std::vector& data); float AnimationScale() const; float DelayScale() const; bool SetState(int state); @@ -153,6 +154,8 @@ public: /* 0014 */ char path[0x104]; /* 0118 */ bool pathEnabled; + void Clear(); + void Refresh(); const char* FileName(int index) const; char* SetPath(const char* value); bool Select(int index);