Gran Turismo File System (GTFS): Ruby parsing library

File extension

vol

KS implementation details

License: CC0-1.0

This page hosts a formal specification of Gran Turismo File System (GTFS) using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Parse a local file and get structure in memory:

data = GranTurismoVol.from_file("path/to/local/file.vol")

Or parse structure from a string of bytes:

bytes = "\x00\x01\x02..."
data = GranTurismoVol.new(Kaitai::Struct::Stream.new(bytes))

After that, one can get various attributes from the structure by invoking getter methods like:

data.magic # => get magic

Ruby source code to parse Gran Turismo File System (GTFS)

gran_turismo_vol.rb

# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

require 'kaitai/struct/struct'

unless Gem::Version.new(Kaitai::Struct::VERSION) >= Gem::Version.new('0.7')
  raise "Incompatible Kaitai Struct Ruby API: 0.7 or later is required, but you have #{Kaitai::Struct::VERSION}"
end

class GranTurismoVol < Kaitai::Struct::Struct
  def initialize(_io, _parent = nil, _root = self)
    super(_io, _parent, _root)
    _read
  end

  def _read
    @magic = @_io.ensure_fixed_contents([71, 84, 70, 83, 0, 0, 0, 0].pack('C*'))
    @num_files = @_io.read_u2le
    @num_entries = @_io.read_u2le
    @reserved = @_io.ensure_fixed_contents([0, 0, 0, 0].pack('C*'))
    @offsets = Array.new(num_files)
    (num_files).times { |i|
      @offsets[i] = @_io.read_u4le
    }
    self
  end
  class FileInfo < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @timestamp = @_io.read_u4le
      @offset_idx = @_io.read_u2le
      @flags = @_io.read_u1
      @name = (Kaitai::Struct::Stream::bytes_terminate(Kaitai::Struct::Stream::bytes_strip_right(@_io.read_bytes(25), 0), 0, false)).force_encoding("ASCII")
      self
    end
    def size
      return @size unless @size.nil?
      @size = ((_root.offsets[(offset_idx + 1)] & 4294965248) - _root.offsets[offset_idx])
      @size
    end
    def body
      return @body unless @body.nil?
      if !(is_dir)
        _pos = @_io.pos
        @_io.seek((_root.offsets[offset_idx] & 4294965248))
        @body = @_io.read_bytes(size)
        @_io.seek(_pos)
      end
      @body
    end
    def is_dir
      return @is_dir unless @is_dir.nil?
      @is_dir = (flags & 1) != 0
      @is_dir
    end
    def is_last_entry
      return @is_last_entry unless @is_last_entry.nil?
      @is_last_entry = (flags & 128) != 0
      @is_last_entry
    end
    attr_reader :timestamp
    attr_reader :offset_idx
    attr_reader :flags
    attr_reader :name
  end
  def ofs_dir
    return @ofs_dir unless @ofs_dir.nil?
    @ofs_dir = offsets[1]
    @ofs_dir
  end
  def files
    return @files unless @files.nil?
    _pos = @_io.pos
    @_io.seek((ofs_dir & 4294965248))
    @files = Array.new(_root.num_entries)
    (_root.num_entries).times { |i|
      @files[i] = FileInfo.new(@_io, self, @_root)
    }
    @_io.seek(_pos)
    @files
  end
  attr_reader :magic
  attr_reader :num_files
  attr_reader :num_entries
  attr_reader :reserved
  attr_reader :offsets
end