This part of the doc is supposed to help you build a parser that just reads plain maps with all the necessary data needed. This is mainly for decoding, but you can also use this for encoding if you have a firm understanding of all the necessary data of the full doc.
Header
Magic Number (4 bytes)
53 53 2B 6D (SS + M when converted to text)
Version Number (2 bytes)
02 00 (2 for SSPMv1)
Object Metadata
Skip ahead 28 bytes (Seek to 0x26)
We just need the total marker count we are just getting notes, nothing else.
Total Marker Count (4 bytes)
32 bit unsigned integer (00 00 00 00)
SSPMv2 Optimized Map Difficulty (1 byte)
Skip ahead 1 byte (Seek to 0x2D)
Audio Exists Bool (1 byte)
00 or 01 (Scan this so you can identify if the map is broken or not)
Cover Exists Bool (1 byte)
00 or 01
Pointers
Skip ahead 17 bytes (Seek to 0x40)
We don’t need a mod chart checker bool, nor the custom data
Byte offset of the Audio Data section (8 bytes)
00 00 00 00 00 00 00 00
Byte length of the Audio Data section (8 bytes)
00 00 00 00 00 00 00 00
Byte offset of the Cover Data section (8 bytes)
00 00 00 00 00 00 00 00
Byte length of the Cover Data section (8 bytes)
00 00 00 00 00 00 00 00
Skip 16 bytes (Seek to 0x70)
We don’t need marker definition, since we are only identifying notes for every marker, and we know the length of the marker section is the integer that we created earlier Byte offset of the Marker section (8 bytes)
00 00 00 00 00 00 00 00
Song Metadata
Map ID Length (2 bytes)
16 bit unsigned integer (00 00)
Map ID
UTF-8 String converted to hexadecimal
Map Name Length (2 bytes)
16 bit unsigned integer (00 00)
Map Name (Artist - Title)
UTF-8 String converted to hexadecimal
Song Name Length (2 bytes)
16 bit unsigned integer (00 00)
Skip bytes using the Song Name Length
Song Name is the exact same as Map Name, so we do not need it
Number of Mappers (2 bytes)
16 bit unsigned integer (00 00)
(Array) Mapper
Mapper Name Length (2 bytes)
16 bit unsigned integer (00 00)
Mapper Name
UTF-8 String converted to hexadecimal
Custom Data (Some maps will have custom difficulty names)
NOTE! If you do not want to support custom difficulty names, then skip to the audio section!!
Number of fields (2 bytes) (make a “for i in get_16 loop” for this integer)
16 bit unsigned integer (00 00)
Custom Data Objects:
Field ID Length (2 bytes)
16 bit unsigned integer (00 00)
Field ID Key (UTF-8 String)
Put an if statement for “difficulty_name” here
Data type ID (1 byte) (Refer to the Data type Table)
00 (It will be 09 for custom difficulty names)
If data type is 0x0C (which is an array):
Custom Data goes after the Data Type
(It isn’t an array, so you can just put the 2 bytes for length first, and then get the UTF-8 String after.)
Audio
If the Audio Exists Bool is true (if byte 0x2E is 0x01):
Seek to the Audio Byte Offset you got earlier
Audio Data (Must contain a full MP3 or OGG audio)
Cover
If the Cover Exists Bool is true:
Seek to the Cover Byte Offset you got earlier
PNG Data (Must contain a full PNG image)
Note Data
Follow these steps and you can just go straight to the notes array!
Seek to the Marker Byte Offset
Note that this is for every note in every note marker. After the end, the next note is written in the same format as shown below.
For the X and Y position, they are on a 1 incremented plane as well, but the origin is at 1,1
2 on the X axis is to the right, and 0 is to the left
The Y axis is actually flipped, so 0 on the Y axis is up, and 2 is down
Make a for i = 0 loop here, and read the bytes as followed.
Note
Time (in milliseconds, 4 bytes)
32 bit unsigned integer (0x00 00 00 00)
Optimized SSPMv2 Note Storage Type (1 byte)
If the storage type is 0x00 (Integer):
X position (1 byte)
8 bit unsigned integer (0x00)
Y position (1 byte)
8 bit unsigned integer (0x00)
If the storage type is 0x01 (Quantum):
X position (4 bytes)
32 bit signed float (0x00 00 00 00)
Y position (4 bytes)
32 bit signed float (0x00 00 00 00)
And that’s it! Your own SSPMv2 decoder should be done with only the necessary data you need.