package download.bt;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * 解析bt种子
 */
@SuppressWarnings(value = { "rawtypes", "unchecked", "unused" })
public class BDecoder {
	public static Charset BYTE_CHARSET = Charset.forName("UTF-8");;
	public static Charset DEFAULT_CHARSET = Charset.forName("UTF-8");;
	private boolean recovery_mode;
	public static Map decode(byte[] data) throws IOException {
		return (new BDecoder().decodeByteArray(data));
	}
	public static Map decode(BufferedInputStream is) throws IOException {
		return (new BDecoder().decodeStream(is));
	}
	public BDecoder() {
	}
	public Map decodeByteArray(byte[] data) throws IOException {
		return (decode(new ByteArrayInputStream(data)));
	}
	public Map decodeStream(BufferedInputStream data) throws IOException {
		Object res = decodeInputStream(data, 0);
		if (res == null) {
			throw (new IOException("BDecoder: zero length file"));
		} else if (!(res instanceof Map)) {
			throw (new IOException("BDecoder: top level isn't a Map"));
		}
		return ((Map) res);
	}
	private Map decode(ByteArrayInputStream data) throws IOException {
		Object res = decodeInputStream(data, 0);

		if (res == null) {
			throw (new IOException("BDecoder: zero length file"));
		} else if (!(res instanceof Map)) {
			throw (new IOException("BDecoder: top level isn't a Map"));
		}

		return ((Map) res);
	}

	private Object decodeInputStream(InputStream bais, int nesting)

	throws IOException {
		if (nesting == 0 && !bais.markSupported()) {
			throw new IOException("InputStream must support the mark() method");
		}
		// set a mark
		bais.mark(Integer.MAX_VALUE);
		// read a byte
		int tempByte = bais.read();
		// decide what to do
		switch (tempByte) {
		case 'd':
			// create a new dictionary object
			Map tempMap = new HashMap();
			try {
				// get the key
				byte[] tempByteArray = null;
				while ((tempByteArray = (byte[]) decodeInputStream(bais, nesting + 1)) != null) {
					// decode some more
					Object value = decodeInputStream(bais, nesting + 1);
					// add the value to the map
					CharBuffer cb = BYTE_CHARSET.decode(ByteBuffer.wrap(tempByteArray));
					String key = new String(cb.array(), 0, cb.limit());
					tempMap.put(key, value);
				}
				bais.mark(Integer.MAX_VALUE);
				tempByte = bais.read();
				bais.reset();
				if (nesting > 0 && tempByte == -1) {
					throw (new IOException("BDecoder: invalid input data, 'e' missing from end of dictionary"));
				}
			} catch (Throwable e) {
				if (!recovery_mode) {
					if (e instanceof IOException) {
						throw ((IOException) e);
					}
					throw (new IOException(e.toString()));
				}
			}
			// return the map
			return tempMap;
		case 'l':
			// create the list
			List tempList = new ArrayList();
			try {
				// create the key
				Object tempElement = null;
				while ((tempElement = decodeInputStream(bais, nesting + 1)) != null) {
					// add the element
					tempList.add(tempElement);
				}
				bais.mark(Integer.MAX_VALUE);
				tempByte = bais.read();
				bais.reset();
				if (nesting > 0 && tempByte == -1) {
					throw (new IOException("BDecoder: invalid input data, 'e' missing from end of list"));
				}
			} catch (Throwable e) {
				if (!recovery_mode) {
					if (e instanceof IOException) {
						throw ((IOException) e);
					}
					throw (new IOException(e.toString()));
				}
			}
			// return the list
			return tempList;

		case 'e':
		case -1:
			return null;
		case 'i':
			return new Long(getNumberFromStream(bais, 'e'));
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			// move back one
			bais.reset();
			// get the string
			return getByteArrayFromStream(bais);
		default: {
			int rem_len = bais.available();
			if (rem_len > 256) {
				rem_len = 256;
			}
			byte[] rem_data = new byte[rem_len];
			bais.read(rem_data);
			throw (new IOException("BDecoder: unknown command '" + tempByte + ", remainder = " + new String(rem_data, DEFAULT_CHARSET)));
		}
		}
	}

	private long getNumberFromStream(InputStream bais, char parseChar) throws IOException {
		StringBuffer sb = new StringBuffer(3);

		int tempByte = bais.read();
		while ((tempByte != parseChar) && (tempByte >= 0)) {
			sb.append((char) tempByte);
			tempByte = bais.read();
		}

		// are we at the end of the stream?
		if (tempByte < 0) {
			return -1;
		}

		return Long.parseLong(sb.toString());
	}

