Technical articleArtículo técnico

Introduction to Compression on Mega DriveIntroducción a la compresión en Mega Drive

Why cartridge games compress assets and what to look for when reverse engineering packed data.Por qué los juegos de cartucho comprimen recursos y qué buscar al hacer ingeniería inversa de datos empaquetados.

UpdatedActualizado

July 23, 202623 de julio de 2026

DifficultyDificultad

BeginnerInicial

Reading timeTiempo de lectura

9 min read9 min de lectura

Why Mega Drive games use compressionPor qué los juegos de Mega Drive usan compresión

Mega Drive games often compress graphics, maps, animation frames, menus, fonts, and other resources because cartridge space is finite. A game can contain far more raw asset data than the ROM budget allows, so repeated patterns are stored in a smaller representation and rebuilt at runtime.Los juegos de Mega Drive suelen comprimir gráficos, mapas, frames de animación, menús, fuentes y otros recursos porque el espacio del cartucho es limitado. Un juego puede contener muchos más datos en bruto de los que permite el presupuesto de ROM, así que los patrones repetidos se guardan en una representación más pequeña y se reconstruyen en tiempo de ejecución.

Compression is not only a storage trick. It shapes how the loader works, where assets are placed, how much RAM is needed for temporary buffers, and how safely a ROM hacker can replace an asset without overwriting the next resource.La compresión no es solo un truco de almacenamiento. También condiciona cómo funciona el loader, dónde se colocan los recursos, cuánta RAM hace falta para buffers temporales y hasta qué punto un romhacker puede sustituir un recurso sin pisar el siguiente.

What compression changesQué cambia la compresión

A compressed resource is not the final asset. It is an instruction stream, dictionary, table, bitstream, or command sequence that produces the final asset only after the correct decoder runs. The same bytes that look meaningless in a tile viewer may become clean graphics once the game has expanded them into RAM or VRAM-ready data.Un recurso comprimido no es el recurso final. Es un flujo de instrucciones, diccionario, tabla, bitstream o secuencia de comandos que solo produce el recurso final después de ejecutar el decodificador correcto. Los mismos bytes que parecen no tener sentido en un visor de tiles pueden convertirse en gráficos correctos cuando el juego los expande a RAM o a datos listos para VRAM.

Resource containers and headersContenedores de recursos y cabeceras

Many packed resources are not stored as a naked compressed stream. They live inside a small resource record that the loader can scan. In the Electronic Arts-style resources documented here, the useful mental model is: total record size, resource name marked with the high bit, then the packed file data handed to the decompression dispatcher.Muchos recursos empaquetados no se guardan como un flujo comprimido desnudo. Viven dentro de un pequeño registro de recurso que el loader puede recorrer. En los recursos de estilo Electronic Arts documentados aquí, el modelo mental útil es: tamaño total del registro, nombre del recurso marcado con el bit alto y después los datos empaquetados que se entregan al despachador de descompresión.

Resource layout
Resource container entry

- Total entry size
  Covers the complete packed file record, including loader metadata,
  the name field, the compression header, and the compressed payload.

- Resource name
  Stored with its high bit set so the game loader can find this entry
  in the resource table or package.

- Packed file data
  The byte range handed to the decompression dispatcher.

  Compression header
  - Method signature: XX FB
  - Decompressed size: expected output length
  - Method payload: data interpreted by the selected decoder
Estructura del recurso
Entrada del contenedor de recursos

- Tamaño total de la entrada
  Cubre el registro empaquetado completo, incluyendo metadatos del loader,
  el campo de nombre, la cabecera de compresión y el payload comprimido.

- Nombre del recurso
  Guardado con el bit alto activado para que el loader pueda encontrar esta
  entrada en la tabla o paquete de recursos.

- Datos del archivo empaquetado
  Rango de bytes entregado al despachador de descompresión.

  Cabecera de compresión
  - Firma de método: XX FB
  - Tamaño descomprimido: longitud de salida esperada
  - Payload del método: datos interpretados por el decodificador seleccionado

Inside the packed file data, the compression header starts with the method signature XX FB, followed by the expected decompressed size and then the method-specific data. For RefPack that means command bytes, literals, and copy references. For EA Huffman it means Huffman tables or trees and a bitstream. For EA BPE it means the pair/dictionary data and the token payload.Dentro de los datos empaquetados, la cabecera de compresión empieza con la firma de método XX FB, seguida del tamaño esperado descomprimido y después los datos específicos del método. En RefPack eso significa bytes de comando, literales y referencias de copia. En EA Huffman significa tablas o árboles Huffman y un bitstream. En EA BPE significa datos de pares/diccionario y el payload de tokens.

Signatures and dispatchersFirmas y despachadores

A signature is a clue, not the whole format. In many Electronic Arts Mega Drive games, a shared 68000 decompression routine reads the first byte into D3, verifies the following FB byte, masks out the low flag bit, and then branches to the method-specific decoder.Una firma es una pista, no el formato entero. En muchos juegos de Electronic Arts para Mega Drive, una rutina compartida de descompresión en 68000 lee el primer byte en D3, verifica el byte FB siguiente, enmascara el bit bajo usado como flag y después salta al decodificador específico del método.

68000 dispatcher
move.b  (a0)+,d3
cmpi.b  #$FB,(a0)+
bne     error_or_exit

btst    #0,d3
beq     continue_decode
addq.w  #3,a0

continue_decode:
andi.b  #$FE,d3
Despachador 68000
move.b  (a0)+,d3
cmpi.b  #$FB,(a0)+
bne     error_o_salida

