Функции для работы с торрент файлом:
; Создание торрент файла и работа с ним.
; Автор - Пётр
; https://purebasic.mybb.ru/viewtopic.php?id=249
EnableExplicit
;- Создание torrent-файла
Structure TorrentFiles_CreateInfo
FileName.s ; Путь к торрент-файлу.
Path.s ; Путь к файлам.
ClientName.s ; Название торрент-клиента (добавляется в торрент-файл).
ProgName.s ; Название торрент-клиента (для вывода сообщений).
BreakThread.b ; Признак того, что нужно завершить поток.
Directory.b ; 0 - файл; 1- папка.
List announce.s() ; Список анонс-серверов.
Publisher.s
Publisher_Url.s
comment.s ; Комментарий к торренту.
pieceLength.l ; Размер сегмента в байтах.
private.b ; Если 1, то торрент приватный.
*PiecesCB ; Адрес процедуры, в котрой будут передаватся данные о обрабатываемом файле и проценте обработки частей при создании torrent-файла.
ErrorCode.b ; Код ошибки создания torrent-файла.
ErrorString.s
Result.b ; 1 - ОК; 0 - Ошибка.
EndStructure
Structure TorrentFiles_FindFile
Path.s
FileSize.q
EndStructure
Procedure TorrentFiles_Bencoding_W_String(File, String.s)
Protected Bytes
If String<>""
Bytes = StringByteLength(String, #PB_UTF8)
If Bytes>0
WriteString(File, Str(Bytes)+":"+String, #PB_UTF8)
EndIf
EndIf
EndProcedure
Procedure TorrentFiles_Bencoding_W_Int(File, Number.q)
WriteByte(File,'i')
WriteString(File, Str(Number))
WriteByte(File,'e')
EndProcedure
Procedure.l TorrentFiles_W_CalcPieces(AllSize.q) ; Вычисление требуемого числа частей, исходя из общего размера файлов.
Protected Result.l
Result = 65536 ; 64 КБ.
If AllSize>0
If AllSize / 65536 <= 1000 ; 64 КБ.
Result = 65536
ElseIf AllSize / 131072 <= 1000 ; 128 КБ.
Result = 131072
ElseIf AllSize / 262144 <= 1000 ; 256 КБ.
Result = 262144
ElseIf AllSize / 524288 <= 1000 ; 512 КБ.
Result = 524288
ElseIf AllSize / 1048576 <= 1000 ; 1024 КБ.
Result = 1048576
ElseIf AllSize / 2097152 <= 1000 ; 2048 КБ.
Result = 2097152
ElseIf AllSize / 4194304 <= 4000 ; 4096 КБ.
Result = 4194304
ElseIf AllSize / 8388608 <= 4000 ; 8 МБ.
Result = 8388608
ElseIf AllSize / 16777216 <=8000 ; 16 МБ.
Result = 16777216
ElseIf AllSize / 33554432 <=10000 ; 32 МБ.
Result = 33554432
Else ; 64 МБ..
Result = 67108864
EndIf
EndIf
ProcedureReturn Result
EndProcedure
Procedure.q TorrentFiles_SizeFiles(List ListFiles.TorrentFiles_FindFile())
Protected Result.q
Result = 0
ForEach ListFiles()
Result + ListFiles()\FileSize
Next
ProcedureReturn Result
EndProcedure
Prototype TorrentFiles_W_GetPieces_CB(File.s, Pos.f) ; Прогресс обработки частей. Имя файла и процент обработаного (0 - 100).
Procedure.b TorrentFiles_W_GetPieces(File_torrent, Piece_Length, Path.s, List ListFiles.TorrentFiles_FindFile(), List SHA1data.s(), *ProcCB, *BreakThread)
Protected FileID, Result.b, *mem, ReadByte
Protected SHA1.s, Err.b, FileName.s
Protected SizeList, Count, MemPos, t_Piece_Length
Protected CB_Piece.f, CB_Piece_Plus.f, Pieces_CB
Result = #False
Err = #False
ClearList(SHA1data())
t_Piece_Length = Piece_Length
*mem = AllocateMemory(Piece_Length+1000)
CB_Piece_Plus = 100 / (TorrentFiles_SizeFiles(ListFiles())/Piece_Length)
CB_Piece = 0
If *ProcCB
Pieces_CB.TorrentFiles_W_GetPieces_CB = *ProcCB
Pieces_CB("",0)
EndIf
If *mem
SizeList = ListSize(ListFiles()) - 1
Count = 0
MemPos = 0
ForEach ListFiles()
If PeekB(*BreakThread)=1 ; Принудительное прекращение создания торрент-файла.
FreeMemory(*mem)
ProcedureReturn 0
EndIf
FileName=Path+ListFiles()\Path
FileID = ReadFile(#PB_Any, FileName)
If FileID
While Eof(FileID) = 0
ReadByte = ReadData(FileID, *mem+MemPos, t_Piece_Length)
If ReadByte+MemPos>=Piece_Length Or (ReadByte>0 And Count>=SizeList And Eof(FileID) <> 0)
SHA1=SHA1Fingerprint(*mem, ReadByte+MemPos)
If Len(SHA1) = 40
If AddElement(SHA1data())
SHA1data() = SHA1
Else
Err = #True
Break
EndIf
Else
Err = #True
Break
EndIf
MemPos = 0
t_Piece_Length = Piece_Length
If *ProcCB
CB_Piece + CB_Piece_Plus
Pieces_CB(FileName, CB_Piece)
EndIf
Else
t_Piece_Length - ReadByte
MemPos + ReadByte
If ReadByte<=0
Break
EndIf
EndIf
If PeekB(*BreakThread)=1 ; Принудительное прекращение создания торрент-файла.
CloseFile(FileID)
FreeMemory(*mem)
ProcedureReturn 0
EndIf
Wend
Count + 1
CloseFile(FileID)
EndIf
If Err = #True
Break
EndIf
Next
If Err = #False
Result = #True
If *ProcCB
CB_Piece + CB_Piece_Plus
Pieces_CB("", 100)
EndIf
EndIf
FreeMemory(*mem)
EndIf
ProcedureReturn Result
EndProcedure
Procedure TorrentFiles_FindFiles(Path.s, SubPath.s, Count, List ListFiles.TorrentFiles_FindFile(), *BreakThread) ; Сканирование выбранной папки для добавления файлов в torrent-файл.
Protected Directory, Type, Name.s
If PeekB(*BreakThread)=1 ; Принудительное прекращение создания торрент-файла.
ProcedureReturn
EndIf
If Right(Path,1)<>"\":Path + "\":EndIf
If Count<100
Directory = ExamineDirectory(#PB_Any, Path, "*.*") ; Начало сканирования папки.
If Directory
While NextDirectoryEntry(Directory) ; Следующий файл / папка.
If PeekB(*BreakThread)=1 ; Принудительное прекращение создания торрент-файла.
Break
EndIf
Type = DirectoryEntryType(Directory) ; Тип объекта (файл или папка).
Name.s = DirectoryEntryName(Directory) ; Имя объекта.
If Name="." Or Name=".."
Continue
EndIf
If Type = #PB_DirectoryEntry_File ; Найден файл.
InsertElement(ListFiles())
ListFiles()\Path = SubPath + Name
ListFiles()\FileSize = DirectoryEntrySize(Directory)
ElseIf Type = #PB_DirectoryEntry_Directory ; Найдена папка.
TorrentFiles_FindFiles(Path+Name, SubPath+Name+"\", Count+1, ListFiles(), *BreakThread)
EndIf
Wend
FinishDirectory(Directory) ; Завершение сканирования папки.
EndIf
EndIf
EndProcedure
Procedure.b TorrentFiles_CreateTorrent(*TorrentInfo.TorrentFiles_CreateInfo)
Protected File_t, Result.b, Piece_Length, Err
Protected NewList SHA1_Data.s(), ListSize
Protected NewList ListFiles.TorrentFiles_FindFile();,
Protected *SHA1_mem, i, Count, Char_b.a
Protected String.s, SHA1_FilePath.s, sTemp.s, Pos
Protected GMT, tzi.TIME_ZONE_INFORMATION, Pieces_CB
Protected FileName.s, AddFile.s, Directory.b, TorrentClientName.s, ProgName.s
ClearList(SHA1_Data())
ClearList(ListFiles())
If *TorrentInfo\PiecesCB
Pieces_CB.TorrentFiles_W_GetPieces_CB = *TorrentInfo\PiecesCB
Pieces_CB("",0)
EndIf
FileName = *TorrentInfo\FileName
AddFile = *TorrentInfo\Path
Directory = *TorrentInfo\Directory
TorrentClientName = *TorrentInfo\ClientName
ProgName = *TorrentInfo\ProgName
Piece_Length = *TorrentInfo\pieceLength
*TorrentInfo\ErrorCode = 0
*TorrentInfo\ErrorString = ""
*TorrentInfo\Result = 0
*TorrentInfo\BreakThread = 0
Result = #False
Err = #False
If Directory = #True
If FileSize(AddFile) <> -2
*TorrentInfo\ErrorCode = 1
*TorrentInfo\ErrorString = AddFile
ProcedureReturn 0
EndIf
Else
If FileSize(AddFile)<=0
*TorrentInfo\ErrorCode = 2
*TorrentInfo\ErrorString = AddFile
ProcedureReturn 0
EndIf
EndIf
*SHA1_mem = AllocateMemory(32)
If *SHA1_mem = 0
*TorrentInfo\ErrorCode = 3
ProcedureReturn 0
EndIf
File_t = CreateFile(#PB_Any, FileName)
If File_t
WriteByte(File_t,'d')
ListSize = ListSize(*TorrentInfo\announce())
If ListSize>0
TorrentFiles_Bencoding_W_String(File_t, "announce")
SelectElement(*TorrentInfo\announce(),0)
TorrentFiles_Bencoding_W_String(File_t, *TorrentInfo\announce())
If ListSize > 1
TorrentFiles_Bencoding_W_String(File_t, "announce-list")
WriteByte(File_t,'l')
WriteByte(File_t,'l')
ForEach *TorrentInfo\announce()
TorrentFiles_Bencoding_W_String(File_t, *TorrentInfo\announce())
Next
WriteByte(File_t,'e')
WriteByte(File_t,'e')
EndIf
EndIf
If *TorrentInfo\comment<>""
TorrentFiles_Bencoding_W_String(File_t, "comment")
TorrentFiles_Bencoding_W_String(File_t, *TorrentInfo\comment)
EndIf
If TorrentClientName<>""
TorrentFiles_Bencoding_W_String(File_t, "created by")
TorrentFiles_Bencoding_W_String(File_t, TorrentClientName)
EndIf
TorrentFiles_Bencoding_W_String(File_t, "creation date")
If GetTimeZoneInformation_(@tzi) = 2 ; значит время летнее
GMT = tzi\Bias/(-60)+1 ; Например в Лондоне 12 часов, в Москве 15, разница tzi\Bias = 12-15 = -180 минут
Else
GMT = tzi\Bias/(-60) ; Иначе - стандартное время
EndIf
TorrentFiles_Bencoding_W_Int(File_t, AddDate(Date(),#PB_Date_Hour,-GMT))
TorrentFiles_Bencoding_W_String(File_t, "encoding")
TorrentFiles_Bencoding_W_String(File_t, "UTF-8")
TorrentFiles_Bencoding_W_String(File_t, "info")
WriteByte(File_t,'d')
If *TorrentInfo\BreakThread = 1 ; Принудительное прекращение создания торрент-файла.
CloseFile(File_t)
FreeMemory(*SHA1_mem)
ProcedureReturn 0
EndIf
If Directory = #True ; Добавляем папку
sTemp=ReverseString(AddFile)
Pos=FindString(sTemp,"\",2)
If Pos>0
sTemp = Right(AddFile, Pos-1)
sTemp = RemoveString(sTemp,"\")
If sTemp=""
*TorrentInfo\ErrorCode = 4 ; Нет папки.
EndIf
Else
*TorrentInfo\ErrorCode = 4 ; Странно, выбрана папка, а слеша нет.
*TorrentInfo\ErrorString = AddFile
Err = #True
EndIf
If Err = #False
If *TorrentInfo\PiecesCB
Pieces_CB("Сбор информации о файлах.",0)
EndIf
TorrentFiles_FindFiles(AddFile, "", 0, ListFiles(), @*TorrentInfo\BreakThread) ; Получаем список файлов для добавления в торрент.
If *TorrentInfo\BreakThread = 1 ; Принудительное прекращение создания торрент-файла.
CloseFile(File_t)
FreeMemory(*SHA1_mem)
ProcedureReturn 0
EndIf
ListSize = ListSize(ListFiles())
If ListSize>0 And TorrentFiles_SizeFiles(ListFiles())>0
TorrentFiles_Bencoding_W_String(File_t, "files")
WriteByte(File_t,'l')
ForEach ListFiles()
WriteByte(File_t,'d')
TorrentFiles_Bencoding_W_String(File_t, "length")
TorrentFiles_Bencoding_W_Int(File_t, ListFiles()\FileSize)
TorrentFiles_Bencoding_W_String(File_t, "path")
WriteByte(File_t,'l')
String = ListFiles()\Path
String = ReplaceString(String, "/","\")
Count = CountString(String, "\")
If Count>0
For i=1 To Count
TorrentFiles_Bencoding_W_String(File_t, StringField(String, i, "\"))
Next i
EndIf
String = GetFilePart(String)
TorrentFiles_Bencoding_W_String(File_t, String)
WriteByte(File_t,'e')
WriteByte(File_t,'e')
If *TorrentInfo\BreakThread = 1 ; Принудительное прекращение создания торрент-файла.
CloseFile(File_t)
FreeMemory(*SHA1_mem)
ProcedureReturn 0
EndIf
Next
WriteByte(File_t,'e')
TorrentFiles_Bencoding_W_String(File_t, "name")
TorrentFiles_Bencoding_W_String(File_t, sTemp)
Else
*TorrentInfo\ErrorCode = 5
Err = #True
EndIf
EndIf
SHA1_FilePath = AddFile
Else ; Добавляем только один файл
If FileSize(AddFile)>0
TorrentFiles_Bencoding_W_String(File_t, "length")
TorrentFiles_Bencoding_W_Int(File_t, FileSize(AddFile))
TorrentFiles_Bencoding_W_String(File_t, "name")
TorrentFiles_Bencoding_W_String(File_t, GetFilePart(AddFile))
If AddElement(ListFiles())
ListFiles()\Path = GetFilePart(AddFile)
ListFiles()\FileSize = FileSize(AddFile)
Else
*TorrentInfo\ErrorCode = 6
Err = #True
EndIf
SHA1_FilePath = GetPathPart(AddFile)
Else
*TorrentInfo\ErrorCode = 7 ; Файл пустой.
Err = #True
EndIf
EndIf
If Err = #False
If Piece_Length < 16*1024
Piece_Length = TorrentFiles_W_CalcPieces(TorrentFiles_SizeFiles(ListFiles())) ; Автоматический рассчет размера частей.
EndIf
If *TorrentInfo\BreakThread = 1 ; Принудительное прекращение создания торрент-файла.
CloseFile(File_t)
FreeMemory(*SHA1_mem)
ProcedureReturn 0
EndIf
TorrentFiles_Bencoding_W_String(File_t, "piece length") ; Размер сегмента в байтах
TorrentFiles_Bencoding_W_Int(File_t, Piece_Length)
TorrentFiles_Bencoding_W_String(File_t, "pieces")
If SHA1_FilePath<>"" And Right(SHA1_FilePath,1)<>"\":SHA1_FilePath + "\":EndIf
; Создание хешей.
If TorrentFiles_W_GetPieces(File_t, Piece_Length, SHA1_FilePath, ListFiles(), SHA1_Data(), *TorrentInfo\PiecesCB, @*TorrentInfo\BreakThread) = #True
ListSize = ListSize(SHA1_Data())
If ListSize>0
WriteString(File_t, Str(ListSize*20)+":")
ForEach SHA1_Data()
FillMemory(*SHA1_mem, 20, 0)
Count=0
String = SHA1_Data()
For i=1 To 40 Step 2
Char_b = Val("$"+Mid(String, i, 2))
PokeA(*SHA1_mem+Count, Char_b)
Count + 1
Next i
WriteData(File_t, *SHA1_mem, 20)
If *TorrentInfo\BreakThread = 1 ; Принудительное прекращение создания торрент-файла.
CloseFile(File_t)
FreeMemory(*SHA1_mem)
ProcedureReturn 0
EndIf
Next
Else
*TorrentInfo\ErrorCode = 8
Err = #True
EndIf
Else
*TorrentInfo\ErrorCode = 9
Err = #True
EndIf
If *TorrentInfo\private = 1
TorrentFiles_Bencoding_W_String(File_t, "private")
TorrentFiles_Bencoding_W_Int(File_t, 1)
EndIf
WriteByte(File_t,'e')
If *TorrentInfo\Publisher<>""
TorrentFiles_Bencoding_W_String(File_t, "publisher")
TorrentFiles_Bencoding_W_String(File_t, *TorrentInfo\Publisher)
EndIf
If *TorrentInfo\Publisher_Url<>""
TorrentFiles_Bencoding_W_String(File_t, "publisher-url")
TorrentFiles_Bencoding_W_String(File_t, *TorrentInfo\Publisher_Url)
EndIf
WriteByte(File_t,'e')
If Err = #False
Result = #True
EndIf
EndIf
CloseFile(File_t)
Else
*TorrentInfo\ErrorCode = 10
*TorrentInfo\ErrorString = FileName
EndIf
If Err = #True
If FileSize(FileName)>=0
DeleteFile(FileName)
EndIf
EndIf
FreeMemory(*SHA1_mem)
FreeList(ListFiles())
FreeList(SHA1_Data())
*TorrentInfo\Result = Result
ProcedureReturn Result
EndProcedure
Procedure TorrentFiles_CreateTorrent_ShowError(ErrorCode.b, Err_String.s="", ProgName.s="") ; Отображение ошибок, произошедших при создании torrent-файла.
Protected ErrorString.s
If ErrorCode>0
If ProgName<>"" : ProgName + " — " : EndIf
Select ErrorCode
Case 1
ErrorString = "Не найдена папка с файлами."+Chr(10)+Err_String
Case 2
ErrorString = "Не найден добавляемый файл."+Chr(10)+Err_String
Case 3
ErrorString = "Ошибка выделения памяти под SHA1 хеш."
Case 4
ErrorString = "Ошибка в пути к папке с файлами."+Chr(10)+"ВНИМАНИЕ! Нельзя указывать в раздаче корневую папку диска!"+Chr(10)+Err_String
Case 5
ErrorString = "Выбранные файлы или папки пустые!"+Chr(10)+"Выберите другое место с файлами."
Case 6
ErrorString = "Ошибка при добавлении элемента в список!"+Chr(10)+"Возможно заканчивается свободная память."
Case 7
ErrorString = "Выбранный файл пустой (0 байт)!"+Chr(10)+"Выберите другй файл."
Case 8
ErrorString = "Отсутсвуют SHA1 хеши файлов."
Case 9
ErrorString = "Ошибка при генерации SHA1 хешей файлов."
Case 10
ErrorString = "Не удалось создать torrent-файл."+Chr(10)+Err_String
EndSelect
MessageRequester(ProgName + "создание torrent-файла.", ErrorString, #MB_ICONWARNING)
EndIf
EndProcedure
;- Расшифровка torrent-файла.
Structure TorrentFiles_TorrentInfo_pieces ; Описание частей.
SHA1_string.s ; SHA1 хеш в виде строки (нижний регистр).
SHA1_bin.a[20] ; SHA1 хеш в бинарном виде (20 байт).
EndStructure
Structure TorrentFiles_TorrentInfo_file ; Информация о файлах торрента.
length.q ; Размер файла.
List path.s() ; Путь, (все папки по отдельности и в конце имя файла).
EndStructure
Structure TorrentFiles_TorrentInfo ; Информация из torrent-файла.
announce.s ; Адрес трекера.
List announce_list.s() ; Если есть еще адреса трекеров, то они будут здесь.
Publisher.s
Publisher_Url.s
comment.s ; Комментарий в torrent-файле.
created_by.s ; Информация о создавшем torrent-файл.
creation_date.l ; Дата создания torrent-файла по Гринвичу (UTC).
encoding.b ; Кодировка файла.
INFO_Hash.s ; SHA1 хеш-сумма области "info" torrent-файла.
TypeDir.b ; Тип торрента. 1 - папка; любое другое значение - файл.
CurrentDir_Name.s ; Имя корневой папки с файлами торрента (только если TypeDir = 1).
List files.TorrentFiles_TorrentInfo_file() ; Список всех файлов торрента.
All_Size.q ; Размер всех файлов торрента в байтах.
piece_length.l ; Размер части.
Array pieces.TorrentFiles_TorrentInfo_pieces(0) ; SHA1 хеш-суммы частей.
private.b ; 1 - торрент приватный; 0 не приватный.
ErrorCode.b ; Признак ошбки при расшифровке torrent-файла.
ErrorString.s ; Описание ошибки (чаще всего - имя файла, при работе с которым произошла ошибка).
EndStructure
Procedure.s TorrentFiles_GetBencoding_String(*TorrentMem, *M_Pos, MaxSize, encoding, *ErrFlag)
Protected MemPos, Bytes, i
Protected Err.b, Char.a, String.s
String=""
Err = #False
MemPos = PeekI(*M_Pos)
For i=MemPos To MemPos+18
Char = PeekA(*TorrentMem+i)
If Char=':'
i+1
Break
ElseIf Char<'0' Or Char>'9'
Err = #True
Break
EndIf
Next i
If Err = #False And Char=':' And i>MemPos+1 And i<=MemPos+18
Bytes = Val(PeekS(*TorrentMem+MemPos, i-MemPos, #PB_Ascii))
If Bytes>=0 And Bytes <= MaxSize-i
MemPos + (i-MemPos)
If Bytes>0
String = PeekS(*TorrentMem+MemPos, Bytes, encoding)
Else
String = ""
EndIf
MemPos + Bytes
PokeI(*M_Pos, MemPos)
Else
Err = #True
EndIf
Else
Err = #True
EndIf
If Err = #True
PokeB(*ErrFlag, 1)
EndIf
ProcedureReturn String
EndProcedure
Procedure.q TorrentFiles_GetBencoding_Int(*TorrentMem, *M_Pos, MaxSize, *ErrFlag)
Protected MemPos, Int.q, i
Protected Err.b, Char.a
Int = 0
Err = #False
MemPos = PeekI(*M_Pos)
Char = PeekA(*TorrentMem+MemPos)
If Char='i' Or Char='I'
MemPos+1
For i=MemPos To MemPos+18
Char = PeekA(*TorrentMem+i)
If Char='e' Or Char='E'
i+1
Break
ElseIf (Char<'0' Or Char>'9') And Char<>'-'
Err = #True
Break
EndIf
Next i
If Err = #False And i-MemPos-1 > 0 And (Char='e' Or Char='E') And i<=MemPos+18
Int = Val(PeekS(*TorrentMem+MemPos, i-MemPos-1, #PB_Ascii))
MemPos = i
PokeI(*M_Pos, MemPos)
Else
Err = #True
EndIf
EndIf
If Err = #True
PokeB(*ErrFlag, 1)
EndIf
ProcedureReturn Int
EndProcedure
Procedure.a TorrentFiles_GetBencoding_Pieces(*Torrent.TorrentFiles_TorrentInfo, *TorrentMem, *M_Pos, MaxSize)
Protected MemPos, String.s, i
Protected Bytes, Result.a, Char.a, EndData
Protected x, y
String=""
Result = #True
MemPos = PeekI(*M_Pos)
For i=MemPos To MemPos+18
Char = PeekA(*TorrentMem+i)
If Char=':'
i+1
Break
ElseIf Char<'0' Or Char>'9'
Result = #False
Break
EndIf
Next i
If Result = #True And i>MemPos+1 And Char=':' And i<=MemPos+18
Bytes = Val(PeekS(*TorrentMem+MemPos, i-MemPos, #PB_Ascii))
If Bytes>0 And Bytes < MaxSize-i
If Bytes % 20 = 0
ReDim *Torrent\pieces(Bytes/20)
MemPos + (i-MemPos)
EndData = MemPos + Bytes - 20
x = 0
For i=MemPos To EndData Step 20
CopyMemory(*TorrentMem+i, @*Torrent\pieces(x)\SHA1_bin, 20)
String=""
For y=i To i+19
String+RSet(Hex(PeekA(*TorrentMem+y),#PB_Ascii), 2, "0")
Next y
*Torrent\pieces(x)\SHA1_string = LCase(String)
x+1
Next i
PokeI(*M_Pos, MemPos + Bytes)
Else
Result = #False
EndIf
Else
Result = #False
EndIf
Else
Result = #False
EndIf
ProcedureReturn Result
EndProcedure
Procedure.b TorrentFiles_Bencode_D_Decoder(Comand.s, *Parameter, *Torrent.TorrentFiles_TorrentInfo, *Temp_f)
Protected Result.b
Result = #True
If Comand<>""
Select LCase(Comand)
Case "created by"
*Torrent\created_by = PeekS(*Parameter)
Case "creation date"
*Torrent\creation_date = PeekL(*Parameter)
Case "encoding"
Select LCase(PeekS(*Parameter))
Case "ascii", "ansi"
*Torrent\encoding = #PB_Ascii
Case "utf-8", "utf 8"
*Torrent\encoding = #PB_UTF8
Case "unicode"
*Torrent\encoding = #PB_Unicode
EndSelect
Case "comment"
*Torrent\comment = PeekS(*Parameter)
Case "announce"
*Torrent\announce = PeekS(*Parameter)
Case "announce-list"
AddElement(*Torrent\announce_list())
*Torrent\announce_list() = PeekS(*Parameter)
Case "length"
If PeekB(*Temp_f)=0
If ListSize(*Torrent\files())>0
LastElement(*Torrent\files())
EndIf
If AddElement(*Torrent\files()) = 0
*Torrent\ErrorCode = 5
Result = #False
ProcedureReturn Result
EndIf
PokeB(*Temp_f, 1)
EndIf
*Torrent\files()\length = PeekQ(*Parameter)
Case "path"
If *Torrent\TypeDir = #True
If ListSize(*Torrent\files())>0
LastElement(*Torrent\files())
If AddElement(*Torrent\files()\path())
*Torrent\files()\path() = PeekS(*Parameter)
Else
*Torrent\ErrorCode = 5
Result = #False
EndIf
Else
Result = #False
EndIf
Else
Result = #False ; Если это не папка, то чего присутсвует "path"? Значит ошибка.
EndIf
Case "name"
If *Torrent\TypeDir = #True ; Много файлов (папка).
*Torrent\CurrentDir_Name = PeekS(*Parameter)
Else ; Один файл.
If PeekB(*Temp_f)=0
If AddElement(*Torrent\files()) = 0
*Torrent\ErrorCode = 5
Result = #False
ProcedureReturn Result
EndIf
PokeB(*Temp_f, 1)
EndIf
If AddElement(*Torrent\files()\path())
*Torrent\files()\path() = PeekS(*Parameter)
Else
*Torrent\ErrorCode = 5
Result = #False
EndIf
EndIf
Case "piece length"
*Torrent\piece_length = PeekL(*Parameter)
Case "private"
If PeekQ(*Parameter) = 1
*Torrent\private = 1
EndIf
Case "publisher"
*Torrent\Publisher = PeekS(*Parameter)
Case "publisher-url"
*Torrent\Publisher_Url = PeekS(*Parameter)
EndSelect
EndIf
ProcedureReturn Result
EndProcedure
Procedure TorrentFiles_LoadTorrent_CheckErr(*Torrent.TorrentFiles_TorrentInfo) ; Проверка на ошибки данных torrent-файла.
Protected Result, F_Size.q, A_size, l_size
Result = #False
F_Size = 0
l_size=ListSize(*Torrent\files())
If l_size>0
If ListSize(*Torrent\files()\path())>0
If *Torrent\INFO_Hash<>""
A_size = ArraySize(*Torrent\pieces())
If A_size > 0
ForEach *Torrent\files()
F_Size + *Torrent\files()\length
Next
If *Torrent\piece_length >= 16384 And F_Size =< *Torrent\piece_length*A_size And F_Size >= *Torrent\piece_length*(A_size-1)
*Torrent\All_Size = F_Size
If *Torrent\TypeDir = #True ; В торренте папка.
Result = #True
Else ; В торренте файл.
*Torrent\TypeDir = #False
If l_size = 1 And ListSize(*Torrent\files()\path()) = 1 ; Поскольку в торренте файл, то должна быть только одна запись о файле.
Result = #True
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
ProcedureReturn Result
EndProcedure
Procedure TorrentFiles_BencodeParser(deep.l, Command.a, MaxSize, ComString.s, *TorrentMem, *Torrent.TorrentFiles_TorrentInfo, *PosMem, *ErrState)
Protected Char.a, State_D.b, Temp_f.b, MemPos
Protected qVar.q, String.s, SubErr.b, Info_pos
If deep>=20 Or PeekB(*ErrState)<>0
ProcedureReturn
EndIf
State_D=0
MemPos = PeekI(*PosMem)
Temp_f = 0
SubErr = 0
Info_pos = 0
Repeat
Char = PeekA(*TorrentMem + MemPos)
Select Char
Case 'd', 'D' ; Найдена директива.
MemPos + 1
TorrentFiles_BencodeParser(deep+1, 'd', MaxSize, ComString, *TorrentMem, *Torrent, @MemPos, *ErrState)
State_D = 0 : Temp_f = 0
If Info_pos>0 And LCase(ComString) = "info"
If Info_pos < MemPos
*Torrent\INFO_Hash = SHA1Fingerprint(*TorrentMem+Info_pos, MemPos-Info_pos)
Else
PokeB(*ErrState, 1)
EndIf
EndIf
Case 'l', 'L' ; Найден список.
MemPos + 1
TorrentFiles_BencodeParser(deep+1, 'l', MaxSize, ComString, *TorrentMem, *Torrent, @MemPos, *ErrState)
State_D = 0 : Temp_f = 0
Case 'i', 'I' ; Найдено число (в символьном виде).
If Command='d'
SubErr = 0
qVar = TorrentFiles_GetBencoding_Int(*TorrentMem, @MemPos, MaxSize, @SubErr)
If SubErr = 0
If State_D <> 0
State_D = 0
If TorrentFiles_Bencode_D_Decoder(ComString, @qVar, *Torrent, @Temp_f) = #False
PokeB(*ErrState, 1)
EndIf
EndIf
Else
PokeB(*ErrState, 1)
EndIf
ElseIf Command='l'
If TorrentFiles_Bencode_D_Decoder(ComString, @qVar, *Torrent, @Temp_f) = #False
PokeB(*ErrState, 1)
EndIf
EndIf
Case '0' To '9' ; Найдена строка.
If State_D = 0 Or LCase(ComString)<>"pieces"
SubErr = 0
String=TorrentFiles_GetBencoding_String(*TorrentMem, @MemPos, MaxSize, *Torrent\encoding, @SubErr)
If SubErr = 0
If Command='d'
If LCase(String)="files" And LCase(ComString)="info"
*Torrent\TypeDir = #True
State_D = 0
Else
If State_D = 0 And String<>""
State_D = 1
ComString = String
If LCase(ComString)="info"
Info_pos = MemPos
EndIf
Else
State_D = 0
If TorrentFiles_Bencode_D_Decoder(ComString, @String, *Torrent, @Temp_f) = #False
PokeB(*ErrState, 1)
EndIf
ComString = ""
EndIf
EndIf
ElseIf Command='l'
If TorrentFiles_Bencode_D_Decoder(ComString, @String, *Torrent, @Temp_f) = #False
PokeB(*ErrState, 1)
EndIf
EndIf
Else
PokeB(*ErrState, 1)
EndIf
Else ; SHA1 хеши.
State_D = 0
If TorrentFiles_GetBencoding_Pieces(*Torrent, *TorrentMem, @MemPos, MaxSize) = #False
PokeB(*ErrState, 1)
EndIf
EndIf
Case 'e', 'E' ; Найдена завершающая команда.
PokeI(*PosMem, MemPos+1)
ProcedureReturn
Default
MemPos + 1
PokeB(*ErrState, 1)
EndSelect
If PeekB(*ErrState)<>0
PokeI(*PosMem, MemPos)
ProcedureReturn
EndIf
Until deep>=20 Or MemPos>MaxSize
If deep>=20
PokeI(*PosMem, MemPos)
ElseIf MemPos>MaxSize And deep>0
PokeI(*PosMem, MemPos)
EndIf
EndProcedure
Procedure TorrentFiles_LoadTorrentFile(TorrentFile.s, *Torrent.TorrentFiles_TorrentInfo)
Protected Result.b, Err.b, File_t, FileSize.q
Protected *TorrentMem,MemPos, Char.a, FileSize_t.q
Protected NewList CommandInfo.a(), deep
Result = #False
Err = #False
deep = 0 ; Глубина рекурсии.
ClearStructure(*Torrent, TorrentFiles_TorrentInfo)
ClearList(CommandInfo())
NewList *Torrent\announce_list()
NewList *Torrent\files()
Dim *Torrent\pieces(0)
*Torrent\ErrorCode = 0
*Torrent\encoding = #PB_UTF8
*Torrent\TypeDir = #False
*TorrentMem = 0
File_t = 0
File_t = ReadFile(#PB_Any, TorrentFile)
If File_t
FileSize = Lof(File_t)
If FileSize>0 And FileSize<20000000
*TorrentMem = AllocateMemory(FileSize+10)
If *TorrentMem
FillMemory(*TorrentMem, FileSize+8, 0)
If ReadData(File_t, *TorrentMem, FileSize) <> FileSize
Err = #True
*Torrent\ErrorCode = 1 ; Число считанных байт не равно размеру файла.
EndIf
Else
Err = #True
*Torrent\ErrorCode = 2 ; Ошибка выделения памяти.
EndIf
Else
Err = #True
*Torrent\ErrorCode = 3 ; Некорректный размер torrent-файла.
EndIf
CloseFile(File_t)
File_t = 0
Else
Err = #True
*Torrent\ErrorCode = 4 ; Не получилось открыть для чтения torrent-файл.
EndIf
If Err = #True
*Torrent\ErrorString = TorrentFile
If *TorrentMem
FreeMemory(*TorrentMem)
EndIf
If File_t
CloseFile(File_t)
EndIf
FreeList(CommandInfo())
ProcedureReturn 0
EndIf
MemPos=0
FileSize_t = FileSize - 1
TorrentFiles_BencodeParser(0, 0, FileSize_t, "", *TorrentMem, *Torrent, @MemPos, @Err)
If Err = #False
If TorrentFiles_LoadTorrent_CheckErr(*Torrent) = #True
Result = #True
If *Torrent\announce<>""
Char=0
ForEach *Torrent\announce_list()
If *Torrent\announce_list() = *Torrent\announce
Char=1
Break
EndIf
Next
If Char=0
FirstElement(*Torrent\announce_list())
If InsertElement(*Torrent\announce_list())
*Torrent\announce_list() = *Torrent\announce
EndIf
EndIf
EndIf
EndIf
EndIf
If Result <> #True
*Torrent\ErrorString = TorrentFile
If *Torrent\ErrorCode <> 5 : *Torrent\ErrorCode = 6 : EndIf
EndIf
If *TorrentMem
FreeMemory(*TorrentMem)
EndIf
FreeList(CommandInfo())
ProcedureReturn Result
EndProcedure
Procedure TorrentFiles_LoadTorrentFile_ShowEror(ErrorCode.a, Err_String.s="", ProgName.s="")
Protected ErrorString.s
If ErrorCode>0
If ProgName<>"" : ProgName + " — " : EndIf
Select ErrorCode
Case 1
ErrorString = "Ошибка при чтении torrent-файла."+Chr(10)+Err_String
Case 2
ErrorString = "Ошибка при чтении torrent-файла (память)."+Chr(10)+Err_String
Case 3
ErrorString = "Некорректный размер torrent-файла."+Chr(10)+Err_String
Case 4
ErrorString = "Не получилось открыть для чтения torrent-файл."+Chr(10)+Err_String
Case 5
ErrorString = "Ошибка при выделении памяти под данные."+Chr(10)+Err_String
Case 6
ErrorString = "Ошибка в структуре torrent-файла."+Chr(10)+Err_String
EndSelect
MessageRequester(ProgName + "декодирование torrent-файла.", ErrorString, #MB_ICONWARNING)
EndIf
EndProcedure
; Эта процедура вызывается для создания тррент файла по уже известным данным.
Procedure.b TorrentFiles_CreateSysFile_Torrent(FileName.s, *TorrentInfo.TorrentFiles_TorrentInfo)
Protected File_t, Result.b, Err, SizeArray
Protected *SHA1_mem, i, Count, Char_b.a
Protected String.s, Pos
Protected Pieces_CB, ListSize
Result = #False
Err = #False
*SHA1_mem = AllocateMemory(32)
If *SHA1_mem = 0
*TorrentInfo\ErrorCode = 3
ProcedureReturn 0
EndIf
File_t = CreateFile(#PB_Any, FileName)
If File_t
WriteByte(File_t,'d')
If *TorrentInfo\announce<>""
TorrentFiles_Bencoding_W_String(File_t, "announce")
TorrentFiles_Bencoding_W_String(File_t, *TorrentInfo\announce)
EndIf
ListSize = ListSize(*TorrentInfo\announce_list())
If ListSize>0
TorrentFiles_Bencoding_W_String(File_t, "announce-list")
WriteByte(File_t,'l')
WriteByte(File_t,'l')
ForEach *TorrentInfo\announce_list()
TorrentFiles_Bencoding_W_String(File_t, *TorrentInfo\announce_list())
Next
WriteByte(File_t,'e')
WriteByte(File_t,'e')
EndIf
If *TorrentInfo\comment<>""
TorrentFiles_Bencoding_W_String(File_t, "comment")
TorrentFiles_Bencoding_W_String(File_t, *TorrentInfo\comment)
EndIf
If *TorrentInfo\created_by<>""
TorrentFiles_Bencoding_W_String(File_t, "created by")
TorrentFiles_Bencoding_W_String(File_t, *TorrentInfo\created_by)
EndIf
TorrentFiles_Bencoding_W_String(File_t, "creation date")
TorrentFiles_Bencoding_W_Int(File_t, *TorrentInfo\creation_date)
TorrentFiles_Bencoding_W_String(File_t, "encoding")
TorrentFiles_Bencoding_W_String(File_t, "UTF-8")
TorrentFiles_Bencoding_W_String(File_t, "info")
WriteByte(File_t,'d')
If *TorrentInfo\TypeDir = #True ; Добавляем папку
If Err = #False
ListSize = ListSize(*TorrentInfo\files())
If ListSize>0
TorrentFiles_Bencoding_W_String(File_t, "files")
WriteByte(File_t,'l')
ForEach *TorrentInfo\files()
WriteByte(File_t,'d')
TorrentFiles_Bencoding_W_String(File_t, "length")
TorrentFiles_Bencoding_W_Int(File_t, *TorrentInfo\files()\length)
TorrentFiles_Bencoding_W_String(File_t, "path")
WriteByte(File_t,'l')
ForEach *TorrentInfo\files()\path()
TorrentFiles_Bencoding_W_String(File_t, *TorrentInfo\files()\path())
Next
WriteByte(File_t,'e')
WriteByte(File_t,'e')
Next
WriteByte(File_t,'e')
TorrentFiles_Bencoding_W_String(File_t, "name")
TorrentFiles_Bencoding_W_String(File_t, *TorrentInfo\CurrentDir_Name)
EndIf
EndIf
Else ; Добавляем только один файл
SelectElement(*TorrentInfo\files(),0)
TorrentFiles_Bencoding_W_String(File_t, "length")
TorrentFiles_Bencoding_W_Int(File_t, *TorrentInfo\files()\length)
SelectElement(*TorrentInfo\files()\path(),0)
TorrentFiles_Bencoding_W_String(File_t, "name")
TorrentFiles_Bencoding_W_String(File_t, *TorrentInfo\files()\path())
EndIf
If Err = #False
TorrentFiles_Bencoding_W_String(File_t, "piece length") ; Размер сегмента в байтах
TorrentFiles_Bencoding_W_Int(File_t, *TorrentInfo\Piece_Length)
TorrentFiles_Bencoding_W_String(File_t, "pieces")
; Создание хешей.
SizeArray = ArraySize(*TorrentInfo\pieces())-1
If SizeArray>=0
WriteString(File_t, Str((SizeArray+1)*20)+":")
For i=0 To SizeArray
FillMemory(*SHA1_mem, 20, 0)
For Pos=0 To 19
PokeA(*SHA1_mem+Pos, *TorrentInfo\pieces(i)\SHA1_bin[Pos])
Next Pos
WriteData(File_t, *SHA1_mem, 20)
Next i
EndIf
If *TorrentInfo\private = 1
TorrentFiles_Bencoding_W_String(File_t, "private")
TorrentFiles_Bencoding_W_Int(File_t, 1)
EndIf
WriteByte(File_t,'e')
If *TorrentInfo\Publisher<>""
TorrentFiles_Bencoding_W_String(File_t, "publisher")
TorrentFiles_Bencoding_W_String(File_t, *TorrentInfo\Publisher)
EndIf
If *TorrentInfo\Publisher_Url<>""
TorrentFiles_Bencoding_W_String(File_t, "publisher-url")
TorrentFiles_Bencoding_W_String(File_t, *TorrentInfo\Publisher_Url)
EndIf
WriteByte(File_t,'e')
If Err = #False
Result = #True
EndIf
EndIf
CloseFile(File_t)
EndIf
If Err = #True
If FileSize(FileName)>=0
DeleteFile(FileName)
EndIf
EndIf
FreeMemory(*SHA1_mem)
ProcedureReturn Result
EndProcedure
DisableExplicitСоздание торрент файла:

; Создание торрент файла.
; Автор - Пётр
; https://purebasic.mybb.ru/viewtopic.php?id=249
XIncludeFile "TorrentFiles.pbi"
; Идентификаторы окон.
Enumeration
#CreateTorrent_Win
EndEnumeration
; Идентификаторы гаджетов.
Enumeration
; Гаджеты окна "Создание торрента".
#CreateTorrent_Text_0
#CreateTorrent_Radio_File
#CreateTorrent_Radio_Dir
#CreateTorrent_String_Path
#CreateTorrent_Button_Path
#CreateTorrent_Text_5
#CreateTorrent_String_TorrentFile
#CreateTorrent_Button_TorrentFile
#CreateTorrent_Frame3D_0
#CreateTorrent_Text_1
#CreateTorrent_String_Treker
#CreateTorrent_Text_2
#CreateTorrent_String_Comment
#CreateTorrent_Text_3
#CreateTorrent_CheckBox_Private
#CreateTorrent_Combo_Piece
#CreateTorrent_ProgressBar
#CreateTorrent_Button_Start
#CreateTorrent_CurrentFile
EndEnumeration
; Идентификаторы таймеров
Enumeration
#CreateTorrent_Timer
EndEnumeration
Procedure CreateTorrentCB(File.s, Pos.f)
Static Old_File.s, Old_Pos
Protected Count, Temp
If Old_File <> File
Old_File = File
If Len(File) > 40
Count = CountString(File, "\")
If Count > 2
Temp=FindString(File, "\", 1)
If Temp > 0
Temp = FindString(File, "\", Temp+1)
If Temp > 0
File.s = Left(File, Temp)+"...\"+GetFilePart(File)
EndIf
EndIf
EndIf
EndIf
If IsGadget(#CreateTorrent_CurrentFile)
SetGadgetText(#CreateTorrent_CurrentFile, File)
EndIf
EndIf
If Round(Pos,#PB_Round_Down) <> Old_Pos
Old_Pos = Pos ;Round(Pos,#PB_Round_Up)
If IsGadget(#CreateTorrent_ProgressBar)
SetGadgetState(#CreateTorrent_ProgressBar, Old_Pos)
EndIf
EndIf
EndProcedure
Path.s : String.s : Dir.s
Torrent.TorrentFiles_CreateInfo
S_Temp.s
If OpenWindow(#CreateTorrent_Win, 277, 288, 384, 374, "Cоздание торрента.", #PB_Window_SystemMenu | #PB_Window_TitleBar | #PB_Window_ScreenCentered | #PB_Window_Invisible)
TextGadget(#CreateTorrent_Text_0, 10, 10, 200, 15, "Путь к файлам:")
OptionGadget(#CreateTorrent_Radio_File, 220, 10, 65, 15, "Файл")
OptionGadget(#CreateTorrent_Radio_Dir, 295, 10, 75, 15, "Папка")
StringGadget(#CreateTorrent_String_Path, 10, 30, 335, 20, "", #PB_String_ReadOnly)
ButtonGadget(#CreateTorrent_Button_Path, 350, 30, 30, 20, "...")
TextGadget(#CreateTorrent_Text_5, 10, 65, 335, 15, "Торрент файл:")
StringGadget(#CreateTorrent_String_TorrentFile, 10, 85, 335, 20, "", #PB_String_ReadOnly)
ButtonGadget(#CreateTorrent_Button_TorrentFile, 350, 85, 30, 20, "...")
Frame3DGadget(#CreateTorrent_Frame3D_0, 15, 120, 355, 205, "Свойства торрента")
TextGadget(#CreateTorrent_Text_1, 25, 140, 335, 15, "Трекеры:")
StringGadget(#CreateTorrent_String_Treker, 25, 160, 335, 74, "", #ES_MULTILINE|#WS_VSCROLL|#ES_AUTOVSCROLL)
TextGadget(#CreateTorrent_Text_2, 25, 245, 330, 15, "Комментарий:")
StringGadget(#CreateTorrent_String_Comment, 25, 265, 335, 20, "")
TextGadget(#CreateTorrent_Text_3, 165, 300, 110, 15, "Размер частей:", #PB_Text_Right)
CheckBoxGadget(#CreateTorrent_CheckBox_Private, 25, 300, 120, 15, "Частный торрент")
ComboBoxGadget(#CreateTorrent_Combo_Piece, 280, 295, 80, 22)
ProgressBarGadget(#CreateTorrent_ProgressBar, 15, 350, 265, 15, 0, 100, #PB_ProgressBar_Smooth)
ButtonGadget(#CreateTorrent_Button_Start, 295, 340, 75, 25, "Создать")
TextGadget(#CreateTorrent_CurrentFile, 15, 335, 265, 15, "")
SetWindowLongPtr_(GadgetID(#CreateTorrent_CurrentFile),#GWL_STYLE, GetWindowLongPtr_(GadgetID(#CreateTorrent_CurrentFile),#GWL_STYLE)|#SS_LEFTNOWORDWRAP)
SetGadgetState(#CreateTorrent_Radio_File, 1)
AddGadgetItem(#CreateTorrent_Combo_Piece, -1, "Авто")
AddGadgetItem(#CreateTorrent_Combo_Piece, -1, "16 КБ")
AddGadgetItem(#CreateTorrent_Combo_Piece, -1, "32 КБ")
AddGadgetItem(#CreateTorrent_Combo_Piece, -1, "64 КБ")
AddGadgetItem(#CreateTorrent_Combo_Piece, -1, "128 КБ")
AddGadgetItem(#CreateTorrent_Combo_Piece, -1, "256 КБ")
AddGadgetItem(#CreateTorrent_Combo_Piece, -1, "512 КБ")
AddGadgetItem(#CreateTorrent_Combo_Piece, -1, "1 МБ")
AddGadgetItem(#CreateTorrent_Combo_Piece, -1, "2 МБ")
AddGadgetItem(#CreateTorrent_Combo_Piece, -1, "4 МБ")
AddGadgetItem(#CreateTorrent_Combo_Piece, -1, "8 МБ")
SetGadgetState(#CreateTorrent_Combo_Piece, 0)
SetActiveGadget(#CreateTorrent_Radio_File)
SendMessage_(WindowID(#CreateTorrent_Win), #WM_UPDATEUISTATE, $30002,0)
HideWindow(#CreateTorrent_Win, 0)
Thread_ID = 0
Repeat
Event = WaitWindowEvent()
If Event = #PB_Event_Gadget And Thread_ID=0
Select EventGadget()
Case #CreateTorrent_Button_Path
If GetGadgetState(#CreateTorrent_Radio_File) ; Файл
Path = OpenFileRequester("","","Все файлы|*.*",0)
If Path<>"" And FileSize(Path)>0
SetGadgetText(#CreateTorrent_String_Path, Path)
EndIf
Else ; Папка
Path = PathRequester("Укажите путь к папке с файлами торрента.",Dir)
If Path<>"" And FileSize(Path)= -2
SetGadgetText(#CreateTorrent_String_Path, Path)
EndIf
EndIf
Case #CreateTorrent_Button_TorrentFile
Path = SaveFileRequester("","","Торрент-файлы (*.torrent)|*.torrent|Все файлы|*.*",0)
If Path<>"" And SelectedFilePattern()>-1
If SelectedFilePattern() = 0 And GetExtensionPart(Path)="" : Path+".torrent" : EndIf
SetGadgetText(#CreateTorrent_String_TorrentFile, Path)
EndIf
Case #CreateTorrent_Radio_File, #CreateTorrent_Radio_Dir
SetGadgetText(#CreateTorrent_String_Path, "")
Case #CreateTorrent_Button_Start
SetGadgetState(#CreateTorrent_ProgressBar, 0)
ClearStructure(@Torrent, TorrentFiles_CreateInfo)
NewList Torrent\announce()
Torrent\Path = GetGadgetText(#CreateTorrent_String_Path)
Torrent\FileName = GetGadgetText(#CreateTorrent_String_TorrentFile)
If Torrent\Path<>"" And Torrent\FileName<>""
String=GetGadgetText(#CreateTorrent_String_Treker)
If String<>""
String = ReplaceString(String, Chr(13), Chr(10))
String = ReplaceString(String, Chr(10)+Chr(10), Chr(10))
If String<>""
String + Chr(10)
Temp = CountString(String, Chr(10))
For i=1 To Temp
S_Temp = StringField(String, i, Chr(10))
If S_Temp<>""
S_Temp=Trim(S_Temp)
S_Temp=RemoveString(S_Temp, Chr(9))
If S_Temp<>""
If AddElement(Torrent\announce())
Torrent\announce() = S_Temp
Else
MessageRequester("Создание торрента.", "Ошибка выделения памяти!", #MB_OK|#MB_ICONERROR)
Break
EndIf
EndIf
EndIf
Next i
EndIf
EndIf
String="" : S_Temp=""
Torrent\BreakThread = 0 ; Признак того, что не нужно прерывать создание торрент-файла.
Torrent\ClientName = "MyTorrent 2.0"
Torrent\comment = GetGadgetText(#CreateTorrent_String_Comment)
Torrent\Directory = GetGadgetState(#CreateTorrent_Radio_Dir) & 1
Torrent\ErrorCode = 0
Torrent\ErrorString = ""
Torrent\Result = 0
Temp = GetGadgetState(#CreateTorrent_Combo_Piece)
If Temp>0 And Temp<12
Torrent\pieceLength = 16384 << (Temp-1)
Else
Torrent\pieceLength = 0
EndIf
Torrent\PiecesCB = @CreateTorrentCB()
Torrent\private = GetGadgetState(#CreateTorrent_CheckBox_Private) & 1
Torrent\ProgName = "MyTorrent 2.0"
Thread_ID = CreateThread(@TorrentFiles_CreateTorrent(), @Torrent)
If Thread_ID
AddWindowTimer(#CreateTorrent_Win, #CreateTorrent_Timer, 400)
Else
MessageRequester("Создание торрента.", "Ошибка при создании потока.", #MB_OK|#MB_ICONWARNING)
EndIf
Else
MessageRequester("Создание торрента.", "Укажите путь к файлам.", #MB_OK|#MB_ICONWARNING)
EndIf
EndSelect
ElseIf Event = #PB_Event_CloseWindow
If Thread_ID <> 0 And IsThread(Thread_ID)
If MessageRequester("Создание торрента.", "Торрент файл в процессе создания."+Chr(10)+"Прервать?", #MB_YESNO|#MB_ICONQUESTION|#MB_DEFBUTTON2) = #IDYES
Torrent\BreakThread = 1 ; Прерываем создание файла.
RemoveWindowTimer(#CreateTorrent_Win, #CreateTorrent_Timer)
For i=1 To 100
Delay(100)
WindowEvent()
If IsThread(Thread_ID)=0
Break
EndIf
Next i
If IsThread(Thread_ID)
KillThread(Thread_ID)
EndIf
Thread_ID = 0
If FileSize(Torrent\FileName)>=0
DeleteFile(Torrent\FileName)
EndIf
SetGadgetText(#CreateTorrent_CurrentFile, "")
SetGadgetState(#CreateTorrent_ProgressBar, 0)
MessageRequester("Создание торрента.", "Торрент файл не был создан - прервано по запросу пользователя.", #MB_OK|#MB_ICONWARNING)
EndIf
Else
Break
EndIf
ElseIf Event = #PB_Event_Timer
If EventTimer() = #CreateTorrent_Timer
If IsThread(Thread_ID) = 0 ; Поток завершился.
RemoveWindowTimer(#CreateTorrent_Win, #CreateTorrent_Timer)
Thread_ID = 0
If Torrent\Result = 1 And Torrent\ErrorCode=0
MessageRequester("Создание торрента.", "Торрент-файл успешно создан.", #MB_OK|#MB_ICONINFORMATION)
ElseIf Torrent\ErrorCode>0
TorrentFiles_CreateTorrent_ShowError(Torrent\ErrorCode, Torrent\ErrorString, "MyTorrent 2.0")
EndIf
EndIf
EndIf
EndIf
ForEver
CloseWindow(#CreateTorrent_Win)
Else
MessageRequester("", "Ошибка при создании окна", #MB_OK|#MB_ICONWARNING)
EndIfРедактирование торрент-файла:

; Загрузка и редактирование торрент файла.
; Автор - Пётр
; https://purebasic.mybb.ru/viewtopic.php?id=249
Enumeration
#Window_0
EndEnumeration
;- Gadget Constants
;
Enumeration
#Text_0
#String_0
#Button_0
#Text_1
#Editor_0
#Text_2
#String_1
#Text_3
#String_2
#Text_6
#String_3
#Text_7
#String_5
#Text_8
#String_6
#Text_9
#String_7
#Text_10
#String_8
#Frame3D_0
#Text_12
#String_10
#CheckBox_0
#Text_14
#String_11
#ListIcon_0
#Button_1
EndEnumeration
XIncludeFile "TorrentFiles.pbi"
Torrent.TorrentFiles_TorrentInfo
FileName.s
Procedure Open_Window_0()
If OpenWindow(#Window_0, 243, 174, 428, 490, "Редактирование Torrent файла", #PB_Window_SystemMenu | #PB_Window_Invisible | #PB_Window_TitleBar | #PB_Window_ScreenCentered )
TextGadget(#Text_0, 5, 15, 40, 15, "Файл:")
StringGadget(#String_0, 50, 10, 330, 20, "")
ButtonGadget(#Button_0, 385, 10, 30, 20, "...")
TextGadget(#Text_1, 5, 40, 75, 15, "Трекеры:")
EditorGadget(#Editor_0, 5, 55, 410, 65)
TextGadget(#Text_2, 5, 135, 110, 15, "Публикатор:")
StringGadget(#String_1, 120, 130, 295, 20, "")
TextGadget(#Text_3, 5, 160, 110, 15, "Адрес публикатора")
StringGadget(#String_2, 120, 155, 295, 20, "")
TextGadget(#Text_6, 5, 185, 110, 15, "Комментарий")
StringGadget(#String_3, 120, 180, 295, 20, "")
TextGadget(#Text_7, 5, 210, 110, 15, "Создано:")
StringGadget(#String_5, 120, 205, 90, 20, "")
TextGadget(#Text_8, 225, 210, 90, 15, "Дата создания:", #PB_Text_Right)
StringGadget(#String_6, 320, 205, 95, 20, "", #PB_String_ReadOnly)
TextGadget(#Text_9, 5, 235, 110, 15, "Размер, байт:")
StringGadget(#String_7, 120, 230, 90, 20, "", #PB_String_ReadOnly)
TextGadget(#Text_10, 225, 235, 90, 15, "Размер части:", #PB_Text_Right)
StringGadget(#String_8, 320, 230, 95, 20, "", #PB_String_ReadOnly)
Frame3DGadget(#Frame3D_0, 5, 255, 410, 194, "При редактировании изменится идентификатор торрента 'INFO Hash'!")
TextGadget(#Text_12, 10, 280, 65, 15, "INFO Hash:")
StringGadget(#String_10, 80, 275, 330, 20, "", #PB_String_ReadOnly)
CheckBoxGadget(#CheckBox_0, 300, 304, 110, 15, "Частный торрент")
TextGadget(#Text_14, 10, 306, 94, 15, "Корневая папка:")
StringGadget(#String_11, 108, 300, 180, 20, "")
ListIconGadget(#ListIcon_0, 10, 330, 400, 110, "Файл", 290, #PB_ListIcon_GridLines|#PB_ListIcon_FullRowSelect)
SendMessage_(GadgetID(#ListIcon_0), #LVM_SETEXTENDEDLISTVIEWSTYLE, #LVS_EX_LABELTIP, #LVS_EX_LABELTIP)
AddGadgetColumn(#ListIcon_0, 1, "Размер", 80)
ButtonGadget(#Button_1, 330, 457, 80, 24, "Сохранить")
SetActiveGadget(#Button_0)
SendMessage_(WindowID(#Window_0), #WM_UPDATEUISTATE, $30002,0)
HideWindow(#Window_0, 0)
EndIf
EndProcedure
Procedure LoadTorrent(FileName.s, *Torrent.TorrentFiles_TorrentInfo)
SetGadgetText(#String_0, FileName)
If TorrentFiles_LoadTorrentFile(FileName, *Torrent) <> #True
TorrentFiles_LoadTorrentFile_ShowEror(*Torrent\ErrorCode, *Torrent\ErrorString, "")
Else
ClearGadgetItems(#Editor_0)
ForEach *Torrent\announce_list()
AddGadgetItem(#Editor_0, -1, *Torrent\announce_list())
Next
SetGadgetText(#String_1, *Torrent\Publisher)
SetGadgetText(#String_2, *Torrent\Publisher_Url)
SetGadgetText(#String_3, *Torrent\comment)
SetGadgetText(#String_5, *Torrent\created_by)
SetGadgetText(#String_6, FormatDate("%dd.%mm.%yyyy %hh:%ii", *Torrent\creation_date))
SetGadgetText(#String_7, Str(*Torrent\All_Size))
SetGadgetText(#String_8, Str(*Torrent\piece_length))
SetGadgetText(#String_10, *Torrent\INFO_Hash)
SetGadgetText(#String_11, *Torrent\CurrentDir_Name)
DisableGadget(#String_11, *Torrent\TypeDir!1)
SetGadgetState(#CheckBox_0,*Torrent\private)
ClearGadgetItems(#ListIcon_0)
ForEach *Torrent\files()
Count = ListSize(*Torrent\files()\path())
If Count>0
File.s=""
x=0
ForEach *Torrent\files()\path()
x + 1
File + *Torrent\files()\path()
If x<Count
File + "\"
EndIf
Next
AddGadgetItem(#ListIcon_0, -1, File+Chr(10)+Str(*Torrent\files()\length))
EndIf
Next
EndIf
EndProcedure
Procedure.b SeveTorrent(FileName.s, *Torrent.TorrentFiles_TorrentInfo)
Protected Result.b=#False
ClearList(*Torrent\announce_list())
*Torrent\announce = ""
Count = CountGadgetItems(#Editor_0)-1
x=0
For i=0 To Count
String.s = GetGadgetItemText(#Editor_0, i)
If String<>"" And RemoveString(String, " ")<>""
If x=0
x=1
*Torrent\announce = String
EndIf
If AddElement(*Torrent\announce_list())
*Torrent\announce_list() = String
EndIf
EndIf
Next i
*Torrent\Publisher = GetGadgetText(#String_1)
*Torrent\Publisher_Url = GetGadgetText(#String_2)
*Torrent\comment = GetGadgetText(#String_3)
*Torrent\created_by = GetGadgetText(#String_5)
If *Torrent\TypeDir = #True
*Torrent\CurrentDir_Name = GetGadgetText(#String_11)
EndIf
*Torrent\private = GetGadgetState(#CheckBox_0)
If TorrentFiles_CreateSysFile_Torrent(FileName, *Torrent) = #True
Result=#True
MessageRequester("", "Торрент файл успешно модифицирован.", #MB_OK|#MB_ICONINFORMATION)
Else
MessageRequester("", "Ошибка при модификации торрент файла.", #MB_OK|#MB_ICONWARNING)
EndIf
ProcedureReturn Result
EndProcedure
Open_Window_0()
Repeat
Event = WaitWindowEvent()
If Event = #PB_Event_Gadget
Select EventGadget()
Case #Button_0 ; Открытть торрент файл.
FileName = OpenFileRequester("","","Торрент-файлы (*.torrent)|*.torrent|Все файлы|*.*",0)
If FileName<>"" And FileSize(FileName)>0
LoadTorrent(FileName, @Torrent)
EndIf
Case #Button_1 ; Сохранить торрент файл.
If FileName<>"" And FileSize(FileName)>0
If SeveTorrent(FileName, @Torrent) = #True
LoadTorrent(FileName, @Torrent)
EndIf
Else
MessageRequester("", "Укажите путь к торрент-файлу", #MB_OK|#MB_ICONWARNING)
EndIf
EndSelect
EndIf
Until Event = #PB_Event_CloseWindow