2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2003-2008 Froenchenko Leonid ( lfroen@gmail.com / http://www.amule.org )
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 using System
.Collections
.Generic
;
28 using System
.Security
.Cryptography
;
32 public class FileGap
: IComparable
{
33 public Int64 m_start
, m_end
;
35 public int CompareTo(object obj
)
37 if ( obj
is FileGap
) {
38 FileGap temp
= (FileGap
)obj
;
39 return m_start
.CompareTo(temp
.m_start
);
41 throw new ArgumentException("object is not a FileGap");
46 public byte m_R
, m_G
, m_B
;
48 unsafe public void WriteToBuffAlpha(UInt32
* ptr
)
50 UInt32 val
= (UInt32
)((m_R
<< 16) | (m_G
<< 8) | m_B
);
55 unsafe public void WriteToBuffWithModifier(UInt32
* ptr
, byte modif
)
57 byte R
= (m_R
> modif
) ? (byte)(m_R
- modif
) : (byte)0;
58 byte G
= (m_G
> modif
) ? (byte)(m_G
- modif
) : (byte)0;
59 byte B
= (m_B
> modif
) ? (byte)(m_B
- modif
) : (byte)0;
60 UInt32 val
= (UInt32
)((R
<< 16) | (G
<< 8) | B
);
67 get { return (Int32)((m_R << 16) | (m_G << 8) | m_B); }
70 m_R
= (byte)(value >> 16);
71 m_G
= (byte)((value >> 8) & 0xff);
72 m_B
= (byte)(value & 0xff);
78 public class FileColoredGap
: FileGap
{
83 public class GapBuffer
{
84 public FileGap
[] m_buffer
;
86 public GapBuffer(byte[] raw_buffer
, int size
)
88 BinaryReader br
= new BinaryReader(new MemoryStream(raw_buffer
));
89 int bufsize
= size
/ (2 * sizeof(Int64
));
90 m_buffer
= new FileGap
[bufsize
];
91 for ( int i
= 0; i
< bufsize
; i
++ ) {
92 m_buffer
[i
] = new FileGap();
93 m_buffer
[i
].m_start
= System
.Net
.IPAddress
.NetworkToHostOrder(br
.ReadInt64());
94 m_buffer
[i
].m_end
= System
.Net
.IPAddress
.NetworkToHostOrder(br
.ReadInt64());
96 System
.Array
.Sort(m_buffer
);
100 public class ColoredGapBuffer
{
101 public FileColoredGap
[] m_buffer
;
103 public ColoredGapBuffer(int size
)
105 m_buffer
= new FileColoredGap
[size
];
106 for ( int i
= 0; i
< size
; i
++ ) {
107 m_buffer
[i
] = new FileColoredGap();
109 m_buffer
[0].m_start
= 0;
110 m_buffer
[0].m_end
= 0;
111 m_buffer
[0].m_color
= 0;
116 /// RLE implementation. I need only decoder part
118 public class RLE_Data
{
124 public RLE_Data(int len
, bool use_diff
)
127 m_use_diff
= use_diff
;
128 // in worst case 2-byte sequence encoded as 3. So, data can grow at 1/3
129 m_enc_buff
= new byte[m_len
*4/3 + 1];
130 m_buff
= new byte[m_len
];
135 get { return m_buff; }
140 get { return m_len; }
143 public void Realloc(int size
)
145 if ( size
== m_len
) {
149 if ( (size
> m_len
) && (size
> m_buff
.Length
) ) {
150 m_buff
= new byte[size
];
151 m_enc_buff
= new byte[size
* 4 / 3 + 1];
156 public void Decode(byte [] buff
, int start_offset
)
158 int len
= buff
.Length
;
160 int i
= start_offset
, j
= 0;
161 while ( j
!= m_len
) {
162 if ( i
< (len
-1) ) {
163 if (buff
[i
+1] == buff
[i
]) {
165 //memset(m_enc_buff + j, buff[i], buff[i + 2]);
166 for(int k
= 0; k
< buff
[i
+ 2]; k
++) {
167 m_enc_buff
[j
+ k
] = buff
[i
];
172 // this is single byte
173 m_enc_buff
[j
++] = buff
[i
++];
176 // only 1 byte left in encoded data - it can't be sequence
177 m_enc_buff
[j
++] = buff
[i
++];
178 // if there's no more data, but buffer end is not reached,
179 // it must be error in some point
181 Console
.WriteLine("RLE_Data: decoding error. {0} bytes decoded to {1} instead of {2}",
183 throw new Exception("RLE_Data: decoding error");
188 for (int k
= 0; k
< m_len
; k
++) {
189 m_buff
[k
] ^
= m_enc_buff
[k
];
195 public class PartFileEncoderData
{
196 public RLE_Data m_part_status
;
197 public RLE_Data m_gap_status
;
199 public PartFileEncoderData(int partcount
, int gapcount
)
201 m_part_status
= new RLE_Data(partcount
+1, true);
202 m_gap_status
= new RLE_Data(gapcount
*sizeof(Int64
)+1, true);
205 public void Decode(byte [] gapdata
, byte [] partdata
)
207 m_part_status
.Decode(partdata
, 0);
209 // in a first dword - real size
210 //uint32 gapsize = ENDIAN_NTOHL(RawPeekUInt32(gapdata));
211 //gapdata += sizeof(uint32);
212 //m_gap_status.Realloc(gapsize * 2 * sizeof(uint64));
213 Int32 gapsize
= System
.Net
.IPAddress
.NetworkToHostOrder(
214 (Int32
)gapdata
[0] | ((Int32
)gapdata
[1] << 8) |
215 ((Int32
)gapdata
[2] << 16) | ((Int32
)gapdata
[3] << 24));
217 m_gap_status
.Realloc(gapsize
*2*sizeof(Int64
));
218 m_gap_status
.Decode(gapdata
, 4);
223 // I: ID for this kind of tag
224 class amuleTag2ItemConnector
<I
> {
225 virtual public I
ID(ecProto
.ecTag tag
)
230 virtual public ecProto
.ecTag
CreateTag(I
value)
236 public interface IContainerUI
{
238 void MyBeginUpdate();
240 void InsertItem(object i
);
241 void UpdateItem(object i
);
245 // T: item in container
246 abstract public class amuleGenericContainer
<T
> {
247 private enum REQ_STATE { IDLE, REQ_SENT, FULL_REQ_SENT }
;
248 REQ_STATE m_state
= REQ_STATE
.IDLE
;
250 LinkedList
<T
> m_items
= new LinkedList
<T
>();
251 Dictionary
<ecProto
.ecMD5
, T
> m_items_hash
= new Dictionary
<ecProto
.ecMD5
, T
>(32);
255 ECTagNames m_item_tagname
;
257 bool m_inc_tags
= true;
259 protected IContainerUI m_owner
;
261 public amuleGenericContainer(ECOpCodes req_cmd
, ECTagNames item_tagname
, IContainerUI owner
)
265 m_item_tagname
= item_tagname
;
268 protected virtual bool Phase1Done()
273 public LinkedList
<T
> Items
275 get { return m_items; }
278 protected virtual ecProto
.ecTag
CreateItemTag(ecProto
.ecMD5 id
)
283 public ecProto
.ecPacket
ReQuery()
285 // can not issue new query until previous one is replied
286 if ( m_state
!= REQ_STATE
.IDLE
) {
290 ecProto
.ecPacket request
= new ecProto
.ecPacket(m_req_cmd
,
291 m_inc_tags
? EC_DETAIL_LEVEL
.EC_DETAIL_INC_UPDATE
: EC_DETAIL_LEVEL
.EC_DETAIL_UPDATE
);
292 m_state
= REQ_STATE
.REQ_SENT
;
297 void ProcessUpdate(ecProto
.ecPacket packet
, ecProto
.ecPacket full_req
)
299 m_owner
.MyBeginUpdate();
300 LinkedList
<ecProto
.ecTag
>.Enumerator i
= packet
.GetTagIterator();
303 ecProto
.ecTag t
= i
.Current
;
304 // sometimes reply contains additional tags
305 if ( t
.Name() != m_item_tagname
) {
308 ecProto
.ecMD5 item_id
= ((ecProto
.ecTagMD5
)t
).ValueMD5();
309 if ( m_items_hash
.ContainsKey(item_id
) ) {
310 T item
= m_items_hash
[item_id
];
311 ProcessItemUpdate(item
, t
);
313 if ( m_owner
!= null ) {
314 m_owner
.UpdateItem(item
);
318 T item
= CreateItem(t
);
319 m_items
.AddLast(item
);
320 m_items_hash
[item_id
] = item
;
322 if (m_owner
!= null) {
323 m_owner
.InsertItem(item
);
327 full_req
.AddSubtag(CreateItemTag(item_id
));
331 // now process item deletion
335 m_owner
.MyEndUpdate();
339 // derived class must provide
341 abstract protected void ProcessItemUpdate(T item
, ecProto
.ecTag tag
);
343 abstract protected T
CreateItem(ecProto
.ecTag tag
);
345 public ecProto
.ecPacket
HandlePacket(ecProto
.ecPacket p
)
347 ecProto
.ecPacket reply
= null;
350 throw new Exception("Should not get packet in IDLE state");
351 case REQ_STATE
.REQ_SENT
:
352 m_state
= REQ_STATE
.IDLE
;
353 if ( !Phase1Done() ) {
356 ecProto
.ecPacket full_request
= new ecProto
.ecPacket(m_req_cmd
);
357 ProcessUpdate(p
, full_request
);
359 // // Phase 3: request full info about files we don't have yet
360 if ( !m_inc_tags
&& (full_request
.SubtagCount() != 0)) {
361 reply
= full_request
;
362 m_state
= REQ_STATE
.FULL_REQ_SENT
;
366 case REQ_STATE
.FULL_REQ_SENT
:
367 m_state
= REQ_STATE
.IDLE
;
374 public class amuleFileItem
{
375 protected ecProto
.ecMD5 m_id
;
377 protected string m_filename
;
378 protected Int64 m_filesize
;
380 protected const Int32 FILE_PARTSIZE
= 9728000;
383 public amuleFileItem(ecProto
.ecMD5 id
, string name
, Int64 size
)
390 public ecProto
.ecMD5 ID
395 public string ValueToPrefix(Int64
value)
399 return string.Format("{0} bytes", value);
401 else if (value < 1048576)
403 return string.Format("{0:f} Kb", ((float)value) / 1024);
405 else if (value < 1073741824)
407 return string.Format("{0:f} Mb", ((float)value) / 1048576);
411 return string.Format("{0:f} Gb", ((float)value) / 1073741824);
417 get { return m_filename; }
422 get { return ValueToPrefix(m_filesize); }
426 get { return m_ui_item; }
427 set { m_ui_item = value; }
431 public class DownloadQueueItem
: amuleFileItem
{
432 Int64 m_size_xfered
, m_size_done
;
435 Int32 m_src_count
, m_non_current_src_count
, m_xfer_src_count
, m_a4af_src_count
;
437 PartFileEncoderData m_decoder
;
440 // Used for colored status
442 ColoredGapBuffer m_color_gap_buff
;
443 GapBuffer m_req_parts
;
446 // Format24BppRgb or similar
450 public static byte[] m_modifiers
;
452 public RGB
[] ColorLine
454 get { return m_color_line; }
457 public static byte[] Get_3D_Modifiers()
462 public DownloadQueueItem(ecProto
.ecMD5 id
, string name
, Int64 size
, PartFileEncoderData encoder
)
463 : base(id
, name
, size
)
466 m_color_gap_buff
= new ColoredGapBuffer((Int32
)(size
/ FILE_PARTSIZE
) + 1 + 1);
469 public static void InitDraw3DModifiers(int height
)
471 if ( m_modifiers
!= null && m_modifiers
.Length
== height
) {
474 m_modifiers
= new byte[height
];
475 for ( int i
= 0; i
< height
; i
++ ) {
476 double curr_mod
= 30 * (1 + System
.Math
.Cos((2*System
.Math
.PI
)*(height
-(((double)i
)/height
))));
477 m_modifiers
[i
] = (byte)System
.Math
.Floor(curr_mod
);
481 public void AllocColorLine(int size
)
483 m_color_line
= new RGB
[size
];
484 for ( int i
= 0; i
< m_color_line
.Length
; i
++ ) {
485 m_color_line
[i
] = new RGB();
489 public void UpdateItem(ecProto
.ecTag tag
)
491 ecProto
.ecTagInt itag
= (ecProto
.ecTagInt
)tag
.SubTag(ECTagNames
.EC_TAG_PARTFILE_SIZE_DONE
);
492 if ( itag
!= null ) {
493 m_size_done
= itag
.Value64();
495 itag
= (ecProto
.ecTagInt
)tag
.SubTag(ECTagNames
.EC_TAG_PARTFILE_SIZE_XFER
);
496 if ( itag
!= null ) {
497 m_size_xfered
= itag
.Value64();
500 itag
= (ecProto
.ecTagInt
)tag
.SubTag(ECTagNames
.EC_TAG_PARTFILE_SPEED
);
501 if ( itag
!= null ) {
502 m_speed
= (Int32
)itag
.Value64();
505 itag
= (ecProto
.ecTagInt
)tag
.SubTag(ECTagNames
.EC_TAG_PARTFILE_SOURCE_COUNT
);
506 if ( itag
!= null ) {
507 m_src_count
= (Int32
)itag
.Value64();
510 itag
= (ecProto
.ecTagInt
)tag
.SubTag(ECTagNames
.EC_TAG_PARTFILE_SOURCE_COUNT_A4AF
);
511 if ( itag
!= null ) {
512 m_a4af_src_count
= (Int32
)itag
.Value64();
515 itag
= (ecProto
.ecTagInt
)tag
.SubTag(ECTagNames
.EC_TAG_PARTFILE_SOURCE_COUNT_NOT_CURRENT
);
516 if ( itag
!= null ) {
517 m_non_current_src_count
= (Int32
)itag
.Value64();
520 itag
= (ecProto
.ecTagInt
)tag
.SubTag(ECTagNames
.EC_TAG_PARTFILE_SOURCE_COUNT_XFER
);
521 if ( itag
!= null ) {
522 m_xfer_src_count
= (Int32
)itag
.Value64();
525 ecProto
.ecTagCustom gapstat
= (ecProto
.ecTagCustom
)tag
.SubTag(ECTagNames
.EC_TAG_PARTFILE_GAP_STATUS
);
526 ecProto
.ecTagCustom partstat
= (ecProto
.ecTagCustom
)tag
.SubTag(ECTagNames
.EC_TAG_PARTFILE_PART_STATUS
);
527 m_decoder
.Decode(gapstat
.Value(), partstat
.Value());
529 ecProto
.ecTagCustom reqstat
= (ecProto
.ecTagCustom
)tag
.SubTag(ECTagNames
.EC_TAG_PARTFILE_REQ_STATUS
);
530 BinaryReader br
= new BinaryReader(new MemoryStream(reqstat
.Value()));
532 m_req_parts
= new GapBuffer(reqstat
.Value(), reqstat
.Value().Length
);
536 public void DrawLine()
538 GapBuffer status_gaps
= new GapBuffer(m_decoder
.m_gap_status
.Buffer
, m_decoder
.m_gap_status
.Length
);
539 byte[] part_info
= m_decoder
.m_part_status
.Buffer
;
541 int colored_gaps_size
= 0;
542 for ( int j
= 0; j
< status_gaps
.m_buffer
.Length
; j
++ ) {
543 Int64 gap_start
= status_gaps
.m_buffer
[j
].m_start
;
544 Int64 gap_end
= status_gaps
.m_buffer
[j
].m_end
;
545 Int64 start
= gap_start
/ FILE_PARTSIZE
;
546 Int64 end
= (gap_end
/ FILE_PARTSIZE
) + 1;
551 Int32 color
= 0xff0000;
552 for ( Int64 i
= start
; i
< end
; i
++ ) {
553 if ( part_info
[i
] != 0 ) {
554 int blue
= 210 - (22 * (part_info
[i
] - 1));
555 if ( blue
< 0 ) { blue = 0; }
556 color
= (blue
<< 8) | 255;
558 Int64 fill_gap_begin
= ((i
== start
) ? gap_start
: FILE_PARTSIZE
* i
);
559 Int64 fill_gap_end
= ((i
== (end
- 1)) ? gap_end
: FILE_PARTSIZE
* (i
+ 1));
561 if ( (m_color_gap_buff
.m_buffer
[colored_gaps_size
].m_end
== fill_gap_begin
) &&
562 (m_color_gap_buff
.m_buffer
[colored_gaps_size
].m_color
== color
) ) {
563 m_color_gap_buff
.m_buffer
[colored_gaps_size
].m_end
= fill_gap_end
;
566 m_color_gap_buff
.m_buffer
[colored_gaps_size
].m_start
= fill_gap_begin
;
567 m_color_gap_buff
.m_buffer
[colored_gaps_size
].m_end
= fill_gap_end
;
568 m_color_gap_buff
.m_buffer
[colored_gaps_size
].m_color
= color
;
573 // Now actual drawing
575 int width
= m_color_line
.Length
;
576 for ( int i
= 0; i
< width
; i
++ ) {
577 m_color_line
[i
].Color
= 0x7f7f7f;
579 if ( m_filesize
< width
) {
581 // if file is that small, draw it in single step
583 if ( m_req_parts
.m_buffer
.Length
!= 0 ) {
584 for ( int i
= 0; i
< width
; i
++ ) {
586 m_color_line
[i
].Color
= 0xffd000;
588 } else if ( m_color_gap_buff
.m_buffer
.Length
!= 0 ) {
589 for ( int i
= 0; i
< width
; i
++ ) {
590 m_color_line
[i
].Color
= m_color_gap_buff
.m_buffer
[i
].m_color
;
594 Int32 factor
= (Int32
)(m_filesize
/ width
);
595 for ( int i
= 1; i
<= colored_gaps_size
; i
++ ) {
596 Int32 start
= (Int32
)(m_color_gap_buff
.m_buffer
[i
].m_start
/ factor
);
597 Int32 end
= (Int32
)(m_color_gap_buff
.m_buffer
[i
].m_end
/ factor
);
598 for ( Int32 j
= start
; j
< end
; j
++ ) {
599 m_color_line
[j
].Color
= m_color_gap_buff
.m_buffer
[i
].m_color
;
602 foreach ( FileGap g
in m_req_parts
.m_buffer
) {
603 Int32 start
= (Int32
)(g
.m_start
/ factor
);
604 Int32 end
= (Int32
)(g
.m_end
/ factor
);
605 for ( Int32 j
= start
; j
< end
; j
++ ) {
606 m_color_line
[j
].Color
= 0xffd000;
612 public string SizeDone
614 get { return ValueToPrefix(m_size_done); }
618 get { return (m_speed == 0) ? "" : (ValueToPrefix(m_speed) + "/s"); }
620 public string PercentDone
622 get { return String.Format("{0}
%", m_size_done * 100 / m_filesize); }
624 public string Sources
629 if (m_non_current_src_count != 0) {
630 result = String.Format("{0}
/ {1}
",
631 m_src_count - m_non_current_src_count, m_src_count);
633 result = String.Format("{0}
", m_src_count);
635 if (m_a4af_src_count != 0) {
636 result += String.Format(" +{0}
", m_a4af_src_count);
638 if ( m_xfer_src_count != 0 ) {
639 result += String.Format(" ({0}
)", m_xfer_src_count);
646 public class DownloadQueueContainer : amuleGenericContainer<DownloadQueueItem> {
647 Dictionary<ecProto.ecMD5, PartFileEncoderData> m_enc_map;
649 private int m_new_item_status_length;
651 public int NewItemStatusLineLength
653 get { return m_new_item_status_length; }
654 set { m_new_item_status_length = value; }
657 public DownloadQueueContainer(IContainerUI owner)
658 : base(ECOpCodes.EC_OP_GET_DLOAD_QUEUE, ECTagNames.EC_TAG_PARTFILE, owner)
660 m_enc_map = new Dictionary<ecProto.ecMD5, PartFileEncoderData>();
663 override protected void ProcessItemUpdate(DownloadQueueItem item, ecProto.ecTag tag)
665 item.UpdateItem(tag);
667 ecProto.ecMD5 id = ((ecProto.ecTagMD5)tag).ValueMD5();
668 if ( !m_enc_map.ContainsKey(id) ) {
669 throw new Exception("No RLE decoder
for download queue item
");
673 override protected DownloadQueueItem CreateItem(ecProto.ecTag tag)
675 ecProto.ecMD5 id = ((ecProto.ecTagMD5)tag).ValueMD5();
676 string filename = ((ecProto.ecTagString)tag.SubTag(ECTagNames.EC_TAG_PARTFILE_NAME)).StringValue();
677 Int64 filesize = (Int64)((ecProto.ecTagInt)tag.SubTag(ECTagNames.EC_TAG_PARTFILE_SIZE_FULL)).Value64();
679 PartFileEncoderData e = new PartFileEncoderData((int)(filesize / 9728000), 10);
682 DownloadQueueItem i = new DownloadQueueItem(id, filename, filesize, e);
684 i.AllocColorLine(m_new_item_status_length);
692 public class SharedFileItem : amuleFileItem {
693 public SharedFileItem(ecProto.ecMD5 id, string name, Int64 size)
694 : base(id, name, size)
699 class SharedFileListContainer : amuleGenericContainer<SharedFileItem>
701 public SharedFileListContainer(IContainerUI owner)
702 : base(ECOpCodes.EC_OP_GET_SHARED_FILES, ECTagNames.EC_TAG_KNOWNFILE, owner)
706 override protected void ProcessItemUpdate(SharedFileItem item, ecProto.ecTag tag)
710 override protected SharedFileItem CreateItem(ecProto.ecTag tag)
712 ecProto.ecMD5 id = ((ecProto.ecTagMD5)tag).ValueMD5();
713 string filename = ((ecProto.ecTagString)tag.SubTag(ECTagNames.EC_TAG_PARTFILE_NAME)).StringValue();
714 Int64 filesize = (Int64)((ecProto.ecTagInt)tag.SubTag(ECTagNames.EC_TAG_PARTFILE_SIZE_FULL)).Value64();
715 SharedFileItem i = new SharedFileItem(id, filename, filesize);