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