Client <-> protocol questions

Technical discussion about the NMDC and <a href="http://dcpp.net/ADC.html">ADC</A> protocol. The NMDC protocol is documented in the <a href="http://dcpp.net/wiki/">Wiki</a>, so feel free to refer to it.

Moderator: Moderators

Locked
zhaPP
Posts: 4
Joined: 2003-03-29 19:50

Client <-> protocol questions

Post by zhaPP » 2003-04-05 08:10

I'm developing an dc client and I have some questions..

1) In the client to client protocol, why is a "$1" added to the filename in the $Get command? I get like this: "$Get MyList.DcLst$1|" ?

2) Is dc using any ack packets when sending files? I've done some sniffing and it seems like a data-packet always is followed by a packet from the client that receives the data.. Is this a feature of DC or just a part od the tcp/ip protocol?

3) What size should the data-packets be? DC++ seems to use 1460b, is this correct? Does it even matter which size you use?

4) The Huffman encoding =)
should I just use ordinary Huffman on an uncompressed DCList file?
---
uncompressed DCList =
dirname
<tab>dirname
<tab><filename>file|size
---
I've had a look at the DC src at it seems to write checksums in the beginning of the file?

thanks in advance!

GargoyleMT
DC++ Contributor
Posts: 3212
Joined: 2003-01-07 21:46
Location: .pa.us

Re: Client <-> protocol questions

Post by GargoyleMT » 2003-04-05 08:24

I'm not the DC protocol expert, but two questions below don't relate, so I'll touch on them.
zhaPP wrote:1) In the client to client protocol, why is a "$1" added to the filename in the $Get command? I get like this: "$Get MyList.DcLst$1|" ?
Isn't $ the separator for arguments? The real question should be what does the 1 stand for, shouldn't it?
2) Is dc using any ack packets when sending files? I've done some sniffing and it seems like a data-packet always is followed by a packet from the client that receives the data.. Is this a feature of DC or just a part of the tcp/ip protocol?
Yes, ACKs are part of the TCP/IP protocol.

[the appropriate place to look in DC++ is client/BufferedSocket.cpp BufferedSocket::threadSendFile for the uploading code]
3) What size should the data-packets be? DC++ seems to use 1460b, is this correct? Does it even matter which size you use?
Well, the maximum ethernet frame size is 1518 or 1522 (extended for a VLAN tag in 1998). You are likely seeing the effects of the MTU settings of various network interfaces between you and the target computer. Search on "MTU Path Discovery" on the internet. The OS does this automatically.

zhaPP
Posts: 4
Joined: 2003-03-29 19:50

Post by zhaPP » 2003-04-05 09:13

k.. thanks for helping me on that part! I'll have a look at the BufferedSocket then.. anyone else that have answers to the other q's?

Sedulus
Forum Moderator
Posts: 687
Joined: 2003-01-04 09:32
Contact:

Post by Sedulus » 2003-04-05 10:31

1) it's the offset of the file (1=beginning, not 0)
2/3) yes, just a tcp stream.. I believe it does matter somewhat how much you send per send(2), but it's not something you have to worry about because it will only influence the speeds.
4) look at CryptoManager.cpp =)

and, look at http://wza.digitalbrains.com/DC/doc/
http://dc.selwerd.nl/hublist.xml.bz2
http://www.b.ali.btinternet.co.uk/DCPlusPlus/index.html (TheParanoidOne's DC++ Guide)
http://www.dslreports.com/faq/dc (BSOD2600's Direct Connect FAQ)

zhaPP
Posts: 4
Joined: 2003-03-29 19:50

Post by zhaPP » 2003-04-05 11:17

Sedulus wrote:1) it's the offset of the file (1=beginning, not 0)
ahh.. that explains everything.. thanks.
Sedulus wrote:2/3) yes, just a tcp stream.. I believe it does matter somewhat how much you send per send(2), but it's not something you have to worry about because it will only influence the speeds.
ok.. will try that
Sedulus wrote:4) look at CryptoManager.cpp =)
hehe.. I've had a look at that one, without understanding any more.. :(

thanks for the help..

