lib/dbf/memo.ex

defmodule DBF.Memo do
  alias DBF.DatabaseError
  defstruct [
    :version,
    :device,
    :block_size
  ]
  @type t :: %DBF.Memo{
    version: integer,
    device: File.stream,
    block_size: integer
  }

  @spec open(binary(), integer()) :: DBF.Memo.t() | {:error, DatabaseError.t()}
  def open(path, version) when is_binary(path) and is_integer(version) do
    if File.exists?(path) do
      {:ok, file} = File.open(path, [:read, :binary])
      {:ok, data} = :file.pread(file, 0, 512)
      <<_next_block::little-unsigned-integer-32, block_size::little-unsigned-16,_rest::binary>> = data
      {:ok, %__MODULE__{
        version: version,
        device: file,
        block_size: (if block_size > 0, do: block_size, else: 512)
      }}
    else
      {:error, DatabaseError.new(:enoent)}
    end
  end

  @spec get_block(DBF.Memo.t(), any()) :: binary() | {:error, atom()}
  def get_block(nil, _) do
    {:error, DatabaseError.new(:missing_memo_file)}
  end

  def get_block(%DBF.Memo{version: 0x83, device: dev, block_size: block_size}, block_number) do
    offset = block_number * block_size
    {:ok, raw_data} = :file.pread(dev, offset, 512)
    raw_data |> String.replace([<<31>>], "") |> String.trim()
  end

  def get_block(%DBF.Memo{device: dev, block_size: block_size}, block_number) do
    offset = block_number * block_size
    {:ok, <<_type::binary-size(4), length::little-unsigned-integer-32>>} = :file.pread(dev, offset, 8)
    {:ok, raw_data} = :file.pread(dev, offset+8, length)
    raw_data |> String.replace([<<31>>], "") |> String.trim()
  end


end