Files

59 lines
1.6 KiB
Elixir

defmodule CobblemonUi.CobblemonFS.PCStore do
@moduledoc """
Parses a player's PC storage `.dat` file.
PC layout:
Root -> boxes -> box0..boxN -> slot0..slotN
"""
alias CobblemonUi.CobblemonFS.{NBT, Pokemon}
@doc """
Reads and parses a PC storage `.dat` file at the given path.
Returns `{:ok, [%{box: integer, pokemon: list}]}` or `{:error, reason}`.
"""
@spec parse(String.t()) :: {:ok, list(map())} | {:error, term()}
def parse(path) do
with {:ok, data} <- File.read(path),
{:ok, {_name, root}} <- NBT.decode(data) do
{:ok, normalize_boxes(root)}
else
{:error, :enoent} -> {:error, :not_found}
{:error, reason} -> {:error, {:corrupt_data, reason}}
end
end
defp normalize_boxes(root) when is_map(root) do
root
|> Enum.filter(fn {key, val} ->
String.match?(key, ~r/^Box\d+$/) and is_map(val) and map_size(val) > 1
end)
|> Enum.sort_by(fn {key, _} -> extract_index(key) end)
|> Enum.map(fn {key, box_data} ->
%{
box: extract_index(key),
pokemon: normalize_box_slots(box_data)
}
end)
end
defp normalize_boxes(_), do: []
defp normalize_box_slots(box) when is_map(box) do
box
|> Enum.filter(fn {key, _} -> String.match?(key, ~r/^Slot\d+$/) end)
|> Enum.sort_by(fn {key, _} -> extract_index(key) end)
|> Enum.map(fn {_key, slot_data} -> Pokemon.normalize(slot_data) end)
end
defp normalize_box_slots(_), do: []
defp extract_index(key) do
case Integer.parse(String.replace(key, ~r/[^\d]/, "")) do
{n, _} -> n
:error -> 0
end
end
end