Gumboot
Posts: 22
Joined: 2003-01-15 20:08
Location: New Zealand

Post by Gumboot » 2003-04-05 19:59

You know huffman coding? Here are the details for the neomodus variant:

byte offset / description
0 = "HE3" (magic bytes)
3 = 13
4 = checksum (XOR of every byte in the uncompressed file)
5 = length of uncompressed file
9 = number of entries in following table
11 = entry 1 [uncompressed byte, compressed code length (in bits)]
13 = entry 2 [uncompressed byte, compressed code length (in bits)]
...
(directly after end of table) = variable length huffman codes

Hope this helps :)

zhaPP
Posts: 4
Joined: 2003-03-29 19:50

Post by zhaPP » 2003-04-07 12:10

Gumboot wrote:You know huffman coding? Here are the details for the neomodus variant:

byte offset / description
0 = "HE3" (magic bytes)
3 = 13
4 = checksum (XOR of every byte in the uncompressed file)
5 = length of uncompressed file
9 = number of entries in following table
11 = entry 1 [uncompressed byte, compressed code length (in bits)]
13 = entry 2 [uncompressed byte, compressed code length (in bits)]
...
(directly after end of table) = variable length huffman codes

Hope this helps :)
do you just huffman-compress the raw filelist and add the header in the beginning?

Gumboot
Posts: 22
Joined: 2003-01-15 20:08
Location: New Zealand

Post by Gumboot » 2003-04-07 22:06

zhaPP wrote:do you just huffman-compress the raw filelist and add the header in the beginning?
Yes, that is exactly what you do.

andlju
Posts: 27
Joined: 2003-02-28 12:58
Location: Stockholm, Sweden
Contact:

Post by andlju » 2003-04-21 10:49

