Skip to content

Instantly share code, notes, and snippets.

@rsp4jack
Last active July 9, 2022 02:37
Show Gist options
  • Save rsp4jack/717fc5d549d67ce69be3ee75e2bb509c to your computer and use it in GitHub Desktop.
Save rsp4jack/717fc5d549d67ce69be3ee75e2bb509c to your computer and use it in GitHub Desktop.
Emule Known.met parser
// Under GPLv3
// you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version.
// This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#include "c8r2mb.hpp"
#include <algorithm>
#include <array>
#include <bit>
#include <cstdint>
#include <cuchar>
#include <fstream>
#include <iostream>
#include <span>
#include <string>
#include <tuple>
#include <variant>
#include <vector>
#include <cppcodec/base32_rfc4648.hpp>
#include <unordered_map>
#include <functional>
using namespace std;
constexpr uint8_t MET_HEADER = 0x0E;
constexpr uint8_t MET_HEADER_I64TAGS = 0x0F;
constexpr size_t MD4Len = 16;
constexpr size_t AICHLen = 20;
using MD4Hash = array<uint8_t, MD4Len>;
using EMFileSize = unsigned long long;
using AICHHash = array<uint8_t, AICHLen>;
using Blob = span<char>;
enum {
TAGTYPE_HASH = 0x01,
TAGTYPE_STRING = 0x02,
TAGTYPE_UINT32 = 0x03,
TAGTYPE_FLOAT32 = 0x04,
TAGTYPE_BOOL = 0x05,
TAGTYPE_BOOLARRAY = 0x06,
TAGTYPE_BLOB = 0x07,
TAGTYPE_UINT16 = 0x08,
TAGTYPE_UINT8 = 0x09,
TAGTYPE_BSOB = 0x0A,
TAGTYPE_UINT64 = 0x0B,
TAGTYPE_STR1 = 0x11,
TAGTYPE_STR2 = 0x12,
TAGTYPE_STR3 = 0x13,
TAGTYPE_STR4 = 0x14,
TAGTYPE_STR5 = 0x15,
TAGTYPE_STR6 = 0x16,
TAGTYPE_STR7 = 0x17,
TAGTYPE_STR8 = 0x18,
TAGTYPE_STR9 = 0x19,
TAGTYPE_STR10 = 0x1A,
TAGTYPE_STR11 = 0x1B,
TAGTYPE_STR12 = 0x1C,
TAGTYPE_STR13 = 0x1D,
TAGTYPE_STR14 = 0x1E,
TAGTYPE_STR15 = 0x1F,
TAGTYPE_STR16 = 0x20,
TAGTYPE_STR17 = 0x21, // accepted by eMule 0.42f (02-Mai-2004) in receiving
// code only because of a flaw, those tags are handled
// correctly, but should not be handled at all
TAGTYPE_STR18 = 0x22, // accepted by eMule 0.42f (02-Mai-2004) in receiving
// code only because of a flaw, those tags are handled
// correctly, but should not be handled at all
TAGTYPE_STR19 = 0x23, // accepted by eMule 0.42f (02-Mai-2004) in receiving
// code only because of a flaw, those tags are handled
// correctly, but should not be handled at all
TAGTYPE_STR20 = 0x24, // accepted by eMule 0.42f (02-Mai-2004) in receiving
// code only because of a flaw, those tags are handled
// correctly, but should not be handled at all
TAGTYPE_STR21 = 0x25, // accepted by eMule 0.42f (02-Mai-2004) in receiving
// code only because of a flaw, those tags are handled
// correctly, but should not be handled at all
TAGTYPE_STR22 = 0x26 // accepted by eMule 0.42f (02-Mai-2004) in receiving
// code only because of a flaw, those tags are handled
// correctly, but should not be handled at all
};
enum {
FT_FILENAME = 0x01, // <string>
FT_FILESIZE = 0x02, // <uint32> (or <uint64> when supported)
FT_FILESIZE_HI = 0x3A, // <uint32>
FT_FILETYPE = 0x03, // <string>
FT_FILEFORMAT = 0x04, // <string>
FT_LASTSEENCOMPLETE = 0x05, // <uint32>
FT_TRANSFERRED = 0x08, // <uint32>
FT_GAPSTART = 0x09, // <uint32>
FT_GAPEND = 0x0A, // <uint32>
FT_DESCRIPTION = 0x0B, // <string>
FT_PARTFILENAME = 0x12, // <string>
// #define FT_PRIORITY 0x13 // Not used anymore
FT_STATUS = 0x14, // <uint32>
FT_SOURCES = 0x15, // <uint32>
FT_PERMISSIONS = 0x16, // <uint32>
// #define FT_ULPRIORITY 0x17 // Not used anymore
FT_DLPRIORITY = 0x18, // Was 13
FT_ULPRIORITY = 0x19, // Was 17
FT_COMPRESSION = 0x1A,
FT_CORRUPTED = 0x1B,
FT_KADLASTPUBLISHKEY = 0x20, // <uint32>
FT_KADLASTPUBLISHSRC = 0x21, // <uint32>
FT_FLAGS = 0x22, // <uint32>
FT_DL_ACTIVE_TIME = 0x23, // <uint32>
FT_CORRUPTEDPARTS = 0x24, // <string>
FT_DL_PREVIEW = 0x25,
FT_KADLASTPUBLISHNOTES = 0x26, // <uint32>
FT_AICH_HASH = 0x27,
FT_FILEHASH = 0x28,
FT_COMPLETE_SOURCES = 0x30, // nr. of sources which share a complete version of the associated
// file (supported by eserver 16.46+)
FT_COLLECTIONAUTHOR = 0x31,
FT_COLLECTIONAUTHORKEY = 0x32,
FT_PUBLISHINFO = 0x33, // <uint32>
FT_LASTSHARED = 0x34, // <uint32>
FT_AICHHASHSET = 0x35, // <uint32>
// statistic
FT_ATTRANSFERRED = 0x50, // <uint32>
FT_ATREQUESTED = 0x51, // <uint32>
FT_ATACCEPTED = 0x52, // <uint32>
FT_CATEGORY = 0x53, // <uint32>
FT_ATTRANSFERREDHI = 0x54, // <uint32>
FT_MAXSOURCES = 0x55, // <uint32>
FT_SPREADSTART = 0x70,
FT_SPREADEND = 0x71,
FT_SPREADCOUNT = 0x72,
// Xman advanced upload-priority
FT_NOTCOUNTEDTRANSFERREDLOW = 0x90,
FT_NOTCOUNTEDTRANSFERREDHIGH = 0x91,
FT_LASTDATAUPDATE = 0x92,
// Xman end
FT_MEDIA_ARTIST = 0xD0, // <string>
FT_MEDIA_ALBUM = 0xD1, // <string>
FT_MEDIA_TITLE = 0xD2, // <string>
FT_MEDIA_LENGTH = 0xD3, // <uint32> !!!
FT_MEDIA_BITRATE = 0xD4, // <uint32>
FT_MEDIA_CODEC = 0xD5, // <string>
FT_FILECOMMENT = 0xF6, // <string>
FT_FILERATING = 0xF7 // <uint8>
};
constexpr auto FT_HIDEOS = "HIDEOS";
constexpr auto FT_SELECTIVE_CHUNK = "SELECT_CHUNK";
constexpr auto FT_SHAREONLYTHENEED = "SHARE_ONLY_THE_NEED";
constexpr auto FT_POWERSHARE = "ZZUL_POWERSHARE";
constexpr auto FT_POWERSHARE_LIMIT = "POWERSHARE_LIMIT";
constexpr auto FT_PS_AMOUNT_LIMIT = "PS_AMOUNT_LIMIT";
constexpr size_t MAX_PS_AMOUNT_LIMIT = 10000;
enum : char {
TAG_FILENAME = '\x01', // <string>
TAG_FILESIZE = '\x02', // <uint32>
TAG_FILESIZE_HI = '\x3A', // <uint32>
TAG_FILETYPE = '\x03', // <string>
TAG_FILEFORMAT = '\x04', // <string>
TAG_COLLECTION = '\x05',
TAG_PART_PATH = '\x06', // <string>
TAG_PART_HASH = '\x07',
TAG_TRANSFERRED = '\x08', // <uint32>
TAG_GAPSTART = '\x09', // <uint32>
TAG_GAPEND = '\x0A', // <uint32>
TAG_DESCRIPTION = '\x0B', // <string>
TAG_PING = '\x0C',
TAG_FAIL = '\x0D',
TAG_PREFERENCE = '\x0E',
TAG_PORT = '\x0F',
TAG_IP_ADDRESS = '\x10',
TAG_VERSION = '\x11', // <string>
TAG_PARTFILENAME = '\x12', // <string>
TAG_PRIORITY = '\x13', // <uint32>
TAG_STATUS = '\x14', // <uint32>
TAG_SOURCES = '\x15', // <uint32>
TAG_PERMISSIONS = '\x16',
TAG_PARTS = '\x17',
TAG_COMPLETE_SOURCES = '\x30',
TAG_PUBLISHINFO = '\x33', // <uint32>
TAG_KADAICHHASHPUB = '\x36', // <AICH Hash>
TAG_KADAICHHASHRESULT = '\x37', // <Count 1>{<Publishers 1><AICH Hash> Count}
TAG_MEDIA_ARTIST = '\xD0', // <string>
TAG_MEDIA_ALBUM = '\xD1', // <string>
TAG_MEDIA_TITLE = '\xD2', // <string>
TAG_MEDIA_LENGTH = '\xD3', // <uint32> !!!
TAG_MEDIA_BITRATE = '\xD4', // <uint32>
TAG_MEDIA_CODEC = '\xD5', // <string>
TAG_KADMISCOPTIONS = '\xF2', // <uint8>
TAG_ENCRYPTION = '\xF3', // <uint8>
TAG_USER_COUNT = '\xF4', // <uint32>
TAG_FILE_COUNT = '\xF5', // <uint32>
TAG_FILECOMMENT = '\xF6', // <string>
TAG_FILERATING = '\xF7', // <uint8>
TAG_BUDDYHASH = '\xF8', // <string>
TAG_CLIENTLOWID = '\xF9', // <uint32>
TAG_SERVERPORT = '\xFA', // <uint16>
TAG_SERVERIP = '\xFB', // <uint32>
TAG_SOURCEUPORT = '\xFC', // <uint16>
TAG_SOURCEPORT = '\xFD', // <uint16>
TAG_SOURCEIP = '\xFE', // <uint32>
TAG_SOURCETYPE = '\xFF' // <uint8>
};
enum : uint8_t {
PR_LOW = 0,
PR_NORMAL = 1, // Don't change this - needed for edonkey clients and server!
PR_HIGH = 2,
PR_VERYHIGH = 3,
PR_VERYLOW = 4, // I Had to change this because it didn't save negative number correctly.. Had to modify the sort function for this change..
PR_AUTO = 5, // UAP Hunter
PR_POWER = 6 // Xman PowerRelease
};
struct Tag {
Tag() noexcept = default;
~Tag() noexcept
{
if (holds_alternative<Blob>(data)) {
delete[] get<Blob>(data).data();
}
}
Tag(const Tag& rhs) noexcept
: type(rhs.type)
, name(rhs.name)
, nameID(rhs.nameID)
, data(rhs.data)
{
if (holds_alternative<Blob>(rhs.data)) {
const auto& rdata = get<Blob>(rhs.data);
auto dat = Blob(new(nothrow) char[rdata.size()], rdata.size());
memcpy(dat.data(), rdata.data(), rdata.size_bytes());
}
}
Tag(Tag&&) noexcept = default;
Tag& operator=(const Tag& rhs) noexcept
{
// What's this?
// This will call move assign operator. Move op will deconstruct *this and move Tag(rhs) in *this.
*this = Tag(rhs);
return *this;
}
Tag& operator=(Tag&&) noexcept = default;
uint8_t type{}; // data type
string name;
uint8_t nameID{};
variant<string, uint32_t, Blob, float, MD4Hash, uint64_t, uint16_t, uint8_t>
data;
};
struct FileStatistic {
uint32_t requested = 0;
uint32_t accepted = 0;
uint64_t transferred = 0;
uint32_t alltimerequested = 0;
uint64_t alltimetransferred = 0;
uint32_t alltimeaccepted = 0;
uint32_t m_uFileupdatetime = 0;
bool InChangedSpreadSortValue = false;
bool InChangedFullSpreadCount = false;
bool InChangedSpreadBar = false;
int lastSize = 0;
bool lastbFlat = false;
float lastSpreadSortValue = 0;
float lastFullSpreadCount = 0;
uint64_t m_unotcountedtransferred = 0;
uint32_t m_tlastdataupdate = 0;
vector<uint64_t> spreadBar; // Stulle use CRBMap in Emule. Seems they do not have container like std::vector.
};
struct KnownFile {
uint32_t utcLastModified{}; // When it is UTC 2038/1/19 03:14:07, Emule will be
// gone away.
MD4Hash checkID{}; // md4 of parthashes concat
uint16_t partCount{};
vector<MD4Hash> partHashes{};
AICHHash aichHash{};
bool hasAICHHash = false;
uint32_t tagCount{};
vector<Tag> tags{};
// high level meta
string fileName{};
EMFileSize fileSize{};
FileStatistic statistic;
uint8_t uploadPriority{};
bool autoUploadPriority{};
uint32_t m_lastPublishTimeKadSrc{};
uint32_t m_lastPublishTimeKadNotes{};
uint32_t metaDataVersion{};
uint32_t timeLastSeen{}; // too, 32bit time_t
vector<AICHHash> aichHashes;
int powershared{};
int hideos{};
int selectiveChunk{};
int shareOnlyTheNeed{};
int PSAmountLimit{};
};
struct KnownMet {
uint8_t header;
bool support64bitTag;
uint32_t length;
vector<KnownFile> files;
};
static uint8_t readU8(istream* stream)
{
stream->exceptions(ios::badbit);
return static_cast<uint8_t>(static_cast<char>(stream->get()));
}
static uint16_t readU16(istream* stream, bool bswap)
{
stream->exceptions(ios::badbit);
uint16_t result;
stream->read(reinterpret_cast<char*>(&result), 2);
if (bswap) {
result = _byteswap_ushort(result);
}
return result;
}
static uint32_t readU32(istream* stream, bool bswap)
{
stream->exceptions(ios::badbit);
uint32_t result;
stream->read(reinterpret_cast<char*>(&result), 4);
if (bswap) {
result = _byteswap_ulong(result);
}
return result;
}
static uint64_t readU64(istream* stream, bool bswap)
{
stream->exceptions(ios::badbit);
uint64_t result;
stream->read(reinterpret_cast<char*>(&result), 8);
if (bswap) {
result = _byteswap_uint64(result);
}
return result;
}
static void readVar(void* target, istream* stream, streamsize len, bool bswap)
{
stream->exceptions(ios::badbit);
stream->read(reinterpret_cast<char*>(target), len);
if (bswap) {
reverse(reinterpret_cast<char*>(target), reinterpret_cast<char*>(target) + len);
}
}
// UTF-8 Support
static string readString(istream* stream, uint16_t size, bool utf8, bool bswap)
{
auto* mem = new char[size + 1]; // +1 for '\0'
auto umem = unique_ptr<char[]>(mem);
mem[size] = '\0';
auto* base = mem;
readVar(base, stream, size, bswap);
bool isUTF8 = false;
if (size >= 3 && static_cast<unsigned char>(base[0]) == 0xEFU && static_cast<unsigned char>(base[1]) == 0xBBU && static_cast<unsigned char>(base[2]) == 0XBFU) { // UTF-8 BOM
base += 3;
isUTF8 = true;
} else if (utf8) {
isUTF8 = true;
}
char* out = mem;
if (isUTF8) {
c8r2mb_s(reinterpret_cast<char*>(&out), 0, reinterpret_cast<char8_t*>(base), true);
}
return {out};
}
static Tag readTag(istream* stream, bool useUTF8, bool bswap)
{
Tag tag;
tag.type = readU8(stream);
if ((tag.type & 0x80) != 0) {
tag.type &= 0x7F;
tag.nameID = readU8(stream);
tag.name.clear();
} else {
uint16_t length = readU16(stream, bswap);
if (length == 1) {
tag.nameID = readU8(stream);
tag.name.clear();
} else {
tag.nameID = 0;
tag.name.resize(length);
readVar(tag.name.data(), stream, length, bswap);
}
}
switch (tag.type) {
case TAGTYPE_STRING: {
uint16_t length = readU16(stream, bswap);
tag.data = readString(stream, length, useUTF8, bswap);
break;
}
case TAGTYPE_UINT32:
tag.data = readU32(stream, bswap);
break;
case TAGTYPE_UINT64:
tag.data = readU64(stream, bswap);
break;
case TAGTYPE_UINT16:
tag.data = readU16(stream, bswap);
tag.type = TAGTYPE_UINT32; // TODO: That's weird.
break;
case TAGTYPE_UINT8:
tag.data = readU8(stream);
tag.type = TAGTYPE_UINT32; // TODO: Weird behavior
break;
case TAGTYPE_FLOAT32:
float value;
readVar(&value, stream, 4, bswap);
tag.data = value;
break;
// Skip fixed-size strings
case TAGTYPE_HASH:
MD4Hash hash;
readVar(hash.data(), stream, MD4Len, bswap);
tag.data = hash;
break;
case TAGTYPE_BOOL:
cout << "Warning: Reading BOOL tag, skip" << endl;
stream->seekg(1, ios::cur);
break;
case TAGTYPE_BOOLARRAY:
cout << "Warning: Reading BOOL ARRAY tag, skip" << endl;
// 07-Apr-2004: eMule versions prior to 0.42e.29 used the formula
// "(len+7)/8"!
stream->seekg((readU16(stream, bswap)), ios::cur);
break;
case TAGTYPE_BLOB: {
// 07-Apr-2004: eMule versions prior to 0.42e.29 handled the "len" as int16!
uint32_t size = readU32(stream, bswap);
auto* data = new char[size];
try {
readVar(data, stream, size, bswap);
} catch (const std::ios_base::failure& e) {
if (stream->eof()) {
cerr << "[Error] Unexcepted EOF when reading blob" << endl;
}
delete[] data;
data = nullptr;
throw e;
}
tag.data = span<char>(data, size);
break;
}
default:
if (tag.type >= TAGTYPE_STR1 && tag.type <= TAGTYPE_STR16) {
uint16_t length = tag.type - TAGTYPE_STR1 + 1;
tag.data = readString(stream, length, useUTF8, bswap);
}
}
return tag;
}
template <class T>
constexpr void variantHelper(const auto& var, const auto& func)
{
assert(holds_alternative<T>(var));
if (holds_alternative<T>(var)) {
func();
}
}
// If readOnly is true, the stream will close after call.
// Otherwise the stream will not close and open in read and write mode.
static tuple<bool, KnownMet, unique_ptr<fstream>>
loadKnown(const string& filename,bool littleendian = true) // file's byte order
{
KnownMet result{};
auto stream = make_unique<fstream>();
bool ok = true;
bool bswap = false;
stream->open(filename, ios::in | ios::binary);
if (littleendian != (endian::native == endian::little)) {
bswap = true;
}
result.header = readU8(stream.get());
switch (result.header) {
case MET_HEADER:
cout << "Does not support 64 bits tag" << endl;
result.support64bitTag = false;
break;
case MET_HEADER_I64TAGS:
cout << "Does support 64 bits tag" << endl;
result.support64bitTag = true;
break;
default:
cout << "Invaild Header:" << std::hex << result.header << std::dec << endl;
return {false, std::move(result), std::move(stream)};
}
result.length = readU32(stream.get(), bswap);
result.files.resize(result.length);
for (uint32_t ef4az7euc8 = 0; ef4az7euc8 < result.length; ++ef4az7euc8) {
auto& file = result.files.at(ef4az7euc8);
file.utcLastModified = readU32(stream.get(), bswap);
// Hash
readVar(file.checkID.data(), stream.get(), MD4Len, bswap);
file.partCount = readU16(stream.get(), bswap);
file.partHashes.resize(file.partCount);
for (uint16_t j = 0; j < file.partCount; ++j) {
readVar((file.partHashes.data() + j)->data(), stream.get(), MD4Len, bswap);
}
// Tag
unordered_map<uint32_t, uint64_t> spread_start_map;
unordered_map<uint32_t, uint64_t> spread_end_map;
unordered_map<uint32_t, uint64_t> spread_count_map;
file.tagCount = readU32(stream.get(), bswap);
file.tags.resize(file.tagCount);
for (uint32_t l0z7f1 = 0; l0z7f1 < file.tagCount; ++l0z7f1) {
file.tags[l0z7f1] = readTag(stream.get(), false, bswap);
auto& tag = file.tags[l0z7f1];
bool hadAICHHashSetTag = false;
switch (tag.nameID) {
case FT_FILENAME:
variantHelper<string>(tag.data, [&]() {
if (file.fileName.empty()) {
file.fileName = get<string>(tag.data);
}
});
break;
case FT_FILESIZE:
variantHelper<uint64_t>(
tag.data, [&]() { file.fileSize = get<uint64_t>(tag.data); }
);
break;
case FT_ATTRANSFERRED:
variantHelper<uint32_t>(tag.data, [&]() {
file.statistic.alltimetransferred = get<uint32_t>(tag.data);
});
break;
case FT_ATTRANSFERREDHI:
variantHelper<uint32_t>(tag.data, [&]() {
file.statistic.alltimetransferred = (static_cast<uint64_t>(get<uint32_t>(tag.data)) << 32) | file.statistic.alltimetransferred;
});
break;
case FT_ATREQUESTED:
variantHelper<uint32_t>(tag.data, [&]() {
file.statistic.alltimerequested = get<uint32_t>(tag.data);
});
break;
case FT_ATACCEPTED:
variantHelper<uint32_t>(tag.data, [&]() {
file.statistic.alltimeaccepted = get<uint32_t>(tag.data);
});
break;
case FT_ULPRIORITY:
variantHelper<uint32_t>(tag.data, [&]() {
file.uploadPriority = static_cast<uint8_t>(get<uint32_t>(tag.data));
/*
This is a editor, not emule. just show raw data
if (file.uploadPriority == PR_AUTO) {
file.uploadPriority = PR_HIGH;
file.autoUploadPriority = true;
} else {
if (file.uploadPriority > PR_POWER) {
file.uploadPriority = PR_NORMAL;
}
file.autoUploadPriority = false;
}
*/
});
break;
case FT_KADLASTPUBLISHSRC:
variantHelper<uint32_t>(tag.data, [&]() {
file.m_lastPublishTimeKadSrc = get<uint32_t>(tag.data);
// too.
});
break;
case FT_KADLASTPUBLISHNOTES:
variantHelper<uint32_t>(tag.data, [&]() {
file.m_lastPublishTimeKadNotes = get<uint32_t>(tag.data);
});
break;
case FT_FLAGS:
// Misc. Flags
// ------------------------------------------------------------------------------
// Bits 3-0: Meta data version
// 0 untrusted meta data which was received via search results
// 1 trusted meta data, Unicode (strings where not stored correctly)
// 2 0.49c: trusted meta data, Unicode
// Bits 31-4: Reserved
variantHelper<uint32_t>(tag.data, [&]() {
file.metaDataVersion = get<uint32_t>(tag.data) & 0x0F;
});
break;
case FT_PERMISSIONS:
// deprecated
variantHelper<uint32_t>(tag.data, []() {});
break;
case FT_KADLASTPUBLISHKEY:
// deprecated
variantHelper<uint32_t>(tag.data, []() {});
break;
case FT_AICH_HASH:
variantHelper<string>(tag.data, [&]() {
if (cppcodec::base32_rfc4648::decode(file.aichHash.data(), AICHLen, get<string>(tag.data)) != AICHLen) {
assert(false);
cerr << "[Error] Base32 A.I.C.H. length is not 20 bytes" << endl;
file.aichHash.fill(0);
file.hasAICHHash = false;
} else {
file.hasAICHHash = true;
}
});
break;
case FT_LASTSHARED:
variantHelper<uint32_t>(tag.data, [&]() {
file.timeLastSeen = get<uint32_t>(tag.data);
});
break;
case FT_AICHHASHSET:
variantHelper<Blob>(tag.data, [&]() {
AICHHash masterHash;
readVar(masterHash.data(), stream.get(), AICHLen, bswap);
if (file.hasAICHHash && masterHash != file.aichHash) {
assert(false);
cerr << "[Error] Loading AICH Part Hashset error: HashSet Masterhash doesn't matches with existing masterhash - hashset not loaded";
} else {
uint16_t count = readU16(stream.get(), bswap);
for (int i = 0; i < count; ++i) {
AICHHash hash;
readVar(hash.data(), stream.get(), AICHLen, bswap);
file.aichHashes.push_back(hash);
}
}
hadAICHHashSetTag = true;
});
break;
case FT_NOTCOUNTEDTRANSFERREDLOW:
variantHelper<uint32_t>(tag.data, [&]() {
file.statistic.m_unotcountedtransferred = get<uint32_t>(tag.data);
});
break;
case FT_NOTCOUNTEDTRANSFERREDHIGH:
variantHelper<uint32_t>(tag.data, [&]() {
file.statistic.m_unotcountedtransferred = static_cast<uint64_t>(get<uint32_t>(tag.data)) | file.statistic.m_unotcountedtransferred;
});
break;
case FT_LASTDATAUPDATE:
variantHelper<uint32_t>(tag.data, [&]() {
file.statistic.m_tlastdataupdate = get<uint32_t>(tag.data);
});
break;
default:
if ((tag.nameID == 0U) && holds_alternative<uint64_t>(tag.data) && !tag.name.empty()) {
uint32_t spreadKey = stoi(tag.name.substr(1));
if (tag.name[0] == FT_SPREADSTART) {
spread_start_map.at(spreadKey) = get<uint64_t>(tag.data);
} else if (tag.name[0] == FT_SPREADEND) {
spread_end_map.at(spreadKey) = get<uint64_t>(tag.data);
} else if (tag.name[0] == FT_SPREADCOUNT) {
spread_count_map.at(spreadKey) = get<uint64_t>(tag.data);
} else if (tag.name == FT_POWERSHARE) {
file.powershared = (get<uint32_t>(tag.data) <= 3 ? get<uint32_t>(tag.data) : -1);
} else if (tag.name == FT_HIDEOS) {
file.hideos = (get<uint32_t>(tag.data) <= 10 ? get<uint32_t>(tag.data) : -1);
} else if (tag.name == FT_SELECTIVE_CHUNK) {
file.selectiveChunk = (get<uint32_t>(tag.data) <= 1 ? get<uint32_t>(tag.data) : -1);
} else if (tag.name == FT_SHAREONLYTHENEED) {
file.shareOnlyTheNeed = (get<uint32_t>(tag.data) <= 1 ? get<uint32_t>(tag.data) : -1);
} else if (tag.name == FT_PS_AMOUNT_LIMIT) {
file.PSAmountLimit = (get<uint32_t>(tag.data) <= MAX_PS_AMOUNT_LIMIT ? get<uint32_t>(tag.data) : -1);
}
}
} // switch end
if (hadAICHHashSetTag) {
// TODO: verify aich hash set.
}
for (const auto& i : spread_start_map) {
const uint32_t& spreadKey = i.first;
if (!spread_end_map.contains(spreadKey) || !spread_count_map.contains(spreadKey)) {
continue;
}
const uint64_t& spread_start = i.second;
const uint64_t& spread_end = spread_end_map.at(spreadKey);
const uint64_t& spread_count = spread_count_map.at(spreadKey);
if (spread_count == 0 || spread_start > spread_end) {
continue;
}
transform(file.statistic.spreadBar.cbegin(), file.statistic.spreadBar.cend(), file.statistic.spreadBar.begin(), [&](uint64_t a) {
return a + spread_count;
});
}
// TODO: clean tags by meta data version: KnownFile.cpp:1159
}
}
return {true, std::move(result), std::move(stream)};
}
@rsp4jack
Copy link
Author

rsp4jack commented Jul 9, 2022

Known.met Parser

This is a Known.met parser for Emule. This is just an example code, not able to run and with many errors.
The code is under GPLv3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment