Files
arcaea-apk-assets/arcaea_apk_assets/parsers/packlist.py
2025-06-06 22:44:02 +08:00

152 lines
4.9 KiB
Python

"""
Copyright (C) 2025 283375
This file is part of "arcaea-apk-assets" (stated as "this program" below).
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
"""
import logging
from typing import Any
from ._shared import blank_str_to_none
from .defs import ARCAEA_LANGUAGE_KEYS, Pack, PackLocalization
logger = logging.getLogger(__name__)
class PacklistFormatError(Exception):
pass
class ArcaeaPacklistParser:
@staticmethod
def _assert_root_content(root: Any):
if not isinstance(root, dict):
msg = f"packlist root should be `dict`, got {type(root)}"
raise PacklistFormatError(msg)
packs = root.get("packs")
if packs is None:
msg = "`packs` field does not exist"
raise PacklistFormatError(msg)
if not isinstance(packs, list):
msg = f"`packs` field should be `list`, got {type(packs)}"
raise PacklistFormatError(msg)
@staticmethod
def _assert_pack_object(obj: Any):
if not isinstance(obj, dict):
msg = f"A pack object should be `dict`, got {type(obj)}"
raise PacklistFormatError(msg)
id_ = obj.get("id")
if not isinstance(id_, str):
msg = f"`id` attribute of a pack should be `str`, got value {id_!r}"
raise PacklistFormatError(msg)
@classmethod
def items(
cls, packlist_content: dict, *, skip_asserts: bool = False
) -> list[Pack | PackLocalization]:
try:
cls._assert_root_content(packlist_content)
except PacklistFormatError as e:
if skip_asserts:
logger.exception("Error skipped during packlist parsing:")
return []
raise e
packs = packlist_content["packs"]
results: list[Pack | PackLocalization] = [
Pack(
id="single",
name="Memory Archive",
description=None,
section=None,
plus_character=None,
append_parent_id=None,
)
]
for pack in packs:
try:
cls._assert_pack_object(pack)
except PacklistFormatError as e:
if skip_asserts:
logger.exception("Error skipped during packlist parsing:")
continue
raise e
name_localized = pack.get("name_localized", {})
description_localized = pack.get("description_localized", {})
id_ = pack["id"]
name = blank_str_to_none(name_localized.get("en"))
description = blank_str_to_none(description_localized.get("en"))
section = blank_str_to_none(pack.get("section"))
append_parent_id = pack.get("pack_parent")
plus_character = (
pack["plus_character"] if pack.get("plus_character", -1) > -1 else None
)
results.append(
Pack(
id=id_,
name=name,
description=description,
section=section,
plus_character=plus_character,
append_parent_id=append_parent_id,
)
)
for lang_key in ARCAEA_LANGUAGE_KEYS:
name_l10n = blank_str_to_none(name_localized.get(lang_key))
description_l10n = blank_str_to_none(
description_localized.get(lang_key)
)
if name_l10n or description_l10n:
results.append(
PackLocalization(
id=id_,
lang=lang_key,
name=name_l10n,
description=description_l10n,
)
)
return results
@classmethod
def packs(cls, packlist_content: dict, *, skip_asserts: bool = False) -> list[Pack]:
return [
item
for item in cls.items(packlist_content, skip_asserts=skip_asserts)
if isinstance(item, Pack)
]
@classmethod
def pack_localizations(
cls, packlist_content: dict, *, skip_asserts: bool = False
) -> list[PackLocalization]:
return [
item
for item in cls.items(packlist_content, skip_asserts=skip_asserts)
if isinstance(item, PackLocalization)
]