t8-mod/source/proxy-dll/demonware/fileshare.cpp
2023-11-10 14:08:30 -08:00

269 lines
8.2 KiB
C++

#include <std_include.hpp>
#include "fileshare.hpp"
#include "component/platform.hpp"
#include <utilities/io.hpp>
#include <utilities/string.hpp>
#include <utilities/cryptography.hpp>
namespace demonware::fileshare
{
const char* get_fileshare_host_name()
{
return "ops4-fileshare.prod.schild.net";
}
const char* get_category_extension(fileshareCategory_e cat)
{
switch (cat)
{
case fileshareCategory_e::FILESHARE_CATEGORY_FILM:
return "demo";
case fileshareCategory_e::FILESHARE_CATEGORY_SCREENSHOT_PRIVATE:
return "jpg";
default:
return "";
}
}
fileshareCategory_e get_extension_category(const char* ext)
{
if (!strcmp(ext, "demo")) {
return fileshareCategory_e::FILESHARE_CATEGORY_FILM;
}
else if (!strcmp(ext, "jpg")) {
return fileshareCategory_e::FILESHARE_CATEGORY_SCREENSHOT_PRIVATE;
}
else {
return fileshareCategory_e::FILESHARE_CATEGORY_ALL;
}
}
std::string get_fileshare_directory()
{
return std::format("players/fileshare-{}", platform::bnet_get_userid());
}
std::string get_file_name(const uint64_t fileID, fileshareCategory_e category)
{
const char* extension = get_category_extension(category);
if(!extension || !strlen(extension)) return std::to_string(fileID);
return std::format("{}.{}", fileID, extension);
}
std::string get_file_url(const std::string& file)
{
return std::format("http://{}/{}", get_fileshare_host_name(), utilities::io::file_name(file));
}
std::string get_file_path(const std::string& file)
{
return std::format("{}/{}", get_fileshare_directory(), utilities::io::file_name(file));
}
std::string get_metadata_path(const std::string& file)
{
return std::format("{}/{}.metadata", get_fileshare_directory(), utilities::io::file_stem(file));
}
std::string FileMetadata::SerializeMetaJSON()
{
rapidjson::Document jsonDocument(rapidjson::kObjectType);
rapidjson::Document::AllocatorType& allocator = jsonDocument.GetAllocator();
jsonDocument.AddMember("status", this->status, allocator);
jsonDocument.AddMember("category", this->category, allocator);
jsonDocument.AddMember("fileName", this->fileName, allocator);
if (this->status == FILE_STATUS_UPLOADED || this->status == FILE_STATUS_DESCRIBED) {
jsonDocument.AddMember("fileSize", this->fileSize, allocator);
}
rapidjson::Value fileInfo(rapidjson::kObjectType);
fileInfo.AddMember("id", this->file.id, allocator);
fileInfo.AddMember("name", this->file.name, allocator);
if (this->status == FILE_STATUS_UPLOADED || this->status == FILE_STATUS_DESCRIBED) {
fileInfo.AddMember("size", this->file.size, allocator);
}
fileInfo.AddMember("timestamp", this->file.timestamp, allocator);
jsonDocument.AddMember("file", fileInfo, allocator);
rapidjson::Value fileAuthor(rapidjson::kObjectType);
fileAuthor.AddMember("name", this->author.name, allocator);
fileAuthor.AddMember("xuid", this->author.xuid, allocator);
jsonDocument.AddMember("author", fileAuthor, allocator);
if (this->status == FILE_STATUS_DESCRIBED) {
rapidjson::Value fileTags(rapidjson::kObjectType);
for (const auto& tag : this->tags)
{
rapidjson::Value key(std::to_string(tag.first), allocator);
fileTags.AddMember(key, tag.second, allocator);
}
jsonDocument.AddMember("tags", fileTags, allocator);
jsonDocument.AddMember("metadata", utilities::cryptography::base64::encode(ddl_metadata), allocator);
}
rapidjson::StringBuffer strbuf;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(strbuf);
jsonDocument.Accept(writer);
return std::string(strbuf.GetString(), strbuf.GetLength());
}
bool FileMetadata::ParseMetaJSON(const std::string& MetaDoc)
{
rapidjson::Document jsonDocument;
jsonDocument.Parse(MetaDoc);
if (jsonDocument.HasMember("status") && jsonDocument["status"].IsInt()) {
this->status = static_cast<file_status>(jsonDocument["status"].GetInt());
}
if (jsonDocument.HasMember("category") && jsonDocument["category"].IsInt()) {
this->category = static_cast<fileshareCategory_e>(jsonDocument["category"].GetInt());
}
if (jsonDocument.HasMember("fileName") && jsonDocument["fileName"].IsString()) {
this->fileName = jsonDocument["fileName"].GetString();
}
if ((this->status == FILE_STATUS_UPLOADED || this->status == FILE_STATUS_DESCRIBED)
&& jsonDocument.HasMember("fileSize") && jsonDocument["fileSize"].IsUint64())
{
this->fileSize = jsonDocument["fileSize"].GetUint64();
}
if (jsonDocument.HasMember("file") && jsonDocument["file"].IsObject())
{
rapidjson::Value& fileInfo = jsonDocument["file"];
if (fileInfo.HasMember("id") && fileInfo["id"].IsUint64()
&& fileInfo.HasMember("name") && fileInfo["name"].IsString()
&& fileInfo.HasMember("timestamp") && fileInfo["timestamp"].IsUint())
{
this->file.id = fileInfo["id"].GetUint64();
this->file.name = fileInfo["name"].GetString();
this->file.timestamp = fileInfo["timestamp"].GetUint();
}
if ((this->status == FILE_STATUS_UPLOADED || this->status == FILE_STATUS_DESCRIBED)
&& fileInfo.HasMember("size") && fileInfo["size"].IsUint())
{
this->file.size = fileInfo["size"].GetUint();
}
}
if (jsonDocument.HasMember("author") && jsonDocument["author"].IsObject())
{
rapidjson::Value& fileAuthor = jsonDocument["author"];
if (fileAuthor.HasMember("name") && fileAuthor["name"].IsString()
&& fileAuthor.HasMember("xuid") && fileAuthor["xuid"].IsUint64())
{
this->author.name = fileAuthor["name"].GetString();
this->author.xuid = fileAuthor["xuid"].GetUint64();
}
}
if (this->status == FILE_STATUS_DESCRIBED) {
if (jsonDocument.HasMember("tags") && jsonDocument["tags"].IsObject())
{
rapidjson::Value& fileTags = jsonDocument["tags"];
for (rapidjson::Value::ConstMemberIterator itr =
fileTags.MemberBegin(); itr != fileTags.MemberEnd(); ++itr)
{
this->tags.insert({ atoi(itr->name.GetString()), itr->value.GetUint64() });
}
}
if (jsonDocument.HasMember("metadata") && jsonDocument["metadata"].IsString())
{
rapidjson::Value& ddl_field = jsonDocument["metadata"];
std::string metadata_b64(ddl_field.GetString(), ddl_field.GetStringLength());
this->ddl_metadata = utilities::cryptography::base64::decode(metadata_b64);
}
}
if (this->status == FILE_STATUS_DESCRIBED) {
return (this->fileName.size() && this->file.id && this->ddl_metadata.size());
}
else if (this->status == FILE_STATUS_UPLOADED) {
return (this->fileName.size() && this->file.id && this->fileSize);
}
else {
return (this->fileName.size() && this->file.id && this->file.name.size());
}
}
bool FileMetadata::MetadataTaskResult(bdFileMetaData* output, bool download)
{
if (this->status == FILE_STATUS_DESCRIBED)
{
output->m_fileID = this->file.id;
output->m_createTime = this->file.timestamp;
output->m_modifedTime = output->m_createTime;
output->m_fileSize = this->fileSize;
output->m_ownerID = this->author.xuid;
output->m_ownerName = this->author.name;
output->m_fileSlot = 0;
output->m_fileName = this->file.name;
output->m_category = this->category;
output->m_metaData = this->ddl_metadata;
output->m_summaryFileSize = 0; // what!?
if (download) {
output->m_url = get_file_url(this->fileName);
}
else {
output->m_tags = this->tags;
}
return true;
}
else {
return false;
}
}
bool FileMetadata::ReadMetaDataJson(const std::string& file, file_status expect)
{
std::string json;
if (utilities::io::read_file(file, &json))
{
return (this->ParseMetaJSON(json)
&& (this->status == expect || expect == FILE_STATUS_UNKNOWN));
}
else {
return false;
}
}
bool FileMetadata::WriteMetaDataJson(const std::string& file, file_status status)
{
if(status != FILE_STATUS_UNKNOWN) this->status = status;
return utilities::io::write_file(file, this->SerializeMetaJSON());
}
std::vector<uint64_t> fileshare_list_demo_ids()
{
std::vector<uint64_t> results;
std::vector<std::string> files = utilities::io::list_files(get_fileshare_directory());
for (auto& file : files)
{
std::string rawName = utilities::io::file_stem(file);
if (utilities::string::is_integer(rawName)
&& utilities::string::ends_with(file, ".demo")
&& utilities::io::file_exists(get_metadata_path(rawName)))
{
results.push_back(std::stoull(rawName));
}
}
return results;
}
}