nnnotes 0.1.2__tar.gz → 0.1.3__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.
Files changed (155) hide show
  1. {nnnotes-0.1.2 → nnnotes-0.1.3}/PKG-INFO +26 -21
  2. nnnotes-0.1.2/src/nnnotes.egg-info/PKG-INFO → nnnotes-0.1.3/README.en.md +6 -32
  3. {nnnotes-0.1.2 → nnnotes-0.1.3}/pyproject.toml +10 -10
  4. nnnotes-0.1.3/rust/Cargo.lock +215 -0
  5. nnnotes-0.1.3/rust/Cargo.toml +23 -0
  6. nnnotes-0.1.3/rust/build.rs +33 -0
  7. nnnotes-0.1.3/rust/rustfmt.toml +2 -0
  8. nnnotes-0.1.3/rust/src/lib.rs +116 -0
  9. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/__init__.py +1 -1
  10. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/addressables.py +56 -10
  11. nnnotes-0.1.3/src/nnnotes/apkset.py +103 -0
  12. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/catalog.py +70 -20
  13. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/catalogdb.py +17 -10
  14. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/cli.py +111 -23
  15. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/cli_assets.py +67 -36
  16. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/config.py +42 -0
  17. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/configfile.py +12 -3
  18. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/cri.py +2 -1
  19. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/crikey.py +4 -3
  20. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/crilips.py +2 -1
  21. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/cristages.py +8 -3
  22. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/deckdata.py +74 -74
  23. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/gameapi.py +19 -7
  24. nnnotes-0.1.3/src/nnnotes/jp.py +294 -0
  25. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/liveaudio.py +2 -2
  26. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/master.py +31 -5
  27. nnnotes-0.1.3/src/nnnotes/musicdata.py +1144 -0
  28. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/nnnotes.example.toml +15 -2
  29. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/player.py +7 -2
  30. nnnotes-0.1.3/src/nnnotes/replaydata.py +101 -0
  31. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/storysite.py +2 -0
  32. nnnotes-0.1.3/src/nnnotes/ui.py +310 -0
  33. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/web.py +23 -3
  34. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/webmodel.py +1 -1
  35. nnnotes-0.1.2/README.en.md +0 -191
  36. nnnotes-0.1.2/README.md +0 -144
  37. nnnotes-0.1.2/setup.cfg +0 -4
  38. nnnotes-0.1.2/src/nnnotes.egg-info/SOURCES.txt +0 -143
  39. nnnotes-0.1.2/src/nnnotes.egg-info/dependency_links.txt +0 -1
  40. nnnotes-0.1.2/src/nnnotes.egg-info/entry_points.txt +0 -2
  41. nnnotes-0.1.2/src/nnnotes.egg-info/requires.txt +0 -16
  42. nnnotes-0.1.2/src/nnnotes.egg-info/top_level.txt +0 -1
  43. nnnotes-0.1.2/tests/test_advvideo.py +0 -522
  44. nnnotes-0.1.2/tests/test_atom_eval.py +0 -231
  45. nnnotes-0.1.2/tests/test_atoms_mesh.py +0 -357
  46. nnnotes-0.1.2/tests/test_atoms_sprite.py +0 -235
  47. nnnotes-0.1.2/tests/test_atoms_texture.py +0 -272
  48. nnnotes-0.1.2/tests/test_blob_memo.py +0 -101
  49. nnnotes-0.1.2/tests/test_cache.py +0 -107
  50. nnnotes-0.1.2/tests/test_catalog.py +0 -206
  51. nnnotes-0.1.2/tests/test_catalog_lazy.py +0 -194
  52. nnnotes-0.1.2/tests/test_catalogdb.py +0 -320
  53. nnnotes-0.1.2/tests/test_census.py +0 -211
  54. nnnotes-0.1.2/tests/test_cli.py +0 -373
  55. nnnotes-0.1.2/tests/test_cli_assets.py +0 -1007
  56. nnnotes-0.1.2/tests/test_compress.py +0 -67
  57. nnnotes-0.1.2/tests/test_config.py +0 -193
  58. nnnotes-0.1.2/tests/test_configfile.py +0 -203
  59. nnnotes-0.1.2/tests/test_contract.py +0 -240
  60. nnnotes-0.1.2/tests/test_crilips.py +0 -199
  61. nnnotes-0.1.2/tests/test_cristages.py +0 -554
  62. nnnotes-0.1.2/tests/test_deckdata.py +0 -461
  63. nnnotes-0.1.2/tests/test_gameapi.py +0 -393
  64. nnnotes-0.1.2/tests/test_goldens.py +0 -125
  65. nnnotes-0.1.2/tests/test_jsonio.py +0 -45
  66. nnnotes-0.1.2/tests/test_layout.py +0 -363
  67. nnnotes-0.1.2/tests/test_link.py +0 -279
  68. nnnotes-0.1.2/tests/test_live_options.py +0 -298
  69. nnnotes-0.1.2/tests/test_live_options_web.py +0 -166
  70. nnnotes-0.1.2/tests/test_master.py +0 -132
  71. nnnotes-0.1.2/tests/test_mesh_texture_guards.py +0 -94
  72. nnnotes-0.1.2/tests/test_modelstages.py +0 -422
  73. nnnotes-0.1.2/tests/test_objexport.py +0 -1016
  74. nnnotes-0.1.2/tests/test_orchestrate.py +0 -305
  75. nnnotes-0.1.2/tests/test_pipeline.py +0 -163
  76. nnnotes-0.1.2/tests/test_plan.py +0 -149
  77. nnnotes-0.1.2/tests/test_player.py +0 -140
  78. nnnotes-0.1.2/tests/test_readsets.py +0 -442
  79. nnnotes-0.1.2/tests/test_regions.py +0 -246
  80. nnnotes-0.1.2/tests/test_repo.py +0 -50
  81. nnnotes-0.1.2/tests/test_room.py +0 -392
  82. nnnotes-0.1.2/tests/test_sniff.py +0 -137
  83. nnnotes-0.1.2/tests/test_speed_exact.py +0 -534
  84. nnnotes-0.1.2/tests/test_spot.py +0 -159
  85. nnnotes-0.1.2/tests/test_stages.py +0 -274
  86. nnnotes-0.1.2/tests/test_store.py +0 -181
  87. nnnotes-0.1.2/tests/test_story_closure.py +0 -137
  88. nnnotes-0.1.2/tests/test_story_fonts.py +0 -842
  89. nnnotes-0.1.2/tests/test_story_host.py +0 -512
  90. nnnotes-0.1.2/tests/test_story_kinds.py +0 -100
  91. nnnotes-0.1.2/tests/test_story_site.py +0 -663
  92. nnnotes-0.1.2/tests/test_story_ui.py +0 -551
  93. nnnotes-0.1.2/tests/test_textstyle.py +0 -253
  94. nnnotes-0.1.2/tests/test_typetrees.py +0 -382
  95. nnnotes-0.1.2/tests/test_views.py +0 -749
  96. nnnotes-0.1.2/tests/test_voices.py +0 -756
  97. nnnotes-0.1.2/tests/test_voices_story.py +0 -541
  98. nnnotes-0.1.2/tests/test_web.py +0 -277
  99. nnnotes-0.1.2/tests/test_webmodel.py +0 -716
  100. {nnnotes-0.1.2 → nnnotes-0.1.3}/LICENSE +0 -0
  101. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/__main__.py +0 -0
  102. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/acb.py +0 -0
  103. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/adv.py +0 -0
  104. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/advcommand.py +0 -0
  105. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/advmedia.py +0 -0
  106. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/advscene.py +0 -0
  107. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/advui.py +0 -0
  108. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/advvideo.py +0 -0
  109. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/atoms/__init__.py +0 -0
  110. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/atoms/astc.py +0 -0
  111. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/atoms/gltf.py +0 -0
  112. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/atoms/mesh.py +0 -0
  113. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/atoms/png.py +0 -0
  114. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/atoms/reader.py +0 -0
  115. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/atoms/sniff.py +0 -0
  116. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/atoms/sprite.py +0 -0
  117. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/atoms/texture.py +0 -0
  118. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/cache.py +0 -0
  119. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/census.py +0 -0
  120. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/compress.py +0 -0
  121. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/contract.py +0 -0
  122. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/export.py +0 -0
  123. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/jsonio.py +0 -0
  124. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/languages.py +0 -0
  125. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/layout.py +0 -0
  126. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/link.py +0 -0
  127. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/live.py +0 -0
  128. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/live2d.py +0 -0
  129. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/livenotes.py +0 -0
  130. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/liveoptions.py +0 -0
  131. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/livescene.py +0 -0
  132. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/liveui.py +0 -0
  133. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/modelstages.py +0 -0
  134. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/motion.py +0 -0
  135. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/objexport.py +0 -0
  136. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/orchestrate.py +0 -0
  137. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/plan.py +0 -0
  138. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/room.py +0 -0
  139. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/score.py +0 -0
  140. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/shader.py +0 -0
  141. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/spot.py +0 -0
  142. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/stages.py +0 -0
  143. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/store.py +0 -0
  144. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/story.py +0 -0
  145. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/storyfonts.py +0 -0
  146. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/storyhost.py +0 -0
  147. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/storyvoices.py +0 -0
  148. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/textstyle.py +0 -0
  149. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/tmpfont.py +0 -0
  150. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/typetrees/6000.3.12f1.json +0 -0
  151. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/unity.py +0 -0
  152. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/viewrules.json +0 -0
  153. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/views.py +0 -0
  154. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/voices.py +0 -0
  155. {nnnotes-0.1.2 → nnnotes-0.1.3}/src/nnnotes/webaudio.py +0 -0
