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 use EUC-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 are Interface, Playing, and Avatar.
  • Patch Number: Used to differentiate multiple patch files. By convention, it is patch build version.
  • Extension: Either opi or opa.

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)
* Prefer to use File Size 1, or check whichever is greater between File Size 1 and File Size 2.
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
All positions in a boundary frame use absolute world-space coordinates.

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 SET objects 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
String encoding is typically standard 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
* Value is 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
ValueDescription
0Body
1Left Arm
2Right Arm
3Left Hand
4Right Hand
5Face
6Hair
7Glasses
8Earring
9Necklace
10Armlet
11Accessories
12Glove
13Pants
14Shoes
15Musical Instrument – Piano
16Musical Instrument – Bass
17Musical Instrument – Drum
18Musical Instrument – Guitar
19Shirts
20Wings
21Musical Accessories
22Pet
23Hair Accessories
24Attributive Item (Skill)
Planet
ValueDescription
0All
1O2Planet
2Aqua
3Eliten
4Graffiti
5Bikini
6Crush
7Wonderland
8Meganut
9Crystal
10Draconic
11Event
Payment method
ValueDescription
0Not for Sale
1Gem
2ePoint / O2Cash / Gash / MCash
3Any (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
  • New mark: Bit 11: (value >> 11) & 1
Gender value
ValueDescription
0Female
1Male
2Unisex
New mark value
ValueDescription
0Not New
1New
Raw value examples
Raw (Hex) Raw (Dec) Gender New mark Description
0x00000FemaleNot NewFemale Item
0x0080128MaleNot NewMale Item
0x0100256UnisexNot NewUnisex Item
0x08002048FemaleNewNew Female Item
0x08802176MaleNewNew Male Item
0x09002304UnisexNewNew 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
ValueDescription
0None
1Power
2Mirror
3Random
4Panic
5Hidden
6Sudden
7Dark
Attributive Category
ValueDescription
0None
1Power
2Arrangement
3Visibility
Item Slot
Value Description Item Type Code
0Musical Instrument15, 16, 17, 18
1Hair6
2Accessories11
3Glove12
4Necklace9
5Shirts19
6Pants13
7Glasses7
8Earring8
Value Description Item Type Code
9Armlet10
10Shoes14
11Face5
12Wing20
13Musical Accessories21
14Pet22
15Hair Accessories23
255Body / Arms / Hands0–4
255Attributive Item24
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:

IndexDescription
0Preview / Thumbnail (Small)
1Preview / Thumbnail (Big)
2Body – No Instrument (Male)
3Body – No Instrument (Female)
4Body – Bass (Male)
5Body – Bass (Female)
6Body – Guitar (Male)
7Body – Guitar (Female)
8Body – Keyboard (Male)
9Body – Keyboard (Female)
10Body – Drum (Male)
11Body – Drum (Female)
12Right Arm – No Instrument (Male)
13Left Arm – No Instrument (Female)
14Left Arm – Bass (Male)
15Left Arm – Bass (Female)
16Left Arm – Guitar (Male)
17Left Arm – Guitar (Female)
18Left Arm – Keyboard (Male)
19Left Arm – Keyboard (Female)
20Left Arm – Drum (Male)
IndexDescription
21Left Arm – Drum (Female)
22Right Arm – No Instrument (Male)
23Right Arm – No Instrument (Female)
24Right Arm – Bass (Male)
25Right Arm – Bass (Female)
26Right Arm – Guitar (Male)
27Right Arm – Guitar (Female)
28Right Arm – Keyboard (Male)
29Right Arm – Keyboard (Female)
30Right Arm – Drum (Male)
31Right Arm – Drum (Female)
32Cape – No Instrument (Male)
33Cape – No Instrument (Female)
34Cape – Bass (Male)
35Cape – Bass (Female)
36Cape – Guitar (Male)
37Cape – Guitar (Female)
38Cape – Keyboard (Male)
39Cape – Keyboard (Female)
40Cape – Drum (Male)
41Cape – 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
Item ID, price, and discount are ignored beyond the specified number of items (e.g, Item ID, Price and Discount 4 are ignored if the number of items in a set is only 3).
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:

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:

https://open2jam.wordpress.com/the-ojm-documentation/