Skip to content
View in the app

A better way to browse. Learn more.

ResHax

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.
Help us keep the site running.
Zero Tolerance for Disrespect

Buccaner 1997 .cat unpack textures

Featured Replies

  • Localization

Hello everyone,I’m looking for some help cracking the per-image compression for Buccaneer (1997, SSI).

I am trying to extract the textures and UI bitmaps from bitmaps256.cat and combat.cat (which share the exact same format).

I've included a summary of what I've accomplished and where I'm stuck, hoping someone can help me.

I've also attached the .cat and .exe files for the game.

Thanks in advance!

buccaner files.zip

Solved by Hazza12555

9 hours ago, UZ.- said:

Hello everyone,I’m looking for some help cracking the per-image compression for Buccaneer (1997, SSI).

I am trying to extract the textures and UI bitmaps from bitmaps256.cat and combat.cat (which share the exact same format).

I've included a summary of what I've accomplished and where I'm stuck, hoping someone can help me.

I've also attached the .cat and .exe files for the game.

Thanks in advance!

buccaner files.zip

Try this QuickBMS script to decompress the BMP files.

cat.zip

  • Localization
  • Solution

The .cat files used by Buccaneer use Haruhiko Okumura's LZSS.C from 1989, used essentially verbatim.

It is not an adaptive entropy coder.

The LZSS parameters are:

N = 4096
F = 18
THRESHOLD = 2

Ring buffer fill = 0x00
Initial write position r = N - F = 4078

The zero-filled ring buffer is important. The original Okumura implementation commonly uses 0x20, but Buccaneer uses 0x00.

1. Container Format

There is a real central TOC (table of contents) at the end of the file.

The actual directory is a single clean list at the end of the file.

At file offset 0 there is a u32 pointing to the TOC:

u32 toc_offset

At toc_offset the TOC is structured as:

u32 count

count x {
    u32 namelen        // includes the trailing NUL
    char name[namelen] // ASCII, NUL-terminated
    u32 offset         // absolute file offset of the image block
}

The structure therefore looks like:

Offset 0:
    u32 toc_offset

At toc_offset:
    u32 count

    Entry 0:
        u32 namelen
        char name[namelen]
        u32 offset

    Entry 1:
        u32 namelen
        char name[namelen]
        u32 offset

    ...

    Entry count-1:
        u32 namelen
        char name[namelen]
        u32 offset

For bitmaps256.cat:

count = 266

There are:

  • 266 entries

  • 266 unique names

  • 266 unique offsets

Nothing is shared or reused.

The TOC consumes exactly to the end of the file with zero trailing bytes.

2. Image Block Format

Each image block is:

u32 f0       // decompressed size
u32 f1       // compressed stream size
u8  stream[f1]

So the complete block size is:

8 + f1

There is no additional marker or class byte.

The values previously identified as supposed markers such as:

0xEF
0xDF
0x5F

are actually just the first byte of the LZSS stream, specifically the first LZSS flag byte.

The compressed size f1 therefore includes this flag byte.

So the block is:

8 + f1 bytes

and not:

9 + f1 bytes

The first flag byte's values appeared to correlate with image size, but this is only incidental.

Specifically, bit 4 of the first flag byte indicates whether bfSize required a third non-zero byte, which effectively corresponds to whether the BMP is over 64 KB.

The image blocks are contiguous starting at offset 4.

There is a total of 285 bytes of slack before the TOC.

3. LZSS Codec

The compression is Okumura's classic LZSS implementation.

The compressed data is processed using a flag byte, with the bits consumed LSB first.

Each flag byte controls 8 tokens.

Flag meanings

bit = 1
    literal byte

bit = 0
    2-byte match token

A match token consists of two bytes:

b0 b1

The match position and length are calculated as:

pos = b0 | ((b1 & 0xF0) << 4)
len = (b1 & 0x0F) + 3

The decoder then copies len bytes from the ring buffer:

ring[(pos + k) & 0xFFF]

Every byte copied is:

  1. appended to the output

  2. written into the ring buffer at the current write position

The write position is incremented as:

ring[r++ & 0xFFF]

Decoding continues until exactly f0 bytes have been produced.

4. Recurring Zero-Runs

Two match tokens appear repeatedly:

E9 F2
EB F0

They appeared 207 times.

These are not special compression markers.

They are simply zero-runs being read from the still-untouched, zero-filled portion of the ring buffer.

For example:

E9 F2

produces:

pos = 0xFE9
len = 5

and therefore copies five zero bytes.

Likewise:

EB F0

produces a three-byte zero run.

5. Compressed Data Can Be Larger Than the Original

Not every bitmap compresses smaller than the original.

For example:

Mouse.BMP

has:

compressed = 7577 bytes
raw        = 7478 bytes

So the compressed data is actually slightly larger than the original.

This is normal LZSS behaviour.

The file uses ordinary 9/8 worst-case expansion for incompressible data. This can occur with things such as the 256-colour palette and noisy pixel data.

6. Usage

extract.py bitmaps256.cat extracted

extract.py

  • Author
  • Localization

Hi guys @DKDave @Hazza12555 ! Thank you both so much for your time, expertise, and willingness to help. Also, thank you for being so quick—you were incredibly fast!!! Both the QuickBMS script and the Python script worked flawlessly. Infinite thanks!!!!!!!!!!!!!!!!!!!!!!! You both are absolute legends!

Create an account or sign in to comment

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.