Music data¶
nnnotes music-data writes one JSON file with every live song and chart of one master data version: what a song
listing shows (titles and credits in every language, bands, vocal characters, category, tags, release time, score
ranks, jacket, BGM length), the Gekisou catalog (member cards, snaps and their Gekisou skills), per difficulty the
chart facts (level, note counts, BPM, chart times, skill events, fevers), and per chart its deck statistics: what
the chart contributes to the live score whatever the deck, in a solo live (Gekisou off) and in a Gekisou live at every
rank, measured by the deck model ournotes-deck, the package
ournotes-sim that the extra deck installs (pip install 'nnnotes[deck]'). The format is nnnotes.music-data/2; its JSON Schema is
music-data.schema.json.
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
--master-files DIR: master data files as served,DIR/MasterManifest.jsonand the.binfiles it lists (nnnotes master download). The file'sregionis[catalog] region(--region).--apk-master: the master data files the APK ships (assets/Master/of[paths] apk, the same layout). The file'sregionisembedded.--decoded-master: master data decoded elsewhere, the directory the other commands read ([paths] master,--master,[servers.<region>] master): one<Table>.jsonper table (nnnotes master decode) and theMasterManifest.jsonof the files they were decoded from, as a master data snapshot published with its manifest carries it. The file'sregionis[catalog] region; no master key is needed.--full: also write the deck model's input, every chart's runtime notes and the master data tables about cards, skills, bonuses, scores and events (the deck input), for tools that run a deck model of their own.--no-deck: do not run the deck model; every chart'sdeckis null. A full statistics build evaluates many complete lives per chart.--workers Nsets the native worker count (default: every available processor;0: one thread).--seeds N: list this many replay seeds for a chart with a luck range (default 8). Expectations and aptitude use the model's independent nominal probabilities, not a sample of these seeds. Increasing this option changes the replay examples without changing the expected scores or their numerical enclosures.--no-gekisou-aptitude: keep the ordinary deck statistics and skip single-skill aptitude measurements.--stats-cache DIR: use the native model's persistent program and run cache. Reuse follows the complete compiled simulation inputs, so unchanged programs and runs can survive a change to the skill catalog. New shapes need their own measurements; changing a shape's numeric index does not by itself identify a new program. Every invocation still passes the complete current master data and charts to the native model, which constructs the current header and assembles the requested statistics. Cache records are isolated by cache schema and model source SHA-256. Each completed result is saved through a unique temporary file, a file sync and an atomic rename. Invalid or unreadable records are measured again; interrupted work retains its completed records. Concurrent processes may compute the same missing record, but publish complete records atomically. Old inputs remain available for later reuse: there is no automatic garbage collection, and unused model-source directories can be removed to reclaim space. A record larger than 64 MiB is not persisted. The command'sdeckStatssummary reports native requests, hits, completed computations, writes, invalid entries and written bytes; these count cache operations, not charts. Cache counters are not part of the published music data. Removing the cache changes the amount of work, not the bytes of the music data.--no-bgm: do not read the cue sheets (everybgm.lengthis null).--jackets DIR: also write every song's jacket, the Texture2DImage/Jacket/<jacket>, asDIR/<jacket>.webp(WebP quality 88, scaled down with Lanczos to at most 320 pixels on the longer side, without alpha when opaque); a page next to the file finds a song's jacket at<DIR>/<song.jacket>.webp.
Each master data file is checked against the SHA-256 the manifest lists and decoded with [master] key and iv.
With --decoded-master the tables are read as decoded, and the master data version and each file's SHA-256 are the
manifest's: the decoded tables cannot be checked against the files as served, so the file records what the manifest
lists (the same values as --master-files on those files).
Charts are read from the catalog of [catalog] language (bundles fetched into the cache as for every command); the
BGM length from the cue sheet's ACB (its CueTable and WaveformTable, no audio is decoded). FILE ending in .gz
is written gzip-compressed. The command prints a summary (songs, charts, deck: the deck model's commit,
unplayable, bytes, sha256 of the file, ...).
The command writes the file only when every table, chart and cue sheet was read and every chart measured: a missing
or mismatching master data file, a table without a column the file exports, a text id that MasterText does not
have, a score id that MasterLiveMusicScore does not have, a missing or unreadable chart asset, a note id that
occurs twice in a chart, a cue sheet without the song's cue, a member card or snap whose character, Gekisou (support)
skill or rank group the master data does not have, (with --jackets) a missing jacket texture, a chart the deck model
cannot measure or whose check deck fails, or deck statistics that disagree with the chart facts or the master data
stops it with exit status 1 and a line naming the input. The file is written through a temporary file and a rename.
An installation without the extension module (a source checkout that was not built) runs only with --no-deck;
the wheels on PyPI carry it. See Building.
Layout¶
{
"format": "nnnotes.music-data/2",
"provenance": {"region": ..., "client": {...}, "catalog": {...}, "master": {...}, "exporter": {...}, "deck": {...}},
"languages": ["ja", "en", "zh-Hant", "zh-Hans", "ko"],
"bands": [...], "characters": [...], "tags": [...], "categories": [...],
"gekisouCatalog": {"skills": [...], "supportSkills": [...], "members": [...], "snaps": [...]},
"deck": {"model": {...}, "kinds": [...], "gekisouAptitude": {...}},
"songs": [{"id": 100001, ..., "charts": [{..., "deck": {...}}, ...]}, ...],
"master": {...}, "charts": [...]
}
master and charts are present with --full only. The file is minified UTF-8 with one line feed at the end. Keys
are always in the order shown here and in the tables below, so the same inputs with the same nnnotes version (and so
the same deck model) give the same bytes. Master data values that are binary32 (single precision) in the game, a
number the master data writes with a fraction or an exponent, are written as the shortest decimal that reads back as
the same binary32 value (infinity as 1e999 / -1e999); the deck model's numbers are binary64 and written as it
writes them. A gzip file has no file name and a zero modification time in its header.
A text is an object with one string per language of languages ({"ja": ..., "en": ..., "zh-Hant": ...,
"zh-Hans": ..., "ko": ...}), the MasterText row of the id; a text field is null when the master data gives no id
(an empty string). A language's string may be empty when the game has no text in that language.
provenance¶
| Field | Content |
|---|---|
region |
the region whose master data this is (a configured region name), or embedded for the APK's master data |
client.versionName, client.versionCode |
the APK's version name and code (null without [paths] apk) |
catalog.resourceVersion |
the resource version used to select the downloaded catalog, or recorded for an explicit file in the catalog store (nnnotes catalog fetch / import); null when unknown |
catalog.sha256 |
SHA-256 of the remote catalog file the charts were read with |
master.source |
api (--master-files, --decoded-master: the region's files) or embedded (--apk-master) |
master.version |
the version of the master data manifest |
master.tables.<Table>.sha256 |
SHA-256 of each table's file as served, before decoding: the song tables (MasterLiveMusic, MasterLiveMusicScore, MasterText, MasterBand, MasterCharacter, MasterTag, MasterLiveMusicCategory, MasterSound, MasterSoundCueSheet, MasterLiveScoreRank), the tables of the Gekisou catalog (MasterMemberCard, MasterSupportCard, MasterSupportCardRank, MasterGekisouSkill, MasterGekisouSkillEffect, MasterGekisouSupportSkill, MasterGekisouSupportSkillEffect) and, when the deck model runs or with --full, the tables of the deck input |
exporter.name, exporter.version |
nnnotes and its version |
exporter.chartFormat |
the format of the chart converter the notes come from, nnnotes.live-score/1 |
deck |
the deck model: {name, version, source, commit, sourceSha256, format}, ournotes-deck, the package version of its crate ournotes-sim, the repository, the git commit nnnotes is built with and the statistics format (ournotes-deck.chart-stats/3); null with --no-deck |
bands, characters, tags, categories¶
| Field | Content |
|---|---|
bands[].id, .name, .mainColor, .subColor |
MasterBand: id, name text, color codes |
characters[].id, .bandId, .name, .shortName, .mainColor |
MasterCharacter |
tags[].id, .name |
MasterTag (the ids of bestMusicTagIds) |
categories[].id, .musicCategories, .name |
MasterLiveMusicCategory (the listing's category tabs; musicCategories are the song category values it shows) |
gekisouCatalog¶
The Gekisou skills a deck brings to a Gekisou live and the cards that carry them, from the master data (written with
--no-deck too): the member cards' Gekisou skills, the snaps' Gekisou support skills, the member cards and the
snaps, every list sorted by id. Texts as above; description is the master data's format text with its
placeholders ({effects[0].value}, ...), which a page fills from the effect rows or leaves out.
| Field | Content |
|---|---|
skills[] |
MasterGekisouSkill, the member cards' Gekisou skills: id, mission (_gekisouMissionType: 1 combo, 2 luck, 3 Just count, 4 every mission), maxLevel (the highest _level of its MasterGekisouSkillEffect rows, 0 without one), name (_nameTextID), description (_descriptionTextFormatID) |
supportSkills[] |
MasterGekisouSupportSkill, the snaps' Gekisou support skills: the same fields (MasterGekisouSupportSkillEffect for maxLevel) |
members[] |
MasterMemberCard: id, characterId, bandId (the character's MasterCharacter._bandID), rarity, gekisouSkillId (_gekisouSkillID, null for 0), name (_nameTextID), subtitle (_subtitleTextID, the card's title) |
snaps[] |
MasterSupportCard: id, characterIds, rarity, gekisouSupportSkillIds (_gekisouSupportSkillId01, _gekisouSupportSkillId02 that are not 0), supportSkillLevel (their level at the snap's highest rank: _gekisouSupportSkill01Level / 02Level of the MasterSupportCardRank row of its rank group with the highest _rank), name (_nameTextID), subtitle (_descriptionTextID, the snap's title) |
A member card's character, Gekisou skill and a snap's Gekisou support skills must be in their tables, and a snap's rank group must have a row; a snap whose two Gekisou support skills have different levels at its highest rank stops the command (the catalog has one level per snap).
songs¶
One entry per MasterLiveMusic row, sorted by id.
| Field | Content |
|---|---|
id, sortOrder, startAt, defaultUnlock |
the row's _id, _sortOrder, _startAt (as served, server time), _defaultUnlock |
title, ruby, phonetic |
texts of _titleTextID, _rubyTitleTextID, _phoneticTextID |
bandIds, bandName |
_bandIDs; the text of _bandNameTextID, a name the song shows instead of its first band's (null: none) |
vocalCharacterIds |
_vocalCharacterIDs |
lyricist, composer, arranger |
texts of _lyricistTextID, _composerTextID, _arrangerTextID |
musicType, musicCategories, bestMusicTagIds |
_musicType (the song type that card type bonuses match), _musicCategories, _bestMusicTagIDs |
jacket |
_jacketAssetName (the jacket sprite Image/Jacket/<jacket>) |
gekisouMissions |
[_gekisouMission1, _gekisouMission2, _gekisouMission3]: the missions of the song's Gekisou ranges (1 combo, 2 luck, 3 Just count) |
bgm.soundId, .cueSheet, .cue |
_musicSoundID and its MasterSound / MasterSoundCueSheet cue |
bgm.length |
{lengthMs, samples, sampleRate, durationMs}: the cue's Length in the ACB CueTable, the sample count and rate of its first waveform, and samples * 1000 // sampleRate; null with --no-bgm |
scoreRanks |
[{rank, requiredScore, battleRequiredScore}]: the MasterLiveScoreRank rows of _liveScoreRankGroup in required-score order (rank E .. SS; _requiredScore, _battleLiveRequiredScore). The group is the song's, so every difficulty shares these thresholds; a live's rank is the last row whose required score its score reaches |
charts |
one entry per difficulty the song has (_easyID .. _expertID not 0), in the order easy, normal, hard, expert |
master.MasterLiveMusic, master.MasterLiveScoreRank |
the whole MasterLiveMusic row and its score rank rows as decoded |
charts¶
| Field | Content |
|---|---|
difficulty |
easy, normal, hard or expert |
scoreId, level, displayLevel, fullComboCount |
MasterLiveMusicScore: _id, _musicScoreLevel, _musicScoreDisplayLevel, _fullComboCount |
asset.key, asset.sha256 |
the chart TextAsset Live/MusicScore/<_musicScoreTextFileName> and the SHA-256 of its bytes as shipped |
notes.judged |
notes that are judged (and count for a full combo) |
notes.total |
every runtime note, including hidden notes, guide notes and slide combo ticks |
notes.byOperateType |
{"<NoteOperateType>": count} over every runtime note, keys in ascending order |
bpm.changes |
every BPM change {timeMs, bpm} in time order |
bpm.main, .min, .max |
over the played span (first to last judged note): the BPM that holds longest (the earliest on a tie), the lowest and the highest |
firstNoteMs, lastJudgedNoteMs |
times of the first and the last judged note |
lastNoteMs |
the latest time of any runtime note (the time the score code calls the last timing note) |
musicLengthMs |
lastNoteMs + 1000: the live's music length on the game's score path (skill effects end at it at the latest) |
skillEventsMs |
skill event times in chart order; event i fires the skill of the member at performance position i |
fevers |
fever ranges [startMs, endMs] sorted by start |
deck |
the chart's deck statistics (below); null with --no-deck |
Chart times are milliseconds of chart time. The two lengths are different facts: bgm.length is how long the music
plays, musicLengthMs is the length the score code uses; a listing chooses the one it needs.
Deck statistics¶
The native statistics format is ournotes-deck.chart-stats/3. It evaluates complete lives on the game's default
frame schedule in two scenarios:
- Gekisou on (
expectation): each note is judged at its exact time, Just inside a Just-count range and Perfect elsewhere, with rank 1 in each range. Random lotteries and probabilistic skills follow independent nominal probabilities. The numbers enclose the expectation under that stated model. - Gekisou off (
offSeeds): each note is Perfect, with seed 0 and no Just, luck, Gekisou combo or rank bonus. This remains a deterministic measurement. A chart with more than three fevers can still play here.
Expected values and their intervals¶
An Estimate is [center, radius], denoting the interval [center - radius, center + radius]. Both values and
both endpoints must be finite, and the radius must be nonnegative. The radius bounds numerical uncertainty; it is
not a standard error, a confidence interval or variation between replay seeds. Point values are serialized at
millipoint precision, lottery counts at five decimal places and weights at twelve, with radii expanded outwards
so rounding keeps the enclosure. A zero radius is a point interval.
The no-skill expected score is measured at deck.model.power (300000). For each ordinary score-up kind and
performance position, expectation.weights[kind][position] encloses the expected gain from one effect at factor
1, divided by that power. Effects run through the simulator's own conditions, frames and appliers. A deck whose
ordinary live skills are represented by these kinds has the score approximation
P * (score / model.power + sum_k factor_k * weights[kind_k][k])
where P is its power. Arithmetic on estimates must carry their radii. The approximation also has a separate
integer-floor error, checked by a complete nominal evaluation at model.checkPower; a numerical radius does not
replace that error bound. The factor follows the game's value conversion: effect type 2000 uses
floor(value / 10000f * 1e5) / 1e5, type 2005 uses floor(value / -10000f * 1e5) / 1e5, and types 2002 and 2004
round the binary32 quotient value / 10000f half to even. Other effect types, including cumulative scoring, life
and judgement conversion, have no ordinary score-up weights.
replaySeeds is separate from these expectations. A playable chart without a luck range lists [0]; otherwise
it lists the first --seeds values of the published sequence. Candidate k is the low 32 bits, interpreted as a
signed integer, of output k + 1 of SplitMix64 started at 0x6765_6B69_736F_7531 (gekisou1). A candidate is kept
when its effective stream-seed pair (|b|, |b ^ 0x9E3779B9|, with -2^31 treated as 2^31 - 1) differs from all
previous pairs. A shorter list is a prefix of a longer list. These values select concrete replay examples; the
exporter never averages them to obtain expectation or gekisouAptitude.
Ranks and Just judgements¶
A range at rank r awards trunc(rangeScore * p(r) / 100), where
p(r) = ranges[i].rankBonusPercents[r - 1]. rankBonus is the expected result of that integer operation at rank 1.
In general, the expectation of a truncated score is not the truncation of the expected score. Readers must
keep the separately measured rankBonus and rankBonusPerfect; neither is reconstructed by truncating an
expectation's center.
When rank changes do not alter range scoring, the other-rank approximation replaces the rank-1 bonus by
E[rangeScore] * p(r) / 100 and adjusts an ordinary weight by
sum_i (p_i(r_i) - p_i(1)) / 100 * rangeWeights[kind][position][i]. The native rank check includes the resulting
integer-truncation slack in its bound. rangeWeights is null when overlapping ranges break that independence;
a kind's row is null when its conditions read a confirmed rank (condition 7012). These nulls mark a limit of the
rank formula, not a zero contribution. Such scenarios require full simulation.
scorePerfect and rangeScorePerfect use the same nominal model with every Just judgement replaced by Perfect.
rankBonusPerfect is measured on that play too. A chart with justNotes == 0 has matching best and Perfect
expectations. Interpolating between these endpoints is a consumer's approximation, not an additional measured
Just-rate curve.
deck¶
| Field | Content |
|---|---|
model |
the native model's description: engine, play, score formula, measurement and check powers, effect unit value, nominal expectation law, rank approximation, Perfect play, Free Live and single-shape aptitude |
kinds[] |
ordinary score-up kinds from MasterLiveSkillEffect types 2000, 2002, 2004 or 2005 without a cumulative condition. Each has a continuous zero-based id, effect type, activation time, duration, targets, condition/release/reset groups, effect limits, source rows and distinct sorted values |
gekisouAptitude |
the independently validated single-shape catalog below, or null when aptitude is disabled |
A kind's duration is ceil(activationTimeSecond * 1000f) milliseconds. Match a card's ordinary live-skill effect at
its actual level to the kind's structural fields. Kind ids and shape ids are indices in the current document;
they are not stable identifiers across master-data releases.
charts[].deck¶
| Field | Content |
|---|---|
convertedNoteCount |
the converted note count used by the score formula |
skip |
points per unit of power for a skipped live: every note Great, combo 0, no skills |
events |
[[position, timeMs], ...] in chart order |
positions |
largest event position plus one; the size of each position dimension |
ranges[] |
index, mission (1 combo, 2 luck, 3 Just count), startMs, endMs, rankBonusPercents for ranks 1–5, and the rank-1 rankBonusPercent |
justNotes |
notes judged Just on the best Gekisou play |
expectation |
the expected Gekisou statistics below, or null when unplayable |
replaySeeds |
concrete replay seeds, empty when unplayable |
offSeeds |
exactly one deterministic Free Live result: seed 0, integer score, scalar weights[kind][position], and check with deck, integer exact, scalar predicted and bound; a weight row is null when the kind reads unavailable Gekisou state |
unplayable |
null, or why Gekisou cannot play this chart; a fourth fever is unsupported by the game. Free Live statistics remain present |
gekisouAptitude |
per-range factors and single-shape increments below, or null |
The expectation object contains:
| Field | Content |
|---|---|
score, scorePerfect |
Estimate of no-skill points, including rank-1 bonuses, on the best and Perfect plays |
ranges[] |
rangeScore, rankBonus, rankBonusPerfect, rangeScorePerfect and luckPoints as Estimates; deterministic integer maxCombo and justCount; four lotResults Estimates in Miss, Hit, Super Hit, Critical order |
weights |
[kind][position] Estimates of points per unit of deck power and effect factor |
rangeWeights |
[kind][position][range] Estimates for rank adjustment, or null; individual kind rows may be null |
check |
deck: [kind, value] or null at each position; ranks: null; expected and predicted Estimates in points at model.checkPower; scalar nonnegative bound |
rankCheck |
the same check deck evaluated at ranks 1–5 per range; the same Estimate fields and scalar bound, or null when no rank check applies |
The exporter checks native chart identities and times against the chart facts, and checks each range's rank
percentages against MasterLiveGekisouRankingScoreBonus. It validates array dimensions, finite estimates,
nonnegative radii, deterministic counters, replay seeds, unavailable rank domains, best/Perfect agreement without
Just notes, and all check bounds. Checks compare the most distant endpoints; the serialized check allows only
the outward millipoint rounding expansion of its two intervals in addition to the native bound. Lottery counts
and luck points outside a luck range must enclose zero.
Gekisou skill aptitude¶
deck.gekisouAptitude describes the shapes measured, and each chart's deck.gekisouAptitude describes the expected
increment with one such shape equipped. Ordinary expectation has no card Gekisou skills. Single-shape gains
cannot be added to estimate several interacting skills; complete formations still require simulation.
The header has plainKind, host, law and shapes. plainKind is the ordinary, unconditional, whole-team
five-second score-up kind used for cross terms, or null. host describes the paired member used for a support
skill. law is exactly independent nominal lottery and skill probabilities.
Each shape has a continuous zero-based id, source (member or support), mission (1 combo, 2 luck, 3 Just,
4 all), bandCondition, normalized effects and skills. Each skill reference is
{id, level, memberTargetIds, bandIds} and joins to the Gekisou catalog for its text. Members use their skill's
highest level; snaps use their support skills' levels at the highest rank. Effects preserve master order and
expose type, trigger, duration, value, limits, targets, four condition groups and cumulative condition, as specified
by the Schema. Equivalent parameters share a shape. Support condition 5000's target is normalized away;
memberTargetIds and bandIds retain the targets and bands for matching, or are null without that condition.
A chart's aptitude is {factors, variants}, or null when disabled, unplayable, without ranges, or without shapes.
factors has one object per range: deterministic judgedNotes, justNotes, perfectNotes, tailNotes and
comboAtStart, plus an Estimate of baseline lotteries. That interval must agree with the sum of the baseline
range's four expected lottery-result counts. The factors of overlapping ranges may refer to the same notes.
Variants appear in shape-id order for the chart's missions and mission 4. A band-conditioned shape has a
bandMatch: true variant followed by false; other shapes use null.
| Field | Content |
|---|---|
shape, bandMatch |
the current shape index and measured band condition |
score, scorePerfect |
Estimate of the total point increment on the best and Perfect plays |
tail, tailPerfect |
Estimate of the increment outside the range scores and their bonuses |
converted |
deterministic integer converted-note increment as [value, 0] |
ranges[] |
Estimates of rangeScore, rankBonus, rankBonusPerfect, rangeScorePerfect and luckPoints; deterministic integer maxCombo and justCount increments as [value, 0] |
weights |
[position] Estimates of cross terms with the plain kind, or null without it |
rangeWeights |
[position][range] Estimates of cross terms, or null without a plain kind or when the chart or shape cannot use linear rank adjustment |
check |
plain-kind deck, explicit ranks, expected/predicted point Estimates and scalar bound; a nonlinear-rank shape is checked at rank 1 in every range |
Point increments are measured at deck.model.power. Their intervals must be consistent with
score = tail + sum(rangeScore + rankBonus) and
scorePerfect = tailPerfect + sum(rangeScorePerfect + rankBonusPerfect). These are interval identities, not
identities between rounded centers. A shape that reads confirmed rank in any condition group or cumulative
condition has no linear rank cross terms, even when the chart's ordinary range weights are available.
The exporter independently checks the shape catalog against the complete master data, including skill and level coverage, normalized effects, member targets and bands. It then checks variant order and coverage, factor counts, interval identities, dimensions, deterministic integer increments and expectation bounds. Aptitude has no sample-count, standard-error target or convergence-cap fields in this format.
The deck input (--full)¶
With --full the file ends with the input the deck model reads: master, the master data tables about cards,
skills, bonuses, scores and events, and charts, every chart as the client builds it at runtime. They hold facts as
the game has them (master data values as served, notes as the client's chart converter creates them), not values
derived from them.
master¶
Each table is {"columns": [...], "rows": [[...], ...]}: one array per row, its values in the order of columns,
rows in the order the master data lists them. Values are as decoded: integers, strings, booleans, arrays; a binary32
column should be parsed as 32-bit floats.
Tables with a column list export those columns, and every row must have them. Tables marked "all" export every
column their rows have, in the order the rows first have them; when such a table has no rows, columns is empty
(the master data carries no field list for an empty table). A reader treats a table missing from master as empty.
| Table | Columns |
|---|---|
MasterMemberCard |
_id _characterID _rarity _cardType _bestMusicTagIDs _performancePowerMax _technicPowerMax _visualPowerMax _memberCardLevelGroup _memberCardAwakeGroup _memberCardRankGroup _leaderSkillID _liveSkillID _gekisouSkillID |
MasterMemberCardLevel |
_id _group _level _exp _performanceRate _technicRate _visualRate |
MasterMemberCardLevelLimit |
_id _rarity _awakeCount _limitLevel |
MasterMemberCardAwake |
_id _group _awakeCount _performanceRate _technicRate _visualRate |
MasterMemberCardRank |
_id _group _rank _performanceRate _technicRate _visualRate _leaderSkillLevel _musicTypeBonusRate _musicTagBonusRate |
MasterSupportCard |
_id _characterIDs _rarity _cardType _performancePowerMax _technicPowerMax _visualPowerMax _supportCardLevelGroup _supportCardRankGroup _supportSkillId01 _supportSkillId02 _gekisouSupportSkillId01 _gekisouSupportSkillId02 |
MasterSupportCardLevel |
_id _group _level _exp _performanceRate _technicRate _visualRate |
MasterSupportCardRank |
_id _group _rank _limitLevel _cardTypeLinkBonusRate _supportSkill01Level _supportSkill02Level _gekisouSupportSkill01Level _gekisouSupportSkill02Level |
MasterCharacter |
_id _bandID |
MasterBand |
_id |
MasterCharacterRank |
_id _rank _exp _bonus |
MasterCharacterTotalRank |
_id _totalRank _bonus |
MasterBandItemSkillEffect |
_id _bandItemId _level _skillTargetIDs _skillEffectType _effectValue |
MasterBandItem |
_id _bandId |
MasterBandItemLevel |
_id _bandItemId _level _playerRank |
MasterVip |
_id _vipRank |
MasterVipRankBonus |
_id _vipRank _vipBonusType _value |
MasterMemoryMusicGroup |
_id _skillTargetIds |
MasterMemoryMusic |
_id _groupId |
MasterMemoryMusicBonus |
_id _groupId _scoreRank _performance _technic _visual |
MasterMemoryMemberLevel |
_id _point _performance _technic _visual |
MasterMemorySupportLevel |
_id _point _performance _technic _visual |
MasterSkillTarget |
_id _skillTargetType _characterID _bandID _cardType _tagID _judgement _liveMusicType _gekisouMissionType _liveSkillCategories _gekisouSkillCategories |
MasterSkillCondition |
_id _conditionType _conditionValues _isPositive _conditionTargetIDs |
MasterSkillConditionSet |
_id _group _conditionIds |
MasterSkillCumulativeCondition |
_id _skillCumulativeConditionType _conditionValues _conditionTargetIDs _maxCumulativeCount |
MasterSkillEffectSetting |
_id _skillEffectType _phase |
MasterLeaderSkillEffect |
_id _leaderSkillID _level _skillConditionGroup _skillTargetIDs _skillEffectType _effectValue _skillCumulativeConditionID |
MasterLiveSkill |
_id _skillCategories |
MasterLiveSkillEffect |
_id _liveSkillID _level _skillConditionGroup _skillReleaseConditionGroup _skillTargetIDs _skillEffectType _activationTimeSecond _effectValue _maxEffectValue _effectLimitCount _skillCumulativeConditionID _effectExecuteLimitCount _effectExecuteLimitResetConditionGroup |
MasterSupportSkill |
_id |
MasterSupportSkillEffect |
_id _supportSkillID _level _skillTriggerConditionGroup _skillTriggerType _skillConditionGroup _skillReleaseConditionGroup _skillTargetIDs _skillEffectType _activationTimeSecond _effectValue _maxEffectValue _effectLimitCount _skillCumulativeConditionID _effectExecuteLimitCount _effectExecuteLimitResetConditionGroup |
MasterGekisouSkill |
_id _gekisouMissionType _skillCategories |
MasterGekisouSkillEffect |
_id _gekisouSkillID _level _skillTriggerConditionGroup _skillTriggerType _skillConditionGroup _skillReleaseConditionGroup _skillTargetIDs _skillEffectType _activationTimeSecond _effectValue _maxEffectValue _effectLimitCount _skillCumulativeConditionID _effectExecuteLimitCount _effectExecuteLimitResetConditionGroup |
MasterGekisouSupportSkill |
_id _gekisouSupportSkillExecTiming _gekisouMissionType |
MasterGekisouSupportSkillEffect |
_id _gekisouSupportSkillID _level _skillTriggerConditionGroup _skillTriggerType _skillConditionGroup _skillReleaseConditionGroup _skillTargetIDs _skillEffectType _activationTimeSecond _effectValue _maxEffectValue _effectLimitCount _skillCumulativeConditionID _effectExecuteLimitCount _effectExecuteLimitResetConditionGroup |
MasterLiveNoteParameter |
_id _noteOperateType _scorePercent |
MasterLiveJudgementParameter |
_id _noteSimulateJudgement _scorePercent _damage |
MasterLiveJudgementTiming |
_id _assistLevel _judgementPriority _noteJudgementType _noteSimulateJudgement _beforeMs _afterMs |
MasterLiveComboScoreBonus |
_id _comboBonusType _requiredComboCount _bonusFactor |
MasterLiveSettings |
all |
MasterParameter |
all |
MasterLiveGekisouLuckBasePoint |
_id _noteCategory _noteSimulateJudgement _weight _basePoint |
MasterLiveGekisouLuckBonusLot |
_id _chanceLotType _lotResult _weight |
MasterLiveGekisouRankingScoreBonus |
_id _missionPattern _rank _count _scoreBonusPercent |
MasterLiveMusic |
_id _musicType _bestMusicTagIDs _liveScoreRankGroup _easyID _normalID _hardID _expertID _gekisouMission1 _gekisouMission2 _gekisouMission3 |
MasterLiveMusicScore |
_id _musicScoreTextFileName _musicScoreLevel _fullComboCount |
MasterArenaMusic |
all |
MasterChallengeMusic |
all |
MasterLiveScoreRank |
_id _group _liveScoreRank _requiredScore _battleLiveRequiredScore |
MasterEvent |
all |
MasterEventEffect |
all |
MasterEventAchievementReward |
_id _eventId _eventPoint _rewardIds |
MasterEventAchievementLoopReward |
_id _eventId _loopStartEventPoint _loopEventPoint _rewardIds |
MasterLiveEventReward |
_id _group _eventGroup _scoreRank _resourceType _resourceId _resourceCount _probability |
MasterChallengeLiveEventReward |
_id _group _eventGroup _scoreRank _resourceType _resourceId _resourceCount _probability |
MasterLiveEventPoint |
all |
MasterChallengeLiveEventPoint |
all |
MasterLiveChallengePoint |
all |
MasterLiveMusicBoostBonus |
_id _consumedLiveBoostCount _liveMusicRewardRate _playerExpRate _memberCardExpRate _friendshipExpRate _eventPointRate |
MasterChallengeMusicBoostBonus |
all |
charts¶
One chart per MasterLiveMusicScore row, sorted by scoreId, including charts of no song. Song and score facts
are not copied into these records: they are in songs and in master, joined by scoreId.
| Field | Content |
|---|---|
scoreId |
MasterLiveMusicScore._id |
asset.key |
Live/MusicScore/<_musicScoreTextFileName>, the chart's TextAsset |
asset.sha256 |
SHA-256 of the TextAsset's bytes as shipped |
notes.id |
note id, unique within the chart |
notes.op |
NoteOperateType |
notes.judgementType |
NoteJudgementType (from the operate type and the critical flag) |
notes.timeMs |
note time in chart milliseconds |
skillEvents.timeMs |
skill event times in chart order; the position in the array is the event index (the times need not ascend) |
fevers.startMs, fevers.endMs |
fever ranges sorted by start; the position in the arrays is the range index |
The notes are columns: notes.id[i], notes.op[i], notes.judgementType[i] and notes.timeMs[i] describe the
same note, and the four arrays have the same length; fevers.startMs[i] and fevers.endMs[i] are one range. The
notes are every note the client creates at runtime, including hidden notes, guide notes, and the combo ticks of
slides (operate types Combo and ComboSkip), in the order the client enumerates its note dictionary. That order is
not the time order: the combo ticks of a slide that fall on a time no other note has come after the slide's end.
Order by timeMs (then id) where time order is needed.
The deck model reads the same content under the format name nnnotes.deck-data/1 (format, provenance,
master, charts); nnnotes hands it over in memory.
Building¶
The deck model is the Python package ournotes-sim that each ournotes-deck
release publishes: the Rust crate ournotes-sim of that release as an extension module, one wheel per platform. The
extra deck pins one release (pip install 'nnnotes[deck]', or pip install -e ".[deck]" in a checkout), so the
same nnnotes version always carries the same deck model: the pin moves only through a pull request
(.github/workflows/deck.yml opens one when ournotes-deck publishes a newer release with its WASM packages and its
wheels), and provenance.deck.version and provenance.deck.commit name the release in every file. provenance.deck.sourceSha256 is the SHA-256 of the deck model's sources (ournotes_sim::SOURCE_SHA256):
releases with the same value measure the same statistics. Replay bundles take the engines of the same release:
the replay and recommendation engines of --replay-engine and --recommend-engine are its WASM packages, whose
build-info.json names the release commit that provenance.deck.commit names; engines of another release are
rejected.
--replay-dir OUT/replay --replay-engine PKG [--recommend-engine PKG] writes normalized runtime inputs, the pinned
model's WASM release packages (the replay engine and, optionally, the recommendation engine) and same-snapshot Snap
label/reference-card metadata as described in replay.md. The music data stays compact and carries
the SHA-bound replay.manifestUrl pointer; its manifest optionally references snap-labels.json. The selected
decoded label tables retain names/templates, original per-level effects and artwork IDs. No encrypted master/chart
blobs or native binary are included in this artifact bundle. Label metadata does not change the score model.
Versions¶
format names the major version. Within nnnotes.music-data/2, fields and tables are only added, never renamed,
removed or given another meaning, and readers ignore keys they do not know. Readers must reject an unknown major
version. This version carries ournotes-deck.chart-stats/3 in provenance.deck.format and requires that exact
statistics format from the extension module.