Skip to content

Chart Root Structure

This section introduces the root structure definition of a chart file.

Note

Compatibility level for each field is specified individually.

JSON Example

json
{
  "Version": 0,
  "CompatLevel": 0,
  "BpmList": ...,
  "ChartInfo": ...,
  "JudgeLines": [],
  "PrprExtra": null,
  "PrprExtraFiles": null,
  "PrprUnlockVideoData": null,
  "PrprUnlockVideo": null,
  "PrprUnlockVideoPath": null
}

Structural Specifications

Unique IdentifierField NameTypeDescriptionCompatibility LevelDefault ValueAdded Version
10000VersionintChart version number, starts from 1011
10001CompatLevelintCompatibility level of the chart, see Compatibility System001
1BpmListList<BPM>List of BPMs used in the chart0-1
2ChartInfoChartInfoMetadata and basic information about the chart0-1
3JudgeLinesList<JudgeLine>List of judge lines that make up the chart0[]1
4PrprExtrastring?PRPR simulator extension, internally stored as JSON, see Phira Documentation4null1
5PrprExtraFilesDictionary<string, byte[]>?List of PRPR simulator extension files, where string is the filename and byte[] is the file content4null1
6PrprUnlockVideoDatabyte[]?PRPR simulator unlock video data, stored as binary data, see Phira Documentation4null1
100PrprUnlockVideostring?Path to the PRPR simulator unlock video, a compatible field that cannot be used with PrprUnlockVideoData simultaneously4null1

Behavior Rules

  • If BpmList contains only one BPM, the formula to convert beat time to actual time (in seconds) is: Actual Time = Beat / BPM * 60
  • If BpmList contains multiple BPM entries, use the following sample code for conversion:
csharp
public float BeatTimeToSecond(float beatTime, List<Bpm> bpmList, float bpmFactor)
{
    float totalTime = 0;
    float currentBeat = 0;
    for (int i = 0; i < bpmList.Count; i++)
    {
        var currentBpm = bpmList[i];
        float secPerBeat = 60f / (currentBpm.Bpm / bpmFactor);
        float endBeat = i < bpmList.Count - 1
            ? Math.Min(BeatToBeatTime(bpmList[i + 1].StartTime), beatTime)
            : beatTime;
        // Calculate beat interval under current BPM
        float beatInterval = endBeat - currentBeat;
        // Accumulate time
        totalTime += beatInterval * secPerBeat;
        currentBeat = endBeat;
        // Stop if target beat reached
        if (currentBeat >= beatTime)
            break;
    }
    return totalTime;
}

Proto Paragraph

protobuf
syntax = "proto3";
package PhiCommonChart.ChartStructs;

message CommonChart {
  repeated Bpm BpmList = 1;
  Info ChartInfo = 2;
  repeated JudgeLine JudgeLines = 3;
  string PrprExtraJson = 4;
  map<string, bytes> PrprExtraFiles = 5;
  bytes PrprUnlockVideoData = 6;
  string PrprUnlockVideoPath = 100;
  int32 Version = 10000;
  int32 CompatLevel = 10001;
}