Skip to content

QEM (Binary)

QEM, or Quantum Editor Map, is an internal format developed for SSQE. While currently unfinished, it will be used to save maps more efficiently and in a standardized location compared to the current TXT/INI system, which is restrictive, unstandardized, and not reasonably scalable.

This format is partly based on SSPMv2, but has been greatly simplified to make reading and writing easier. However, this format does not contain audio data - that will be delegated to an upcoming QEMZ format which relies on QEM files for object data. QEMZ will be a lossless export option for sharing maps in one file, regardless of what game or format you intend to map for, and could be a reasonable option for any future derivatives of Sound Space who don’t want to develop an entirely new format.

All integer values below should be assumed to be SIGNED unless otherwise stated. (This just makes parsing slightly easier and no map would reasonably need an unsigned integer for those values)

Similar to SSPM, QEM uses a lookup table for data types to determine how to read a following set of bytes. Every entry in the format marked with (*) is an example of one of these types, which will always come before a value of that type. These data types are different from what SSPM offers, and are listed below:

Data Types
Type ID
Hex Code
Type description
0
0x00
Byte
1
0x01
16-bit signed integer
2
0x02
16-bit unsigned integer
3
0x03
32-bit signed integer
4
0x04
32-bit unsigned integer
5
0x05
64-bit signed integer
6
0x06
64-bit unsigned integer
7
0x07
32-bit floating point number
8
0x08
64-bit floating point number
9
0x09
8-bit boolean (true/false)
10
0x0A
Variable length UTF-8 string, starting with a 16-bit signed integer for the number of bytes in the string
11
0x0B
Variable length UTF-8 string, starting with a 32-bit signed integer for the number of bytes in the string
12
0x0C
Variable length buffer, starting with a 16-bit signed integer for the number of bytes in the buffer
13
0x0D
Variable length buffer, starting with a 32-bit signed integer for the number of bytes in the buffer
14
0x0E
Variable length array, starting with a 16-bit signed integer for the number of elements in the array, and one byte for the data type contained in this array (nested arrays are allowed)
15
0x0F
Variable length array, starting with a 32-bit signed integer for the number of elements in the array, and one byte for the data type contained in this array (nested arrays are allowed)
There are no rows in this table
Note: a “buffer” is a raw block of bytes, with no inherent information on how it is encoded. It will not be written by SSQE, but should be accounted for in case you want to use and/or modify this format for your own purposes. This can be used to store another file within QEM, although this is not recommended (see QEMZ).


Now we get to the actual byte layout of the format, starting with the header. Note that a block marked with (static) must be present in every QEM file and will only occur once in that file.
Header (static)
Bytes
Example in Hex
Description
4
0x4951454D (IQEM)
File Signature
2
0x0000
Version Number (Just in case, should be 0)
There are no rows in this table

Following the header will be a metadata block with zero or more metadata fields. Each field is represented by a UTF-8 string identifier and a value, with this layout:
Metadata Block (static)
Bytes
Example in Hex
Description
2
0x0000
Number of metadata fields
[x]
[...]
Metadata fields (layout below)
There are no rows in this table

Metadata Field
Bytes
Example in Hex
Description
2
0x0500
Byte length of the field’s identifier
[x]
“title”
Field identifier as a UTF-8 string
1
0x06
(*)Type of the field’s value
[x]
0x0000000000000000
Field value
There are no rows in this table

Below are all the fields written by SSQE into metadata, along with their default values if not present. A QEM file cannot be expected to contain all of these fields, and it may contain more fields than are listed here, so all present fields must be handled appropriately. Additionally, a field could have a different type than what is listed here, which is not recommended but must be accounted for.
Possible Metadata Fields
Identifier
Data type
Description
Default
(to be completed)
There are no rows in this table

Next up in the format are object blocks, or sets of what SSPMv2 considers to be “markers” - notes, timing points, etc.
Object Blocks (static)
Bytes
Example in Hex
Description
1
0x02
Number of object blocks
[x]
[...]
Object blocks (layout below)
There are no rows in this table

Object Block
Bytes
Example in Hex
Description
1
0x00
Numeric object ID
2
0x0000
UTF-8 object name
[x]
“”
Object name (must be blank if you intend these objects to be read by SSQE, which means the object’s types must align)
1
0x05
Number of types present in each object (excluding timestamp)
[x]
0x07, 0x07, 0x09, 0x00, 0x00
(*)The list of types present in all objects
There are no rows in this table

Object Data
Bytes
Example in Hex
Description
8
0x0000000000000000
Timestamp as a 64-bit floating point number, in milliseconds
[x]
[...]
Object data as defined in the object block’s types
There are no rows in this table

Below are the possible object IDs and type lists recognized by SSQE. These must be accounted for even if you don’t use all of them. If you intend to create a new object type for your own purposes, it’s recommended to use IDs descending from 255 instead of ascending from where these IDs stop.
Possible Object Data
Object ID
Name
(*)Types
Defaults
Game of Origin
0
Note
0x07: X position 0x07: Y position 0x09: Uses a tweened approach rate 0x00: Easing style 0x00: Easing direction
0 0 false 0 0
- - Novastra Novastra Novastra
1
Timing Point (BPM)
0x07: BPM 0x00: Time signature top 0x00: Time signature bottom
0 4 4
- - -
12
Beat
-
-
Novastra
13
Glide
0x00: Glide direction
4
Novastra
14
Mine
0x07: X position 0x07: Y position
0 0
Novastra Novastra
15
Lyric
0x0A: Text 0x09: Fades in 0x09: Fades out
“” false false
Novastra Novastra Novastra
16
Fever
0x08: Duration in milliseconds
0
Novastra
There are no rows in this table

Keep in mind that an object block may identify more or less types than are listed here. An object will always have as many types as the block defines, but these types aren’t guaranteed to be identical to what your parser can understand. If an object has less types than you expect, you should use the defaults listed next to each value here as a placeholder. If an object has more types than you expect, you can safely discard them after reading them.

Miscellaneous lookup tables:
Easing Styles
Index
Value
0
Linear
1
Sine
2
Back
3
Quad
4
Quart
5
Quint
6
Bounce
7
Elastic
8
Exponential
9
Circular
10
Cubic
There are no rows in this table
Easing Directions
Index
Value
0
In
1
Out
2
InOut
There are no rows in this table
Glide Directions
Index
Value
0
Up
1
Right
2
Down
3
Left
4
None
There are no rows in this table

Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.