Skip to content

IOP Pack Format

An .iop file is a pack of game files. The client reads almost everything (models, textures, XML, configs, texts) out of rs.iop and map.iop. This page describes the file as the 2014, 2019 and 2021 sources and the web tools handle it. It is the base for every other page in this section, because most formats only exist inside a pack.

Anything marked not confirmed yet was not verified in code or in a file.

The client code (ioPac, class CBigPac) opens a pack with a ZIP library, ziparchive320 (ZipArchive 3.2.0). So a pack is a ZIP archive with a few differences:

  • The local header and the central directory entry have two field pairs swapped (see below). A normal ZIP tool cannot open it without a fix.
  • Entries use raw deflate (method 8). Stored entries (method 0) are also read.
  • Most entries are encrypted with the classic ZIP password scheme (PKWARE “ZipCrypto”), using the pack password.
  • Each entry can carry a comment. The client reads it as a number.

“The pack password” is the same for the whole pack and differs per region. It is not written on this wiki. The tools try the passwords they know; see First Steps for what to do when a pack shows Unknown password.

All numbers are little-endian. These offsets come from the pack writer in the web tools (core/iop.js), which reproduces the client’s own packs.

Local header (30 bytes, then name, then data):

Offset Size Field
0 4 PK\3\4 (0x04034b50)
4 2 name length (standard ZIP: version needed)
6 2 extra field length (standard ZIP: flags). Packs write 0
8 2 method (8 = deflate, 0 = stored)
10 2 DOS time
12 2 DOS date
14 4 CRC32 of the plain data (0 when flag bit 3 is set)
18 4 compressed size (0 when flag bit 3 is set)
22 4 plain size (0 when flag bit 3 is set)
26 2 version needed (0x0014) (standard ZIP: name length)
28 2 flags (standard ZIP: extra length)

Central directory entry (46 bytes, then name, extra, comment):

Offset Size Field
0 4 PK\1\2 (0x02014b50)
4 2 name length (standard ZIP: version made by)
6 2 extra length (standard ZIP: version needed)
8 2 flags
10 2 method
12 2 DOS time
14 2 DOS date
16 4 CRC32
20 4 compressed size
24 4 plain size
28 2 version made by (0x0014) (standard ZIP: name length)
30 2 version needed (0x0014) (standard ZIP: extra length)
32 2 comment length
42 4 offset of the local header

The rest (disk number, attributes) is standard. The end record PK\5\6 is standard and sits at the end of the file. Names are stored with /; the client treats / and \ as the same.

With flag bit 0 set, the data starts with a 12-byte encryption header, then the encrypted deflate stream. The password check byte is the last byte of that header:

  • flag bit 3 set (data descriptor follows): the high byte of the DOS time
  • flag bit 3 clear: the high byte of the CRC32

Packs written by the client’s tools use flags 0x000b (encrypted, data descriptor, best compression). The data descriptor is PK\7\8, CRC32, compressed size, plain size (16 bytes). A wrong password shows up as a failed check byte, then as a CRC mismatch.

The comment of an entry is a short number as text. The client does two things with it:

Comment Meaning
1 on an .ini entry The file content is scrambled with a fixed XOR. The client undoes it after reading. See DAT Tables and INI Configs
1 on a downloaded patch piece The piece was encrypted with the second password (type 1) and is re-encrypted with the pack password when merged
empty or 0 Normal entry

The tools keep the comment when they replace an entry. If you replace an .ini and its comment is 1, the tools scramble your new content the same way.

At login the client adds two packs, in this order (CLogin::ProcessOptions):

  1. rs.iop
  2. map.iop

Both are opened with the pack password. To load a file, the client asks the packs in that order and takes the first pack that has the file (ioPac::GetPacHasFile). So rs.iop wins over map.iop. The same order is in the 2014, 2019 and 2021 sources.

Inside one pack, the client builds a lookup table of all entries (BuildSetFileInfo):

  • Names are lower-cased and compared with \. Case does not matter.
  • If a name appears more than once, the last entry in the central directory wins. The entry with the highest index replaces the earlier ones. The old data stays in the file as unused space.

A patch therefore appends the new data at the end of the pack and writes the central directory again after it. The web tools do the same when you replace an entry, and Optimize drops the old copies.

The client can also hide files. info\delete_file_list.cfg.iop (a one-entry pack itself) lists names to remove. Lines starting with _map are removed from map.iop, all other lines from rs.iop. This is read when the resource loader starts.

  • The central directory counts entries in 16 bits: at most 65 535 entries per pack.
  • Offsets are 32 bits: a pack cannot pass 4 GB.
  • Empty files cannot be added by the web tools.
Part 2014 2019 2021
Search order, delete list, lookup rule same same same
CBigPac has the lookup used by the client adds OpenBigPacAll and a vector index (used by pack tools, not by the game) same as 2019
Pack names rs.iop, map.iop rs.iop, map.iop rs.iop, map.iop

Login.cpp in all three sources calls AddPackFile only for rs.iop (no prefix) and map.iop (prefix _map). Any other .iop in a game folder (from a modified client) is not opened by these clients.

Some unpacked clients (loose config\, resource\, xml\ folders) skip the packs. In that case the client opens the loose files directly (IsLoadInPack is off) and the entry comment is not used.

Tool What it does with packs
Game Folder Reads the end record and central directory of each pack, finds the password that opens it
IOP Manager Lists, previews, extracts, adds, replaces, renames, deletes entries and builds a new pack as a download
every other tool Reads files through the same lookup (newest entry wins, rs.iop before map.iop)
  • Whether other clients (other regions or modified builds) add more packs. The 2014, 2019 and 2021 sources add only rs.iop and map.iop. The web tools read any other pack after these two.
  • The exact limits of the client’s ZIP library for entries larger than 2 GB.