Hi.. I just reached this part of my client.. I was wondering if someone happens to have some Huffman encoding/decoding algorithms (preferably in C# or VB.NET) to share? It would really make my day!

/Anders - not too keen on porting CryptoManager.cpp.. :wink:

Gumboot
Posts: 22
Joined: 2003-01-15 20:08
Location: New Zealand

Post by Gumboot » 2003-04-21 20:06

Since you asked so nicely, here's my huffman decoder. :)

It's written in C# and IIRC has been tested and verified as working. YMMV. If you find any bugs please contact me.

Code: Select all

/// <summary>
/// Class for decoding NMDC file lists (of the form MyList.DcLst).
/// Encoding is not supported.
/// </summary>
/// <remarks>
/// Written by Paul Bartrum ([email protected])
/// </remarks>
public class NMDCHuffmanStream : Stream
{
	Stream baseStream;
	BitReader bitReader;

	int precomputedParity;
	int calculatedParity;
	int dataLength;		// Uncompressed data length.

	class HuffmanNode
	{
		public HuffmanNode Zero;
		public HuffmanNode One;
		public byte Symbol;
	}
	HuffmanNode root;
	int position;

	public NMDCHuffmanStream(Stream baseStream)
	{
		this.baseStream = baseStream;

		if (baseStream.CanRead)
			InitReader();
		else
			throw new NotSupportedException("This stream supports read-access only.");
	}

	private void InitReader()
	{
		BinaryReader reader = new BinaryReader(baseStream);

		// Read magic bytes "HE3" + 13
		byte[] magic = reader.ReadBytes(4);
		if (magic[0] != (int) 'H' || magic[1] != (int) 'E' ||
			magic[2] != (int) '3' || magic[3] != 13)
			throw new IOException("The specified stream is not HE3 (huffman) compressed.");

		// Read checksum byte.  This is a XOR of every byte in the uncompressed file.
		this.precomputedParity = reader.ReadByte();

		// Read the length of the uncompressed file.
		this.dataLength = reader.ReadInt32();

		// Read an array of [uncompressed byte, compressed code length (in bits)].
		int numCouples = reader.ReadInt16();
		byte[,] couples = new byte[numCouples, 2];
		for (int i = 0; i < numCouples; i ++)
		{
			couples[i,0] = reader.ReadByte();	// Uncompressed byte (symbol).
			couples[i,1] = reader.ReadByte();	// Compressed code length.
		}

		// Init the bit reader.
		this.bitReader = new BitReader(this.baseStream);
		this.bitReader.HighToLow = false;	// Bits are read from least
		// significant to most significant.

		// Construct the huffman tree.
		this.root = new HuffmanNode();
		for (int i = 0; i < numCouples; i ++)
		{	
			HuffmanNode node = this.root;
			for (int j = 0; j < couples[i,1]; j ++)
			{
				// Check the validity of the tree.
				if (node.Symbol != 0)
					throw new IOException("The huffman-compressed stream is corrupt.");

				// Move the node to the zero or one nodes, depending on the value
				// of the bit that is read.
				if (bitReader.ReadBit())
				{
					if (node.One == null)
						node.One = new HuffmanNode();
					node = node.One;
				}
				else
				{
					if (node.Zero == null)
						node.Zero = new HuffmanNode();
					node = node.Zero;
				}
			}

			// Check the validity of the tree.
			if (node.Zero != null || node.One != null)
				throw new IOException("The huffman-compressed stream is corrupt.");

			// Store the uncompressed byte.
			node.Symbol = couples[i,0];
		}

		// Ignore the rest of the bits in this byte.
		bitReader.ReadBits(8 - bitReader.BitPosition);
	}

	public Stream BaseStream
	{
		get { return baseStream; }
	}

	public override void Close()
	{
	}

	public override bool CanRead
	{
		get { return true; }
	}

	public override bool CanSeek
	{
		get { return false; }
	}

	public override bool CanWrite
	{
		get { return false; }
	}

	public override long Length
	{
		get { return dataLength; }
	}

	public override long Position
	{
		get { return position; }
		set { throw new NotSupportedException(); }
	}

	public override int Read(byte[] buffer, int offset, int count)
	{
		// Make sure we don't read past the end.
		count = Math.Min(count, dataLength - position);

		HuffmanNode node = this.root;
		for (int i = offset; i < offset + count; i ++)
		{
			// For each bit, branch to the left or right.
			while (node.Symbol == 0)
			{
				if (bitReader.ReadBit())
					node = node.One;
				else
					node = node.Zero;
			}

			// Store the uncompressed symbol in the supplied buffer.
			buffer[i] = node.Symbol;
			calculatedParity ^= node.Symbol;	// Update the parity.

			// Start at the beginning again.
			node = this.root;
		}

		// Update the position and return the number of bytes that were read.
		position += count;
		return count;
	}

	public override void Write(byte[] buffer, int offset, int count)
	{
		throw new NotSupportedException();
	}

	public override void SetLength(long value)
	{
		throw new NotSupportedException();
	}

	public override void Flush() 
	{
	}

	public override long Seek(long offset, SeekOrigin origin)
	{
		throw new NotSupportedException();
	}
}

/// <summary>
/// Reads bits from a stream.
/// </summary>
public class BitReader
{
	Stream input;

	// Settings.
	bool highToLow;
	bool throwOnEOF;

	byte cachedByte;
	int bitPos;
	bool eof;

	public BitReader(Stream input)
	{
		this.input = input;

		this.highToLow = true;
		this.throwOnEOF = true;

		this.cachedByte = 0;
		this.bitPos = 8;
		this.eof = false;
	}

	public bool HighToLow
	{
		get { return highToLow; }
		set { highToLow = value; }
	}

	public bool ThrowOnEOF
	{
		get { return throwOnEOF; }
		set { throwOnEOF = value; }
	}

	// The position in the current byte (0 - 8).
	// Zero means that none of the bits in the current byte have been read.
	public int BitPosition
	{
		get { return bitPos; }
	}

	public bool ReadBit() 
	{
		return ReadBits(1) != 0;
	}

	public int ReadBits(int numBits)
	{
		if (numBits < 0 || numBits > 31)
			throw new ArgumentOutOfRangeException("numBits");
		if (eof)
			return -1;

		int result = 0;
		while (numBits > 0)
		{
			if (this.bitPos == 8)
			{
				// Read another byte.
				int b = input.ReadByte();
				if (b == -1)
				{
					eof = true;
					if (throwOnEOF)
						throw new EndOfStreamException();
					else
						this.cachedByte = 0;		// Pad with zeros.
				}
				else
					this.cachedByte = (byte) b;
				this.bitPos = 0;
			}

			int partialNumBits = Math.Min(numBits, 8 - this.bitPos);
			result |= GetByteBits(this.cachedByte, this.bitPos, partialNumBits, highToLow) <<
				(numBits - partialNumBits);
			this.bitPos += partialNumBits;
			numBits -= partialNumBits;
		}

		return result;
	}

	public static int GetByteBits(byte value, int start, int length, bool highToLow)
	{
		if (highToLow)
			start = 8 - start - length;	// Big-endian bit order.
		return (value & (((1 << (start + length)) - 1) & ~((1 << start) - 1)))
			>> start;
	}
}

andlju
Posts: 27
Joined: 2003-02-28 12:58
Location: Stockholm, Sweden
Contact:

Post by andlju » 2003-04-22 01:32

Thank you! You are hereby declared to be my new hero! :D

I'll try it a soon as I get back from work!

/A

andlju
Posts: 27
Joined: 2003-02-28 12:58
Location: Stockholm, Sweden
Contact:

Post by andlju » 2003-04-22 13:40

Oh.. and by the way, if someone has an encoder as well, there is always room for another hero.. :wink:

/A

HooMair
Posts: 1
Joined: 2003-04-22 18:32
Location: Germany
Contact:

Post by HooMair » 2003-04-22 19:03

First: thanks to you, Gumboot...I'm playing around with the DC protocol at the moment and got caught at this DcLst decoding. Your source code has helped me understanding how this is done.

I wanted to be able to decompress DcLst files in my linux console, and because I haven't found a tool for this task, I decided to write one myself. Since the postings here helped me a lot, I thought it would just be fair to publish my results here where they might help someone else someday...

This C code has been successfully compiled with gcc / linux (and until now, it seems to work perfectly well), but I think it should be no problem to port it for example to windows...:

Code: Select all

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int bitreader(char * st,int bp);

struct hnode {
		int zero;
		int one;
		char symbol;
} node[514];

int main(int argc,char *argv[])
{
if(argc<3) {
	printf("DcLst decompressor\n");
	printf("coded 2003 by HooMair\n\n");
	printf("Syntax: %s <dclst-filename> <output-filename>\n",argv[0]);
	return 0;
}

char is[9000000];
int flen,i,f;

f=fopen(argv[1],"r");
printf("file opened\n");
flen=0;
while(!feof(f)) {
	is[flen]=fgetc(f);
	flen++;
}
fclose(f);

printf("%i bytes read from %s\n",flen-1,argv[1]);

if(is[0]!='H' || is[1]!='E' || is[2]!='3' ||is[3]!=13) {
	printf("ERROR: this is not a DcLst file!\n");
	return 0;
} else {
	printf("File correctly identified as DcLst file\n");
}

int size;
size = *(int*)&is[5];
printf("uncompressed file size: %i\n",size);

short numcouples;
numcouples = *(short*)&is[9];
printf("%i different encoded bytes found\n",numcouples);

unsigned char couples[256][2];
int pos=11;

int nodecount=2,bitpos=0,j,currentnode=1;


for(i=0;i<numcouples;i++) {
	couples[i][0]=is[pos];
	pos++;
	couples[i][1]=is[pos];
	pos++;
}

for(i=0;i<514;i++) {
	node[i].zero=0;
	node[i].one=0;
	node[i].symbol=0;
}

bitpos=pos*8;

for(i=0;i<numcouples;i++) {
	for(j=0;j<couples[i][1];j++) {
		if(node[currentnode].symbol!=0) {
			printf("error building huffman tree at node %i after %i nodes!\n",currentnode,nodecount);
			return 0;
		}
		if(bitreader(is,bitpos)==0) {
			if(node[currentnode].zero==0) { 
				node[currentnode].zero=nodecount; 
				currentnode=nodecount;
				nodecount++;
			} else {
				currentnode=node[currentnode].zero;
			}
		} else {
			if(node[currentnode].one==0) { 
				node[currentnode].one=nodecount; 
				currentnode=nodecount;
				nodecount++; 
			} else {
				currentnode=node[currentnode].one;
			}
		}
		bitpos++;
	}
	if(node[currentnode].zero!=0 || node[currentnode].one!=0) {
		printf("error building huffman tree at node %i!\n",currentnode);
		return 0;
	}				
	node[currentnode].symbol=couples[i][0];
	currentnode=1;
}

bitpos=bitpos+(8-(bitpos%8));

printf("huffman tree building successful: %i nodes created\n",nodecount);
printf("decompression started...\n");

f=fopen(argv[2],"w");
int count=0;

do {
	currentnode=1;
	while(node[currentnode].symbol==0) {
		if(bitreader(is,bitpos)==0) {
			currentnode=node[currentnode].zero;
		} else {
			currentnode=node[currentnode].one;
		}
		bitpos++;
	}
	fputc(node[currentnode].symbol,f);
	count++;
} while(count<size);

fclose(f);

printf("done!\n");
}



int bitreader(char * st,int bp) 
{
int charnum,bitnum;
charnum=(bp-(bp%8))/8;
bitnum=bp%8;
if((st[charnum] & (1<<bitnum))==0) return 0; else return 1;
}

Gumboot
Posts: 22
Joined: 2003-01-15 20:08
Location: New Zealand

Post by Gumboot » 2003-04-23 00:36

andlju wrote:Oh.. and by the way, if someone has an encoder as well, there is always room for another hero.. :wink:
Well, if you can tell me you've tested my decoder and it works, then I'll post my huffman encoder. (Just trying to get some free testing :wink:)

I guess then I'll be a hero twice over! :lol:

andlju
Posts: 27
Joined: 2003-02-28 12:58
Location: Stockholm, Sweden
Contact:

Post by andlju » 2003-04-23 11:26

Well, I just tried it, and it works like a charm..!
I was almost on my way to implement an encoder myself (sometimes, you need a challenge...), but I realize that it would take me quite some time to get it right.. So I would be really grateful if you could post an encoder as well!

Gumboot
Posts: 22
Joined: 2003-01-15 20:08
Location: New Zealand

Post by Gumboot » 2003-04-24 01:14

Well OK then. :) Same priviso as above; mail me if you find any bugs.