	// This one causes lots of "Query Information" calls to the filesystem
	private long getNumberFromStreamOld(InputStream bais, char parseChar) throws IOException {
		int length = 0;
		// place a mark
		bais.mark(Integer.MAX_VALUE);
		int tempByte = bais.read();
		while ((tempByte != parseChar) && (tempByte >= 0)) {
			tempByte = bais.read();
			length++;
		}

		// are we at the end of the stream?
		if (tempByte < 0) {
			return -1;
		}

		// reset the mark
		bais.reset();
		// get the length
		byte[] tempArray = new byte[length];
		int count = 0;
		int len = 0;

		// get the string
		while (count != length && (len = bais.read(tempArray, count, length - count)) > 0) {
			count += len;
		}

		// jump ahead in the stream to compensate for the :
		bais.skip(1);
		// return the value
		CharBuffer cb = DEFAULT_CHARSET.decode(ByteBuffer.wrap(tempArray));
		String str_value = new String(cb.array(), 0, cb.limit());

		return Long.parseLong(str_value);
	}

	private byte[] getByteArrayFromStream(InputStream bais) throws IOException {
		int length = (int) getNumberFromStream(bais, ':');
		if (length < 0) {
			return null;
		}
		// note that torrent hashes can be big (consider a 55GB file with 2MB
		// pieces
		// this generates a pieces hash of 1/2 meg

		if (length > 8 * 1024 * 1024) {
			throw new IOException("Byte array length too large (" + length + ")");
		}

		byte[] tempArray = new byte[length];
		int count = 0;
		int len = 0;
		// get the string
		while (count != length && (len = bais.read(tempArray, count, length - count)) > 0) {
			count += len;
		}
		if (count != tempArray.length) {
			throw (new IOException("BDecoder::getByteArrayFromStream: truncated"));
		}
		return tempArray;
	}

	public void setRecoveryMode(boolean r) {
		recovery_mode = r;
	}

	private void print(PrintWriter writer, Object obj) {
		print(writer, obj, "", false);
	}

	private void print(PrintWriter writer, Object obj, String indent, boolean skip_indent) {
		String use_indent = skip_indent ? "" : indent;
		if (obj instanceof Long) {
			writer.println(use_indent + obj);
		} else if (obj instanceof byte[]) {
			byte[] b = (byte[]) obj;
			if (b.length == 20) {
				writer.println(use_indent + " { " + b + " }");
			} else if (b.length < 64) {
				writer.println(new String(b, DEFAULT_CHARSET));
			} else {
				writer.println("[byte array length " + b.length);
			}

		} else if (obj instanceof String) {
			writer.println(use_indent + obj);
		} else if (obj instanceof List) {
			List l = (List) obj;
			writer.println(use_indent + "[");

			for (int i = 0; i < l.size(); i++) {
				writer.print(indent + "  (" + i + ") ");
				print(writer, l.get(i), indent + "    ", true);
			}
			writer.println(indent + "]");

		} else {
			Map m = (Map) obj;
			Iterator it = m.keySet().iterator();

			while (it.hasNext()) {
				String key = (String) it.next();
				if (key.length() > 256) {
					writer.print(indent + key.substring(0, 256) + "... = ");
				} else {
					writer.print(indent + key + " = ");
				}
				print(writer, m.get(key), indent + "  ", true);
			}
		}
	}

	private static void print(File f, File output) {
		try {
			BDecoder decoder = new BDecoder();
			decoder.setRecoveryMode(false);
			PrintWriter pw = new PrintWriter(new FileWriter(output));
			decoder.print(pw, decoder.decodeStream(new BufferedInputStream(new FileInputStream(f))));
			pw.flush();
		} catch (Throwable e) {
		}
	}

	public static void main(String[] args) {
		print(new File("D:/360安全浏览器下载/天将雄师.torrent"), new File("D:/360安全浏览器下载/天将雄师.txt"));
	}
}
最近下载更多
taoyanwangxi  LV2 2021年9月13日
179209648  LV1 2021年8月4日
丨小短丨  LV1 2021年6月6日
sularman  LV2 2020年10月23日
luohaipeng  LV23 2019年12月6日
y812480647  LV1 2019年9月19日
低调人  LV38 2019年7月31日
哥123456  LV3 2019年6月28日
p51520  LV1 2019年4月17日
学习者11  LV1 2018年12月27日
最近浏览更多
taoyanwangxi  LV2 2021年9月13日
179209648  LV1 2021年8月4日
丨小短丨  LV1 2021年6月6日
qazqaz32  LV5 2021年4月5日
qzcyl123 2021年3月31日
暂无贡献等级
sularman  LV2 2020年10月23日
poilkjmnb 2020年10月22日
暂无贡献等级
231313 2020年6月11日
暂无贡献等级
luohaipeng  LV23 2019年12月6日
y812480647  LV1 2019年9月19日
顶部 客服 微信二维码 底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友