Saints Rows 2 game packages: Ruby parsing library

File extension

vpp_pc

KS implementation details

License: MIT

This page hosts a formal specification of Saints Rows 2 game packages using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All parsing code for Ruby generated by Kaitai Struct depends on the Ruby runtime library. You have to install it before you can parse data.

The Ruby runtime library can be installed from RubyGems:

gem install kaitai-struct

Code

Parse a local file and get structure in memory:

data = SaintsRow2VppPc.from_file("path/to/local/file.vpp_pc")

Or parse structure from a string of bytes:

bytes = "\x00\x01\x02..."
data = SaintsRow2VppPc.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 Saints Rows 2 game packages

saints_row_2_vpp_pc.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.9')
  raise "Incompatible Kaitai Struct Ruby API: 0.9 or later is required, but you have #{Kaitai::Struct::VERSION}"
end

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

  def _read
    @magic = @_io.read_bytes(5)
    raise Kaitai::Struct::ValidationNotEqualError.new([206, 10, 137, 81, 4].pack('C*'), magic, _io, "/seq/0") if not magic == [206, 10, 137, 81, 4].pack('C*')
    @pad1 = @_io.read_bytes(335)
    @num_files = @_io.read_s4le
    @container_size = @_io.read_s4le
    @len_offsets = @_io.read_s4le
    @len_filenames = @_io.read_s4le
    @len_extensions = @_io.read_s4le
    @smth5 = @_io.read_s4le
    @smth6 = @_io.read_s4le
    @smth7 = @_io.read_s4le
    @smth8 = @_io.read_s4le
    @smth9 = @_io.read_s4le
    self
  end
  class Offsets < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @entries = []
      i = 0
      while not @_io.eof?
        @entries << Offset.new(@_io, self, @_root)
        i += 1
      end
      self
    end
    class Offset < Kaitai::Struct::Struct
      def initialize(_io, _parent = nil, _root = self)
        super(_io, _parent, _root)
        _read
      end

      def _read
        @name_ofs = @_io.read_u4le
        @ext_ofs = @_io.read_u4le
        @smth2 = @_io.read_s4le
        @ofs_body = @_io.read_s4le
        @len_body = @_io.read_s4le
        @always_minus_1 = @_io.read_s4le
        @always_zero = @_io.read_s4le
        self
      end
      def filename
        return @filename unless @filename.nil?
        io = _root.filenames._io
        _pos = io.pos
        io.seek(name_ofs)
        @filename = (io.read_bytes_term(0, false, true, true)).force_encoding("UTF-8")
        io.seek(_pos)
        @filename
      end
      def ext
        return @ext unless @ext.nil?
        io = _root.extensions._io
        _pos = io.pos
        io.seek(ext_ofs)
        @ext = (io.read_bytes_term(0, false, true, true)).force_encoding("UTF-8")
        io.seek(_pos)
        @ext
      end
      def body
        return @body unless @body.nil?
        io = _root._io
        _pos = io.pos
        io.seek((_root.data_start + ofs_body))
        @body = io.read_bytes(len_body)
        io.seek(_pos)
        @body
      end
      attr_reader :name_ofs
      attr_reader :ext_ofs
      attr_reader :smth2
      attr_reader :ofs_body
      attr_reader :len_body
      attr_reader :always_minus_1
      attr_reader :always_zero
    end
    attr_reader :entries
  end
  class Strings < Kaitai::Struct::Struct
    def initialize(_io, _parent = nil, _root = self)
      super(_io, _parent, _root)
      _read
    end

    def _read
      @entries = []
      i = 0
      while not @_io.eof?
        @entries << (@_io.read_bytes_term(0, false, true, true)).force_encoding("UTF-8")
        i += 1
      end
      self
    end
    attr_reader :entries
  end
  def filenames
    return @filenames unless @filenames.nil?
    _pos = @_io.pos
    @_io.seek(ofs_filenames)
    @_raw_filenames = @_io.read_bytes(len_filenames)
    _io__raw_filenames = Kaitai::Struct::Stream.new(@_raw_filenames)
    @filenames = Strings.new(_io__raw_filenames, self, @_root)
    @_io.seek(_pos)
    @filenames
  end
  def ofs_extensions
    return @ofs_extensions unless @ofs_extensions.nil?
    @ofs_extensions = (((ofs_filenames + len_filenames) & 4294965248) + 2048)
    @ofs_extensions
  end
  def files
    return @files unless @files.nil?
    _pos = @_io.pos
    @_io.seek(2048)
    @_raw_files = @_io.read_bytes(len_offsets)
    _io__raw_files = Kaitai::Struct::Stream.new(@_raw_files)
    @files = Offsets.new(_io__raw_files, self, @_root)
    @_io.seek(_pos)
    @files
  end
  def data_start
    return @data_start unless @data_start.nil?
    @data_start = (((ofs_extensions + len_extensions) & 4294965248) + 2048)
    @data_start
  end
  def extensions
    return @extensions unless @extensions.nil?
    _pos = @_io.pos
    @_io.seek(ofs_extensions)
    @_raw_extensions = @_io.read_bytes(len_extensions)
    _io__raw_extensions = Kaitai::Struct::Stream.new(@_raw_extensions)
    @extensions = Strings.new(_io__raw_extensions, self, @_root)
    @_io.seek(_pos)
    @extensions
  end
  def ofs_filenames
    return @ofs_filenames unless @ofs_filenames.nil?
    @ofs_filenames = (((2048 + len_offsets) & 4294965248) + 2048)
    @ofs_filenames
  end
  attr_reader :magic
  attr_reader :pad1
  attr_reader :num_files
  attr_reader :container_size
  attr_reader :len_offsets
  attr_reader :len_filenames
  attr_reader :len_extensions
  attr_reader :smth5
  attr_reader :smth6
  attr_reader :smth7
  attr_reader :smth8
  attr_reader :smth9
  attr_reader :_raw_filenames
  attr_reader :_raw_files
  attr_reader :_raw_extensions
end