Code: Select all

/// <summary> 
/// Class for compressing NMDC file lists.
/// </summary> 
/// <remarks> 
/// Written by Paul Bartrum ([email protected]) 
/// </remarks>
public class NMDCHuffmanCompressor
{
	class HuffmanCompressionNode
	{
		public HuffmanCompressionNode Parent;
		public HuffmanCompressionNode Zero;
		public HuffmanCompressionNode One;
		public int Frequency;
		public int Symbol;
	}

	struct HuffmanTableEntry
	{
		public int Code;
		public int CodeLength;
	}

	public static void Compress(Stream inputStream, Stream outputStream)
	{
		byte[] inputBuffer = new byte[inputStream.Length];
		inputStream.Read(inputBuffer, 0, inputBuffer.Length);
		Compress(inputBuffer, outputStream);
	}

	public static void Compress(byte[] inputBuffer, Stream outputStream)
	{
		// Construct a frequency table and calculate the parity.
		int[] frequencyTable = new int[256];
		byte parity = 0;
		foreach (byte b in inputBuffer)
		{
			frequencyTable[b] ++;
			parity ^= b;
		}

		// Create a node for each symbol.
		HuffmanCompressionNode[] leaves = new HuffmanCompressionNode[256];
		for (int i = 0; i < 256; i ++)
		{
			// Ignore symbols that don't appear in the data.
			if (frequencyTable[i] == 0)
				continue;

			// Create a new node.
			HuffmanCompressionNode node = new HuffmanCompressionNode();
			node.Frequency = frequencyTable[i];
			node.Symbol = i;

			// Add the node to the list of leaf nodes.
			leaves[i] = node;
		}

		System.Collections.ArrayList roots = new System.Collections.ArrayList(256);

		// Initially, every leaf node is a root.
		foreach (HuffmanCompressionNode leaf in leaves)
			if (leaf != null)
				roots.Add(leaf);

		// Construct the huffman tree.
		while (roots.Count > 1)
		{
			// Find the two lowest frequency roots.
			int lowestFrequency = int.MaxValue;
			HuffmanCompressionNode lowestFrequencyRoot = null;
			int secondLowestFrequency = int.MaxValue;
			HuffmanCompressionNode secondLowestFrequencyRoot = null;
			foreach (HuffmanCompressionNode root in roots)
			{
				if (root.Frequency < lowestFrequency)
				{
					secondLowestFrequency = lowestFrequency;
					secondLowestFrequencyRoot = lowestFrequencyRoot;

					lowestFrequency = root.Frequency;
					lowestFrequencyRoot = root;
				}
				else if (root.Frequency < secondLowestFrequency)
				{
					secondLowestFrequency = root.Frequency;
					secondLowestFrequencyRoot = root;
				}
			}

			// Remove the two lowest roots from the list of roots.
			roots.Remove(lowestFrequencyRoot);
			roots.Remove(secondLowestFrequencyRoot);

			// Create the parent node.
			HuffmanCompressionNode parent = new HuffmanCompressionNode();
			parent.Frequency = lowestFrequencyRoot.Frequency + secondLowestFrequencyRoot.Frequency;

			// Connect the dots. :-)
			lowestFrequencyRoot.Parent = parent;
			secondLowestFrequencyRoot.Parent = parent;
			parent.Zero = lowestFrequencyRoot;
			parent.One = secondLowestFrequencyRoot;
			roots.Add(parent);
		}

		// Construct a table from the tree.
		HuffmanTableEntry[] lookupTable = new HuffmanTableEntry[256];
		int numNonZeroEntries = 0;
		for (int i = 0; i < 256; i ++)
		{
			HuffmanCompressionNode node = leaves[i];
			if (node == null)
				continue;

			while (node.Parent != null)
			{
				lookupTable[i].Code <<= 1;
				if (node.Parent.One == node)
					lookupTable[i].Code |= 1;
				lookupTable[i].CodeLength ++;
				node = node.Parent;
			}

			numNonZeroEntries ++;
		}


		// Write header.
		BinaryWriter writer = new BinaryWriter(outputStream);
		writer.Write(new byte[] { (byte) 'H', (byte) 'E', (byte) '3', 13 });
		writer.Write((byte) parity);
		writer.Write((int) inputBuffer.Length);

		// Write couples.
		writer.Write((short) numNonZeroEntries);
		for (int i = 0; i < 256; i ++)
			if (lookupTable[i].CodeLength > 0)
			{
				writer.Write((byte) i);
				writer.Write((byte) lookupTable[i].CodeLength);
			}

		// Write codes.
		BitWriter bitWriter = new BitWriter(outputStream);
		bitWriter.HighToLow = false;
		for (int i = 0; i < 256; i ++)
			if (lookupTable[i].CodeLength > 0)
				bitWriter.WriteBits(lookupTable[i].Code, lookupTable[i].CodeLength);

		// Pad the rest of the byte.
		bitWriter.Flush();

		// Start writing data to the output stream.
		foreach (byte b in inputBuffer)
			bitWriter.WriteBits(lookupTable[b].Code, lookupTable[b].CodeLength);
		bitWriter.Flush();
	}
}

