Ruby で構造体もどき
Ruby から離れていたのでリハビリがてらにちょろっとコードを書いてみた。
バイナリデータを扱う場合に unpack は使いづらい、だがしかし Gem の BinData とかを使うほど大げさでもない場合用。
構造体もどきを定義して名前でアクセスできる、が、やっぱり使いづらい('ω` ) 配列を扱えないので非常に無駄な部分が多発しそうです。配列対応すれば使えなくもないかもしれない。unpack のラッパーなので速度的にはこれが一番出ると思います。BinData はまさかの1バイトずつ読み込みでちょ〜っと速度低下がきになるレベル。
Ruby ぽいプログラムにはできたと思います。たぶん。
(PMD ファイルフォーマットは こちら を参考にしました)
(2/19 更新)
inherited を使うように変更
module BinStruct class UnknownPattern < StandardError; end class UnsupportTemplate < StandardError; end class InvalidStruct < StandardError; end class Base < BasicObject TEMPLATE_BYTES = { "a" => 1, "A" => 1, "Z" => 1, "h" => 2, "H" => 2, "c" => 1, "C" => 1, "s" => 2, "S" => 2, "i" => 4, "I" => 4, "l" => 4, "L" => 4, "q" => 8, "Q" => 8, "n" => 2, "N" => 2, "v" => 2, "V" => 2, "f" => 4, "d" => 8, "e" => 4, "E" => 4, "g" => 4, "G" => 8, "x" => 1 } @@index_table = {} @@pattern = {} @@size = {} @@index = {} class << self def inherited(klass) @@index_table[klass] = {} @@pattern[klass] = "" @@size[klass] = 0 @@index[klass] = 0 end def method_missing(name, pattern) @@index_table[self][name] = @@index[self] @@index[self] += 1 @@pattern[self] += pattern unless /(\w!?)(\d*)/ =~ pattern raise UnknownPattern, "unkonwn pattern '#{pattern}'" end template = $1 length = $2.to_i length = 1 if length == 0 bytes = TEMPLATE_BYTES[template] unless bytes raise UnsupportTemplate, "'#{template}' is not supported" end @@size[self] += bytes * length module_eval <<-EOS def #{name} return @values[#{@@index_table[self][name]}] end EOS end def __pattern__ return @@pattern[self] end def __size__ return @@size[self] end end def initialize(values) @values = values end end class Stream def initialize(file_name = nil) open(file_name) if file_name end def open(file_name) @buffer = File.read(file_name, File.size(file_name)) @pos = 0 end def read(struct_class) unless struct_class.superclass == Base raise InvalidStruct, "invalid struct class" end pattern = struct_class.__pattern__ struct = struct_class.new(@buffer.unpack("@#{@pos}#{pattern}")) @pos += struct_class.__size__ return struct end def pos=(position) @pos = position end end end class PMD class Header < BinStruct::Base magic "a3" version "f" model_name "Z20" comment "Z256" vert_count "L" end class TVertex < BinStruct::Base x "f"; y "f"; z "f" nx "f"; ny "f"; nz "f" u "f"; v "f" bone_num_1 "S"; bone_num_2 "S" bone_weight "C" edge_flag "C" end def test bin = BinStruct::Stream.new("normal.pmd") header = bin.read(Header) puts header.magic puts header.version puts header.model_name puts header.comment puts header.vert_count t_vertex = bin.read(TVertex) puts t_vertex.x puts t_vertex.y puts t_vertex.z puts t_vertex.nx puts t_vertex.ny puts t_vertex.nz puts t_vertex.u puts t_vertex.v puts t_vertex.bone_num_1 puts t_vertex.bone_num_2 puts t_vertex.bone_weight puts t_vertex.edge_flag end end pmd = PMD.new pmd.test
Pmd 1.0 LatミクVer2.3N Lat式ミクVer2.3 Normalモデル Ver.20100627 エッジは「0.3〜0.6」くらいがオススメです モデリング&データ変換 :Lat Copyright :CRYPTON FUTURE MEDIA, INC 15847 -0.10337000340223312 16.221189498901367 -0.6946200132369995 -0.9994639158248901 0.025453666225075722 0.020592058077454567 0.84170001745224 0.18515999615192413 33 33 100 0