Upstream tarball 20080928
[amule.git] / src / remote-gui.net / amuleData.cs
blob1ddfc18a0359db07b4f3400ec817d25becd51a13
1 //
2 // This file is part of the aMule Project.
3 //
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 )
6 //
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
9 // respective authors.
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.
20 //
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
25 using System;
26 using System.IO;
27 using System.Collections.Generic;
28 using System.Security.Cryptography;
30 namespace amule.net
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");
45 public class RGB {
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);
51 val |= 0xff000000;
52 *ptr = val;
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);
61 val |= 0xff000000;
62 *ptr = val;
65 public Int32 Color
67 get { return (Int32)((m_R << 16) | (m_G << 8) | m_B); }
68 set
70 m_R = (byte)(value >> 16);
71 m_G = (byte)((value >> 8) & 0xff);
72 m_B = (byte)(value & 0xff);
78 public class FileColoredGap : FileGap {
79 public Int32 m_color;
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 {
119 bool m_use_diff;
120 int m_len;
121 byte[] m_enc_buff;
122 byte[] m_buff;
124 public RLE_Data(int len, bool use_diff)
126 m_len = len;
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];
133 public byte[] Buffer
135 get { return m_buff; }
138 public int Length
140 get { return m_len; }
143 public void Realloc(int size)
145 if ( size == m_len ) {
146 return;
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];
153 m_len = size;
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]) {
164 // this is sequence
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];
169 j += buff[i + 2];
170 i += 3;
171 } else {
172 // this is single byte
173 m_enc_buff[j++] = buff[i++];
175 } else {
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
180 if ( j != m_len ) {
181 Console.WriteLine("RLE_Data: decoding error. {0} bytes decoded to {1} instead of {2}",
182 len, j, m_len);
183 throw new Exception("RLE_Data: decoding error");
187 if ( m_use_diff ) {
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)
227 return default(I);
230 virtual public ecProto.ecTag CreateTag(I value)
232 return null;
236 public interface IContainerUI {
237 void MyEndUpdate();
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);
253 ECOpCodes m_req_cmd;
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)
263 m_owner = owner;
264 m_req_cmd = req_cmd;
265 m_item_tagname = item_tagname;
268 protected virtual bool Phase1Done()
270 return true;
273 public LinkedList<T> Items
275 get { return m_items; }
278 protected virtual ecProto.ecTag CreateItemTag(ecProto.ecMD5 id)
280 return null;
283 public ecProto.ecPacket ReQuery()
285 // can not issue new query until previous one is replied
286 if ( m_state != REQ_STATE.IDLE ) {
287 return null;
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;
294 return request;
297 void ProcessUpdate(ecProto.ecPacket packet, ecProto.ecPacket full_req)
299 m_owner.MyBeginUpdate();
300 LinkedList<ecProto.ecTag>.Enumerator i = packet.GetTagIterator();
301 while (i.MoveNext())
303 ecProto.ecTag t = i.Current;
304 // sometimes reply contains additional tags
305 if ( t.Name() != m_item_tagname ) {
306 continue;
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);
316 } else {
317 if ( m_inc_tags ) {
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);
326 } else {
327 full_req.AddSubtag(CreateItemTag(item_id));
331 // now process item deletion
333 // TODO
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;
348 switch (m_state) {
349 case REQ_STATE.IDLE:
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() ) {
354 break;
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;
365 break;
366 case REQ_STATE.FULL_REQ_SENT:
367 m_state = REQ_STATE.IDLE;
368 break;
370 return reply;
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;
381 object m_ui_item;
383 public amuleFileItem(ecProto.ecMD5 id, string name, Int64 size)
385 m_id = id;
386 m_filename = name;
387 m_filesize = size;
390 public ecProto.ecMD5 ID
392 get { return m_id; }
395 public string ValueToPrefix(Int64 value)
397 if (value < 1024)
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);
409 else
411 return string.Format("{0:f} Gb", ((float)value) / 1073741824);
415 public string Name
417 get { return m_filename; }
420 public string Size
422 get { return ValueToPrefix(m_filesize); }
424 public object UiItem
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;
433 Int32 m_speed;
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
448 RGB[] m_color_line;
450 public static byte[] m_modifiers;
452 public RGB [] ColorLine
454 get { return m_color_line; }
457 public static byte[] Get_3D_Modifiers()
459 return m_modifiers;
462 public DownloadQueueItem(ecProto.ecMD5 id, string name, Int64 size, PartFileEncoderData encoder)
463 : base(id, name, size)
465 m_decoder = encoder;
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 ) {
472 return;
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);
533 DrawLine();
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;
549 // Order is RGB
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;
564 } else {
565 colored_gaps_size++;
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++ ) {
585 // yellow
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;
593 } else {
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); }
616 public string Speed
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
626 get
628 string result;
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);
632 } else {
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);
641 return result;
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);
680 m_enc_map[id] = e;
682 DownloadQueueItem i = new DownloadQueueItem(id, filename, filesize, e);
684 i.AllocColorLine(m_new_item_status_length);
686 i.UpdateItem(tag);
688 return i;
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);
717 //i.UpdateItem(tag);
719 return i;