/// <summary>
/// Writes bits to a stream.
/// </summary>
public class BitWriter
{
	Stream output;

	// Settings.
	bool highToLow;

	byte cachedByte;
	int bitPos;

	public BitWriter(Stream output)
	{
		this.output = output;
		this.highToLow = true;
		this.cachedByte = 0;
		this.bitPos = 0;
	}

	public bool HighToLow
	{
		get { return highToLow; }
		set { highToLow = value; }
	}

	// The position in the current byte (0 - 8).
	// Zero means that none of the bits in the current byte have been written.
	public int BitPosition
	{
		get { return bitPos; }
	}

	public void WriteBit(bool value) 
	{
		WriteBits(value ? 1 : 0, 1);
	}

	public void WriteBits(int value, int numBitsArg)
	{
		int numBits = numBitsArg;
		if (numBits < 0 || numBits > 31)
			throw new ArgumentOutOfRangeException("numBits");

		while (numBits > 0)
		{
			int partialNumBits = Math.Min(numBits, 8 - this.bitPos);
			if (highToLow)
				this.cachedByte |= (byte) (GetUIntBits((uint) value, 32 - numBits, partialNumBits, true) <<
					(8 - partialNumBits - this.bitPos));
			else
				this.cachedByte |= (byte) (GetUIntBits((uint) value, numBitsArg - numBits, partialNumBits, false) <<
					this.bitPos);
			this.bitPos += partialNumBits;
			numBits -= partialNumBits;

			if (this.bitPos == 8)
				Flush();
		}
	}

