class MyFile < File
def peek(len)
str = self.read(len)
self.pos -= len
str
end
end
#字段信息
class TVal
attr_accessor :Type
attr_accessor :Value
def to_s()
@Value
end
end
#文件信息
class TFile
attr_reader :Length
attr_reader :Path
attr_reader :Name
attr_reader :FirstPiece
attr_reader :PieceLength
def initialize(size, fullPath, pStart, pLen)
@Length = Size
@FirstPiece = pStart
@PieceLength = pLen
fileNamePos = fullPath.rindex('\\')
if fileNamePos.nil? then
@Path = ""
@Name = fullPath
else
@Path = fullPath[0..fileNamePos]
@Name = fullPath[fileNamePos..fullPath.length-1]
end
end
end
class TorrentParser
attr_accessor :Root
def initialize(tPath)
@Path = tPath
@Size = nil
@Root = nil
@Files = nil
@Torrent = nil
@TotalValues = 0
@InfoStart = nil
@InfoEnd = nil
end
#主函数
def Process()
raise "File doesn't exist" if !MyFile.exist? @Path #检测文件路径
@TotalValues = 0
@Torrent = MyFile.new @Path
@Torrent.binmode
raise "Torrent File Error" if @Torrent.read(1) != 'd'
@Root = ProcessDict()
@Files = GetFiles()
end
#读取一个字典
def ProcessDict()
tempDict = Hash.new
while @Torrent.peek(1) != 'e' #向后读一个字符 看看是不是e 如果是e的话 说明dictionary结束了
key = ProcessString() #读取一个Key Name
if key == 'info' then #如果是名字为info的key
@InfoStart = @Torrent.pos #设置info start的index
end
if key == 'pieces' then #如果是pieces的key 则按照特殊的byte方式读取
val = TVal.new
val.Type = "BYTE"
val.Value = ProcessByte()
tempDict[key] = val
else
val = ProcessVal() #否则读该key的Val
tempDict[key] = val
end
if key == "info" then
@InfoEnd = @Torrent.pos - @InfoStart
end
end
@Torrent.read 1 #读掉结尾的"e"
return tempDict
end
#读取一个字串
def ProcessString()
tempLen = ''
tempLen += @Torrent.read(1) while @Torrent.peek(1) != ':' #获取字串长度
@Torrent.read(1) # 将后面的冒号读掉
content = @Torrent.read(tempLen.to_i) #然后读指定长度的字符串 并返回
return content
end
#读取字节串
def ProcessByte()
tempLen = ''
tempLen += @Torrent.read(1) while @Torrent.peek(1) != ':' #获取字串长度
@Torrent.read(1) # 将后面的冒号读掉
chrs = Array.new
(1..tempLen.to_i).each{|idx|
byte = @Torrent.readchar
chrs << "%02X" % byte
}
chrs
end
#读一个数字
def ProcessInt()
tempInt = ''
tempInt += @Torrent.read(1) while @Torrent.peek(1) != 'e'
@Torrent.read(1) #把最后的e读掉
tempInt.to_i
end
#读一个列表
def ProcessList()
tempList = Array.new
while @Torrent.peek(1) != 'e'
tempList << ProcessVal
end
tempList
end
#读取一个Val
def ProcessVal()
@TotalValues += 1
val = TVal.new
case @Torrent.peek(1)
when 'd'
@Torrent.read(1) #把d读掉
val.Type = "DICT"
val.Value = ProcessDict()
when 'l'
@Torrent.read(1) #把l读掉
val.Type = "LIST"
val.Value = ProcessList()
when 'i'
@Torrent.read(1) #把i读掉
val.Type = "INT"
val.Value = ProcessInt()
else
val.Type = "STRING"
val.Value = ProcessString()
end
val
end
end
begin
_torrent = TorrentParser.new ARGV.shift
_torrent.Process
_torrent.Root.map{|key,value|
case value.Type
when 'LIST'
puts "#{key}(#{value.Type})\t\t#{value.Value}"
when 'BYTE'
puts "#{key}(#{value.Type})\t\t%0X", value.Value
when 'DICT'
value.Value.map{|skey,svalue|
puts "--#{skey}(#{svalue.Type})\t\t#{svalue.Value}"
}
else
puts "#{key}(#{value.Type})\t\t#{value.Value}"
end
}
rescue
puts $!
puts $@
end