@@ -1,36 +1,39 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nnnotes
3
- Version: 0.1.2
4
- Summary: Offline data toolkit for BanG Dream! Our Notes game files: catalogs, bundles, master data, stories, Live2D, audio and live charts
5
- Author: MetaMiku, emptysekai
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/MetaSekaiLab/nnnotes
8
- Project-URL: Source, https://github.com/MetaSekaiLab/nnnotes
9
- Project-URL: Issues, https://github.com/MetaSekaiLab/nnnotes/issues
3
+ Version: 0.1.3
10
4
  Classifier: Programming Language :: Python :: 3
11
5
  Classifier: Operating System :: OS Independent
12
6
  Classifier: Topic :: Multimedia
13
- Requires-Python: >=3.11
14
- Description-Content-Type: text/markdown
15
- License-File: LICENSE
16
- Requires-Dist: UnityPy>=1.25
7
+ Requires-Dist: unitypy>=1.25
17
8
  Requires-Dist: numpy>=2.0
18
- Requires-Dist: Pillow>=10.0
9
+ Requires-Dist: pillow>=10.0
19
10
  Requires-Dist: pycryptodome>=3.20
20
11
  Requires-Dist: grpcio>=1.49.1
21
12
  Requires-Dist: brotli>=1.1
22
- Provides-Extra: test
23
- Requires-Dist: pytest>=8; extra == "test"
24
- Requires-Dist: pyflakes>=3.2; extra == "test"
25
- Requires-Dist: jsonschema>=4.18; extra == "test"
13
+ Requires-Dist: fonttools>=4.50 ; extra == 'fonts'
14
+ Requires-Dist: freetype-py>=2.5 ; extra == 'fonts'
15
+ Requires-Dist: scipy>=1.13 ; extra == 'fonts'
16
+ Requires-Dist: pytest>=8 ; extra == 'test'
17
+ Requires-Dist: pyflakes>=3.2 ; extra == 'test'
18
+ Requires-Dist: jsonschema>=4.18 ; extra == 'test'
26
19
  Provides-Extra: fonts