	public void Flush()
	{
		// Check if any data is cached.
		if (this.bitPos == 0)
			return;

		// Write the cached data to the stream.
		output.WriteByte(this.cachedByte);
		this.bitPos = 0;
		this.cachedByte = 0;
	}

	public static int GetUIntBits(uint value, int start, int length, bool highToLow)
	{
		if (highToLow)
			start = 32 - start - length;	// Big-endian bit order.
		return (int) ((uint) (value & (((1 << (start + length)) - 1) & ~((1 << start) - 1))) >> start);
	}
}

arnetheduck
The Creator Himself
Posts: 296
Joined: 2003-01-02 17:15

Post by arnetheduck » 2003-04-24 03:20

heh, gumboot, a bit more and it should be your name on that client =)

Gumboot
Posts: 22
Joined: 2003-01-15 20:08
Location: New Zealand

Post by Gumboot » 2003-04-24 04:41

arnetheduck wrote:heh, gumboot, a bit more and it should be your name on that client =)
LOL :D

You're right. In fact, I am hereby making that a requirement of using my code: you have to call your app "Gumboot's DC client". :P

andlju
Posts: 27
Joined: 2003-02-28 12:58
Location: Stockholm, Sweden
Contact:

Post by andlju » 2003-04-24 08:34

Well.. I guess I almost could agree to that.. It's not like it is very likely to ever be released.. :wink:

