Hi,
As I was DRYing out my code, I came up with the following way to detect whether or not I had hit :eof when parsing the file.
{:ok, current_cursor_position} = :file.position(assets_file_pointer, :cur)
%File.Stat{size: size} = File.stat!("assets/000.lfl")
if (current_cursor_position < size) do
#do stuff
else
#stop recursing
end
find_a_block/2
is my recursive function
Like I said, it works, but I wonder if there is a better way…
defmodule ScummIndex do
defguard common_block_type(value) when value === "0R" or value === "0S" or value === "0N" or value === "0C"
def parse do
File.open!("assets/000.lfl")
|> find_a_block(%{})
end
def find_a_block(assets_file_pointer, data_store) do
{:ok, current_cursor_position} = :file.position(assets_file_pointer, :cur)
%File.Stat{size: size} = File.stat!("assets/000.lfl")
if (current_cursor_position < size) do
block_meta_data = get_block_meta_data(assets_file_pointer)
data_store = Map.merge(data_store, parse_block(block_meta_data, assets_file_pointer))
find_a_block(assets_file_pointer,data_store)
else
data_store
end
end
defp get_block_meta_data(assets_file_pointer) do
block_size = Helpers.binread_reverse_decode(assets_file_pointer,4)
block_type = IO.binread(assets_file_pointer,2)
{block_size, block_type}
end
@spec parse_block({any, <<_::16>>}, atom | pid | {:file_descriptor, atom, any}) :: %{
optional(<<_::40, _::_*8>>) => any
}
def parse_block( {block_size, "RN"} , assets_file_pointer) do
# block data includes block size of 4 bytes, block type of 2 bytes and trailing null byte
# so 7 bytes in total, each room is 10 bytes in size. This does not currently account for
# a corrupt disk image
number_of_rooms = trunc( (block_size - 7) / 10 )
room_data = Enum.reduce(1..number_of_rooms, %{}, fn(_, acc) ->
room_number = Helpers.binread_decode(assets_file_pointer,1)
room_name = assets_file_pointer
|> IO.binread(9)
|> Helpers.xor(0xFF)
Map.put(acc, room_name, room_number)
Map.put(acc, room_number, room_name)
end)
# discard end of block null byte
:file.position(assets_file_pointer, block_size)
%{"rooms" => room_data}
end
def parse_block( { _, block_type} , assets_file_pointer) when common_block_type(block_type) do
number_of_items = Helpers.binread_reverse_decode(assets_file_pointer,2)
block_data = Enum.reduce(1..number_of_items, %{}, fn(_, acc) ->
file_number = Helpers.binread_reverse_decode(assets_file_pointer,1)
offset = Helpers.binread_reverse_decode(assets_file_pointer,4)
Map.put(acc, file_number, offset)
end)
%{"scripts_data" => block_data}
end
def parse_block( {_, "0O"} , assets_file_pointer) do
number_of_items = Helpers.binread_reverse_decode(assets_file_pointer,2)
block_data = Enum.reduce(1..number_of_items, %{}, fn(_, acc) ->
class_data = Helpers.binread_decode(assets_file_pointer,3)
owner_state = Helpers.binread_decode(assets_file_pointer,1)
#extract the 4 bits for state and owner
owner = owner_state
|> Bitwise.band(0xF0)
|> Bitwise.>>>(4)
state = Bitwise.band(owner_state, 0x0F)
Map.put(acc, class_data, %{owner: owner, state: state})
end)
%{"room_directory" => block_data}
end
end