59 lines
1.6 KiB
Elixir
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
|