Anyway, thank's again Gumboot! If I find any bugs (unlikely as that seems), I'll report them!

andlju
Posts: 27
Joined: 2003-02-28 12:58
Location: Stockholm, Sweden
Contact:

Post by andlju » 2003-04-25 14:07

In case you're curious, the encoder seems to work splendidly as well!

Gumboot
Posts: 22
Joined: 2003-01-15 20:08
Location: New Zealand

Post by Gumboot » 2003-04-25 18:51

Great! I was sure I had tested them and they worked, but I wasn't sure because both those files were restored from an old backup (long story).

futti
Posts: 4
Joined: 2003-07-27 03:00
Location: mostly at the computer
Contact:

Post by futti » 2003-07-28 19:06

Can anybody port this stuff to PHP or know where I can find it in PHP?
I don't know these languages it's written in... :?

Dj_Offset
Posts: 48
Joined: 2003-02-22 19:22
Location: Oslo, Norway
Contact:

Post by Dj_Offset » 2003-07-30 11:17

Totally lame, untested and non-compressing PHP version of the HE3 encoder ;-)
(It should work without much hassle at least... :-)

param: your plain text list as a string
returns: the encoded string

Code: Select all

	function encode($in) {
		$datalen = strlen($in);
		$out = "HE3" . chr(0xD);
		$checksum = 0;
		if ($datalen == 0) return $out.chr(0) .chr(0).chr(0).chr(0).chr(0).chr(0).chr(0) ;
		for ($i = 0; $i < $datalen; $i++) $checksum ^= substr($in,$i,1);
		$out .= chr($checksum) .  intval($datalen) . chr(255) . chr(0);
		for ($i = 0; $i < 255; $i++) $out .= chr(8);
	        for ($i = 0; $i < 255; $i++) $out .= chr($i);
        	$out .= $in;
		return $out;
	}
I wrote QuickDC - A DC++ compatible client for Linux and FreeBSD.

futti
Posts: 4
Joined: 2003-07-27 03:00
Location: mostly at the computer
Contact:

Post by futti » 2003-07-30 17:15

