diff --git a/.github/actions/setup-python/action.yml b/.github/actions/setup-python/action.yml index 7301538..0d5d3bd 100644 --- a/.github/actions/setup-python/action.yml +++ b/.github/actions/setup-python/action.yml @@ -5,7 +5,7 @@ inputs: python-version: description: Python version required: false - default: "3.12" + default: "3.13" runs: using: "composite" diff --git a/AGENTS.md b/AGENTS.md index fd660c4..9e9dfe0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -41,13 +41,12 @@ Common flags: - `storage/asset/gamedata/`: decoded game data - `storage/asset/raw/`: raw extracted assets - `OpenArknightsFBS/FBS/`: flatbuffer schemas -- `bin/flatc` (`bin/flatc.exe` on Windows): flatc binary ## Config (Env) - `ENVIRONMENT`, `LOG_LEVEL`, `TIMEOUT` - `TOKEN`, `BACKEND_ENDPOINT` (for upload-related tasks, e.g. `ItemDemand`) -- `FLATC_PATH`, `SENTRY_DSN` +- `SENTRY_DSN` Use `BACKEND_ENDPOINT` (not `ENDPOINT`). diff --git a/Dockerfile b/Dockerfile index 7e2cf84..3ec8b95 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 -FROM python:3.12-bookworm AS requirements-stage +FROM python:3.13-bookworm AS requirements-stage WORKDIR /tmp @@ -12,7 +12,7 @@ COPY ./pyproject.toml ./uv.lock* /tmp/ RUN uv export --format requirements.txt -o requirements.txt --no-editable --no-hashes --no-dev --no-emit-project -FROM python:3.12-bookworm AS build-stage +FROM python:3.13-bookworm AS build-stage WORKDIR /wheel @@ -20,7 +20,7 @@ COPY --from=requirements-stage /tmp/requirements.txt /wheel/requirements.txt RUN pip wheel --wheel-dir=/wheel --no-cache-dir --requirement /wheel/requirements.txt -FROM python:3.12-bookworm AS metadata-stage +FROM python:3.13-bookworm AS metadata-stage WORKDIR /tmp @@ -29,7 +29,7 @@ RUN --mount=type=bind,source=./.git/,target=/tmp/.git/ \ || git rev-parse --short HEAD > /tmp/VERSION \ && echo "Building version: $(cat /tmp/VERSION)" -FROM python:3.12-slim-bookworm +FROM python:3.13-slim-bookworm WORKDIR /app @@ -55,6 +55,4 @@ COPY --from=metadata-stage /tmp/VERSION /app/VERSION COPY . /app/ -RUN chmod -R +x /app/bin - ENTRYPOINT ["python", "-m", "torappu"] diff --git a/README.md b/README.md index 526a4ef..9142e45 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ An unpacker for Arknights assets with a focus on resource extraction and analysi ## Requirements -- Python 3.12+ +- Python 3.13+ - Dependencies as specified in pyproject.toml ## Installation @@ -76,7 +76,6 @@ docker run -e TOKEN=your_token -v $(pwd)/storage:/app/storage torappu [CLIENT_VE - `core/`: Core functionality - `OpenArknightsFBS/`: FlatBuffer schema definitions - `assets/`: Asset resources -- `bin/`: Binary tools (includes flatc for FlatBuffer compilation) - `scripts/`: Utility scripts - `storage/`: Storage for extracted assets diff --git a/bin/flatc b/bin/flatc deleted file mode 100755 index e5af0a9..0000000 Binary files a/bin/flatc and /dev/null differ diff --git a/bin/flatc.exe b/bin/flatc.exe deleted file mode 100755 index c9f2172..0000000 Binary files a/bin/flatc.exe and /dev/null differ diff --git a/bin/macos/flatc b/bin/macos/flatc deleted file mode 100755 index 0a7356b..0000000 Binary files a/bin/macos/flatc and /dev/null differ diff --git a/pyproject.toml b/pyproject.toml index 76cea9a..6e4aed2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,7 @@ dev = ["ruff>=0.15.0,<1", "pre-commit>=4.0.0,<5"] [tool.ruff] line-length = 88 -target-version = "py312" +target-version = "py313" [tool.ruff.format] line-ending = "lf" @@ -75,7 +75,7 @@ keep-runtime-typing = true [tool.pyright] extraPaths = ["./"] -pythonVersion = "3.12" +pythonVersion = "3.13" pythonPlatform = "All" executionEnvironments = [{ root = "./" }] diff --git a/torappu/config.py b/torappu/config.py index a40e4b2..824f8fc 100644 --- a/torappu/config.py +++ b/torappu/config.py @@ -1,19 +1,7 @@ -from pathlib import Path from typing import Literal from pydantic_settings import BaseSettings, SettingsConfigDict -from torappu.consts import MACOS, WINDOWS - - -def get_flatc_path(): - if WINDOWS: - return Path("bin/flatc.exe") - elif MACOS: - return Path("bin/macos/flatc") - else: - return Path("bin/flatc") - class Config(BaseSettings): model_config = SettingsConfigDict( @@ -31,7 +19,6 @@ class Config(BaseSettings): timeout: int = 10 backend_endpoint: str | None = None - flatc_path: Path = get_flatc_path() sentry_dsn: str | None = None diff --git a/torappu/consts.py b/torappu/consts.py index 38853b8..e5c78ef 100644 --- a/torappu/consts.py +++ b/torappu/consts.py @@ -17,6 +17,8 @@ GAMEDATA_DIR = STORAGE_DIR / "asset" / "gamedata" HOT_UPDATE_LIST_DIR = STORAGE_DIR / "hot_update_list" +RESOURCE_MANIFEST_IDX_NAME = "resource_manifest_idx.json" + HEADERS = { "user-agent": "Dalvik/2.1.0 (Linux; U; Android 6.0.1; vivo X9L Build/MMB29M)" } diff --git a/torappu/core/client.py b/torappu/core/client.py index 266d63e..74d9984 100644 --- a/torappu/core/client.py +++ b/torappu/core/client.py @@ -1,15 +1,15 @@ import asyncio import json -import subprocess from hashlib import md5 from io import BytesIO from pathlib import Path -from tempfile import TemporaryDirectory from zipfile import ZipFile import anyio import httpx import UnityPy +from ark_fbs import Options as FBOptions +from ark_fbs import Schema as FBSchema from tenacity import retry, wait_random_exponential from UnityPy.classes import MonoBehaviour @@ -20,12 +20,19 @@ HG_CN_BASEURL, HOT_UPDATE_LIST_DIR, PRE_RESOLVE_PATHS, + RESOURCE_MANIFEST_IDX_NAME, STORAGE_DIR, ) from torappu.core.utils.path import hg_normalize_url from torappu.log import logger from torappu.models import ABInfo, Diff, HotUpdateInfo, Version +resource_manifest_schema: FBSchema = FBSchema.from_fbs_file( + "assets/ResourceManifest.fbs", + include_paths=["assets"], + options=FBOptions(), +) + class Client: def __init__( @@ -52,12 +59,7 @@ async def init(self): self.prev_hot_update_list = None if self.hot_update_list.manifest_name is not None: idx_path = await self.fetch_asset_bundle(self.hot_update_list.manifest_name) - self.load_idx( - idx_path, - GAMEDATA_DIR.joinpath( - self.version.res_version, self.hot_update_list.manifest_name - ), - ) + self.load_idx(idx_path, self.hot_update_list.manifest_name) else: await self.load_torappu_index() @@ -246,30 +248,25 @@ async def load_torappu_index(self): for item in torappu_index.assetToBundleList # type: ignore } - def load_idx(self, idx_path: str, decoded_path: Path): - tmp_dir = TemporaryDirectory() - tmp_path = Path(tmp_dir.name) + def load_idx(self, idx_path: str, manifest_name: str): idx = Path(idx_path).read_bytes() - flatbuffer_data_path = tmp_path / "idx.bin" - flatbuffer_data_path.write_bytes(idx[128:]) - params = [ - self.config.flatc_path, - "-o", - decoded_path.resolve(), - "--no-warnings", - "--json", - "--strict-json", - "--natural-utf8", - "--defaults-json", - "--raw-binary", - "assets/ResourceManifest.fbs", - "--", - flatbuffer_data_path, - ] - subprocess.run(params) - flatbuffer_data_path.unlink() - json_path = decoded_path / "idx.json" - jsons = json.loads(json_path.read_text(encoding="utf-8")) + flatbuffer_data = idx[128:] + decoded_path = GAMEDATA_DIR.joinpath( + self.version.res_version, RESOURCE_MANIFEST_IDX_NAME + ) + decoded_path.parent.mkdir(parents=True, exist_ok=True) + + try: + jsons = json.loads(resource_manifest_schema.binary_to_json(flatbuffer_data)) + decoded_path.write_text( + json.dumps(jsons, ensure_ascii=False, separators=(",", ":")), + encoding="utf-8", + ) + except Exception as exc: + raise RuntimeError( + f"failed to decode idx flatbuffer from {idx_path!r}" + ) from exc + self.asset_to_bundle = { item["assetName"]: jsons["bundles"][item["bundleIndex"]]["name"] for item in jsons["assetToBundleList"] diff --git a/torappu/core/tasks/gamedata.py b/torappu/core/tasks/gamedata.py index e051791..0a07abc 100644 --- a/torappu/core/tasks/gamedata.py +++ b/torappu/core/tasks/gamedata.py @@ -3,13 +3,13 @@ import json import os import platform -import subprocess from pathlib import Path -from tempfile import TemporaryDirectory from typing import ClassVar import bson import UnityPy +from ark_fbs import Options as FBOptions +from ark_fbs import Schema as FBSchema from Crypto.Cipher import AES from Crypto.Util.Padding import unpad from UnityPy.classes import TextAsset @@ -95,6 +95,7 @@ plaintexts = ["levels/levels_meta.json", "data_version.txt"] signed_list = ["excel", "_table", "[uc]lua"] chat_mask = "UITpAi82pHAWwnzqHRMCwPonJLIB3WCl" +flatbuffer_schema_cache: dict[str, FBSchema] = {} class Task(BaseTask): @@ -138,35 +139,33 @@ def _get_flatbuffer_output_name(self, relative_path: str, fb_name: str) -> str: return source_name return fb_name + def _get_flatbuffer_schema(self, fb_name: str) -> FBSchema: + schema = flatbuffer_schema_cache.get(fb_name) + if schema is not None: + return schema + + schema_path = FBS_DIR / f"{fb_name}.fbs" + schema = FBSchema.from_fbs_file( + str(schema_path), + include_paths=[str(FBS_DIR)], + options=FBOptions(), + ) + flatbuffer_schema_cache[fb_name] = schema + return schema + @run_sync def _decode_flatbuffer(self, path: str, obj: TextAsset, fb_name: str): - tmp_dir = TemporaryDirectory() - tmp_path = Path(tmp_dir.name) relative_path = path.replace("dyn/gamedata/", "") output_name = self._get_flatbuffer_output_name(relative_path, fb_name) + flatbuffer_data = m_script_to_bytes(obj.m_Script)[128:] + try: + schema = self._get_flatbuffer_schema(fb_name) + jsons = json.loads(schema.binary_to_json(flatbuffer_data)) + except Exception as exc: + raise RuntimeError( + f"failed to decode flatbuffer {fb_name!r} for asset {path!r}" + ) from exc - flatbuffer_data_path = tmp_path.joinpath(f"{output_name}.bytes") - output_path = tmp_path.joinpath(os.path.dirname(relative_path)) - flatbuffer_data_path.write_bytes(m_script_to_bytes(obj.m_Script)[128:]) - - params = [ - self.client.config.flatc_path, - "-o", - output_path.resolve(), - "--no-warnings", - "--json", - "--strict-json", - "--natural-utf8", - "--defaults-json", - "--raw-binary", - f"{FBS_DIR}/{fb_name}.fbs", - "--", - flatbuffer_data_path.resolve(), - ] - subprocess.run(params) - flatbuffer_data_path.unlink() - json_path = output_path / f"{output_name}.json" - jsons = json.loads(json_path.read_text(encoding="utf-8")) if fb_name == "activity_table": for k, v in jsons["dynActs"].items(): if "base64" in v: @@ -190,8 +189,6 @@ def _decode_flatbuffer(self, path: str, obj: TextAsset, fb_name: str): encoding="utf-8", ) - tmp_dir.cleanup() - @run_sync def _decrypt(self, path: str, obj: TextAsset, is_signed: bool): key: bytes = chat_mask[:16].encode() diff --git a/uv.lock b/uv.lock index d73f060..79dbd6b 100644 --- a/uv.lock +++ b/uv.lock @@ -34,31 +34,31 @@ wheels = [ [[package]] name = "ark-fbs" -version = "0.1.2" +version = "0.1.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/28/57be5dc1b30d85424395d8f1b84a3e67e34efd6375e656149becc8d0cdf9/ark_fbs-0.1.2.tar.gz", hash = "sha256:da259598f66b2848fba42f822b0220875668e6e699ebbba8b09d7f584cd290a8", size = 2613937, upload-time = "2026-03-12T05:36:54.603Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/4a/7be8b73d37863c58a9d681ac40b9283d63a2cff94bc5cfcecc0a6ff292a6/ark_fbs-0.1.5.tar.gz", hash = "sha256:8ae2dfcb3522ac306125c511a95fd508b2541d29bca2dc2e805579655e754521", size = 2616331, upload-time = "2026-03-12T15:53:46.611Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/09/6b661e8bdeeee9953730516fb5171f98a270e8b3f8f239c6e8c20a7b8e27/ark_fbs-0.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6510df7c4f0770a596deb2e45b9c94dcefa413f8fc8ad9a013931663cbc265d7", size = 320788, upload-time = "2026-03-12T05:36:26.289Z" }, - { url = "https://files.pythonhosted.org/packages/2e/53/7b09197229e68ee8033c9de3a598778871cb95e8ac7a3e814112768eaba9/ark_fbs-0.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d902b08854ceea0a2c09da07f6285cd8ba97c9b0df08f0b4f8362e8e969aba98", size = 291790, upload-time = "2026-03-12T05:36:27.453Z" }, - { url = "https://files.pythonhosted.org/packages/45/10/0b55bb9f344a482c3659cb821212a5d14bd578e8bb01f7fe199271165240/ark_fbs-0.1.2-cp313-cp313-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:f27df5c24013307cceba25c02a892c6aa0820ed389219b3cebc6c669d83eb8d2", size = 370515, upload-time = "2026-03-12T05:36:29.37Z" }, - { url = "https://files.pythonhosted.org/packages/a9/b4/76ab97590274e49eeed754edde871990939dcd82bd7d81906c9b8f39bc80/ark_fbs-0.1.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eba2b6e7f8949de6b716d1115d628944fb44c95773a7204b7b107e83f7e4005e", size = 338421, upload-time = "2026-03-12T05:36:31.119Z" }, - { url = "https://files.pythonhosted.org/packages/a0/ff/051ad6aa9e126277b2ace7490ff36327440ac426f92757a959cc2ec30bb5/ark_fbs-0.1.2-cp313-cp313-win32.whl", hash = "sha256:e2e46687bd201d7af27cd7a95ec7592a628b2ff9230a936d39fc3be713242da1", size = 268298, upload-time = "2026-03-12T05:36:32.631Z" }, - { url = "https://files.pythonhosted.org/packages/1d/2e/a4724c2bdb50332b4ee059ad0d92e688e9dd80ab8fef9a1bb2a060f81e3a/ark_fbs-0.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:645fa9a777d9dad497181286745487ff1241af708103a7a78ac82e33bab10bf9", size = 302354, upload-time = "2026-03-12T05:36:33.892Z" }, - { url = "https://files.pythonhosted.org/packages/46/92/94755be69991e0a7cb5674479a59a15d62cc19e4d41645ac448ec3bbc0d2/ark_fbs-0.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:53076a8658eeae214c7fad83bea171eb8b7b1b0fcda52f41a36c64d238a41c0c", size = 284932, upload-time = "2026-03-12T05:36:35.058Z" }, - { url = "https://files.pythonhosted.org/packages/5b/7d/5be3ecdee5926e4d9d684d198cb6b5ddd0f8ae52fd85f65d823d73dc9250/ark_fbs-0.1.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:8b32d14e2966b9c5a7d33f5070538048bcfa30db4c53e55a68051bdef4651df1", size = 321184, upload-time = "2026-03-12T05:36:36.433Z" }, - { url = "https://files.pythonhosted.org/packages/76/b5/b7f789905ad0869d7fe8e91c41d73140c17bc1fa48c5aea36027448363f2/ark_fbs-0.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e5f1d1ead4a4be7ecf63a1d79fd8c06eb021ff4cb59b8a90a18ba50943b7431", size = 292056, upload-time = "2026-03-12T05:36:37.654Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e4/3fdf56b8dc6859e94179af22590139bbf1646ceca06411583624233b62dd/ark_fbs-0.1.2-cp314-cp314-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:0320b3bc12454130a31ab6651e39836037938acdf63a98bdecd1dbc352c985b5", size = 370449, upload-time = "2026-03-12T05:36:38.818Z" }, - { url = "https://files.pythonhosted.org/packages/29/f6/2920f01fbede1e94b856e44402721492954cce9049c03228f8a6c33bfc59/ark_fbs-0.1.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4783c349f8717e27f7c3de9acbf357af2befaa952b8d669a6c3996c4a493ed13", size = 338646, upload-time = "2026-03-12T05:36:40.357Z" }, - { url = "https://files.pythonhosted.org/packages/83/1d/476f6b2ec9473763f0099ff0c37a9392728512cc888c8861bfd8d04a85d1/ark_fbs-0.1.2-cp314-cp314-win32.whl", hash = "sha256:f68cd400e4e0b8c9b0e3cf3acf9c679e853c24faf6b93a694ad9999daf12f529", size = 275174, upload-time = "2026-03-12T05:36:41.559Z" }, - { url = "https://files.pythonhosted.org/packages/fe/88/115eb7ee91cfb089b016b53fcc5db93c1f3dd634f0b82ec88ee6a3f7de1c/ark_fbs-0.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:b6a1e6e1e625254456d876492864cc571125e24de83cb1b7f7d85398dbb9e019", size = 311513, upload-time = "2026-03-12T05:36:43.062Z" }, - { url = "https://files.pythonhosted.org/packages/3c/7a/9a64bf2489e89d5e0192d2de0d5e775c23c5e8e29609303487e06dd3db0b/ark_fbs-0.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:615383da80a3774ef39536a073184c6e4a0b40cc4edbac0c07a816bf7d04d6c3", size = 293933, upload-time = "2026-03-12T05:36:44.544Z" }, - { url = "https://files.pythonhosted.org/packages/38/77/250ec02422a46daf561a3d7525baca3382c65aaea8f0848eb011b089a555/ark_fbs-0.1.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:16b8e0bd131faca504cb1c88fa678a08ea91ea7e7b1cc2df96a2688a32387e54", size = 326442, upload-time = "2026-03-12T05:36:45.698Z" }, - { url = "https://files.pythonhosted.org/packages/ac/13/7f79a6eda7b9d247e008dec6b19ff5cfb06deca367d3917a9ff73bda3aba/ark_fbs-0.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:83ae32736b5ab696c547940c673371c085bafd7c195e006c16eb854635a98031", size = 298655, upload-time = "2026-03-12T05:36:46.822Z" }, - { url = "https://files.pythonhosted.org/packages/05/45/1796e7f4a5ec006187540cab372ae3be2d1f7cba6784eb70d56ea3c61a28/ark_fbs-0.1.2-cp314-cp314t-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:c4d3e0173deff99db7d00d9128428adac82d84fab9cea0b318befb2dde47c6bc", size = 371793, upload-time = "2026-03-12T05:36:47.95Z" }, - { url = "https://files.pythonhosted.org/packages/20/b1/430bd110de30e11c3656f0c136f6f2979a113dc37248b2a87c7429923125/ark_fbs-0.1.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ff4c8e2c1cea7d8b261b55eaa96e48877a110202ae10da8150c31975a6d263ab", size = 340101, upload-time = "2026-03-12T05:36:49.195Z" }, - { url = "https://files.pythonhosted.org/packages/5e/eb/138bf41f1f7c0ef70cffaa978d04acf3296018dfa702a32515e80144b9b4/ark_fbs-0.1.2-cp314-cp314t-win32.whl", hash = "sha256:248c893273460a2d9f97bd3e12fbd39055067124093c64f3cfad105c16b7d1ed", size = 282556, upload-time = "2026-03-12T05:36:50.407Z" }, - { url = "https://files.pythonhosted.org/packages/8b/80/9d26da1ca06df25af3725d0711090ec283735ea62284321f83a899c666d0/ark_fbs-0.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:b022a3397d476a1a380519481f9875fe1322986035eda1c37020c8bce7256e2b", size = 320722, upload-time = "2026-03-12T05:36:51.812Z" }, - { url = "https://files.pythonhosted.org/packages/b8/38/aaca774ced850ac2e7fdf0d42fa8706f7337486e54c50a6901c4202cf243/ark_fbs-0.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1c26d7087b103ac0bd644df4a33b74960af74220db196302610c306e4b517eba", size = 298825, upload-time = "2026-03-12T05:36:52.963Z" }, + { url = "https://files.pythonhosted.org/packages/23/7c/1c51aa35c1eb04a1dd6f68e722cb4ce496dad42d388035872008eed1331a/ark_fbs-0.1.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e8af789ea3a9609a260393ff77389fe7189d09963eefdb6b91123c7224be0abc", size = 324664, upload-time = "2026-03-12T15:53:14.282Z" }, + { url = "https://files.pythonhosted.org/packages/11/90/a8c44feab0992ef021f9bbf3a95263b17c23f60523278897b911f31dd68d/ark_fbs-0.1.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a4784fdf02f135365b3c79832a0b487f3d000050d0391bc80d2b4ccb2a59a67f", size = 295236, upload-time = "2026-03-12T15:53:15.676Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b2/d98f8996dc82e29661ccb659947b9f96ef8034a96e35cb486c0c699da494/ark_fbs-0.1.5-cp313-cp313-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:38df038fd5c4ee568ee0978736b5dcdd7b7fc4a384bcecb57f074462149911a0", size = 373968, upload-time = "2026-03-12T15:53:17.304Z" }, + { url = "https://files.pythonhosted.org/packages/22/28/9b9d91de643445cda0a077556d71e76f7669349617d1f66bf9a16423b411/ark_fbs-0.1.5-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e4e64176c0f8d778cde2265d2e1ea3e71fee2fee994b8e2247a657a9bd688155", size = 342140, upload-time = "2026-03-12T15:53:19.07Z" }, + { url = "https://files.pythonhosted.org/packages/74/0d/f9da5df19ed553696abe003c678f07fe389853e131de37b20166740f25f1/ark_fbs-0.1.5-cp313-cp313-win32.whl", hash = "sha256:83160c7cf73d5c455d48f2a5d848314c4e574571263b4d1520abf77ed5c4c653", size = 270622, upload-time = "2026-03-12T15:53:20.337Z" }, + { url = "https://files.pythonhosted.org/packages/9f/9e/8a0fed882e39e9c13d54781b6721a9ac1368715e5f014edd29cd25612027/ark_fbs-0.1.5-cp313-cp313-win_amd64.whl", hash = "sha256:429c1f8ff86be801004e10a71b51834c8a8d278c22c33bf5da0c5b602f4b45e4", size = 304864, upload-time = "2026-03-12T15:53:21.956Z" }, + { url = "https://files.pythonhosted.org/packages/b1/11/98768994f3c7b5598c427d1f0cc78d389ee2a0e78269ad938ac21edf6005/ark_fbs-0.1.5-cp313-cp313-win_arm64.whl", hash = "sha256:8af9a54ff7da73706e14f6b8b13a284d5c78c557e3c4688b000bde2c9f474456", size = 287282, upload-time = "2026-03-12T15:53:23.317Z" }, + { url = "https://files.pythonhosted.org/packages/ce/5e/279668f7d93bed1a282156b7d1ccf61386ef83e05c5cdda648cfee45cccd/ark_fbs-0.1.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:886f7501541eebc2f774c8ff94807a09aa712a8031f1f82ab653cbd23442fbf4", size = 325133, upload-time = "2026-03-12T15:53:25.022Z" }, + { url = "https://files.pythonhosted.org/packages/fd/27/d5a6c97e0c8e752db6ef632451d8568facdd8c0968395b898c3926fbba28/ark_fbs-0.1.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:273259edca9bd320cd0480ff034cdba9df469242ab0610c06ac398184815139f", size = 295571, upload-time = "2026-03-12T15:53:26.338Z" }, + { url = "https://files.pythonhosted.org/packages/5e/db/b7d8674bfef4a0581c175d0842ac197e7e608d1b4e1565b1fc5b777dbfa8/ark_fbs-0.1.5-cp314-cp314-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:26fa2904b5a2e89966e4c3274a4d11e8f0a2037ce3cc588db7c79622e80d8f0b", size = 373795, upload-time = "2026-03-12T15:53:27.959Z" }, + { url = "https://files.pythonhosted.org/packages/fa/1e/c7ea2d7b2b19964394b1b181bada0450ca3e333cde9923b4f774db45f2ec/ark_fbs-0.1.5-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b96042e908d2794e1c9f8736fcbd8f4f33ac84da4233231eebd4a1a5034a89b3", size = 342432, upload-time = "2026-03-12T15:53:29.868Z" }, + { url = "https://files.pythonhosted.org/packages/7c/a2/76026248ff71ad0f9a6415aec4bf20ca74f9729b88f4d2641728e511cc0b/ark_fbs-0.1.5-cp314-cp314-win32.whl", hash = "sha256:aab972108ee96de58fc4ca7e571472509e8050066b6c9f9f277ba54325aa03f4", size = 277633, upload-time = "2026-03-12T15:53:31.273Z" }, + { url = "https://files.pythonhosted.org/packages/71/98/a39e28be24a8ad056ff6b1f8af0cc9d53998b9e18efc986f97cb94583841/ark_fbs-0.1.5-cp314-cp314-win_amd64.whl", hash = "sha256:70ef28e7c2649a06db441f08c770335e9fd0d1a900fc337d46701cb37dfa8f64", size = 314169, upload-time = "2026-03-12T15:53:32.698Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/efc926386ed233a0260676d2e901d10f9258f643a26bf1246820ce082fec/ark_fbs-0.1.5-cp314-cp314-win_arm64.whl", hash = "sha256:0b961edaa19adc106f70eb835011d90db51b3d61be1b1fbcea596f9a803d47eb", size = 296261, upload-time = "2026-03-12T15:53:34.214Z" }, + { url = "https://files.pythonhosted.org/packages/69/b9/ffdf885e690ef40b809e5d0e1e74690cb2ca9236c67fb2b66c827b78f24d/ark_fbs-0.1.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:3b7dc0c14d91a0dc89a33e226aeaa7843ca65225a43e96ece084a2ab2d35bd49", size = 330254, upload-time = "2026-03-12T15:53:35.597Z" }, + { url = "https://files.pythonhosted.org/packages/56/6b/a5bb025d1aa323c010b2772f8e833a43786e654527846b4051a6870d27b4/ark_fbs-0.1.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1c9a905aa083d9d3f06ec71d22fc5e14b393faf34f4168429d827d5d9f59ce4e", size = 302391, upload-time = "2026-03-12T15:53:37.004Z" }, + { url = "https://files.pythonhosted.org/packages/f2/98/4f59fb037e983936a14ba3c2f00e3f6bc69b372691bc839dfda7538c37ae/ark_fbs-0.1.5-cp314-cp314t-manylinux_2_24_i686.manylinux_2_28_i686.whl", hash = "sha256:5de7b4576bb8f84f1906d8c386fbdaf6ed7338781b9f3a4ecc3dbd1f06cb1a26", size = 375619, upload-time = "2026-03-12T15:53:38.804Z" }, + { url = "https://files.pythonhosted.org/packages/c1/a6/ec3c3d08128047dbbf5e9754bc8c507cc8b75a5bca9a96691ee0450bcafa/ark_fbs-0.1.5-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4dba55e17ae69d4e2a33d1167f9142b46d382ceece4174a976a499561f0ece2e", size = 343978, upload-time = "2026-03-12T15:53:40.196Z" }, + { url = "https://files.pythonhosted.org/packages/73/d2/70679defe5dddd19fd8df7f558a08e708f2c13340aae58ed833de6bbc25e/ark_fbs-0.1.5-cp314-cp314t-win32.whl", hash = "sha256:6fa1f889e9326065e19a2f29d9b326193e6bed882c1b0b872db92164aea5ae78", size = 284931, upload-time = "2026-03-12T15:53:41.573Z" }, + { url = "https://files.pythonhosted.org/packages/45/8a/07a211d60056a5f055046b37cecf7613dedec85696a0b707fb1c9945014f/ark_fbs-0.1.5-cp314-cp314t-win_amd64.whl", hash = "sha256:ffe013921243c7283e8f713de0b38f2bf16463806327423570550a647cc15bfe", size = 322982, upload-time = "2026-03-12T15:53:43.404Z" }, + { url = "https://files.pythonhosted.org/packages/99/22/a813112d5b746b7257982f035675cd78aa0f58f2fcd48f4c479213274a4a/ark_fbs-0.1.5-cp314-cp314t-win_arm64.whl", hash = "sha256:0150233014d2608f13356c7bc523b6e3b1929597e41dbdb48967867bd498f7c2", size = 301202, upload-time = "2026-03-12T15:53:45.082Z" }, ] [[package]]