O2Jam File Format Documentation
In the early days of the modding era, most of the modding tools were made to modify game assets; almost, if not, none of these tools dealt with binary patching or DLL injection. Yet despite this, file format documentation has been scarce and could never be found easily in one place. So today, I’d like to dump all formats used in the O2Jam client; both new and old into a single place.
Also, I won’t be reposting existing documentation that has already been shared publicly. I’ll instead link to them here.
O2Jam Content Structure
Let’s start with the whole picture. Here’s what the typical O2Jam game client directory looks like:
O2Jam/
├─ Image/
│ ├─ (TEMP/)
│ ├─ Interface(1).opi
│ ├─ Playing(1).opi
│ ├─ Avatar.opa
│ ├─ OJNList.dat (or X2OJNList.dat for X2 client)
├─ Music/
│ ├─ (TEMP/)
│ ├─ BGM.ojm
│ ├─ BgEffect.ojm
│ ├─ Planet.ojm
│ ├─ O2PlanetNPC.ojm
│ ├─ (BGMWinter.ojm)
│ ├─ Tutorial.ojn
│ ├─ Tutorial.ojm
│ ├─ o2ma100.ojn
│ ├─ o2ma100.ojm
│ ├─ o2ma101.ojn
│ ├─ o2ma101.ojm
│ ├─ o2ma...
├─ OTwo.exe
├─ O2Jam.exe
├─ O2JamPatchClient.exe
├─ (O2JamLauncher.exe)
Parenthesis denotes optional.
Before reading further, please keep the following in mind for the rest of this document:
- All formats assume little-endian byte order unless stated otherwise.
- All strings are null-terminated string.
- String encoding varies by client distribution. For example, e-Games clients commonly use standard
ASCII, while mgame and NOWCOM (Korea) clients useEUC-KR.
Asset patching
Temporary folders (TEMP/) inside both Image and Music hold patch files that yet to be applied by the game client.
Image Patch
Image patch files are typically use .opi or .opa format depending on the patch file. See package interface and avatar below to learn about the format. Patch files are typically downloaded by O2JamPatchClient.exe and not by the game client itself.
The patch package file contains sprite or metadata files that will be added to the target package. If the file is exist, it will override the content instead. The patch filename use the following format:
<Target>(1)_<Patch Number>.<Extension>
For example: Playing1_300.opi
Target: Valid targets areInterface,Playing, andAvatar.Patch Number: Used to differentiate multiple patch files. By convention, it is patch build version.Extension: Eitheropioropa.
Music Patch
Unlike Image Patch, Music Patch is typically downloaded by the main client (OTwo.exe) rather than the game patcher. It hold in-progress download files for both OJN and OJM files, typically via Music Shop. Once the download completed, it will be moved outside the TEMP/ folder.
In addition to the game music files, the client also persist download state via INI file. The filename would o2ma<music_id>.ini:
[FileInfo]
FileNum=2
FileName1=o2ma100.ojn
FileStatus1=0
FileName2=o2ma100.ojm
FileStatus2=0
Image
The Image directory holds all of the visual assets used by the game client: everything from UI elements to avatar sprites. It also contains music list metadata.
O2Jam Package Interface & Avatar
OPI (O2Jam Package Interface) and OPA (O2Jam Package Avatar) are archive container formats that hold multiple sprite resources as well as a handful of metadata files. Both use the same structure; the only difference is the archive signature value: 01 for OPA and 02 for OPI.
Typically, the client ships with Interface.opi (or Interface1.opi), Playing.opi (or Playing1.opi), and Avatar.opa. Some distributions (notably the e-Games international client, and NOWCOM) use the names Interface1.opi and Playing1.opi with a numeral suffix, while others (such as the GAMANIA or 9you clients) simply use Interface.opi and Playing.opi. These files are identical in structure, the naming is just a distribution-level difference for reasons unknown.
The Interface archive contains all non-gameplay UI sprites (login screen, server and channel select, music select, etc.), Playing archive contains gameplay-related sprites (note skins, judgement indicators, combo numbers, gauge, etc.), and Avatar archive contains the character avatar sprites and related item visuals.
In addition to visual assets, the archive contains one or two metadata files. Note that the game may not refer them with filename, but instead refer them by the index inside the archive (in which usually these metadata are last in the file header list). See Control List and Item Data to learn more about the format.
Archive file format
| Archive header | ||
|---|---|---|
| Offset | Type | Description |
| 0 | int32 | Archive Signature |
| 4 | int32 | File count |
| 8 | int32 | (Unused) |
| 12 | int32 | (Unused) |
| 16 | bytes[N] | File contents |
| 16 + N | bytes[152 × File count] | File headers |
| File header | ||
|---|---|---|
| Offset | Type | Description |
| +0 | int32 | File Signature |
| +4 | char[128] | Filename |
| +132 | int32 | Data offset |
| +136 | int32 | File Size 1* |
| +140 | int32 | File Size 2* |
| +144 | int32 | (Unused) |
| Archive signature | |
|---|---|
| Value | Description |
| 1 | OPA |
| 2 | OPI |
| File signature | |
|---|---|
| Value | Description |
| 0 | Unused |
| 1 | Valid |
O2Jam Sprites
O2Jam sprite files typically use one of these extensions: .ojs, .oji, .ojt, and .oja, with .ojs being the most common. A sprite file is a container that holds multiple frame headers and texture with raw bitmap data format for each corresponding sprite frame. These bitmaps are stored as individual frames, not combined into a texture atlas that commonly found in many other games.
All of these extensions share the same overall file format structure, but the extension may hint at how the game should use them (e.g., use a custom blending mode, or render via Direct3D instead of DirectDraw). Note that this is not always the case; the game can override this behavior, sometimes without any obvious reason.
Sprite file format
| Sprite header | ||
|---|---|---|
| Offset | Type | Description |
| 0 | int16 | Signature |
| 2 | int16 | Format |
| 4 | int16 | Total frames |
| 6 | int16 | Transparency Color Key |
| Frame header | ||
|---|---|---|
| Offset | Type | Description |
| +0 | int16 | X position of the sprite frame |
| +2 | int16 | Y position of the sprite frame |
| +4 | int16 | Width of the sprite frame |
| +6 | int16 | Height of the sprite frame |
| +8 | int32 | Bitmap data offset |
| +12 | int32 | Bitmap data size |
| +16 | int32 | (Unused) |
| Sprite signature | |
|---|---|
| Value | Description |
| 1 | Valid |
| Format | |
|---|---|
| Value | Description |
0x0555 | 16-bit per pixel (RGB555) |
0x555A | Custom 16-bit per pixel |
Important notes
- The frame header defines the position of the sprite relative to world space, as well as the frame dimensions.
- Bitmap data is raw / headerless. It does not include a typical bitmap header.
- The pixel format is always 16-bit per pixel RGB555, but one particular format variant encodes the RGB color with custom logic.
- If the sprite frame width is Not Power of Two (NPOT), the bitmap data will be uneven on every other scanline. Refer to this code sample for handling the scanline logic when decoding or encoding the bitmap data.
- The position in the frame header is a world-space coordinate used only for rendering. For input hit-testing, see O2Jam Boundary below.
O2Jam Boundary
A boundary file defines the input hit-test regions, that is, the coordinates and dimensions of each clickable area, for objects in a scene. The file extension is .bnd.
The boundary information stored in this file is extremely simple: each bound does not contain a reference to the scene object it describes. As such, the mapping between each scene object (defined in the Control List) and boundary data in this file is typically index-based, but this is not always the case, particularly for control list entries or scene objects that are hardcoded inside the client.
Typically, one boundary frame belongs to one sprite object. This means that even if a sprite has multiple frames, each with a unique size or position, the bound (input hit-test) for the object remains unchanged.
| Boundary list header | ||
|---|---|---|
| Offset | Type | Description |
| 0 | int32 | Signature |
| 4 | int16 | Total bounds |
| Boundary frame header | ||
|---|---|---|
| Offset | Type | Description |
| +0 | int32 | Left |
| +4 | int32 | Top |
| +8 | int32 | Right |
| +12 | int32 | Bottom |
O2Jam Control List
The control list is a text-based metadata file that defines the composition of objects in every scene of the game. The files are typically named ControlList_Interface.txt or ControlList_Playing.txt, depending on the archive. The structure is somewhat similar to VDF.
The ordering of objects in this table does not affect the z-index render order, but it may affect the mapping with the boundary table. Note that the game sometimes refers to a sprite by its OJS filename directly instead of looking it up from this table. As such, many sprites may be missing from the list. In such cases, the boundary is either resolved using hardcoded indices from the boundary file or the bound uses values hardcoded inside the game.
File Structure
The control list uses a hierarchical structure of states (a.k.a. scenes), objects, and nested sets.
- Comments begin with double slashes (
//) and continue to the end of the line. - Parameters are separated by whitespace (spaces, tabs, or a combination).
- Parameters are either a number formatted in hexadecimal or a string expressing a filename enclosed in double quotes (
"file_name.ext"). - Each object definition (name and parameters) must appear on a single line.
- Curly braces (
{}) represent a container object that can contain other objects.
Top-Level Definitions
NUMBER_OF_STATES
Defines the number of STATE_* objects.
NUMBER_OF_STATES 0x01
Parameters:
- Number of states
NUMBER_OF_DIALOGS
Defines the number of DIALOG_* objects.
NUMBER_OF_DIALOGS 0x01
Parameters:
- Number of dialogs
STATE
Represents a state (also known as a scene).
Each state must have a unique name and typically starts with the STATE_ prefix.
STATE_LOGO 0x03 0x00
{
// State objects
}
Parameters:
- Number of state objects
- Number of
SETobjects within state objects
DIALOG
Represents a modal dialog.
Each dialog must have a unique name and typically starts with the DIALOG_ prefix.
DIALOG_INFORMATION 0x03
{
// Dialog objects
}
Parameters:
- Number of dialog objects
Object Types
BOUND Declaration
Each STATE must contain exactly one BOUND declaration, which points to a boundary file describing the input hit-test regions of the state’s objects.
BOUND "file.bnd"
Parameter:
- Boundary filename
IDC Objects
IDC (short for ID Control) is a regular object that acts as a basic building block of the control list. These typically follow the naming convention IDC_<Type>_<Name>.
IDC_IMAGE_BACKGROUND 0x11020304 "background.ojs"
Parameters:
- Object ID: in hexadecimal format, typically composed of four parts (1 byte each):
- Byte 1: State ID (typically state index + 11)
- Byte 2: Object type
- Byte 3: Group/layer ID
- Byte 4: Object ID
- Asset filename: typically refers to a sprite file
Important notes
Note that the four-part convention for the Object ID is optional. The game does not parse the Object ID into four parts. It stores them as a single integer and uses it as an identifier to associate with the corresponding sprite object.
Object Type Codes
The second byte of the Object ID maps to one of the following object types:
| Code | Object Type |
|---|---|
| 00 | STATE |
| 01 | Toggle/Radio/Checkbox |
| 02 | Scroll Bar |
| 10 | Image/Sprite |
| 20 | Button |
| 30 | Input Text Box |
| 50 | Interactable/Clickable |
| 60 | SET |
| 70 | Avatar |
Note: The assets often use Interactable interchangeably with Button or Toggle.
SET Objects
SET objects are containers that group multiple objects, typically used for repeated elements such as row items in a list or table.
SET 0x02 0x11600200
{
// Nested objects
}
Parameters:
- Number of objects inside the SET
- Object ID (same format as regular objects)
Example
// Define composition with one state
NUMBER_OF_STATES 0x01
// Logo screen state with 3 objects
STATE_LOGO 0x03 0x00
{
// Boundary definition
BOUND "logo.bnd"
// Background sprite
IDC_IMAGE_BACKGROUND 0x11100100 "background.ojs"
// Button set containing 2 buttons
SET 0x02 0x11600200
{
IDC_BUTTON_START 0x11200201 "button_start.ojs"
IDC_BUTTON_EXIT 0x11200202 "button_exit.ojs"
}
}
O2Jam Item Data Table
Item data is a collection of avatar item metadata available in the game. The filename is ItemData.dat (or ItemData_TICT.dat, ItemData_Korea.dat, ItemData_China.dat, etc. — this depends on the distribution). There is a legacy filename for item data called O2-x001.ojs, it is unclear at which point the client use this in the past.
There are two known layout variants: the New version includes two extra int32 special item flags before the name field, while the Old version does not.
File header
| ItemData header | ||
|---|---|---|
| Offset | Type | Description |
| 0 | int32 | Number of items |
Item entry header
| Item (common fields) | ||
|---|---|---|
| Offset | Type | Description |
| +0 | int32 | Item ID |
| +4 | byte | Item Type (see Item Type) |
| +5 | byte | Planet Origin (see Planet) |
| +6 | int16 | Gender (bitflag, see Gender) |
| +8 | int16 | Quantity (for Attributive Items) |
| +10 | byte | Attributive Effect (see Attributive Effect) |
| +11 | byte | Attributive Category (see Attributive Category) |
| +12 | byte | Payment Method (see Payment Method) |
| +13 | int32 | Price in GEM |
| +17 | int32 | Price in ePoint / O2Cash / Gash / MCash |
| +21 | byte | Item Slot (see Item Slot) |
The remaining fields differ depending on the version:
| Old version (e-Games) | |||
|---|---|---|---|
| Offset | Type | Description | |
| +22 | int32 | Name Length (in bytes) | |
| +26 | char[] | Item Name | |
| +26+n | int32 | Description Length (in bytes) | |
| +30+n+m | char[] | Item Description | |
ASCII. | New version (NOWCOM) | |||
|---|---|---|---|
| Offset | Type | Description | |
| +22 | int32 | Special Item Flag (Male)* | |
| +26 | int32 | Special Item Flag (Female)* | |
| +30 | int32 | Name Length (in bytes) | |
| +34 | char[] | Item Name | |
| +34+n | int32 | Description Length (in bytes) | |
| +38+n+m | char[] | Item Description | |
0 or 10 (0x0A). String encoding typically EUC-KR. After the name and description, each item entry contains exactly 42 file header references, each referencing an OJS sprite file. The order of these references determines which body part and instrument animation combination the sprite represents. See Item File References below.
Item file header slot (repeated 42 times per item):
| Item file header slot | ||
|---|---|---|
| Offset | Type | Description |
| +0 | bool | Slot status (active / unused) |
| +1 | int32 | Filename Length (in bytes) |
| +5 | char[] | OJS Filename (null-terminated string) |
Enumerations
Item Type, Planets and Payment Method
| Item Type | |
|---|---|
| Value | Description |
| 0 | Body |
| 1 | Left Arm |
| 2 | Right Arm |
| 3 | Left Hand |
| 4 | Right Hand |
| 5 | Face |
| 6 | Hair |
| 7 | Glasses |
| 8 | Earring |
| 9 | Necklace |
| 10 | Armlet |
| 11 | Accessories |
| 12 | Glove |
| 13 | Pants |
| 14 | Shoes |
| 15 | Musical Instrument – Piano |
| 16 | Musical Instrument – Bass |
| 17 | Musical Instrument – Drum |
| 18 | Musical Instrument – Guitar |
| 19 | Shirts |
| 20 | Wings |
| 21 | Musical Accessories |
| 22 | Pet |
| 23 | Hair Accessories |
| 24 | Attributive Item (Skill) |
| Planet | |
|---|---|
| Value | Description |
| 0 | All |
| 1 | O2Planet |
| 2 | Aqua |
| 3 | Eliten |
| 4 | Graffiti |
| 5 | Bikini |
| 6 | Crush |
| 7 | Wonderland |
| 8 | Meganut |
| 9 | Crystal |
| 10 | Draconic |
| 11 | Event |
| Payment method | |
|---|---|
| Value | Description |
| 0 | Not for Sale |
| 1 | Gem |
| 2 | ePoint / O2Cash / Gash / MCash |
| 3 | Any (NOWCOM version only) |
Gender
Gender is stored as a bitflag in the int16 value. The field encodes two pieces of information:
- Gender: Bits 7–10:
(value >> 7) & 0xF Newmark: Bit 11:(value >> 11) & 1
| Gender value | |
|---|---|
| Value | Description |
| 0 | Female |
| 1 | Male |
| 2 | Unisex |
| New mark value | |
|---|---|
| Value | Description |
| 0 | Not New |
| 1 | New |
| Raw value examples | ||||
|---|---|---|---|---|
| Raw (Hex) | Raw (Dec) | Gender | New mark | Description |
0x0000 | 0 | Female | Not New | Female Item |
0x0080 | 128 | Male | Not New | Male Item |
0x0100 | 256 | Unisex | Not New | Unisex Item |
0x0800 | 2048 | Female | New | New Female Item |
0x0880 | 2176 | Male | New | New Male Item |
0x0900 | 2304 | Unisex | New | New Unisex Item |
Attributive (Skills)
These properties describe Attributive Items (also known as Skills) and are only valid when the Item Type is 24.
| Attributive Effect | |
|---|---|
| Value | Description |
| 0 | None |
| 1 | Power |
| 2 | Mirror |
| 3 | Random |
| 4 | Panic |
| 5 | Hidden |
| 6 | Sudden |
| 7 | Dark |
| Attributive Category | |
|---|---|
| Value | Description |
| 0 | None |
| 1 | Power |
| 2 | Arrangement |
| 3 | Visibility |
Item Slot
| Value | Description | Item Type Code |
|---|---|---|
| 0 | Musical Instrument | 15, 16, 17, 18 |
| 1 | Hair | 6 |
| 2 | Accessories | 11 |
| 3 | Glove | 12 |
| 4 | Necklace | 9 |
| 5 | Shirts | 19 |
| 6 | Pants | 13 |
| 7 | Glasses | 7 |
| 8 | Earring | 8 |
| Value | Description | Item Type Code |
|---|---|---|
| 9 | Armlet | 10 |
| 10 | Shoes | 14 |
| 11 | Face | 5 |
| 12 | Wing | 20 |
| 13 | Musical Accessories | 21 |
| 14 | Pet | 22 |
| 15 | Hair Accessories | 23 |
| 255 | Body / Arms / Hands | 0–4 |
| 255 | Attributive Item | 24 |
Item File References
There are 42 file references per item. The order determines which body part and instrument animation the sprite will be used for:
| Index | Description |
|---|---|
| 0 | Preview / Thumbnail (Small) |
| 1 | Preview / Thumbnail (Big) |
| 2 | Body – No Instrument (Male) |
| 3 | Body – No Instrument (Female) |
| 4 | Body – Bass (Male) |
| 5 | Body – Bass (Female) |
| 6 | Body – Guitar (Male) |
| 7 | Body – Guitar (Female) |
| 8 | Body – Keyboard (Male) |
| 9 | Body – Keyboard (Female) |
| 10 | Body – Drum (Male) |
| 11 | Body – Drum (Female) |
| 12 | Right Arm – No Instrument (Male) |
| 13 | Left Arm – No Instrument (Female) |
| 14 | Left Arm – Bass (Male) |
| 15 | Left Arm – Bass (Female) |
| 16 | Left Arm – Guitar (Male) |
| 17 | Left Arm – Guitar (Female) |
| 18 | Left Arm – Keyboard (Male) |
| 19 | Left Arm – Keyboard (Female) |
| 20 | Left Arm – Drum (Male) |
| Index | Description |
|---|---|
| 21 | Left Arm – Drum (Female) |
| 22 | Right Arm – No Instrument (Male) |
| 23 | Right Arm – No Instrument (Female) |
| 24 | Right Arm – Bass (Male) |
| 25 | Right Arm – Bass (Female) |
| 26 | Right Arm – Guitar (Male) |
| 27 | Right Arm – Guitar (Female) |
| 28 | Right Arm – Keyboard (Male) |
| 29 | Right Arm – Keyboard (Female) |
| 30 | Right Arm – Drum (Male) |
| 31 | Right Arm – Drum (Female) |
| 32 | Cape – No Instrument (Male) |
| 33 | Cape – No Instrument (Female) |
| 34 | Cape – Bass (Male) |
| 35 | Cape – Bass (Female) |
| 36 | Cape – Guitar (Male) |
| 37 | Cape – Guitar (Female) |
| 38 | Cape – Keyboard (Male) |
| 39 | Cape – Keyboard (Female) |
| 40 | Cape – Drum (Male) |
| 41 | Cape – Drum (Female) |
O2Jam Set Info Table
Set info data table defines item set packages available in the item shop. The filename is typically SetInfoData.ojs. The item set bundle up to 5 items at a time and typically, at discounted price.
File header
| SetInfoData header | ||
|---|---|---|
| Offset | Type | Description |
| 0 | int32 | Number of sets |
| 4 | int32 | (Unused) |
| 8 | int32 | (Unused) |
Set entry header
| Set item | ||
|---|---|---|
| Offset | Type | Description |
| +0 | int32 | Set ID |
| +4 | byte | Planet Origin (see Planet) |
| +5 | int16 | New mark, Gender, and Payment Method Bitflag (see Set Bitflag) |
| +7 | int16 | Number of items included in the set |
| +9 | int32 | Item ID 1 |
| +13 | int32 | Item ID 2 |
| +17 | int32 | Item ID 3 |
| +21 | int32 | Item ID 4 |
| +25 | int32 | Item ID 5 |
| +29 | int32 | Item Price 1 |
| +31 | int32 | Item Price 2 |
| +35 | int32 | Item Price 3 |
| +39 | int32 | Item Price 4 |
| +43 | int32 | Item Price 5 |
| +47 | int32 | Item Discount 1 |
| +51 | int32 | Item Discount 2 |
| +55 | int32 | Item Discount 3 |
| +59 | int32 | Item Discount 4 |
| +53 | int32 | Item Discount 5 |
| +57 | int32 | Name Length (in bytes) |
| +61 | char[] | Set Name |
| +65+n | int32 | Description Length (in bytes) |
| +69+n+m | char[] | Item Description |
Set Bitflag
New marker, Gender and Payment Method are stored as a bitflag in the int16 value. The fields are encoded as follows:
- Gender:
(bitFlag >> 6) & 1 - New marker: TBA
- Payment Method: TBA
O2Jam Music List
O2Jam stores the entire music list that the game recognizes in a file called OJNList.dat (or X2OJNList.dat in X2 version). Each entry contains music metadata, which is exactly the entire header (the first 300 bytes) of OJN file.
The music list defines what songs the game knows about, and not necessarily what’s playable. Actual playability depends on whether the corresponding OJN and OJM files are present locally.
In O2Jam v3.10, OJNList.dat is embedded within Playing(1).opi. While a remnant of OJNList.dat still exists inside Playing(1).opi in newer client versions, it is no longer used.
File header
| OJNList header | ||
|---|---|---|
| Offset | Type | Description |
| 0 | int32 | Number of music |
Music header
The music header is equal to the first 300 bytes of OJN file. As such, refer to the documentation below.
Music List Extra Sections
In e-Games and NOWCOM distribution, OJNList.dat (and X2OJNList.dat) typically has extra section at the end of Music header array. Sometimes these data interpreted differently in different client version (e.g, NX vs. X2).
Unfortunately, most of the fields in these sections are unknown, but the primary purpose of the data is more or less known.
New marker
Defines music ids with new marker.
| New marker section header | ||
|---|---|---|
| Offset | Type | Description |
| +0 | int32 | Number of music |
| New marker entry header | ||
|---|---|---|
| Offset | Type | Description |
| +0 | int32 | Music ID |
| +4 | int32 | Unknown1 |
| +8 | int32 | Unknown2 |
| +12 | int32 | Unknown3 |
1. Always 0
2. Sometimes 1, but otherwise 0
3. Sometimes 0, but otherwise 1
Premium (Standard client)
Defines premium music ids, which need to be purchased in music shop before become playable.
| Premium section header | ||
|---|---|---|
| Offset | Type | Description |
| +0 | int32 | Number of music |
| Premium entry header | ||
|---|---|---|
| Offset | Type | Description |
| +0 | int32 | Music ID |
| +4 | int32 | Unknown1 |
| +8 | int32 | Unknown2 |
| +12 | int32 | Unknown3 |
1. Likely to be Price in premium currency (ePoint, MCash, Gash)
2. Likely to be Price in second premium currency (O2Cash, MusicCash)
3. Likely to be Price in GEM
Mission (X2)
Defines list of mission metadata that maps to the actual music ids.
| Music section header | ||
|---|---|---|
| Offset | Type | Description |
| +0 | int32 | Number of missions |
| Mission entry header | ||
|---|---|---|
| Offset | Type | Description |
| +0 | int32 | Music ID |
| +4 | int32 | Music Difficulty |
| +8 | int32 | (Unused) |
| +12 | int32 | Mission Level |
| Music Difficulty | ||
|---|---|---|
| Value | Code | Description |
| 1 | EX | Easy |
| 2 | NX | Normal |
| 3 | HX | Hard |
Extra metadata section
A section that contains extra metadata such as release date information.
| Extra metadata section header | ||
|---|---|---|
| Offset | Type | Description |
| +0 | int32 | Number of music |
| Extra metadata entry header | ||
|---|---|---|
| Offset | Type | Description |
| +0 | int32 | Music ID |
| +4 | char[10] | Release Date (Format: yyyy-MM-dd) |
| +14 | int16 | Unknown |
| +16 | int32 | Unknown |
| +20 | int32 | Unknown |
| +24 | int32 | Unknown |
Unknown fields are not verified. It might be possible that it is not int32.
Music
The Music directory holds all of the music assets used by the game client. Music assets consist of only two file formats: .ojn and .ojm.
All playable music filenames (except tutorial) are always start with o2ma followed by music id. Officially, music id start from 100, but there’s no prevention to have music id with lesser value than 100. Apart from the playable music, the game also provide a couple of OJM files that act as game background music or ui sound effects.
O2Jam Note
The OJN file holds music metadata, cover and thumbnail art, and note information.
Refer to the Open2Jam documentation for the file format:
- https://open2jam.wordpress.com/the-ojn-documentation/
- https://open2jam.wordpress.com/2010/10/05/the-notes-section/
Encrypted OJN
Starting from the late O2Jam NX, the OJN file may be encrypted. The game client however, is able to process both encrypted and decrypted OJN file.
| Encrypted OJN header | ||
|---|---|---|
| Offset | Type | Description |
| 0 | char[3] | Signature (new) |
| 3 | byte | XOR key block size |
| 4 | byte | Primary XOR Key |
| 5 | byte | Middle XOR Key |
| 6 | byte | Initial XOR Key |
In order to perform decryption, you have to do 2 steps based on the above data:
- Construct XOR Key
- XOR the reversed OJN data with the XOR Key
Constructing XOR Key
Use the values from header to create an array of bytes that act as the XOR key. Set the length of the array to XOR key block size and fill it entirely with the Primary XOR Key, except for two special positions: the very first byte is replaced with Initial XOR Key, and the byte at index floor(xor key block size / 2) is replaced with Middle XOR Key.
Decrypting the data
Before XOR-ing, the entire payload (everything after the 8-byte header) is read backwards. Walks through the reversed data in chunks of that equal to XOR Key block size, XOR-ing each chunk against the key block. The result is the original, unencrypted OJN data.
Refer to the following code sample for decryption algorithm.
O2Jam Music
OJM is a sound bank container that holds either .wav or .ogg files.
Refer to the Open2Jam documentation for the file format: