nnnotes 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nnnotes-0.1.0/src/nnnotes.egg-info → nnnotes-0.1.2}/PKG-INFO +34 -12
- {nnnotes-0.1.0 → nnnotes-0.1.2}/README.en.md +32 -11
- {nnnotes-0.1.0 → nnnotes-0.1.2}/README.md +22 -10
- {nnnotes-0.1.0 → nnnotes-0.1.2}/pyproject.toml +2 -1
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/__init__.py +1 -1
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/advscene.py +3 -15
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/cli.py +221 -16
- nnnotes-0.1.2/src/nnnotes/compress.py +91 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/config.py +73 -14
- nnnotes-0.1.2/src/nnnotes/configfile.py +344 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/export.py +0 -5
- nnnotes-0.1.2/src/nnnotes/nnnotes.example.toml +72 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/room.py +75 -40
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/story.py +21 -36
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/storyhost.py +51 -40
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/storysite.py +102 -31
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/unity.py +12 -7
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/web.py +113 -48
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/webmodel.py +199 -35
- {nnnotes-0.1.0 → nnnotes-0.1.2/src/nnnotes.egg-info}/PKG-INFO +34 -12
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes.egg-info/SOURCES.txt +5 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes.egg-info/requires.txt +1 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_cli.py +2 -1
- nnnotes-0.1.2/tests/test_compress.py +67 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_config.py +30 -0
- nnnotes-0.1.2/tests/test_configfile.py +203 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_crilips.py +5 -4
- nnnotes-0.1.2/tests/test_room.py +392 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_speed_exact.py +13 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_story_host.py +54 -16
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_story_site.py +170 -17
- nnnotes-0.1.2/tests/test_web.py +277 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_webmodel.py +296 -27
- nnnotes-0.1.0/tests/test_room.py +0 -204
- nnnotes-0.1.0/tests/test_web.py +0 -134
- {nnnotes-0.1.0 → nnnotes-0.1.2}/LICENSE +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/setup.cfg +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/__main__.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/acb.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/addressables.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/adv.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/advcommand.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/advmedia.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/advui.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/advvideo.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/atoms/__init__.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/atoms/astc.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/atoms/gltf.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/atoms/mesh.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/atoms/png.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/atoms/reader.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/atoms/sniff.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/atoms/sprite.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/atoms/texture.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/cache.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/catalog.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/catalogdb.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/census.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/cli_assets.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/contract.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/cri.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/crikey.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/crilips.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/cristages.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/deckdata.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/gameapi.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/jsonio.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/languages.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/layout.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/link.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/live.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/live2d.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/liveaudio.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/livenotes.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/liveoptions.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/livescene.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/liveui.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/master.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/modelstages.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/motion.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/objexport.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/orchestrate.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/plan.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/player.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/score.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/shader.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/spot.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/stages.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/store.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/storyfonts.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/storyvoices.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/textstyle.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/tmpfont.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/typetrees/6000.3.12f1.json +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/viewrules.json +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/views.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/voices.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes/webaudio.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes.egg-info/dependency_links.txt +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes.egg-info/entry_points.txt +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/src/nnnotes.egg-info/top_level.txt +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_advvideo.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_atom_eval.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_atoms_mesh.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_atoms_sprite.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_atoms_texture.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_blob_memo.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_cache.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_catalog.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_catalog_lazy.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_catalogdb.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_census.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_cli_assets.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_contract.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_cristages.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_deckdata.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_gameapi.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_goldens.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_jsonio.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_layout.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_link.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_live_options.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_live_options_web.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_master.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_mesh_texture_guards.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_modelstages.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_objexport.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_orchestrate.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_pipeline.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_plan.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_player.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_readsets.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_regions.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_repo.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_sniff.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_spot.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_stages.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_store.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_story_closure.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_story_fonts.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_story_kinds.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_story_ui.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_textstyle.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_typetrees.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_views.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_voices.py +0 -0
- {nnnotes-0.1.0 → nnnotes-0.1.2}/tests/test_voices_story.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nnnotes
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Offline data toolkit for BanG Dream! Our Notes game files: catalogs, bundles, master data, stories, Live2D, audio and live charts
|
|
5
5
|
Author: MetaMiku, emptysekai
|
|
6
6
|
License-Expression: MIT
|
|
@@ -18,6 +18,7 @@ Requires-Dist: numpy>=2.0
|
|
|
18
18
|
Requires-Dist: Pillow>=10.0
|
|
19
19
|
Requires-Dist: pycryptodome>=3.20
|
|
20
20
|
Requires-Dist: grpcio>=1.49.1
|
|
21
|
+
Requires-Dist: brotli>=1.1
|
|
21
22
|
Provides-Extra: test
|
|
22
23
|
Requires-Dist: pytest>=8; extra == "test"
|
|
23
24
|
Requires-Dist: pyflakes>=3.2; extra == "test"
|
|
@@ -34,13 +35,29 @@ Dynamic: license-file
|
|
|
34
35
|
|
|
35
36
|
nnnotes is an offline data toolkit for the game files of BanG Dream! Our Notes: it reads Addressables catalogs,
|
|
36
37
|
downloads and decrypts asset bundles, downloads and decodes master data, and exports stories, Live2D models, spots,
|
|
37
|
-
shaders, CRI audio and live charts as structured JSON and common file formats. The
|
|
38
|
-
[ournotes-player](https://github.com/empty-sekai/ournotes-player) reads. The naming was inspired by [mos9527/sssekai](https://github.com/mos9527/sssekai).
|
|
38
|
+
shaders, CRI audio and live charts as structured JSON and common file formats. The charts, Live2D models and stories `web`
|
|
39
|
+
exports are the data that [ournotes-player](https://github.com/empty-sekai/ournotes-player) reads. The naming was inspired by [mos9527/sssekai](https://github.com/mos9527/sssekai).
|
|
39
40
|
|
|
40
41
|
This is an unofficial fan project, not affiliated with the game's developer or operator. The repository contains no
|
|
41
42
|
game assets, keys or server addresses: users supply the game files, the keys needed for decryption and the server
|
|
42
43
|
addresses in their own configuration, and the exports stay in local directories the user chooses.
|
|
43
44
|
|
|
45
|
+
## Install and set up
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
pip install nnnotes # Python 3.11+, 3.13 recommended
|
|
49
|
+
nnnotes config init --user # asks for each setting in the terminal (keys without echo), writes the per-user config file read from any directory
|
|
50
|
+
nnnotes config check # each setting's origin and whether it is valid, without its value
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- The values (keys, the CDN and API roots of the regions, the paths of your own data and tools) come from the game
|
|
54
|
+
client you own; see Configuration below.
|
|
55
|
+
- Scripts and AI agents get no questions: `nnnotes config init --user --no-input --set SECTION.KEY=VALUE ...` writes
|
|
56
|
+
the file at once, `nnnotes config set SECTION.KEY VALUE` changes one value (VALUE `-` reads it from standard input,
|
|
57
|
+
so a key need not appear on the command line), and `nnnotes config check --json` reports the states for machines.
|
|
58
|
+
- `--fonts game` also needs `pip install 'nnnotes[fonts]'`; the APK, the master data and the external tools are listed
|
|
59
|
+
under Requirements below.
|
|
60
|
+
|
|
44
61
|
## Features
|
|
45
62
|
|
|
46
63
|
| Command | Input | Output |
|
|
@@ -53,7 +70,7 @@ addresses in their own configuration, and the exports stay in local directories
|
|
|
53
70
|
| `master download` | master data version, or `--latest` (the region's current one) | that version's `MasterManifest.json` and every `.bin` file (SHA-256 checked) |
|
|
54
71
|
| `master decode` | master data `.bin` files or directories | one JSON per table (Rijndael-256 CBC decryption + gzip) |
|
|
55
72
|
| `adv` | episode ID | `episode.json`: command list, lines in five languages, voice / sound / video index |
|
|
56
|
-
| `story` | episode ID | a
|
|
73
|
+
| `story` | episode ID | a story directory: episode, audio, stage scene and shaders, story UI, frames / particle effects / post effects / stills / talk windows / chat assets, videos (WebM); the Live2D models it uses are exported into a models directory (`--models`, default `live2d/` next to the story directory), where other stories reuse them |
|
|
57
74
|
| `live2d` | model key or model id | a Live2D (Cubism) runtime directory: moc3, textures, motion3, physics, expressions, prefab parameters |
|
|
58
75
|
| `spot` | spot ID | `spot.json` + Spine characters + the room as `room.glb` + shaders |
|
|
59
76
|
| `room` | background prefab key | the room model (binary glTF) |
|
|
@@ -62,7 +79,7 @@ addresses in their own configuration, and the exports stay in local directories
|
|
|
62
79
|
| `crikey` | APK | the CRI HCA keycode from the game's boot data (shows whether it was found; can write a `.hcakey`) |
|
|
63
80
|
| `player` | APK | render-related global settings (color space, quality levels, renderers) as JSON |
|
|
64
81
|
| `live` | music ID + difficulty | a full chart directory: chart and runtime notes, 3D scene, note and effect assets, BGM and sounds, sound routing |
|
|
65
|
-
| `web` | `--pair music:difficulty` (repeatable) or `--all`; `--live2d model` (repeatable) or `--all-live2d`; `--story episode` (repeatable) or `--all-stories`; `--region region` (repeatable) or `--all-regions` | an ournotes-player static site: shared player + per-chart / per-model / per-episode manifests + content-addressed assets; one site can serve several regions, with listing texts in five languages; a story's interface texts are grouped by language, with TextMeshPro font assets generated from open fonts (the game's fonts with `--fonts game`) |
|
|
82
|
+
| `web` | `--pair music:difficulty` (repeatable) or `--all`; `--live2d model` (repeatable) or `--all-live2d`; `--story episode` (repeatable) or `--all-stories`; `--region region` (repeatable) or `--all-regions` | an ournotes-player static site: shared player + per-chart / per-model / per-episode manifests + content-addressed assets; the Live2D models of the stories are built first as models, listed in `models.json`, and the story manifests reference them; one site can serve several regions, with listing texts in five languages; a story's interface texts are grouped by language, with TextMeshPro font assets generated from open fonts (the game's fonts with `--fonts game`); compressible assets (JSON, shaders, moc3, ...) are stored gzip encoded by default (`--compress br` / `none`) |
|
|
66
83
|
| `deck-data` | master data files (`--master-files` directory or `--apk-master`) | one JSON file for deck-building tools: every chart's runtime notes, skill events and fever ranges, and the master data tables about cards, skills, bonuses, scores and events ([format](https://github.com/MetaSekaiLab/nnnotes/blob/main/docs/deck-data.md)) |
|
|
67
84
|
|
|
68
85
|
Export conventions:
|
|
@@ -88,18 +105,18 @@ Based on all data of the Taiwan server, version 1.0.1 (zh-Hant):
|
|
|
88
105
|
| catalog / bundle decryption / dependency closure | working |
|
|
89
106
|
| master data decoding | working |
|
|
90
107
|
| stories, `adv` | 946 / 946 episodes |
|
|
91
|
-
| stories, `story` (full directory) | 946 / 946 episodes have all their resources in the catalog and of supported kinds (the resource closure equals the game's own per-episode download list);
|
|
108
|
+
| stories, `story` (full directory) | 946 / 946 episodes have all their resources in the catalog and of supported kinds (the resource closure equals the game's own per-episode download list); 946 / 946 exported and checked one by one: the files, models and audio the output refers to exist, every JSON parses and every path it refers to resolves; the 235 episodes with frames, effects, post effects, stills, chats, talk windows or videos are byte-identical across two exports except for the models directory path |
|
|
92
109
|
| Live2D models | 239 / 239 (every model of the catalog; episodes use 185 of them) |
|
|
93
110
|
| CRI audio | 681 / 681 cue sheets |
|
|
94
111
|
| charts, `live` | 336 / 336 (music, difficulty) pairs |
|
|
95
|
-
| web site, `web` | 336 / 336 charts, 239 / 239 Live2D models, 946 / 946 story episodes (two full builds in English without audio are byte-identical and 946 / 946 pass the data validation;
|
|
96
|
-
| spots, `spot` / `room` |
|
|
97
|
-
| other regions (en / kr) and languages | checked: the regions serve the same catalog for a language and the same bundles, and the keys are shared; the master tables the charts use are the same in the three regions, and the text tables have all five languages; the chart exports checked match the Taiwan server's. A full multi-region site build is
|
|
112
|
+
| web site, `web` | 336 / 336 charts, 239 / 239 Live2D models, 946 / 946 story episodes (two full builds in English without audio are byte-identical and 946 / 946 pass the data validation); two full builds of the three regions in five languages with AAC audio (`--all-regions`, with the live master data: 340 charts of 85 musics) are likewise byte-identical and pass the data validation in full |
|
|
113
|
+
| spots, `spot` / `room` | 39 / 39 spots, every reference resolves; `room` exports 20 / 20 room backgrounds of the catalog. Every glb passes the Khronos glTF-Validator with 0 errors |
|
|
114
|
+
| other regions (en / kr) and languages | checked: the regions serve the same catalog for a language and the same bundles, and the keys are shared; the master tables the charts use are the same in the three regions, and the text tables have all five languages; the chart exports checked match the Taiwan server's. A full multi-region site build is verified (the row above) |
|
|
98
115
|
|
|
99
116
|
## Requirements
|
|
100
117
|
|
|
101
118
|
- Python 3.11+, 3.13 recommended (a `web` build reads and writes a lot of JSON, and the standard library encodes
|
|
102
|
-
JSON faster on 3.13);
|
|
119
|
+
JSON faster on 3.13); for development `pip install -e .` in the repository
|
|
103
120
|
- the game's `base.apk`: APK-local bundles, the CRI keycode, boot settings. `player`, `story`, `live` and `web` read
|
|
104
121
|
MonoBehaviours of its boot data with type trees that ship with nnnotes; supported now: game version 1.0.1
|
|
105
122
|
(Unity 6000.3.12f1). With an APK of another version whose classes do not match, these commands stop with an
|
|
@@ -115,12 +132,17 @@ Based on all data of the Taiwan server, version 1.0.1 (zh-Hant):
|
|
|
115
132
|
The code contains no keys, server addresses or default paths. Settings are read in this order, later sources
|
|
116
133
|
overriding earlier ones:
|
|
117
134
|
|
|
118
|
-
1. config file: `--config <file>`, else `NNNOTES_CONFIG`, else `nnnotes.toml` in the working directory
|
|
135
|
+
1. config file: `--config <file>`, else `NNNOTES_CONFIG`, else `nnnotes.toml` in the working directory, else the
|
|
136
|
+
per-user file (`%APPDATA%\nnnotes\nnnotes.toml` on Windows, `~/.config/nnnotes/nnnotes.toml` elsewhere;
|
|
137
|
+
`nnnotes config path` lists the lookup order and the file read)
|
|
119
138
|
2. environment variables: `NNNOTES_<SECTION>_<KEY>` (e.g. `NNNOTES_BUNDLE_KEY`, `NNNOTES_SERVERS_TW_CDN`)
|
|
120
139
|
3. command-line flags: `--region`, `--language`, `--catalog`, `--cache`, `--master`, `--apk`, `--ffmpeg`,
|
|
121
140
|
`--vgmstream`, `--node` go before the command name; `--player` is an option of `web`
|
|
122
141
|
|
|
123
|
-
|
|
142
|
+
`nnnotes config init` writes the config template (every setting empty; it ships with the package, in the repository
|
|
143
|
+
[`src/nnnotes/nnnotes.example.toml`](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/nnnotes.example.toml)),
|
|
144
|
+
asking for each value in a terminal; `config set` / `config unset` change one value and keep the file's comments.
|
|
145
|
+
The settings are: the bundle
|
|
124
146
|
key and nonce seed, the master data key and IV, the region in use (`[catalog] region`) and the catalog language
|
|
125
147
|
(`[catalog] language`), the CDN base and API root of each region (`cdn`, `api` of `[servers.<region>]`), the client
|
|
126
148
|
version (`[client] version`; unset: the APK's versionName), optionally the bootstrap API root (`[bootstrap] api`, for
|
|
@@ -4,13 +4,29 @@
|
|
|
4
4
|
|
|
5
5
|
nnnotes is an offline data toolkit for the game files of BanG Dream! Our Notes: it reads Addressables catalogs,
|
|
6
6
|
downloads and decrypts asset bundles, downloads and decodes master data, and exports stories, Live2D models, spots,
|
|
7
|
-
shaders, CRI audio and live charts as structured JSON and common file formats. The
|
|
8
|
-
[ournotes-player](https://github.com/empty-sekai/ournotes-player) reads. The naming was inspired by [mos9527/sssekai](https://github.com/mos9527/sssekai).
|
|
7
|
+
shaders, CRI audio and live charts as structured JSON and common file formats. The charts, Live2D models and stories `web`
|
|
8
|
+
exports are the data that [ournotes-player](https://github.com/empty-sekai/ournotes-player) reads. The naming was inspired by [mos9527/sssekai](https://github.com/mos9527/sssekai).
|
|
9
9
|
|
|
10
10
|
This is an unofficial fan project, not affiliated with the game's developer or operator. The repository contains no
|
|
11
11
|
game assets, keys or server addresses: users supply the game files, the keys needed for decryption and the server
|
|
12
12
|
addresses in their own configuration, and the exports stay in local directories the user chooses.
|
|
13
13
|
|
|
14
|
+
## Install and set up
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
pip install nnnotes # Python 3.11+, 3.13 recommended
|
|
18
|
+
nnnotes config init --user # asks for each setting in the terminal (keys without echo), writes the per-user config file read from any directory
|
|
19
|
+
nnnotes config check # each setting's origin and whether it is valid, without its value
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- The values (keys, the CDN and API roots of the regions, the paths of your own data and tools) come from the game
|
|
23
|
+
client you own; see Configuration below.
|
|
24
|
+
- Scripts and AI agents get no questions: `nnnotes config init --user --no-input --set SECTION.KEY=VALUE ...` writes
|
|
25
|
+
the file at once, `nnnotes config set SECTION.KEY VALUE` changes one value (VALUE `-` reads it from standard input,
|
|
26
|
+
so a key need not appear on the command line), and `nnnotes config check --json` reports the states for machines.
|
|
27
|
+
- `--fonts game` also needs `pip install 'nnnotes[fonts]'`; the APK, the master data and the external tools are listed
|
|
28
|
+
under Requirements below.
|
|
29
|
+
|
|
14
30
|
## Features
|
|
15
31
|
|
|
16
32
|
| Command | Input | Output |
|
|
@@ -23,7 +39,7 @@ addresses in their own configuration, and the exports stay in local directories
|
|
|
23
39
|
| `master download` | master data version, or `--latest` (the region's current one) | that version's `MasterManifest.json` and every `.bin` file (SHA-256 checked) |
|
|
24
40
|
| `master decode` | master data `.bin` files or directories | one JSON per table (Rijndael-256 CBC decryption + gzip) |
|
|
25
41
|
| `adv` | episode ID | `episode.json`: command list, lines in five languages, voice / sound / video index |
|
|
26
|
-
| `story` | episode ID | a
|
|
42
|
+
| `story` | episode ID | a story directory: episode, audio, stage scene and shaders, story UI, frames / particle effects / post effects / stills / talk windows / chat assets, videos (WebM); the Live2D models it uses are exported into a models directory (`--models`, default `live2d/` next to the story directory), where other stories reuse them |
|
|
27
43
|
| `live2d` | model key or model id | a Live2D (Cubism) runtime directory: moc3, textures, motion3, physics, expressions, prefab parameters |
|
|
28
44
|
| `spot` | spot ID | `spot.json` + Spine characters + the room as `room.glb` + shaders |
|
|
29
45
|
| `room` | background prefab key | the room model (binary glTF) |
|
|
@@ -32,7 +48,7 @@ addresses in their own configuration, and the exports stay in local directories
|
|
|
32
48
|
| `crikey` | APK | the CRI HCA keycode from the game's boot data (shows whether it was found; can write a `.hcakey`) |
|
|
33
49
|
| `player` | APK | render-related global settings (color space, quality levels, renderers) as JSON |
|
|
34
50
|
| `live` | music ID + difficulty | a full chart directory: chart and runtime notes, 3D scene, note and effect assets, BGM and sounds, sound routing |
|
|
35
|
-
| `web` | `--pair music:difficulty` (repeatable) or `--all`; `--live2d model` (repeatable) or `--all-live2d`; `--story episode` (repeatable) or `--all-stories`; `--region region` (repeatable) or `--all-regions` | an ournotes-player static site: shared player + per-chart / per-model / per-episode manifests + content-addressed assets; one site can serve several regions, with listing texts in five languages; a story's interface texts are grouped by language, with TextMeshPro font assets generated from open fonts (the game's fonts with `--fonts game`) |
|
|
51
|
+
| `web` | `--pair music:difficulty` (repeatable) or `--all`; `--live2d model` (repeatable) or `--all-live2d`; `--story episode` (repeatable) or `--all-stories`; `--region region` (repeatable) or `--all-regions` | an ournotes-player static site: shared player + per-chart / per-model / per-episode manifests + content-addressed assets; the Live2D models of the stories are built first as models, listed in `models.json`, and the story manifests reference them; one site can serve several regions, with listing texts in five languages; a story's interface texts are grouped by language, with TextMeshPro font assets generated from open fonts (the game's fonts with `--fonts game`); compressible assets (JSON, shaders, moc3, ...) are stored gzip encoded by default (`--compress br` / `none`) |
|
|
36
52
|
| `deck-data` | master data files (`--master-files` directory or `--apk-master`) | one JSON file for deck-building tools: every chart's runtime notes, skill events and fever ranges, and the master data tables about cards, skills, bonuses, scores and events ([format](https://github.com/MetaSekaiLab/nnnotes/blob/main/docs/deck-data.md)) |
|
|
37
53
|
|
|
38
54
|
Export conventions:
|
|
@@ -58,18 +74,18 @@ Based on all data of the Taiwan server, version 1.0.1 (zh-Hant):
|
|
|
58
74
|
| catalog / bundle decryption / dependency closure | working |
|
|
59
75
|
| master data decoding | working |
|
|
60
76
|
| stories, `adv` | 946 / 946 episodes |
|
|
61
|
-
| stories, `story` (full directory) | 946 / 946 episodes have all their resources in the catalog and of supported kinds (the resource closure equals the game's own per-episode download list);
|
|
77
|
+
| stories, `story` (full directory) | 946 / 946 episodes have all their resources in the catalog and of supported kinds (the resource closure equals the game's own per-episode download list); 946 / 946 exported and checked one by one: the files, models and audio the output refers to exist, every JSON parses and every path it refers to resolves; the 235 episodes with frames, effects, post effects, stills, chats, talk windows or videos are byte-identical across two exports except for the models directory path |
|
|
62
78
|
| Live2D models | 239 / 239 (every model of the catalog; episodes use 185 of them) |
|
|
63
79
|
| CRI audio | 681 / 681 cue sheets |
|
|
64
80
|
| charts, `live` | 336 / 336 (music, difficulty) pairs |
|
|
65
|
-
| web site, `web` | 336 / 336 charts, 239 / 239 Live2D models, 946 / 946 story episodes (two full builds in English without audio are byte-identical and 946 / 946 pass the data validation;
|
|
66
|
-
| spots, `spot` / `room` |
|
|
67
|
-
| other regions (en / kr) and languages | checked: the regions serve the same catalog for a language and the same bundles, and the keys are shared; the master tables the charts use are the same in the three regions, and the text tables have all five languages; the chart exports checked match the Taiwan server's. A full multi-region site build is
|
|
81
|
+
| web site, `web` | 336 / 336 charts, 239 / 239 Live2D models, 946 / 946 story episodes (two full builds in English without audio are byte-identical and 946 / 946 pass the data validation); two full builds of the three regions in five languages with AAC audio (`--all-regions`, with the live master data: 340 charts of 85 musics) are likewise byte-identical and pass the data validation in full |
|
|
82
|
+
| spots, `spot` / `room` | 39 / 39 spots, every reference resolves; `room` exports 20 / 20 room backgrounds of the catalog. Every glb passes the Khronos glTF-Validator with 0 errors |
|
|
83
|
+
| other regions (en / kr) and languages | checked: the regions serve the same catalog for a language and the same bundles, and the keys are shared; the master tables the charts use are the same in the three regions, and the text tables have all five languages; the chart exports checked match the Taiwan server's. A full multi-region site build is verified (the row above) |
|
|
68
84
|
|
|
69
85
|
## Requirements
|
|
70
86
|
|
|
71
87
|
- Python 3.11+, 3.13 recommended (a `web` build reads and writes a lot of JSON, and the standard library encodes
|
|
72
|
-
JSON faster on 3.13);
|
|
88
|
+
JSON faster on 3.13); for development `pip install -e .` in the repository
|
|
73
89
|
- the game's `base.apk`: APK-local bundles, the CRI keycode, boot settings. `player`, `story`, `live` and `web` read
|
|
74
90
|
MonoBehaviours of its boot data with type trees that ship with nnnotes; supported now: game version 1.0.1
|
|
75
91
|
(Unity 6000.3.12f1). With an APK of another version whose classes do not match, these commands stop with an
|
|
@@ -85,12 +101,17 @@ Based on all data of the Taiwan server, version 1.0.1 (zh-Hant):
|
|
|
85
101
|
The code contains no keys, server addresses or default paths. Settings are read in this order, later sources
|
|
86
102
|
overriding earlier ones:
|
|
87
103
|
|
|
88
|
-
1. config file: `--config <file>`, else `NNNOTES_CONFIG`, else `nnnotes.toml` in the working directory
|
|
104
|
+
1. config file: `--config <file>`, else `NNNOTES_CONFIG`, else `nnnotes.toml` in the working directory, else the
|
|
105
|
+
per-user file (`%APPDATA%\nnnotes\nnnotes.toml` on Windows, `~/.config/nnnotes/nnnotes.toml` elsewhere;
|
|
106
|
+
`nnnotes config path` lists the lookup order and the file read)
|
|
89
107
|
2. environment variables: `NNNOTES_<SECTION>_<KEY>` (e.g. `NNNOTES_BUNDLE_KEY`, `NNNOTES_SERVERS_TW_CDN`)
|
|
90
108
|
3. command-line flags: `--region`, `--language`, `--catalog`, `--cache`, `--master`, `--apk`, `--ffmpeg`,
|
|
91
109
|
`--vgmstream`, `--node` go before the command name; `--player` is an option of `web`
|
|
92
110
|
|
|
93
|
-
|
|
111
|
+
`nnnotes config init` writes the config template (every setting empty; it ships with the package, in the repository
|
|
112
|
+
[`src/nnnotes/nnnotes.example.toml`](https://github.com/MetaSekaiLab/nnnotes/blob/main/src/nnnotes/nnnotes.example.toml)),
|
|
113
|
+
asking for each value in a terminal; `config set` / `config unset` change one value and keep the file's comments.
|
|
114
|
+
The settings are: the bundle
|
|
94
115
|
key and nonce seed, the master data key and IV, the region in use (`[catalog] region`) and the catalog language
|
|
95
116
|
(`[catalog] language`), the CDN base and API root of each region (`cdn`, `api` of `[servers.<region>]`), the client
|
|
96
117
|
version (`[client] version`; unset: the APK's versionName), optionally the bootstrap API root (`[bootstrap] api`, for
|
|
@@ -2,10 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
[简体中文](README.md) | [English](README.en.md)
|
|
4
4
|
|
|
5
|
-
nnnotes 是 BanG Dream! Our Notes 游戏文件的离线数据工具包:读取 Addressables catalog、下载并解密资源包、下载与解码 masterdata,再把剧情、Live2D、据点场景、着色器、CRI 音频和谱面导出成结构化的 JSON
|
|
5
|
+
nnnotes 是 BanG Dream! Our Notes 游戏文件的离线数据工具包:读取 Addressables catalog、下载并解密资源包、下载与解码 masterdata,再把剧情、Live2D、据点场景、着色器、CRI 音频和谱面导出成结构化的 JSON 与通用格式文件。`web` 导出的谱面、Live2D 模型与剧情站点就是 [ournotes-player](https://github.com/empty-sekai/ournotes-player) 读取的数据。命名灵感来自 [mos9527/sssekai](https://github.com/mos9527/sssekai)。
|
|
6
6
|
|
|
7
7
|
本项目为非官方爱好者项目,与游戏的开发和运营方无关。仓库不包含任何游戏资源、密钥或服务器地址:游戏文件、解密所需的密钥和服务器地址都由使用者在配置中自行提供,导出结果只保存在使用者指定的本地目录。
|
|
8
8
|
|
|
9
|
+
## 安装与初始化
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pip install nnnotes # Python 3.11+,推荐 3.13
|
|
13
|
+
nnnotes config init --user # 在终端里逐项填写设置(密钥不回显),写入用户配置文件,在任何目录下都会读取
|
|
14
|
+
nnnotes config check # 每项设置的来源和格式是否有效,不显示设置值
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- 设置值(密钥、区服的 CDN 与 API 地址、本地数据与工具的路径)都来自使用者自己的游戏客户端,见[配置](#配置)。
|
|
18
|
+
- 在脚本或 AI 代理中不会有交互提问:`nnnotes config init --user --no-input --set 段.键=值 ...` 一次写好,之后用 `nnnotes config set 段.键 值` 修改单项(值写 `-` 时从标准输入读取,密钥不必出现在命令行里),`nnnotes config check --json` 给出机器可读的状态。
|
|
19
|
+
- `--fonts game` 另需 `pip install 'nnnotes[fonts]'`;APK、masterdata 与外部工具见[需要准备](#需要准备)。
|
|
20
|
+
|
|
9
21
|
## 功能
|
|
10
22
|
|
|
11
23
|
| 命令 | 输入 | 输出 |
|
|
@@ -18,7 +30,7 @@ nnnotes 是 BanG Dream! Our Notes 游戏文件的离线数据工具包:读取
|
|
|
18
30
|
| `master download` | masterdata 版本号,或 `--latest`(区服当前版本) | 该版本的 `MasterManifest.json` 与全部 `.bin` 文件(SHA-256 校验) |
|
|
19
31
|
| `master decode` | masterdata `.bin` 文件或目录 | 每张表一个 JSON(Rijndael-256 CBC 解密 + gzip 解压) |
|
|
20
32
|
| `adv` | 剧情 ID | `episode.json`:命令表、五语台词、语音 / 音效 / 视频索引 |
|
|
21
|
-
| `story` | 剧情 ID |
|
|
33
|
+
| `story` | 剧情 ID | 剧情目录:episode、音频、舞台场景与着色器、剧情 UI、Frame / 粒子特效 / 后处理 / 静帧 / 对话框 / 聊天资源、视频(WebM);用到的 Live2D 模型导出到模型目录(`--models`,默认为剧情目录旁的 `live2d/`),已导出的模型供其他剧情复用 |
|
|
22
34
|
| `live2d` | 模型资源键或模型 ID | Live2D(Cubism)运行时目录:moc3、贴图、motion3、物理、表情、预制体参数 |
|
|
23
35
|
| `spot` | 据点 ID | `spot.json` + Spine 角色 + 房间 `room.glb` + 着色器 |
|
|
24
36
|
| `room` | 背景预制体键 | 房间模型(binary glTF) |
|
|
@@ -27,7 +39,7 @@ nnnotes 是 BanG Dream! Our Notes 游戏文件的离线数据工具包:读取
|
|
|
27
39
|
| `crikey` | APK | 读出游戏启动数据中的 CRI HCA 解码密钥(只显示是否找到,可写成 `.hcakey`) |
|
|
28
40
|
| `player` | APK | 渲染相关的全局设置(色彩空间、画质等级、渲染器)JSON |
|
|
29
41
|
| `live` | 曲目 ID + 难度 | 完整谱面目录:谱面与运行时音符、3D 场景、音符与特效资源、BGM 与音效、声音路由 |
|
|
30
|
-
| `web` | `--pair 曲目:难度`(可重复)或 `--all`;`--live2d 模型`(可重复)或 `--all-live2d`;`--story 剧情 ID`(可重复)或 `--all-stories`;`--region 区服`(可重复)或 `--all-regions` | ournotes-player 静态站点:共享播放器 + 每谱 / 每模型 / 每集剧情清单 +
|
|
42
|
+
| `web` | `--pair 曲目:难度`(可重复)或 `--all`;`--live2d 模型`(可重复)或 `--all-live2d`;`--story 剧情 ID`(可重复)或 `--all-stories`;`--region 区服`(可重复)或 `--all-regions` | ournotes-player 静态站点:共享播放器 + 每谱 / 每模型 / 每集剧情清单 + 内容寻址资源;剧情用到的 Live2D 模型先按模型构建并列入 `models.json`,剧情清单引用它们;一个站点可服务多个区服,列表文本含五种语言;剧情的界面文字按语言分组,字形由开源字体生成 TextMeshPro 字体资源(`--fonts game` 时用游戏字体);可压缩的资源(JSON、着色器、moc3 等)默认以 gzip 存储,`--compress br` / `none` 可改 |
|
|
31
43
|
| `deck-data` | masterdata 文件(`--master-files` 目录或 `--apk-master`) | 供组卡工具读取的单个 JSON:每张谱面的运行时音符、技能事件与 fever 区间,以及卡牌、技能、加成、分数与活动相关的 masterdata 表([格式](docs/deck-data.md)) |
|
|
32
44
|
|
|
33
45
|
导出约定:
|
|
@@ -47,17 +59,17 @@ nnnotes 是 BanG Dream! Our Notes 游戏文件的离线数据工具包:读取
|
|
|
47
59
|
| catalog / 资源包解密 / 依赖闭包 | 可用 |
|
|
48
60
|
| masterdata 解码 | 可用 |
|
|
49
61
|
| 剧情 `adv` | 946 / 946 集可导出 |
|
|
50
|
-
| 剧情 `story`(完整目录) | 946 / 946 集的资源都在 catalog
|
|
62
|
+
| 剧情 `story`(完整目录) | 946 / 946 集的资源都在 catalog 中且类型均受支持(资源闭包与游戏自带的每集下载清单一致);946 / 946 集已逐集导出验证:输出中引用的文件、模型与音频均存在,JSON 均可解析,引用的路径均可解析;其中 235 集用到 Frame / Effect / PostEffect / Still / Chat / TalkWindow / 视频资源,两次导出除模型目录路径外逐字节一致 |
|
|
51
63
|
| Live2D 模型 | 239 / 239 个可导出(catalog 中的全部模型,剧情用到其中 185 个) |
|
|
52
64
|
| CRI 音频 | 681 / 681 个 cue sheet 可解码 |
|
|
53
65
|
| 谱面 `live` | 336 / 336 个(曲目, 难度)组合可导出 |
|
|
54
|
-
| 网页站点 `web` | 336 / 336 张谱面,239 / 239 个 Live2D 模型,946 / 946 集剧情(英语、无音频的全量构建两次结果逐字节一致,946 / 946
|
|
55
|
-
| 据点 `spot` / `room` |
|
|
56
|
-
| 其他区服(en / kr)与其他语言 | 已核对:各区服同一语言的 catalog 相同、资源包相同,密钥通用;谱面用到的 masterdata
|
|
66
|
+
| 网页站点 `web` | 336 / 336 张谱面,239 / 239 个 Live2D 模型,946 / 946 集剧情(英语、无音频的全量构建两次结果逐字节一致,946 / 946 集通过数据校验);三个区服、五种语言、含 AAC 音频的全量构建(`--all-regions`,用线上 masterdata:85 首曲目的 340 张谱面)两次结果同样逐字节一致,全部通过数据校验 |
|
|
67
|
+
| 据点 `spot` / `room` | 39 / 39 个据点可导出,引用均可解析;catalog 中 20 / 20 个房间背景可由 `room` 导出。生成的 glb 均通过 Khronos glTF-Validator,0 个错误 |
|
|
68
|
+
| 其他区服(en / kr)与其他语言 | 已核对:各区服同一语言的 catalog 相同、资源包相同,密钥通用;谱面用到的 masterdata 表在三个区服间一致,文本表五种语言齐全;抽查的谱面导出与台服一致。多区服整站已全量构建验证(见上一行) |
|
|
57
69
|
|
|
58
70
|
## 需要准备
|
|
59
71
|
|
|
60
|
-
- Python 3.11+,推荐 3.13(`web` 构建读写大量 JSON,3.13 的标准库 JSON
|
|
72
|
+
- Python 3.11+,推荐 3.13(`web` 构建读写大量 JSON,3.13 的标准库 JSON 编码更快);开发时在仓库内 `pip install -e .`
|
|
61
73
|
- 游戏安装包 `base.apk`:APK 内置资源包、CRI 解码密钥、启动设置。`player`、`story`、`live`、`web` 用 nnnotes 自带的类型树读取 APK 启动数据中的 MonoBehaviour,目前支持游戏版本 1.0.1(Unity 6000.3.12f1);其他版本的 APK 若类型不符,这些命令会报错并给出类名、游戏版本和 Unity 版本
|
|
62
74
|
- 解码后的 masterdata 目录(可用 `master download` + `master decode` 生成):`adv`、`story`、`spot`、`live` 和 `web` 的谱面需要;`web` 的 Live2D 模型只用它取角色名(可选)
|
|
63
75
|
- 外部工具:[vgmstream](https://vgmstream.org/)(CRI HCA 解码)、[FFmpeg](https://ffmpeg.org/)(转码,含剧情视频的 WebM 封装与 Opus 音频);`web` 另需 Node.js 20+ 与构建好的 ournotes-player
|
|
@@ -66,11 +78,11 @@ nnnotes 是 BanG Dream! Our Notes 游戏文件的离线数据工具包:读取
|
|
|
66
78
|
|
|
67
79
|
代码中没有任何密钥、服务器地址和默认路径。所有设置按以下顺序读取,后者覆盖前者:
|
|
68
80
|
|
|
69
|
-
1. 配置文件:`--config <文件>`,否则 `NNNOTES_CONFIG`,否则当前目录的 `nnnotes.toml`
|
|
81
|
+
1. 配置文件:`--config <文件>`,否则 `NNNOTES_CONFIG`,否则当前目录的 `nnnotes.toml`,否则用户配置文件(Windows 为 `%APPDATA%\nnnotes\nnnotes.toml`,其他系统为 `~/.config/nnnotes/nnnotes.toml`;`nnnotes config path` 列出查找顺序与实际读取的文件)
|
|
70
82
|
2. 环境变量:`NNNOTES_<节>_<键>`(如 `NNNOTES_BUNDLE_KEY`、`NNNOTES_SERVERS_TW_CDN`)
|
|
71
83
|
3. 命令行参数:`--region`、`--language`、`--catalog`、`--cache`、`--master`、`--apk`、`--ffmpeg`、`--vgmstream`、`--node` 写在命令名之前;`--player` 是 `web` 的参数
|
|
72
84
|
|
|
73
|
-
|
|
85
|
+
`nnnotes config init` 写出配置模板(每项设置为空,模板随包安装,仓库中为 [`src/nnnotes/nnnotes.example.toml`](src/nnnotes/nnnotes.example.toml)),在终端中会逐项询问;`config set` / `config unset` 修改单项并保留文件中的注释。需要的设置包括:资源包解密密钥与 nonce 种子、masterdata 的密钥与 IV、使用的区服 `[catalog] region` 与 catalog 语言 `[catalog] language`、每个区服的 CDN 地址与 API 根地址(`[servers.<区服>]` 的 `cdn`、`api`)、客户端版本 `[client] version`(未设置时读取 APK 的 versionName)、可选的引导 API 根地址 `[bootstrap] api`(`servers` 使用),以及缓存目录、APK、masterdata 目录、ournotes-player 和 vgmstream / FFmpeg / Node.js 的路径(三个工具未设置时在 `PATH` 中查找)。这些值都来自使用者自己的游戏客户端。
|
|
74
86
|
|
|
75
87
|
缺少或格式错误的设置会让命令以退出码 2 结束,并用一行说明对应的配置键、环境变量和命令行参数,不会输出任何设置值。`nnnotes.toml` 已在 `.gitignore` 中,请勿提交。
|
|
76
88
|
|
|
@@ -16,6 +16,7 @@ dependencies = [
|
|
|
16
16
|
"Pillow>=10.0",
|
|
17
17
|
"pycryptodome>=3.20",
|
|
18
18
|
"grpcio>=1.49.1",
|
|
19
|
+
"brotli>=1.1",
|
|
19
20
|
]
|
|
20
21
|
classifiers = [
|
|
21
22
|
"Programming Language :: Python :: 3",
|
|
@@ -43,7 +44,7 @@ version = { attr = "nnnotes.__version__" }
|
|
|
43
44
|
where = ["src"]
|
|
44
45
|
|
|
45
46
|
[tool.setuptools.package-data]
|
|
46
|
-
nnnotes = ["typetrees/*.json", "viewrules.json"]
|
|
47
|
+
nnnotes = ["typetrees/*.json", "viewrules.json", "nnnotes.example.toml"]
|
|
47
48
|
|
|
48
49
|
[tool.pytest.ini_options]
|
|
49
50
|
testpaths = ["tests"]
|
|
@@ -4,4 +4,4 @@ Reads Addressables catalogs, fetches and decrypts asset bundles, decodes master
|
|
|
4
4
|
models, spots, shaders, CRI audio and live charts as JSON and common file formats. Game files, keys and server
|
|
5
5
|
addresses are supplied by the user through the configuration; nothing of the game is included.
|
|
6
6
|
"""
|
|
7
|
-
__version__ = "0.1.
|
|
7
|
+
__version__ = "0.1.2"
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""ADV presentation data -> scene.json + textures + shaders.
|
|
2
2
|
|
|
3
3
|
Everything the game uses to present an ADV episode besides the episode script,
|
|
4
|
-
the Live2D models
|
|
4
|
+
the Live2D models (with the materials of their mask pass: webmodel.py) and the
|
|
5
|
+
audio:
|
|
5
6
|
|
|
6
7
|
player colour space, quality levels, URP pipelines, renderer data and
|
|
7
8
|
features, post-process data (APK boot data, see player.py)
|
|
@@ -13,8 +14,6 @@ the Live2D models and the audio:
|
|
|
13
14
|
settings AdvPlayerSettings (+ focus data), AdvMasterIdSettings
|
|
14
15
|
stages every stage prefab the episode uses (AdvStage, lights,
|
|
15
16
|
background sprite, volume profiles)
|
|
16
|
-
resources Resources.Load materials the runtime draws with (Cubism mask
|
|
17
|
-
pass: Mask / MaskCulling)
|
|
18
17
|
postTextures per camera renderer: post-process data textures used by the
|
|
19
18
|
URP chain (film grain textures, indexed by FilmGrain.type)
|
|
20
19
|
|
|
@@ -47,13 +46,6 @@ SETTINGS_KEYS = {
|
|
|
47
46
|
STAGE_PREFIX = "Adv/Stage/"
|
|
48
47
|
|
|
49
48
|
|
|
50
|
-
# Resources.Load paths used by the ADV runtime (Cubism mask pass materials)
|
|
51
|
-
RESOURCES = {
|
|
52
|
-
"cubismMask": "Live2D/Cubism/Materials/Mask",
|
|
53
|
-
"cubismMaskCulling": "Live2D/Cubism/Materials/MaskCulling",
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
49
|
def camera_renderer(player_graphics: dict, pipeline: str, renderer_index: int) -> str:
|
|
58
50
|
rl = player_graphics["pipelines"][pipeline]["m_RendererDataList"]
|
|
59
51
|
i = player_graphics["pipelines"][pipeline]["m_DefaultRendererIndex"] if renderer_index < 0 else renderer_index
|
|
@@ -71,7 +63,6 @@ def extract(cat: Catalog, player: PlayerData, episode: dict, out_dir: Path, shad
|
|
|
71
63
|
for name, key in ADV_KEYS.items():
|
|
72
64
|
doc[name] = ex.prefab(key)
|
|
73
65
|
doc["settings"] = {name: ex.asset(key) for name, key in SETTINGS_KEYS.items()}
|
|
74
|
-
doc["resources"] = {name: ex.material(player.resource(path)) for name, path in RESOURCES.items()}
|
|
75
66
|
|
|
76
67
|
stages = {}
|
|
77
68
|
for c in episode["commands"]:
|
|
@@ -82,10 +73,7 @@ def extract(cat: Catalog, player: PlayerData, episode: dict, out_dir: Path, shad
|
|
|
82
73
|
stages[c["TargetAssetName"]] = ex.prefab(key)
|
|
83
74
|
doc["stages"] = stages
|
|
84
75
|
|
|
85
|
-
# shaders:
|
|
86
|
-
for r in episode["resources"]:
|
|
87
|
-
if r["kind"] == "live2d":
|
|
88
|
-
ex.closure_shaders(r["address"])
|
|
76
|
+
# shaders: everything referenced above, the ADV camera renderer
|
|
89
77
|
renderers = set()
|
|
90
78
|
for node in doc["cameraManager"]["nodes"]:
|
|
91
79
|
for comp in node["components"]:
|