Command outputs¶
nnnotes <group> <command> [options]
Global options (before or after the command name): --config, --region, --language, --catalog,
--catalog-version, --cache, --store, --master, --apk, --ffmpeg, --vgmstream, --node and --json,
plus --version and --help. They set the settings described in configuration.md, which also
lists the settings each command needs. nnnotes <group> <command> --help prints the options of a command;
cli.md lists every command with its options. The asset export commands (export assets, store plan,
store run, store verify and the catalog versions of the store) are described in assets.md.
Commands that write files take the output path with -o/--out (required). A command prints a short summary on
standard output; with --json it prints its report as JSON (UTF-8). Every JSON file is written by one
writer: UTF-8, LF line endings, non-finite numbers as 1e999 / -1e999 (a NaN is an error). The output is
deterministic for a given installation: the same inputs with the same versions of nnnotes, its Python dependencies
and the external tools give byte-identical files. Other versions can encode the same content into other bytes (a
PNG written by another Pillow version can differ in its bytes while its pixels are equal).
nnnotes sets OPENBLAS_NUM_THREADS=1 unless the environment sets it: numpy's OpenBLAS otherwise starts one busy
thread per CPU in every process, and no command uses its parallelism.
A key, id or model the catalog or the master data does not have is a usage error: the command stops with exit
status 2 and a line naming it (nnnotes <command>: error: ...), before any bundle is fetched or file written.
config¶
nnnotes config init [--user | --file F] [--set SECTION.KEY=VALUE ...] [--no-input] [--force]
nnnotes config set SECTION.KEY VALUE [--user | --file F]
nnnotes config unset SECTION.KEY [--user | --file F]
nnnotes config check [--json]
nnnotes config path [--json]
Write the config file from the template (asking in a terminal, else from --set), set or empty one value in place,
check every setting's origin and format, and list where the config file is looked up. No config command prints a
setting's value. Details in configuration.md.
Cache¶
bundle pull and every extractor read bundles through the cache ([paths] cache):
<cache>/catalog_main_<language>.bin the legacy main catalog (explicit main or no API/version pin)
<cache>/bundles/<bundle file name> bundles of the dependency closures, decrypted (UnityFS)
<cache>/raw/<path> raw CDN files stored as they are (e.g. CRI cue sheet data)
<cache>/catalogs/<region>/ catalogs downloaded by `catalog browse`
<cache>/international/<CDN SHA256>/<resource version>/
versioned catalog, bundles and raw files; isolated from old main caches
Files already in the cache are not downloaded again. When [paths] apk is set, the APK's own catalog is merged with
the region's and bundles that ship inside the APK are read from it.
For international regions with a configured API, catalog-based commands discover resource_version first and
download catalog_<resource_version>_<language>.bin. catalog fetch labels the bytes with the version used to
select that file, rather than querying a newer label after downloading main. Use --catalog-version VERSION or
the version settings to pin builds and use cached data offline. An explicit --catalog FILE
bypasses discovery. JP keeps its existing Version-driven path.
catalog keys¶
nnnotes catalog keys [--prefix PREFIX] [--limit LIMIT]
Prints the addressable keys that start with PREFIX (default: all), sorted, at most LIMIT (default 200), one per
line; the total count goes to standard error.
catalog browse¶
nnnotes catalog browse [--port PORT] [--host HOST]
Serves a local web page on HOST:PORT (default 127.0.0.1:8000) that lists every configured region, its catalog
languages, and per catalog the asset paths as directories. A link under an asset path downloads the bundle holding
it, decrypted; bundles/<bundle key> lists every bundle directly.
bundle pull¶
nnnotes bundle pull KEY [KEY ...]
Fetches the bundle closure (the key's location and all its dependencies) of each key into the cache and prints the cached path of every bundle.
servers¶
nnnotes servers [--show-hosts]
Reads the server list from the bootstrap API root ([bootstrap] api) with an anonymous call to the game's API and
reports {servers: [{name, displayName, areaId, region, cdnRoots, apiRoots}]}: per server its name, its area id, the
configured region ([servers.<region>] table) whose cdn or api is one of the server's roots, or null, and how
many alternative CDN and API roots the server list gives. --show-hosts adds the roots themselves (cdn, api:
lists), ready for the cdn and api settings of a region. The exit status is 1 when the call fails.
master version¶
nnnotes master version
Asks the region's API root ([servers.<region>] api) for the master data version and the resource version the
region serves now (the game's API, anonymous) and reports {region, masterVersion, resourceVersion}. The client
version sent with the call is [client] version, else the versionName of [paths] apk. The exit status is 1 when
the call fails.
master decode¶
nnnotes master decode INPUT [INPUT ...] -o OUT [--workers N]
INPUT: master data files, or directories whose *.bin files are decoded. Each file (64-byte prefix, then
Rijndael-256 CBC with PKCS7 padding over gzip-compressed JSON) is written as OUT/<file stem>.json (UTF-8, one-space
indent, non-finite numbers as 1e999 / -1e999; a table with a NaN fails). --workers (default: the CPUs the process may use; 0: one at a time) decodes in parallel. The report is
{decoded, failed: [{file, error}], out}; the exit
status is 1 when a file failed.
master download¶
nnnotes master download (--version VERSION | --latest) -o OUT [--fetch-workers N]
Downloads master data version VERSION, or with --latest the version the region serves now (as
master version), from the region's CDN: OUT/MasterManifest.json and every .bin file it lists, each checked
against the manifest's SHA-256. Files already present with the right hash are kept.
--fetch-workers (default 16) downloads in parallel. The report is {version, files, downloaded, kept, failed, out};
the exit
status is 1 when a file failed or, with --latest, the version could not be fetched.
export episode¶
nnnotes export episode ADV_ID -o OUT.json
One ADV episode as JSON: advId, asset, commandCount, commands (the episode's command list with the text,
sound and video rows resolved), text (every line in the five languages), sounds, cuesheets, videos,
resources (kind, address, present in the catalog), master (the MasterAdv row) and title.
resources lists the assets the game loads for the episode's command rows (rows marked IgnoreData load nothing;
cue sheets are in cuesheets): live2d, stage, still, frame, effect, posteffect, timeline and
chatstamp named by the row's asset name; transition for FadeIn / FadeOut rows (a row without a transition name
takes the player settings' default transition, which is read from [paths] apk); talkwindow for TalkWindow rows;
chatwindow and chaticon from the MasterAdvChat row of the chat rows' chat id; video from the episode's video
row of a Movie / Clip row's video id.
export story¶
nnnotes export story ADV_ID -o OUT [--models MODELS] [--force] [--format flac|ogg|wav] [--flac-level N] [--no-audio]
[--fonts open|game]
One ADV episode as a directory, and its Live2D models in a models directory:
MODELS/<id>/ every Live2D model of the episode (MODELS: `--models`, default OUT/../live2d), the files
of a site's model manifest (see `site build`): model.json, moc3, prefab, the atlas pages and the
shader programs its drawables use
OUT/episode.json as `export episode`
OUT/audio/<cueSheet>/ every cue sheet of the episode, one file per cue + cues.json, streams.json
OUT/scene.json player graphics, cameras, ADV fields, volumes, settings and stages
OUT/frames.json Frame prefabs by asset name: uGUI, Animator controllers and clips, UI particle systems
OUT/effects.json particle effect prefabs by asset name, `instances` (effect name -> asset name)
OUT/posteffects.json PostEffect volume profiles by asset name
OUT/stills.json Still prefabs by asset name
OUT/talkwindows.json talk window prefabs of the TalkWindow rows
OUT/chat.json chat window prefabs, icons and stamps (sprites), the MasterAdvChat rows, shared chat texts,
sounds and cue sheet rows
OUT/textures/, shaders/ textures and shaders of the scene and of the files above
OUT/videos/ Movie / Clip videos as WebM + videos.json (video id -> file, video row, size, frame rate)
OUT/ui/ ADV UI: ui.json (front canvas, still / frame / video canvases and their camera, letterbox),
packed textures, UI shaders, rule transitions
OUT/crilips/ crilips.json + crilips.bin: the CRI Lips analysis data, when a voice reaches the analysis
OUT/story.json index of the above (a file the episode does not need is null and not written)
story.json models maps the key of each model to its id (<name> of Character/Live2D/<group>/<name>/model/<name>)
and modelsDir is the path from OUT to MODELS. A model directory that exists is used as it is, so the stories of one
models directory export each model once; --force exports the episode's models again. The printed summary lists
the models exported (modelsBuilt) and those used as they were (modelsSkipped). Several export story processes may
share a models directory: a model appears in it in one rename, whole, and a model two processes export at once is
kept from the first to finish (the other's export, the same bytes, is dropped and counted in modelsSkipped).
--format (default flac) is the audio format and --flac-level its FLAC compression level (as for export audio);
--no-audio leaves the cue sheets undecoded (no audio/, audio
in story.json is empty). With audio, crilips/ holds the weights of the game's CRI Lips mouth analysis, read from the
CRI Lips library in [paths] apk (or in the arm64-v8a split next to it, split_config.arm64_v8a.apk), when a voice
reaches that analysis: a lip-synced voice while a model has no MotionSync controller (model.json motionSync), or a
voice that other speakers follow (story.json crilips; ournotes-player docs/crilips.md). Videos keep the VP9 stream
of the game's USM file and carry its ADX audio as Opus (FFmpeg,
[paths] ffmpeg); the USM streams are unmasked with the CRI key read from [paths] apk. TextMesh Pro text in
frames, talk windows and chat keeps its layout and style; with --fonts open its font and sprite assets and text
materials are only named (no atlas is written), with --fonts game they are exported. The command stops before
writing anything when the episode uses resources that are not in the catalog or resource kinds that are not
supported (timeline). Prints the index with a summary.
The shared chat sound rows (chat.json sounds, cueSheets) name the cue sheet AdvSe_Common. In the version
1.0.1 data nothing holds it: no catalog language (ja, en, zh-Hant, zh-Hans, ko) has a key
Cri/Sound/AdvSe_Common or EmbCri/Sound/AdvSe_Common (remote or APK catalog), the APK has no ACB file of that
name, and it is not a Resources asset. The story therefore has no audio for these rows; the rows are kept as they
are. Every cue sheet of the episodes' own -SoundCueSheet shards has its Cri/Sound/ key.
ui/ui.json holds the parts of the game's ADV widget the story draws: the front canvas (talk window, speaker plate,
location caption, title, rule transition, curtains, flash, subtitles caption, next indicator, menu entry button, menu
panel and video buttons, backlog, choices, tap areas), the canvases of stills, frames and videos with the screen
image and the camera they render with (videoAndStillCamera), the UI camera that renders the frame canvas
(uiCamera), and the letterbox bands; per node its transform, rect
and uGUI components, the views' serialized references as node paths (the menu view with its fast-forward sprites),
the other game components' serialized fields (behaviours), the widget's canvas sort orders (widget), the ADV
screen's dialogs (dialogs: the skip confirm and common dialog prefabs) and their texts of the language
(masterIdTexts), the chat phone (chatWidget), the text
records of the episode's chat windows (chatTexts) with their status texts (chatStatusTexts). The talk windows
sit under TalkView: the default window,
then each other window the episode's TalkWindow rows attach; when one of them dims and blurs the screen behind its
talk (UICenterTalkWindow), the front canvas' CenterTalkBackdrop node, the renderer's blur settings (blur) and
the blur shader come with it.
ui/ follows the client language [catalog] language (ja, en, zh-Hant, zh-Hans or ko): the localized
fonts and materials, the line spacing LocalizeText applies, and, with --fonts game, the characters of the lines
(Talk, Location and Subtitles rows, ruby readings included), speaker names, title and UI texts in that language
(ui/ui.json language: mode, field, lineSpacing).
--fonts (default open) chooses how ui/ handles text. Both modes write a text record textStyle for each text
node of ui/ui.json, and a document-level textStyle that holds the units and the line metrics of each font role.
With open, ui/ contains no font data: no font atlases, glyph tables, font materials or text shader. game also
exports the game's TextMesh Pro fonts: per node the serialized text settings with the localized font and material
(text), and in the document the font assets reduced to the characters the episode shows (fonts), runtime glyphs,
text materials, glyphCoverage, tmpSettings and the text shader. game needs the optional fonts dependencies
(pip install -e ".[fonts]").
The ui/ui.json text record (textStyle of a node; the node's rect and transform give its layout box):
| Field | Value |
|---|---|
class, enabled |
TMP component class, enabled flag |
fontRole |
font slot of the game's localized fonts: primary (main text face), number (Latin / numeral face) |
materialType |
material style name of the text (Default, OutlineAdvCommon, ...) |
localized, textKey |
LocalizeText enabled; its master text id, or null when the runtime sets the text (talk, speaker, location and title texts come from episode.json, one field per language) |
text, richText, parseControlCharacters |
serialized text, rich text tags on, \n-style escapes parsed |
fontSize, autoSize |
size in canvas units; auto-size enabled, min, max, maxCharWidthAdjust, maxLineSpacingAdjust |
fontStyle, fontWeight |
style flags (bold, italic, underline, strikethrough, lowerCase, upperCase, smallCaps, superscript, subscript, highlight), weight 100-900 |
alignment |
horizontal (left, center, right, justified, flush, geometry), vertical (top, middle, bottom, baseline, geometry, capline) |
wrapping, overflow |
noWrap / normal / preserveWhitespace / preserveWhitespaceNoWrap; overflow / ellipsis / masking / truncate / scrollRect / page / linked |
margin |
left, top, right, bottom insets of the rect |
lineSpacing |
serialized, applied (the value for the export language), byLanguage (the per-language line spacing LocalizeText applies; null when not localized) |
paragraphSpacing, characterSpacing, wordSpacing |
spacing in 1/100 em |
characterHorizontalScale, kerning, rightToLeft, orthographic |
as serialized |
color, colorMode, colorGradient, overrideHtmlColors |
vertex colour, gradient mode, four-corner gradient (null when off) |
face |
color (fill = color x face.color), dilateEm, boldDilateEm (glyph edge moved outwards), softnessEm (edge ramp width) |
outline |
null, or color, widthEm (band on each side of the glyph edge: stroke width 2 x widthEm, drawn over the fill), softnessEm |
underlay |
null, or color, offsetEm [x, y] (+x right, +y down), dilateEm, softnessEm, inner (shadow inside the glyph) |
*Em values are fractions of the drawn font size, converted from the text material's distance-field properties the
way the TMP shader applies them. The document-level textStyle.roles gives, per role, lineHeightEm, ascentEm,
descentEm (line pitch = lineHeightEm + lineSpacing / 100 em) and the font's spacingOffset / boldSpacing
(1/100 em) for the export language.
export live2d¶
nnnotes export live2d MODEL -o OUT
A Live2D (Cubism) model prefab as a Cubism runtime directory. MODEL is the model's key
Character/Live2D/<group>/<name>/model/<name> or its id <name> (nnnotes catalog keys --prefix Character/Live2D/ lists
the keys):
OUT/<name>.moc3 the model's moc3
OUT/textures/*.png atlas pages
OUT/<name>.prefab.json the whole prefab: every GameObject and component, motions and expressions inlined
OUT/<exp>.exp3.json expressions
OUT/<name>.physics3.json physics (not written for a model without physics)
OUT/motions/<motion>.motion3.json motions (and motions/_fades.json)
OUT/<name>.model3.json file references, EyeBlink / LipSync groups
Needs [paths] apk (component classes are resolved through the APK). Prints a JSON summary.
export spot¶
nnnotes export spot SPOT_ID -o OUT
OUT/spot.json master row, names, tap-talk episodes, situation settings, tap targets, Spine characters
OUT/spine/ Spine skeletons (.json or .skel), atlases, atlas page PNGs
OUT/room.glb the spot's background as binary glTF (as `export room`), with room.json
OUT/shaders/ shaders of the spot's bundles (as `export shader`)
Needs [paths] apk and master data (MasterHomeSpot, MasterText, MasterStoryHomeSpotTapTalkEpisode). room.glb and
room.json are the export room export of the spot's background.
A reference the situation prefab leaves empty is written as null: a Spine character with no _animation has null
skeletonData, animation and world, and a tap target with no _focus has a null focusWorld.
export room¶
nnnotes export room KEY -o OUT.glb
A spot background prefab as binary glTF: every mesh baked into prefab space, converted to glTF's right-handed space,
textures embedded, materials translated from the shader's render state. Objects inactive in the prefab are kept with
extras.unityActive = false. A summary (meshCount, materials, textures, samplers) is written next to it as
OUT.json.
Materials and textures are told apart by serialized file and path id (the prefab's bundle and its dependencies can
reuse a path id). Each texture is one glTF texture with its own sampler, and textures whose PNG bytes are equal share
one glTF image. The summary's textures has one entry per glTF texture, in glTF order (the index a material's
baseColorTexture names): the Unity texture's name.
The render state is that of the shader's first pass for the material: blend factors (colour and alpha), blend
operations, colour mask, culling, depth write, depth test and alpha to mask. A state the shader takes from a
property (Blend [_SrcBlend] [_DstBlend], Cull [_Cull], ZWrite [_ZWrite], ...) is the material's value of that
property, else the shader's default for it. Each glTF material keeps the resolved state in
extras.unityRenderState (and OUT.json in materials[].renderState); a shader or a state glTF cannot express
stops the command. A submesh whose material slot is empty is not written (there is no material to draw it with): the
summary lists each as nullMaterialSubmeshes (mesh, object path in the prefab, submesh index), and a mesh
left with no submesh is not written. A MeshFilter with no mesh, or a mesh with no triangles (no index data), draws
nothing and is not written; the summary lists the object path in nullMeshFilters, or the mesh and object path in
meshesWithoutTriangles.
export shader¶
nnnotes export shader (--key KEY | --apk-bundle SUBSTRING [SUBSTRING ...]) -o OUT
Every Shader object in the bundle closure of KEY, or in the APK bundles whose file names contain the substrings
(each substring must match exactly one bundle; needs [paths] apk):
OUT/<name>.json properties, subshaders, passes, render state, keywords
OUT/<name>/<platform>/s<S>p<P>_<stage>_<N>.<ext> every compiled sub-program (GLSL ES as text, others as stored)
OUT/shaders.json index with the keywords of every variant
export audio¶
nnnotes export audio CUE_SHEET -o OUT [--format flac|ogg|wav] [--flac-level N]
Decodes the CRI cue sheet of the key Cri/Sound/<CUE_SHEET>: one file per stream (a name repeated within the
sheet gets <name>_<stream>), cues.json (first stream per name: file, sample rate, channels, samples, loop
points) and streams.json (every stream in order). flac (default) keeps the decoded PCM bit-exact; ogg is lossy.
--flac-level (0 to 12, default 8) is ffmpeg's FLAC compression level; every level decodes to the same samples.
The HCA keycode is read from [paths] apk.
voices¶
nnnotes voices list [--from OUT] [--character C] [--category C] [--source S] [--episode ADV] [--status S]
[--limit N]
nnnotes voices search TEXT [--from OUT] [--search-language L] [--character C] [--category C] [--source S]
[--episode ADV] [--status S] [--limit N]
nnnotes voices summary [--from OUT]
nnnotes voices get ID -o DIR [--from OUT] [--format flac|ogg|wav] [--flac-level N]
Queries the voices index (views.md): the character voices the master data names and the voices
of the story episodes, with their source row, characters (for story voices, inferred from the speaker), category,
five-language text, cue sheet and cue. --from OUT reads OUT/views/voices.json, written by
nnnotes export assets --views voices (or all) with the exported audio and episodes (--select key:Adv/Episode/): then
every voice also has its status and the files of its streams. Without --from the index is built from the
configured master data and catalog: content and catalog keys only, every present voice not-exported; story
voices only for the episode of --episode (or of a get row id), read from the catalog.
--character: a MasterCharacter id, or a part of a character's name in any language (full, short or English display name, case ignored).--category: a category (CharacterRankUp; case,-and_ignored, socharacter-rank-upworks),Source.Category(Story.Main) or a source (Talk).--source:Talk,CharacterVoice,MemberCard,LiveCharacter,LiveGekisouVoice,LiveDialogueCommon,LiveDialogueFixedPair,LiveStartCharacterVoice,HomeSpot,Title,SoundorStory.--episode: the story voices of one episode, by MasterAdv id or episode asset.--status: a status of the index.listandsearchprint one line per voice: id, characters (story voices: the speaker's name in--language),Source.Category,sheet/cue, status and the text in the global--language(default[catalog] language);--jsonprints the rows.search TEXTmatches a part of the text in--search-language(every language when not given) or of the cue name, case ignored.summary: rows per status, per source and category and per character, the gaps (voices whose sheet has no key or whose decoded sheet lacks the cue, episodes without a key) and the unreferenced keys and streams; for the story episodes, their number per status and kind, the rows and inferred characters per speaker name, and the story voices whose cue a row of another source also names.get ID:IDis a row id (MasterTalk:561,MasterAdv:<adv id>:<command index>) or a sound id. Writes the files of the voice's streams intoDIR, named as the export names them, andvoice.json(its rows and files). With--fromand the files exported, they are copied; otherwise (or with--flac-level) the voice's cue sheet alone is decoded asnnnotes export audiodoes (--format,--flac-level, default 8) and the cue's streams are picked by the ACB's cue table. Exit status 1 when the voice has no file (its status says why).
apk hca-key¶
nnnotes apk hca-key [--write DIR]
Looks up the CRI HCA keycode in the APK's boot data and prints whether one was found (and its number of digits,
not its value). --write writes it as DIR/.hcakey (8 bytes, big-endian), the file vgmstream reads next to its
input; DIR is created when missing.
apk settings¶
nnnotes apk settings -o OUT.json
The game's render settings from the APK's boot data as JSON: colorSpace, defaultPipeline, qualityLevels,
qualityPerPlatform, pipelines, renderers, postProcessData. Needs [paths] apk. The MonoBehaviours are read
with the type trees that ship with nnnotes; an APK whose classes do not match them stops the command with exit
status 2 and a line naming the class, the game version and the Unity version (as export story, export live and site build).
export live¶
nnnotes export live MUSIC_ID -o OUT [--difficulty easy|normal|hard|expert] [--format flac|ogg|wav]
[--flac-level N] [--fonts open|game] [--band BAND | --leader-card CARD_ID]
[--live-option OPTION[=VALUES] ...]
One chart (music + difficulty, default expert) as a self-contained directory, the format ournotes-player reads:
OUT/score/ the chart as shipped, the converted runtime notes, master rows (title and band
names in every language), summary.json
OUT/audio/<cueSheet>/ the BGM cue sheet decoded per cue + cues.json, streams.json
OUT/audio/live-audio.json the live's sounds (BGM, note SE, live SE: the start and finish cheers and the finish
sound of every result, all perfect, full combo, assist full combo and clear) with
their CRI routing; their cue sheets decoded next to the BGM
OUT/livescene/ scene graph, cameras, lane, background, start timeline, textures and shaders
OUT/livenotes/ note, line and effect prefabs, skins, clips, particle systems, textures and shaders
OUT/liveui/ the start canvas in [catalog] language: strings, text records or fonts, sprites
OUT/live.json index of the above
--format (default flac) is the format of every decoded cue sheet and --flac-level its FLAC compression level
(as for export audio).
The start canvas follows the client language [catalog] language (ja, en, zh-Hant, zh-Hans or ko): its
text table column, fonts and line spacing. --fonts open (default) writes each text's layout and style (size,
alignment, wrapping, spacing, colours, outline / underlay from its material) and only the names of the game's fonts
and materials; --fonts game also writes the game's TextMesh Pro fonts reduced to the characters shown, with their
atlas textures (needs the optional fonts dependencies: pip install 'nnnotes[fonts]').
The game takes the band of the background and start timeline from the player's deck centre. Without a deck the
band is --band, or the band of the character of --leader-card (a MasterMemberCard id), or by default the band
of the music's first vocal character; the choice is recorded in livescene/scene.json. Prints the index with a
summary.
The directory holds what a live reads with the game's default options (option preset 1 of a fresh profile).
--live-option (repeatable) adds the files of other values of the options that select files, so that a player can
offer them; the options are named as in the game (the names ournotes-player's settings use):
--live-option |
Adds |
|---|---|
MirrorChart |
score/<chart>.mirror.notes.json, the chart converted with lanes and flick directions mirrored (the game mirrors while it converts the chart); live.json notesMirror |
NoteDesignId |
the other note skins (MasterLiveNoteSkin): livenotes/notes.json noteSkins (skin asset name -> a record like noteSkin) and settings.skins (NoteDesignId -> skin asset name) |
NoteEffectId |
the other note effect sets (MasterLiveNoteEffectSkin; the lane effects are the same for every set): their assets in livenotes/notes.json assets, and settings.effects (NoteEffectId -> effect set name) |
LiveQuality |
the qualities of MasterLiveQualitySettings; at the Low quality (2) the game loads the <name>Light variant of a note effect set where the catalog has one (else the set itself), whose assets are added (and settings.effects) |
NoteSePatternId |
the other note sound sets (MasterLiveNoteSe groups): audio/live-audio.json noteSe.groups (set -> LiveNoteSeType -> sound id) and their sounds, decoded next to the others |
MeasureLineDisplay |
the bar lines: livenotes/notes.json prefabs.bar_line_view, the bar line view the live scene's bar line container instantiates (a node list like the note view prefabs), and its sprite's texture; the bar times and the BPM and time signature changes are in the score's notes (barLineTimeMs, bpmChanges, barChanges) with or without the option |
defaults |
only settings.optionDefaults / optionRanges of every option the player offers (note timing, the live and note sound volumes and mutes, the note sound set and per-type sounds, ...) |
all |
every option above with every value |
An option without values offers every value the master data has; NAME=v,v,... offers those values (the default
value is always included; a value the master data does not have is a usage error). Any option also writes the
defaults tables. Without --live-option the directory holds the files of the default options only.
site build¶
nnnotes site build -o SITE [--pair MUSIC_ID:DIFFICULTY [--pair ...] | --all] [--live2d MODEL [--live2d ...] | --all-live2d]
[--story ADV_ID [--story ...] | --all-stories] [--story-languages LANG,...] [--font LANG=PATH ...]
nnnotes site build -o SITE (--player-only | --reingest-json)
[--player DIR] [--format aac|opus|vorbis|mp3|flac] [--no-audio] [--compress gzip|br|none]
[--force]
[--tmp DIR] [--workers N] [--read-workers N] [--band BAND | --leader-card CARD_ID]
[--site-region REGION [--site-region ...] | --all-regions] [--fonts open|game]
[--live-option OPTION[=VALUES] ...]
Builds or updates a static ournotes-player site of charts, Live2D models, stories or any of them:
SITE/index.html, chart-list.js ... the player's chart list page; ?music=<id>&difficulty=<d> plays one chart
SITE/ournotes-player.element.min.js the player's built bundle (and its source map)
SITE/charts.json chart index: listing facts (texts in every language), regions,
manifest path, sizes
SITE/charts/<musicId>_<difficulty>.json chart manifest: every path the player reads -> {asset, size[, stored]},
or {parts: [[key, asset, size[, stored]], ...], size} for a large JSON
object
SITE/charts/<region>/<id>.json the manifest of a region whose chart files differ (see Regions)
SITE/models.json Live2D model index: id, key, group, canvas, manifest path, sizes; with
master data the character and its names
SITE/models/<id>.json model manifest, the same entry forms as a chart manifest
SITE/live2d/ the player's Live2D model page and its bundle (when the player has them)
SITE/stories.json story index: titles and story groups in every language, commands,
languages, manifest path, sizes, regions
SITE/stories/<advId>.json story manifest: the common files and one file group per language, the
same entry forms as a chart manifest; the model manifests of its models
and the path to the site root
SITE/stories/<region>/<advId>.json the manifest of a region whose story files differ (see Regions)
SITE/story/ the player's story page and its bundle (when the player has them)
SITE/assets/<sha256>.<ext>[.gz|.br] content-addressed files shared by all charts, models and stories
(see --compress)
--pair(repeatable;<musicId>:<difficulty>or<musicId>_<difficulty>) adds the given charts (a chart that no region of the site has aMasterLiveMusicScorerow for is a usage error);--alladds every music and difficulty that has aMasterLiveMusicScorerow (in the master data of any of the site's regions).--live2d(repeatable; a model id<name>or keyCharacter/Live2D/<group>/<name>/model/<name>) adds the given Live2D models;--all-live2dadds every model key of the catalog. A model's id is its<name>. Its manifest lists the files the player's Live2D viewer and story page read:model.json(index: moc3, prefab, textures, shader index, the Cubism mask materials andmotionSync, whether the prefab's root has the MotionSync controller with its CRI audio input), the moc3 and prefab asexport live2dwrites them, the atlas pages the drawables use, and the GLSL ES 3.00 programs of the Live2D shaders that the drawables' materials select, each keyword set also with_ADDITIONAL_LIGHTS_VERTEX(the story player adds it at quality 4), and the mask shader's for a model with masked drawables. Charts and models can be added in one run; models are built first.--story(repeatable; aMasterAdvid) adds the given story episodes (an id no region of the site has is a usage error);--all-storiesadds everyMasterAdvepisode. See Stories below. Stories are built after models and charts; the Live2D models the stories to build use are built before them, as with--live2d(listed inmodels.json, names from the master data as for--live2d).- A chart, model or story whose manifest exists is skipped unless
--force(with stories, their models too). A model manifest whosemodel.jsonis of an older format (withoutmotionSync) is outdated and built again; the summary lists such models inmodelsRebuilt(storyModels.modelsRebuiltfor the models of stories). Assets no chart, no model and no story (common or language file, or model manifest it names) references are removed. --player-onlyrewrites the player files,charts.json,models.jsonandstories.jsononly;--reingest-jsonstores every chart's and model's JSON files again from the site's own assets (then rewrites the player files and the indexes; story manifests are left as they are).--player: the ournotes-player checkout (after its build) or installed package; it must containscripts/read-set.mjs,dist/ournotes-player.element.min.jsandexamples/chart-list/index.html. The read-set script runs under Node.js to list the files the player reads for each chart; only those are stored. When it also hasexamples/live2d/index.html, that page anddist/ournotes-player.live2d.element.min.jsgo toSITE/live2d/; when it hasexamples/story-list/index.htmlanddist/ournotes-player.story.element.min.js, they go toSITE/story/(a build that adds stories stops when the page is there without its bundle).--compress(defaultgzip): how the compressible assets are stored: JSON files (split parts included), GLSL, moc3, atlas, skel, bin, wav and glb files. Withgziporbr(brotli, quality 11) such a file is stored encoded when that makes it smaller:assets/<sha256>.<ext>.gz(or.br), named by the SHA-256 of the decoded bytes; its entry'ssizeis the decoded size andstoredthe size of the stored file (a part:[key, asset, size, stored]). Other files, and every file withnone, are stored as they are, withoutstored.--reingest-jsonstores the JSON files again with the given--compress.- Hosting: the player decodes a
.gzor.brasset when the bytes it receives have the entry'sstoredlength and uses them as they are when they have itssize. A gzip site can be served as plain files;Content-Encoding: gzipon its.gzfiles is optional (with it the browser decodes them, without it the player does). A brotli site needsContent-Encoding: bron its.brfiles for Chromium-based browsers, which cannot decode brotli in the page. --format(defaultaac) is the BGM format of the charts and the format of every story sound (music, sound effects, voices); the charts' note SE, cheers and voices stay FLAC.--no-audiostores no audio (the player then runs the chart silent on its own clock, and plays stories without their sounds; story videos keep their sound track).--workers: parallel music processes (default a quarter of the CPUs, up to 8), model processes (default up to 4) and story processes (default a quarter of the CPUs, up to 8);0or1builds in this process.--read-workers: chart read sets run at a time, each a Node.js process (default half the CPUs, up to 16). With a player whose read-set script lists a chart's files from its plan (without stepping the chart) and serves many charts from one process, the plans run in one long-lived Node.js process per read-set slot, ended with the build; the full simulations that check a sample of the plans still run in a process each.--tmp: directory for the temporary live and model builds (defaultSITE.tmp); the build directories in it are removed after use.SITE.tmp/cache/is kept: decoded cue sheets, encoded PNGs, shader dumps and read sets, each stored under a hash of everything it was made from (input bytes, settings, the external tools and libraries in use and nnnotes' own code), so a later build with the same--tmpreuses what is unchanged and writes the same files faster. Deleting it is safe at any time outside a build; the next build then makes everything again.--band/--leader-card: as forexport live, for every chart.--fonts: for charts as forexport live(the site stores no start canvas files); for stories where the glyphs of the story text come from (see Stories).--live-option(repeatable): as forexport live, for the charts of this run (with--pairor--all). The live directories carry the variants' files, and a chart's read set is the union of its read set with the default options and its read sets with the settings of each offered variant (every combination of the offered mirror, note skin, effect set and quality values, the bar lines on, and each other note sound set; the player's read-set script needs its--settings); only the files a variant reads are stored, the shared ones once per site. A chart manifest lists the offered qualities asoptions({"LiveQuality": [0, 1, 2]}); the other options are offered by the chart's files. Each variant adds a read-set plan per chart (allmakes several dozen), so such a build takes longer. Charts whose manifest exists keep the options they were built with unless--force.- Models need
[paths] apk(component classes and the mask materials are read from the APK). Master data is optional: when it is configured (of the site's first region), each model that aMasterCharacterCostumerow maps to a character (its_live2dPathis the key afterCharacter/Live2D/) gets, in its manifest'smodeland itsmodels.jsonentry,character(theMasterCharacterid),names({language: the character's name}, from theMasterTextrow of_nameTextID, the languages that have a text) andlabel(the name in[catalog] language). A key the rows map to two characters gets none. The manifests of skipped models get this build's fields; without master data the fields are not written and skipped manifests keep theirs. --site-region(repeatable) /--all-regions: the regions the site serves (see Regions); default: the one[catalog] region. The global--regionsets[catalog] region.
Stories¶
A story's manifest lists what the player's story page reads, in the layout of export story (below the site's assets/):
story.json, episode.json, scene.json, the scene shaders (GLSL ES 3.00 programs only), textures, per cue sheet
cues.json and the file of each cue in the --format (an AAC file's encoder delay in
cues.json as encoderDelay), the media files the episode uses and its videos; per language (--story-languages,
default ja,en,zh-Hant,zh-Hans,ko) the story UI in that language (ui/ui.json), its font data (ui/fonts.json with
glyph pages under ui/fonts/) and ui/languages.json. UI files that are equal in every language are common files.
scene.json and ui/ui.json are stored split per top-level key, so the parts stories and languages share are stored
once. The Live2D models are the site's: the manifest names the model manifest of each model the story uses
(models, {id: "models/<id>.json"}, relative to the site root; root is the path from the manifest to the site
root), and stories.json counts their files in size.models. The format is described in the player's
docs/story-data-format.md.
- Story text is laid out with TextMeshPro's rules from TextMeshPro font assets holding exactly the characters the
episode shows in the language (its text table, title and UI labels).
--fonts open(default) generates them from a font file per language:--font LANG=PATH(repeatable) orfonts.<lang>in the[paths]table of the config file (NNNOTES_PATHS_FONTS_<LANG>, e.g.NNNOTES_PATHS_FONTS_ZH_HANT); every language of--story-languagesneeds one. Any OpenType or TrueType font (the first face of a collection) with the characters of the language works. The game's fallback chains are mirrored: where the game takes a character from a fallback font asset of another language's font (Korean text falls back to the Japanese font, for example), the generated asset falls back to an asset of that language's font file, which then needs to be given too (the build stops with the setting it needs when a character reaches it); characters no font file of the chain has are listed inui/fonts.jsoncoverage.missing. Characters the game's own font assets lack are drawn as the game draws them, as its missing glyph (U+25A1 where the game's font has it), not from the font file (coverage.missingGlyph). The simple talk window of an Overlay story is handled the same way. Each generated asset takes the point size, padding, style settings and render mode of the game font asset the texts use in that language, so the game's text materials apply unchanged; those values, the game's fallback chains, the characters the game's assets have and the text materials come from the table of them shipped with nnnotes (src/nnnotes/unity/fontsettings.json,fontsettings.FORMAT), so an open-font export reads no game font asset, material or font bundle; its face info and glyph metrics come from the font file (FreeType, no hinting), its distance field from the generator--fonts gameuses for runtime glyphs (supersampled render modes at up to 8x). The asset records the font's family, version, license and SHA-256.--fonts gameuses the game's font assets instead (thefontsextra); the format is the same. No font file is stored in the site other than as the generated glyph pages. - Emoji are laid out as the game lays them out: its UI texts (the talk and chat texts) draw a character their font
assets lack from the game's emoji sprite asset, and emoji sequences (several code points with U+200D or U+FE0F)
become sprite tags through the game's emoji search. The layout keeps the game's sprite metrics; the images come
from a colour emoji font of your own:
--font emoji=PATHorfonts.emojiin the[paths]table (NNNOTES_PATHS_FONTS_EMOJI), a font with PNG bitmap glyphs (CBDT or sbix tables). Noto Color Emoji (SIL Open Font License 1.1) is the tested font. Only the sprites an episode's texts can draw are generated, each at the game's sprite size, into one page underui/fonts/;ui/fonts.jsonrecords the emoji font as it records a text font. Without an emoji font the sprites keep their layout with empty glyphs, andui/fonts.jsoncoverage.sprites.missinglists them.--fonts gameuses the game's sprite asset. - Stories need
[paths] apkand the optionalfontsdependencies (pip install 'nnnotes[fonts]') in both modes. - The story index's groups come from the story tables of the master data (
MasterStoryEpisodeand its chapter,MasterStoryFriendshipEpisode,MasterStoryHomeSpotTapTalkEpisode,MasterStoryLiveResultEpisode,MasterHomeSpot), with chapter, character and spot names in every language. - An Overlay episode (
MasterAdv._playbackMode1: the game plays it in its simple ADV player over the screen that opened it) is listed withplaybackMode1. With--fonts openits story also has the host screen (host/: the home spot or the live result screen of its story group) and, per language, the simple talk window (ui/simple/ui.json,ui/simple/fonts.jsonand its glyph pages); the manifest names them inhost. The host has no game-font variant: with--fonts gamean Overlay story has neither. - With
--fonts openthe chat window texts of an episode with chat rows get their bindings inui/fonts.jsonchatTexts, and the font assets also hold the chat windows' status texts and the texts they format at run time. The text nodes of the episode's frames get theirs inframeTexts, and the font assets also hold the texts a frame that receives texts is given at run time (the texts of its Frame rows'TargetTextIDs, as the frame formats them). - Stories that fail are listed in the printed summary and in
SITE.story-failures.json; the exit status is then 1.
Regions and languages¶
One site serves several regions and every language:
- The regions serve the same catalog for a language, so a chart's files depend on the region's master data only.
Regions whose chart tables (the master tables the chart build reads) are identical share one manifest,
charts/<id>.json, built once from the first region's data. A region with other chart tables is built on its own intocharts/<region>/<id>.json; such a manifest whose files equal the shared one's is dropped and the region joins the shared manifest. Models read no master data: one build serves every region. Stories follow the charts' rule with the master tables the story build reads (stories/<id>.json,stories/<region>/<id>.json); a region offers the stories of itsMasterAdv. - Each region's master data:
[servers.<region>] master(else[paths] master, for at most one of the regions; the global--masterflag is refused with more than one region). A region offers the charts its master data has aMasterLiveMusicScorerow for. - Chart files come from the catalog of
[catalog] language; the listing texts come from the text tables in every language: a manifest'scharthastitleandbandsin[catalog] language(language), andtitles/bandNamesinja,en,zh-Hant,zh-Hansandko. The manifest'sregionslists the regions it serves; regions accumulate over builds (a skipped chart gains the regions of the build), so a region is dropped by building the site again from scratch. charts.jsonhas one entry per manifest (an id appears once per region) withregions,titles,bandNames, and at the toplanguage(the default listing language:[catalog] languageof the latest build),languagesandregions(id,namefrom[servers.<region>] name,languagesfrom[servers.<region>] languages). The chart list page switches with?region=<id>&lang=<language>.
The same inputs with the same versions of nnnotes, its libraries and tools (and the same font files) give
byte-identical outputs; the encoded assets also need the same zlib and brotli versions. Charts that fail are listed
in the printed summary and in SITE.failures.json, models that fail in the summary and in
SITE.model-failures.json, stories in SITE.story-failures.json; the exit status is then 1.
music-data¶
nnnotes music-data (--master-files DIR | --apk-master | --decoded-master) [--full] [--no-deck] [--seeds N]
[--workers N] [--no-gekisou-aptitude]
[--stats-cache DIR] [--no-bgm] [--jackets DIR] -o FILE
Writes one JSON file with every MasterLiveMusic song and its charts for one master data version: titles, readings
and credits in the five text languages, bands (and a song's own band name), vocal characters, category, tags,
release time, jacket, Gekisou missions, score ranks, the whole MasterLiveMusic row, the live BGM's cue and length
(read from the cue sheet's ACB, without decoding audio); the Gekisou catalog (member cards and snaps with their
characters, bands and Gekisou skills and support skills: missions, highest levels, names and descriptions); per
difficulty the chart facts (level and display level, full combo count, note counts, BPM, note times, the live's music
length, skill event times, fever ranges) and the chart's deck statistics: the no-skill score and the weight of every
score-up skill kind at every performance position, with Gekisou on (a Gekisou live at rank 1, with what every other
rank and the Perfect play need) and off (a solo live), measured by the deck model ournotes-deck (the package ournotes-sim,
installed with pip install 'nnnotes[deck]') on its whole-live simulation and checked against the chart facts and the master data. --full also writes the deck model's input: every
MasterLiveMusicScore row's chart as the client builds it at runtime (notes, skill events, fever ranges) and the
master data tables about cards, skills, bonuses, scores and events. Single-skill Gekisou aptitude is included by
default as independent nominal expectations; --no-gekisou-aptitude omits it.
--no-deck leaves every chart's deck null. --seeds N (default 8) gives replay seeds for luck charts;
--workers N sets measurement threads (default: every processor). --stats-cache DIR keeps every chart's statistics in DIR under the SHA-256 of what
they are a function of (the deck model's sources, these options, the master data tables and the chart) and measures
only the charts it lacks; DIR then holds this file's charts only. The master data is decoded from the files as served: --master-files DIR reads
DIR/MasterManifest.json and the .bin files it lists (master download; the file's region is [catalog] region),
--apk-master the same files inside [paths] apk (region embedded); each file is checked against the manifest's
SHA-256. --decoded-master reads master data decoded elsewhere instead, without the master key: the <Table>.json
files of the master data directory ([paths] master, --master) and the MasterManifest.json of the files they were
decoded from, whose version and SHA-256 the file records (region [catalog] region). --no-bgm skips the cue sheets
(every bgm.length is null). --jackets DIR also writes every song's jacket as DIR/<jacket>.webp (at most 320 px
on the longer side). FILE ending in .gz is written gzip-compressed; the file is canonical: the same inputs and
nnnotes version give the same bytes. Reports {out, format, region, masterSource, masterVersion, songs, charts, deck,
unplayable, full, bgm, jackets, bytes, fileBytes, sha256}. A missing or unreadable input (a master data file, a
column, a text id, a chart asset, a cue sheet or cue, a jacket), a chart the deck model cannot measure, or deck
statistics that disagree with the chart facts stop the command with exit status 1 before the file is written. The
format is described in music-data.md.