ItemRT (Item Rare Table) is PSO’s rare drop table. It determines whichrare itemeach enemy or box can drop, and at what probability. Figures such as “Dragon Slayer 1/456” and “Tyrell's Parasol 1/3660” come from ItemRT. Together with ItemPT (whether anything drops and its category) and ItemPMT (the item’s own parameters), it forms PSO’s complete drop system.
In PSO’s drop checks, rare checks areindependent ofcommon drops. When an enemy dies or a box is broken, the game first checks for a rare drop:
An enemy’s common drops and rare drops therefore follow separate paths. A low rare drop rate is not reduced further by the number of common items.
ItemRT stores subtables for each difficulty × Section ID combination, giving 4 × 10 = 40 subtables (EP4 has a separate episode table). Each subtable has two parts:
PSOBB uses two separate files: ItemRT.gsl (Ep1 & Ep2) and ItemRT_Ep4.gsl. GSL is Sega’s compact archive format (GStreamLib); each entry contains one subtable.
(item ID, probability).In PSOBB’s ItemRT files, a rare item’s probability is not stored directly as a fraction. Instead, it is compressed into a uint8 (one byte), allowing Sega to fit hundreds of rare entries into a small file. The byte is interpreted as SSSSS VVV :
(2 << shift) × (value + 7), producing a 32-bit probability value.0xFFFFFFFF (4,294,967,295).Two examples:
0x40000000: final drop rate = 25% (0x40000000 / 0xFFFFFFFF ≈ 1/4)0x00FFFFFF: final drop rate ≈ 1/256 The compressed format offers only 256 possible values, so ItemRT cannot represent arbitrary probabilities exactly. Familiar rates such as 1/456, 1/3660, 1/11702 look unusual because they are the nearest values this format can represent.
Each PSOBB character has one of ten fixed colour-based Section IDs, determined by a hash of the name at creation and described here as unchangeable. The ID:
This is why players hunt under particular IDs for particular rares. The Section ID calculator can help find a name that yields the required ID.
(item ID, packed probability).The raw ItemRT probabilities are Sega’s base values. Servers commonly apply a global drop rate multiplier:
1x (base rate). Anniversary milestones can raise this multiplier.The binary ItemRT format uses a fixed-length packed-probability/item-code record as its smallest unit. Each difficulty × ID subtable starts with this offset header.
Offsets (0x10 bytes)| Offset | Field | Type | Description |
|---|---|---|---|
| 0x00 | monster_rares_offset | u32 | Enemy rare pool pointer → PackedDrop[0x65] (only 0x33 entries in v1), indexed by rt_index. |
| 0x04 | box_count | u32 | Total box rare entries, usually fixed at 30 (0x1E). |
| 0x08 | box_areas_offset | u32 | Box area array → u8[box_count]. Entry i gives the area of rare entry i . |
| 0x0C | box_rares_offset | u32 | Box rare pool pointer → PackedDrop[box_count], corresponding one-to-one with box_areas. |
PackedDrop (4 bytes)Each rare record occupies four bytes, the smallest ItemRT unit. Enemy and box rare pools use the same structure.
| Offset | Field | Type | Description |
|---|---|---|---|
| 0x00 | probability | u8 | Packed probability (SSSSS VVV bit layout). |
| 0x01 | item_code | u8×3 | Item code data1[0..2]; for example, 00 01 00 means Saber. |
The one-byte probability is interpreted as SSSSS VVV :
S determine the shift: shift = S − 4, range [−4, 27]; negative values are treated as 0.V determine the value: value = V + 7, range [7, 14].(2 << shift) × value. This is the numerator for the rare drop rate; the denominator is 0xFFFFFFFF (all 32 bits).0xFFFFFFFFExamples:
| packed (hex) | shift | value | expanded | Approximately |
|---|---|---|---|---|
| 0xFF | 27 | 14 | 0xE0000000 | 87.5% |
| 0xC0 | 20 | 7 | 0x01C00000 | About 1/152 |
| 0x80 | 12 | 7 | 0x0001C000 | About 1/38836 |
| 0x00 | 0 | 7 | 0x0000000E | Extremely small (< 1/300 million) |
With only 256 available values, arbitrary exact drop rates cannot be stored. Figures such as 1/456, 1/3660, 1/11702 are the nearest representable values. An exact rate such as 1/500 cannot be specified.
ExpandedDropnewserv expands each PackedDrop into an in-memory structure used for calculations, interaction and serialisation:
| Field | Type | Description |
|---|---|---|
| probability | u32 | Expanded 32-bit probability (numerator; denominator fixed at 0xFFFFFFFF) |
| data | ItemData | Full item data, including data1/data2, not just the three-byte ID |
BoxRareBox rare records embed the area number rather than being indexed directly by area:
| Field | Type | Description |
|---|---|---|
| area_norm_plus_1 | u8 | Area number + 1 (0 marks an unused entry) |
| drop | ExpandedDrop | Probability + item data |
SpecCollectionEach top-level subtable corresponds to a difficulty × Section ID key and contains the following after expansion:
| Field | Type | Description |
|---|---|---|
| enemy_specs | map⟨Enemy, ExpandedDrop[]⟩ | Rare pool for each enemy type. |
| box_specs | ExpandedDrop[][] | Outer array indexed by area_norm; each area contains a candidate list whose entries are checked separately. |
An enemy death or a broken box initiates a drop event. These separate paths first check for a rare, then use ItemPT’s common flow if that fails.
// ① Initial check: does the enemy drop anything?
type_drop_prob = pt.enemy_type_drop_probs[enemy_type] // 0–100
if rand(0..99) >= type_drop_prob:
return nothing // No drop
// ② Rare check (stacking: one roll for all specs)
specs = rare_item_set.get_enemy_specs(mode, episode, difficulty, section_id, enemy_type)
det = rand(0, 0x100000000) // 32 bits + 1
for spec in specs:
det -= spec.probability // Subtract in order
if det < 0:
return create_rare_item(spec.data, area) // Rare selected; finish
// ③ ItemPT common flow: choose a category
determinant = allow_meseta ? rand(0..2) : rand(0..1) + 1
switch determinant:
case 0: item_class = 5 (Meseta)
case 1: item_class = 4 (Consumable)
case 2: item_class = pt.enemy_type_item_classes[enemy_type] // 0..3: weapon/frame/barrier/unit
// ④ Generate the item by category (weights, grind, attributes, special…)
generate_common_item_variances(item, area)// ① Rare check (same stacking algorithm as enemies)
table_index = table_index_for_area(area)
specs = rare_item_set.get_box_specs(mode, episode, difficulty, section_id, table_index)
det = rand(0, 0x100000000)
for spec in specs:
det -= spec.probability
if det < 0:
return create_rare_item(spec.data, area)
// ② No rare → choose a weighted category from box_item_class_prob_table
item_class = weighted_pick(pt.box_item_class_prob_table, table_index)
// 0=weapon 1=frame 2=barrier 3=unit 4=consumable 5=Meseta 6=empty
// ③ Generate an item if item_class < 6
if item_class < 6:
generate_common_item_variances(item, area)An important note on stacking:
Stacking starts with det, subtracts each spec’s probability in turn, and succeeds when the result becomes negative. This is equivalent to one random roll deciding among all specs; spec order does not affect the final distribution.
Sega’s original client instead rolls independently for each spec using rand(0, 0xFFFFFFFF) and compares the result with spec.probability. Later specs are effectively rarer because a success ends the flow. This makes no difference for enemies with one spec, but box lists with many candidates differ from stacking. The newserv author documents this in the comments.
The search interface could not download. Reload this page to try again.