btst    #0,d3
beq     continuar
addq.w  #3,a0

continuar:
andi.b  #$FE,d3
Method signatures
10 FB  -> RefPack
30 FB  -> EA Huffman
32 FB  -> EA Huffman
34 FB  -> EA Huffman
46 FB  -> EA BPE
6E FB  -> uncompressed data, ASCII 'n'
72 FB  -> differential coding, ASCII 'r'
7A FB  -> RLE, ASCII 'z'
Firmas de método
10 FB  -> RefPack
30 FB  -> EA Huffman
32 FB  -> EA Huffman
34 FB  -> EA Huffman
46 FB  -> EA BPE
6E FB  -> datos sin comprimir, ASCII 'n'
72 FB  -> codificación diferencial, ASCII 'r'
7A FB  -> RLE, ASCII 'z'

This is why 10 FB, 30 FB, 32 FB, 34 FB, 46 FB, 6E FB, 72 FB, and 7A FB should be treated as different decoder paths even though they share the FB suffix. The byte before FB is the method identity.Por eso 10 FB, 30 FB, 32 FB, 34 FB, 46 FB, 6E FB, 72 FB y 7A FB deben tratarse como rutas de decodificador distintas aunque compartan el sufijo FB. El byte anterior a FB es la identidad del método.

6E FB, 72 FB, and 7A FB6E FB, 72 FB y 7A FB

SignatureFirmaMethodMétodoParticularityParticularidad
6E FBUncompressed dataDatos sin comprimirThe data is still wrapped by the EA-style header, but the decoder path mainly copies the payload to the output. The method byte 6Eh is ASCII 'n'.Los datos siguen envueltos por la cabecera de estilo EA, pero la ruta del decodificador básicamente copia el payload a la salida. El byte de método 6Eh es el ASCII 'n'.
72 FBDifferential codingCodificación diferencialThe payload stores changes relative to previous output values instead of final values directly. The method byte 72h is ASCII 'r'.El payload almacena cambios relativos a valores de salida anteriores en vez de guardar directamente los valores finales. El byte de método 72h es el ASCII 'r'.
7A FBRLERLERun-length encoding compresses repeated values or repeated spans. The method byte 7Ah is ASCII 'z'.Run-length encoding comprime valores repetidos o tramos repetidos. El byte de método 7Ah es el ASCII 'z'.

6E FB is useful because it lets the same loader handle an asset that is effectively stored raw. It may still carry size information and container metadata, so treating it as plain bytes without respecting the wrapper can shift offsets and break the next entry.6E FB es útil porque permite que el mismo loader gestione un recurso que, en la práctica, está guardado sin comprimir. Aun así puede llevar información de tamaño y metadatos del contenedor, por lo que tratarlo como bytes planos sin respetar el envoltorio puede desplazar offsets y romper la entrada siguiente.

72 FB requires reversing the reconstruction step, not just copying bytes. Differential coding usually means each stored value modifies the previous output value or a small predictor. The details that matter are width, signedness, initial value, and whether the stream works on bytes, words, pixels, or another unit.72 FB requiere invertir el paso de reconstrucción, no solo copiar bytes. La codificación diferencial suele significar que cada valor guardado modifica el valor de salida anterior o un pequeño predictor. Los detalles importantes son el ancho, el signo, el valor inicial y si el flujo trabaja sobre bytes, words, píxeles u otra unidad.

7A FB is the classic repeated-run case. It can be excellent for blank tiles, masks, flat regions, repeated map values, or padding-like data, but the exact command layout must still be confirmed from the game decoder: which bytes mean literal run, repeated run, length, and value.7A FB es el caso clásico de tramos repetidos. Puede ir muy bien para tiles vacíos, máscaras, regiones planas, valores repetidos de mapas o datos parecidos a relleno, pero el layout exacto de comandos debe confirmarse en el decodificador del juego: qué bytes significan tramo literal, tramo repetido, longitud y valor.

Reverse engineering workflowFlujo de ingeniería inversa

  1. Search for candidate signatures, but keep the offsets as candidates until a loader reference confirms them.Busca firmas candidatas, pero conserva los offsets como candidatos hasta que una referencia del loader los confirme.
  2. Find the container boundary first: total entry size, name field, pointer table entry, or next resource offset.Encuentra primero el límite del contenedor: tamaño total de entrada, campo de nombre, entrada de tabla de punteros u offset del siguiente recurso.
  3. Read the header and expected decompressed size before trusting any decoder output.Lee la cabecera y el tamaño esperado descomprimido antes de confiar en cualquier salida del decodificador.
  4. Decode one known asset and validate it against its consumer: tile viewer, tilemap parser, palette, table, menu renderer, or game routine.Decodifica un recurso conocido y valídalo contra su consumidor: visor de tiles, parser de tilemap, paleta, tabla, renderer de menú o rutina del juego.
  5. Only then build tooling for batch extraction or recompression.Solo entonces construye herramientas para extracción masiva o recompresión.

Recompression risksRiesgos al recomprimir

Recompression is stricter than decompression. A decoder can tolerate only the exact format the game expects, and the rebuilt block must still fit the original allocation unless you also relocate the resource and update every pointer or table entry that reaches it.Recomprimir es más estricto que descomprimir. Un decodificador solo tolera el formato exacto que espera el juego, y el bloque reconstruido debe caber todavía en la asignación original salvo que también reloques el recurso y actualices cada puntero o entrada de tabla que llega hasta él.