27
- Requires-Dist: fonttools>=4.50; extra == "fonts"
28
- Requires-Dist: freetype-py>=2.5; extra == "fonts"
29
- Requires-Dist: scipy>=1.13; extra == "fonts"
30
- Dynamic: license-file
20
+ Provides-Extra: test
21
+ License-File: LICENSE
22
+ Summary: Offline data toolkit for BanG Dream! Our Notes game files: catalogs, bundles, master data, stories, Live2D, audio and live charts
23
+ Author: MetaMiku, emptysekai
24
+ License-Expression: MIT
25
+ Requires-Python: >=3.11
26
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
27
+ Project-URL: Homepage, https://github.com/MetaSekaiLab/nnnotes
28
+ Project-URL: Issues, https://github.com/MetaSekaiLab/nnnotes/issues
29
+ Project-URL: Source, https://github.com/MetaSekaiLab/nnnotes
31
30
 
32
31
  # nnnotes?!
33
32
 
33
+ Japanese-release support includes anonymous version discovery, authenticated CDN downloads, gzip catalogs,
34
+ snapshot-isolated caches and split APKs. See the [JP guide](https://github.com/MetaSekaiLab/nnnotes/blob/main/docs/jp.md)
35
+ for configuration and validation limits.
36
+
34
37
  [简体中文](https://github.com/MetaSekaiLab/nnnotes/blob/main/README.md) | [English](https://github.com/MetaSekaiLab/nnnotes/blob/main/README.en.md)
35
38
 
36
39
  nnnotes is an offline data toolkit for the game files of BanG Dream! Our Notes: it reads Addressables catalogs,
@@ -78,9 +81,10 @@ nnnotes config check # each setting's origin and whether it is valid
78
81
  | `audio` | CRI cue sheet | one audio file per cue (FLAC / Ogg / WAV) + cue metadata |
79
82
  | `crikey` | APK | the CRI HCA keycode from the game's boot data (shows whether it was found; can write a `.hcakey`) |
80
83
  | `player` | APK | render-related global settings (color space, quality levels, renderers) as JSON |
84
+ | `ui` (experimental) | APK set and its embedded catalog | an offline serialized prefab library with dependency roots, AnimatorControllers, stable references and PNG/font resources for the optional ournotes-player UI preview ([format and limits](https://github.com/MetaSekaiLab/nnnotes/blob/main/docs/ui.md)) |
81
85
  | `live` | music ID + difficulty | a full chart directory: chart and runtime notes, 3D scene, note and effect assets, BGM and sounds, sound routing |
82
86
  | `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`) |
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)) |
87
+ | `music-data` | master data files (`--master-files` directory or `--apk-master`), or decoded master data with its manifest (`--decoded-master`) | one JSON file with every song and chart: titles and credits in five languages, bands, vocal characters, category, tags, release time, score ranks, BGM length; the Gekisou catalog (member cards, snaps and their Gekisou skills and support skills); per difficulty the level, note counts, BPM, chart times, skill events and fever ranges; and the chart statistics the deck model ournotes-deck (built into nnnotes) measures on its whole-live simulation (the no-skill score, the weight of every score-up skill kind at every position), plus single-shape Gekisou aptitude (increments and standard errors, not an optimal deck; omit with `--no-gekisou-aptitude`). `--full` adds the deck model's input: every chart's runtime notes and the master data tables about cards, skills, bonuses, scores and events ([format](https://github.com/MetaSekaiLab/nnnotes/blob/main/docs/music-data.md)) |
84
88
 
85
89
  Export conventions:
86
90
 
@@ -220,3 +224,4 @@ See [docs/performance.md](https://github.com/MetaSekaiLab/nnnotes/blob/main/docs
220
224
  ## License
221
225
 
222
226
  MIT, see [LICENSE](https://github.com/MetaSekaiLab/nnnotes/blob/main/LICENSE).
227
+
@@ -1,36 +1,9 @@
1
- Metadata-Version: 2.4
2
- Name: nnnotes
3
- Version: 0.1.2
4
- Summary: Offline data toolkit for BanG Dream! Our Notes game files: catalogs, bundles, master data, stories, Live2D, audio and live charts
5
- Author: MetaMiku, emptysekai
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/MetaSekaiLab/nnnotes
8
- Project-URL: Source, https://github.com/MetaSekaiLab/nnnotes
9
- Project-URL: Issues, https://github.com/MetaSekaiLab/nnnotes/issues
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Operating System :: OS Independent
12
- Classifier: Topic :: Multimedia
13
- Requires-Python: >=3.11
14
- Description-Content-Type: text/markdown
15
- License-File: LICENSE
16
- Requires-Dist: UnityPy>=1.25
17
- Requires-Dist: numpy>=2.0
18
- Requires-Dist: Pillow>=10.0
19
- Requires-Dist: pycryptodome>=3.20
20
- Requires-Dist: grpcio>=1.49.1
21
- Requires-Dist: brotli>=1.1
22
- Provides-Extra: test
23
- Requires-Dist: pytest>=8; extra == "test"
24
- Requires-Dist: pyflakes>=3.2; extra == "test"
25
- Requires-Dist: jsonschema>=4.18; extra == "test"
26
- Provides-Extra: fonts
27
- Requires-Dist: fonttools>=4.50; extra == "fonts"
28
- Requires-Dist: freetype-py>=2.5; extra == "fonts"
29
- Requires-Dist: scipy>=1.13; extra == "fonts"
30
- Dynamic: license-file
31
-
32
1
  # nnnotes?!
33
2
 
3
+ Japanese-release support includes anonymous version discovery, authenticated CDN downloads, gzip catalogs,
4
+ snapshot-isolated caches and split APKs. See the [JP guide](https://github.com/MetaSekaiLab/nnnotes/blob/main/docs/jp.md)
5
+ for configuration and validation limits.
6
+
34
7
  [简体中文](https://github.com/MetaSekaiLab/nnnotes/blob/main/README.md) | [English](https://github.com/MetaSekaiLab/nnnotes/blob/main/README.en.md)
35
8
 
36
9
  nnnotes is an offline data toolkit for the game files of BanG Dream! Our Notes: it reads Addressables catalogs,
@@ -78,9 +51,10 @@ nnnotes config check # each setting's origin and whether it is valid
78
51
  | `audio` | CRI cue sheet | one audio file per cue (FLAC / Ogg / WAV) + cue metadata |
79
52
  | `crikey` | APK | the CRI HCA keycode from the game's boot data (shows whether it was found; can write a `.hcakey`) |
80
53
  | `player` | APK | render-related global settings (color space, quality levels, renderers) as JSON |
54
+ | `ui` (experimental) | APK set and its embedded catalog | an offline serialized prefab library with dependency roots, AnimatorControllers, stable references and PNG/font resources for the optional ournotes-player UI preview ([format and limits](https://github.com/MetaSekaiLab/nnnotes/blob/main/docs/ui.md)) |
81
55
  | `live` | music ID + difficulty | a full chart directory: chart and runtime notes, 3D scene, note and effect assets, BGM and sounds, sound routing |
82
56
  | `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`) |
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)) |
57
+ | `music-data` | master data files (`--master-files` directory or `--apk-master`), or decoded master data with its manifest (`--decoded-master`) | one JSON file with every song and chart: titles and credits in five languages, bands, vocal characters, category, tags, release time, score ranks, BGM length; the Gekisou catalog (member cards, snaps and their Gekisou skills and support skills); per difficulty the level, note counts, BPM, chart times, skill events and fever ranges; and the chart statistics the deck model ournotes-deck (built into nnnotes) measures on its whole-live simulation (the no-skill score, the weight of every score-up skill kind at every position), plus single-shape Gekisou aptitude (increments and standard errors, not an optimal deck; omit with `--no-gekisou-aptitude`). `--full` adds the deck model's input: every chart's runtime notes and the master data tables about cards, skills, bonuses, scores and events ([format](https://github.com/MetaSekaiLab/nnnotes/blob/main/docs/music-data.md)) |
84
58
 
85
59
  Export conventions:
86
60
 
@@ -1,6 +1,6 @@
1
1
  [build-system]
2
- requires = ["setuptools>=77"]
3
- build-backend = "setuptools.build_meta"
2
+ requires = ["maturin>=1.9,<2"]
3
+ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "nnnotes"
@@ -37,14 +37,14 @@ Homepage = "https://github.com/MetaSekaiLab/nnnotes"
37
37
  Source = "https://github.com/MetaSekaiLab/nnnotes"
38
38
  Issues = "https://github.com/MetaSekaiLab/nnnotes/issues"
39
39
 
40
- [tool.setuptools.dynamic]
41
- version = { attr = "nnnotes.__version__" }
42
-
43
- [tool.setuptools.packages.find]
44
- where = ["src"]
45
-
46
- [tool.setuptools.package-data]
47
- nnnotes = ["typetrees/*.json", "viewrules.json", "nnnotes.example.toml"]
40
+ [tool.maturin]
41
+ # the pure Python package src/nnnotes with the extension module nnnotes._deck (rust/: ournotes-deck, pinned by
42
+ # commit); the version is rust/Cargo.toml's, kept equal to nnnotes.__version__ (tests/test_musicdata.py)
43
+ python-source = "src"
44
+ module-name = "nnnotes._deck"
45
+ manifest-path = "rust/Cargo.toml"
46
+ profile = "release"
47
+ locked = true
48
48
 
49
49
  [tool.pytest.ini_options]
50
50
  testpaths = ["tests"]
@@ -0,0 +1,215 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "heck"
7
+ version = "0.5.0"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
10
+
11
+ [[package]]
12
+ name = "itoa"
13
+ version = "1.0.18"
14
+ source = "registry+https://github.com/rust-lang/crates.io-index"
15
+ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
16
+
17
+ [[package]]
18
+ name = "libc"
19
+ version = "0.2.189"
20
+ source = "registry+https://github.com/rust-lang/crates.io-index"
21
+ checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
22
+
23
+ [[package]]
24
+ name = "memchr"
25
+ version = "2.8.3"
26
+ source = "registry+https://github.com/rust-lang/crates.io-index"
27
+ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
28
+
29
+ [[package]]
30
+ name = "nnnotes-deck"
31
+ version = "0.1.3"
32
+ dependencies = [
33
+ "ournotes-deck",
34
+ "pyo3",
35
+ "serde_json",
36
+ ]
37
+
38
+ [[package]]
39
+ name = "once_cell"
40
+ version = "1.21.4"
41
+ source = "registry+https://github.com/rust-lang/crates.io-index"
42
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
43
+
44
+ [[package]]
45
+ name = "ournotes-deck"
46
+ version = "0.0.1"
47
+ source = "git+https://github.com/empty-sekai/ournotes-deck?rev=dbd9cf01a4854808dceb6aedcd4041af372faeee#dbd9cf01a4854808dceb6aedcd4041af372faeee"
48
+ dependencies = [
49
+ "serde",
50
+ "serde_json",
51
+ ]
52
+
53
+ [[package]]
54
+ name = "portable-atomic"
55
+ version = "1.15.0"
56
+ source = "registry+https://github.com/rust-lang/crates.io-index"
57
+ checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
58
+
59
+ [[package]]
60
+ name = "proc-macro2"
61
+ version = "1.0.107"
62
+ source = "registry+https://github.com/rust-lang/crates.io-index"
63
+ checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
64
+ dependencies = [
65
+ "unicode-ident",
66
+ ]
67
+
68
+ [[package]]
69
+ name = "pyo3"
70
+ version = "0.29.2"
71
+ source = "registry+https://github.com/rust-lang/crates.io-index"
72
+ checksum = "4688ddedf473e32662b9b067670129a8afb8c18e351482c70d62ba4a88171e8b"
73
+ dependencies = [
74
+ "libc",
75
+ "once_cell",
76
+ "portable-atomic",
77
+ "pyo3-build-config",
78
+ "pyo3-ffi",
79
+ "pyo3-macros",
80
+ ]
81
+
82
+ [[package]]
83
+ name = "pyo3-build-config"
84
+ version = "0.29.2"
85
+ source = "registry+https://github.com/rust-lang/crates.io-index"
86
+ checksum = "f41027e41b4bd03f6e60f9f417fe24a6341a6bb744edd62b6f709f2a52ea30e9"
87
+ dependencies = [
88
+ "target-lexicon",
89
+ ]
90
+
91
+ [[package]]
92
+ name = "pyo3-ffi"
93
+ version = "0.29.2"
94
+ source = "registry+https://github.com/rust-lang/crates.io-index"
95
+ checksum = "e591a95526fead067432c3b3a33fc74770b87b1e04e73671090d9c2055a2b327"
96
+ dependencies = [
97
+ "libc",
98
+ "pyo3-build-config",
99
+ ]
100
+
101
+ [[package]]
102
+ name = "pyo3-macros"
103
+ version = "0.29.2"
104
+ source = "registry+https://github.com/rust-lang/crates.io-index"
105
+ checksum = "73225868fc1cd84eef2c3c230ddb91273bf1de46aeb8a4248da76d32a0924a1c"
106
+ dependencies = [
107
+ "proc-macro2",
108
+ "pyo3-macros-backend",
109
+ "quote",
110
+ "syn 2.0.119",
111
+ ]
112
+
113
+ [[package]]
114
+ name = "pyo3-macros-backend"
115
+ version = "0.29.2"
116
+ source = "registry+https://github.com/rust-lang/crates.io-index"
117
+ checksum = "571575aa3749fa6216757dd47d2a3e7ef360f329a40f0666a9fbd14889024952"
118
+ dependencies = [
119
+ "heck",
120
+ "proc-macro2",
121
+ "quote",
122
+ "syn 2.0.119",
123
+ ]
124
+
125
+ [[package]]
126
+ name = "quote"
127
+ version = "1.0.47"
128
+ source = "registry+https://github.com/rust-lang/crates.io-index"
129
+ checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
130
+ dependencies = [
131
+ "proc-macro2",
132
+ ]
133
+
134
+ [[package]]
135
+ name = "serde"
136
+ version = "1.0.229"
137
+ source = "registry+https://github.com/rust-lang/crates.io-index"
138
+ checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
139
+ dependencies = [
140
+ "serde_core",
141
+ "serde_derive",
142
+ ]
143
+
144
+ [[package]]
145
+ name = "serde_core"
146
+ version = "1.0.229"
147
+ source = "registry+https://github.com/rust-lang/crates.io-index"
148
+ checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
149
+ dependencies = [
150
+ "serde_derive",
151
+ ]
152
+
153
+ [[package]]
154
+ name = "serde_derive"
155
+ version = "1.0.229"
156
+ source = "registry+https://github.com/rust-lang/crates.io-index"
157
+ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
158
+ dependencies = [
159
+ "proc-macro2",
160
+ "quote",
161
+ "syn 3.0.6",
162
+ ]
163
+
164
+ [[package]]
165
+ name = "serde_json"
166
+ version = "1.0.151"
167
+ source = "registry+https://github.com/rust-lang/crates.io-index"
168
+ checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
169
+ dependencies = [
170
+ "itoa",
171
+ "memchr",
172
+ "serde",
173
+ "serde_core",
174
+ "zmij",
175
+ ]
176
+
177
+ [[package]]
178
+ name = "syn"
179
+ version = "2.0.119"
180
+ source = "registry+https://github.com/rust-lang/crates.io-index"
181
+ checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
182
+ dependencies = [
183
+ "proc-macro2",
184
+ "quote",
185
+ "unicode-ident",
186
+ ]
187
+
188
+ [[package]]
189
+ name = "syn"
190
+ version = "3.0.6"
191
+ source = "registry+https://github.com/rust-lang/crates.io-index"
192
+ checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee"
193
+ dependencies = [
194
+ "proc-macro2",
195
+ "quote",
196
+ "unicode-ident",
197
+ ]
198
+
199
+ [[package]]
200
+ name = "target-lexicon"
201
+ version = "0.13.5"
202
+ source = "registry+https://github.com/rust-lang/crates.io-index"
203
+ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
204
+
205
+ [[package]]
206
+ name = "unicode-ident"
207
+ version = "1.0.26"
208
+ source = "registry+https://github.com/rust-lang/crates.io-index"
209
+ checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
210
+
211
+ [[package]]
212
+ name = "zmij"
213
+ version = "1.0.23"
214
+ source = "registry+https://github.com/rust-lang/crates.io-index"
215
+ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
@@ -0,0 +1,23 @@
1
+ [package]
2
+ name = "nnnotes-deck"
3
+ # the nnnotes version (src/nnnotes/__init__.py); the wheel takes its version from here
4
+ version = "0.1.3"
5
+ edition = "2024"
6
+ rust-version = "1.85"
7
+ description = "nnnotes._deck: the ournotes-deck chart statistics as a Python extension module"
8
+ license = "MIT"
9
+ publish = false
10
+
11
+ [lib]
12
+ name = "_deck"
13
+ crate-type = ["cdylib"]
14
+
15
+ [dependencies]
16
+ pyo3 = { version = "0.29", features = ["abi3-py311"] }
17
+ # pinned by commit; .github/workflows/deck.yml opens a pull request when ournotes-deck moves
18
+ ournotes-deck = { git = "https://github.com/empty-sekai/ournotes-deck", rev = "dbd9cf01a4854808dceb6aedcd4041af372faeee" }
19
+ serde_json = { version = "1", features = ["raw_value", "arbitrary_precision"] }
20
+
21
+ [profile.release]
22
+ lto = "thin"
23
+ codegen-units = 1
@@ -0,0 +1,33 @@
1
+ //! Records the ournotes-deck package the module is built with (version and git commit, from Cargo.lock) as the
2
+ //! environment variables DECK_VERSION, DECK_SOURCE and DECK_COMMIT of the crate.
3
+
4
+ use std::path::Path;
5
+
6
+ fn main() {
7
+ let dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR");
8
+ let lock = Path::new(&dir).join("Cargo.lock");
9
+ println!("cargo:rerun-if-changed={}", lock.display());
10
+ let text = std::fs::read_to_string(&lock).expect("rust/Cargo.lock (the deck commit is read from it)");
11
+ let (mut version, mut source) = (None, None);
12
+ for block in text.split("[[package]]") {
13
+ let field = |k: &str| {
14
+ block.lines().find_map(|l| {
15
+ let v = l.trim().strip_prefix(k)?.trim_start().strip_prefix('=')?.trim();
16
+ Some(v.trim_matches('"').to_string())
17
+ })
18
+ };
19
+ if field("name").as_deref() == Some("ournotes-deck") {
20
+ version = field("version");
21
+ source = field("source");
22
+ }
23
+ }
24
+ let version = version.expect("Cargo.lock has no ournotes-deck package");
25
+ let source = source.expect("ournotes-deck has no source in Cargo.lock");
26
+ // git+https://github.com/empty-sekai/ournotes-deck?rev=<rev>#<commit>
27
+ let (url, commit) = source.split_once('#').expect("ournotes-deck is not a git dependency");
28
+ let url = url.strip_prefix("git+").unwrap_or(url);
29
+ let url = url.split_once('?').map_or(url, |(u, _)| u);
30
+ println!("cargo:rustc-env=DECK_VERSION={version}");
31
+ println!("cargo:rustc-env=DECK_SOURCE={url}");
32
+ println!("cargo:rustc-env=DECK_COMMIT={commit}");
33
+ }
@@ -0,0 +1,2 @@
1
+ max_width = 120
2
+ use_small_heuristics = "Max"
@@ -0,0 +1,116 @@
1
+ //! `nnnotes._deck`: the chart statistics of ournotes-deck (`ournotes-deck.chart-stats/2`) on a deck data document
2
+ //! held in memory, the charts measured in parallel.
3
+
4
+ use std::sync::Mutex;
5
+ use std::sync::atomic::{AtomicUsize, Ordering};
6
+
7
+ use ournotes_deck::chartstats::{self, AptitudeOptions, ChartStats, GEKISOU_SEEDS, Options};
8
+ use ournotes_deck::data::DeckData;
9
+ use ournotes_deck::error::Error;
10
+ use pyo3::exceptions::PyValueError;
11
+ use pyo3::prelude::*;
12
+ use pyo3::types::PyDict;
13
+
14
+ fn value_error(e: impl std::fmt::Display) -> PyErr {
15
+ PyValueError::new_err(e.to_string())
16
+ }
17
+
18
+ /// The statistics of every chart, in chart order, on `workers` threads.
19
+ fn measure(data: &DeckData, options: &Options, workers: usize) -> Result<Vec<ChartStats>, Error> {
20
+ let kinds = chartstats::kinds(&data.master);
21
+ let n = data.charts.len();
22
+ let next = AtomicUsize::new(0);
23
+ let out: Mutex<Vec<Option<Result<ChartStats, Error>>>> = Mutex::new((0..n).map(|_| None).collect());
24
+ std::thread::scope(|s| {
25
+ for _ in 0..workers.clamp(1, n.max(1)) {
26
+ s.spawn(|| {
27
+ loop {
28
+ let i = next.fetch_add(1, Ordering::Relaxed);
29
+ if i >= n {
30
+ break;
31
+ }
32
+ let c = &data.charts[i];
33
+ // as chartstats::document names a chart's domain errors
34
+ let r = chartstats::chart_stats_with(&data.master, c, &kinds, options).map_err(|e| match e {
35
+ Error::Domain(m) => Error::Domain(format!("chart {}: {m}", c.score_id)),
36
+ e => e,
37
+ });
38
+ let failed = r.is_err();
39
+ out.lock().expect("results")[i] = Some(r);
40
+ if failed {
41
+ next.store(n, Ordering::Relaxed);
42
+ }
43
+ }
44
+ });
45
+ }
46
+ });
47
+ // charts are taken in order and a taken chart always completes, so every chart before the first failure has
48
+ // its result: the error returned is the first in chart order, as the command's
49
+ let mut stats = Vec::with_capacity(n);
50
+ for r in out.into_inner().expect("results") {
51
+ stats.push(r.expect("a chart before the first failure has run")?);
52
+ }
53
+ Ok(stats)
54
+ }
55
+
56
+ /// chart_stats(data, seeds=None, workers=None, aptitude=True, aptitude_max_seeds=None,
57
+ /// aptitude_cross_seeds=None) -> str
58
+ ///
59
+ /// The `ournotes-deck.chart-stats/2` document (JSON text) of a deck data document (`nnnotes.deck-data/1` JSON text),
60
+ /// as `ournotes-deck chart-stats` writes it. `seeds`: the size of the seed set of charts with a luck range (default
61
+ /// 8); `workers`: threads measuring charts (default: the available parallelism). Raises ValueError for data the
62
+ /// deck model cannot read or a chart whose check deck fails. `aptitude`: measure single Gekisou skill shapes
63
+ /// (default true); `aptitude_max_seeds` and `aptitude_cross_seeds`: sample caps (defaults 65536 and 64).
64
+ #[pyfunction]
65
+ #[pyo3(signature = (data, seeds=None, workers=None, aptitude=true, aptitude_max_seeds=None, aptitude_cross_seeds=None))]
66
+ fn chart_stats(
67
+ py: Python<'_>,
68
+ data: &str,
69
+ seeds: Option<usize>,
70
+ workers: Option<usize>,
71
+ aptitude: bool,
72
+ aptitude_max_seeds: Option<usize>,
73
+ aptitude_cross_seeds: Option<usize>,
74
+ ) -> PyResult<String> {
75
+ let seeds = seeds.unwrap_or(GEKISOU_SEEDS);
76
+ let defaults = AptitudeOptions::default();
77
+ let options = Options {
78
+ seeds,
79
+ aptitude: aptitude.then_some(AptitudeOptions {
80
+ max_seeds: aptitude_max_seeds.unwrap_or(defaults.max_seeds),
81
+ cross_seeds: aptitude_cross_seeds.unwrap_or(defaults.cross_seeds),
82
+ }),
83
+ };
84
+ let workers = workers.unwrap_or_else(|| std::thread::available_parallelism().map_or(1, |n| n.get()));
85
+ py.detach(|| {
86
+ let mut data = DeckData::from_json(data).map_err(value_error)?;
87
+ let charts = std::mem::take(&mut data.charts);
88
+ // the document of no chart: the format, source, model and kinds, exactly as the command writes them
89
+ let mut doc = chartstats::document_with(&data, &options).map_err(value_error)?;
90
+ data.charts = charts;
91
+ let stats = measure(&data, &options, workers).map_err(value_error)?;
92
+ doc["charts"] = serde_json::to_value(stats).map_err(value_error)?;
93
+ serde_json::to_string(&doc).map_err(value_error)
94
+ })
95
+ }
96
+
97
+ /// info() -> dict: the ournotes-deck package of this module (name, version, source, commit) and the formats it reads
98
+ /// and writes.
99
+ #[pyfunction]
100
+ fn info(py: Python<'_>) -> PyResult<Bound<'_, PyDict>> {
101
+ let d = PyDict::new(py);
102
+ d.set_item("name", "ournotes-deck")?;
103
+ d.set_item("version", env!("DECK_VERSION"))?;
104
+ d.set_item("source", env!("DECK_SOURCE"))?;
105
+ d.set_item("commit", env!("DECK_COMMIT"))?;
106
+ d.set_item("dataFormat", ournotes_deck::data::FORMAT)?;
107
+ d.set_item("format", chartstats::FORMAT)?;
108
+ Ok(d)
109
+ }
110
+
111
+ #[pymodule]
112
+ fn _deck(m: &Bound<'_, PyModule>) -> PyResult<()> {
113
+ m.add_function(wrap_pyfunction!(chart_stats, m)?)?;
114
+ m.add_function(wrap_pyfunction!(info, m)?)?;
115
+ Ok(())
116
+ }
@@ -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.2"
7
+ __version__ = "0.1.3"