Havn't got a decoder to? :wink:
I will be testing later to day or tomorrow :D

Dj_Offset
Posts: 48
Joined: 2003-02-22 19:22
Location: Oslo, Norway
Contact:

Post by Dj_Offset » 2003-07-31 03:18

futti wrote:Havn't got a decoder to? :wink:
I will be testing later to day or tomorrow :D
Nah, I wrote the above in two minutes...
The decoder have to decompress if the data is compressed, thus cannot be a quick and dirty hack the same way as the encoder... ;-)

That being said, I remember someone trying to do a DC client in PHP before, look over at sourceforge. Maybe you'll find what you are looking for there?
I wrote QuickDC - A DC++ compatible client for Linux and FreeBSD.

darcone
Posts: 4
Joined: 2004-07-13 16:38
Location: Sweden
Contact:

Post by darcone » 2004-07-14 11:52

11 = entry 1 [uncompressed byte, compressed code length (in bits)]
Exactly what does this mean? is the first byte the word and the second the code ?

PseudonympH
Forum Moderator
Posts: 366
Joined: 2004-03-06 02:46

Post by PseudonympH » 2004-07-14 12:14

The first byte is the character, and the second is the length of the code in bits. I would assume that the compression uses the same clever trick that DEFLATE uses in that bytes with the same code length have codes that come in the same order as the bytes. Ex. if 'A' and 'B' both have a code length of 3, then A's code would be 110 and B's would be 111 because 110 comes first. It's not quite as cool as DEFLATE in that it doesn't store the code lengths as Huffman codes, though. :wink:

darcone
Posts: 4
Joined: 2004-07-13 16:38
Location: Sweden
Contact:

Post by darcone » 2004-07-14 12:42

Oh, I assumed that the second byte was the code itself... So I have to do some trick to get the code for the characters?

Bicou
Posts: 3
Joined: 2005-01-18 16:54

Post by Bicou » 2006-06-13 05:36

HooMair wrote:This C code has been successfully compiled with gcc / linux (and until now, it seems to work perfectly well), but I think it should be no problem to port it for example to windows...:

Code: Select all

#include<stdio>
#include<stdlib>
#include<string>

int bitreader(char * st,int bp);

struct hnode {
		int zero;
		int one;
		char symbol;
} node[514];

[....code removed to preserve topic readability....]
I am using this very code in my DC client, using C/C++ & Visual Studio 2003 (under WinXP), but someone on my hub has a file list which crashes the client in the bitreader() function (charnum becomes > than strlen(st).. :()

Does someone have the same problem with the same file list? : http://tamaire.free.fr/-BoB-Wish.DcLst (446kiB)
Did someone ever had the same problem with another file list ?
Thanks for the help

Quattro
Posts: 166
Joined: 2006-01-11 09:23

Post by Quattro » 2006-06-13 06:14

you're almost 2 years later than the last post :P
You can send a message around the world in 1/7 of a second; yet it may take several years to move a simple idea through a 1/4 inch of human skull.

PseudonympH
Forum Moderator
Posts: 366
Joined: 2004-03-06 02:46

Post by PseudonympH » 2006-06-13 21:04

And the .DcLst format is deprecated (and even unsupported in the newest version) in favor of the more flexible and standard .xml.bz2 ones.

Bicou
Posts: 3
Joined: 2005-01-18 16:54

Post by Bicou » 2006-06-15 12:19

Quattro: it doesn't matter, I think it's better to use this topic rather than creating a new one
PseudonympH: I'm on a lan hub where people use old client versions, so my bot needs to use this format :?

ivulfusbar
Posts: 506
Joined: 2003-01-03 07:33

Post by ivulfusbar » 2006-06-15 18:18

But, then, why don't you use the bz2-extension filelist instead of the xml.bz2 one. The bz2-extension without xml is extremly simple to parse. I would imagine all clients you try to connect to will support it.
Everyone is supposed to download from the hubs, - I don´t know why, but I never do anymore.

Bicou
Posts: 3
Joined: 2005-01-18 16:54

Post by Bicou » 2006-06-20 12:26

Okay, thanks for the advice :-)

Locked