This commit is contained in:
2025-07-06 18:43:00 +08:00
parent 155e8b9664
commit bcaa1a268e
9 changed files with 390 additions and 401 deletions

View File

@ -16,11 +16,15 @@ You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import logging
from typing import Any
from arcaea_offline_schemas.dis.v5.dataclass import AodisPack, AodisPackLocalization
from typeguard import TypeCheckError, check_type
from ._shared import blank_str_to_none
from .defs import ARCAEA_LANGUAGE_KEYS, Pack, PackLocalization
from .defs import ARCAEA_LANGUAGE_KEYS
logger = logging.getLogger(__name__)
@ -31,121 +35,145 @@ class PacklistFormatError(Exception):
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)
def check_root_content(root: dict) -> None:
try:
check_type(root, dict)
check_type(root["packs"], list[dict])
except (KeyError, TypeCheckError) as e:
raise PacklistFormatError from e
@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]:
def check_pack_object(obj: dict) -> None:
try:
cls._assert_root_content(packlist_content)
except PacklistFormatError as e:
if skip_asserts:
logger.exception("Error skipped during packlist parsing:")
return []
check_type(obj, dict)
check_type(obj["id"], str)
except (KeyError, TypeCheckError) as e:
raise PacklistFormatError from e
raise e
@staticmethod
def parse_pack(pack: dict) -> AodisPack:
id_ = pack["id"]
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,
)
]
name_localized = pack.get("name_localized", {})
description_localized = pack.get("description_localized", {})
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
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
)
raise e
return AodisPack(
id=id_,
name=name or "",
isWorldExtend=pack.get("is_extend_pack") is True,
description=description,
section=section,
unlocksPartner=plus_character,
appendParentId=append_parent_id,
)
name_localized = pack.get("name_localized", {})
description_localized = pack.get("description_localized", {})
@staticmethod
def parse_pack_localizations(pack: dict) -> list[AodisPackLocalization]:
results = []
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
name_localized = pack.get("name_localized", {})
description_localized = pack.get("description_localized", {})
id_ = pack["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),
)
results.append(
Pack(
id=id_,
name=name,
description=description,
section=section,
plus_character=plus_character,
append_parent_id=append_parent_id,
if name_l10n or description_l10n:
results.append(
AodisPackLocalization(
id=id_,
lang=lang_key,
name=name_l10n,
description=description_l10n,
),
)
)
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]:
def items(
cls,
packlist_content: dict,
*,
skip_format_checks: bool = False,
) -> list[AodisPack | AodisPackLocalization]:
try:
cls.check_root_content(packlist_content)
except PacklistFormatError:
if skip_format_checks:
logger.exception("Error skipped during packlist parsing:")
return []
raise
packs = packlist_content["packs"]
results: list[AodisPack | AodisPackLocalization] = [
AodisPack(
id="single",
name="Memory Archive",
isWorldExtend=False,
description=None,
section=None,
unlocksPartner=None,
appendParentId=None,
),
]
for pack in packs:
try:
cls.check_pack_object(pack)
except PacklistFormatError:
if skip_format_checks:
logger.exception("Error skipped during packlist parsing:")
continue
raise
results.append(cls.parse_pack(pack))
results.extend(cls.parse_pack_localizations(pack))
return results
@classmethod
def packs(
cls,
packlist_content: dict,
*,
skip_format_checks: bool = False,
) -> list[AodisPack]:
return [
item
for item in cls.items(packlist_content, skip_asserts=skip_asserts)
if isinstance(item, Pack)
for item in cls.items(
packlist_content,
skip_format_checks=skip_format_checks,
)
if isinstance(item, AodisPack)
]
@classmethod
def pack_localizations(
cls, packlist_content: dict, *, skip_asserts: bool = False
) -> list[PackLocalization]:
cls,
packlist_content: dict,
*,
skip_format_checks: bool = False,
) -> list[AodisPackLocalization]:
return [
item
for item in cls.items(packlist_content, skip_asserts=skip_asserts)
if isinstance(item, PackLocalization)
for item in cls.items(
packlist_content,
skip_format_checks=skip_format_checks,
)
if isinstance(item, AodisPackLocalization)
]