This document provides a comprehensive reference for every file format used by the O2Jam game client. It describes how the game read and use every assets in the game client.
Existing documentation that has already been published elsewhere is linked rather than duplicated.
Unless stated otherwise, the following conventions apply throughout this document:
- All formats use little-endian byte order.
- All strings are null-terminated.
- String encoding varies by client distribution. For example, e-Games clients commonly use standard
ASCII, while mgame and NOWCOM (Korea) clients useEUC-KR.
A typical O2Jam game client directory is structured as follows:
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.
Temporary folders (TEMP/) inside both Image and Music hold patch files that have yet to be applied by the game client.
Image patch files typically use .opi or .opa format depending on the target archive. See Package Interface and Avatar for the format specification. Patch files are downloaded by O2JamPatchClient.exe, not the game client itself.
The patch package file contains sprite or metadata files that will be merged into the target package. If a file already exists in the target package, the patch overrides it. The game will not process sprite files and metadata files directly; the patch file must be a package file.
Patch filenames 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, this is the patch build version.Extension: Eitheropioropa.
Unlike image patches, music patches are downloaded by the main client (OTwo.exe) rather than the game patcher. The TEMP/ folder holds in-progress download files for both OJN and OJM files, typically via Music Shop. Once a download completes, the files are moved out of TEMP/.
In addition to the game music files, the client persists download state via INI file until the requested files are fully downloaded (or the download fails). The filename follows the pattern o2ma<music_id>.ini:
[FileInfo]
FileNum=2
FileName1=o2ma100.ojn
FileStatus1=0
FileName2=o2ma100.ojm
FileStatus2=0The Image directory holds all visual assets used by the game client: UI elements, avatar sprites, and music list metadata.
OPI (O2Jam Package Interface) and OPA (O2Jam Package Avatar) are archive container formats that hold multiple sprite resources and 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 e-Games and NOWCOM) use names with a numeral suffix (Interface1.opi, Playing1.opi), while others (such as GAMANIA or 9you) use names without it. The files are identical in structure; the naming is a distribution-level difference.
- The Interface archive contains all non-gameplay UI sprites (login screen, server/channel select, music select, etc.).
- The Playing archive contains gameplay-related sprites (note skins, judgement indicators, combo numbers, gauge, etc.).
- The Avatar archive contains character avatar sprites and related item visuals.
In addition to visual assets, each archive contains one or two metadata files. The game may reference these by index in the file header list rather than by filename, though the client may still verify the filename before parsing. See Control List and Item Data for details.
Certain archives contain legacy files that are no longer used:
OJNList.datinPlaying(1).opi: May have been used by the v3.10 client, asOJNList.datis absent from theImage/directory in that version.JamTable.datinPlaying(1).opi: Purpose unknown; the client version that used it is unclear.O2-x001.ojsinAvatar.opa: Contains an item data table with the same structure asItemData.dat.
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 File Size 1, or use whichever is greater between File Size 1 and File Size 2.
Archive signature values
| Value | Description |
|---|---|
| 1 | OPA |
| 2 | OPI |
File signature values
| Value | Description |
|---|---|
| 0 | Unused |
| 1 | Valid |
O2Jam sprite files use one of these extensions: .ojs, .oji, .ojt, and .oja, with .ojs being the most common. A sprite file is a container holding multiple frame headers and raw bitmap data for each corresponding sprite frame. Bitmaps are stored as individual frames, not as a combined texture atlas.
All extensions share the same file format structure, but the extension may hint at how the game should use the sprite (e.g., custom blending mode, or rendering via Direct3D instead of DirectDraw). This is not always the case; the game can override this behavior.
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 values
| Value | Description |
|---|---|
| 1 | Valid |
Format values
| Value | Description |
|---|---|
0x0555 |
16-bit per pixel (RGB555) |
0x555A |
Custom 16-bit per pixel |
- 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 format variant encodes 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 scanline handling when decoding or encoding bitmap data.
- The position in the frame header is a world-space coordinate used only for rendering. For input hit-testing, see O2Jam Boundary.
A boundary file defines the input hit-test regions — the coordinates and dimensions of each clickable area — for objects in a scene. The file extension is .bnd.
Each bound does not contain a reference to the scene object it describes. The mapping between scene objects (defined in the Control List) and boundary data is typically index-based, but this is not always the case, particularly for control list entries or scene objects that are hardcoded in the client.
Typically, one boundary frame belongs to one sprite object. Even if a sprite has multiple frames with unique sizes or positions, the bound 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.
The control list is a text-based metadata file that defines the composition of objects in every scene of the game. Files are typically named ControlList_Interface.txt or ControlList_Playing.txt, depending on the archive. The structure is similar to VDF.
The ordering of objects in this table does not affect z-index render order, but it may affect mapping with the boundary table. The game sometimes refers to a sprite by its OJS filename directly rather than looking it up from this table. Many sprites may be missing from the list; in such cases, the boundary is either resolved using hardcoded indices from the boundary file or uses values hardcoded in the game.
The control list uses a hierarchical structure of states (scenes), objects, and nested sets.
- Comments begin with double slashes (
//) and continue to end of line. - Parameters are separated by whitespace (spaces, tabs, or a combination).
- Parameters are either hexadecimal numbers or strings 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.
Defines the number of STATE_* objects.
NUMBER_OF_STATES 0x01
Parameters:
- Number of states
Defines the number of DIALOG_* objects.
NUMBER_OF_DIALOGS 0x01
Parameters:
- Number of dialogs
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
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
Each STATE must contain exactly one BOUND declaration, which points to a boundary file describing the input hit-test regions of the state's or dialog's objects.
BOUND "file.bnd"
Parameter:
- Boundary filename
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: 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
Note: The four-part convention for the Object ID is informal. The game does not parse it into four parts; it stores the value as a single integer and uses it as an identifier to associate with the corresponding sprite object.
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
Interactableinterchangeably withButtonorToggle.
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)
// 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"
}
}
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., depending on the distribution). A legacy filename O2-x001.ojs uses the same structure. It is unclear at which point the client used this legacy file.
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.
| Offset | Type | Description |
|---|---|---|
| 0 | int32 |
Number of items |
| 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 by 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 | 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 | char[] |
Item Description |
* Value is 0 or 10 (0x0A). String encoding is 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 determines which body part and instrument animation combination the sprite represents. See Item File References.
Repeated 42 times per item:
| Offset | Type | Description |
|---|---|---|
| +0 | bool |
Slot status (active / unused) |
| +1 | int32 |
Filename Length (in bytes) |
| +5 | char[] |
OJS Filename (null-terminated string) |
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 is stored as a bitflag in the int16 value. The field encodes two pieces of information:
- Gender: Bits 7–10:
(value >> 7) & 0xF Newlabel: Bit 11:(value >> 11) & 0x1
Gender values
| Value | Description |
|---|---|
| 0 | Female |
| 1 | Male |
| 2 | Unisex |
New label values
| Value | Description |
|---|---|
| 0 | Not New |
| 1 | New |
Raw value examples
| Raw (Hex) | Raw (Dec) | Gender | New Label | 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 |
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 |
| 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 |
| 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 |
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) |
| 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) |
The set info data table defines item set packages available in the item shop. The filename is typically SetInfoData.ojs. Item sets bundle up to 5 items at a time, typically at a discounted price.
| Offset | Type | Description |
|---|---|---|
| 0 | int32 |
Number of sets |
| Offset | Type | Description |
|---|---|---|
| +0 | int32 |
Set ID |
| +4 | byte |
First Bitflag: New label and Planet Origin (see Set Bitflag & Planet) |
| +5 | byte |
Second Bitflag: Gender (see Set Bitflag) |
| +6 | bool |
Use discounted price* |
| +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 |
| +33 | int32 |
Item Price 2 |
| +37 | int32 |
Item Price 3 |
| +41 | int32 |
Item Price 4 |
| +45 | int32 |
Item Price 5 |
| +49 | int32 |
Item Sale Price 1 |
| +53 | int32 |
Item Sale Price 2 |
| +57 | int32 |
Item Sale Price 3 |
| +61 | int32 |
Item Sale Price 4 |
| +65 | int32 |
Item Sale Price 5 |
| +69 | int32 |
Name Length (in bytes) |
| +73 | char[] |
Set Name |
| +73+n | int32 |
Description Length (in bytes) |
| +77+n | char[] |
Item Description |
Item ID, price, and discount fields are ignored beyond the specified number of items (e.g., Item ID 4, Price 4, and Discount 4 are ignored if the set contains only 3 items).
* The actual numeric value may fall outside the range 0–1. The value often correlates with floor((total_price − final_price) / total_price * 100) (discount rate) for many entries, but is not used numerically by the client — only the zero/non-zero check matters.
New label, Gender, and Planet are stored as bitflags. The fields are encoded as follows:
- New label:
first & 0x7F - Planet:
(first >> 7) & 1 - Gender:
(second >> 6)
O2Jam stores the entire music list that the game recognizes in a file called OJNList.dat (or X2OJNList.dat in the X2 version). Each entry contains music metadata, which is the entire header (the first 300 bytes) of an OJN file.
The music list defines what songs the game knows about, not necessarily what is 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. A remnant still exists inside Playing(1).opi in newer client versions, but it is no longer used.
| Offset | Type | Description |
|---|---|---|
| 0 | int32 |
Number of music entries |
Each music header is equal to the first 300 bytes of an OJN file. Refer to that section for the field layout.
In e-Games and NOWCOM distributions, OJNList.dat (and X2OJNList.dat) typically contains extra sections appended after the music header array. These sections may be interpreted differently across client versions (e.g., NX vs. X2).
Most fields in these sections are unknown, but their primary purposes are documented below.
Defines music IDs with the "new" label.
Section header
| Offset | Type | Description |
|---|---|---|
| +0 | int32 |
Number of music entries |
Entry header
| Offset | Type | Description |
|---|---|---|
| +0 | int32 |
Music ID |
| +4 | int32 |
Unknown¹ |
| +8 | int32 |
Unknown² |
| +12 | int32 |
Unknown³ |
The client never read the (Unused) values. This list is merely used to mark songs with new label.
1. Always 0
2. Sometimes 1, but otherwise 0
3. Sometimes 0, but otherwise 1
Defines premium music IDs that must be purchased in the music shop before becoming playable.
Section header
| Offset | Type | Description |
|---|---|---|
| +0 | int32 |
Number of music entries |
Entry header
| Offset | Type | Description |
|---|---|---|
| +0 | int32 |
Music ID |
| +4 | int32 |
Price in ePoint / O2Cash / Gash / MCash |
| +8 | int32 |
Price in GEM |
| +12 | int32 |
(Unused)* |
* Likely price in second premium currency (O2Cash, MusicCash)
Defines a list of mission metadata that maps to actual music IDs.
Section header
| Offset | Type | Description |
|---|---|---|
| +0 | int32 |
Number of missions |
Entry header
| Offset | Type | Description |
|---|---|---|
| +0 | int32 |
Music ID |
| +4 | int32 |
Music Difficulty |
| +8 | int32 |
(Unused) |
| +12 | int32 |
Mission Level |
Music Difficulty values
| Value | Code | Description |
|---|---|---|
| 1 | EX |
Easy |
| 2 | NX |
Normal |
| 3 | HX |
Hard |
A section that contains extra metadata, such as release date information. Note that O2Jam NX (up to v3.82) does not read this section at all.
Section header
| Offset | Type | Description |
|---|---|---|
| +0 | int32 |
Number of music entries |
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 |
The game does not read this section.
Unknown fields are not verified. It might be possible that the bandwith size is not correct (it is not int16 or int32).
The Music directory holds all music assets used by the game client, consisting of two file formats: .ojn and .ojm.
All playable music filenames (except the tutorial) begin with o2ma followed by a music ID. Officially, music IDs start from 100, but there is no enforcement preventing IDs with lower values. In addition to playable music, the game includes several OJM files that serve as background music or UI sound effects.
The OJN file holds music metadata, cover and thumbnail art, and note information.
Refer to the Open2Jam documentation for the file format:
Starting from late O2Jam NX, the OJN file may be encrypted. The game client can process both encrypted and unencrypted OJN files.
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 |
Decryption requires two steps:
Step 1: Construct the XOR Key
Create a byte array with length equal to the XOR key block size. Fill the entire array with the Primary XOR Key, then replace two positions: set the first byte (index 0) to the Initial XOR Key, and set the byte at index floor(xor_key_block_size / 2) to the Middle XOR Key.
Step 2: Decrypt the data
Read the entire payload (everything after the 7-byte header) in reverse byte order. Walk through the reversed data in chunks equal to the XOR key block size, XOR-ing each chunk against the key block. The result is the original, unencrypted OJN data.
Refer to this code sample for the decryption algorithm.
OJM is a sound bank container that holds either .wav or .ogg files.
Refer to the Open2Jam documentation for the file format: