mirror of
https://onedev.site.tesses.net/tesses-framework
synced 2026-02-08 15:55:46 +00:00
Push failed torrent code as backup before I remove it
This commit is contained in:
379
src/BitTorrent/TorrentFile.cpp
Normal file
379
src/BitTorrent/TorrentFile.cpp
Normal file
@@ -0,0 +1,379 @@
|
||||
#include "TessesFramework/BitTorrent/TorrentFile.hpp"
|
||||
#include "TessesFramework/Streams/MemoryStream.hpp"
|
||||
#include "TessesFramework/Crypto/Crypto.hpp"
|
||||
#include "TessesFramework/Http/HttpUtils.hpp"
|
||||
namespace Tesses::Framework::BitTorrent
|
||||
{
|
||||
|
||||
TorrentFileInfo::TorrentFileInfo()
|
||||
{
|
||||
this->fileListOrLength = 0;
|
||||
}
|
||||
TorrentFileInfo::TorrentFileInfo(const Serialization::Bencode::BeDictionary& tkn)
|
||||
{
|
||||
Load(tkn);
|
||||
}
|
||||
int64_t TorrentFileInfo::GetTorrentFileSize()
|
||||
{
|
||||
if(std::holds_alternative<int64_t>(this->fileListOrLength))
|
||||
return std::get<int64_t>(fileListOrLength);
|
||||
else if(std::holds_alternative<std::vector<TorrentFileEntry>>(this->fileListOrLength)) {
|
||||
auto& files= std::get<std::vector<TorrentFileEntry>>(this->fileListOrLength);
|
||||
int64_t no=0;
|
||||
for(auto itm : files)
|
||||
{
|
||||
no += itm.length;
|
||||
}
|
||||
return no;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Serialization::Bencode::BeDictionary& TorrentFileInfo::GenerateInfoDictionary()
|
||||
{
|
||||
this->info = {};
|
||||
this->info.tokens.emplace_back("name",this->name);
|
||||
this->info.tokens.emplace_back("piece length",this->piece_length);
|
||||
this->info.tokens.emplace_back("pieces",this->pieces);
|
||||
if(this->isPrivate)
|
||||
this->info.tokens.emplace_back("private",1);
|
||||
|
||||
if(std::holds_alternative<int64_t>(this->fileListOrLength))
|
||||
{
|
||||
this->info.tokens.emplace_back("length",std::get<int64_t>(this->fileListOrLength));
|
||||
}
|
||||
else if(std::holds_alternative<std::vector<TorrentFileEntry>>(this->fileListOrLength))
|
||||
{
|
||||
Serialization::Bencode::BeArray a;
|
||||
for(auto& item : std::get<std::vector<TorrentFileEntry>>(this->fileListOrLength))
|
||||
{
|
||||
Serialization::Bencode::BeDictionary dict;
|
||||
dict.tokens.emplace_back("length",item.length);
|
||||
Serialization::Bencode::BeArray path;
|
||||
for(auto& p : item.path.path)
|
||||
path.tokens.push_back(p);
|
||||
dict.tokens.emplace_back("path",path);
|
||||
|
||||
a.tokens.push_back(dict);
|
||||
}
|
||||
this->info.tokens.emplace_back("files", a);
|
||||
}
|
||||
return this->info;
|
||||
}
|
||||
Serialization::Bencode::BeDictionary& TorrentFileInfo::GetInfoDictionary()
|
||||
{
|
||||
return this->info;
|
||||
}
|
||||
|
||||
void TorrentFileInfo::Load(const Serialization::Bencode::BeDictionary& dict)
|
||||
{
|
||||
this->info = dict;
|
||||
auto o=dict.GetValue("name");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeString>(o))
|
||||
this->name = std::get<Serialization::Bencode::BeString>(o);
|
||||
else
|
||||
this->name = {};
|
||||
|
||||
o=dict.GetValue("piece length");
|
||||
if(std::holds_alternative<int64_t>(o))
|
||||
this->piece_length = std::get<int64_t>(o);
|
||||
else
|
||||
this->piece_length = DEFAULT_PIECE_LENGTH;
|
||||
|
||||
o=dict.GetValue("private");
|
||||
if(std::holds_alternative<int64_t>(o))
|
||||
this->isPrivate = std::get<int64_t>(o);
|
||||
else
|
||||
this->isPrivate = 0;
|
||||
|
||||
o=dict.GetValue("pieces");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeString>(o))
|
||||
this->pieces = std::get<Serialization::Bencode::BeString>(o);
|
||||
else
|
||||
this->pieces = {};
|
||||
|
||||
|
||||
|
||||
o=dict.GetValue("files");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeArray>(o))
|
||||
{
|
||||
std::vector<TorrentFileEntry> ents;
|
||||
|
||||
for(auto& item : std::get<Serialization::Bencode::BeArray>(o).tokens)
|
||||
{
|
||||
if(std::holds_alternative<Serialization::Bencode::BeDictionary>(item))
|
||||
{
|
||||
TorrentFileEntry fe;
|
||||
fe.path.relative=true;
|
||||
|
||||
auto& d2=std::get<Serialization::Bencode::BeDictionary>(item);
|
||||
auto o2=d2.GetValue("length");
|
||||
if(std::holds_alternative<int64_t>(o2))
|
||||
{
|
||||
fe.length = std::get<int64_t>(o2);
|
||||
}
|
||||
o2=d2.GetValue("path");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeArray>(o2))
|
||||
{
|
||||
auto& arr=std::get<Serialization::Bencode::BeArray>(o2);
|
||||
for(auto& itm : arr.tokens)
|
||||
{
|
||||
if(std::holds_alternative<Serialization::Bencode::BeString>(itm))
|
||||
{
|
||||
fe.path.path.push_back(std::get<Serialization::Bencode::BeString>(itm));
|
||||
}
|
||||
}
|
||||
}
|
||||
ents.push_back(fe);
|
||||
}
|
||||
}
|
||||
this->fileListOrLength = ents;
|
||||
}
|
||||
else {
|
||||
o = dict.GetValue("length");
|
||||
if(std::holds_alternative<int64_t>(o))
|
||||
{
|
||||
this->fileListOrLength = std::get<int64_t>(o);
|
||||
}
|
||||
else {
|
||||
this->fileListOrLength = (int64_t)0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Serialization::Bencode::BeString TorrentFileInfo::GetInfoHash()
|
||||
{
|
||||
auto strm = std::make_shared<Streams::MemoryStream>(true);
|
||||
Serialization::Bencode::Bencode::Save(strm,this->info);
|
||||
strm->Seek(0L,Streams::SeekOrigin::Begin);
|
||||
return Crypto::Sha1::ComputeHash(strm);
|
||||
}
|
||||
|
||||
|
||||
TorrentFile::TorrentFile()
|
||||
{
|
||||
this->created_by = "TessesFrameworkTorrent";
|
||||
this->creation_date = (int64_t)time(NULL);
|
||||
}
|
||||
TorrentFile::TorrentFile(const Serialization::Bencode::BeDictionary& tkn)
|
||||
{
|
||||
auto o=tkn.GetValue("info");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeDictionary>(o))
|
||||
{
|
||||
this->info.Load(std::get<Serialization::Bencode::BeDictionary>(o));
|
||||
}
|
||||
o=tkn.GetValue("announce");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeString>(o))
|
||||
{
|
||||
this->announce = std::get<Serialization::Bencode::BeString>(o);
|
||||
}
|
||||
o=tkn.GetValue("announce-list");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeArray>(o))
|
||||
{
|
||||
auto& ls = std::get<Serialization::Bencode::BeArray>(o);
|
||||
for(auto& item : ls.tokens)
|
||||
{
|
||||
if(std::holds_alternative<Serialization::Bencode::BeArray>(item))
|
||||
{
|
||||
auto ls2 = std::get<Serialization::Bencode::BeArray>(item);
|
||||
auto item2=ls2.tokens.at(0);
|
||||
if(std::holds_alternative<Serialization::Bencode::BeString>(item2))
|
||||
{
|
||||
this->announce_list.push_back(std::get<Serialization::Bencode::BeString>(item2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
o=tkn.GetValue("creation date");
|
||||
if(std::holds_alternative<int64_t>(o))
|
||||
{
|
||||
this->creation_date = std::get<int64_t>(o);
|
||||
}
|
||||
o=tkn.GetValue("comment");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeString>(o))
|
||||
{
|
||||
this->comment = std::get<Serialization::Bencode::BeString>(o);
|
||||
}
|
||||
o=tkn.GetValue("created by");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeString>(o))
|
||||
{
|
||||
this->created_by = std::get<Serialization::Bencode::BeString>(o);
|
||||
}
|
||||
o=tkn.GetValue("url-list");
|
||||
if(std::holds_alternative<Serialization::Bencode::BeArray>(o))
|
||||
{
|
||||
auto& li =std::get<Serialization::Bencode::BeArray>(o);
|
||||
for(auto& itm : li.tokens)
|
||||
{
|
||||
if(std::holds_alternative<Serialization::Bencode::BeString>(itm))
|
||||
this->url_list.push_back(std::get<Serialization::Bencode::BeString>(itm));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Serialization::Bencode::BeDictionary TorrentFile::ToDictionary()
|
||||
{
|
||||
Serialization::Bencode::BeDictionary dict;
|
||||
dict.SetValue("info",this->info.GetInfoDictionary());
|
||||
dict.SetValue("announce",this->announce);
|
||||
if(!this->announce_list.empty())
|
||||
{
|
||||
Serialization::Bencode::BeArray a;
|
||||
for(auto& item : this->announce_list)
|
||||
{
|
||||
Serialization::Bencode::BeArray a2;
|
||||
a2.tokens.push_back(item);
|
||||
a.tokens.push_back(a2);
|
||||
}
|
||||
dict.SetValue("announce-list",a);
|
||||
}
|
||||
if(!this->url_list.empty())
|
||||
{
|
||||
Serialization::Bencode::BeArray ls;
|
||||
for(auto& itm : this->url_list)
|
||||
{
|
||||
ls.tokens.push_back(itm);
|
||||
}
|
||||
dict.SetValue("url-list",ls);
|
||||
}
|
||||
|
||||
dict.SetValue("created by", this->created_by);
|
||||
dict.SetValue("creation date", this->creation_date);
|
||||
dict.SetValue("comment",this->comment);
|
||||
return dict;
|
||||
}
|
||||
|
||||
void TorrentFile::Print(std::shared_ptr<TextStreams::TextWriter> writer)
|
||||
{
|
||||
writer->WriteLine("Announce: " + (std::string)this->announce);
|
||||
writer->WriteLine("Announce List:");
|
||||
for(auto& item : this->announce_list)
|
||||
{
|
||||
writer->WriteLine("\t" + (std::string)item);
|
||||
}
|
||||
writer->WriteLine("Comment: " + (std::string)this->comment);
|
||||
writer->WriteLine("Created By: " + (std::string)this->created_by);
|
||||
Date::DateTime dt(this->creation_date);
|
||||
dt.SetToLocal();
|
||||
writer->WriteLine("Creation Date: " + dt.ToString());
|
||||
writer->WriteLine("Info Hash: " + Http::HttpUtils::BytesToHex(this->info.GetInfoHash().data));
|
||||
writer->WriteLine("Info:");
|
||||
writer->WriteLine("\tName: " + (std::string)this->info.name);
|
||||
writer->WriteLine(this->info.isPrivate ? "\tPrivate: true" : "\tPrivate: false");
|
||||
writer->WriteLine("\tPiece Length: " + TF_FileSize(this->info.piece_length));
|
||||
if(std::holds_alternative<int64_t>(this->info.fileListOrLength))
|
||||
{
|
||||
writer->WriteLine("\tIs Single File: true");
|
||||
writer->WriteLine("\tFile length: " + TF_FileSize((uint64_t)std::get<int64_t>(this->info.fileListOrLength)));
|
||||
}
|
||||
else if(std::holds_alternative<std::vector<TorrentFileEntry>>(this->info.fileListOrLength))
|
||||
{
|
||||
writer->WriteLine("\tIs Single File: false");
|
||||
writer->WriteLine("\tFiles:");
|
||||
auto& files = std::get<std::vector<TorrentFileEntry>>(this->info.fileListOrLength);
|
||||
for(auto& file : files)
|
||||
{
|
||||
writer->WriteLine("\t\tPath: " + file.path.ToString());
|
||||
writer->WriteLine("\t\tLength: " + TF_FileSize(file.length));
|
||||
writer->WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ReadWriteAt> TorrentFileInfo::GetStreamFromFilesystem(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, const Tesses::Framework::Filesystem::VFSPath& path)
|
||||
{
|
||||
if(std::holds_alternative<std::vector<TorrentFileEntry>>(this->fileListOrLength))
|
||||
{
|
||||
return std::make_shared<TorrentDirectoryStream>(vfs,path / this->name, std::get<std::vector<TorrentFileEntry>>(this->fileListOrLength));
|
||||
}
|
||||
else if(std::holds_alternative<int64_t>(this->fileListOrLength)) {
|
||||
return std::make_shared<TorrentFileStream>(vfs,path / this->name, (uint64_t)std::get<int64_t>(this->fileListOrLength));
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
}
|
||||
|
||||
static void ParsePieces(TorrentFileInfo* fi,std::shared_ptr<ReadWriteAt> rwa, int64_t flength)
|
||||
{
|
||||
int64_t pieces = fi->pieces.data.size()/20;
|
||||
std::vector<uint8_t> buffer;
|
||||
buffer.resize((size_t)fi->piece_length);
|
||||
size_t lastPieceSize = (size_t)(flength % (int64_t)buffer.size());
|
||||
for(int64_t i = 0; i < pieces; i++)
|
||||
{
|
||||
size_t len = buffer.size();
|
||||
if(i == pieces-1 && lastPieceSize != 0) len = std::min(len,lastPieceSize);
|
||||
rwa->ReadBlockAt(i*buffer.size(), buffer.data(), len);
|
||||
auto hsh=Crypto::Sha1::ComputeHash(buffer.data(),len);
|
||||
std::copy(hsh.begin(),hsh.end(),fi->pieces.data.begin()+(i*20));
|
||||
}
|
||||
}
|
||||
void TorrentFileInfo::CreateFromFilesystem(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, const Tesses::Framework::Filesystem::VFSPath& path, bool isPrivate, int64_t pieceLength)
|
||||
{
|
||||
this->isPrivate=isPrivate;
|
||||
this->piece_length = pieceLength;
|
||||
this->pieces.data.clear();
|
||||
this->name = path.GetFileName();
|
||||
int64_t len=0;
|
||||
if(vfs->FileExists(path))
|
||||
{
|
||||
auto strm = vfs->OpenFile(path,"rb");
|
||||
len = strm->GetLength();
|
||||
this->fileListOrLength= len;
|
||||
int64_t pieces = len / piece_length;
|
||||
if((len % piece_length) != 0) pieces++;
|
||||
|
||||
this->pieces.data.resize(pieces*20);
|
||||
}
|
||||
else if(vfs->DirectoryExists(path))
|
||||
{
|
||||
std::vector<TorrentFileEntry> ents;
|
||||
std::function<void(Tesses::Framework::Filesystem::VFSPath,Tesses::Framework::Filesystem::VFSPath)> crawl;
|
||||
crawl= [&](Tesses::Framework::Filesystem::VFSPath inFS, Tesses::Framework::Filesystem::VFSPath inTorrent)->void{
|
||||
for(auto ent : vfs->EnumeratePaths(inFS))
|
||||
{
|
||||
if(vfs->FileExists(ent))
|
||||
{
|
||||
auto strm = vfs->OpenFile(ent,"rb");
|
||||
auto flen = strm->GetLength();
|
||||
ents.emplace_back(inTorrent / ent.GetFileName(),flen);
|
||||
|
||||
len += flen;
|
||||
}
|
||||
else if(vfs->DirectoryExists(ent))
|
||||
{
|
||||
crawl(ent,inTorrent / ent.GetFileName());
|
||||
}
|
||||
}
|
||||
};
|
||||
Tesses::Framework::Filesystem::VFSPath p2;
|
||||
p2.relative=true;
|
||||
crawl(path, p2);
|
||||
|
||||
this->fileListOrLength = ents;
|
||||
int64_t pieces = len / piece_length;
|
||||
if((len % piece_length) != 0) pieces++;
|
||||
|
||||
this->pieces.data.resize(pieces*20);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("File or directory does not exist");
|
||||
}
|
||||
ParsePieces(this,this->GetStreamFromFilesystem(vfs,path.GetParent()),len);
|
||||
|
||||
|
||||
this->GenerateInfoDictionary();
|
||||
}
|
||||
|
||||
void TorrentFile::CreateTorrent(std::shared_ptr<Tesses::Framework::Streams::Stream> torrent_file_stream,const std::vector<Serialization::Bencode::BeString>& trackers,const std::vector<Serialization::Bencode::BeString>& webseeds, std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, const Tesses::Framework::Filesystem::VFSPath& path, bool isPrivate, int64_t pieceLength, Serialization::Bencode::BeString comment, Serialization::Bencode::BeString created_by)
|
||||
{
|
||||
TorrentFile file;
|
||||
file.announce = trackers.at(0);
|
||||
file.announce_list = trackers;
|
||||
file.url_list = webseeds;
|
||||
file.comment = comment;
|
||||
file.created_by = created_by;
|
||||
file.info.CreateFromFilesystem(vfs,path,isPrivate,pieceLength);
|
||||
auto dict=file.ToDictionary();
|
||||
Serialization::Bencode::Bencode::Save(torrent_file_stream,dict);
|
||||
}
|
||||
}
|
||||
657
src/BitTorrent/TorrentManager.cpp
Normal file
657
src/BitTorrent/TorrentManager.cpp
Normal file
@@ -0,0 +1,657 @@
|
||||
#include "TessesFramework/BitTorrent/TorrentManager.hpp"
|
||||
#include "TessesFramework/Serialization/BitConverter.hpp"
|
||||
#include "TessesFramework/Crypto/Crypto.hpp"
|
||||
#include "TessesFramework/Random.hpp"
|
||||
|
||||
namespace Tesses::Framework::BitTorrent
|
||||
{
|
||||
|
||||
TorrentBitField::TorrentBitField()
|
||||
{
|
||||
|
||||
}
|
||||
TorrentBitField::TorrentBitField(size_t len)
|
||||
{
|
||||
resize(len);
|
||||
}
|
||||
size_t TorrentBitField::size()
|
||||
{
|
||||
return this->no_bits;
|
||||
}
|
||||
void TorrentBitField::resize(size_t len)
|
||||
{
|
||||
this->no_bits = len;
|
||||
size_t no = len / 8;
|
||||
if((len % 8) != 0)
|
||||
no++;
|
||||
this->bits.resize(no);
|
||||
}
|
||||
bool TorrentBitField::get(size_t bit)
|
||||
{
|
||||
size_t byte = bit / 8;
|
||||
size_t byteBit = 7-bit % 8;
|
||||
|
||||
return (this->bits.at(byte) >> byteBit) & 1;
|
||||
}
|
||||
bool TorrentBitField::allone()
|
||||
{
|
||||
|
||||
for(size_t i = 0; i < this->no_bits; i++)
|
||||
{
|
||||
size_t byte = i / 8;
|
||||
size_t byteBit = i % 8;
|
||||
if(((this->bits.at(byte) >> byteBit) & 1) == 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void TorrentBitField::set(size_t bit, bool val)
|
||||
{
|
||||
size_t byte = bit / 8;
|
||||
size_t byteBit = 7-bit % 8;
|
||||
|
||||
|
||||
if(val)
|
||||
{
|
||||
this->bits.at(byte) |= 1 << byteBit;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->bits.at(byte) &= ~(1 << byteBit);
|
||||
}
|
||||
}
|
||||
void TorrentBitField::zero()
|
||||
{
|
||||
std::fill(this->bits.begin(),this->bits.end(),0);
|
||||
}
|
||||
std::vector<uint8_t>& TorrentBitField::data()
|
||||
{
|
||||
return this->bits;
|
||||
}
|
||||
|
||||
|
||||
void TorrentManager::Start()
|
||||
{
|
||||
|
||||
}
|
||||
bool ActiveTorrent::mustAnnounce()
|
||||
{
|
||||
time_t curTime = time(NULL);
|
||||
if(curTime < lastTime || (lastTime + 1800) <= curTime)
|
||||
{
|
||||
std::cout << "Must anounce" << std::endl;
|
||||
this->lastTime = curTime;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
std::string bt_handshake_str = "BitTorrent protocol";
|
||||
void ActiveTorrent::addPeer(std::string ip, uint16_t port)
|
||||
{
|
||||
std::cout << "Got peer: " << ip << ":" << port << std::endl;
|
||||
if(port == 0) return;
|
||||
for(auto conn : this->connections)
|
||||
{
|
||||
if(conn->ip == ip && conn->port == port) return;
|
||||
}
|
||||
auto peer = std::make_shared<TorrentPeer>();
|
||||
peer->ip = ip;
|
||||
peer->port = port;
|
||||
peer->isChoked=true;
|
||||
peer->isChokingMe=true;
|
||||
peer->intrested=false;
|
||||
peer->has.resize(this->has.size());
|
||||
peer->blocksRequested.resize(this->has.size());
|
||||
peer->stream = std::make_shared<Tesses::Framework::Streams::NetworkStream>(ip,port,false,false,false);
|
||||
|
||||
std::vector<uint8_t> handshake;
|
||||
handshake.resize(49+bt_handshake_str.size());
|
||||
handshake[0] = (uint8_t)bt_handshake_str.size();
|
||||
std::copy(bt_handshake_str.begin(),bt_handshake_str.end(),handshake.begin()+1);
|
||||
auto off = bt_handshake_str.size()+1;
|
||||
for(size_t i = 0; i < 8; i++)
|
||||
handshake[off+i] = 0;
|
||||
std::copy(this->info_hash.data.begin(),this->info_hash.data.end(),handshake.begin()+off+8);
|
||||
std::copy(this->peer_id.begin(),this->peer_id.end(),handshake.begin()+off+28);
|
||||
peer->stream->WriteBlock(handshake.data(),handshake.size());
|
||||
handshake.resize(5+this->has.data().size());
|
||||
Serialization::BitConverter::FromUint32BE(handshake[0],this->has.data().size());
|
||||
handshake[4] = 5;
|
||||
|
||||
std::copy(this->has.data().begin(),this->has.data().end(),handshake.begin()+5);
|
||||
peer->stream->WriteBlock(handshake.data(),handshake.size());
|
||||
|
||||
Serialization::BitConverter::FromUint32BE(handshake[0],1);
|
||||
handshake[4] = 2;
|
||||
|
||||
peer->stream->WriteBlock(handshake.data(),5);
|
||||
std::cout << "Handshake send complete for: " << ip << ":" << port << std::endl;
|
||||
|
||||
|
||||
this->connections.push_back(peer);
|
||||
}
|
||||
void ActiveTorrent::udpAnounce(Tesses::Framework::Http::Uri uri)
|
||||
{
|
||||
std::string connUrl = uri.HostPort();
|
||||
std::cout << "udp anouncing via: " << connUrl << std::endl;
|
||||
Streams::NetworkStream strm(uri.host,uri.port,true,false,false);
|
||||
|
||||
auto trans_id = rng.Next(0xFFFFFFFF);
|
||||
if(this->udp_connection_ids.count(connUrl) == 0)
|
||||
{
|
||||
|
||||
|
||||
Serialization::BitConverter::FromUint64BE(this->buffer[0],0x41727101980);
|
||||
Serialization::BitConverter::FromUint32BE(this->buffer[8],0);
|
||||
Serialization::BitConverter::FromUint32BE(this->buffer[12],trans_id);
|
||||
|
||||
std::cout << "writing ze data" << std::endl;
|
||||
strm.Write(this->buffer.data(),16);
|
||||
std::cout << "reading ze data" << std::endl;
|
||||
if(!strm.DataAvailable(5000)) {
|
||||
std::cout << "timeout" << std::endl;
|
||||
return;
|
||||
}
|
||||
if(strm.Read(this->buffer.data(),this->buffer.size()) != 16) return;
|
||||
std::cout << "read ze data" << std::endl;
|
||||
if(Serialization::BitConverter::ToUint32BE(this->buffer[0]) == 0 && Serialization::BitConverter::ToUint32BE(this->buffer[4]) == trans_id)
|
||||
{
|
||||
this->udp_connection_ids[connUrl] = Serialization::BitConverter::ToUint64BE(this->buffer[8]);
|
||||
|
||||
trans_id = rng.Next(0xFFFFFFFF);
|
||||
} else return;
|
||||
}
|
||||
Serialization::BitConverter::FromUint64BE(this->buffer[0],this->udp_connection_ids[connUrl]);
|
||||
Serialization::BitConverter::FromUint32BE(this->buffer[8],1);
|
||||
Serialization::BitConverter::FromUint32BE(this->buffer[12],trans_id);
|
||||
std::copy(this->info_hash.data.begin(),this->info_hash.data.end(),this->buffer.begin()+16);
|
||||
|
||||
std::copy(this->peer_id.begin(),this->peer_id.end(),this->buffer.begin()+36);
|
||||
Serialization::BitConverter::FromUint64BE(this->buffer[56], this->downloaded);
|
||||
Serialization::BitConverter::FromUint64BE(this->buffer[64], this->getLeft());
|
||||
Serialization::BitConverter::FromUint64BE(this->buffer[72], this->uploaded);
|
||||
Serialization::BitConverter::FromUint32BE(this->buffer[80],0);
|
||||
Serialization::BitConverter::FromUint32BE(this->buffer[84],0);
|
||||
Serialization::BitConverter::FromUint32BE(this->buffer[88],0);
|
||||
Serialization::BitConverter::FromUint32BE(this->buffer[92],1); //peers
|
||||
Serialization::BitConverter::FromUint16BE(this->buffer[96],0);
|
||||
strm.Write(this->buffer.data(),98);
|
||||
if(!strm.DataAvailable(5000)) {
|
||||
std::cout << "timeout" << std::endl;
|
||||
return;
|
||||
}
|
||||
size_t read = strm.Read(this->buffer.data(),this->buffer.size());
|
||||
if(read < 20) return;
|
||||
if(Serialization::BitConverter::ToUint32BE(this->buffer[0]) == 1 && Serialization::BitConverter::ToUint32BE(this->buffer[4]) == trans_id)
|
||||
{
|
||||
std::cout << "Found " << ((read - 20) / 6) << " peers" << std::endl;
|
||||
for(size_t peerIdx = 20; peerIdx + 6 <= read; peerIdx+=6)
|
||||
{
|
||||
std::string ip = std::to_string((uint32_t)this->buffer[peerIdx]);
|
||||
ip += "." + std::to_string((uint32_t)this->buffer[peerIdx+1]);
|
||||
ip += "." + std::to_string((uint32_t)this->buffer[peerIdx+2]);
|
||||
ip += "." + std::to_string((uint32_t)this->buffer[peerIdx+3]);
|
||||
|
||||
uint16_t port = Tesses::Framework::Serialization::BitConverter::ToUint16BE(this->buffer[peerIdx+4]);
|
||||
|
||||
addPeer(ip,port);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
int64_t ActiveTorrent::getLeft()
|
||||
{
|
||||
int64_t left = 0;
|
||||
size_t normalPieceSize = pieceSize(0);
|
||||
size_t lastPieceSize = pieceSize(this->has.size()-1);
|
||||
for(size_t i = 0;i < this->has.size(); i++)
|
||||
{
|
||||
if(!this->has.get(i))
|
||||
if(i == this->has.size() - 1)
|
||||
left += lastPieceSize;
|
||||
else
|
||||
left += normalPieceSize;
|
||||
}
|
||||
return left;
|
||||
}
|
||||
void ActiveTorrent::httpAnounce(std::string url)
|
||||
{
|
||||
std::string newUrl = url + "?info_hash=" + Http::HttpUtils::UrlEncode((std::string)this->info_hash) + "&peer_id=" + Http::HttpUtils::UrlEncode(this->peer_id) + "&uploaded=" + std::to_string(this->uploaded) + "&downloaded=" + std::to_string(this->downloaded) + "&left=" + std::to_string(getLeft()) + "&numwant=10&compact=1&no_peer_id=1&port=0";
|
||||
Http::HttpRequest req;
|
||||
req.url= newUrl;
|
||||
req.method = "GET";
|
||||
Http::HttpResponse resp(req);
|
||||
if(resp.statusCode == 200)
|
||||
{
|
||||
auto bencode=Tesses::Framework::Serialization::Bencode::Bencode::Load(resp.ReadAsStream());
|
||||
if(std::holds_alternative<Tesses::Framework::Serialization::Bencode::BeDictionary>(bencode))
|
||||
{
|
||||
auto& dict = std::get<Tesses::Framework::Serialization::Bencode::BeDictionary>(bencode);
|
||||
auto peers = dict.GetValue("peers");
|
||||
if(std::holds_alternative<Tesses::Framework::Serialization::Bencode::BeString>(peers))
|
||||
{
|
||||
//this is compact
|
||||
auto compactPeers=std::get<Tesses::Framework::Serialization::Bencode::BeString>(peers);
|
||||
for(size_t peerIdx = 0; peerIdx + 6 <= compactPeers.data.size(); peerIdx+=6)
|
||||
{
|
||||
std::string ip = std::to_string((uint32_t)compactPeers.data[peerIdx]);
|
||||
ip += "." + std::to_string((uint32_t)compactPeers.data[peerIdx+1]);
|
||||
ip += "." + std::to_string((uint32_t)compactPeers.data[peerIdx+2]);
|
||||
ip += "." + std::to_string((uint32_t)compactPeers.data[peerIdx+3]);
|
||||
|
||||
uint16_t port = Tesses::Framework::Serialization::BitConverter::ToUint16BE(compactPeers.data[peerIdx+4]);
|
||||
|
||||
addPeer(ip,port);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto normalPeers = std::get<Tesses::Framework::Serialization::Bencode::BeArray>(peers);
|
||||
for(auto& item : normalPeers.tokens)
|
||||
{
|
||||
if(std::holds_alternative<Tesses::Framework::Serialization::Bencode::BeDictionary>(item))
|
||||
{
|
||||
auto& dict2=std::get<Tesses::Framework::Serialization::Bencode::BeDictionary>(item);
|
||||
auto ip = dict2.GetValue("ip");
|
||||
auto port = dict2.GetValue("port");
|
||||
if(std::holds_alternative<Tesses::Framework::Serialization::Bencode::BeString>(ip) && std::holds_alternative<int64_t>(port))
|
||||
{
|
||||
addPeer((std::string)std::get<Tesses::Framework::Serialization::Bencode::BeString>(ip),(uint16_t)(uint64_t)std::get<int64_t>(port));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActiveTorrent::process()
|
||||
{
|
||||
if(mustAnnounce())
|
||||
{
|
||||
std::vector<Tesses::Framework::Serialization::Bencode::BeString> announces;
|
||||
if(this->file.announce_list.empty())
|
||||
announces.push_back(this->file.announce);
|
||||
announces.insert(announces.end(),this->file.announce_list.begin(),this->file.announce_list.end());
|
||||
|
||||
for(auto& a : announces)
|
||||
{
|
||||
Tesses::Framework::Http::Uri uri;
|
||||
if(Tesses::Framework::Http::Uri::TryParse(a,uri))
|
||||
{
|
||||
if(uri.scheme == "udp:")
|
||||
{
|
||||
udpAnounce(uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
httpAnounce(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << "end of anounces" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
for(auto item : this->connections)
|
||||
{
|
||||
if(!item->isChokingMe)
|
||||
{
|
||||
for(size_t piece = 0; piece < this->has.size(); piece++)
|
||||
{
|
||||
if(!this->has.get(piece) && item->has.get(piece))
|
||||
{
|
||||
bool hasFound=false;
|
||||
//find this piece in bitfields
|
||||
//find a block we don't have and that we never requested
|
||||
//we need to ensure that both the peer and I have the bitfields for the piece
|
||||
|
||||
size_t _pieceSize = pieceSize(piece);
|
||||
auto blockCount = _pieceSize / 16384;
|
||||
if((_pieceSize % 16384) != 0) blockCount++;
|
||||
if(!this->blocksAquired[piece] )
|
||||
{
|
||||
this->blocksAquired[piece] =TorrentBitField(blockCount);
|
||||
}
|
||||
if(!item->blocksRequested[piece] )
|
||||
{
|
||||
item->blocksRequested[piece] =TorrentBitField(blockCount);
|
||||
}
|
||||
|
||||
for(size_t block = 0; block < blockCount; block++)
|
||||
{
|
||||
if(!this->blocksAquired[piece]->get(block) && !item->blocksRequested[piece]->get(block))
|
||||
{
|
||||
item->blocksRequested[piece]->set(block,true);
|
||||
hasFound=true;
|
||||
|
||||
std::vector<uint8_t> msg((size_t)17);
|
||||
Serialization::BitConverter::FromUint32BE(msg[0],13);
|
||||
msg[4] = 6;
|
||||
Serialization::BitConverter::FromUint32BE(msg[5], (uint32_t)piece);
|
||||
|
||||
Serialization::BitConverter::FromUint32BE(msg[9], (uint32_t)(16384*block));
|
||||
size_t readBlockSize = 16384;
|
||||
if((_pieceSize % 16384) != 0 && block >= blockCount - 1) readBlockSize = _pieceSize % 16384;
|
||||
|
||||
Serialization::BitConverter::FromUint32BE(msg[13], (uint32_t)(readBlockSize));
|
||||
|
||||
item->stream->WriteBlock(msg.data(),msg.size());
|
||||
|
||||
std::cout << "Request block: " << block << " of piece: " << piece << " from peer: " << item->ip << ":" << item->port << std::endl;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(hasFound) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else std::cout << "Is choking" << std::endl;
|
||||
if(item->stream->DataAvailable(1))
|
||||
{
|
||||
std::cout << "Has message" << std::endl;
|
||||
size_t read = item->stream->Read(buffer.data(),buffer.size());
|
||||
item->messages.insert(item->messages.cend(),buffer.begin(),buffer.begin()+read);
|
||||
if(!processMessages(item))
|
||||
{
|
||||
std::cout << "Peer " << item->ip << ":" << item->port << " misbehaved" << std::endl;
|
||||
item->stream=nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(auto idx = this->connections.begin(); idx != this->connections.end(); idx++)
|
||||
{
|
||||
auto item = *idx;
|
||||
bool destroy = true;
|
||||
if(item)
|
||||
{
|
||||
destroy = !item->stream;
|
||||
}
|
||||
if(destroy)
|
||||
{
|
||||
this->connections.erase(idx);
|
||||
idx--;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
size_t ActiveTorrent::pieceSize(size_t piece)
|
||||
{
|
||||
if(piece == this->has.size()-1)
|
||||
{
|
||||
int64_t remainder = this->torrentSize % this->file.info.piece_length;
|
||||
if(remainder != 0)
|
||||
return (size_t)remainder;
|
||||
}
|
||||
return this->file.info.piece_length;
|
||||
}
|
||||
bool ActiveTorrent::processMessages(std::shared_ptr<TorrentPeer> peer)
|
||||
{
|
||||
if(!peer->stream) return false;
|
||||
using namespace Tesses::Framework::Serialization;
|
||||
while(peer->messages.size() >= 4)
|
||||
{
|
||||
auto len = BitConverter::ToUint32BE(peer->messages[0]);
|
||||
if(len + 4 > peer->messages.size()) break;
|
||||
|
||||
if(len > 0)
|
||||
{
|
||||
uint8_t id = peer->messages[4];
|
||||
switch(id)
|
||||
{
|
||||
case 0:
|
||||
peer->isChokingMe=true;
|
||||
std::cout << peer->ip << ":" << peer->port << " choked me :(" << std::endl;
|
||||
break;
|
||||
case 1:
|
||||
peer->isChokingMe=false;
|
||||
|
||||
std::cout << peer->ip << ":" << peer->port << " unchoked me :)" << std::endl;
|
||||
if(this->has.allone())
|
||||
{
|
||||
//send not intrested
|
||||
std::vector<uint8_t> msg((size_t)5);
|
||||
BitConverter::FromUint32BE(msg[0],1);
|
||||
msg[4] = 3;
|
||||
|
||||
peer->stream->WriteBlock(msg.data(),msg.size());
|
||||
}
|
||||
else {
|
||||
std::vector<uint8_t> msg((size_t)5);
|
||||
BitConverter::FromUint32BE(msg[0],1);
|
||||
msg[4] = 2;
|
||||
|
||||
peer->stream->WriteBlock(msg.data(),msg.size());
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
|
||||
std::cout << peer->ip << ":" << peer->port << " is intrested" << std::endl;
|
||||
peer->intrested=true;
|
||||
break;
|
||||
case 3:
|
||||
|
||||
std::cout << peer->ip << ":" << peer->port << " is not intrested" << std::endl;
|
||||
peer->intrested=false;
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
|
||||
auto pieceIndex = BitConverter::ToUint32BE(peer->messages[5]);
|
||||
if(pieceIndex < peer->has.size())
|
||||
peer->has.set((size_t)pieceIndex,true);
|
||||
else return false;
|
||||
|
||||
std::cout << peer->ip << ":" << peer->port << " has piece " << pieceIndex << std::endl;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
{
|
||||
if(len-1 == peer->has.data().size())
|
||||
{
|
||||
std::copy(peer->messages.begin()+5,peer->messages.begin()+4+len,peer->has.data().begin());
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
{
|
||||
if(peer->isChoked) break;
|
||||
if(len == 13)
|
||||
{
|
||||
auto pieceIndex = BitConverter::ToUint32BE(peer->messages[5]);
|
||||
auto offset = BitConverter::ToUint32BE(peer->messages[9]);
|
||||
auto length = BitConverter::ToUint32BE(peer->messages[13]);
|
||||
|
||||
|
||||
|
||||
std::cout << peer->ip << ":" << peer->port << " wants piece: " << pieceIndex << " offset: " << offset << " length: " << length << std::endl;
|
||||
|
||||
|
||||
|
||||
if(pieceIndex < this->has.size())
|
||||
{
|
||||
size_t _pieceSize = pieceSize((size_t)pieceIndex);
|
||||
if((size_t)offset >= _pieceSize) return false;
|
||||
if(length > 16384) return false;
|
||||
if(length+offset > _pieceSize) return false;
|
||||
|
||||
if(this->has.get((size_t)pieceIndex))
|
||||
{
|
||||
for(auto index = peer->cancel_requests.begin(); index != peer->cancel_requests.end(); index++)
|
||||
{
|
||||
if(index->piece == pieceIndex && index->begin == offset && index->length == length)
|
||||
{
|
||||
peer->cancel_requests.erase(index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> message;
|
||||
message.resize(13+length);
|
||||
BitConverter::FromUint32BE(message[0],9+length);
|
||||
message[4] = 7;
|
||||
BitConverter::FromUint32BE(message[5], pieceIndex);
|
||||
BitConverter::FromUint32BE(message[9], offset);
|
||||
|
||||
this->torrent_disk->ReadBlockAt(pieceIndex * this->file.info.piece_length + offset, message.data()+13 ,message.size()-13);
|
||||
peer->stream->WriteBlock(message.data(),message.size());
|
||||
}
|
||||
}
|
||||
else return false;
|
||||
} else return false;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
{
|
||||
if(len > 9)
|
||||
{
|
||||
auto pieceIndex = BitConverter::ToUint32BE(peer->messages[5]);
|
||||
auto offset = BitConverter::ToUint32BE(peer->messages[9]);
|
||||
auto length = len - 9;
|
||||
|
||||
std::cout << peer->ip << ":" << peer->port << " gave me a piece! piece: " << pieceIndex << " offset: " << offset << " length: " << length << std::endl;
|
||||
|
||||
if(pieceIndex < this->has.size())
|
||||
{
|
||||
size_t _pieceSize = pieceSize((size_t)pieceIndex);
|
||||
if((size_t)offset >= _pieceSize) return false;
|
||||
if(length > 16384) return false;
|
||||
if(length+offset > _pieceSize) return false;
|
||||
if(!this->has.get((size_t)pieceIndex))
|
||||
{
|
||||
this->torrent_disk->WriteBlockAt(pieceIndex * this->file.info.piece_length + offset, peer->messages.data()+9 ,length);
|
||||
if(!this->blocksAquired[pieceIndex] )
|
||||
{
|
||||
auto blockCount = _pieceSize / 16384;
|
||||
if((_pieceSize % 16384) != 0) blockCount++;
|
||||
|
||||
this->blocksAquired[pieceIndex] = Tesses::Framework::BitTorrent::TorrentBitField(blockCount);
|
||||
}
|
||||
|
||||
this->blocksAquired[pieceIndex]->set(offset/16384, true);
|
||||
if(this->blocksAquired[pieceIndex]->allone())
|
||||
{
|
||||
std::vector<uint8_t> data(_pieceSize);
|
||||
if(this->torrent_disk->ReadBlockAt(pieceIndex*this->file.info.piece_length,data.data(),data.size()))
|
||||
{
|
||||
auto hash = Tesses::Framework::Crypto::Sha1::ComputeHash(data.data(),data.size());
|
||||
if(std::equal(hash.begin(),hash.end(),this->file.info.pieces.data.begin()+(pieceIndex*20)))
|
||||
{
|
||||
this->has.set(pieceIndex,true);
|
||||
auto pieceFile = directory / this->file.info.name + ".tftpart";
|
||||
{
|
||||
auto pfs = vfs->OpenFile(pieceFile,"wb");
|
||||
pfs->WriteBlock(has.data().data(),has.data().size());
|
||||
}
|
||||
this->blocksAquired[pieceIndex] = std::nullopt;
|
||||
peer->blocksRequested[pieceIndex] = std::nullopt;
|
||||
{
|
||||
std::vector<uint8_t> msg((size_t)9);
|
||||
BitConverter::FromUint32BE(msg[0],5);
|
||||
msg[4] = 4;
|
||||
BitConverter::FromUint32BE(msg[5],(uint32_t)pieceIndex);
|
||||
for(auto& peer2 : this->connections)
|
||||
{
|
||||
if(peer2->stream)
|
||||
{
|
||||
peer2->stream->WriteBlock(msg.data(),msg.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
if(this->has.allone())
|
||||
{
|
||||
TF_LOG("The torrent: " + (std::string)this->file.info.name + " is completed");
|
||||
std::vector<uint8_t> msg((size_t)5);
|
||||
BitConverter::FromUint32BE(msg[0],1);
|
||||
msg[4] = 3;
|
||||
for(auto& peer2 : this->connections)
|
||||
{
|
||||
if(peer2->stream)
|
||||
{
|
||||
//send not intrested
|
||||
peer2->stream->WriteBlock(msg.data(),msg.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
//CORRUPT
|
||||
this->blocksAquired[pieceIndex]->zero();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//CORRUPT
|
||||
this->blocksAquired[pieceIndex]->zero();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
{
|
||||
if(len == 13)
|
||||
{
|
||||
auto pieceIndex = BitConverter::ToUint32BE(peer->messages[5]);
|
||||
auto offset = BitConverter::ToUint32BE(peer->messages[9]);
|
||||
auto length = BitConverter::ToUint32BE(peer->messages[13]);
|
||||
CancelRequest r;
|
||||
r.piece = pieceIndex;
|
||||
r.begin = offset;
|
||||
r.length = length;
|
||||
|
||||
peer->cancel_requests.push_back(r);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
peer->messages.erase(peer->messages.begin(),peer->messages.begin()+4+len);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ActiveTorrent::ActiveTorrent(TorrentFile file, std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs,Tesses::Framework::Filesystem::VFSPath directory, std::string peer_id)
|
||||
{
|
||||
this->file = file;
|
||||
this->vfs = vfs;
|
||||
this->directory = directory;
|
||||
this->peer_id = peer_id;
|
||||
this->has.resize(this->file.info.pieces.data.size()/20);
|
||||
this->torrentSize = this->file.info.GetTorrentFileSize();
|
||||
this->info_hash = this->file.info.GetInfoHash();
|
||||
this->lastTime = 0;
|
||||
this->blocksAquired.resize(this->has.size());
|
||||
this->uploaded = 0;
|
||||
this->downloaded = 0;
|
||||
|
||||
auto pieceFile = directory / this->file.info.name + ".tftpart";
|
||||
if(vfs->FileExists(pieceFile))
|
||||
{
|
||||
auto strm = vfs->OpenFile(pieceFile,"rb");
|
||||
strm->ReadBlock(has.data().data(),has.data().size());
|
||||
}
|
||||
this->torrent_disk = this->file.info.GetStreamFromFilesystem(vfs,directory);
|
||||
}
|
||||
std::string lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
std::string GeneratePeerId()
|
||||
{
|
||||
std::string peerId((size_t)20,' ');
|
||||
peerId.insert(0,"TFWT");
|
||||
Random r;
|
||||
for(size_t i = 4; i < peerId.size(); i++)
|
||||
peerId[i] = lookup[(int)r.Next((uint32_t)lookup.size())];
|
||||
return peerId;
|
||||
}
|
||||
}
|
||||
122
src/BitTorrent/TorrentStream.cpp
Normal file
122
src/BitTorrent/TorrentStream.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "TessesFramework/BitTorrent/TorrentStream.hpp"
|
||||
|
||||
namespace Tesses::Framework::BitTorrent
|
||||
{
|
||||
TorrentFileEntry::TorrentFileEntry()
|
||||
{
|
||||
|
||||
}
|
||||
TorrentFileEntry::TorrentFileEntry(Tesses::Framework::Filesystem::VFSPath path, int64_t length): path(path), length(length)
|
||||
{}
|
||||
|
||||
ReadWriteAt::~ReadWriteAt(){}
|
||||
|
||||
TorrentFileStream::TorrentFileStream(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, Tesses::Framework::Filesystem::VFSPath path,uint64_t length) : vfs(vfs), path(path), length(length)
|
||||
{
|
||||
}
|
||||
bool TorrentFileStream::ReadBlockAt(uint64_t offset, uint8_t* data, size_t len)
|
||||
{
|
||||
if(!vfs->FileExists(path)) return false;
|
||||
auto strm = vfs->OpenFile(path,"rb");
|
||||
strm->Seek((int64_t)offset,Streams::SeekOrigin::Begin);
|
||||
strm->ReadBlock(data,len);
|
||||
return true;
|
||||
}
|
||||
void TorrentFileStream::WriteBlockAt(uint64_t offset, const uint8_t* data, size_t len)
|
||||
{
|
||||
len = (size_t)std::min((int64_t)len, (int64_t)this->length-(int64_t)offset);
|
||||
if(len == 0) return;
|
||||
|
||||
mtx.Lock();
|
||||
auto strm = vfs->OpenFile(path,"wb");
|
||||
strm->Seek((int64_t)offset,Streams::SeekOrigin::Begin);
|
||||
strm->WriteBlock(data,len);
|
||||
mtx.Unlock();
|
||||
}
|
||||
|
||||
TorrentDirectoryStream::TorrentDirectoryStream(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, Tesses::Framework::Filesystem::VFSPath path,std::vector<TorrentFileEntry> entries) : vfs(vfs), path(path), entries(entries)
|
||||
{
|
||||
if(!vfs->DirectoryExists(path))
|
||||
this->vfs->CreateDirectory(path);
|
||||
this->mtxs.resize(entries.size());
|
||||
}
|
||||
|
||||
// From https://www.seanjoflynn.com/research/bittorrent.html , which is licensed under MIT based on code repo
|
||||
bool TorrentDirectoryStream::ReadBlockAt(uint64_t offset, uint8_t* data, size_t len)
|
||||
{
|
||||
uint64_t currentOffset = 0;
|
||||
uint64_t end = offset + len;
|
||||
for(size_t i = 0; i < this->entries.size(); i++)
|
||||
{
|
||||
if(offset < currentOffset && end < currentOffset) {
|
||||
currentOffset += this->entries[i].length;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(offset > currentOffset + this->entries[i].length && end > currentOffset + this->entries[i].length ){
|
||||
currentOffset += this->entries[i].length;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto path = this->path / this->entries[i].path;
|
||||
if(!vfs->FileExists(path)) return false;
|
||||
|
||||
int64_t fstart = std::max((int64_t)0,(int64_t)offset - (int64_t)currentOffset);
|
||||
int64_t fend = std::min((int64_t)end - (int64_t)currentOffset, (int64_t)this->entries[i].length);
|
||||
int flength = (int)(fend - fstart);
|
||||
int bstart = std::max((int)0,(int)((int64_t)currentOffset - (int64_t)offset));
|
||||
auto strm = vfs->OpenFile(path,"rb");
|
||||
strm->Seek(fstart, Streams::SeekOrigin::Begin);
|
||||
strm->ReadBlock(data+bstart,flength);
|
||||
|
||||
|
||||
|
||||
currentOffset += this->entries[i].length;
|
||||
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// From https://www.seanjoflynn.com/research/bittorrent.html , which is licensed under MIT based on code repo
|
||||
void TorrentDirectoryStream::WriteBlockAt(uint64_t offset, const uint8_t* data, size_t len)
|
||||
{
|
||||
uint64_t currentOffset = 0;
|
||||
uint64_t end = offset + len;
|
||||
for(size_t i = 0; i < this->entries.size(); i++)
|
||||
{
|
||||
if(offset < currentOffset && end < currentOffset) {
|
||||
currentOffset += this->entries[i].length;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(offset > currentOffset + this->entries[i].length && end > currentOffset + this->entries[i].length ){
|
||||
currentOffset += this->entries[i].length;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto path = this->path / this->entries[i].path;
|
||||
auto parent = path.GetParent();
|
||||
if(!vfs->DirectoryExists(parent))
|
||||
vfs->CreateDirectory(parent);
|
||||
|
||||
int64_t fstart = std::max((int64_t)0,(int64_t)offset - (int64_t)currentOffset);
|
||||
int64_t fend = std::min((int64_t)end - (int64_t)currentOffset, (int64_t)this->entries[i].length);
|
||||
int flength = (int)(fend - fstart);
|
||||
int bstart = std::max((int)0,(int)((int64_t)currentOffset - (int64_t)offset));
|
||||
|
||||
this->mtxs[i].Lock();
|
||||
{
|
||||
auto strm = vfs->OpenFile(path,"wb");
|
||||
strm->Seek(fstart, Streams::SeekOrigin::Begin);
|
||||
strm->WriteBlock(data+bstart,flength);
|
||||
}
|
||||
this->mtxs[i].Unlock();
|
||||
|
||||
|
||||
currentOffset += this->entries[i].length;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,7 +230,27 @@ namespace Tesses::Framework::Filesystem
|
||||
#endif
|
||||
}
|
||||
|
||||
std::shared_ptr<LocalFilesystem> LocalFS;
|
||||
|
||||
void LocalFilesystem::Lock(VFSPath path)
|
||||
{
|
||||
auto p2 = VFSPathToSystem(path);
|
||||
const char* fopenPath = p2.c_str();
|
||||
while(true)
|
||||
{
|
||||
FILE* fp = fopen(fopenPath,"wx");
|
||||
if(fp) {
|
||||
fclose(fp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void LocalFilesystem::Unlock(VFSPath path)
|
||||
{
|
||||
std::error_code error;
|
||||
std::filesystem::remove(VFSPathToSystem(path),error);
|
||||
}
|
||||
|
||||
std::shared_ptr<LocalFilesystem> LocalFS = std::make_shared<LocalFilesystem>();
|
||||
}
|
||||
|
||||
// C:/Users/Jim/Joel
|
||||
|
||||
@@ -311,6 +311,34 @@ namespace Tesses::Framework::Filesystem
|
||||
if(vfs != nullptr)
|
||||
vfs->DeleteFile(destPath);
|
||||
|
||||
}
|
||||
void MountableFilesystem::Lock(VFSPath path)
|
||||
{
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
if(vfs != nullptr)
|
||||
vfs->Lock(destPath);
|
||||
|
||||
}
|
||||
void MountableFilesystem::Unlock(VFSPath path)
|
||||
{
|
||||
path = path.CollapseRelativeParents();
|
||||
|
||||
VFSPath destRoot;
|
||||
VFSPath destPath = path;
|
||||
std::shared_ptr<VFS> vfs = root;
|
||||
|
||||
GetFS(path, destRoot, destPath, vfs);
|
||||
|
||||
if(vfs != nullptr)
|
||||
vfs->Unlock(destPath);
|
||||
|
||||
}
|
||||
void MountableFilesystem::GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess)
|
||||
{
|
||||
|
||||
@@ -87,6 +87,15 @@ namespace Tesses::Framework::Filesystem
|
||||
{
|
||||
this->parent->DeleteFile(ToParent(path));
|
||||
}
|
||||
void SubdirFilesystem::Lock(VFSPath path)
|
||||
{
|
||||
this->parent->Lock(ToParent(path));
|
||||
}
|
||||
|
||||
void SubdirFilesystem::Unlock(VFSPath path)
|
||||
{
|
||||
this->parent->Unlock(ToParent(path));
|
||||
}
|
||||
void SubdirFilesystem::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile)
|
||||
{
|
||||
this->parent->CreateSymlink(ToParent(existingFile),ToParent(symlinkFile));
|
||||
|
||||
@@ -109,6 +109,16 @@ namespace Tesses::Framework::Filesystem {
|
||||
if(this->vfs == nullptr) return;
|
||||
this->vfs->DeleteFile(path);
|
||||
}
|
||||
void TempFS::Lock(VFSPath path)
|
||||
{
|
||||
if(this->vfs == nullptr) return;
|
||||
this->vfs->Lock(path);
|
||||
}
|
||||
void TempFS::Unlock(VFSPath path)
|
||||
{
|
||||
if(this->vfs == nullptr) return;
|
||||
this->vfs->Unlock(path);
|
||||
}
|
||||
void TempFS::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile)
|
||||
{
|
||||
if(this->vfs == nullptr) return;
|
||||
@@ -202,4 +212,5 @@ namespace Tesses::Framework::Filesystem {
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -519,5 +519,13 @@ namespace Tesses::Framework::Filesystem
|
||||
}
|
||||
void VFS::Close() {
|
||||
|
||||
}
|
||||
void VFS::Lock(VFSPath path)
|
||||
{
|
||||
|
||||
}
|
||||
void VFS::Unlock(VFSPath path)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -965,5 +965,43 @@ namespace Tesses::Framework::Http {
|
||||
if(!TryGetFirst(key,val)) return false;
|
||||
return val == "true" || val == "on";
|
||||
}
|
||||
std::string HttpUtils::BytesToHex(const std::vector<uint8_t>& data)
|
||||
{
|
||||
std::string text;
|
||||
BytesToHex(text,data);
|
||||
return text;
|
||||
}
|
||||
void HttpUtils::BytesToHex(std::string& text,const std::vector<uint8_t>& data)
|
||||
{
|
||||
if(data.empty()) {
|
||||
text.clear();
|
||||
return;
|
||||
}
|
||||
text.resize(data.size()*2);
|
||||
for(size_t i = 0; i < data.size(); i++)
|
||||
{
|
||||
text[i*2] = NibbleToHex(data[i] >> 4);
|
||||
text[i*2+1] += NibbleToHex(data[i]);
|
||||
}
|
||||
}
|
||||
std::vector<uint8_t> HttpUtils::HexToBytes(const std::string& text)
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
HexToBytes(data,text);
|
||||
return data;
|
||||
}
|
||||
void HttpUtils::HexToBytes(std::vector<uint8_t>& data,const std::string& text)
|
||||
{
|
||||
if(text.empty()) { data.clear(); return;}
|
||||
if(text.size() % 2 != 0) throw std::runtime_error("Text length is not even");
|
||||
|
||||
|
||||
data.resize(text.size()/2);
|
||||
for(size_t i = 0; i < text.size(); i+=2)
|
||||
{
|
||||
uint8_t b = HexToNibble(text[i]) << 4;
|
||||
b |= HexToNibble(text[i+1]);
|
||||
data[i/2] = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
31
src/Random.cpp
Normal file
31
src/Random.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "TessesFramework/Random.hpp"
|
||||
|
||||
namespace Tesses::Framework {
|
||||
Random::Random() : Random((uint64_t)time(NULL))
|
||||
{
|
||||
|
||||
}
|
||||
Random::Random(uint64_t seed) : num(seed)
|
||||
{
|
||||
|
||||
}
|
||||
uint32_t Random::Next(uint32_t max)
|
||||
{
|
||||
return (uint32_t)Next(0,(int32_t)max);
|
||||
}
|
||||
int32_t Random::Next(int32_t min, int32_t max)
|
||||
{
|
||||
uint32_t number = (uint32_t)(Next() >> 31);
|
||||
int32_t range = max-min;
|
||||
|
||||
return (uint32_t)((((double)number / (double)0xFFFFFFFF) * (double)range)+min);
|
||||
}
|
||||
uint64_t Random::Next()
|
||||
{
|
||||
return num = 6364136223846793005 * num + 1;
|
||||
}
|
||||
uint8_t Random::NextByte()
|
||||
{
|
||||
return (uint8_t)Next(0,256);
|
||||
}
|
||||
}
|
||||
253
src/Serialization/Bencode.cpp
Normal file
253
src/Serialization/Bencode.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
#include "TessesFramework/Serialization/Bencode.hpp"
|
||||
|
||||
namespace Tesses::Framework::Serialization::Bencode {
|
||||
BeToken BeDictionary::GetValue(BeString key) const
|
||||
{
|
||||
for(auto item : this->tokens)
|
||||
if(item.first == key) return item.second;
|
||||
return BeUndefined();
|
||||
}
|
||||
void BeDictionary::SetValue(BeString key, BeToken value)
|
||||
{
|
||||
if(std::holds_alternative<BeUndefined>(value))
|
||||
{
|
||||
for(auto idx = this->tokens.begin(); idx != this->tokens.end(); idx++)
|
||||
{
|
||||
if(idx->first == key) {
|
||||
this->tokens.erase(idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto& item : this->tokens)
|
||||
{
|
||||
if(item.first == key)
|
||||
{
|
||||
item.second = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->tokens.emplace_back(key,value);
|
||||
}
|
||||
}
|
||||
|
||||
BeString::BeString()
|
||||
{
|
||||
|
||||
}
|
||||
BeString::BeString(const std::string& text)
|
||||
{
|
||||
this->data.insert(this->data.end(),text.cbegin(),text.cend());
|
||||
}
|
||||
BeString::BeString(const char* text)
|
||||
{
|
||||
size_t len = strlen(text);
|
||||
this->data.insert(this->data.end(),text,text+len);
|
||||
}
|
||||
BeString::BeString(const std::vector<uint8_t>& data) : data(data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BeString::operator std::string() const
|
||||
{
|
||||
return std::string(data.cbegin(),data.cend());
|
||||
}
|
||||
|
||||
|
||||
bool operator==(const BeString& lStr, const BeString& rStr)
|
||||
{
|
||||
return lStr.data == rStr.data;
|
||||
}
|
||||
bool operator==(const BeString& lStr, const std::string& rStr)
|
||||
{
|
||||
if(lStr.data.size() != rStr.size()) return false;
|
||||
return std::equal(lStr.data.cbegin(),lStr.data.cend(),rStr.cbegin());
|
||||
}
|
||||
bool operator==(const std::string& lStr, const BeString& rStr)
|
||||
{
|
||||
if(lStr.size() != rStr.data.size()) return false;
|
||||
return std::equal(lStr.cbegin(),lStr.cend(),rStr.data.cbegin());
|
||||
}
|
||||
bool operator==(const BeString& lStr, const char* rStr)
|
||||
{
|
||||
size_t len = strlen(rStr);
|
||||
if(lStr.data.size() != len) return false;
|
||||
return std::equal(lStr.data.cbegin(),lStr.data.cend(),rStr);
|
||||
}
|
||||
bool operator==(const char* lStr, const BeString& rStr)
|
||||
{
|
||||
size_t len = strlen(lStr);
|
||||
if(rStr.data.size() != len) return false;
|
||||
|
||||
return std::equal(lStr,lStr+len,rStr.data.cbegin());
|
||||
}
|
||||
|
||||
bool operator!=(const BeString& lStr, const BeString& rStr)
|
||||
{
|
||||
return !(lStr == rStr);
|
||||
}
|
||||
bool operator!=(const BeString& lStr, const std::string& rStr)
|
||||
{
|
||||
return !(lStr == rStr);
|
||||
}
|
||||
bool operator!=(const std::string& lStr, const BeString& rStr)
|
||||
{
|
||||
return !(lStr == rStr);
|
||||
}
|
||||
bool operator!=(const BeString& lStr, const char* rStr)
|
||||
{
|
||||
return !(lStr == rStr);
|
||||
}
|
||||
bool operator!=(const char* lStr, const BeString& rStr)
|
||||
{
|
||||
return !(lStr == rStr);
|
||||
}
|
||||
|
||||
void Bencode::Save(std::shared_ptr<Tesses::Framework::Streams::Stream> strm,const BeToken& value)
|
||||
{
|
||||
if(std::holds_alternative<BeArray>(value))
|
||||
{
|
||||
auto& array = std::get<BeArray>(value);
|
||||
strm->WriteByte((uint8_t)'l');
|
||||
for(auto& item : array.tokens)
|
||||
{
|
||||
Save(strm,item);
|
||||
}
|
||||
strm->WriteByte((uint8_t)'e');
|
||||
}
|
||||
else if(std::holds_alternative<BeDictionary>(value))
|
||||
{
|
||||
auto& dict = std::get<BeDictionary>(value);
|
||||
strm->WriteByte((uint8_t)'d');
|
||||
for(auto& item : dict.tokens)
|
||||
{
|
||||
Save(strm,item.first);
|
||||
Save(strm,item.second);
|
||||
}
|
||||
strm->WriteByte((uint8_t)'e');
|
||||
}
|
||||
else if(std::holds_alternative<BeString>(value))
|
||||
{
|
||||
auto& str = std::get<BeString>(value);
|
||||
std::string prefix = std::to_string(str.data.size()) + ":";
|
||||
strm->WriteBlock((const uint8_t*)prefix.data(),prefix.size());
|
||||
strm->WriteBlock(str.data.data(),str.data.size());
|
||||
}
|
||||
else if(std::holds_alternative<int64_t>(value))
|
||||
{
|
||||
int64_t val = std::get<int64_t>(value);
|
||||
std::string str = "i" + std::to_string(val) + "e";
|
||||
strm->WriteBlock((const uint8_t*)str.data(),str.size());
|
||||
}
|
||||
}
|
||||
BeToken Bencode::Load(std::shared_ptr<Tesses::Framework::Streams::Stream> strm)
|
||||
{
|
||||
auto chr = strm->ReadByte();
|
||||
switch(chr)
|
||||
{
|
||||
case 'i':
|
||||
{
|
||||
std::string no;
|
||||
while(true) {
|
||||
chr = strm->ReadByte();
|
||||
if(chr == -1) throw std::out_of_range("End of file");
|
||||
if(chr == 'e') break;
|
||||
no.push_back((char)chr);
|
||||
}
|
||||
return std::stoll(no);
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
{
|
||||
BeDictionary dict;
|
||||
while(true)
|
||||
{
|
||||
auto key = Load(strm);
|
||||
if(std::holds_alternative<BeUndefined>(key)) break;
|
||||
if(!std::holds_alternative<BeString>(key)) throw std::runtime_error("Key must be a string");
|
||||
auto value = Load(strm);
|
||||
if(std::holds_alternative<BeUndefined>(key)) throw std::runtime_error("Incomplete dictionary entry");
|
||||
dict.tokens.emplace_back(std::get<BeString>(key),value);
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
{
|
||||
BeArray array;
|
||||
while(true)
|
||||
{
|
||||
auto tkn = Load(strm);
|
||||
if(std::holds_alternative<BeUndefined>(tkn)) break;
|
||||
array.tokens.push_back(tkn);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
return BeUndefined();
|
||||
case -1:
|
||||
throw std::out_of_range("End of file");
|
||||
default:
|
||||
{
|
||||
std::string no({(char)chr});
|
||||
while(true) {
|
||||
chr = strm->ReadByte();
|
||||
if(chr == -1) throw std::out_of_range("End of file");
|
||||
if(chr == ':') break;
|
||||
no.push_back((char)chr);
|
||||
}
|
||||
auto len = std::stoll(no);
|
||||
if(len < 0) throw std::out_of_range("Less than zero byte string");
|
||||
BeString str;
|
||||
str.data.resize((size_t)len);
|
||||
|
||||
size_t result = strm->ReadBlock(str.data.data(),str.data.size());
|
||||
if(result != str.data.size() || result != (size_t)len) throw std::out_of_range("Didn't read entire string");
|
||||
return str;
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
Json::JToken Bencode::ToJson(const BeToken& tkn)
|
||||
{
|
||||
if(std::holds_alternative<BeDictionary>(tkn))
|
||||
{
|
||||
auto& dict = std::get<BeDictionary>(tkn);
|
||||
Json::JObject o;
|
||||
for(auto& itm : dict.tokens)
|
||||
{
|
||||
o.SetValue(itm.first,ToJson(itm.second));
|
||||
}
|
||||
return o;
|
||||
}
|
||||
if(std::holds_alternative<BeArray>(tkn))
|
||||
{
|
||||
auto& array = std::get<BeArray>(tkn);
|
||||
Json::JArray a;
|
||||
for(auto& itm : array.tokens)
|
||||
{
|
||||
a.Add(ToJson(itm));
|
||||
}
|
||||
return a;
|
||||
}
|
||||
if(std::holds_alternative<BeString>(tkn))
|
||||
{
|
||||
return (std::string)std::get<BeString>(tkn);
|
||||
}
|
||||
if(std::holds_alternative<int64_t>(tkn))
|
||||
{
|
||||
return std::get<int64_t>(tkn);
|
||||
}
|
||||
return Json::JUndefined();
|
||||
}
|
||||
void Bencode::Print(std::shared_ptr<Tesses::Framework::TextStreams::TextWriter> writer, BeToken tkn)
|
||||
{
|
||||
writer->WriteLine(Json::Json::Encode(ToJson(tkn),true));
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ using HttpUtils = Tesses::Framework::Http::HttpUtils;
|
||||
#include <network.h>
|
||||
#define NETWORK_GETSOCKNAME net_getsockname
|
||||
#define NETWORK_RECV net_recv
|
||||
#define NETWORK_POLL net_poll
|
||||
#define sockaddr_storage sockaddr_in
|
||||
#error "Not supported yet"
|
||||
#else
|
||||
@@ -46,6 +47,7 @@ extern "C" {
|
||||
#if defined(AF_UNIX) && !defined(GEKKO) && !defined(__SWITCH__) && !defined(__PS2__)
|
||||
#include <sys/un.h>
|
||||
#endif
|
||||
#include <sys/poll.h>
|
||||
}
|
||||
#endif
|
||||
#if defined(GEKKO)
|
||||
@@ -68,10 +70,13 @@ extern "C" uint32_t if_config( char *local_ip, char *netmask, char *gateway,bool
|
||||
#define NETWORK_GETADDRINFO getaddrinfo
|
||||
#define NETWORK_FREEADDRINFO freeaddrinfo
|
||||
#define NETWORK_GETSOCKNAME getsockname
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define NETWORK_CLOSE closesocket
|
||||
#define NETWORK_POLL WSAPoll
|
||||
#else
|
||||
#define NETWORK_CLOSE close
|
||||
#define NETWORK_POLL poll
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -288,6 +293,28 @@ namespace Tesses::Framework::Streams {
|
||||
uint32_t addr;
|
||||
uint8_t addr_parts[4];
|
||||
} my_addr_t;
|
||||
|
||||
bool NetworkStream::DataAvailable(int timeout)
|
||||
{
|
||||
pollfd fd;
|
||||
fd.events = POLLIN;
|
||||
fd.fd = this->sock;
|
||||
int res= NETWORK_POLL(&fd,1,timeout);
|
||||
if (res == -1) {
|
||||
if(errno == EINTR) return false;
|
||||
throw std::runtime_error("poll() failed");
|
||||
}
|
||||
else if(res == 0) return false;
|
||||
else if(fd.revents & POLLIN)
|
||||
return true;
|
||||
else if(fd.revents & (POLLERR | POLLNVAL))
|
||||
{
|
||||
this->endOfStream=true;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
NetworkStream::NetworkStream(std::string unixPath,bool isServer)
|
||||
{
|
||||
this->endOfStream=false;
|
||||
|
||||
@@ -182,7 +182,6 @@ namespace Tesses::Framework
|
||||
}
|
||||
void TF_Init()
|
||||
{
|
||||
Tesses::Framework::Filesystem::LocalFS = std::make_shared<Tesses::Framework::Filesystem::LocalFilesystem>();
|
||||
#if defined(TESSESFRAMEWORK_ENABLE_SQLITE)
|
||||
sqlite3_initialize();
|
||||
#if defined(GEKKO) || defined(__SWITCH__) || defined(__PS2__)
|
||||
@@ -303,4 +302,41 @@ if (iResult != 0) {
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static std::string TF_FileSizeBin(uint64_t bytes)
|
||||
{
|
||||
if(bytes == 1) return "1 Byte";
|
||||
if(bytes < 1024ULL) return std::to_string(bytes) + " Bytes";
|
||||
if(bytes < 1024ULL*1024ULL) return std::to_string(bytes / 1024ULL) + " kiB";
|
||||
if(bytes < 1024ULL*1024ULL*1024ULL) return std::to_string(bytes / (1024ULL*1024ULL)) + " MiB";
|
||||
if(bytes < 1024ULL*1024ULL*1024ULL*1024ULL) return std::to_string(bytes / (1024ULL*1024ULL*1024ULL)) + " GiB";
|
||||
if(bytes < 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL) return std::to_string(bytes / (1024ULL*1024ULL*1024ULL*1024ULL)) + " TiB";
|
||||
return std::to_string(bytes / (1024ULL*1024ULL*1024ULL*1024ULL*1024ULL)) + " PiB";
|
||||
}
|
||||
static std::string TF_FileSizeDec(uint64_t bytes)
|
||||
{
|
||||
if(bytes == 1) return "1 Byte";
|
||||
if(bytes < 1000ULL) return std::to_string(bytes) + " Bytes";
|
||||
if(bytes < 1000ULL*1000ULL) return std::to_string(bytes / 1000ULL) + " kB";
|
||||
if(bytes < 1000ULL*1000ULL*1000ULL) return std::to_string(bytes / (1000ULL*1000ULL)) + " MB";
|
||||
if(bytes < 1000ULL*1000ULL*1000ULL*1000ULL) return std::to_string(bytes / (1000ULL*1000ULL*1000ULL)) + " GB";
|
||||
if(bytes < 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL) return std::to_string(bytes / (1000ULL*1000ULL*1000ULL*1000ULL)) + " TB";
|
||||
return std::to_string(bytes / (1000ULL*1000ULL*1000ULL*1000ULL*1000ULL)) + " PB";
|
||||
}
|
||||
std::string TF_FileSize(uint64_t bytes, bool usesBin)
|
||||
{
|
||||
return usesBin ? TF_FileSizeBin(bytes) : TF_FileSizeDec(bytes);
|
||||
}
|
||||
|
||||
std::optional<std::string> _argv0=std::nullopt;
|
||||
|
||||
void TF_AllowPortable(std::string argv0)
|
||||
{
|
||||
_argv0 = argv0;
|
||||
}
|
||||
|
||||
std::optional<std::string> TF_GetCommandName()
|
||||
{
|
||||
return _argv0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user