Permalink
Browse files

Working parser/builder, and added new tags

  • Loading branch information...
1 parent 7315d70 commit 9dde0c06774d0ccaee69f001571bf63306b99d68 Jack Chen committed Apr 17, 2010
Showing with 552 additions and 10 deletions.
  1. +3 −2 Rakefile
  2. +57 −0 dmap-ng.gemspec
  3. +1 −0 lib/dmap-ng.rb
  4. +185 −0 lib/dmap.rb
  5. +173 −0 lib/dmap/tags.rb
  6. +11 −0 lib/true_false_ext.rb
  7. +0 −7 spec/dmap-ng_spec.rb
  8. +88 −0 spec/dmap_spec.rb
  9. +33 −0 spec/parser_spec.rb
  10. +1 −1 spec/spec_helper.rb
View
@@ -5,12 +5,13 @@ begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
gem.name = "dmap-ng"
- gem.summary = %Q{TODO: one-line summary of your gem}
- gem.description = %Q{TODO: longer description of your gem}
+ gem.summary = %Q{An improved DMAP gem that allows easy building and parsing of DMAP structures}
+ gem.description = %Q{An improved DMAP gem that allows easy building and parsing of DMAP structures}
gem.email = "chendo@chendo.net"
gem.homepage = "http://github.com/chendo/dmap-ng"
gem.authors = ["Jack Chen"]
gem.add_development_dependency "rspec", ">= 1.2.9"
+ gem.files += Dir['lib/**/*.rb']
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
end
Jeweler::GemcutterTasks.new
View
@@ -0,0 +1,57 @@
+# Generated by jeweler
+# DO NOT EDIT THIS FILE DIRECTLY
+# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+ s.name = %q{dmap-ng}
+ s.version = "0.1.0"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.authors = ["Jack Chen"]
+ s.date = %q{2010-04-17}
+ s.description = %q{An improved DMAP gem that allows easy building and parsing of DMAP structures}
+ s.email = %q{chendo@chendo.net}
+ s.extra_rdoc_files = [
+ "LICENSE",
+ "README.rdoc"
+ ]
+ s.files = [
+ ".document",
+ ".gitignore",
+ "LICENSE",
+ "README.rdoc",
+ "Rakefile",
+ "VERSION",
+ "lib/dmap-ng.rb",
+ "lib/dmap.rb",
+ "lib/dmap/tags.rb",
+ "lib/true_false_ext.rb",
+ "spec/spec.opts",
+ "spec/spec_helper.rb"
+ ]
+ s.homepage = %q{http://github.com/chendo/dmap-ng}
+ s.rdoc_options = ["--charset=UTF-8"]
+ s.require_paths = ["lib"]
+ s.rubygems_version = %q{1.3.5}
+ s.summary = %q{An improved DMAP gem that allows easy building and parsing of DMAP structures}
+ s.test_files = [
+ "spec/dmap_spec.rb",
+ "spec/parser_spec.rb",
+ "spec/spec_helper.rb"
+ ]
+
+ if s.respond_to? :specification_version then
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+ s.specification_version = 3
+
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
+ else
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
+ end
+ else
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
+ end
+end
+
View
@@ -0,0 +1 @@
+require 'dmap'
View
@@ -0,0 +1,185 @@
+$:.unshift(File.dirname(__FILE__)) unless
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
+
+require 'dmap/tags'
+require 'true_false_ext'
+
+class Object
+ def tap(&block)
+ yield
+ block
+ end
+
+ def tapp(prefix = nil, &block)
+ block ||= lambda { |x| x }
+ str = (ret = block[self]).is_a?(String) ? ret : ret.inspect
+ puts [prefix, str].compact.join(': ')
+ self
+ end
+end
+
+
+class DMAP
+ VERSION = '0.0.1'
+
+ class << self
+
+ def build(&block)
+ builder = TagBuilder.new
+ builder.instance_eval &block
+ builder.result.first
+ end
+
+ def parse(data)
+ Parser.parse(data)
+ end
+
+
+ end
+
+ class Tag
+
+ class << self
+ def lookup(code)
+ TAGS[code]
+ end
+ end
+
+ attr_accessor :code, :value, :tag, :type
+
+ def initialize(code, value)
+ @code, @value = code, value
+ @tag, @type = lookup
+ end
+
+ def to_dmap
+ data = case type
+ when :string
+ value
+ when :list
+ value.inject('') { |mem, v| mem += v.to_dmap }
+ when :version
+ value.pack(pack_code)
+ when :long
+ # Some types seem to be the wrong endian
+ # so there are endian hacks
+ [value].pack('Q').reverse
+ when :signed_integer
+ [value].pack(pack_code).reverse
+ else
+ [value.to_i].pack(pack_code)
+ end
+ "#{code}#{[length].pack('N')}#{data}"
+ end
+
+ def ==(obj)
+ self.to_dmap == obj.to_dmap
+ end
+
+ def pack_code
+ @pack_code ||= STATIC_LENGTH_TYPES[type].last rescue raise("No pack code definition for #{code}")
+ end
+
+
+ def length
+ @length ||= case type
+ when :string
+ value.length
+ when :list
+ value.inject(0) { |mem, v| mem += v.length + 8 }
+ else
+ STATIC_LENGTH_TYPES[type].first
+ end
+ end
+
+
+
+
+ def lookup
+ self.class.lookup(code)
+ end
+
+
+
+ def inspect(level = 0)
+ pad = ' ' * (level * 2)
+ case value
+ when Array
+ if value.any? { |e| e.is_a? Tag }
+ (["#{pad}#{code}[#{length}]:"] + value.map{ |v| v.inspect(level + 1) }).join("\n")
+ else
+ "#{pad}#{code}[#{length}]: #{value.inspect}"
+ end
+ else
+ "#{pad}#{code}[#{length}]: #{value.inspect}"
+ end
+ end
+
+ end
+
+ class TagBuilder
+ def initialize
+ @tags = []
+ end
+
+ def method_missing(meth, *args, &block)
+ if block_given?
+ value = self.class.new.instance_eval &block
+ else
+ value = args.size > 1 ? args : args.first
+ end
+ @tags << Tag.new(meth, value)
+ end
+
+ def result
+ @tags
+ end
+ end
+
+ class Parser
+
+
+ def self.parse(buffer)
+ buffer = StringIO.new(buffer)
+
+ ret = []
+ while !buffer.eof?
+ code = buffer.read(4).to_sym
+
+ raise "Invalid tag code format while parsing" unless code.to_s =~ /^[a-z]{4}$/i
+
+ length = buffer.read(4).unpack('N').first
+ data = buffer.read(length)
+
+ definition = Tag.lookup(code)
+
+ raise "No tag definition for #{code}, length: #{length}, data: #{data.inspect}" unless definition
+
+ ret << if (type = definition.last) == :list
+ Tag.new(code, parse(data))
+ else
+ expected_length, pack_code = STATIC_LENGTH_TYPES[type]
+ value = case type
+ when :string
+ data
+ when :version
+ data.unpack(pack_code)
+ when :long
+ # Some types seem to be the wrong endian
+ # so there are endian hacks
+ data.reverse.unpack(pack_code).first
+ when :signed_integer
+ data.reverse.unpack(pack_code).first
+ else
+ data.unpack(pack_code).first
+ end
+ Tag.new(code, value)
+ end
+ end
+
+ (ret.size > 1) ? ret : ret.first
+ end
+
+
+ end
+end
Oops, something went wrong.

0 comments on commit 9dde0c0

Please sign in to comment.