/**
* Created : May 27, 2012
*
* @author pquiring
*/
import java.io.*;
import java.security.*;
import java.util.*;
import javaforce.*;
public class TorrentMaker {
public static void usage() {
System.out.println("Usage : outfile.torrent <infile | infolder> \"url\" [--name=NAME] [-comment=COMMENT]");
System.out.println("Example : jftorrentMaker test.torrent test.zip \"http://192.168.1.2:6969/\"");
System.exit(1);
}
private static long totalLength = 0;
private static final int piece_length = 512 * 1024;
private static ArrayList<String> files = new ArrayList<String>();
private static ArrayList<Long> flength = new ArrayList<Long>();
private static ArrayList<byte[]> pieces = new ArrayList<byte[]>();
private static byte[] buf = new byte[piece_length];
private static int bufpos = 0;
private static String fsPath, torrentPath;
public static void main(String args[]) {
if (args.length == 0) {
usage();
}
String outFile = null;
String inFile = null;
String name = null;
String url = null;
String comment = null;
for(int a=0;a<args.length;a++) {
if (args[a].startsWith("--name=")) {
name = args[a].substring(7);
continue;
}
if (args[a].startsWith("--comment=")) {
comment = args[a].substring(10);
continue;
}
if (outFile == null) {
outFile = args[a];
continue;
}
if (inFile == null) {
inFile = args[a];
continue;
}
if (url == null) {
url = args[a];
continue;
}
usage();
}
if ((outFile == null) || (inFile == null) || (url == null)) {
usage();
}
if (JF.isWindows()) {
outFile = outFile.replaceAll("\\\\", "/");
inFile = inFile.replaceAll("\\\\", "/");
}
try {
File file = new File(inFile);
if (!file.exists()) throw new Exception("infile doesn't exist");
if (name == null) {
int idx = inFile.lastIndexOf("/");
if (idx == -1)
name = inFile;
else
name = inFile.substring(idx+1);
}
if (file.isDirectory()) {
String fullPath = file.getAbsolutePath();
int idx = fullPath.lastIndexOf("/");
if (idx == -1) {
fsPath = ".";
} else {
fsPath = fullPath.substring(0, idx);
}
torrentPath = "";
if (inFile.endsWith("/")) inFile = inFile.substring(0, inFile.length()-1);
addFolder(inFile);
} else {
fsPath = ".";
torrentPath = "";
addFile(inFile);
}
lastPiece();
ByteArrayOutputStream bb = new ByteArrayOutputStream();
bb.write("d".getBytes());
bb.write("8:announce".getBytes());
bb.write(("" + url.length() + ":" + url).getBytes());
if (comment != null) {
bb.write("7:comment".getBytes());
bb.write((comment.length() + ":" + comment).getBytes());
}
bb.write("4:info".getBytes());
bb.write("d".getBytes());
bb.write("6:length".getBytes());
bb.write(("i" + totalLength + "e").getBytes());
bb.write(("4:name").getBytes());
bb.write((name.length() + ":" + name).getBytes());
bb.write("12:piece length".getBytes());
bb.write(("i" + piece_length + "e").getBytes());
if (files.size() > 1) {
bb.write("5:files".getBytes());
bb.write("l".getBytes());
int noFiles = files.size();
for(int a=0;a<noFiles;a++) {
bb.write("d".getBytes());
bb.write("6:length".getBytes());
bb.write(("i" + flength.get(a).longValue() + "e").getBytes());
bb.write("4:path".getBytes());
bb.write("l".getBytes());
String path[] = files.get(a).split("/");
for(int b=0;b<path.length;b++) {
bb.write((path[b].length() + ":" + path[b]).getBytes());
}
bb.write("e".getBytes()); //path
bb.write("e".getBytes()); //file dict
}
bb.write("e".getBytes()); //files list
}
int noPieces = pieces.size();
bb.write("6:pieces".getBytes());
bb.write(((noPieces * 20) + ":").getBytes());
for(int a=0;a<noPieces;a++) {
bb.write(pieces.get(a));
}
bb.write("e".getBytes()); //info dict
bb.write("e".getBytes()); //torrent dict
FileOutputStream fos = new FileOutputStream(outFile);
fos.write(bb.toByteArray());
fos.close();
System.out.println("Done!");
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
private static final int tmplength = 1024 * 64;
private static byte tmpbuf[] = new byte[tmplength];
private static void addFile(String filename) {
try {
File file = new File(fsPath + "/" + torrentPath + "/" + filename);
files.add(torrentPath + "/" + filename);
long length = file.length();
totalLength += length;
flength.add(length);
FileInputStream fis = new FileInputStream(file);
long toRead = length;
int read;
while (toRead > 0) {
read = piece_length - bufpos;
if (read > tmplength) read = tmplength;
if (read > toRead) read = (int)toRead;
read = fis.read(tmpbuf, 0, read);
if (read <= 0) throw new Exception("file read failed");
System.arraycopy(tmpbuf, 0, buf, bufpos, read);
toRead -= read;
bufpos += read;
if (bufpos == piece_length) {
addPiece();
}
}
fis.close();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
private static void addFolder(String folder) {
String orgPath = torrentPath;
if (torrentPath.length() > 0) torrentPath += "/";
torrentPath += folder;
File file = new File(fsPath + "/" + torrentPath);
File files[] = file.listFiles();
if (files != null) {
for(int a=0;a<files.length;a++) {
if (files[a].isDirectory()) {
addFolder(files[a].getName());
} else {
addFile(files[a].getName());
}
}
}
torrentPath = orgPath;
}
private static void lastPiece() {
if (bufpos == 0) return; //no partial piece
Arrays.fill(buf, bufpos, piece_length, (byte)0);
addPiece();
}
private static void addPiece() {
if (bufpos == piece_length) {
pieces.add(SHA1sum(buf));
} else {
pieces.add(SHA1sum(Arrays.copyOfRange(buf, 0, bufpos)));
}
bufpos = 0;
}
public static byte[] SHA1sum(byte[] data) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
return md.digest(data);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
return null;
}
}
}