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日

