Contents

Nocash PSXSPX Playstation Specifications
Memory Map
I/O Map
Graphics Processing Unit (GPU)
Geometry Transformation Engine (GTE)
Macroblock Decoder (MDEC)
Sound Processing Unit (SPU)
Interrupts
DMA Channels
Timers
CDROM Drive
CDROM File Formats
Controllers and Memory Cards
Pocketstation
Serial Port (SIO)
Expansion Port (PIO)
Memory Control
Unpredictable Things
CPU Specifications
Kernel (BIOS)
Arcade Cabinets
Cheat Devices
PSX Dev-Board Chipsets
Hardware Numbers
Pinouts
About & Credits

Latest Research
CDROM File Formats

Nocash PSX Emulator/Debugger
No$psx Emulation Controls
No$psx Emulation Files
No$psx Emulation Notes
No$psx Debugger - Hotkeys in Debug Mode
No$psx Debugger - Breakpoints
No$psx Debugger - General Debug Features



Memory Map

Memory Map
  KUSEG     KSEG0     KSEG1
  00000000h 80000000h A0000000h  2048K  Main RAM (first 64K reserved for BIOS)
  1F000000h 9F000000h BF000000h  8192K  Expansion Region 1 (ROM/RAM)
  1F800000h 9F800000h    --      1K     Scratchpad (D-Cache used as Fast RAM)
  1F801000h 9F801000h BF801000h  8K     I/O Ports
  1F802000h 9F802000h BF802000h  8K     Expansion Region 2 (I/O Ports)
  1FA00000h 9FA00000h BFA00000h  2048K  Expansion Region 3 (whatever purpose)
  1FC00000h 9FC00000h BFC00000h  512K   BIOS ROM (Kernel) (4096K max)
        FFFE0000h (KSEG2)        0.5K   I/O Ports (Cache Control)
Additionally, there are a number of memory mirrors.

Additional Memory (not mapped to the CPU bus)
  1024K   VRAM (Framebuffers, Textures, Palettes) (with 2KB Texture Cache)
  512K    Sound RAM (Capture Buffers, ADPCM Data, Reverb Workspace)
  0.5K    CDROM controller RAM (see CDROM Test commands)
  16.5K   CDROM controller ROM (Firmware and Bootstrap for MC68HC05 cpu)
  32K     CDROM Buffer (IC303) (32Kx8) (BUG: only two sectors accessible?)
  128K    External Memory Card(s) (EEPROMs)

KUSEG,KSEG0,KSEG1,KSEG2 Memory Regions
  Address   Name   Size   Privilege    Code-Cache  Data-Cache
  00000000h KUSEG  2048M  Kernel/User  Yes         (Scratchpad)
  80000000h KSEG0  512M   Kernel       Yes         (Scratchpad)
  A0000000h KSEG1  512M   Kernel       No          No
  C0000000h KSEG2  1024M  Kernel       (No code)   No
Kernel Memory: KSEG1 is the normal physical memory (uncached), KSEG0 is a mirror thereof (but with cache enabled). KSEG2 is usually intended to contain virtual kernel memory, in the PSX it's containing Cache Control I/O Ports.
User Memory: KUSEG is intended to contain 2GB virtual memory (on extended MIPS processors), the PSX doesn't support virtual memory, and KUSEG simply contains a mirror of KSEG0/KSEG1 (in the first 512MB) (trying to access memory in the remaining 1.5GB causes an exception).

Code Cache
Works in the cached regions (KUSEG and KSEG0).
There are reportedly some restrictions... not sure there... eventually it is using the LSBs of the address as cache-line number... so, for example, it couldn't simultaneously memorize opcodes at BOTH address 80001234h, AND at address 800F1234h (?)

Data Cache aka Scratchpad
The MIPS CPU usually have a Data Cache, but, in the PSX, Sony has misused it as "Scratchpad", that is, the "Data Cache" is mapped to a fixed memory location at 1F800000h..1F8003FFh (ie. it's used as Fast RAM, rather than as cache).
There <might> be a way to disable that behaviour (via Port FFFE0130h or so), but, the Kernel is accessing I/O ports via KUSEG, so activating Data Cache would cause the Kernel to access cached I/O ports.
Not tested yet, but most probably the Scratchpad can be used only for Data (ie. NOT for program Code?).

Memory Mirrors
As described above, the 512Mbyte KUSEG, KSEG0, and KSEG1 regions are mirrors of each other. Additional mirrors within these 512MB regions are:
  2MB RAM can be mirrored to the first 8MB (strangely, enabled by default)
  512K BIOS ROM can be mirrored to the last 4MB (disabled by default)
  Expansion hardware (if any) may be mirrored within expansion region
  The seven DMA Control Registers at 1F8010x8h are mirrored to 1F8010xCh
The size of the RAM, BIOS, Expansion regions can be configured by software, for Expansion Region it's also possible to change base address, see:
Memory Control
The Scratchpad is mirrored only in KUSEG and KSEG0, but not in KSEG1.

Memory Exceptions
  Memory Error ------> Misalignments
               (and probably also KSEG access in User mode)
  Bus Error    ------> Unused Memory Regions (including Gaps in I/O Region)
               (unless RAM/BIOS/Expansion mirrors are mapped to "unused" area)

More Memory Info
For Info on Exception vectors, Unused/Garbage memory locations, I/O Ports, Expansion ROM Headers, and Memory Waitstate Control, etc. see:
I/O Map
Memory Control
EXP1 Expansion ROM Header
BIOS Memory Map
BIOS Memory Allocation
COP0 - Exception Handling
Unpredictable Things


I/O Map

Expansion Region 1
  1F000000h 80000h Expansion Region (default 512 Kbytes, max 8 MBytes)
  1F000000h 100h   Expansion ROM Header (IDs and Entrypoints)
Scratchpad
  1F800000h 400h Scratchpad (1K Fast RAM) (Data Cache mapped to fixed address)
Memory Control 1
  1F801000h 4    Expansion 1 Base Address (usually 1F000000h)
  1F801004h 4    Expansion 2 Base Address (usually 1F802000h)
  1F801008h 4    Expansion 1 Delay/Size (usually 0013243Fh; 512Kbytes 8bit-bus)
  1F80100Ch 4    Expansion 3 Delay/Size (usually 00003022h; 1 byte)
  1F801010h 4    BIOS ROM    Delay/Size (usually 0013243Fh; 512Kbytes 8bit-bus)
  1F801014h 4    SPU_DELAY   Delay/Size (usually 200931E1h)
  1F801018h 4    CDROM_DELAY Delay/Size (usually 00020843h or 00020943h)
  1F80101Ch 4    Expansion 2 Delay/Size (usually 00070777h; 128-bytes 8bit-bus)
  1F801020h 4    COM_DELAY / COMMON_DELAY (00031125h or 0000132Ch or 00001325h)
Peripheral I/O Ports
  1F801040h 1/4  JOY_DATA Joypad/Memory Card Data (R/W)
  1F801044h 4    JOY_STAT Joypad/Memory Card Status (R)
  1F801048h 2    JOY_MODE Joypad/Memory Card Mode (R/W)
  1F80104Ah 2    JOY_CTRL Joypad/Memory Card Control (R/W)
  1F80104Eh 2    JOY_BAUD Joypad/Memory Card Baudrate (R/W)
  1F801050h 1/4  SIO_DATA Serial Port Data (R/W)
  1F801054h 4    SIO_STAT Serial Port Status (R)
  1F801058h 2    SIO_MODE Serial Port Mode (R/W)
  1F80105Ah 2    SIO_CTRL Serial Port Control (R/W)
  1F80105Ch 2    SIO_MISC Serial Port Internal Register (R/W)
  1F80105Eh 2    SIO_BAUD Serial Port Baudrate (R/W)
Memory Control 2
  1F801060h 4/2  RAM_SIZE (usually 00000B88h; 2MB RAM mirrored in first 8MB)
Interrupt Control
  1F801070h 2    I_STAT - Interrupt status register
  1F801074h 2    I_MASK - Interrupt mask register
DMA Registers
  1F80108xh      DMA0 channel 0 - MDECin
  1F80109xh      DMA1 channel 1 - MDECout
  1F8010Axh      DMA2 channel 2 - GPU (lists + image data)
  1F8010Bxh      DMA3 channel 3 - CDROM
  1F8010Cxh      DMA4 channel 4 - SPU
  1F8010Dxh      DMA5 channel 5 - PIO (Expansion Port)
  1F8010Exh      DMA6 channel 6 - OTC (reverse clear OT) (GPU related)
  1F8010F0h      DPCR - DMA Control register
  1F8010F4h      DICR - DMA Interrupt register
  1F8010F8h      unknown
  1F8010FCh      unknown
Timers (aka Root counters)
  1F801100h 2    Timer 0 Current Counter Value (R/W)  ;\
  1F801104h 2    Timer 0 Counter Mode          (R/W)  ; Dotclock
  1F801108h 2    Timer 0 Counter Target Value  (R/W)  ;/
  1F801110h 2    Timer 1 Current Counter Value (R/W)  ;\
  1F801114h 2    Timer 1 Counter Mode          (R/W)  ; Horizontal Retrace
  1F801118h 2    Timer 1 Counter Target Value  (R/W)  ;/
  1F801120h 2    Timer 2 Current Counter Value (R/W)  ;\
  1F801124h 2    Timer 2 Counter Mode          (R/W)  ; 1/8 system clock
  1F801128h 2    Timer 2 Counter Target Value  (R/W)  ;/
CDROM Registers (Address.Read/Write.Index)
  1F801800h.x.x   1   CD Index/Status Register (Bit0-1 R/W, Bit2-7 Read Only)
  1F801801h.R.x   1   CD Response Fifo (R) (usually with Index1)
  1F801802h.R.x   1/2 CD Data Fifo - 8bit/16bit (R) (usually with Index0..1)
  1F801803h.R.0   1   CD Interrupt Enable Register (R)
  1F801803h.R.1   1   CD Interrupt Flag Register (R/W)
  1F801803h.R.2   1   CD Interrupt Enable Register (R) (Mirror)
  1F801803h.R.3   1   CD Interrupt Flag Register (R/W) (Mirror)
  1F801801h.W.0   1   CD Command Register (W)
  1F801802h.W.0   1   CD Parameter Fifo (W)
  1F801803h.W.0   1   CD Request Register (W)
  1F801801h.W.1   1   Unknown/unused
  1F801802h.W.1   1   CD Interrupt Enable Register (W)
  1F801803h.W.1   1   CD Interrupt Flag Register (R/W)
  1F801801h.W.2   1   Unknown/unused
  1F801802h.W.2   1   CD Audio Volume for Left-CD-Out to Left-SPU-Input (W)
  1F801803h.W.2   1   CD Audio Volume for Left-CD-Out to Right-SPU-Input (W)
  1F801801h.W.3   1   CD Audio Volume for Right-CD-Out to Right-SPU-Input (W)
  1F801802h.W.3   1   CD Audio Volume for Right-CD-Out to Left-SPU-Input (W)
  1F801803h.W.3   1   CD Audio Volume Apply Changes (by writing bit5=1)
GPU Registers
  1F801810h.Write 4   GP0 Send GP0 Commands/Packets (Rendering and VRAM Access)
  1F801814h.Write 4   GP1 Send GP1 Commands (Display Control)
  1F801810h.Read  4   GPUREAD Read responses to GP0(C0h) and GP1(10h) commands
  1F801814h.Read  4   GPUSTAT Read GPU Status Register
MDEC Registers
  1F801820h.Write 4   MDEC Command/Parameter Register (W)
  1F801820h.Read  4   MDEC Data/Response Register (R)
  1F801824h.Write 4   MDEC Control/Reset Register (W)
  1F801824h.Read  4   MDEC Status Register (R)
SPU Voice 0..23 Registers
  1F801C00h+N*10h 4   Voice 0..23 Volume Left/Right
  1F801C04h+N*10h 2   Voice 0..23 ADPCM Sample Rate
  1F801C06h+N*10h 2   Voice 0..23 ADPCM Start Address
  1F801C08h+N*10h 4   Voice 0..23 ADSR Attack/Decay/Sustain/Release
  1F801C0Ch+N*10h 2   Voice 0..23 ADSR Current Volume
  1F801C0Eh+N*10h 2   Voice 0..23 ADPCM Repeat Address
SPU Control Registers
  1F801D80h 4  Main Volume Left/Right
  1F801D84h 4  Reverb Output Volume Left/Right
  1F801D88h 4  Voice 0..23 Key ON (Start Attack/Decay/Sustain) (W)
  1F801D8Ch 4  Voice 0..23 Key OFF (Start Release) (W)
  1F801D90h 4  Voice 0..23 Channel FM (pitch lfo) mode (R/W)
  1F801D94h 4  Voice 0..23 Channel Noise mode (R/W)
  1F801D98h 4  Voice 0..23 Channel Reverb mode (R/W)
  1F801D9Ch 4  Voice 0..23 Channel ON/OFF (status) (R)
  1F801DA0h 2  Unknown? (R) or (W)
  1F801DA2h 2  Sound RAM Reverb Work Area Start Address
  1F801DA4h 2  Sound RAM IRQ Address
  1F801DA6h 2  Sound RAM Data Transfer Address
  1F801DA8h 2  Sound RAM Data Transfer Fifo
  1F801DAAh 2  SPU Control Register (SPUCNT)
  1F801DACh 2  Sound RAM Data Transfer Control
  1F801DAEh 2  SPU Status Register (SPUSTAT) (R)
  1F801DB0h 4  CD Volume Left/Right
  1F801DB4h 4  Extern Volume Left/Right
  1F801DB8h 4  Current Main Volume Left/Right
  1F801DBCh 4  Unknown? (R/W)
SPU Reverb Configuration Area
  1F801DC0h 2  dAPF1  Reverb APF Offset 1
  1F801DC2h 2  dAPF2  Reverb APF Offset 2
  1F801DC4h 2  vIIR   Reverb Reflection Volume 1
  1F801DC6h 2  vCOMB1 Reverb Comb Volume 1
  1F801DC8h 2  vCOMB2 Reverb Comb Volume 2
  1F801DCAh 2  vCOMB3 Reverb Comb Volume 3
  1F801DCCh 2  vCOMB4 Reverb Comb Volume 4
  1F801DCEh 2  vWALL  Reverb Reflection Volume 2
  1F801DD0h 2  vAPF1  Reverb APF Volume 1
  1F801DD2h 2  vAPF2  Reverb APF Volume 2
  1F801DD4h 4  mSAME  Reverb Same Side Reflection Address 1 Left/Right
  1F801DD8h 4  mCOMB1 Reverb Comb Address 1 Left/Right
  1F801DDCh 4  mCOMB2 Reverb Comb Address 2 Left/Right
  1F801DE0h 4  dSAME  Reverb Same Side Reflection Address 2 Left/Right
  1F801DE4h 4  mDIFF  Reverb Different Side Reflection Address 1 Left/Right
  1F801DE8h 4  mCOMB3 Reverb Comb Address 3 Left/Right
  1F801DECh 4  mCOMB4 Reverb Comb Address 4 Left/Right
  1F801DF0h 4  dDIFF  Reverb Different Side Reflection Address 2 Left/Right
  1F801DF4h 4  mAPF1  Reverb APF Address 1 Left/Right
  1F801DF8h 4  mAPF2  Reverb APF Address 2 Left/Right
  1F801DFCh 4  vIN    Reverb Input Volume Left/Right
SPU Internal Registers
  1F801E00h+N*04h  4 Voice 0..23 Current Volume Left/Right
  1F801E60h      20h Unknown? (R/W)
  1F801E80h     180h Unknown? (Read: FFh-filled) (Unused or Write only?)
Expansion Region 2 (default 128 bytes, max 8 KBytes)
  1F802000h      80h Expansion Region (8bit data bus, crashes on 16bit access?)
Expansion Region 2 - Dual Serial Port (for TTY Debug Terminal)
  1F802020h/1st    DUART Mode Register 1.A (R/W)
  1F802020h/2nd    DUART Mode Register 2.A (R/W)
  1F802021h/Read   DUART Status Register A (R)
  1F802021h/Write  DUART Clock Select Register A (W)
  1F802022h/Read   DUART Toggle Baud Rate Generator Test Mode (Read=Strobe)
  1F802022h/Write  DUART Command Register A (W)
  1F802023h/Read   DUART Rx Holding Register A (FIFO) (R)
  1F802023h/Write  DUART Tx Holding Register A (W)
  1F802024h/Read   DUART Input Port Change Register (R)
  1F802024h/Write  DUART Aux. Control Register (W)
  1F802025h/Read   DUART Interrupt Status Register (R)
  1F802025h/Write  DUART Interrupt Mask Register (W)
  1F802026h/Read   DUART Counter/Timer Current Value, Upper/Bit15-8 (R)
  1F802026h/Write  DUART Counter/Timer Reload Value,  Upper/Bit15-8 (W)
  1F802027h/Read   DUART Counter/Timer Current Value, Lower/Bit7-0 (R)
  1F802027h/Write  DUART Counter/Timer Reload Value,  Lower/Bit7-0 (W)
  1F802028h/1st    DUART Mode Register 1.B (R/W)
  1F802028h/2nd    DUART Mode Register 2.B (R/W)
  1F802029h/Read   DUART Status Register B (R)
  1F802029h/Write  DUART Clock Select Register B (W)
  1F80202Ah/Read   DUART Toggle 1X/16X Test Mode (Read=Strobe)
  1F80202Ah/Write  DUART Command Register B (W)
  1F80202Bh/Read   DUART Rx Holding Register B (FIFO) (R)
  1F80202Bh/Write  DUART Tx Holding Register B (W)
  1F80202Ch/None   DUART Reserved Register (neither R nor W)
  1F80202Dh/Read   DUART Input Port (R)
  1F80202Dh/Write  DUART Output Port Configuration Register (W)
  1F80202Eh/Read   DUART Start Counter Command (Read=Strobe)
  1F80202Eh/Write  DUART Set Output Port Bits Command (Set means Out=LOW)
  1F80202Fh/Read   DUART Stop Counter Command (Read=Strobe)
  1F80202Fh/Write  DUART Reset Output Port Bits Command (Reset means Out=HIGH)
Expansion Region 2 - Int/Dip/Post
  1F802000h 1 DTL-H2000: ATCONS STAT (R)
  1F802002h 1 DTL-H2000: ATCONS DATA (R and W)
  1F802004h 2 DTL-H2000: Whatever 16bit data ?
  1F802030h 1/4 DTL-H2000: Secondary IRQ10 Flags
  1F802032h 1 DTL-H2000: Whatever IRQ Control ?
  1F802040h 1 DTL-H2000: Bootmode "Dip switches" (R)
  1F802041h 1 PSX: POST (external 7 segment display, indicate BIOS boot status)
  1F802042h 1 DTL-H2000: POST/LED (similar to POST) (other addr, 2-digit wide)   1F802070h 1 PS2: POST2 (similar to POST, but PS2 BIOS uses this address)
Expansion Region 2 - Nocash Emulation Expansion
  1F802060h Emu-Expansion ID1 "E" (R)
  1F802061h Emu-Expansion ID2 "X" (R)
  1F802062h Emu-Expansion ID3 "P" (R)
  1F802063h Emu-Expansion Version (01h) (R)
  1F802064h Emu-Expansion Enable1 "O" (R/W)
  1F802065h Emu-Expansion Enable2 "N" (R/W)
  1F802066h Emu-Expansion Halt (R)
  1F802067h Emu-Expansion Turbo Mode Flags (R/W)
Expansion Region 3 (default 1 byte, max 2 MBytes)
  1FA00000h - Not used by BIOS or any PSX games
  1FA00000h - POST3 (similar to POST, but PS2 BIOS uses this address)
BIOS Region (default 512 Kbytes, max 4 MBytes)
  1FC00000h 80000h   BIOS ROM (512Kbytes) (Reset Entrypoint at BFC00000h)
Memory Control 3 (Cache Control)
  FFFE0130h 4        Cache Control

Coprocessor Registers
  COP0 System Control Coprocessor           - 32 registers (not all used)
  COP1 N/A
  COP2 Geometry Transformation Engine (GTE) - 64 registers (most are used)
  COP3 N/A


Graphics Processing Unit (GPU)

The GPU can render Polygons, Lines, or Rectangles to the Drawing Buffer, and sends the Display Buffer to the Television Set. Polygons are useful for 3D graphics (or rotated/scaled 2D graphics), Rectangles are useful for 2D graphics and Text output.

GPU I/O Ports, DMA Channels, Commands, VRAM
GPU Render Polygon Commands
GPU Render Line Commands
GPU Render Rectangle Commands
GPU Rendering Attributes
GPU Memory Transfer Commands
GPU Other Commands
GPU Display Control Commands (GP1)
GPU Status Register
GPU Versions
GPU Depth Ordering
GPU Video Memory (VRAM)
GPU Texture Caching
GPU Timings
GPU (MISC)


GPU I/O Ports, DMA Channels, Commands, VRAM

GPU I/O Ports (1F801810h and 1F801814h in Read/Write Directions)
  Port            Name    Expl.
  1F801810h-Write GP0     Send GP0 Commands/Packets (Rendering and VRAM Access)
  1F801814h-Write GP1     Send GP1 Commands (Display Control) (and DMA Control)
  1F801810h-Read  GPUREAD Receive responses to GP0(C0h) and GP1(10h) commands
  1F801814h-Read  GPUSTAT Receive GPU Status Register
It (=GP0 only?) has a 64-byte (16-word) command FIFO buffer.
Optionally, Port 1F801810h (Read/Write) can be also accessed via DMA2.

GPU Timers / Synchronization
Most of the Timers are bound to GPU timings, see
Timers
Interrupts

GPU-related DMA Channels (DMA2 and DMA6)
  Channel                  Recommended for
  DMA2 in Linked Mode    - Sending rendering commands  ;GP0(20h..7Fh,E1h..E6h)
  DMA2 in Continous Mode - VRAM transfers to/from GPU  ;GP0(A0h,C0h)
  DMA6                   - Initializing the Link List  ;Main RAM
Note: Before using DMA2, set up the DMA Direction in GP1(04h).
DMA2 is equivalent to accessing Port 1F801810h (GP0/GPUREAD) by software.
DMA6 just initializes data in Main RAM (not physically connected to the GPU).

GPU Command Summary
Commands/Packets consist of a 8bit command number (MSBs) and a 24bit parameter (LSBs), which are written as 32bit value to GP0 or GP1.
  GP0(00h)                 - Nop?
  GP0(01h,02h,80h,A0h,C0h) - Direct VRAM Access
  GP0(03h)                 - Unknown (does take up FIFO space!!!)
  GP0(1Fh)                 - Interrupt Request (IRQ1)
  GP0(20h..3Fh)            - Render Polygons
  GP0(40h..5Fh)            - Render Lines
  GP0(60h..7Fh)            - Render Rectangles
  GP0(E1h..E6h)            - Rendering Attributes
  GP1(00h..09h,10h,20h)    - Display Control (these via GP1 register)
Some GP0 commands require additional parameters, which are written (following to the command) as further 32bit values to GP0. The execution of the command starts when all parameters have been received (or, in case of Polygon/Line commands, when the first 3/2 vertices have been received).

VRAM Overview / VRAM Addressing
VRAM is 1MByte (not mapped to the CPU bus) (it can be read/written only via I/O or DMA). The memory is used for:
  Framebuffer(s)      ;Usually 2 buffers (Drawing Area, and Display Area)
  Texture Page(s)     ;Required when using Textures
  Texture Palette(s)  ;Required when using 4bit/8bit Textures
The 1MByte VRAM is organized as 512 lines of 2048 bytes. It is accessed via coordinates, ranging from (0,0)=Upper-Left to (N,511)=Lower-Right.
  Unit  = 4bit  8bit  16bit  24bit   Halfwords   | Unit   = Lines
  Width = 4096  2048  1024   682.66  1024        | Height = 512
The horizontal coordinates are addressing memory in 4bit/8bit/16bit/24bit/halfword units (depending on what data formats you are using) (or a mixup thereof, eg. a halfword-base address, plus a 4bit texture coordinate).


GPU Render Polygon Commands

GP0(20h) - Monochrome three-point polygon, opaque
GP0(22h) - Monochrome three-point polygon, semi-transparent
GP0(28h) - Monochrome four-point polygon, opaque
GP0(2Ah) - Monochrome four-point polygon, semi-transparent
  1st  Color+Command     (CcBbGgRrh)
  2nd  Vertex1           (YyyyXxxxh)
  3rd  Vertex2           (YyyyXxxxh)
  4th  Vertex3           (YyyyXxxxh)
 (5th) Vertex4           (YyyyXxxxh) (if any)

GP0(24h) - Textured three-point polygon, opaque, texture-blending
GP0(25h) - Textured three-point polygon, opaque, raw-texture
GP0(26h) - Textured three-point polygon, semi-transparent, texture-blending
GP0(27h) - Textured three-point polygon, semi-transparent, raw-texture
GP0(2Ch) - Textured four-point polygon, opaque, texture-blending
GP0(2Dh) - Textured four-point polygon, opaque, raw-texture
GP0(2Eh) - Textured four-point polygon, semi-transparent, texture-blending
GP0(2Fh) - Textured four-point polygon, semi-transparent, raw-texture
  1st  Color+Command     (CcBbGgRrh) (color is ignored for raw-textures)
  2nd  Vertex1           (YyyyXxxxh)
  3rd  Texcoord1+Palette (ClutYyXxh)
  4th  Vertex2           (YyyyXxxxh)
  5th  Texcoord2+Texpage (PageYyXxh)
  6th  Vertex3           (YyyyXxxxh)
  7th  Texcoord3         (0000YyXxh)
 (8th) Vertex4           (YyyyXxxxh) (if any)
 (9th) Texcoord4         (0000YyXxh) (if any)

GP0(30h) - Shaded three-point polygon, opaque
GP0(32h) - Shaded three-point polygon, semi-transparent
GP0(38h) - Shaded four-point polygon, opaque
GP0(3Ah) - Shaded four-point polygon, semi-transparent
  1st  Color1+Command    (CcBbGgRrh)
  2nd  Vertex1           (YyyyXxxxh)
  3rd  Color2            (00BbGgRrh)
  4th  Vertex2           (YyyyXxxxh)
  5th  Color3            (00BbGgRrh)
  6th  Vertex3           (YyyyXxxxh)
 (7th) Color4            (00BbGgRrh) (if any)
 (8th) Vertex4           (YyyyXxxxh) (if any)

GP0(34h) - Shaded Textured three-point polygon, opaque, texture-blending
GP0(36h) - Shaded Textured three-point polygon, semi-transparent, tex-blend
GP0(3Ch) - Shaded Textured four-point polygon, opaque, texture-blending
GP0(3Eh) - Shaded Textured four-point polygon, semi-transparent, tex-blend
  1st  Color1+Command    (CcBbGgRrh)
  2nd  Vertex1           (YyyyXxxxh)
  3rd  Texcoord1+Palette (ClutYyXxh)
  4th  Color2            (00BbGgRrh)
  5th  Vertex2           (YyyyXxxxh)
  6th  Texcoord2+Texpage (PageYyXxh)
  7th  Color3            (00BbGgRrh)
  8th  Vertex3           (YyyyXxxxh)
  9th  Texcoord3         (0000YyXxh)
 (10th) Color4           (00BbGgRrh) (if any)
 (11th) Vertex4          (YyyyXxxxh) (if any)
 (12th) Texcoord4        (0000YyXxh) (if any)

GP0(35h,37h,3Dh,3Fh) - Undocumented/Nonsense (Raw Texture + UNUSED shading)
These are undocumented inefficient nonsense commands: Parameters are same as for GP0(34h,36h,3Ch,3Eh), ie. with colors for all vertices, but without actually using that colors. Instead, the commands are rendering raw textures without blending.
In other words, the commands have same function as GP0(25h,27h,2Dh,2Fh), but with additional/unused parameters (and possible additional/unused internal gouraud shading calculations).
For whatever reason, Castlevania is actually using these nonsense commands, namely GP0(3Dh) and GP0(3Fh).

GP0(21h,23h,29h,2Bh,31h,33h,39h,3Bh) - Undocumented/Nonsense
These commands have texture-blending disabled, which is nonsense because they are using untextured polygons anyways, ie. they are probably same as GP0(20h,22h,28h,2Ah,30h,32h,38h,3Ah).

Notes
Polygons are displayed up to <excluding> their lower-right coordinates.
Four-point polygons are internally processed as two Three-point polygons, the first consisting of Vertices 1,2,3, and the second of Vertices 2,3,4.
Within the Three-point polygons, the ordering of the vertices is don't care at the GPU side (a front-back check, based on clockwise or anti-clockwise ordering, can be implemented at the GTE side).
Dither enable (in Texpage command) affects ONLY polygons that do use Gouraud Shading or Texture Blending.


GPU Render Line Commands

GP0(40h) - Monochrome line, opaque
GP0(42h) - Monochrome line, semi-transparent
GP0(48h) - Monochrome Poly-line, opaque
GP0(4Ah) - Monochrome Poly-line, semi-transparent
  1st   Color+Command     (CcBbGgRrh)
  2nd   Vertex1           (YyyyXxxxh)
  3rd   Vertex2           (YyyyXxxxh)
 (...)  VertexN           (YyyyXxxxh) (poly-line only)
 (Last) Termination Code  (55555555h) (poly-line only)

GP0(50h) - Shaded line, opaque
GP0(52h) - Shaded line, semi-transparent
GP0(58h) - Shaded Poly-line, opaque
GP0(5Ah) - Shaded Poly-line, semi-transparent
  1st   Color1+Command    (CcBbGgRrh)
  2nd   Vertex1           (YyyyXxxxh)
  3rd   Color2            (00BbGgRrh)
  4th   Vertex2           (YyyyXxxxh)
 (...)  ColorN            (00BbGgRrh) (poly-line only)
 (...)  VertexN           (YyyyXxxxh) (poly-line only)
 (Last) Termination Code  (55555555h) (poly-line only)

Note
Lines are displayed up to <including> their lower-right coordinates (ie. unlike as for polygons, the lower-right coordinate is not excluded).
If dithering is enabled (via Texpage command), then both monochrome and shaded lines are drawn with dithering (this differs from monochrome polygons and monochrome rectangles).

Termination Codes for Poly-Lines (aka Linestrips)
The termination code should be usually 55555555h, however, Wild Arms 2 uses 50005000h (unknown which exact bits/values are relevant there).

Wire-Frame
Poly-Lines can be used (among others) to create Wire-Frame polygons (by setting the last Vertex equal to Vertex 1).


GPU Render Rectangle Commands

Rectangles are drawn much faster than polygons. Unlike for polygons, gouroud shading is not possible, dithering isn't applied, the rectangle must forcefully have horizontal and vertical edges, textures cannot be rotated or scaled, and, of course, the GPU does render Rectangles at once (without splitting them into triangles).

GP0(60h) - Monochrome Rectangle (variable size) (opaque)
GP0(62h) - Monochrome Rectangle (variable size) (semi-transparent)
GP0(68h) - Monochrome Rectangle (1x1) (Dot) (opaque)
GP0(6Ah) - Monochrome Rectangle (1x1) (Dot) (semi-transparent)
GP0(70h) - Monochrome Rectangle (8x8) (opaque)
GP0(72h) - Monochrome Rectangle (8x8) (semi-transparent)
GP0(78h) - Monochrome Rectangle (16x16) (opaque)
GP0(7Ah) - Monochrome Rectangle (16x16) (semi-transparent)
  1st  Color+Command     (CcBbGgRrh)
  2nd  Vertex            (YyyyXxxxh)
 (3rd) Width+Height      (YsizXsizh) (variable size only) (max 1023x511)

GP0(64h) - Textured Rectangle, variable size, opaque, texture-blending
GP0(65h) - Textured Rectangle, variable size, opaque, raw-texture
GP0(66h) - Textured Rectangle, variable size, semi-transp, texture-blending
GP0(67h) - Textured Rectangle, variable size, semi-transp, raw-texture
GP0(6Ch) - Textured Rectangle, 1x1 (nonsense), opaque, texture-blending
GP0(6Dh) - Textured Rectangle, 1x1 (nonsense), opaque, raw-texture
GP0(6Eh) - Textured Rectangle, 1x1 (nonsense), semi-transp, texture-blending
GP0(6Fh) - Textured Rectangle, 1x1 (nonsense), semi-transp, raw-texture
GP0(74h) - Textured Rectangle, 8x8, opaque, texture-blending
GP0(75h) - Textured Rectangle, 8x8, opaque, raw-texture
GP0(76h) - Textured Rectangle, 8x8, semi-transparent, texture-blending
GP0(77h) - Textured Rectangle, 8x8, semi-transparent, raw-texture
GP0(7Ch) - Textured Rectangle, 16x16, opaque, texture-blending
GP0(7Dh) - Textured Rectangle, 16x16, opaque, raw-texture
GP0(7Eh) - Textured Rectangle, 16x16, semi-transparent, texture-blending
GP0(7Fh) - Textured Rectangle, 16x16, semi-transparent, raw-texture
  1st  Color+Command     (CcBbGgRrh) (color is ignored for raw-textures)
  2nd  Vertex            (YyyyXxxxh) (upper-left edge of the rectangle)
  3rd  Texcoord+Palette  (ClutYyXxh) (for 4bpp Textures Xxh must be even!)
 (4th) Width+Height      (YsizXsizh) (variable size only) (max 1023x511)
Unlike for Textured-Polygons, the "Texpage" must be set up separately for Rectangles, via GP0(E1h). Width and Height can be up to 1023x511, however, the maximum size of the texture window is 256x256 (so the source data will be repeated when trying to use sizes larger than 256x256).

Texture Origin and X/Y-Flip
Vertex & Texcoord specify the upper-left edge of the rectangle. And, normally, screen coords and texture coords are both incremented during rendering the rectangle pixels.
Optionally, X/Y-Flip bits can be set in Texpage.Bit12/13, these bits cause the texture coordinates to be decremented (instead of incremented). The X/Y-Flip bits do affect only Rectangles (not Polygons, nor VRAM Transfers).
Caution: Reportedly, the X/Y-Flip feature isn't supported on old PSX consoles (unknown which ones exactly, maybe such with PU-7 mainboards, and unknown how to detect flipping support; except of course by reading VRAM).

Note
There are also two VRAM Transfer commands which work similar to GP0(60h) and GP0(65h). Eventually, that commands might be even faster... although not sure if they do use the Texture Cache?
The difference is that VRAM Transfers do not clip to the Drawig Area boundary, do not support fully-transparent nor semi-transparent texture pixels, and do not convert color depths (eg. without 4bit texture to 16bit framebuffer conversion).


GPU Rendering Attributes

Vertex (Parameter for Polygon, Line, Rectangle commands)
  0-10   X-coordinate (signed, -1024..+1023)
  11-15  Not used (usually sign-extension, but ignored by hardware)
  16-26  Y-coordinate (signed, -1024..+1023)
  26-31  Not used (usually sign-extension, but ignored by hardware)
Size Restriction: The maximum distance between two vertices is 1023 horizontally, and 511 vertically. Polygons and lines that are exceeding that dimensions are NOT rendered. For example, a line from Y1=-300 to Y2=+300 is NOT rendered, a line from Y1=-100 to Y2=+400 is rendered (as far as it is within the drawing area).
If portions of the polygon/line/rectangle are located outside of the drawing area, then the hardware renders only the portion that is inside of the drawing area. Unknown if the hardware is skipping all clipped pixels at once (within a single clock cycle), or if it's (slowly) processing them pixel by pixel?

Color Attribute (Parameter for all Rendering commands, except Raw Texture)
  0-7    Red   (0..FFh)
  8-15   Green (0..FFh)
  16-23  Blue  (0..FFh)
  24-31  Command (in first parameter) (don't care in further parameters)
Caution: For untextured graphics, 8bit RGB values of FFh are brightest. However, for texture blending, 8bit values of 80h are brightest (values 81h..FFh are "brighter than bright" allowing to make textures about twice as bright as than they were originially stored in memory; of course the results can't exceed the maximum brightness, ie. the 5bit values written to the framebuffer are saturated to max 1Fh).

Texpage Attribute (Parameter for Textured-Polygons commands)
  0-8    Same as GP0(E1h).Bit0-8 (see there)
  9-10   Unused (does NOT change GP0(E1h).Bit9-10)
  11     Same as GP0(E1h).Bit11  (see there)
  12-13  Unused (does NOT change GP0(E1h).Bit12-13)
  14-15  Unused (should be 0)
This attribute is used in all Textured-Polygons commands.

Clut Attribute (Color Lookup Table, aka Palette)
This attribute is used in all Textured Polygon/Rectangle commands. Of course, it's relevant only for 4bit/8bit textures (don't care for 15bit textures).
  0-5      X coordinate X/16  (ie. in 16-halfword steps)
  6-14     Y coordinate 0-511 (ie. in 1-line steps)
  15       Unknown/unused (should be 0)
Specifies the location of the CLUT data within VRAM.

GP0(E1h) - Draw Mode setting (aka "Texpage")
  0-3   Texture page X Base   (N*64) (ie. in 64-halfword steps)    ;GPUSTAT.0-3
  4     Texture page Y Base   (N*256) (ie. 0 or 256)               ;GPUSTAT.4
  5-6   Semi Transparency     (0=B/2+F/2, 1=B+F, 2=B-F, 3=B+F/4)   ;GPUSTAT.5-6
  7-8   Texture page colors   (0=4bit, 1=8bit, 2=15bit, 3=Reserved);GPUSTAT.7-8
  9     Dither 24bit to 15bit (0=Off/strip LSBs, 1=Dither Enabled) ;GPUSTAT.9
  10    Drawing to display area (0=Prohibited, 1=Allowed)          ;GPUSTAT.10
  11    Texture Disable (0=Normal, 1=Disable if GP1(09h).Bit0=1)   ;GPUSTAT.15
          (Above might be chipselect for (absent) second VRAM chip?)
  12    Textured Rectangle X-Flip   (BIOS does set this bit on power-up...?)
  13    Textured Rectangle Y-Flip   (BIOS does set it equal to GPUSTAT.13...?)
  14-23 Not used (should be 0)
  24-31 Command  (E1h)
The GP0(E1h) command is required only for Lines, Rectangle, and Untextured-Polygons (for Textured-Polygons, the data is specified in form of the Texpage attribute; except that, Bit9-10 can be changed only via GP0(E1h), not via the Texpage attribute).
Texture page colors setting 3 (reserved) is same as setting 2 (15bit).
Note: GP0(00h) seems to be often inserted between Texpage and Rectangle commands, maybe it acts as a NOP, which may be required between that commands, for timing reasons...?

GP0(E2h) - Texture Window setting
  0-4    Texture window Mask X   (in 8 pixel steps)
  5-9    Texture window Mask Y   (in 8 pixel steps)
  10-14  Texture window Offset X (in 8 pixel steps)
  15-19  Texture window Offset Y (in 8 pixel steps)
  20-23  Not used (zero)
  24-31  Command  (E2h)
Mask specifies the bits that are to be manipulated, and Offset contains the new values for these bits, ie. texture X/Y coordinates are adjusted as so:
  Texcoord = (Texcoord AND (NOT (Mask*8))) OR ((Offset AND Mask)*8)
The area within a texture window is repeated throughout the texture page. The data is not actually stored all over the texture page but the GPU reads the repeated patterns as if they were there.

GP0(E3h) - Set Drawing Area top left (X1,Y1)
GP0(E4h) - Set Drawing Area bottom right (X2,Y2)
  0-9    X-coordinate (0..1023)
  10-18  Y-coordinate (0..511)   ;\on Old 160pin GPU (max 1MB VRAM)
  19-23  Not used (zero)         ;/
  10-19  Y-coordinate (0..1023)  ;\on New 208pin GPU (max 2MB VRAM)
  20-23  Not used (zero)         ;/(retail consoles have only 1MB though)
  24-31  Command  (Exh)
Sets the drawing area corners. The Render commands GP0(20h..7Fh) are automatically clipping any pixels that are outside of this region.

GP0(E5h) - Set Drawing Offset (X,Y)
  0-10   X-offset (-1024..+1023) (usually within X1,X2 of Drawing Area)
  11-21  Y-offset (-1024..+1023) (usually within Y1,Y2 of Drawing Area)
  22-23  Not used (zero)
  24-31  Command  (E5h)
If you have configured the GTE to produce vertices with coordinate "0,0" being located in the center of the drawing area, then the Drawing Offset must be "X1+(X2-X1)/2, Y1+(Y2-Y1)/2". Or, if coordinate "0,0" shall be the upper-left of the Drawing Area, then Drawing Offset should be "X1,Y1". Where X1,Y1,X2,Y2 are the values defined with GP0(E3h-E4h).

GP0(E6h) - Mask Bit Setting
  0     Set mask while drawing (0=TextureBit15, 1=ForceBit15=1)   ;GPUSTAT.11
  1     Check mask before draw (0=Draw Always, 1=Draw if Bit15=0) ;GPUSTAT.12
  2-23  Not used (zero)
  24-31 Command  (E6h)
When bit0 is off, the upper bit of the data written to the framebuffer is equal to bit15 of the texture color (ie. it is set for colors that are marked as "semi-transparent") (for untextured polygons, bit15 is set to zero).
When bit1 is on, any (old) pixels in the framebuffer with bit15=1 are write-protected, and cannot be overwritten by (new) rendering commands.
The mask setting affects all rendering commands, as well as CPU-to-VRAM and VRAM-to-VRAM transfer commands (where it acts on the separate halfwords, ie. as for 15bit textures). However, Mask does NOT affect the Fill-VRAM command.

Note
GP0(E3h..E5h) do not take up space in the FIFO, so they are probably executed immediately (even if there're still other commands in the FIFO). Best use them only if you are sure that the FIFO is empty (otherwise the new Drawing Area settings might accidently affect older Rendering Commands in the FIFO).


GPU Memory Transfer Commands

GP0(01h) - Clear Cache
  1st  Command           (Cc000000h)
"Seems to be the same as the GP1 command." Uh, which GP1 command?
Before using GP(A0h) or GP(C0h) one should reportedly send:
Clear Cache (01000000h)
"Reset command buffer (write to GP1 or GP0)" Uh? Bullshit.
However, there <may> be some situations in which it is neccessary to flush the texture cache.

GP0(02h) - Fill Rectangle in VRAM
  1st  Color+Command     (CcBbGgRrh)  ;24bit RGB value (see note)
  2nd  Top Left Corner   (YyyyXxxxh)  ;Xpos counted in halfwords, steps of 10h
  3rd  Width+Height      (YsizXsizh)  ;Xsiz counted in halfwords, steps of 10h
Fills the area in the frame buffer with the value in RGB. Horizontally the filling is done in 16-pixel (32-bytes) units (see below masking/rounding).
The "Color" parameter is a 24bit RGB value, however, the actual fill data is 16bit: The hardware automatically converts the 24bit RGB value to 15bit RGB (with bit15=0).
Fill is NOT affected by the Mask settings (acts as if Mask.Bit0,1 are both zero).

GP0(80h) - Copy Rectangle (VRAM to VRAM)
  1st  Command           (Cc000000h)
  2nd  Source Coord      (YyyyXxxxh)  ;Xpos counted in halfwords
  3rd  Destination Coord (YyyyXxxxh)  ;Xpos counted in halfwords
  4th  Width+Height      (YsizXsizh)  ;Xsiz counted in halfwords
Copys data within framebuffer. The transfer is affected by Mask setting.

GP0(A0h) - Copy Rectangle (CPU to VRAM)
  1st  Command           (Cc000000h)
  2nd  Destination Coord (YyyyXxxxh)  ;Xpos counted in halfwords
  3rd  Width+Height      (YsizXsizh)  ;Xsiz counted in halfwords
  ...  Data              (...)      <--- usually transferred via DMA
Transfers data from CPU to frame buffer. If the number of halfwords to be sent is odd, an extra halfword should be sent (packets consist of 32bit units). The transfer is affected by Mask setting.

GP0(C0h) - Copy Rectangle (VRAM to CPU)
  1st  Command           (Cc000000h) ;\
  2nd  Source Coord      (YyyyXxxxh) ; write to GP0 port (as usually)
  3rd  Width+Height      (YsizXsizh) ;/
  ...  Data              (...)       ;<--- read from GPUREAD port (or via DMA)
Transfers data from frame buffer to CPU. Wait for bit27 of the status register to be set before reading the image data. When the number of halfwords is odd, an extra halfword is read at the end (packets consist of 32bit units).

Masking and Rounding for FILL Command parameters
  Xpos=(Xpos AND 3F0h)                       ;range 0..3F0h, in steps of 10h
  Ypos=(Ypos AND 1FFh)                       ;range 0..1FFh
  Xsiz=((Xsiz AND 3FFh)+0Fh) AND (NOT 0Fh)   ;range 0..400h, in steps of 10h
  Ysiz=((Ysiz AND 1FFh))                     ;range 0..1FFh
Fill does NOT occur when Xsiz=0 or Ysiz=0 (unlike as for Copy commands). Xsiz=400h works only indirectly: Param=400h is handled as Xsiz=0, however, Param=3F1h..3FFh is rounded-up and handled as Xsiz=400h.

Masking for COPY Commands parameters
  Xpos=(Xpos AND 3FFh)                       ;range 0..3FFh
  Ypos=(Ypos AND 1FFh)                       ;range 0..1FFh
  Xsiz=((Xsiz-1) AND 3FFh)+1                 ;range 1..400h
  Ysiz=((Ysiz-1) AND 1FFh)+1                 ;range 1..200h
Parameters are just clipped to 10bit/9bit range, the only special case is that Size=0 is handled as Size=max.

Notes
The coordinates for the above VRAM transfer commands are absolute framebuffer addresses (not relative to Draw Offset, and not clipped to Draw Area).
Non-DMA transfers seem to be working at any time, but GPU-DMA Transfers seem to be working ONLY during V-Blank (outside of V-Blank, portions of the data appear to be skipped, and the following words arrive at wrong addresses), unknown if it's possible to change that by whatever configuration settings...? That problem appears ONLY for continous DMA aka VRAM transfers (linked-list DMA aka Ordering Table works even outside V-Blank).

Wrapping
If the Source/Dest starting points plus the width/height value exceed the 1024x512 pixel VRAM size, then the Copy/Fill operations wrap to the opposite memory edge (without any carry-out from X to Y, nor from Y to X).


GPU Other Commands

GP0(1Fh) - Interrupt Request (IRQ1)
  1st  Command           (Cc000000h)                    ;GPUSTAT.24
Requests IRQ1. Can be acknowledged via GP1(02h). This feature is rarely used.
Note: The command is used by Blaze'n'Blade, but the game doesn't have IRQ1 enabled, and the written value (1F801810h) looks more like an I/O address, rather than like a command, so not sure if it's done intentionally, or if it is just a bug.

GP0(03h) - Unknown?
Unknown. Doesn't seem to be used by any games. Unlike the "NOP" commands, GP0(03h) does take up space in FIFO, so it is apparently not a NOP.

GP0(00h) - NOP (?)
This command doesn't take up space in the FIFO (eg. even if a VRAM-to-VRAM transfer is still busy, one can send dozens of GP0(00h) commands, without the command FIFO becoming full. So, either the command is ignored (or, if it has a function, it is executed immediately, even while the transfer is busy).
...
GP0(00h) unknown, used with parameter = 08A16Ch... or rather 08FDBCh ... the written value seems to be a bios/ram memory address, anded with 00FFFFFFh... maybe a bios bug?
GP0(00h) seems to be often inserted between Texpage and Rectangle commands, maybe it acts as a NOP, which may be required between that commands, for timing reasons...?

GP0(04h..1Eh,E0h,E7h..EFh) - Mirrors of GP0(00h) - NOP (?)
Like GP0(00h), these commands don't take up space in the FIFO. So, maybe, they are same as GP0(00h), however, the Drawing Area/Offset commands GP0(E3h..E5h) don't take up FIFO space either, so not taking up FIFO space doesn't neccessarily mean that the command has no function.

GP0(81h..9Fh) - Mirror of GP0(80h) - Copy Rectangle (VRAM to VRAM)
GP0(A1h..BFh) - Mirror of GP0(A0h) - Copy Rectangle (CPU to VRAM)
GP0(C1h..DFh) - Mirror of GP0(C0h) - Copy Rectangle (VRAM to CPU)
Mirrors.


GPU Display Control Commands (GP1)

GP1 Display Control Commands are sent by writing the 8bit Command number (MSBs), and 24bit parameter (LSBs) to Port 1F801814h. Unlike GP0 commands, GP1 commands are passed directly to the GPU (ie. they can be sent even when the FIFO is full).

GP1(00h) - Reset GPU
  0-23  Not used (zero)
Resets the GPU to the following values:
  GP1(01h)      ;clear fifo
  GP1(02h)      ;ack irq (0)
  GP1(03h)      ;display off (1)
  GP1(04h)      ;dma off (0)
  GP1(05h)      ;display address (0)
  GP1(06h)      ;display x1,x2 (x1=200h, x2=200h+256*10)
  GP1(07h)      ;display y1,y2 (y1=010h, y2=010h+240)
  GP1(08h)      ;display mode 320x200 NTSC (0)
  GP0(E1h..E6h) ;rendering attributes (0)
Accordingly, GPUSTAT becomes 14802000h. The x1,y1 values are too small, ie. the upper-left edge isn't visible. Note that GP1(09h) is NOT affected by the reset command.

GP1(01h) - Reset Command Buffer
  0-23  Not used (zero)
Clears the command FIFO, and aborts the current rendering command (eg. this may end up with an incompletely drawn triangle).

GP1(02h) - Acknowledge GPU Interrupt (IRQ1)
  0-23  Not used (zero)                                        ;GPUSTAT.24
Resets the IRQ flag in GPUSTAT.24. The flag can be set via GP0(1Fh).

GP1(03h) - Display Enable
  0     Display On/Off   (0=On, 1=Off)                         ;GPUSTAT.23
  1-23  Not used (zero)
Turns display on/off. "Note that a turned off screen still gives the flicker of NTSC on a PAL screen if NTSC mode is selected."
The "Off" settings displays a black picture (and still sends /SYNC signals to the television set). (Unknown if it still generates vblank IRQs though?)

GP1(04h) - DMA Direction / Data Request
  0-1  DMA Direction (0=Off, 1=FIFO, 2=CPUtoGP0, 3=GPUREADtoCPU) ;GPUSTAT.29-30
  2-23 Not used (zero)
Notes: Manually sending/reading data by software (non-DMA) is ALWAYS possible, regardless of the GP1(04h) setting. The GP1(04h) setting does affect the meaning of GPUSTAT.25.

Display start/end
Specifies where the display area is positioned on the screen, and how much data gets sent to the screen. The screen sizes of the display area are valid only if the horizontal/vertical start/end values are default. By changing these you can get bigger/smaller display screens. On most TV's there is some black around the edge, which can be utilised by setting the start of the screen earlier and the end later. The size of the pixels is NOT changed with these settings, the GPU simply sends more data to the screen. Some monitors/TVs have a smaller display area and the extended size might not be visible on those sets. "(Mine is capable of about 330 pixels horizontal, and 272 vertical in 320*240 mode)"

GP1(05h) - Start of Display area (in VRAM)
  0-9   X (0-1023)    (halfword address in VRAM)  (relative to begin of VRAM)
  10-18 Y (0-511)     (scanline number in VRAM)   (relative to begin of VRAM)
  19-23 Not used (zero)
Upper/left Display source address in VRAM. The size and target position on screen is set via Display Range registers; target=X1,Y2; size=(X2-X1/cycles_per_pix), (Y2-Y1).

GP1(06h) - Horizontal Display range (on Screen)
  0-11   X1 (260h+0)       ;12bit       ;\counted in 53.222400MHz units,
  12-23  X2 (260h+320*8)   ;12bit       ;/relative to HSYNC
Specifies the horizontal range within which the display area is displayed. For resolutions other than 320 pixels it may be necessary to fine adjust the value to obtain an exact match (eg. X2=X1+pixels*cycles_per_pix).
The number of displayed pixels per line is "(((X2-X1)/cycles_per_pix)+2) AND NOT 3" (ie. the hardware is rounding the width up/down to a multiple of 4 pixels).
Most games are using a width equal to the horizontal resolution (ie. 256, 320, 368, 512, 640 pixels). A few games are using slightly smaller widths (probably due to programming bugs). Pandemonium 2 is using a bigger "overscan" width (ensuring an intact picture without borders even on mis-calibrated TV sets).
The 260h value is the first visible pixel on normal TV Sets, this value is used by MOST NTSC games, and SOME PAL games (see below notes on Mis-Centered PAL games).

GP1(07h) - Vertical Display range (on Screen)
  0-9   Y1 (NTSC=88h-(224/2), (PAL=A3h-(264/2))  ;\scanline numbers on screen,
  10-19 Y2 (NTSC=88h+(224/2), (PAL=A3h+(264/2))  ;/relative to VSYNC
  20-23 Not used (zero)
Specifies the vertical range within which the display area is displayed. The number of lines is Y2-Y1 (unlike as for the width, there's no rounding applied to the height). If Y2 is set to a much too large value, then the hardware stops to generate vblank interrupts (IRQ0).
The 88h/A3h values are the middle-scanlines on normal TV Sets, these values are used by MOST NTSC games, and SOME PAL games (see below notes on Mis-Centered PAL games).
The 224/264 values are for fullscreen pictures. Many NTSC games display 240 lines (overscan with hidden lines). Many PAL games display only 256 lines (underscan with black borders).

GP1(08h) - Display mode
  0-1   Horizontal Resolution 1     (0=256, 1=320, 2=512, 3=640) ;GPUSTAT.17-18
  2     Vertical Resolution         (0=240, 1=480, when Bit5=1)  ;GPUSTAT.19
  3     Video Mode                  (0=NTSC/60Hz, 1=PAL/50Hz)    ;GPUSTAT.20
  4     Display Area Color Depth    (0=15bit, 1=24bit)           ;GPUSTAT.21
  5     Vertical Interlace          (0=Off, 1=On)                ;GPUSTAT.22
  6     Horizontal Resolution 2     (0=256/320/512/640, 1=368)   ;GPUSTAT.16
  7     "Reverseflag"               (0=Normal, 1=Distorted)      ;GPUSTAT.14
  8-23  Not used (zero)
Note: Interlace must be enabled to see all lines in 480-lines mode (interlace is causing ugly flickering, so a non-interlaced low resolution image is typically having better quality than a high resolution interlaced image, a pretty bad example are the intro screens shown by the BIOS). The Display Area Color Depth does NOT affect the Drawing Area (the Drawing Area is <always> 15bit).
When the "Reverseflag" is set, the display scrolls down 2 lines or so, and colored regions are getting somehow hatched/distorted, but black and white regions are still looking okay. Don't know what that's good for? Probably relates to PAL/NTSC-Color Clock vs PSX-Dot Clock mismatches: Bit7=0 causes Flimmering errors (errors at different locations in each frame), and Bit7=1 causes Static errors (errors at same locations in all frames)?

GP1(10h) - Get GPU Info
GP1(11h..1Fh) - Mirrors of GP1(10h), Get GPU Info
After sending the command, the result can be immediately read from GPUREAD register (there's no NOP or other delay required) (namely GPUSTAT.Bit27 is used only for VRAM-Reads, but NOT for GPU-Info-Reads, so do not try to wait for that flag).
  0-23  Select Information which is to be retrieved (via following GPUREAD)
On Old 180pin GPUs, following values can be selected:
  00h-01h = Returns Nothing (old value in GPUREAD remains unchanged)
  02h     = Read Texture Window setting  ;GP0(E2h) ;20bit/MSBs=Nothing
  03h     = Read Draw area top left      ;GP0(E3h) ;19bit/MSBs=Nothing
  04h     = Read Draw area bottom right  ;GP0(E4h) ;19bit/MSBs=Nothing
  05h     = Read Draw offset             ;GP0(E5h) ;22bit
  06h-07h = Returns Nothing (old value in GPUREAD remains unchanged)
  08h-FFFFFFh = Mirrors of 00h..07h
On New 208pin GPUs, following values can be selected:
  00h-01h = Returns Nothing (old value in GPUREAD remains unchanged)
  02h     = Read Texture Window setting  ;GP0(E2h) ;20bit/MSBs=Nothing
  03h     = Read Draw area top left      ;GP0(E3h) ;20bit/MSBs=Nothing
  04h     = Read Draw area bottom right  ;GP0(E4h) ;20bit/MSBs=Nothing
  05h     = Read Draw offset             ;GP0(E5h) ;22bit
  06h     = Returns Nothing (old value in GPUREAD remains unchanged)
  07h     = Read GPU Type (usually 2)    ;see "GPU Versions" chapter
  08h     = Unknown (Returns 00000000h) (lightgun on some GPUs?)
  09h-0Fh = Returns Nothing (old value in GPUREAD remains unchanged)
  10h-FFFFFFh = Mirrors of 00h..0Fh
The selected data is latched in GPUREAD, the same/latched value can be read multiple times, but, the latch isn't automatically updated when changing GP0 registers.

GP1(09h) - New Texture Disable
  0     Texture Disable (0=Normal, 1=Allow Disable via GP0(E1h).11) ;GPUSTAT.15
  1-23  Unknown (seems to have no effect)
This feature seems to be intended for debugging purposes (most released games do contain program code for disabling textures, but do never execute it).
GP1(09h) seems to be supported only on New GPUs. Old GPUs don't support it all, and there seem to be some Special/Prototype GPUs that use GP1(20h) instead of GP1(09h).

GP1(20h) - Special/Prototype Texture Disable
  0-23  Unknown (501h=Texture Enable, 504h=Texture Disable, or so?)
Seems to be a used only on whatever arcade/prototype GPUs. New GPUs are using GP1(09h) instead of GP1(20h).

GP1(0Bh) - Unknown/Internal?
  0-10  Unknown (GPU crashes after a while when set to 274h..7FFh)
  11-23 Unknown (seems to have no effect)
The register doesn't seem to be used by any games.

GP1(0Ah,0Ch..0Fh,21h..3Fh) - N/A
Not used?

GP1(40h..FFh) - N/A (Mirrors)
Mirrors of GP1(00h..3Fh).

Mis-Centered PAL Games (wrong GP1(06h)/GP1(07h) settings)
NTSC games are typically well centered (using X1=260h, and Y1/Y2=88h+/-N).
PAL games should be centered as X1=260h, and Y1/Y2=A3h+/-N) - these values would be looking well on a Philips Philetta TV Set, and do also match up with other common picture positions (eg. as used by Nintendo's SNES console).
However, most PAL games are using completely different "random" centering values (maybe caused by different developers trying to match the centering to the different TV Sets) (although it looks more as if the PAL developers just went amok: Many PAL games are even using different centerings for their Intro, Movie, and actual Game sequences).
In result, most PAL games are looking like crap when playing them on a real PSX. For PSX emulators it may be recommended to ignore the GP1(06h)/GP1(07h) centering, and instead, apply auto-centering to PAL games.
For PAL game developers, it may be recommended to add a screen centering option (as found in Tomb Raider 3, for example). Unknown if this is really required... or if X1=260h, and Y1/Y2=A3h+/-N would work fine on most or all PAL TV Sets?


GPU Status Register

1F801814h - GPUSTAT - GPU Status Register (R)
  0-3   Texture page X Base   (N*64)                              ;GP0(E1h).0-3
  4     Texture page Y Base   (N*256) (ie. 0 or 256)              ;GP0(E1h).4
  5-6   Semi Transparency     (0=B/2+F/2, 1=B+F, 2=B-F, 3=B+F/4)  ;GP0(E1h).5-6
  7-8   Texture page colors   (0=4bit, 1=8bit, 2=15bit, 3=Reserved)GP0(E1h).7-8
  9     Dither 24bit to 15bit (0=Off/strip LSBs, 1=Dither Enabled);GP0(E1h).9
  10    Drawing to display area (0=Prohibited, 1=Allowed)         ;GP0(E1h).10
  11    Set Mask-bit when drawing pixels (0=No, 1=Yes/Mask)       ;GP0(E6h).0
  12    Draw Pixels           (0=Always, 1=Not to Masked areas)   ;GP0(E6h).1
  13    Interlace Field       (or, always 1 when GP1(08h).5=0)
  14    "Reverseflag"         (0=Normal, 1=Distorted)             ;GP1(08h).7
  15    Texture Disable       (0=Normal, 1=Disable Textures)      ;GP0(E1h).11
  16    Horizontal Resolution 2     (0=256/320/512/640, 1=368)    ;GP1(08h).6
  17-18 Horizontal Resolution 1     (0=256, 1=320, 2=512, 3=640)  ;GP1(08h).0-1
  19    Vertical Resolution         (0=240, 1=480, when Bit22=1)  ;GP1(08h).2
  20    Video Mode                  (0=NTSC/60Hz, 1=PAL/50Hz)     ;GP1(08h).3
  21    Display Area Color Depth    (0=15bit, 1=24bit)            ;GP1(08h).4
  22    Vertical Interlace          (0=Off, 1=On)                 ;GP1(08h).5
  23    Display Enable              (0=Enabled, 1=Disabled)       ;GP1(03h).0
  24    Interrupt Request (IRQ1)    (0=Off, 1=IRQ)       ;GP0(1Fh)/GP1(02h)
  25    DMA / Data Request, meaning depends on GP1(04h) DMA Direction:
          When GP1(04h)=0 ---> Always zero (0)
          When GP1(04h)=1 ---> FIFO State  (0=Full, 1=Not Full)
          When GP1(04h)=2 ---> Same as GPUSTAT.28
          When GP1(04h)=3 ---> Same as GPUSTAT.27
  26    Ready to receive Cmd Word   (0=No, 1=Ready)  ;GP0(...) ;via GP0
  27    Ready to send VRAM to CPU   (0=No, 1=Ready)  ;GP0(C0h) ;via GPUREAD
  28    Ready to receive DMA Block  (0=No, 1=Ready)  ;GP0(...) ;via GP0
  29-30 DMA Direction (0=Off, 1=?, 2=CPUtoGP0, 3=GPUREADtoCPU)    ;GP1(04h).0-1
  31    Drawing even/odd lines in interlace mode (0=Even or Vblank, 1=Odd)
In 480-lines mode, bit31 changes per frame. And in 240-lines mode, the bit changes per scanline. The bit is always zero during Vblank (vertical retrace and upper/lower screen border).

Note
Further GPU status information can be retrieved via GP1(10h) and GP0(C0h).

Ready Bits
Bit28: Normally, this bit gets cleared when the command execution is busy (ie. once when the command and all of its parameters are received), however, for Polygon and Line Rendering commands, the bit gets cleared immediately after receiving the command word (ie. before receiving the vertex parameters). The bit is used as DMA request in DMA Mode 2, accordingly, the DMA would probably hang if the Polygon/Line parameters are transferred in a separate DMA block (ie. the DMA probably starts ONLY on command words).
Bit27: Gets set after sending GP0(C0h) and its parameters, and stays set until all data words are received; used as DMA request in DMA Mode 3.
Bit26: Gets set when the GPU wants to receive a command. If the bit is cleared, then the GPU does either want to receive data, or it is busy with a command execution (and doesn't want to receive anything).
Bit25: This is the DMA Request bit, however, the bit is also useful for non-DMA transfers, especially in the FIFO State mode.


GPU Versions

Summary of GPU Differences
  Differences...                Old 160pin GPU          New 208pin GPU
  GPU Chip                      CXD8514Q                CXD8561Q/BQ/CQ/CXD9500Q
  Mainboard                     EARLY-PU-8 and below    LATE-PU-8 and up
  Memory Type                   Dual-ported VRAM        Normal DRAM
  GPUSTAT.13 when interlace=off always 0                always 1
  GPUSTAT.14                    always 0                reverseflag
  GPUSTAT.15                    always 0                texture_disable
  GP1(10h:index3..4)            19bit (1MB VRAM)        20bit (2MB VRAM)
  GP1(10h:index7)               N/A                     00000002h version
  GP1(10h:index8)               mirror of index0        00000000h zero
  GP1(10h:index9..F)            mirror of index1..7     N/A
  GP1(20h)                      whatever? used for detecting old gpu
  GP0(E1h).bit12/13             without x/y-flip        with x/y-flip
  GP0(03h)                      N/A (no stored in fifo) unknown/unused command
  Shaded Textures               ((color/8)*texel)/2     (color*texel)/16
  GP0(02h) FillVram             xpos.bit0-3=0Fh=bugged  xpos.bit0-3=ignored
  dma-to-vram: doesn't work with blksiz>10h (new gpu works with blksiz=8C0h!)
  dma-to-vram: MAYBE also needs extra software-handshake to confirm DMA done?
   320*224 pix = 11800h pix = 8C00h words
  GP0(80h) VramToVram           works                   Freeze on large moves?

Shaded Textures
The Old GPU crops 8:8:8 bit gouraud shading color to 5:5:5 bit before multiplying it with the texture color, resulting in rather poor graphics. For example, the snow scence in the first level of Tomb Raider I looks a lot smoother on New GPUs.
The cropped colors are looking a bit as if dithering would be disabled (although, technically dithering works fine, but due to the crippled color input, it's always using the same dither pattern per 8 intensities, instead of using 8 different dither patterns).

Memory/Rendering Timings
The Old GPU uses two Dual-ported VRAM chips (each with two 16bit databusses, one for CPU/DMA/rendering access, and one for output to the video DAC). The New GPU uses s normal DRAM chip (with single 32bit databus).
The exact timing differences are unknown, but the different memory types should result in quite different timings:
The Old GPU might perform better on non-32bit aligned accesses, and on memory accesses performed simultaneously with DAC output.
On the other hand, the New GPU's DRAM seems to be faster in some cases (for example, during Vblank, it's fast enough to perform DMA's with blksiz>10h, which exceeds the GPU's FIFO size, and causes lost data on Old GPUs).

X/Y-Flip and 2MB Video RAM
The X/Y-flipping feature may be used by arcade games (provided that the arcade board is fitted with New GPUs). The flipping feature does also work on retail consoles with New GPUs, but PSX games should never use that feature (for maintaining compatiblity with older PSX consoles).
2Mbyte Video RAM is used on some arcade boards. Whilst PSX retail consoles are always containing only 1MByte RAM, so the feature cannot be used even if the console contains a New GPU. There's one special case: Some PSone consoles are actually fitted with 2MB chips (maybe because smaller chips haven't been in production anymore), but the chips are wired so that only half of the memory is accessible (the extra memory could be theoretically unlocked with some minimal hardware modification).

GPU Detection (and optional texture disable)
Below is slightly customized GPU Detection function taken from Perfect Assassin (the index7 latching works ONLY on New GPUs, whilst old GPUs would leave the latched value unchanged; as a workaround, the index4 latching is used to ensure that the latch won't contain 000002h on old GPUs, assuming that index4 is never set to 000002h).
  [1F801814h]=10000004h       ;GP1(10h).index4 (latch draw area bottom right)
  [1F801814h]=10000007h       ;GP1(10h).index7 (latch GPU version, if any)
  if ([1F801810h] AND 00FFFFFFh)=00000002h then goto @@gpu_v2
  [1F801810h]=([1F801814h] AND 3FFFh) OR E1001000h ;change GPUSTAT via GP0(E1h)
  dummy=[1F801810h]           ;dummy read (unknown purpose)
  if ([1F801814h] AND 00001000h) then goto @@gpu_v1 else goto @@gpu_v0
 ;---
 @@gpu_v0:  ;Old 160pin GPU (EARLY-PU-8)
  return 0
 ;---
 @@gpu_v1:  ;unknown GPU type, maybe some custom arcade/prototype version ?
  if want_tex_dis then [1F801814h]=20000504h  ;GP1(20h)
  return 1
 ;---
 @@gpu_v2:  ;New 208pin GPU (LATE-PU-8 and up)
  if want_tex_dis then [1F801814h]=09000001h  ;GP1(09h)
  return 2

GP0(02h) FillVram
The FillVram command does normally ignore the lower 4bit of the x-coordinate (and software should always set those bits to zero). However, if the 4bits are all set, then the Old GPU does write each 2nd pixel to wrong memory address. For example, a 32x4 pixel fill produces following results for x=0..1Fh:
  0h              10h             20h             30h             40h
  |               |               |               |               |
  ################################                                 ;\x=00h..0Eh
  ################################                                 ; and, x=0Fh
  ################################                                 ; on NEW GPU
  ################################                                 ;/
   # # # # # # # ################## # # # # # # #                  ;\
   # # # # # # # ################## # # # # # # #                  ; x=0Fh
   # # # # # # # ################## # # # # # # #                  ; on OLD GPU
   # # # # # # # ################## # # # # # # #                  ;/
                  ################################                 ;\x=10h..1Eh
                  ################################                 ; and, x=1Fh
                  ################################                 ; on NEW GPU
                  ################################                 ;/
                   # # # # # # # ################## # # # # # # #  ;\
                   # # # # # # # ################## # # # # # # #  ; x=1Fh
                   # # # # # # # ################## # # # # # # #  ; on OLD GPU
                   # # # # # # # ################## # # # # # # #  ;/

Arcade GPUs
Some arcade boards are using normal retail GPUs, however, there are also two special non-retail 208pin GPUs which seem to be solely used on arcade boards:
  IC21  - 208pin - "SONY CXD8538Q"   ;seen on GP-11 (namco System 11) boards
  IC103 - 208pin - "SONY CXD8654Q"   ;seen on GP-15 (namco System 12) boards
The exact differences to retail GPUs are unknown. One of the special GPUs is said to use entierly different command numbers for rendering commands (maybe some old prototype variant, or maybe some protection against cloning arcade boards with retail chips).


GPU Depth Ordering

Absent Depth Buffer
The PlayStation's GPU stores only RGB colors in the framebuffer (ie. unlike modern 3D processors, it's NOT buffering Depth values; leaving apart the Mask bit, which could be considered as a tiny 1bit "Depth" or "Priority" value). In fact, the GPU supports only X,Y coordinates, and it's totally unaware of Z coordinates. So, when rendering a polygon, the hardware CANNOT determine which of the new pixels are in front/behind of the old pixels in the buffer.

Simple Ordering
The rendering simply takes place in the ordering as the data is sent to the GPU (ie. the most distant objects should be sent first). For 2D graphics, it's fairly easy follow that order (eg. even multi-layer 2D graphics can be using DMA2-continous mode).

Depth Ordering Table (OT)
For 3D graphics, the ordering of the polygons may change more or less randomly (eg. when rotating/moving the camera). To solve that problem, the whole rendering data is usually first stored in a Depth Ordering Table (OT) in Main RAM, and, once when all polygons have been stored in the OT, the OT is sent to the GPU via "DMA2-linked-list" mode.

Initializing an empty OT (via DMA6)
DMA channel 6 can be used to set up an empty linked list, in which each entry points to the previous:
  DPCR    - enable bits                                 ;Example=x8xxxxxxh
  D6_MADR - pointer to the LAST table entry             ;Example=8012300Ch
  D6_BCR  - number of list entries                      ;Example=00000004h
  D6_CHCR - control bits (should be 11000002h)          ;Example=11000002h
Each entry has a size of 00h words (upper 8bit), and a pointer to the previous entry (lower 24bit). With the above Example values, the generated table would look like so:
  [80123000h]=00FFFFFFh  ;1st entry, points to end code (xxFFFFFFh)
  [80123004h]=00123000h  ;2nd entry, points to 1st entry
  [80123008h]=00123004h  ;3rd entry, points to 2nd entry
  [8012300Ch]=00123008h  ;last entry, points to 3rd entry (table entrypoint)

Inserting Entries (Passing GTE data to the OT) (by software)
The GTE commands AVSZ3 and AVSZ4 can be used to calculate the Average Z coordinates of a polygon (based on its three or four Z coordinates). The result is returned as a 16bit Z value in GTE register OTZ, the commands do also allow to divide the result, to make it less than 16bit (the full 16bit would require an OT of 256KBytes - for the EMPTY table, which would be a waste of memory, and which would slowdown the DMA2/DMA6 operations) (on the other hand, a smaller table means less depth resolution).
  [PacketAddr+0]      = [80123000h+OTZ*4] + (N SHL 24)   <--internal link chain
  [PacketAddr+4..N*4] = GP0 Command(s) and Parameters    <--data (send to GP0)
  [80123000h+OTZ*4]   = PacketAddr AND FFFFFFh           <--internal link chain
If there's been already an entry (at the same OTZ index), then the new polygon will be processed first (ie. it will appear "behind" of the old entry).
Not sure if the packet size must be limited to max N=16 words (ie. as for the DMA2-continous block size) (due to GP0 FIFO size limits)?

Sending the OT to the CPU (via DMA2-linked-list mode)
  1 - Wait until GPU is ready to receive commands ;GPUSTAT.28
  2 - Enable DMA channel 2                  ;DPCR
  3 - Set GPU to DMA cpu->gpu mode          ;[GP1]=04000002h aka GP1(04h)
  3 - Set D2_MADR to the start of the list  ;(LAST Entry) ;Example=80123010h
  4 - Set D2_BCR to zero                    ;(length unused, end at END-CODE)
  5 - Set D2_CHCR to link mode, mem->GPU and dma enable   ;=01000401h


GPU Video Memory (VRAM)

Framebuffer
The framebuffer contains the image that is to be output to the Television Set. The GPU supports 10 resolutions, with 16bit or 24bit per pixel.
  Resolution  16bit      24bit      |  Resolution  16bit      24bit
  256x240     120Kbytes  180Kbytes  |  256x480     240Kbytes  360Kbytes
  320x240     150Kbytes  225Kbytes  |  320x480     300Kbytes  450Kbytes
  368x240     xx0Kbytes  xx0Kbytes  |  368x480     xx0Kbytes  xx0Kbytes
  512x240     240Kbytes  360Kbytes  |  512x480     480Kbytes  720Kbytes
  640x240     300Kbytes  450Kbytes  |  640x480     600Kbytes  900Kbytes
Note: In most cases, you'll need TWO framebuffers (one being displayed, and used as rendering target) (unless you are able to draw the whole new image during vblank, or unless when using single-layer 2D graphics). So, resolutions that occupy more than 512K would exceed the available 1MB VRAM when using 2 buffers. Also, high resolutions mean higher rendering load, and less texture memory.
  15bit Direct Display (default) (works with polygons, lines, rectangles)
  0-4   Red       (0..31)
  5-9   Green     (0..31)
  10-14 Blue      (0..31)
  15    Mask flag (0=Normal, 1=Do not allow to overwrite this pixel)
  24bit Direct Display (works ONLY with direct vram transfers)
  0-7    Red      (0..255)
  8-15   Green    (0..255)
  16-23  Blue     (0..255)
Note: The 24bit pixels occupy 3 bytes (not 4 bytes with unused MSBs), so each 6 bytes contain two 24bit pixels. The 24bit display mode works only with VRAM transfer commands like GP0(A0h); the rendering commands GP0(20h..7Fh) cannot output 24bit data. Ie. 24bit mode is used mostly for MDEC videos (and some 2D games like Heart of Darkness).

Texture Bitmaps
A texture is an image put on a polygon or sprite. The data of a texture can be stored in 3 different modes:
  16bit Texture (Direct Color)             ;(One 256x256 page = 128Kbytes)
  0-4   Red       (0..31)         ;\Color 0000h        = Fully-Transparent
  5-9   Green     (0..31)         ; Color 0001h..7FFFh = Non-Transparent
  10-14 Blue      (0..31)         ; Color 8000h..FFFFh = Semi-Transparent (*)
  15    Semi Transparency Flag    ;/(*) or Non-Transparent for opaque commands
  8bit Texture (256 Color Palette)         ;(One 256x256 page = 64Kbytes)
  0-7   Palette index for 1st pixel (left)
  8-15  Palette index for 2nd pixel (right)
  4bit Texture (16 Color Palette)          ;(One 256x256 page = 32Kbytes)
  0-3   Palette index for 1st pixel (left)
  4-7   Palette index for 2nd pixel (middle/left)
  8-11  Palette index for 3rd pixel (middle/right)
  12-15 Palette index for 4th pixel (right)
A Texture Page is a 256x256 texel region in VRAM (the Polygon rendering commands are using Texcoords with 8bit X,Y coordinates, so polygons cannot use textures bigger than 256x256) (the Rectangle rendering commands with width/height parameters could theoretically use larger textures, but the hardware clips their texture coordinates to 8bit, too).
The GP0(E2h) Texture Window (aka Texture Repeat) command can be used to reduce the texture size to less than 256x256 texels.
The Texture Pages can be located in the frame buffer on X multiples of 64 halfwords and Y multiples of 256 lines.

Texture Palettes - CLUT (Color Lookup Table)
The clut is a the table where the colors are stored for the image data in the CLUT modes. The pixels of those images are used as indexes to this table. The clut is arranged in the frame buffer as a 256x1 image for the 8bit clut mode, and a 16x1 image for the 4bit clut mode.
  0-4   Red       (0..31)         ;\Color 0000h        = Fully-Transparent
  5-9   Green     (0..31)         ; Color 0001h..7FFFh = Non-Transparent
  10-14 Blue      (0..31)         ; Color 8000h..FFFFh = Semi-Transparent (*)
  15    Semi Transparency Flag    ;/(*) or Non-Transparent for opaque commands
The clut data can be arranged in the frame buffer at X multiples of 16 (X=0,16,32,48,etc) and anywhere in the Y range of 0-511.

Texture Color Black Limitations
On the PSX, texture color 0000h is fully-transparent, that means textures cannot contain Black pixels. However, in some cases, Color 8000h (Black with semi-transparent flag) can be used, depending on the rendering command:
  opaque command, eg. GP0(24h)      --> 8000h = Non-Transparent Black
  semi-transp command, eg. GP0(26h) --> 8000h = Semi-Transparent Black
So, with semi-transparent rendering commands, it isn't possible to use Non-Transparent Black pixels in textures, the only workaround is to use colors like 0001h (dark red) or 0400h (dark blue). However, due to the PSX's rather steeply increasing intensity ramp, these colors are clearly visible to be brighter than black.

RGB Intensity Notes
The Playstations RGB values aren't linear to normal RGB values (as used on PCs). The min/max values are of course the same, but the medium values differ:
  Intensity        PC      PSX
  Minimum          0       0
  Medium (circa)   16      8
  Maximum          31      31
Ie. on the PSX, the intensity increases steeply from 0 to 15, and less steeply from 16 to 31.


GPU Texture Caching

The GPU has 2 Kbyte Texture Cache
The Texture Cache is (maybe) also used for CLUT data - or is there a separate CLUT Cache - or is the CLUT uncached - but that'd be trash?

If polygons with texture are displayed, the GPU needs to read these from the frame buffer. This slows down the drawing process, and as a result the number of polygons that can be drawn in a given timespan. To speed up this process the GPU is equipped with a texture cache, so a given piece of texture needs not to be read multiple times in succession.
The texture cache size depends on the color mode used for the textures.
In 4 bit CLUT mode it has a size of 64x64, in 8 bit CLUT it's 32x64 and in 15bitDirect is 32x32. A general speed up can be achieved by setting up textures according to these sizes. For further speed gain a more precise knowledge of how the cache works is necessary.

Cache blocks
The texture page is divided into non-overlapping cache blocks, each of a unit size according to color mode. These cache blocks are tiled within the texture page.
  +-----+-----+-----+--
  |cache|     |     |
  |block|     |
  |    0|   1 |    2   ..
  +-----+-----+--
  |..   |     |

Cache entries
Each cache block is divided into 256 cache entries, which are numbered sequentially, and are 8 bytes wide. So a cache entry holds 16 4bit clut pixels 8 8bit clut pixels, or 4 15bitdirect pixels.
  4bit and 8bit clut:        15bitdirect:
  +----+----+----+----+     +----+----+----+----+----+----+----+----+
  |   0|   1|   2|   3|     |   0|   1|   2|   3|   4|   5|   6|   7|
  +----+----+----+----+     +----+----+----+----+----+----+----+----+
  |   4|   5|   6|   7|     |   8|   9|   a|   b|   c|   d|   e|   f|
  +----+----+----+----+     +----+----+----+----+----+----+----+----+
  |   8|   9|  ..           |  10|  11|  ..
  +----+----+--             +----+----+--
  |   c|  ..|               |  18|  ..|
  +----+--                  +----+--
  |  ..                     |  ..
The cache can hold only one cache entry by the same number, so if f.e. a piece of texture spans multiple cache blocks and it has data on entry 9 of block 1, but also on entry 9 of block 2, these cannot be in the cache at once.


GPU Timings

Video Clock
The PSone/PAL video clock is the cpu clock multiplied by 11/7.
  CPU Clock   =  33.868800MHz (44100Hz*300h)
  Video Clock =  53.222400MHz (44100Hz*300h*11/7)
For other PSX/PSone PAL/NTSC variants, see:
Pinouts - CLK Pinouts

Vertical Timings
  PAL:  314 scanlines per frame (13Ah)
  NTSC: 263 scanlines per frame (107h)
Timer1 can use the hblank signal as input, allowing to count scanlines (unless the display is configured to 0 pixels width, which would cause an endless hblank). The hblank signal is generated even during vertical blanking/retrace.

Horizontal Timings
  PAL:  3406 video cycles per scanline (or 3406.1 or so?)
  NTSC: 3413 video cycles per scanline (or 3413.6 or so?)
Dotclocks:
  PSX.256-pix Dotclock =  5.322240MHz (44100Hz*300h*11/7/10)
  PSX.320-pix Dotclock =  6.652800MHz (44100Hz*300h*11/7/8)
  PSX.368-pix Dotclock =  7.603200MHz (44100Hz*300h*11/7/7)
  PSX.512-pix Dotclock = 10.644480MHz (44100Hz*300h*11/7/5)
  PSX.640-pix Dotclock = 13.305600MHz (44100Hz*300h*11/7/4)
  Namco GunCon 385-pix =  8.000000MHz (from 8.00MHz on lightgun PCB)
Dots per scanline are, depending on horizontal resolution, and on PAL/NTSC:
  320pix/PAL: 3406/8  = 425.75 dots     320pix/NTSC: 3413/8  = 426.625 dots
  640pix/PAL: 3406/4  = 851.5 dots      640pix/NTSC: 3413/4  = 853.25 dots
  256pix/PAL: 3406/10 = 340.6 dots      256pix/NTSC: 3413/10 = 341.3 dots
  512pix/PAL: 3406/5  = 681.2 dots      512pix/NTSC: 3413/5  = 682.6 dots
  368pix/PAL: 3406/7  = 486.5714 dots   368pix/NTSC: 3413/7  = 487.5714 dots
Timer0 can use the dotclock as input, however, the Timer0 input "ignores" the fractional portions (in most cases, the values are rounded down, ie. with 340.6 dots/line, the timer increments only 340 times/line; the only value that is rounded up is 425.75 dots/line) (for example, due to the rounding, the timer isn't running exactly twice as fast in 512pix/PAL mode than in 256pix/PAL mode). The dotclock signal is generated even during horizontal/vertical blanking/retrace.

Frame Rates
  PAL:  53.222400MHz/314/3406 = ca. 49.76 Hz (ie. almost 50Hz)
  NTSC: 53.222400MHz/263/3413 = ca. 59.29 Hz (ie. almost 60Hz)

Note
Above values include "hidden" dots and scanlines (during horizontal and vertical blanking/retrace).


GPU (MISC)

GP0(20h..7Fh) - Render Command Bits
  0-23  Color for (first) Vertex                   (Not for Raw-Texture)
  24    Texture Mode      (0=Blended, 1=Raw)       (Textured-Polygon/Rect only)
  25    Semi Transparency (0=Off, 1=On)            (All Render Types)
  26    Texture Mapping   (0=Off, 1=On)            (Polygon/Rectangle only)
  27-28 Rect Size   (0=Var, 1=1x1, 2=8x8, 3=16x16) (Rectangle only)
  27    Num Vertices      (0=Triple, 1=Quad)       (Polygon only)
  27    Num Lines         (0=Single, 1=Poly)       (Line only)
  28    Shading           (0=Flat, 1=Gouroud)      (Polygon/Line only)
  29-31 Primitive Type    (1=Polygon, 2=Line, 3=Rectangle)

Perspective (in-)correct Rendering
The PSX doesn't support perspective correct rendering: Assume a polygon to be rotated so that it's right half becomes more distant to the camera, and it's left half becomes closer. Due to the GTE's perspective division, the right half should appear smaller than the left half.
The GPU supports only linear interpolations for rendering - that is correct concerning the X and Y screen coordinates (which are still linear to each other, even after perspective division, since both are divided by the same value).
However, texture coordinates (and Gouraud shaded colors) are NOT linear to the screen coordinates, and so, the linear interpolated PSX graphics are often looking rather distorted, that especially for textures that contain straight lines. For color shading the problem is less obvious (since shading is kinda blurry anyways).

Perspective correct Rendering
For perspective correct rendering, the polygon's Z-coordinates would be needed to be passed from the GTE to the GPU, and, the GPU would then need to use that Z-coordinates to "undo" the perspective division for each pixel (that'd require some additional memory, and especially a powerful division unit, which isn't implemented in the hardware).
As a workaround, you can try to reduce the size of your polygons (the interpolation errors increase in the center region of larger polygons). Reducing the size would be only required for polygons that occupy a larger screen region (which may vary depending on the distance to the camera).
Ie. you may check the size AFTER perspective division, if it's too large, then break it into smaller parts (using the original coordinates, NOT the screen coordinates), and then pass the fragments to the GTE another time.
Again, perspective correction would be relevant only for certain textures (not for randomly dithered textures like sand, water, fire, grass, and not for untextured polygons, and of course not for 2D graphics, so you may exclude those from size reduction).

24bit RGB to 15bit RGB Dithering (enabled in Texpage attribute)
For dithering, VRAM is broken to 4x4 pixel blocks, depending on the location in that 4x4 pixel region, the corresponding dither offset is added to the 8bit R/G/B values, the result is saturated to +00h..+FFh, and then divided by 8, resulting in the final 5bit R/G/B values.
  -4  +0  -3  +1   ;\dither offsets for first two scanlines
  +2  -2  +3  -1   ;/
  -3  +1  -4  +0   ;\dither offsets for next two scanlines
  +3  -1  +2  -2   ;/(same as above, but shifted two pixels horizontally)
POLYGONs (triangles/quads) are dithered ONLY if they do use gouraud shading or texture blending.
LINEs are dithered (no matter if they are mono or do use gouraud shading).
RECTs are NOT dithered (no matter if they do use texture blending).

Shading information
"Texture RGB values control the brightness of the individual colors ($00-$7f). A value of $80 in a color will take the former value as data." (What...? probably means the "double brightness" effect... or does it want to tell that ALL colors of 80h..FFh have only single brightness.. rather than reaching double brightness at FFh...?)

Shading
The GPU has a shading function, which will scale the color of a primitive to a specified brightness. There are 2 shading modes: Flat shading, and gouraud shading. Flat shading is the mode in which one brightness value is specified for the entire primitive. In Gouraud shading mode, a different brightness value can be given for each vertex of a primitive, and the brightness between these points is automatically interpolated.

Semi Transparency
When semi transparency is set for a pixel, the GPU first reads the pixel it wants to write to, and then calculates the color it will write from the 2 pixels according to the semitransparency mode selected. Processing speed is lower in this mode because additional reading and calculating are necessary. There are 4 semitransparency modes in the GPU.
  B=Back  (the old pixel read from the image in the frame buffer)
  F=Front (the new halftransparent pixel)
  * 0.5 x B + 0.5 x F    ;aka B/2+F/2
  * 1.0 x B + 1.0 x F    ;aka B+F
  * 1.0 x B - 1.0 x F    ;aka B-F
  * 1.0 x B +0.25 x F    ;aka B+F/4

Draw to display enable
This will enable/disable any drawing to the area that is currently displayed. Not sure yet WHY one should want to disable that?
Also not sure HOW and IF it works... the SIZE of the display area is implied by the screen size - which is horizontally counted in CLOCK CYCLES, so, to obtain the size in PIXELS, the hardware would require to divide that value by the number of cycles per pixel, depending on the current resolution...?


Geometry Transformation Engine (GTE)

GTE Overview
GTE Registers
GTE Saturation
GTE Opcode Summary
GTE Coordinate Calculation Commands
GTE General Purpose Calculation Commands
GTE Color Calculation Commands
GTE Division Inaccuracy


GTE Overview

GTE Operation
The GTE doesn't have any memory or I/O ports mapped to the CPU memory bus, instead, it's solely accessed via coprocessor opcodes:
  mov  cop0r12,rt          ;-enable/disable COP2 (GTE) via COP0 status register
  mov  cop2r0-63,rt        ;\write parameters to GTE registers
  mov  cop2r0-31,[rs+imm]  ;/
  mov  cop2cmd,imm25       ;-issue GTE command
  mov  rt,cop2r0-63        ;\read results from GTE registers
  mov  [rs+imm],cop2r0-31  ;/
  jt   cop2flg,dest        ;-jump never  ;\implemented (no exception), but,
  jf   cop2flg,dest        ;-jump always ;/flag seems to be always "false"
GTE (memory-?) load and store instructions have a delay of 2 instructions, for any GTE commands or operations accessing that register. Any? That's wrong!
GTE instructions and functions should not be used in
  - Delay slots of jumps and branches
  - Event handlers or interrupts (sounds like nonsense?) (need push/pop though)
If an instruction that reads a GTE register or a GTE command is executed before the current GTE command is finished, the CPU will hold until the instruction has finished. The number of cycles each GTE instruction takes is shown in the command list.

GTE Command Encoding (COP2 imm25 opcodes)
  31-25  Must be 0100101b for "COP2 imm25" instructions
  20-24  Fake GTE Command Number (00h..1Fh) (ignored by hardware)
  19     sf - Shift Fraction in IR registers (0=No fraction, 1=12bit fraction)
  17-18  MVMVA Multiply Matrix    (0=Rotation. 1=Light, 2=Color, 3=Reserved)
  15-16  MVMVA Multiply Vector    (0=V0, 1=V1, 2=V2, 3=IR/long)
  13-14  MVMVA Translation Vector (0=TR, 1=BK, 2=FC/Bugged, 3=None)
  11-12  Always zero                        (ignored by hardware)
  10     lm - Saturate IR1,IR2,IR3 result (0=To -8000h..+7FFFh, 1=To 0..+7FFFh)
  6-9    Always zero                        (ignored by hardware)
  0-5    Real GTE Command Number (00h..3Fh) (used by hardware)
The MVMVA bits are used only by the MVMVA opcode (the bits are zero for all other opcodes).
The "sf" and "lm" bits are usually fixed (either set, or cleared, depending on the command) (for MVMVA, the bits are variable) (also, "sf" can be changed for some commands like SQR) (although they are usually fixed for most other opcodes, changing them might have some effect on some/all opcodes)?

GTE Data Register Summary (cop2r0-31)
  cop2r0-1   3xS16 VXY0,VZ0              Vector 0 (X,Y,Z)
  cop2r2-3   3xS16 VXY1,VZ1              Vector 1 (X,Y,Z)
  cop2r4-5   3xS16 VXY2,VZ2              Vector 2 (X,Y,Z)
  cop2r6     4xU8  RGBC                  Color/code value
  cop2r7     1xU16 OTZ                   Average Z value (for Ordering Table)
  cop2r8     1xS16 IR0                   16bit Accumulator (Interpolate)
  cop2r9-11  3xS16 IR1,IR2,IR3           16bit Accumulator (Vector)
  cop2r12-15 6xS16 SXY0,SXY1,SXY2,SXYP   Screen XY-coordinate FIFO  (3 stages)
  cop2r16-19 4xU16 SZ0,SZ1,SZ2,SZ3       Screen Z-coordinate FIFO   (4 stages)
  cop2r20-22 12xU8 RGB0,RGB1,RGB2        Color CRGB-code/color FIFO (3 stages)
  cop2r23    4xU8  (RES1)                Prohibited
  cop2r24    1xS32 MAC0                  32bit Maths Accumulators (Value)
  cop2r25-27 3xS32 MAC1,MAC2,MAC3        32bit Maths Accumulators (Vector)
  cop2r28-29 1xU15 IRGB,ORGB             Convert RGB Color (48bit vs 15bit)
  cop2r30-31 2xS32 LZCS,LZCR             Count Leading-Zeroes/Ones (sign bits)

GTE Control Register Summary (cop2r32-63)
  cop2r32-36 9xS16 RT11RT12,..,RT33 Rotation matrix     (3x3)        ;cnt0-4
  cop2r37-39 3x 32 TRX,TRY,TRZ      Translation vector  (X,Y,Z)      ;cnt5-7
  cop2r40-44 9xS16 L11L12,..,L33    Light source matrix (3x3)        ;cnt8-12
  cop2r45-47 3x 32 RBK,GBK,BBK      Background color    (R,G,B)      ;cnt13-15
  cop2r48-52 9xS16 LR1LR2,..,LB3    Light color matrix source (3x3)  ;cnt16-20
  cop2r53-55 3x 32 RFC,GFC,BFC      Far color           (R,G,B)      ;cnt21-23
  cop2r56-57 2x 32 OFX,OFY          Screen offset       (X,Y)        ;cnt24-25
  cop2r58 BuggyU16 H                Projection plane distance.       ;cnt26
  cop2r59      S16 DQA              Depth queing parameter A (coeff) ;cnt27
  cop2r60       32 DQB              Depth queing parameter B (offset);cnt28
  cop2r61-62 2xS16 ZSF3,ZSF4        Average Z scale factors          ;cnt29-30
  cop2r63      U20 FLAG             Returns any calculation errors   ;cnt31


GTE Registers

Note in some functions format is different from the one that's given here.

Matrix Registers
  Rotation matrix (RT)   Light matrix (LLM)     Light Color matrix (LCM)
  cop2r32.lsbs=RT11      cop2r40.lsbs=L11       cop2r48.lsbs=LR1
  cop2r32.msbs=RT12      cop2r40.msbs=L12       cop2r48.msbs=LR2
  cop2r33.lsbs=RT13      cop2r41.lsbs=L13       cop2r49.lsbs=LR3
  cop2r33.msbs=RT21      cop2r41.msbs=L21       cop2r49.msbs=LG1
  cop2r34.lsbs=RT22      cop2r42.lsbs=L22       cop2r50.lsbs=LG2
  cop2r34.msbs=RT23      cop2r42.msbs=L23       cop2r50.msbs=LG3
  cop2r35.lsbs=RT31      cop2r43.lsbs=L31       cop2r51.lsbs=LB1
  cop2r35.msbs=RT32      cop2r43.msbs=L32       cop2r51.msbs=LB2
  cop2r36     =RT33      cop2r44     =L33       cop2r52     =LB3
Each element is 16bit (1bit sign, 3bit integer, 12bit fraction). Reading the last elements (RT33,L33,LB3) returns the 16bit value sign-expanded to 32bit.

Translation Vector (TR) (Input, R/W?)
  cop2r37 (cnt5) - TRX - Translation vector X (R/W?)
  cop2r38 (cnt6) - TRY - Translation vector Y (R/W?)
  cop2r39 (cnt7) - TRZ - Translation vector Z (R/W?)
Each element is 32bit (1bit sign, 31bit integer).
Used only for MVMVA, RTPS, RTPT commands.

Background Color (BK) (Input?, R/W?)
  cop2r45 (cnt13) - RBK - Background color red component
  cop2r46 (cnt14) - GBK - Background color green component
  cop2r47 (cnt15) - BBK - Background color blue component
Each element is 32bit (1bit sign, 19bit integer, 12bit fraction).

Far Color (FC) (Input?) (R/W?)
  cop2r53 (cnt21) - RFC - Far color red component
  cop2r54 (cnt22) - GFC - Far color green component
  cop2r55 (cnt23) - BFC - Far color blue component
Each element is 32bit (1bit sign, 27bit integer, 4bit fraction).

Screen Offset and Distance (Input, R/W?)
  cop2r56 (cnt24) - OFX - Screen offset X
  cop2r57 (cnt25) - OFY - Screen offset Y
  cop2r58 (cnt26) - H   - Projection plane distance
  cop2r59 (cnt27) - DQA - Depth queing parameter A.(coeff.)
  cop2r60 (cnt28) - DQB - Depth queing parameter B.(offset.)
The X and Y values are each 32bit (1bit sign, 15bit integer, 16bit fraction).
The H value is 16bit unsigned (0bit sign, 16bit integer, 0bit fraction). BUG: When reading the H register, the hardware does accidently <sign-expand> the <unsigned> 16bit value (ie. values +8000h..+FFFFh are returned as FFFF8000h..FFFFFFFFh) (this bug applies only to "mov rd,cop2r58" opcodes; the actual calculations via RTPS/RTPT opcodes are working okay).
The DQA value is only 16bit (1bit sign, 7bit integer, 8bit fraction).
The DQB value is 32bit (1bit sign, 7bit integer, 24bit? fraction).
Used only for RTPS/RTPT commands.

Average Z Registers (ZSF3/ZSF4=Input, R/W?) (OTZ=Result, R)
  cop2r61 (cnt29) ZSF3 |  0|ZSF3 1,3,12| Z3 average scale factor (normally 1/3)
  cop2r62 (cnt30) ZSF4 |  0|ZSF4 1,3,12| Z4 average scale factor (normally 1/4)
  cop2r7       OTZ (R) |   |OTZ 0,15, 0| Average Z value (for Ordering Table)
Used only for AVSZ3/AVSZ4 commands.

Screen XYZ Coordinate FIFOs
  cop2r12 - SXY0  rw|SY0 1,15, 0|SX0 1,15, 0| Screen XY fifo (older)
  cop2r13 - SXY1  rw|SY1 1,15, 0|SX1 1,15, 0| Screen XY fifo (old)
  cop2r14 - SXY2  rw|SY2 1,15, 0|SX2 1,15, 0| Screen XY fifo (new)
  cop2r15 - SXYP  rw|SYP 1,15, 0|SXP 1,15, 0| SXY2-mirror with move-on-write
  cop2r16 - SZ0   rw|          0|SZ0 0,16, 0| Screen Z fifo (oldest)
  cop2r17 - SZ1   rw|          0|SZ1 0,16, 0| Screen Z fifo (older)
  cop2r18 - SZ2   rw|          0|SZ2 0,16, 0| Screen Z fifo (old)
  cop2r19 - SZ3   rw|          0|SZ3 0,16, 0| Screen Z fifo (new)
SX,SY,SZ are used as Output for RTPS/RTPT. Additionally, SX,SY are used as Input for NCLIP, and SZ is used as Input for AVSZ3/AVSZ4.
The SZn Fifo has 4 stages (required for AVSZ4 command), the SXYn Fifo has only 3 stages, and a special mirrored register: SXYP is a mirror of SXY2, the difference is that writing to SXYP moves SXY2/SXY1 to SXY1/SXY0, whilst writing to SXY2 (or any other SXYn or SZn registers) changes only the written register, but doesn't move any other Fifo entries.

16bit Vectors (R/W)
  Vector 0 (V0)         Vector 1 (V1)       Vector 2 (V2)       Vector 3 (IR)
  cop2r0.lsbs - VX0     cop2r2.lsbs - VX1   cop2r4.lsbs - VX2   cop2r9  - IR1
  cop2r0.msbs - VY0     cop2r2.msbs - VY1   cop2r4.msbs - VY2   cop2r10 - IR2
  cop2r1      - VZ0     cop2r3      - VZ1   cop2r5      - VZ2   cop2r11 - IR3
All elements are signed 16bit. The IRn and VZn elements occupy a whole 32bit register, reading these registers returns the 16bit value sign-expanded to 32bit. Note: IRn can be also indirectly accessed via IRGB/ORGB registers.

Color Register and Color FIFO
  cop2r6  - RGBC  rw|CODE |B    |G    |R    | Color/code
  cop2r20 - RGB0  rw|CD0  |B0   |G0   |R0   | Characteristic color fifo.
  cop2r21 - RGB1  rw|CD1  |B1   |G1   |R1   |
  cop2r22 - RGB2  rw|CD2  |B2   |G2   |R2   |
  cop2r23 - (RES1)  |                       | Prohibited
RES1 seems to be unused... looks like an unused Fifo stage... RES1 is read/write-able... unlike SXYP (for SXYn Fifo) it does not mirror to RGB2, nor does it have a move-on-write function...

Interpolation Factor
  cop2r8   IR0   rw|Sign       |IR0 1, 3,12| Intermediate value 0.
Used as Output for RTPS/RTPT, and as Input for various commands.

XX...
  cop2r24  MAC0  rw|MAC0 1,31,0            | Sum of products value 0

XX...
  cop2r25  MAC1  rw|MAC1 1,31,0            | Sum of products value 1
  cop2r26  MAC2  rw|MAC2 1,31,0            | Sum of products value 2
  cop2r27  MAC3  rw|MAC3 1,31,0            | Sum of products value 3

cop2r28 - IRGB - Color conversion Input (R/W)
Expands 5:5:5 bit RGB (range 0..1Fh) to 16:16:16 bit RGB (range 0000h..0F80h).
  0-4    Red   (0..1Fh) (R/W)  ;multiplied by 80h, and written to IR1
  5-9    Green (0..1Fh) (R/W)  ;multiplied by 80h, and written to IR2
  10-14  Blue  (0..1Fh) (R/W)  ;multiplied by 80h, and written to IR3
  15-31  Not used (always zero) (Read only)
After writing to IRGB, the result can be read from IR3 after TWO nop's, and from IR1,IR2 after THREE nop's (for uncached code, ONE nop would work). When using IR1,IR2,IR3 as parameters for GTE commands, similar timing restrictions might apply... depending on when the specific commands use the parameters?

cop2r29 - ORGB - Color conversion Output (R)
Collapses 16:16:16 bit RGB (range 0000h..0F80h) to 5:5:5 bit RGB (range 0..1Fh). Negative values (8000h..FFFFh/80h) are saturated to 00h, large positive values (1000h..7FFFh/80h) are saturated to 1Fh, there are no overflow or saturation flags set in cop2r63 though.
  0-4    Red   (0..1Fh) (R)  ;IR1 divided by 80h, saturated to +00h..+1Fh
  5-9    Green (0..1Fh) (R)  ;IR2 divided by 80h, saturated to +00h..+1Fh
  10-14  Blue  (0..1Fh) (R)  ;IR3 divided by 80h, saturated to +00h..+1Fh
  15-31  Not used (always zero) (Read only)
Any changes to IR1,IR2,IR3 are reflected to this register (and, actually also to IRGB) (ie. ORGB is simply a read-only mirror of IRGB).

cop2r30 - LZCS - Count Leading Bits Source data (R/W)
cop2r31 - LZCR - Count Leading Bits Result (R)
Reading LZCR returns the leading 0 count of LZCS if LZCS is positive and the leading 1 count of LZCS if LZCS is negative. The results are in range 1..32.

cop2r63 (cnt31) - FLAG - Returns any calculation errors.
See GTE Saturation chapter.


GTE Saturation

Maths overflows are indicated in FLAG register. In most cases, the result is saturated to MIN/MAX values (except MAC0,MAC1,MAC2,MAC3 which aren't saturated). For IR1,IR2,IR3 many commands allow to select the MIN value via "lm" bit of the GTE opcode (though not all commands, RTPS/RTPT always act as if lm=0).

cop2r63 (cnt31) - FLAG - Returns any calculation errors.
  31   Error Flag (Bit30..23, and 18..13 ORed together) (Read only)
  30   MAC1 Result positive 44bit overflow (max +7FFFFFFFFFFh) ;\triggered
  29   MAC2 Result positive 44bit overflow (max +7FFFFFFFFFFh) ; during
  28   MAC3 Result positive 44bit overflow (max +7FFFFFFFFFFh) ; calculations
  27   MAC1 Result negative 44bit overflow (min -80000000000h) ;
  26   MAC2 Result negative 44bit overflow (min -80000000000h) ;
  25   MAC3 Result negative 44bit overflow (min -80000000000h) ;/
  24   IR1 saturated to +0000h..+7FFFh (lm=1) or to -8000h..+7FFFh (lm=0)
  23   IR2 saturated to +0000h..+7FFFh (lm=1) or to -8000h..+7FFFh (lm=0)
  22   IR3 saturated to +0000h..+7FFFh (lm=1) or to -8000h..+7FFFh (lm=0)
  21   Color-FIFO-R saturated to +00h..+FFh
  20   Color-FIFO-G saturated to +00h..+FFh
  19   Color-FIFO-B saturated to +00h..+FFh
  18   SZ3 or OTZ saturated to +0000h..+FFFFh
  17   Divide overflow. RTPS/RTPT division result saturated to max=1FFFFh
  16   MAC0 Result positive 32bit overflow (max +7FFFFFFFh)    ;\triggered on
  15   MAC0 Result negative 32bit overflow (min -80000000h)    ;/final result
  14   SX2 saturated to -0400h..+03FFh
  13   SY2 saturated to -0400h..+03FFh
  12   IR0 saturated to +0000h..+1000h
  0-11 Not used (always zero) (Read only)
Bit30-12 are read/write-able, ie. they can be set/reset by software, however, that's normally not required - all bits are automatically reset at the begin of a new GTE command.
44bit MAC1-3 overflows can occur during calculations (positive+positive+negative can trigger positive overflow on 1st addition, and negative overflow on 2nd addition, in that case the final result is okay, but both overflow flags are set).
32bit MAC0 overflows occur if the final result exceeds 32bit (internally, the register seems to be bigger than 32bit).
IR1-3 overflows occur when lower 32bit of MAC1-3 exceeds the desired range (MAC1-3 are 44bit wide, but the upper 12bit are ignored for IR1-3).
Bit31 is apparently intended for RTPS/RTPT commands, since it triggers only on flags that are affected by these two commands, but even for that commands it's totally useless since one could as well check if FLAG is nonzero.
Note: Writing 32bit values to 16bit GTE registers by software does not trigger any overflow/saturation flags (and does not do any saturation), eg. writing 12008900h (positive 32bit) to a signed 16bit register sets that register to FFFF8900h (negative 16bit).


GTE Opcode Summary

GTE Command Summary (sorted by Real Opcode bits) (bit0-5)
  Opc  Name      Clk Expl.
  00h  -             N/A (modifies similar registers than RTPS...)
  01h  RTPS      15  Perspective Transformation single
  0xh  -             N/A
  06h  NCLIP     8   Normal clipping
  0xh  -             N/A
  0Ch  OP(sf)    6   Outer product of 2 vectors
  0xh  -             N/A
  10h  DPCS      8   Depth Cueing single
  11h  INTPL     8   Interpolation of a vector and far color vector
  12h  MVMVA(..) 8   Multiply vector by matrix and add vector (see below)
  13h  NCDS      19  Normal color depth cue single vector
  14h  CDP       13  Color Depth Que
  15h  -             N/A
  16h  NCDT      44  Normal color depth cue triple vectors
  1xh  -             N/A
  1Bh  NCCS      17  Normal Color Color single vector
  1Ch  CC        11  Color Color
  1Dh  -             N/A
  1Eh  NCS       14  Normal color single
  1Fh  -             N/A
  20h  NCT       30  Normal color triple
  2xh  -             N/A
  28h  SQR(sf)   5   Square of vector IR
  29h  DCPL      8   Depth Cue Color light
  2Ah  DPCT      17  Depth Cueing triple (should be fake=08h, but isn't)
  2xh  -             N/A
  2Dh  AVSZ3     5   Average of three Z values
  2Eh  AVSZ4     6   Average of four Z values
  2Fh  -             N/A
  30h  RTPT      23  Perspective Transformation triple
  3xh  -             N/A
  3Dh  GPF(sf)   5   General purpose interpolation
  3Eh  GPL(sf)   5   General purpose interpolation with base
  3Fh  NCCT      39  Normal Color Color triple vector
Unknown if/what happens when using the "N/A" opcodes?

GTE Command Summary (sorted by Fake Opcode bits) (bit20-24)
The fake opcode number in bit20-24 has absolutely no effect on the hardware, it seems to be solely used to (or not to) confuse developers. Having the opcodes sorted by their fake numbers gives a more or less well arranged list:
  Fake Name      Clk Expl.
  00h  -             N/A
  01h  RTPS      15  Perspective Transformation single
  02h  RTPT      23  Perspective Transformation triple
  03h  -             N/A
  04h  MVMVA(..) 8   Multiply vector by matrix and add vector (see below)
  05h  -             N/A
  06h  DCPL      8   Depth Cue Color light
  07h  DPCS      8   Depth Cueing single
  08h  DPCT      17  Depth Cueing triple (should be fake=08h, but isn't)
  09h  INTPL     8   Interpolation of a vector and far color vector
  0Ah  SQR(sf)   5   Square of vector IR
  0Bh  -             N/A
  0Ch  NCS       14  Normal color single
  0Dh  NCT       30  Normal color triple
  0Eh  NCDS      19  Normal color depth cue single vector
  0Fh  NCDT      44  Normal color depth cue triple vectors
  10h  NCCS      17  Normal Color Color single vector
  11h  NCCT      39  Normal Color Color triple vector
  12h  CDP       13  Color Depth Que
  13h  CC        11  Color Color
  14h  NCLIP     8   Normal clipping
  15h  AVSZ3     5   Average of three Z values
  16h  AVSZ4     6   Average of four Z values
  17h  OP(sf)    6   Outer product of 2 vectors
  18h  -             N/A
  19h  GPF(sf)   5   General purpose interpolation
  1Ah  GPL(sf)   5   General purpose interpolation with base
  1Bh  -             N/A
  1Ch  -             N/A
  1Dh  -             N/A
  1Eh  -             N/A
  1Fh  -             N/A
For the sort-effect, DCPT should use fake=08h, but Sony seems to have accidently numbered it fake=0Fh in their devkit (giving it the same fake number as for NCDT). Also, "Wipeout 2097" accidently uses 0140006h (fake=01h and distorted bit18) instead of 1400006h (fake=14h) for NCLIP.

GTE nonsense SDK command numbers (as from SDK file INLINE_A.H)
  RTPS   macro               dw   $0000007f              (01x40)
  RTPT   macro               dw   $000000bf              (02x40)
  DCPL   macro               dw   $00000dff              (37x40)
  DPCS   macro               dw   $00000e3f              (38x40)
  DPCT   macro               dw   $00000e7f              (39x40)
  INTPL  macro               dw   $00000ebf              (3Ax40)
  NCS    macro               dw   $00000f7f              (3Dx40)
  NCT    macro               dw   $00000fbf              (3Ex40)
  NCDS   macro               dw   $00000fff              (3Fx40)
  NCDT   macro               dw   $0000103f              (40x40)
  NCCS   macro               dw   $0000107f              (41x40)
  NCCT   macro               dw   $000010bf              (42x40)
  CDP    macro               dw   $000010ff              (43x40)
  CC     macro               dw   $0000113f              (44x40)
  NCLIP  macro               dw   $0000117f              (45x40)
  AVSZ3  macro               dw   $000011bf              (46x40)
  AVSZ4  macro               dw   $000011ff              (47x40)
  MVMVA  macro sf,mx,v,cv,lm dw   $000013bf|sf<<25|mx<<23|v<<21|cv<<19|lm<<18
  SQR    macro sf            dw   $000013ff|sf<<25       (4Fx40)
  OP     macro sf            dw   $0000143f|sf<<25       (50x40)
  GPF    macro sf            dw   $0000147f|sf<<25       (51x40)
  GPL    macro sf            dw   $000014bf|sf<<25       (52x40)
"Be warned that the DWORD codes are actually not GTE cop2 instructions. The way you deal with them in the official SDK is that you'd run the compiled object file of your assembly source that uses said macros through DMPSX which translates those DWORD codes into the correct GTE cop2 instructions. I don't know why Sony made it this way."

GTE Clock Cycles
The overall command execution time is shown in the above tables. The MIPS CPU will be automatically halted when trying to read a cop2 register (or executiong another cop2 command) when the GTE command is still busy.
The CPU isn't halted when writing cop2 registers or when trying to use the conditional cop2 jump opcodes.
For whatever reason, trying to read the IRGB/ORGB registers (cop2r28/cop2r29) will add an extra clock cycle (if a GTE command is busy).

Additional Functions
The LZCS/LZCR registers offer a Count-Leading-Zeroes/Leading-Ones function.
The IRGB/ORGB registers allow to convert between 48bit and 15bit RGB colors.
These registers work without needing to send any COP2 commands. However, unlike for commands (which do automatically halt the CPU when needed), one must insert dummy opcodes between writing and reading the registers.


GTE Coordinate Calculation Commands

COP2 0180001h - 15 Cycles - RTPS - Perspective Transformation (single)
COP2 0280030h - 23 Cycles - RTPT - Perspective Transformation (triple)
RTPS performs final Rotate, translate and perspective transformation on vertex V0. Before writing to the FIFOs, the older entries are moved one stage down. RTPT is same as RTPS, but repeats for V1 and V2. The "sf" bit should be usually set.
  IR1 = MAC1 = (TRX*1000h + RT11*VX0 + RT12*VY0 + RT13*VZ0) SAR (sf*12)
  IR2 = MAC2 = (TRY*1000h + RT21*VX0 + RT22*VY0 + RT23*VZ0) SAR (sf*12)
  IR3 = MAC3 = (TRZ*1000h + RT31*VX0 + RT32*VY0 + RT33*VZ0) SAR (sf*12)
  SZ3 = MAC3 SAR ((1-sf)*12)                           ;ScreenZ FIFO 0..+FFFFh
  MAC0=(((H*20000h/SZ3)+1)/2)*IR1+OFX, SX2=MAC0/10000h ;ScrX FIFO -400h..+3FFh
  MAC0=(((H*20000h/SZ3)+1)/2)*IR2+OFY, SY2=MAC0/10000h ;ScrY FIFO -400h..+3FFh
  MAC0=(((H*20000h/SZ3)+1)/2)*DQA+DQB, IR0=MAC0/1000h  ;Depth cueing 0..+1000h
If the result of the "(((H*20000h/SZ3)+1)/2)" division is greater than 1FFFFh, then the division result is saturated to +1FFFFh, and the divide overflow bit in the FLAG register gets set; that happens if the vertex is exceeding the "near clip plane", ie. if it is very close to the camera (SZ3<=H/2), exactly at the camara position (SZ3=0), or behind the camera (negative Z coordinates are saturated to SZ3=0). For details on the division, see:
GTE Division Inaccuracy
For "far plane clipping", one can use the SZ3 saturation flag (MaxZ=FFFFh), or the IR3 saturation flag (MaxZ=7FFFh) (eg. used by Wipeout 2097), or one can compare the SZ3 value with any desired MaxZ value by software.
Note: The command does saturate IR1,IR2,IR3 to -8000h..+7FFFh (regardless of lm bit). When using RTP with sf=0, then the IR3 saturation flag (FLAG.22) gets set <only> if "MAC3 SAR 12" exceeds -8000h..+7FFFh (although IR3 is saturated when "MAC3" exceeds -8000h..+7FFFh).
  IR1,IR2,IR3 are saturated according to lm, but without setting FLAG.22 on IR3
  FLAG.22 is set when SZ3 exceeds -8000h..+7FFFh
  FLAG.18 is set when SZ3 exceeds 0000h..+FFFFh (and SZ3 is staturate as so).
  For RTPT, the DQA,DQB,IR0 calculation is done ONLY for 3rd vertex (V2)
  FLAG.12 is thus set only on last vertex (ie for RTPT: on V2)

COP2 1400006h - 8 Cycles - NCLIP - Normal clipping
  MAC0 = SX0*SY1 + SX1*SY2 + SX2*SY0 - SX0*SY2 - SX1*SY0 - SX2*SY1     ;slow
Or, more efficient, with same result:
  MAC0 = SX0*(SY1-SY2) + SX1*(SY2-SY0) + SX2*(SY0-SY1)                 ;fast
Error FLAG bit31,16,15 can get set if the final result exceeds 32bit range.
The sign of the result indicates whether the polygon coordinates are arranged clockwise or anticlockwise (ie. whether the front side or backside is visible). If the result is zero, then it's neither one (ie. the vertices are all arranged in a straight line; the GPU renders such straight lines as invisble 0 pixel width lines).

COP2 158002Dh - 5 Cycles - AVSZ3 - Average of three Z values (for Triangles)
COP2 168002Eh - 6 Cycles - AVSZ4 - Average of four Z values (for Quads)
  MAC0 = ZSF3*(SZ1+SZ2+SZ3)       ;for AVSZ3
  MAC0 = ZSF4*(SZ0+SZ1+SZ2+SZ3)   ;for AVSZ4
  OTZ  = MAC0/1000h               ;for both (saturated to 0..FFFFh)
Adds three or four Z values together and multiplies them by a fixed point value. The result can be used as index in the GPU's Ordering Table (OT).
GPU Depth Ordering
Commonly used scaling factors are ZSF3=N/30h and ZSF4=N/40h, where "N" is the number of entries in the OT (max 10000h). SZn and OTZ are unsigned 16bit values, for whatever reason ZSFn registers are signed 16bit values (negative values would allow a negative result in MAC0, but would saturate OTZ to zero).


GTE General Purpose Calculation Commands

COP2 0400012h - 8 Cycles - MVMVA(sf,mx,v,cv,lm)
Multiply vector by matrix and vector addition.
  Mx = matrix specified by mx  ;RT/LLM/LCM - Rotation, light or color matrix
  Vx = vector specified by v   ;V0, V1, V2, or [IR1,IR2,IR3]
  Tx = translation vector specified by cv  ;TR or BK or Bugged/FC, or None
Calculation:
  MAC1 = (Tx1*1000h + Mx11*Vx1 + Mx12*Vx2 + Mx13*Vx3) SAR (sf*12)
  MAC2 = (Tx2*1000h + Mx21*Vx1 + Mx22*Vx2 + Mx23*Vx3) SAR (sf*12)
  MAC3 = (Tx3*1000h + Mx31*Vx1 + Mx32*Vx2 + Mx33*Vx3) SAR (sf*12)
  [IR1,IR2,IR3] = [MAC1,MAC2,MAC3]
Multiplies a vector with either the rotation matrix, the light matrix or the color matrix and then adds the translation vector or background color vector.
Vx=2 selects the far color vector (FC), but this vector is not added correctly by the hardware: The MAC calculation is split into two parts, part1 does merely affect the IR saturation flags and resets sum to 0. For example, for MAC1 (and equivalent for MAC2,MAC3):
  IR1 = MAC1 = (Tx1*1000h + Mx11*Vx1) SAR (sf*12)  ;part1, saturate as if lm=0
  IR1 = MAC1 = (Mx12*Vx2 + Mx13*Vx3) SAR (sf*12)   ;part2, saturate by lm
Mx=3 selects a garbage matrix (with elements -R*10h, +R*10h, IR0, RT13, RT13, RT13, RT22, RT22, RT22; whereas, R is LSB of RGBC register).

COP2 0A00428h+sf*80000h - 5 Cycles - SQR(sf) - Square vector
  [MAC1,MAC2,MAC3] = [IR1*IR1,IR2*IR2,IR3*IR3] SHR (sf*12)
  [IR1,IR2,IR3]    = [MAC1,MAC2,MAC3]    ;IR1,IR2,IR3 saturated to max 7FFFh
Calculates the square of a vector. The result is, of course, always positive, so the "lm" flag for negative saturation has no effect.

COP2 170000Ch+sf*80000h - 6 Cycles - OP(sf,lm) - Outer product of 2 vectors
  [MAC1,MAC2,MAC3] = [IR3*D2-IR2*D3, IR1*D3-IR3*D1, IR2*D1-IR1*D2] SAR (sf*12)
  [IR1,IR2,IR3]    = [MAC1,MAC2,MAC3]                        ;copy result
Calculates the outer product of two signed 16bit vectors. Note: D1,D2,D3 are meant to be the RT11,RT22,RT33 elements of the RT matrix "misused" as vector. lm should be usually zero.

LZCS/LZCR registers - ? Cycles - Count-Leading-Zeroes/Leading-Ones
The LZCS/LZCR registers offer a Count-Leading-Zeroes/Leading-Ones function.


GTE Color Calculation Commands

COP2 0C8041Eh - 14 Cycles - NCS - Normal color (single)
COP2 0D80420h - 30 Cycles - NCT - Normal color (triple)
COP2 108041Bh - 17 Cycles - NCCS - Normal Color Color (single vector)
COP2 118043Fh - 39 Cycles - NCCT - Normal Color Color (triple vector)
COP2 0E80413h - 19 Cycles - NCDS - Normal color depth cue (single vector)
COP2 0F80416h - 44 Cycles - NCDT - Normal color depth cue (triple vectors)
In: V0=Normal vector (for triple variants repeated with V1 and V2), BK=Background color, RGBC=Primary color/code, LLM=Light matrix, LCM=Color matrix, IR0=Interpolation value.
  [IR1,IR2,IR3] = [MAC1,MAC2,MAC3] = (LLM*V0) SAR (sf*12)
  [IR1,IR2,IR3] = [MAC1,MAC2,MAC3] = (BK*1000h + LCM*IR) SAR (sf*12)
  [MAC1,MAC2,MAC3] = [R*IR1,G*IR2,B*IR3] SHL 4          ;<--- for NCDx/NCCx
  [MAC1,MAC2,MAC3] = MAC+(FC-MAC)*IR0                   ;<--- for NCDx only
  [MAC1,MAC2,MAC3] = [MAC1,MAC2,MAC3] SAR (sf*12)       ;<--- for NCDx/NCCx
  Color FIFO = [MAC1/16,MAC2/16,MAC3/16,CODE], [IR1,IR2,IR3] = [MAC1,MAC2,MAC3]

COP2 138041Ch - 11 Cycles - CC(lm=1) - Color Color
COP2 1280414h - 13 Cycles - CDP(...) - Color Depth Que
In: [IR1,IR2,IR3]=Vector, RGBC=Primary color/code, LCM=Color matrix, BK=Background color, and, for CDP, IR0=Interpolation value, FC=Far color.
  [IR1,IR2,IR3] = [MAC1,MAC2,MAC3] = (BK*1000h + LCM*IR) SAR (sf*12)
  [MAC1,MAC2,MAC3] = [R*IR1,G*IR2,B*IR3] SHL 4
  [MAC1,MAC2,MAC3] = MAC+(FC-MAC)*IR0                   ;<--- for CDP only
  [MAC1,MAC2,MAC3] = [MAC1,MAC2,MAC3] SAR (sf*12)
  Color FIFO = [MAC1/16,MAC2/16,MAC3/16,CODE], [IR1,IR2,IR3] = [MAC1,MAC2,MAC3]

COP2 0680029h - 8 Cycles - DCPL - Depth Cue Color light
COP2 0780010h - 8 Cycles - DPCS - Depth Cueing (single)
COP2 0x8002Ah - 17 Cycles - DPCT - Depth Cueing (triple)
COP2 0980011h - 8 Cycles - INTPL - Interpolation of a vector and far color
In: [IR1,IR2,IR3]=Vector, FC=Far Color, IR0=Interpolation value, CODE=MSB of RGBC, and, for DCPL, R,G,B=LSBs of RGBC.
  [MAC1,MAC2,MAC3] = [R*IR1,G*IR2,B*IR3] SHL 4          ;<--- for DCPL only
  [MAC1,MAC2,MAC3] = [IR1,IR2,IR3] SHL 12               ;<--- for INTPL only
  [MAC1,MAC2,MAC3] = [R,G,B] SHL 16                     ;<--- for DPCS/DPCT
  [MAC1,MAC2,MAC3] = MAC+(FC-MAC)*IR0
  [MAC1,MAC2,MAC3] = [MAC1,MAC2,MAC3] SAR (sf*12)
  Color FIFO = [MAC1/16,MAC2/16,MAC3/16,CODE], [IR1,IR2,IR3] = [MAC1,MAC2,MAC3]
DPCT executes thrice, and reads the R,G,B values from RGB0 (ie. reads from the Bottom of the Color FIFO, instead of from the RGBC register) (the CODE value is kept read from RGBC as usually), so, after DPCT execution, the RGB0,RGB1,RGB2 Fifo entries are modified.

COP2 190003Dh - 5 Cycles - GPF(sf,lm) - General purpose Interpolation
COP2 1A0003Eh - 5 Cycles - GPL(sf,?) - General Interpolation with base
  [MAC1,MAC2,MAC3] = [0,0,0]                            ;<--- for GPF only
  [MAC1,MAC2,MAC3] = [MAC1,MAC2,MAC3] SHL (sf*12)       ;<--- for GPL only
  [MAC1,MAC2,MAC3] = (([IR1,IR2,IR3] * IR0) + [MAC1,MAC2,MAC3]) SAR (sf*12)
  Color FIFO = [MAC1/16,MAC2/16,MAC3/16,CODE], [IR1,IR2,IR3] = [MAC1,MAC2,MAC3]
Note: Although the SHL in GPL is theoretically undone by the SAR, 44bit overflows can occur internally when sf=1.

Details on "MAC+(FC-MAC)*IR0"
  [IR1,IR2,IR3] = (([RFC,GFC,BFC] SHL 12) - [MAC1,MAC2,MAC3]) SAR (sf*12)
  [MAC1,MAC2,MAC3] = (([IR1,IR2,IR3] * IR0) + [MAC1,MAC2,MAC3])
Note: Above "[IR1,IR2,IR3]=(FC-MAC)" is saturated to -8000h..+7FFFh (ie. as if lm=0), anyways, further writes to [IR1,IR2,IR3] (within the same command) are saturated as usually (ie. depending on lm setting).

Details on "(LLM*V0) SAR (sf*12)" and "(BK*1000h + LCM*IR) SAR (sf*12)"
Works like MVMVA command (see there), but with fixed Tx/Vx/Mx parameters, the sf/lm bits can be changed and do affect the results (although normally both bits should be set for use with color matrices).

Notes
The 8bit RGB values written to the top of Color Fifo are the 32bit MACn values divided by 16, and saturated to +00h..+FFh, and of course, the older Fifo entries are moved downwards. Note that, at the GPU side, the meaning of the RGB values depends on whether or not texture blending is used (for untextured polygons FFh is max brightness) (for texture blending FFh is double brightness and 80h is normal brightness).
The 8bit CODE value is intended to contain a GP0(20h..7Fh) Rendering command, allowing to automatically merge the 8bit command number, with the 24bit color value.
The IRGB/ORGB registers allow to convert between 48bit and 15bit RGB colors.
Although the result of the commands in this chapter is written to the Color FIFO, some commands like GPF/GPL may be also used for other purposes (eg. to scale or scale/translate single vertices).


GTE Division Inaccuracy

GTE Division Inaccuracy (for RTPS/RTPT commands)
Basically, the GTE division does (attempt to) work as so (using 33bit maths):
  n = (((H*20000h/SZ3)+1)/2)
alternatly, below would give (almost) the same result (using 32bit maths):
  n = ((H*10000h+SZ3/2)/SZ3)
in both cases, the result is saturated about as so:
  if n>1FFFFh or division_by_zero then n=1FFFFh, FLAG.Bit17=1, FLAG.Bit31=1
However, the real GTE hardware is using a fast, but less accurate division mechanism (based on Unsigned Newton-Raphson (UNR) algorithm):
  if (H < SZ3*2) then                            ;check if overflow
    z = count_leading_zeroes(SZ3)                ;z=0..0Fh (for 16bit SZ3)
    n = (H SHL z)                                ;n=0..7FFF8000h
    d = (SZ3 SHL z)                              ;d=8000h..FFFFh
    u = unr_table[(d-7FC0h) SHR 7] + 101h        ;u=200h..101h
    d = ((2000080h - (d * u)) SHR 8)             ;d=10000h..0FF01h
    d = ((0000080h + (d * u)) SHR 8)             ;d=20000h..10000h
    n = min(1FFFFh, (((n*d) + 8000h) SHR 16))    ;n=0..1FFFFh
  else n = 1FFFFh, FLAG.Bit17=1, FLAG.Bit31=1    ;n=1FFFFh plus overflow flag
the GTE's unr_table[000h..100h] consists of following values:
  FFh,FDh,FBh,F9h,F7h,F5h,F3h,F1h,EFh,EEh,ECh,EAh,E8h,E6h,E4h,E3h ;\
  E1h,DFh,DDh,DCh,DAh,D8h,D6h,D5h,D3h,D1h,D0h,CEh,CDh,CBh,C9h,C8h ; 00h..3Fh
  C6h,C5h,C3h,C1h,C0h,BEh,BDh,BBh,BAh,B8h,B7h,B5h,B4h,B2h,B1h,B0h ;
  AEh,ADh,ABh,AAh,A9h,A7h,A6h,A4h,A3h,A2h,A0h,9Fh,9Eh,9Ch,9Bh,9Ah ;/
  99h,97h,96h,95h,94h,92h,91h,90h,8Fh,8Dh,8Ch,8Bh,8Ah,89h,87h,86h ;\
  85h,84h,83h,82h,81h,7Fh,7Eh,7Dh,7Ch,7Bh,7Ah,79h,78h,77h,75h,74h ; 40h..7Fh
  73h,72h,71h,70h,6Fh,6Eh,6Dh,6Ch,6Bh,6Ah,69h,68h,67h,66h,65h,64h ;
  63h,62h,61h,60h,5Fh,5Eh,5Dh,5Dh,5Ch,5Bh,5Ah,59h,58h,57h,56h,55h ;/
  54h,53h,53h,52h,51h,50h,4Fh,4Eh,4Dh,4Dh,4Ch,4Bh,4Ah,49h,48h,48h ;\
  47h,46h,45h,44h,43h,43h,42h,41h,40h,3Fh,3Fh,3Eh,3Dh,3Ch,3Ch,3Bh ; 80h..BFh
  3Ah,39h,39h,38h,37h,36h,36h,35h,34h,33h,33h,32h,31h,31h,30h,2Fh ;
  2Eh,2Eh,2Dh,2Ch,2Ch,2Bh,2Ah,2Ah,29h,28h,28h,27h,26h,26h,25h,24h ;/
  24h,23h,22h,22h,21h,20h,20h,1Fh,1Eh,1Eh,1Dh,1Dh,1Ch,1Bh,1Bh,1Ah ;\
  19h,19h,18h,18h,17h,16h,16h,15h,15h,14h,14h,13h,12h,12h,11h,11h ; C0h..FFh
  10h,0Fh,0Fh,0Eh,0Eh,0Dh,0Dh,0Ch,0Ch,0Bh,0Ah,0Ah,09h,09h,08h,08h ;
  07h,07h,06h,06h,05h,05h,04h,04h,03h,03h,02h,02h,01h,01h,00h,00h ;/
  00h    ;<-- one extra table entry (for "(d-7FC0h)/80h"=100h)    ;-100h
Above can be generated as "unr_table[i]=min(0,(40000h/(i+100h)+1)/2-101h)".
Some special cases: NNNNh/0001h uses a big multiplier (d=20000h), in practice, this can occur only for 0000h/0001h and 0001h/0001h (due to the H<SZ3*2 overflow check).
The min(1FFFFh) limit is needed for cases like FE3Fh/7F20h, F015h/780Bh, etc. (these do produce UNR result 20000h, and are saturated to 1FFFFh, but without setting overflow FLAG bits).


Macroblock Decoder (MDEC)

The MDEC is a JPEG-style Macroblock Decoder, that can decompress pictures (or a series of pictures, for being displayed as a movie).

MDEC I/O Ports
MDEC Commands
MDEC Decompression
MDEC Data Format


MDEC I/O Ports

1F801820h - MDEC0 - MDEC Command/Parameter Register (W)
  31-0  Command or Parameters
Used to send command word, followed by parameter words to the MDEC (usually, only the command word is written to this register, and the parameter words are transferred via DMA0).

1F801820h.Read - MDEC Data/Response Register (R)
  31-0  Macroblock Data (or Garbage if there's no data available)
The data is always output as a 8x8 pixel bitmap, so, when manually reading from this register and using colored 16x16 pixel macroblocks, the data from four 8x8 blocks must be re-ordered accordingly (usually, the data is received via DMA1, which is doing the re-ordering automatically). For monochrome 8x8 macroblocks, no re-ordering is needed (that works with DMA1, too).

1F801824h - MDEC1 - MDEC Status Register (R)
  31    Data-Out Fifo Empty (0=No, 1=Empty)
  30    Data-In Fifo Full   (0=No, 1=Full, or Last word received)
  29    Command Busy  (0=Ready, 1=Busy receiving or processing parameters)
  28    Data-In Request  (set when DMA0 enabled and ready to receive data)
  27    Data-Out Request (set when DMA1 enabled and ready to send data)
  26-25 Data Output Depth  (0=4bit, 1=8bit, 2=24bit, 3=15bit)      ;CMD.28-27
  24    Data Output Signed (0=Unsigned, 1=Signed)                  ;CMD.26
  23    Data Output Bit15  (0=Clear, 1=Set) (for 15bit depth only) ;CMD.25
  22-19 Not used (seems to be always zero)
  18-16 Current Block (0..3=Y1..Y4, 4=Cr, 5=Cb) (or for mono: always 4=Y)
  15-0  Number of Parameter Words remaining minus 1  (FFFFh=None)  ;CMD.Bit0-15
If there's data in the output fifo, then the Current Block bits are always set to the current output block number (ie. Y1..Y4; or Y for mono) (this information is apparently passed to the DMA1 controller, so that it knows if and how it must re-order the data in RAM). If the output fifo is empty, then the bits indicate the currently processsed incoming block (ie. Cr,Cb,Y1..Y4; or Y for mono).

1F801824h - MDEC1 - MDEC Control/Reset Register (W)
  31    Reset MDEC (0=No change, 1=Abort any command, and set status=80040000h)
  30    Enable Data-In Request  (0=Disable, 1=Enable DMA0 and Status.bit28)
  29    Enable Data-Out Request (0=Disable, 1=Enable DMA1 and Status.bit27)
  28-0  Unknown/Not used - usually zero
The data requests are required to be enabled for using DMA (and for reading the request status flags by software). The Data-Out request acts a bit strange: It gets set when a block is available, but, it gets cleared after reading the first some words of that block (nethertheless, one can keep reading the whole block, until the fifo-empty flag gets set).

DMA
MDEC decompression uses a lot of DMA channels (and CPU processing):
  1) DMA3 (CDROM)    to send Huffman compressed data from CDROM to RAM
  2) CPU  (MIPS)     to convert Huffman bitstream to 16bit MDEC RLE values
  3) DMA0 (MDEC.In)  to send MDEC compressed data from RAM to MDEC
  4) DMA1 (MDEC.Out) to send uncompressed macroblocks from MDEC to RAM
  5) DMA2 (GPU)      to send uncompressed macroblocks from RAM to GPU
DMA0 and DMA1 should be usually used with a blocksize of 20h words. If necessary, the parameters for the MDEC(1) command should be padded with FE00h halfwords to match the 20h words (40h halfwords) DMA blocksize.


MDEC Commands

MDEC(1) - Decode Macroblock(s)
  31-29 Command (1=decode_macroblock)
  28-27 Data Output Depth  (0=4bit, 1=8bit, 2=24bit, 3=15bit)      ;STAT.26-25
  26    Data Output Signed (0=Unsigned, 1=Signed)                  ;STAT.24
  25    Data Output Bit15  (0=Clear, 1=Set) (for 15bit depth only) ;STAT.23
  24-16 Not used (should be zero)
  15-0  Number of Parameter Words (size of compressed data)
This command is followed by one or more Macroblock parameters (usually, all macroblocks for the whole image are sent at once).

MDEC(2) - Set Quant Table(s)
  31-29 Command (2=set_iqtab)
  28-1  Not used (should be zero)  ;Bit25-28 are copied to STAT.23-26 though
  0     Color   (0=Luminance only, 1=Luminance and Color)
The command word is followed by 64 unsigned parameter bytes for the Luminance Quant Table (used for Y1..Y4), and if Command.Bit0 was set, by another 64 unsigned parameter bytes for the Color Quant Table (used for Cr and Cb).

MDEC(3) - Set Scale Table
  31-29 Command (3=set_scale)
  28-0  Not used (should be zero)  ;Bit25-28 are copied to STAT.23-26 though
The command is followed by 64 signed halfwords with 14bit fractional part, the values should be usually/always the same values (based on the standard JPEG constants, although, MDEC(3) allows to use other values than that constants).

MDEC(0) - No function
This command has no function. Command bits 25-28 are reflected to Status bits 23-26 as usually. Command bits 0-15 are reflected to Status bits 0-15 (similar as the "number of parameter words" for MDEC(1), but without the "minus 1" effect, and without actually expecting any parameters).

MDEC(4..7) - Invalid
These commands act identical as MDEC(0).


MDEC Decompression

decode_colored_macroblock ;MDEC(1) command (at 15bpp or 24bpp depth)
  rl_decode_block(Crblk,src,iq_uv)                 ;Cr (low resolution)
  rl_decode_block(Cbblk,src,iq_uv)                 ;Cb (low resolution)
  rl_decode_block(Yblk,src,iq_y), yuv_to_rgb(0,0)  ;Y1 (and upper-left Cr,Cb)
  rl_decode_block(Yblk,src,iq_y), yuv_to_rgb(0,8)  ;Y2 (and upper-right Cr,Cb)
  rl_decode_block(Yblk,src,iq_y), yuv_to_rgb(8,0)  ;Y3 (and lower-left Cr,Cb)
  rl_decode_block(Yblk,src,iq_y), yuv_to_rgb(8,8)  ;Y4 (and lower-right Cr,Cb)

decode_monochrome_macroblock ;MDEC(1) command (at 4bpp or 8bpp depth)
  rl_decode_block(Yblk,src,iq_y), y_to_mono        ;Y

rl_decode_block(blk,src,qt)
  for i=0 to 63, blk[i]=0, next i   ;initially zerofill all entries (for skip)
 @@skip:
  n=[src], src=src+2, k=0           ;get first entry, init dest addr k=0
  if n=FE00h then @@skip            ;ignore padding (FE00h as first halfword)
  q_scale=(n SHR 10) AND 3Fh        ;contains scale value (not "skip" value)
  val=signed10bit(n AND 3FFh)*qt[k] ;calc first value (without q_scale/8) (?)
 @@lop:
  if q_scale=0 then val=signed10bit(n AND 3FFh)*2   ;special mode without qt[k]
  val=minmax(val,-400h,+3FFh)            ;saturate to signed 11bit range
  val=val*scalezag[i]                    ;<-- for "fast_idct_core" only
  if q_scale>0 then blk[zagzig[k]]=val   ;store entry (normal case)
  if q_scale=0 then blk[k]=val           ;store entry (special, no zigzag)
  n=[src], src=src+2                     ;get next entry (or FE00h end code)
  k=k+((n SHR 10) AND 3Fh)+1             ;skip zerofilled entries
  val=(signed10bit(n AND 3FFh)*qt[k]*q_scale+4)/8  ;calc value for next entry
  if k<=63 then jump @@lop          ;should end with n=FE00h (that sets k>63)
  idct_core(blk)
  return (with "src" address advanced)

fast_idct_core(blk) ;fast "idct_core" version
Fast code with only 80 multiplications, works only if the scaletable from MDEC(3) command contains standard values (which is the case for all known PSX games).
  src=blk, dst=temp_buffer
  for pass=0 to 1
    for i=0 to 7
      if src[(1..7)*8+i]=0 then      ;when src[(1..7)*8+i] are all zero:
        dst[i*8+(0..7)]=src[0*8+i]   ;quick fill by src[0*8+i]
      else
        z10=src[0*8+i]+src[4*8+i], z11=src[0*8+i]-src[4*8+i]
        z13=src[2*8+i]+src[6*8+i], z12=src[2*8+i]-src[6*8+i]
        z12=(1.414213562*z12)-z13          ;=sqrt(2)
        tmp0=z10+z13, tmp3=z10-z13, tmp1=z11+z12, tmp2=z11-z12
        z13=src[3*8+i]+src[5*8+i], z10=src[3*8+i]-src[5*8+i]
        z11=src[1*8+i]+src[7*8+i], z12=src[1*8+i]-src[7*8+i]
        z5  =(1.847759065*(z12-z10))       ;=sqrt(2)*scalefactor[2]
        tmp7=z11+z13
        tmp6=(2.613125930*(z10))+z5-tmp7   ;=scalefactor[2]*2
        tmp5=(1.414213562*(z11-z13))-tmp6  ;=sqrt(2)
        tmp4=(1.082392200*(z12))-z5+tmp5   ;=sqrt(2)/scalefactor[2]
        dst[i*8+0]=tmp0+tmp7, dst[i*8+7]=tmp0-tmp7
        dst[i*8+1]=tmp1+tmp6, dst[i*8+6]=tmp1-tmp6
        dst[i*8+2]=tmp2+tmp5, dst[i*8+5]=tmp2-tmp5
        dst[i*8+4]=tmp3+tmp4, dst[i*8+3]=tmp3-tmp4
      endif
    next i
    swap(src,dst)
  next pass

real_idct_core(blk) ;low level "idct_core" version
Low level code with 1024 multiplications, using the scaletable from the MDEC(3) command. Computes dst=src*scaletable (using normal matrix maths, but with "src" being diagonally mirrored, ie. the matrices are processed column by column, instead of row by column), repeated with src/dst exchanged.
  src=blk, dst=temp_buffer
  for pass=0 to 1
    for x=0 to 7
      for y=0 to 7
        sum=0
        for z=0 to 7
          sum=sum+src[y+z*8]*(scaletable[x+z*8]/8)
        next z
        dst[x+y*8]=(sum+0fffh)/2000h               ;<-- or so?
      next y
    next x
    swap(src,dst)
  next pass
The "(sum+0fffh)/2000h" part is meant to strip fractional bits, and to round-up the result if the fraction was BIGGER than 0.5. The hardware appears to be working roughly like that, still the results aren't perfect.
Maybe the real hardware is doing further roundings in other places, possibly stripping some fractional bits before summing up "sum", possibly stripping different amounts of bits in the two "pass" cycles, and possibly keeping a final fraction passed on to the y_to_mono stage.

yuv_to_rgb(xx,yy)
  for y=0 to 7
    for x=0 to 7
      R=[Crblk+((x+xx)/2)+((y+yy)/2)*8], B=[Cbblk+((x+xx)/2)+((y+yy)/2)*8]
      G=(-0.3437*B)+(-0.7143*R), R=(1.402*R), B=(1.772*B)
      Y=[Yblk+(x)+(y)*8]
      R=MinMax(-128,127,(Y+R))
      G=MinMax(-128,127,(Y+G))
      B=MinMax(-128,127,(Y+B))
      if unsigned then BGR=BGR xor 808080h  ;aka add 128 to the R,G,B values
      dst[(x+xx)+(y+yy)*16]=BGR
    next x
  next y
Note: The exact fixed point resolution for "yuv_to_rgb" is unknown. And, there's probably also some 9bit limit (similar as in "y_to_mono").

y_to_mono
  for i=0 to 63
    Y=[Yblk+i]
    Y=Y AND 1FFh                  ;clip to signed 9bit range
    Y=MinMax(-128,127,Y)          ;saturate from 9bit to signed 8bit range
    if unsigned then Y=Y xor 80h  ;aka add 128 to the Y value
    dst[i]=Y
  next i

set_iqtab ;MDEC(2) command
  iqtab_core(iq_y,src), src=src+64       ;luminance quant table
  if command_word.bit0=1
    iqtab_core(iq_uv,src), src=src+64    ;color quant table (optional)
  endif

iqtab_core(iq,src) ;src = 64 unsigned parameter bytes
  for i=0 to 63, iq[i]=src[i], next i
Note: For "fast_idct_core" one could precalc "iq[i]=src[i]*scalezag[i]", but that would conflict with the RLE saturation/rounding steps (though those steps aren't actually required, so a very-fast decoder could omit them).

scalefactor[0..7] = cos((0..7)*90'/8) ;for [1..7]: multiplied by sqrt(2)
  1.000000000, 1.387039845, 1.306562965, 1.175875602,
  1.000000000, 0.785694958, 0.541196100, 0.275899379

zigzag[0..63] =
  0 ,1 ,5 ,6 ,14,15,27,28,
  2 ,4 ,7 ,13,16,26,29,42,
  3 ,8 ,12,17,25,30,41,43,
  9 ,11,18,24,31,40,44,53,
  10,19,23,32,39,45,52,54,
  20,22,33,38,46,51,55,60,
  21,34,37,47,50,56,59,61,
  35,36,48,49,57,58,62,63

scalezag[0..63] (precalulated factors, for "fast_idct_core")
  for y=0 to 7
   for x=0 to 7
    scalezag[zigzag[x+y*8]] = scalefactor[x] * scalefactor[y] / 8
   next x
  next y

zagzig[0..63] (reversed zigzag table)
  for i=0 to 63, zagzig[zigzag[i]]=i, next i

set_quant_table: ;MDEC(2) command
This command defines the quant tables, there are two tables (one for luminance and one for chroma). For STR movies and BS pictures both 64-byte tables should be almost always set to following 64 bytes:
  02h,10h,10h,13h,10h,13h,16h,16h
  16h,16h,16h,16h,1Ah,18h,1Ah,1Bh
  1Bh,1Bh,1Ah,1Ah,1Ah,1Ah,1Bh,1Bh
  1Bh,1Dh,1Dh,1Dh,22h,22h,22h,1Dh
  1Dh,1Dh,1Bh,1Bh,1Dh,1Dh,20h,20h
  22h,22h,25h,26h,25h,23h,23h,22h
  23h,26h,26h,28h,28h,28h,30h,30h
  2Eh,2Eh,38h,38h,3Ah,45h,45h,53h
Note: The exception are BS fraquant movies in X-Files and Eagle One (these do also use the above values, but do have them multiplied with a fixed point number).

set_scale_table: ;MDEC(3) command
This command defines the IDCT scale matrix, which should be usually/always:
  5A82 5A82 5A82 5A82 5A82 5A82 5A82 5A82
  7D8A 6A6D 471C 18F8 E707 B8E3 9592 8275
  7641 30FB CF04 89BE 89BE CF04 30FB 7641
  6A6D E707 8275 B8E3 471C 7D8A 18F8 9592
  5A82 A57D A57D 5A82 5A82 A57D A57D 5A82
  471C 8275 18F8 6A6D 9592 E707 7D8A B8E3
  30FB 89BE 7641 CF04 CF04 7641 89BE 30FB
  18F8 B8E3 6A6D 8275 7D8A 9592 471C E707
Note that the hardware does actually use only the upper 13bit of those 16bit values. The values are choosen like so,
  +s0  +s0  +s0  +s0  +s0  +s0  +s0  +s0
  +s1  +s3  +s5  +s7  -s7  -s5  -s3  -s1
  +s2  +s6  -s6  -s2  -s2  -s6  +s6  +s2
  +s3  -s7  -s1  -s5  +s5  +s1  +s7  -s3
  +s4  -s4  -s4  +s4  +s4  -s4  -s4  +s4
  +s5  -s1  +s7  +s3  -s3  -s7  +s1  -s5
  +s6  -s2  +s2  -s6  -s6  +s2  -s2  +s6
  +s7  -s5  +s3  -s1  +s1  -s3  +s5  -s7
whereas, s0..s7 = scalefactor[0..7], multiplied by sqrt(2) (ie. by 1.414), and multiplied by 4000h (ie. with 14bit fractional part).


MDEC Data Format

Colored Macroblocks (16x16 pixels) (in 15bpp or 24bpp depth mode)
Each macroblock consists of six blocks: Two low-resolution blocks with color information (Cr,Cb) and four full-resolution blocks with luminance (grayscale) information (Y1,Y2,Y3,Y4). The color blocks are zoomed from 8x8 to 16x16 pixel size, merged with the luminance blocks, and then converted from YUV to RGB format.
   .-----.       .-----.       .-----.         .-----.
   |     |       |     |       |Y1|Y2|         |     |
   | Cr  |   +   | Cb  |   +   |--+--|  ---->  | RGB |
   |     |       |     |       |Y3|Y4|         |     |
   '-----'       '-----'       '-----'         '-----'
Native PSX files are usually containing vertically arranged Macroblocks (eg. allowing to send them to the GPU as 16x240 portion) (JPEG-style horizontally arranged Macroblocks would require to send the data in 16x16 pixel portions to the GPU) (something like 320x16 won't work, since that'd require to wrap from the bottom of the first macroblock to the top of the next macroblock).

Monochrome Macroblocks (8x8 pixel) (in 4bpp or 8bpp depth mode)
Each macroblock consist of only one block: with luminance (grayscale) information (Y), the data comes out as such (it isn't converted to RGB).
   .--.         .--.
   |Y |  ---->  |Y |
   '--'         '--'
The output is an 8x8 bitmap (not 16x16), so it'd be send to the GPU as 8x8 pixel rectangle, or multiple blocks at once as 8x240 pixel rectangle. Since the data isn't RGB, it should be written to Texture memory (and then it can be forwarded to the frame buffer in form of a texture with monochrome 15bit palette with 32 grayscales). Alternately, one could convert the 8bpp image to 24bpp by software (this would allow to use 256 grayscales).

Blocks (8x8 pixels)
An (uncompressed) block consists of 64 values, representing 8x8 pixels. The first (upper-left) value is an absolute value (called "DC" value), the remaining 63 values are relative to the DC value (called "AC" values). After decompression and zig-zag reordering, the data in unfiltered horizontally and vertically (IDCT conversion, ie. the relative "AC" values are converted to absolute "DC" values).

.STR Files
PSX Video files are usually having file extension .STR (for "Streaming").
CDROM File Video STR Streaming and BS Picture Compression (Sony)

MDEC vs JPEG
The MDEC data format is very similar to the JPEG file format, the main difference is that JPEG uses Huffman compressed blocks, whilst MDEC uses Run-Length (RL) compressed blocks.
The (uncompressed) blocks are same as in JPEGs, using the same zigzag ordering, AC to DC conversion, and YUV to RGB conversion (ie. the MDEC hardware can be also used to decompress JPEGs, when handling the file header and huffman decompression by software).
Some other differences are that MDEC has only 2 fixed-purpose quant tables, whilst JPEGs <can> use up to 4 general-purpose quant tables. Also, JPEGs <can> use other color resolutions than the 8x8 color info for 16x16 pixels. Whereas, JPEGs <can> do that stuff, but most standard JPEG files aren't actually using 4 quant tables, nor higher color resolution.

Run-Length compressed Blocks
Within each block the DCT information and RLE compressed data is stored:
  DCT               ;1 halfword
  RLE,RLE,RLE,etc.  ;0..63 halfwords
  EOB               ;1 halfword

DCT (1st value)
DCT data has the quantization factor and the Direct Current (DC) reference.
  15-10 Q    Quantization factor (6 bits, unsigned)
  9-0   DC   Direct Current reference (10 bits, signed)
Contains the absolute DC value (the upper-left value of the 8x8 block).

RLE (Run length data, for 2nd through 64th value)
  15-10 LEN  Number of zero AC values to be inserted (6 bits, unsigned)
  9-0   AC   Relative AC value (10 bits, signed)
Example: AC values "000h,000h,123h" would be compressed as "(2 shl 10)+123h".

EOB (End Of Block)
Indicates the end of a 8x8 pixel block, causing the rest of the block to be padded with zero AC values.
  15-0  End-code (Fixed, FE00h)
EOB isn't required if the block was already fully defined (up to including blk[63]), however, most games seem to append EOB to all blocks (although it's just acting as dummy/padding value in case of fully defined blocks).

Dummy halfwords
Data is sent in units of words (or, when using DMA, even in units of 32-words), which is making it neccessary to send some dummy halfwords (unless the compressed data size should match up the transfer unit). The value FE00h can be used as dummy value: When FE00h appears at the begin of a new block, or after the end of block, then it is simply ignored by the hardware (if it occurs elsewhere, then it acts as EOB end code, as described above).


Sound Processing Unit (SPU)

SPU Overview
SPU ADPCM Samples
SPU ADPCM Pitch
SPU Volume and ADSR Generator
SPU Voice Flags
SPU Noise Generator
SPU Control and Status Register
SPU Memory Access
SPU Interrupt
SPU Reverb Registers
SPU Reverb Formula
SPU Reverb Examples
SPU Unknown Registers


SPU Overview

SPU I/O Port Summary
  1F801C00h..1F801D7Fh - Voice 0..23 Registers (eight 16bit regs per voice)
  1F801D80h..1F801D87h - SPU Control (volume)
  1F801D88h..1F801D9Fh - Voice 0..23 Flags (six 1bit flags per voice)
  1F801DA2h..1F801DBFh - SPU Control (memory, control, etc.)
  1F801DC0h..1F801DFFh - Reverb configuration area
  1F801E00h..1F801E5Fh - Voice 0..23 Internal Registers
  1F801E60h..1F801E7Fh - Unknown?
  1F801E80h..1F801FFFh - Unused?

SPU Memory layout (512Kbyte RAM)
  00000h-003FFh  CD Audio left  (1Kbyte) ;\CD Audio before Volume processing
  00400h-007FFh  CD Audio right (1Kbyte) ;/signed 16bit samples at 44.1kHz
  00800h-00BFFh  Voice 1 mono   (1Kbyte) ;\Voice 1 and 3 after ADSR processing
  00C00h-00FFFh  Voice 3 mono   (1Kbyte) ;/signed 16bit samples at 44.1kHz
  01000h-xxxxxh  ADPCM Samples  (first 16bytes usually contain a Sine wave)
  xxxxxh-7FFFFh  Reverb work area
As shown above, the first 4Kbytes are used as special capture buffers, and, if desired, one can also use the Reverb hardware to capture output from other voice(s).
The SPU memory is not mapped to the CPU bus, it can be accessed only via I/O, or via DMA transfers (DMA4).

Voices
The SPU has 24 hardware voices. These voices can be used to reproduce sample data, noise or can be used as frequency modulator on the next voice. Each voice has it's own programmable ADSR envelope filter. The main volume can be programmed independently for left and right output.

Voice Capabilities
All 24 voices are having exactly the same capabilities(?), with the exception that Voice 1 and 3 are having a special Capture feature (see SPU Memory map).
There seems to be no way to produce square waves (without storing a square wavefrom in memory... although, since SPU RAM isn't connected to the CPU bus, the "useless" DMA for square wave data wouldn't slowdown the CPU bus)?

Additional Sound Inputs
External Audio can be input (from the Expansion Port?), and the CDROM drive can be commanded to playback normal Audio CDs (via Play command), or XA-ADPCM sectors (via Read command), and to pass that data to the SPU.

Unstable and Delayed I/O
The SPU occassionally seems to "miss" I/O writes (not sure if that can be fixed by any Memory Control settings?), a stable workaround is too write all values twice (except of course, Fifo writes). The SPU seems to process written values at 44100Hz rate (so it may take 1/44100 seconds (300h clock cycles) until it has actually realized the new value).

Mono/Stereo Audio Output
The standard PSX Audio cables have separate Left/Right signals, that is good for stereo TVs, but, when using a normal mono TV, only one of the two audio signals (Left or Right) can be connected. PSX programs should thus offer an option to disable stereo effects, and to output an equal volume to both cables.

SPU Bus-Width
The SPU is connected to a 16bit databus. 8bit/16bit/32bit reads and 16bit/32bit writes are implemented. However, 8bit writes are NOT implemented: 8bit writes to ODD addresses are simply ignored (without causing any exceptions), 8bit writes to EVEN addresses are executed as 16bit writes (eg. "movp r1,12345678h, movb [spu_port],r1" will write 5678h instead of 78h).


SPU ADPCM Samples

The SPU supports only ADPCM compressed samples (uncompressed samples seem to be totally unsupported; leaving apart that one can write uncompressed 16bit PCM samples to the Reverb Buffer, which can be then output at 22050Hz, as long as they aren't overwritten by the hardware).

1F801C06h+N*10h - Voice 0..23 ADPCM Start Address (R/W)
This register holds the sample start address (not the current address, ie. the register doesn't increment during playback).
  15-0   Startaddress of sound in Sound buffer (in 8-byte units)
Writing to this register has no effect on the currently playing voice.
The start address is copied to the current address upon Key On.

1F801C0Eh+N*10h - Voice 0..23 ADPCM Repeat Address (R/W)
If the hardware finds an ADPCM header with Loop-Start-Bit, then it copies the current address to the repeat addresss register.
If the hardware finds an ADPCM header with Loop-Stop-Bit, then it copies the repeat addresss register setting to the current address; that, <after> playing the current ADPCM block.
  15-0  Address sample loops to at end (in 8-byte units)
Normally, repeat works automatically via the above start/stop bits, and software doesn't need to deal with the Repeat Address Register. However, reading from it may be useful to sense if the hardware has reached a start bit, and writing may be also useful in some cases, eg. to redirect a one-shot sample (with stop-bit, but without any start-bits) to a silent-loop located elsewhere in memory.

Sample Data (SPU-ADPCM)
Samples consist of one or more 16-byte blocks:
  00h       Shift/Filter (reportedly same as for CDROM XA-ADPCM) (see there)
  01h       Flag Bits (see below)
  02h       Compressed Data (LSBs=1st Sample, MSBs=2nd Sample)
  03h       Compressed Data (LSBs=3rd Sample, MSBs=4th Sample)
  04h       Compressed Data (LSBs=5th Sample, MSBs=6th Sample)
  ...       ...
  0Fh       Compressed Data (LSBs=27th Sample, MSBs=28th Sample)

Flag Bits (in 2nd byte of ADPCM Header)
  0   Loop End    (0=No change, 1=Set ENDX flag and Jump to [1F801C0Eh+N*10h])
  1   Loop Repeat (0=Force Release and set ADSR Level to Zero; only if Bit0=1)
  2   Loop Start  (0=No change, 1=Copy current address to [1F801C0Eh+N*10h])
  3-7 Unknown    (usually 0)
Possible combinations for Bit0-1 are:
  Code 0 = Normal     (continue at next 16-byte block)
  Code 1 = End+Mute   (jump to Loop-address, set ENDX flag, Release, Env=0000h)
  Code 2 = Ignored    (same as Code 0)
  Code 3 = End+Repeat (jump to Loop-address, set ENDX flag)

Looped and One-shot Samples
The Loop Start/End flags in the ADPCM Header allow to play one or more sample block(s) in a loop, that can be either all block(s) endless repeated, or only the last some block(s) of the sample.
There's no way to stop the output, so a one-shot sample must be followed by dummy block (with Loop Start/End flags both set, and all data nibbles set to zero; so that the block gets endless repeated, but doesn't produce any sound).

SPU-ADPCM vs XA-ADPCM
The PSX supports two ADPCM formats: SPU-ADPCM (as described above), and XA-ADPCM. XA-ADPCM is decompressed by the CDROM Controller, and sent directly to the sound mixer, without needing to store the data in SPU RAM, nor needing to use a Voice channel.
The actual decompression algorithm is the same for both formats. However, the XA nibbles are arranged in different order, and XA uses 2x28 nibbles per block (instead of 2x14), XA blocks can contain mono or stereo data, XA supports only two sample rates, and, XA doesn't support looping.


SPU ADPCM Pitch

1F801C04h+N*10h - Voice 0..23 ADPCM Sample Rate (R/W) (VxPitch)
  0-15  Sample rate (0=stop, 4000h=fastest, 4001h..FFFFh=usually same as 4000h)
Defines the ADPCM sample rate (1000h = 44100Hz). This register (and PMON) does only affect the ADPCM sample frequency (but not the Noise frequency, which is defined - and shared for all voices - in the SPUCNT register).

1F801D90h - Voice 0..23 Pitch Modulation Enable Flags (PMON)
Pitch modulation allows to generate "Frequency Sweep" effects by mis-using the amplitude from channel (x-1) as pitch factor for channel (x).
  0     Unknown... Unused?
  1-23  Flags for Voice 1..23 (0=Normal, 1=Modulate by Voice 0..22)
  24-31 Not used
For example, output a very loud 1Hz sine-wave on channel 4 (with ADSR volume 4000h, and with Left/Right volume=0; unless you actually want to output it to the speaker). Then additionally output a 2kHz sine wave on channel 5 with PMON.Bit5 set. The "2kHz" sound should then repeatedly sweep within 1kHz..3kHz range (or, for a more decent sweep in 1.8kHz..2.2kHz range, drop the ADSR volume of channel 4).

Pitch Counter
The pitch counter is adjusted at 44100Hz rate as follows:
  Step = VxPitch                  ;range +0000h..+FFFFh (0...705.6 kHz)
  IF PMON.Bit(x)=1 AND (x>0)      ;pitch modulation enable
    Factor = VxOUTX(x-1)          ;range -8000h..+7FFFh (prev voice amplitude)
    Factor = Factor+8000h         ;range +0000h..+FFFFh (factor = 0.00 .. 1.99)
    Step=SignExpand16to32(Step)   ;hardware glitch on VxPitch>7FFFh, make sign
    Step = (Step * Factor) SAR 15 ;range 0..1FFFFh (glitchy if VxPitch>7FFFh)
    Step=Step AND 0000FFFFh       ;hardware glitch on VxPitch>7FFFh, kill sign
  IF Step>3FFFh then Step=4000h   ;range +0000h..+3FFFh (0.. 176.4kHz)
  Counter = Counter + Step
Counter.Bit12 and up indicates the current sample (within a ADPCM block).
Counter.Bit3..11 are used as 8bit gaussian interpolation index.

Maximum Sound Frequency
The Mixer and DAC supports a 44.1kHz output rate (allowing to produce max 22.1kHz tones). The Reverb unit supports only half the frequency.
The pitch counter supports sample rates up to 176.4kHz. However, exceeding the 44.1kHz limit causes the hardware to skip samples (or actually: to apply incomplete interpolation on the 'skipped' samples).
VxPitch can be theoretically 0..FFFFh (max 705.6kHz), normally 4000h..FFFFh are simply clipped to max=4000h (176.4kHz). Except, 4000h..FFFFh could be used with pitch modulation (as they are multiplied by 0.00..1.99 before clipping; in practice this works only for 4000h..7FFFh; as values 8000h..FFFFh are mistaken as signed values).

4-Point Gaussian Interpolation
Interpolation is applied on the 4 most recent 16bit ADPCM samples (new,old,older,oldest), using bit4-11 of the pitch counter as 8bit interpolation index (i=00h..FFh):
  out =       ((gauss[0FFh-i] * oldest) SAR 15)
  out = out + ((gauss[1FFh-i] * older)  SAR 15)
  out = out + ((gauss[100h+i] * old)    SAR 15)
  out = out + ((gauss[000h+i] * new)    SAR 15)
The Gauss table contains the following values (in hex):
  -001h,-001h,-001h,-001h,-001h,-001h,-001h,-001h ;\
  -001h,-001h,-001h,-001h,-001h,-001h,-001h,-001h ;
  0000h,0000h,0000h,0000h,0000h,0000h,0000h,0001h ;
  0001h,0001h,0001h,0002h,0002h,0002h,0003h,0003h ;
  0003h,0004h,0004h,0005h,0005h,0006h,0007h,0007h ;
  0008h,0009h,0009h,000Ah,000Bh,000Ch,000Dh,000Eh ;
  000Fh,0010h,0011h,0012h,0013h,0015h,0016h,0018h ; entry
  0019h,001Bh,001Ch,001Eh,0020h,0021h,0023h,0025h ; 000h..07Fh
  0027h,0029h,002Ch,002Eh,0030h,0033h,0035h,0038h ;
  003Ah,003Dh,0040h,0043h,0046h,0049h,004Dh,0050h ;
  0054h,0057h,005Bh,005Fh,0063h,0067h,006Bh,006Fh ;
  0074h,0078h,007Dh,0082h,0087h,008Ch,0091h,0096h ;
  009Ch,00A1h,00A7h,00ADh,00B3h,00BAh,00C0h,00C7h ;
  00CDh,00D4h,00DBh,00E3h,00EAh,00F2h,00FAh,0101h ;
  010Ah,0112h,011Bh,0123h,012Ch,0135h,013Fh,0148h ;
  0152h,015Ch,0166h,0171h,017Bh,0186h,0191h,019Ch ;/
  01A8h,01B4h,01C0h,01CCh,01D9h,01E5h,01F2h,0200h ;\
  020Dh,021Bh,0229h,0237h,0246h,0255h,0264h,0273h ;
  0283h,0293h,02A3h,02B4h,02C4h,02D6h,02E7h,02F9h ;
  030Bh,031Dh,0330h,0343h,0356h,036Ah,037Eh,0392h ;
  03A7h,03BCh,03D1h,03E7h,03FCh,0413h,042Ah,0441h ;
  0458h,0470h,0488h,04A0h,04B9h,04D2h,04ECh,0506h ;
  0520h,053Bh,0556h,0572h,058Eh,05AAh,05C7h,05E4h ; entry
  0601h,061Fh,063Eh,065Ch,067Ch,069Bh,06BBh,06DCh ; 080h..0FFh
  06FDh,071Eh,0740h,0762h,0784h,07A7h,07CBh,07EFh ;
  0813h,0838h,085Dh,0883h,08A9h,08D0h,08F7h,091Eh ;
  0946h,096Fh,0998h,09C1h,09EBh,0A16h,0A40h,0A6Ch ;
  0A98h,0AC4h,0AF1h,0B1Eh,0B4Ch,0B7Ah,0BA9h,0BD8h ;
  0C07h,0C38h,0C68h,0C99h,0CCBh,0CFDh,0D30h,0D63h ;
  0D97h,0DCBh,0E00h,0E35h,0E6Bh,0EA1h,0ED7h,0F0Fh ;
  0F46h,0F7Fh,0FB7h,0FF1h,102Ah,1065h,109Fh,10DBh ;
  1116h,1153h,118Fh,11CDh,120Bh,1249h,1288h,12C7h ;/
  1307h,1347h,1388h,13C9h,140Bh,144Dh,1490h,14D4h ;\
  1517h,155Ch,15A0h,15E6h,162Ch,1672h,16B9h,1700h ;
  1747h,1790h,17D8h,1821h,186Bh,18B5h,1900h,194Bh ;
  1996h,19E2h,1A2Eh,1A7Bh,1AC8h,1B16h,1B64h,1BB3h ;
  1C02h,1C51h,1CA1h,1CF1h,1D42h,1D93h,1DE5h,1E37h ;
  1E89h,1EDCh,1F2Fh,1F82h,1FD6h,202Ah,207Fh,20D4h ;
  2129h,217Fh,21D5h,222Ch,2282h,22DAh,2331h,2389h ; entry
  23E1h,2439h,2492h,24EBh,2545h,259Eh,25F8h,2653h ; 100h..17Fh
  26ADh,2708h,2763h,27BEh,281Ah,2876h,28D2h,292Eh ;
  298Bh,29E7h,2A44h,2AA1h,2AFFh,2B5Ch,2BBAh,2C18h ;
  2C76h,2CD4h,2D33h,2D91h,2DF0h,2E4Fh,2EAEh,2F0Dh ;
  2F6Ch,2FCCh,302Bh,308Bh,30EAh,314Ah,31AAh,3209h ;
  3269h,32C9h,3329h,3389h,33E9h,3449h,34A9h,3509h ;
  3569h,35C9h,3629h,3689h,36E8h,3748h,37A8h,3807h ;
  3867h,38C6h,3926h,3985h,39E4h,3A43h,3AA2h,3B00h ;
  3B5Fh,3BBDh,3C1Bh,3C79h,3CD7h,3D35h,3D92h,3DEFh ;/
  3E4Ch,3EA9h,3F05h,3F62h,3FBDh,4019h,4074h,40D0h ;\
  412Ah,4185h,41DFh,4239h,4292h,42EBh,4344h,439Ch ;
  43F4h,444Ch,44A3h,44FAh,4550h,45A6h,45FCh,4651h ;
  46A6h,46FAh,474Eh,47A1h,47F4h,4846h,4898h,48E9h ;
  493Ah,498Ah,49D9h,4A29h,4A77h,4AC5h,4B13h,4B5Fh ;
  4BACh,4BF7h,4C42h,4C8Dh,4CD7h,4D20h,4D68h,4DB0h ;
  4DF7h,4E3Eh,4E84h,4EC9h,4F0Eh,4F52h,4F95h,4FD7h ; entry
  5019h,505Ah,509Ah,50DAh,5118h,5156h,5194h,51D0h ; 180h..1FFh
  520Ch,5247h,5281h,52BAh,52F3h,532Ah,5361h,5397h ;
  53CCh,5401h,5434h,5467h,5499h,54CAh,54FAh,5529h ;
  5558h,5585h,55B2h,55DEh,5609h,5632h,565Bh,5684h ;
  56ABh,56D1h,56F6h,571Bh,573Eh,5761h,5782h,57A3h ;
  57C3h,57E2h,57FFh,581Ch,5838h,5853h,586Dh,5886h ;
  589Eh,58B5h,58CBh,58E0h,58F4h,5907h,5919h,592Ah ;
  593Ah,5949h,5958h,5965h,5971h,597Ch,5986h,598Fh ;
  5997h,599Eh,59A4h,59A9h,59ADh,59B0h,59B2h,59B3h ;/
The PSX table is a bit different as the SNES table: Values up to 3569h are smaller as on SNES, the remaining values are bigger as on SNES, and the width of the PSX table entries is 4bit higher as on SNES.
The PSX table is slightly bugged: Theoretically, each four values (gauss[000h+i], gauss[0FFh-i], gauss[100h+i], gauss[1FFh-i]) should sum up to 8000h, but in practice they do sum up to 7F7Fh..7F81h (fortunately the PSX sum doesn't exceed the 8000h limit; meaning that the PSX interpolations won't overflow, which has been a hardware glitch on the SNES).

Waveform Examples
  Incoming ADPCM Data ---> Interpolated Data
   _   _   _   _
  | | | | | | | |           .   .   .   .    Nibbles=79797979, Filter=0
  | | | | | | | |     ---> / \ / \ / \ / \   HALF-volume ZIGZAG-wave
  | |_| |_| |_| |_            '   '   '   '
   ___     ___
  |   |   |   |              .'.     .'.     Nibbles=77997799, Filter=0
  |   |   |   |       --->  /   \   /   \    FULL-volume SINE-wave
  |   |___|   |___         '     '.'     '.
   _______                     ___
  |       |                  .'   '.         Nibbles=77779999, Filter=0
  |       |           --->  /       \        SQUARE wave (with rounded edges)
  |       |_______         '         '.____
   _____         _             __
  |     |_     _|            .'  ''.    .'   Nibbles=7777CC44, Filter=0
  |       |___|       --->  /       '..'     CUSTOM wave-form
  |                        '
   ___     __
  |   |___|  |    _         \ ! /  .  \ ! /  Nibbles=77DE9HZK, Filter=V
  |_     ____|  _|    --->  - + -  +  - + -  SOLAR STORM wave-form
  __|   |______|___         / ! \  '  / ! \



SPU Volume and ADSR Generator

1F801C08h+N*10h - Voice 0..23 Attack/Decay/Sustain/Release (ADSR) (32bit)
  ____lower 16bit (at 1F801C08h+N*10h)___________________________________
  15    Attack Mode       (0=Linear, 1=Exponential)
  -     Attack Direction  (Fixed, always Increase) (until Level 7FFFh)
  14-10 Attack Shift      (0..1Fh = Fast..Slow)
  9-8   Attack Step       (0..3 = "+7,+6,+5,+4")
  -     Decay Mode        (Fixed, always Exponential)
  -     Decay Direction   (Fixed, always Decrease) (until Sustain Level)
  7-4   Decay Shift       (0..0Fh = Fast..Slow)
  -     Decay Step        (Fixed, always "-8")
  3-0   Sustain Level     (0..0Fh)  ;Level=(N+1)*800h
  ____upper 16bit (at 1F801C0Ah+N*10h)___________________________________
  31    Sustain Mode      (0=Linear, 1=Exponential)
  30    Sustain Direction (0=Increase, 1=Decrease) (until Key OFF flag)
  29    Not used?         (should be zero)
  28-24 Sustain Shift     (0..1Fh = Fast..Slow)
  23-22 Sustain Step      (0..3 = "+7,+6,+5,+4" or "-8,-7,-6,-5") (inc/dec)
  21    Release Mode      (0=Linear, 1=Exponential)
  -     Release Direction (Fixed, always Decrease) (until Level 0000h)
  20-16 Release Shift     (0..1Fh = Fast..Slow)
  -     Release Step      (Fixed, always "-8")
The Attack phase gets started when the software sets the voice ON flag (see below), the hardware does then automatically go through Attack/Decay/Sustain, and switches from Sustain to Release when the software sets the Key OFF flag.

1F801D80h - Mainvolume left
1F801D82h - Mainvolume right
1F801C00h+N*10h - Voice 0..23 Volume Left
1F801C02h+N*10h - Voice 0..23 Volume Right
Fixed Volume Mode (when Bit15=0):
  15    Must be zero      (0=Volume Mode)
  0-14  Voice volume/2    (-4000h..+3FFFh = Volume -8000h..+7FFEh)
Sweep Volume Mode (when Bit15=1):
  15    Must be set       (1=Sweep Mode)
  14    Sweep Mode        (0=Linear, 1=Exponential)
  13    Sweep Direction   (0=Increase, 1=Decrease)
  12    Sweep Phase       (0=Positive, 1=Negative)
  7-11  Not used?         (should be zero)
  6-2   Sweep Shift       (0..1Fh = Fast..Slow)
  1-0   Sweep Step        (0..3 = "+7,+6,+5,+4" or "-8,-7,-6,-5") (inc/dec)
Sweep is another Volume envelope, additionally to the ADSR volume envelope (unlike ADSR, sweep can be used for stereo effects, such like blending from left to right).
Sweep starts at the current volume (which can be set via Bit15=0, however, caution - the Bit15=0 setting isn't applied until the next 44.1kHz cycle; so setting the initial level with Bit15=0, followed by the sweep parameter with Bit15=1 works only if there's a suitable delay between the two operations). Once when sweep is started, the current volume level increases to +7FFFh, or decreases to 0000h.
Sweep Phase should be equal to the sign of the current volume (not yet tested, in the negative mode it does probably "increase" to -7FFFh?). The Phase bit seems to have no effect in Exponential Decrease mode.

1F801DB0h - CD Audio Input Volume (for normal CD-DA, and compressed XA-ADPCM)
1F801DB4h - External Audio Input Volume
  0-15  Volume Left   (-8000h..+7FFFh)
  16-31 Volume Right  (-8000h..+7FFFh)
Note: The CDROM controller supports additional CD volume control (including ability to convert stereo CD output to mono, or to swap left/right channels).

Envelope Operation depending on Shift/Step/Mode/Direction
  AdsrCycles = 1 SHL Max(0,ShiftValue-11)
  AdsrStep = StepValue SHL Max(0,11-ShiftValue)
  IF exponential AND increase AND AdsrLevel>6000h THEN AdsrCycles=AdsrCycles*4
  IF exponential AND decrease THEN AdsrStep=AdsrStep*AdsrLevel/8000h
  Wait(AdsrCycles)              ;cycles counted at 44.1kHz clock
  AdsrLevel=AdsrLevel+AdsrStep  ;saturated to 0..+7FFFh
Exponential Increase is a fake (simply changes to a slower linear increase rate at higher volume levels).

1F801C0Ch+N*10h - Voice 0..23 Current ADSR volume (R/W)
  15-0  Current ADSR Volume  (0..+7FFFh) (or -8000h..+7FFFh on manual write)
Reportedly Release can go down to -1 (FFFFh), but that isn't true; and release ends at 0... or does THAT depend on an END flag found in the sample-data?
The register is read/writeable, writing allows to let the ADSR generator to "jump" to a specific volume level. But, ACTUALLY, the ADSR generator does overwrite the setting (from another internal register) whenever applying a new Step?!

1F801DB8h - Current Main Volume Left/Right
1F801E00h+voice*04h - Voice 0..23 Current Volume Left/Right
  0-15  Current Volume Left  (-8000h..+7FFFh)
  16-31 Current Volume Right (-8000h..+7FFFh)
These are internal registers, normally not used by software (the Volume settings are usually set via Ports 1F801D80h and 1F801C00h+N*10h).

Note
Negative volumes are phase inverted, otherwise same as positive.


SPU Voice Flags

1F801D88h - Voice 0..23 Key ON (Start Attack/Decay/Sustain) (KON) (W)
  0-23  Voice 0..23 On  (0=No change, 1=Start Attack/Decay/Sustain)
  24-31 Not used
Starts the ADSR Envelope, and automatically initializes ADSR Volume to zero, and copies Voice Start Address to Voice Repeat Address.

1F801D8Ch - Voice 0..23 Key OFF (Start Release) (KOFF) (W)
  0-23  Voice 0..23 Off (0=No change, 1=Start Release)
  24-31 Not used
For a full ADSR pattern, OFF would be usually issued in the Sustain period, however, it can be issued at any time (eg. to abort Attack, skip the Decay and Sustain periods, and switch immediately to Release).

1F801D9Ch - Voice 0..23 ON/OFF (status) (ENDX) (R)
  0-23  Voice 0..23 Status (0=Newly Keyed On, 1=Reached LOOP-END)
  24-31 Not used
The bits get CLEARED when setting the corresponding KEY ON bits.
The bits get SET when reaching an LOOP-END flag in ADPCM header.bit0.

R/W
Key On and Key Off should be treated as write-only (although, reading returns the most recently 32bit value, this doesn't doesn't provide any status information about whether sound is on or off).
The on/off (status) (ENDX) register should be treated read-only (writing is possible in so far that the written value can be read-back for a short moment, however, thereafter the hardware is overwriting that value).


SPU Noise Generator

1F801D94h - Voice 0..23 Noise mode enable (NON)
  0-23  Voice 0..23 Noise (0=ADPCM, 1=Noise)
  24-31 Not used

SPU Noise Generator
The signed 16bit output Level is calculated as so (repeated at 44.1kHz clock):
  Wait(1 cycle)          ;at 44.1kHz clock
  Timer=Timer-NoiseStep  ;subtract Step (4..7)
  ParityBit = NoiseLevel.Bit15 xor Bit12 xor Bit11 xor Bit10 xor 1
  IF Timer<0 then NoiseLevel = NoiseLevel*2 + ParityBit
  IF Timer<0 then Timer=Timer+(20000h SHR NoiseShift)  ;reload timer once
  IF Timer<0 then Timer=Timer+(20000h SHR NoiseShift)  ;reload again if needed
Note that the Noise frequency is solely controlled by the Shift/Step values in SPUCNT register (the ADPCM Sample Rate has absolutely no effect on noise), so when using noise for multiple voices, all of them are forcefully having the same frequency; the only workaround is to store a random ADPCM pattern in SPU RAM, which can be then used with any desired sample rate(s).


SPU Control and Status Register

1F801DAAh - SPU Control Register (SPUCNT)
  15    SPU Enable              (0=Off, 1=On)       (Don't care for CD Audio)
  14    Mute SPU                (0=Mute, 1=Unmute)  (Don't care for CD Audio)
  13-10 Noise Frequency Shift   (0..0Fh = Low .. High Frequency)
  9-8   Noise Frequency Step    (0..03h = Step "4,5,6,7")
  7     Reverb Master Enable    (0=Disabled, 1=Enabled)
  6     IRQ9 Enable (0=Disabled/Acknowledge, 1=Enabled; only when Bit15=1)
  5-4   Sound RAM Transfer Mode (0=Stop, 1=ManualWrite, 2=DMAwrite, 3=DMAread)
  3     External Audio Reverb   (0=Off, 1=On)
  2     CD Audio Reverb         (0=Off, 1=On) (for CD-DA and XA-ADPCM)
  1     External Audio Enable   (0=Off, 1=On)
  0     CD Audio Enable         (0=Off, 1=On) (for CD-DA and XA-ADPCM)
Changes to bit0-5 aren't applied immediately; after writing to SPUCNT, it'd be usually recommended to wait until the LSBs of SPUSTAT are updated accordingly. Before setting a new Transfer Mode, it'd be recommended first to set the "Stop" mode (and, again, wait until Stop is applied in SPUSTAT).

1F801DAEh - SPU Status Register (SPUSTAT) (R)
  15-12 Unknown/Unused (seems to be usually zero)
  11    Writing to First/Second half of Capture Buffers (0=First, 1=Second)
  10    Data Transfer Busy Flag          (0=Ready, 1=Busy)
  9     Data Transfer DMA Read Request   (0=No, 1=Yes)
  8     Data Transfer DMA Write Request  (0=No, 1=Yes)
  7     Data Transfer DMA Read/Write Request ;seems to be same as SPUCNT.Bit5
  6     IRQ9 Flag                        (0=No, 1=Interrupt Request)
  5-0   Current SPU Mode   (same as SPUCNT.Bit5-0, but, applied a bit delayed)
When switching SPUCNT to DMA-read mode, status bit9 and bit7 aren't set immediately (apparently the SPU is first internally collecting the data in the Fifo, before transferring it).
Bit11 indicates if data is currently written to the first or second half of the four 1K-byte capture buffers (for CD Audio left/right, and voice 1/3). Note: Bit11 works only if Bit2 and/or Bit3 of Port 1F801DACh are set.
The SPUSTAT register should be treated read-only (writing is possible in so far that the written value can be read-back for a short moment, however, thereafter the hardware is overwriting that value).


SPU Memory Access

1F801DA6h - Sound RAM Data Transfer Address
  15-0  Address in sound buffer divided by eight
Used for manual write and DMA read/write SPU memory. Writing to this registers stores the written value in 1F801DA6h, and does additional store the value (multiplied by 8) in another internal "current address" register (that internal register does increment during transfers, whilst the 1F801DA6h value DOESN'T increment).

1F801DA8h - Sound RAM Data Transfer Fifo
  15-0  Data (max 32 halfwords)
Used for manual-write. Not sure if it can be also used for manual read?

1F801DACh - Sound RAM Data Transfer Control (should be 0004h)
  15-4   Unknown/no effect?                       (should be zero)
  3-1    Sound RAM Data Transfer Type (see below) (should be 2)
  0      Unknown/no effect?                       (should be zero)
The Transfer Type selects how data is forwarded from Fifo to SPU RAM:
  __Transfer Type___Halfwords in Fifo________Halfwords written to SPU RAM__
  0,1,6,7  Fill     A,B,C,D,E,F,G,H,...,X    X,X,X,X,X,X,X,X,...
  2        Normal   A,B,C,D,E,F,G,H,...,X    A,B,C,D,E,F,G,H,...
  3        Rep2     A,B,C,D,E,F,G,H,...,X    A,A,C,C,E,E,G,G,...
  4        Rep4     A,B,C,D,E,F,G,H,...,X    A,A,A,A,E,E,E,E,...
  5        Rep8     A,B,C,D,E,F,G,H,...,X    H,H,H,H,H,H,H,H,...
Rep2 skips the 2nd halfword, Rep4 skips 2nd..4th, Rep8 skips 1st..7th.
Fill uses only the LAST halfword in Fifo, that might be useful for memfill purposes, although, the length is probably determined by the number of writes to the Fifo (?) so one must still issue writes for ALL halfwords...?
Note:
The above rather bizarre results apply to WRITE mode. In READ mode, the register causes the same halfword to be read 2/4/8 times (for rep2/4/8).

SPU RAM Manual Write
- Be sure that [1F801DACh] is set to 0004h
- Set SPUCNT to "Stop" (and wait until it is applied in SPUSTAT)
- Set the transfer address
- Write 1..32 halfword(s) to the Fifo
- Set SPUCNT to "Manual Write" (and wait until it is applied in SPUSTAT)
- Wait until Transfer Busy in SPUSTAT goes off (that, AFTER above apply-wait)
For multi-block transfers: Repeat the above last three steps (that is rarely done by any games, but it is done by the BIOS intro; observe that waiting for SPUCNT writes being applied in SPUSTAT won't work in that case (since SPUCNT was already in manual write mode from previous block), so one must instead use some hardcoded delay of at least 300h cycles; the BIOS is using a much longer bizarre delay though).

SPU RAM DMA-Write
- Be sure that [1F801DACh] is set to 0004h
- Set SPUCNT to "Stop" (and wait until it is applied in SPUSTAT)
- Set the transfer address
- Set SPUCNT to "DMA Write" (and wait until it is applied in SPUSTAT)
- Start DMA4 at CPU Side (blocksize=10h, control=01000201h)
- Wait until DMA4 finishes (at CPU side)

SPU RAM Manual-Read
As by now, there's no known method for reading SPU RAM without using DMA.

SPU RAM DMA-Read (stable reading, with [1F801014h].bit24-27 = nonzero)
- Be sure that [1F801014h] is set to 220931E1h (bit24-27 MUST be nonzero)
- Be sure that [1F801DACh] is set to 0004h
- Set SPUCNT to "Stop" (and wait until it is applied in SPUSTAT)
- Set the transfer address
- Set SPUCNT to "DMA Read" (and wait until it is applied in SPUSTAT)
- Start DMA4 at CPU Side (blocksize=10h, control=01000200h)
- Wait until DMA4 finishes (at CPU side)

SPU RAM DMA-Read (unstable reading, with [1F801014h].bit24-27 = zero)
Below describes some dirt effects and some trickery to get around those dirt effects.
  Below problems (and workarounds) apply ONLY if [1F801014h].bit24-27 = zero.
  Ie. below info describes what happens when [1F801014h] is mis-initialized.
  Normally one should set [1F801014h]=220931E1h (and can ignore below info).
With [1F801014h].bit24-27=zero, reading SPU RAM via DMA works glitchy:
The first received halfword within each block is FFFFh. So with a DMA blocksize of 10h words (=20h halfwords), the following is received:
  1st block:   FFFFh, halfwords[00h..1Eh]
  2nd block:   FFFFh, halfwords[20h..3Eh]
  etc.
that'd theoretically match the SPU Fifo Size, but, because of the inserted FFFFh value, the last Fifo entry isn't received, ie. halfword[1Fh,3Fh] are lost. As a workaround, one can increase the DMA blocksize to 11h words, and then the following is received:
  1st block:   FFFFh, halfwords[00h..1Eh], twice halfword[1Fh]
  2nd block:   FFFFh, halfwords[20h..3Eh], twice halfword[3Fh]
  etc.
this time, all data is received, but after the transfer one must still remove the FFFFh values, and the duplicated halfwords by software. Aside from the <inserted> FFFFh values there are occassionaly some unstable halfwords ORed by FFFFh (or ORed by other garbage values), this can be fixed by using "rep2" mode, which does then receive:
  1st block:   FFFFh, halfwords[00h,00h,..0Eh,0Eh], triple halfword[0Fh]
  2nd block:   FFFFh, halfwords[10h,10h,..1Eh,1Eh], triple halfword[1Fh]
  etc.
again, remove the first halfword (FFFFh) and the last halfword, and, take the duplicated halfwords ANDed together. Unstable values occur only every 32 halfwords or so (probably when the SPU is simultaneously reading ADPCM data), but do never occur on two continous halfwords, so, even if one halfword was ORed by garbage, the other halfword is always correct, and the result of the ANDed halfwords is 100% stable.
Note: The unstable reading does NOT occur always, when resetting the PSX a couple of times it does occassionally boot-up with totally stable reading, since there is no known way to activate the stable "mode" via I/O ports, the stable/unstable behaviour does eventually depend on internal clock dividers/multipliers, and whether they are starting in sync with the CPU or not.
Caution: The "rep2" trick cannot be used in combination with reverb (reverb seems to be using the Port 1F801DACh Sound RAM Data Transfer Control, too).


SPU Interrupt

1F801DA4h - Sound RAM IRQ Address (IRQ9)
  15-0  Address in sound buffer divided by eight
See also: SPUCNT (IRQ enable/disable/acknowledge) and SPUSTAT (IRQ flag).

Voice Interrupt
Triggers an IRQ when a voice reads ADPCM data from the IRQ address.
Mind that ADPCM cannot be stopped (uh, except, probably they CAN be stopped, by setting the sample rate to zero?), all voices are permanently reading data from SPU RAM - even in Noise mode, even if the Voice Volume is zero, and even if the ADSR pattern has finished the Release period - so even inaudible voices can trigger IRQs. To prevent unwanted IRQs, best set all unused voices to an endless looped dummy ADPCM block.
For stable IRQs, the IRQ address should be aligned to the 16-byte ADPCM blocks. If if the IRQ address is in the middle of a 16-byte ADPCM block, then the IRQ doesn't seem to trigger always (unknown why, but it seems to occassionally miss IRQs, even if the block gets repeated several times).

Capture Interrupt
Setting the IRQ address to 0000h..01FFh (aka byte address 00000h..00FFFh) will trigger IRQs on writes to the four capture buffers. Each of the four buffers contains 400h bytes (=200h samples), so the IRQ rate will be around 86.13Hz (44100Hz/200h).
CD-Audio capture is always active (even if CD-Audio output is disabld in SPUCNT, and even if the drive door is open). Voice capture is (probably) also always active (even if the corresponding voice is off).
Capture IRQs do NOT occur if 1F801DACh.bit3-2 are both zero.

Reverb Interrupt
Reverb is also triggering interrupts if the IRQ address is located in the reverb buffer area. Unknown <which> of the various reverb read(s) and/or reverb write(s) are triggering interrupts.

Data Transfers
Data Transfers (usually via DMA4) to/from SPU-RAM do also trap SPU interrupts.

Note
IRQ Address is used by Metal Gear Solid, Legend of Mana, Tokimeki Memorial 2, Crash Team Racing, The Misadventures of Tron Bonne, and (somewhat?) by Need For Speed 3.


SPU Reverb Registers

Reverb Volume and Address Registers (R/W)
  Port      Reg   Name    Type    Expl.
  1F801D84h spu   vLOUT   volume  Reverb Output Volume Left
  1F801D86h spu   vROUT   volume  Reverb Output Volume Right
  1F801DA2h spu   mBASE   base    Reverb Work Area Start Address in Sound RAM
  1F801DC0h rev00 dAPF1   disp    Reverb APF Offset 1
  1F801DC2h rev01 dAPF2   disp    Reverb APF Offset 2
  1F801DC4h rev02 vIIR    volume  Reverb Reflection Volume 1
  1F801DC6h rev03 vCOMB1  volume  Reverb Comb Volume 1
  1F801DC8h rev04 vCOMB2  volume  Reverb Comb Volume 2
  1F801DCAh rev05 vCOMB3  volume  Reverb Comb Volume 3
  1F801DCCh rev06 vCOMB4  volume  Reverb Comb Volume 4
  1F801DCEh rev07 vWALL   volume  Reverb Reflection Volume 2
  1F801DD0h rev08 vAPF1   volume  Reverb APF Volume 1
  1F801DD2h rev09 vAPF2   volume  Reverb APF Volume 2
  1F801DD4h rev0A mLSAME  src/dst Reverb Same Side Reflection Address 1 Left
  1F801DD6h rev0B mRSAME  src/dst Reverb Same Side Reflection Address 1 Right
  1F801DD8h rev0C mLCOMB1 src     Reverb Comb Address 1 Left
  1F801DDAh rev0D mRCOMB1 src     Reverb Comb Address 1 Right
  1F801DDCh rev0E mLCOMB2 src     Reverb Comb Address 2 Left
  1F801DDEh rev0F mRCOMB2 src     Reverb Comb Address 2 Right
  1F801DE0h rev10 dLSAME  src     Reverb Same Side Reflection Address 2 Left
  1F801DE2h rev11 dRSAME  src     Reverb Same Side Reflection Address 2 Right
  1F801DE4h rev12 mLDIFF  src/dst Reverb Different Side Reflect Address 1 Left
  1F801DE6h rev13 mRDIFF  src/dst Reverb Different Side Reflect Address 1 Right
  1F801DE8h rev14 mLCOMB3 src     Reverb Comb Address 3 Left
  1F801DEAh rev15 mRCOMB3 src     Reverb Comb Address 3 Right
  1F801DECh rev16 mLCOMB4 src     Reverb Comb Address 4 Left
  1F801DEEh rev17 mRCOMB4 src     Reverb Comb Address 4 Right
  1F801DF0h rev18 dLDIFF  src     Reverb Different Side Reflect Address 2 Left
  1F801DF2h rev19 dRDIFF  src     Reverb Different Side Reflect Address 2 Right
  1F801DF4h rev1A mLAPF1  src/dst Reverb APF Address 1 Left
  1F801DF6h rev1B mRAPF1  src/dst Reverb APF Address 1 Right
  1F801DF8h rev1C mLAPF2  src/dst Reverb APF Address 2 Left
  1F801DFAh rev1D mRAPF2  src/dst Reverb APF Address 2 Right
  1F801DFCh rev1E vLIN    volume  Reverb Input Volume Left
  1F801DFEh rev1F vRIN    volume  Reverb Input Volume Right
All volume registers are signed 16bit (range -8000h..+7FFFh).
All src/dst/disp/base registers are addresses in SPU memory (divided by 8), src/dst are relative to the current buffer address, the disp registers are relative to src registers, the base register defines the start address of the reverb buffer (the end address is fixed, at 7FFFEh). Writing a value to mBASE does additionally set the current buffer address to that value.

1F801D98h - Voice 0..23 Reverb mode aka Echo On (EON) (R/W)
  0-23  Voice 0..23 Destination (0=To Mixer, 1=To Mixer and to Reverb)
  24-31 Not used
Sets reverb for the channel. As soon as the sample ends, the reverb for that channel is turned off... that's fine, but WHEN does it end?
In Reverb mode, the voice seems to output BOTH normal (immediately) AND via Reverb (delayed).

Reverb Bits in SPUCNT Register (R/W)
The SPUCNT register contains a Reverb Master Enable flag, and Reverb Enable flags for External Audio input and CD Audio input.
When the Reverb Master Enable flag is cleared, the SPU stops to write any data to the Reverb buffer (that is useful when zero-filling the reverb buffer; ensuring that already-zero values aren't overwritten by still-nonzero values).
However, the Reverb Master Enable flag does not disable output from Reverb buffer to the speakers (that might be useful to output uncompressed 22050Hz samples) (otherwise, to disable the buffer output, set the Reverb Output volume to zero and/or zerofill the reverb buffer).


SPU Reverb Formula

Reverb Formula
  ___Input from Mixer (Input volume multiplied with incoming data)_____________
  Lin = vLIN * LeftInput    ;from any channels that have Reverb enabled
  Rin = vRIN * RightInput   ;from any channels that have Reverb enabled
  ____Same Side Reflection (left-to-left and right-to-right)___________________
  [mLSAME] = (Lin + [dLSAME]*vWALL - [mLSAME-2])*vIIR + [mLSAME-2]  ;L-to-L
  [mRSAME] = (Rin + [dRSAME]*vWALL - [mRSAME-2])*vIIR + [mRSAME-2]  ;R-to-R
  ___Different Side Reflection (left-to-right and right-to-left)_______________
  [mLDIFF] = (Lin + [dRDIFF]*vWALL - [mLDIFF-2])*vIIR + [mLDIFF-2]  ;R-to-L
  [mRDIFF] = (Rin + [dLDIFF]*vWALL - [mRDIFF-2])*vIIR + [mRDIFF-2]  ;L-to-R
  ___Early Echo (Comb Filter, with input from buffer)__________________________
  Lout=vCOMB1*[mLCOMB1]+vCOMB2*[mLCOMB2]+vCOMB3*[mLCOMB3]+vCOMB4*[mLCOMB4]
  Rout=vCOMB1*[mRCOMB1]+vCOMB2*[mRCOMB2]+vCOMB3*[mRCOMB3]+vCOMB4*[mRCOMB4]
  ___Late Reverb APF1 (All Pass Filter 1, with input from COMB)________________
  Lout=Lout-vAPF1*[mLAPF1-dAPF1], [mLAPF1]=Lout, Lout=Lout*vAPF1+[mLAPF1-dAPF1]
  Rout=Rout-vAPF1*[mRAPF1-dAPF1], [mRAPF1]=Rout, Rout=Rout*vAPF1+[mRAPF1-dAPF1]
  ___Late Reverb APF2 (All Pass Filter 2, with input from APF1)________________
  Lout=Lout-vAPF2*[mLAPF2-dAPF2], [mLAPF2]=Lout, Lout=Lout*vAPF2+[mLAPF2-dAPF2]
  Rout=Rout-vAPF2*[mRAPF2-dAPF2], [mRAPF2]=Rout, Rout=Rout*vAPF2+[mRAPF2-dAPF2]
  ___Output to Mixer (Output volume multiplied with input from APF2)___________
  LeftOutput  = Lout*vLOUT
  RightOutput = Rout*vROUT
  ___Finally, before repeating the above steps_________________________________
  BufferAddress = MAX(mBASE, (BufferAddress+2) AND 7FFFEh)
  Wait one 22050Hz cycle, then repeat the above stuff

Notes
The values written to memory are saturated to -8000h..+7FFFh.
The multiplication results are divided by +8000h, to fit them to 16bit range.
All memory addresses are relative to the current BufferAddress, and wrapped within mBASE..7FFFEh when exceeding that region.
All data in the Reverb buffer consists of signed 16bit samples. The Left and Right Reverb Buffer addresses should be choosen so that one half of the buffer contains Left samples, and the other half Right samples (ie. the data is L,L,L,L,... R,R,R,R,...; it is NOT interlaced like L,R,L,R,...), during operation, when the buffer address increases, the Left half will overwrite the older samples of the Right half, and vice-versa.
The reverb hardware spends one 44100h cycle on left calculations, and the next 44100h cycle on right calculations (unlike as shown in the above formula, where left/right are shown simultaneously at 22050Hz).

Reverb Disable
SPUCNT.bit7 disables writes to reverb buffer, but reads from reverb buffer do still occur. If vAPF2 is zero then it does simply read "Lout=[mLAPF2-dAPF2]" and "Rout=[mRAPF2-dAPF2]". If vAPF2 is nonzero then it does additionally use data from APF1, if vAPF1 and vAPF2 are both nonzero then it's also using data from COMB. However, the SAME/DIFF stages aren't used when reverb is disabled.

Bug
vIIR works only in range -7FFFh..+7FFFh. When set to -8000h, the multiplication by -8000h is still done correctly, but, the final result (the value written to memory) gets negated (this is a pretty strange feature, it is NOT a simple overflow bug, it does affect the "+[mLSAME-2]" addition; although that part normally shouldn't be affected by the "*vIIR" multiplication). Similar effects might (?) occur on some other volume registers when they are set to -8000h.

Speed of Sound
The speed of sound is circa 340 meters per second (in dry air, at room temperature). For example, a voice that travels to a wall at 17 meters distance, and back to its origin, should have a delay of 0.1 seconds.


SPU Reverb Examples

Reverb Examples
Below are some Reverb examples, showing the required memory size (ie. set Port 1F801DA2h to "(80000h-size)/8"), and the Reverb register settings for Port 1F801DC0h..1F801DFFh, ie. arranged like so:
  dAPF1  dAPF2  vIIR   vCOMB1 vCOMB2  vCOMB3  vCOMB4  vWALL   ;1F801DC0h..CEh
  vAPF1  vAPF2  mLSAME mRSAME mLCOMB1 mRCOMB1 mLCOMB2 mRCOMB2 ;1F801DD0h..DEh
  dLSAME dRSAME mLDIFF mRDIFF mLCOMB3 mRCOMB3 mLCOMB4 mRCOMB4 ;1F801DE0h..EEh
  dLDIFF dRDIFF mLAPF1 mRAPF1 mLAPF2  mRAPF2  vLIN    vRIN    ;1F801DF0h..FEh
Also, don't forget to initialize Port 1F801D84h, 1F801D86h, 1F801D98h, and SPUCNT, and to zerofill the Reverb Buffer (so that no garbage values are output when activating reverb). For whatever reason, one MUST also initialize Port 1F801DACh (otherwise reverb stays off).

Room (size=26C0h bytes)
  007Dh,005Bh,6D80h,54B8h,BED0h,0000h,0000h,BA80h
  5800h,5300h,04D6h,0333h,03F0h,0227h,0374h,01EFh
  0334h,01B5h,0000h,0000h,0000h,0000h,0000h,0000h
  0000h,0000h,01B4h,0136h,00B8h,005Ch,8000h,8000h

Studio Small (size=1F40h bytes)
  0033h,0025h,70F0h,4FA8h,BCE0h,4410h,C0F0h,9C00h
  5280h,4EC0h,03E4h,031Bh,03A4h,02AFh,0372h,0266h
  031Ch,025Dh,025Ch,018Eh,022Fh,0135h,01D2h,00B7h
  018Fh,00B5h,00B4h,0080h,004Ch,0026h,8000h,8000h

Studio Medium (size=4840h bytes)
  00B1h,007Fh,70F0h,4FA8h,BCE0h,4510h,BEF0h,B4C0h
  5280h,4EC0h,0904h,076Bh,0824h,065Fh,07A2h,0616h
  076Ch,05EDh,05ECh,042Eh,050Fh,0305h,0462h,02B7h
  042Fh,0265h,0264h,01B2h,0100h,0080h,8000h,8000h

Studio Large (size=6FE0h bytes)
  00E3h,00A9h,6F60h,4FA8h,BCE0h,4510h,BEF0h,A680h
  5680h,52C0h,0DFBh,0B58h,0D09h,0A3Ch,0BD9h,0973h
  0B59h,08DAh,08D9h,05E9h,07ECh,04B0h,06EFh,03D2h
  05EAh,031Dh,031Ch,0238h,0154h,00AAh,8000h,8000h

Hall (size=ADE0h bytes)
  01A5h,0139h,6000h,5000h,4C00h,B800h,BC00h,C000h
  6000h,5C00h,15BAh,11BBh,14C2h,10BDh,11BCh,0DC1h
  11C0h,0DC3h,0DC0h,09C1h,0BC4h,07C1h,0A00h,06CDh
  09C2h,05C1h,05C0h,041Ah,0274h,013Ah,8000h,8000h

Half Echo (size=3C00h bytes)
  0017h,0013h,70F0h,4FA8h,BCE0h,4510h,BEF0h,8500h
  5F80h,54C0h,0371h,02AFh,02E5h,01DFh,02B0h,01D7h
  0358h,026Ah,01D6h,011Eh,012Dh,00B1h,011Fh,0059h
  01A0h,00E3h,0058h,0040h,0028h,0014h,8000h,8000h

Space Echo (size=F6C0h bytes)
  033Dh,0231h,7E00h,5000h,B400h,B000h,4C00h,B000h
  6000h,5400h,1ED6h,1A31h,1D14h,183Bh,1BC2h,16B2h
  1A32h,15EFh,15EEh,1055h,1334h,0F2Dh,11F6h,0C5Dh
  1056h,0AE1h,0AE0h,07A2h,0464h,0232h,8000h,8000h

Chaos Echo (almost infinite) (size=18040h bytes)
  0001h,0001h,7FFFh,7FFFh,0000h,0000h,0000h,8100h
  0000h,0000h,1FFFh,0FFFh,1005h,0005h,0000h,0000h
  1005h,0005h,0000h,0000h,0000h,0000h,0000h,0000h
  0000h,0000h,1004h,1002h,0004h,0002h,8000h,8000h

Delay (one-shot echo) (size=18040h bytes)
  0001h,0001h,7FFFh,7FFFh,0000h,0000h,0000h,0000h
  0000h,0000h,1FFFh,0FFFh,1005h,0005h,0000h,0000h
  1005h,0005h,0000h,0000h,0000h,0000h,0000h,0000h
  0000h,0000h,1004h,1002h,0004h,0002h,8000h,8000h

Reverb off (size=10h dummy bytes)
  0000h,0000h,0000h,0000h,0000h,0000h,0000h,0000h
  0000h,0000h,0001h,0001h,0001h,0001h,0001h,0001h
  0000h,0000h,0001h,0001h,0001h,0001h,0001h,0001h
  0000h,0000h,0001h,0001h,0001h,0001h,0000h,0000h
Note that the memory offsets should be 0001h here (not 0000h), otherwise zerofilling the reverb buffer seems to fail (maybe because zero memory offsets somehow cause the fill-value to mixed with the old value or so; that appears even when reverb master enable is zero). Also, when not using reverb, Port 1F801D84h, 1F801D86h, 1F801D98h, and the SPUCNT reverb bits should be set to zero.


SPU Unknown Registers

1F801DA0h - Some kind of a read-only status register.. or just garbage..?
  0-15 Unknown?
Usually 9D78h, occassionaly changes to 17DAh or 108Eh for a short moment.
Other day: Usually 9CF8h, or occassionally 9CFAh.
Another day: Usually 0000h, or occassionally 4000h.

1F801DBCh - 4 bytes - Unknown? (R/W)
  80 21 4B DF
Other day (dots = same as above):
  .. 31 .. ..

1F801E60h - 32 bytes - Unknown? (R/W)
  7E 61 A9 96 47 39 F9 1E E1 E1 80 DD E8 17 7F FB
  FB BF 1D 6C 8F EC F3 04 06 23 89 45 C1 6D 31 82
Other day (dots = same as above):
  .. .. .. .. .. .. .. .. .. .. .. .. .. .. 7B ..
  .. .. .. .. .. .. .. .. 04 .. .. .. .. .. .. 86

The bytes at 1F801DBCh and 1F801E60h usually have the above values on cold-boot. The registers are read/write-able, although writing any values to them doesn't seem to have any effect on sound output. Also, the SPU doesn't seem to modify the registers at any time during sound output, nor reverb calculations, nor activated external audio input... the registers seem to be just some kind of general-purpose RAM.


Interrupts

1F801070h I_STAT - Interrupt status register (R=Status, W=Acknowledge)
1F801074h I_MASK - Interrupt mask register (R/W)
Status: Read I_STAT (0=No IRQ, 1=IRQ)
Acknowledge: Write I_STAT (0=Clear Bit, 1=No change)
Mask: Read/Write I_MASK (0=Disabled, 1=Enabled)
  0     IRQ0 VBLANK (PAL=50Hz, NTSC=60Hz)
  1     IRQ1 GPU   Can be requested via GP0(1Fh) command (rarely used)
  2     IRQ2 CDROM
  3     IRQ3 DMA
  4     IRQ4 TMR0  Timer 0 aka Root Counter 0 (Sysclk or Dotclk)
  5     IRQ5 TMR1  Timer 1 aka Root Counter 1 (Sysclk or H-blank)
  6     IRQ6 TMR2  Timer 2 aka Root Counter 2 (Sysclk or Sysclk/8)
  7     IRQ7 Controller and Memory Card - Byte Received Interrupt
  8     IRQ8 SIO
  9     IRQ9 SPU
  10    IRQ10 Controller - Lightpen Interrupt (reportedly also PIO...?)
  11-15 Not used (always zero)
  16-31 Garbage

Secondary IRQ10 Controller (Port 1F802030h)
EXP2 DTL-H2000 I/O Ports

Interrupt Request / Execution
The interrupt request bits in I_STAT are edge-triggered, ie. the get set ONLY if the corresponding interrupt source changes from "false to true".
If one or more interrupts are requested and enabled, ie. if "(I_STAT AND I_MASK)=nonzero", then cop0r13.bit10 gets set, and when cop0r12.bit10 and cop0r12.bit0 are set, too, then the interrupt gets executed.

Interrupt Acknowledge
To acknowledge an interrupt, write a "0" to the corresponding bit in I_STAT. Most interrupts (except IRQ0,4,5,6) must be additionally acknowledged at the I/O port that has caused them (eg. JOY_CTRL.bit4).
Observe that the I_STAT bits are edge-triggered (they get set only on High-to-Low, or False-to-True edges). The correct acknowledge order is:
  First, acknowledge I_STAT                (eg. I_STAT.bit7=0)
  Then, acknowledge corresponding I/O port (eg. JOY_CTRL.bit4=1)
When doing it vice-versa, the hardware may miss further IRQs (eg. when first setting JOY_CTRL.4=1, then a new IRQ may occur in JOY_STAT.4 within a single clock cycle, thereafter, setting I_STAT.7=0 would successfully reset I_STAT.7, but, since JOY_STAT.4 is already set, there'll be no further edge, so I_STAT.7 won't be ever set in future).

COP0 Interrupt Handling
Relevant COP0 registers are cop0r13 (CAUSE, reason flags), and cop0r12 (SR, control flags), and cop0r14 (EPC, return address), and, cop0cmd=10h (aka RFE opcode) is used to prepare the return from interrupts. For more info, see
COP0 - Exception Handling

PSX specific COP0 Notes
COP0 has six hardware interrupt bits, of which, the PSX uses only cop0r13.bit10 (the other ones, cop0r13.bit11-15 are always zero). cop0r13.bit10 is NOT a latch, ie. it gets automatically cleared as soon as "(I_STAT AND I_MASK)=zero", so there's no need to do an acknowledge at the cop0 side. COP0 additionally has two software interrupt bits, cop0r13.bit8-9, which do exist in the PSX, too, these bits are read/write-able latches which can be set/cleared manually to request/acknowledge exceptions by software.

Halt Function (Wait for Interrupt)
The PSX doesn't have a HALT opcode, so, even if the program is merely waiting for an interrupt to occur, the CPU is always running at full speed, which is resulting in high power consumption, and, in case of emulators, high CPU emulation load. To save energy, and to make emulation smoother on slower computers, I've added a Halt function for use in emulators:
EXP2 Nocash Emulation Expansion


DMA Channels

DMA Register Summary
  1F80108xh DMA0 channel 0  MDECin  (RAM to MDEC)
  1F80109xh DMA1 channel 1  MDECout (MDEC to RAM)
  1F8010Axh DMA2 channel 2  GPU (lists + image data)
  1F8010Bxh DMA3 channel 3  CDROM   (CDROM to RAM)
  1F8010Cxh DMA4 channel 4  SPU
  1F8010Dxh DMA5 channel 5  PIO (Expansion Port)
  1F8010Exh DMA6 channel 6  OTC (reverse clear OT) (GPU related)
  1F8010F0h DPCR - DMA Control register
  1F8010F4h DICR - DMA Interrupt register
These ports control DMA at the CPU-side. In most cases, you'll additionally need to initialize an address (and transfer direction, transfer enabled, etc.) at the remote-side (eg. at the GPU-side for DMA2).

1F801080h+N*10h - D#_MADR - DMA base address (Channel 0..6) (R/W)
  0-23  Memory Address where the DMA will start reading from/writing to
  24-31 Not used (always zero)
In SyncMode=0, the hardware doesn't update the MADR registers (it will contain the start address even during and after the transfer) (unless Chopping is enabled, in that case it does update MADR, same does probably also happen when getting interrupted by a higher priority DMA channel).
In SyncMode=1 and SyncMode=2, the hardware does update MADR (it will contain the start address of the currently transferred block; at transfer end, it'll hold the end-address in SyncMode=1, or the 00FFFFFFh end-code in SyncMode=2)
Note: Address bit0-1 are writeable, but any updated current/end addresses are word-aligned with bit0-1 forced to zero.

1F801084h+N*10h - D#_BCR - DMA Block Control (Channel 0..6) (R/W)
For SyncMode=0 (ie. for OTC and CDROM):
  0-15  BC    Number of words (0001h..FFFFh) (or 0=10000h words)
  16-31 0     Not used (usually 0 for OTC, or 1 ("one block") for CDROM)
For SyncMode=1 (ie. for MDEC, SPU, and GPU-vram-data):
  0-15  BS    Blocksize (words) ;for GPU/SPU max 10h, for MDEC max 20h
  16-31 BA    Amount of blocks  ;ie. total length = BS*BA words
For SyncMode=2 (ie. for GPU-command-lists):
  0-31  0     Not used (should be zero) (transfer ends at END-CODE in list)
BC/BS/BA can be in range 0001h..FFFFh (or 0=10000h). For BS, take care not to set the blocksize larger than the buffer of the corresponding unit can hold. (GPU and SPU both have a 16-word buffer). A larger blocksize means faster transfer.
SyncMode=1 decrements BA to zero, SyncMode=0 with chopping enabled decrements BC to zero (aside from that two cases, D#_BCR isn't changed during/after transfer).

1F801088h+N*10h - D#_CHCR - DMA Channel Control (Channel 0..6) (R/W)
  0       Transfer Direction    (0=To Main RAM, 1=From Main RAM)
  1       Memory Address Step   (0=Forward;+4, 1=Backward;-4)
  2-7     Not used              (always zero)
  8       Chopping Enable       (0=Normal, 1=Chopping; run CPU during DMA gaps)
  9-10    SyncMode, Transfer Synchronisation/Mode (0-3):
            0  Start immediately and transfer all at once (used for CDROM, OTC)
            1  Sync blocks to DMA requests   (used for MDEC, SPU, and GPU-data)
            2  Linked-List mode              (used for GPU-command-lists)
            3  Reserved                      (not used)
  11-15   Not used              (always zero)
  16-18   Chopping DMA Window Size (1 SHL N words)
  19      Not used              (always zero)
  20-22   Chopping CPU Window Size (1 SHL N clks)
  23      Not used              (always zero)
  24      Start/Busy            (0=Stopped/Completed, 1=Start/Enable/Busy)
  25-27   Not used              (always zero)
  28      Start/Trigger         (0=Normal, 1=Manual Start; use for SyncMode=0)
  29      Unknown (R/W) Pause?  (0=No, 1=Pause?)     (For SyncMode=0 only?)
  30      Unknown (R/W)
  31      Not used              (always zero)
The Start/Trigger bit is automatically cleared upon BEGIN of the transfer, this bit needs to be set only in SyncMode=0 (setting it in other SyncModes would force the first block to be transferred instantly without DRQ, which isn't desired).
The Start/Busy bit is automatically cleared upon COMPLETION of the transfer, this bit must be always set for all SyncModes when starting a transfer.
For DMA6/OTC there are some restrictions, D6_CHCR has only three read/write-able bits: Bit24,28,30. All other bits are read-only: Bit1 is always 1 (step=backward), and the other bits are always 0.

1F8010F0h - DPCR - DMA Control Register (R/W)
  0-2   DMA0, MDECin  Priority      (0..7; 0=Highest, 7=Lowest)
  3     DMA0, MDECin  Master Enable (0=Disable, 1=Enable)
  4-6   DMA1, MDECout Priority      (0..7; 0=Highest, 7=Lowest)
  7     DMA1, MDECout Master Enable (0=Disable, 1=Enable)
  8-10  DMA2, GPU     Priority      (0..7; 0=Highest, 7=Lowest)
  11    DMA2, GPU     Master Enable (0=Disable, 1=Enable)
  12-14 DMA3, CDROM   Priority      (0..7; 0=Highest, 7=Lowest)
  15    DMA3, CDROM   Master Enable (0=Disable, 1=Enable)
  16-18 DMA4, SPU     Priority      (0..7; 0=Highest, 7=Lowest)
  19    DMA4, SPU     Master Enable (0=Disable, 1=Enable)
  20-22 DMA5, PIO     Priority      (0..7; 0=Highest, 7=Lowest)
  23    DMA5, PIO     Master Enable (0=Disable, 1=Enable)
  24-26 DMA6, OTC     Priority      (0..7; 0=Highest, 7=Lowest)
  27    DMA6, OTC     Master Enable (0=Disable, 1=Enable)
  28-30 Unknown, Priority Offset or so? (R/W)
  31    Unknown, no effect? (R/W)
Initial value on reset is 07654321h. If two or more channels have the same priority setting, then the priority is determined by the channel number (DMA0=Lowest, DMA6=Highest).

1F8010F4h - DICR - DMA Interrupt Register (R/W)
  0-5   Unknown  (read/write-able)
  6-14  Not used (always zero)
  15    Force IRQ (sets bit31)                        (0=None, 1=Force Bit31=1)
  16-22 IRQ Enable setting bit24-30 upon DMA0..DMA6    (0=None, 1=Enable)
  23    IRQ Enable setting bit31 when bit24-30=nonzero (0=None, 1=Enable)
  24-30 IRQ Flags for DMA0..DMA6    (Write 1 to reset) (0=None, 1=IRQ)
  31    IRQ Signal (0-to-1 triggers 1F801070h.bit3)    (0=None, 1=IRQ) (R)
IRQ flags in Bit(24+n) are set upon DMAn completion - but caution - they are set ONLY if enabled in Bit(16+n).
Bit31 is a simple readonly flag that follows the following rules:
  IF bit15=1 OR (bit23=1 AND bit(24-30)>0) THEN bit31=1 ELSE bit31=0
Upon 0-to-1 transition of Bit31, the IRQ3 flag (in Port 1F801070h) gets set.
Bit24-30 are acknowledged (reset to zero) when writing a "1" to that bits (and, additionally, IRQ3 (DMA) must be acknowledged via Port 1F801070h).

1F8010F8h (usually 7FFAC68Bh? or 0BFAC688h)
    (changes to 7FE358D1h after DMA transfer)
1F8010FCh (usually 00FFFFF7h) (...maybe OTC fill-value)
    (stays so even after DMA transfer)
Contains strange read-only values (but not the usual "Garbage").
Not yet tested during transfer, might be remaining length and address?

Commonly used DMA Control Register values for starting DMA transfers
  DMA0 MDEC.IN  01000201h (always)
  DMA1 MDEC.OUT 01000200h (always)
  DMA2 GPU      01000200h (VramRead), 01000201h (VramWrite), 01000401h (List)
  DMA3 CDROM    11000000h (normal), 11400100h (chopped, rarely used)
  DMA4 SPU      01000201h (write), 01000200h (read, rarely used)
  DMA5 PIO      N/A       (not used by any known games)
  DMA6 OTC      11000002h (always)
XXX: DMA2 values 01000201h (VramWrite), 01000401h (List) aren't 100% confirmed to be used by ALL existing games. All other values are always used as listed above.

DMA Transfer Rates
  DMA0 MDEC.IN     1 clk/word   ;0110h clks per 100h words ;\plus whatever
  DMA1 MDEC.OUT    1 clk/word   ;0110h clks per 100h words ;/decompression time
  DMA2 GPU         1 clk/word   ;0110h clks per 100h words ;-plus ...
  DMA3 CDROM/BIOS  24 clks/word ;1800h clks per 100h words ;\plus single/double
  DMA3 CDROM/GAMES 40 clks/word ;2800h clks per 100h words ;/speed sector rate
  DMA4 SPU         4 clks/word  ;0420h clks per 100h words ;-plus ...
  DMA5 PIO         20 clks/word ;1400h clks per 100h words ;-not actually used
  DMA6 OTC         1 clk/word   ;0110h clks per 100h words ;-plus nothing
MDEC decompression time is still unknown (may vary on RLE and color/mono).
GPU polygon rendering time is unknown (may be quite slow for large polys).
GPU vram read/write time is unknown (may vary on horizontal screen resolution).
CDROM BIOS default is 24 clks, for some reason most games change it to 40 clks.
SPU transfer is unknown (may have some extra delays).
XXX is SPU really only 4 clks (theoretically SPU access should be slower)?
PIO isn't used by any games (and if used: could be configured to other rates)
OTC is just writing to RAM without extra overload.
CDROM/SPU/PIO timings can be configured via Memory Control registers.

DRAM Hyper Page mode
DMA is using DRAM Hyper Page mode, allowing it to access DRAM rows at 1 clock cycle per word (effectively around 17 clks per 16 words, due to required row address loading, probably plus some further minimal overload due to refresh cycles). This is making DMA much faster than CPU memory accesses (CPU DRAM access takes 1 opcode cycle plus 6 waitstates, ie. 7 cycles in total)

CPU Operation during DMA
Basically, the CPU is stopped during DMA (theoretically, the CPU could be kept running when accessing only cache, scratchpad and on-chip I/O ports like DMA registers, and during the CDROM/SPU/PIO waitstates it could even access Main RAM, but these situations aren't supported).
However, the CPU operation resumes during periods when DMA gets interrupted (ie. after SyncMode 1 blocks, after SyncMode 2 list entries) (or in SyncMode 0 with Chopping enabled).


Timers

1F801100h+N*10h - Timer 0..2 Current Counter Value (R/W)
  0-15  Current Counter value (incrementing)
  16-31 Garbage
This register is automatically incrementing. It is write-able (allowing to set it to any value). It gets forcefully reset to 0000h on any write to the Counter Mode register, and on counter overflow (either when exceeding FFFFh, or when exceeding the selected target value).

1F801104h+N*10h - Timer 0..2 Counter Mode (R/W)
  0     Synchronization Enable (0=Free Run, 1=Synchronize via Bit1-2)
  1-2   Synchronization Mode   (0-3, see lists below)
         Synchronization Modes for Counter 0:
           0 = Pause counter during Hblank(s)
           1 = Reset counter to 0000h at Hblank(s)
           2 = Reset counter to 0000h at Hblank(s) and pause outside of Hblank
           3 = Pause until Hblank occurs once, then switch to Free Run
         Synchronization Modes for Counter 1:
           Same as above, but using Vblank instead of Hblank
         Synchronization Modes for Counter 2:
           0 or 3 = Stop counter at current value (forever, no h/v-blank start)
           1 or 2 = Free Run (same as when Synchronization Disabled)
  3     Reset counter to 0000h  (0=After Counter=FFFFh, 1=After Counter=Target)
  4     IRQ when Counter=Target (0=Disable, 1=Enable)
  5     IRQ when Counter=FFFFh  (0=Disable, 1=Enable)
  6     IRQ Once/Repeat Mode    (0=One-shot, 1=Repeatedly)
  7     IRQ Pulse/Toggle Mode   (0=Short Bit10=0 Pulse, 1=Toggle Bit10 on/off)
  8-9   Clock Source (0-3, see list below)
         Counter 0:  0 or 2 = System Clock,  1 or 3 = Dotclock
         Counter 1:  0 or 2 = System Clock,  1 or 3 = Hblank
         Counter 2:  0 or 1 = System Clock,  2 or 3 = System Clock/8
  10    Interrupt Request       (0=Yes, 1=No) (Set after Writing)    (W=1) (R)
  11    Reached Target Value    (0=No, 1=Yes) (Reset after Reading)        (R)
  12    Reached FFFFh Value     (0=No, 1=Yes) (Reset after Reading)        (R)
  13-15 Unknown (seems to be always zero)
  16-31 Garbage (next opcode)
In one-shot mode, the IRQ is pulsed/toggled only once (one-shot mode doesn't stop the counter, it just suppresses any further IRQs until a new write to the Mode register occurs; if both IRQ conditions are enabled in Bit4-5, then one-shot mode triggers only one of those conditions; whichever occurs first).
Normally, Pulse mode should be used (Bit10 is permanently set, except for a few clock cycles when an IRQ occurs). In Toggle mode, Bit10 is set after writing to the Mode register, and becomes inverted on each IRQ (in one-shot mode, it remains zero after the IRQ) (in repeat mode it inverts Bit10 on each IRQ, so IRQ4/5/6 are triggered only each 2nd time, ie. when Bit10 changes from 1 to 0).

1F801108h+N*10h - Timer 0..2 Counter Target Value (R/W)
  0-15  Counter Target value
  16-31 Garbage
When the Target flag is set (Bit3 of the Control register), the counter increments up to (including) the selected target value, and does then restart at 0000h.

Dotclock/Hblank
For more info on dotclock and hblank timings, see:
GPU Timings
Caution: Reading the Current Counter Value can be a little unstable (when using dotclk or hblank as clock source); the GPU clock isn't in sync with the CPU clock, so the timer may get changed during the CPU read cycle. As a workaround: repeat reading the timer until the received value is the same (or slightly bigger) than the previous value.


CDROM Drive

Playstation CDROM I/O Ports
CDROM Controller I/O Ports

Playstation CDROM Commands
CDROM Controller Command Summary
CDROM - Control Commands
CDROM - Seek Commands
CDROM - Read Commands
CDROM - Status Commands
CDROM - CD Audio Commands
CDROM - Test Commands
CDROM - Secret Unlock Commands
CDROM - Video CD Commands
CDROM - Mainloop/Responses
CDROM - Response Timings
CDROM - Response/Data Queueing

General CDROM Disk Format
CDROM Disk Format
CDROM Subchannels
CDROM Sector Encoding
CDROM Scrambling
CDROM XA Subheader, File, Channel, Interleave
CDROM XA Audio ADPCM Compression
CDROM ISO Volume Descriptors
CDROM ISO File and Directory Descriptors
CDROM ISO Misc
CDROM File Formats
CDROM Video CDs (VCD)

Playstation CDROM Protection
CDROM Protection - SCEx Strings
CDROM Protection - Bypassing it
CDROM Protection - Modchips
CDROM Protection - Chipless Modchips
CDROM Protection - LibCrypt

General CDROM Disk Images
CDROM Disk Images CCD/IMG/SUB (CloneCD)
CDROM Disk Images CDI (DiscJuggler)
CDROM Disk Images CUE/BIN/CDT (Cdrwin)
CDROM Disk Images MDS/MDF (Alcohol 120%)
CDROM Disk Images NRG (Nero)
CDROM Disk Images PBP (Sony)
CDROM Disk Images CHD (MAME)
CDROM Disk Image/Containers CDZ
CDROM Disk Image/Containers ECM
CDROM Subchannel Images
CDROM Disk Images Other Formats

Playstation CDROM Coprocessor
CDROM Internal Info on PSX CDROM Controller


CDROM Controller I/O Ports

1F801800h - Index/Status Register (Bit0-1 R/W) (Bit2-7 Read Only)
  0-1 Index   Port 1F801801h-1F801803h index (0..3 = Index0..Index3)   (R/W)
  2   ADPBUSY XA-ADPCM fifo empty  (0=Empty) ;set when playing XA-ADPCM sound
  3   PRMEMPT Parameter fifo empty (1=Empty) ;triggered before writing 1st byte
  4   PRMWRDY Parameter fifo full  (0=Full)  ;triggered after writing 16 bytes
  5   RSLRRDY Response fifo empty  (0=Empty) ;triggered after reading LAST byte
  6   DRQSTS  Data fifo empty      (0=Empty) ;triggered after reading LAST byte
  7   BUSYSTS Command/parameter transmission busy  (1=Busy)
Bit3,4,5 are bound to 5bit counters; ie. the bits become true at specified amount of reads/writes, and thereafter once on every further 32 reads/writes.

1F801801h.Index0 - Command Register (W)
  0-7  Command Byte
Writing to this address sends the command byte to the CDROM controller, which will then read-out any Parameter byte(s) which have been previously stored in the Parameter Fifo. It takes a while until the command/parameters are transferred to the controller, and until the response bytes are received; once when completed, interrupt INT3 is generated (or INT5 in case of invalid command/parameter values), and the response (or error code) can be then read from the Response Fifo. Some commands additionally have a second response, which is sent with another interrupt.

1F801802h.Index0 - Parameter Fifo (W)
  0-7  Parameter Byte(s) to be used for next Command
Before sending a command, write any parameter byte(s) to this address.

1F801803h.Index0 - Request Register (W)
  0-4 0    Not used (should be zero)
  5   SMEN Want Command Start Interrupt on Next Command (0=No change, 1=Yes)
  6   BFWR ...
  7   BFRD Want Data         (0=No/Reset Data Fifo, 1=Yes/Load Data Fifo)

1F801802h.Index0..3 - Data Fifo - 8bit/16bit (R)
After ReadS/ReadN commands have generated INT1, software must set the Want Data bit (1F801803h.Index0.Bit7), then wait until Data Fifo becomes not empty (1F801800h.Bit6), the datablock (disk sector) can be then read from this register.
  0-7  Data 8bit  (one byte), or alternately,
  0-15 Data 16bit (LSB=First byte, MSB=Second byte)
The PSX hardware allows to read 800h-byte or 924h-byte sectors, indexed as [000h..7FFh] or [000h..923h], when trying to read further bytes, then the PSX will repeat the byte at index [800h-8] or [924h-4] as padding value.
Port 1F801802h can be accessed with 8bit or 16bit reads (ie. to read a 2048-byte sector, one can use 2048 load-byte opcodes, or 1024 load halfword opcodes, or, more conventionally, a 512 word DMA transfer; the actual CDROM databus is only 8bits wide, so CPU/DMA are apparently breaking 16bit/32bit reads into multiple 8bit reads from 1F801802h).

1F801801h.Index1 - Response Fifo (R)
1F801801h.Index0,2,3 - Response Fifo (R) (Mirrors)
  0-7  Response Byte(s) received after sending a Command
The response Fifo is a 16-byte buffer, most or all responses are less than 16 bytes, after reading the last used byte (or before reading anything when the response is 0-byte long), Bit5 of the Index/Status register becomes zero to indicate that the last byte was received.
When reading further bytes: The buffer is padded with 00h's to the end of the 16-bytes, and does then restart at the first response byte (that, without receiving a new response, so it'll always return the same 16 bytes, until a new command/response has been sent/received).

1F801802h.Index1 - Interrupt Enable Register (W)
1F801803h.Index0 - Interrupt Enable Register (R)
1F801803h.Index2 - Interrupt Enable Register (R) (Mirror)
  0-4  Interrupt Enable Bits (usually all set, ie. 1Fh=Enable All IRQs)
  5-7  Unknown/unused (write: should be zero) (read: usually all bits set)
XXX WRITE: bit5-7 unused should be 0 // READ: bit5-7 unused

1F801803h.Index1 - Interrupt Flag Register (R/W)
1F801803h.Index3 - Interrupt Flag Register (R) (Mirror)
  0-2   Read: Response Received   Write: 7=Acknowledge   ;INT1..INT7
  3     Read: Unknown (usually 0) Write: 1=Acknowledge   ;INT8  ;XXX CLRBFEMPT
  4     Read: Command Start       Write: 1=Acknowledge   ;INT10h;XXX CLRBFWRDY
  5     Read: Always 1 ;XXX "_"   Write: 1=Unknown              ;XXX SMADPCLR
  6     Read: Always 1 ;XXX "_"   Write: 1=Reset Parameter Fifo ;XXX CLRPRM
  7     Read: Always 1 ;XXX "_"   Write: 1=Unknown              ;XXX CHPRST
Writing "1" bits to bit0-4 resets the corresponding IRQ flags; normally one should write 07h to reset the response bits, or 1Fh to reset all IRQ bits. Writing values like 01h is possible (eg. that would change INT3 to INT2, but doing that would be total nonsense). After acknowledge, the Response Fifo is made empty, and if there's been a pending command, then that command gets send to the controller.
The lower 3bit indicate the type of response received,
  INT0   No response received (no interrupt request)
  INT1   Received SECOND (or further) response to ReadS/ReadN (and Play+Report)
  INT2   Received SECOND response (to various commands)
  INT3   Received FIRST response (to any command)
  INT4   DataEnd (when Play/Forward reaches end of disk) (maybe also for Read?)
  INT5   Received error-code (in FIRST or SECOND response)
         INT5 also occurs on SECOND GetID response, on unlicensed disks
         INT5 also occurs when opening the drive door (even if no command
            was sent, ie. even if no read-command or other command is active)
  INT6   N/A
  INT7   N/A
The other 2bit indicate something else,
  INT8   Unknown (never seen that bit set yet)
  INT10h Command Start (when INT10h requested via 1F801803h.Index0.Bit5)
The response interrupts are queued, for example, if the 1st response is INT3, and the second INT5, then INT3 is delivered first, and INT5 is not delivered until INT3 is acknowledged (ie. the response interrupts are NOT ORed together to produce INT7 or so). The upper bits however can be ORed with the lower bits (ie. Command Start INT10h and 1st Response INT3 would give INT13h).
Caution - Unstable IRQ Flag polling
IRQ flag changes aren't synced with the MIPS CPU clock. If more than one bit gets set (and the CPU is reading at the same time) then the CPU does occassionally see only one of the newly bits:
  0 ----------> 3   ;99.9%  normal case INT3's
  0 ----------> 5   ;99%    normal case INT5's
  0 ---> 1 ---> 3   ;0.1%   glitch: occurs about once per thousands of INT3's
  0 ---> 4 ---> 5   ;1%     glitch: occurs about once per hundreds of INT5's
As workaround, do something like:
 @@polling_lop:
  irq_flags = [1F801803h] AND 07h       ;<-- 1st read (may be still unstable)
  if irq_flags = 00h then goto @@polling_lop
  irq_flags = [1F801803h] AND 07h       ;<-- 2nd read (should be stable now)
  handle irq_flags and acknowledge them
The problem applies only when manually polling the IRQ flags (an actual IRQ handler will get triggered when the flags get nonzero, and the flags will have stabilized once when the IRQ handler is reading them) (except, a combination of IRQ10h followed by IRQ3 can also have unstable LSBs within the IRQ handler).
The problem occurs only on older consoles (like LATE-PU-8), not on newer consoles (like PSone).

1F801802h.Index2 - Audio Volume for Left-CD-Out to Left-SPU-Input (W)
1F801803h.Index2 - Audio Volume for Left-CD-Out to Right-SPU-Input (W)
1F801801h.Index3 - Audio Volume for Right-CD-Out to Right-SPU-Input (W)
1F801802h.Index3 - Audio Volume for Right-CD-Out to Left-SPU-Input (W)
Allows to configure the CD for mono/stereo output (eg. values "80h,0,80h,0" produce normal stereo volume, values "40h,40h,40h,40h" produce mono output of equivalent volume).
When using bigger values, the hardware does have some incomplete saturation support; the saturation works up to double volume (eg. overflows that occur on "FFh,0,FFh,0" or "80h,80h,80h,80h" are clipped to min/max levels), however, the saturation does NOT work properly when exceeding double volume (eg. mono with quad-volume "FFh,FFh,FFh,FFh").
  0-7  Volume Level (00h..FFh) (00h=Off, FFh=Max/Double, 80h=Default/Normal)
After changing these registers, write 20h to 1F801803h.Index3.
Unknown if any existing games are actually supporting mono output. Resident Evil 2 uses these ports to produce fade-in/fade-out effects (although, for that purpose, it should be much easier to use Port 1F801DB0h).

1F801803h.Index3 - Audio Volume Apply Changes (by writing bit5=1)
  0    ADPMUTE Mute ADPCM                 (0=Normal, 1=Mute)
  1-4  -       Unused (should be zero)
  5    CHNGATV Apply Audio Volume changes (0=No change, 1=Apply)
  6-7  -       Unused (should be zero)

1F801801h.Index1 - Sound Map Data Out (W)
  0-7  Data
This register seems to be restricted to 8bit bus, unknown if/how the PSX DMA controller can write to it (it might support only 16bit data for CDROM).

1F801801h.Index2 - Sound Map Coding Info (W)
  0    Mono/Stereo     (0=Mono, 1=Stereo)
  1    Reserved        (0)
  2    Sample Rate     (0=37800Hz, 1=18900Hz)
  3    Reserved        (0)
  4    Bits per Sample (0=4bit, 1=8bit)
  5    Reserved        (0)
  6    Emphasis        (0=Off, 1=Emphasis)
  7    Reserved        (0)

 =============================================================================

Command Execution
Command/Parameter transmission is indicated by bit7 of 1F801800h.
When that bit gets zero, the response can be read immediately (immediately for MOST commands, but not ALL commands; so better wait for the IRQ).
Alternately, you can wait for an IRQ (which seems to take place MUCH later), and then read the response.
If there are any pending cdrom interrupts, these MUST be acknowledged before sending the command (otherwise bit7 of 1F801800h will stay set forever).

Command Busy Flag - 1F801800h.Bit7
Indicates ready-to-send-new-command,
  0=Ready to send a new command
  1=Busy sending a command/parameters
Trying to send a new command in the Busy-phase causes malfunction (the older command seems to get lost, the newer command executes and returns its results and triggers an interrupt, but, thereafter, the controller seems to hang). So, always wait until the Busy-bit goes off before sending a command.
When the Busy-flag goes off, a new command can be send immediately (even if the response from the previous command wasn't received yet), however, the new command stays in the Busy-phase until the IRQ from the previous command is acknowledged, at that point the actual transmission of the new command starts, and the Busy-flag goes off (once when the transmission completes).

Misc
Trying to do a 32bit read from 1F801800h returns the 8bit value at 1F801800h multiplied by 01010101h.

To init the CD
  -Flush all IRQs
  -1F801803h.Index0=0
  -Com_Delay=4901 (=1325h) (Port 1F801020h) (means 16bit or 32bit write?)
     (the write seems to be 32bit, clearing the upper16bit of the register)
  -Send two Getstat commands
  -Send Command 0Ah (Init)
  -Demute

Seek-Busy Phase
Warning: most or all of the info in the sentence below appear to incorrect (either that, or I didn't understand that rather confusing sentence).
REPORTEDLY:
"You should not send some commands while the CD is seeking (ie. Getstat returns with bit6 set). Thing is that stat only gets updated after a new command. I haven't tested this for other command, but for the play command (03h) you can just keep repeating the [which?] command and checking stat returned by that, for bit6 to go low (and bit7 to go high in this case). If you don't and try to do a getloc [GetlocP and/or GetlocL?] directly after the play command reports it's done [what done? meaning sending start-to-play was "done"? or meaning play reached end-of-disc?], the CD will stop. (I guess the CD can't get it's current location while it's seeking, so the logic stops the seek to get an exact fix, but never restarts..)"

Sound Map Flowchart
Sound Map mode allows to output XA-ADPCM from Main RAM (rather than from CDROM).
  SPU: Init Master Volume Left/Right (Port 1F801D80h/1F801D82h)
  SPU: Init CD Audio Volume Left/Right (Port 1F801DB0h/1F801DB2h)
  SPU: Enable CD Audio (Port 1F801DAAh.Bit0=1)
  CDROM/CMD: send Stop command (probably better to avoid conflicts)
  CDROM/CMD: send Demute command (if muted) (but works only if disc inserted)
  CDROM/HOST: init Codinginfo (Port 1F801801h.Index2)
  CDROM/HOST: enable ADPCM (Port 1F801803h.Index3.Bit0=0)  ;probably needed?
  ... set dummy addr/len with DISHXFRC=1 ?  <-- NOT required !
  ... set SMEN ... and dummy BFWR?    <-- BOTH bits required ?
  ... maybe SMADPCLR (1F801803h.Index1.bit5) does clear SoundMap ADPCM buf?
  transfer 900h bytes (same format as ADPCM sectors) (Port 1F801801h.Index1)
  Note: Before sending a byte, one should wait for DRQs (1F801801h.Bit6=1)
  Note: ADPCM output doesn't start until the last (900h'th) byte is transferred
Sound Map mode may be very useful for testing XA-ADPCM directly from within an exe file (without needing a cdrom with ADPCM sectors). And, Sound Map supports both 4bit and 8bit compression (the SPU supports only 4bit).
Caution: If ADPCM wasn't playing, and one sends one 900h-byte block, then it will get stored in one of three 900h-byte slots in SRAM, and one would expect that slot to be played when the ADPCM output starts - however, actually, the hardware will more or less randomly play one of the three slots; not necessarily the slot that was updated most recently.


CDROM Controller Command Summary

Command Summary
  Command          Parameters      Response(s)
  00h -            -               INT5(11h,40h)  ;reportedly "Sync" uh?
  01h Getstat      -               INT3(stat)
  02h Setloc     E amm,ass,asect   INT3(stat)
  03h Play       E (track)         INT3(stat), optional INT1(report bytes)
  04h Forward    E -               INT3(stat), optional INT1(report bytes)
  05h Backward   E -               INT3(stat), optional INT1(report bytes)
  06h ReadN      E -               INT3(stat), INT1(stat), datablock
  07h MotorOn    E -               INT3(stat), INT2(stat)
  08h Stop       E -               INT3(stat), INT2(stat)
  09h Pause      E -               INT3(stat), INT2(stat)
  0Ah Init         -               INT3(late-stat), INT2(stat)
  0Bh Mute       E -               INT3(stat)
  0Ch Demute     E -               INT3(stat)
  0Dh Setfilter  E file,channel    INT3(stat)
  0Eh Setmode      mode            INT3(stat)
  0Fh Getparam     -               INT3(stat,mode,null,file,channel)
  10h GetlocL    E -               INT3(amm,ass,asect,mode,file,channel,sm,ci)
  11h GetlocP    E -               INT3(track,index,mm,ss,sect,amm,ass,asect)
  12h SetSession E session         INT3(stat), INT2(stat)
  13h GetTN      E -               INT3(stat,first,last)  ;BCD
  14h GetTD      E track (BCD)     INT3(stat,mm,ss)       ;BCD
  15h SeekL      E -               INT3(stat), INT2(stat)  ;\use prior Setloc
  16h SeekP      E -               INT3(stat), INT2(stat)  ;/to set target
  17h -            -               INT5(11h,40h)  ;reportedly "SetClock" uh?
  18h -            -               INT5(11h,40h)  ;reportedly "GetClock" uh?
  19h Test         sub_function    depends on sub_function (see below)
  1Ah GetID      E -               INT3(stat), INT2/5(stat,flg,typ,atip,"SCEx")
  1Bh ReadS      E?-               INT3(stat), INT1(stat), datablock
  1Ch Reset        -               INT3(stat), Delay            ;-not DTL-H2000
  1Dh GetQ       E adr,point       INT3(stat), INT2(10bytesSubQ,peak_lo) ;\not
  1Eh ReadTOC      -               INT3(late-stat), INT2(stat)           ;/vC0
  1Fh VideoCD      sub,a,b,c,d,e   INT3(stat,a,b,c,d,e)   ;<-- SCPH-5903 only
  1Fh..4Fh -       -               INT5(11h,40h)  ;-Unused/invalid
  50h Secret 1     -               INT5(11h,40h)  ;\
  51h Secret 2     "Licensed by"   INT5(11h,40h)  ;
  52h Secret 3     "Sony"          INT5(11h,40h)  ; Secret Unlock Commands
  53h Secret 4     "Computer"      INT5(11h,40h)  ; (not in version vC0, and,
  54h Secret 5     "Entertainment" INT5(11h,40h)  ; nonfunctional in japan)
  55h Secret 6     "<region>"      INT5(11h,40h)  ;
  56h Secret 7     -               INT5(11h,40h)  ;/
  57h SecretLock   -               INT5(11h,40h)  ;-Secret Lock Command
  58h..5Fh Crash   -               Crashes the HC05 (jumps into a data area)
  6Fh..FFh -       -               INT5(11h,40h)  ;-Unused/invalid
E = Error 80h appears on some commands (02h..09h, 0Bh..0Dh, 10h..16h, 1Ah, 1Bh?, and 1Dh) when the disk is missing, or when the drive unit is disconnected from the mainboard.
Some commands (04h,05h,10h,11h,1Dh) do also trigger Error 80h when the disk is stopped.

sub_function numbers (for command 19h)
Test commands are invoked with command number 19h, followed by a sub_function number as first parameter byte. The Kernel seems to be using only sub_function 20h (to detect the CDROM Controller version).
  sub  params  response           ;Effect
  00h      -   INT3(stat)         ;Force motor on, clockwise, even if door open
  01h      -   INT3(stat)         ;Force motor on, anti-clockwise, super-fast
  02h      -   INT3(stat)         ;Force motor on, anti-clockwise, super-fast
  03h      -   INT3(stat)         ;Force motor off (ignored during spin-up)
  04h      -   INT3(stat)         ;Start SCEx reading and reset counters
  05h      -   INT3(total,success);Stop SCEx reading and get counters
  06h *    n   INT3(old)  ;\early ;Adjust balance in RAM, send CX(30+n XOR 7)
  07h *    n   INT3(old)  ; PSX   ;Adjust gain in RAM, send CX(38+n XOR 7)
  08h *    n   INT3(old)  ;/only  ;Adjust balance in RAM only
  06h..0Fh -   INT5(11h,10h)      ;N/A (11h,20h when NONZERO number of params)
  10h      -   INT3(stat) ;CX(..) ;Force motor on, anti-clockwise, super-fast
  11h      -   INT3(stat) ;CX(03) ;Move Lens Up (leave parking position)
  12h      -   INT3(stat) ;CX(02) ;Move Lens Down (enter parking position)
  13h      -   INT3(stat) ;CX(28) ;Move Lens Outwards
  14h      -   INT3(stat) ;CX(2C) ;Move Lens Inwards
  15h      -   INT3(stat) ;CX(22) ;If motor on: Move outwards,inwards,motor off
  16h      -   INT3(stat) ;CX(23) ;No effect?
  17h      -   INT3(stat) ;CX(E8) ;Force motor on, clockwise, super-fast
  18h      -   INT3(stat) ;CX(EA) ;Force motor on, anti-clockwise, super-fast
  19h      -   INT3(stat) ;CX(25) ;No effect?
  1Ah      -   INT3(stat) ;CX(21) ;No effect?
  1Bh..1Fh -   INT5(11h,10h)      ;N/A (11h,20h when NONZERO number of params)
  20h      -   INT3(yy,mm,dd,ver) ;Get cdrom BIOS date/version (yy,mm,dd,ver)
  21h      -   INT3(n)            ;Get Drive Switches (bit0=POS0, bit1=DOOR)
  22h ***  -   INT3("for ...")    ;Get Region ID String
  23h ***  -   INT3("CXD...")     ;Get Chip ID String for Servo Amplifier
  24h ***  -   INT3("CXD...")     ;Get Chip ID String for Signal Processor
  25h ***  -   INT3("CXD...")     ;Get Chip ID String for Decoder/FIFO
  26h..2Fh -   INT5(11h,10h)      ;N/A (11h,20h when NONZERO number of params)
  30h *    i,x,y     INT3(stat)       ;Prototype/Debug stuff   ;\supported on
  31h *    x,y       INT3(stat)       ;Prototype/Debug stuff   ; early PSX only
  4xh *    i         INT3(x,y)        ;Prototype/Debug stuff   ;/
  30h..4Fh ..        INT5(11h,10h)    ;N/A always 11h,10h (no matter of params)
  50h      a[,b[,c]] INT3(stat)       ;Servo/Signal send CX(a:b:c)
  51h **   39h,xx    INT3(stat,hi,lo) ;Servo/Signal send CX(39xx) with response
  51h..5Fh -         INT5(11h,10h)    ;N/A
  60h      lo,hi     INT3(databyte)   ;HC05 SUB-CPU read RAM and I/O ports
  61h..70h -         INT5(11h,10h)    ;N/A
  71h ***  adr       INT3(databyte)   ;Decoder Read one register
  72h ***  adr,dat   INT3(stat)       ;Decoder Write one register
  73h ***  adr,len   INT3(databytes..);Decoder Read multiple registers, bugged
  74h ***  adr,len,..INT3(stat)       ;Decoder Write multiple registers, bugged
  75h ***  -         INT3(lo,hi,lo,hi);Decoder Get Host Xfer Info Remain/Addr
  76h ***  a,b,c,d   INT3(stat)       ;Decoder Prepare Transfer to/from SRAM
  77h..FFh -         INT5(11h,10h)    ;N/A
  80h..8Fh a,b       ?                ;seem to do something on PS2
* sub_functions 06h..08h, 30h..31h, and 4xh are supported only in vC0 and vC1.
** sub_function 51h is supported only in BIOS version vC2 and up.
*** sub_functions 22h..25h, 71h..76h supported only in BIOS version vC1 and up.

Unsupported GetQ,VCD,SecretUnlock (command 1Dh,1Fh,5xh)
INT5 will be returned if the command is unsupported. That, WITHOUT removing the Parameters from the FIFO, so the parameters will be accidently passed to the NEXT command. To avoid that: clear the parameter FIFO via [1F801803h.Index1]=40h after receiving the INT5 error.


CDROM - Control Commands

Sync - Command 00h --> INTx(stat+1,40h) (?)
Reportedly "command does not succeed until all other commands complete. This can be used for synchronization - hence the name."
Uh, actually, returns error code 40h = Invalid Command...?

Setfilter - Command 0Dh,file,channel --> INT3(stat)
Automatic ADPCM (CD-ROM XA) filter ignores sectors except those which have the same channel and file numbers in their subheader. This is the mechanism used to select which of multiple songs in a single .XA file to play.
Setfilter does not affect actual reading (sector reads still occur for all sectors).
XXX err... that is... does not affect reading of non-ADPCM sectors (normal "data" sectors are kept received regardless of Setfilter).

Setmode - Command 0Eh,mode --> INT3(stat)
  7   Speed       (0=Normal speed, 1=Double speed)
  6   XA-ADPCM    (0=Off, 1=Send XA-ADPCM sectors to SPU Audio Input)
  5   Sector Size (0=800h=DataOnly, 1=924h=WholeSectorExceptSyncBytes)
  4   Ignore Bit  (0=Normal, 1=Ignore Sector Size and Setloc position)
  3   XA-Filter   (0=Off, 1=Process only XA-ADPCM sectors that match Setfilter)
  2   Report      (0=Off, 1=Enable Report-Interrupts for Audio Play)
  1   AutoPause   (0=Off, 1=Auto Pause upon End of Track) ;for Audio Play
  0   CDDA        (0=Off, 1=Allow to Read CD-DA Sectors; ignore missing EDC)
The "Ignore Bit" does reportedly force a sector size of 2328 bytes (918h), however, that doesn't seem to be true. Instead, Bit4 seems to cause the controller to ignore the sector size in Bit5 (instead, the size is kept from the most recent Setmode command which didn't have Bit4 set). Also, Bit4 seems to cause the controller to ignore the <exact> Setloc position (instead, data is randomly returned from the "Setloc position minus 0..3 sectors"). And, Bit4 causes INT1 to return status.Bit3=set (IdError). Purpose of Bit4 is unknown?

Init - Command 0Ah --> INT3(stat) --> INT2(stat)
Multiple effects at once. Sets mode=20h, activates drive motor, Standby, abort all commands.

Reset - Command 1Ch,(...) --> INT3(stat) --> Delay(1/8 seconds)
  Caution: Not supported on DTL-H2000 (v01)
Resets the drive controller, reportedly, same as opening and closing the drive door. The command executes no matter if/how many parameters are used (tested with 0..7 params). INT3 indicates that the command was started, but there's no INT that would indicate when the command is finished, so, before sending any further commands, a delay of 1/8 seconds (or 400000h clock cycles) must be issued by software.
Note: Executing the command produces a click sound in the drive mechanics, maybe it's just a rapid motor on/off, but it might something more serious, like ignoring the /POS0 signal...?

MotorOn - Command 07h --> INT3(stat) --> INT2(stat)
Activates the drive motor, works ONLY if the motor was off (otherwise fails with INT5(stat,20h); that error code would normally indicate "wrong number of parameters", but means "motor already on" in this case).
Commands like Read, Seek, and Play are automatically starting the Motor when needed (which makes the MotorOn command rather useless, and it's rarely used by any games).
Myth: Older homebrew docs are referring to MotorOn as "Standby", claiming that it would work similar as "Pause", that is wrong: the command does NOT pause anything (if the motor is on, then it does simply trigger INT5, but without pausing reading or playing).
Note: The game "Nightmare Creatures 2" does actually attempt to use MotorOn to "pause" after reading files, but the hardware does simply ignore that attempt (aside from doing the INT5 thing).

Stop - Command 08h --> INT3(stat) --> INT2(stat)
Stops motor with magnetic brakes (stops within a second or so) (unlike power-off where it'd keep spinning for about 10 seconds), and moves the drive head to the begin of the first track. Official way to restart is command 0Ah, but almost any command will restart it.
The first response returns the current status (this already with bit5 cleared), the second response returns the new status (with bit1 cleared).

Pause - Command 09h --> INT3(stat) --> INT2(stat)
Aborts Reading and Playing, the motor is kept spinning, and the drive head maintains the current location within reasonable error.
The first response returns the current status (still with bit5 set if a Read command was active), the second response returns the new status (with bit5 cleared).

Data/ADPCM Sector Filtering/Delivery
The PSX CDROM BIOS is first trying to send sectors to the ADPCM decoder, and, if that didn't work out, then it's trying to send them to the main CPU (and if that didn't work out either, then it's silently ignoring the sector).
 try_deliver_as_adpcm_sector:
  reject if CD-DA AUDIO format
  reject if sector isn't MODE2 format
  reject if adpcm_disabled(setmode.6)
  reject if filter_enabled(setmode.3) AND selected file/channel doesn't match
  reject if submode isn't audio+realtime (bit2 and bit6 must be both set)
  deliver: send sector to xa-adpcm decoder when passing above cases
 try_deliver_as_data_sector:
  reject data-delivery if "try_deliver_as_adpcm_sector" did do adpcm-delivery
  reject if filter_enabled(setmode.3) AND submode is audio+realtime (bit2+bit6)
  1st delivery attempt: send INT1+data, unless there's another INT pending
  delay, and retry at later time... but this time with file/channel checking!
  reject if filter_enabled(setmode.3) AND selected file/channel doesn't match
  2nd delivery attempt: send INT1+data, unless there's another INT pending
BUG: Note that the data delivery is done in two different attempts: The first one regardless of file/channel, and the second one only on matching file/channel (if filtering is enabled).


CDROM - Seek Commands

Setloc - Command 02h,amm,ass,asect --> INT3(stat)
Sets the seek target - but without yet starting the seek operation. The actual seek is invoked by certain commands: SeekL (Data) and SeekP (Audio) are doing plain seeks (and do Pause after completion). ReadN/ReadS are similar to SeekL (and do start reading data after the seek operation). Play is similar to SeekP (and does start playing audio after the seek operation).
The amm,ass,asect parameters refer to the entire disk (not to the current track). To seek to a specific location within a specific track, use GetTD to get the start address of the track, and add the desired time offset to it.

SeekL - Command 15h --> INT3(stat) --> INT2(stat)
Seek to Setloc's location in data mode (using data sector header position data, which works/exists only on Data tracks, not on CD-DA Audio tracks).
After the seek, the disk stays on the seeked location forever (namely: when seeking sector N, it does stay at around N-8..N-0 in single speed mode, or at around N-5..N+2 in double speed mode).
Trying to use SeekL on Audio CDs passes okay on the first response, but (after two seconds or so) the second response will return an error (stat+4,04h), and stop the drive motor... that error doesn't appear ALWAYS though... works in some situations... such like when previously reading data sectors or so...?

SeekP - Command 16h --> INT3(stat) --> INT2(stat)
Seek to Setloc's location in audio mode (using the Subchannel Q position data, which works on both Audio on Data disks).
After the seek, the disk stays on the seeked location forever (namely: when seeking sector N, it does stay at around N-9..N-1 in single speed mode, or at around N-2..N in double speed mode).
Note: Some older docs claim that SeekP would recurse only "MM:SS" of the "MM:SS:FF" position from Setloc - that is wrong, it does seek to MM:SS:FF (verified on a PSone).
After the seek, status is stat.bit7=0 (ie. audio playback off), until sending a new Play command (without parameters) to start playback at the seeked location.

SetSession - Command 12h,session --> INT3(stat) --> INT2(stat)
Seeks to session (ie. moves the drive head to the session, with stat bit6 set during the seek phase).
When issued during active-play, the command returns error code 80h.
When issued during play-spin-up, play is aborted.
  ___Errors___
  session = 00h causes error code 10h.     ;INT5(03h,10h), no 2nd/3rd response
  ___On a non-multisession-disk___
  session = 01h passes okay.               ;INT3(stat), and once INT2(stat)
  session = 02h or higher cause seek error ;INT3(stat), and twice INT5(06h,40h)
  ___On a multisession-disk with N sessions___
  session = 01h..N+1 passes okay   ;where N+1 moves to the END of LAST session
  session = N+2 or higher cause seek error  ;2nd response = INT5(06h,20h)
after seek error --> disk stops spinning at 2nd response, then restarts spinning for 1 second or so, then stops spinning forever... and following gettn/gettd/getid/getlocl/getlocp fail with error 80h...
The command does automatically read the TOC of the new session. BUG: Older CD Firmwares (16 May 1995 and older) don't clear the old TOC when loading Session 1, in that case SetSession(1) may update some (not all) TOC entries; ending up with a mixup of old and new TOC entries.
There seems to be no way to determine the current sessions number (via Getparam or so), and more important, no way to determine if the disk is a multi-session disk or not... except by trial... which would stop the drive motor on seek errors on single-session disks...?
For setloc, one must probably specifiy minutes within the 1st track of the new session (the 1st track of 1st session usually/always starts at 00:02:00, but for other sessions one would need to use GetTD)...?


CDROM - Read Commands

ReadN - Command 06h --> INT3(stat) --> INT1(stat) --> datablock
Read with retry. The command responds once with "stat,INT3", and then it's repeatedly sending "stat,INT1 --> datablock", that is continued even after a successful read has occured; use the Pause command to terminate the repeated INT1 responses.
Unknown which responses are sent in case of read errors?
====
ReadN and ReadS cause errors if you're trying to read an unlicensed CD or CD-R without a mod chip. Sectors on Audio CDs can be read only when CDDA is enabled via Setmode (otherwise error code 40h is returned).
====
Actually, Read seems to work on unlicensed CD-R's, but the returned data is the whole sector or so (the 2048 data bytes preceeded by a 12byte header, and probably/maybe followed by error-correction info; in fact the total received data in the Data Fifo is 4096 bytes; the last some bytes probably being garbage) (however error correction is NOT performed by hardware, so the 2048 data bytes may be trashy) (however, if the error correction info IS received, then error correction could be performed by software) (also Setloc doesn't seem to work accurately on unlicensed CD-R's).
====
     ;Read occasionally returns 11h,40h ..? when TOC isn't loaded?
After receiving INT1, the Kernel does,
  [1F801800h]=00h
  00h=[1F801800h]
  [1F801803h]=00h
  00h=[1F801803h]
  [1F801800h]=00h
  [1F801803h]=80h
and then,
  [1F801018h]=00020943h  ;cdrom_delay
  [1F801020h]=0000132Ch  ;com_delay
then,
  x=[1F8010F4h] AND 00FFFFFFh   ;result is 00840000h
  [1F8010F4h] = x OR 00880000h
  [1F8010F0h] = [1F8010F0h] OR 00008000h
  [1F8010B0h] = A0010000h ;addr
  [1F8010B4h] = 00010200h ;LSBs=num words, MSBs=ignored/bullshit
  [1F8010B4h] = 11000000h ;DMA control
thereafter,
  [1F801800h]=01h
  [1F801803h]=40h    ;reset parameter fifo
  [0]=00000000h
  [0]=00000001h
  [0]=00000002h
  [0]=00000003h
  [1F801800h]=00h
  [1F801801h]=09h    ;command9 (pause)

ReadS - Command 1Bh --> INT3(stat) --> INT1(stat) --> datablock
Read without automatic retry. Not sure what that means... does WHAT on errors? Maybe intended for continous streaming video output (to skip bad frames, rather than to interrupt the stream by performing read-retrys).

ReadN/ReadS
Both ReadN/ReadS are reading data sequentially, starting at the sector specified with Setloc, and then automatically reading the following sectors.

CDROM Incoming Data / Buffer Overrun Timings
The Read commands are continously receiving 75 sectors per second (or 150 sectors at double speed), and, basically, the software must be fast enough to process that amount of incoming data. However, the PSX hardware includes a buffer that can hold up to a handful (exact number is unknown?) of sectors, so, occasional delays of more than 1/75 seconds between processing two sectors aren't causing lost sectors, unless the delay(s) are summing up too much. The relevant steps for receiving data are:
  Wait for Interrupt Request (INT1)          ;indicates that data is available
  Send Data Request (1F801803h.Index0.Bit7=1);accept data
  Acknowledge INT1                           ;
  Copy Data to Main RAM (via I/O or DMA)     ;read data
The Data Request accepts the data for the currently pending interrupt, it should be usually issued between receiving/acknowledging INT1 (however, it can be also issued shortly after the acknowledge; even if there are further sectors in the buffer, there seems to be a small delay between the acknowledge and the next interrupt, and Data Requests during that period are still treated to belong to the old interrupt).
If a buffer overrun has occured <before> issuing the Data Request, then wrong data will be received, ie. some sectors will be skipped (the hardware doesn't seem to support a buffer-overrun error flag? Anyways, see GetlocL description for a possible way to detect buffer-overruns).
If a buffer overrun occurs <after> issuing the Data Request, then the requested data can be still read via I/O or DMA intactly, ie. the requested data is "locked", and the overrun will affect only the following sectors.

ReadTOC - Command 1Eh --> INT3(stat) --> INT2(stat)
  Caution: Supported only in BIOS version vC1 and up. Not supported in vC0.
Reread the Table of Contents of current session without reset. The command is rather slow, the second response appears after about 1 second delay. The command itself returns only status information (to get the actual TOC info, use GetTD and GetTN commands).
Note: The TOC contains information about the tracks on the disk (not file names or so, that kind of information is obtained via Read commands). The TOC is read automatically on power-up, when opening/closing the drive door, and when changing sessions (so, normally, it isn't required to use this command).

Setloc, Read, Pause
A normal CDROM access (such like reading a file) consists of three commands:
  Setloc, Read, Pause
Normally one shouldn't mess up the ordering of those commands, but if one does, following rules do apply:
Setloc is memorizing the wanted target, and marks it as unprocessed, and has no other effect (it doesn't start reading or seeking, and doesn't interrupt or redirect any active reads).
If Read is issued with an unprocessed Setloc, then the drive is automatically seeking the Setloc location (and marks Setloc as processed).
If Read is issued without an unprocessed Setloc, the following happens: If reading is already in progress then it just continues reading. If Reading was Paused, then reading resumes at the most recently received sector (ie. returning that sector once another time).


CDROM - Status Commands

Status code (stat)
The 8bit status code is returned by Getstat command (and many other commands), the meaning of the separate stat bits is:
  7  Play          Playing CD-DA         ;\only ONE of these bits can be set
  6  Seek          Seeking               ; at a time (ie. Read/Play won't get
  5  Read          Reading data sectors  ;/set until after Seek completion)
  4  ShellOpen     Once shell open (0=Closed, 1=Is/was Open)
  3  IdError       (0=Okay, 1=GetID denied) (also set when Setmode.Bit4=1)
  2  SeekError     (0=Okay, 1=Seek error)     (followed by Error Byte)
  1  Spindle Motor (0=Motor off, or in spin-up phase, 1=Motor on)
  0  Error         Invalid Command/parameters (followed by Error Byte)
If the shell is closed, then bit4 is automatically reset to zero after reading stat with the Getstat command (most or all other commands do not reset that bit after reading). If stat bit0 or bit2 is set, then the normal respons(es) and interrupt(s) are not send, and, instead, INT5 occurs, and an error-byte is send as second response byte, with the following values:
  ___These values appear in the FIRST response; with stat.bit0 set___
  10h - Invalid Sub_function (for command 19h), or invalid parameter value
  20h - Wrong number of parameters
  40h - Invalid command
  80h - Cannot respond yet (eg. required info was not yet read from disk yet)
           (namely, TOC not-yet-read or so)
           (also appears if no disk inserted at all)
  ___These values appear in the SECOND response; with stat.bit2 set___
  04h - Seek failed (when trying to use SeekL on Audio CDs)
  ___These values appear even if no command was sent; with stat.bit2 set___
  08h - Drive door became opened
80h appears on some commands (02h..09h, 0Bh..0Dh, 10h..16h, 1Ah, 1Bh?, and 1Dh) when the disk is missing, or when the drive unit is disconnected from the mainboard.

Stat Seek/Play/Read bits
There's is only max ONE of the three Seek/Play/Read bits set at a time, ie. during Seek, ONLY the seek bit is set (and Read or Play doesn't get until seek completion), that is important for Gran Turismo 1, which checks for seek completion by waiting for READ getting set (rather than waiting for SEEK getting cleared).

Getstat - Command 01h --> INT3(stat)
Returns stat (like many other commands), and additionally does reset the shell open flag (for the following commands; unless the shell is still opened). This is different as for most or all other commands (which may return stat, but which do not reset the shell open flag).
In other docs, the command is eventually referred to as "Nop", believing that it does nothing than returning stat (ignoring the fact that it's having the special shell open reset feature).

Getparam - Command 0Fh --> INT3(stat,mode,null,file,channel)
Returns stat (see Getstat above), mode (see Setmode), a null byte (always 00h), and file/channel filter values (see Setfilter).

GetlocL - Command 10h --> INT3(amm,ass,asect,mode,file,channel,sm,ci)
Retrieves 4-byte sector header, plus 4-byte subheader of the current sector. GetlocL can be send during active Read commands (but, mind that the GetlocL-INT3-response can't be received until any pending Read-INT1's are acknowledged).
The PSX hardware can buffer a handful of sectors, the INT1 handler receives the <oldest> buffered sector, the GetlocL command returns the header and subheader of the <newest> buffered sector. Note: If the returned <newest> sector number is much bigger than the expected <oldest> sector number, then it's likely that a buffer overrun has occured.
GetlocL fails (with error code 80h) when playing Audio CDs (or Audio Tracks on Data CDs). These errors occur because Audio sectors don't have any header/subheader (instead, equivalent data is stored in Subchannel Q, which can be read with GetlocP).
GetlocL also fails (with error code 80h) when the drive is in Seek phase (such like shortly after a new ReadN/ReadS command). In that case one can retry issuing GetlocL (until it passes okay, ie. until the seek has completed). During Seek, the drive seems to decode only Subchannel position data (but no header/subheader data), accordingly GetlocL won't work during seek (however, GetlocP does work during Seek).

GetlocP - Command 11h - INT3(track,index,mm,ss,sect,amm,ass,asect)
Retrieves 8 bytes of position information from Subchannel Q with ADR=1. Mainly intended for displaying the current audio position during Play. All results are in BCD.
  track:  track number (AAh=Lead-out area) (FFh=unknown, toc, none?)
  index:  index number (Usually 01h)
  mm:     minute number within track (00h and up)
  ss:     second number within track (00h to 59h)
  sect:   sector number within track (00h to 74h)
  amm:    minute number on entire disk (00h and up)
  ass:    second number on entire disk (00h to 59h)
  asect:  sector number on entire disk (00h to 74h)
Note: GetlocP is also used for reading the LibCrypt protection data:
CDROM Protection - LibCrypt

GetTN - Command 13h --> INT3(stat,first,last) ;BCD
Get first track number, and last track number in the TOC of the current Session. The number of tracks in the current session can be calculated as (last-first+1). The first track number is usually 01h in the first (or only) session, and "last track of previous session plus 1" in further sessions.

GetTD - Command 14h,track --> INT3(stat,mm,ss) ;BCD
For a disk with NN tracks, parameter values 01h..NNh return the start of the specified track, parameter value 00h returns the end of the last track, and parameter values bigger than NNh return error code 10h.
The GetTD values are relative to Index=1 and are rounded down to second boundaries (eg. if track=N Index=0 starts at 12:34:56, and Track=N Index=1 starts at 12:36:56, then GetTD(N) will return 12:36, ie. the sector number is truncated, and the Index=0 region is skipped).

GetQ - Command 1Dh,adr,point --> INT3(stat) --> INT2(10bytesSubQ,peak_lo)
  Caution: Supported only in BIOS version vC1 and up. Not supported in vC0.
  Caution: When unsupported, Parameter Fifo isn't cleared after the command.
Allows to read 10 bytes from Subchannel Q in Lead-In (see CDROM Subchannels chapter for details). Unlike GetTD, this command allows to receive the exact MM:SS:FF address of the point'ed Track (GetTD reads a memorized MM:SS value from RAM, whilst GetQ reads the full MM:SS:FF from the disk, which is slower than GetTD, due to the disk-access).
With ADR=1, point can be a any point number for ADR=1 in Lead-in (eg. 01h..99h=Track N, A2h=Lead-Out). The returned 10 bytes are raw SubQ data (starting with the ADR/Control value; of which the lower 4bits are always ADR=1).
The 11th returned byte is the Peak LSB (similar as in Play+Report, but in this case only the LSB is transferred, which is apparently a bug in CDROM BIOS, the programmer probably wanted to send 10 bytes without peak, or 12 bytes with full peak; although peak wouldn't be too useful, as it should always zero during Lead-In... but some discs do seem return non-zero values for whatever reason).
Aside from ADR=1, a value of ADR=5 can be used on multisession disks (eg. with point B0h, C0h). Not sure if any other ADR values can be used (ADR=3, ISRC is usually not in the Lead-In, ADR=2, EAN may be in the lead-in, but one may need to specify point equal to the first EAN byte).
If the ADR/Point combination isn't found, then a timeout occurs after circa 6 seconds (to avoid this, use GetTN to see which tracks/points exist). After the timeout, the command starts playing track 1. If the controller wasn't already in audio mode before sending the command, then it does switch off the drive motor for a moment (that, after the timeout, and before starting playback).
In case of timeout, the normal INT3/INT2 responses are replaced by INT3/INT5/INT5 (INT3 at command start, 1st INT5 at timeout/stop, and 2nd INT5 at restart/play).
Note: GetQ sends scratch noise to the SPU while seeking to the Lead-In area.

GetID - Command 1Ah --> INT3(stat) --> INT2/5 (stat,flags,type,atip,"SCEx")
  Drive Status           1st Response   2nd Response
  Door Open              INT5(11h,80h)  N/A
  Spin-up                INT5(01h,80h)  N/A
  Detect busy            INT5(03h,80h)  N/A
  No Disk                INT3(stat)     INT5(08h,40h, 00h,00h, 00h,00h,00h,00h)
  Audio Disk             INT3(stat)     INT5(0Ah,90h, 00h,00h, 00h,00h,00h,00h)
  Unlicensed:Mode1       INT3(stat)     INT5(0Ah,80h, 00h,00h, 00h,00h,00h,00h)
  Unlicensed:Mode2       INT3(stat)     INT5(0Ah,80h, 20h,00h, 00h,00h,00h,00h)
  Unlicensed:Mode2+Audio INT3(stat)     INT5(0Ah,90h, 20h,00h, 00h,00h,00h,00h)
  Debug/Yaroze:Mode2     INT3(stat)     INT2(02h,00h, 20h,00h, 20h,20h,20h,20h)
  Licensed:Mode2         INT3(stat)     INT2(02h,00h, 20h,00h, 53h,43h,45h,4xh)
  Modchip:Audio/Mode1    INT3(stat)     INT2(02h,00h, 00h,00h, 53h,43h,45h,4xh)
The status byte (ie. the first byte in the responses), may differ in some cases; values shown above are typically received when issuing GetID shortly after power-up; however, shortly after the detect-busy phase, seek-busy flag (bit6) bit may be set, and, after issuing commands like Play/Read/Stop, bit7,6,5,1 may differ. The meaning of the separate 2nd response bytes is:
  1st byte: stat  (as usually, but with bit3 same as bit7 in 2nd byte)
  2nd byte: flags (bit7=denied, bit4=audio... or reportedly import, uh?)
    bit7: Licensed (0=Licensed Data CD, 1=Denied Data CD or Audio CD)
    bit6: Missing  (0=Disk Present, 1=Disk Missing)
    bit4: Audio CD (0=Data CD, 1=Audio CD) (always 0 when Modchip installed)
  3rd byte: Disk type (from TOC Point=A0h) (eg. 00h=Audio or Mode1, 20h=Mode2)
  4th byte: Usually 00h (or 8bit ATIP from Point=C0h, if session info exists)
    that 8bit ATIP value is taken form the middle 8bit of the 24bit ATIP value
  5th-8th byte: SCEx region (eg. ASCII "SCEE" = Europe) (0,0,0,0 = Unlicensed)
The fourth letter of the "SCEx" string contains region information: "SCEI" (Japan/NTSC), "SCEA" (America/NTSC), "SCEE" (Europe/PAL). The "SCEx" string is displayed in the intro, and the PSX refuses to boot if it doesn't match up for the local region.
With a modchip installed, the same response is sent for Mode1 and Audio disks (except for Audio disks with very short TOCs (eg. singles) because SCEX reading is aborted immediately after reading all TOC entries on Audio disks); whether it is Audio or Mode1 can be checked by examining Subchannel Q ADR/Control.Bit6 (eg. via command 19h,60h,50h,00h).
Yaroze does return "SCEA" for SCEA discs, but, for SCEI,SCEE,SCEW discs it does return four ASCII spaces (20h).


CDROM - CD Audio Commands

To play CD-DA Audio CDs, init the following SPU Registers: CD Audio Volume, Main Volume, and SPU Control Bit0. Then send Demute command, and Play command.

Mute - Command 0Bh --> INT3(stat)
Turn off audio streaming to SPU (affects both CD-DA and XA-ADPCM).
Even when muted, the CDROM controller is internally processing audio sectors (as seen in 1F801800h.Bit2, which works as usually for XA-ADPCM), muting is just forcing the CD output volume to zero.
Mute is used by Dino Crisis 1 to mute noise during modchip detection.

Demute - Command 0Ch --> INT3(stat)
Turn on audio streaming to SPU (affects both CD-DA and XA-ADPCM). The Demute command is needed only if one has formerly used the Mute command (by default, the PSX is demuted after power-up (...and/or after Init command?), and is demuted after cdrom-booting).

Play - Command 03h (,track) --> INT3(stat) --> optional INT1(report bytes)
Starts CD Audio Playback. The parameter is optional, if there's no parameter given (or if it is 00h), then play either starts at Setloc position (if there was a pending unprocessed Setloc), or otherwise starts at the current location (eg. the last point seeked, or the current location of the current song; if it was already playing). For a disk with N songs, Parameters 1..N are starting the selected track. Parameters N+1..99h are restarting the begin of current track. The motor is switched off automatically when Play reaches the end of the disk, and INT4(stat) is generated (with stat.bit7 cleared).
The track parameter seems to be ignored when sending Play shortly after power-up (ie. when the drive hasn't yet read the TOC).
===
"Play is almost identical to CdlReadS, believe it or not. The main difference is that this does not trigger a completed read IRQ. CdlPlay may be used on data sectors. However, all sectors from data tracks are treated as 00, so no sound is played. As CdlPlay is reading, the audio data appears in the sector buffer, but is not reliable. Game Shark "enhancement CDs" for the 2.x and 3.x versions used this to get around the PSX copy protection."
Hmmm, what/where is the sector buffer... in the SPU?
And, what/who are the 2.x and 3.x versions?

Forward - Command 04h --> INT3(stat) --> optional INT1(report bytes)
Backward - Command 05h --> INT3(stat) --> optional INT1(report bytes)
After sending the command, the drive is in fast forward/backward mode, skipping every some sectors. The skipping rate is fixed (it doesn't increase after some seconds) (however, it increases when (as long as) sending the command again and again). The sound becomes (obviously) non-continous, and also rather very silent, muffled, and almost inaudible (that's making it rather useless; unless it's combined with a track/minute/second display). To terminate forward/backward, send a new Play command (with no parameters, so play starts at the "searched" location). Backward automatically switches to Play when reaching the begin of Track 1. Forward automatically Stops the drive motor with INT4(stat) when reaching the end of the last track.
Forward/Backwards work only if the drive was in Play state, and only if Play had already started (ie. not shortly/immediately after a Play command); if the drive was not in Play state, then INT5(stat+1,80h) occurs.

Setmode bits used for Play command
During Play, only bit 7,2,1 of Setmode are used, all other Setmode bits are ignored (that, including bit0, ie. during Play the drive is always in CD-DA mode, regardless of that bit).
Bit7 (double speed) should be usually off, although it can be used for a fast forward effect (with audible output). Bit2 (report) activates an optional interrupt for Play, Forward, and Backward commands (see below). Bit1 (autopause) pauses play at the end of the track.

Report --> INT1(stat,track,index,mm/amm,ss+80h/ass,sect/asect,peaklo,peakhi)
With report enabled via Setmode, the Play, Forward, and Backward commands do repeatedly generate INT1 interrupts, with eight bytes response length. The interrupt isn't generated on ALL sectors, and the response changes between absolute time, and time within current track (the latter one indicated by bit7 of ss):
  amm/ass/asect are returned on asect=00h,20h,40h,60h   ;-absolute time
  mm/ss+80h/sect are returned on asect=10h,30h,50h,70h  ;-within current track
  (or, in case of read errors, report may be returned on other asect's)
The last two response bytes (peaklo,peakhi) contain the Peak value, as received from the CXD2510Q Signal Processor. That is: An unsigned absolute peak level in lower 15bit, and an L/R flag in upper bit. The L/R bit is toggled after each SUBQ read, however the PSX Report mode does usually forward SUBQ only every 10 frames (but does read SUBQ in <every> frame), so L/R will stay stuck in one setting (but may toggle after one second; ie. after 75 frames). And, peak is reset after each read, so 9 of the 10 frames are lost.
Note: Report mode affects only CD Audio (not Data, nor XA-ADPCM sectors).

AutoPause --> INT4(stat)
Autopause can be enabled/disabled via Setmode.bit1:
  Setmode.bit1=1: AutoPause=On  --> Issue INT4(stat) and PAUSE at end of TRACK
  Setmode.bit1=0: AutoPause=Off --> Issue INT4(stat) and STOP at end of DISC
End of Track is determined by sensing a track number transition in SubQ position info. After autopause, the disc stays at the <end> of the old track, NOT at the <begin> of the next track (so trying to resume playing by sending a new Play command without new Seek/Setloc command will instantly pause again).
Caution: SubQ track transitions may pause instantly when accidently starting to play at the end of the previous track rather than at begin of desired track (this <might> happen due to seek inaccuracies, for example, GetTD does round down TOC entries from MM:SS:FF to MM:SS:00, which may be off by 0.99 seconds, although this error should be usually compensated by the leading 2-second pregap/index0 region at the begin of each track, unfortunately there are a few .CUE sheet files that do lack both PREGAP and INDEX 00 entries on audio tracks, which might cause problems with autopause).
AutoPause is used by Rayman and Tactics Ogre.

Playing XA-ADPCM Sectors (compressed audio data)
Aside from normal uncompressed CD Audio disks, the PSX can also play XA-ADPCM compressed sectors. XA-ADPCM sectors are organized in Files (not in tracks), and are "played" with Read command (not Play command).
To play XA-ADPCM, initialize the SPU for CD Audio input (as described above), enable ADPCM via Setmode, then select the sector via Setloc, and issue a Read command (typically ReadS).
XA-ADPCM sectors are interleaved, ie. only each Nth sector should be played (where "N" depends on the Motor Speed, mono/stereo format, and sample rate). If the "other" sectors do contain XA-ADPCM data too, then the Setfilter command (and XA-Filter enable flag in Setmode) must be used to select the desired sectors. If the "other" sectors do contain code or data (eg. MDEC video data) which is wanted to be send to the CPU, then SetFilter isn't required to be enabled (although it shouldn't disturb reading even if it is enabled).
If XA-ADPCM (and/or XA-Filter) is enabled via Setmode, then INT1 is generated only for non-ADPCM sectors.
The Setmode sector-size selection is don't care for forwarding XA-ADPCM sectors to the SPU (the hardware does always decompress all 900h bytes).


CDROM - Test Commands

CDROM - Test Commands - Version, Switches, Region, Chipset, SCEx
CDROM - Test Commands - Test Drive Mechanics
CDROM - Test Commands - Prototype Debug Transmission
CDROM - Test Commands - Read/Write Decoder RAM and I/O Ports
CDROM - Test Commands - Read HC05 SUB-CPU RAM and I/O Ports


CDROM - Test Commands - Version, Switches, Region, Chipset, SCEx

19h,20h --> INT3(yy,mm,dd,ver)
Indicates the date (Year-month-day, in BCD format) and version of the HC05 CDROM controller BIOS. Known/existing values are:
  (unknown)        ;DTL-H2000 (with SPC700 instead HC05)
  94h,09h,19h,C0h  ;PSX (PU-7)               19 Sep 1994, version vC0 (a)
  94h,11h,18h,C0h  ;PSX (PU-7)               18 Nov 1994, version vC0 (b)
  94h,11h,28h,01h  ;PSX (DTL-H2000)          28 Nov 1994, version v01 (debug)
  95h,05h,16h,C1h  ;PSX (LATE-PU-8)          16 May 1995, version vC1 (a)
  95h,07h,24h,C1h  ;PSX (LATE-PU-8)          24 Jul 1995, version vC1 (b)
  95h,07h,24h,D1h  ;PSX (LATE-PU-8,debug ver)24 Jul 1995, version vD1 (debug)
  96h,08h,15h,C2h  ;PSX (PU-16, Video CD)    15 Aug 1996, version vC2 (VCD)
  96h,08h,18h,C1h  ;PSX (LATE-PU-8,yaroze)   18 Aug 1996, version vC1 (yaroze)
  96h,09h,12h,C2h  ;PSX (PU-18) (japan)      12 Sep 1996, version vC2 (a.jap)
  97h,01h,10h,C2h  ;PSX (PU-18) (us/eur)     10 Jan 1997, version vC2 (a)
  97h,08h,14h,C2h  ;PSX (PU-20)              14 Aug 1997, version vC2 (b)
  98h,06h,10h,C3h  ;PSX (PU-22)              10 Jun 1998, version vC3 (a)
  99h,02h,01h,C3h  ;PSX/PSone (PU-23, PM-41) 01 Feb 1999, version vC3 (b)
  A1h,03h,06h,C3h  ;PSone/late (PM-41(2))    06 Jun 2001, version vC3 (c)
  (unknown)        ;PS2,   xx xxx xxxx, late PS2 models...?

19h,21h --> INT3(flags)
Returns the current status of the POS0 and DOOR switches.
  Bit0   = HeadIsAtPos0 (0=No, 1=Pos0)
  Bit1   = DoorIsOpen   (0=No, 1=Open)
  Bit2   = EjectButtonOrOutSwOrSo? (DTL-H2000 only) (always 0 on retail)
  Bit3-7 = AlwaysZero

19h,22h --> INT3("for Europe")
  Caution: Supported only in BIOS version vC1 and up. Not supported in vC0.
Indicates the region that console is to be used in:
  INT5(11h,10h)      --> NTSC, Japan (vC0)         --> requires "SCEI" discs
  INT3("for Europe") --> PAL, Europe               --> requires "SCEE" discs
  INT3("for U/C")    --> NTSC, North America       --> requires "SCEA" discs
  INT3("for Japan")  --> NTSC, Japan / NTSC, Asia  --> requires "SCEI" discs
  INT3("for NETNA")  --> Region-free yaroze version--> requires "SCEx" discs
  INT3("for US/AEP") --> Region-free debug version --> accepts unlicensed CDRs
The CDROMs must contain a matching SCEx string accordingly.
The string "for Europe" does also suggest 50Hz PAL/SECAM video hardware.
The Yaroze accepts any normal SCEE,SCEA,SCEI discs, plus special SCEW discs.

19h,23h --> INT3("CXD2940Q/CXD1817Q/CXD2545Q/CXD1782BR") ;Servo Amplifier
19h,24h --> INT3("CXD2940Q/CXD1817Q/CXD2545Q/CXD2510Q") ;Signal Processor
19h,25h --> INT3("CXD2940Q/CXD1817Q/CXD1815Q/CXD1199BQ") ;Decoder/FIFO
  Caution: Supported only in BIOS version vC1 and up. Not supported in vC0.
Indicates the chipset that the CDROM controller is intended to be used with. The strings aren't always precisely correct (CXD1782BR is actually CXA1782BR, ie. CXA, not CXD) (and CXD1199BQ chips exist on PU-7 boards, but later PU-8 boards do actually use CXD1815Q) (and CXD1817Q is actually CXD1817R) (and newer PSones are using CXD2938Q or possibly CXD2941R chips, but nothing called CXD2940Q).
Note: Yaroze responds by CXD1815BQ instead of CXD1199BQ (but not by CXD1815Q).

19h,04h --> INT3(stat) ;Read SCEx string (and force motor on)
Resets the total/success counters to zero, and does then try to read the SCEx string from the current location (the SCEx is stored only in the Lead-In area, so, if the drive head is elsewhere, it will usually not find any strings, unless a modchip is permanently simulating SCEx strings).
This is a raw test command (the successful or unsuccessful results do not lock/unlock the disk). The results can be read with command 19h,05h (which will terminate the SCEx reading), or they can be read from RAM with command 19h,60h,lo,hi (which doesn't stop reading). Wait 1-2 seconds before expecting any results.
Note: Like 19h,00h, this command forces the drive motor to spin at standard speed (synchronized with the data on the disk), works even if the shell is open (but stops spinning after a while if the drive is empty).

19h,05h --> INT3(total,success) ;Get SCEx Counters
Returns the total number of "Sxxx" strings received (where at least the first byte did match), and the number of full "SCEx" strings (where all bytes did match). Typically, the values are "01h,01h" for Licensed PSX Data CDs, or "00h,00h" for disk missing, unlicensed data CDs, Audio CDs.
The counters are reset to zero, and SCEx receive mode is active for a few seconds after booting a new disk (on power up, on closing the drive door, on sending a Reset command, and on sub_function 04h). The disk is unlocked if the "success" counter is nonzero, the only exception is sub_function 04h which does update the counters, but does not lock/unlock the disk.


CDROM - Test Commands - Test Drive Mechanics

Signal Processor and Servo Amplifier

19h,50h,msb[,mid,[lsb[,xlo]]] --> INT3(stat)
Sends an 8bit/16bit/24bit command to the hardware, depending on number of parameters:
  1 byte  --> send CX(Xx)       ;short 8bit command
  2 bytes --> send CX(Xxxx)     ;longer 16bit command
  3 bytes --> send CX(Xxxxxx)   ;full 24bit command
  4 bytes --> send CX(Xxxxxxxx) ;extended 32bit command (BIOS vC3 only)
  4..15 bytes: acts same as max (3 or 4 bytes) (extra bytes are ignored)
  0 bytes or more than 15 bytes: generates an error

19h,51h,msb[,mid,[lsb]] --> INT3(stat,hi,lo) ;BIOS vC2/vC3 only
Supported by newer CDROM BIOSes only (such that use CXD2545Q or newer chips).
Works same as 19h,50h, but does additionally receive a response.
The command is always sending a 24bit CX(Xxxxxx) command, but it doesn't verify the number of parameter bytes (when using more than 3 bytes: extra bytes are ignored, when using less than 3 bytes: garbage is appended, which is somewhat valid because 8bit/16bit commands can be padded to 24bit size by appending "don't care" bits).
The command can be used to send any CX(..) command, but actually it does make sense only for the get-status commands, see below "19h,51h,39h,xxh" description.

19h,51h,39h,xxh --> INT3(stat,hi,lo) ;BIOS vC2/vC3 only
Supported by newer CDROM BIOSes only (such that use CXD2545Q or newer chips).
Sends CX(39xx) to the hardware, and receives a response (the response.hi byte is usually 00h for 8bit responses, or 00h..01h for 9bit responses). For example, this can be used to dump the Coefficient RAM.

19h,03h --> INT3(stat) ;force motor off
Forces the motor to stop spinning (ignored during spin-up phase).

19h,17h --> INT3(stat) ;force motor on, clockwise, super-fast
19h,01h --> INT3(stat) ;force motor on, anti-clockwise, super-fast
19h,02h --> INT3(stat) ;force motor on, anti-clockwise, super-fast
19h,10h --> INT3(stat) ;force motor on, anti-clockwise, super-fast
19h,18h --> INT3(stat) ;force motor on, anti-clockwise, super-fast
Forces the drive motor to spin at maximum speed (which is much faster than normal or double speed), in normal (clockwise), or reversed (anti-clockwise) direction. The commands work even if the shell is open. The commands do not try to synchronize the motor with the data on the disk (and do thus work even if no disk is inserted).

19h,00h --> INT3(stat) ;force motor on, clockwise (even if shell open)
This command seems to have effect only if the drive motor was off. If it was off, it does FFh-fills the TOC entries in RAM, and seek to the begin of the TOC at 98:30:00 or so (where minute=98 means minus two). From that location, it follows the spiral on the disk, although it does occassionally jump back some seconds. After clearing the TOC, the command does not write new data to the TOC buffer in RAM.
Note: Like 19h,04h, this command forces the drive motor to spin at standard speed (synchronized with the data on the disk), works even if the shell is open (but stops spinning after a while if the drive is empty).

19h,11h --> INT3(stat) ;Move Lens Up (leave parking position)
19h,12h --> INT3(stat) ;Move Lens Down (enter parking position)
19h,13h --> INT3(stat) ;Move Lens Outwards (away from center of disk)
19h,14h --> INT3(stat) ;Move Lens Inwards (towards center of disk)
Moves the laser lens. The inwards/outwards commands do move ONLY the lens (ie. unlike as for Seek commands, the overall-laser-unit remains in place, only the lens is moved).

19h,15h - if motor on: move head outwards + inwards + motor off
Moves the drive head to outer-most and inner-most position. Note that the drive doesn't have a switch that'd tell the controller when it has reached the outer-most position (so it'll forcefully hit against the outer edge) (ie. using this command too often may destroy the drive mechanics).
Note: The same destructive hit-outer-edge effect happens when using Setloc/Seek with too large values (like minute=99h).

19h,16h --> INT3(stat) ;Unknown / makes some noise if motor is on
19h,19h --> INT3(stat) ;Unknown / no effect
19h,1Ah --> INT3(stat) ;Unknown / makes some noise if motor is on
Seem to have no effect?
19h,16h seems to Move Lens Inwards, too.

19h,06h,new --> INT3(old) ;Adjust balance in RAM, and apply it via CX(30+n)
19h,07h,new --> INT3(old) ;Adjust gain in RAM, and apply it via CX(38+n)
19h,08h,new --> INT3(old) ;Adjust balance in RAM only
These commands are supported only by older CDROM BIOS versions (those with CXA1782BR Servo Amplifier).
Later BIOSes will respond with INT5(11h,20h) when trying to use these commands (because CXD2545Q and later Servo Amplifiers don't support the CX(30/38+n) commands).


CDROM - Test Commands - Prototype Debug Transmission

Serial Debug Messages
Older CDROM BIOSes are supporting debug message transmission via serial bus, using lower 3bit of the HC05 "databus" combined with the so-called "ROMSEL" pin (which apparently doesn't refer to Read-Only-Memory, but rather something like Runtime-Output-Message, or whatever).
Data is transferred in 24bit units (8bit command/index from HC05, followed by 16bit data to/from HC05), bigger messages are divided into multiple such 24bit snippets.
There are no connectors for external debug hardware on any PSX mainboards, so the whole stuff seems to be dating back to prototypes. And it seems to be removed from later BIOSes (which appear to use "ROMSEL" as "SCLK"; for receiving status info from the new CXD2545Q chips).

19h,30h,index,dat1,dat2 --> INT3(stat) ;Prototype/Debug stuff
19h,31h,dat1,dat2 --> INT3(stat) ;Prototype/Debug stuff
19h,4xh,index --> INT3(dat1,dat2) ;Prototype/Debug stuff
These functions are supported on older CDROM BIOS only; later BIOSes respond by INT5(11h,10h).
The functions do not affect the CDROM operation (they do simple allow to transfer data between Main CPU and external debug hardware).
Sub functions 30h and 31h may fail with INT5(11h,80h) when receiving wrong signals on the serial input line.
Sub function "4xh" value can be 40h..4Fh (don't care).

INT5 Debug Messages
Alongsides to INT5 errors, the BIOS is usually also sending information via the above serial bus (the error info is divided into multiple 8bit+16bit snippets, and contains stat, error code, mode, current SubQ position, and most recently issued command).


CDROM - Test Commands - Read/Write Decoder RAM and I/O Ports

Caution: Below commands 19h,71h..76h are supported only in BIOS version vC1 and up. Not supported in vC0.

19h,71h,index --> INT3(databyte) ;Read single register
index can be 00h..1Fh, bigger values seem to be mirrored to "index AND 1Fh", with one exception: index 13h in NOT mirrored, instead, index 33h, 53h, 93h, B3h, D3h, F3h return INT5(stat+1,10h), and index 73h returns INT5(stat+1,20h).
Aside from returning a value, the commands seem to DO something (like moving the drive head when a disk is inserted). Return values are usually:
  index     value
  00h       04h      ;04h=empty, 8Eh=licensed, 24h=audio
  01h       [0B1h]   ;DCh=empty/licensed, DDh=audio
  02h       00h
  03h       00h          ;or variable when disk inserted
  04h       00h
  05h       80h          ;or 86h or 89h when disk inserted
  06h       C0h
  07h       02h
  08h       8Ah
  09h       C0h
  0Ah       00h
  0Bh       C0h
  0Ch       [1F2h]
  0Dh       [1F3h]
  0Eh       00h          ;or 8Eh or E6h when disk inserted    ;D4h/audio
  0Fh       00h          ;or sometimes 01h when disk inserted ;50h/audio
  10h       C0h
  11h       E0h
  12h       71h
  13h       stat
  14h       FFh
  15h..1Fh  C0h-filled        ;or 17h --> DEh

19h,72h,index,databyte --> INT3(stat) ;Write single register
  ;other response on param xx16h,xx18h with xx>00h

19h,73h,index,len --> INT3(databytes...) ;Read multiple registers (bugged)
19h,74h,index,len,databytes --> INT3(stat) ;Write multiple registers (bugged)
Same as read/write single register, but trying to transfer multiple registers at once. BUG: The transfer should range from 00h to len-1, but the loop counter is left uninitialized (set to X=48h aka "command number 19h-minus-1-mul-2" instead of X=00h). Causing to the function to read/write garbage at index 48h..FFh, it does then wrap to 00h and do the correct intended transfer, but the preceeding bugged part may have smashed RAM or I/O ports.

19h,75h --> INT3(remain.lo,remain.hi,addr.lo,addr.hi) ;Get Host Xfer Info
Returns a 4-byte value. In my early tests, on the first day it returned B1h,CEh,4Ch,01h, on the next day 2Ch,E4h,95h,D5h, and on all following days 00h,C0h,00h,00h (no idea why/where the earlier values came from).
The first byte seems to be always 00h; no matter of [1F0h].
The second byte seems to be always C0h; no matter of [1F1h].
The third,fourth bytes are [1F2h,1F3h].
That two bytes are 0Ch,08h after Read commands.
  The first bytes are NOT affected by:
  destroying [1F0h] via too-many-parameters in command-buffer,
  changes to [1F1h] which may occur after read command (eg. may be 20h)

19h,76h,len_lo,len_hi,addr_lo,addr_hi --> INT3(stat) ;Prepare SRAM Transfer
Prepare Transfer to/from 32K SRAM.
After INT3, data can be read (same way as sector data after INT1).


CDROM - Test Commands - Read HC05 SUB-CPU RAM and I/O Ports

19h,60h,addr_lo,addr_hi --> INT3(data) ;Read one byte from Drive RAM or I/O
Reads one byte from the controller's RAM or I/O area, see the memory map below for more info. Among others, the command allows to read Subchannel Q data, eg. at [200h..209h], including ADR=2/UPC/EAN and ADR=3/ISRC values (which are suppressed by GetlocP). Eg. wait for ADR<>2, then for ADR=2, then read the remaining 9 bytes (because of the delayed IRQs, this works only at single speed) (at double speed one can read only 5 bytes before the values get overwritten by new data). Unknown if older boards (with 4.00MHz oscillators) are fast enough to read all 10 SubQ bytes.

CDROM Controller I/O Area and RAM Memory Map
First 40h bytes are I/O ports (as in MC68HC05 datasheet):
  000h 4        FF 7B 00 FF  (other when disk inserted)
  004h 5        11 00 20 20 0C
  009h 1        00 (when disk inserted: changes between 00 or 80)
  00Ah 2        71 00
  00Ch 1        00 (when disk inserted: changes between 00 or 80)
  00Dh 3        20 20 20
  010h 8        02 80 00 60 00 00 99(orBB) 98
  018h 4     changes randomly (even when no disk inserted)
  01Ch 3        40 00 41
  01Fh 1     changes randomly (even when no disk inserted)
  020h 30    20h-filled
  03Eh 2        82h 20h
Next 200h bytes are RAM:
  040h 4        08 00 00 00  ;or 98 07 xx 0B when disk inserted ;[40].Bit1=MUTE
  044h 4     00h-filled
  048h 3        40 20 00     ;or 58 71 0F when disk inserted
  04Bh 1     changes randomly (nodisk: 00 or 80 / disk: BFh)
  04Ch 1     Zero (or C0h)
  04Dh 3     MM:SS:FF (begin of current track MM:SS:00h) (or increasing addr)
  050h 10    Subchannel Q (adjusted position values)
  05Ah 2        ...
  05Ch 1     00h (or 64h)
  05Dh 3     MM:SS:FF (current read address) (sticky address during pause)
  060h 1     increments at circa 16Hz or so (or other rate when spinning)
  061h 12    00h-filled ;or else when disk inserted
  06Dh 1        01      ;or 0C when disk inserted
  06Eh 2     SetFilter setting (file,channel)
  070h 16    00h-filled ;or else when disk inserted
  080h 8     00h-filled
  088h 3        03:SS:FF (three, second, fraction)
  08Bh 3        03:SS:FF (three, second, fraction)
  08Eh 2        01 FF (or other values)
  090h 1        00h (or 91h when disk inserted + spinning)
  091h 13    Zero
  09Eh 1        00h (or 01h when disk inserted + spinning)
  09Fh 1     Zero
  0A0h 1     Always 23h
  0A1h 1        09h (5Dh when disk inserted)
  0A2h 7     00h-filled
  0A9h 1        40
  0AAh 4     00h-filled
  0AEh 1        00 (no disk) or 01 (disk) or so
  0AFh 1        00            ;or 06 when disk inserted
  0B0h 7        00 DC 00 02 00 E0 08             ;\or else when disk inserted
  0B7h 1        20         ;Bit6+7=MUTE          ;
  0B8h 3        DE 00 00                         ;/
  0BBh 1     SetMode setting (mode)
  0BCh 1     GetStat setting (stat)
  0BDh 3     00h-filled
  0C0h 6     FFh-filled            ;stack...                    ;\
  0C6h 1     Usually DFh           ;sometimes [0EBh and up] are non-FFh, too
  0C7h 15    FFh-filled            ;(depending on disk or commands or so)
  0D6h 1     Usually FDh (or FFh)  ;                            ;
  0D7h 24    FFh-filled                                         ; stack
  0EFh 4     on power-up FFh-filled, other once when disk read  ;
  0F3h 7     changes randomly (even when no disk inserted)      ;
  0FAh 6       2E 3C 2A D6 10 95                                ;/
  100h 2x99  TOC Entries for Start of Track 1..99 (MM:SS)
  1C6h 1     TOC First Track number (usually 01h)
  1C7h 1     TOC Last Track number (usually 01h or higher)
  1C8h 3     TOC Entry for Start of Lead-Out (MM:SS:FF)
  1CBh 2     Zero
  1CDh 1     Depends on disk (01 or 02 or 06) (or 00 when no disk)
  1CEh 1     Zero
  1CFh 1     Depends on disk (NULL minus N*6) (or 00 when no disk)
               (maybe reflection level / laser intensity or so)
                [1CDh..1CFh]
                01 00 E8 --> licensed/metalgear/kain
                01 00 EE --> licensed/alone2
                06 00 E2 or 00 00 02 00 E8 --> licensed/wipeout
                02 00 DC --> unlicensed/elo
                02 00 D6 --> unlicensed/driver
                00 00 EE --> audio/lola
                00 00 FA --> audio/marilyn
                00 00 F4 --> audio/westen
                00 00 00 --> disk missing
               last byte is always in steps of 6
  1D0h 4     SCEx String
  1D4h 4     Zero
  1D8h 2     SCEx Counters (total,success)  ;for command 19h,05h
  1DAh 6       00h-filled     (or ... SS:FF)
  1E0h 6     Command Buffer (usually 19h,60h,E2h,01h = Read RAM Command)
  1E6h 7       00h-filled (unless destroyed by more-than-6-byte-commands)
  1EDh 3     Setloc setting (MM:SS:FF)
  1F0h 1       00h          (unless destroyed by more-than-6-byte-commands)
  1F1h 3       C0h 00h 00h ;or 20h,0Ch,50h or C0h,0Ch,08h ;for command(19h,75h)
                           ;or 00h,00h,00h for audio
                           ;or 80h,00h,00h for disk missing
  1F4h 4       00h-filled ... or SCEx string
  1F8h 1       00h
  1F9h 1     Selected Target (parameter from Play and SetSession commands)
  1FAh 5       00h-filled       ;01 01 00 8B 00 00   ;or 01 02 8B 00 00
                       01 00 8B 00 00 -- audio/unlicensed
                       01 01 00 00 00 -- licensed
  1FFh 1       00h-on power up, changes when disk inserted  ;or 01 = Playing
    1FDh 3       MM:SS:FF (only during command 19h,00h) (MM=98..99=TOC)
  200h 10    Subchannel Q (real values)
  20Ah 2       whatever
  20Ch 1     Zero
  20Dh 1     Desired Session (from SetSession command)
  20Eh 1     Current Session (actual location of drive head)
  20Fh 1     Zero
  210h 10    Subchannel Q (adjusted position values)
  21Ah 6     00h-filled
  220h 4     Data Sector Header (MM:SS:FF:Mode)
  224h 4     Data Sector CD-XA Subheader (file,channel,sm,ci)
  228h 1         00h
  229h 1     Usually 00h (shortly other value on power-up, and maybe on seek)
  22Ah 1         10h (or 00h when no disk)
  22Bh 3     00h-filled
  22Eh 2         01,03 or 0A,00 or 03,01 (or else for other disk)
  230h 3     00h-filled (or other during spin-up / read-toc or so)
  233h 0Dh   00h-filled (unused RAM)
Other/invalid addresses are:
  240h..2FFh  - Invalid (00h-filled) (no ROM, RAM, or I/O mapped here)
  300h..3FFh  - Mirror of 200h..2FFh    ;\the BIOS is doing that
  400h..FFFFh - Mirrors of 000h..3FFh   ;/mirroring by software

DTL-H2000 Memory Map
This version allows to read the whole 64Kbyte memory space (withou mirroring everything to first 300h bytes). I/O Ports and Variables are at different locations:
  000h..0DFh   RAM Part 1 (C0h bytes)
  0E0h..0FFh   I/O Area
  100h..1DFh   RAM Part 2 (C0h bytes)
  1E0h..1FFh   I/O Area
  200h..2DFh   RAM Part 3 (100h bytes)
  2E0h..7FFFh  Unknown
  8000h-BFFFh  Unknown  (lower 16K of 32K EPROM) (or unused?)
  C000h-FFFFh  Firmware (upper 16K of 32K EPROM)

Writing to RAM
There is no command for writing to RAM. Except that, one can write to the command/parameter buffer at 1E0h and up. Normally, the longest known command should have 6 bytes (19h,76h,a,b,c,d), and longer commands results in "bad number of parameters" response - however, despite of that error message, the controller does still store ALL parameter bytes in RAM (at address 1E1h..2E0h, then wrapping back to 1E1h). Whereas, writing more than 16 bytes (FIFO storage size) will mirror the FIFO content twice, and more than 32 bytes (FIFO counter size) will work only when feeding extra data into the FIFO during transmission. Anyways, writing to 1E1h and up doesn't allow to do interesting things (such like manipulating the stack and executing custom code on the CPU).

Subchannel Q Notes
The "adjusted position values" at 050h, 210h, 310h contain only position information (with ADR=1) (the PSX seems to check only the lower 2bit of the 4bit ADR value, so it also treats ADR=5 as ADR=1, too). Additionally, during Lead-In, bytes 7..9 are overwritten by the position value from bytes 3..5. The "real values" contain unadjusted data, including ADR=2 and ADR=3 etc.


CDROM - Secret Unlock Commands

SecretUnlockPart1 - Command 50h --> INT5(11h,40h)
SecretUnlockPart2 - Command 51h,"Licensed by" --> INT5(11h,40h)
SecretUnlockPart3 - Command 52h,"Sony" --> INT5(11h,40h)
SecretUnlockPart4 - Command 53h,"Computer" --> INT5(11h,40h)
SecretUnlockPart5 - Command 54h,"Entertainment" --> INT5(11h,40h)
SecretUnlockPart6 - Command 55h,<region> --> INT5(11h,40h)
SecretUnlockPart7 - Command 56h --> INT5(11h,40h)
  Caution: Supported only in BIOS version vC1 and up. Not supported in vC0.
  Caution: Supported only in Europe/USA. Nonfunctional in Japan/Asia.
  Caution: When unsupported, Parameter Fifo isn't cleared after the command.
Sending these commands with the correct strings (in order 50h through 56h) does disable the "SCEx" protection. The region can be detected via test command 19h,22h, and must be translated to the following <region> string:
  "of America"    ;for NTSC/US          ;\
  "(Europe)"      ;for PAL/Europe       ; handled, and actually working
  "World wide"    ;for Yaroze           ;/
  "Inc."          ;for NTSC/JP          ;-non-functional
In the unlocked state, ReadN/ReadS are working for unlicensed CD-Rs, and for imported CDROMs from other regions (both without needing modchips). However there are some cases which may still cause problems: The GetID command (1Ah) does still identify the disc as being unlicensed, same for the Get SCEx Counters test command (19h,05h). And, if a game should happen to send the Reset command (1Ch) for some weird reason, then the BIOS would forget the unlocking, same for games that set the "HCRISD" I/O port bit. On the contrary, opening/closing the drive door does not affect the unlocking state.
The commands have been discovered in September 2013, and appear to be supported by all CDROM BIOS versions (from old PSXes up to later PSones).
Note that the commands do always respond with INT5 errors (even on successful unlocking).
Japanese consoles are internally containing code for processing the Secret Unlock commands, but they are not actually executing that code, and even if they would do so: they are ignoring the resulting unlocking flag, making the commands nonfunctional in Japan/Asia regions.

SecretLock - Command 57h --> INT5(11h,40h)
Undoes the unlocking and restores the normal locked state (same happens when sending the Unlocking commands in wrong order or with wrong parameters).

SecretCrash - Command 58h..5Fh --> Crash
Jumps to a data area and executes random code. Results are more or less unpredictable (as they involve executing undefined opcodes). Eventually the CPU might hit a RET opcode and recover from the crash.


CDROM - Video CD Commands

  Caution: Supported only on SCPH-5903, not supported on any other consoles.
  Caution: When unsupported, Parameter Fifo isn't cleared after the command.

  1Fh VideoCD      sub,a,b,c,d,e   INT3(stat,a,b,c,d,e)   ;<-- SCPH-5903 only
  1Fh..4Fh -       -               INT5(11h,40h)  ;-Unused/invalid

VideoCdSio - Cmd 1Fh,01h,JoyL,JoyH,State,Task,0 --> INT3(stat,req,mm,ss,ff,x)
The JoyL/JoyH bytes contain 16bit button (and drive door) bits:
  0  Drive Door  (0=Open)    (from CDROM stat bit4) ;Open
  1  Button /\   (0=Pressed) (from PSX pad bit12) ;N/A       ;PBC: Back/LevelUp
  2  Button []   (0=Pressed) (from PSX pad bit15) ;Enter Menu
  3  Button ()   (0=Pressed) (from PSX pad bit13) ;Leave Menu     ;PBC: Confirm
  4  Button ><   (0=Pressed) (from PSX pad bit14) ;N/A
  5  Start       (0=Pressed) (from PSX pad bit3)  ;Play/Pause
  6  Select      (0=Pressed) (from PSX pad bit0)  ;Stop (prompt restart/resume)
  7  Always 0    (0)         (fixed)              ;N/A
  8  DPAD Up     (0=Pressed) (from PSX pad bit4)  ;Menu Up           ;PBC: +1
  9  DPAD Right  (0=Pressed) (from PSX pad bit5)  ;Menu Right/change ;PBC: +10
  10 DPAD Down   (0=Pressed) (from PSX pad bit6)  ;Menu Down         ;PBC: -1
  11 DPAD Left   (0=Pressed) (from PSX pad bit7)  ;Menu Left/change  ;PBC: -10
  12 Button R1   (0=Pressed) (from PSX pad bit11) ;Prev Track/Restart Track
  13 Button R2   (0=Pressed) (from PSX pad bit9)  ;Fast Forward (slowly)
  14 Button L1   (0=Pressed) (from PSX pad bit10) ;Next Track (if any)
  15 Button L2   (0=Pressed) (from PSX pad bit8)  ;Fast Backward (slowly)
The State byte can be:
  00h  Motor Off (or spin-up)   (when stat.bit1=0)
  01h  Playing                  (when stat.bit7=1)
  02h  Paused (and not seeking) (when stat.bit6=0)
  (note: State remains unchanged when seeking)
The Task byte can be:
  00h = Confirms that "Tocread" (aka setsession 1) request was processed
  01h = Detect VCD Disc (used on power-up, and after door open) (after spin-up)
  02h = Handshake (request ack response)
  0Ah = Door opened during play (int5/door error)
  80h = No disc
  FFh = No change (nop)
The req byte in the INT3 response can be:
  00h  Normal (no special event occured and no action requested)
  01h  Request CD to Seek_and_play (using mm:ss:ff response parameter bytes)
  02h  Request CD to Pause                ;cmd(09h)    -->int3(stat),int2(stat)
  03h  Request CD to Stop                 ;cmd(08h)    -->int3(stat),int2(stat)
  04h  Request CD to Tocread (setsession1);cmd(12h,01h)-->int3(stat),int2(stat)
  05h  Handshake Command was processed, and this is the "ack" response
  06h  Request CD to Fast Forward         ;cmd(04h)    -->int3(stat)
  07h  Request CD to Fast Backward        ;cmd(05h)    -->int3(stat)
  80h  Detect Command was processed, and disc was detected as VCD
  81h  Detect Command was processed, and disc was detected as Non-VCD

VideoCdSwitch - Cmd 1Fh,02h,flag,x,x,x,x --> INT3(stat,0,0,x,x,x)
  00h      = Normal PSX Mode  (PortF.3=LOW)  (Audio/Video from GPU/SPU chips)
  01h..FFh = Special VCD Mode (PortF.3=HIGH) (Audio/Video from MDEC/OSD chips)

Some findings on the SC430924 firmware...
The version/date is "15 Aug 1996, version C2h", although the "C2h" is misleading: The firmware is nearly identical to version "C1h" from PU-8 boards (the stuff added in normal "C2h" versions would be for PU-18 boards with different cdrom chipset).

Compared to the original C1h version, there are only a few changes: A initialization function for initializing port F on power-up. And new command (command 1Fh, inserted in the various command tables), with two subfunctions (01h and 02h):
- Command 1Fh,01h,a,b,c,d,e --> INT3(stat,a,b,c,d,e) Serial 5-byte read-write
- Command 1Fh,02h,v,x,x,x,x --> INT3(stat,0,0,x,x,x) Toggle 1bit (port F.bit3)
Whereas,
  x = don't care/garbage
  v = toggle state (00h=normal=PortF.3=LOW, 01h..FFh=special=PortF.3=HIGH)
      (toggle gpu vs mpeg maybe?)
  a,b,c,d,e = five bytes sent serially, and five bytes response received
      serially (send/receive done simultaneously)

The Port F bits are:
  Port F.Bit0 = Serial Data In
  Port F.Bit1 = Serial Data Out
  Port F.Bit2 = Serial Clock Out
  Port F.Bit3 = Toggle (0=Normal, 1=Special)

And that's about all. Ie. essentially, the only change is that the new command controls Port F. There is no interaction with the remaining firmware (ie. reading, seeking, and everything is working as usually, without any video-cd related changes).
The SCEx stuff is also not affected (ie. Video CDs would be seen as unlicensed discs, so the PSX couldn't read anything from those discs, aside from Sub-Q position data, of course). The SCEx region is SCEI aka "Japan" (or actually for Asia in this case).

Note
The SPU MUTE Flag (SPUCNT.14) does also affect VCD Audio (mute is applied to the final analog audio amplifier). All other SPUCNT bits can be zero for VCD.


CDROM - Mainloop/Responses

SUB-CPU Mainloop
The SUB-CPU is running a mainloop that is handling hardware events (by simple polling, not by IRQs):
  check for incoming sectors (from CDROM decoder)
  check for incoming commands (from Main CPU)
  do maintenance stuff on the drive mechanics
There is no fixed priority: if both incoming sector and incoming command are present, then the SUB-CPU may handle either one, depending on which portion of the mainloop it is currently executing.
There is no fixed timing: if the mainloop is just checking for a specific event, then a new event may be processed immediately, otherwise it may take whole mainloop cycle until the SUB-CPU sees the event.
Whereas, the mainloop cycle execution time isn't constant: It may vary depending on various details. Especially, some maintenance stuff is only handled approximately around 15 times per second (so there are 15 slow mainloop cycles per second).

Responses
The PSX can deliver one INT after another. Instead of using a real queue, it's merely using some flags that do indicate which INT(s) need to be delivered. Basically, there seem to be two flags: One for Second Response (INT2), and one for Data/Report Response (INT1). There is no flag for First Response (INT3); because that INT is generated immediately after executing a command.
The flag mechanism means that the SUB-CPU cannot hold more than one undelivered INT1. That, although the CDROM Decoder does notify the SUB-CPU about all newly received sectors, and it can hold up to eight sectors in the 32K SRAM. However, the SUB-CPU BIOS merely sets a sector-delivery-needed flag (instead of memorizing which/how many sectors need to be delivered, and, accordingly, the PSX can use only three of the available eight SRAM slots: One for currently pending INT1, one for undelivered INT1, and one for currently/incompletely received sector).

First Response (INT3) (or INT5 if failed)
The first response is sent immediately after processing a command. In detail:
The mainloop checks for incoming commands once every some clock cycles, and executes commands under following condition:
  Main CPU has sent a command, AND, there is no INT pending
  (if an INT is pending, then the command won't be executed yet, but will be
  executed in following mainloop cycles; once when INT got acknowledged)
  (even if no INT is pending, the mainloop may generate INT1/INT2 before
  executing the command, if so, as said above, the command won't execute yet)
Once when the command gets executed it will sent the first response immediately after the command execution (which may only take a few clock cycles, or some more cycles, for example Init/ReadTOC do include some time consuming initializations). Anyways, there will be no other INTs generated during command execution, so once when the command execution has started, it's guaranteed that the next INT will contain the first response.

Second Responses (INT2) (or INT5 if failed)
Some commands do send a second response after actual command execution:
  07h MotorOn    E -               INT3(stat), INT2(stat)
  08h Stop       E -               INT3(stat), INT2(stat)
  09h Pause      E -               INT3(stat), INT2(stat)
  0Ah Init         -               INT3(late-stat), INT2(stat)
  12h SetSession E session         INT3(stat), INT2(stat)
  15h SeekL      E -               INT3(stat), INT2(stat)  ;\use prior Setloc
  16h SeekP      E -               INT3(stat), INT2(stat)  ;/to set target
  1Ah GetID      E -               INT3(stat), INT2/5(stat,flg,typ,atip,"SCEx")
  1Dh GetQ       E adr,point       INT3(stat), INT2(10bytesSubQ,peak_lo)
  1Eh ReadTOC      -               INT3(late-stat), INT2(stat)
In some cases (like seek or spin-up), it may take more than a second until the 2nd response is sent.
It should be highly recommended to WAIT until the second response is generated BEFORE sending a new command (it wouldn't make too much sense to send a new command between first and second response, and results would be unknown, and probably totally unpredictable).
Error Notes: If the command has been rejected (INT5 sent as 1st response) then the 2nd response isn't sent (eg. on wrong number of parameters, or if disc missing). If the command fails at a later stage (INT5 as 2nd response), then there are cases where another INT5 occurs as 3rd response (eg. on SetSession=02h on non-multisession-disk).

Data/Report Responses (INT1)
  03h Play       E (track)         INT3(stat), optional INT1(report bytes)
  04h Forward    E -               INT3(stat), optional INT1(report bytes)
  05h Backward   E -               INT3(stat), optional INT1(report bytes)
  06h ReadN      E -               INT3(stat), INT1(stat), datablock
  1Bh ReadS      E?-               INT3(stat), INT1(stat), datablock


CDROM - Response Timings

Here are some response timings, measured in 33MHz units on a PAL PSone. The CDROM BIOSes mainloop is doing some maintenance stuff once and when, meaning that the response time will be higher in such mainloop cycles (max values), and less in normal cycles (min values). The maintenance timings do also depend on whether the motor is on or off (and probably on various other factors like seeking).

First Response
The First Response interrupt is sent almost immediately after processing the command (that is, when the mainloop sees a new command without any old interrupt pending). For GetStat, timings are as so:
  Command                Average   Min       Max
  GetStat (normal)       000c4e1h  0004a73h..003115bh
  GetStat (when stopped) 0005cf4h  000483bh..00093f2h
Timings for most other commands should be similar as above. One exception is the Init command, which is doing some initialization before sending the 1st response:
  Init                   0013cceh  000f820h..00xxxxxh
The ReadTOC command is doing similar initialization, and should have similar timing as Init command. Some (rarely used) Test commands include things like serial data transfers, which may be also quite slow.

Second Response
  Command                Average   Min       Max
  GetID                  0004a00h  0004922h..0004c2bh
  Pause (single speed)   021181ch  020eaefh..0216e3ch ;\time equal to
  Pause (double speed)   010bd93h  010477Ah..011B302h ;/about 5 sectors
  Pause (when paused)    0001df2h  0001d25h..0001f22h
  Stop (single speed)    0d38acah  0c3bc41h..0da554dh
  Stop (double speed)    18a6076h  184476bh..192b306h
  Stop (when stopped)    0001d7bh  0001ce8h..0001eefh
Moreover, Seek/Play/Read/SetSession/MotorOn/Init/ReadTOC are sending second responses which depend on seek time (and spin-up time if the motor was off). The seek timings are still unknown, and they are probably quite complicated:
The CDROM BIOS seems to split seek distance somehow into coarse steps (eg. minutes) and fine steps (eg. seconds/sectors), so 1-minute seek distance may have completely different timings than 59-seconds distance.
The amount of data per spiral winding increases towards ends of the disc (so the drive head will need to be moved by shorter distance when moving from minute 59 to 60 as than moving from 00 to 01).
The CDROM BIOS contains some seek distance table, which is probably optimized for 72-minute discs (or whatever capacity is used on original PSX discs). 80-minute CDRs may have tighter spiral windings (the above seek table is probably causing the drive head to be moved too far on such discs, which will raise the seek time as the head needs to be moved backwards to compensate that error).

INT1 Rate
  Command                Average   Min       Max
  Read (single speed)    006e1cdh  00686dah..0072732h
  Read (double speed)    0036cd2h  00322dfh..003ab2bh
The INT1 rate needs to be precise for CD-DA and CD-XA Audio streaming, exact clock cycle values should be: SystemClock*930h/4/44100Hz for Single Speed (and half as much for Double Speed) (the "Average" values are AVERAGE values, not exact values).


CDROM - Response/Data Queueing

[Below are some older/outdated test cases]

Sector Buffer
The CDROM sector buffer is 32Kx8 SRAM (IC303). The buffer is apparently divided into 8 slots, theoretically allowing to buffer up to 8 sectors.
BUG: The drive controller seems to allow only 2 of those 8 sectors (the oldest sector, and the current/newest sector).
Ie. after processing the INT1 for the oldest sector, one would expect the controller to generate another INT1 for next newer sector - but instead it appears to jump directly to INT1 for the newest sector (skipping all other unprocessed sectors). There is no known way to get around that effect.
So far, the big 32Kbyte buffer is entirely useless (the two accessible sectors could have been as well stored in a 8Kbyte chip) (unless, maybe the 32Kbytes have been intended for some error-correction "read-ahead" purposes, rather than as "look-back" buffer for old sectors; one of the unused slots might be also used for XA-ADPCM sectors).
The bottom line is that one should process INT1's as soon as possible (ie. before the cdrom controller receives and skips further sectors). Otherwise sectors would be lost without notice (there appear to be absolutely no overrun status flags, nor overrun error interrupts).

Sector Buffer Test Cases
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  Process INT1 --> receives sector header for 0:2:1
  Process INT1 --> receives sector header for 0:2:2
  Process INT1 --> receives sector header for 0:2:3
Above shows the normal flow when processing INT1's as they arise. Now, inserting delays (and not processing INT1's during that delays):
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  delay(1)
  Process INT1 --> receives sector header for 0:2:1 (oldest sector)
  Process INT1 --> receives sector header for 0:2:6 (newest sector)
  Process INT1 --> receives sector header for 0:2:7 (next sector)
Above suggests that the CDROM buffer can hold max 2 sectors (the oldest and current one). However, using a longer delay:
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  delay(2)
  Process INT1 --> receives sector header for 0:2:9  (oldest/overwritten)
  Process INT1 --> receives sector header for 0:2:11 (newest sector)
  Process INT1 --> receives sector header for 0:2:12 (next sector)
Above indicates that sector buffer can hold 8 sectors (as the sector 1 slot is overwritten by sector 9). And, another test with even longer delay:
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  delay(3)
  Process INT1 --> receives sector header for 0:2:17 (currently received)
  Process INT1 --> receives sector header for 0:2:16 (newest full sector)
  Process INT1 --> receives sector header for 0:2:17 (next sector)
  Process INT1 --> receives sector header for 0:2:18 (next sector)
Above is a special case where sector 17 appears twice; the first one is the sector 1 slot (which was overwritten by sector 9, and apparently then half overwritten by sector 17).

Sector Buffer VS GetlocL Response Tests
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  GetlocL
  Process INT3 --> receives getloc info for 0:2:0
  Process INT1 --> receives sector header for 0:2:1
  Process INT1 --> receives sector header for 0:2:2
  Process INT1 --> receives sector header for 0:2:3
Another test, with Delay BEFORE Getloc:
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  Delay(1)
  GetlocL
  Process INT1 --> receives sector header for 0:2:1
  Process INT3 --> receives getloc info for 0:2:6
  Process INT1 --> receives sector header for 0:2:6
  Process INT1 --> receives sector header for 0:2:7
Another test, with Delay AFTER Getloc:
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  GetlocL
  Delay(1)
  Process INT3 --> receives getloc info for 0:2:0
  Process INT1 --> receives sector header for 0:2:5
  Process INT1 --> receives sector header for 0:2:6
  Process INT1 --> receives sector header for 0:2:7
Another test, with Delay BEFORE and AFTER Getloc:
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  Delay(1)
  GetlocL
  Delay(1)
  Process INT1 --> receives sector header for 0:2:9
  Process INT1 --> receives sector header for 0:2:11
  Process INT3 --> receives getloc info for 0:2:12
  Process INT1 --> receives sector header for 0:2:12
  Process INT1 --> receives sector header for 0:2:13

Sector Buffer VS Pause Response Tests
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  Pause
  Process INT3 --> receives stat=22h (first pause response)
  Process INT2 --> receives stat=02h (second pause response)
Another test, with Delay BEFORE Pause:
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  Delay(1)
  Pause
  Process INT1 --> receives sector header for 0:2:1 (oldest)
  Process INT3 --> receives stat=22h (first pause response)
  Process INT2 --> receives stat=02h (second pause response)
Another test, with Delay AFTER Pause:
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  Pause
  Delay(1)
  Process INT3 --> receives stat=22h (first pause response)
  Process INT2 --> receives stat=02h (second pause response)
Another test, with Delay BEFORE and AFTER Pause:
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  Delay(1)
  Pause
  Delay(1)
  Process INT1 --> receives sector header for 0:2:9 (oldest/overwritten)
  Process INT3 --> receives stat=22h (first pause response)
  Process INT2 --> receives stat=02h (second pause response)
For above: Note that, despite of Pause, the CDROM is still writing to the internal buffer (and overwrites slot 1 by sector 9) (this might be because the Pause command isn't processed at all until INT1 is processed).

Double Commands (Getloc then Pause)
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  GetlocL
  Pause
  Process INT3 --> receives stat=22h (first pause response)
  Process INT2 --> receives stat=02h (second pause response)
Another test,
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  Delay(1)
  GetlocL
  Pause
  Process INT1 --> receives sector header for 0:2:1
  Process INT1 --> receives sector header for 0:2:6
  Process INT3 --> receives stat=22h (first pause response)
  Process INT2 --> receives stat=02h (second pause response)
Another test,
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  GetlocL
  Delay(1)
  Pause
  Process INT3 --> receives getloc info for 0:2:0 (first getloc response)
  Process INT3 --> receives stat=22h (first pause response)
  Process INT2 --> receives stat=02h (second pause response)
Another test,
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  Delay(1)
  GetlocL
  Delay(1)
  Pause
  Process INT1 --> receives sector header for 0:2:9 (oldest/overwritten)
  Process INT3 --> receives stat=22h (first pause response)
  Process INT2 --> receives stat=02h (second pause response)

Double Commands (Pause then Getloc)
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  Pause
  GetlocL
  Process INT3 --> receives getloc info for 0:2:0 (first getloc response)
  Process INT1 --> receives sector header for 0:2:1
  Process INT1 --> receives sector header for 0:2:2
  Process INT1 --> receives sector header for 0:2:3
Another test,
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  Delay(1)
  Pause
  GetlocL
  Process INT1 --> receives sector header for 0:2:1
  Process INT3 --> receives getloc info for 0:2:6 (first getloc response)
  Process INT1 --> receives sector header for 0:2:6
  Process INT1 --> receives sector header for 0:2:7
Another test,
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  Pause
  Delay(1)
  GetlocL
  Process INT3 --> receives stat=22h (first pause response)
  Process INT3 --> receives getloc info for 0:2:6 (first getloc response)
  (No further INT's, ie. read is paused, but second-pause-response is lost).
Another test,
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  Pause
  Delay(1)
  GetlocL
  Delay(1)
  Process INT3 --> receives stat=22h (first pause response)
  Process INT3 --> receives getloc info for 0:2:6 (first getloc response)
  Process INT2 --> receives stat=02h (second pause response)
Another test,
  Setloc(0:2:0)+Read
  Process INT1 --> receives sector header for 0:2:0
  Delay(1)
  Pause
  Delay(1)
  GetlocL
  Process INT1 --> receives sector header for 0:2:9
  Process INT1 --> receives sector header for 0:2:11
  Process INT3 --> receives getloc info for 0:2:12 (first getloc response)
  Process INT1 --> receives sector header for 0:2:12
  Process INT1 --> receives sector header for 0:2:13


CDROM Disk Format

Overview
The PSX uses a ISO 9660 filesystem, with data stored on CD-XA (Mode2) Sectors. ISO 9660 is standard for CDROM disks, although newer CDROMs may use extended filesystems, allowing to use long filenames and lowercase filenames, the PSX Kernel doesn't support such stuff, and, in fact, it's putting some restrictions on the ISO standard: it's limiting file names to MSDOS-style 8.3 format, and it's allowing only a limited number of files and directories per disk.

CDROM Filesystem (ISO 9660 aka ECMA-119)
  Originally intended for Mode1 Sectors (but is also used for CD-XA Mode2)
  Supports "FILENAME.EXT;VERSION" filenames (version is usually "1")
  Supports all-uppercase filenames and directory names (0-9, A-Z, underscore)
  For PSX: Max 8-character filenames with max 3-character extensions
  For PSX: Max 8-character directory names, without extension
  For PSX: Max one sector per directory (?)
  For PSX: Max one sector (or less?) per path table (?)

CDROM Extended Architecture (CD-ROM XA aka CD-XA)
  Uses Mode2 Sectors (see Sector Encoding chapter)
  Allows 800h or 914h byte data per sector (with/without error correction)
  Allows to break interleaved data into separate files/channels
  Supports XA-ADPCM compressed audio data
  Stores "CD-XA001" at 400h Primary Volume Descriptor (?)
  Stores 14 extra bytes in System Use area (LEN_SU) of Directory Entries

Physical Audio/CDROM Disk Format (ISO/IEC 10149 aka ECMA-130)
  Defines physical metrics of the CDROM and Audio disks
  Defines Sub-channels and Track.Index and Minute.Second.Fraction numbering
  Defines 14bit-per-byte encoding, and splits sectors into frames
  Defines ECC and EDC (error correction and error detection codes)

Available Documentation
ISO documents are commercial standards (not available for download), however, they are based on ECMA standards (which are free for download, however, the ECMA stuff is in PDF format, so one may treat it as commercial bullshit, too). CD-ROM XA is commercial only (not available for download), and, CD-XA doesn't seem to have become very popular outside of the PSX-world, so there's very little information available, portions of CD-XA are also used in the CD-i standard (which may be a little better or worse documented).

Stuff
  sessions  one or more sessions per disk
  tracks    99 tracks per disk     (01h..99h) (usually only 01h on Data Disks)
  index     99 indices per track   (01h..99h) (rarely used, usually always 01h)
  minutes   74 minutes per disk    (00h..73h) (or more, with some restrictions)
  seconds   60 seconds per minute  (00h..59h)
  sectors   75 sectors per second  (00h..74h)
  frames    98 frames per sector
  bytes     33 bytes per frame (24+1+8 = data + subchannel + error correction)
  bits      14 bits per byte   (256 valid combinations, and many invalid ones)

Track.Index (stored in subchannel, in BCD format)
Multiple Tracks are usually used only on Audio Disks (one track for each song, numbered 01h and up), a few Audio Disks may also split Tracks into separate fragments with different Index values (numbered 01h and up, but most tracks have only Index 01h). A simple Data Disk would usually contain only one Track (all sectors marked Track=01h and Index=01h), although some more complex Data Disks may have multiple Data tracks and/or Audio tracks.

Minute.Second.Sector (stored in subchannel, and in Data sectors, BCD format)
The sectors on CDROMs and CD Audio disks are numbered in Minutes, Seconds, and 1/75 fragments of a second (where a "second" is referring to single-speed drives, ie. the normal CD Audio playback speed).
Minute.Second.Sector is stored twice in the subchannel (once the "absolute" time, and once the "local" time).
The "absolute" sector number (counted from the begin of the disk) is mainly relevant for Seek purposes (telling the controller if the drive head is on the desired location, or if it needs to move the head backwards or forwards).
The "local" sector number (counted from the begin of the track) is mainly relevant for Audio Players, allowing to pass the data directly to the Minute:Second display, without needing to subtract the start address of the track.
Data disks are additionally storing the "absolute" values in their Data Areas, basically that's just the subchannel data duplicated, but more precisely assigned - the problem with the subchannel data is that the CD Audio standard seems to lack a clear definition that would assign the begin of the sub-channel block to the exact begin of a sector; so, when using only the subchannel data, some Drive Controllers may assign the begin of a new sector to another location as than other Controllers do, for Audio Disks that isn't too much of a problem, but for Data Disks it'd be fatal.

Subchannels
Each frame contains 8 subchannel bits (named P,Q,R,S,T,U,V,W). So, a sector (with 98 frames) contains 98 bits (12.25 bytes) for each subchannel.
CDROM Subchannels

Error Correction
Each Frame contains 8 bytes Error Correction information, which is mainly used for Audio Disks, but it isn't 100% fail-proof, for that reason, Data Disks are containing additional Error Correction in the 930h-byte data area (the audio correction is probably focusing on repairing the MSBs of the 16bit samples, and gives less priority on the LSBs). Error Correction is some kind of a huge complex checksum, which allows to detect the location of faulty bytes, and to fix them.

930h-Byte Sectors
The "user" area for each sector is 930h bytes (2352 bytes). That region is combined of the 24-byte data per frame (and excludes the 8-byte audio error correction info, and the 1-byte subchannel data).
Most CDROM Controllers are only giving access to this 930h-byte region (ie. there's no way to read the audio error correction info by software, and only limited access to the subchannel data, such like allowing to read only the Q-channel for showing track/minute/second in audio playback mode).
On Audio disks, the 930h bytes are plain data, on Data disks that bytes are containing headers, error correction, and usually only 800h bytes user data (for more info see Sector Encoding chapter).

Sessions
Multi-Sessions are mainly used on CDR's, allowing to append newer data at the end of the disk at a later time. First of, the old session must contain a flag indicating that there may be a newer session, telling the CDROM Controller to search if one such exists (and if that is equally flagged, to search for an even newer session, and so on until reaching the last and newest session).
Each session contains a complete new ISO Volume Descriptor, and may additionally contain new Path Tables, new Directories, and new Files. The Driver Controller is usually recursing only the Volume Descriptor of the newest session. However, the various Directory Records of the new session may refer to old files or old directories from previous sessions, allowing to "import" the older files, or to "rename" or "delete" them by assigning new names to that files, or by removing them from the directory.
The PSX is reportedly not supporting multi-session disks, but that doesn't seem to be correct, namely, the Setsession command is apparently intended for that purpose... though not sure if the PSX Kernel is automatically searching the newest session... otherwise the boot executable in the first session would need to do that manually by software, and redirect control to the boot executable in the last session.


CDROM Subchannels

Subchannel P
Subchannel P contains some kind of a Pause flag (to indicate muted areas between Audio Tracks). This subchannel doesn't have any checksum, so the data cannot be trusted to be intact (unless when sensing a longer stream of all-one's, or all zero's). Theoretically, the 98 pause bits are somehow associated to the 98 audio frames (with 24 audio bytes each) of the sector. However, reportedly, Subchannel P does contain two sync bits, if that is true, then there'd be only 96 pause flags for 98 audio frames. Strange.
Note: Another way to indicate "paused" regions is to set Subchannel Q to ADR=1 and Index=00h.

Subchannel Q
contains the following information:
  Bits Expl.
  2    Sub-channel synchronization field
  8    ADR/Control (see below)
  72   Data (content depends on ADR)
  16   CRC-16-CCITT error detection code (big-endian: bytes ordered MSB, LSB)
Possible values for the ADR/Control field are:
  Bit0-3 ADR (0=No data, 1..3=see below, 4..0Fh=Reserved)
  Bit4   Audio Preemphasis  (0=No, 1=Yes)      (Audio only, must be 0 for Data)
  Bit5   Digital Copy       (0=Prohibited, 1=Allowed)
  Bit6   Data               (0=Audio, 1=Data)
  Bit7   Four-Channel Audio (0=Stereo, 1=Quad) (Audio only, must be 0 for Data)
The 72bit data regions are, depending on the ADR value...

Subchannel Q with ADR=1 during Lead-In -- Table of Contents (TOC)
  8    Track number (fixed, must be 00h=Lead-in)
  8    Point (01h..99h or A0h..A2h, see last three bytes for more info)
  24   MSF address (incrementing address within the Lead-in area)
         Note: On some disks, these values are choosen so that the lead-in
         <starts> at 00:00:00, on other disks so that it <ends> at 99:59:74.
  8    Reserved (00h)
When Point=01h..99h (Track 1..99) or Point=A2h (Lead-Out):
  24   MSF address (absolute address, start address of the "Point" track)
When Point=A0h (First Track Number):
  8    First Track number (BCD)
  8    Disk Type Byte (00h=CD-DA or CD-ROM, 10h=CD-I, 20h=CD-ROM-XA)
  8    Reserved (00h)
When Point=A1h (Last Track Number):
  8    Last Track number (BCD)
  16   Reserved (0000h)
ADR=1 should exist in 3 consecutive lead-in sectors.

Subchannel Q with ADR=1 in Data region -- Position
  8    Track number (01h..99h=Track 1..99)
  8    Index number (00h=Pause, 01h..99h=Index within Track)
  24   Track relative MSF address (decreasing during Pause)
  8    Reserved (00h)
  24   Absolute MSF address
ADR=1 is required to exist in at least 9 out of 10 consecutive data sectors.

Subchannel Q with ADR=1 during Lead-Out -- Position
  8    Track number (fixed, must be AAh=Lead-Out)
  8    Index number (fixed, must be 01h) (there's no Index=00h in Lead-Out)
  24   Track relative MSF address (increasing, 00:00:00 and up)
  8    Reserved (00h)
  24   Absolute MSF address
ADR=1 should exist in 3 consecutive lead-out sectors (and may then be followed by ADR=5 on multisession disks).

Subchannel Q with ADR=2 -- Catalogue number of the disc (UPC/EAN barcode)
  52   EAN-13 barcode number (13-digit BCD)
  12   Reserved (000h)
  8    Absolute Sector number (BCD, 00h..74h) (always 00h during Lead-in)
If the first digit of the EAN-13 number is "0", then the remaining digits are a UPC-A barcode number. Either the 13-digit EAN-13 number, or the 12-digit UPC-A number should be printed as barcode on the rear-side of the CD package.
The first some digits contain a country code (EAN only, not UPC), followed by a manufacturer code, followed by a serial number. The last digit contains a checksum, which can be calculated as 250 minus the sum of the first 12 digits, minus twice the sum of each second digit, modulated by 10.
ADR=2 isn't included on all CDs, and, many CDs do have ADR=2, but the 13 digits are all zero. Most CDROM drives do not allow to read EAN/UPC numbers.
If present, ADR=2 should exist in at least 1 out of 100 consecutive sectors. ADR=2 may occur also in Lead-in.

Subchannel Q with ADR=3 -- ISRC number of the current track
(ISO 3901 and DIN-31-621):
  12   Country Code      (two 6bit characters)   (ASCII minus 30h) ;eg. "US"
  18   Owner Code        (three 6bit characters) (ASCII minus 30h)
  2    Reserved          (zero)
  8    Year of recording (2-digit BCD) ;eg. 82h for 1982
  20   Serial number     (5-digit BCD) ;usually increments by 1 or 10 per track
  4    Reserved          (zero)
  8    Absolute Sector number (BCD, 00h..74h) (always 00h during Lead-in)
Most CDROM drives for PC's do not allow to read ISRC numbers (or even worse, they may accidently return the same ISRC number on every two tracks).
If present, ADR=3 should exist in at least 1 out of 100 consecutive sectors. However, reportedly, ADR=3 should not occur in Lead-in.

Subchannel Q with ADR=5 in Lead-in -- Multisession Lead-In Info
When Point=B0h:
  8    Track number (fixed, must be 00h=Lead-in)
  8    POINT = B0h (multi-session disc)
  24   MM:SS:FF = the start time for the next possible session's program area,
       a final session is indicated by FFh:FFh:FFh,
       or when the ADR=5 / Point=B0h is absent.
  8    Number of different Mode-5 pointers present.
  24   MM:SS:FF = the maximum possible start time of the outermost Lead-out
When Point=C0h:
  8    Track number (fixed, must be 00h=Lead-in)
  8    POINT = C0h (Identifies a Multisession disc, together with POINT=B0h)
  24   ATIP values from Special Information 1, ID=101
  8    Reserved (must be 00h)
  24   MM:SS:FF = Start time of the first Lead-in area of the disc
And, optionally, when Point=C1h:
  8    Track number (fixed, must be 00h=Lead-in)
  8    POINT=C1h
  8x7  Copy of information from A1 point in ATIP

Subchannel Q with ADR=5 in Lead-Out -- Multisession Lead-Out Info
  8    Track number (fixed, must be AAh=Lead-out)
  8    POINT = D1h (Identifies a Multisession lead-out)
  24   Usually zero (or maybe ATIP as in Lead-In with Point=C0h...?)
  8    Seems to be the session number?
  24   MM:SS:FF = Absolute address of the First data sector of the session
Present in 3 consequtive sectors (3x ADR=1, 3x ADR=5, 3x ADR=1, 3x ADR=5, etc).

Subchannel Q with ADR=5 in Lead-in -- CDR/CDRW Skip Info (Audio Only)
When Point=01h..40h:
  8    Track number (fixed, must be 00h=Lead-in)
  8    POINT=01h..40h (This identifies a specific playback skip interval)
  24   MM:SS:FF Skip interval stop time in 6 BCD digits
  8    Reserved (must be 00h)
  24   MM:SS:FF Skip interval start time in 6 BCD digits
When Point=B1h:
  8    Track number (fixed, must be 00h=Lead-in)
  8    POINT=B1h (Audio only: This identifies the presence of skip intervals)
  8x4  Reserved (must be 00h,00h,00h,00h)
  8    the number of skip interval pointers in POINT=01h..40h
  8    the number of skip track assignments in POINT=B2h..B4h
  8    Reserved (must be 00h)
When Point=B2h,B3h,B4h:
  8    Track number (fixed, must be 00h=Lead-in)
  8    POINT=B2h,B3h,B4h (This identifies tracks that should be skipped)
  8    1st Track number to skip upon playback (01h..99h, must be nonzero)
  8    2nd Track number to skip upon playback (01h..99h, or 00h=None)
  8    3rd Track number to skip upon playback (01h..99h, or 00h=None)
  8    Reserved (must be 00h)... unclear... OR... 4th (of 7) skip info's...?
  8    4th Track number to skip upon playback (01h..99h, or 00h=None)
  8    5th Track number to skip upon playback (01h..99h, or 00h=None)
  8    6th Track number to skip upon playback (01h..99h, or 00h=None)
Note: Skip intervals are seldom written by recorders and typically ignored by readers.

Subchannel R..W
Subchannels R..W are usually unused, except for some extended formats:
  CD-TEXT in the Lead-In area (see below)
  CD-TEXT in the Data area    (rarely used)
  CD plus Graphics (CD+G)     (rarely used)
Most CDROM drives do not allow to read these subchannels. CD-TEXT was designed by Sony and Philips in 1997, so it should be found only on (some) newer discs. Most CD/DVD players don't support it (the only exception is that CD-TEXT seems to be popular for car hifi equipment). Most record labels don't support CD-TEXT, even Sony seems to have discontinued it on their own records after some years (so CD-TEXT is very rare on original disks, however, CDR software does often allow to write CD-TEXT on CDRs).

Subchannel R..W, when used for CD-TEXT in the Lead-In area
CD-TEXT is stored in the six Subchannels R..W. Of the 12.25 bytes (98 bits) per subchannel, only 12 bytes are used. Together, all 6 subchannels have a capacity of 72 bytes (6x12 bytes) per sector. These 72 bytes are divided into four CD-TEXT fragments (of 18 bytes each). The format of these 18 bytes is:
  00h 1  Header Field ID1: Pack Type Indicator
  01h 1  Header Field ID2: Track Number
  02h 1  Header Field ID3: Sequence Number
  03h 1  Header Field ID4: Block Number and Character Position Indicator
  04h 12 Text/Data Field
  10h 2  CRC-16-CCITT (big-endian) (across bytes 00h..0Fh)
ID1 - Pack Type Indicator:
  80h   Titel      (TEXT)
  81h   Performer  (TEXT)
  82h   Songwriter (TEXT)
  83h   Composer   (TEXT)
  84h   Arranger   (TEXT)
  85h   Message    (TEXT)
  86h   Disc ID    (TEXT?)  (content/format/purpose unknown?)
  87h   Genre      (BINARY) (ID codes unknown?)
  88h   TOC        (BINARY) (content/format/purpose unknown?)
  89h   TOC2       (BINARY) (content/format/purpose unknown?)
  8Ah   Reserved for future
  8Bh   Reserved for future
  8Ch   Reserved for future
  8Dh   Reserved for "content provider" aka "closed information"
  8Eh   UPC/EAN and ISRC Codes (TEXT) (content/format/purpose unknown?)
  8Fh   Blocksize (BINARY) (see below)
ID2 - Track Number:
  00h       Title/Performer/etc. for the Disc
  01h..63h  Title/Performer/etc. for Track 1..99 (Non-BCD) (Bit7=Extension)
ID3 - Sequence Number:
  00h..FFh  Incrementing Number (00h=First 18-byte fragment, 01h=Second, etc.)
ID4 - Block Number and Character Position Indicator:
  Bit7      Character Set      (0=8bit, 1=16bit)
  Bit6-4    Block Number       (0..7 = Language number, as set by "Blocksize")
  Bit3-0    Character Position (0..0Eh=Position, 0Fh=Append to prev fragment)
Example Data (generated with CDRWIN):
  ID TR SQ CH <------------Text/Data------------> -CRC-  <---Text--->
  80 00 00 00 54 65 73 74 44 69 73 6B 54 69 74 6C E2 22  TestDiskTitl
  80 00 01 0C 65 00 54 65 73 74 54 72 61 63 6B 54 C9 1B  e.TestTrackT
  80 01 02 0A 69 74 6C 65 31 00 54 65 73 74 54 72 40 3A  itle1.TestTr
  80 02 03 06 61 63 6B 54 69 74 6C 65 32 00 00 00 80 E3  ackTitle2...
  81 00 04 00 54 65 73 74 44 69 73 6B 50 65 72 66 03 DF  TestDiskPerf
  81 00 05 0C 6F 72 6D 65 72 00 54 65 73 74 54 72 12 A5  ormer.TestTr
  81 01 06 06 61 63 6B 50 65 72 66 6F 72 6D 65 72 BC 5B  ackPerformer
  81 01 07 0F 31 00 54 65 73 74 54 72 61 63 6B 50 AC 41  1.TestTrackP
  81 02 08 0A 65 72 66 6F 72 6D 65 72 32 00 00 00 64 1A  erformer2...
  8F 00 09 00 01 01 02 00 04 05 00 00 00 00 00 00 6D E2  ............
  8F 01 0A 00 00 00 00 00 00 00 00 03 0B 00 00 00 CD 0C  ............
  8F 02 0B 00 00 00 00 00 09 00 00 00 00 00 00 00 FC 8C  ............
  00   ;<--- for some reason, CDRWIN stores an ending 00h byte in .CDT files
Each Text string is terminated by a 00h byte (or 0000h for 16bit character set). If there's still room in the 12-byte data region, then first characters for the next Text string (for the next track) are appended after the 00h byte (if there's no further track, then the remaining bytes should be padded with 00h).
The "Blocksize" (ID1=8Fh) consists of three packs with 24h bytes of data (first 0Ch bytes stored with ID2=00h, next 0Ch bytes with ID2=01h, and last 0Ch bytes with ID2=02h):
  00h  1   Character set (00h,01h,80h,81h,82h = see below)
  01h  1   First track number (usually/always 01h)
  02h  1   Last track number (01h..63h)
  03h  1   1bit-cd-text-in-data-area-flag, 7bit-copy-protection-flags
  04h  16  Number of 18-byte packs for ID1=80h..8Fh
  14h  8   Last sequence number of block 0..7 (or 00h=none)
  1Ch  8   Language codes for block 0..7 (definitions are unknown)
Character Set values (for ID1=8Fh, ID2=00h, DATA[0]=charset):
  00h ISO 8859-1
  01h ISO 646, ASCII
  80h MS-JIS
  81h Korean character code
  82h Mandarin (standard) Chinese character code
  Other = reserved
"In case the same character stings is used for consecutive tracks, character 09h (or 0909h for 16bit charset) may be used to indicate the same as previous track. It shall not used for the first track."

adjust_crc_16_ccitt(addr_len) ;for CD-TEXT and Subchannel Q
  lsb=00h, msb=00h      ;-initial value (zero for both CD-TEXT and Sub-Q)
  for i=0 to len-1      ;-len (10h for CD-TEXT, 0Ah for Sub-Q)
    x = [addr+i] xor msb
    x = x xor (x shr 4)
    msb = lsb xor (x shr 3) xor (x shl 4)
    lsb = x xor (x shl 5)
  next i
  [addr+len+0]=msb xor FFh, [addr+len+1]=lsb xor FFh   ;inverted / big-endian


CDROM Sector Encoding

Audio
  000h 930h Audio Data (2352 bytes) (LeftLsb,LeftMsb,RightLsb,RightMsb)
Mode0 (Empty)
  000h 0Ch  Sync   (00h,FFh,FFh,FFh,FFh,FFh,FFh,FFh,FFh,FFh,FFh,00h)
  00Ch 4    Header (Minute,Second,Sector,Mode=00h)
  010h 920h Zerofilled
Mode1 (Original CDROM)
  000h 0Ch  Sync   (00h,FFh,FFh,FFh,FFh,FFh,FFh,FFh,FFh,FFh,FFh,00h)
  00Ch 4    Header (Minute,Second,Sector,Mode=01h)
  010h 800h Data (2048 bytes)
  810h 4    EDC (checksum across [000h..80Fh])
  814h 8    Zerofilled
  81Ch 114h ECC (error correction codes)
Mode2/Form1 (CD-XA)
  000h 0Ch  Sync   (00h,FFh,FFh,FFh,FFh,FFh,FFh,FFh,FFh,FFh,FFh,00h)
  00Ch 4    Header (Minute,Second,Sector,Mode=02h)
  010h 4    Sub-Header (File, Channel, Submode AND DFh, Codinginfo)
  014h 4    Copy of Sub-Header
  018h 800h Data (2048 bytes)
  818h 4    EDC (checksum across [010h..817h])
  81Ch 114h ECC (error correction codes)
Mode2/Form2 (CD-XA)
  000h 0Ch  Sync   (00h,FFh,FFh,FFh,FFh,FFh,FFh,FFh,FFh,FFh,FFh,00h)
  00Ch 4    Header (Minute,Second,Sector,Mode=02h)
  010h 4    Sub-Header (File, Channel, Submode OR 20h, Codinginfo)
  014h 4    Copy of Sub-Header
  018h 914h Data (2324 bytes)
  92Ch 4    EDC (checksum across [010h..92Bh]) (or 00000000h if no EDC)

encode_sector
  sector[000h]=00h,FFh,FFh,FFh,FFh,FFh,FFh,FFh,FFh,FFh,FFh,00h
  sector[00ch]=bcd(adr/75/60)      ;0..7x
  sector[00dh]=bcd(adr/75 MOD 60)  ;0..59
  sector[00eh]=bcd(adr MOD 75)     ;0..74
  sector[00fh]=mode
  if mode=00h then
    sector[010h..92Fh]=zerofilled
  if mode=01h then
    adjust_edc(sector+0, 800h+10h)
    sector[814h..817h]=00h,00h,00h,00h,00h,00h,00h,00h
    calc_p_parity(sector)
    calc_q_parity(sector)
  if mode=02h and form=1
    sector[012h]=sector[012h] AND (NOT 20h)  ;indicate not form2
    sector[014h..017h]=sector[010h..013h]    ;copy of sub-header
    adjust_edc(sector+10h,800h+8)
    push sector[00ch]           ;\temporarily clear header
    sector[00ch]=00000000h      ;/
    calc_p_parity(sector)
    calc_q_parity(sector)
    pop sector[00ch]            ;-restore header
  if mode=02h and form=2
    sector[012h]=sector[012h] OR 20h         ;indicate form2
    sector[014h..017h]=sector[010h..013h]    ;copy of sub-header
    adjust_edc(sector+10h,914h+8)            ;edc is optional for form2

calc_parity(sector,offs,len,j0,step1,step2)
  src=00ch, dst=81ch+offs, srcmax=dst
  for i=0 to len-1
    base=src, x=0000h, y=0000h
    for j=j0 to 42
      x=x xor GF8_PRODUCT[j,sector[src+0]]
      y=y xor GF8_PRODUCT[j,sector[src+1]]
      src=src+step1, if (step1=2*44) and (src>=srcmax) then src=src-2*1118
    sector[dst+2*len+0]=x AND 0FFh, [dst+0]=x SHR 8
    sector[dst+2*len+1]=y AND 0FFh, [dst+1]=y SHR 8
    dst=dst+2, src=base+step2
calc_p_parity(sector) = calc_parity(sector,0,43,19,2*43,2)
calc_q_parity(sector) = calc_parity(sector,43*4,26,0,2*44,2*43)

adjust_edc(addr,len)
  x=00000000h
  for i=0 to len-1
    x=x xor byte[addr+i], x=(x shr 8) xor edc_table[x and FFh]
  word[addr+len]=x  ;append EDC value (little endian)

init_tables
  for i=0 to FFh
    x=i, for j=0 to 7, x=x shr 1, if carry then x=x xor D8018001h
    edc_table[i]=x
  GF8_LOG[00h]=00h, GF8_ILOG[FFh]=00h, x=01h
  for i=00h to FEh
    GF8_LOG[x]=i, GF8_ILOG[i]=x
    x=x SHL 1, if carry8bit then x=x xor 1dh
  for j=0 to 42
    xx=GF8_ILOG[44-j],  yy=subfunc(xx xor 1,19h)
    xx=subfunc(xx,01h), xx=subfunc(xx xor 1,18h)
    xx=GF8_LOG[xx], yy = GF8_LOG[yy]
    GF8_PRODUCT[j,0]=0000h
    for i=01h to FFh
      x=xx+GF8_LOG[i], if x>=255 then x=x-255
      y=yy+GF8_LOG[i], if y>=255 then y=y-255
      GF8_PRODUCT[j,i]=GF8_ILOG[x]+(GF8_ILOG[y] shl 8)

subfunc(a,b)
  if a>0 then
    a=GF8_LOG[a]-b, if a<0 then a=a+255
    a=GF8_ILOG[a]
  return(a)


CDROM Scrambling

Scrambling
Scambling does XOR the data sectors with random values (done to avoid regular patterns). The scrambling is applied to Data sector bytes[00Ch..92Fh] (not to CD-DA audio sectors, and not to the leading 12-byte Sync mark in Data sectors).
The (de-)scrambling is done automatically by the CDROM controller, so disc images should usually contain unscrambled data (there are some exceptions such like CD-i discs that have audio and data sectors mixed inside of the same track; which may confuse the CDROM controller about whether or not to apply scrambling to which sectors; so one may need to manually XOR the faulty sectors in the disc image).
The scrambling pattern is derived from a 15bit polynomial counter (much like a noise generator in sound chips). The data bits are XORed with the counters low bit, and the counters lower 2bit are XORed with each other, and shifted in to the counters upper bit. To compute 8 bits and once, and store them in a 924h-byte table:
  poly=0001h  ;init 15bit polynomial counter
  for i=0 to 924h-1
    scramble_table[i]=poly AND FFh
    poly=(((poly XOR poly/2) AND 0FFh)*80h) XOR (poly/100h)
  next i
The resulting table content should be:
  01h,80h,00h,60h,00h,28h,00h,1Eh,80h,08h,60h,06h,A8h,02h,FEh,81h,
  80h,60h,60h,28h,28h,1Eh,9Eh,88h,68h,66h,AEh,AAh,FCh,7Fh,01h,E0h,
  etc.
After scrambling, the data is reportedly "shuffled and byte-swapped". Unknown what shuffling means. And unknown what/where/why byte-swapping is done (it does reportedly swap each two bytes in the whole(?) 930h-byte (data-?) sector; which might date back to different conventions for disc images to contain "16bit audio samples" in big- or little-endian format).


CDROM XA Subheader, File, Channel, Interleave

The Sub-Header for normal data sectors is usually 00h,00h,08h,00h (some PSX sectors have 09h instead 08h, indicating the end of "something" or so?

1st Subheader byte - File Number (FN)
  0-7 File Number    (00h..FFh) (for Audio/Video Interleave, see below)

2nd Subheader byte - Channel Number (CN)
  0-4 Channel Number (00h..1Fh) (for Audio/Video Interleave, see below)
  5-7 Should be always zero
Whilst not officially allowed, PSX Ace Combat 3 Electrosphere does use Channel=FFh for unused gaps in interleaved streaming sectors.

3rd Subheader byte - Submode (SM)
  0   End of Record (EOR) (all Volume Descriptors, and all sectors with EOF)
  1   Video     ;\Sector Type (usually ONE of these bits should be set)
  2   Audio     ; Note: PSX .STR files are declared as Data (not as Video)
  3   Data      ;/
  4   Trigger           (for application use)
  5   Form2             (0=Form1/800h-byte data, 1=Form2, 914h-byte data)
  6   Real Time (RT)
  7   End of File (EOF) (or end of Directory/PathTable/VolumeTerminator)
The EOR bit is set in all Volume Descriptor sectors, the last sector (ie. the Volume Descriptor Terminator) additionally has the EOF bit set. Moreover, EOR and EOF are set in the last sector of each Path Table, and last sector of each Directory, and last sector of each File.

4th Subheader byte - Codinginfo (CI)
When used for Data sectors:
  0-7 Reserved (00h)
When used for XA-ADPCM audio sectors:
  0-1 Mono/Stereo     (0=Mono, 1=Stereo, 2-3=Reserved)
  2-2 Sample Rate     (0=37800Hz, 1=18900Hz, 2-3=Reserved)
  4-5 Bits per Sample (0=Normal/4bit, 1=8bit, 2-3=Reserved)
  6   Emphasis        (0=Normal/Off, 1=Emphasis)
  7   Reserved        (0)

Audio/Video Interleave (Multiple Files/Channels)
The CDROM drive mechanics are working best when continously following the data spiral on the disk, that works fine for uncompressed Audio Data at normal speed, but compressed Audio Data the disk is spinning much too fast. To avoid the drive to need to pause reading or to do permanent backwards seeking, CD-XA allows to store data interleaved in separate files/channels. With common interleave values like so:
  Interleave   Data Format
  1/1 (none)   44100Hz Stereo CD Audio at normal speed
  1/8          37800Hz Stereo ADPCM compressed Audio at double speed
  1/16         18900Hz Stereo ADPCM compressed Audio at double speed
  1/16         37800Hz Mono   ADPCM compressed Audio at double speed
  1/32         18900Hz Mono   ADPCM compressed Audio at double speed
  7/8          15fps 320x224 pixel MDEC compressed Videos at double speed
  Unknown if 1/16 and 1/32 interleaves are actually possible (the PSX cdrom
  controller seems to overwrite the IC303 sector buffer entries once every
  eight sectors, so ADPCM data may get destroyed on interleaves above 1/8).
  (Crash Team Racing uses 37800Hz Mono at Double speed, so 1/16 must work).
For example, 1/8 means that the controller processes only each 8th sector (each having the same File Number and Channel Number), and ignores the next 7 sectors (which must have other File Number and/or other Channel Number). There are various ways to arrange multiple files or channels, for example,
  one file with eight 1/8 audio channels
  one file with one 1/8 audio channels, plus one 7/8 video channel (*)
  one file with one 1/8 audio channels, plus 7 unused channels
  eight different files with one 1/8 audio channel each
  etc.
(*) If the Audio and Video data belongs together then both should use the SAME channel.
Note: Above interleave values are assuming that PSX Game Disks are always running at double speed (that's fastest for normal data files, and ADPCM files are usually using the same speed; otherwise it'd be neccessary to change the drive speed everytime when switching between Data to ADPCM modes).
Note: The file/channel numbers can be somehow selected with the Setfilter command. No idea if the controller is automatically switching to the next channel or so when reaching the end of the file?

Unused sectors in Interleave
There are different ways to mark unused sectors in interleaved streams. Ace Combat 3 uses Channel=FFh=Invalid. Tron Bonne uses Submode=00h=Nothing (notably, that game has a 74Mbyte XA file that leaves about 75% unused).
  Subheader bytes: 01h,FFh,64h,01h   ;Ace Combat 3 Electrosphere
  Subheader bytes: 01h,00h,00h,00h   ;Misadventures of Tron Bonne (XA\*.XA)

Real Time Streaming
With the above Interleave, files can be played continously at real time - that, unless read-errors do occur. In that case the drive controller would usually perform time-consuming error-correction and/or read-retries. For video/audio streaming the resulting delay would be tendencially more annoying as than processing or skipping the incorrect data.
In such cases the drive controller is allowed to ignore read errors; that probably on sectors that have the Real Time (RT) flag set in their subheaders. The controller is probably doing some read-ahead buffering (so, if it has buffered enough data, then it may still perform read retries and/or error correction, as long as it doesn't affect real time playback).


CDROM XA Audio ADPCM Compression

CD-ROM XA ADPCM is used for Audio data compression. Each 16bit sample is encoded in 4bit nibbles; so the compression rate is almost 1:4 (only almost 1:4 because there are 16 header bytes within each 128-byte portion). The data is usually/always stored on 914h-byte sectors (without error correction).

Subheader
The Subheader (see previous chapter) contains important info for ADPCM: The file/channel numbers for Interleaved data, and the codinginfo flags: mono/stereo flag, 37800Hz/18900Hz sampling rate, 4bit/8bit format, and emphasis.

ADPCM Sectors
Each sector consists of 12h 128-byte portions (=900h bytes) (the remaining 14h bytes of the sectors 914h-byte data region are 00h filled).
The separate 128-byte portions consist of a 16-byte header,
  00h..03h  Copy of below 4 bytes (at 04h..07h)
  04h       Header for 1st Block/Mono, or 1st Block/Left
  05h       Header for 2nd Block/Mono, or 1st Block/Right
  06h       Header for 3rd Block/Mono, or 2nd Block/Left
  07h       Header for 4th Block/Mono, or 2nd Block/Right
  08h       Header for 5th Block/Mono, or 3rd Block/Left  ;\unknown/unused
  09h       Header for 6th Block/Mono, or 3rd Block/Right ; for 8bit ADPCM
  0Ah       Header for 7th Block/Mono, or 4th Block/Left  ; (maybe 0, or maybe
  0Bh       Header for 8th Block/Mono, or 4th Block/Right ;/copy of above)
  0Ch..0Fh  Copy of above 4 bytes (at 08h..0Bh)
followed by twentyeight data words (4x28-bytes),
  10h..13h  1st Data Word (packed 1st samples for 2-8 blocks)
  14h..17h  2nd Data Word (packed 2nd samples for 2-8 blocks)
  18h..1Bh  3rd Data Word (packed 3rd samples for 2-8 blocks)
  ...       Nth Data Word (packed Nth samples for 2-8 blocks)
  7Ch..7Fh  28th Data Word (packed 28th samples for 2-8 blocks)
and then followed by the next 128-byte portion.
The "Copy" bytes are allowing to repair faulty headers (ie. if the CDROM controller has sensed a read-error in the header then it can eventually replace it by the copy of the header).

XA-ADPCM Header Bytes
  0-3   Shift  (0..12) (0=Loudest) (13..15=Reserved/Same as 9)
  4-5   Filter (0..3) (only four filters, unlike SPU-ADPCM which has five)
  6-7   Unused (should be 0)
Note: The 4bit (or 8bit) samples are expanded to 16bit by left-shifting them by 12 (or 8), that 16bit value is then right-shifted by the selected 'shift' amount. For 8bit ADPCM shift should be 0..8 (values 9..12 will cut-off the LSB(s) of the 8bit value, this works, but isn't useful). For both 4bit and 8bit ADPCM, reserved shift values 13..15 will act same as shift=9).

XA-ADPCM Data Words (32bit, little endian)
  0-3   Nibble for 1st Block/Mono, or 1st Block/Left  (-8h..+7h)
  4-7   Nibble for 2nd Block/Mono, or 1st Block/Right (-8h..+7h)
  8-11  Nibble for 3rd Block/Mono, or 2nd Block/Left  (-8h..+7h)
  12-15 Nibble for 4th Block/Mono, or 2nd Block/Right (-8h..+7h)
  16-19 Nibble for 5th Block/Mono, or 3rd Block/Left  (-8h..+7h)
  20-23 Nibble for 6th Block/Mono, or 3rd Block/Right (-8h..+7h)
  24-27 Nibble for 7th Block/Mono, or 4th Block/Left  (-8h..+7h)
  28-31 Nibble for 8th Block/Mono, or 4th Block/Right (-8h..+7h)
or, for 8bit ADPCM format:
  0-7   Byte for 1st Block/Mono, or 1st Block/Left    (-80h..+7Fh)
  8-15  Byte for 2nd Block/Mono, or 1st Block/Right   (-80h..+7Fh)
  16-23 Byte for 3rd Block/Mono, or 2nd Block/Left    (-80h..+7Fh)
  24-31 Byte for 4th Block/Mono, or 2nd Block/Right   (-80h..+7Fh)

decode_sector(src)
  src=src+12+4+8   ;skip sync,header,subheader
  for i=0 to 11h
   for blk=0 to 3
    IF stereo ;left-samples (LO-nibbles), plus right-samples (HI-nibbles)
      decode_28_nibbles(src,blk,0,dst_left,old_left,older_left)
      decode_28_nibbles(src,blk,1,dst_right,old_right,older_right)
    ELSE      ;first 28 samples (LO-nibbles), plus next 28 samples (HI-nibbles)
      decode_28_nibbles(src,blk,0,dst_mono,old_mono,older_mono)
      decode_28_nibbles(src,blk,1,dst_mono,old_mono,older_mono)
    ENDIF
   next blk
   src=src+128
  next i
  src=src+14h+4    ;skip padding,edc

decode_28_nibbles(src,blk,nibble,dst,old,older)
  shift  = 12 - (src[4+blk*2+nibble] AND 0Fh)
  filter =      (src[4+blk*2+nibble] AND 30h) SHR 4
  f0 = pos_xa_adpcm_table[filter]
  f1 = neg_xa_adpcm_table[filter]
  for j=0 to 27
    t = signed4bit((src[16+blk+j*4] SHR (nibble*4)) AND 0Fh)
    s = (t SHL shift) + ((old*f0 + older*f1+32)/64);
    s = MinMax(s,-8000h,+7FFFh)
    halfword[dst]=s, dst=dst+2, older=old, old=s
  next j

Pos/neg Tables
  pos_xa_adpcm_table[0..4] = (0, +60, +115, +98, +122)
  neg_xa_adpcm_table[0..4] = (0,   0,  -52, -55,  -60)
Note: XA-ADPCM supports only four filters (0..3), unlike SPU-ADPCM which supports five filters (0..4).

Old/Older Values
The incoming old/older values are usually that from the previous part, or garbage (in case of decoding errors in the previous part), or whatever (in case there was no previous part) (ie. maybe zero on power-up?) (and maybe there's also a way to reset the values to zero at the begin of a new file, or *maybe* it's silently done automatically when issuing seek commands?).

25-point Zigzag Interpolation
The CDROM decoder is applying some weird 25-point zigzag interpolation when resampling the 37800Hz XA-ADPCM output to 44100Hz. This part is different from SPU-ADPCM (which uses 4-point gaussian pitch interpolations). For example, XA-ADPCM interpolation applied to a square wave looks like this:
                                                 .            .
           .--------------.                      | |        | |
           |              |                     .'.'.'----'.'.'.
           |              |                     | |          | |
           |              |                     |              |
           | Decompressed |                     |    Final     |
           |   XA-ADPCM   |                     |   XA-ADPCM   |
           |   Waveform   |                     |    Output    |
           |              |                   | |              | |
           |              |             ---.'.'.'              '.'.'.---
   --------'              '--------          | |                | |
                                               '                '
The zigzagging does produce some (inaudible) 22050Hz noise, and does produce some low-pass (?) filtering ("sinc filter"). The effect can be reproduced somewhat like so:
  Output37800Hz(sample):
    ringbuf[p AND 1Fh]=sample, p=p+1, sixstep=sixstep-1
    if sixstep=0
      sixstep=6
      Ouput44100Hz(ZigZagInterpolate(p,Table1))
      Ouput44100Hz(ZigZagInterpolate(p,Table2))
      Ouput44100Hz(ZigZagInterpolate(p,Table3))
      Ouput44100Hz(ZigZagInterpolate(p,Table4))
      Ouput44100Hz(ZigZagInterpolate(p,Table5))
      Ouput44100Hz(ZigZagInterpolate(p,Table6))
      Ouput44100Hz(ZigZagInterpolate(p,Table7))
    endif
  ZigZagInterpolate(p,TableX):
    sum=0
    for i=1 to 29, sum=sum+(ringbuf[(p-i) AND 1Fh]*TableX[i])/8000h, next i
    return MinMax(sum,-8000h,+7FFFh)
  Table1, Table2, Table3, Table4, Table5, Table6, Table7  ;Index
    0     , 0     , 0     , 0     , -0001h, +0002h, -0005h  ;1
    0     , 0     , 0     , -0001h, +0003h, -0008h, +0011h  ;2
    0     , 0     , -0001h, +0003h, -0008h, +0010h, -0023h  ;3
    0     , -0002h, +0003h, -0008h, +0011h, -0023h, +0046h  ;4
    0     , 0     , -0002h, +0006h, -0010h, +002Bh, -0017h  ;5
    -0002h, +0003h, -0005h, +0005h, +000Ah, +001Ah, -0044h  ;6
    +000Ah, -0013h, +001Fh, -001Bh, +006Bh, -00EBh, +015Bh  ;7
    -0022h, +003Ch, -004Ah, +00A6h, -016Dh, +027Bh, -0347h  ;8
    +0041h, -004Bh, +00B3h, -01A8h, +0350h, -0548h, +080Eh  ;9
    -0054h, +00A2h, -0192h, +0372h, -0623h, +0AFAh, -1249h  ;10
    +0034h, -00E3h, +02B1h, -05BFh, +0BCDh, -16FAh, +3C07h  ;11
    +0009h, +0132h, -039Eh, +09B8h, -1780h, +53E0h, +53E0h  ;12
    -010Ah, -0043h, +04F8h, -11B4h, +6794h, +3C07h, -16FAh  ;13
    +0400h, -0267h, -05A6h, +74BBh, +234Ch, -1249h, +0AFAh  ;14
    -0A78h, +0C9Dh, +7939h, +0C9Dh, -0A78h, +080Eh, -0548h  ;15
    +234Ch, +74BBh, -05A6h, -0267h, +0400h, -0347h, +027Bh  ;16
    +6794h, -11B4h, +04F8h, -0043h, -010Ah, +015Bh, -00EBh  ;17
    -1780h, +09B8h, -039Eh, +0132h, +0009h, -0044h, +001Ah  ;18
    +0BCDh, -05BFh, +02B1h, -00E3h, +0034h, -0017h, +002Bh  ;19
    -0623h, +0372h, -0192h, +00A2h, -0054h, +0046h, -0023h  ;20
    +0350h, -01A8h, +00B3h, -004Bh, +0041h, -0023h, +0010h  ;21
    -016Dh, +00A6h, -004Ah, +003Ch, -0022h, +0011h, -0008h  ;22
    +006Bh, -001Bh, +001Fh, -0013h, +000Ah, -0005h, +0002h  ;23
    +000Ah, +0005h, -0005h, +0003h, -0001h, 0     , 0       ;24
    -0010h, +0006h, -0002h, 0     , 0     , 0     , 0       ;25
    +0011h, -0008h, +0003h, -0002h, +0001h, 0     , 0       ;26
    -0008h, +0003h, -0001h, 0     , 0     , 0     , 0       ;27
    +0003h, -0001h, 0     , 0     , 0     , 0     , 0       ;28
    -0001h, 0     , 0     , 0     , 0     , 0     , 0       ;29
The above formula/table gives nearly correct results, but with small rounding errors in some cases - possibly due to actual rounding issues, or due to factors with bigger fractional portions, or due to a completely different formula...
Probably, the hardware does actually do the above stuff in two steps: first, applying a zig-zag filter (with only around 21-points) to the 37800Hz output, and then doing 44100Hz interpolation (2-point linear or 4-point gaussian or whatever) in a second step.
That two-step theory would also match well for 18900Hz resampling (which has lower-pitch zigzag, and gets spread across about fifty 44100Hz samples).

XA-ADPCM Emphasis
With XA-Emphasis enabled in Sub-header, output will appear as so:
           .------------.                           ....-----.
           |            |                        .''         |
           |    Raw     |                       .'    XA     |
           |   ADPCM    |                       |  Emphasis  '.
           |  Waveform  |                       |   Output    '..
   --------'            '----------     --------'                ''''---
The exact XA-Emphasis formula is unknown (maybe it's just same as for CD-DA's SUBQ emphasis). Additionally, zig-zag interpolation is applied (somewhere before or after applying the emphasis stuff).
Note: The Emphasis feature isn't used by any known PSX games.

Uninitialized Six-step Counter
The hardware does contain some six-step counter (for interpolating 37800Hz to 44100Hz, ie. to insert one extra sample after each six samples). The 900h-byte sectors contain a multiple of six samples, so the counter will be always same before & after playing a sector. However, the initial counter value on power-up is uninitialized random (and the counter will fallback to that initial random setting after each 900h-byte sector).

RIFF Headers (on PCs)
When reading files that consist of 914h-byte sectors on a PC, the PC seems to automatically insert a 2Ch-byte RIFF fileheader. Like so, for ADPCM audio files:
  00h 4   "RIFF"
  04h 4   Total Filesize (minus 8)
  08h 8   "CDXAfmt "
  10h 4   Size of below stuff (10h)
  14h 14  Stuff (looks like the "LEN_SU" region from XA-Directory Record)
  22h 2   Zero  (probably just dummy padding for 32bit alignment)
  24h 4   "data"
  28h 4   Size of following data (usually N*930h)
That RIFF stuff isn't stored on the CDROM (at least not in the file area) (however, some of that info, like the "=UXA" stuff, is stored in the directory area of the CDROM).
After the RIFF header, the normal sector data is appended, that, with the full 930h bytes per sector (ie. the 914h data bytes preceeded by sync bytes, header, subheader, and followed by the EDC value).
The Channel Interleave doesn't seem to be resolved, ie. the Channels are kept arranged as how they are stored on the CDROM. However, File Interleave <should> be resolved, ie. other Files that "overlap" the file shouldn't be included in the file.


CDROM ISO Volume Descriptors

System Area (prior to Volume Descriptors)
The first 16 sectors on the first track are the system area, for a Playstation disk, it contains the following:
  Sector 0..3   - Zerofilled (Mode2/Form1, 4x800h bytes, plus ECC/EDC)
  Sector 4      - Licence String
  Sector 5..11  - Playstation Logo (3278h bytes) (remaining bytes FFh-filled)
  Sector 12..15 - Zerofilled (Mode2/Form2, 4x914h bytes, plus EDC)
Of which, the Licence String in sector 4 is,
  000h 32    Line 1      ("          Licensed  by          ")
  020h 32+6  Line 2 (EU) ("Sony Computer Entertainment Euro"," pe   ") ;\either
  020h 32+1  Line 2 (JP) ("Sony Computer Entertainment Inc.",0Ah)      ; one of
  020h 32+6  Line 2 (US) ("Sony Computer Entertainment Amer","  ica ") ;/these
  041h 1983  Empty (JP)    (filled by repeating pattern 62x30h,1x0Ah, 1x30h)
  046h 1978  Empty (EU/US) (filled by 00h-bytes)
The Playstation Logo in sectors 5..11 contains data like so,
  0000h ..   41h,00h,00h,00h,00h,00h,00h,00h,01h,00h,00h,00h,1Ch,23h,00h,00h
  0010h ..   51h,01h,00h,00h,A4h,2Dh,00h,00h,99h,00h,00h,00h,1Ch,00h,00h,00h
  0020h ..   ...
  3278h 588h FF-filled (remaining bytes on sector 11)
the Logo contains a .TMD header, polygons, vertices and normals for the "PS" logo (which is displayed when booting from CDROM). Some BIOS versions are comparing these 3278h bytes against an identical copy in ROM, and refuse to boot if the data isn't 1:1 the same:
- NTSC US/ASIA BIOS always accepts changed logos.
- PAL EU BIOS accepts changed logos up to v3.0E (and refuses in v4.0E and up).
- NTSC JP BIOS never accepts changed logos (and/or changed license strings?).
Note: A region-patch-modchip causes PAL BIOS to behave same as US/ASIA BIOS.

Volume Descriptors (Sector 16 and up)
Playstation disks usually have only two Volume Descriptors,
  Sector 16    - Primary Volume Descriptor
  Sector 17    - Volume Descriptor Set Terminator

Primary Volume Descriptor (sector 16 on PSX disks)
  000h 1    Volume Descriptor Type        (01h=Primary Volume Descriptor)
  001h 5    Standard Identifier           ("CD001")
  006h 1    Volume Descriptor Version     (01h=Standard)
  007h 1    Reserved                      (00h)
  008h 32   System Identifier             (a-characters) ("PLAYSTATION")
  028h 32   Volume Identifier             (d-characters) (max 8 chars for PSX?)
  048h 8    Reserved                      (00h)
  050h 8    Volume Space Size             (2x32bit, number of logical blocks)
  058h 32   Reserved                      (00h)
  078h 4    Volume Set Size               (2x16bit) (usually 0001h)
  07Ch 4    Volume Sequence Number        (2x16bit) (usually 0001h)
  080h 4    Logical Block Size in Bytes   (2x16bit) (usually 0800h) (1 sector)
  084h 8    Path Table Size in Bytes      (2x32bit) (max 800h for PSX)
  08Ch 4    Path Table 1 Block Number     (32bit little-endian)
  090h 4    Path Table 2 Block Number     (32bit little-endian) (or 0=None)
  094h 4    Path Table 3 Block Number     (32bit big-endian)
  098h 4    Path Table 4 Block Number     (32bit big-endian) (or 0=None)
  09Ch 34   Root Directory Record         (see next chapter)
  0BEh 128  Volume Set Identifier         (d-characters) (usually empty)
  13Eh 128  Publisher Identifier          (a-characters) (company name)
  1BEh 128  Data Preparer Identifier      (a-characters) (empty or other)
  23Eh 128  Application Identifier        (a-characters) ("PLAYSTATION")
  2BEh 37   Copyright Filename            ("FILENAME.EXT;VER") (empty or text)
  2E3h 37   Abstract Filename             ("FILENAME.EXT;VER") (empty)
  308h 37   Bibliographic Filename        ("FILENAME.EXT;VER") (empty)
  32Dh 17   Volume Creation Timestamp     ("YYYYMMDDHHMMSSFF",timezone)
  33Eh 17   Volume Modification Timestamp ("0000000000000000",00h)
  34Fh 17   Volume Expiration Timestamp   ("0000000000000000",00h)
  360h 17   Volume Effective Timestamp    ("0000000000000000",00h)
  371h 1    File Structure Version        (01h=Standard)
  372h 1    Reserved for future           (00h-filled)
  373h 141  Application Use Area          (00h-filled for PSX and VCD)
  400h 8    CD-XA Identifying Signature   ("CD-XA001" for PSX and VCD)
  408h 2    CD-XA Flags (unknown purpose) (00h-filled for PSX and VCD)
  40Ah 8    CD-XA Startup Directory       (00h-filled for PSX and VCD)
  412h 8    CD-XA Reserved                (00h-filled for PSX and VCD)
  41Ah 345  Application Use Area          (00h-filled for PSX and VCD)
  573h 653  Reserved for future           (00h-filled)

Volume Descriptor Set Terminator (sector 17 on PSX disks)
  000h 1    Volume Descriptor Type    (FFh=Terminator)
  001h 5    Standard Identifier       ("CD001")
  006h 1    Terminator Version        (01h=Standard)
  007h 2041 Reserved                  (00h-filled)

Boot Record (none such on PSX disks)
  000h 1    Volume Descriptor Type    (00h=Boot Record)
  001h 5    Standard Identifier       ("CD001")
  006h 1    Boot Record Version       (01h=Standard)
  007h 32   Boot System Identifier    (a-characters)
  027h 32   Boot Identifier           (a-characters)
  047h 1977 Boot System Use           (not specified content)

Supplementary Volume Descriptor (none such on PSX disks)
  000h 1    Volume Descriptor Type (02h=Supplementary Volume Descriptor)
  001h ..   Same as for Primary Volume Descriptor (see there)
  007h 1    Volume Flags           (8bit)
  008h ..   Same as for Primary Volume Descriptor (see there)
  058h 32   Escape Sequences       (32 bytes)
  078h ..   Same as for Primary Volume Descriptor (see there)
In practice, this is used for Joliet:
CDROM Extension Joliet

Volume Partition Descriptor (none such on PSX disks)
  000h 1    Volume Descriptor Type      (03h=Volume Partition Descriptor)
  001h 5    Standard Identifier         ("CD001")
  006h 1    Volume Partition Version    (01h=Standard)
  007h 1    Reserved                    (00h)
  008h 32   System Identifier           (a-characters) (32 bytes)
  028h 32   Volume Partition Identifier (d-characters) (32 bytes)
  048h 8    Volume Partition Location   (2x32bit) Logical Block Number
  050h 8    Volume Partition Size       (2x32bit) Number of Logical Blocks
  058h 1960 System Use                  (not specified content)

Reserved Volume Descriptors (none such on PSX disks)
  000h 1    Volume Descriptor Type    (04h..FEh=Reserved, don't use)
  001h 2047 Reserved                  (don't use)


CDROM ISO File and Directory Descriptors

The location of the Root Directory is described by a 34-byte Directory Record being located in Primary Volume Descriptor entries 09Ch..0BDh. The data therein is: Block Number (usually 22 on PSX disks), LEN_FI=01h, Name=00h, and, LEN_SU=00h (due to the 34-byte limit).

Format of a Directory Record
  00h 1      Length of Directory Record (LEN_DR) (33+LEN_FI+pad+LEN_SU) (0=Pad)
  01h 1      Extended Attribute Record Length (usually 00h)
  02h 8      Data Logical Block Number (2x32bit)
  0Ah 8      Data Size in Bytes        (2x32bit)
  12h 7      Recording Timestamp       (yy-1900,mm,dd,hh,mm,ss,timezone)
  19h 1      File Flags 8 bits         (usually 00h=File, or 02h=Directory)
  1Ah 1      File Unit Size            (usually 00h)
  1Bh 1      Interleave Gap Size       (usually 00h)
  1Ch 4      Volume Sequence Number    (2x16bit, usually 0001h)
  20h 1      Length of Name            (LEN_FI)
  21h LEN_FI File/Directory Name ("FILENAME.EXT;1" or "DIR_NAME" or 00h or 01h)
  xxh 0..1   Padding Field (00h) (only if LEN_FI is even)
  xxh LEN_SU System Use (LEN_SU bytes) (see below for CD-XA disks)
LEN_SU can be calculated as "LEN_DR-(33+LEN_FI+Padding)". For CD-XA disks (as used in the PSX), LEN_SU is 14 bytes:
  00h 2      Owner ID Group  (whatever, usually 0000h, big endian)
  02h 2      Owner ID User   (whatever, usually 0000h, big endian)
  04h 2      File Attributes (big endian):
               0   Owner Read    (usually 1)
               1   Reserved      (0)
               2   Owner Execute (usually 1)
               3   Reserved      (0)
               4   Group Read    (usually 1)
               5   Reserved      (0)
               6   Group Execute (usually 1)
               7   Reserved      (0)
               8   World Read    (usually 1)
               9   Reserved      (0)
               10  World Execute (usually 1)
               11  IS_MODE2        (0=MODE1 or CD-DA, 1=MODE2)
               12  IS_MODE2_FORM2  (0=FORM1, 1=FORM2)
               13  IS_INTERLEAVED  (0=No, 1=Yes...?) (by file and/or channel?)
               14  IS_CDDA         (0=Data or ADPCM, 1=CD-DA Audio Track)
               15  IS_DIRECTORY    (0=File or CD-DA, 1=Directory Record)
             Commonly used Attributes are:
               0D55h=Normal Binary File (with 800h-byte sectors)
               1555h=Uncommon           (fade to black .DPS and .XA files)
               2555h=Uncommon           (wipeout .AV files) (MODE1 ??)
               4555h=CD-DA Audio Track  (wipeout .SWP files, alone .WAV file)
               3D55h=Streaming File     (ADPCM and/or MDEC or so)
               8D55h=Directory Record   (parent-, current-, or sub-directory)
  06h 2      Signature     ("XA")
  08h 1      File Number   (Must match Subheader's File Number)
  09h 5      Reserved      (00h-filled)
Directory sectors do usually have zeropadding at the end of each sector:
  - Directory sizes are always rounded up to N*800h-bytes.
  - Directory entries should not cross 800h-byte sector boundaries.
  There may be further directory entries on the next sector after the padding.
  To deal with that, skip 00h-bytes until finding a nonzero LEN_DR value (or
  slightly faster, upon a 00h-byte, directly jump to next sector instead of
  doing a slow byte-by-byte skip).
  Note: Padding between sectors does rarely happen on PSX discs because the
  PSX kernel supports max 800h bytes per directory (one exception is PSX Hot
  Shots Golf 2, which has an ISO directory with more than 800h bytes; it does
  use a lookup file instead of actually parsing the while ISO directory).
Names are alphabetically sorted, no matter if the names refer to files or directories (ie. SUBDIR would be inserted between STRFILE.EXT and SYSFILE.EXT). The first two entries (with non-ascii names 00h and 01h) are referring to current and parent directory.

Path Tables
The Path Table contain a summary of the directory names (the same information is also stored in the directory records, so programs may either use path tables or directory records; the path tables are allowing to read the whole directory tree quickly at once, without neeeding to seek from directory to directory).
Path Table 1 is in Little-Endian format, Path Table 3 contains the same data in Big-Endian format. Path Table 2 and 4 are optional copies of Table 1 and 3. The size and location of the tables is stored in Volume Descriptor entries 084h..09Bh. The format of the separate entries within a Path Table is,
  00h 1       Length of Directory Name (LEN_DI) (01h..08h for PSX)
  01h 1       Extended Attribute Record Length  (usually 00h)
  02h 4       Directory Logical Block Number
  06h 2       Parent Directory Number           (0001h and up)
  08h LEN_DI  Directory Name (d-characters, d1-characters) (or 00h for Root)
  xxh 0..1    Padding Field (00h) (only if LEN_FI is odd)
The first entry (directory number 0001h) is the root directory, the root doesn't have a name, nor a parent (the name field contains a 00h byte, rather than ASCII text, LEN_DI is 01h, and parent is 0001h, making the root it's own parent; ignoring the fact that incest is forbidden in many countries).
The next entries (directory number 0002h and up) (if any) are sub-directories within the root (sorted in alphabetical order, and all having parent=0001h). The next entries are sub-directories (if any) of the first sub-directory (also sorted in alphabetical order, and all having parent=0002h). And so on.
PSX disks usually contain all four tables (usually on sectors 18,19,20,21).

Format of an Extended Attribute Record (none such on PSX disks)
If present, an Extended Attribute Record shall be recorded over at least one Logical Block. It shall have the following contents.
  00h 4       Owner Identification (numerical value)  ;\used only if
  04h 4       Group Identification (numerical value)  ; File Flags Bit4=1
  08h 2       Permission Flags (16bit, little-endian) ;/
  0Ah 17      File Creation Timestamp      ("YYYYMMDDHHMMSSFF",timezone)
  1Bh 17      File Modification Timestamp  ("0000000000000000",00h)
  2Ch 17      File Expiration Timestamp    ("0000000000000000",00h)
  3Dh 17      File Effective Timestamp     ("0000000000000000",00h)
  4Eh 1       Record Format                (numerical value)
  4Fh 1       Record Attributes            (numerical value)
  50h 4       Record Length                (numerical value)
  54h 32      System Identifier            (a-characters, a1-characters)
  74h 64      System Use                   (not specified content)
  B4h 1       Extended Attribute Record Version (numerical value)
  B5h 1       Length of Escape Sequences   (LEN_ESC)
  B6h 64      Reserved for future standardization (00h-filled)
  F6h 4       Length of Application Use    (LEN_AU)
  FAh LEN_AU  Application Use
  xxh LEN_ESC Escape Sequences
Unknown WHERE that data is located... the Directory Records can specify the Extended Attribute Length, but not the location... maybe it's meant to be located in the first some bytes or blocks of the File or Directory...?


CDROM ISO Misc

Both Byte Order
All 16bit and 32bit numbers in the ISO region are stored twice, once in Little-Endian order, and then in Big-Endian Order. For example,
  2x16bit value 1234h     ---> stored as 34h,12h,12h,34h
  2x32bit value 12345678h ---> stored as 78h,56h,34h,12h,12h,34h,56h,78h
Exceptions are the 16bit Permission Flags which are stored only in Little-Endian format (although the flags are four 4bit groups, so that isn't a real 16bit number), and, the Path Tables are stored in both formats, but separately, ie. one table contains only Little-Endian numbers, and the other only Big-Endian numbers.

d-characters (Filenames)
  "0..9", "A..Z", and "_"

a-characters
  "0..9", "A..Z", SPACE, "!"%&'()*+,-./:;<=>?_"
Ie. all ASCII characters from 20h..5Fh except "#$@[\]^"

SEPARATOR 1 = 2Eh (aka ".") (extension; eg. "EXT")
SEPARATOR 2 = 3Bh (aka ";") (file version; "1".."32767")

Fixed Length Strings/Filenames
The Volume Descriptors contain a number fixed-length string/filename fields (unlike the Directory Records and Path Tables which have variable lengths). These fields should be padded with SPACE characters if they are empty, or if the string is shorter than the maximum length.
Filename fields in Volume Descriptors are referring to files in the Root Directory. On PSX disks, the filename fields are usually empty, but some disks are mis-using the Copyright Filename to store the Company Name (although no such file exists on the disk).

Volume Descriptor Timestamps
The various timestamps occupy 17 bytes each, in form of
  "YYYYMMDDHHMMSSFF",timezone
  "0000000000000000",00h         ;empty timestamp
The first 16 bytes are ASCII Date and Time digits (Year, Month, Day, Hour, Minute, Second, and 1/100 Seconds. The last byte is Offset from Greenwich Mean Time in number of 15-minute steps from -48 (West) to +52 (East); or actually: to +56 when recursing Kiribati's new timezone.
Note: PSX games manufactured in year 2000 were accidently marked to be created in year 0000.

Recording Timestamps
Occupy only 7 bytes, in non-ascii format
  year-1900,month,day,hour,minute,second,timezone
  00h,00h,00h,00h,00h,00h,00h    ;empty timestamp
The year ranges from 1900+0 to 1900+255.

File Flags
If this Directory Record identifies a directory then bit 2,3,7 shall be set to ZERO.
If no Extended Attribute Record is associated with the File Section identified by this Directory Record then bit positions 3 and 4 shall be set to ZERO.
  0  Existence       (0=Normal, 1=Hidden)
  1  Directory       (0=File, 1=Directory)
  2  Associated File (0=Not an Associated File, 1=Associated File)
  3  Record
        If set to ZERO, shall mean that the structure of the information in
        the file is not specified by the Record Format field of any associated
        Extended Attribute Record (see 9.5.8).
        If set to ONE, shall mean that the structure of the information in
        the file has a record format specified by a number other than zero in
        the Record Format Field of the Extended Attribute Record (see 9.5.8).
  4  Restrictions    (0=None, 1=Restricted via Permission Flags)
  5  Reserved        (0)
  6  Reserved        (0)
  7  Multi-Extent    (0=Final Directory Record for the file, 1=Not final)

Permission Flags (in Extended Attribute Records)
  0-3   Permissions for upper-class owners
  4-7   Permissions for normal owners
  8-11  Permissions for upper-class users
  12-15 Permissions for normal users
This is a bit bizarre, an upper-class owner is "an owner who is a member of a group of the System class of user". An upper-class user is "any user who is a member of the group specified by the Group Identification field". The separate 4bit permission codes are:
  Bit0  Permission to read the file    (0=Yes, 1=No)
  Bit1  Must be set (1)
  Bit2  Permission to execute the file (0=Yes, 1=No)
  Bit3  Must be set (1)


CDROM Extension Joliet

Typical Joliet Disc Header
The discs contains two separate filesystems, the ISO one for backwards compatibilty, and the Joliet one with longer filenames and Unicode characters.
  Sector 16 - Primary Volume Descriptor (with 8bit uppercase ASCII ISO names)
  Sector 17 - Secondary Volume Descriptor (with 16bit Unicode Joliet names)
  Sector 18 - Volume Descriptor Set Terminator
  Sector .. - Path Tables and Directory Records (for ISO)
  Sector .. - Path Tables and Directory Records (for Joliet)
  Sector .. - File Data Sectors (shared for ISO and Joliet)
There is no way to determine which ISO name belongs to which Joliet name (except, filenames do usually point to the same file data sectors, but that doesn't work for empty files, and doesn't work for folder names).
The ISO names can be max 31 chars (or shorter for compatibility with DOS short names: Nero does truncate them to max 14 chars "FILENAME.EXT;1", all uppercase, with underscores instead of spaces, and somehow assigning names like "FILENAMx.EXT;1" in case of duplicated short names).

Secondary Volume Descriptor (aka Supplementary Volume Descriptor)
This is using the same format as ISO Primary Volume Descriptor (but with some changed entries).
CDROM ISO Volume Descriptors
Changed entries are:
  000h 1     Volume Descriptor Type (02h=Supplementary instead of 01h=Primary)
  007h 1     Volume Flags           (whatever, instead of Reserved)
  008h 2x32  Identifier Strings     (16-char Unicode instead 32-char ASCII)
  058h 32    Escape Sequences       (see below, instead of Reserved)
  08Ch 4x4   Path Tables            (point to new tables with Unicode chars)
  09Ch 34    Root Directory Record  (point to root with Unicode chars)
  0BEh 4x128 Identifier Strings     (64-char Unicode instead 128-char ASCII)
  2BEh 3x37  Filename Strings       (18-char Unicode instead 37-char ASCII)
The Escape Sequences entry contains three ASCII chars (plus 29-byte zeropadding), indicating the ISO 2022 Unicode charset:
  %/@   UCS-2 Level 1
  %/C   UCS-2 Level 2
  %/E   UCS-2 Level 3

Directory Records and Path Tables
This is using the standard ISO format (but with 16bit Unicode characters instead of 8bit ASCII chars).
CDROM ISO File and Directory Descriptors

File and Directory Name Characters
All characters are stored in 16bit Big Endian format. The LEN_FI filename entry contains the length in bytes (ie. numchars*2). Charaters 0000h/0001h are current/parent directory. Characters 0020h and up can be used for file/directory names, except six reserved characters: */:;?\
All names must be sorted by their character numbers, padded with zero (without attempting to merge uppercase, lowercase, or umlauts to nearby locations).

File and Directory Name Length
  max 64 chars according to original Joliet specs from 1995
  max 110 chars (on standard CDROMs, with LEN_SU=0)
  max 103 chars (on CD-XA discs, with LEN_SU=14)
Joliet Filenames include ISO-style version suffices (usually ";1", so the actual filename lengths are two chars less than shown above).
The original 64-char limit was perhaps intended to leave space for future extensions in the LEN_SU region. The 64-char limit can cause problems with verbose names (eg. "Interprete - Title (version).mp3"). Microsoft later changed the limit to up to 110 chars.
The 110/103-char limit is caused by the 8bit "LEN_DR=(33+LEN_FI+pad+LEN_SU)" entry in the Directory Records.
Joliet allows to exceed the 8-level ISO directory nesting limit, however, it doesn't allow to exceed the 240-byte (120-Unicode-char) limit in ISO 9660 section 6.8.2.1 for the total "path\filename" lengths.

Official Specs
Joliet Specification, CD-ROM Recording Spec ISO 9660:1988, Extensions for Unicode Version 1; May 22, 1995, Copyright 1995, Microsoft Corporation
  http://littlesvr.ca/isomaster/resources/JolietSpecification.html


CDROM File Formats

Official PSX File Formars
CDROM File Official Sony File Formats

Executables
CDROM File Playstation EXE and SYSTEM.CNF
CDROM File PsyQ .CPE Files (Debug Executables)
CDROM File PsyQ .SYM Files (Debug Information)

Video Files
CDROM File Video Texture Image TIM/PXL/CLT (Sony)
CDROM File Video Texture/Bitmap (Other)
CDROM File Video 2D Graphics CEL/BGD/TSQ/ANM/SDF (Sony)
CDROM File Video 3D Graphics TMD/PMD/TOD/HMD/RSD (Sony)
CDROM File Video STR Streaming and BS Picture Compression (Sony)

Audio Files
CDROM File Audio Single Samples VAG (Sony)
CDROM File Audio Sample Sets VAB and VH/VB (Sony)
CDROM File Audio Sequences SEQ/SEP (Sony)
CDROM File Audio Other Formats
CDROM File Audio Streaming XA-ADPCM
CDROM File Audio CD-DA Tracks

Virtual Filesystem Archives
PSX titles are quite often using virtual filesystems, with numerous custom file archive formats.
CDROM File Archives with Filename
CDROM File Archives with Offset and Size
CDROM File Archives with Offset
CDROM File Archives with Size
CDROM File Archives with Chunks
CDROM File Archives with Folders
CDROM File Archives in Hidden Sectors
More misc stuff...
CDROM File Archive HED/DAT/BNS/STR (Ape Escape)
CDROM File Archive WAD.WAD, BIG.BIN, JESTERS.PKG (Crash/Herc/Pandemonium)
CDROM File Archive BIGFILE.BIG (Gex)
CDROM File Archive BIGFILE.DAT (Gex - Enter the Gecko)
CDROM File Archive FF9 DB (Final Fantasy IX)
CDROM File Archive Ace Combat 2 and 3
CDROM File Archive NSD/NSF (Crash Bandicoot 1-3)
CDROM File Archive STAGE.DIR and *.DAT (Metal Gear Solid)
CDROM File Archive DRACULA.DAT (Dracula)
CDROM File Archive Croc 1 (DIR, WAD, etc.)
CDROM File Archive Croc 2 (DIR, WAD, etc.)
CDROM File Archive Headerless Archives
Using archives can avoid issues with the PSX's poorly implemented ISO filesystem: The PSX kernel supports max 800h bytes per directory, and lacks proper caching for most recently accessed directories (additionally, some archives can load the whole file/directory tree from continous sectors, which could be difficult in ISO filesystems).

Compression
CDROM File Compression

Misc
CDROM File XYZ and Dummy/Null Files

FILENAME.EXT
The BIOS seems to support only (max) 8-letter filenames with 3-letter extension, typically all uppercase, eg. "FILENAME.EXT". Eventually, once when the executable has started, some programs might install drivers for long filenames(?)

The PSX uses the standard CDROM ISO9660 filesystem without any encryption (ie. you can put an original PSX CDROM into a DOS/Windows computer, and view the content of the files in text or hex editors without problems).

Note
MagDemoNN is short for "Official U.S. Playstation Magazine Demo Disc NN"


CDROM File Official Sony File Formats

Official Sony File Formats
https://psx.arthus.net/sdk/Psy-Q/DOCS/Devrefs/Filefrmt.pdf - Sony 1998
  File Formats
    (c) 1998 Sony Computer Entertainment Inc.
    Publication date: November 1998
  Chapter 1: Streaming Audio and Video Data
    STR: Streaming (Movie) Data                               1-3
    BS: MDEC Bitstream Data                                   1-8
    XA: CD-ROM Voice Data                                     1-31
  Chapter 2: 3D Graphics
    RSD: 3D Model Data [RSD,PLY,MAT,GRP,MSH,PVT,COD,MOT,OGP]  2-3
    TMD: Modeling Data for OS Library                         2-24
    PMD: High-Speed Modeling Data                             2-35
    TOD: Animation Data                                       2-40
    HMD: Hierarchical 3D Model, Animation and Other Data      2-49
  Chapter 3: 2D Graphics
    TIM: Screen Image Data                                    3-3
    SDF: Sprite Editor Project File                           3-8
    PXL: Pixel Image Data                                     3-11
    CLT: Palette Data                                         3-14
    ANM: Animation Information                                3-16
    TSQ: Animation Time Sequence                              3-22
    CEL: Cell Data                                            3-23
    BGD: BG Map Data                                          3-27
  Chapter 4: Sound
    SEQ: PS Sequence Data                                     4-3
    SEP: PS Multi-Track Sequence Data                         4-3
    VAG: PS Single Waveform Data                              4-5
    VAB: PS Sound Source Data [VAB and VH/VB]                 4-5
    DA: CD-DA Data                                            4-7
  Chapter 5: PDA and Memory Card
    FAT: Memory Card File System Specification                5-3
Most games are using their own custom file formats. However, VAG, VAB/VH(VB, STR/XA, and TIM are quite popular (because they are matched to the PSX low-level data encoding). Obviously, EXE is also very common (although not included in the above document).


CDROM File Playstation EXE and SYSTEM.CNF

SYSTEM.CNF
Contains boot info in ASCII/TXT format, similar to the CONFIG.SYS or AUTOEXEC.BAT files for MSDOS. A typical SYSTEM.CNF would look like so:
  BOOT = cdrom:\abcd_123.45;1 arg ;boot exe (drive:\path\name.ext;version)
  TCB = 4                         ;HEX (=4 decimal)   ;max number of threads
  EVENT = 10                      ;HEX (=16 decimal)  ;max number of events
  STACK = 801FFF00                ;HEX (=memtop-256)
The first line specifies the executable to load, from the "cdrom:" drive, "\" root directory, filename "abcd_123.45" (case-insensitive, the real name in the disk directory would be uppercase, ie. "ABCD_123.45"), and, finally ";1" is the file's version number (a rather strange ISO-filesystem specific feature) (the version number should be usually/always 1). Additionally, "arg" may contain an optional 128-byte command line argument string, which is copied to address 00000180h, where it may be interpreted by the executable (most or all games don't use that feature).
Each line in the file should be terminated by 0Dh,0Ah characters... not sure if it's also working with only 0Dh, or only 0Ah...?

ABCD_123.45
This is a normal executable (exactly as for the .EXE files, described below), however, the filename/extension is taken from the game code (the "ABCD-12345" text that is printed on the CD cover), but, with the minus replaced by an underscore, and due to the 8-letter filename limit, the last two characters are stored in the extension region.
That "XXXX_NNN.NN" naming convention seems to apply for all official licensed PSX games. Wild Arms does unconventionally have the file in a separate folder, "EXE\SCUS_946.06".

PSX.EXE (Boot-Executable) (default filename when SYSTEM.CNF doesn't exist)
XXXX_NNN.NN (Boot-Executable) (with filename as specified in SYSTEM.CNF)
FILENAME.EXE (General-Purpose Executable)
PSX executables are having an 800h-byte header, followed by the code/data.
  000h-007h ASCII ID "PS-X EXE"
  008h-00Fh Zerofilled
  010h      Initial PC                   (usually 80010000h, or higher)
  014h      Initial GP/R28               (usually 0)
  018h      Destination Address in RAM   (usually 80010000h, or higher)
  01Ch      Filesize (must be N*800h)    (excluding 800h-byte header)
  020h      Unknown/Unused ;Addr         (usually 0)  ;\optional overlay?
  024h      Unknown/Unused ;Size         (usually 0)  ;/(not auto-loaded)
  028h      Memfill Start Address        (usually 0) (when below Size=None)
  02Ch      Memfill Size in bytes        (usually 0) (0=None)
  030h      Initial SP/R29 & FP/R30 Base (usually 801FFFF0h) (or 0=None)
  034h      Initial SP/R29 & FP/R30 Offs (usually 0, added to above Base)
  038h-04Bh Reserved for A(43h) Function (should be zerofilled in exefile)
  04Ch-xxxh ASCII marker
             "Sony Computer Entertainment Inc. for Japan area"         ;NTSC
             "Sony Computer Entertainment Inc. for Europe area"        ;PAL
             "Sony Computer Entertainment Inc. for North America area" ;NTSC
             (or often zerofilled in some homebrew files)
             (the BIOS doesn't verify this string, and boots fine without it)
  xxxh-7FFh Zerofilled
  800h...   Code/Data                  (loaded to entry[018h] and up)
The code/data is simply loaded to the specified destination address, ie. unlike as in MSDOS .EXE files, there is no relocation info in the header.
Note: In bootfiles, SP is usually 801FFFF0h (ie. not 801FFF00h as in system.cnf). When SP is 0, the unmodified caller's stack is used. In most cases (except when manually calling DoExecute), the stack values in the exeheader seem to be ignored though (eg. replaced by the SYSTEM.CNF value).
The memfill region is zerofilled by a "relative" fast word-by-word fill (so address and size must be multiples of 4) (despite of the word-by-word filling, still it's SLOW because the memfill executes in uncached slow ROM).
The reserved region at [038h-04Bh] is internally used by the BIOS to memorize the caller's RA,SP,R30,R28,R16 registers (for some bizarre reason, this information is saved in the exe header, rather than on the caller's stack).
Additionally to the initial PC,R28,SP,R30 values that are contained in the header, two parameter values are passed to the executable (in R4 and R5 registers) (however, usually that values are simply R4=1 and R5=0).
Like normal functions, the executable can return control to the caller by jumping to the incoming RA address (provided that it hasn't destroyed the stack or other important memory locations, and that it has pushed/popped all registers) (returning works only for non-boot executables; if the boot executable returns to the BIOS, then the BIOS will simply lockup itself by calling the "SystemErrorBootOrDiskFailure" function.

Relocatable EXE
Fade to Black (CINE.EXR) contains ID "PS-X EXR" (instead "PS-X EXE") and string "PSX Relocable File - Delphine Software Int.", this is supposedly some custom relocatable exe file (unsupported by the PSX kernel).

MSDOS.EXE and WINDOWS.EXE Files
Some PSX discs contain DOS or Windows .EXE files (with "MZ" headers), eg. devkit leftovers, or demos/gimmicks.


CDROM File PsyQ .CPE Files (Debug Executables)

Fileheader
  00h 4   File ID (01455043h aka "CPE",01h)

Chunk 00h: End of File
  00h 1   Chunk ID (00h)

Chunk 01h: Load Data
  00h 1   Chunk ID (01h)
  01h 4   Address (usually 80010000h and up)
  05h 4   Size (LEN)
  09h LEN Data (binary EXE code/data)
Theoretically, this could contain the whole EXE body in a single chunk. However, the PsyQ files are usually containing hundreds of small chunks (with each function and each data item in a separate chunk). For converting CPE to EXE, use "ExeOffset = (CpeAddress AND 1FFFFFFFh)-10000h+800h".

Chunk 02h: Run Address (whatever, optional, usually not used in CPE files)
  00h 1   Chunk ID (02h)
  01h 4   Address
Unknown what this is. It's not the entrypoint (which is set via chunk 03h). Maybe intended to change the default load address (usually 80010000h)?

Chunk 03h: Set Value 32bit (LEN=4) (used for entrypoint)
Chunk 04h: Set Value 16bit (LEN=2) (unused)
Chunk 05h: Set Value 8bit (LEN=1) (unused)
Chunk 06h: Set Value 24bit (LEN=3) (unused)
  00h 1   Chunk ID (03h..06h)
  01h 2   Register (usually 0090h=Initial PC, aka Entrypoint)
  03h LEN Value (8bit..32bit)

Chunk 07h: Select Workspace (whatever, optional, usually not used in CPE)
  00h 1   Chunk ID (07h)
  01h 4   Workspace number (usually 00000000h)

Chunk 08h: Select Unit (whatever, usually first chunk in CPE file)
  00h 1   Chunk ID (08h)
  01h 1   Unit (usually 00h)

Example from LameGuy's sample.cpe:
  0000h 4    File ID ("CPE",01h)
  0004h 2    Select Unit 0            (08h,00h)
  0006h 7    Set Entrypoint 8001731Ch (03h,0090h,8001731Ch)
  000Dh 0Dh  Load   (01h,800195F8h,00000004h,0,0,0,0)
  001Ah ..   Load   (01h,80010000h,0000002Bh,...)
  004Eh ..   Load   (01h,8001065Ch,00000120h,...)
  0177h ...  Load   (01h,8001077Ch,0000012Ch,...)
  02ACh ...  Load   (01h,800108A8h,000000A4h,...)
  ...   ...  Load   (...)
  98F4h ...  Load   (01h,800195F0h,00000008h,...)
  9905h 1    End    (00h)


CDROM File PsyQ .SYM Files (Debug Information)

PsyQ .SYM Files contain debug info, usually bundled with PsyQ .MAP and Psy .CPE files. Those files are generated by PsyQ tools, which appear to be still in use for homebrew PSX titles.
The files are occassionally also found on PSX CDROMs:
  Legacy of Kain PAL version (\DEGUG\NTSC\KAIN2.SYM+MAP+CPE)
  RC Revenge (\RELEASE.SYM)
  Twisted Metal: Small Brawl (MagDemo54: TMSB\TM.SYM)
  Jackie Chan Stuntmaster (GAME_REL.SYM+CPE)
  SnoCross Championship Racing (MagDemo37: SNOCROSS\SNOW.TOC\SNOW.MAP)
  Sled Storm (MagDemo24: DEBUG\MAIN.MAP)
  E.T. Interplanetary Mission (MagDemo54: MEGA\MEGA.CSH\* has SYM+CPE+MAP)

Fileheader .SYM
  00h 4  File ID ("MND",01h)
  04h 4  Whatever (0,0,0,0)    ;TOMB5: 0,02h,0,0
  08h .. Chunks (see below)

 _______________________________ Symbol Chunks ________________________________

Chunk 01h: Symbol (Immediate, eg. memsize, or membase)
Chunk 02h: Symbol (Function Address for Internal & External Functions)
Chunk 05h: Symbol (?)
Chunk 06h: Symbol (?)
  00h 4   Address/Value
  04h 1   Chunk ID (01h/02h/05h/06h)
  05h 1   Symbol Length (LEN)
  06h LEN Symbol (eg. "VSync")

 __________________________ Source Code Line Chunks ___________________________

Chunk 80h: Source Code Line Numbers: Address for 1 Line
  00h 4   Address (for 1 line, starting at current line)
  04h 1   Chunk ID (80h)

Chunk 82h: Source Code Line Numbers: Address for N Lines (8bit)
  00h 4   Address (for N lines, starting at current line)
  04h 1   Chunk ID (82h)
  05h 1   Number of Lines (00h=None, or 02h and up?)

Chunk 84h: Source Code Line Numbers: Address for NN Lines (16bit)
  00h 4   Address (for N lines, starting at current line)
  04h 1   Chunk ID (84h)
  05h 2   Number of Lines (?)

Chunk 86h: Source Code Line Numbers: Address for Line NNN (32bit?)
  00h 4   Address (for 1 line, starting at newly assigned current line)
  04h 1   Chunk ID (84h)
  05h 4   Absolute Line Number (rather than number of lines) (?)

Chunk 88h: Source Code Line Numbers: Start with Filename
  00h 4   Address (start address)
  04h 1   Chunk ID (88h=Filename)
  05h 4   First Line Number (after comments/definitions) (32bit?)
  09h 1   Filename Length (LEN)
  0Ah LEN Filename (eg. "C:\path\main.c")

Chunk 8Ah: Source Code Line Numbers: End of Source Code
  00h 4   Address (end address)
  04h 1   Chunk ID (8Ah)

 __________________________ Internal Function Chunks __________________________

Chunk 8Ch: Internal Function: Start with Filename
  00h 4    Address
  04h 1    Chunk ID (8Ch)
  05h 4    Whatever (1Eh,00h,20h,00h)  ;or 1Eh,00h,18h,00h
  09h 4    Whatever (00h,00h,1Fh,00h)
  0Dh 4    Whatever (00h,00h,00h,C0h)
  11h 4    Whatever (FCh,FFh,FFh,FFh)  ;mask? neg.offset?
  15h 4    Whatever (10h,00h,00h,00h)  <-- line number (32bit?)
  19h 1    Filename Length (LEN1)
  1Ah LEN1 Filename (eg. "C:\path\main.c")
  xxh 1    Symbol Length (LEN2)
  xxh LEN2 Symbol (eg. "VSync")

Chunk 8Eh: Internal Function: End of Function (end of chunk 8Ch)
  00h 4   Address
  04h 1   Chunk ID (8Eh)
  05h 4   Line Number                 <-- line number (32bit?)

Chunk 90h: Internal Function:Whatever90h... first instruction in main func?
Chunk 92h: Internal Function:Whatever92h... last instruction in main func?
Maybe line numbers? Or end of definitions for incoming parameters?
  00h 4   Address
  04h 1   Chunk ID (90h/92h)
  05h 4   Whatever (1Fh,00h,00h,00h)  <-- line number relative to main.start?

 _____________________________ Class/Type Chunks ______________________________

Chunk 94h: Type/Symbol (Simple Types?)
  00h 4   Offset (when used within a structure, or stack-N, or otherwise zero)
  04h 1   Chunk ID (94h)
  05h 2   Class (000Dh=Type.alias, 000Ah=Address, 0001h=Stack, 0002h=Addr)
  07h 2   Type (XX = 8bit,16bit,signed,etc.?)
  09h 4   Zero, or Size in Bytes (for "memblocks")
  0xh 1   Symbol Name Length (LEN)
  0xh LEN Symbol Name (eg. "size_t")

Chunk 96h: Type/Symbol (Complex Structures/Arrays?)
  00h 4   Offset (when used within a structure, otherwise zero)
  04h 1   Chunk ID (96h)
  05h 2   Class (02h=Array,08h=RefToStruct,0Dh=DefineAlias,66h=StructEnd)
  07h 2   Type (0xh=Small, 3xh=WithArrayStuff?) (same/similar as in chunk 94h)
  09h 4     Struct Size in Bytes
  0Dh 2     Array Dimensions (DIM) (0=none) ;eg. [3][4] --> 0002h
  0Fh DIM*4 Array Entries per Dimension     ;eg. [3][4] --> 00000003h,00000004h
  xxh 1     Internal Fake Name Length (LEN1) (0=none)
  xxh LEN1  Internal Fake Name  (eg. ".1fake")
  xxh 1     Symbol Name Length (LEN2)
  xxh LEN2  Symbol Name (eg. "r")

 ______________________________ Class/Type Values _____________________________

Class definition (in chunk 94h) (and somewhat same/similar in chunk 96h)
(looks same/similar as C_xxx class values in COFF files!)
  0001h = Local variable              (with Offset = negative stack offset)
  0002h = Global variable or Function (with Offset = address)
  0008h = Item in Structure           (with Offser = offset within struct)
  0009h = Incoming Function param     (with Offset = index; 0,4,8,etc.)
  000Ah = Type address / struc start? (with Offset = zero)
  000Dh = Type alias                  (with Offset = zero)

Type definition (in chunk 94h/96h)
(maybe lower 4bit=type, and next 4bit=usage/variant?)
(looks same/similar as T_xxx type values in COFF files!)
  0000h =
  0001h =
  0002h =
  0003h =                (16bit signed?)
  0004h = int            (32bit signed?)
  0005h =
  0006h =
  0007h =
  0008h = (address)      (32bit unsigned?) (with Definition=000Ah)
  0009h =
  000Ah =
  000Bh =
  000Ch = u_char         (8bit unsigned?)
  000Dh = u_short,ushort (16bit unsigned?)
  000Eh = u_int          (32bit unsigned?)
  000Fh = u_long         (64bit unsigned?) (or rather SAME as above?)
  0021h = function with 0 params, and/or return="nothing"?
  0024h = main function with 2 params, and/or return="int"?
  0052h = argv           (string maybe?)
  0038h = GsOT           (huh?)
  00F8h = GsOT_TAG       (huh?)
  00FCh = PACKET         (huh?)
  ??    = float,bool,string,ptr,packet,(un-)signed8/16/32/64bit,etc
  ??    = custom type/struct (using value 000xh plus "fake" name, or so?)

 __________________________________ .MAP File _________________________________

PsyQ .MAP File
The .SYM file is usually bundled with a .MAP file, which is containing a summary of the symbolic info as ASCII text (but without info on line numbers or data types). For example:
    Start     Stop   Length      Obj Group            Section name
 80010000 80012D5B 00002D5C 80010000 text             .rdata
 80012D5C 800C8417 000B56BC 80012D5C text             .text
 800C8418 800CDAB7 000056A0 800C8418 text             .data
 800CDAB8 800CFB63 000020AC 800CDAB8 text             .sdata
 800CFB64 800D5C07 000060A4 800CFB64 bss              .sbss
 800D5C08 800DD33F 00007738 800D5C08 bss              .bss

  Address  Names alphabetically
 800CFE80 ACE_amount
 800CFB94 AIMenu
 800CDE5C AXIS_LENGTH
 8005E28C AddClippedTri
 8005DFEC AddVertex
 ...

  Address  Names in address order
 00000000 _cinemax_obj
 00000000 _cinemax_header_org
 00000000 _cinemax_org
 00000000 _mcardx_sbss_size
 00000000 _mcardx_org
 ...


CDROM File Video Texture Image TIM/PXL/CLT (Sony)

TIM/PXL/CLT are standard formats from Sony's devkit. TIM is used by many PSX games.
  .TIM   contains Pixel data, and (optional) CLUT data  ;-all in one file
  .PXL   contains Pixel data only                       ;\in two separate files
  .CLT   contains CLUT data only (if any)               ;/

TIM Format
  000h 1  File ID  (always 10h=TIM)
  001h 1  Version  (always 00h)
  002h 2  Reserved (always 0000h) (or 1 or 2 for Compressed TIM, see below)
  004h 4  Flags (bit0-2=Type; see below, bit3=HasCLUT, bit4-31=Reserved/zero)
  ...  .. Data Section for CLUT (Palette), only exists if Flags.bit3=1, HasCLUT
  ...  .. Data Section for Pixels (Bitmap/Texture)
The Type in Flags.bit0-2 can be 0=4bpp, 1=8bpp, 2=16bpp, 3=24bpp, 4=Mixed.
NFL Blitz 2000 (MagDemo26: B2000\DATA\ARTD_G.BIN) does additionally use Type 5=8bit.
The Type value value is only a hint on how to view the Pixel data (the data is copied to VRAM regardless of the type; 4=Mixed is meant to indicate that the data contains different types, eg. both 4bpp & 8bpp textures).
Type 3=24bpp is quite rare, but does exist (eg. Colony Wars (MagDemo02: CWARS\GAME.RSC\DEMO.TIM).

The format of the CLUT and Pixel Data Section(s) is:
  000h 4  Size of Data Section (Xsiz*2*Ysiz+0Ch)  ;maybe rounded to 4-byte?
  004h 4  Destination Coord    (YyyyXxxxh)  ;Xpos counted in halfwords
  008h 4  Width+Height         (YsizXsizh)  ;Xsiz counted in halfwords
  00Ch .. VRAM Data (to be DMAed to frame buffer)
Note: Above is usually a multiple of 4 bytes, but not always:
Shadow Madness (MagDemo18: SHADOW\DATA\ANDY\LOADSAVE\*.TIM) contains TIM bitmaps with 27x27 or 39x51 halfwords; those files have odd section size & odd total filesize. Gran Turismo 2 (GT2.VOL\arcade\arc_other.tim\0000) also has odd size. Unknown if the CLUT can also have odd size (which would misalign the following Bitmap section).
Bust A Groove (MagDemo18: BUSTGR_A\G_COMMON.DFS\0005) has 0x0 pixel Bitmaps (with CLUT data).

PXL/CLT Format
PXL/CLT is very rare. And oddly, with swapped ID values (official specs say 11h=PXL, 12h=CLT, but the existing games do use 11h=CLT, 12h=PXL).
Used by Granstream Saga (MagDemo10 GS\*)
Used by Bloody Roar 1 (MagDemo06: BL\*)
Used by Bloody Roar 2 (MagDemo22: ASC,CMN,EFT,LON,SND,ST5,STU\*)

CLT Format
  000h 1  File ID  (always 11h=CLT) (although Sony's doc says 12h)
  001h 1  Version  (always 00h)
  002h 2  Reserved (always 0000h)
  004h 4  Flags (bit0-1=Type=2; bit2-31=Reserved/zero)
  ...  .. Data Section for CLUT (Palette)
The .CLT Type should be always 2 (meant to indicate 16bit CLUT entries).

PXL Format
  000h 1  File ID  (always 12h=PXL) (although Sony's doc says 11h)
  001h 1  Version  (always 00h)
  002h 2  Reserved (always 0000h)
  004h 4  Flags (bit0-?=Type; see below, bit?-31=Reserved/zero)
  ...  .. Data Section for Pixels (Bitmap/Texture)
This does probably support the same 5 types as in .TIMs (though official Sony docs claim the .PXL type to be only 1bit wide, but netherless claim that PXL can be 4bpp, 8bpp, or 16bpp).

 _______________________________ Compressed TIM _______________________________

Compressed TIMs
Ape Escape (Sony 1999) is using a customized TIM format with 4bpp compression:
CDROM File Compression TIM-RLE4/RLE8
Other than that, TIMs can be compressed via generic compression functions (like LZSS, GZIP), or via bitmap dedicated compression formats (like BS, JPG, GIF).

 ______________________________ Malformed Files _______________________________

Malformed TIMs in BIGFILE.DAT
  Used by Legacy of Kain: Soul Reaver (eg. BIGFILE.DAT\folder04h\file13h)
  Used by Gex - Enter the Gecko (eg. BIGFILE.DAT\file0Fh\LZcompressed)
Malformed TIMs contain texture data preceeded by a dummy 14h-byte TIM header with following constant values:
  10 00 00 00  02 00 00 00  04 00 08 00  00 02 00 00  00 02 00 02  ;<-- this
  10 00 00 00  02 00 00 00  04 00 08 00  00 00 00 00  00 02 00 02  ;<-- or this
The malformed entries include:
  [04h]=Type should indicated the color depth, but it's always 02h=16bpp.
  [08h]=Width*2*Height+0Ch should be 8000Ch, but malformed is 80004h.
  Total filesize should be 80014h, but Gecko files are often MUCH smaller.
Also, destination yloc should be 0..1FFh, but PSX "Lemmings & Oh No! More Lemmings" (FILES\GFX\*.TIM) has yloc=200h (that game also has vandalized .BMP headers with 2-byte alignment padding after ID "BM", whilst pretending that those extra bytes aren't there in data offset and total size entries).

Oversized TIMs
  Used by Pong (MagDemo24: LES02020\*\*.TIM)
Has 200x200h pix, but section size (and filesize) are +2 bigger than that:
  10 00 00 00 02 00 00 00 0E 00 08 00 C0 01 00 00 00 02 00 02  ;Pong *.TIM
  10 00 00 00 02 00 00 00 0E 00 07 00 00 02 00 00 C0 01 00 02  ;Pong WORLD.TIM
  10 00 00 00 02 00 00 00 0E 80 03 00 00 02 00 01 C0 01 00 01  ;Pong ZONE*.TIM

Miscomputed Section Size
NBA Basketball 2000 (MagDemo28: FOXBB\TIM\*.TIM) has TIMs with section size "0Ch+Xsiz*Ysiz" instead of "0Ch+Xsiz*2*Ysiz".

NonTIMs in Bloody Roar 1 and 2
  Bloody Roar 1 (CMN\INIT.DAT\000Eh)
  Bloody Roar 2 (CMN\SE00.DAT, CMD\SEL00.DAT\0030h and CMN\VS\VS.DAT\0000h)
This looks somehow TIM-inspired, but has ID=13h.
  13 00 00 00 02 00 00 00 0C 20 00 00 00 00 F8 01 00 01 10 00  ;Bloody Roar 1
  13 00 00 00 02 00 00 00 0C 20 00 00 00 00 00 00 00 01 10 00  ;Bloody Roar 2

Other uncommon/malformed TIM variants
And, Heart of Darkness has a TIM with Size entry set to Xsiz*2*Ysiz+0Eh (instead of +0Ch) (that malformed TIM is found inside of the RNC compressed IMAGES\US.TIM file).
Also, NFL Gameday '99 (MagDemo17: GAMEDAY\PHOTOS.FIL) contains a TIM cropped to 800h-byte size (containing only the upper quarter of the photo).
Also, not directly malformed, but uncommon: Final Fantasy IX contains 14h-byte 0x0 pixel TIMs (eg. FF9.IMG\dir04\file0046\1B-0000\04-0001).
Klonoa (MagDemo08: KLONOA\FILE.IDX\3\2\0..1) has 0x0pix TIM (plus palette).

Malformed CLTs
  Used by Secret of Mana, WM\WEFF\*.CLT
ID is 10h=TIM, Flags=10101009h (should be ID=12h, Flags=02h).


CDROM File Video Texture/Bitmap (Other)

Apart from Sony's TIM (and PXL/CLT) format, there are a bunch of other texture/bitmap formats:

Compressed Bitmaps
  .BS used by several games (and also in most .STR videos)
  .GIF used by Lightspan Online Connection CD
  .JPG used by Lightspan Online Connection CD
  .BMP with RLE4 used by Lightspan Online Connection CD (MONOFONT, PROPFONT)
  .BMP with RLE8+Delta also used by Online Connection CD (PROPFONT\ARIA6.BMP)
  .PCX with RLE used by Jampack Vol. 1 (MDK\CD.HED\*.pcx)

Uncompressed Bitmaps
  .BMP
  .BMP used by Mat Hoffman's Pro BMX (MagDemo39: BMX\BMXCD.HED\*)
  .BMP used by Mat Hoffman's Pro BMX (MagDemo48: MHPB\BMXCD.HED\*)
  .BMP used by Thrasher: Skate and Destroy (MagDemo27: SKATE\ASSETS\*.ZAL)
  .BMP used by Dave Mirra Freestyle BMX (MagDemo36,46: BMX\ASSETS\*.ZAL)
  .VRM .IMG .TEX .TIM .RAW .256 .COL .4B .15B .R16 .TPG - raw VRAM data
  "SC" memory card icons

Targa TGA and Paintbrush PCX
CDROM File Video Texture/Bitmap (TGA)
CDROM File Video Texture/Bitmap (PCX)

PSI bitmap - Power Spike (MagDemo43: POWER\GAME.IDX\*.BIZ\*.PSI)
  000h 10h   Name 1 ("FILENAME.BMP", zeropadded)
  010h 10h   Name 2 ("FILENAME.PSI", zeropadded)
  020h 4     Bits per pixel (usually 4, 8, or 16)
  024h 2     Bitmap VRAM Dest.X ?
  026h 2     Bitmap VRAM Dest.Y ?
  028h 2     Bitmap Width in pixels
  02Ah 2     Bitmap Height in pixels
  02Ch 2     Palette VRAM Dest.X ?   ;\zero for 16bpp
  02Eh 2     Palette VRAM Dest.Y ?   ;/
  030h 2     Bitmap Width in Halfwords (PixelWidth*bpp/16)
  032h 2     Palette Size in Halfwords (0, 10h, 100h for 16bpp,4npp,8bpp)
  034h 4     Maybe Bitmap present flag (always 1)
  038h 4     Maybe Palette present flag (0=16bpp, 1=4bpp/8bpp)
  03Ch ..    Bitmap pixels
  ...  ..    Palette (if any, for 4bpp: 16x16bit, for 8bpp: 256x16bit)

JumpStart Wildlife Safari Field Trip (MagDemo52: DEMO\DATA.DAT\*.DAT+*.PSX)
This game does use two different (but nearly identical) bitmap formats (with either palette or bitmap data stored first).
  000h 4     Total Filesize (Width*Height+20Ch)
  004h 2     Bitmap Width
  006h 2     Bitmap Height
  008h 4     Unknown, always 1 (maybe 1=8bpp?)
 In .DAT files (512x192 or 256x64 pix), palette first:
  00Ch 200h  Palette data
  20Ch ..    Bitmap data
 In .PSX files (64x64 pix), bitmap first:
  00Ch ..    Bitmap data
  ...  200h  Palette data
To detect the "palette first" format, check for these conditions(s):
  Filename extension is ".DAT"
  Bitmap Width<>Height (non-square)
  [00Ch..20Bh] has AllMSBs>=80h, and SomeLSBs<80h
Note: The bitmaps are vertically mirrored (starting with bottom-most scanline).

WxH Bitmap (Width*Height)
Used by Alone in the Dark The New Nightmare (FAT.BIN\BOOK,DOC,INTRO,MENU\*)
Used by Rayman (RAY\JUN,MON,MUS\*) (but seems to contain map data, not pixels)
  000h 2   Width  (W)   ;\usually 320x240 (or 512x240 or 80x13)
  002h 2   Height (H)   ;/
  004h ..  Bitmap 16bpp (W*H*2 bytes)

RAWP Bitmap
Used by Championship Motocross (MagDemo25: SMX\RESHAD.BIN\*) ("RAWP")
  000h 4     ID "RAWP" (this variant has BIG-ENDIAN width/height!)
  004h 2     Width  (usually 280h=640pix or 140h=320pix) (big-endian!!!)
  006h 2     Height (usually 1E0h=480pix or F0h=240pix)  (big-endian!!!)
  008h ..    Bitmap data, 16bpp (width*height*2 bytes)

XYWH Bitmap/Palette (X,Y,Width*Height) (.BIT and .CLT)
Used by CART World Series (MagDemo04: CART\*.BIT and *.BIN\*)
Used by NFL Gameday '98 (MagDemo04: GAMEDAY\BUILD\GRBA.FIL\*)
Used by NFL Gameday '99 (MagDemo17: GAMEDAY\*.BIT and *.FIL\*)
Used by NFL Gameday 2000 (MagDemo27: GAMEDAY\*.BIT)
Used by NCAA Gamebreaker '98 (MagDemo05: GBREAKER\*.BIT and UFLA.BIN\*)
Used by NCAA Gamebreaker 2000 (MagDemo27: GBREAKER\*.BIT and *.FIL\*)
Used by Twisted Metal 4 (MagDemo30: TM4DATA\*.MR,*.IMG\*.bit,*.clt)
  000h 2   VRAM.X             (X) (0..3FFh)
  002h 2   VRAM.X             (Y) (0..1FFh)
  004h 2   Width in halfwords (W) (1..400h)
  006h 2   Height             (H) (1..200h)
  008h ..  Bitmap or Palette data (W*H*2 bytes)

Doom (PSXDOOM\ABIN\PSXDOOM.WAD\*\*)
  000h 2     Hotspot X (signed) (usually 0)
  002h 2     Hotspot Y (signed) (usually 0)
  004h 2     Width in bytes
  006h 2     Height
  008h ..    Bitmap 8bpp (Width*Height bytes)
Most files have Hotspot X=0,Y=0, WAD\LOADING has X=FF80h,Y=FF8Ah, and WAD\S\* has X=0..Width, Y=0..Height+1Ah (eg. S\BKEY*, S\BFG*, S\PISFA0 have large Y).
The files do not contain any palette info... maybe 2800h-byte PLAYPAL does contain the palette(s)?

Lemmings & Oh No! More Lemmings (FILES\GFX\*.BOB, FILES\SMLMAPS\*.BOB)
  000h 2       Width
  002h 2       Height
  004h 100h*3  Palette 24bit RGB888
  304h ..      Bitmap 8bpp (Width*Height bytes)
  ..   (1700h) Unknown (only in SMLMAPS\*.BOB, not in GFX\*.BOB)
Apart from .BOB, the FILES\GFX folder also has vandalized .BMP (with ID "BM",00h,00h) and corrupted .TIM (with VRAM.Y=200h).

Perfect Assassin (DATA.JFS\DATA\*.BM)
  000h 4      Format 1 (0=8bpp, 1=16bpp)
  004h 4      Format 2 (1=8bpp, 2=16bpp)
  008h 4      Width in pixels
  00Ch 4      Height in pixels
  010h ..     Bitmap Data
  ...  (300h) Palette 18bit RGB666 (R,G,B range 00h..3Fh) (only if format 8bpp)

One (DIRFILE.BIN\*.VCF)
  000h 4     Unknown (always 1)
  004h 4     Unknown (always 8)
  008h 4     Unknown (always 2) (maybe 2=16bpp?)
  00Ch 4     Width in pixels (3Ah, 140h, or 280h)
  010h 4     Height          (3Ah, or F0h)
  014h ..    Bitmap 16bpp (Width*Height*2 bytes)

One (DIRFILE.BIN\*.VCK and DIRFILE.BIN\w*\sect*.bin\TEXTURE 001)
  000h 2     Number if Files (N)
  002h 2     Number of VRAM.Slots (less or equal than Number of Files)
  004h 4     ID "BLK0"
  008h N*10h File List
  ...  ..    1st File Bitmap
  ...  ..    1st File Palette (20h/200h/0 bytes for 4bpp/8bpp/16bpp)
  ...  ..    2nd File Bitmap
  ...  ..    2nd File Palette (only if PaletteID=FileNo=1)
  ...  ..    3rd File Bitmap
  ...  ..    3rd File Palette (only if PaletteID=FileNo=2)
  ...  ..    etc.
File List entries:
  000h 2     VRAM.X in halfwords (0..1Fh, +bit15=Blank)  ;\within current
  002h 2     VRAM.Y              (0..3Fh)                ;/VRAM.Slot
  004h 2     Width in pixels (max 80h/40h/20h for 4bpp/8bpp/16bpp)
  006h 2     Height          (max 40h)
  008h 2     VRAM.Slot       (0,1,2,3,...,NumSlots-1)
  00Ah 2     Unknown         (0,1,2,4 in *.vck, 4 in sect*.bin)
  00Ch 2     Color Depth     (0=4bpp, 1=8bpp, 2=16bpp)
  00Eh 2     Palette ID      (0..FileNo-1=Old, FileNo=New, FFFFh=None/16bpp)
  NumFiles-1, or ID of already used palette)
Note: VRAM.Slots are 20h*40h halfwords.
Bitmaps can either have newly defined palettes (when PaletteID=FileNo), or re-use previously defined "old" palettes (when PaletteID<FileNo).
The Blank flag allows to define a blank region (for whatever purpose), the file doesn't contain any bitmap/palette data for such blank regions.

BMR Bitmaps
These are 16bpp bitmaps, stored either in uncompressed .BMR files, or in compressed .RLE files:
CDROM File Compression RLE_16
  Apocalypse (MagDemo16: APOC\CD.HED\*.RLE and *.BMR)
  Spider-Man 1 older version (MagDemo31: SPIDEY\CD.HED\*.RLE)
  Spider-Man 1 newer version (MagDemo40: SPIDEY\CD.HED\*.RLE and .BMR)
  Spider-Man 2 (MagDemo50: HARNESS\CD.HED\*.RLE)
  Tony Hawk's Pro Skater (MagDemo22: PROSKATE\CD.HED\*.BMR)
The width/height for known filesizes are:
  33408h bytes --> 512x205pix, 16bpp (Apocalypse warning.rle)
  3C008h bytes --> 512x240pix, 16bpp (most common)
  96008h bytes --> 640x480pix, 16bpp (tony hawk's pro skater)
Most of the older BMR files (in Apocalypse) have valid 8-byte headers:
  000h 2     Unknown (FFA0h) (ID for files with valid headers?)
  002h 2     Dest.Y (usually 0) (11h=(240-205)/2 in Apocalypse warning.rle)
  004h 2     Width  (usually 200h=512pix)
  006h 2     Height (usually F0h=240pix) (CDh=205pix in Apocalypse warning.rle)
  008h ..    Bitmap data, 16bpp (width*height*2 bytes)
Most or all newer BMR files (in Apocalypse "loadlogo.rle", and in all files in Spider-Man 1, Spider-Man-2, Tony Hawk's Pro Skater) have the 8-byte header replaced by unused 8-byte at end of file:
  000h ..    Bitmap data, 16bpp (width*height*2 bytes)
  ..   8     Unused (garbage or extra pixels, not transferred to VRAM)
BUG: The bitmaps in all .BMR files (both with/without header) are distorted: The last 4-byte (rightmost 2pix) of each scanline should be actually located at the begin of the scanline, and the last scanline is shifted by an odd amount of bytes (resulting in nonsense 16bpp pixel colors); Spider-Man is actually displaying the bitmap in that distorted form (although it does mask off some glitches: one of the two bad rightmost pixels is replaced by a bad black leftmost pixel, and glitches in upper/lower lines aren't visible on 224-line NTSC screens).

Croc 1 (retail: *.IMG) (retail only, not in MagDemo02 demo version)
Croc 2 (MagDemo22: CROC2\CROCII.DIR\*.IMG)
Disney's The Emperor's New Groove (MagDemo39: ENG\KINGDOM.DIR\*.IMG)
Disney's Aladdin in Nasira's Rev. (MagDemo46: ALADDIN\ALADDIN.DIR\*.IMG)
Contains raw 16bpp bitmaps, with following sizes:
  25800h bytes = 12C00h pixels (320x240)  ;Croc 1 (retail version)
  3C000h bytes = 1E000h pixels (512x240)
  96000h bytes = 4B000h pixels (640x480)
Note: The .IMG format is about same as .BMR files (but without the 8-byte header, and without distorted scanlines).

Mat Hoffman's Pro BMX (MagDemo39: BMX\FE.WAD+STR\*.BIN) (Activision)
Mat Hoffman's Pro BMX (MagDemo48: MHPB\FE.WAD+STR\*.BIN) (Shaba/Activision)
  000h 2     Bits per pixel (4 or 8)
  002h 2     Bitmap Width in pixels
  004h 2     Bitmap Height in pixels
  006h 2     Zero
  008h N*2   Palette (with N=(1 SHL bpp))
  ...  ..    Bitmap (with Width*Height*bpp/8 bytes)
  ...  (..)  Zeropadding to 4-byte boundary (old version only)
The trailing alignment padding exists only in old demo version (eg. size of 78x49x8bpp "coreypp.bin" is old=10F8h, new=10F6h).

E.T. Interplanetary Mission (MagDemo54: MEGA\MEGA.CSH\*)
  000h 2     Type (0=4bpp, 1=8bpp, 2=16bpp)
  002h 2     Unknown (usually 0000h, or sometimes CCCCh)
  004h 2     Bitmap Width in pixels
  006h 2     Bitmap Height in pixels
  008h 200h  Palette (always 200h-byte, even for 4bpp or 16bpp)
  208h ..    Bitmap (Width*Height*bpp/8 bytes)
Palette is 00h-or-CCh-padded when 4bpp, or CCh-filled when 16bpp.
Note: Some files contain two or more such bitmaps (of same or different sizes) badged together.

EA Sports: Madden NFL '98 (MagDemo02: TIBURON\*.DAT\*)
EA Sports: Madden NFL 2000 (MagDemo27: MADN00\*.DAT\*)
EA Sports: Madden NFL 2001 (MagDemo39: MADN01\*.DAT\*)
This format is used in various EA Sports Madden .DAT archives, it contains standard TIMs with extra Headers/Footers.
  000h 4     Offset to TIM (1Ch) (Hdr size)    (1Ch)               ;\
  004h 4     Offset to Footer    (Hdr+TIM size)(123Ch,1A3Ch,1830h) ;
  008h 2     Bitmap Width in pixels      (40h or 60h or 30h)       ;
  00Ah 2     Bitmap Height in pixels     (40h)                     ;
  00Ch 4     Unknown, always 01h         (01h)                     ; Header
  010h 4     Unknown, always 23h         (23h)                     ; 1Ch bytes
  014h 2     Unknown, always 0101h       (101h)                    ;
  016h 1     Bitmap Width in pixels      (40h or 60h or 30h)       ;
  017h 1     Bitmap Height in pixels     (40h)                     ;
  018h 4     Unknown, always 00h         (0)                       ;/
  01Ch ..    TIM (Texture, can be 4bpp, 8bpp, 16bpp)               ;-TIM
  ...  4     Unknown, always C0000222h   (C0000222h)               ;\
  ...  2     Unknown, always 0001h       (0001h)                   ;
  ...  1     Bitmap Width in pixels      (40h or 60h or 30h)       ; Footer
  ...  1     Bitmap Height in pixels     (40h)                     ; 12h bytes
  ...  4     Unknown, always 78000000h   (78000000h)               ;
  ...  6     Unknown                     (0,0,80h,0,0,0)           ;/
Purpose is unknown; the 8bit Width/Height entries might be TexCoords.
The PORTRAITS.DAT archives are a special case:
  Madden NFL '98 (MagDemo02: TIBURON\PORTRAIT.DAT) (48x64, 16bpp)
  Madden NFL 2000 (MagDemo27: MADN00\PORTRAIT.DAT) (96x64, 8bpp plus palette)
  Madden NFL 2001 (MagDemo39: MADN01\PORTRAIT.DAT) (64x64, 8bpp plus palette)
Those PORTRAITS.DAT don't have any archive header, instead they do contain several images in the above format, each one zeropadded to 2000h-byte size.

989 Sports: NHL Faceoff '99 (MagDemo17: FO99\*.KGB\*.TEX)
989 Sports: NHL Faceoff 2000 (MagDemo28: FO2000\*.TEX)
989 Sports: NCAA Final Four 2000 (MagDemo30: FF00\*.TEX)
  000h 0Ch  ID "TEX PSX ",01h,00h,00h,00h    ;used in 989 Sports games
  00Ch 4    Number of Textures
  010h 4    Total Filesize
  014h 4    Common Palette Size    (0=200h, 1=None, 2=20h)
  018h (..) Common Palette, if any (0,20h,200h bytes)
  ...  ..   Texture(s)
 Texture format:
  000h 10h  Filename (eg. "light1", max 16 chars, zeropadded if shorter)
  010h 4    Width in pixels  (eg. 40h)
  014h 4    Height           (eg. 20h or 40h)
  018h 4    Unknown          (always 0)
  01Ch 4    Number of Colors (eg. 10h, 20h or 100h)
  020h ..   Bitmap (4bpp when NumColors<=10h, 8bpp when NumColors>10h)
  ...  (..) Palette (NumColors*2 bytes, only present if Common Palette=None)
The .TEX files may be in ISO folders, KGB archives, DOTLESS archives. And, some are stored in headerless .DAT/.CAT archives (which start with ID "TEX PSX ", but seem to have further files appended thereafter).

Electronic Arts .PSH (SHPP)
FIFA - Road to World Cup 98 (with chunk C0h/C1h = RefPack compression)
NCAA March Madness 2000 (MagDemo32: MM2K\*.PSH)
Need for Speed 3 Hot Pursuit (*.PSH, ZLOAD*.QPS\RefPack.PSH)
ReBoot (DATA\*.PSH) (with chunk 6Bh)
Sled Storm (MagDemo24: DEBUG,ART,ART2,ART3,SOUND\*.PSH) (with Comment, Mipmap)
WCW Mayhem (MagDemo28: WCWDEMO\*.BIG\*.PSH) (with chunk C0h/C1h = RefPack)
  000h 4    ID "SHPP"
  004h 4    Total Filesize (or Filesize-0Ch, eg. FIFA'98 ZLEG*.PSH)
  008h 4    Number of Textures (N)
  00Ch 4    ID "GIMX"
  010h N*8  File List
  ...  ..   Data (each File contains a Bitmap chunk, and Palette chunk, if any)
 File List entries:
  000h 4    Name (ascii) (Mipmaps use the same name for each mipmap level)
  004h 4    Offset from begin of archive to first Chunk of file
  Caution: Most PSH files do have the above offsets sorted in increasing order,
  but some have UNSORTED offsets, eg. Sled Storm (MagDemo24: ART3\LOAD1.PSH),
  so one cannot easily compute sizes as NextOffset-CurrOffset.
  Note: Mipmap textures consist of two files with same name and different
  resolution, eg. in Sled Storm (MagDemo24: ART\WORLD0x.PSH)
 Bitmap Chunk:
  000h 1    Chunk Type (40h=PSX/4bpp, 41h=PSX/8bpp, 42h=PSX/16bpp)
  001h 3    Offset from current chunk to next chunk (000000h=None)
  004h 2    Bitmap Width in pixels (can be odd, pad lines to 2-byte boundary)
  006h 2    Bitmap Height
  008h 2    Center X   (whatever that is)
  00Ah 2    Center Y   (whatever that is)
  00Ch 2    Position X (whatever that is, plus bit12-15=flags?)
  00Eh 2    Position Y (whatever that is, plus bit12-15=flags?)
  010h ..   Bitmap data (each scanline is padded to 2-byte boundary)
  ...  ..   Padding to 8-byte boundary
 Compressed Bitmap Chunk:
  000h 1    Chunk Type (C0h=PSX/4bpp, C1h=PSX/8bpp, and probably C2h=PSX/16bpp)
  001h 0Fh  Same as in Chunk 40h/41h/42h (see there)
  010h ..   Compressed Bitmap data (usually/always with Method=10FBh)
  ...  ..   Padding to 8-byte boundary
 Palette Chunk (if any) (only for 4bpp/8bpp bitmaps, not for 16bpp):
  000h 1    Chunk Type (23h=PSX/Palette)
  001h 3    Offset from current chunk to next chunk (000000h=None)
  004h 2    Palette Width in halfwords (10h or 100h)
  006h 2    Palette Height             (1)
  008h 2    Unknown (usually same as Width) (or 80D0h or 9240h)
  00Ah 2    Unknown (usually 0000h)         (or 0001h or 0002h)
  00Ch 2    Unknown (usually 0000h)
  00Eh 2    Unknown (usually 00F0h)
  010h ..   Palette data (16bit per color)
  Note: The odd 80D0h,0001h values occur in Sled Storm ART\WORKD00.PSH\TBR1)
 Unknown Chunk (eg. ReBoot (DATA\AREA15.PSH\sp*))
  000h 1    Chunk Type (6Bh)
  001h 3    Offset from current chunk to next chunk (000000h=None)
  004h 8    Unknown (2C,00,00,3C,03,00,00,00)
  00Ch -    For whatever reason, there is no 8-byte padding here
 Comment Chunk (eg. Sled Storm (MagDemo24: ART\WORLD0x.PSH))
  000h 1    Chunk Type (6Fh=PSX/Comment)
  001h 3    Offset from current chunk to next chunk (000000h=None)
  004h ..   Comment ("Saved in Photoshop Plugin made by PEE00751@...",00h)
  ...  ..   Zeropadding to 8-byte boundary
 Unknown Chunk (eg. Sled Storm (MagDemo24: ART\WORLD09.PSH\ADAA))
  000h 1    Chunk Type (7Ch)
  001h 3    Offset from current chunk to next chunk (000000h=None)
  004h 2Ch  Unknown (reportedly Hot spot / Pix region, but differs on PSX?)
The whole .PSH file or the bitmap chunks can be compressed:
CDROM File Compression EA Methods
Variants of the .PSH format are also used on PC, PS2, PSP, XBOX (with other Chunk Types for other texture/palette formats, and for optional extra data). For details, see: http://wiki.xentax.com/index.php/EA_SSH_FSH_Image

Destruction Derby Raw (MagDemo35: DDRAW\*.PCK,*.FNT,*.SPR)
This format can contain one single Bitmap, or a font with several small character bitmaps.
  000h 2     ID "BC"                                        ;\
  002h 1     Color Depth (1=4bpp, 2=8bpp, 4=16bpp)          ; Header
  003h 1     Type        (40h=Bitmap, C0h=Font)             ;/
  ...  (2)   Palette Unknown (0 or 1)                       ;\only if Bitmap
  ...  (2)   Palette Unknown (1)                            ; 4bpp or 8bpp
  ...  (..)  Palette data (20h or 200h bytes for 4bpp/8bpp) ;/
  ...  2     Bitmap Number of Bitmaps-1 (N-1)               ;\
  ...  2     Bitmap Width in pixels                         ;
  ...  2     Bitmap Height in pixels                        ; Bitmap(s)
  ...  N*1   Bitmap Tilenumbers (eg. "ABCDEFG..." for Fonts);
  ...  N*1   Bitmap Proportional Font widths? (0xh or FFh)  ;
  ...  N*BMP Bitmap(s) for all characters                   ;/
  ...  (20h) Palette Data (20h bytes for 4bpp)              ;-only if Font/4bpp
All bitmap scanlines are padded to 2-byte boundary, eg. needed for:
  INGAME1\BOWL2.PTH\SPRITES.PTH\ST.SPR    30x10x4bpp: 15 --> 16 bytes/line
  INGAME1\BOWL2.PTH\SPRITES.PTH\STOPW.SPR 75x40x4bpp: 37.5 --> 38 bytes/line
The BC files are usually compressed (either in PCK file, or in the compressed DAT portion of a PTH+DAT archive).

Cool Boarders 2 (MagDemo02: CB2\DATA*\*.FBD)
  000h 2    ID ("FB")                                ;\File Header
  002h 2    Always 1 (version? 4bpp? num entries?)   ;/
  004h 2    Palette VRAM Dest X (eg. 300h)           ;\
  006h 2    Palette VRAM Dest Y (eg. 1CCh,1EDh,1FFh) ; Palette Header
  008h 2    Palette Width in halfwords (eg. 100h)    ; (all zero when unused)
  00Ah 2    Palette Height (eg. 1 or 0Dh)            ;/
  00Ch 2    Bitmap VRAM Dest X (eg. 140h or 200h)    ;\
  00Eh 2    Bitmap VRAM Dest Y (eg. 0 or 100h)       ; Bitmap Header
  010h 2    Bitmap Width in halfwords                ;
  012h 2    Bitmap Height                            ;/
  ...  ..   Palette Data (if any)                    ;-Palette Data
  ...  ..   Bitmap Data                              ;-Bitmap Data
The bitmap data seems to be 4bpp and/or 8bpp, but it's hard to know the correct palette (some files have more than 16 or 256 palette colors, or don't have any palette at all).


CDROM File Video Texture/Bitmap (TGA)

Targa TGA
  000h 1   Image ID Size (00h..FFh, usually 0=None)      ;0
  001h 1   Palette Present Flag (0=None, 1=Present)      ;0            iv=1
  002h 1   Data Type code (0,1,2,3,9,10,11,32,33)        ;NEBULA=2     iv=1
  003h 2   Palette First Color (usually 0)               ;0            iv=0
  005h 2   Palette Number of Colors (usually 100h)       ;0            iv=100h
  007h 1   Palette Bits per Color (16,24,32, usually 24) ;0            iv=18h
  008h 2   Bitmap X origin (usually 0)                   ;0
  00Ah 2   Bitmap Y origin (usually 0)                   ;0
  00Ch 2   Bitmap Width                                  ;NEBULA=20h LOGO=142h
  00Eh 2   Bitmap Height                                 ;NEBULA=20h
  010h 1   Bitmap Bits per Pixel (8,16,24,32 exist?)     ;NEBULA=18h   iv=8
  011h 1   Image Descriptor (usually 0)                  ;0
  012h ..  Image ID Data (if any, len=[00h], usually 0=None)
  ...  ..  Palette
  ...  ..  Bitmap
  ...  1Ah Footer (8x00h, "TRUEVISION-XFILE.", 00h) (not present in iview)
Data Type [02h]:
  00h = No image data included  ;-Unknown purpose
  01h = Color-mapped image      ;\
  02h = RGB image               ; Uncompressed
  03h = Black and white image   ;/
  09h = Color-mapped image      ;\Runlength
  0Ah = RGB image               ;/
  0Bh = Black and white image   ;-Unknown compression method
  20h = Color-mapped image      ;-Huffman+Delta+Runlength
  21h = Color-mapped image      ;-Huffman+Delta+Runlength+FourPassQuadTree
The official specs do list the above 9 types, but do describe only 4 types in detail (type 01h,02h,09h,0Ah).
  Type 01h and 09h lack details on supported bits per pixel (8bpp with 100h
    colors does exist; unknown if less (or more) than 8bpp are supported,
    and if so, in which bit order.
  Type 02h and 0Ah are more or less well documented.
  Type 03h has unknown bit-order, also unknown if/how it differs from type
    01h with 1bpp.
  Type 0Bh, 20h, 21h lack any details on the compression method.
TGA's are used by a couple of PSX games/demos (all uncompressed):
  16bpp: Tomb Raider 2 (MagDemo01: TOMBRAID\*.RAW)
  24bpp: Tomb Raider 2 (MagDemo05: TOMB2\*.TGA)
  24bpp: Colony Wars Venegance (MagDemo14: CWV\GAME.RSC\NEBULA*.TGA, *SKY.TGA)
  24bpp: Colony Wars Red Sun (MagDemo31: CWREDSUN\GAME.RSC\000A\*)
  16bpp: Colony Wars Venegance (MagDemo14: CWV\GAME.RSC\LOGO.DAT)
  16bpp: X-Men: Mutant Academy (MagDemo50: XMEN2\*)
  16bpp: Disney's Tarzan (MagDemo42: TARZAN\*)
  8bpp+Wrong8bitAttr: SnoCross Championship Racing (MagDemo37: SNOCROSS\*.TGA)
  16bpp+WrongYflip: SnoCross Championship Racing (MagDemo37: SNOCROSS\*.TGA)
For whatever reason, TGA is still in use on newer consoles:
  32bpp: 3DS AR Games (RomFS:\i_ar\tex\hm*.lz77


CDROM File Video Texture/Bitmap (PCX)

PC Paintbrush .PCX files (ZSoft)
Default extension is .PCX (some tools did use .PCX for the "main" image, and .PCC for smaller snippets that were clipped/cropped/copied from from a large image).
  000h 1    File ID (always 0Ah=PCX/ZSoft)
  001h 1    Version (0,2,3,4,5)
  002h 1    Compression (always 01h=RLE) (or inofficial: 00h=Uncompressed)
  003h 1    Bits per Pixel (per Plane) (1, 2, 4, or 8)
  004h 2    Window X1   ;\
  006h 2    Window Y1   ; Width  = X2+1-X1
  008h 2    Window X2   ; Height = Y2+1-Y1
  00Ah 2    Window Y2   ;/
  00Ch 2    Horizontal Resolution in DPI  ;\often square, but can be also zero,
  00Eh 2    Vertical Resolution in DPI    ;/or screen size, or other values
  010h 30h  EGA/VGA Palette (16 colors, 3-byte per color = R,G,B) (or garbage)
  010h 1    CGA: Bit7-4=Background Color (supposedly IRGB1111 ?)
  013h 1    CGA: Bit7:0=Color,1=Mono,Bit6:0=Yellow,1=White,Bit5:0=Dim,1=Bright
  014h 1    Paintbrush IV: New CGA Color1 Green  ;\weird new way to encode CGA
  015h 1    Paintbrush IV: New CGA Color1 Red    ;/palette in these two bytes
  040h 1    Reserved (00h) (but is 96h in animals.pcx)
  041h 1    Number of color planes (1=Palette, 3=RGB, or 4=RGBI)
  042h 2    Bytes per Line (per plane) (must be N*2) (=(Width*Bits+15)/16*2)
  044h 2    PaletteInfo? (0000h/xxxxh=Normal, 0001h=Color/BW, 0002h=Grayscale)
  046h 2    Horizontal screen size in pixels  ;\New fields, found only
  048h 2    Vertical screen size in pixels    ;/in Paintbrush IV/IV Plus
  04Ah 36h  Reserved (zerofilled) (or garbage in older files, custom in MGS)
  080h ..   Bitmap data (RLE compressed)
  ...  1    VGA Palette ID (0Ch=256 colors)                      ;\when 8bpp
  ..   300h VGA Palette (256 colors, 3-byte per color  = R,G,B)  ;/
Decoding PCX files is quite a hardcore exercise due to a vast amount of versions, revisions, corner cases, incomplete & bugged specifications, and inofficial third-party glitches.

PCX Versions
  00h = Version 2.5 whatever ancient stuff
  02h = Version 2.8 with custom 16-color palette
  03h = Version 2.8 without palette (uses fixed CGA/EGA palette)
  04h = Version ?.? without palette (uses fixed CGA/EGA palette)
  05h = Version 3.0 with custom 16-color or 256-color palette or truecolor
NOTE: Version[01h]=05h with PaletteInfo[44h]=0001h..0002h is Paintbrush IV?

Known PCX Color Depths
  planes=1, bits=1  P1        ;1bit, HGC 2 color (iview and paint shop pro 2)
  planes=1, bits=2  P2        ;2bit, CGA 4 color (with old/new palette info)
  planes=3, bits=1  RGB111    ;3bit, EGA 8 color (official samples)  ;\version
  planes=4, bits=1  IRGB1111  ;4bit, EGA 16 color (paint shop pro 2) ;/03h..04h
  planes=1, bits=4  P4        ;4bit, BMP 16 color (iview)
  planes=1, bits=8  P8        ;8bit, VGA 256 color palette
  planes=1, bits=8  I8        ;8bit, VGA 256 level grayscale (gmarbles.pcx)
  planes=3, bits=8  BGR888    ;24bit, truecolor (this is official 24bit format)
 ;planes=1, bits=24 BGR888 ?  ;24bit, reportedly exists? poor compression
 ;planes=4, bits=4  ABGR4444  ;16bit, wikipedia-myth? unlikely to exist
 ;planes=4, bits=8  ABGR8888  ;32bit, truecolor+alpha (used in abydos.dcx\*)

Width and Height
These are normally calculated as so:
  Width  = X2+1-X1      ;width for normal files
  Height = Y2+1-Y1      ;height for normal files
However, a few PCX files do accidentally want them to be calculated as so:
  Width  = X2-X1        ;width for bugged files
  Height = Y2-Y1        ;height for bugged files
Files with bugged width can be (sometimes) detected as so:
  (Width*Bits+15)/16*2) > BytesPerLine
Files with bugged height can be detected during decompression:
  BeginOfLastScanline >= Filesize (or Filesize-301h for files with palette)
Bugged sample files are SAMPLE.DCX, marbles.pcx and gmarbles.pcx. RLE decompression may crash when not taking care of such files.

Color Planes and Palettes
The official ZSoft PCX specs are - wrongly - describing planes as:
  plane0 = red         ;\
  plane1 = green       ; this is WRONG, NONSENSE, does NOT exist
  plane2 = blue        ;
  plane3 = intensity   ;/
The 8-color and 16-color EGA images are actually using plane0,1,2,(3) as bit0,1,2,(3) of the EGA color number; which implies plane0=blue (ie. red/blue are opposite of the ZSoft document).
The truecolor and truecolor+alpha formats have plane0..2=red,green,blue (as described by ZSoft), but they don't have any intensity plane (a few files are using plane3=alpha).

Mono 2-Color Palette
This format was intended for 640x200pix 2-color CGA graphics, it's also common for higher resolution FAX or print images. The general rule for these files is to use this colors:
  color0=black
  color1=white
There are rumours that color1 could be changed to any of the 16 CGA colors (supposedly via [10h].bit7-4, but most older & newer 2-color files have that byte set to 00h, so one would end up with black-on-black).
Some newer 2-color files contain RGB palette entries [10h]=000000h, [13h]=FFFFFFh (and [16h..3Fh]=00h-filled or FFh-filled).
Iview does often display 2-color images with color1=dark green (somewhat mysteriously; it's doing that even for files that don't contain any CGA color numbers or RGB palette values that could qualify as dark green).

4-Color Palettes
This format was intended for 320x200pix 4-color CGA graphics, and the palette is closely bound to colors available in CGA graphics modes. Color0 is defined in [10h], and Color1-3 were originally defined in [13h], and later in [14h,15h]:
  color0=[10h].bit7-4  ;(Color0 IRGB)  ;CGA Port 3D9h.bit3-0 (usually 0=black)
  bright=[13h].bit5                    ;CGA Port 3D9h.bit4    ;\
  palette=[13h].bit6                   ;CGA Port 3D9h.bit5    ; old method
  if [13h].bit7 then palette=2         ;CGA Port 3D8h.bit2    ;/
  if [01h]=05h and [44h]=0001h then                           ;\new "smart"
    if [14h]>200 or [15h]>200 then bright=1, else bright=0    ; method used in
    if [14h]>[15h] then palette=0 else palette=1              :/Paintbrush IV
  if palette=0 and bright=0 then color1..3=02h,04h,06h  ;\green-red-yellow
  if palette=0 and bright=1 then color1..3=0Ah,0Ch,0Eh  ;/
  if palette=1 and bright=0 then color1..3=03h,05h,07h  ;\cyan-magenta-white
  if palette=1 and bright=1 then color1..3=0Bh,0Dh,0Fh  ;/
  if palette=2 and bright=0 then color1..3=03h,04h,07h  ;\cyan-red-white
  if palette=2 and bright=1 then color1..3=0Bh,0Ch,0Fh  ;/
Palette=2 uses some undocumented CGA glitch, it was somewhat intended to output grayscale by disabling color burst on CGA hardware with analog composite output, but actually most or all CGA hardware is having digital 4bit IRGB output, which outputs cyan-red-white.
The new "smart" method is apparently trying to detect if [13h-1Bh] contains RGB values with Color1=Green or Cyan, and to select the corresponding CGA palette; unfortunately such PCX files are merely setting [14h,15h] to match up with the "smart" formula, without actually storing valid RGB values in [13h-1Bh].

8-Color and 16-Color, with fixed EGA Palettes (version=03h or 04h)
These images have 3 or 4 planes. Plane0-3 correspond to bit0-3 of the EGA color numbers (ie. blue=plane0, green=plane1, red=plane2, and either intensity=plane3 for 16-color, or intensity=0 for 8-color images).
Some 8-Color sample images (with version=03h and 04h) can be found bundled with PC Paintbrush Plus 1.22 for Windows. A 16-color sample called WINSCR.PCX can be found elsewhere in internet.
Caution 1: Official ZSoft specs are wrongly claiming plane0=red and plane2=blue; this is wrong (although Paint Shop Pro 2 is actually implementing it that way) (whilst MS Paint for Win95b can properly display them) (most other tools are trying to read a palette from [10h..3Fh], which is usually garbage filled in version=03h..04h).
Caution 2: The standard EGA palette is used for version=03h..04h (many docs claim it to be used for version=03h only).

16-Color, with custom EGA/VGA Palettes (version=02h or 05h)
These can have 1 plane with 4 bits, or 4 planes with 1 bit. Header[10h..3Fh] contains a custom 16-color RGB palette with 3x8bit per R,G,B.
Classic VGA hardware did only use the upper 6bit of the 8bit values.
Classic EGA hardware did only use the upper 2bit of the 8bit values (that, only when having a special EGA monitor with support for more than 16 colors).

256-Color VGA Palettes (version=05h)
These have 1 plane with 8 bits. And a 256-color RGB palette with 3x8bit per R,G,B appended at end of file.
The appended 256-color palette should normally exist only in 256-color images, some PCX tools are reportedly always appending the extra palette to all version=05h files (even for 2-color files).

256-Level Grayscale Images (version=05h and [44h]=0002h)
The most obvious and reliable way is to use a palette with grayscale RGB values. However, Paintbrush IV is explicetly implementing (or ignoring?) an obscure grayscale format with following settings:
  [01h]=version=05h, and [44h]=0002h=grayscale
That settings are used in a file called gmarbles.pcx (which does contain a 256-color RGB palette with gray RGB values, ie. one can simply ignore the special settings, and display it as normal 256-color image).

Default 16-color CGA/EGA Palettes
  Color  Name                     IRGB1111 RGB222 RGB888   Windows
  00h    dark black               0000     000    000000   000000
  01h    dark blue                0001     002    0000AA   000080
  02h    dark green               0010     020    00AA00   008000
  03h    dark cyan                0011     022    00AAAA   008080
  04h    dark red                 0100     200    AA0000   800000
  05h    dark magenta             0101     202    AA00AA   800080
  06h    dark yellow (brown)      0110     210!!  AA5500!! 808000
  07h    dark white (light gray)  0111     222    AAAAAA   C0C0C0!!
  08h    bright black (dark gray) 1000     111    555555   808080!!
  09h    bright blue              1001     113    5555FF   0000FF
  0Ah    bright green             1010     131    55FF55   00FF00
  0Bh    bright cyan              1011     133    55FFFF   00FFFF
  0Ch    bright red               1100     311    FF5555   FF0000
  0Dh    bright magenta           1101     313    FF55FF   FF00FF
  0Eh    bright yellow            1110     331    FFFF55   FFFF00
  0Fh    bright white             1111     333    FFFFFF   FFFFFF
Some notes on number of colors:
 CGA supports 16 colors in text mode (but only max 4 colors in graphics mode).
 EGA supports the same 16 colors as CGA in both text and graphics mode.
 EGA-with-special-EGA-monitor supports 64 colors (but only max 16 at once).
 VGA supports much colors (but can mimmick CGA/EGA colors, or similar colors)
CGA is using a 4pin IRGB1111 signal for up to 16 colors in text mode (max 4 colors in graphics mode), and CGA monitors contain some circuitry to convert "dark yellow" to "brown" (though cheap CGA clones may display it as "dark yellow").
EGA can display CGA colors (with all 16 colors in graphics mode). EGA-with-special-EGA-monitor uses 6pin RGB222 signals for up to 64 colors (but not more than 16 colors at once).
Windows is also using those 16 standard colors (when not having any VGA driver installed, and also in 256-color VGA mode, in the latter case the 16 standard colors are held to always available (even if different tasks are trying to simultanously display different images with different palettes).
However, Windows has dropped brown, and uses non-pastelized bright colors.

PCX files in PSX games
  .PCX with RLE used by Jampack Vol. 1 (MDK\CD.HED\*.pcx)
  .PCX with RLE used by Hot Wheels Extreme Racing (MagDemo52: US_01293\MISC\*)
  .PCX with RLE used by Metal Gear Solid (slightly corrupted PCX files)

PCX files in PSX Metal Gear Solid (MGS)
MGS is storing some extra data at [4Ah..57h] (roughly resembling the info in TIM files).
  04Ah 2    Custom MGS ID (always 3039h)
  04Ch 2    Display Mode? (08h/18h=4bit, 09h/19h=8bit)
  04Eh 2    Bitmap X-coordinate in VRAM (reportedly "divided by 2" ???)
  050h 2    Bitmap Y-coordinate in VRAM
  052h 2    Palette X-coordinate in VRAM
  054h 2    Palette Y-coordinate in VRAM
  056h 2    Palette number of actually used colors (can be less than 16/256)
  058h 28h  Reserved (zerofilled)
  080h ..   Bitmap data (RLE compressed)
  ...  1    VGA Palette ID (0Ch=256 colors)                      ;\when 8bpp
  ..   300h VGA Palette (256 colors, 3-byte per color  = R,G,B)  ;/
  ..   ..   Padding to 4-byte boundary, ie. palette isn't at filesize-301h !!!
MGS has filesize padded to 4-byte boundary. That is causing problems for files with 256-color palette: The official way to find the palette is to stepback 301h bytes from end of file, which won't work with padding. To find the MGS palette, one must decompress the whole bitmap, and then expect the 301h-byte palette to be located after the compressed data.
As an extra oddity, MGS uses non-square ultra-high DPI values.

DCX Archives
DCX archives contain multiple PCX files (eg. multi-page FAX documents). The standard format is as so:
  0000h 4     ID (3ADE68B1h) (987654321 decimal)
  0004h 4000h File List (32bit offsets) (max 1023 files, plus 0=End of List)
  1004h ..    File Data area (PCX files)
However, some files have the first PCX at offset 1000h (ie. the list is only 3FFCh bytes tall). Reportedly there are also files that start with yet smaller offsets (for saving space when the file list contains fewer entries).
The PCX filesize is next-curr offset (or total-curr for last file).

References
https://www.fileformat.info/format/pcx/egff.htm


CDROM File Video 2D Graphics CEL/BGD/TSQ/ANM/SDF (Sony)

CEL/BGD/TSQ/ANM/SDF

CEL: Cell Data (official format with 8bit header entries)
This does merely translate Tile Numbers to VRAM Addresses and Attributes (with the actual VRAM bitmap data usually being stored in .TIM files).
  000h 1   File ID (22h)
  001h 1   Version (3)
  002h 2   Flag (bit15=WithAttr, bit14=AttrDataSize:0=8bit,1=16bit, bit13-0=0)
  004h 2   Number of cell data items (in cell units) (N)
  006h 1   Sprite Editor Display Window Width  (in cell units)
  007h 1   Sprite Editor Display Window Height (in cell units)
  008h ..  Cell Data[N] (64bit entries)
  ...  ..  Cell Attr[N] (0bit/8bit/16bit user data? depending on Flag)
Cell Data:
  0-7   Tex Coord X (8bit)
  8-15  Tex Coord Y (8bit)
  16-21 Clut X      (6bit)
  22-30 Clut X      (9bit)
  31    Semi-transparency enable        ;-only in Version>=3
  32    Vertical Reversal   (Y-Flip)    ;\only in Version=0 and Version>=2
  33    Horizontal Reversal (X-Flip)    ;/
  34-47 Unused
  48-52 Texture Page (5bit)
  53-54 Semi Transparency     (0=B/2+F/2, 1=B+F, 2=B-F, 3=B+F/4)
  55-56 Texture page colors   (0=4bit, 1=8bit, 2=15bit, 3=Reserved)
  57-60 Sprite Editor Color Set Number  ;\
  61    Unused                          ; only in Version>=3
  62-63 Sprite Editor TIM Bank          ;/     XXX else hardcoded?
This is used in R-Types, CG.1\file3Dh\file00h, but [6,7] are 16bit wide! And there are a LOT of ZEROes appended (plus FFh-padding due to CG.1 archive size units).
Used by R-Types (CG.1\file07h\file01h, size 08h*04h, with 8bit attr)
Used by R-Types (CG.1\file07h\file03h, size 10h*08h, with 16bit attr)
Used by R-Types (CG.1\file07h\file05h, size 04h*04h, with 16bit attr)
Used by Tiny Tank (MagDemo23: TINYTANK\TMD05.DSK\*.CEL, size 08h*05h)

CEL16: Inofficial CEL hack with 16bit entries and more extra data (R-Types)
This is an inofficial hack used by R-Types, the game does use both the official CEL and inofficial CEL16 format.
  000h 1   File ID (22h)        ;\same as in official CEL version
  001h 1   Version (3)          ;/
  002h 2   Flag (...unknown meaning in this case...?)           ;<-- ?
  004h 2   Number of cell data items (in cell units) (N)
  006h 2   Sprite Editor Display Window Width  (in cell units)  ;<-- 16bit!
  008h 2   Sprite Editor Display Window Height (in cell units)  ;<-- 16bit!
  00Ah ..  Cell Data[N] (64bit entries)
  ...  ..  Cell Attr[N] (16bit/192bit user data, depending on Flag or so...?)
Used by R-Types (CG.1\file12h\file00h, size 0120h*000Fh with 192bit attr)
Used by R-Types (CG.1\file15h\file00h, size 0168h*000Fh with ? attr)
Used by R-Types (CG.1\file1Ch\file00h, size 00D8h*000Fh with ? attr)

BGD: BG Map Data (official format with 8bit header entries)
  000h 1   File ID (23h)
  001h 1   Version (0)
  002h 2   Flag (bit15=WithAttr, bit14=AttrDataSize:0=8bit,1=16bit, bit13-0=0)
  004h 1   BG Map Width  (in cell units) (W)
  005h 1   BG Map Height (in cell units) (H)
  006h 1   Cell Width    (in pixels)
  007h 1   Cell Height   (in pixels)
  008h ..  BG Map Data[W*H] (16bit cell numbers)
  ...  ..  BG Map Attr[W*H] (0bit/8bit/16bit user data? depending on Flag)
Used by R-Types (CG.1\file07h\file00h, official BGD format)
Used by Cardinal Syn (MagDemo03,09: SYN\SONY\KROLOGO.WAD\*.BGD)
Used by Tiny Tank (MagDemo23: TINYTANK\TMD05.DSK\*.BGD, with 8bit entries).

BGD16: Inofficial BGD hack with 16bit entries (R-Types)
This is an inofficial hack used by R-Types, the game does use both the official BGD and inofficial BGD16 format. Apparently invented to support bigger BG Map Widths for huge sidescrolling game maps.
  000h 1   File ID (23h)        ;\same as in official BGD version
  001h 1   Version (0)          ;/
  002h 2   Flag (bit15=WithAttr, bit14=AttrDataSize:0=8bit,1=16bit, bit13-0=0)
  004h 2   BG Map Width  (in cell units) (W)                    ;<-- 16bit!
  006h 2   BG Map Height (in cell units) (H)                    ;<-- 16bit!
  008h 2   Cell Width    (in pixels)                            ;<-- 16bit!
  00Ah 2   Cell Height   (in pixels)                            ;<-- 16bit!
  00Ch ..  BG Map Data[W*H] (16bit cell numbers)
  ...  ..  BG Map Attr[W*H] (0bit/8bit/16bit user data? depending on Flag)
  ...  ..  FFh-padding (in case being stored in R-Types' DOT1 archives)
Used by R-Types (CG.1\file3Ch\file00h, inofficial BGD16 format)

TSQ: Animation Time Sequence
  000h 1   File ID (24h)
  001h 1   Version (1)
  002h 2   Number of Sequence data entries (N)
  004h N*8 Sequence Data (64bit entries)
Sequence Data:
  0-15  Sprite Group Number to be displayed
  16-23 Display Time
  24-27 Unused
  28-31 Attribute (user defined) (only in Version>=1)
  32-47 Hotspot X Coordinate
  48-63 Hotspot Y Coordinate
There aren't any known games using .TSQ files.

ANM: Animation Information
  000h 1    File ID (21h)
  001h 1    Version (3=normal) (but see below notes on older versions)
  002h 2    Flag (bit0-1=TPF, bit2-11=0, bit12-15=CLT)
             0-1   TPF PixFmt (0=4bpp, 1=8bpp, 2/3=Reserved)   ;version>=2 only
             2-11  -   Reserved (0)
             12-15 CLT Number of CLUT Groups, for color animation
  004h 2    Number of Sprites Groups
  006h 2    Number of Sequences (N) (can be 0=None)
  008h N*8  Sequence(s) (64bit per entry)  ;Num=[004h]
  ...  ..   Sprite Group(s)                ;Num=[006h]
  ...  ..   CLUT Group(s)                  ;Num=[002h].bit12-15
Sequence entries:
  000h 2  Sprite Group Number to be displayed (range 0..AnimHdr[004h]-1)
  002h 1  Display Time (can be 00h or 0Ah or whatever)
  003h 1  Attribute (bit0-3=Unused/Zero, bit4-7=User defined)  ;version>=3 only
  004h 2  Hotspot X Coordinate (usually 0, or maybe can be +/-NN ?)
  006h 2  Hotspot Y Coordinate (usually 0, or maybe can be +/-NN ?)
Sprite Group entries:
 Each "Group" seems to represent one animation frame.
 Each "Group" can contain one or more sprites (aka metatiles).
 Below stuff is "4+N*14h" bytes, that seems to repeat "AnmHeader[004h] times"
 XXX... actually below can be "4+N*10h" or "4+N*14h" bytes
 XXX... so, maybe maybe some entries like width/height are optional?
  000h 4     Number of Sprites in this Sprite Group ("sprites per metatile"?)
  004h 14h*N Sprite(s) (see below)
 Sprites:
  000h 1   Tex Coord X (8bit)
  001h 1   Tex Coord Y (8bit)
  002h 1   Offset X from Hotspot within frame (maybe vertex x ?)
  003h 1   Offset Y from Hotspot within frame (maybe vertex y ?)
  004h 2   CBA Clut Base (bit0-5=ClutX, Bit6-14=ClutY, bit15=SemiTransp)
  006h 2   FLAGs (bit0-4, bit5-6, bit7-8, bit9, bit10, bit11, bit12-15)
            0-4   TPN Texture Page Number
            5-6   ABR Semi-Transparency Rate
            7-8   TPF Pixel depth (0=4bpp, 1=8bpp, 2=16bpp)
            9     -   Reserved
            10    RSZ Scaling  (0=No, 1=Scaled)
            11    ROT Rotation (0=No, 1=Rotated)
            12-15 THW Texture Width/Height div8 (0=Other custom width/height)
  008h (2) Texture Width    "of optional size" (uh?)  ;\only present if
  00Ah (2) Texture Height   "of optional size" (uh?)  ;/FLAGs.bit12-15=0 ?)
  00Ch 2   Angle of Rotation (in what units?)
  00Eh 2   Sprite Editor info (bit0-7=Zero, bit8-13=ClutNo, bit14-15=TimBank)
  010h 2   Scaling X (for Vertex?) (as whatever fixed point number) (eg. 1000h)
  012h 2   Scaling Y (for Vertex?) (as whatever fixed point number) (eg. 1000h)
CLUT Group entries:
  000h 4  CLUT size in bytes (Width*Height*2+0Ch)
  004h 2  Clut X Coordinate
  006h 2  Clut Y Coordinate
  008h 2  Clut Width
  00Ah 2  Clut Height
  00Ch .. CLUT entries (16bit per entry, Width*Height*2 bytes)
Note: ALICE.PAC\MENU.PAC\CON00.ANM has NumSequences=0 and NumSpriteGroups=2Dh (unknown if/how that is animated, maybe it has 2Dh static groups? or the groups are played in order 0..2Ch with display time 1 frame each?).
Used by Alice in Cyberland (ALICE.PAC\*.ANM) (ANM v3)
Unknown if there are any other games are using that format.

SDF: Sprite Editor Project File
This is an ASCII text file for "artist boards" with following entries:
  TIM0 file0.tim             ;\
  TIM1 file1.pxl file1.clt   ; four TIM banks (with TIM or PXL/CLT files)
  TIM2                       ; (or no filename for empty banks)
  TIM3                       ;/
  CEL0 file0.cel             ;-one CEL (with CEL, or no filename if none)
  MAP0 file0.bgd             ;\
  MAP1 file1.bgd             ; four BG MAP banks (with BGD filenames)
  MAP2                       ; (or no filename for empty banks)
  MAP3                       ;/
  ANM0 file0.anm             ;-one ANM (with ANM, or no filename if none)
  DISPLAY n       ;0-3=256/320/512/640x240, 4-7=256/320/512/640x480
  COLOR n         ;0=4bpp, 1=8bpp  ;docs are unclear, is it COLORn or COLOR n?
  ADDR0 texX texY clutX clutY numColorSets ;\
  ADDR1 texX texY clutX clutY numColorSets ; four texture/palette offsets
  ADDR2 texX texY clutX clutY numColorSets ; for the corresponding TIM banks
  ADDR3 texX texY clutX clutY numColorSets ;/ (or whatever for empty banks?)


CDROM File Video 3D Graphics TMD/PMD/TOD/HMD/RSD (Sony)

 ____________________________________ TMD _____________________________________

TMD - Modeling Data for OS Library
  000h 4     ID (00000041h)
  004h 4     Flags (bit0=FIXP, bit1-31=Reserved/zero)
  008h 4     Number of Objects (N)     ;"integral value" uh?
  00Ch N*1Ch Object List (1Ch-byte per entry)
  ...  ..    Data (Vertices, Normals, Primitives)
Object List entries:
  000h 4    Start address of a Vertex     ;\Address values depend on the
  004h 4    Number of Vertices            ; file header's FIXP flag:
  008h 4    Start address of a Normal     ;  FIXP=0 Addr from begin of Object
  00Ch 4    Number of Normals             ;  FIXP=0 Addr from begin of TMD File
  010h 4    Start address of a Primitive  ;
  014h 4    Number of Primitives          ;/
  018h 4    Scale (signed shift value, Pos=SHL, Neg=SHR) (not used by LIBGS)
Vertex entries (8-byte):
  000h 2    Vertex X (signed 16bit)
  002h 2    Vertex Y (signed 16bit)
  004h 2    Vertex Z (signed 16bit)
  006h 2    Unused
Normal entries (8-byte) (if any, needed only for computing light directions):
  000h 2    Normal X (fixed point 1.3.12)
  002h 2    Normal Y (fixed point 1.3.12)
  004h 2    Normal Z (fixed point 1.3.12)
  006h 2    Unused
Primitive entries (variable length):
  000h 1    Output Size/4 of the GPU command (after GTE conversion)
  001h 1    Input Size/4 of the Packet Data in the TMD file
  002h 1    Flag
              0   Light source calculation (0=On, 1=Off)
              1   Clip Back (0=Clip, 1=Don't clip) (for Polygons only)
              2   Shading (0=Flat, 1=Gouraud)
                   (Valid only for the polygon not textured,
                   subjected to light source calculation)
              3-7 Reserved (0)
  003h 1    Mode (20h..7Fh) (same as GP0(20h..7Fh) command value in packet)
  004h ..   Packet Data
Packet Data (for Polygons)
  000h 4   GPU Command+Color for that packet (CcBbGgRrh), see GP0(20h..3Fh)
  ... (4)  Texcoord1+Palette (ClutYyXxh)               ;\
  ... (4)  Texcoord2+Texpage (PageYyXxh)               ; only if Mode.bit2=1
  ... (4)  Texcoord3         (0000YyXxh)               ;
  ... (4)  Texcoord4         (0000YyXxh) ;-quad only   ;/
  ... (4)  Color2 (00BbGgRrh)                          ;\
  ... (4)  Color3 (00BbGgRrh)                          ; only if Flag.bit2=1
  ... (4)  Color4 (00BbGgRrh) ;-quad only              ;/
  ... (2)  Normal1 (index in Normal list?)  ;always, unless Flag.bit0=1
  ...  2   Vertex1 (index in Vertex list?)
  ... (2)  Normal2 (index in Normal list?)             ;-only if Mode.bit4=1
  ...  2   Vertex2 (index in Vertex list?)
  ... (2)  Normal3 (index in Normal list?)             ;-only if Mode.bit4=1
  ...  2   Vertex3 (index in Vertex list?)
  ... (2)  Normal4 (index in Normal list?) ;\quad only ;-only if Mode.bit4=1
  ...  2   Vertex4 (index in Vertex list?) ;/
  ... (2)  Unused zeropadding (to 4-byte boundary)
Packet Data (for Lines)
  000h 4   GPU Command+Color for that packet (CcBbGgRrh), see GP0(40h,50h)
  ... (4)  Color2 (00BbGgRrh)                          ;-only if Mode.bit4=1
  ...  2   Vertex1 (index in Vertex list?)
  ...  2   Vertex2 (index in Vertex list?)
Packet Data (for Rectangle/Sprites)
  000h 4   GPU Command+Color for that packet (CcBbGgRrh), see GP0(60h..7Fh)
  ...  ..  Unknown, reportedy "with 3-D coordinates and the drawing
           content is the same as a normal sprite."
Note: Objects should usually contain Primitives and Vertices (and optionally Normals), however, N2O\SHIP.TMD does contain some dummy Objects with Number of Vertices/Normals/Primitives all set to zero.
Used by Playstation Logo (in sector 5..11 on all PSX discs, 3278h bytes)
Used by ...???model???... (MagDemo54: MODEL\*.BIN\*.TMD)
Used by Alice in Cyberland (ALICE.PAC\xxx_TM*.FA\*.TMD)
Used by Armored Core (MagDemo02: AC10DEMP\MS\MENU_TMD.T\*)
Used by Bloody Roar 1 (MagDemo06: CMN\EFFECT.DAT\0005h)
Used by Deception III Dark Delusion (MagDemo33: DECEPT3\K3_DAT.BIN\056A,0725\*)
Used by Gundam Battle Assault 2 (DATA\*.PAC\*)
Used by Hear It Now (Playstation Developer's Demo) (*.TMD and FISH.DAT).
Used by Jersey Devil (MagDemo10: JD\*.BZZ\*)
Used by Klonoa (MagDemo08: KLONOA\FILE.IDX\*)
Used by Legend of Dragoon (MagDemo34: LOD\DRAGN0.BIN\16xxh)
Used by Macross VF-X 2 (MagDemo23: VFX2\DATA01\*.TMD)
Used by Madden NFL '98 (MagDemo02: TIBURON\MODEL01.DAT\*)
Used by No One Can Stop Mr. Domino (MagDemo18: DATA\*, .TMD and DOT1\TMD)
Used by O.D.T. (MagDemo17: ODT\*.LNK\*)
Used by Parappa (MagDemo01: PARAPPA\COMPO01.INT\3\*.TMD)
Used by Resident Evil 1 (PSX\ITEM_M1\*.DOR\0001)
Used by Starblade Alpha (FLT\SB2.DAT\* and TEX\SB2.DAT\*)
Used by Tiny Tank (MagDemo23: TINYTANK\TMD*.DSK\*.TMD)
Used by WCW/nWo Thunder (MagDemo19: THUNDER\RING\*.TMD)
Used by Witch of Salzburg (the MODELS\*.MDL\*.TMD)
Used by Scooby Doo and the Cyber Chase (MagDemo54: MODEL\*\*)

 ____________________________________ PMD _____________________________________

PMD - High-Speed Modeling Data
This is about same as TMD, with less features, intended to work fasrer.
  000h 4    ID (00000042h)
  004h 4    Offset to Primitives
  008h 4    Offset to Shared Vertices (or 0=None)
  00Ch 4    Number of Objects
  010h ..   Objects         (4+N*4 bytes each, with offsets to Primitives)
  ...  ..   Primitives
  ...  ..   Shared Vertices (8-bytes each, if any)
Vertex entries (8-byte):
  000h 2    Vertex X (signed 16bit)
  002h 2    Vertex Y (signed 16bit)
  004h 2    Vertex Z (signed 16bit)
  006h 2    Unused
Objects:
  000h 4    Number of Primitives
  004h N*4  Offsets to Primitives ... maybe relative to hdr[004h] ?
Primitives:
  000h 2    Number of Packets
  002h 2    Type flags
             0    Polygon   (0=Triangle, 1=Quadrilateral)
             1    Shading   (0=Flat, 1=Gouraud)           ;uh, with ONE color?
             2    Texture   (0=Texture-On, 1=Texture-Off) ;uh, withoutTexCoord?
             3    Shared    (0=Independent vertex, 1=Shared vertex)
             4    Light source calculation (0=Off, 1=On)  ;uh, withoutNormal?
             5    Clip      (0=Back clip, 1=No back clip)
             6-15 Reserved for system
  004h ...  Packet(s)
Packet entries, when Type.bit3=0 (independent vertex):
  000h 4   GPU Command+Color for that packet (CcBbGgRrh), see GP0(20h..7Fh)
  004h 8   Vertex1 (Xxxxh,Yyyyh,Zzzzh,0000h)
  00Ch 8   Vertex2 (Xxxxh,Yyyyh,Zzzzh,0000h)
  014h 8   Vertex3 (Xxxxh,Yyyyh,Zzzzh,0000h)
  01Ch (8) Vertex4 (Xxxxh,Yyyyh,Zzzzh,0000h) ;<-- only when Type.bit0=1 (quad)
Packet entries, when Type.bit3=1 (shared vertex):
  000h 4   GPU Command+Color for that packet (CcBbGgRrh), see GP0(20h..7Fh)
  004h 4   Offset to Shared Vertex1    ;offsets are
  008h 4   Offset to Shared Vertex2    ;"from the start of a row"
  00Ch 4   Offset to Shared Vertex3    ;aka from "Packet+04h" ?
  010h (4) Offset to Shared Vertex4          ;<-- only when Type.bit0=1(quad)
Unknown if/how Texture/Light is implemented... without TexCoords/Normals?
Unknown if/how Gouraud is implemented... with ONE color and without Normals?
Used only by a few games:
  Cool Boarders 2 (MagDemo02: CB2\DATA3\*.PMD)
  Cardinal Syn (MagDemo03,09: SYN\*\*.WAD\*.PMD)    (4-byte hdr plus PMD file)
  Sesame Streets Sports (MagDemo52: SSS\LV*\*MRG\*) (4-byte hdr plus PMD file)
Unknown if/which other games are using the PMD format.

 ____________________________________ TOD _____________________________________

TOD - Animation Data
  000h 1    ID (50h)
  001h 1    Version (0)
  002h 2    Resolution (time per frame in 60Hz units, can be 0) (60Hz on PAL?)
  004h 4    Number of Frames
  008h ..   Frame1
  ...  ..   Frame2
  ...  ..   Frame3
  ...  ..   etc.
Frames:
  000h 2    Frame Size in words (ie. size/4)
  002h 2    Number of Packets (can be 0=None, ie. do nothing this frame)
  004h 4    Frame Number (increasing 0,1,2,3,..)
  008h ...  Packet(s)
Packet:
  000h 2    Object ID
  002h 1    Type/Flag (bit0-3=Type, bit4-7=Flags)
  003h 1    Packet Size ("in words (4 bytes)")
  004h ...  Packet Data
XXX... in Sony's doc.
Used by Witch of Salzburg (ANIM\ANM0\ANM0.TOD) (oddly with [02h]=0000h)
Used by Parappa (MagDemo01: PARAPPA\COMPO01.INT\3\*.TOD)
Used by Macross VF-X 2 (MagDemo23: VFX2\DATA01\*.TOD and *.TOX)
Used by Alice in Cyberland (ALICE.PAC\xxx_T*.FA\*.TOD)
Unknown if/which other games are using the TOD format.

 ____________________________________ HMD _____________________________________

HMD - Hierarchical 3D Model, Animation and Other Data
  000h 4    ID (00000050h)   ;same as in TOD, which CAN ALSO have MSBs=zero(!)
  004h 4    MAP FLAG (0 or 1, set when mapped via GsMapUnit() function)
  008h 4    Primitive Header Section pointer (whut?)
  00Ch 4    Number of Blocks
  010h 4*N  Pointers to Blocks
  ...       Primitive Header section    (required)
  ...       Coordinate section          (optional)
  ...       Primitive section           (required)
This format is very complicated, see Sony's "File Formats" document for details.
.HMD used by Brunswick Bowling (MagDemo13: THQBOWL\*).
.HMD used by Soul of the Samurai (MagDemo22: RASETSU\0\OPT01T.BIN\0\0\*)
.HMD used by Bloody Roar 2 (MagDemo22: LON\LON*.DAT\*, ST5\ST*.DAT\02h..03h)
.HMD used by Ultimate Fighting Championship (MagDemo38: UFC\CU00.RBB\6Bh..EFh)
Unknown if/which games other are using the HMD format.

 ____________________________________ RSD _____________________________________

RSD Files (RSD,PLY,MAT,GRP,MSH,PVT,COD,MOT,OGP)
RSD files consist of a set of several files (RSD,PLY,MAT,etc). The files contain the "polygon source code" in ASCII text format, generated from Sony's "SCE 3D Graphics Tool". For use on actual hardware, the "RSDLINK" utility can be used to convert them to binary (TMD, PMD, TOD?, HMB?) files.
  RSD Main project file
  PLY Polygon Vertices (Vertices, Normals, Polygons)
  MAT Polygon Material (Color, Blending, Texture)
  GRP Polygon Grouping
  MSH Polygon Linking                   ;\
  PVT Pivot Rotation center offsets     ; New Extended
  COD Vertex Coordinate Attributes      ; (since RSD version 3)
  MOT Animation Information             ;/
  OGP Vertex Object Grouping            ;-Sub-extended
All of the above files are in ASCII text format. Each file is starting with a "@typYYMMDD" string in the first line of the file, eg. "@RSD970401" for RSD version 3. Vertices are defined as floating point values (as ASCII strings).
There's more info in Sony's "File Formats" document, but the RSD stuff isn't used on retail discs. Except:
  RSD/GRP/MAT/PLY (and DXF=whatever?) used on Yaroze disc (DTL-S3035)


CDROM File Video STR Streaming and BS Picture Compression (Sony)

STR Files (movie streams)
CDROM File Video Streaming STR (Sony)
CDROM File Video Streaming STR Variants
CDROM File Video Streaming Framerate
CDROM File Video Streaming Audio
CDROM File Video Streaming Chunk-based formats
CDROM File Video Streaming Mis-mastered files
Apart from the 20h-byte STR headers, movies basically consist of a series of BS files (see below).

BS Files (Huffman compressed MDEC codes)
BS stands for bitstream, which might refer to the use in STR files, or to the Huffman bitstreams.
CDROM File Video BS Compression Versions
CDROM File Video BS Compression Headers
The header is followed by the bitstream...
  v1/v2/v3/ea/iki --> first bit in bit15 of first halfword (good for psx)
  v0              --> first bit in bit7 of first byte (not so good for psx)
  (to use the same decoder for all version: swap each 2 bytes in v0)
For each block, the bitstream contains one DC value, up to 63 AC values, terminated by EOB (end of block).
CDROM File Video BS Compression DC Values
CDROM File Video BS Compression AC Values
Apart from being used in STR movies, BS can be also used to store single pictures:
CDROM File Video BS Picture Files

Wacwac (similar as BS, but with completely different Huffman codes)
CDROM File Video Wacwac MDEC Streams

Credits
Thanks to Michael Sabin for info on various STR and BS variants:
https://github.com/m35/jpsxdec/


CDROM File Video Streaming STR (Sony)

.STR Sectors (with 20h-byte headers) (for MDEC Movies, or User data)
  000h 2    StStatus (0160h) (RV6Rh; R=Reserved=0, V=Version=1, 6=Fixed ID)
  002h 2    StType (0000h..7FFFh=User Defined, 8000h..FFFFh=System; 8001h=MDEC)
  004h 2    StSectorOffset (Sector number in the frame, 0=First)
  006h 2    StSectorSize   (Number of sectors in the frame) (eg. 4 or 5)
  008h 4    StFrameNo      (Frame number, 1=First) (except Viewpoint=0)
  00Ch 4    StFrameSize    (in bytes, in this frame, excluding headers/padding)
 When StType=0000h..7FFFh:
  010h 10h  StUser         (user defined data)
  020h 7E0h User data      (more user defined data)
 When StType=8001h=MDEC (the only system defined type) (with StStatus=0160h):
  010h 2    StMovieWidth                  (eg. 0140h)
  012h 2    StMovieHeight                 (eg. 00F0h)
  014h 4    StHeadM (reserved for system) (eg. 38000720h) ;\same as [020h-027h]
  018h 4    StHeadV (reserved for system) (eg. 00020001h) ;/from 1st STR sector
  01Ch 4    Unspecified                   (eg. 00000000h) (except Viewpoint<>0)
  020h 7E0h Data (in BS format) (or padding, when image is smaller than frame)
The default file extension .STR is used by various games (though some games use other extensions, the .FMV files in Tomb Raider do also contain standard 20h-byte .STR sector headers).

Video Frames
The video frames consist of BS compressed images (that is, all sectors have STR headers at 000h..01Fh, and the first sector of each frame does additionally contain a standard BS fileheader at offset 020h..027h).
  See "CDROM File Video BS Compression" chapters
Less common, there is also a format for streaming polygon animations instead of BS compressed bitmaps:
CDROM File Video Polygon Streaming

STR Resolution
The Width/Height entries are almost always multiples of 16 pixels. But there are a few exceptions:
  Height=260 (104h) in Star Wars Rebel Assault II, NTSC (S1\L01_PLAY.STR)
  Height=200 (0C8h) in Perfect Assassin (DATA.JFS\CDV\*.STR)
  Height=40  (028h) in Gran Turismo 1 (TITLE.DAT\*, MagDemo10 and MagDemo15)
  Width=232  (0E8h) in Gran Turismo 1 (TITLE.DAT\*, MagDemo10 only)
For such videos, the width/height of MDEC decompression buffer in RAM must be rounded up to multiples of 16 pixels (and the decompressed picture should be cropped to the STR header width/height before forwarding it to VRAM).
Note: The extra scanlines are usually padded with the bottom-most scanline (except, Gran Turismo 1 has gray-padding in lower/right pixels). Ideally, one would repeat the bottom-most pixels in zigzag order.

Subtitles
Metal Gear Solid MGS\ZMOVIE.STR contains subtitles as text strings: The first sector of the .STR file is something custom (without STR header), the remaining movie consists of STR sectors with StType=0001h for subtitles and StType=8001h for picture frames.
Unknown if other games are using the same method, or other methods.
Obviously, subtitles could be also displayed as part of the compressed image, but text strings are much smaller, have better quality, and would also allow to support multiple languages.


CDROM File Video Streaming STR Variants

STR ID Values
  2-byte  0160h         ;Standard STR header
  1-byte  01h           ;Ace Combat 3 Electrosphere
  4-byte  "SMJ",01h     ;Final Fantasy 8, Video
  4-byte  "SMN",01h     ;Final Fantasy 8, Audio/left
  4-byte  "SMR",01h     ;Final Fantasy 8, Audio/righ
  4-byte  0000000xh     ;Judge Dredd
  4-byte  DDCCBBAAh     ;Crusader: No Remorse, older Electronic Arts
  4-byte  08895574h     ;Chunk header in 1st sector only, Best Sports (demo)
  4-byte  "VLC0"        ;Chunk header in 1st sector only, newer Electronic Arts
  4-byte  "VMNK"        ;Chunk header in 1st sector only, Policenauts
  4-byte  01h,"XSP"     ;Sentient header in 1st sector only
  N-byre  zero(es)      ;Polygons? (in last 150Mbyte of PANEKIT.STR)

STR Type values (for videos that do have STR ID=0160h):
The official definition from Sony's File Formats document is as so;
  0000h..7FFFh=User Defined
  8000h..FFFFh=System (with 8001h=MDEC being the only officially defined type)
In practice, the following values are used (of which, 8001h is most common).
  0000h=Polygon Video, Wacwac as Polygon Stream
  0000h=Polygon Video?, Army Men Air Attack 2 (MagDemo40: AMAA2\*.PMB)
  0000h=MDEC Video, Alice in Cyberland
  0001h=MDEC Video, Ridge Racer Type 4 (PAL version, 320x176 pix)
  0001h=Whatever extra data for XA-ADPCM streams (Bits Laboratory games)
  0001h=Whatever non-audio waverform? (3D Baseball)
  0001h=Subtitles, Metal Gear Solid MGS\ZMOVIE.STR
  0002h=Software-rendered video (without using MDEC/GTE) (Cyberia)
  0002h=MDEC Video, Wacwac with IntroTableSet
  0003h=MDEC Video, Wacwac with EndingTableSet
  0004h=MDEC Video, Final Fantasy 9 (MODE2/FORM2)
  0008h=SPU-ADPCM, AKAO audio (Final Fantasy 9)
  0000h=SPU-ADPCM, AKAO audio (Chrono Cross Disc 1, Legend of Mana)
  0001h=SPU-ADPCM, AKAO audio (Chrono Cross Disc 1, Legend of Mana)
  0100h=SPU-ADPCM, AKAO audio (Chrono Cross Disc 2)
  0101h=SPU-ADPCM, AKAO audio (Chrono Cross Disc 2)
  0000h=Whatever special, channel 0 header (Nightmare Project: Yakata)
  0400h=Whatever special, channel 1 header (Nightmare Project: Yakata)
  0001h=Whatever special, channel 0 data   (Nightmare Project: Yakata)
  0401h=Whatever special, channel 1 data   (Nightmare Project: Yakata)
  5349h=MDEC Video, Gran Turismo 1 and 2 (with BS iki)
  0078h=MDEC Ending Dummy  (Mat Hoffman's Pro BMX (MagDemo48: MHPB\SHORT.STR)
  5673h=MDEC Leading Dummy (Mat Hoffman's Pro BMX (MagDemo48: MHPB\SHORT.STR)
  8001h=MDEC Video, Standard MDEC (most common type value)
  8001h=Polygon Video (Ape Escape) (same ID as standard MDEC)
  8001h=Eagle One: Harrier Attack various types (MDEC and other data)
  8001h=Dance series SPU-ADPCM streaming (with STR[1Ch]=DDCCBBAAh)
  8101h=MDEC Video, Standard MDEC plus bit8=FlagDisc2 (Chrono Cross Disc 2)

 ______________________________________________________________________________

Leading XA-ADPCM
Most movies start with STR video sectors. But a few games start with XA-ADPCM:
  Ace Combat 3 Electrosphere (*.SPB)
  Alice in Cyber Land (*.STR)
  Judge Dredd (*.IXA) ;and very small 4-byte STR header
  ReBoot (MOVIES\*.WXA)
Also, Aconcagua (Wacwac) has XA-ADPCM before Video (but, yet before that, it has 150 leading zerofilled sectors).
Also, Porsche Challenge (SRC\MENU\STREAM\*.STR) starts with corrupted Subheaders, which may appear as leading XA-ADPCM (depending on how to interprete the corrupted header bits).

Leading SPU-ADPCM
  EA videos     ;\
  Crusader      ; chunks
  Policenauts   ;/
  AKAO videos

Metal Gear Solid (MGS\ZMOVIE.STR, 47Mbyte)
This is an archive dedicated to STR movies (with number of frames instead of filesize entries). Metal Gear Solid does also have cut-scenes with polygon animations (but those are supposedly stored elsewhere?).
  000h 4    Number of entries (4)
  004h N*8  File List
  ...  ..   Zerofilled
File List entries:
  000h 2    Unknown... decreasing values?
  002h 2    Number of Frames (same as last frame number in STR header)
  004h 4    Offset/800h (to begin of STR movie, with subtiltes in 1st sector)
Disc 1 has four movies: The first one has a bit more than 12.5 sectors/frame, the other three have a bit more than 10 sectors/frame (eg. detecting the archive format could be done checking for entries wirh 8..16 sectors/frame).
Example, from Disc 1:
  04 00 00 00
  ED 97 9E 01  01 00 00 00 ;num sectors=1439h ;div19Eh=C.81h ;97EDh-6137h=36B6h
  37 61 86 01  3A 14 00 00 ;num sectors=0F41h ;div186h=A.03h ;6137h-38D0h=2867h
  D0 38 10 03  7B 23 00 00 ;num sectors=1EA1h ;div310h=A.00h ;38D0h-2302h=15CEh
  02 23 73 02  1C 42 00 00 ;num sectors=1881h ;div273h=A.01h ;2302h-0000h=2302h
The files in the ZMOVIE.STR archive start with subtitles in 1st sector (this is usually/always only one single sector for the whole movie):
  000h 2    STR ID   (0160h)                                       ;\
  002h 2    STR Type (0001h=Subtitles)                             ;
  004h 2    Sector number within Subtitles (0)                     ; STR
  006h 2    Number of Sectors with Subtitles (1)                   ; header
  008h 4    Frame number (1)                                       ;
  00Ch 4    Data size counted in 4-byte units (same as [02Ch]/4)   ;
  010h 10h  Zerofilled                                             ;/
  020h 4    Unknown (2)                                            ;\
  024h 4    Unknown (1AAh, 141h, or 204h)                          ; Data
  028h 4    Unknown (00100000h)                                    ; part
  02Ch 4    Size of all Subtitle entries in bytes plus 10h         ;
  030h ..   Subtitle entries                                       ;/
  ...  ..   Zeropadding to 800h-byte boundary                      ;-padding
Subtitle entries:
  000h 4    Offset from current subtitle to next subtitle (or 0=Last subtitle)
  004h 4    First Frame number when to display the subtitle?
  008h 4    Number of frames when to display the subtitle?
  00Ch 4    Zero
  010h ..   Text string, terminated by 00h
  ...  ..   Zeropadding to 4-byte boundary
The text strings are ASCII, with special 2-byte codes (80h,7Bh=Linebreak, 1Fh,20h=u-Umlaut, etc).

 ________________________ Customized STR Video Headers ________________________

Viewpoint (with slightly modified STR header)
  008h 4    Frame number (0=First)                      ;<-- instead of 1=First
  01Ch 2    Unknown (always D351h)                      ;<-- instead of zero
  01Eh 2    Number of Frames in this STR file           ;<-- instead of zero

Capcom games
Resident Evil 2 (ZMOVIE\*.STR, PL0\ZMOVIE\*.STR)
Super Puzzle Fighter II Turbo (STR/CAPCOM15.STR)
  01Ch 4    Sector number of 1st sector of current frame  ;<-- instead of zero

Chrono Cross Disc 2 Video
Chrono Cross Disc 1 does have normal STR headers, but Disc 2 has Type.bit8 toggled:
  002h 2    STR Type (8101h=Disc 2)                       ;<-- instead of 8001h
And, the Chrono Cross "final movie" does reportedly have "additional properties". Unknown, what that means, it does probably refer to the last movie on Chrono Cross Disc 2, which is quite huge (90Mbyte), and has lower resolution (160x112), and might have whatever "additional properties"?

Need for Speed 3
Need for Speed 3 Hot Pursuit (MOVIES\*.XA, contains videos, not raw XA-ADPCM)
Jackie Chan Stuntmaster (FE\MOVIES\*.STR)
With slightly modified STR headers:
  014h 4    Number of Frames (..excluding last some frames?) ;-instead BS[0..3]
  018h 4    Unlike the above modified entry, this is normal  ;-copy of BS[4..7]

ReBoot (MOVIES\*.WXA)
This has leading XA-ADPCM, and customized STR header:
  014h 2    Type (0000h=Normal, 01FFh=Empty frames at end of video)
  016h 2    Number of Frames (excluding empty ones at end of video)
  018h 8    Zerofilled

Gran Turismo 1 (230Mbyte STREAM.DAT) and Gran Turismo 2 (330Mbyte STREAM.DAT)
These two games use BS iki format, and (unlike other iki videos) also special STR headers:
  002h 2    STR Type (5349h) ("IS")         ;-special (instead 8001h)
  010h 2    Total number of frames in video ;-special (instead width)
  012h 2    Flags (bit15=1st, bit14=last)   ;-special (instead height)
  014h 8    Zero                            ;-special (instead BS header copy)
  020h 7E0h Data (in BS iki format)         ;-BS iki header (with width/height)
Caution: The STR header values aren't constant throughout the frame:
  Namely, flags in [012h] are toggled on first/last sector of each frame,
  and of course [04h] does also increase per sector.

PGA Tour 96, 97, 98 (VIDEO\..\*.XA and ZZBUFFER\*.STR)
Used by all movies in PGA Tour 96, 97 (and for the ZZBUFFER\BIGSPY.STR dummy padding movie in PGA Tour 98).
The videos have normal BS v2 data, but the Frame Size entry is 8 smaller than usually. As workaround, always load [0Ch]+8 for all movies with standard STR headers (unless that would exceed [06h]*7E0h).
  00Ch 4    Frame Size-8 (ie. excluding 8-byte BS header)  ;instead of Size-0
The padding videos in ZZBUFFER folder have additional oddities in STR header:
  ZZBUFFER\SPY256.STR    [14h..1Fh]=normal copy of 8-byte BS v2 header and zero
  ZZBUFFER\SPYGLASS.STR  [14h..1Fh]=zerofilled                          ;\BS v1
  ZZBUFFER\SPYTEST.STR   [14h..1Fh]=00 00 10 00 00 00 00 09 00 00 07 EE ;/
  ZZBUFFER\BIGSPY.STR    Used in PGA Tour 98 (instead of above three files)
SPYTEST.STR has nonsense quant values exceeding the 0000h..003Fh range (first frame has quant=00B1h, and later frames go as high as quant=FFxxh, that kind of junk is probably unrelated to BS fraquant). The oddities for SPYTEST.STR do also occur in some frames in PGA Tour 98 BIGSPY.STR. Anyways, those ZZBUFFER files seem to be only unused padding files.

Alice in Cyber Land (*.STR)
Note: First sector contains XA-ADPCM audio (video starts in 2nd sector).
 STR Sector Header:
  002h 2    STR Type (0000h=Alice in Cyber Land video)            ;-special
  008h 4    Frame number (1=First) (bit15 set in last frame, or FFFFh)
  010h 10h  Zerofilled (instead width/height and BS header copy)  ;-special
  020h 7E0h Data (in BS v2 format)
Frames are always 320x240.
The frame number of the last used frame of a movie has the bit15 set. After that last frame, there are some empty frame(s) with frame number FFFFh.
For some reason there are "extra audio sectors in between movies" (uh?).
Many of the movies have a variable frame rate. All movies contain frames sequences that match one of the following frame rates: 7.5 fps, 10 fps, 15 fps, 30 fps.

Encrypted iki (Panekit - Infinitive Crafting Toy Case)
  014h 8    Copy of decrypted BS header (instead of encrypted BS header)

Princess Maker: Yumemiru Yousei (PM3.STR)
Parappa (Japanese Demo version only) (S0/GUIDE.STR)
These files do have BS ID=3000h (except, the first and last some frames have nromal ID=3800h). The STR header is quite normal (apart from reflecting the odd BS ID):
  016h 2    Copy of BS ID, 3000h in most frames (instead of 3800h)
  020h 7E0h Data (in BS format, also with BS ID 3000h, instead of 3800h)

Starblade Alpha and Galaxian 3
These movies have Extra stuff in the data section. The STR header is quite normal (apart from reflecting the Extra stuff):
  00Ch 4    Frame Size in bytes (=size of ExtraHeader + BsData + ExtraData)
  014h 4    Copy of Extra Header        ;instead of BS[0..3]
  018h 4    Copy of BS[0..3]            ;instead of BS[4..7]
  020h 7E0h Data (ExtraHeader + BsData + ExtraData)
The data part looks as so:
  000h 2    Size of BS Data area    (S1)        ;\Extra Header
  002h 2    Size of Extra Data area (S2)        ;/
  004h S1   BS Data (in BS v3 format)           ;-BS Data
  ..   S2   Extra Data (unknown purpose)        ;-Extra Data
Note: Starblade Alpha does use that format for GAMEn.STR and NAME.STR in FLT and TEX folders (the other movies in that game are in normal STR format).

Largo Winch: Commando SAR (FMV\NSPIN_W.RNG)
This is a somewhat "normal" movie, without audio, and with the STR headers moved to the begin of the file:
  000h Nx20h   STR Headers  ;size = filesize/800h*20h
  ...  Nx7E0h  Data         ;size = filesize/800h*7E0h
Note: The movie contains the rotating "W" logo, which is looped in Start screen.

Player Manager (1996, Anco Software) (FILMS\1..3\*.STR)
  006h 2    Number of Sectors in this Frame-1 (8..9 = 9..10 sectors)
  00Ch 4    Frame Size in bytes               (8..9*7E0h = 3F00h or 46E0h)
  010h 2    Bitmap Width                      (always F0h)
  012h 2    Bitmap Width                      (always 50h)
  014h 0Ch  Zerofilled (instead copy of BS header or copy of Extra header)
  020h 7E0h Data (Extra Stuff, BS v2 data, plus Unused stuff)
The data part occupies 9-10 sectors, consisting of:
  0000h Extra Stuff   (7E0h bytes, whatever, often starts with 00,FF,00,FF,..)
  07E0h BS v2 data    (3720h or 3F00h bytes, including FFh-padding)
  ...   Unused Sector (7E0h bytes, same as in previous frame or zerofilled)
The compressor tries to match the picture quality to the number of sectors per frame, but it's accidentally leaving the last sector unused:
  For 9 sectors:  Only 1..7 are used, sector 8 is same as in previous frame
  For 10 sectors: Only 1..8 are used, sector 9 is zerofilled
Apart from the odd format in FILMS\1..3\*.STR, the game does also have normal videos in FILMS\*.STR.

Chiisana Kyojin Microman (DAT\STAGE*\*.MV)
The .MV files have 5 sectors/frame: Either 5 video sectors without audio, or 4-5 video sectors plus XA-ADPCM audio (in the latter case, audio is in each 8th sector (07h,0Fh,17h,1Fh,etc), hence having filesize rounded up to N*8 sectors):
  Filesize = 800h*((NumberOfFrames*5))             ;5 sectors, no xa-adpcm
  Filesize = 800h*((NumberOfFrames*5+7) AND not 7) ;4-5 sectors, plus xa-adpcm
Caution: The STR header values aren't constant throughout the frame:
  Sector 0: [10h] = Number of Frames,   [12h]=Junk
  Sector 1: [10h] = Junk,               [12h]=0
  Sector 2: [10h] = Junk,               [12h]=Junk
  Sector 3: [10h] = Junk,               [12h]=Same as below (Bitmap Height)
 Below ONLY when having 5 sectors per frame:
  Sector 4: [10h] = Bitmap Width (140h) [12h]=Bitmap Height (D0h)
 That is, frames with 4 sectors do NOT have any Bitmap Width entry
 (the duplicated Height entry in sector 3 exists, so one could compute
 Width=NumMacroBlocks*100h/Height, or assume fixed Width=320, Height=208).
The Junk values can be zero, or increase/decrease during the movie, some or all of them seem to be sign-expanded from 12bit (eg. increasing values can wrap from 07xxh to F8xxh).
Apart from the odd DAT\STAGE*\*.MV files, the game does also have .STR files with normal STR headers and more sectors per frame (DAT\STAGE16,21,27\*.STR, DAT\OTHER\*.STR, DAT\OTHER\CM\*.STR, and MAT\DAT\*.STR).

Black Silence padding
Used by Bugriders: The Race of Kings (MOVIE\*XB.STR)
Used by Rugrats Studio Tour (MagDemo32: RUGRATS\DATA\OPEN\*B.STR)
  Each movie file is followed by dummy padding file. For example, in Bugriders:
  MOVIE\*XA.STR  Movie clip             (with correct size, 320x192)
  MOVIE\*XB.STR  Black Silence padding  (wrong size 640x192, should be 320x192)
The names are sorted alphabetically and exist in pairs (eg. CHARMXA.STR and CHARMXB.STR), and the disc sectors are following the same sort order.
The padding files contain only black pixels and silent XA-ADPCM sectors, with following unique STR header entries, notably with wrong Width entry (the MDEC data contains only 320x192 pixels).
  00Ch 4    Frame Size    (087Ch)
  010h 2    Bitmap Width  (wrongly set to 640, should be 320)
  012h 2    Bitmap Height (192)
  014h 2    MDEC Size     (05A0h)
  016h 2    BS ID         (3800h)
  018h 2    BS Quant      (0001h)
  01Ah 2    BS Version    (0002h)
  Filesize is always 44Fh sectors (about 2.2Mbyte per *XB.STR file)
The huge 7 second padding is a very crude way to avoid the next movie to be played when not immediately pausing the CDROM at end of current movie.

Ridge Racer Type 4 (only PAL version) (R4.STR)
The 570Mbyte R4.STR file contains XA-ADPCM in first three quarters, and two STR movies in last quarter:
  1st NTSC/US movie: 320x160 pix, 0F61h frames, 4-5 sectors/frame, normal STR
  1st PAL/EUR movie: 320x176 pix, 0CD0h frames, 5-6 sectors/frame, special STR
  2nd NTSC/US movie: 320x160 pix, 1D6Ah frames, 4-5 sectors/frame, normal STR
  2nd PAL/EUR movie: 320x160 pix, 18B5h frames, 5-6 sectors/frame, normal STR
As seen above, the PAL movies have lower framerate. And, the 1st PAL movie has higher resolution, plus some other customized STR header entries:
  002h 2    STR Type (0001h=Custom, 176pix PAL video)         ;instead of 8001h
  006h 2    Number of Sectors in this Frame (always 5..6)
  00Ch 4    Frame Size (always 2760h or 2F40h, aka 7E0h*5..6)
  012h 2    Bitmap Height (00B0h, aka 176 pixels)             ;instead of 00A0h
  014h 8    Zerofilled                                        ;instead BS[0..7]
  020h 7E0h Data (in BS v3 format, plus FFh-padding)
That is, the special video is standard MDEC, the only problem is detecting it as such (despite of the custom STR Type entry).

Mat Hoffman's Pro BMX (MagDemo48: MHPB\SHORT.STR)
This contains a normal MDEC movie, but with distorted "garbage" in first and last some sectors.
  1st sector          STR Type 5673h (Leading Dummy)                ;\
  2nd sector          STR Type 8001h (distorted/empty MDEC)         ; junk
  3rd..6th sector     STR Type 8001h (distorted/garbage MDEC)       ;/
  7th sector and up   STR Type 8001h (normal MDEC, with odd [01Ch]) ;-movie
  Last 96h sectors    STR Type 0078h (Ending Dummy)                 ;-junk
1st Sector:
  002h 2    STR Type (5673h=Leading Dummy)
  004h 4    Whatever (0004000Ch)
  008h 4    Whatever (0098967Fh)
  00Ch 4    Frame Size (always 100h)
  010h 7F0h EAh-filled
2nd Sector:
  002h 2    STR Type (8001h=Normal MDEC ID, but content is empty)
  004h 4    Whatever (0004000Ch)        ;\
  008h 4    Whatever (0098967Fh)        ; same as in 1st sector
  00Ch 4    Frame Size (always 100h)    ; (but ID at [002h] differes)
  010h 7F0h EAh-filled                  ;/
3rd-6th Sector:
  002h 2    STR Type (8001h=Normal MDEC ID, but content is distorted)
  004h 2    Sector number within current Frame (always 0)
  006h 2    Number of Sectors in this Frame (always 1)
  008h 4    Frame number (increasing, 1..4 for 3rd..6th sector)
  00Ch 4    Frame Size (always 7D0h)
  010h 10h  EAh-filled
  020h 7D0h Unknown (random/garbage?)
  7F0h 10h  EAh-filled
7th Sector and up (almost standard MDEC):
 Caution: The STR header values aren't constant throughout the frame:
 Entry entry [01Ch] is incremented per sector (or wraps to 0 in new section).
  01Ch 4    Increasing sector number (within current movie section or so)
Last 96h Sectors:
  002h 2    STR Type (0078h=Ending Dummy)
  004h 2    Sector number within current Frame (always 0)
  006h 2    Number of Sectors in this Frame (always 1)
  008h 4    Frame number (increasing, in last 96h sectors)
  00Ch 4    Frame Size (always 20h)
  010h 2    Bitmap Width  (always 40h)
  012h 2    Bitmap Height (always 40h)
  014h 7ECh Zerofilled

Final Fantasy VII (FF7) (MOVIE\*.MOV and MOVIE\*.STR)
These movies have Extra stuff in the data section. The STR header is quite normal (apart from reflecting the Extra stuff):
  00Ch 4    Frame Size in bytes (including 28h-byte extra stuff)
  014h 8    Copy of Extra data [0..7]           :-instead of BS header[0..7]
  020h 7E0h Data (ExtraData + BsData)
The data part looks as so:
  000h 28h  Extra data (unknown purpose, reportedly "Camera data" ... whut?)
  028h ..   BS Data (in BS v1 format)

Final Fantasy IX (FF9) (*.STR and *.MBG)
There are several customized STR header entries:
  002h 2    STR Type (0004h=FF9/Video)                      ;instead of 8001h
  004h 2    Sector number within current Frame (02h..num-1) (2..9 for video)
  006h 2    Total number of Audio+Video sectors in this frame (always 0Ah)
  00Ch 4    Frame Size/4 (of BS data, excluding MBG extra)  ;instead of Size/1
  014h 8    Copy of BS[0..7] from 8th video sector          ;instead 1st sector
  01Ch 2    Usually 0000h (or 0004h in some MBG sectors)    ;inszead of 0000h
  01Eh 2    Usually 0000h (or 3xxxh in some MBG sectors)    ;inszead of 0000h
  020h 8F4h Data (in BS v2 format, plus MBG extra data, if any)
Caution: The STR header values aren't constant throughout the frame:
  Namely, entry [1Ch..1Fh]=nonzero occurs only on the sector that does contain
  the end of BS data (=and begin of MBG extra data), and of course [04h] does
  also increase per sector.
Sector ordering has BS data snippets arranged backwards, for example, if BS data does occupy 2.5 sectors:
  [04h]=00h-01h 1st-2nd audio sector, SPU-ADPCM (see Audio streaming chapter)
  [04h]=02h-06h 1st-5th video sector, unused, [020h..913h] is FFh-filled
  [04h]=07h     6th video sector, contains end of BS data and MBG extra, if any
  [04h]=08h     7th video sector, contains middle of BS data
  [04h]=09h     8th video sector, contains begin of BS data
Sector type/size, very unusually with FORM2 sectors:
  Audio sectors are MODE2/FORM1 (800h bytes, with error correction)
  Video sectors are MODE2/FORM2 (914h bytes, without error correction)
Huffman codes are standard BS v2, with one odd exception: MDEC 001Eh/03E1h (run=0, level=+/-1Eh) should be usually encoded as 15bit Huffman codes, FF9 is doing that for 001Eh, but 03E1h is instead encoded as 22bit Escape code:
  000000000100010         MDEC=001Eh (run=0, level=+1Eh) ;-normal (used)
  000000000100011         MDEC=03E1h (run=0, level=-1Eh) ;-normal (not used)
  0000010000001111100001  MDEC=03E1h (run=0, level=-1Eh) ;-escape (used)
There are two movie variants: *.STR and *.MBG. Most MBG files (except SEQ02\MBG102.MBG) contain extra MBG info in [01Ch..01Fh] and extra MBG data appended after the BS data. If present, the appended MBG data is often/always(?) just these 28h-bytes:
  FF FF FF FF FE FF FE 41 AD AD AD AD AD AD AD AD
  AD AD AD AD AD AD AD AD AD AD AD AD AD AD AD AD
  AD AD AD AD AD AD AD AD
  (followed by FF's, which might be padding, or part of the extra data)
Unknown if some sectors contain more/other MBG data, perhaps compressed BG pixel-depth values for drawing OBJs in front/behind BG pixels?

 _______________________ Non-standard STR Video Headers _______________________

Final Fantasy VIII (FF8)
Video frames are always 320x224. The video frames are preceeded by two SPU-ADPCM audio sectors.
  000h 4    ID "SMJ",01h=Video
  004h 1    Sector number within current Frame (02h..num-1) (2..9 for video)
  005h 1    Total number of Audio+Video sectors in this frame, minus 1 (9)
  006h 2    Frame number (0=First)
  008h 7F8h Data (in BS v2 format)

Ace Combat 3 Electrosphere (in 520Mbyte ACE.SPH/SPB archive)
The videos start with one XA-ADPCM sector, followed by the first Video sector.
 STR Sector Header:
  000h 1    Always 01h
  001h 1    Sector number within current Frame (00h..num-1) (8bit)
  002h 2    Number of Sectors in this Frame
  004h 2    Unknown (1 or 3)
  006h 2    Frame number (decreasing, 0=Last)
  008h 2    Bitmap Width in pixels    ;\130hxE0h or 140hxB0h or 80hx60h
  00Ah 2    Bitmap Height in pixels   ;/
  00Ch 4    Zero
  010h 2    Zero, or decreasing timer (decreases approx every 2 sectors)
  012h 2    Zero, or decreasing timer (decreases approx every 1 sector)
  014h 3    Zero
  017h 1    Zero, or increases with step 2 every some hundred sectors
  018h 2    Zero, or Timer (increments when [1Ah] wraps from 04h to 01h)
  01Ah 1    Zero, or Timer (increments when [1Bh] wraps from 5Fh to 00h]
  01Bh 1    Zero, or Timer (increments approx every 1 sector)
  01Ch 2    Zero, or Whatever (changes to whatever every many hundred sectors)
  01Eh 2    Zero, or 0204h
  020h 7E0h Data (in BS v3 format)
Caution: The STR header values aren't constant throughout the frame:
  Namely, entry [10h..1Fh] can change within the frame (happens in japanese
  version), and of course [01h] does also increase per sector.
The Japanese version may be the only game that has two streaming videos running in parallel on different channels.
That means, non-japanese version is different...?

Judge Dredd (1998, Gremlin) (CUTS\*.IXA and LEVELS\*\*.IXA)
This is a lightgun-game with "interactive movies". The gameplay consists of running on a fixed path through a scene with pre-recorded background graphics, the only player interaction is aiming the gun at other people that show up in that movie scene. There are two movie types:
  LEVELS\*\*.IXA  - Interactive gameplay movies
  CUTS\*.IXA      - Non-interactive cut-scene movies
Both CUTS and LEVELS have unusually small 4-byte STR headers:
  000h 4    Sector number within current Frame (LEVELS=0..8, or CUTS=0..9)
  004h 7FCh Data (see below)
Data for CUTS is 320x240pix (10 sectors per frame):
  Note: CUTS videos have 2 leading XA-ADPCM sectors
  000h ..   BS Data (in BS v2/v3 format)                        ;-BS picture
Data for LEVELS is 320x352pix plus extra stuff (9 sectors per frame):
  Note: LEVELS videos have 1 leading XA-ADPCM sector
  000h 4    Offset to BS Data (always 28h)                      ;\
  004h 4*6  Offsets to Extra Stuff 1..6                         ; extra header
  01Ch 0Ch  Zerofilled                                          ;/
  028h ..   BS Data (in BS v2/v3 format)                        ;-BS picture
  ...  ..   Extra Stuff 1..6                                    ;-extra data
The unusual 320x352pix resoltution contains a 320x240pix BG image, with additional 320x112pix texture data appended at the bottom.
Extra Stuff 1..6 does supposedly contain info for animating enemies and/or backgrounds.

______________________________________________________________________________

iki
The .iki video format (found in files with .IKI or .IK2 extension) is used in several games made by Sony. iki movie sectors have some different properties:
  * There are only as many iki video sectors as needed to hold all the
    frame's data. Remaining sectors are null.
  * The first sector's Submode.Channel starts at zero, then increments for
    each sector after that, and resets to zero after an audio sector.
  * IK2 videos can also have variable frame rates that are very inconsistent.


CDROM File Video Streaming Framerate

According to Sony, BS encoded 320x240pix videos can be played at 30fps (with cdrom running at double speed).

STR Frame Rate
As a general rule, the frame rate is implied in CDROM rotation speed (150 or 75 sectors per second, minus the audio sectors, divided by the number of sectors per video frame).

Fixed/Variable Framerates
The frame can drop on video frames that contain more sectors than usually. Video frames that require fewer sectors than often padded with zerofilled sectors. However, some games don't have that padding, so they could end up reeceiving up to 150 single-sector frames per second; the actual framerate is supposedly slowed down to 60Hz or less via Vblank timer (and with the CDROM reading getting paused when the read-ahead buffer gets full).

Audio Samplerate
XA-ADPCM audio contains samplerate info (in the FORM2 subheader), the samplerate versus amount of audio sectors can be used to compute the CDROM rotation speed.
There are two exceptions: Some movies don't have any audio at all, and some movies use SPU-ADPCM instead of XA-ADPCM. In the latter case, the SPU Pitch (samplerate) may (or may not) be found somewhere in the audio sector headers.

CDROM Rotation speed
As said above, the speed can be often detected via audio sample rate. Otherwise, the general rule is that most PSX games are used 2x speed (150 sectors/second). But, there are a few games with 1x speed (see below).

CDROM Single speed (75 sectors/frame)
Here are probably most of the USA games with videos at 1x speed.
  007 - The World Is Not Enough
  1Xtreme
  Arcade Party Pak
  Atari Anniversary Edition Redux
  Blast Radius
  Blue's Clues - Blue's Big Musical
  Chessmaster II
  Chronicles of the Sword
  Civilization II
  Colin McRae Rally
  Creatures - Raised in Space
  Cyberia
  Demolition Racer
  Dune 2000
  ESPN Extreme Games
  FIFA Soccer 97
  Fade to Black
  Family Connection - A Guide to Lightspan
  Fear Effect
  Fox Hunt
  Interactive CD Sampler Volume 1
  Jade Cocoon - Story of the Tamamayu
  Jeopardy! 2nd Edition
  Juggernaut
  Krazy Ivan
  MTV Sports - Skateboarding featuring Andy Macdonald
  MTV Sports - T.J. Lavin's Ultimate BMX
  Medal of Honor
  Medal of Honor - Underground
  Official U.S. PlayStation Magazine Demo Disc 23
  Planet of the Apes
  PlayStation Underground Number 2
  Shockwave Assault
  Starblade Alpha
  Starwinder - The Ultimate Space Race
  Str.at.e.s. 1 - Match-A-Batch
  Str.at.e.s. 5 - Parallel Lives!
  Str.at.e.s. 7 - Riddle Roundup!
  The X-Files
  Top Gun - Fire at Will!
  Um Jammer Lammy
  Uprising X
  Wheel of Fortune - 2nd Edition
  Williams Arcade's Greatest Hits


CDROM File Video Streaming Audio

Audio Stream
STR movies are usually interleaved with XA-ADPCM sectors (the audio sectors are automatically decoded by the CDROM hardware and consist of raw ADPCM data without STR headers).
CDROM File Audio Streaming XA-ADPCM
However, there are also movies without audio. And a few movies with SPU-ADPCM audio.

SPU-ADPCM in Chunk-based formats
CDROM File Video Streaming Chunk-based formats

SPU-ADPCM in Chrono Cross/Legend of Mana Audio Sector
Chrono Cross Disc 1 (HiddenDirectory\1793h..17A6h)
Chrono Cross Disc 2 (HiddenDirectory\1793h..179Dh)
Legend of Mana (MOVIE\*.STR, except some movies without audio)
  000h 2    STR ID   (0160h)
  002h 2    STR Type (0000h, 0001h, 0100h, or 0101h)
              0000h=Legend of Mana, Audio normal sectors
              0001h=Legend of Mana, Audio sectors near end of movie
              0000h=Chrono Cross Disc 1, Audio.left?
              0001h=Chrono Cross Disc 1, Audio.right?
              0100h=Chrono Cross Disc 2, Audio.left?
              0101h=Chrono Cross Disc 2, Audio.right?
  004h 2    Sector number in Frame (0=Audio.left?, 1=Audio.right?)
  006h 2    Number of Audio sectors in this frame (always 2)
  008h 4    Frame number (1=First)
  00Ch 4    Unused (Chrono: FFh-filled or Mana: 00000FC0h=2x7E0h=Framesize?)
  010h 10h  Unused (Chrono: FFh-filled or Mana: 00h-filled)
  020h 60h  Unused (FFh-filled)
  080h 4    ID "AKAO"
  084h 4    Frame number (0=First)
  088h 8    Unused (zerofilled)
  090h 4    Remaining Time (step 690h) (can get stuck at 0340h or 0B20h at end)
  094h 4    Zero
  098h 4    Unknown (11h)
  09Ch 4    Pitch (1000h=44100Hz)
  0A0h 4    Number of bytes of audio data (always 690h)
  0A4h 2Ch  Unused (zerofilled)
  0D0h 690h Audio  (10h-byte SPU-ADPCM blocks) (1680 bytes)
  760h A0h  Unused (10h-byte SPU-ADPCM blocks with flag=03h and other bytes=0)
Note: The Chrono/Mana STR files start with Audio frames in first sector (except, some Legend of Mana movies don't have any Audio, and do start with Video frames).

SPU-ADPCM in Final Fantasy VIII (FF8)
  000h 4    ID "SMN",01h=Audio/left, "SMR",01h=Audio/right
  004h 1    Sector number in Frame (0=Audio.left, 1=Audio.right)
  005h 1    Total number of Audio+Video sectors in this frame, minus 1 (1 or 9)
  006h 2    Frame number (0=First)
  008h E8h  Unknown (camera data?) (232 bytes)
  0F0h 6    Audio ID (usually "MORIYA", sometimes "SHUN.M")
  0F6h 0Ah  Unknown (10 bytes) (reportedly 10 bytes at offset 250 = FAh ?????)
  100h 4    ID "AKAO"
  104h 4    Frame number (0=First)
  108h 14h  Unknown (20 bytes)
  11Ch 4    Pitch (1000h=44100Hz)
  120h 4    Number of bytes of audio data (always 690h)
  124h 2Ch  Unknown (44 bytes)
  150h 20h  Unknown (32 bytes)
  170h 690h SPU-ADPCM Audio data (690h bytes)
There is one special case on disc 1: a movie with no video. Each 'frame' consists of two sectors: the first is the left audio channel, the second is the right audio channel.

SPU-ADPCM in Final Fantasy IX (FF9) (*.STR and *.MBG)
The FF9 audio sectors are normal MODE2/FORM1 sectors (unlike the FF9 video sectors, which are MODE2/FORM2).
  000h 2    STR ID   (0160h)
  002h 2    STR Type (0008h=FF9/Audio)
  004h 2    Sector number in Frame (0=Audio.left, 1=Audio.right)
  006h 2    Total number of Audio+Video sectors in this frame (always 0Ah)
  008h 4    Frame number (1=First)
  00Ch 4    Zero
  010h 1    Audio flag? (00h=No Audio, 01h=Audio)
  011h 4Fh  Zerofilled --- XXX or whatever (when above is 00h)
  060h 4    Number of Frames in this STR file
  064h 1Ch  EEh-filled
 Below 780h bytes are all zerofilled when [10h]=00h (no audio)
 Below 780h bytes are reportedly all ABh-filled "in the last frame of a movie
 on Disc 4" (unknown which movie, and if that occurs in other movies, too)
  080h 4    ID "AKAO"
  084h 4    Frame number (0=First)
  088h 14h  Unknown (20 bytes)
  09Ch 4    Pitch (116Ah=48000Hz) (or 1000h=44100Hz in final movie)
  0A0h 4    Number of bytes of audio data (0, 720h, 730h, or 690h=final movie)
  0A4h 2Ch  Unknown (44 bytes)
  0D0h 730h SPU-ADPCM audio (plus leftover/padding when less than 730h bytes)

Dance series SPU-ADPCM streaming (bigben interactive, DATA.PAK\stream\*.str)
This format is used for raw SPU-ADPCM streaming (without video).
SLES-04121 Dance: UK
SLES-04161 Dance: UK eXtra TraX
SLES-04129 Dance Europe
SLES-04162 All Music Dance! (Italy)
  000h 2    STR ID   (0160h)
  002h 2    STR Type (8001h, same as MDEC)
  004h 2    Sector number within current Frame (0000h..num-1)
  006h 2    Number of Sectors in this Frame (always 9)
  008h 4    Frame number (0=First)
  00Ch 4    Frame Size in bytes (always 4000h)
  010h 4    Whatever (always 00A000A0h, would be width/height if it were video)
  014h 8    Zerofilled
  01Ch 4    Special ID (always DDCCBBAAh for Dance audio)
  020h 7E0h Data (in SPU-ADPCM format, mono, 22200Hz aka Pitch=07F5h)
Note: Sector 0..8 contain 9*7E0h=46E0h bytes data per frame, but only 4000h bytes are used (the last 6E0h bytes in sector 8 are same as in sector 7).

Raw SPU-ADPCM Streaming
Some games are using raw SPU-ADPCM for streaming. That is, the file is basically a normal .VB file, but it can be dozens of megabytes tall (ie. too large to be loaded into RAM all at once).
  Disney's The Emperor's New Groove (MagDemo39: ENG\STREAM\*.CVS)
  Disney's Aladdin in Nasira's Revenge (MagDemo46: ALADDIN\STREAM\*.CVS)


CDROM File Video Streaming Chunk-based formats

Newer Electronic Arts videos (EA)
EA videos are chunk based (instead of using 20h-byte .STR headers). The next chunk starts right at the end of the previous chunk (without padding to sector boundaries).
 STR Sector Header:
  No STR Sector header (first sector starts directly with "VLC0" chunk)
 VLC0 Chunk (at begin of movie file):
  000h 4     Chunk ID "VLC0"
  004h 4     Chunk Size (always 1C8h)     (big-endian)
  008h 1C0h  16bit MDEC values for E0h huffman AC codes (little-endian)
 MDEC Chunks (video frames):
  000h 4     Chunk ID "MDEC"                           ;\
  004h 4     Chunk Size (...)             (big-endian) ; custom chunk header,
  008h 2     Bitmap Width in pixels       (big-endian) ; instead of STR header
  00Ah 2     Bitmap Height in pixels      (big-endian) ;
  00Ch 4     Frame Number (starting at 0) (big-endian) ;/
  010h ..    Data (in BS v2 format, but using custom Huffman codes from VLC0)
  ...  ..    Zeropadding to 4-byte boundary
 Audio Chunks (au00/au01):
  000h 4     Chunk ID ("au00"=normal, "au01"=last audio chunk)
  004h 4     Chunk Size (...)                                   (big-endian)
  008h 4     Total number of 2x4bit samples in previous chunks  (big-endian)
  00Ch 2     Unknown (always 800h) (maybe Pitch: 800h=22050Hz)  (big-endian)
  00Eh 2     Unknown (always 200h)                              (big-endian)
  ...  ..    SPU-ADPCM audio data, left  (0Fh bytes per sample block)
  ...  ..    SPU-ADPCM audio data, right (0Fh bytes per sample block)
  ...  ..    Garbagepadding to 4-byte boundary
  Note: SPU-ADPCM does normally have 10h-byte blocks, but in this case,
  the 2nd byte (with loop flags) is omitted, hence only 0Fh-byte blocks.
 Zero Chunk (zeropadding at end of file, exists only in some EA videos):
  000h ..    Zeropadding

Older Electronic Arts videos
Crusader: No Remorse (1996 Origin Systems) (MOVIES\*.STR)
Soviet Strike (1996 Electronic Arts)
Battle Stations (1997 Electronic Arts)
Andretti Racing (1996 Electronic Arts)
 STR Sector Header:
  000h 4    ID (DDCCBBAAh) (aka AABBCCDDh big-endian)
  004h 4    Sector number within STR file (0=First, up to Filesize/800h-1)
  008h 7F8h Data (video and audio chunks, see below) (first chunk is "ad20")
 Video Chunks (MDEC):
  000h 4    Chunk ID "MDEC"                           ;\
  004h 4    Chunk Size (...)             (big-endian) ;
  008h 2    Bitmap Width in pixels       (big-endian) ; custom chunk header
  00Ah 2    Bitmap Height in pixels      (big-endian) ;
  00Ch 4    Frame Number (starting at 0) (big-endian) ;/
  010h ..   Data (in BS v2 format)                    ;-standard BS v2 data
 Audio Chunks (ad20/ad21) (22050Hz stereo):
  000h 4    Chunk ID ("ad20"=normal, "ad21"=last audio chunk)
  004h 4    Chunk Size (1A50h or 1A70h)                        (big-endian)
  008h 4    Total number of 2x4bit samples in previous chunks  (big-endian)
  00Ch 2    Unknown (always 800h) (maybe Pitch: 800h=22050Hz)  (big-endian)
  00Eh 2    Unknown (always 200h)                              (big-endian)
  010h ..   SPU-ADPCM audio data, left  (10h bytes per sample block)
  ...  ..   SPU-ADPCM audio data, right (10h bytes per sample block)
 Last STR Sector:
  000h 18h  FFh-filled (aka 8-byte STR header and 10h-byte Chunk header)
  018h -    Nothing (total STR filesize is N*800h+18h bytes)

Oldest Electronic Arts videos
Wing Commander III: Heart of the Tiger (MOVIES1.LIB\*.wve) (1995, EA/Origin)
 STR Sector Header:
  No STR Sector header (first sector starts directly with "Ad10" chunk)
 Video Chunks (MDEC):
  000h 4    Chunk ID "MDEC"                           ;\
  004h 4    Chunk Size (2xx0h)           (big-endian) ;
  008h 2    Bitmap Width in pixels       (big-endian) ; custom chunk header
  00Ah 2    Bitmap Height in pixels      (big-endian) ;
  00Ch 2    Unknown (7FFFh)              (big-endian) ;
  00Eh 2    Unknown (AD14h or AD24h)     (big-endian) ;/
  010h ..   Data (in BS v2 format)                    ;-standard BS v2 data
  ...  ..   Padding, up to circa 20h bytes, FFh-filled
 Audio Chunks (Ad10/Ad11) (22050Hz stereo):
  000h 4    Chunk ID ("ad20"=normal, "ad21"=last audio chunk)
  004h 4    Chunk Size (D38h or D28h) (or less in last chunk)  (big-endian)
  010h ..   SPU-ADPCM audio data, left  ? (10h bytes per sample block)
  ...  ..   SPU-ADPCM audio data, right ? (10h bytes per sample block)
Audio seems to be 22050Hz stereo, however, chunks with size=D38h have odd amounts of sampleblocks, so it isn't as simple as having left/right in first/second half.

Policenauts (Japan, 1996 Konami) (NAUTS\MOVIE\*.MOV)
 STR Sector Header:
  No STR Sector header (first sector starts directly with "VMNK" chunk)
 First chunk (800h bytes):
  000h 4     ID "VMNK" (aka KNMV backwards, maybe for Konami Video/Movie)
  004h 4     Unknown (01h)
  008h 4     Unknown (01h)
  00Ch 4     Unknown (F0h)
  010h 4     Size of KLBS chunks?          (40000h)
  014h 4     Bitmap X1 (aka left border)?  (16pix, 10h)
  018h 4     Bitmap Y1 (aka upper border)? (16pix, 10h)
  01Ch 4     Bitmap Width                  (288pix, 120h)
  020h 4     Bitmap Height                 (144pix, 90h)
  024h 7E4h  Zerofilled
 Further chunks (40000h bytes, each):
  000h 8     Zerofilled
  008h 4     Chunk ID "KLBS" (aka SBLK backwards, maybe for Stream Block)
  00Ch 4     Chunk Size (usually 40000h)
  010h 4     Number of Name List entries
  014h 4     Number of Name List entries (same as above)
  018h 8     Zerofilled
  020h N*30h Name List
  ...  ..    Data (referenced from Name List)
  ...  ..    Zeropadding (to end of 40000h-byte chunk)
The Name List does resemble a file archive, however, the "filenames" are just Type IDs (eg. all picture frames do have the same name).
 Name List entries:
  000h 8     Zerofilled
  008h 8     Data Type Name (eg. "SCIPPDTS")
  010h 4     Time when to play/display the frame (0 and up)
  014h 4     Time duration for that frame (usually 14h for Picture frames)
  018h 4     Data Offset in bytes (from begin of chunk)
  01Ch 4     Data Size in bytes
  020h 10h   Zerofilled
Data Formats for the different Data Types...
 Type "SDNSHDTS" aka SNDS,STDH - SoundStdHeader (Size=800h, Duration=0)
  000h 4     Maybe Pitch? (800h)                            (big-endian)
  004h 4     Maybe Pitch? (800h)                            (big-endian)
  008h 4     Total SPU-ADPCM size in bytes (for whole .MOV) (big-endian)
  00Ch 4     Unknown (FFFFFFFFh)                            (whatever)
  010h 4     Unknown (00007FFFh)                            (big-endian)
  014h 7ECh  Zerofilled
 Type "SDNSSDTS" aka SNDS,STDS - SoundStdStream (Size=10h..4000h, Duration=9Ch)
  000h 4000h SPU-ADPCM data in 10h-byte blocks (last chunk is less than 4000h)
 Type "SCIPPDTS" aka PICS,STDP - PictureStdPicture (Size=3xxxh, Duration=14h)
  000h 3xxxh Picture Frame (in BS v1 format)
 Type "SCTELLEC" aka ETCS,CELL - ExtraCells? (Size=0Ch, Duration=1)
  000h ..    Maybe subtitle related...?
 Type "SCTEGOLD" aka ETCS,DLOG - ExtraD-log? (Size=19h..31h, Duration=27h..44h)
  000h ..    Maybe subtitle related...?
Note: Total number of 10h-byte SPU-ADPCM blocks can be odd (so the audio seems to be mono).
Apart from the .MOV files, there's also one standard .STR file for the Knnami Intro (with normal STR headers and BS v2 data).

Best Sports Games Ever (DD\*.VLC and MOVIES\*.VLC) (Powerline Demo Disc menu)
This format is used for still images with only frame, and for looping short animation sequences in the Demo Disc Menu. There's no audio.
 Header Chunk:
  000h 4    Fixed ID (74h,55h,89h,08h aka 08895574h)
  004h 2    Bitmap Width           (140h)
  006h 2    Bitmap Height          (100h)
  008h 2    Video Frame Size/4     (17A0h or 13B0h)
  00Ah 2    Number of Video Frames (01h or 32h)
  00Ch 4    Frame End ID (eg. 62DCCACEh) (random?, but stays same within movie)
 Video Frame Chunk(s):
  ...  ..   Data (in BS v1/v2/v3 format)         ;\size = hdr[008h]*4
  ...  ..   FFh-filled (padding to Frame Size)   ;/
  ...  4    Frame End ID (eg. 62DCCACEh)         ;-same value as in hdr[00Ch]
For random access, best is seeking "fpos=N*(Framesize+4)+10h", alternately one could search "fpos=LocationAfterFrameEndID".

Sentient (FILMS\*.FXA)
This is having neither per-sector STR headers nor Chunk headers, instead it's having raw data with fixed size of 10 sectors per frame.
File Header (sector 0, 800h bytes):
  000h 4    File ID (01h,"XSP") (aka PSX backwards)
  004h 2    Unknown (0001h)
  006h 2    Unknown (0040h) (this is used for something...)
  008h 2    Bitmap Width  (0140h)
  00Ah 2    Bitmap Height (00F0h)
  00Ch 4    Total number of video frames
  010h 4    Number of video sectors per frame (always 8)
  014h 4    Total number of video sectors, excluding audio/dummy (=NumFrames*8)
  018h 1    Zero
  019h 1    Sector List size (28h) (ie. each 4 frames)  ;\or zerofilled when
  01Ah 28h  Sector Types (2=Video, 1=Audio, 0=Dummy)    ;/not present
  042h ..   Zerofilled
  7xxh ..   Unknown, maybe just garbage ...?
  ...  ..   Zerofilled
The frame rate is 15fps with 10 sectors per frame (8xVideo and either 2xAudio or 1xAudio+1xDummy). The Video/Audio/Dummy sector arrangement does repeat each 40 sectors (aka each 4 frames):
  vVvvvvv--vvVvvv--vvvvVv--vvvvvv-Vvvvvvv-  Video
  -------A-------A-------A-------A-------A  Audio
  --------D-------D-------D---------------  Dummy
  V = 1st sector of video frame
  v = 2nd..8th sector of video frame (or fileheader in case of sector 0)
  A = Audio (each 8th sector, ie. sector 07h,0Fh,17h,1Fh,etc.)
  D = Dummy (occurs after some (not all) audio sectors)
 Some files have that sector arrangement stored in header[019h..041h], but
 other files have that header entries zerofilled (despite of using the same
 arrangement).
Video frames are 8 sectors (4000h-byte), first and last 8 bytes are swapped:
  0000h 8     Last 8 bytes of BS v1 bitstream   ;\or garbage padding
  0008h 3FF0h First 3FF0h of BS v1 bitstream    ;/
  3FF8h 8     Footer (64bit, with squeezed BS header and other info)
 The footer bits are:
  0-4    5bit   Quant (00h..1Fh) (only 5bit, not 6bit)
  5-15   11bit  MDEC Size in 20h-word units (80h-byte units)
  16-23  8bit   Unknown (lowbits are often same as bit48 and up?)
  24-31  8bit   BS ID/100h (3800h/100h)
  32-47  16bit  Frame Number (0=First)
  48-63  16bit  Next Sector Number (start of next video frame)
 To decrypt/convert the frame to standard BS v1 format:
  x=[3FF8h]                      ;get footer
  [3FF8h..3FFFh]=[0000h..0007h]  ;last 8 bytes of bitstream
  [0000h]=(x AND FF00FFE0h)      ;size and ID=3800h
  [0004h]=(x AND 1Fh)+10000h     ;quant and version=v1
 The next_sector number is usually current_sector+1 (or +2 if that would be
 audio), in last frame it does point to end of file.
 Bitstreams smaller than 3FF8h are garbage padded (initially some 32bit garbage
 values, and in later frames leftovers from previous bitstream sectors).
Dummy sectors contain 800h bytes:
  000h 4     Always FFFFFFFFh (unfortunately, this isn't a unique ID)
  004h 7FCh  Garbage (zeroes, random, or even leaked ASM source code)
 Dummy sectors have the same Subheader as video sectors, the leading FFFFFFFFh
 could also occur in BS bitstreams or frames with garbage padding, so one must
 use the sector arrangement pattern to identify dummy sectors.
Audio sectors are XA-ADPCM and can be filtered via Subheader, or via sector arrangement pattern.


CDROM File Video Streaming Mis-mastered files

Mis-mastered streaming files
There are several discs that have streaming data stored as partial CDROM images (instead of as real CDROM sectors).
  Format        Content    Where
  raw 920h-byte STR        K9.5 1 - Live in Airedale (ZZBUFFER.STR)  ;\
  raw 920h-byte STR        Need for Speed 3 (MOVIES\ZZZZZZZ*.PAD)    ;
  raw 920h-byte STR        3D Baseball (ZZZZZZZZ.ZZZ)                ; intended
  raw 920h-byte STR        Wing Commander III (DUMMY.DAT)            ; padding
  raw 920h-byte STR        R-Types (DMY\DUMMY.BIN)                   ;
  raw 920h+junk STR+junk   Grand Slam (DUMMY.BIN)                    ;
  raw 920h-byte XA-ADPCM   Spec Ops Airborne Commando (PADDING.NUL)  ;
  raw 920h-byte SW-STR     Cyberia (ENDFILL\*.STR) (software render) ;
  RIFFs/CDXAfmt STRs       Sonic Wings Special (SW00.DMY = two RIFFs);/
  raw 920h-byte XA-ADPCM   Rugrats (MagDemo19: STREAMS\DB02.ISF)     ;\nonsense
  raw 920h-byte Data BABEh Rugrats (MagDemo19: STREAMS\OPEN.BIN)     ; dupes
  raw ???-byte  CDDA       Championship Surfer (MagDemo43: HWX\MUSIC);/
  raw ???-byte  CDDA       Twisted Metal 2 (MagDemo50: TM2\FRWYSUB.DA) ;-?
  raw 920h-byte STR        Sonic Wings Special (MOV\MQ*.STR)         ;-unused?
  raw 920h-byte STR        Apocalypse (MagDemo16: APOC\*.STR)
  raw 920h-byte XA-ADPCM   Apocalypse (MagDemo16: APOC\*.XA)
  raw 920h-byte XA-ADPCM   NFL Xtreme (MagDemo13: NFLX\GAME\SOUND\2PLAYRNO.XA)
  raw 920h-byte XA-ADPCM   Ace Combat 2 (MagDemo01: ACE2.STP)
  raw 920h-byte XA-ADPCM   Colony Wars (MagDemo02: CWARS\DEMO.PAK)
  raw 920h-byte XA-ADPCM   Best Sports demo (AH2\GAMEDATA\COM\MUSIC\MUSIC.IXA)
  raw 920h-byte XA-ADPCM   Tomb Raider: Last Revelation (MagDemo29: TR4\XA1.XA)
  raw 800h-byte XA-ADPCM   Croc 1 demo (MagDemo02: CROC\MAGMUS.STR) (FORM1)
  RIFF/CDXAfmt  XA-ADPCM   Best Sports demo (LOMUDEMO\SFX\COMMENT.STR)
  RIFF/CDXAfmt  ?+XA-ADPCM Ace Combat 3 Electrosphere (MagDemo30: AC3\*.SPB)
  RIFF/CDXAfmt  XA-ADPCM   Colony Wars Venegance (MagDemo14: CWV\SONYDEMO.PAK)
  RIFF/WAVEfmt  CDDA       T'ai Fu (MagDemo16: TAIFU\3_10.WAV, 2x16bit 44100Hz)
  RIFF/WAVEfmt  CDDA       Psalm69 (beta) FRONT\FIRE.TRK
The 920h-byte sectors exclude the leading Sync mark and MM:SS:FF:Mode2 value.
 Data/movie sectors look as so:
  000h 4    Sub-Header (File, Channel, Submode OR 20h, Codinginfo)
  004h 4    Copy of Sub-Header
  008h 800h Data (2048 bytes)           ;<-- contains STR movie sectors
  808h 4    EDC (zerofilled)
  80Ch 114h ECC (zerofilled)
 And XA-ADPCM sectors look as so:
  000h 4    Sub-Header (File, Channel, Submode OR 64h, Codinginfo)
  004h 4    Copy of Sub-Header
  008h 900h Data (18*128 bytes)         ;\contains XA-ADPCM audio sectors
  908h 14h  Data (zerofilled)           ;/
  91Ch 4    EDC (zerofilled)
The RIFF/CDXAfmt has a standard RIFF header, followed by 930h-byte sectors (same format as when opening CDROM streaming files in Windows). The RIFF/WAVEfmt is just a standard .WAV file.
In case of the ZZ*.* files on retail discs, the developers did intentionally append some non-functional dummy STR files (instead of appending zerofilled 30Mbyte at end of disc).
CDROM File XYZ and Dummy/Null Files
In case of the Demo Discs, the developers did probably have high hopes to release a demo version with working streaming data, just to find out that Sony had screwed up the data format (or maybe they had only accidentally included streaming data, without actually using it in demo version). Confusingly, the corrupted files were released on several discs (magazine demos, and other demo releases).
The Rugrats demo has intact files in RUGRATS\CINEMAT and RUGRATS\XA folders, plus nonsense copies of that files in 920h-byte format in STREAMS folder.

Partially mis-mastered files
Legend of Dragoon (MagDemo34: LOD\XA\LODXA00.XA has FIRST SECTOR mis-mastered (it has TWO sub-headers (01,00,48,00,01,00,48,00,01,01,64,04,01,01,64,04), the remaining sectors are looking okay).

Porsche Challenge (USA) (SRC\MENU\STREAM\*.STR)
The subheader and data of the 1st sector are accidently overwritten by some ASCII string:
  000h 4    Subheader       01 44 2D 52            ".D-R"    ;\distorted
  004h 4    Subheader copy  01 4D 20 47            ".M G"    ;/"CD-ROM G"
  008h 299h Data ASCII      65 6E 65 72 61 ...     "enerator for Windows"...
  2A1h 567h Data BS bitstream (but lacks BS header and start of bitstream)
The 2nd sector and up are containing intact STR headers (for the 2nd-Nth sector of 1st frame, but the whole 1st frame is unusable due to missing 1st sector; however, the following frames are intact).


CDROM File Video BS Compression Versions

STR/BS Version Summary, with popularity in percents (roughly)
  Version         .STR movies   .BS pictures
  BS v2            60%           6%            Most games
  BS v3            20%           4%            Some newer games
  BS v1            15%           0.1%          Old games
  BS ea            2%            - (?)         Electronic Arts titles
  BS iki           0.5%          0.1%          Several games
  BS fraquant      0.2%          0.1%          Rare (X-Files, Eagle One)
  BS v0            0.1%          -             Rare (Serial Experiments Lain)
  BS v2/v3.crypt   0.2%          -             Rare (Star Wars games)
  BS iki.encrypted 0.1%          -             Rare (Panekit)
  Wacwac MDEC      0.1%          -             Rare (Aconcagua)
  Polygon Streams  0.x% (?)      -             Some titles
  Raw MDEC         -             -             Was never used in files?
  MPEG1            -             -             VCD Video CDs
  None             ?%   (?)      90%           No videos or BS pictures
Most games can decrypt v1/v2/v3 videos (no matter which of the three versions they are actually using), newer games do occassionally use v3 for picture compression, but often stick with v2 for video streaming (perhaps because v3 does require slightly more CPU load; unknown if the higher CPU load has been an actual issue, and if it has been solved in the later (more optimized) decompressor versions) (unknown if there are other benefits like v2 having better DC quality or better compression in some cases?).

BS v0 (used by only one known game)
  v0 used by Serial Experiments Lain
This game is apparently using a very old and very unoptimized decoder (although it was released in 1997, when most or all other games did already have decoders with v1/v2/v3 support).
The v0 decoder has different header, lacks End of Frame codes, and uses Huffman codes with different AC values than v1/v2/v3/iki.

BS v1 (used by older games, some of them also having v2 videos)
  v1 used by Wipeout 2097 (MAKE.AV, XTRO*.AV)
  v1 used by Viewpoint (MOVIES\*.STR) (oddly with [08h]=FirstFrame=0 and
        [1Ch]=Unspecified=Nonzero) (the game also has ".str" files in
        VIEW.DIR\streams, but that isn't MDEC/STR stuff)
  v1 used by Ridge Racer Revolution (MOVIE\*.STR)
  v1 used by Policenauts
  v1 used by Final Fantasy VII (FF7)
  v1? used by Tekken 2
  v1/v2 used by Final Fantasy Tactics (OPEN*.STR)
  v1/v2 used by Project Horned Owl (*.STR)
  v1/v2 used by Gex (*.FMV)
  (and probably more)
v1 and v2 can be decoded with the same decompressor. The only difference is that v1 was generated with an older compressor (which did accidently store nonsense 22bit escape codes with run=N, level=0 in the bitstream; whereas one could as well use run+N+1 in the next code, or omit it completely if next code is EOB).

BS v2 (most games)
  v2 used by Gex - Enter the Gecko (*.STR)
  v2 used by Tomb Raider (FMV\*.FMV)
  v2 used by Alone (STR*\*.STR)
  v2 used by Kain (*.STR)
  v2 used by Fear Effect (BOOT.SID, LOGO.SID, ABGA\ABGA.FLX)
  v2 used by Parasite Eve 2 (INTERx.STR, and in .CDF's eg. stage1\folder501)
  v2 used by Witch of Salzburg (MOVIE\*.STR)
  v2 used by Breath of Fire III (LOGO\*.STR)
  v2 used by Hear it Now (MOVIE\*.STR)
  v2 used by Legend of Mana (MOVIE\*.STR)
  v2 used by Misadventures of Tron Bonne (STR\*.STR)
  v2 used by Rayman (VIDEO\*.STR)
  v2 used by Resident Evil 1 (PSX\MOVIE\*.STR)                ;\although v3 is
  v2 used by Resident Evil 2 (PL0\ZMOVIE\*.STR, ZMOVIE\*.STR) ;/used in *.BSS
  v2 used by Tokimeki Memorial 2 (VX*.STR)
  v2 used by Spider-Man (CINEMAS\*.STR)
  v2 used by Perfect Assassin (CDV\*.STR)
  v2 used by Pandemonium 2 (*.STR)
  v2 used by Die Hard Trilogy 2 (MOVIE\*.STR)
  v2 used by Need for Speed 3 (MOVIES\*.STR) (oddly with [14h,18h]<>[20h,24h])
  v2 used by Wild Arms (STR\*.STR)
  v2 used by Wild Arms 2 (STR\*.STR)
  v2 used by Frogger (*.STR)
  v2 used by Gundam Battle Assault (XA\*.STR)
  v2 used by Alundra (MOVIE\*.MOV)
  v2 used by Spec Ops (file 95h,96h within BIGFILE.CAT)
  v2 used by Crash Team Racing (file 1E1h..1F8h,1FAh within BIGFILE.BIG)
  (and many more)
Same as v1, but without the compressor bug.

BS v3 (used by some newer games, some of them also having v2 videos)
  v2/v3 used by Lemmings Oh No More Lemmings (ANIMS\*.STR)
  v2/v3 used by Castlevania (*.STR)
  v3 used by Heart of Darkness (CINE\*.STR, SETUP\*.STR)
  v3 used by R-Types (MV\*.STR)
  v3 used by Black Matrix (MOVIE\*.STR)
  v3 used by Nightmare Creatures II (INTRO\*.STR, LEVEL*\*.STR)
  (and many more)
Same as v2, but using Huffman compressed DC values.

BS ea (Electronic Arts)
Used by many EA Sports titles and several other titles from Electronic Arts:
  Castrol Honda Superbike Racing
  EA Sports Supercross 2000, 2001
  Future Cop - L.A.P.D. (retail and MagDemo14: FCOPLAPD\*.WVE and *.FSV)
  Hot Wheels - Turbo Racing
  Jampack Vol. 2
  Knockout Kings 99, 2000, 2001
  Madden NFL 99, 2000, 2001, 2002, 2003, 2004, 2005 (eg. MADN00\FMVIDEO.DAT\*)
  NASCAR 98, 99, 2000, 2001 (and 98 Collector's Edition, and 99 Legacy)
  NASCAR Thunder 2002, 2003, 2004 and NASCAR Rumble
  Nuclear Strike
  Official U.S. PlayStation Magazine Demo Disc 39 (...XXX which game?)
  PlayStation Underground Jampack - Winter 2000
  Road Rash Jailbreak, and Road Rash 3D
  Tiger Woods PGA Tour Golf, and Tiger Woods USA Tour 2001
Uses VLC0 and MDEC chunks (instead of STR headers), the MDEC chunks contain standard BS v2 data, but using custom MDEC values from VLC0 chunk.

BS fraquant
  X-Files (Fox Interactive/Hyperbole Studios, 1999)
  Eagle One: Harrier Attack (Infogrames/Glass Ghost, 2000)
  Blue's Clues: Blue's Big Musical (Mattel/Viacom/TerraGlyph, 2000)
This replaces the 6bit quant value by a 16bit fixed-point quant value (done by manipulating the Quant Table instead of using QuantDC, apart from that extra feature it's internally using normal BS v1/v2/v3 decoding).

BS iki
  iki: Gran Turismo 1 (STREAM.DAT)   ;\with uncommon STR header
  iki: Gran Turismo 2 (STREAM.DAT)   ;/
  iki: Hot Shots Golf 2 / Everybody's Golf 2 (MagDemo31: HSG2\MINGOL2X.BIN)
  iki: Legend of Legaia (MagDemo20: LEGAIA\MOV\MV2.STR)
  iki: Legend of Dragoon (STR\*.IKI)
  iki: Omega Boost (MOVIE\*.IKI)
  iki: Um Jammer Lammy (MagDemo24: UJL\*.IKI) (retail: *\*.IKI and CM\*.IK2)
  iki: plus a dozen of japanese-only titles
This might have been used between v2 and v3, iki is using uncommon BS headers and LZ compressed Quant/DC values (whilst v3 is using Huffman compressed DC values).

Encrypted iki
  Panekit - Infinitive Crafting Toy Case (first 13Mbyte in PANEKIT.STR)
Same as normal iki, with some SWAP/ADD/XOR-encrytion in first 20h-bytes.

Encrypted v2/v3
  v3.xor used by Star Wars Masters of Teras Kasi (MagDemo03: MASTERS\*.STR)
  v2.xor supported (but not actually used) by Star Wars Masters (MagDemo03)
  v3.swap used by Star Wars Rebel Assault II (*.STR, *.SED, Stills)
  v2.swap used by Star Wars Rebel Assault II (*.STR)
  v3.swap used by BallBlazer Champions (*.STR)
Same as normal v2/v3 with simple XOR-encryption or SWAP-encryption.

Wacwac MDEC
  Aconcagua (JP) (2000 Sony/WACWAC!) (STR_01_00.STR and STR_09_01.STR)
Similar to v3, but uses completely different Huffman codes than BS video.

Polygon Streaming (instead of MDEC picture streaming)
  Ape Escape (DEMO\*.STR, STR\*.STR, and KKIIDDZZ.HED\STR\0006h and up)
  Aconcagua (most STRs are Polygon Streams, except two are Wacwac MDEC streams)
  Panekit - Infinitive Crafting Toy Case (last 150Mbyte in PANEKIT.STR)
Polygon streams contain vertices (for textures that are stored elsewhere). Usually needing only one sector per frame. This can be useful for animations that were recorded from real actors. Drawbacks are more edgy graphics and lower color depth (although that may fit in with the game engine).
CDROM File Video Polygon Streaming

MPEG1 (on VCD Video CDs)
MPEG1 uses I/P/B-Frames, the I-Frames may reach similar compression as BS files. However, P-Frames and B-Frames do compress much better than BS files.
CDROM Video CDs (VCD)
MPEG1 isn't used in any PSX games, but VCDs can be viewed on SCPH-5903 consoles (or via software decoder in nocash PSX kernel clone).

Titles without movies
Most PSX titles do include movies, exceptions are some early launch titles and educational titles:
  Ridge Racer 1 (1994)
  Lightspan Online Connection CD


CDROM File Video BS Compression Headers

There are several different BS headers. The File ID/Version entries can be used to detect the correct type. The MDEC Size entry contains the size after Huffman decompression (ie. the half-decompressed size before passing the data to the MDEC decompression hardware) (usually divided by 4 and rounded up to 80h/4 bytes).

BS v1/v2/v3 header
  000h 2    MDEC Size/4 (after huffman decompression) (rounded to 80h/4 bytes)
  002h 2    File ID (3800h)
  004h 2    Quantization step/factor (0000h..003Fh, for MDEC "DCT.bit10-15")
  006h 2    Version (1, 2, or 3) (2 is most common)
  008h ...  Huffman compressed data blocks (Cr,Cb,Y1,Y2,Y3,Y4, Cr,Cb,Y1,Y2..)

Encrypted v2/v3
Encryption is used in Star Wars games, there are two encryption schemes (XOR and SWAP).
XOR-encrypt: Star Wars Masters of Teras Kasi (MagDemo03: MASTERS\*.STR):
  000h 2   MDEC Size/4 (rounded to 80h/4 bytes) (unencrypted) ;\same as normal
  002h 2   File ID (3800h)                      (unencrypted) ; BS v1/v2/v3
  004h 2   Quant (0..3Fh)                       (unencrypted) ;/
  006h 2   Version (in bit15, plus random in LSBs):
             00xxh..7FFFh for v2 (unknown if this could include values 0..3)
             8000h..FFFFh for v3 (bit14-0=random, varies in each frame)
  008h ..  Encrypted bitstream
             (each halfword XORed by BE67h for v2, or XORed by E67Bh for v3)
  ...  (2) Zeropadding to 4-byte boundary (unencrypted)
  ...  ..  Zeropadding to end of sector   (unencrypted)
 The XOR values BE67h/E67Bh are hardcoded in the Star Wars Masters of Teras
 Kasi .EXE (same XOR values for both retail and demo version), unknown if any
 other games are also using that kind of encryption (and if yes, if they are
 using the same XOR values).
SWAP-encrypt: BallBlazer Champions, Star Wars Rebel Assault II (*.STR, *.SED):
  000h 2   MDEC Size/4 (rounded to 80h/4 bytes) ;\same as normal
  002h 2   File ID (3800h)                      ; BS v1/v2/v3
  004h 2   Quant (0..3Fh)                       ;/
  006h 2   Version (random 16bit, 00xxh..FFFFh) ;-no meaningful version info
  008h 2   Bitstream 2nd halfword               ;\to "decrypt" the file,
  00Ah 2   Bitstream 1st halfword               ;/these must be swapped
  00Ch ..  Bitstream 3rd halfword and up        ;-in normal order
Whilst XORing or SWAPping the halfwords is simple, the more difficult part is distinguishing between SWAP-v2/v3 and XOR-v2/v3 encryption. This can be done as so:
  if header[06h]<=0003h then assume unencrypted v0/v1/v2/v3
  if header[06h]>=0004h then strip any trailing 0 bits, and check EndOfFrame..
  if last 10bit = 0111111111 then assume SWAP.v2
  if last 10bit = 1111111111 then assume SWAP.v3
  otherwise assume XOR.v2/v3 (and use header[06h].bit15 to distinguish v2/v3)

BS iki Header
IKI videos have a custom .BS header, including some GT-ZIP compressed data:
  000h 2   MDEC Size/4 (rounded to 80h/4 bytes)         ;\same as normal
  002h 2   File ID (3800h)                              ;/BS v1/v2/v3
  004h 2   Bitmap Width in pixels     ;instead of Quant
  006h 2   Bitmap Height in pixels    ;instead of Version
  008h 2   Size of GT-ZIP compressed data (plus 2-byte alignment padding)
  00Ah ..  GT-ZIP compressed DC/Quant values (plus 2-byte alignment padding)
  ...  ..  Huffman compressed AC data blocks (Cr,Cb,Y1,Y2,Y3,Y4, Cr,Cb,Y1,Y2..)
The number of blocks is NumBlocks=(Width+15)/16*(height+15)/16*6. The size of the decompressed GT-ZIP data is NumBlocks*2.

Encrypted iki
The first 20h byte of the iki header & data are encrypted. Among others, the ID 3800h is inverted (=C7FFh). To decrypt them:
  [buf+00h]=[buf+00h] XOR FFFFFFFFh
  [buf+04h] <--> [buf+08h]          ;exchange 2x32bit
  [buf+0Ch] <--> [buf+0Eh]          ;exchange 2x16bit
  [buf+10h]=[buf+10h]+FFFF6F7Bh
  [buf+14h]=[buf+14h]+69140000h
  [buf+18h]=[buf+18h]+FFFF7761h
  [buf+1Ch]=[buf+1Ch]+6B040000h
Note: The .STR header's StHeadM/StHeadV fields contain a copy of the decrypted values. The PANEKIT.STR file is 170Mbyte tall, but only the first 13Mbyte contain movie data... the rest is unknown stuff... often with zeroes followed by 7B,44,F0,29,E0,28 unknown what for...?

BS fraquant
  X-Files, GRAPHICS\*.STR,*.BIN, LOGOS\*.STR,*.BS
  Eagle One: Harrier Attack (\*.STR, DATA*\*.STR) (leading zerofilled sectors)
  Blue's Clues: Blue's Big Musical (*.STR) (has one leading zerofilled sector)
This has a normal BS v1/v2/v3 header, with special quant entry:
  004h 2    Quant (0001h..0003h, or fixed-point 8000h..9xxxh)
The decoder is using the default_quant_table (02h,10h,10h,13h,..,53h) multiplied with a fixed point number:
  quant=BsHeader[04h]   ;get fractional quant value
  BsHeader[04h]=0001h   ;force quant=1 (for use in BS v1/v2/v3 decoder)
  if quant<8000h then quant=quant*200h else quant=quant AND 7FFFh
  quant[0]=default_quant_table[0]
  for i=1 to 3Fh,
    x=(default_quant_table[i]*quant)/200h
    if x=00000000h then quant[i]=01h else quant[i]=(x AND FFh)
  next i
  use MDEC(2) command to apply quant[0..3Fh] to both Luma and Chroma tables
  use normal BS v1/v2/v3 decoder to decompress the bitmap
BsHeader[04h] should be 0001h..0003h, or 8000h..862Bh (values outside that range would overflow the 8bit quant table entries). Values 0001h..0003h should should give same results as for normal BS decoding, so only values 8000h and up do need special decoding.
Caution: Despite of the overflows, quant>862Bh is used (eg. X-Files GRAPHICS\GRAPHICS.BIN has quant=88C4h, Blue's Big Musical has quant=93E9h; those images do look okay, so the compressor seems to have recursed the overflows; or the overflow affects only a few pixels), however, very large with LSBs all zero (eg. 9000h) can cause 8bit table entries to become 00h (due to ANDing the result with FFh).
Note: X-Files LOGOS\POP*.STR have quant=8001h (=near zero), that files are only 60Kbyte and seem to be all black.
Note: The movie engine uses COP2 GPF opcodes to calculate quant values.

v0 Header (in STR files)
  000h 1   Quant for Y1,Y2,Y3,Y4  (00h..3Fh)
  001h 1   Quant for Cr,Cb        (00h..3Fh)
  002h 2   File ID (3800h) (or Frame Number in ENDROLL1.STR on Disc 2)
  004h 2   MDEC Size/2 (!), and without padding (!) (unlike v1/v2/v3/iki)
  006h 2   BS Version (0) (actually MSBs of above Size, but it's always 0)
  008h ..  Huffman Bitstream, first bit in bit7 of first byte

v0 Header (in LAPKS.BIN chunks)
LAPKS.BIN contains several chunks, each chunk contains an animation sequence with picture frame(s), each frame starts with following header:
  000h 2   Bitmap Width in pixels     ;\cropped to non-black screen area,
  002h 2   Bitmap Height in pixels    ;/size can vary within the sequence
  004h 2   Quant for Y1,Y2,Y3,Y4  (0000h..003Fh)
  006h 2   Quant for Cr,Cb        (0000h..003Fh)
  008h 4   Size of compressed BS Bitstream plus 4 ;Transparency at [008h]+0Ch
  00Ch 2   Size/2 of MDEC data (after huffman decompression, without padding)
  00Eh 2   BS Version (0) (actually MSBs of above Size, but it's always 0)
  010h ..  BS Bitstream with DC and AC values (Huffman compressed MDEC data)
  ...  4   Transparency Mask Decompressed Size (Width*Height*2/8) (=2bpp)
  ...  ..  Transparency Mask LZSS-compressed data
For decompressing the transparency mask:
CDROM File Compression LZSS (Serial Experiments Lain)
The Transparency Mask is stored as scanlines (not as macroblocks), the upper/left pixel is in bit7-6 of first byte, the 2bit alpha values are ranging from 0=Transparent to 3=Solid.

BS ea Headers (Electronic Arts)
EA videos are chunk based (instead of using 20h-byte .STR headers).
CDROM File Video Streaming Chunk-based formats
VLC0 Chunk: Custom MDEC values (to be assigned to normal BS v2 Huffman codes).
MDEC Chunks: Width/Height and BS v2 data (using MDEC values from VLC0 chunk).

Raw MDEC
There aren't any known pictures or movies in raw MDEC format. However, the Huffman decompression functions do usually output raw data in this format:
  000h 2   MDEC Size/4 (after huffman decompression) (rounded to 80h/4 bytes)
  002h 2   File ID (3800h)
  004h ..  MDEC data (16bit DC/AC/EOB codes)
  ...  ..  Padding (FE00h-filled to 80h-byte DMA transfer block size boundary)
The first 4 bytes are the MDEC(1) command, the "ID" is always 3800h (equivalent to selecting 16bpp output; for 24bpp this must be changed to 3000h before passing the command to the MDEC hardware). The remaining bytes are MDEC data (padded to 80h-byte boundary).
Macroblock Decoder (MDEC)


CDROM File Video BS Compression DC Values

DC v0
  nnnnnnnnnn        DC Value (signed 10bit, -200h..+1FFh)
This is similar as v1/v2, except there is no End code for End of Frame, and the .BS header contains two separate quant values (for Cr/Cb and Y1-Y4).
  If output_size=NumberOfMdecCodes*2 then EndOfFrame
  If BlockIsCrCb then QuantDC=DC+QuantC*400h else QuantDC=DC+QuantY*400h

DC v1/v2/ea
  nnnnnnnnnn        DC Value (signed 10bit, -200h..+1FEh)
  0111111111        End of Frame (+1FFh, that, in place of Cr)
This is similar as v0, except there is only one Quant value for all blocks, and the header lacks info about the exact decompressed size, instead, compression end is indicated by a newly added end code:
  If DC=+1FFh then EndOfFrame
  QuantDC=DC+Quant*400h

DC v3
Similar as v1/v2, but DC values (and End code) are now Huffman compressed offsets relative to old DC, with different Huffman codes for Cr/Cb and Y1-Y4:
  For Cr/Cb         For Y1..Y4      Offset (added to old DC of Y/Cr/Cb block)
  00                100             +(00h)                      ;\
  01s               00s             -(01h)*4     ,+(01h)*4      ;
  10sn              01sn            -(03h..02h)*4,+(02h..03h)*4 ; required
  110snn            101snn          -(07h..04h)*4,+(04h..07h)*4 ; codes
  1110snnn          110snnn         -(0Fh..08h)*4,+(08h..0Fh)*4 ; for 10bit
  11110snnnn        1110snnnn       -(1Fh..10h)*4,+(10h..1Fh)*4 ; range
  111110snnnnn      11110snnnnn     -(3Fh..20h)*4,+(20h..3Fh)*4 ;
  1111110snnnnnn    111110snnnnnn   -(7Fh..40h)*4,+(40h..7Fh)*4 ;/
  11111110snnnnnnn  1111110snnnnnnn -(FFh..80h)*4,+(80h..FFh)*4 ;-11bit (!)
  -                 11111110        Unused                      ;\
  111111110         111111110       Unused                      ; unused
  1111111110        1111111110      Unused                      ;/
  1111111111        1111111111      End of Frame                ;-end code
  Note: the "snnn" bits are indexing the values in right column,
  with s=0 for negative values, and s=1 for positive values.
The decoding works as so (with oldDcXxx=0 for first macroblock):
  If bits=1111111111 then EndOfFrame
  If BlockIsCr then DC=DecodeHuffman(HuffmanCodesCbCr)+oldDcCr, oldDcCr=DC
  If BlockIsCb then DC=DecodeHuffman(HuffmanCodesCbCr)+oldDcCb, oldDcCb=DC
  If BlockIsY1234 then DC=DecodeHuffman(HuffmanCodesY1234)+oldDcY, oldDcY=DC
  If older_version AND DC>=0 then QuantDC=Quant*400h or (DC)       ;\requires
  If older_version AND DC<0  then QuantDC=Quant*400h or (DC+400h)  ;/11bit
  If newer_version           then QuantDC=Quant*400h+(DC AND 3FFh) ;-wrap 10bit
Note: The offsets do cover signed 11bit range -3FCh..+3FCh. Older v3 decoders did require 11bit offsets (eg. add +3FCh to change DC from -200h to +1FCh). Newer v3 decoders can wrap within 10bit (eg. add -4 to wrap DC from -200h to +1FCh).

DC iki
The DC values (including Quant values for each block) are separately stored as GT-ZIP compressed data in the IKI .BS header.
CDROM File Compression GT-ZIP (Gran Turismo 1 and 2)
Calculate NumBlocks=(Width+15)/16*(height+15)/16*6, decompress the DC values (until DecompressedSize=NumBlocks*2). During Huffman decompression, read the DC values from the decompressed DC buffer (instead of from the Huffman bitstream):
  If BlockNo>=NumBlocks then EndOfFrame
  QuantDC = DCbuf[BlockNo]*100h + DCbuf[BlockNo+NumBlocks]
As shown above, the Hi- and Lo-bytes are stored in separate halves of the DC buffer (which may gain better compression).


CDROM File Video BS Compression AC Values

Below shows the huffman codes and corresponding 16bit MDEC values; the "xx" bits contain an index in the list of 16bit MDEC values, the "s" bit means to negate the AC level (in lower 10bit of the 16bit MDEC value) when s=1.

Huffman codes for AC values BS v1/v2/v3/iki
  10                     FE00h          ;End of Block, EOB
  11s                    0001h
  011s                   0401h
  010xs                  0002h,0801h
  0011xs                 1001h,0C01h
  00101s                 0003h
  00100xxxs              3401h,0006h,3001h,2C01h,0C02h,0403h,0005h,2801h
  0001xxs                1C01h,1801h,0402h,1401h
  00001xxs               0802h,2401h,0004h,2001h
  000001xxxxxxxxxxxxxxxx 0000h..FFFFh   ;Escape code for raw 16bit values
  000001xxxxxx0000000000 0000h..FC00h   ;Escape nonsense level=0 (used in v1)
  0000001xxxs            4001h,1402h,0007h,0803h,0404h,3C01h,3801h,1002h
  00000001xxxxs          000Bh,2002h,1003h,000Ah,0804h,1C02h,5401h,5001h,
                         0009h,4C01h,4801h,0405h,0C03h,0008h,1802h,4401h
  000000001xxxxs         2802h,2402h,1403h,0C04h,0805h,0407h,0406h,000Fh,
                         000Eh,000Dh,000Ch,6801h,6401h,6001h,5C01h,5801h
  0000000001xxxxs        001Fh,001Eh,001Dh,001Ch,001Bh,001Ah,0019h,0018h,
                         0017h,0016h,0015h,0014h,0013h,0012h,0011h,0010h
  00000000001xxxxs       0028h,0027h,0026h,0025h,0024h,0023h,0022h,0021h,
                         0020h,040Eh,040Dh,040Ch,040Bh,040Ah,0409h,0408h
  000000000001xxxxs      0412h,0411h,0410h,040Fh,1803h,4002h,3C02h,3802h,
                         3402h,3002h,2C02h,7C01h,7801h,7401h,7001h,6C01h
  000000000000           Unused

Huffman codes for AC values BS v0 (Serial Experiments Lain)
  10                           FE00h          ;End of Block, EOB
  11s                          0001h
  011s                         0002h
  010xs                        0401h,0003h
  0011xs                       0801h,0005h
  00101s                       0004h
  00100xxxs                    000Ah,000Bh,0403h,1801h,000Ch,000Dh,1C01h,000Eh
  0001xxs                      0006h,0C01h,0402h,0007h
  00001xxs                     0008h,1001h,0009h,1401h
  000001xxxxxx0xxxxxxx         0000h..FC00h+(+001h..+07Fh AND 3FFh) ;\
  000001xxxxxx000000001xxxxxxx 0000h..FC00h+(+080h..+0FFh AND 3FFh) ; Escape
  000001xxxxxx000000000xxxxxxx Unused                               ; codes
  000001xxxxxx1xxxxxxx         0000h..FC00h+(-080h..-001h AND 3FFh) ;
  000001xxxxxx100000000xxxxxxx 0000h..FC00h+(-100h..-081h AND 3FFh) ;
  000001xxxxxx100000001xxxxxxx Unused                               ;/
  0000001xxxs                  000Fh,0802h,2001h,0404h,0010h,0011h,2401h,0012h
  00000001xxxxs                0013h,0405h,0014h,2801h,0015h,0C02h,3001h,0017h,
                               0016h,2C01h,0018h,001Ch,0019h,0406h,0803h,001Bh
  000000001xxxxs               001Ah,3401h,001Dh,0407h,1002h,001Fh,001Eh,3801h,
                               0020h,0021h,0408h,0023h,0022h,1402h,0024h,0025h
  0000000001xxxxs              0804h,0409h,0418h,0026h,3C01h,0027h,0C03h,1C03h,
                               0028h,0029h,002Ah,002Bh,040Ah,002Ch,1802h,002Dh
  00000000001xxxxs             002Fh,002Eh,4001h,0805h,0030h,040Bh,0031h,0033h,
                               0032h,1C02h,0034h,1003h,0035h,4401h,040Ch,0037h
  000000000001xxxxs            0036h,0038h,0039h,5401h,003Ah,0C04h,040Dh,5C01h,
                               2002h,003Bh,0806h,4C01h,003Ch,2402h,6001h,4801h
  000000000000                 Unused
Uses different 16bit MDEC values, and the Escape code is different: 8bit levels are 2bit shorter than v1/v2/v3, but 9bit levels are much longer, and 10bit levels are not supported at all (those v0 Escape codes are described in Sony's File Format documented; albeit accidentally because the doc was actually trying to describe v2/v3).

Huffman codes for AC values BS ea (Electronic Arts)
This is using custom MDEC values from VLC0 chunk, and assigns them to the standard Huffman codes. There are two special MDEC values:
  FE00h End of Block (EOB)
  7C1Fh Escape code (huffman code will be followed by v2-style 16bit value)
VLC0 chunk entries 00h..DFh are mapped to the following Huffman codes:
  10                   00
  11x                  01,02
  011x                 03,04
  010xx                05,06,07,08
  0011xx               0D,0E,0B,0C
  00101x               09,0A
  00100xxxx            2E,2F,22,23,2C,2D,2A,2B,26,27,24,25,20,21,28,29
  0001xxx              15,16,13,14,0F,10,11,12
  00001xxx             1A,1B,1E,1F,18,19,1C,1D
  000001               17h
  0000001xxxx          3E,3F,38,39,30,31,34,35,32,33,3C,3D,3A,3B,36,37
  00000001xxxxx        46,47,54,55,4E,4F,44,45,4A,4B,52,53,5E,5F,5C,5D,
                       42,43,5A,5B,58,59,48,49,4C,4D,40,41,50,51,56,57
  000000001xxxxx       74,75,72,73,70,71,6E,6F,6C,6D,6A,6B,68,69,66,67,
                       64,65,62,63,60,61,7E,7F,7C,7D,7A,7B,78,79,76,77
  0000000001xxxxx      9E,9F,9C,9D,9A,9B,98,99,96,97,94,95,92,93,90,91,
                       8E,8F,8C,8D,8A,8B,88,89,86,87,84,85,82,83,80,81
  00000000001xxxxx     B0,B1,AE,AF,AC,AD,AA,AB,A8,A9,A6,A7,A4,A5,A2,A3,
                       A0,A1,BE,BF,BC,BD,BA,BB,B8,B9,B6,B7,B4,B5,B2,B3
  000000000001xxxxx    C6,C7,C4,C5,C2,C3,C0,C1,C8,C9,D4,D5,D2,D3,D0,D1,
                       CE,CF,CC,CD,CA,CB,DE,DF,DC,DD,DA,DB,D8,D9,D6,D7
  000000000000         Unused
All codes can be freely assigned (Escape and EOB don't need to be at 10 and 000001, and the last huffman bit doesn't have to serve as sign bit).

Notes
All BS versions are using the same Huffman codes (the different BS versions do just assign different 16bit MDEC codes to them).
The huffman codes can be neatly decoded by "counting leading zeroes" (without needing bitwise node-by-node processing; this is done in IKI video decoders via GTE registers LZCS and LZCR). Sony's normal v2/v3 decoders are using a yet faster method: A large table to interprete the next 13bit of the bitstream, the table lookup can decode up to 3 huffman codes at once (if the 13bit contain several small huffman codes).


CDROM File Video BS Picture Files

BS Picture Files
A couple of games are storing single pictures in .BS files:
  Alice in Cyberland (ALICE.PAC\*.BS)
  BallBlazer Champions (BBX_EXTR.DAT\Pics\*) (SWAP-encrypted)
  Bugriders: The Race of Kings (*\*.BS and STILLS\MENUS.BS\*)
  Die Hard Trilogy 2 (DATA\*.DHB, DATA\DH*\L*\*.DHB, MOVIE\*.DHB)
  Dino Crisis 2 (PSX\DATA\ST*.DBS\*)
  Duke Nukem (MagDemo12: DN_TTK\*)
  Final Fantasy VII (FF7) (MOVIE\FSHIP2*.BIN\*) (BS v1)
  Gran Turismo 1 (retail TITLE.DAT\* and MagDemo10/15) (in BS iki format)
  Jet Moto 2 (MagDemo03: JETMOTO2\*)
  Mary-Kate and Ashley Crush Course (MagDemo52: CRUSH\SCRN\*.BS)
  Mat Hoffman's Pro BMX (MagDemo48: MHPB\STILLS.BIN\*) (with width/height info)
  NFL Gameday '99 (MagDemo17: GAMEDAY\FE\GD98DATA.DAT)
  Official U.S. PlayStation Magazine Demo Disc 01-02 (MENU\DATA\*.BSS)
  Official U.S. PlayStation Magazine Demo Disc 03-54 (MENU.FF\*)
  Parasite Eve 2 (INIT.BS, and within .HED/.CDF archives)
  Resident Evil 1 (PSX\STAGE*\*.BSS, headerless archive, 8000h-byte align)
  Resident Evil 2 (COMMON\BSS\*.BSS, headerless archive, 10000h-byte align)
  Rugrats (MagDemo19: RUGRATS\*)
  Rugrats Studio Tour (MagDemo32: RUGRATS\DATA\RAW\*.BS)
  Starwars Demolition (MagDemo39+MagDemo41: STARWARS\SHELL\.BS+.TBL\*)
  Star Wars Rebel Assault 2 (RESOURCE.000\Stills\*) (SWAP-encrypted)
  Ultimate Fighting Championship (MagDemo38: UFC\CU00.RBB\390h..3E2h)
  Vigilante 8 (MagDemo09: EXAMPLE\*)
  Witch of Salzburg (PICT\PIC*\*.BS and DOT1 archives *.BSS, *.DAT, *.BIN)
  X-Files (LOGOS\*.BS and GRAPHICS\GRAPHICS.BIN and GRAPHICS\PACKEDBS.BIN\*)
  You Don't Know Jack 2 (MagDemo41: YDKJV2\RES\UI\*.BS)
Note: Those .BS files are usually hidden in custom file archives.

BS Picture Resolution
Movies have Width/Height entries (in the .STR header). Raw .BS picture files don't have any such information. However, there are ways to guess the correct resolution:
  For BS iki format, use resolution from iki header (eg. Gran Turismo 1)
  For MHPB\STILLS.BIN, there's width/height in chunk headers
  Count the number of blocks (EOB codes) during Huffman decompression
  Divide that number by 6 to get the number of Macroblocks
  Search matches for Height=NumBlocks/Width with Width>=Height and Remainder=0
  If Height=300..400, assume double H-resolution, repeat with Width/2>=Height
  And/or use a list of known common resoltions (see below examples)
  Search arrangements with many similar colors on adjacent macroblocks
Common resolutions are:
  Blocks Pixels   Example
  F0h    256x240  any?
  12Ch   320x240  Resident Evil 2 (COMMON\BSS\*.BSS)
  1E0h   512x240  Demo Disc 03-54 (MENU.FF\*), Duke Nukem (MagDemo12)
  1E0h   640x192  Less common than above (but used by Witch of Salzburg)
  4B0h   640x480  Vigilante 8 (MagDemo09), Jet Moto 2 (MagDemo03)
  var    random   Witch of Salzburg has various random resolutions
  iki    ikihdr   Gran Turismo 1 has A0hxA0h and odd size (!) E8hx28h
  ?      ?        Final Fantasy VII (FF7)
  ?      ?        Ultimate Fighting Championship (UFC\CU00.RBB\3B7h..3E2h)
  118h   320x224  Alice in Cyberland (most files; or two such as panorama)
  230h   ?        Alice in Cyberland (AD_115.BS and AD_123A.BS)
Some other possible, but rather unlikely results would be:
  C8h    320x160  Unlikely for pictures (but used for STR videos, eg. Alone)
  F0h    320x192  Unlikely for pictures (but used for STR videos, eg. Wipeout)
  1E0h   384x320  Very unlikely to see that vertical resolution on PSX
Witch of Salzburg has many small .BS files with various uncommon resolutions (most of them are bundled with 16-byte .TXT files with resolution info).

Extended BS with Width/Height
Starwars Demolition (MagDemo39: STARWARS\SHELL\DEMOLOGO.BS+RESOURCE.TBL\*)
Starwars Demolition (MagDemo41: STARWARS\SHELL\DEMOLOGO.BS+RESOURCE.TBL\*)
  000h 2    Width  (280h)       ;\extra header
  002h 2    Height (1E0h)       ;/
  004h 2    MDEC Size/4 (after huffman decompression) (rounded to 80h/4 bytes)
  006h 2    File ID (3800h)
  008h 2    Quantization step/factor (0000h..003Fh, for MDEC "DCT.bit10-15")
  00Ah 2    Version (1, 2, or 3) (2 is most common)
  00Ch ...  Huffman compressed data blocks (Cr,Cb,Y1,Y2,Y3,Y4, Cr,Cb,Y1,Y2..)


CDROM File Video Wacwac MDEC Streams

Wacwac uses different Huffman codes than BS videos, the decoder has some promising ideas that might yield slightly better compression than BS v3. However, it is used by only one known game:
  Aconcagua (JP) (2000 Sony/WACWAC!)
And even that game is only using it in two movies, and the movies are barely making any use of it: The 20Mbyte intro scene is a picture slide show (where the camera is zooming across twelve black and white images), the 50Mbyte ending scene is providing a more cinematic experience (the camera is scrolling through a text file with developer staff names).

Wacwac MDEC Stream Sectors
  000h 2    STR ID   (0160h)
  002h 2    STR Type WACWAC Tables (0002h=IntroTableSet, 0003h=EndingTableSet)
  004h 2    Sector number within current Frame (0000h..num-1)
  006h 2    Number of Sectors in this Frame
  008h 4    Frame number (6 or 11 and up, because 1st some frames are Polygons)
  00Ch 4    Frame Size in bytes
  010h 2    Bitmap Width  (always 140h)  ;\always 320x208 (in fact, the
  012h 2    Bitmap Height (always 0D0h)  ;/decoder is hardcoded as so)
  014h 4    Quant (0..3Fh) (same for all sectors within the frame)
  018h 8    Zerofilled
  020h 7E0h Raw Bitstream data (without Quant or BS header) (garbage padded)
Aconcagua has dozens of STR files with Polygon Streams. MDEC Streams are found only in two STR files for Intro and Ending scenes:
  Intro=Disc1:\ST01_01\STR_01_00.STR     Ending=Disc2:\ST09_01\STR_09_01.STR
  Leading zeroes (150 sectors)           Leading zeroes (150 sectors)
  Frame 0001h..0005h Polygon Frames      Frame 0001h..000Ah Polygon Frames
  Frame 0006h..0545h MDEC Frames 20MB    Frame 000Bh..0D79h MDEC Frames 50MB
  Frame 0546h..1874h Polygon Frames 48MB
Audio is normal XA-ADPCM, with the first audio sector occuring before 1st frame (after the leading zeropadded 150 sectors).

Wacwac Huffman Bitstreams
Wacwac uses little-endian bitstreams (starting with low bit in bit0 of first byte). To decode the separate blocks in the bitstream:
  Read Huffman code for DC, and output Quant*400h+(DC AND 3FFh)
  Read Huffman code for Size, aka num1,num2,num3 values for below reads
  Repeat num1 times: Read Huffman code for AC1, and output AC
  Repeat num2 times: Read Huffman code for AC2, and output AC
  Repeat num3 times: Read Huffman code for AC3, and output AC
  Output EOB (end of block)
The header/data lacks info about MDEC size after Huffman decompression, the worst case size for 320x208pix would be:
  14h*0Dh*6*41h*2+Align(80h)+Header(4) = 31880h+4 bytes
Note: The bitstream consists of separate 16x208pix slices (set DC for Cr,Cb,Y to zero at begin of each slice, and skip padding to 32bit-boundary at end of each slice).

Wacwac Huffman Table Sets
Aconcagua has two table sets, stored in PROGRAM.BIN (in compressed form, appearing as so: FF,90,16,2E,06,20,03,D6,etc). While watching the intro movie, the uncompressed sets can be found at these RAM locations:
  80112AF8h (1690h bytes)  ;Table Set for Intro Scene
  80114188h (1B68h bytes)  ;Table Set for Ending Scene
Each Table Set has a 38h-byte header, followed by five tables:
  000h 4    Table Set size (1690h or 1B68h)
  004h 4    Table Set exploded size (when allocating 16bit/DC, 32bit/Size/AC)
  008h 2    Size Table max Huffman size in bits  (0Ah or 09h)           ;\Size
  00Ah 2    Size Table number of entries         (40h)                  ;/
  00Ch 2    DC Table max Huffman size in bits    (0Bh)                  ;\
  00Eh 2    DC Table number of entries           (100h)                 ; DC
  010h 2    DC Huffman code Escape 10bit (non-relative 10bit DC value)  ;
  012h 2    DC Huffman size Escape 10bit (3 or 6, escape prefix size)   ;/
  014h 2    AC1 Table max Huffman size in bits   (0Eh or 0Bh)           ;\
  016h 2    AC1 Table number of entries          (0DAh or 100h)         ;
  018h 2    AC1 Huffman code Escape 7bit  (run=0bit, level=signed7bit)  ; AC1
  01Ah 2    AC1 Huffman code Escape 16bit (run=6bit, level=10bit)       ;
  01Ch 2    AC1 Huffman size Escape 7bit  (9 or 7, escape prefix size)  ;
  01Eh 2    AC1 Huffman size Escape 16bit (9 or 7, escape prefix size)  ;/
  020h 2    AC2 Table max Huffman size in bits   (0Eh)                  ;\
  022h 2    AC2 Table number of entries          (AAh or F4h)           ;
  024h 2    AC2 Huffman code Escape 8bit  (run=3bit, level=signed5bit)  ; AC2
  026h 2    AC2 Huffman code Escape 16bit (run=6bit, level=10bit)       ;
  028h 2    AC2 Huffman size Escape 8bit  (10 or 9, escape prefix size) ;
  02Ah 2    AC2 Huffman size Escape 16bit (10 or 9, escape prefix size) ;/
  02Ch 2    AC3 Table max Huffman size in bits   (0Eh)                  ;\
  02Eh 2    AC3 Table number of entries          (87h or B2h)           ;
  030h 2    AC3 Huffman code Escape 8bit  (run=4bit, level=signed4bit)  ; AC3
  032h 2    AC3 Huffman code Escape 16bit (run=6bit, level=10bit)       ;
  034h 2    AC3 Huffman size Escape 8bit  (10 or 9, escape prefix size) ;
  036h 2    AC3 Huffman size Escape 16bit (10 or 9, escape prefix size) ;/
  038h ..   Size Table (64bit per entry)    ;\
  ...  ..   DC Table   (32bit per entry)    ;
  ...  ..   AC1 Table  (64bit per entry)    ; Tables
  ...  ..   AC2 Table  (64bit per entry)    ;
  ...  ..   AC3 Table  (64bit per entry)    ;/
Size Table entries (64bit):
  0-1    Zero
  2-31   Huffman code (10bit max)
  32-39  Number of AC1 codes in this block  ;\implies End of Block (EOB)
  40-47  Number of AC2 codes in this block  ; after those AC codes
  48-55  Number of AC3 codes in this block  ;/
  56-63  Huffman size (1..10 bits)
DC Table entries (32bit):
  0-9    Relative DC Value (relative to old DC from memorized Cr,Cb,Y)
  10-15  Huffman size (1..11 bits)
  16-31  Huffman code (11bit max)
 Notes: For the relative DC's, the decoder does memorize DC for Cr,Cb,Y upon
  decoding Cr,Cb,Y1,Y3 (but does NOT memorize DC when decoding Y2,Y4).
  Initial DC for Cr,Cb,Y is zero at begin of each 16x208pix slice.
 Obscurities: The decoder does accidentally use bit10 to sign-expand the
  DC value in bit0-9 (but does mask-off those bugged sign bits thereafter),
  and the decoder does uselessly memorize Y1 and Y3 separately (but uses only
  the most recently memorized value).
AC1/AC2/AC3 Table entries (64bit):
  0-1    Zero
  2-31   Huffman code (14bit max)
  32-47  MDEC code (6bit run, and 10bit AC level)
  48-63  Huffman size (1..14 bits)
The Escape codes are stored in the 38h-byte Table Set header (instead of in the tables), the init function uses that info for patching escape-related opcodes in the decoder function (that would allow to omit table lookups upon escape codes; the decoder doesn't actually omit such lookups though).
To simplify things, one could store the escape codes in the tables (eg. using special MDEC values like FC00h+35h for run=3bit, level=signed5bit).


CDROM File Video Polygon Streaming

Ape Escape - Polygon Streaming
Used by Ape Escape (Sony 1999) (DEMO\*.STR and some STR\*.STR files and KKIIDDZZ.HED\STR\0006h and up).
The files start with zerofilled sectors (without STR headers), followed by sectors with STR headers with [00h]=0160h, [02h]=8001h (same values as for MDEC), but with [10h..1Fh]=zero (without resolution/header info). And the data at [20h] starts with something like 14h,00h,03h,FFh,2Ah,02h,00h,00h.
That data seems to consist of polygon coordinates/attributes that are rendered as movie frames. The texture seems to be stored elsewhere (maybe in the .ALL files that are bundled with some .STR files).

Panekit - Polygon Streaming
Panekit STR seems to use Polygon Streaming (except 1st some Megabytes are MDEC).

Aconcagua - Polygon Streaming
Aconcagua STR does use Polygon Streaming (except first+last movie are MDEC).

Cyberia (1996) (TF\STR\*.STR)
Cyberia is using Software-rendering for both movies and in-game graphics. That is, PSX hardware features like MDEC, GTE, and GPU-Polygons are left all unused, and the GPU is barely used for transferring data from CPU to VRAM.
The STR header for software-rendered movie frames looks as so:
  000h 2    STR ID   (0160h)
  002h 2    STR Type (0002h=Custom, Software rendering)
  004h 2    Sector number within current Frame (0..num-1)
  006h 2    Number of Sectors in this Frame    (varies)
  008h 4    Frame Number (1=First)
  00Ch 4    Frame Size in Bytes/4 (note: first frame in MAP*.STR is quite big)
  010h 2    Rendering Width  (0140h)
  012h 2    Rendering Height (00C0h)
  014h 0Ch  Unknown (zerofilled or random garbage)
  020h 7E0h Custom data for software rendering
Note: First sector of First frame does usually have byte[22h]=88h (except FINMUS.STR). The Custom data part is often have garbage padding (such like ASCII strings with "c2str" command line tool usage instructions).

Croc 1 (CUTS\*.AN2)
Probably cut-scenes with polygon animations. The files seem to contain 2300h-byte data frames (plus XA-ADPCM sectors inserted here and there).
  000h 4     Number of remaining frames
  ...  22FCh Unknown data (zeropadded if smaller)

 _______________ Unknown Streaming Data (Polygons or whatever) ________________

Custom STR - 3D Baseball (BIGFILE.FOO)
This is used for several files in 3D Baseball (BIGFILE.FOO):
  BIGFILE.FOO\0151h\0005h,0009h,000Fh,0017h,001Bh, 02E5h,02E9h,..,0344h,0348h
  BIGFILE.FOO\0152h\0186h,018Ch,0192h,0198h)
  BIGFILE.FOO\0153h\029Ah,02A0h,02A6h,02ACh)
The files contain some kind of custom streaming data, with custom STR header, and data containing increasing/decreasing bytes... maybe non-audio waveforms?
  000h 2    STR ID   (0160h)
  002h 2    STR Type (0001h=Custom)
  004h 2    Sector number within current Frame (always 0)
  006h 2    Number of Sectors in this Frame    (always 1)
  008h 4    Frame Number (1=First)
  00Ch 4    Frame Size (6FAh or 77Ah, sometimes 17Ah or 1FAh or 20Ah)
  010h 2    Unknown (280h, or sometimes 300h or 340h)
  012h 2    Frame Time (0=First, increases with step [19h], usually +5 or +7)
  014h 2    Unknown (280h, or sometimes 300h or 3C0h, or 0)
  016h 1    Frame Time (same as [012h] AND FFh)
  017h 1    Unknown (0 or 1)
  018h 1    Unknown (40h, or 80h, or C0h)
  019h 1    Duration? (5 or 7, or sometimes less, step for Frame Time)
  01Ah 1    Unknown (3, or less in last some frames)
  01Bh 5    Zerofilled
  020h 7E0h Data (increasing/decreasing bytes... maybe non-audio waveforms?)

Army Men Air Attack 2 (MagDemo40: AMAA2\*.PMB)
  000h 2     STR ID   (0160h)
  002h 2     STR Type (0000h=Custom)
  004h 2     Sector number within current Frame (0..2)
  006h 2     Number of Sectors in this Frame    (always 4) (3xSTR + 1xADPCM)
  008h 4     Frame Number (1=First)
  00Ch 4     Frame Size? (800h, despite of having 3 sectors with 7E0h each?)
  010h 2     Unknown (00h or 01h)
  012h 2     Unknown (A3h or ABh ... 6Ch or 7Bh ... or 43h or 49h)
  014h 2     Sector number within current Frame (0..2) (same as [004h])
  016h 0Ah   Zerofilled
  020h 7E0h  Data (polygon streaming or so?)
Note: The .PMB file is bundled with a .PMH file, which might contain header info?

Bits Laboratory games (Charumera, and True Love Story series)
  Charumera                  ENDING.XA     (with dummy/zero data)
  True Love Story            TLS\MULTI.XA  (with nonzero data)
  True Love Story 2          TLS2\ENDING.STR and TLS2\MULTI.XA
  True Love Story Fan Disc            ;\probably use that format, too
  True Love Story: Remember My Heart  ;/(not verified)
The STR headers have STR ID=0160h and STR Type=0001h, STR header[10h..1Fh] contains nonsense BS video info (with BS ID=3800h, although there isn't any BS data in the actual data part at offset 20h and up).
The files do mainly contain XA-ADPCM sectors, plus some STR sectors in non-MDEC format. Unknown if that STR sectors are separate channels, or if they are used in parallel with the XA-ADPCM channel(s).
Unknown what the STR sectors are used for (perhaps Polygon Streaming, audio subtitles, or simple garbage padding for unused audio sectors). In some files, the STR sectors appear to be just dummy padding (STR header plus zerofilled data area).

Nightmare Project: Yakata
This game has normal MDEC Streams, and Special Streams in non-MDEC format (eg. Disc1, File 0E9h-16Eh and 985h-B58h), perhaps containing Polygon Streams or whatever.
There are two channels (file=1/channel=00h-01h), each channel contains data that consists of 5 sectors per frame (1xHeader plus 4xData). The sectors have STR ID=0160h, and STR Type as follows:
  0000h=Whatever special, channel 0 header (sector 0)
  0400h=Whatever special, channel 1 header (sector 1)
  0001h=Whatever special, channel 0 data   (sector 2,4,6,8)
  0401h=Whatever special, channel 1 data   (sector 3,5,7,9)

Eagle One: Harrier Attack STR files
  \*.STR            MDEC movies  ;\BS fraquant (except, demo version
  \DATA*\*.STR      MDEC movies  ;/            on MagDemo31 uses mormal BS v2)
  \DATA*\M*\L*.STR  Multi-language TXT files with STR header on each sector
  \DATA*\M*\I*.STR  unknown binary data (whatever and SPU-ADPCM)
  \LANGN.STR        unknown binary data (whatever)
All of the above have STR Type=8001h (but only the MDEC movies have BS ID 3800h; the MDEC movies start with 13 zerofilled sectors that are all zeroes without any STR/BS headers).


CDROM File Audio Single Samples VAG (Sony)

VAG audio samples
PSX Lightspan Online Connection CD, cdrom:\CD.TOC:\UI*\*.VAG
PSX Wipeout 2097, cdrom:\WIPEOUT2\SOUND\SAMPLES.WAD:\*.vag (version=02h)
PSX Perfect Assassin, DATA.JFS:\AUDIO\*.VAG and DATA.JFS:\SND\*.VAG
  000h 4    File ID (usually "VAGp")
  004h 4    Version (usually 02h, or 20h)                       (big-endian)
  008h 4    Reserved (0) (except when ID="VAGi")                (big-endian)
  00Ch 4    Channel Size (data size... per channel?)            (big-endian)
  010h 4    Sample Rate (in Hertz) (eg. 5622h=22050Hz)          (big-endian)
  014h 0Ch  Reserved (0) (except when version=2)
  020h 10h  Name (ASCII, zeropadded)
  ... (..)  Optional ID string (eg. "STEREO" in upper/lowercase)
  ... (..)  Optional Padding to Data start
  ...  ..   ADPCM Data for channel(s) (usually at offset 030h)
VAG files are used on PSX, PSP, PS2, PS3, PS4. The overall 1-channel mono format is same for consoles. But there are numerous different variants for interleaved 2-channel stereo data.

VAG Filename Extensions
  .vag      default (eg. many PSX games)
  .vig      2-channel with interleave=10h (eg. PS2 MX vs ATV Untamed)
  .vas      2-channel with interleave=10h (eg. PS2 Kingdom Hearts II)
  .swag     2-channel with interleave=filesize/2 (eg. PSP Frantix)
  .l and .r 2-channel in l/r files (eg. PS2 Gradius V, PS2 Crash Nitro Kart)
  .str      whatever (eg. P?? Ben10 Galactic Racing)
  .abc      whatever (eg. PSP F1 2009 (v6), according to wiki.xentax.com)

VAG File IDs (header[000h])
  "VAGp"  default (eg. many PSX games)
  "VAG1"  1-channel (eg. PS2 Metal Gear Solid 3)
  "VAG2"  2-channel (eg. PS2 Metal Gear Solid 3)
  "VAGi"  2-channel interleaved (eg. ?)
  "pGAV"  little endian with extended header (eg. PS2 Jak 3, PS2 Jak X)
  "AAAp"  extra header, followed by "VAGp" header (eg. PS2 The Red Star)

VAG Versions (header[004h])
  00000000h   v1.8 PC
  00000002h   v1.3 Mac (eg. PSX Wipeout 2097, in SAMPLES.WAD)
  00000003h   v1.6+ Mac
  00000020h   v2.0 PC (most common, eg. PSX Perfect Assassin)
  00000004h   ? (later games, uh when/which?)
  00000006h   ? (vagconf, uh when/which?)
  00020001h   v2.1 (vagconf2)   ;\with HEVAG coding instead SPU-ADPCM
  00030000h   v3.0 (vagconf2)   ;/(eg. PS4/Vita)
  40000000h   ? (eg. PS2 Killzone) (1-channel, little endian header)

Reserved Header entries for ID="VAGi"
  008h 4  Interleave (little endian) (the other header entries are big endian)

Reserved Header entries for Version=00000002h (eg. PSX Wipeout 2097)
This does reportedly contain some default "base" settings for the PSX SPU:
  014h 2  Volume left                    4Eh,82h  ;-Port 1F801C00h
  016h 2  Volume right                   4Eh,82h  ;-Port 1F801C02h
  018h 2  Pitch (includes fs modulation) A8h,88h  ;-Port 1F801C04h +extra bit?
  01Ah 2  ADSR1                          00h,00h  ;-Port 1F801C08h
  01Ch 2  ADSR2                          00h,E1h  ;-Port 1F801C0Ah
  01Eh 2  ?                              A0h,23h  ;-Port 1F801C0xh maybe?

Reserved Header entries for Version=00000003h (according to wiki.xentax.com)
  01Eh 1  Number of channels (0 or 1=Mono, 2=Stereo)

Reserved Header entries for Version=00020001h and Version=00030000h
  01Ch 2  Zero                                      ;if non-zero: force Mono
  01Eh 1  Number of channels (0 or 1=Mono, 2=Stereo ;if 10h..FFh: force Mono
  01Fh 1  Zero                                      ;if non-zero: force Mono
Unknown if the above "force Mono" stuff is really needed (maybe it was intended to avoid problems with Version=00000002h, and maybe never happens in Version=00000003h and up)?

VAG ADPCM Data
The ADPCM data uses PSX SPU-ADPCM encoding (even on PS2 and up, except PS4 with Version=0002001h or Version=00030000h, which do use HEVAG encoding).
SPU ADPCM Samples
The data does usually start at offset 0030h (except, some files have extra header data or padding at that location).
The first 10h-byte ADPCM block is usually all zero (used to initialize the SPU).
2-channel (stereo) files are usually interleaved in some way.

VAG Endiannes
The file header entries are almost always big-endian (even so when used on little endian consoles). There are a few exceptions:
ID="VAG1" has little endian [008h]=Interleave (remaining header is big-endian).
ID="pVAG" has (some?) header entries in little endian.
Version=40000000h has most or all header entries in little endian (perhaps including the version being meant to be 00000040h).

VAG Channels
VAGs can be 1-channel (mono) or 2-channel (stereo). There is no standarized way to detect the number of channels (it can be implied in the Filename Extension, Header ID, in Reserved Header entries, in the Name string at [020h..02Fh], in optional stuff at [030h], or in a separate VAG Header in the middle of the file).

VAG Interleave
  None       default (for 1-channel mono) (and separate .l .r stereo files)
  800h       when ID="VAG2"
  [008h]     when ID="VAGi" (little-endian 32bit header[008h])
  1000h      when ID="pGAV" and [020h]="Ster" and this or that
  2000h      when ID="pGAV" and [020h]="Ster" and that or this
  10h        when filename extension=".vig"
  10h        when Version=0002001h or Version=00030000h (and channels=2)
  filesize/2 when filename extension=".swag"
  6000h      when [6000h]="VAGp" (eg. PSX The Simpsons Wrestling)
  1000h      when [1000h]="VAGp" (eg. PS2 Sikigami no Shiro)
  ...

AAAp Header
  000h 4     ID "AAAp"
  004h 2     Interleave
  006h 2     Number of Channels (can be 1 or 2?)
  008h 30h*N VAGp header(s) for each channel, with Version=00000020h
  ...  ..    ADPCM Data (interleaved when multiple channels)

See also
http://github.com/vgmstream/vgmstream/blob/master/src/meta/vag.c ;very detailed
http://wiki.xentax.com/index.php/VAG_Audio ;rather incomplete and perhaps wrong


CDROM File Audio Sample Sets VAB and VH/VB (Sony)

VAB vs VH/VB
  .VAB  contains VAB header, and ADPCM binaries   ;-all in one file
  .VH   contains only the VAB header              ;\in two separate files
  .VB   contains only the ADPCM binaries          ;/
PSX Perfect Assassin has some v7 .VH/.VB's (in \DATA.JFS:\SND\*.*)
PSX Resident Evil 2, COMMON\DATA\*.DIE (contains .TIM+.VAB badged together)
PSX Spider-Man, CD.HED\l2a1.vab is VAB v5 (other VABs in that game are v7)
PSX Tenchu 2 (MagDemo35: TENCHU2\VOLUME.DAT\5\* has VAB v20h, maybe a typo)

VAB Header (VH)
  0000h 4       File ID ("pBAV")
  0004h 4       Version (usually 7) (reportedly 6 exists, too) (5, 20h exists)
  0008h 4       VAB ID (usually 0)
  000Ch 4       Total .VAB filesize in bytes (or sum of .VH and .VB filesizes)
  0010h 2       Reserved (EEEEh)
  0012h 2       Number of Programs, minus 1 (0000h..007Fh = 1..128 programs)
  0014h 2       Number of Tones, minus? (max 0800h?) (aka max 10h per program)
  0016h 2       Number of VAGs, minus? (max 00FEh)
  0018h 1       Master Volume (usually 7Fh)
  0019h 1       Master Pan    (usually 40h)
  001Ah 1       Bank Attribute 1 (user defined) (usually 00h)
  001Bh 1       Bank Attribute 2 (user defined) (usually 00h)
  001Ch 4       Reserved (FFFFFFFFh)
  0020h 800h    Program Attributes 10h-byte per Program 00h..7Fh   (fixed size)
  0820h P*200h  Tone Attributes 200h-byte per Program 00h..P-1  (variable size)
  xx20h 200h    16bit VAG Sizes (div8) for VAG 00h..FFh            (fixed size)
  xx20h (...)   ADPCM data (only in .VAB files, otherwise in separate .VB file)
Program Attributes (10h-byte per Program, max 80h programs)
  000h 1      tones        Number of Tones in the Program (Yaroze: 4) (uh?)
  001h 1      mvol         Master Volume   (Yaroze: 0..127)
  002h 1      prior                        (Yaroze: N/A)
  003h 1      mode                         (Yaroze: N/A)
  004h 1      mpan         Master Panning  (Yaroze: 0..127)
  005h 1      reserved0
  006h 2      attr                         (Yaroze: N/A)
  008h 4      reserved1
  00Ch 4      reserved2
Tone Attributes (20h-byte per Tone, max 10h tones per Program)
  000h 1      prior        Tone Priority   (Yaroze: 0..127, 127=highest)
  001h 1      mode         Mode            (Yaroze: 0=Normal, 4=Reverberation)
  002h 1      vol          Tone Volume     (Yaroze: 0..127)
  003h 1      pan          Tone Panning    (Yaroze: 0..127)
  004h 1      center       Centre note (in semitone units) (Yaroze: 0..127)
  005h 1      shift        Centre note fine tuning         (Yaroze: 0..127)
  006h 1      min          Note limit minimum value     (Yaroze: 0..127)
  007h 1      max          Note limit maximum value     (Yaroze: 0..127)
  008h 1      vibW                                      (Yaroze: N/A)
  009h 1      vibT                                      (Yaroze: N/A)
  00Ah 1      porW                                      (Yaroze: N/A)
  00Bh 1      porT                                      (Yaroze: N/A)
  00Ch 1      pbmin        Max? value for downwards pitchbend  (Yaroze: 0..127)
  00Dh 1      pbmax        Max value for upwards pitchbend     (Yaroze: 0..127)
  00Eh 1      reserved1
  00Fh 1      reserved2
  010h 2      ADSR1        Attack,Decay    (Yaroze: 0..127,0..15)
  012h 2      ADSR2        Release,Sustain (Yaroze: 0..127,0..31)
  014h 2      prog         Program number that tone belongs to (Yaroze: 0..127)
  016h 2      vag          VAG number                          (Yaroze: 0..254)
  018h 8      reserved

VAB Binary (VB) (ADPCM data) (to be loaded to SPU RAM)
This can contain max 254 "VAG files" (maybe because having two (?) reserved 8bit numbers?).
Sony wants the total size of the ADPCM data to be max 7E000h bytes (which would occupy most of the 512Kbyte SPU RAM, leaving little space for the echo buffer or additional effects).
Note: The "VAG files" inside of VAB/VB are actually raw SPU-ADPCM data, without any VAG file header. The first 10h-byte ADPCM block is usually zerofilled.


CDROM File Audio Sequences SEQ/SEP (Sony)

SEQ - Single Sequence
.SEQ contains MIDI-style sequences, the samples for the instruments can be stored in a separate .VAB file (or .VH and .VB files).
Used by Perfect Assassin, DATA.JFS:\SND\*.SEQ (bundled with *.VH and *.VB)
Used by Croc (MagDemo02: CROC\CROCFILE.DIR\AMBI*.BIN, MAP*.BIN, JRHYTHM.BIN)
Used by many other games.
  000h 4   File ID "pQES"
  004h 4   Version (1)                    (big endian?)
  008h 2   Resolution per quarter note      (01h,80h)
  00Ah 3   Tempo 24bit (8bit:16bit maybe?)  (07h,27h,0Eh)
  00Dh 2   Rhythm (NN/NN)                   (04h,02h)
  00Fh ... Score data, uh?    (with many MIDI KeyOn's: xx,9x,xx,xx)
  ...  3   End of SEQ (2Fh=End of Track)    (FFh,2Fh,00h)
The "Score data" seems to be more or less same as in Standard Midi Format (.smf files), ie. containing timing values and MIDI commands/parameters.

SEP - Multi-Track Sequences
This is a simple "archive" with several SEQ-like sequences.
  000h 4   File ID "pQES"  ;same ID as in .SEQ files (!)
  004h 2   Version (0)     ;value 0, and only 16bit, unlike .SEQ files
  006h ..  1st Sequence
  ...  ..  2nd Sequence
  ...  ..  etc.
Sequences:
  000h 2   Sequence ID (0000h and up)     (big endian)        ;-ID number
  002h 2   Resolution per quarter note      (01h,80h)         ;\
  004h 3   Tempo 24bit                      (07h,27h,0Eh)     ; as in SEQ files
  007h 2   Rhythm (NN/NN)                   (04h,02h)         ;/
  009h 4   Data size (big endian, from 00Dh up to including End of SEQ(
  00Dh ... Score data, uh?                  (...)             ;\as in SEQ files
  ...  3   End of SEQ (2Fh=End of Track)    (FFh,2Fh,00h)     ;/
Used by Hear It Now (Playstation Developer's Demo) (RCUBE\RCUBE.SEP)
Used by Rayman (SND\BIGFIX.ALL\0002)
Used by Monster Rancher (MagDemo06, MR_DEMO\DATA\MF_DATA.OBJ\025B)
Used by Rugrats (MagDemo19: RUGRATS\DB02\*.SEP and MENU\SOUND\SEPS\*.SEP)
Used by Rugrats Studio Tour (MagDemo32: RUGRATS\DATA\SEPS\*.SEP)
Used by Monkey Hero (MagDemo17: MONKEY\BIGFILE.PSX}*.SEP)
Used by Pitfall 3D
Used by Blue's Clues: Blue's Big Musical (SEPD chunks in *.TXD)


CDROM File Audio Other Formats

 __________________________ .SQ .HD .HD (SSsq/SShd) ___________________________

This is a newer Sony format from 1999 (resembling the older .SEQ .VH .VB format).
Used by Alundra 2, Ape Escape, Arc the Lad 3, Koukidou Gensou - Gunparade March, Omega Boost, PoPoLoCrois Monogatari II, The Legend of Dragoon, Wild Arms 2.
  .SQ Sequence Data (with ID "SSsq")
  .HD Voice Header  (with ID "SShd")
  .BD Voice Binary  (raw SPU-ADPCM, same as .VB)

Sequence Data (*.SQ)
  000h 2       Unknown (always 64h)
  002h 2       Unknown (always 1E0h)
  004h 1?      Unknown (varies)
  005h 7       Zerofilled
  00Ch 4       ID "SSsq"
  010h 10h*10h Unknown Table
  110h ..      Unknown Data

Voice Header (*.HD)
  000h 4     Size of the .HD file itself
  004h 4     Size of the corresponding .BD file
  008h 4     Zero
  00Ch 4     ID "SShd"
  010h 1Ch*4 Offsets to data (or FFFFFFFFh=None)
  080h ..    Data

Voice Bonary (*.BD) (same as .VB files)
  000h ..    SPU-ADPCM data (usually starting with zerofilled 10h-byte block)

 ____________________________ DNSa/PMSa/FNSa/FMSa _____________________________

There are four four file types:
  "DNSa"  (aka SouND backwards)         ;sequence data
  "PMSa"  (aka SaMPles backwards)       ;samples with small header
  "FMSa"  (aka SaMples-F... backwards)  ;samples with bigger header   ;\Legacy
  "FNSa"  (aka SouNd-F... backwards)    ;whatever tiny file           ;/of Kain
Used by several games (usually inside of BIGFILE.DAT):
  Akuji (MagDemo18: AKUJI\BIGFILE.DAT\*) (DNSa,PMSa)
  Gex 2 (MagDemo08: GEX3D\BIGFILE.DAT\*) (DNSa)
  Gex 3: Deep Cover Gecko (MagDemo20: G3\BIGFILE.DAT\*) (DNSa,PMSa)
  Legacy of Kain 2 (MagDemo13: KAIN2\BIGFILE.DAT\*) (DNSa)
  Legacy of Kain 2 (MagDemo26: KAIN2\BIGFILE.DAT\*) (DNSa,PMSa,FNSa,FMSa)
  Walt Disney World Racing Tour (MagDemo35: GK\BIGFILE.DAT\*) (DNSa,PMSa)
Note: The exact file format does reportedly differ in each game.

"PMSa" (aka SaMPles backwaords)
  000h 4     ID "PMSa"
  004h 4     Total Filesize
  008h 8     Zerofilled
  010h ..    SPU-ADPCM data (usually starting with zerofilled 10h-byte block)

"DNSa" (aka SouND backwards)
  000h 4      ID "DNSa"   ;aka SND backwards
  004h 2      Offset from DNSa+4 to 8-byte entries (can be odd)
  006h 1      Unknown (3)
  007h 1      Number of 8-byte entries   (N1)
  008h 1?     Number of 10h-byte entries (N2)
  ...  ..     Unknown (..)
  ...  N1*8   Whatever 8-byte entries
  ...  N2*10h Whatever 10h-byte entries
  ...  ..     ... circa 40h 4-byte entries...?
  ...  ..     Unknown (..)
  ...  ..     Several blocks with ID "QESa" or "QSMa"  ;supposedly MIDI-style?

"FNSa" (aka SouNd-F... backwards)
These are whatever tiny files (with filesize 1Ch or 2Ch).
  000h 4     ID "FNSa"
  ...  ..    Unknown

"FMSa" (aka SaMples-F... backwards)
  000h 4     ID "FMSa"
  008h ..    Unknown..
  ...  ..    SPU-ADPCM data (usually starting with zerofilled 10h-byte block)

 ____________________________________ AKAO ____________________________________

There a several games that have sound files with ID "AKAO".
  XXX does that include different AKAO formats... for Samples and Midi?
AKAO is also used in several streaming movies:
CDROM File Video Streaming Audio

 ___________________________________ Others ___________________________________

Alone in the Dark IV has MIDB and DSND chunks (which contain sound files).

See also
The page below does mention several PSX sound formats, plus some open source & closed source tools for handling those files.
github.com/loveemu/vgmdocs/blob/master/Conversion_Tools_for_Video_Game_Music.md


CDROM File Audio Streaming XA-ADPCM

Audio Streaming (XA-ADPCM)
Audio streaming is usually done by interleaving the .STR or .BS file's Data sectors with XA-ADPCM audio sectors (the .STR/.BS headers don't contain any audio info; because XA-ADPCM sectors are automatically decoded by the CDROM controller).
Raw XA-ADPCM files (without video) are usually have .XA file extension.


CDROM File Audio CD-DA Tracks

The eleven .SWP files in Wipeout 2097 seem to be CD-DA audio tracks.
The one TRACK01.WAV in Alone in the Dark, too?
Other than that, tracks can be accessed via TOC instead of filenames.


CDROM File Archives with Filename

 _______________________________ Entrysize=08h ________________________________

WWF Smackdown (MagDemo33: TAI\*.PAC)
  000h 4     ID ("DPAC")                                        ;\
  004h 4     Unknown (100h)                                     ;
  008h 4     Number of files (N)                                ;
  00Ch 4     Directory Size (N*8)                               ; Header
  010h 4     File Data area size (SIZE = Totalsize-Headersize)  ;
  014h 4     Unknown (1)                                        ;
  018h 7E8h  Zerofilled (padding to 800h-byte boundary)         ;
  800h N*8   File List                                          ;
  ...  ..    Zerofilled (padding to 800h-byte boundary)         ;/
  ...  SIZE  File Data area                                     ;-Data area
 File List entries:
  000h 8     Filename ("NAME")
  004h 2     File Offset/800h (increasing)
  006h 2     File Size/800h
The DPAC archives can contain generic files (eg .TIM) and child archives (in a separate archive format, with ID "PAC ").

 _______________________________ Entrysize=10h ________________________________

Championship Motocross (MagDemo25: SMX\RESHEAD.BIN and RESBODY.BIN)
RESHEAD.BIN:
  000h N*10h  File List (220h bytes)
 File List entries:
  000h 8      Filename ("FILENAME", if shorter: terminated by 00h plus garbage)
  008h 4      Filesize in bytes
  00Ch 4      Offset/800h in RESBODY.BIN (increasing) (or FFFFFFFFh if Size=0)
RESBODY.BIN:
  000h ..     File Data (referenced from RESHEAD.BIN)

One (DIRFILE.BIN\w*\sect*.bin)
  000h N*10h File List
  ...  ..    File Data area
 File List entries:
  000h 0Ch   Filename (eg. "FILENAME 001")     ;for last entry: "END      000"
  00Ch 4     Offset (increasing, N*10h and up) ;for last entry: zero

True Love Story 1 and 2 (TLS*\MCD.DIR and MCD.IMG)
MCD.DIR:
  000h N*10h  File List
  ...  10h    End marker (FFh-filled)
 File List entries:
  000h 8      Filename (zeropadded if less than 8 chars)
  008h 2      Zero (0000h)
  00Ah 2      Size/800h
  00Ch 4      Offset/800h in MCD.IMG
 Note: Filenames are truncated to 8 chars (eg. "FOREST.T" instead "FOREST.TIM")
MCD.IMG:
  000h ..     File Data area (encrypted in True Love Story 2)
In True Love Story 2, the MCD.IMG data is encrypted as follows:
 init_key_by_filename(name):    ;for MCD.IMG (using filenames from MCD.DIR)
  i=0, key0=0001h, key1=0001h, key2=0001h
  while i<8 and name[i]<>00h
    key0=(key0 XOR name[i])
    key1=(key1 * name[i]) AND FFFFh
    key2=(key2 + name[i]) AND FFFFh
  ret
 init_key_by_numeric_32bit_seed(seed):  ;maybe for LINEAR.IMG and PICT.IMG ?
  key0=(seed) AND FFFFh
  key1=(seed - (seed*77975B9h/400000000h)*89h) AND FFFFh
  key2=(seed - (seed*9A1F7E9h/20000000000h)*3527h) AND FFFFh
  ret
 decrypt_data(addr,len):
  for i=1 to len/2
    key2=key2/2 + (key0 AND 1)*8000h
    key0=key0/2 + (key1 AND 1)*8000h
    key1=key1/2 + ((key1/2 OR key0) AND 1)*8000h
    key0=((((key1+47h) AND FFFFh)/4) XOR key0)+key2+(((key1+47h)/2) AND 1)
    halfword[addr]=halfword[addr] XOR key0, addr=addr+2
  ret
The MCD.* files don't contain any encryption flag. Below are some values that could be used to distinguish between encrypted and unencrypted MCD archives (though that may fail in case of any other games/versions with other values):
  Item                    Unencrypted   Encrypted
  Parent Folder name      "TLS"         "TLS2"
  First name in MCD.DIR   "BACKTILE"    "TEST.RPS"
  First word in MCD.IMG   00000010h     074D4C8Ah

Star Wars Rebel Assault 2 (RESOURCE.*, and nested therein)
BallBlazer Champions (*.DAT, and nested therein)
The Rebel RESOURCE.* files start with name "bigEx" or "fOFS", BallBlazer *.DAT start with "SFXbase" or "tpage", nested files start with whatever other names.
  000h N*10h File List
  ...  (4)   CRC32 on above header (Top-level only, not in Nested archives)
  ...  ..    File Data area
  ...  (..)  Huge optional padding to xx000h-byte boundary (in BallBlazer .DAT)
 File List entries in Top-level archives (with [0Ch].bit31=1):
  000h 8     Filename (zeropadded if less than 8 chars)
  008h 4     Decompressed Size (or 0=File isn't compressed)
  00Ch 4     Offset, self-relative from current List entry (plus bit31=1)
 File List entries in Nested archives (with [0Ch].bit31=0):
  000h 0Ch   Filename (zeropadded if less than 12 chars)
  00Ch 4     Offset, self-relative from current List entry (plus bit31=0)
 Last File List entry has [00h..0Bh]=zerofilled, and Offset to end of file.
Uncompressed Data Format (when List entry [08h]=0 or [0Ch].bit31=0):
  000h ..    Uncompressed Data
  ...  ..    CRC32 on above Data (Top-level only, not in Nested archives)
Compressed Data Format (when List entry [08h]>0 and [0Ch].bit31=1)::
  000h 1     Compression Method (01h=LZ/16bit, 02h=LZ/24bit)
  001h 3     Decompressed Size (big-endian)
  004h ..    Compressed Data
  ...  ..    Zeropadding to 4-byte boundary
  ...  ..    CRC32 on above bytes (method, size, compressed data, padding)
CDROM File Compression RESOURCE (Star Wars Rebel Assault 2)

 _______________________________ Entrysize=14h ________________________________

Fighting Force (MagDemo01: FGHTFRCE\*.WAD)
  000h 4     Number of files                                  (big endian)
  004h N*14h File List
  ...  ..    File Data
File List entries:
  000h 0Ch   Filename ("FILENAME.EXT", zeropadded if shorter than 12 chars)
  00Ch 4     Filesize in bytes (can be odd)                   (big endian)
  010h 4     Fileoffset in bytes (increasing, 4-byte aligned) (big endian)

Parappa (MagDemo01: PARAPPA\*.INT)
Um Jammer Lammy (MagDemo24: UJL\*.INT)
  0000h 2000h Folder 1
  2000h ..    File Data for Folder 1
  ...   2000h Folder 2
  ...   ..    File Data for Folder 2
  ...   2000h Folder End marker (FFFFFFFFh, plus zeropadding)
Folder entries:
  0000h 4      Folder ID (increasing, 1,2,3, or FFFFFFFFh=End)
  0004h 4      Number of files (max 198h) (N)
  0008h 4      File Data Area size/800h   (S)
  000Ch 4      Zero (0)
  0010h N*14h  File List
  ...   ..     Zeropadding to 2000h
  2000h S*800h File Data Area for this folder
File List entries:
  000h 4     Filesize in bytes
  004h 10h   Filename (FILENAME.EXT, zeropadded)
File Offsets are always 4-byte aligned (required for Um Jammer Lammy, which contains Filesizes that aren's multiples of 4).
Note: There can be more than one folder with same ID (ie. when having more than 198h TIM files, which won't fit into a single 2000h-byte folder).

Gran Turismo 1 (MagDemo10: GT\BG.DAT\*, GT\COURSE.DAT\*)
Gran Turismo 1 (MagDemo15: GT\BG.DAT\*, GT\COURSE.DAT\*)
JumpStart Wildlife Safari Field Trip (MagDemo52: DEMO\DATA.DAT\*.DAT)
These are child archives found inside of the main GT-ARC and DATA.DAT archives.
  000h 4     Number of Files (eg. 26h) (usually at least 02h or higher)
  004h N*14h File List
  ...  ..    File Data area
 File List entries:
  000h 10h   Filename ("FILENAME.EXT", zeropadded if shorter)
  010h 4     Offset in bytes (increasing, 4-byte-aligned?)

Croc 2 (MagDemo22: CROC2\CROCII.DAT and CROCII.DIR)
Disney's The Emperor's New Groove (MagDemo39: ENG\KINGDOM.DIR+DAT)
Disney's Aladdin in Nasira's Revenge (MagDemo46: ALADDIN\ALADDIN.DIR+DAT)
 DIR:
  000h 4     Number of Entries (0Eh)
  004h N*14h File List
 DAT:
  000h ..    File Data (referenced from CROCII.DIR)
File List entries:
  000h 0Ch   Filename ("FILENAME.EXT", zeropadded if shorter)
  00Ch 4     File Size in bytes
  010h 4     File Offset in .DAT file (800h-byte aligned, increasing)

Alice in Cyberland (ALICE.PAC, and nested .PAC, .FA, .FA2 archives)
  000h N*14h File List
  ...  14h   Zerofilled (File List end marker)
  ...  ..    File Data area
 File List entries:
  000h 0Ch   Filename ("FILENAME.EXT", zeropadded if shorter)
  00Ch 4     Offset (increasing, 4-byte aligned)
  010h 4     Filesize in bytes (can be odd, eg. for .FA2 files)
PAC and FA are uncompressed, FA2 is compressed via some LZ5-variant:
CDROM File Compression LZ5 and LZ5-variants

Interplay Sports Baseball 2000 (MagDemo22:BB2000\DATA\HOG.TOC\UNIFORMS\*.UNI)
  000h N*14h  File List (3Ch*14b bytes, unused entries are zeropadded)
  4B0h ..     Data area (TIM files for player uniforms)
 File List entries:
  000h 10h    Filename ("FILENAME.EXT", zeropadded)
  010h 4      Offset (zerobased, from begin of Data area, increasing)

 _______________________________ Entrysize=18h ________________________________

Invasion from Beyond (MagDemo15: IFB\*.CC)
  000h 0Ch   Fixed ID (always "KotJCo01Dir ") (always that same string)
  00Ch 4     Number of Files
  010h N*18h File List
  ...  ..    File Data area
File List entries:
  000h 10h   Filename ("FILENAME.EXT", zeropadded)
  010h 4     Offset in bytes (increasing, 1-byte or 4-byte aligned)
  014h 4     Filesize in bytes (can be odd)
Note: Alignment is optional: Files in IFB\HANGAR\*.CC and IFB\MAPS\*.CC use 4-byte aligned offsets (but may have odd filesizes). Files in IFB\INCBINS\*.CC don't use any alignment/padding.

Ghost in the Shell (MagDemo03: GITSDEMO\S01\*.FAC)
  000h N*18h File List (18h-bytes each)
  ...  18h   File List end marker (zerofilled)
  ...  ..    File Data
File List entries:
  000h 1     Filename Checksum (sum of bytes at [001h..00Dh])
  001h 1     Filename Length (excluding ending zeroes) (eg. 8, 9, 10, 12)
  002h 0Ch   Filename ("FILENAME.EXT", zeropadded if less than 12 chars)
  00Eh 2     Unknown (2000h) (maybe attr and/or ending zero for filename)
  010h 4     Filesize in bytes (can be odd)
  014h 4     Offset (increasing, 4-byte aligned)

Oddworld: Abe's Exodus (MagDemo17: ABE2\*.LVL)
Oddworld: Abe's Exodus (MagDemo21: ABE2\*.LVL and nested .IDX files)
  000h 4     Header Size in bytes  (2800h) (can be MUCH bigger than needed)
  004h 4     Zero
  008h 4     ID "Indx"
  00Ch 4     Zero
  010h 4     Number of Files (N)   (CEh) (can be zero=empty in .IDX files)
  014h 4     Header Size/800h      (05h)
  018h 4     Zero
  01Ch 4     Zero
  020h N*18h File List
  ...  ..    Zeropadding to end of Headersize
  ...  ..    File Data area
File List entries (in .LVL files):
  000h 0Ch   Filename ("FILENAME.EXT", zeropadded if shorter)
  00Ch 4     Offset/800h
  010h 4     File Size/800h
  014h 4     File Size in bytes
File List entries (in .IDX files):
  IDX files use the same File List entry format as LVL, but the offsets
  seem to refer to an external file with corresponding name, for example:
    cdrom:\ABE2\CR.LVL\CR.IDX     ;directory info
    cdrom:\ABE2\CR.MOV            ;external data (the .MOV being a .STR video)
  XXX: That's not tested/verified, and not implemented in no$psx file viewer.

Monkey Hero (MagDemo17: MONKEY\BIGFILE.PSX and nested .PSX files)
  000h 4     Unknown              (6)
  004h 4     Total Filesize       (1403800h)
  008h 2     Unknown, Alignment?  (800h)
  00Ah 2     Number of Files, excluding zerofilled File List entries (ACh)
  00Ch 4     Header Size          (1800h)
  010h 4     Unknown, Entrysize?  (18h)
  014h 4     Unknown, Entrysize?  (18h)
  018h N*18h File List (can contain unused zerofilled entries here and there!)
  ...  ..    File Data area
File List entries:
  000h 10h   Filename ("FILENAME.EXT", zeropadded)
  010h 4     File Offset in bytes (800h-byte aligned, unusorted/not increasing)
  014h 4     File Size in bytes

NHL Faceoff '99 (MagDemo17: FO99\*.KGB and nested *.PRM *.TMP *.ZAM)
NHL Faceoff 2000 (MagDemo28: FO2000\*.KGB, Z.CAT, and nested *.PRM and *.TMP)
  000h 4     ID "KGB",00h
  004h 4     Number of Files         (N)
  008h (4)   Number of Files negated (-N)   ;<-- optional, not in LITESHOW.KGB
  ...  N*18h File List
  ...  (..)  CBh-padding to alignment boundary (only if align=800h)
  ...  ..    File Data area
File List entries:
  000h 10h   Filename ("FILENAME.EXT", terminated by 00h, padded with CDh)
  010h 4     File Size in bytes
  014h 4     File Offset (800h-byte or 1/4-byte? aligned)

Syphon Filter 1 (MagDemo18: SYPHON\SUBWAY.FOG) (4Mbyte, namelen=10h)
  000h 4     Unknown (80000001h)
  004h 4     Offset/800h to Final Padding area
  008h 8     Zerofilled
  010h N*18h File List
  ...  (..)  CDh-padding to 800h-byte alignment boundary
  ...  ..    File Data area
  ...  800h  Some text string talking about "last-sector bug"
  ...  40BEh Final Padding area (CDh-filled)
File List entries:
  000h 10h   Filename ("FILENAME.EXT", terminated by 00h, padded with CDh)
  010h 4     File Offset/800h (increasing)
  014h 4     File Size/800h
This is almost same as the newer v2 format in Syphon Filter 2 (see there for details).

Centipede (MagDemo23: ARTFILES\*.ART)
  000h 0Fh   ID ("Art", zeropadded)             ;\
  00Fh 1     Type or so ("?")                   ; sorts of File List entry
  010h 4     Number of entries plus 1 (N+1)     ; for root folder
  014h 4     Total Size in bytes (can be odd)   ;/
  018h N*18h File List
  ...  ...   File Data area
 File List entries:
  000h 0Fh   Filename ("FILENAME", zeropadded)
  00Fh 1     Type/extension or so ("X" or "D")
  010h 4     File Offset (unaligned, increasing)
  014h 4     File Size in bytes (can be odd)
Note: C0L7.ART includes zerofilled 18h-bytes as last File List entry, BONU.ART doesn't have any such zerofilled entry.
Unknown if this can have child folders (maybe in similar form as the root folder entry).

Sheep Raider (MagDemo52: SDWDEMO\*.SDW)
Sheep Raider (MagDemo54: SDWDEMO\*.SDW)
  000h 4     Unknown (301h)
  004h 4     Zero (0)
  008h 4     Number of files (N)
  00Ch N*18h File List
  ...  ..    Zeropadding to 800h-byte boundary
  ...  ..    File Data area
 File List entries:
  000h 4     Offset (800h-byte aligned, increasing)
  004h 4     Filesize in bytes
  008h 1     Unknown (01h)
  009h 0Fh   Filename ("FILENAME.EXT",00h, plus garbage padding)
The SDW archive contains malformed 200h*1A4h pixel TIMs.
  Texsize is 6900Eh, but should be 6900Ch = 200h*1A4h*2+0Ch
  Filesize is 6A000h, but should be 69014h = 200h*1A4h*2+14h

Wing Commander III (*.LIB)
  000h 2     Number of Files (C9h)
  002h N*18h File List
  ...  (..)  Padding to 800h-byte boundary (if any, eg. in MOVIES.LIB)
  ...  ..    File data area (800h-byte aligned, or unaligned)
 File List entries:
  000h 4     Filesize in bytes
  004h 4     Offset (increasing, 800h-byte aligned, or unaligned)
  008h 10h   Filename ("filename.ext", zeropadded)

Largo Winch - Commando SAR (LEVELS\*.DCF)
  000h 4     ID "DCAT"
  004h 4     Number of Entries
  008h N*18h File List
  ...  ..    Zerofilled (padding to 800h-byte boundary)
  ...  ..    File Data area
 File List entries:
  000h 10h   Filename ("FILENAME.EXT", terminated by 00h, plus garbage padding)
  010h 4     Filesize in bytes
  014h 4     Offset (increasing, 800h-byte aligned)

Policenauts (NAUTS\*.DPK)
  000h 4     ID "FRID"
  004h 4     Always E0000000h
  008h 4     Always 800h (...maybe alignment)
  00Ch 4     Number of Entries (N)
  010h 4     Header Size (N*18h+20h, plus padding to 800h-byte boundary)
  014h 4     Always 18h (...maybe entry size)
  018h 8     Zerofilled
  020h N*18h File List
  ...  ..    Zerofilled (padding to 800h-byte boundary)
  ...  ..    File Data area
 File List entries:
  000h 0Ch   Filename ("FILENAME.EXT", zeropadded if shorter)
  00Ch 4     Offset (increasing, 800h-byte aligned)
  010h 4     Filesize in bytes
  014h 4     Unknown (checksum? random?)

Actua Ice Hockey 2 (Best Sports Games Ever (demo), AH2\GAMEDATA\*.MAD)
  000h N*18h File List
  ...  ..    File Data area (directly after File List, without end-code)
 Note: There is no file-list end-marker (instead, the Offset in 1st File
       entry does imply the end of File List).
 File List entries:
  000h 10h  Filename ("FILENAME.EXT", zeropadded)
  010h 4    Offset (increasing, 4-byte aligned, or unaligned for TXT files)
  014h 4    Filesize in bytes (or weird nonsense in SFX.MAD)
There are several oddities in demo version (unknown if that's in retail, too):
 SFX.MAD has nonsense Filesize entries (eg. 164h for a 15150h-byte file).
 FACES.MAD contains only one TIM file... but as 3Mbyte junk appended?
 RINKS.MAD and TEAMS.MAD start with 0Dh,0Ah,1Ah followed by 4Mbyte junk.
 MISCFILE.MAD contains several nested .mad files.
 MISCFILE.MAD\panfont.mad\*.txt --> starts with FF,FE --> that's 16bit Unicode?

Muppet Monster Adventure (MagDemo37: MMA\GAMEDATA+WORLDS*\*.INF+WAD)
 INF:
  000h N*18h File List
 WAD:
  000h ..    File Data area
File List entries:
  000h 4     File Offset/800h in .WAD file
  004h 4     File Size in bytes
  008h 10h   Filename ("FILENAME.EXT", zeropadded)

Army Men Air Attack 2 (MagDemo40: AMAA2\*.PCK)
  000h 4     Number of entries (N)
  004h N*18h File List
  ...  ..    Zeropadding to 800h-byte boundary
  ...  ..    File Data area
 File List entries:
  000h 10h   Filename ("FILENAME.EXT", zeropadded)
  010h 4     Fileoffset (800h-byte aligned, increasing)
  014h 4     Filesize in bytes

Mort the Chicken (MagDemo41: MORT\*.PPF and .TPF)
  000h 2     Type (31h=TPF with TIMs, 32=PPF with PMDs)
  002h 2     Number of entries (N) (can be 0=None, eg. STAGE*\MORT.PPF)
  004h 4     File List Size (N*18h)
  008h 4     Header Size (always 14h)
  00Ch 4     Data area Size (Filesize-14h-N*18h)
  010h 4     Data area Offset (14h+N*18h)
  014h N*18h File List
  ...  ..    File Data area
 File List entries:
  000h 10h   Filename ("FILENAME.EXT", zeropadded)
  010h 4     Filesize in bytes
  014h 4     Fileoffset (from begin of Data area, increasing)

Hot Wheels Extreme Racing (MagDemo52: US_01293\VEHICLES\*.CAB)
  000h 4     ID "BACR" (aka RCAB backwards)
  004h 4     Number of entries (N)
  008h N*18h File List
  ...  ..    File Data area
 File List entries:
  000h 10h   Filename ("FILENAME.EXT", zeropadded)
  020h 4     Offset (from begin of Data area, increasing, 4-byte aligned)
  024h 4     Filesize in bytes (can be odd)

 _______________________________ Entrysize=19h ________________________________

WAD Format (Wipeout 2097)
PSX Wipeout 2097, cdrom:\WIPEOUT2\SOUND\SAMPLES.WAD:\*.vag
PSX Wipeout 2097, cdrom:\WIPEOUT2\TRACK*\TRACK.WAD:\*.*
PSX Wipeout 3 (MagDemo25: WIPEOUT3\*)
  000h 2     Number of files
  002h N*19h Directory Entries for all files
  ...  ..    Data for all files (without any alignment, in same order as above)
Directory Entries
  000h 10h Filename (ASCII, can be lowercase), terminated by 00h, plus garbage
  010h 4   Filesize in bytes  ;\maybe compressed/uncompressed, or rounded,
  014h 4   Filesize in bytes  ;/always both same
  018h 1   Unknown (always 00h)
The filesize entry implies offset to next file.

 _______________________________ Entrysize=1Ch ________________________________

Command & Conquer, Red Alert (MagDemo05: RA\*) FAT/MIX/XA
  000h 4     Number of entries with location 0=MIX (M=65h)
  000h 4     Number of entries with location 1=XA  (X=1)
  008h M*1Ch File List for location 0=MIX
  ...  X*1Ch File List for location 1=XA
File List entries:
  000h 10h   Filename (terminated by 00h, padded with garbage)
  010h 4     Offset/800h in DATA.MIX or Offset/930h DATA.XA file (increasing)
  014h 4     Filesize in bytes
  018h 4     File Location (0=DATA.MIX, 1=DATA.XA)

Syphon Filter 2 (MagDemo30: SYPHON\TRAIN.FOG) (2.8Mbyte, namelen=14h)
  000h 4     Unknown (80000001h)
  004h 4     Offset/800h to Final Padding area
  008h 8     Zerofilled
  010h N*1Ch File List
  ...  (..)  CDh-padding to 800h-byte alignment boundary
  ...  ..    File Data area
  ...  3394h Final Padding area (CDh-filled)
File List entries:
  000h 14h   Filename ("FILENAME.EXT", terminated by 00h, padded with CDh)
  014h 4     File Offset/800h (increasing)
  018h 4     File Size/800h
This is almost same as the older v1 format in Syphon Filter 1:
  v1 (Syphon Filter 1) has filename_len=10h (and filelist_entrysize=18h)
  v2 (Syphon Filter 2) has filename_len=14h (and filelist_entrysize=1Ch)
To detect the version: Count the length of the "ASCII chars + 00h byte + CDh padding bytes" at offset 10h.
Note: The FOG archive in Syphon Filter 2 demo version does contain some empty dummy files (with intact filename, but with offset=0 and size=0).

 _______________________________ Entrysize=20h ________________________________

Colony Wars (MagDemo02: CWARS\GAME.RSC)
Colony Wars Venegance (MagDemo14: CWV\GAME.RSC, 8Mbyte)
  000h 4     Number of Files
  004h N*20h File List
  ...  10h   File List End: Name    (zerofilled)
  ...  4     File List End: Offset  (total filesize, aka end of last file)
  ...  0Ch   File List End: Padding (zerofilled)
  ...  ..    File Data area
File List entries:
  000h 10h   Filename ("FILENAME.EXT", terminated by 00h, padded with garbage)
  010h 4     File Offset in bytes (increasing, 4-byte aligned)
  014h 0Ch   Padding (garbage) (usually 800F68A0h,800F68A0h,800F68A0h)
Note: Colony Wars Red Sun does also have a GAME.RSC file (but in different format, with folder structure).

WarGames (MagDemo14: WARGAMES\*.DAT)
  000h 4     Number of Files (1C3h)
  004h N*20h File List
  ...  ..    Zeropadding to 800h-byte boundary
  ...  ..    File Data area
File List entries:
  000h 10h   Filename ("FILENAME.EXT", zeropadded, sorted alphabetically)
  010h 4     File Offset/800h (unsorted, not increasing)
  014h 4     File Size in bytes
  018h 4     File Size/800h
  01Ch 4     Zero

Running Wild (MagDemo15: RUNWILD\*.BIN)
  000h N*20h File List
  ...  4     File List End Offset/800h (end of last file)
  ...  4     File List End Size (zero)
  ...  18h   File List End Name (zerofilled)
  ...  ..    Padding to 800h-byte boundary (each 20h-byte: 01h, and 1Fh zeroes)
  ...  ..    File Data
File List entries:
  000h 4     Offset/800h (increasing)
  004h 4     Filesize in bytes
  008h 18h   Filename ("FILENAME.EXT" or ":NAME" or ":NAME:NAME", zeropadded)
Files with extension .z or .Z are compressed:
CDROM File Compression Z (Running Wild)

Test Drive Off-Road 3 (MagDemo27: TDOR3\TDOR3.DAT)
About same as the other Test Drive games, but with shorter filenames.
  000h N*20h  File List (1920h bytes used; with padding: 5800h bytes in total)
  ...  ..     Zeropadding to Headersize (5800h)
  ...  ..     File Data area
 File List entries:
  000h 18h    Filename ("FILENAME.EXT" or "PATH\FILENAME.EXT", zeropadded)
  018h 4      Filesize in bytes
  01Ch 4      File (Offset-Headersize)/800h
TDOR3.DAT contains DOT1 child archives and many RNC compressed files: --> CDROM File Compression RNC (Rob Northen Compression)

Tiny Tank (MagDemo23: TINYTANK\*.DSK)
  000h 4     ID ("TDSK")                                      ;\
  004h 4     Number of Files (1Bh)                            ; Directory
  008h N*20h File List                                        ;/
  ...  4     1st File Size (same as Size entry in File List)  ;\File Data area
  ...  ..    1st File Data                                    ; (each file os
  ...  4     2nd File Size (same as Size entry in File List)  ; preceeded by
  ...  ..    2nd File Data                                    ; a size entry)
  ...  ..    etc.                                             ;/
 File List entries:
  000h 10h   Filename ("FILENAME.EXT", zeropadded)
  010h 4     File Size in bytes
  014h 4     Unknown (35xxxxxxh..372xxxxxh)
  018h 4     Unknown (3724xxxxh) (Timestamp maybe?)
  01Ch 4     File Offset in bytes (increasing, 4-byte aligned)
Note: The File Offset points to a 32bit value containing a copy of the Filesize, and the actual file starts at Offset+4.

MAG 3 (MagDemo26: MAG3\MAG3.DAT, 7Mbyte)
  000h N*20h  File List (B60h bytes)
  ...  ..     Zeropadding to 800h-byte boundary
  ...  ..     File Data area (files are AAh-padded to 800h-byte boundary)
 File List entries:
  000h 4      Filesize in bytes
  004h 2      File Offset/800h (16bit) (increasing)
  006h 1Ah    Filename ("FILENAME.EXT" or "PATH\FILENAME.EXT", zeropadded)

Play with the Teletubbies (MagDemo35: TTUBBIES\*.RES)
  000h 2     Zero (0000h)
  002h 2     Number of Files (N)
  004h 4     Data Base (N*20h+10h)
  008h 4     Unknown (20h)  ;-maybe File List entry size?
  00Ch 2     Unknown (10h)  ;\maybe filename length and/or header size?
  00Eh 2     Unknown (10h)  ;/
  010h N*20h File List
  ...  ..    File Data area
File List entries:
  000h 4     Zero
  004h 4     File Offset (increasing, 4-byte aligned, relative to Data Base)
  008h 4     File Size in bytes (can be odd)
  00Ch 4     Zero
  010h 10h   Filename ("FILENAME.EXT", zeropadded)

Mat Hoffman's Pro BMX (old demo) (MagDemo39: BMX\FE.WAD+STR) (uncompressed)
Mat Hoffman's Pro BMX (new demo) (MagDemo48: MHPB\FE.WAD+STR) (compressed)
 WAD:
  000h N*20h File List
 STR:
  000h ..    File Data (MagDemo39: 4.5Mbyte, MagDemo48: compressed/2.8Mbyte)
 File List entries:
  000h 14h   Filename ("FILENAME.EXT", zeropadded)
  014h 4     Offset in bytes, 4-byte aligned, in STR file
  018h 4     Filesize, compressed (always rounded to multiple of 4 bytes)
  01Ch 4     Filesize, decompressed (zero when not compressed)
The decompressor is using an Inflate variant with slightly customized block headers:
  - end flag is processed immediately (instead of after the block)
  - blocktype is only 1bit wide (instead of 2bit)
  - stored blocks have plain 16bit len (without additional 16bit inverse len)
Everything else is same as described here:
CDROM File Compression ZIP/GZIP/ZLIB (Inflate/Deflate)
Instead of "tinf_uncompress", use the function below:
 bmx_tinf_style_uncompress(dst,src)
  tinf_init()                   ;init constants (needed to be done only once)
 @@lop:
  if tinf_getbit()=0 then goto @@done   ;end flag, 1bit
  if tinf_getbit()=0 then               ;blocktype, 1bit
    tinf_align_src_to_byte_boundary()
    len=LittleEndian16bit[src], src=src+2   ;get len (without inverse len)
    for i=0 to len-1, [dst]=[src], dst=dst+1, src=src+1, next i   ;uncompressed
  else
    tinf_decode_dynamic_trees(), tinf_inflate_compressed_block()  ;compressed
  gpto @@lop
 @@done:
  ret
Note: Apart from the MHPB\FE.WAD archive, many MHPB\*.BIN files seem to be also compressed (unknown if that's the same compression method; and, if so, they would lack decompressed size info).

 _______________________________ Entrysize=28h ________________________________

Demo Menu, PlayStation Magazine Demo Disc 03-54, MENU.FF
Used on most PlayStation Magazine Demo Discs (Disc 03-54, except Disc 01-02)
Used on PlayStation Underground 3.1 (and maybe other issues)
Used on Interactive CD Sampler Disc Volume 10 (maybe others, but not Vol 4,5)
  000h 4     Number of entries (eg. 20h or 28h)
  004h N*28h File List
  ...  ..    Garbage padding to 800h-byte boundary
  ...  ..    File Data
  ...  ..    Huge zeropadding to 200000h or 2EE000h (2048Kbyte or 3000Kbyte)
File List entries:
  000h 20h   Filename (terminated by 00h, padded with... looks like garbage)
  020h 4     Size/800h
  024h 4     Offset/800h (increasing)
Contains .BS, .TIM, .TXT, .VH, .VB files. The size seems to be always(?) 2048Kbytes, 2992Kbytes, 2000Kbytes, or 3000Kbytes (often using only the first quarter, and having the remaining bytes zeropadded).

Test Drive 4 (MagDemo03: TD4.DAT) (headersize=2000h, used=0...h)
Test Drive 5 (MagDemo13: TD5.DAT) (headersize=3000h, used=1EF8h)
Demolition Racer (MagDemo27: DR\DD.DAT) (headersize=5000h, used=2328h)
This is used by several games, with different Headersizes (2000h or 3000h or 5000h), with Offsets relative to the Headersize. To detect the Headersize, skip used entries, skip following zeropadding, then round-down to 800h-byte boundary (in case the 1st file contains some leading zeroes).
  000h N*28h File List (less than 0C00h bytes used in TD4 demo)
  ...   ..   Zeropadding to Headersize (2000h or 3000h or 5000h)
  ...   ..   File Data
File List entries:
  000h 20h   Filename ("PATH\FILENAME.EXT", zeropadded)
  020h 4     Size in bytes
  024h 4     (Offset-Headersize)/800h (increasing)
TD5.DAT and DD.DAT contain DOT1 child archives and many RNC compressed files:
CDROM File Compression RNC (Rob Northen Compression)

Gekido (MagDemo31: GEKIDO\GLOBAL.CD)
  0000h N*28h File List
  21C0h ...   Unknown random gibberish? (23h,E8h,0Ch,1Dh,79h,C5h,24h,...)
  4000h ...   File Data area
File List entries:
  000h 1Ch   Filename ("\PATH\FILENAME.EXT;0", zeropadded)
  01Ch 4     Filesize in bytes
  020h 4     Fileoffset in bytes (4000h and up, increasing)
  024h 4     Filechecksum (32bit sum of all bytes in the file)
There is no "number of files" entry, and no "file list end marker" (though the "random gibberish" might serve as end marker, as long it doesn't start with "\" backslash).

Team Buddies (MagDemo37: BUDDIES\BUDDIES.DAT\* and nested *.BND files)
  000h 4     ID "BIND"
  004h 4     Number of files (N)
  008h N*28h File List
  ...  ..    File Data area
File List entries:
  000h 20h   Filename ("\FILENAME.EXT", zeropadded)
  020h 4     File Offset (increasing, 4-byte aligned)     ;\see note
  024h 4     File Size in bytes (always a multiple of 4)  ;/
Note: There is a 4-byte gap between most files, that appears to be caused by weird/bugged alignment handling done as so:
  size=((filesize+3) AND not 3)       ;size entry for curr file (plus 3)
  offs=((filesize+4) AND not 3)+offs  ;offs entry for next file (plus 4 !!!)
Namely, odd filesizes (eg. for TXT files in BUDDIES.DAT\00D2h..00D7h) are forcefully rounded-up to 4 bytes boundary. If that rounding has occurred then there is no additional 4-byte gap (but the 4-byte gap will appear if the original filesize was already 4-byte aligned).

JumpStart Wildlife Safari Field Trip (MagDemo52: DEMO\DATA.DAT)
  000h 4     Number of entries (N)
  004h 4     Number of entries (same as above)
  008h 4     Number of entries (same as above)
  00Ch 4     Number of entries (same as above)
  010h N*28  File List
  ...  ..    Zeropadding to 800h-byte boundary
  ...  ..    File Data area
 File List entries:
  000h 20h   Filename ("\PATH\FILENAME.EXT", zeropadded)
  020h 4     Offset/800h, from begin of Data area (increasing)
  024h 4     Filesize in bytes

 _______________________________ Entrysize=34h ________________________________

Army Men: Air Attack (MagDemo28: AMAA\PAK\*.PAK)
  000h 4      Number of Files
  004h N*34h  File List
  ...  ..     Zeropadding to 4000h
  4000h ..    File Data area
 File List entries:
  000h 10h    Filename ("FILENAME.EXT", zeropadded)
  010h 4      Filesize in bytes  ;\always both same, always
  014h 4      Filesize in bytes  ;/both multiple of 800h
  018h 4      Zero
  01Ch 4      Type    (07h..1Ah)
  020h 4      Subtype (00h..01h)
  024h 10h    Zero
The used Type.Subtype values are:
  07h.0   .TIM (*.TIM)
  07h.01h .TIM (HUD_*.TIM)
  08h.0   .TIM (PSTART.TIM)
  09h.0   .TIM (FONT.TIM)
  0Ah.0   .SFX
  0Eh.0   .MBL
  10h.0   .ATR
  11h.0   .RLC
  13h.0   .AST
  15h.0   .SCD
  16h.0   .TXT (PAUSED.TXT)
  17h.0   .TXT (OBJECT*.TXT)
  18h.0   .BIN
  1Ah.0   Misc (.3DO=TIM, .V=TXT, and TERRAIN.CLP .HI .LIT .MAP .PAT .POB .TER)

 _______________________________ Entrysize=40h ________________________________

Ninja (MagDemo13: NINJA\CUTSEQ\*.WAD and NINJA\WADS\*.WAD)
  000h 4     Number of Files (N)
  004h 4     Size of File Data area (SIZ) (total filesize-8-N*40h)
  008h N*40h File List
  ...  SIZ   File Data area
 File List entries:
  000h 4     Filesize in bytes
  004h 4     Fileoffset in bytes (zerobased, from begin of File Data area)
  008h 38h   Filename, zeropadded
You Don't Know Jack (MagDemo23: YDKJ\RES\*.GLU)
You Don't Know Jack 2 (MagDemo41: YDKJV2\*\*.GLU)
  000h 4     ID ("GLUE")
  004h 4     Unknown (always 400h)
  008h 4     Number of Files (N)
  00Ch 4     Header Size (40h+N*40h)
  010h 30h   Zerofilled
  040h N*40h File List
  ...  ..    Garbage padding to alignment boundary
  ...  ..    File Data area
 File List entries:
  000h 20h   Filename ("FILENAME.EXT", zeropadded)
  020h 4     File Offset in bytes (increasing, 800h-byte aligned)
  024h 4     File Size in bytes
  028h 2     File ID Number 1 (eg. 1-71 for C01.GLU-C71.GLU)
  02Ah 2     Unknown (random, checksum, ?)
  02Ch 4     File ID Number 2 (eg. increasing: 1, 2, 3)
  030h 10h   Zerofilled
Most .GLU files are 800h-byte aligned (except SHORTY\*.GLU and THREEWAY\*GLU which use 4-byte alignment).
The files do start on alignment boundaries, but there is no alignment padding after end of last file.

 _______________________________ Entrysize=60h ________________________________

Army Men Air Attack 2 (MagDemo40: AMAA2\*.PCK\*.PAK)
  000h 4     Number of entries (N)
  010h N*60h File List
  ...  ..    Zeropadding to 2000h
  2000h ..   File Data area
 File List entries:
  000h 4     Timestamp? (BFxxxxh..C0xxxxh) (or zero, in first file)
  004h 4     Unknown (always 421C91h)
  008h 4     Unknown (200h or 60200h)
  00Ch 4     Filesize (uncompressed)
  010h 4     Filesize (compressed, or 0 when not compressed)
  014h 4     File Checksum (sum of all bytes in uncompressed file data)
  018h 4     Unknown (random 32bit value?)
  01Ch 10h   Filename ("FILENAME.EXT", zeropadded)
  02Ch 4     Zerofilled
  030h 4     Unknown (0 or 1 or 8)
  034h 4     File Type (see below)
  038h 8     Zerofilled
  040h 4     Offset MSBs (Fileoffset-2000h)/800h  ;\increasing, 4-byte aligned
  044h 4     Offset LSBs (Fileoffset AND 7FFh)    ;/(or zero when filesize=0)
  048h 18h   Zerofilled
File Type values are 07h=TIM, 0Ah=SFX, 0Eh=MBL, 10h=ATR, 13h=AST, 15h=SCD, 19h=VTB, 1Bh=DCS, 1Dh=DSS, 1Eh=STR, 1Fh=DSM, 20h=FNT, 21h=TER, 25h=PMH, 26h=Misc.
Most of the files are SCRATCH compressed:
CDROM File Compression LZ5 and LZ5-variants
There are also several uncompressed files (eg. VERSION.V, *.SFX, and many of the TERRAIN.* files).

 _______________________________ Entrysize=90h ________________________________

Grind Session (MagDemo33: GRIND\SLIP.GRV)
Grind Session (MagDemo36: GRIND\SLIP.GRV)
Grind Session (MagDemo42: GRIND\SLIP.GRV)
Grind Session (MagDemo45: GRIND\SLIP.GRV)
  000h 4     ID (A69AA69Ah)
  004h 4     Number of files (N)
  008h N*90h File List
  ...  ..    File Data area
 File List entries:
  000h 80h   Filename ("DATA\FILENAME.EXT",00h, plus CDh-padding)
  080h 4     File Offset in bytes (increasing, 4-byte aligned)
  084h 4     File Size in bytes
  088h 8     Unknown (random/checksum?)

 _____________________________ Variable Entrysize _____________________________

HED/WAD
  Used by Spider-Man (MagDemo31,40: SPIDEY\CD.HED and CD.WAD)
  Used by Spider-Man 2 (MagDemo52: SPIDEY\CD.HED and CD.WAD)
  Used by Tony Hawk's Pro Skater (MagDemo22: PROSKATE\CD.HED and CD.WAD)
  Used by Apocalypse (MagDemo16: APOC\CD.HED and CD.WAD)       ;with PADBUG
  Used by MDK (Jampack Vol. 1: MDK\CD.HED and CD.WAD)          ;without ENDCODE
  Used by Mat Hoffman's Pro BMX (old demo) (MagDemo39: BMX\BMXCD.HED+WAD)
Format of the CD.HED file:
  000h ..  File Entries (see below)
  ...  (1) End code (FFh) (if any, not present in MDK)
File Entry format:
  000h ..  Filename (ASCII, terminated by 00h, zeropadded to 4-byte boundary)
  ...  4   Offset in CD.WAD (in bytes, usually 800h-byte aligned)
  ...  4   Filesize (in bytes)
PADBUG: Apocalypse does append 1..800h bytes alignment padding (instead of 1..7FFh or 0 bytes).

Dance UK (DATA.PAK)
  000h 4      Number of Files (N) (1ADh)
  004h 4      Unknown (7) (maybe HeaderSize/800h, same as first Offset/800h ?)
  008h 4      Unknown (1430h = 14h+N*0Ch, same as first Name pointer)
  00Ch 4      Unknown (1430h = 14h+N*0Ch, same as first Name pointer)
  010h 4      Unknown (1430h = 14h+N*0Ch, same as first Name pointer)
  014h N*4    Name List (pointers to name strings, 1430h and up)  6B4h bytes
  ...  N*4    Size List (filesize in bytes)                       6B4h bytes
  ...  N*4    Offset List (Offset/800h)                           6B4h bytes
  ...  N*var  Name Strings (ASCII strings, "folder\filename.ext",00h)
  ...  ..     Zerofilled (padding to 800h-byte boundary)
  ...  ..     File Data area

Kula Quest / Kula World / Roll Away (*.PAK)
  000h 4     Number of Files (N)
  004h N*8   File List (2x32bit entries: Offset, Size) (unaligned, can be odd)
  ...  N*4   File Name Offsets
  ...  N*var File Name Strings ("FILE NN",0Ah,00h)
  ...  ..    Garbage-padding to 4-byte boundary
  ...  (4)   Optional extra garbage? ("MON " in ATLANTFI.PAK, MARSFI.PAK, etc.)
  ...  ..    File Data area (ZLIB compressed, starting with big-endian 789Ch)
CDROM File Compression ZIP/GZIP/ZLIB (Inflate/Deflate)

Largo Winch - Commando SAR (NTEXTURE\*.GRP and LEVELS\*.DCF\*.CAT and *.GRP)
  000h 4     ID (12h,34h,56h,78h) (aka 12345678h in big endian)
  004h 4     Header Size (offset to File Data area)
  008h 4     Number of Entries (can be 0=None, eg. LEVELS\LARGO07.DCF\Z16.CAT)
  00Ch N*var Name List (Filenames in form "FILENAME.EXT",00h)
  ...  ..    Zeropadding to 4-byte boundary
  ...  N*4   Size List (Filesizes in bytes)
  ...  ..    File Data area

Jackie Chan Stuntmaster (RTARGET\GAME.GCF and LEV*.LCF)
  000h 4     Number of files (N) (3..EBh)                 (big-endian)
  004h N*Var File List (list size is implied in first file offset)
  ...  ..    Zeropadding to 800h-byte boundary
  ...  ..    File Data area
 File List entries:
  000h 4     File Type (ascii, .LLN .TXI .TPG .RCI .RCP .WDB .PCI .PCP .BLK)
  004h 4     File Size (can be odd)                       (big-endian)
  008h 4     File Offset (increasing, 800h-byte aligned)  (big-endian)
  00Ch 4     Extra Size (0 or 4 or 8)                     (big-endian)
  010h ..    Extra Data (if any) (32bit number, or "TEXTURES")

Syphon Filter 1 (MagDemo18: SYPHON\*.HOG, SYPHON\SUBWAY.FOG\*.HOG,SLF.RFF)
Syphon Filter 2 (MagDemo30: SYPHON\*.HOG, SYPHON\TRAIN.FOG\*.HOG,SLF.RFF)
  000h 4     Timestamp? (36xxxxxxh=v1?, 38xxxxxxh=v2?, other=SLF.RFF)
  004h 4     Number of Files          (N)
  008h 4     Base for Offset List  (always 14h)
  00Ch 4     Base for String Table (v1=N*4+14h, or v2=N*4+18h)
  010h 4     Base for File Data (end of String Table plus align 4/800h/920h)
  014h N*4   Offsets to File(s) (increasing, first=0, relative to above [010h])
  ... (4)    v2 only: End Offset for Last File (HOG filesize minus [010h])
  ...  ..    String Table (filename list in form of "FILENAME.EXT",00h)
  ...  ..    Zeropadding to 4-byte or 800h-byte boundary
  ...  ..    File Data area
There are two versions: Syphon Filter 1 (v1) and Syphon Filter 2 (v2):
  v1 has [0Ch]=N*4+14h (without end-of-last-file entry; use end=total_size)
  v2 has [0Ch]=N*4+18h (and does have end-of-last-file entry)
  v1 has STR files in ISO filesystem (not in HOG archives)
  v2 has STR files in MOVIES.HOG (with [10h]=920h and [14h and up]=sectors)
Normally, the following is common for v1/v2:
  v1/v2 has [10h]=data base, aligned to 4 or 800h
  v1/v2 has [14h and up] in BYTE-offsets, relative to base=[10h]
  v1/v2 uses HOG format in .HOG files also in SLF.RFF
  v1/v2 has further .RFF files (but that aren't in HOG format)
There are several inconsistent special cases for some v2 files:
  v2 MOVIE.HOG has [10h]=920h (which is meant to mean base="after 1st sector")
  v2 MOVIE.HOG has [14h and up] in SECTOR-units, with base="after 1st sector"
  v2 SLF.RFF does contain two HOG archives badged together (plus final padding)
  v2 has some empty 0-byte .HOG files (at least so in demo version)
Danger: The special value 920h means that headersize is one 800h-byte sector (whereas 920h is dangerously close to REAL headersize, eg. v1 PCHAN.HOG has headersize=908h which means one 800h-byte sector plus 108h bytes) (the 920h thing should occur only in v2 though, since v1 has STR files stored in ISO filesystem instead of in HOG archives).

Electronic Arts 32bit BIGF archives
  000h 4   ID "BIGF" (normal case, all big-endian, 4-byte aligned)     ;\
           ID "BIGH" (with [04h]=little-endian instead big-endian)     ;
           ID "BIG4" (with 40h-byte alignment padding instead 4-byte)  ;
  004h 4   Sum of Header+Filesizes (excluding Padding's!) (big-endian) ; Header
  008h 4   Number of entries (N)                  ;11h    (big-endian) ;
  00Ch 4   Size of Header (including File List)   ;11Fh   (big-endian) ;
  010h ..  File List                                                   ;/
  ...  ..  Padding to 1/4/8-byte boundary (optional, before each file) ;\Data   ...  ..  File Data                                                   ;/
File List entries (with variable length names, entries aren't 4-byte aligned):
  000h 4   Offset in bytes (increasing, often 4/8-byte aligned)    (big-endian)
  004h 4   Size in bytes (can be odd, but often rounded to 4-byte) (big-endian)
  008h ..  Filename (ASCII, terminated by 00h) ;variable length
  Note: Filenames can be empty ("",00h) (eg. in WCWDEMO\ZSOUND.BIG)
Used by PGA Tour 96, 97, 98 (*.VIV)
Used by FIFA - Road to World Cup 98 (MOP*.BK*, Z4TBLS.BIG\*.t, ZMO*.BIG\*.viv)
Used by Fifa 2000 (Best Sports demo: FIFADEMO\*.BIG, *.SBK, and nested .viv)
Used by Need for Speed 3 Hot Pursuit (*.VIV)
Used by WCW Mayhem (MagDemo28: WCWDEMO\*.BIG) (odd filesizes & nameless files)
This is reportedly also used for various other Electronic Arts games for PC, PSX, and PS2 (often with extension *.BIG, *.VIV).
Reportedly also "BIGH" and "BIG4" exist:
  http://wiki.xentax.com/index.php/EA_BIG_BIGF_Archive
Other Electronic Arts file formats (used inside or alongside big archives):
  https://wiki.multimedia.cx/index.php/Electronic_Arts_Formats_(2) - BNK etc

Electronic Arts 24bit C0FB archives
  000h 2   ID C0FBh                (C0h,FBh)  (big-endian)      ;\
  002h 2   Size of Header-4        (00h,15h)  (big-endian)      ; Header
  004h 2   Number of Files         (00h,01h)  (big-endian)      ;
  006h ..  File List                                            ;/
  019h ..  Padding to 4-byte boundary?                          ;-Padding
  01Ch ..  File Data                                            ;-Data
  ...  4   "CRCF"                                               ;\
  ...  4   Unknown (0C,00,00,00) (chunk-size little-endian?)    ; Footer
  ...  4   Unknown (3B,2E,00,00) (checksum maybe?)              ;/
File List entries (with variable length names, and unaligned 24bit values):
  000h 3   Offset in bytes (increasing)        ;(big-endian, 24bit)
  004h 3   Size in bytes                       ;(big-endian, 24bit)
  008h ..  Filename (ASCII, terminated by 00h) ;variable length
Used by FIFA - Road to World Cup 98 (*.BIG)
Used by Sled Storm (MagDemo24: ART\ZZRIDER.UNI, with 8 files insides)

Destruction Derby Raw (MagDemo35: DDRAW\*.PTH+.DAT, and nested therein)
 PTH File:
  000h N*var File List
 DAT File:
  000h ..    File Data area
File List entries:
  000h ..    Filename ("FILENAME.EXT",00h) (variable length)
  ...  4     File Size in bytes (can be odd)
  ...  4     File Offset in bytes in DAT file (increasing, unaligned)
Caution: Filenames in PTH archives aren't sorted alphabetically (so DAT isn't always guaranteed to be the previous entry from PTH, namely, that issue occurs in MagDemo35: DDRAW\INGAME\NCKCARS.PTH\*.PTH+DAT).
Caution: The whole .DAT file can be compressed: If the sum of the filesizes in PTH file does exceed the size of the DAT file then assume compression to be used (normally, the top-level DATs are uncompressed, and nested DATs are compressed).
CDROM File Compression PCK (Destruction Derby Raw)

SnoCross Championship Racing (MagDemo37: SNOCROSS\SNOW.TOC+.IMG)
 TOC:
  000h N*var File List
 IMG:
  000h ..    File Data area
File List entries:
  000h ..    Filename ("DATA\FILENAME.EXT",00h) (variable length)
  ...  4     File Offset (increasing, 800h-byte aligned, in .IMG file)
  ...  4     File Size in bytes
Resembles DDRAW\*.PTH+.DAT (but Offset/Size are swapped, and uses 800h-align).
Note: The archive contains somewhat corrupted TGA's:
  TGA[10h..11h] = 08h,08h  ;bpp=8 (okay) and attr=8 (nonsense)
  TGA[10h..11h] = 10h,01h  ;bpp=16 (okay) and attr=1 (okay) but it's yflipped


CDROM File Archives with Offset and Size

Crash Team Racing (retail: BIGFILE.BIG, and MagDemo30/42: KART\SAMPLER.BIG)
  000h 4     Zero
  004h 4     Number of Files (260h)
  010h N*8   File entries
  ...  ..    Zeropadding to 800h byte boundary
  ...  ..    File Data
File Entries:
  000h 4   Fileoffset/800h (increasing)
  004h 4   Filesize in bytes
Filetypes in the archive include...
  MDEC v2 STR's  (file 1E1h..1F8h,1FAh)
  TIM textures  (file 01FBh..0200h and others)
  empty files   (file 01F9h and others)
  small archives with named entries (file B5h,124h,125h,126h and others)
  stuff with date string and names (file 253h,256h)
  there seem to be no nested BIG files inside of the main BIG file

Black Matrix (*.DAT)
  000h 4    Number of files (N) (eg. 196h)
  004h 4    Unknown (always 0Bh) (maybe sector size shift?)
  008h N*4  File List
  ...  ..   Zeropadding to 800h-byte boudary
  ...  ..   File Data
File List entries:
  000h 2    Offset/800h (increasing)
  002h 2    Size/800h (can be zero)
The "files" might actually contain small child folders? Or the whole stuff is just some kind of data structure, not an actual file system archive.

Charumera (*.CVF)
  000h N*4  File List
  ...  ..   Zeropadding to 800h-byte boundary
  ...  ..   File Data area
 File List entries:
  000h 1    Size/800h   (8bit)
  001h 3    Offset/800h (24bit, increasing)

Vs (MagDemo03: THQ\*) has .CDB archives
  000h  N*8   File List
  ...   ..    Zeropadding to 800h-byte boundary
  ...   ..    File Data
  ...   ..    Garbage padding (can be several megabytes tall)
File List entries:
  000h 2     Offset/800h (increasing)
  002h 2     Size/800h (same as below, rounded up to sector units)
  004h 4     Size in bytes
Note: The files may consist of multiple smaller files badged together (eg. DISPLAY.CDB contains several TIMs per file).
Some CDB archives have garbage padding at end of file: BIN.CDB (2Kbyte), CSEL.CDB (80K), DISPLAY.CDB (70K), MOT.CDB (10648Kbyte). Maybe that's related to deleted files in the Vs demo version and/or to updating the CDB archives with newer/smaller content, but without truncating the CDB filesize accordingly.

Monster Rancher (MagDemo06: MR_DEMO\*.OBJ)
Deception III Dark Delusion (MagDemo33: DECEPT3\K3_DAT.BIN)
Star Trek Invasion (MagDemo34: STARTREK\STARTREK.RES)
Similar as .CDB archives (but with 32bit offset, and without duplicated size).
  000h  N*8   File List
  ...   4     File List end marker (00000000h)
  ...   ..    Garbage padding to 800h-byte boundary
  ...   ..    File Data
File List entries:
  000h 4     Offset/800h (increasing)
  004h 4     Size in bytes (often zero; for unused file numbers)
Note: Files are usually padded with 0..7FFh bytes to 800h-byte boundary, but STARTREK.RES does append additional 800h-byte padding after each file (ie. 800h..FFFh padding bytes in total).

Einhander (MagDemo08: BININDEX.BIN/BINPACK0.BIN/BINPACK1.BIN)
  000h X*4  File List for BINPACK0.BIN                   ;\
  ...  ..   Zeropadding                                  ; BINPACK0
  410h ..   Unknown (some/all of it looks like garbage)  ;/
  800h Y*4  File List for BINPACK1.BIN                   ;\
  ...  ..   Zeropadding                                  ; BINPACK1
  C10h ..   Unknown (some/all of it looks like garbage)  ;/
File List entries:
  000h 2    Offset/800h in BINPACK0.BIN or BINPACK1.BIN
  002h 2    Size/800h

SO98 Archives (NBA Shootout '98, MagDemo10: SO98\..*.MDL *.TEX *.ANI *.DAT)
Resembles .BZE (in terms of duplicated size entry).
  000h 4     Number of Files
  004h 4     Size of File Data area (total filesize-N*0Ch-8)
  008h N*0Ch File List
  ...  ..    File Data area
File List entries:
  000h 4   Offset (zerobased, from begin of File Data area)
  004h 4   Size in bytes
  008h 4   Size rounded to mutiple of 4-bytes
.DAT contains .TIM .SEQ .VB .VH and nested SO98 archives
.MDL contains whatever (and empty 0-byte files)
.TEX contains .TIM
.ANI contains whatever

Gran Turismo 1 (MagDemo10: GT\*.DAT) GT-ARC
Gran Turismo 1 (MagDemo15: GT\*.DAT) GT-ARC
Gran Turismo 2 (GT2.VOL\arcade\arc_fontinfo) GT-ARC
  000h 0Ch   ID "@(#)GT-ARC",00h,00h
  00Ch 2     Content Type (8001h=Compressed, 0001h=Uncompressed)
  00Eh 2     Number of Files (eg. 0Fh)
  010h N*0Ch File List
  ...  ..    File Data area
 File List entries:
  000h 4     Offset in bytes (increasing, unaligned)
  004h 4     Compressed File Size (can be odd)  ;\both same when uncompressed
  008h 4     Decompressed File Size             ;/(ie. when [00Ch]=0001h)
MESSAGES.DAT, SOUND.DAT, TITLE.DAT which are completely uncompressed GT-ARC's. Most other GT-ARC's contain LZ compressed files. In case of CARINF.DAT it's vice-versa, the files are uncompressed, but the GT-ARC itself is LZ compressed (the fileheader contains 00h,"@(#)GT-A",00h,"RC",00h,00h; it can be detected via those bytes, but lacks info about decompressed size).
CDROM File Compression GT-ZIP (Gran Turismo 1 and 2)

O.D.T. (MagDemo17: ODT\*.LNK and ODT\RSC\NTSC\ALLSOUND.SND and nested LNK's)
Barbie Explorer (MagDemo50: BARBIEX\*.STR and nested therein)
  000h 4     Number of Files (N)
  004h N*8   File List
  ...  ..    File Data area
File List entries:
  000h 4     Offset in bytes (increasing, 1/4-byte? aligned)
  004h 4     File Size in bytes (usually N*4, TXT's in ODT are padded as so)
Quirk: Instead of rounding only Offsets to N*4 byte boundary, all Sizes are rounded to N*4 bytes (eg. TXT files in ODT\RSC\NTSC\GFILES.LNK\01 with odd number of characters are are zeropadded to N*4 bytes).
Note: The PADBUG archives in Final Fantasy VIII (FF8) are very similar (but have a different alignment quirk).

Bust A Groove (MagDemo18: BUSTGR_A\*.DFS and BUSTGR_B\*.DFS) (DFS)
Bust-A-Groove 2 (MagDemo37: BUSTAGR2\BUST2.BIN\*) (main=DF2 and child=DFS)
Same as in O.D.T. with extra "DFS_" ID at start of file.
  000h 4     ID "DFS_" (with align 4) or "DF2_" (with align 800h)
  004h 4     Number of Files (N)
  008h N*8   File List
  ...  ..    File Data area
 File List entries:
  000h 4     Fileoffset in bytes (4-byte or 800h-byte aligned, increasing)
  004h 4     Filesize in bytes (can be odd, eg. in BUSTGR_A\SELECT.BPE\*)
The game does use uncompressed DFS archives (in .DFS files) and compressed DFS archives (in .BPE files):
CDROM File Compression BPE (Byte Pair Encoding)
The game does also use .DBI files (which contain filenames and other strings, whatever what for).

Monaco Grand Prix Racing Simulation 2 (MagDemo24: EXE\*\*.SUN)
Same as DFS, but with Total Filesize instead of "DFS_".
  000h 4     Total used filesize (excluding zeropadding to 2EE000h)
  004h 4     Number of Files (N)
  008h N*8   File List
  ...  ..    File Data area
  ...  (..)  In some files: Zeropadding to 2EE000h (3072Kbytes)
File Entries:
  000h 4     Offset (increasing, 4-byte aligned, see note)
  004h 4     Filesize in bytes (can be odd in Monaco)
Note: The alignment in Monaco is a bit glitchy:
  If (Size AND 3)=0 then NextOffset=Offset+Size             ;Align4
  If (Size AND 3)>0 then NextOffset=Offset+Size+Align800h   ;Align800h
  Namely, Monaco has files with Size=3BC5h.
The first file starts with unknown 32bit value, followed by "pBAV".

Rollcage (MagDemo19: ROLLCAGE\SPEED.IMG) (2Mbyte)
Rollcage Stage II (MagDemo31: ROLLCAGE\SPEED.IDX+SPEED.IMG) (3Kbyte+9Mbyte)
Sydney 2000 (MagDemo37: OLY2000\DEMO.IDX+DEMO.IMG) (1Kbyte+2Mbyte)
 Rollcage 1 uses a single IMG file that contains both directory and data:
  000h 4     Header offset (0)          ;\
  004h 4     Header size (10h+N*10h)    ; this seems to be a File List entry
  008h 4     Header size (10h+N*10h)    ; for the header itself
  00Ch 4     Zero                       ;/
  010h N*10h File List                  ;-File List for actual files
  ...  ..    Zeropadding to 800h-byte boundary
  ...  ..    File Data area
  Number of files is "IMG[04h]/10h" (minus 1 for excluding the header itself)
 The other titles have seaparate IDX and IMG files for directory and data:
  SPEED.IDX = Directory (N*10h bytes File List with offsets into SPEED.IMG)
  SPEED.IMG = File data
  Number of files is "Filesize(SPEED.IDX)/10h"
File List entries:
  000h 4     Fileoffset in bytes (800h-byte aligned, increasing)
  004h 4     Filesize in bytes
  008h 4     When compressed:   GT20 Header [004h] (decompressed size)
             When uncompressed: Same as filesize
  00Ch 4     When compressed:   GT20 Header [008h] (overlap, usuallly 3, or 7)
             When uncompressed: Zero
The compression related entries allow to pre-allocated the decompression buffer (without needing to load the actual GT20 file header), and then load the comprssed file to the top of the decompression buffer.
CDROM File Compression GT20 and PreGT20

Ultimate 8 Ball (MagDemo23: POOL.DAT) (5.5Mbyte)
  000h 4      Number of Entries
  004h N*0Ch  File List
  ...  ..     Zeropadding to 800h-byte boundary
  ...  ..     File Data area
 File List entries:
  000h 4      Unknown (random/checksum?)
  004h 4      File Offset (800h-byte aligned, increasing)
  008h 4      File Size in bytes
Notes: The LAST file isn't zeropadded to 800h-byte boundary. The File List includes some unused entries (all 0Ch-bytes zerofilled).

BIGFOOL - 3D Baseball (BIGFILE.FOO)
  000h N*0Ch  File List                                   (154h entries)
  ...  N*4    Filename Checksums (?)                      (154h entries)
  ...  ..     Zerofilled (padding to 800h-byte boundary)
  ...  ..     File Data area
The 1st list entry describes the current directory itself, as so:
  000h 4      Number of entries (including the 1st entry itself)
  004h 4      Offset/800h (always 0, relative from begin of directory)
  008h 4      Type        (always 3=Directory)
Further list entries are Files or Subdirectories, as so:
  000h 4      For Files: Size in bytes, for Directories: Number of entries
  004h 4      Offset/800h (from begin of current directory, increasing)
  008h 4      Type        (0=File, 3=Directory)

Spec Ops - Airborne Commando (BIGFILE.CAT and nested CAT files therein)
  000h 4     File ID                                 (always 01h,02h,04h,08h)
  004h 4     Maybe Version?                          (always 01h,00h,01h,00h)
  008h 4     Header Size (18h+N*8+ArchiveNameLength)    ;eg. 4ECh
  00Ch 4     Sector Alignment (can be 4 or 800h)
  010h 4     Number of Files (N)                        ;eg. 99h
  014h 4     Length of Archive Name (including ending 00h)
  018h N*8   File entries (see below)
  ...  ..    Archive Name, ASCII, terminated by 00h     ;eg. "bigfile.dir",00h
  ...  ..    Zeropadding to Sector Alignment boundary
  ...  ..    File Data
File Entries:
  000h 4   Fileoffset (with above Sector Alignment) (increasing)
  004h 4   Filesize in bytes
Filetypes in the archive include...
  nested CAT archives (file 07h,0Ch,11h,16h,1Bh,20h,25h,etc)
  empty files         (file 3Eh,5Ah-5Fh,62h-67h,etc)
  MDEC v2 STR's       (file 95h-96h)
  XA-ADPCM's          (inside of nested CAT, in file94h\file*)
There are "strings" in some files, are those filenames, eg. Icon_xxx etc?

Hot Shots Golf 2 (retail: DATA\F0000.BIN, MagDemo31/42: HSG2\MINGOL2.BIN)
The DATA directory is 13800h bytes tall. But, the PSX kernel supports max 800h bytes per ISO directory (so the kernel can only see the first 33 files in that directory). The game isn't actually trying to parse the ISO directory entries, instead, it's using the 2800h-byte offset/size list in F0000.BIN to access the directory content:
  0000h+N*4 1     Sector MM in BCD      ;\based at 00:06:00 for file 0
  0001h+N*4 1     Sector SS in BCD      ; (unused files are set to 00:00:00)
  0002h+N*4 1     Sector FF in BCD      ;/
  0003h+N*4 1     Size MSB in hex (Size/800h/100h)
  2000h+N   1     Size LSB in hex (Size/800h AND FFh)
  2800h     (..)  Data area for file 001h..590h (demo version only)
Retail Version disc layout:
  Sector 000ADh  SCUS_944.76       ;exefile     ;\
  Sector 00130h  SYSTEM.CNF                     ; iso root folder
  Sector 00131h  DATA (sub-folder, 27h sectors) ;/
  Sector 00158h  (padding)                      ;-padding to 00:06:00
  Sector 001C2h  DATA\F0000.BIN    ;file 000h   ;\
  Sector 001C7h  DATA\F0001.BIN    ;file 001h   ;
  ...                                           ; iso data folder
  Sector 00B54h  DATA\F0032.BIN    ;file 020h   ;
  Sector 00B9Bh  DATA\F0033.BIN    ;file 021h   ;  ;\files exceeding the 800h
  ...            ...                            ;  ; directory size limit, not
  Sector 1A0C9h  DATA\F1907.BIN    ;file 773h   ;/ ;/accessible via PSX kernel
  Sector 1AAF1h  DUMMY.BIN                      ;-iso root folder (padding)
Demo version in Playstation Magazine is a bit different: It has only two large .BIN files (instead of hundreds of smaller .BIN files). The directory is stored in first 2800h bytes of MINGOL2.BIN. The MM:SS:FF offsets are numbered as if they were located on sector 00:06:00 and up (to get the actual location: subtract 00:06:00 and then add the starting sector number of MINGOL2.BIN).
  Sector 07148h  HSG2\MINGOL2.BIN  ;file 000h..590h  ;demo binary files
  Sector 0AC1Dh  HSG2\MINGOL2X.BIN ;file 76Ch        ;demo streaming file(s)
  Sector 0B032h  HSG2\SCUS_944.95  ;exefile          ;demo exe file
Note: File 000h is a dummy entry referring to the 2800h-byte list itself (retail file 000h has offset=00:06:00 but size=0, demo file 000h has offset and size set to zero). File 001h is the first actual file (at offset=00:06:05, ie. after the 2800h-byte list)

Threads of Fate (MagDemo33: TOF\DEWPRISM.HED+.EXE+.IMG)
The demo version uses "Virtual Sectors" in HED+EXE+IMG files. Apart from that, the format is same as for the "Hidden Sectors" in retail version:
CDROM File Archives in Hidden Sectors

WWF Smackdown (MagDemo33: TAI\*.PAC\*, and nested therein)
These "PAC " files are found in the main archives (which use a separate archive format, with ID "DPAC").
  000h 4     ID ("PAC ")                                        ;\
  004h 4     Number of files (N)                                ; Header
  008h N*8   File List                                          ;/
  ...  ..    File Data area                                     ;-Data area
File List entries:
  000h 2     File ID (inreasing, but may skip numbers, ie. non-linear)
  002h 3     File Offset (increasing, relative to begin of Data area)
  005h 3     File Size
Bug: TAI\C.PAC\EFFC\0001h has TWO entries with File ID=0002h.

Tyco R/C Racing (MagDemo36: TYCO\MAINRSRC.BFF)
  000h 4     Unknown (1)
  004h 4     Filelist Offset          (800h)
  008h 4     Filelist Size (N*8+4)    (7ACh)
  ...  ..    Padding to 800h-byte boundary (see note)
  800h 4     Number of files (N)      (F5h)
  804h N*8   File List
  ...  ..    Padding to 800h-byte boundary (see note)
  ...  ..    File Data area
File List entries:
  000h 4     File Offset in bytes (increasing, 800h-byte aligned)
  004h 4     File Size in bytes
Padding Note: Padding after headers & files is weirdly done in two steps:
  Step 1: Zeropadding to 200h-byte boundary    (first 0..1FFh bytes)
  Step 2: Garbagepadding to 800h-byte boundary (last 0..600h bytes)

Team Buddies (MagDemo37: BUDDIES\BUDDIES.DAT)
  000h 2     ID ("BD")
  002h 2     Number of files (N)
  004h N*8   File List
  ...  ..    Zeropadding to 3000h
  3000h ..   File Data area
File List entries:
  000h 4     File Offset/800h (increasing)
  004h 4     File Size in bytes

Gundam Battle Assault 2 (DATA\*.PAC, and nested therein)
  000h 4     ID ("add",00h)
  004h 4     Fixed (4)
  008h 4     Offset to File List (usually/always 20h)
  00Ch 4     Number of Files (N)
  010h 4     Fixed (10h)
  014h 0Ch   Zerofilled
  020h N*10h File List
  ...  ..    File Data area
File List entries:
  000h 4     Offset (increasing, 4-byte aligned)  ;\or both zero
  004h 4     Size (can be odd)                    ;/
  008h 4     Unknown (0) (or 00h,10h,11h,20h,30h,40h when Offset/Size=0)
  00Ch 4     Zero (0)

Incredible Crisis (MagDemo38: IC\*.CDB)
  000h 4     Number of files (N)
  004h N*4   File List
  ...  ..    Zeropadding to 800h-byte boundary
File List entries:
  000h 2     File Offset/800h (increasing)
  002h 2     File Size/800h

Ape Escape Sound Archive (MagDemo22:KIDZ\KKIIDDZZ.HED\DAT\1Bh-1Dh,49h-53h,..)
Ape Escape Sound Archive (MagDemo44:KIDZ\KKIIDDZZ.HED\DAT\1Bh-1Dh,4Fh-59h,..)
  000h 5*4   File Sizes   (can be odd) (can be 0 for 2nd and 5th file)
  014h 5*4   File Offsets (28h and up, increasing by sizes rounded to N*10h)
  028h ..    File Data area (first file usually/always contains "SShd")

Ultimate Fighting Championship (MagDemo38: UFC\CU00.RBB)
  0000h 4    ID "siff"                                  ;\Header
  0004h 4    Total Filesize (DADB1Ch)                   ;/
  0008h 4    ID "RSRC"                                  ;\
  000Ch 4    String Size (70h)                          ; ASCII string
  0010h 70h  String "RC ver1.0 Copyright",...,00h       ;/
  0080h 4    ID "RIDX"                                  ;\
  0084h 4    File List Size     (1F78h) (3EFh*8)        ; Directory
  0088h N*8  File List (Offset, Size1)                  ;/
  2000h 4    ID "EXIX"                                  ;\
  2004h 4    Extended List Size (FBCh)  (3EFh*4)        ; Extended
  2008h N*4  Extended List (Size2)                      ;/
  2FC4h 4    ID "GAP0"                                  ;\Alignment Padding
  2FC8h 4    Padding Size (2Ch)                         ; (so that next chunk
  2FCCh 2Ch  Padding (1Ah-filled)                       ;/starts at boundary-8)
  2FF8h 4    ID "RBB0"                                  ;\
  2FFCh 4    File Data area Size (DAAB1Ch)              ; Data area
  3000h ..   File Data area                             ;/
File List entries (RIDX):
  000h 4     File Offset (increasing, 4-byte aligned, from ID "RBB0" plus 8)
  004h 4     File Size in bytes (can be odd)
Extended List entries (EXIX):
  000h 4     File Size in bytes (always the same size as in RIDX chunk)

Ultimate Fighting Championship (MagDemo38: UFC\CU00.RBB\183h,37Bh..3EBh)
  000h 4     ID "OIFF"                                  ;\Header
  004h 4     Total Filesize                             ;/
  008h 4     ID "TIMT" or "ANMT"                        ;\
  00Ch 4     Size (N*4)                                 ; Directory Table
  010h N*4   File List (offsets from begin of Data ID+8);/
  ...  4     ID "TIMD" or "ANMD"                        ;\
  ...  4     Data Area size (SIZ) (Filesize-18h-N*4)    ; Data area
  ...  SIZ   Data Area                                  ;/

E.T. Interplanetary Mission (MagDemo54: MEGA\MEGA.CSH+.BIN)
 MEGA.CSH:
  000h N*0Ch File List
 MEGA.BIN:
  000h ..    File Data area
File List entries:
  000h 4     Offset (in MEGA.BIN file, 800h-byte aligned, increasing)
  004h 4     Unknown (32bit id/random/checksum/whatever)
  008h 4     Filesize in bytes

Driver 2 The Wheelman is Back (MagDemo40: DRIVER2\SOUND\*\*)
  000h 4     Number of entries (1 or more)
  004h N*10h File List
  ...  ..    File Data area (.VB aka SPU-ADPCM)
 File List entries:
  000h 4     Offset from begin of Data area, increasing
  004h 4     Filesize in bytes
  008h 4     Unknown (0 or 1)
  00Ch 4     Unknown (AC44h, 0FA0h, 2EE0h, 2710h, 2B11h, 3E80h, 1F40h, etc.)
 Note: Above AC44h might 44100Hz, or just file number 44100 decimal?

Thrasher: Skate and Destroy (MagDemo27: SKATE\ASSETS\*.ZAL) (Z-Axis)
Dave Mirra Freestyle BMX (MagDemo36: BMX\ASSETS\*.ZAL) (Z-Axis)
Dave Mirra Freestyle BMX (MagDemo46: BMX\ASSETS\*.ZAL) (Z-Axis)
  000h 4     ID (always 2A81511Ch)
  004h 0Ch   Zerofilled
  010h 1     Unknown (1)
  011h 1     Compression Flag for all files (00h=Uncompressed, 80h=Compressed)
  012h 2     Number of files (bit0-13?=N, bit14=Unknown, can be set)
  014h N*0Ch File List, 12 bytes/entry      ;<-- when [11h]=00h=uncompressed
  014h N*10h File List, 16 bytes/entry      ;<-- when [11h]=80h=compressed
  ...  ..    File Data area
File List entries (0Ch or 10h bytes per entry, depending on compression):
  000h 4     File ID (usually 0=first, increasing) (or 0001h,7531h,7532h,...)
  004h 4     Offset-10h in bytes (increasing, 4h-byte aligned)
  008h 4     Filesize, uncompressed (can be odd)
  00Ch (4)   Filesize, compressed (can be odd)   ;<-- exists only if compressed
For decompression, see:
CDROM File Compression ZAL (Z-Axis)

Speed Punks (MagDemo32: SPUNKS\*.GDF)
  000h 4     ID "0FDG XSP" (aka PSX GDF0 backwards)
  008h 4     Header Size (N*10h+10h)
  00Ch 4     Number of files (N)
  010h N*10h File List
  ...  ..    Zeropadding to 800h-byte boundary
  ...  ..    File Data area
 File List entries:
  000h 4     ID/Type ("MARV", "MARS", "MARD", "PMET", "COLR", "MROF")
  004h 4     ID/Num  (usually 1 SHL N, or all zero)
  008h 4     Offset (800h-byte aligned, increasing)
  00Ch 4     Size in bytes

Legend of Dragoon (MagDemo34: LOD\SECT\*.BIN, and nested therein)
  000h 4     ID "MRG",1Ah
  004h 4     Number of Files (eg. 0, 1, 2, 193h, 2E7h, or 1DBBh)
  008h N*8   File List
  ...  ..    Padding to 800h-byte boundary (8Ch-filled) (not in nested MRG's)
  ...  ..    File Data area
 File List entries:
  000h 4     Offset/800h, or 4-byte aligned Offset/1 (increasing)
  004h 4     Size (can be odd, and can be zero)
 Size oddities:
  Empty files in demo version have Size=0 and Offset=0.
  Empty files in retail version have Size=0 and Offset=OffsetOfNextFile.
  MRG archives can start or end with Empty files.
  All files can be empty (eg. retail DRAGN0.BIN\1190h).
  NumFiles can be zero (eg. retail DRAGN0.BIN\1111h, demo DRAGN0.BIN\10E2h).
 Offset oddities:
  SECT\*.BIN have Offset/800h
  Nested MRGs have 4-byte aligned Offset/1
  The two variants can be detected as:
   if FirstOffset=(NumFiles*8+8) then NestedVariant
   if FirstOffset=(NumFiles*8+8+7FFh) AND NOT 7FFh then RootVariant
  Whereas, FirstOffset is the first NONZERO offset in file list (important
  for demo version, which has archives that start with ZERO offsets).

RC Revenge (MagDemo37: RV2\BB\3.BBK and Retail: BB\*\*.BBK)
This does basically contain four large files (and four info blocks with info on the content of those files).
  000h 4     Random/Checksum?
  004h 4     Faded ID (FADED007h)
  008h 4     Part 1 Offset (Sound)     (always E5Ch)
  00Ch 4     Part 2 Offset (Texture)   (when Type=01h: Offset-E5Ch)
  010h 4     Part 3 Offset (?)         (when Type=01h: Offset-E5Ch)
  014h 4     Part 4 Offset (?)         (when Type=01h: Offset-E5Ch)
  018h 4     Type (10h or 20h=Normal)  (or 01h=Special in BB\8\*.BBK)
  01Ch B0Ch  Part 1 Info (Sound)       (when Type=01h: garbage-filled)
  B28h 314h  Part 2 Info (Texture)
  E3Ch 14h   Part 3 Info (?)
  E50h 0Ch   Part 4 Info (?)
  E5Ch ..    Part 1 Data (Sound, SPU-ADPCM data, if any)
  ...  ..    Part 2 Data (Texture data) (starts with BDEF1222h or BDEF1111h)
  ...  ..    Part 3 Data (?)   ;\maybe map, models, and/or whatever
  ...  ..    Part 4 Data (?)   ;/
Part 1 Info (Sound info) (if any):
  01Ch 4     Random/Checksum?
  020h 4     Faded ID (FADED007h)
  024h 4     Part 1 Size              (eg.7C7F0h)
  028h 4     SPU Start Addr           (1010h) (for data from file offset E5Ch)
  02Ch 4     SPU Middle Addr          (eg. 58F70h)
  030h 4     SPU End Addr             (eg. 7D800h) (start+size)
  034h 2     Middle entry number      (often 3Ch)
  036h 2     Number of used entries-1 (eg. 50h means that 51h entries are used)
  038h AF0h  Sample List (100 entries, unused ones are zerofilled)
  914h 214h  Zerofilled (unused 1Ch-byte entries) (total is 1Ch*64h)
 Sample List entries:
  000h 4     SPU Offset (1010h and up) (SpuOffset=1010h is FileOffset=E5Ch)
  004h 4     Sample Size in bytes
  008h 4     Unknown (0)
  00Ch 4     Unknown (0)
  010h 4     Pitch   (400h=11025Hz, 800h=22050Hz, 2E7h=8000Hz, 8B5h=24000Hz)
  014h 4     Unknown (0 or 1)
  018h 4     File ID (00001F08h and up)
Part 2 Info (Texture info):
  B28h 4     Random/Checksum?
  B2Ch 4     Faded ID (FADED007h)
  B30h 4     Part 2 Size      (N*16000h)  ;Width=2C0h halfwords, Height=N*64
  B34h 4     Zero             (0h)
  B38h 4     Some RAM Address (8010xxxxh)
  B3Ch 4     Unknown          (eg. 195h or E3h) ;same as at [DA4h]
  B40h 4+4   VRAM Address X,Y (140h,0)          ;maybe load target
  B48h 4+4   VRAM Address X,Y (140h,0)          ;maybe palette base?
  B50h 4+4   VRAM Address X,Y (xx0h,Height-40h) ;often at/near end of used area
  B58h 4     Unknown          (eg. 1D0h or 1E0h)
  B5Ch 4     Unknown          (eg. 1Ah or 0Dh)
  B60h 200h  Some halfwords?  (most are FFFFh, some are 0000h)
  D60h 40h   Zerofilled       (0)
  DA0h 4     Unknown          (eg. 185h or E2h)
  DA4h 4     Unknown          (eg. 195h or E3h) ;same as at [B3Ch]
  DA8h 9x10h Special Texpages (VramX,Y, SizeX,Y, StepX,Y, Flag/Type/Num or so?)
  E38h 4     Some RAM Address (800Axxxxh)
Part 3 Info:
  E3Ch 4     Random/Checksum?
  E40h 4     Faded ID (FADED007h)
  E44h 4     Part 3 Size                  (eg. A9728h or 51264h)
  E48h 4     RAM End Address (start+size) (eg. 801Fxxxxh) (near memtop)
  E4Ch 4     RAM Start Address (end-size) (eg. 801xxxxxh)
Part 4 Info:
  E50h 4     Random/Checksum?
  E54h 4     Faded ID (FADED007h)
  E58h 4     Part 4 Size (usually 10CCCh) (or 105E0h in demo version)
Note: File CAT\RDS.CAT does also start with ID=FADED007h (but contains whatever different stuff).


CDROM File Archives with Offset

Below are archives that start with a simple Offset list. The DOT1 and DOTLESS types are "standard" archives used by many PSX games (although the "standard" was probably independently created by different developers).

DOT1 Archives (named after the ".1" extension in R-Types)
Used by various titles:
  R-Types (CG.1, PR\PR.1, and nested inside CG.1)
  Final Fantasy IX (nested inside FF9.IMG, FF9.IMG\DB, FF9.IMG\DB\DOT1)
  Legend of Mana (*.EFF,*.SET,*.BTP(?) in folders SND*,SOUND,WM(?))
  Witch of Salzburg (*.ANM/BIN/BSS/DAT/MDL/SCE)
  Rayman (RAY\*.XXX, RAY\SND\*.ALL, and nested inside *.XXX)
  Pandemonium II (JESTERS.PKG\0101\0008 and JESTERS.PKG\0101\000D)
  Incredible Crisis (MagDemo38: IC\TAN_DAT.CDB\<DOTLESS>\<DOT1>\<SHIFTJIS>)
  Various games on PlayStation Magazine Demo Discs (Disc 03-54)
DOT1 (in lack of a better name) is a simple archive format that contains Number of Entries and List with Increasing Offsets to File data.
  000h 4    Number of Files (N)                 (eg. 2..18)
  004h N*4  File List (offsets to each file, increasing, aligned)
  ...  (4)  Optional: Total filesize (aka end-offset for last list entry)
  ...  ..   Optional: Zeropadding to alignment boundary (when alignment>4)
  ...  ..   File Data
There are four variants with different alignment (and in some cases, with an extra entry with end-offset for last file):
  Align800h, no extra entry    R-Types (CG.1 and PR\PR.1)
  Align4,    no extra entry    R-Types (nested in CG.1), FF9 (in IMG, IMG\DB)
  Align2,    no extra entry    Incredible Crisis (IC\TAN_DAT.CDB\*\*)
  Align800h, with extra entry  MLB 2000 (DATA.WAD)
  Align10h,  with extra entry  Witch of Salzburg (*.ANM/BIN/BSS/DAT/MDL/SCE)
  Align4,    with extra entry  Rayman (*.XXX, *.ALL)
The files can be detected by checking [004h]=4+(N*4), 4+(N*4)+Align800h, 4+(N*4)+4, or 4+(N*4)+4+Align10h, and checking that the offsets are increasing with correct alignment (Rayman has some empty files with same offset), and don't exceed the total filesize. And that the alignment space is zeropadded (in case of R-Types, only the header is 00h-padded, but files are FFh-padded).
The detection could go wrong, especially if the archive contains very few files, some of the nested DOT1's contain only one file (header "00000001h, 00000008h", without any further increasing offsets or padding). As workaround, accept such files only if they have a ".1" filename extension, or if they were found inside of a bigger DOT1, IMG, or DB archive.
Final Fantasy IX contains some DOT1's with fewer than few entries (the file being only 4-bytes tall, containing value NumEntries=00000000h).

NFL Gameday '98 (MagDemo04: GAMEDAY\*.FIL) (32bit) (with nested FIL's)
NFL Gameday '99 (MagDemo17: GAMEDAY\*.FIL) (32bit)
NFL Gameday 2000 (MagDemo27: GAMEDAY\*.FIL) (16bit and 32bit)
NCAA Gamebreaker '98 (MagDemo05: GBREAKER\*.FIL,*.BIN) (16bit and 32bit)
NCAA Gamebreaker 2000 (MagDemo27: GBREAKER\*.FIL) (16bit and 32bit)
FIL/32bit (with [02h]=FFFFh):
  000h 2    Number of Files (N)
  002h 2    ID for 32bit version (FFFFh=32bit entries)
  004h N*4  File List (offsets to each file, increasing, 4-byte aligned)
  ...  ..   File Data
FIL/16bit (with [02h]<>FFFFh, eg. FLAG*.FIL and VARS\STARTUP2.FIL\0\*):
  000h 2    Number of Files (N)
  002h N*2  File List (offsets to each file, increasing, 4-byte aligned)
  ...  ..   Zeropadding to 4-byte boundary
  ...  ..   File Data

PreSizeDOT1 (Ace Combat 2) (retail and MagDemo01: ACE2.DAT\*)
Like DOT1, but with Total Filesize being oddly stored at begin of file.
  000h 4    Total Filesize (aka end-offset for last list entry)
  004h 4    Number of Files (N)
  008h N*4  File List (offsets to each file, increasing, 4-byte aligned)
  ...  ..   File Data
Note: Ace Combat 2 contains PreSizeDOT1 (ACE2.DAT\02h..1Dh,36h..B2h) and normal DOT1 archives (nested in PreSizeDOT1's and in ACE2.DAT\B3h..E1h).

DOT-T (somewhat same as DOT1, but with 16bit entries)
Armored Core (MagDemo02, AC10DEMP\*.T)
  000h 2    Number of Files
  002h N*2  File List (Offset/800h to file data, increasing)
  ...  2    Total Size/800h (end-offset for last file)
  ...  ..   Zeropadding to 800h-byte boundary
  ...  ..   File Data
This can contain many empty 0-byte files (aka unused file numbers; though maybe those files exist in the retail version, but not in the demo version).

DOTLESS Archive
Hot Shots Golf (MagDemo07: HSG\*.DAT)
Hot Shots Golf 2 (retail: DATA\F0000.BIN\*, MagDemo31/42: HSG2\MINGOL2.BIN\*)
Starblade Alpha (FLT\*.DAT, TEX\*.DAT)
Incredible Crisis (MagDemo38: IC\TAN_DAT.CDB\<DOTLESS>)
  000h N*4  Offsets to File data (increasing, usually 4-byte aligned)
  ...  (4)  Filesize (end-offset for last file) (only in Ape Escape)
  ...  ...  File Data
Like DOT1, but without Number of Files entry (instead, the first offset does imply the end of file list). There's no extra entry for end of last file (instead, that's implied in the total filesize). Most files have at least 5 entries, but HSG\TITLE0.DAT seems to contain only one entry (ie. the whole header contains only one value, 00000004h, followed by something that looks like raw bitmap data).
Also used by Ape Escape (MINIGAME\* included nested ones), the Ape Escape files do have an end-marker with last-offset (that will appear as an empty 0-byte file at end of list when not specifically handling it). MINIGAME\MINI2\BXTIM.BIN does also have several 0-byte files inside of the file list.

Twisted Metal: Small Brawl (MagDemo54: TMSB\SHL\*.TMS)
  000h 4     Size of Data Area (total filesize minus 0D0h)
  004h 4     Number of files
  008h N*4   File List (zerobased offsets from begin of Data Area)
  ...  ..    Zeropadding to 0D0h
  0D0h ..    File Data Area
This resembles DOT1, with an extra size entry and padding to 0D0h.

Ridge Racer Type 4 (MagDemo19: R4DEMO\R4.BIN, 39Mbyte)
Ridge Racer Type 4 (MagDemo21: R4DEMO\R4.BIN, 39Mbyte)
Basically, this is alike DOT1, but SECTOR numbers, and with extra entries...
  000h 4     Number of Files (N) (3C9h)
  004h N*4   File List (Offset/800h)
  ...  4     Total Size/800h                  ;<-- last offset
  ...  4     Unknown (00,E8,82,2E)            ;<-- ??? maybe chksum*800h or so?
  ...  ..    Zeropadding to 800h-byte boundary
  ...  ..    File Data area

Legend of Legaia (MagDemo20: LEGAIA\PROT.DAT)
  000h 4     Zero
  004h 4     Number of Entries (4D3h)
  008h N*4   File List (Offset/800h)
  ...  4     Total Size/800h (aka end Offset/800h of last file)
  ...  ..    Zeropadding to 800h-byte boundary
  ...  ..    File Data area
The PROT.DAT does not contain filenames, however, it's bundled with CDNAME.TXT, which appears to contain symbolic names for (some) indices:
  #define init_data 0           ;for file 0000h
  #define gameover_data 1       ;for file 0001h
  #define town01 3              ;for file 0003h
  #define town0b 12             ;for file 000Ch
  ...                           ;...
  #define other6 1222           ;for file 04C6h
  #define other7 1228           ;for file 04CCh
The DAT file contains many zerofilled "dummy" files with 800h-byte size.

Bloody Roar 1 (MagDemo06: BL\*.DAT)
Bloody Roar 2 (MagDemo22: ASC,CMN,EFT,LON,SND,ST5,STU\*.DAT)
  000h 4     Number of Entries (N)
  004h N*4   File List (Offset-(4+N*4), increasing) (or FFFFFFFFh=Unused entry)
  ...  ..    File Data area
Most or all files in DAT archives are PreGT20 compressed.
CDROM File Compression GT20 and PreGT20
Note: Unused entries can occur anywhere, eg. Bloody Roar 2 CMN\SEL01.DAT does have both first and LAST entry marked as unused (FFFFFFFFh). Also, there may be a lot of unused entries, eg. Bloady Roar 1 CMN\TITLE00.DAT uses only 5 of 41h entries).

Klonoa (MagDemo08: KLONOA\FILE.IDX\*)
  000h 4     ID "OA05"
  004h N*4   Offset List (usually/always 5 used entries, plus zeropadding)
  030h ..    File Data area (usually/always starting at offset 30h)

C - The Contra Adventure (DATA\SND\*.SGG)
  000h 4    ID "SEGG"
  004h 4    Offset to .VH file
  008h 4    Offset to .VB file
  00Ch 4    Number of .SEQ files (N) (usually 6Eh, or 08h in MENU.SGG)
  010h N*4  Offsets to .SEQ files (increasing, unaligned)
  ...  ..   SEQ files
  ...  ..   Padding to 4-byte boundary
  ...  ..   VH file
  ...  ..   VB file

Ninja (MagDemo13: NINJA\VRW\*.VRW)
  000h 8     ID "VRAM-WAD" (here as archive ID, although same as compress ID)
  004h N*4   File List (offsets to Data)  ;NumFiles=(FirstOffset-8)/4
  ...  ..    Data (compressed .PAK files, which do ALSO have ID="VRAM-WAD")
The compressed .PAK files are using a LZ5-variant:
CDROM File Compression LZ5 and LZ5-variants

The Next Tetris (MagDemo22: TETRIS\*) has PSX.BSE (and nested therein)
  000h 4     Unknown (3)
  004h 4     Total Size
  008h 4     Number of Files (N) (max 40h, for max 40h*4 bytes in file list)
  00Ch N*4   File List (increasing offsets, 800h-byte aligned)
  ...  ..    Unknown (looks like garbage padding for unused File List entries)
  10Ch 6F4h  42h-filled padding to 800h-byte boundary
  800h ..    File Data area

Tactics Ogre (UBF*.BIN)
  000h 8     Fixed (88h,0,0,0,0,0,0,0)
  008h 4     Number of Files (eg. 1Dh or 585h, including last/end file)
  00Ch N*4   File List (increasing offsets, 800h-byte aligned)
  ...  ..    Zeropadding to 800h-byte boundary
  ...  ..    File Data area
Note: The last file is a TXT file containing "LINK-FILE END....",0Dh,0Ah,1Ah, plus zeropadding to 800h-byte boundary.

Spyro the Dragon (MagDemo12: SPYRO\PETE.WAD)
  000h 4     Total Filesize (3E800h in Spyro)
  004h N*8   File List      (1B0h bytes in Spyro)
  ...  ..    Zeropadding to 800h-byte boundary
  ...  ..    File Data (4-byte aligned, despite of above 800h-byte hdr padding)
File List entries:
  000h 4     Fileoffset (increasing, 4-byte aligned)
  004h 4     File ID? (unsorted, not increasing, used range is 000h..1FAh)


CDROM File Archives with Size

Disney-Pixar's Monsters, Inc. (MagDemo54: MINC\*.BZE)
  000h 4     Zero (0)
  004h 4     Type/ID (27100h=160000, 2BF20h=180000, 30D40h=200000 decimal)
  008h 4     Number of files
  00Ch N*0Ch File List
  ...  ..    Zeropadding to 7FCh
  7FCh 4     Checksum (32bit sum of SIGN-EXPANDED bytes at [000h..7FBh])
  ...  ..    File Data
File List entries:
  000h 4     File Type/ID or so (roughly increasing, eg. 1,3,6,5,7,8,9,A,B)
  004h 4     Filesize in bytes
  008h 4     Filesize rounded up to multiple of 800h bytes

Bugs Bunny: Lost in Time (MagDemo25: BBLIT\*.BZZ) (without extra entry)
The Grinch (MagDemo40: GRINCH\*.BZZ) (with extra entry)
Resembles .BZE, but without the Type entry in Header.
  000h 4     Fixed 1 (maybe version, or compression flag)
  004h (4)   Unknown (000xxxx0h)   ;<-- Extra in The Grinch only (not Bunny)
  ...  4     Number of files
  ...  N*0Ch File List
  ...  ..    Zeropadding to 7FCh
  7FCh 4     Checksum (32bit sum of SIGN-EXPANDED bytes at [000h..7FBh])
  ...  ..    File Data
File List entries:
  000h 4     File Type/ID or so (roughly increasing, eg. 1,2,3,6,5,7,8,9,A)
  004h 4     Filesize in bytes (rounded to N*4 even if compressed data is less)
  008h 4     Filesize rounded up to multiple of 800h bytes
Files are compressed, starting with 0Bh, same as in Jersey Devil...
CDROM File Compression BZZ
Note: The TIM files in Bugs Bunny and The Grinch BZZ archives consists of two TIMs badged together: A 4x4 pix dummy TIM, followed by the actual 512x125 pix TIM (in some cases followed some extra bytes at end of file?).

Jersey Devil .BZZ (MagDemo10: JD\*.BZZ)
Resembles .BZE, but without the Type entries in Header and File List, and without Header checksum.
  000h 4     Fixed 1 (maybe version, or compression flag)
  004h 4     Number of files (4)
  008h N*8   File List
  ...  ..    Zeropadding to 800h-byte boundary (without checksum, unlike .BZE)
  ...  ..    File Data
File List entries:
  000h 4     Size in bytes
  004h 4     Size rounded to multiple of 800h
Files are compressed, starting with 0Bh, same as in Bugs Bunny...
CDROM File Compression BZZ

Jackie Chan Stuntmaster (RCHARS\*.RR)
NBA Basketball 2000 (MagDemo28: FOXBB\*.RR)
  000h 2     ID ("PX")
  002h 2     Unknown (1 or 3)
  004h 4     Header Size (eg. 80h, 7C0h, or 1730h) (N*8+8)
  008h N*8   File List
  ...  ..    Zeropadding to 800h-byte boundary
  ...  ..    File Data area
 File List entries:
  000h 4     Offset (increasing, 800h-byte aligned)
  004h 1     Zero
  005h 3     Filesize in bytes (24bit) (can be odd)
Jackie Chan Stuntmaster does always have headersize=1730h (with many unused entries with size=0, both in the middle & at the end of File List).

Bomberman World (MagDemo15: BOMBER\*.RC)
                XXX detect this WITH extension=".RC" check before OBJ
                    (else type=1 could be mistaken as offs=1) (eg RC1\BP0*.RC)
Resembles .OBJ but contains Filetype? instead of Offset.
  000h N*8   File List
  ...  8     File List end (zerofilled)
  ...  ..    Garbage padding to 800h-byte boundary
File List entries:
  000h 4     Filetype (see below)
  004h 4     Filesize in bytes
There can be several files with same type in one .RC archive. Type values are:
  00h = End of File List (at least so when Type and Size are both zero)
  01h = .TIM
  02h = Unknown
  03h = Unknown
  05h = .VH
  06h = .VB
  09h = Unknown
  0Ah = .TIM (left half of a larger image) (right half has type 01h)
  0Bh = Unknown
  0Ch = Unknown

Mat Hoffman's Pro BMX (new demo) (MagDemo48: MHPB\BMXCD.HED+WAD)
This format is used by the NEW demo version on MagDemp48 (the OLD demo version on MagDemo39 did use Spider-Man-style HED/WAD format with filenames).
 HED:
  000h 2     Number of entries (N)
  002h N*6   File List
 WAD:
  000h ...   File data (at 800h-byte aligned locations)
File List entries:
  000h 3     File ID (24bit)
  003h 3     File Size in bytes (21bit, max 2Mbyte) (upper 3bit=unused?)
Note: HED is processed at 80052AC0h in MagDemo48.

Madden NFL 2000 (MagDemo27: MADN00\*.DAT and nested therein)
Madden NFL 2001 (MagDemo39: MADN01\*.DAT and nested therein)
  000h 4     Header Size (N*SectorSize) (xxh, 800h, 1000h, 4800h, or 920h)
  004h 4     Sector Size (4=ChildArchive, 800h=MainArchive, 920h=FMV/MADN00)
  008h 4     File List entrysize (0=32bit, 1=16bit/MADN00, 4=16bit/MADN01)
  00Ch N*2/4 File List (16bit or 32bit filesizes in bytes)
  ...  ..    Zeropadding to SectorSize boundary
  ...  ..    Files (with above sizes, each zeropadded to SectorSize boundary)
Dummy files have filesize=1 (but they do nethertheless occupy a whole data sector).
Unknown why the FMV file in MADN00 is using SectorSize=920h (it appears to be FORM2 related, although the file seems to be stored in FORM1 sectors, but the STR movie appears to work okay despite of the odd size).

Croc 2 (MagDemo22: CROC2\CROCII.DIR\FESOUND.WAD)
Disney's The Emperor's New Groove (MagDemo39:ENG\KINGDOM.DIR\FESOUND.WAD)
Disney's Aladdin in Nasira's Rev. (MagDemo46:ALADDIN\ALADDIN.DIR\FESOUND.WAD)
  000h 4     Total Filesize-4
  004h N*14h File List (2 entries in Croc2, 3 entries in Aladdin/Emperor)
  ...  ..    File Data area (SPU-ADPCM( (.VB files with leading zeroes)
 File List entries:               (Aladdin/Emperor) (Croc2)
  000h 4     Sample Rate in Hertz (AC44h=44100Hz)   (5622h=22050Hz)
  004h 2     Sample Rate Pitch    (1000h=44100Hz)   (0800h=22050Hz)
  006h 2     Unknown              (7Fh)             (32h)
  008h 4     Unknown              (1)               (8)
  00Ch 4     Unknown              (1FC0001Fh)       (40008Fh)
  010h 4     Filesize             (xxx0h)           (xxx0h)
The number of files is implied in sum of filesizes versus total size.

Dino Crisis 1 and 2 (PSX\DATA\*.DAT and *.DBS and *.TEX) ("dummy header")
  000h 800h  File List (with 10h or 20h bytes per entry)
  800h ..    File Data (each file is zeropadded to 800h-byte boundary)
File List entrysize can be 10h or 20h bytes:
  Dino Crisis 1 --> always size 10h
  Dino Crisis 2 --> usually size 20h
  Dino Crisis 2 --> sometimes size 10h (eg. SC24.DAT, SC48.DAT, WEP_*.DAT)
File List entries:
 File List entries, type 0 and 7:
  000h 4     Type (0=Data (or .BS pictures), 7=CompressedData)
  004h 4     Size
  008h 4     RAM Addresss (80000000h..801FFFFFh)
  00Ch 4     Zero
  010h (10h) Zerofilled
 File List entries, type 1 and 2 and 8:
  000h 4     Type (1=Bitmap, 2=Palette, 8=CompressedBitmap)
  004h 4     Size (see below Size Notes)
  008h 2     VRAM Address X     (0..3FFh)
  00Ah 2     VRAM Address Y     (0..1FFh) (or 280h in Dino 2 ST703.DAT)
  00Ch 2     Width in halfwords (1..400h)
  00Eh 2     Height             (1..200h)
  010h (10h) Zerofilled
 File List entries, type 3 and 4:
  000h 4     Type (3=VoiceHeader("Gian"), 4=VoiceData(SPU-ADPCM))
  004h 4     Size
  008h 4     SPU Address (0..7FFF0h)
  00Ch 2     Unknown (0..7)  ;\usually both same (or val1=0, val2>0)
  00Eh 2     Unknown (0..7)  ;/
  010h (10h) Zerofilled
 File List entries, type 5 (eg. ME*.DAT):
  000h 4     Type (5=Unknown... maybe Midi-style or so)
  004h 4     Size
  008h 4     Load Address (0, or on next 4-byte boundary after previous file)
  00Ch 2     Unknown (0..2)  ;\always both same
  00Eh 2     Unknown (0..2)  ;/
  010h (10h) Zerofilled
 File List entries, type 6 and 9:
  The EXE code does also accept type 6 and 9 (type 6 is handled same as
  type 0, and type 9 is ignored), but the actual archives don't seem to
  contain any files with those types.
 File List entries, padding for unused entries:
  000h 10h   Type ("dummy header    ")
  010h (10h) Zerofilled
Size Notes:
 Bitmaps and Palettes can have following sizes:
  Width*Height*2                       ;normal case
  Width*Height*2 + Align(1000h)        ;eg. Dino Crisis 1 DOOR*.DAT
  Width*Height*2 + Align(800h)         ;eg. Dino Crisis 2 DOOR27.DAT
 CompressedBitmaps can have following sizes in compressed form:
  Less than Width*Height*2             ;normal case
  Less than Width*Height*2 + 1000h     ;eg. Dino Crisis 2 M_RESULT,ST002.DAT
 CompressedBitmaps can have following sizes after decompression:
  Width*Height*2 + 8                   ;normal case
  Width*Height*2 + Align(1000h?) + 8   ;eg. Dino Crisis 2 M_RESULT,ST002.DAT
Note: Dino Crisis DEMO version (MagDemo28: DINO\TRIAL.DAT) does also contain "dummy header" DAT archives (but, unlike as in retail version, they are hidden somewhere inside of the headerless 14Mbyte TRIAL.DAT archive).
Type 7 and 8 are using LZSS compression:
CDROM File Compression LZSS (Dino Crisis 1 and 2)
Apart from LZSS, Type 4 is using SPU-ADPCM compression, and some Type 0 files contain .BS compressed pictures (eg. Dino Crisis 2 PSX\DATA\ST*.DBS\*).


CDROM File Archives with Chunks

Chunk-based archives have chunk headers for each file, but don't have a central directory. That's mainly useful when loading the whole archive to memory.

Interchange File Format (IFF)
IFF has been invented by Electronic Arts in 1985 on Amiga (hence using 2-byte alignment and big-endian size values).
IFF does mainly define a standarized file structure for use with custom group/chunk types (it does also define some Amiga-specific standard audio/video types, but those are barely useful on PSX).
The files are starting with a Group Header, followed by Chunks:
 Group Header:
  000h 4     Group ID ("FORM") (or "LIST" or "CAT " or "PROP")
  004h 4     Group Size-08h (SIZ) (filesize-8) (big-endian)
  008h 4     Group Type (4-character ASCII) (should be an unique identifier)
  00Ch SIZ-4 Chunk(s), and/or nested Group(s)
 Chunk Format:
  000h 4     Chunk Type (4-character ASCII) (meaning depends on Group Type)
  004h 4     Chunk Size (SIZ) (big-endian)
  00Ch SIZ   Data (eg. .TIM, .VB, .VH or custom data)
  ...  ..    Zeropadding to 2-byte boundary
Used by Forsaken (MagDemo09: FORSAKEN\*\*.BND,MP,PCO)
Used by Perfect Assassin (DATA.JFS\DATA\SCREEN1.LBM)
Used by Star Wars Demolition (MagDemo39,41: STARWARS\*.EXP)
Used by Turbo Prop Racing (MagDemo11: RRACER\*.IFF, except COURSE.IFF)
Used by Viewpoint (VIEW.DIR\*.VCF,*.VCS,*.ST*) - some have wrong Size entry?
Used by Vigilante 8 (MagDemo09: EXAMPLE\*.EXP)
Used by Wing Commander III (*.LIB\*.IFF)
Bugs in Viewpoint: fonts\*.vcf have correct Groupsize=Filesize-8, but screens\*.vcf have incorrect Groupsize=Filesize-4, and streams\*.vcf have weirdest random Groupsize=Filesize+(-04h,+08h,+14h,+5A0h).

Z-Axis little-endian IFF variant
Unlike real IFF, these are using little-endian, and don't have a Group Type entry. There seem to be no nested FORMs. Alignment is kept as 2-byte.
 Group Header:
  000h 4     Group ID ("FORM" or "BODY")
  004h 4     Group Size-08h (SIZ) (little-endian)
  008h SIZ   Chunk(s)
 Chunk Format:
  000h 4     Chunk Type (4-character ASCII)
  004h 4     Chunk Size (SIZ) (little-endian)
  00Ch SIZ   Data
  ...  ..    Zeropadding to 2-byte boundary
ID "FORM" used by Thrasher: Skate and Destroy (MagDemo27: SKATE\ASSETS\*.ZAL\*)
ID "FORM" used by Dave Mirra Freestyle BMX (MagDemo36,46: BMX\ASSETS\*.ZAL\*)
ID "BODY" used by Colony Wars (MagDemo02: CWARS\GAME.RSC\*.BND)
ID "BODY" used by Colony Wars Venegance (MagDemo14: CWV\GAME.RSC\*.BND)

Alice in Cyberland little-endian IFF variant (.TPK)
Same as Z-Axis IFF variant, except Group IDs are different, and the Header sizes are included in the Group/Chunk sizes.
 Group Header:
  000h 4     Group ID ("hTIX","hFNT","hMBD","hHBS")
  004h 4     Group Size (total filesize) (little-endian)
  ...  (8)   Unknown extra (0,0,0,0,0Ch,0,0,0)   ;<-- only in "hHBS" files
  ...  ..    Chunk(s)
 Chunk Format:
  000h 4     Chunk Type ("cCLT","cBIT","cSTR","cMAP","cIDX","cVAB","cSEQ")
  004h 4     Chunk Size (SIZ) (little-endian)
  00Ch SIZ-8 Data
  ...  ..    Maybe Zeropadding to boundary? (Chunk Size is always N*4 anyways)
ID "hTIX" used by Alice in Cyberland (ALICE.PAC\alice.tpk, csel.tpk, etc.)
ID "hFNT" used by Alice in Cyberland (ALICE.PAC\alice.tpk, juri.tpk, etc.)
ID "hMBD" used by Alice in Cyberland (ALICE.PAC\*.FA2\*.MBD)
ID "hHBS" used by Alice in Cyberland (ALICE.PAC\0x_xx.HBS)

Touring Car Championship (MagDemo09: TCAR\GAME\*\*.BFX)
Jarret & LaBonte Stock Car Racing (MagDemo38: WTC\*\*.BFX)
Contains several simple chunks:
  000h 4     Chunksize in bytes (SIZ) (usually a multiple of 4)
  004h SIZ   Chunkdata (eg. .TIM file or other stuff)
There is no end-marker in last chunk (it simply ends at total filesize).

Colony Wars Venegance (MagDemo14: CWV\GAME.RSC\VAG.WAD)
Colony Wars Red Sun (MagDemo31: CWREDSUN\GAME.RSC\0002\VAG_WAD)
Contains several simple chunks with filenames:
  000h 0Ch   Chunk Filename ("filename.ext", zeropadded if shorter)
  00Ch 4     Chunk Data Size in bytes (SIZ)
  010h SIZ   Chunk Data (usually VAGp files, in VAG.WAD)
There is no end-marker in last chunk (it simply ends at total filesize).
Red Sun VAG_WAD is a bit odd: The "extension" is _WAD instead .WAD, the chunk names include prefix "RedSun\", which leaves only 5 chars for the actual name, causig duplicated names like "RedSun\laser" (which were supposedly meant to be named laser1, laser2, laser3 or the like), and many of the Red Dun VAG files contain damaged 30h-byte VAG header entries, eg. zero instead of ID "VAGp").

Mat Hoffman's Pro BMX (new demo) (MagDemo48: MHPB\STILLS.BIN)
Contains .BS files in several chunks:
  000h ..    Chunk(s) (.BS files with extra header info)
  ...  4     End Marker (00000000h)
 Chunk format:
  000h 4     Chunk size (including whole chunk header)
  004h 2     Bitmap Width  (eg. F0h)
  006h 2     Bitmap Height (eg. 80h)
  008h 2     Data Size/4 (same as (Chunksize-0Ch-filenamelen)/4)
  00Ah 2     MDEC Size/4 (same as at Data[0])
  00Ch ..    Filename (eg. "lsFact",00h or "bsRooftop1",00h)  ;\filename field
  ...  ..    Filename Zeropadding to 4-byte boundary          ;/
  ...  ..    Data (in BS v2 format) (MDEC Size/4, BS ID 3800h, etc.)
Note: STILLS.BIN exists in newer BMX demo in MagDemo48 only (not in MagDemo39).

Ridge Racer (TEX*.TMS)
Ridge Racer Revolution (BIG*.TMS)
Ridge Racer Type 4 (MagDemo19+21: R4DEMO\R4.BIN\*\*)
  000h 4     ID (100h)
  004h ..    Chunk(s)
  ...  4     Zero (Chunk Size=0=End)
  ...  ..    Optional zeropadding to 800h-byte boundary (in R4.BIN\*)
Chunk Format:
  000h 4     Chunk Size (SIZ)
  004h SIZ   Chunk Data (TIM file) (note: includes 0x0pix TIMs with palette)

Jet Moto 2 (MagDemo03: JETMOTO2\*.TMS)
Twisted Metal 2 (MagDemo50: TM2\*.TMS)
Contains a fileheader and .TIM files in several chunks:
  000h 8     ID "TXSPC",0,0,0 (aka CPSXT backwards)
  008h 4     Timestamp? (32A5C8xxh)
  00Ch 4     Number of Chunks (N) (can be 0=None, eg. TM2\SCREEN\ARROWS.TMS)
  010h N*4   Unknown
  ...  N*var Chunks
 Chunk format:
  000h 4     Chunk Size-4 (SIZ)
  004h SIZ   Chunk Data (TIM file)

Princess Maker - Yumemiru Yousei (BDY\*.BD and PM.*)
The BDY\*.BD files do simply contain several chunks:
  000h ..   Chunk(s)
The PM.* files do contain several "folders" with fixed size:
  000h ..   Chunk(s) for 1st folder              ;\Foldersizes are:
  ...  ..   Zeropadding to Foldersize-boundary   ;  20000h (PM.DT0 and PM.PCC)
  ...  ..   Chunk(s) for 2nd folder              ;  28000h (PM.MAP)
  ...  ..   Zeropadding to Foldersize-boundary   ;  42000h (PM.SD0)
  ...  ..   etc.                                 ;/
Chunk Format:
  000h 4    Chunk ID   (800000xxh)
  004h 4    Chunk Size (size of Data part, excluding ID+Size)
  008h ..   Data
The Data for different Chunk IDs does usually have a small header (often with w,h,x,y entries, aka width/height, vram.x/y) followed by the actual data body:
  80000004h  x(2),y(2),width(2),height(2)    Bitmap 8bpp          ;PM.PCC,MAP
  80000005h  w(2),h(2),zero(4)               Array32bit(w,h)      ;PM.MAP
  80000006h  x(2),width(2)                   Bitmap Palette       ;PM.*
  80000007h  x(2),y(2),w(1),h(1),zero(2)     Array8bit(w,h)       ;PM.MAP
  80000010h  width(2),height(2),x(2),y(2)    Bitmap 16bpp         ;*.BD
  80000012h  zero(0)                         ?                    ;*.BD
  80000014h  x(2),y(2),width(2),height(2)    Bitmap 4bpp          ;PM.DT0
  80000016h  x(2),y(2),w(1),h(1),n(1),3Fh(1) BitmapArray4bpp(n*2) ;PM.DT0
  80000018h  ...                             ?                    ;PM.PCC
  8000001Ah  zero(8)                         ?                    ;PM.PCC
  8000001Ch  x(2),y(2),width(2),height(2)    Bitmap 1bpp flags?   ;*.BD
  80000020h  zero(8)                         Sound .SEQ file      ;PM.SD0
  80000021h  zero(8)                         Sound .VH file       ;PM.SD0
  80000022h  zero(8)                         Sound .VB file       ;PM.SD0
  80000024h  x(2),zero(6)                    ?                    ;PM.DT0\4\0
  00000000h  Zeropadding to next folder      Zeropadding          ;PM.*

Project Horned Owl (COMDATA.BIN, DEMODATA.BIN, ROLL.BIN, ST*DATA.BIN)
  000h ..    Chunks
Chunk Format:
  000h 1     Chunk Type (see below)
  001h 3     Unknown (some flags or file ID, or zero in many files)
  004h 4     Chunk Size (SIZ)
  008h SIZ   Chunk Data (eg. SEQ file)
Chunk Type values:
  02h  unknown                      ST*.BIN
  05h  .TXT                         ROLL.BIN
  05h  LZ-compressed TIM            DEMODATA.BIN, ST*.BIN (except ST1*.BIN)
  06h  DOT1 with stuff and TSQ??    ST*.BIN
  07h  .TMD                         DEMODATA.BIN, ST*.BIN (except ST1*.BIN)
  08h  unknown                      ST*.BIN
  09h  "PRM:"                       ST*.BIN
  0Ah  unknown                      ST*.BIN
  0Bh  DOT1 with stuff              ST*.BIN (except ST1*.BIN) (odd: ST3*.BIN)
  0Ch  .SEQ                         ROLL.BIN, ST*.BIN
  0Dh  unknown                      COMDATA.BIN
  0Eh  unknown                      ST*.BIN
  0Fh  DOT1 with LZ-compressed TIMs ST*.BIN
  10h  DEFLATE-compressed TIM       COMDATA.BIN, ROLL.BIN, ST*.BIN
  11h  DOT1 with stuff              ST*.BIN
  Note: Type=05h can be uncompressed TXT or compressed TIM.
For detection, the existing .BIN files start with following values:
  07 00 00 00 xx xx 00 00 41 00 00 00 ..   TMD Model ("A")
  0C 00 00 00 xx xx 00 00 70 51 45 53 ..   SEQ Midi  ("pQES")
  0E xx 00 00 08 00 00 00 xx xx xx xx ..   Whatever in ST7DATA.BIN (see note)
  10 01 00 00 24 28 00 00 EC 9B 7F 70 ..   Deflated TIM in COMDATA.BIN
  10 08 1A 00 30 0C 00 00 ED 58 4F 88 ..   Deflated TIM in ROLL.BIN
  ST7DATA.BIN has 2 chunks with Type=0Eh, followed by SEQ chunk at offset=20h.
TIMs are compressed via HornedLZ (Type=05h,0Fh) or Deflate (Type=10h).
CDROM File Compression HornedLZ
CDROM File Compression ZIP/GZIP/ZLIB (Inflate/Deflate)
The game's Inflate function does ignore the 2bit blocktype: All blocks must have dynamic trees (fixed trees and uncompressed blocks aren't supported).

Blaster Master (DATA\*.IDX, DATA\*.DAT)
DATA\GRP.IDX, DATA\MAP.IDX, DATA\SEQ.IDX DATA\VAB.IDX:
  000h N*2  Chunk List (16bit Offset/800h to Part-1-Chunks in .DAT files)
  ...  ..   Zeropadding to 800h-byte boundary
  Notes:
  The Chunk List can contain zeroes (as first entry at offset 0, and as
  unused entries; in VAB.IDX those can be followed by further USED entries).
  For 2-part DAT files, the Chunk List contains offsets for Part 1 only.
DATA\SEQ.DAT:
  000h 4    Chunksize/800h                                           ;\
  004h 4    Datasize in bytes                                        ; Single
  008h 4    Always 015A5A01h or 015A5A00h                            ; Part
  00Ch 4    Always 2803h                                             ; with
  010h ..   Midi data .SEQ file                                      ; 1 file
  ...  ..   Zeropadding to 800h-byte boundary                        ;/
DATA\VAB.DAT:
  000h 4    Chunksize/800h                                           ;\
  004h 4    Size of .VH Voice Header in bytes                        ; Single
  008h 4    Size of .VB Voice Binary in bytes                        ; Part
  00Ch ..   Voice Header .VH file                                    ; with
  ...  ..   Zeropadding to 800h-byte boundary                        ; 2 files
  ...  ..   Voice Binary .VB file                                    ;
  ...  ..   Zeropadding to 800h-byte boundary                        ;/
DATA\GRP.DAT and DATA\MAP.DAT:
  000h 4    Part 1 Chunksize/800h                                    ;\
  004h 4    Size of all TIM files in bytes (can be 0=None)           ; Part 1
  008h ..   Texture data (several TIMs appended after each other)    ;
  ...  ..   Zeropadding to 800h-byte boundary                        ;/
  ...  4    Number of Files (N)                                      ;\
  ...  4    Part 2 Chunksize/800h                                    ;
  ...  N*8  File List                                                ; Part 2
  ...  ..   Garbage-padding to 800h-byte boundary?                   ;
  ...  ..   File Data area (each file Garbage-padded to 800h-byte)   ;
 File List entries:                                                  ;
  000h 4    File Type/ID                                             ;
  004h 4    Size in bytes                                            ;/
The DAT files are chunk-based (unfortunately, each DAT file is using its own chunk format, some of them are using 2-part chunks).
The DAT chunks can be parsed without using the IDX file (the IDX can be helpful for quick lookup, but even then, one will still need to parse the DAT chunk headers to find the actual contents like TIM, SEQ, VB, VH files).

See also
CDROM File Archive Darkworks Chunks (Alone in the Dark)
CDROM File Archive Blue Chunks (Blue's Clues)
CDROM File Archive HED/CDF (Parasite Eve 2)
CDROM File Compression LZSS (Serial Experiments Lain)
CDROM File Compression SLZ/01Z (chunk-based compressed archive)


CDROM File Archives with Folders

There are several ways to implement folder-like directory trees:
  - Using multiple archive files nested within each other
  - Using filenames with path string (eg. "path\filename.ext")
Other than that, below are special formats with dedicated folder structures.

Archives with Folders
CDROM File Archive HUG/IDX/BIZ (Power Spike)
CDROM File Archive TOC/DAT/LAY
CDROM File Archive WAD (Doom)
CDROM File Archive WAD (Cardinal Syn/Fear Effect)
CDROM File Archive DIR/DAT (One/Viewpoint)
CDROM File Archive HED/CDF (Parasite Eve 2)
CDROM File Archive IND/WAD (MTV Music Generator)
CDROM File Archive GAME.RSC (Colonly Wars Red Sun)
CDROM File Archive BIGFILE.DAT (Soul Reaver)
CDROM File Archive FF8 IMG (Final Fantasy VIII)
CDROM File Archive FF9 IMG (Final Fantasy IX)
CDROM File Archive GTFS (Gran Turismo 2)
CDROM File Archive Nightmare Project: Yakata
CDROM File Archive FAdj0500 (Klonoa)
See also: PKG (a WAD.WAD variant with folders)

Perfect Assassin (*.JFS)
 Overall File Structure
  JFS for root                                   ;\
  JFS for 1st folder   ;\these are dupicated,    ; header with complete list
  JFS for 2nd folder   ; also stored in below    ; of all file/folder names
  JFS for 3rd folder   ; data area               ;
  etc.                 ;/                        ;/
  JFS for 1st folder, plus data for files in that folder  ;\
  JFS for 2nd folder, plus data for files in that folder  ; data area
  JFS for 3rd folder, plus data for files in that folder  ;
  etc.                                                    ;/
JFS Headers (0Ch+N*14h bytes)
  00h 4     ID "JFS",00h
  04h 4     Size in bytes (for root: including nearby child JFS's)
  08h 4     Number of file/folder entries in this folder (N)
  0Ch N*14h File/Folder entries
File Entries (with [10h].bit31=0):
  00h 12  "FILENAME.EXT" (or zeropadded if shorter)
  0Ch 4   Offset from begin of JFS in data area (without any alignment)
  10h 4   Size in bytes, plus 00000000h=File
Folder Entries (with [10h].bit31=1):
  00h 12  "DIRNAME.EXT" (or zeropadded if shorter)
  0Ch 4   Offset to child JFS in data area
  10h 4   Offset to child JFS in header area, plus 80000000h=ChildFolder
The JFS format is almost certainly unrelated to IBM's "Journaled File System".

Alone in the Dark The New Nightmare (FAT.BIN=Directory, and DATA.BIN=Data)
 FAT.BIN:
  00h 2     Number of folders (X) (43h)
  02h 2     Number of files   (Y) (8F0h)
  04h 4     Unknown               (1000h)
  08h X*10h Directory Entry 0000h..X-1 (entry 0000h is named "ROOT")
  ..  Y*10h File Entry 0000h..Y-1
 DATA.BIN:
  00h ..    File Data area
Directory Entries (10h bytes):
  00h 8    Name (terminated by 00h if less than 8 chars)
  08h 2    First Subdirectory number (0001h and up, 0000h would be root)
  0Ah 2    Number of Subdirectories  (0000h=None, if so above is usually 00FFh)
  0Ch 2    First File number         (0000h and up)
  0Eh 2    Number of files           (0000h=None, if so above is usually 00FFh)
File Entries (10h bytes):
  00h 8    Name (terminated by 00h if less than 8 chars)
  08h 4    Offset/800h to DATA.BIN
  0Ch 4    Size in bytes (when compressed: decompressed size+02000000h)
Compressed files (in LEVELS\*\* with Size.bit25=1) can be decompressed as so:
CDROM File Compression Darkworks
The files include some TIM images, WxH images, binary files, and chunks:
CDROM File Archive Darkworks Chunks (Alone in the Dark)

Interplay Sports Baseball 2000 (MagDemo22: BB2000\* HOG.DAT and HOG.TOC)
 HOG.TOC:
  000h N*14h Folder/File List (starting with root folder)
 HOG.DAT:
  000h ..    File Data (referenced from HOG.TOC)
Folder entries:
  000h 1     Type      ("D"=Directory)
  001h 8     Name      ("FILENAME", zeropadded if shorter) (or "\" for root)
  009h 3     Extension (usually zero for directories)
  00Ch 4     Folder Offset/14h in .TOC file (aka 1st child file/folder index)
  010h 4     Folder Size/14h                (aka number of child files/folders)
File entries:
  000h 1     Type      ("F"=File)
  001h 8     Name      ("FILENAME", zeropadded if shorter)
  009h 3     Extension ("EXT", zeropadded if shorter)
  00Ch 4     File Offset/800h in .DAT file (increasing)
  010h 4     File Size in bytes

Tenchu 2 (MagDemo35: TENCHU2\VOLUME.DAT)
  000h 4     Unknown (demo=A0409901h, us/retail=A0617023h)
  004h 4     Unknown (0h)
  008h 4     Number of files   (F) (demo=B7h, us/retail=1294h)
  00Ch 4     Number of folders (D) (demo=0Fh, us/retail=3Eh)
  010h D*8   Folder List
  ...  ..    Zerofilled (padding to 800h-byte boundary)
  800h F*10h File List
  ...  ..    Zerofilled (padding to 800h-byte boundary)
  ...  ..    File Data area
Folder List entries:
  000h 4     Folder ID (Random, maybe folder name checksum?)
  004h 4     First file number in this folder (0=first, increasing)
File List entries:
  000h 4     File Offset/800h
  004h 4     File Size in bytes
  008h 4     Folder ID (same as Parent Folder ID in Folder List)
  00Ch 4     File ID (Random, maybe file name checksum?)

Blasto (MagDemo10: BLASTO\BLASTO.DAT and BLASTO\BLASTO.LFS)
 LFS:
  000h N*18h File/Folder List
 DAT:
  000h ..    File data
File entries (with [10h]=Positive):
  000h 10h   Filename ("FILENAME.EXT", zeropadded)
  010h 4     Offset in bytes, in BLASTO.DAT
  014h 4     Size in bytes
Folder entries (with [10h]=Negative):
  000h 10h   Foldername ("DIRNAME", zeropadded)
  010h 4     Index to first child (at Offset=(-Index)*18h in BLASTO.LFS)
  014h 4     Zero
Folder end marker (with [00h]=00h or 80h):
  000h 1     End marker, at end of root & child directories (00h or 80h)
  001h 17h   Unknown

Twisted Metal 4 (MagDemo30: TM4DATA\*.MR and *.IMG)
These are relative small archives with hundreds of tiny chunks (with registry style Symbol=Value assignments), and a few bigger chunks (with .mod .vab .bit .clt files).
  000h 4     Fixed ID (CCCC0067h)
  004h ..    Root Folder (with Name="Root",00h,FDh,FDh,FDh)
 Folder Chunk format:
  000h 1     Length of Name (including 4-byte padding)
  001h 1     Number of Child Folders
  002h 2     Number of Child Files
  004h ..    Name ("name",00h, CDh-padded to 4-byte boundary; Root=FDh-padded)
  ...  ..    Child File(s)
  ...  ..    Child Folder(s)
 File Chunk format:
  000h 1     Length of filename (including 4-byte padding)
  001h 1     Filetype           (see below)
  002h 2     Array Size         (or FFFFh for non-array filetypes)
  004h 4     Filesize (SIZ)     (including 4-byte padding)
  008h 4     Decompressed Size  (or 0=Uncompressed)
  00Ch ..    Filename/Symbol    ("name.ext",00h, CDh-padded to 4-byte boundary)
  ...  SIZ   Data/Value         (CDh-padded to 4-byte boundary)
Some filenames have trailing non-ascii characters, for example:
  "AXEL.MR\display\resolution\r3\Groups\Combined_Polyset",1Ah,01h,04h,00h
  "CALYPSO.MR\display\resolution\r3\Groups\Combined_Polyset",A8h,00h, CDh,CDh
Filetypes:
  Typ Size  Expl.
  02h var   Text String (terminated by 00h, garbage-or-00h-padded to 4-byte)
  03h 8     Misc (*.IMG\textures\*)                          ;\
  03h 20h   Misc (*.MR\display\resolution\r*\Groups\*)       ; these are all
  03h var   Misc (*.MR\display\resolution\*List)             ; filetype=03h
  03h file  Misc (*.MR\display\*.bit) (same as type=0Ch)     ;/
  04h 4     Numeric 32bit
  05h 8     Numeric 4x16bit point (X,Y,Z,CDCDh)
  06h file  Model (*.mod) (DOTLESS archive with model data)
  0Bh 4     Numeric 32bit repeat,light
  0Ch file  XYWH Bitmap/Palette (*.bit, *.clt) (in GAME.IMG, MENU\menu)
  0Dh 4     Numeric 32bit delay
  0Eh 4     Numeric 32bit color (maybe 24bit RGB plus 00h-padding?)
  0Fh 10h   Whatever 10h-byte "pos"
  10h file  Sony .VAB file (*.vab)
  12h N*1   Array? (with Arraysize=0014h)
  16h N*??  Array Text Strings (with Arraysize=0001h) (in MAIN.MR\worlds)
  1Ah N*10h Array Guns,startpoints (RCCAR.MR\*, NEON.MR\world)
  1Bh 4     Numeric 2x16bit (X,Y) (in MENU.MR)
  1Ch N*4   Array lloc (in MENU.MR\menu\screens) (with Arraysize=04h or 1Fh)
  25h 8     Whatever 8-byte (in GAME.MR\dualShock)
  26h N*8   Array CollideArray (in GAME.MR\dualShock) (with Arraysize=4 or 6)
Compressed Data (when [008h]<>0):
  000h ..    ZLIB compressed data (usually starting with big-endian 789Ch)
  (compression is used for almost all files, except VERY small ones)
CDROM File Compression ZIP/GZIP/ZLIB (Inflate/Deflate)


CDROM File Archive HUG/IDX/BIZ (Power Spike)

Power Spike (MagDemo43: POWER\GAME.IDX and .HUG)
POWER\GAME.HUG
  000h ..    File Data
POWER\GAME.IDX
  000h 4     ID "HUGE"
  004h 4     Checksum (sum of all bytes at [010h and up])
  008h 4     Number of Folders (D) (87h)
  00Ch 4     Number of Files   (F) (F9h)
  010h D*1Ch Folder List (Folder 0..D-1)
  ...  F*18h File List   (File 0..F-1)
Folder List entries:
  000h 0Ch   Folder Name ("DIRNAME", zeropadded)
  00Ch 4     First Child File      (or FFFFFFFFh=None)
  010h 4     Number of Child Files (or 00000000h=None)
  014h 4     First Child Folder    (or FFFFFFFFh=None)
  018h 4     Next Sibling Folder   (or FFFFFFFFh=None)
File List entries:
  000h 0Ch   File Name ("FILENAME.EXT", zeropadded if shorter than 12)
  00Ch 4     File Checksum (sum of all bytes in file added together)
  010h 4     File Offset/800h in GAME.HUG
  014h 4     File Size in bytes
The root entries are Folder 0 (and its siblings). That is, the root can contain only folders (not files).
The IDX/HUG archive contains many BIZ archives (and some TXT files).

Power Spike (MagDemo43: POWER\GAME.IDX\*.BIZ) (BIZ nested in IDX/HUG)
  000h 4     ID "BIG!"
  004h 4     Number of entries (N)
  008h N*1Ch File List
  ...  ..    BIZ compressed File Data
File List entries
  000h 10h   Filename (zeropadded)
  010h 4     File Offset (increasing, unaligned, can be odd)
  014h 4     File Size decompressed
  018h 4     File Size compressed
All files in the BIZ archive are BIZ compressed (unknown if it does also support uncompressed files).
CDROM File Compression LZ5 and LZ5-variants
The BIZ archive seems to be solely containing PSI bitmaps (even files in GAME.IDX\SOUND\MUSIC\*.BIZ do merely contain PSI bitmaps, not audio files).


CDROM File Archive TOC/DAT/LAY

Used in PSX Lightspan Online Connection CD (CD.TOC, CD.DAT, CD.LAY).
  CD.TOC contains File/Folder entries
  CD.DAT contains the actual File bodies
  CD.LAY devkit leftover (list of filenames to be imported from PC to TOC/DAT)
The .TOC file doesn't have any file header, it does just start with the first File/Folder folder entry in root directory. The directory chains with file/folder entries are sorted alphabetically, each chain is terminated by a final entry which does point to parent directory.

File Entries
  00h 4   Offset to next Sibling File/Folder/Final entry
  04h 4   Filesize in bytes
  08h 4   Filedata Offset/800h in CD.DAT
  0Ch ..  Filename (ASCII, terminated by 00h)
  ... ..  Padding to 4-byte boundary (garbage)

Folder Entries (with Filesize=FFFFFFFFh)
  00h 4   Offset to next Sibling File/Folder/Final entry
  04h 4   Filesize (always FFFFFFFFh in Folder entries)
  08h 4   Offset to first File/Folder in Child directory
  0Ch ..  Name of Child directory (ASCII, terminated by 00h)
  ... ..  Padding to 4-byte boundary (garbage)

Final Entries (with Name="",00h and Filesize=FFFFFFFxh)
  00h 4   Offset to next Sibling entry (00000000h=None)
  04h 4   Filesize (FFFFFFFFh in child folders, FFFFFFFEh in root folder)
  08h 4   Offset to first File/Folder in Parent directory (or to self for root)
  0Ch 1   Empty Name ("",00h)
  0Dh 3   Padding to 4-byte boundary (garbage)


CDROM File Archive WAD (Doom)

Doom, PSXDOOM\ABIN\*.WAD and PSXDOOM\MAPDIR*\*.WAD)
The .WAD format is used by Doom (for DOS, Jaguar, PSX, etc), various homebrew Doom hacks, and some other developers have adopted the format and used .WAD in other game engines.
  000h 4     ID "IWAD" (or "PWAD" for homebrew patches, or "PACK" in A.D. Cop)
  004h 4     Number of File List entries (N) (including final ENDOFWAD entry)
  008h 4     Offset to Directory Area (filesize-N*10h)
  00Ch ..    File Data area
  ...  N*10h File List
File List entries:
  000h 4   Offset to file data (increasing by compressed size, 4-byte aligned)
  004h 4   Filesize in bytes   (uncompressed size) (zero in ENDOFWAD file)
  008h 8   Filename (uppercase ASCII, zeropadded if less than 8 chars)

Folders
The directory can contain names like F_START, F_END, P1_START, P1_END with filesize=0 to mark begin/end of something; that stuff can be considered as subdirectories with 1- or 2-character names.
Notes: There are also regular files with underscores which are unrelated to folders (eg. F_SKY01). There are also 0-byte dummy files (eg. MAP17 in first entry MAP17.WAD). And there's a 0-byte dummy file with name ENDOFWAD in last file list entry (at least, it's present versions with compression support).

LZSS Decompression
Compression is indicated by Filename[0].bit7=1. The compressed size is NextFileOffset-FileOffset (that requires increasing offsets in File List, including valid offsets for 0-byte files like F_START, F_END, ENDOFWAD).
  @@collect_more:
   flagbits=[src]+100h, src=src+1    ;8bit flags
  @@decompress_lop:
   flagbits=flagbits SHR 1
   if zero then goto @@collect_more
   if carry=0 then
     [dst]=[src], dst=dst+1, src=src+1
   else
     disp=([src]*10h)+([src+1]/10h)+1, len=([src+1] AND 0Fh)+1, src=src+2
     if len=1 then goto @@decompress_done
     for i=1 to len, [dst]=[dst-disp], dst=dst+1, next i
   endif
   goto @@decompress_lop
  @@decompress_done:
   ret
The game engine may insist on some files to be compressed or uncompressed (so compression may be required even if the uncompressed data would be smaller).

More info: http://doomwiki.org/wiki/WAD


CDROM File Archive WAD (Cardinal Syn/Fear Effect)

.WAD files (Cardinal Syn/Fear Effect)
This format exists in two version:
  Old format: Without leading Header Size entry (Cardinal Syn MagDemo03: SYN\*)
  New format: With leading Header Size entry    (eg. Fear Effect)
Version detection could be done somewhat as so:
  if [04h]*1Ch+8 >= [00h] then OLD version
For loading the Old Header, one must guess the max header size (4000h should work, in fact, most or all Old Headers seem to be max 800h), or load more data on the fly as needed.
  000h (4) Header Size (including folder/type/file directories) (new version)
  ...  4   Number of Folders
  ...  ..  Folder List (root)
  ...  ..  Type Lists  (for each folder)
  ...  ..  File Lists  (for each folder\type)
  ...  ..  File Data   (for each folder\type\file)
 Folder List Entries:
  000h 14h Folder name (ASCII, zeropadded)
  014h 4   Offset to Type List
  018h 4   Number of different Types in this folder
 Type List Entries:
  000h 4   Offset to file entries (of same type, eg. .TIM files)
  004h 4   Number of file entries (of same type, eg. .TIM files)
  008h 4   Sum of all Filesizes with that type
  00Ch 4   Group Type (0000000xh)
 File List entries (Files within Type list):
  000h 14h Name (ASCII, terminated by 00h, plus garbage padding)
  014h 4   Offset to File Data  (seems 4-byte aligned... always?)
  018h 4   File Type (000x00xxh)
  01Ch 4   Filesize in bytes  ;\maybe compressed/uncompressed, or rounded,
  020h 4   Filesize in bytes  ;/always both same
Note: The Type List for one folder can contain several entries with same Group Type, eg. Fear Effect GSHELLE.WAD\CREDIT has 5 type list entries (with 2xGroup0, 2xGroup1, 1xGroup2).

The Type List, Group Type and File Type stuff seems to have no function, apart from faster look up (the types are also implied in the filename extension). Except, Fear Effect .RMD .VB .VH have some unknown stuff encoded in File Type bit16-19.
Group Type is usually 0 (except for .TIM .VB .VH .MSG .SPU .OFF).
The .TIM .VB .VH .SEQ files are using standard Sony file formats. The .PMD file seems to be also Sony standard (except that it contains a 00000000h prefix, then followed by the 00000042h PMD format ID).

Cardinal Syn Types
  .BGD FileType=00000001h
  .ANM FileType=00000003h
  .TIM FileType=00000004h (GroupType=1)
  .SP2 FileType=00000005h
  .PMD FileType=00000007h
  .MOV FileType=00000008h
  .SPR FileType=0000000Ch
  .PVT FileType=0000000Dh
  .DB  FileType=0000000Eh
  .VH  FileType=00000010h (GroupType=1) ;only in OLDER demo version MagDemo03
  .VB  FileType=00000011h (GroupType=1)
  .MSG FileType=00000012h (GroupType=1) (actually, this is .TIM, too)
  .KMD FileType=00000013h
  .OC  FileType=00000018h
  .EMD FileType=00000019h
  .COL FileType=0000001Bh
  .CF  FileType=0000001Ch
  .CFB FileType=0000001Dh
  .CL  FileType=0000001Eh
  .SPU FileType=0000001Fh (GroupType=1) ;added in newer demo version MagDemo09
  .OFF FileType=00000020h (GroupType=1) ;added in newer demo version MagDemo09
  .RCT FileType=00000021h               ;added in newer demo version MagDemo09

Fear Effect Types
  .TIM FileType=00000000h (GroupType=1)
  .RMD FileType=000x0001h
  .DB  FileType=00000002h
  .ANM FileType=00000003h
  .SYM FileType=00000004h
  .VB  FileType=000x0008h (GroupType=1)
  .SEQ FileType=00000010h
  .BIN FileType=00000012h
  .SFX FileType=00000013h
  .VH  FileType=000x0014h (GroupType=2)
  .TM  FileType=00000015h
  .NRM FileType=00000017h
  .WPD FileType=00000018h


CDROM File Archive DIR/DAT (One/Viewpoint)

DIR/DAT (One/Viewpoint)
  Used by One (DATAFILE.BIN and DIRFILE.BIN)
  Used by Viewpoint (VIEW.DAT and VIEW.DIR)
Format of the DIR file:
  000h 60h Extension List (20h x 3-char ASCII, zeropadded if shorter than 3)
  060h ..  Root Directory    (can contain folders and files)
  ...  ..  Child Directories (can contain files) (maybe also sub-folders?)
Extension List contains several uppercase 3-character ASCII extensions, in a hex editor this will appear as a continous string of gibberish (dots=00h):
  In Viewpoint: "...VCSVCFBINTXTVH.VB.STRST1ST2ST3......//..."
  In One:       "...VCTVCKSNDBINCPEINI..................//..."
Directory Entries contain bitstreams with ASCII characters squeezed into 6bit values:
  000h 1   Length of Filename and Extension index
             bit7-3  File Extension Index (0..1Fh = Offset I*3 in DIR file)
             bit2-0  Filename Length-1    (0..7 = 1..8 chars)
  001h ..  Filename in 6bit chars (N*6+7/8 bytes = 1..6 bytes for 1..8 chars)
             bit7-2  1st character, whole 6bit            ;\1st byte
             bit1-0  2nd character, upper 2bit (if any)   ;/
             bit7-4  2nd character, lower 4bit (if any)   ;\2nd byte (if any)
             bit3-0  3rd character, upper 4bit (if any)   ;/
             bit7-6  3rd character, lower 2bit (if any)   ;\3rd byte (if any)
             bit5-0  4th character, whole 6bit (if any)   ;/
             bit7-2  5th character, whole 6bit (if any)   ;\4th byte (if any)
             bit1-0  6th character, upper 2bit (if any)   ;/
             bit7-4  6th character, lower 4bit (if any)   ;\5th byte (if any)
             bit3-0  7th character, upper 4bit (if any)   ;/
             bit7-6  7th character, lower 2bit (if any)   ;\6th byte (if any)
             bit5-0  8th character, whole 6bit (if any)   ;/
             bitN-0  Zeropadding in LSBs of last byte     ;-zeropadding
           The 6bit characters codes are:
             00h..09h="0..9", 0Ah..23h="a..z", 24h="_", 25h..3Fh=Unused
  ...  4   Filesize and End Flag
             bit31   End of Directory Flag   (0=Not last entry, 1=Last entry)
             bit30-0 Filesize 31bit          (or 0=Child Folder)
  ...  4   Offset and fixed bit
             bit31   Unknown (always 1)
             bit30-0 File Offset in DAT file (or Folder offset in DIR file)


CDROM File Archive Darkworks Chunks (Alone in the Dark)

Alone in the Dark The New Nightmare (FAT.BIN\*)
The files in FAT.BIN are using a messy chunk format: There's no clear ID+Size structure. There are 7 different chunk types (DRAM, DSND, MIDB, G3DB, VRAM, WEAP, HAND), each type requires different efforts to compute the chunk size.

VRAM Chunks (Texture/Palette) (in various files)
  000h 4     ID "VRAM"
  004h 4     With Tags (0=No, 1=Yes) (or "DRAM" when empty 4-byte chunk)
  008h (4)   Number of Tagged items (N) (0=None)  ;\only when [4]=1
  00Ch N*10h Tagged Item(s)                       ;/(not so in LEVELS\*\VIEW*)
  ...  ..    Scanline Rows(s)
  ...  4     End code (00000000h) (aka final Scanline Row with width=0)
 Tagged Item(s) (IMG, LINE, GLOW, FLARE, BALLE, BLINK, COURIER7, BMP_xxx):
  000h 8     Tag (ASCII, if less than 8 chars: terminate by 00h, pad by FDh)
  008h 8     Data
 Scanline Row(s) (bitmap scanlines and palette data):
  000h 4     Header (bit0-8=Width, bit10-18=Y, bit20-29=X, bit9,19,30,31=?)
  004h W*2   Data (Width*2 bytes, to be stored at VRAM(X,Y))
Empty VRAM chunks can be either 4 or 10h bytes tall. The 4-byte variant is directly followed by another chunk name (eg. "VRAMDRAM"), the 10h-byte variant contains four words ("VRAM",WithTags=1,NumTags=0,EndCode=0).
Note: Some files contain two VRAM chunks (eg. LEVELS\*\VIEW*).

G3DB Chunks (Models) (in various files)
  000h 4   ID "G3DB"
  004h 4   Unknown (0, 1, or 2)
  008h 4   Size of Data part (SIZ)
  00Ch 4   Number of List entries (eg. 6 or 0Ah or 117Ch) (N)
  010h SIZ Data (supposedly LibGDX models in G3DB format)
  ...  N*4 List

DRAM Chunks (Text and Binary data) (in various files)
  000h 4   ID "DRAM"
  004h 4   Size of Data part (SIZ) (can be odd)
  008h 4   Number of List entries (N)
  00Ch SIZ Data (raw data, and/or tags TEXT, SPC, COURIER7)
  ...  N*4 List

WEAP Chunks (Weapons) (in WEAPON\*\*)
  000h 4   ID "WEAP"
  004h 4   Size-10h?
  008h ..  Data
Followed by VRAM and DSND chunks.

HAND Chunks (Hands) (in LEFTHAND\*\HAND*)
  000h 4   ID "HAND"
  004h 4   Size-0Ch?  (18h)
  008h 8   Zerofilled
  010h 4x4 Unknown (FFh,FF00h,xF0000h,FF3232h,FF6464h,FFDCDCh,FFFFFFh,..)
  020h 4   Unknown (0, 1, 101h, or 201h)
Followed by VRAM and G3DB chunks.

MIDB Chunks (Music) (in MIDI\*\*)
  000h 4      ID "MIDB"
  004h 1      Unknown (0 or 1)
  005h 1      Number of SEQ blocks              (1..4) (S)
  006h 1      Number of Unknown 80h-byte blocks (1..2) (U)
  007h U*80h  Unknown Blocks (mostly FFh-filled)
  ...  S*Var  SEQ Block(s)
  ...  ..     VAB Block
 SEQ Blocks:
  Probably some MIDI sequence data, similar to Sony's .SEQ format.
  000h 4      Size-0Ch (can be odd)
  004h 8      Name (zeropadded if less than 8 chars)
  00Ch 4      ID "DSEQ"    ;\Size
  010h ..     Data         ;/
 VAB Blocks:
  Apparently inspired on Sony's .VAB format (but the ID is spelled other way
  around, Lists have variable size, and entries have different format).
  000h 4      ID "VABp"  (this is: not pBAV, unlike normal .VAB files)
  004h 4      Unknown (0)
  008h 4      Unknown (0)
  00Ch 4      Size of all SPU-ADPCM samples (SIZ)
  010h 2      Number of List 1 entries (N1)
  012h 2      Number of List 2 entries (N2)
  014h 2      Number of Samples        (N3)
  016h 6      Unused? (CCh-filled)
  01Ch N1*10h List 1
  ...  N2*10h List 2
  ...  N3*2   Sample Size List (size of each SPU-ADPCM sample)
  ...  SIZ    SPU-APDCM Sample(s)

DSND Chunks (Sounds) (in various files)
  000h 4   ID "DSND"
  004h 4   Unknown (0 or 2)
  008h ..  VAB Block (same as in MIDB chunks, see there)

Note
DRAM and MIDB chunks can have odd size; there isn't any alignment padding, so all following chunks can start at unaligned locations.


CDROM File Archive Blue Chunks (Blue's Clues)

Blue's Clues: Blue's Big Musical (*.TXD)
  000h 4    Size of AUDD+SEPD+VABB chunks ;\for quick look-up only
  004h 4    Size of all VRAM chunks       ; (can be ignored by chunk crawlers)
  008h 4    Size of STGE+ANIM+FRAM chunks ;/(note: sum is total filesize-0Ch)
  ...  ..   AUDD Chunk    (contains .VH)                  ;\
  ...  ..   SEPD Chunk(s) (contains .SEP)                 ; sound
  ...  ..   VABB Chunk    (contains .VB)                  ;/
  ...  (..) VRAM Chunk(s) (not in IN\FE2.TXD)             ;-textures/palettes
  ...  (..) STGE Chunk    (if any, not in IN\FE*.TXD)     ;-stage data?
  ...  (..) ANIM Chunk    (if any, not in IN\FE*.TXD)     ;\animation
  ...  (..) FRAM Chunk(s) (if any, not in IN\FE*.TXD)     ;/
  ...  (..) Further groups with ANIM+FRAM Chunks (if any) ;-more animation(s)
 AUDD Chunks:
  000h 4    Chunk ID ("AUDD")
  004h 4    Chunk Size (of whole chunk from Chunk ID and up)
  008h 4    Compression Flag (0=Uncompressed)
  00Ch 4    Zero
  010h ..   VH File (Sony Voice Header, starting with ID "pBAV")
 SEPD Chunks:
  000h 4    Chunk ID ("SEPD")
  004h 4    Chunk Size (of whole chunk from Chunk ID and up)
  008h 4    Compression Flag (0=Uncompressed)
  00Ch 2    Zero
  00Eh 2    Number of sequences (in the SEP sequence archive)
  010h 4    Zero
  014h ..   SEP File (Sony Sequence archive, starting with ID "pQES")
  ...  ..   Zeropadding to 4-byte boundary
 VABB Chunks:
  000h 4    Chunk ID ("VABB")
  004h 4    Chunk Size (of whole chunk from Chunk ID and up)
  008h 4    Compression Flag (0=Uncompressed)
  00Ch ..   VB File (Sony Voice Binary, with raw SPU-ADPCM samples)
 VRAM Chunks:
  000h 4    Chunk ID ("VRAM")
  004h 4    Chunk Size (of whole chunk from Chunk ID and up)
  008h 4    Compression Flag (1=Compressed)
  00Ch 2    VRAM.X
  00Eh 2    VRAM.Y
  010h 2    Width in halfwords
  012h 2    Height
  014h 4    Decompressed Size (Width*Height*2)  ;\Texture Bitmaps 8bpp
  018h ..   Compressed Data                     ; (or Palettes, in last VRAM
  ...  ..   Zeropadding to 4-byte boundary      ;/chunk)
 STGE Chunks:
  000h 4    Chunk ID ("STGE")
  004h 4    Chunk Size (of whole chunk from Chunk ID and up)
  008h 4    Compression Flag (0=Uncompressed)
  00Ch ..   Unknown (stage data?)
 ANIM Chunks:
  000h 4    Chunk ID ("ANIM")
  004h 4    Chunk Size (of whole chunk from Chunk ID and up)
  008h 4    Compression Flag (0=Uncompressed)
  00Ch ..   Unknown (animation sequence info?)
 FRAM Chunks:
  000h 4    Chunk ID ("FRAM")
  004h 4    Chunk Size (of whole chunk from Chunk ID and up)
  008h 4    Compression Flag (0=When Chunksize=14h, 1=When Chunksize>14h)
  00Ch 1    Width in bytes
  00Dh 1    Height
  00Eh 6    Unknown, looks like three signed 16bit values (maybe X,Y,Z)?
  014h (4)  Decompressed Size (Width*Height*1)  ;\Animation Frame Bitmap 8bpp
  018h (..) Compressed Data                     ; (only if Chunksize>14h)
  ...  (..) Zeropadding to 4-byte boundary      ;/
VRAM and FRAM chunks with [08h]=1 (and Chunksize>14h) are compressed:
CDROM File Compression Blues


CDROM File Archive HED/CDF (Parasite Eve 2)

Crazy Data Format (CDF) is used by Parasite Eve 2, on Disc 1 and 2:
1: PE_Disk.01 Stage0.hed Stage0.cdf Stage1.cdf Stage2.cdf Stage3.cdf Inter0.str
2: PE_Disk.02 Stage0.hed Stage0.cdf Stage3.cdf Stage4.cdf Stage5.cdf Inter1.str

STAGE0.HED and STAGE0.CDF
This uses separate header/data files. The directory is stored in STAGE0.HED:
  0000h 78h   Streaming List (03h entries, 28h-bytes each, all entries used)
  0078h 1B00h File List (360h entries, 8 bytes each, all entries used)
  1B78b 8     File List End Code (FFFFFFFFh,FFFFFFFFh)
The actual data for the files (and audio stream) is stored in STAGE0.CDF.

STAGE1.CDF .. STAGE5.CDF
  0000h 800h  Root: Folder List (100h entries, 8-byte each, unused=zeropadded)
  0800h ..    1st Folder (File/Streaming List and Data)
  ...   ..    2nd Folder (File/Streaming List and Data)
  ...   ..    etc.
Folder List entries:
  000h 4  Folder ID (usually N*100+1 decimal, increasing, eg. 101,201,301,etc.)
  004h 4  Folder Size/800h (of whole folder, with File/Stream List and Data)
  The Folder List ends with unused/zeropadded entries with ID/Size=00000000h.
Folder format:
  0000h 510h  File List      (A2h entries, 8-bytes each, unused=zeropadded)
  0510h 4     Zero           (padding to decimally-minded offset 1300 aka 514h)
  0514h 2D0h  Streaming List (12h entries, 28h-bytes each, unused=zeropadded)
  07E4h 1Ch   Zero           (padding to end of sector)
  0800h ...   Data (for Files, Audio streams, and sometimes also Movie streams)

File List entries (in STAGE0 and STAGE1-5)
  000h 4  File ID (increasing, eg. 0,1,2,3,4,etc.) (or 99) (or N*100+x)
  004h 4  File Offset/800h in in .CDF (from begin of current Folder)
For STAGE0, file list ends with ID/Offset=FFFFFFFFh at end of HED file. For STAGE1-5, file list ends with unused/zeropadded entries with ID/Offset=00000000h.
The filesize can be computed as "NextOffset-CurrOffset" (at 800h-byte resolution). Whereas, "NextOffset" can be:
  The offset of next File in File List (same as CurrOffset for 0-byte files)
  The offset of next Audio stream in Streaming List
  The offset of next Movie stream in Streaming List (if it's in .CDF, not .STR)
  The size of the current Folder (for STAGE1-5)
  The size of the whole .CDF file (for STAGE0)
For STAGE1-5, audio streams are usually stored at the end of folder (after the files). However, for STAGE0, audio streams are oddly inserted between file21000 and file30100.

File Chunks (for files within File List)
Most CDF files in STAGE0 and STAGE1-5 do contain one or more chunks with 10h-byte chunk headers (this can be considered as an additional filesystem layer, with the chunk data being the actual files).
  000h 1  Chunk Type (see below)
  001h 1  End Flag (01h=More Chunks follow, FFh=Last Chunk)
  002h 2  Unknown (usually 800h, sometimes 500h or 600h)
            (eg. 500h in stage0\file30301\chunkX)
            (eg. 600h in stage1\folder1201\file0\chunkXYZ)
  004h 4  Chunk Size/800h
  008h 4  Unknown (usually zero) (or 80xxxx00h in Chunk Type 0 files?)
  00Ch 4  Zero (0)
  010h .. Data (Chunk Size-10h bytes)
Chunk Types:
  00h=Room package            .pe2pkg
  01h=Image                   .pe2img
  02h=CLUT                    .pe2clut
  04h=CAP2 Text               .pe2cap2
  05h=Room backgrounds        .bs
  06h=SPK/MPK music program   .spk  ;stereo/mono, sound/music, single/multiple?
  07h=ASCII text              .txt     (eg. stage0\20101..20132)
 ;Reportedy also (but wrong):
 ;60h=Sounds                  .pe2snd  (but nope, that's wrong, see below)
 ;60h is a MDEC movie from Streaming List (unrelated to File List chunks),
 ;60h is 20h-byte .STR header each 800h-bytes (occurs in "stage1\folder501")
There are some chunkless files:
  stage0\40105...40198 are raw hMPK files without chunks
  stage0\11000, 20213, 20214, 20300, .., 660800 and 900000 are empty 0-byte

Streaming List Movie entries (stream type 1)
  000h 2    Stream Type (0001h=Movie)
  002h 2    Unknown (8000h or 0000h)
  004h 4    Offset/800h in current Folder of .CDF file ;<-- used when [024h]=0
  008h 4    Offset/800h in INTERx.STR file             ;<-- used when [024h]>0
  00Ch 2    Unknown (0000h)
  00Eh 2    Stream ID (increasing, usually starting at 64h aka 100 decimal)
  010h 2    Stream sub.ID (usually 0, increases +1 upon multiple same IDs)
  012h 2    Picture Width  (0140h = 320 decimal)
  014h 2    Picture Height (00F0h = 224 decimal)
  016h 2    Unknown (0000h)
  018h 2    Unknown (0000h or 0018h)   maybe 24bpp or 24fps
  01Ah 2    Unknown (73Ah or 359h or 3DCh)  (Size? but it's slighty too large?)
  01Ch 6    Unknown (zero)
  022h 2    Unknown (0 or 1) (often 1 when [024h]>0, but not always)
  024h 2    Movie number in INTERx.STR, 1 and up? (or 0=Movie is in STAGEx.CDF)
  026h 2    Unknown (0 or 1)
The size of movie streams in .CDF can be computed in similar fashion as for File List entries (see there for details).
The size of movie streams in .STR cannot be computed easily (the next stream isn't neccassarily stored at the next higher offset; even if it's within same folder). As workaround, one could create a huge list with all streams from all Folders in all STAGEx.CDFs (or scan the MDEC .STR headers in .STR file; and check when the increasing frame number wraps to next stream).
The dual offsets are oddly computed as: [004h]=[008h]+EndOfLastFileInFolder (that gives the correct value in the used entry, and a nonsensical value in the other entry).

Streaming List Audio entries (stream type 2)
  000h 2    Stream Type (0002h=Audio)
  002h 2    Unknown (806Ah or increasing 0133h,0134h,0135h)
  004h 4    Offset/800h in STAGEx.CDF file (increasing offsets)
  008h 4    Unknown (0 or 13000h or E000h)
  00Ch 2    Stage Number (0..5 = STAGE0-5)
  00Eh 2    Stream ID (1, or increasing 3Ah,3Bh,3Ch)
  010h 4    Stream sub.ID (usually 0Bh, increases +0Ah upon multiple same IDs)
  014h 2    Unknown (0 or 2B0h or 3ADh or 398h) (Size/800h minus something?)
  016h 2    Unknown (usually 20h, sometimes 0Fh)
  018h 4    Unknown (2 or 1)              ... maybe num channels ?
  01Ch 2+2  Unknown (0,0 or 800h,800h)
  020h 8    Unknown (0)
The size of audio streams can be computed in similar fashion as for File List entries (see there for details).

Audio Stream Data (stored alongsides with file data in STAGEx.CDF file)
This contains a 800h-byte header a list of 32bit indices:
  000h 800h Whatever increasing 32bit index/timing values? FFFFFFFFh=special?
  ;That header exists in stage0\ and stage3\folder101\
  ;That header doesn't exist in all files (eg. not in stage1\folder301\)
then followed by several chunk-like STM blocks with 10h-byte headers:
  000h 4  Chunk Index (increases each second chunk, from 0 and up)
  004h 4  Number of Chunk Indices
  008h 4  Fixed (02h,"STM")                                  ;2-channel Stream?
  00Ch 1  Chunk Subindex (toggles 00h or 01h per each chunk) ;ch left/right?
  00Dh 1  Chunk Size/800h
  00Eh 4  Unknown (can be 00h, 01h, 11h, 20h, 21h)
  00Fh 4  Unknown (can be A0h or C0h)
  010h .. Data (Chunk Size-10h bytes) (looks like SPU-ADPCM audio)
After the last STM chunk, there is more unknown stuff:
  000h 0   Number of ADPCM blocks?            (eg. 28h    or 49h)
  004h 4   Size of extra data block in bytes  (eg. 13900h or 24200h)
  008h 38h Zerofilled
  040h 8   Zerofilled (maybe 1st sample of 1st SPU-ADPCM block)
  048h ..  Looks like more SPU-ADPCM block(s), terminated by ADPCM end flag(s)
  ...  ..  Zerofilled (padding to end of last 800h-byte sector)

Movie Stream Data (stored in .CDF, or in separate INTERx.STR file)
The movies are usually stored in INTERx.STR (except, some have them stored in STAGEx.CDF, eg. stage1\folder501, stage1\folder801, stage2\folder2101, stage2\folder3001).
The data consists of standard .STR files (with 20h-byte headers on each 800h-byte sector), with the MDEC data being in huffman .BS format (with .BS header... per frame?).
And, supposedly interleaved with XA-ADPCM audio sectors...?

PE_DISK.01 and PE_DISK.02
The presence of these files is probably used to detect which disc is inserted. The file content is unknown (looks like 800h-byte random values).

Note
Reportedly "Files inside archive may be compressed with custom LZSS compression" (unknown if/when/where/really/which files).


CDROM File Archive IND/WAD (MTV Music Generator)

MTV Music Generator (IND/WAD) (MagDemo30: JESTER\WADS\ECTS.IND and .WAD)
ECTS.IND contains FOLDER info:
  0000h 20h   Name/ID ("Music 2", zeropadded)
  0020h 4     Unknown (110000h)
  0024h 4     Filesize-1000h (size excluding last 1000h-byte padding)
  0028h 4     Unknown (17E0h)
  002Ch 4     Unknown (5)
  0030h N*10h Folder List, starting with Root in first 10h-byte
  2CF0h 4     Small Padding (34h-filled)
  2CF4h 1000h Final Padding (34h-filled)
 Folder List entries that refer to Child Folders in ECTS.IND:
  000h 8     Folder Name ("EXTRA*~*", zeropadded if less than 8) ("" for root)
  008h 2     Self-relative Index to first Child folder (positive)
  00Ah 2     Number of Child Folders (0..7FFFh)
  00Ch 4     Always 0007FFFFh (19bit Offset=7FFFFh, plus 13bit Size=0000h)
 Folder List entries that refer to File Folders in ECTS.WAD:
  000h 8     Folder Name ("EXTRA*~*", zeropadded if less than 8)
  008h 2     Self-relative Index to Parent folder (negative)
  00Ah 2     Number of Child Folders (always 8000h=None)
  00Ch 4     Offset and Size in ECTS.WAD
 The 32bit "Offset and Size" entry consists of:
  0-18   19bit Offset/800h in ECTS.WAD
  19-31  13bit Size/800h-1 in ECTS.WAD
ECTS.WAD contains FILE info and actual FILE data:
 There are several File Folders (at the locations specified in ECTS.IND).
 The separate File Folders look as so:
  000h 4     Number of files (N)
  004h N*10h File List
  ...  ..    34h-Padding to 800h-byte boundary
  ...  ..    File Data area
 File List entries:
  000h 8     File Name ("NAMELIST", "ACIDWO~1", etc.) (00h-padded if shorter)
  008h 4     Offset/800h (always from begin of WAD, not from begin of Folder)
  00Ch 4     Filesize in bytes
 The first file in each folder is called "NAMELIST" and contains this:
  000h 20h   Long Name for Parent Folder (eg. "Backgrounds", zeropadded)
  020h 20h   Long Name for this Folder   (eg. "Extra 1", zeropadded)
  040h N*20h Long Names for all files in folder (except for NAMELIST itself)
 For example, Long name for "ACIDWO~1" would be "Acidworld". Short names are
 uppercase, max 8 chars, without spaces (with "~N" suffix if the long name
 contains spaces or more than 8 chars). Many folder names are truncated to
 one char (eg. "D" for Long name "DTex"), in such cases short names CAN be
 lowercase (eg. "z" for  Long name "zTrans").
 The Long Names are scattered around in the NAMELIST files in ECTS.WAD file,
 so they aren't suitable for lookup (unless when loading all NAMELIST's).


CDROM File Archive GAME.RSC (Colonly Wars Red Sun)

Colony Wars Red Sun (MagDemo31: CWREDSUN\GAME.RSC, 13Mbyte)
  0000h 4     Offset to Bonkers List (2794h)
  0004h F*8   Folder List                      (80h bytes, 10h entries)
  0084h N*14h File List(s) for each folder     (2710h bytes, 1F4h entries)
  2794h 4     Number of Bonkers     (FE3h)
  2798h B*8   Bonkers List                     (7F18h bytes, FE3h entries)
  A6B0h 8     Unknown (zerofilled)
  A6B8h ..    File Data area
Folder List entries:
  000h 4     Offset to File List for this folder   ;\both zero when empty
  004h 4     Number of Files in this folder        ;/
File List entries:
  000h 10h   Filename ("FILENAME_EXT", zeropadded)
  010h 3     Index (in Bonkers list) (000h..Fxxh)
  013h 1     Folder Number where the file is stored (00h..0Fh)
Bonkers List entries:
  000h 4     File Offset (to Data, inreasing, 4-byte aligned, A6B8h and up)
  004h 4     Folder Number where the file is stored (00h..0Fh)
Offsets/Indices in Folder/File list are unsorted (not increasing).
Offsets in Bonkers List are increasing (so filesizes can be computed as size=next-curr, except, the LAST file must be computed as size=total-curr).
There is no "number of folders entry" nor "folder list end marker", as workaround, while crawling the folder list, search the smallest file list offset, and treat that as folder list end offset.
In the demo version, all File List entries for Folder 5 are pointing to files with filesize=0, however, the Bonkers List has a lot more "hidden" entries that are marked to belong to Folder 5 with nonzero filesize.
Note: Older Colony Wars titles did also have a GAME.RSC file (but in different format, without folder structure).


CDROM File Archive BIGFILE.DAT (Soul Reaver)

Legacy of Kain: Soul Reaver - BIGFILE.DAT
Legacy of Kain: Soul Reaver (MagDemo26: KAIN2\BIGFILE.DAT)
  000h 2     Number of Folders (175h in retail, 0Ah in demo)
  002h 2     Zero
  004h N*8   Folder List (8-byte per Folder)
  ...  ..    Zeropadding (to 800h-byte boundary)
  ...  ..    1st Folder (with File List, and File Data for that folder)
  ...  ..    2nd Folder (with File List, and File Data for that folder)
  ...  ..    3rd Folder (with File List, and File Data for that folder)
  ...  ..    etc.
Folder List entries:
  000h 2     Unknown (somehow randomly increases from -8000h to +7E8Fh)
  002h 2     Number of Files in this Folder (eg. 97h)
  004h 4     Offset to Folder (usually 800h-aligned)
Folder format:
  000h 2     Number of Files (same value as FolderistEntry[002h]) ;\encrypted
  002h 2     Zero                                                 ; by 16bit
  004h N*10h File List (10h-byte per Folder)                      ; XOR value
  ...  ..    Zeropadding (to 800h-byte boundary)                  ;/
  ...  ..    File Data for this folder                            ;-unencrypted
File List entries:
  000h 4     Unknown (random? filename hash? encrypted name?)
  004h 4     File Size in bytes
  008h 4     File Offset (usually 800h-aligned)
  00Ch 4     Unknown (random? filename hash? encrypted name?)
Encryption:
The file header, the first some Folder headers (those in first quarter or so), and (all?) File Data is unencrypted (aka XORed with 0000h).
The Folder headers at higher offsets are encrypted with a 16bit XOR value. That XOR value is derived from Subchannel Q via LibCrypt:
CDROM Protection - LibCrypt
When not having the Subchannel data (or when not knowing which Folders are encrypted or unencrypted), one can simply obtain the encryption key from one of these entries (which will be key=0000h when unencrypted):
  key = FileListEntry[000h] XOR FolderListEntry[002h]  ;encrypted num entries
  key = FileListEntry[002h]                            ;encrypted Zero
  key = FileListEntry[zeropadding, if any]             ;encrypted Zeropadding
LibCrypt seems to be used only in PAL games, unknown if the Soul Reaver NTSC version does also have some kind of encryption.


CDROM File Archive FF8 IMG (Final Fantasy VIII)

FF8 is quite a mess without clear directory structure. Apart from SYSTEM.CNF and boot EXE, there is only one huge IMG file. There are at least two central directories: The Root directory (usually at the start of the IMG file), and the Fields directory (hidden in a compressed file that can be found in the Root directory). Moreover, there are files that exist in neither of the directories (most notably the Movies at the end of the IMG file).

IMG File
The IMG file doesn't have a unique file header, it can be best detected by checking the filename: FF8DISCn.IMG with n=1-4 for Disc 1-4 (or only FF8DISC1.IMG or FF8.EXE+FF8TRY.IMG for demo versions).
The directories contain ISO sector numbers (originated from begin of the ISO area at sector 00:02:00). Accordingly, it's best to extract data from the whole disc image (in CUE/BIN format or the like). When having only the raw IMG file, one most know/guess the starting sector number (eg. assume that the first Root File is located on the sector after the Root Directory, and convert sector numbers ISO-to-IMG accordingly).
Another oddity is that many files contain RAM addresses (80000000h-801FFFFFh), unknown how far that's relevant, and if there are cases where one would need to convert RAM addresses to IMG offsets.

Root Directory
The Root Directory is found at:
  Offset 0000h in FF8DISCn.IMG in NTSC retail versions
  Offset 2800h in FF8DISCn.IMG in PAL retail versions
  Offset 0000h in FF8DISC1.IMG in french demo version
  Offset ?????h in FF8.EXE in MagDemo23 (...maybe offset 3357Ch ?)
  Offset 33510h in FF8.EXE in japanese demo version ?
  Offset 33584h in FF8.EXE in other demo versions   ?
For detection:
  if FF8DISCn.IMG starts with 000003xxh --> assume Root at IMG offset 0
  if FF8DISCn.IMG starts with xxxxxxxxh --> assume Root at IMG offset 2800h
  if FF8TRY.IMG starts with "SmCdReadCore" --> assume Root somewhere in EXE
File List:
  000h N*8  File List entries
  ...  ..   Zeropadding to end of 800h-byte sector
File List entries:
  000h 4    ISO Sector Number (origin at 00:02:00) (unsorted, not increasing)
  004h 4    Filesize in bytes
The file list does usually end with zeropadding (unknown if that applies to all versions; namely the Demo version might end with gibberish instead of having 800h-byte sector padding).

Fields Directory
The Fields Directory is located in Root file 0002h. First of, decompress that file, then search the following byte sequences to find the start/end of the directory:
  retail.start  040005241800bf8f1400b18f1000b08f2000bd270800e00300000000
  retail.end    0000010002000300
  demo.start    76DF326F34A8D0B863C8C0EC4BE817F8
  demo.end      0000000000000000000000000000000000100010
The bytes between those start/end pattern contain the Directory, with entries in same format as Root directory:
  000h 4    ISO Sector Number (origin at 00:02:00)
  004h 4    Filesize in bytes
Notes: Root file 0002h is about 190Kbyte (decompressed), of which, the Fields Directory takes up about 8Kbytes, the remaining data contains other stuff.
The sector numbers in the Fields Directory refer to other locations in the IMG file (not to data in Root File 0002h).

Movie List
There is no known central directory for the movies (unknown if such a thing exists, or if the movie sector numbers are scattered around, stored in separate files). However, a movie list can be generated by crawling the movie headers, starting at end of IMG file:
  sector = NumSectors(IMG file)
 @@lop:
  seek(sector-1), read(buf,08h bytes)
  if first4byte[buf+0]=("SMJ",01h), or ("SMN",01h) then
    num_sectors=(byte[buf+5]+1)*(halfword[buf+6]+1)
    sector=sector-num_sectors
    AddToMovieFileList(sector, num_sectors)
    goto @@lop
  endif
That should cover all movies, which are all at the end of the IMG file (except, there's one more movie-like file elsewhere in the middle of IMG file, that file has only SMN/SMR audio sectors, without any SMJ video sectors).

PADBUG archives
PADBUG archives are used in Root files 001Eh..007Fh, most of them contain two AKAO files (except file 004Bh contains one AKAO and one TXT file).
  000h 4     Number of Files (N) (usually 2)
  004h N*8   File List
  ...  ..    File Data area
File List entries:
  000h 4     Offset in bytes (increasing, 4-byte aligned, see Quirk)
  004h 4     File Size in bytes (can be odd)
Quirk: All files are zeropadded with 1-4 bytes to 4-byte boundary (ie. files that do end on a 4-byte boundary will be nethertheless padded with 4 zeroes).
Note: The PADBUG archives resemble LNK archives in O.D.T. (though those LNK archives have a different unique 4-byte padding quirk).

Compression
CDROM File Compression LZ5 and LZ5-variants
FF8 does reportedly also use GZIP (unknown in which files).

Known/unknown sectors for US version FF8DISC1.IMG
  root sectors:       27CBh ;\
  field sectors:      D466h ; total known sectors: 36D13h
  movie sectors:     270E2h ;/
  unknown sectors:   14F49h
  total IMG sectors: 4BC5Ch

See also
https://github.com/myst6re/deling/blob/master/FF8DiscArchive.cpp
https://ff7-mods.github.io/ff7-flat-wiki/FF8/PlaystationMedia.html


CDROM File Archive FF9 IMG (Final Fantasy IX)

Final Fantasy IX (FF9.IMG, 320Mbyte) Overall format
  000h Root Directory
  800h 1st Child Folder
  ...  2nd Child Folder
  ...  3rd Child Folder
  ...  ...
  8000h ?     Last folder, with Type3, contains 1FFh x increasing 16bit numbers
  ...  Data for files in 1st Child Folder
  ...  Data for files in 2nd Child Folder
  ...  Data for files in 3rd Child Folder
  ...

IMG Root Directory
  000h 4     ID "FF9 "
  004h 4     Unknown (06h on Disc 1 of 4) (maybe version, or disc id?)
  008h 4     Number of Folder List entries (0Fh)
  00Ch 4     Unknown (01h on Disc 1 of 4) (maybe version, or disc id?)
                 (or Offset/800h to first file list?)
  010h N*10h Folder List
  ...  ..    Padding to 800h-byte boundary ("FF9 FF9 FF9 FF9 ")
Folder List entries:
  000h 4     FolderType (2=Normal, 3=Special, 4=Last entry)
  004h 4     Number of entries in File List (0..1FFh ?)
  008h 4     Offset/800h to Child Folder with File List
  00Ch 4     Offset/800h to File Data (same as 1st offs in File List) (0=Last)

IMG Child Folders (FolderType=2)
  000h N*8   File List entries (N=Number of files, from Root directory)
  N*8  8     File List END entry (ID=FFFFh, Attr=FFFFh, Offs=EndOfLastFile)
  ...  ..    Zeropadding to 800h-byte boundary
File List entries:
  000h 2     File ID (increasing, often decimal 0,10,100, or FFFFh=Last)
  002h 2     Attr (unknown purpose, eg. 0,2,3,4,8,21h,28h,2Fh,44h,114h,FFFFh)
  004h 4     Offset/800h to File Data (increasing, implies end of prev entry)

IMG Child Folders (FolderType=3)
  000h N*2   File Offsets/800h, from File Data Offset in Root (or FFFFh=None)
  N*2  2     End Offset for last file
The filesize can be computed as (NextOffs-CurrOffs)*800h, however, one must skip unused entries (FFFFh) to find NextOffs.

Nested Child Archives
Most of the files in FF9.IMG are DB archives, there are also some DOT1 archives.
CDROM File Archive FF9 DB (Final Fantasy IX)
There are various combinations of IMG, DB, DOT1 archives nested up to 4 levels deep:
  IMG\DOT1         (eg. dir01\file003C)
  IMG\DB           (eg. dir01\file2712)
  IMG\DB\DOT1      (eg. dir01\file2712\00-0411)
  IMG\DB\DOT1\DOT1 (eg. dir01\file2712\00-0443\*)
  IMG\DB\DB        (eg. dir03\file2328\1B-000*)

Folders in Root directory
  dir00 - Status/Menu/Battle/... -Text and random stuff.
  dir01 - Misc Images (Logos, Fonts, World 'mini' Map images, etc).
  dir02 - Dialog Text
  dir03 - Map models (Mini-zidane, airships, save point moogle, tent...)
  dir04 - Field models
  dir05 - Monster Data (Part I, stats, names, etc).
  dir06 - Location Data (Dungeon, Cities, etc).
  dir07 - Monster Data (Part II, 3d models)
  dir08 - Weapon Data (including models)
  dir09 - Samplebanks and Sequencer Data (ie music).
  dir0A - party members Data (including models)
  dir0B - Sound effects
  dir0C - World Map Data
  dir0D - Special effects (magic, summons...)

See also
https://ninjatoes.blogspot.com/2020/07/
https://wiki.ffrtt.ru/index.php?title=Main_Page


CDROM File Archive GTFS (Gran Turismo 2)

Gran Turismo 2 (MagDemo27: GT2\GT2.VOL, GT2.VOL\arcade\arc_carlogo) - GTFS
  000h 4     ID "GTFS"                                             ;\
  004h 4     Zero                                                  ;
  008h 2     Number of 4-byte File Offset List entries (N)         ; File(0)
  00Ah 2     Number of 20h-byte File/Folder Name List entries (F)  ;
  00Ch 4     Zero                                                  ;
  010h N*4   File Offset List (see below)                          ;/
  ...  ..    Zeropadding to 800h-byte boundary
  ...  F*20h File/Folder Name List (see below)                     ;-File(1)
  ...  ..    Zeropadding to 800h-byte boundary
  ...  ..    File Data                                             ;-File(2)
  ...  ..    Zeropadding to 800h-byte boundary
  ...        File Data                                             ;-File(3)
  ...  ..    ...
  ...        File Data                                             ;-File(N-2)
  ...  ..    Zeropadding to 800h-byte boundary
  EOF  0     End of File                                           ;-File(N-1)
That is, for N files, numbered File(0)..File(N-1):
  File(0) and File(1) = Directory information
  File(2)..File(N-2)  = Regular data files
  File(N-1)           = Offset List entry points to the end of .VOL file
File Offset List entries, in File(0):
Contains information for all files, including File(0) and File(1), and including an entry for File(N-1), which contains the end offset for the last actual file, ie. for File(N-2).
  Bit0-10  = Number of padding bytes in last sector of this file (0..7FFh)
  Bit11-31 = Offset/800h to first sector of this file (increasing)
  To compute the filesize: Size=(Entry[N+1] AND FFFFF800h)-Entry[N]
File/Folder Name List entries, in File(1):
Contains information for all files, excpet File(0), File(1), File(N-1), plus extra entries for Folders, plus ".." entries for links to Parent folders.
  000h 4     Unknown (379xxxxxh) (maybe timestamp?)
  004h 2     When Flags.bit0=0: Index of File in File Offset List (2 and up)
             When Flags.bit0=1: Index of first child in Name List, or...
             When Flags.bit0=1: Index of 1st? parent in Name List (Name="..")
  006h 1     Flags (bit0:0=File, 1=Directory; bit7:1=Last Child entry)
  007h 19h   Name (ASCII, zeropadded)
The game does use several archive formats: GTFS (including nested GTFS inside of main GTFS) and WAD.WAD and DOT1.
The game does use some GT-ZIP compressed files, and many GZIP compressed files (albeit with corrupted/zeropadded GZIP footers; due to DOT1 filesize 4-byte padding and (unneccessarily) GTFS 800h-byte padding).
CDROM File Compression GT-ZIP (Gran Turismo 1 and 2)
CDROM File Compression ZIP/GZIP/ZLIB (Inflate/Deflate)
To extract the decompressed size from the corrupted GZIP footers, one could compute the compressed "size" (excluding the GZIP header, footer, and padding), and search for a footer entry that is bigger than "size".
  size=gz_filesize
  size=size-GzipHeader(including ExtraHeader, Filename, Comment, HeaderCrc)
  size=size-GzipFooter(8)   ;initially assuming 8-byte footer (without padding)
  i=gz_filesize-4
 @@search_footer:
  if buf[i]<size then i=i-1, size=size-1 goto @@search_footer
  decompressed_size = buf[i]
Note: Above doesn't recurse the worst-case compression ratio, where compressed files could be slightly bigger than decompressed files.


CDROM File Archive Nightmare Project: Yakata

Nightmare Project: Yakata
ISO Files:
  CD.IMG      550Mbyte  Contains file 004h..FFFh
  CDRTBL.DAT  32Kbyte   Alias for file 000h (File List for file 000h..FFFh)
  FDTBL.DAT   2Kbyte    Alias for file 001h (Folder List and Disc ID)
  SLPS_010.4* 500Kbyte  Alias for file 003h (Boot EXE)
  SYSTEM.CNF  72bytes   Alias for file 002h (Boot Info)
  XXXXXXXX.   27Mbyte   Padding (zerofilled)
FDTBL.DAT (Folder List):
 FDLTBL.DAT seems to be used to divide the file list in CDRTBL.DAT into
 separate folders. The Folder List entries are containing the first file number
 for each folder. Empty folders have same file number as next entry.
 The last folder contains the specified file number plus all remaining files.
  000h 56h*2 Folder List (16bit File Numbers, increasing from 0004h to 0xxxh)
  0ACh 748h  Zerofilled
  7F4h 0Ah   Game ID (ASCII "SLPS1045",00h,00h; always so on Disc 1..3)
  7FEh 2     Disc ID (1..3 = Disc 1..3)
CDRTBL.DAT (File List):
  000h 8000h File List (1000h x 8-byte entries)
 File List entries:
  000h 4   Sector (MM:SS:FF:00 in BCD, increasing)  ;\all zero for
  004h 2   Size1  (NumFramesCh1 or NumSectors)      ; unused entries
  006h 2   Size0  (NumFramesCh0 or Zero)            ;/
 The meaning of the Size entries depends on the file type:
  Normal binaries:  [004h]=NumSectors     [006h]=0             (1 channel)
  XA-ADPCM streams: [004h]=NumSectors-50h [006h]=0             (16 channels)
  MDEC streams:     [004h]=NumFrames      [006h]=0             (audio+video)
  Special streams:  [004h]=NumFramesCh1   [006h]=NumFramesCh0  (2 channels)
 To determine the actual filesize, one must compute the difference between
  sectors for current and next used file entry (or end of CD.IMG for last file;
  or alternately assume last file to be a Normal Binary with Size=NumSectors).
 Normal Binaries:
  Contains single files (file=0/channel=0). Filetypes include TIM, VB, VH,
  other/custom file formats, and DOT1 archives.
  The DOT1 archives have 4-byte aligned offsets, but, unconventionally, with
  some offsets set to ZERO (usually the last entry, and sometimes also other
  entries):
  SEQ files (Disc1:Dir08h\File173h)       ;with ZERO entries    (=uncommon)
  SEQ files (Disc1:Dir09h\File176h..3D7h) ;with ZERO entries    (=uncommon)
  SEQ files (Disc1:Dir0Ah\File3DAh..3E6h) ;with ZERO entries    (=uncommon)
  TIM files (Disc1:Dir4Fh\File962h..983h) ;with ZERO entries    (=uncommon)
  TIM files (Disc1:Dir0Ch\File414h..426h) ;without ZERO entries (=normal DOT1)
 XA-ADPCM Streams (Disc1:Dir0Bh\File3E7h..413h):
  These contain 16 audio streams (file=1/channel=00h-0Fh). The Size entry is
  set to total size in sectors for all streams, minus 50h (ie. there appears
  to be 50h sectors appended as padding before next file).
 MDEC Streams (Disc1:Dir53h\FileBD1h..BEBh):
  These are standard STR files with MDEC video (file=0/channel=1) and
  XA-ADPCM (file=1/channel=1). There are 10 sectors per frame (8-9 video
  sectors plus 1-2 audio sectors). The total filesize is NumFrames*10+Align(8)
  sectors; the Align(8) might be there to include one final audio sector.
 Special Streams (Disc1:Dir07h\File0E9h-16Eh and Dir50h\File985h..B58h):
  These are custom STR files (non-MDEC format), perhaps containing Polygon
  streams or whatever.
  There are two channels (file=1/channel=00h-01h), each channel contains
  data that consists of 5 sectors per frame (1xHeader plus 4xData).
  The sectors have STR ID=0160h, and STR Type as follows:
    0000h=Whatever special, channel 0 header (sector 0)
    0400h=Whatever special, channel 1 header (sector 1)
    0001h=Whatever special, channel 0 data   (sector 2,4,6,8)
    0401h=Whatever special, channel 1 data   (sector 3,5,7,9)
  The File List size entries contain Number of Frames for each channel (either
  of these entries may be zero, or bigger/smaller/same than the other entry).
  The smaller channel is padded to same size as bigger channel (ie. total
  filesize is "max(NumFramesCh0,NumFramesCh1)*10 sectors"; though that formula
  doesn't always hold true, for example, Disc1:Dir50h\FileA2Dh and FileB1Bh
  are bigger or smaller than expected).


CDROM File Archive FAdj0500 (Klonoa)

Klonoa (MagDemo08: KLONOA\FILE.IDX+FILE.BIN)
 FILE.IDX
  000h 8     ID "FAdj0500"
  008h 38h   RAM addresses       (80xxxxxxh, 0Ch words)
  038h 4     Zero
  03Ch 4     RAM address         (80xxxxxxh)
  040h N*10h File List (including Folder start/end markers)
 FILE.BIN
  000h ..    File Data area (split into filesizes from FILE.IDX)
File List entries:
 Type 0 (Folder End):
  000h 4     Type (0=Folder End)
  000h 4     Zero
  008h 4     RAM address         (always 801EAF8Ch)
  00Ch 4     Zero
 Type 1.a (Folder Start):
  000h 4     Type (1=Folder Start)
  000h 4     Folder Offset/800h  (offset of FIRST file in this Folder)
  008h 4     RAM address         (always 801EAF8Ch)
  00Ch 4     Folder Size/800h    (size of ALL files in this Folder)
 Type 1.b (Force Offset, can occur between Files within a Folder):
  000h 4     Type (1=Same as Folder Start)
  000h 4     Folder Offset/800h  (offset of NEXT file in this Folder)
  008h 4     RAM address         (always 801EAF8Ch)
  00Ch 4     Folder Size/800h    (zero for Force Offset)
 Type 2 (File entries, within Folder Start/End):
  000h 4     Type (2=File)
  004h 4     Filesize in bytes   (4-byte aligned?)
  008h 4     RAM address 1       (80xxxxxxh, or zero)
  00Ch 4     RAM address 2       (80xxxxxxh)
File Offsets are usually 4-byte aligned (at offset+filesize from previous entry). Except, the first file after Folder Start (and Force Offset) is 800h-byte aligned.
The archive contains DOT1 archives, OA05 archives, Ulz compression, and TIM, TMD, VAB, SEQ, VB files.


CDROM File Archives in Hidden Sectors

Hidden Sector Overview
Xenogears, Chrono Cross, and Threads of Fate contain only two files in the ISO filesystem (SYSTEM.CNF and the boot executable). The CDROMs contain standard ISO data in Sector 10h-16h, followed by Hidden stuff in Sector 17h and up:
  Sector 10h (00:02:16)  Volume Descriptor (CD001)      ;\
  Sector 11h (00:02:17)  Volume Terminator (CD001)      ;
  Sector 12h (00:02:18)  Path Table 1                   ;
  Sector 13h (00:02:19)  Path Table 2                   ; standard ISO
  Sector 14h (00:02:20)  Path Table 3                   ;
  Sector 15h (00:02:21)  Path Table 4                   ;
  Sector 16h (00:02:22)  Root Directory                 ;/
  Sector 17h (00:02:23)  Hidden ID                      ;\
  Sector 18h (00:02:24)  Hidden Directory               ; hidden directory
  Sector ..  (00:02:xx)  Hidden Unknown                 ;/
  Sector ..  (00:02:xx)  Hidden Files... (referenced via Hidden Directory)
Note: Like normal files, all hidden entries have their last sector flagged as SM=89h (that applies to all three Hidden ID, Directory, Unknown entries, and to all Hidden Files). For details, see:
CDROM XA Subheader, File, Channel, Interleave

Xenogears (2 discs, 1998)
 Sector 17h (Hidden.ID)
  000h 0Eh  ID ("DS01_XENOGEARS"=Disc 1, or "DS02_XENOGEARS"=Disc 2)
  00Eh 7F2h Zerofilled
 Sector 18h..27h
  000h N*7  File List entries
 Sector 28h (Hidden.Unknown)
  Seems to contain a list of 16bit indices 0000h..1037h,FFFFh in File List
  (that, as raw list indices, regardless of the directory structure)
  000h      Unknown 0016 0018 FFFF FFFF 01A8 FFFF FFFF FFFF  ;\
  010h      Unknown FFFF FFFF FFFF FFFF 0A35 0A3A 0D35 0AD3  ; as so on Disc 2
  020h      Unknown 0A22 0A2E 0A2F FFFF FFFF FFFF FFFF FFFF  ; (values<>FFFFh
  030h      Unknown 0014 0001 0013 FFFF 0075 FFFF FFFF FFFF  ; on Disc 1
  040h      Unknown 0C10 0C14 0C15 0C19 0F52 FFFF FFFF FFFF  ; are 5 less, eg.
  050h      Unknown 0F4C 0B6E 0C4D 1037 0C09 0BAD FFFF FFFF  ; 0011,0013,FFFF..)
  060h      Unknown 002E 0034 FFFF FFFF FFFF FFFF FFFF FFFF  ;
  070h      Unknown FFFF FFFF FFFF FFFF                      ;/
  078h 2    Disc Number      (0001h=Disc 1, 0002h=Disc 2)
  07Ah 786h Zerofilled
 Sector 29h 1st file
File List entries:
  000h 3    24bit Offset (increasing sector number, or 0=special)
  003h 4    32bit Size   (filesize in bytes, or negative or 0=special)
The Offset/Size can have following meanings:
  offset=curr,    size=+N    file at sector=curr, size N bytes
  offset=curr,    size=-N    begin of sub-directory, with N files
  offset=curr,    size=0     empty file, size 0 bytes
  offset=0,       size=0     unused file entry
  offset=FFFFFFh, size=0     end of root-directory
Notes: The Hidden.Directory size seems to be hardcoded to 10h sectors (alternately, one could treat the sector of the 1st file entry as end of Hidden.Directory plus Hidden.Unknown).
Root entry 0004h and 0005h are aliases for ISO files SYSTEM.CNF and boot EXE. There seem to be no nested sub-directories (but there are several DOT1 child archives, in root- and sub-directories, eg. 00DCh\0000h\*).

Chrono Cross (2 discs, 1999,2000)
Threads of Fate (aka Dewprism) (1 disc, 1999,2000)
 Sector 17h (Hidden.ID)
  000h 2    Disc Number      (0001h=Disc 1, 0002h=Disc 2)
  002h 2    Number of Discs? (0002h) (always 2, even if only 1 disc)
  004h 2+2  Sector and Size for Hidden.ID        (Sector=0017h, Size=002Ch)
  008h 2+2  Sector and Size for Hidden.Directory (Sector=0018h, Size=60E0h)
  00Ch 2+2  Sector and Size for Hidden.Unknown   (Sector=0025h, Size=0022h)
  010h 10h  Zerofilled
  020h 0Ch  Title ID ("CHRONOCROSS",00h)      ;Chrono Cross (retail)
       09h  Title ID ("DEWPRISM",00h)         ;Threads of Fate (retail)
       10h  Title ID ("DEWPRISM_TAIKEN",00h)  ;Threads of Fate (demo)
  0xxh 7xxh Zerofilled (unused, since Hidden.ID has only Size=2Ch/29h/30h)
 Sector 18h..24h (Hidden.Directory)
  000h N*4  File List entries
  ...  ..   Zeropadding (till Size=60E0h, aka 6200 entries)
  ...  720h Zeropadding (till end of 800h-byte sector)
 Sector 25h (Hidden.Unknown)
  Seems to contain a list of 16bit indices 0000h..1791h,FFFFh in File List
  (though many of the listed indices are unused file list entries)
  000h 2    Disc Number      (0001h=Disc 1, 0002h=Disc 2)
  002h 10h  Unknown 0000 1791 1777 1775 00ED 09DF FFFF 0002    ;\same on
  012h 10h  Unknown 0025 0943 10E3 FFFF FFFF 0C77 0FD9 0FA3    ;/Disc 1+2
  022h ..   Zerofilled (unused, since Hidden.ID has only Size=0022h)
 Sector 26h  1st file (same as boot EXE in ISO)
File List entries:
  0-22   Sector number
  23     Flag (0=Normal, 1=Unused entry)
  24-31  Number of unused bytes in last sector, div8 (0..FFh = 0..7F8h bytes)
The directory is just a huge list of root files (without any folder structure; many of the root files do contain DOT1 child archives though).
Root entry 0000h and 0001h are aliases for ISO files boot EXE and SYSTEM.CNF.
Filesizes can be computed as follows (that works for all entries including last used entry; which is followed by some unused entries with bit23=1):
  filesize = ([addr+4]-[addr] AND 7FFFFFh)*800h - ([addr+3] AND FFh)*8
Unused entries with bit23=1 have Sector pointing to end of previous file (needed for filesize calculation). There are some zeropadded entries at end of list (with whole 32bit zero). There are hundreds of dummy txt files (24-byte "It's CDMAKE Dummy!",0Dh,0Ah,,0Dh,0Ah,20h and File08xxh: 8-byte "dummy",0,0,0) although those are real used file entries, each occupying a whole separate 800h-byte sector.

Threads of Fate (demo version) (MagDemo33: TOF\DEWPRISM.HED+.EXE+.IMG)
The demo version is using the same directory format as retail version (but with Virtual Sector numbers in HED+EXE+IMG files instead of Hidden Sectors).
  TOF\DEWPRISM.HED (6000h bytes)    VirtSector=1Ah,  PhysSector=A0A5h
  TOF\DEWPRISM.EXE (97800h bytes)   VirtSector=26h,  PhysSector=A0B1h
  TOF\DEWPRISM.IMG (19EA800h bytes) VirtSector=155h, PhysSector=A1E0h
The demo's Virtual Sectors start at 1Ah (instead of 17h), to convert them to Physical Sectors: Subtract 1Ah, then add starting Sector Number of HED file. The HED file contains Hidden.ID, Hidden.Directory, and Hidden.Unknown.


CDROM File Archive HED/DAT/BNS/STR (Ape Escape)

Ape Escape KKIIDDZZ.HED/.DAT/.BNS/.STR
  000h 52Ch List for .DAT file    ;value 0000h..6FFFh = sector 0..6FFFh in DAT
  52Ch D4h  Zerofilled
  600h C4h  List for .BNS file    ;value 7000h..71AFh = sector 0..1AFh in BNS
  6C4h 3Ch  Zerofilled
  700h 50h  List for .STR file(s) ;raw CDROM sector numbers from 00:02:00
  750h B0h  Zerofilled
List entries, for all three lists (32bit values):
  0-19   File Offset/800h (20bit)
  20-31  File Size/800h   (12bit)
The sector numbers in DAT and BNS are basically counted from begin of the .DAT file (which has 7000h sectors in retail version, and the .BNS file does follow right thereafter on the next sector) (the demo version (MagDemo22: KIDZ\*) has only 105Ah sectors in .DAT, and the BNS entries at offset 600h start with 105Ah accordingly).
There are 29 STR files in DEMO\*.STR and STR\*.STR, and 20 of them (?) are referenced in HED ? There are also several .ALL files in above folders.
Note: Most of the STR files in Ape Escape contain polygon animation streams rather than BS compressed bitmaps. Ape Escape is (c)1999 by Sony.
  .HED is 2048 bytes
  .DAT is 58720256 bytes = 3800000h bytes  ;div800h would be 7000h
  .BNS is 884736 bytes = D8000h bytes      ;div800h would be 1B0h
  .STR's: 7D3Bh+150 = 7DD1h = sector for STR\LAB.STR
Some files contain RLE compressed TIMs:
CDROM File Compression TIM-RLE4/RLE8
Some files contain raw headerless SPU-ADPCM (eg. DAT file 00Ah).


CDROM File Archive WAD.WAD, BIG.BIN, JESTERS.PKG (Crash/Herc/Pandemonium)

Below are two slightly different formats. WAD.WAD has unused entries 00h-filled. The PKG format has them FFh-filled, and does additionally support Folders, and does have a trailing ASCII string. There's also a difference on whether or not to apply alignment to empty 0-byte files.
However, the formats can appear almost identical (unused entries, 0-byte files, and folders are optional, without them, the only difference would be the presence of the ASCII string; which does exist only in 800h-byte aligned PKG's though).

WAD.WAD (Crash/Crash)
Used by Crash Bandicoot 3 (DRAGON\WAD.WAD, plus nested WADs inside of WAD.WAD)
Used by Crash Team Racing (SPYR02\WAD.WAD, plus nested WADs inside of WAD.WAD)
Used by Madden NFL'98 (MagDemo02: TIBURON\.DAT except PORTRAIT,SPRITES,XA.DAT)
Used by N2O (MagDemo09, N2O\PSXMAP.TRM and N2O\PSXSND.SND)
Used by Speed Racer (MagDemo10: SPDRACER\ALL1.BIN, with 0-byte, unpadded eof)
Used by Gran Turismo 2 (MagDemo27: GT2\GT2.OVL = 128Kbyte WAD.WAD with GZIP's)
Used by Jonah Lomu Rugby (LOMUDEMO\SFX\*.VBS, ENGLISH\*.VBS)
Used by Judge Dredd (*.CAP and *.MAD)
Used by Spyro 2 Ripto's Rage (SPYRO2\WAD.WAD, and nested WAD's therein)
Used by Spyro 3 Year of the Dragon (SPYRO3\WAD.WAD, and nested WAD's therein)
Used by Men: Mutant Academy (MagDemo33: PSXDATA\WAD.WAD\*, childs in PWF)
  000h N*8  File List
  ...  ..   Zeropadding to 4-byte or 800h-byte boundary (or garbage padding)
  ...  ..   File Data...
The File List can contain Files, and Unused entries:
  000h 4    Offset in bytes (4- or 800h-byte aligned, increasing) ;\both zero
  004h 4    Size in bytes (always multiples of 800h bytes)        ;/when Unused
The Offset in first entry implies size of the File List (the list has no end-marker other than the following zeropadding; which doesn't always exist, ie. not in 4-byte aligned files, and not in case of garbage padding).
The last entry has Offset+Size+Align = Total WAD filesize (except, Speed Racer doesn't have alignment padding after the last file).
The WAD.WAD format doesn't have folder entries, however, it is often used with nested WADs inside of the main WAD, which is about same as folders.
The alignment can be 4-byte or 800h-byte: N2O uses 4-byte for the main WADs. Madden NFL '98 uses 800h-byte for main WAD and 4-byte for child WADs (file 08h,0Ah,0Ch in TIBURON\MODEL01.DAT and file 76h in PIX01.DAT). Crash Bandicoor 3 and Crash Team Racing use 800h-byte for both main & child WADs (although with garbage padding instead of zeropadding in child WAD headers).
Unused entries have Offset=0, Size=0.
Empty 0-byte files (should) have Size=0 and Offset=PrevOffs+PrevSize+Align (except, Speed Racer has Offset=PrevOffs+PrevSize, ie. without Align for 0-byte files).

X-Men: Mutant Academy (MagDemo33,50: PSXDATA\WAD.WAD)
This does resemble standard WAD.WAD, but with leading 800h-byte extra stuff.
  000h 4     ID ("PWF ")                                ;\
  004h 4     Total Filesize (707800h)                   ;
  008h 4     Unknown (1)                                ; extra stuff
  00Ch 4     Number of files (N)                        ;
  010h 7F0h  Zerofilled                                 ;/
  800h N*8   File List                                  ;\
  ...  ..    Zerofilled (padding to 800h-byte boundary) ; standard WAD.WAD
  ...  ..    File Data area                             ;/
 File List entries:
  000h 4     File Offset in bytes (increasing, 800h-byte aligned)
  004h 4     File Size in bytes
The archive contains child archives in DOT1 format, and in standard WAD.WAD format (without PWF header).

PKG (Herc/Pandemonium/UnholyWar)
Used by Pandemonium II (JESTERS.PKG, with Files+Folders+Unused entries)
Used by Herc's Adventure (BIG.BIN, with Files+Unused entries, without Folders)
Used by Unholy War (MagDemo12:CERBSAMP.PKG, with 0-byte files and nested PKG's)
Used by 102 Dalmatians (MagDemo40: PTTR\PSXDEMO.PKG)
  000h N*8  File List
  ...  ..   ASCII string (junk, but somewhat needed as nonzero end marker)
  ...  ..   Zeropadding to 800h-byte boundary; not in 4-byte aligned nested PKG
  ...  ..   File Data...
The File List can contain Files, Folders, and Unused entries. The overall format of the list entries is:
  000h 4    Offset in bytes (increasing, or 0=First child)  ;\both FFFFFFFFh
  004h 4    Size in bytes (always nonzero)                  ;/when Unused
Files and Folders do have exactly the same format, the only difference is that Folders will have Offset=00000000h in the NEXT list entry (in other words, the folder entry is followed by child entries, which start with Offset=0).
Offsets for Root entries are 800h-byte aligned, relative to begin of PKG file.
Offsets for Child entries are 4-byte aligned, relative to Parent Folder Offset.
The last Child entry has Offset+Size+Align(4) = Parent Folder Size.
The last Root entry has Offset+Size+Align(800h) = Total PKG filesize.
The last Root entry is usually followed by the ASCII string (which looks like junk, but it is useful because it equals to NextOffset=Nonzero=NoChilds).
  Example
  00003800h,00000666h   ;root00h          (file 666h bytes, padded=800h)
  00004000h,00000300h   ;root01h\..       (folder 300h bytes, padded=800h)
  00000000h,000000FDh   ;root01h\child00h (file FDh bytes, padded=100h)  ;\300h
  FFFFFFFFh,FFFFFFFFh   ;root01h\child01h (unused)                       ; byte
  00000100h,000001FDh   ;root01h\child02h (file 1FDh bytes, padded=200h) ;/
  00004800h,00001234h   ;root02h          (file 1234h bytes, padded=1800h)
  00006000h,00001234h   ;root03h          (file 1234h bytes, padded=1800h)
  FFFFFFFFh,FFFFFFFFh   ;root04h          (unused)
  00007800h,00001234h   ;root05h          (file 1234h bytes, padded=1800h)
  etc.
Notes: Unused entries can occur in both root and child folders (except, of course, not as first or last entry in child folders). Folders seem to occur only in root folder (although the format would allow nested folders).
Alternately, instead of Folders, one can use nested PKG's (the nested ones are using 4-byte align, without ASCII string and zeropadding in header).


CDROM File Archive BIGFILE.BIG (Gex)

Gex (GXDATA\BIGFILE.BIG and nested BIG files therein)
  000h 4     Number of Files (eg. F4h)
  004h 0Ch   Zero
  010h N*10h File entries
  ...  4     Archive ID (eg. 00000000h, FF53EC8Bh, or 83FFFFFFh)
  ...  ..    Zeropadding to 800h byte boundary
  ...  ..    File Data
File Entries:
  000h 4   Archive ID (same value as in above header)
  004h 4   Filename checksum or so (randomly ordered, not increasing)
  008h 4   Filesize in bytes
  00Ch 4   Fileoffset (800h-byte aligned) (increasing)
Filetypes in the archive include...
  looks like a lot of raw data without meaningful file headers...
  file C3h,ECh are raw SPU-ADPCM
  file 08h,09h are nested BIG archives, but with FileEntry[00h]=FF53EC8Bh
  file D9h,DAh are nested BIG archives, but with FileEntry[00h]=83FFFFFFh
FileEntry[04h] sometimes has similar continous values (maybe caused by similar filenames, and using a simple checksum, not CRC32).


CDROM File Archive BIGFILE.DAT (Gex - Enter the Gecko)

Gex - Enter the Gecko - BIGFILE.DAT
Used by Gex 2: Enter the Gecko (BIGFILE.DAT)
Used by Gex 3: Deep Cover Gecko (MagDemo20: G3\BIGFILE.DAT) -- UNSORTED
Used by Akuji (MagDemo18: AKUJI\BIGFILE.DAT)
Used by Walt Disney World Racing Tour (MagDemo35: GK\BIGFILE.DAT) -- UNSORTED
  000h 4     Number of Files      (C0h)
  004h N*18h File entries
  ...  ..    Zeropadding to 800h byte boundary
  ...  ..    File Data
File Entries:
  000h 4     Random
  004h 4     Filesize in bytes (uncompressed size)
  008h 4     Filesize in bytes (compressed size, or 0=uncompressed)
  00Ch 4     Fileoffset (800h-byte aligned) (increasing, unless UNSORTED)
  010h 4     Random
  014h 4     Random (or ascii in 1st file)
LZ Decompression:
  @@collect_more:
   flagbits=[src]+[src+1]*100h+10000h, src=src+2    ;16bit flags, unaligned
  @@decompress_lop:
   if dst>=dst.end then goto @@decompress_done
   flagbits=flagbits SHR 1
   if zero then goto @@collect_more
   if carry=0 then
     [dst]=[src], dst=dst+1, src=src+1
   else
     len=([src] AND 0Fh)+1), disp=([src] AND 0F0h)*10h+[src+1], src=src+2
     if len=1 or disp=0 then goto invalid   ;weirdly, these are left unused
     for i=1 to len, [dst]=[dst-disp], dst=dst+1, next i
   endif
   goto @@decompress_lop
  @@decompress_done:
   ret
Filetypes in the archive include...
  standard TIM  (eg. file 01h,02h)
  malformed TIM (eg. file 0Fh,14h) (with [8]=2*cx*cy+4 instead 2*cx*cy+0Ch)
  crippled VAB  (eg. file 0Eh,13h) (with hdr=filesize-4 plus raw ADPCM samples)
  several DNSa  (eg. file 0Dh,12h,17h,BCh) SND sound? (also used by kain)
  PMSa          (eg. Gex 3, World Racing) (SMP spu-adpcm samples)
  there seem to be no nested DAT files inside of the main DAT file
Note: same malformed TIMs are also in Legacy of Kain (folder0004h\file0013h).


CDROM File Archive FF9 DB (Final Fantasy IX)

DB Archive
  000h 1   ID (DBh)
  001h 1   Number of Types
  002h 2   Zero (0)
  004h N*4 Type List
  ...  ..  File Lists & File Data for each Type
Type List entries:
  000h 3   Offset to File List (self-relative, from current entry in Type List)
  003h 1   Data Type (00h..1Fh)
File List:
  000h 1   Data type (00h..1Fh) (same as in Type List)
  001h 1   Number of Files
  002h 2   Zero (0)
  004h N*2 File ID List (unique ID per type) (different types may have same ID)
  ...  ..  Zeropadding to 4-byte boundary
  ...  N*4 Offset List (self-relative, from current entry in Offset List)
  ...  4   End Offset  (first-relative, from first entry in Offset List)
  ...  ..  File Data (referenced from above Offset List)

Data Types
  00h  Misc (DOT1 Archives, or other files)
  01h  Unused?
  02h  Reportedly 3D Model data (vertices,quads,triangles,texcoords)
  03h  Reportedly 3D Animation sequences
  04h  TIM Texture
  05h  Reportedly Scripts (hdr="EV")              (eg. dir04\file32\1B-0001)
  06h  ?                                          (eg. dir02\file*)
  07h  Sound "Sequencer Data" (hdr="AKAO")        (eg. dir09\file*)
  08h  Sound? tiny files (hdr="AKAO")             (eg. dir04\file32\1B-0001)
  09h  Sound Samples (hdr="AKAO")                 (eg. dir0B\file*)
  0Ah  Reportedly Field Tiles and Field Camera parameters
  0Bh  Reportedly Field Walkmesh                  (eg. dir04\file32\1B-0001)
  0Ch  Reportedly Battle Scene geometry           (eg. dir06\file*)
  0Dh  ?                                          (eg. dir01\file01)
  0Eh  Unused?
  0Fh  Unused?
  10h  ?                                          (eg. dir05\file*)
  11h  ?                                          (eg. dir05\file*)
  12h  Reportedly CLUT and TPage info for models  (eg. dir04\file32\1B-0001)
  13h  Unused?
  14h  ?                                          (eg. dir05\file*)
  15h  Unused?
  16h  ?  (eg. dir04\file32\1B-0001)
  17h  ?  (eg. dir04\file32\1B-0000)
  18h  Sound (hdr="AKAO")  (eg. dir04\file32\1B-0001)
  19h  ?  (eg. dir04\file32\1B-0001)
  1Ah  ?  (eg. dir06\file*)
  1Bh  DB Archives (ie. further DB's nested inside of the parent DB archive)
  1Ch  ?  (eg. dir04\file32\1B-0001)
  1Dh  ?  (eg. dir03\file2328\1B-0001)
  1Eh  ?  (eg. dir04\file32\1B-0001)
  1Fh  ?  (eg. dir04\file32\1B-0001)
  20h..FFh Unused?


CDROM File Archive Ace Combat 2 and 3

Ace Combat 2 (Namco 1997) (ACE2.DAT and ACE2.STH/STP)
There are two archives, stored in three files:
  ACE2.DAT Directory for Data in ACE2.DAT itself         ;normal binary data
  ACE2.STH Directory for Data in separate ACE2.STP file  ;streaming data
Directory Format:
  000h 4    Unknown (1)
  004h 4    Number of entries (N)
  008h N*8  File List
File List entries (64bit):
  0-27   28bit Size/N (DAT=Size/4, STP=Size/800h)
  28-31  4bit  Type or Channel Number (see below)
  32-63  32bit Offset/800h in ACE2.STP or ACE2.DAT file
The files are interleaved depending on the Type/Channel number:
  File    Bit28-31 Channel Sector types...             Interleave Notes
  DAT     0        ch=0    DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD 1:1   data (normal)
  DAT     2        ch=0    DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD 1:1   data (exe)
  STH     0-6      ch=0-6  S.......S.......S.......S....... 1:8   stereo
  STH     8        ch=1    vvvvvvvSvvvvvvvSvvvvvvvSvvvvvvvS 1:1   video+stereo
  Whereas D=data, S=Stereo/Audio, v=video, .=other channels
Note: The DAT file does additionally contain PreSizeDOT1 and DOT1 child archives.
Demo: The archives in demo version (MagDemo01: ACE2.*) contain only a handful of files; the two EXE files in demo DAT archive are only 800h-byte dummy files, and demo STP is corrupted: Recorded as CDROM image with 920h-byte sectors, instead of as actual CD-XA sectors).

Ace Combat 3 Electrosphere (Namco 1999) (ACE.BPH/BPB and ACE.SPH/SPB)
There are two archives, stored in four files:
  ACE.BPH Directory for Data in separate ACE.BPB file  ;normal binary data
  ACE.SPH Directory for Data in separate ACE.SPB file  ;streaming data
Directory Format:
  000h 4    ID "AC3E" (=Ace Combat 3 Electrosphere)
  004h 4    Type (BPH=3=Data?, SPH=1=Streaming?)
  008h 2    BCD Month/Day?     (Japan=0427h, US=1130h)
  00Ah 2    BCD Year (or zero) (SPH=1999h, BPH=0)
  00Ch 4    Unknown (SPH=0, BPH/US=16CFh or BPH/JP=1484h)
  010h 4    Number of entries (N)
  014h N*8  File List
File List entries (64bit), when Bit31=1 (normal entries):
  0-18   19bit Size/N           (BPH=Size/4, SPB=Size/800h)
  19-23  5bit  Channel Number   (BPH=0, SPH=0..1Fh)
  24-26  3bit  Channel Interval (BPH=0, SPH=1 SHL N, eg. 3=Interval 1:8)
  27     1bit  Video Flag       (0=No, 1=Has Video sectors)
  28     1bit  Audio Flag       (0=No, 1=Has Audio sectors)
  29     1bit  Always 1 (except special entries with Bit31=0, see below)
  30     1bit  Unknown (US: Always 1, Japan: 0 or 1)
  31     1bit  Always 1 (except special entries with Bit31=0, see below)
  32-63  32bit Offset/800h in ACE.BPB or ACE.SPB file   (or 0 when bit31=0 ?)
File List entries (64bit), when Bit31=0:
  For unknown purpose, the normal entries with Bit31=1 are occassionally   followed by one or more entries with Bit31=0.
  Unknown if those entries do affect the actual storage (like switching to
  different channel numbers, or jumping to non-continous sector numbers).
  That unknown stuff exists in Japanese version only, not in US version.
  0-18   19bit Unknown (maybe some snippet size value in whatever units?)
  19-23  5bit  Always 0     (instead of Channel)
  24-27  4bit  Same as in most recent entry with Bit31=1
  28-31  4bit  Always 5     (instead of Flags)
  32-63  32bit Always 0     (instead of Offset)
The files are interleaved depending on the Channel Interval setting (and with types data/audio/video depending on Flags).
  File    Bit24-31    Sector types...                  Interval  Content
  BPH.US  E0h         DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD 1:1       data
  SPH.US  F8h         SvvvvvvvSvvvvvvvSvvvvvvvSvvvvvvv 1:1       stereo+video
  SPH.US  FBh         S.......v.......S.......v....... 1:8       stereo+video
  SPH.US  F3h         S.......S.......S.......S....... 1:8       stereo
  SPH.US  F4h         S...............S............... 1:16      stereo
  SPH.US  F5h         M............................... 1:32      mono
  BPH.JAP E0h         DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD 1:1       data
  SPH.JAP B8h,F8h     SvvvvvvvSvvvvvvvSvvvvvvvSvvvvvvv 1:1       stereo+video
  SPH.JAP B9h         Svvv....vvvv....Svvv....vvvv.... 1:2 (4:8) stereo+video
  SPH.JAP BAh,FAh     Mv......vv......vv......vv...... 1:4 (2:8) mono+video
  SPH.JAP BBh,FBh     S.......v.......S.......v....... 1:8       stereo+video
  SPH.JAP B3h,F3h     S.......S.......S.......S....... 1:8       stereo
  SPH.JAP B5h,F5h     M............................... 1:32      mono
  Whereas D=data, S=Stereo/Audio, M=Mono/Audio, v=Video, .=Other channels
As shown above, interval 1:2 and 1:4 are grouped as 4:8 and 2:8 (ie. 4 or 2 continous sectors per 8 sectors).
The Subheader's Channel number is specified in the above directory entries, Subheader's File number is fixed (0 for BPB, and 1 for SPB).
CDROM XA Subheader, File, Channel, Interleave
The SPB file is about 520Mbyte in both US and Japan, however, the Japanese version does reportedly contain more movies and some storyline that is missing in US/EU versions.
The BPB file contains DOT1 child archives, and Ulz compressed files.
CDROM File Compression Ulz/ULZ (Namco)
The SPB file contains movies with non-standard STR headers (and also uncommon: interleaved videos on different channels, at least so in the japanese version).
Demo: The archives do also exist on the demo version (MagDemo30: AC3\*), but the .SPB file is corrupted: Recorded as a RIFF/CDXAfmt file, instead of as actual CD-XA sectors).


CDROM File Archive NSD/NSF (Crash Bandicoot 1-3)

NSD/NSF versions
  v0  Crash Bandicoot Prototype (oldest known prototype from 08 Apr 1996)
  v1  Crash Bandicoot 1         (retail: S*\*.NSD and .NSF)
  v2  Crash Bandicoot 2         (MagDemo02: CRASH\S0\*.NSD and .NSF)
  v3  Crash Bandicoot 3 Warped  (MagDemo26,50: (S0\*.NSD and .NSF)

 ____________________________________ NSD _____________________________________

Overall NSD Structure (v0 contains only the Lookup entries)
  0000h 100h*4  Lookup Table, using index=((Filename/8000h) AND FFh) ;\
  0400h 4       Number of Chunks in .NSF file                        ; Lookup
  0404h 4       Number of Files in Lookup File List (N)              ;/
  0408h 4       Level Data Filename (eg. 4F26E8DFh="DATh.L")         ;-LevelDat
  040Ch 4       Bitmap Number of Colors (100h) (P) (0=None)          ;\
  0410h 4       Bitmap Width    (200h or 1B0h) (X) (0=None)          ; Bitmap
  0414h 4       Bitmap Height   (0D8h or 090h) (Y) (0=None)          ;/
  0418h 4       Compression: Offset/800h of first uncompressed chunk ;\
  041Ch 4       Compression: Number of compressed chunks (0..40h)    ; Compress
  0420h 40h*4   Compression: Compressed Chunk List (0=unused entry)  ;/
  ...   N*8     Lookup File List                                     ;-Lookup
  ...   ..      Level Data (size/format varies, see below)           ;-LevelDat
  ...   P*2     Bitmap Palette (16bit values, 8000h..FFFFh)          ;\Bitmap
  ...   X*Y     Bitmap Pixels  (0D8h*200h)                           ;/
There are four .NSD versions, which can be distinguished via filesize:
  v0  NSD Filesize=408h + N*8                           ;-Lookup only
  v1  NSD Filesize=520h + N*8 + P*2+X*Y + 210h          ;\
  v2  NSD Filesize=520h + N*8 + P*2+X*Y + 1DCh+S*18h    ; with extra stuff
  v3  NSD Filesize=520h + N*8 + P*2+X*Y + 2DCh+S*18h    ;/
Note: v0 is mainly used by the Crash Bandicoot prototype, but the Crash Bandicoot 1 retail version does also have a few v0 files.

NSD Lookup
The lookup table allows to find files (by filenames) in the NSF files. It does merely contain the NSF chunk number, so one must load/decompress that chunk to find the file's exact size/location in that chunk.
One can create a complete file list by scanning the whole NSF file without using the NDS lookup table.
 Lookup File List entries (indexed via Lookup Table):
  00h 4   Chunk Number in .NSF file
  04h 4   Filename (five 6bit characters)
Filenames:
  0     Type (always 1=Filename) (as opposed to 0=Memory Pointer)
  1-6   5th character ;-Extension  ;\character set is:
  7-12  4th character ;\           ; 00h..09h="0..9"
  13-18 3rd character ; Name       ; 0Ah..23h="a..z"
  19-24 2nd character ;            ; 24h..3Dh="A..Z"
  25-30 1st character ;/           ;/3Eh..3Fh="_" and "!"
  31    Always zero?
Special name: 6396347Fh="NONE.!"

NSD Level Data
Level Data exists in NSD v1-v3 (v0 does also have Level Data, but it's stored in NSF file "DAT*.L" instead of in the NSD file). There are two major versions:
 Level Data in NSD v1 (or NSF v0 file DAT*.L):
  000h  4       01h                                                  ;\
  004h  4       Level Number (xxh) (same as xx in S00000xx.NSD/NSF)  ;
  008h  4       3807C8FBh = "s0_h.Z" ?                               ; LevelDat
  00Ch  4       Zero                                                 ; v1
  010h  4       Zero                                                 ;
  014h  L*4     Namelist (40h*4)                                     ;
  ...   4       5Ah                                                  ;
  ...   F8h     Zerofilled                                           ;/
 Level Data in NSD v2-v3:
  000h  4       Number of Spawn Points (S)                           ;\
  004h  4       Zero                                                 ;
  008h  4       Level Number (xxh) (same as xx in S00000xx.NSD/NSF)  ; LevelDat
  00Ch  4       Number of Objects? (can be bigger than below list)   ; v2/v3
                  (eg. 1BDh or A5h or E4h)                           ;
  010h  L*4     Namelist for Objects?  (v2=40h*4, or v3=80h*4)       ;
  ...   4       Unknown, always 5Ah (maybe just list end marker?)    ;
  ...   C8h     Zerofilled                                           ;
  ...   S*18h   Spawn Points                                         ;/

NSD Bitmap
This bitmap is displayed while loading the level.

NSD Compression Info
Compression is only used in v1 (v2-v3 do also have the compression entries at [418h..51Fh], but they are always zerofilled).
 Compressed Chunk List entries at [420h..51Fh]:
  0-5   Compressed Chunk Size/800h (1..1Fh=800h..F800h bytes, 20h..3Fh=Bad?)
  6-31  Compressed Chunk Offset/800h
Note: Crash Bandicoot 1 retail does also have a few uncompressed files (either v0 files without compression info, or v1 files with zerofilled compression info).

 ____________________________________ NSF _____________________________________

NSF files consist of 64Kbyte chunks (compressed chunks are smaller, but will be 64Kbyte after decompression). Each chunk can contain one or more file(s). That implies that all files must be smaller than 64Kbyte (larger textures or ADPCM samples must be broken into multiple smaller files).
All files (except Textures) are NSF Child Archives which contain one or more smaller files/items.

NSF Chunk Types
 N*8Kbyte-Compressed-chunks:
  000h 2    ID, always 1235h (instead of 1234h)
  002h 2    Zero
  004h 4    Decompressed Size (max 10000h) (usually 9xxxh..Fxxxh, often Fxxxh)
  008h 4    Skip Size (max 40h or so, when last LZSS_len was 40h)
  00Ch ..   Compressed data
  ...  SK   Unused (Skip size)
  ...  ..   Final uncompressed bytes (10000h-compressed_size-skip_size)
 64Kbyte-Texture-chunks:
  000h 2    ID, always 1234h
  002h 2    Chunk Family (1=Texture)
  004h 4    Filename (five 6bit characters)
  008h 4    File Type (5=Texture)
  00Ch 4    Checksum (sum of bytes ar [0..FFFFh], with initial [0Ch]=00000000h)
  010h ...  Zerofilled
  020h ...  Texture data (raw VRAM data, FFE0h bytes?)
 64Kbyte-NonTexture-chunks:
  000h 2    ID, always 1234h
  002h 2    Chunk Family (0=Misc or 2..5=Sound)
  004h 4    Chunk Number*2+1
  008h 4    Number of Files (N) (can be 0, eg. prototype S0000003 chunk21h)
  00Ch 4    Checksum (sum of bytes ar [0..FFFFh], with initial [0Ch]=00000000h)
  010h N*4  File List (Offsets from ID=1234h to entries) (4-byte aligned)
  ...  ..   Offset for end of last File
  ...  ..   File Data (NSF Child Archives) (includes Type/Filename)
  ...  ..   Padding to 10000h-byte boundary

NSF Child Archives
  000h 4    ID, always 0100FFFFh
  004h 4    Filename (five 6bit characters)
  008h 4    File Type (01h..04h, or 06h..15h)
  00Ch 4    Item Count (I)
  010h I*4  Item List (Offsets from ID=0100FFFFh to items) (...unaligned?)
  ...  ..   Offset for end last item
  ...  ..   Data (Items)

NSF Chunk Loading and Decompression
The compression is a mixup of LZSS and RLE. Compressed chunks are max F800h bytes tall (10000h bytes after decompression).
  dst=chunk_buffer_64kbyte
  if chunksize is known (from NSD file)
    src=dest=dst+10000h-chunksize
    diskread(fpos,src,chunksize)
  else (when parsing raw NSF file without NSD file)
    src=temp_buffer_64kbyte
    diskread(fpos,src,10000h)
  dst_start=dst, src_start=src
  if halfword[src+00h]<>1234h then   ;check ID (1234h=raw, or 1235h=compressed)
    dst_end=dst+word[src+04h]
    skip_size=word[src+08h]
    src=src+0Ch
    while dst<dst_end
      x=[src], src=src+1
      if x<80h then
        for i=0 to x-1, [dst]=[src], dst=dst+1, src=src+1, next i ;uncompressed
      else
        x=(x AND 7Fh)*100h+[src], src=src+1
        disp=x/8, len=(x AND 7)+3, if len=0Ah then len=40h
        for i=0 to len-1, [dst]=[dst-disp], dst=dst+1, next i     ;compressed
    src=src+src_skip
  if src<>dst then
    while dst<dst_start+10000h, [dst]=[src], dst=dst+1, src=src+1 ;uncompressed
  chunksize=src-src_start  ;<-- compute (when chunksize was unknown)
  fpos=fpos+chunksize      ;<-- fileposition of next chunk
As shown above, the chunk is intended to be loaded to the end of the decompression buffer, so trailing uncompressed bytes would be already in place without needing further relocation (despite of that intention, the actual game code is uselessly relocating src to dst, even when src=dst).
Note: All compressed files seem to have an uncompressed copy with same filename in another chunk (the NSD Lookup table does probably(?) point to the compressed variant, which should reduce CDROM loading time).

 _________________________________ Filetypes __________________________________

Filetype Summary
Below shows File Type, Chunk Family, Extension (5th character of filename), the version where the type is used, 4-letter type names (as found in the EXE files), and a more verbose description.
  Typ Family Ext Ver  Name Description
  00h -      !   -    NONE Nothing
  01h 0      V   all  SVTX Misc.Vertices
  02h 0      G   all  TGEO Misc.Model         ;\changed format in v2-v3 ?
  03h 0      W   all  WGEO Misc.WorldScenery  ;/
  04h 0      S   all  SLST Misc.UnknownSLST
  05h 01h    T   all  TPAG Texture.VRAM
  06h 0      L   v0   LDAT Misc.LevelData     ;-stored in NSD in v1-v3
  07h 0      Z   all  ZDAT Misc.Entity        ;-changed format in v2-v3 ?
  08h -      -   -    CPAT Internal?
  09h -      -   -    BINF Internal?
  0Ah -      -   -    OPAT Internal?
  0Bh 0      C   all  GOOL Misc.GoolBytecode
  0Ch 02h    A   v0   ADIO OldSound.Adpcm     ;\type 0Ch
  0Ch 03h    A   all  ADIO Sound.Adpcm        ;/
  0Dh 0      M   all  MIDI Misc.MidiMusic     ;-changed format in v1-v3 ?
  0Eh 04h    N   all  INST Sound.Instruments
  0Fh 0      D   v0-1 IMAG Misc.UnknownIMAG   ;\type 0Fh
  0Fh 0      X   v2-3 VCOL Misc.UnknownVCOL   ;/
  10h -      -   -    LINK Internal?
  11h 0      P   v0-1 MDAT Misc.UnknownMDAT   ;\type 11h
  11h 0      R   v3   RAWD Misc.UnknownRAWD   ;/
  12h 0      U   v0-1 IPAL Misc.Unknown      ;-Crash 1 only? (eg. S0000019.NSF)
  13h 0      B   v1-3 PBAK Misc.DemoPlayback ;-eg. in MagDemo02
  14h 0      V   v0-1 CVTX Misc.UnknownCVTX   ;\type 14h
  14h 05h    O   v2-3 SDIO Speech.Adpcm       ;/
  15h 0      D   v2-3 VIDO Misc.UnknownVIDO
As shown above, Type 0Ch is used with family 02h/03h, and Type 0Fh,11h,14h have two variants each (with different extensions). The Extensions do usually corresponding with the Types (although extension V,D are used for two different types each).

See also:
https://gist.github.com/ughman/3170834
https://dl.dropbox.com/s/fu29g6xn97sa4pl/crash2fileformat.html

Weird Note
"Sound entries don't need to be aligned as strictly for most (all?) emulators."
What does that mean???
Is there a yet unknown 16-byte DMA alignment requirement on real hardware?


CDROM File Archive STAGE.DIR and *.DAT (Metal Gear Solid)

Metal Gear Solid (MagDemo13: MGS\*)
Metal Gear Solid (MagDemo25: MGS\*)
Metal Gear Solid (MagDemo44: MGS\*) (looks same as in MagDemo13)
Metal Gear Solid (Retail: MGS\*)

Summary of ISO files in MGS folder (with filesizes for different releases)
  File        MagDemo13/44 MagDemo25  Retail/PAL
  .EXE        9C000h       9C800h     9D800h      ;-executable
  STAGE.DIR   590800h      11A7800h   42AE000h    ;-main archive
  FACE.DAT    2CA000h      3Dh (txt)  358800h     ;-face animation archive
  ZMOVIE.STR  -            -          2D4E800h    ;-movie archive
  DEMO.DAT    149B000h     3Dh (txt)  EC20000h    ;\DAT/SYM combos (the .SYM
  DEMO.SYM    88h          -          -           ; files were leaked in
  VOX.DAT     14F2000h     9F800h     B054800h    ; MagDemo13/MagDemo44 only)
  VOX.SYM     988h         -          -           ;/
  BRF.DAT     -            66800h     575800h     ;\whatever, unknown format(s)
  RADIO.DAT   16CB8h       3Dh (txt)  1AA956h     ;/

STAGE.DIR:
  000h 4     Size of File List (N*0Ch)
  004h N*0Ch Folder List
  ...  ..    Zeropadding to 800h-byte boundary
  ...  ..    Folder Data
 Folder List entries:
  000h 8     Foldername (zeropadded if less than 8 chars)  ;nickname=stg
  008h 4     Offset/800h to File List
 Folder Data (per folder):
  000h 2     Unknown (always 1) (maybe File List size/800h?)
  002h 2     Folder Size/800h (of whole folder, with file list plus file data)
  004h N*8   File List
  ...        Zeropadding to 800h-byte
  800h       Data (for files in current folder)
 File List entries:
  000h 2     File ID (checksum on name)
  002h 1     File Family (one of following chars: "cnrs")
  003h 1     File Type   (one of following chars: "abcdeghiklmoprswz",FFh)
  004h 4     File Size (or File Offset, when File Family="c")
Combinations of Family/Type characters are:
  .?a    ???? if any ???? (does NOT exist on PAL disc 1)    ;nickname=azm
  .sb    MIPS binary code  (leading)                        ;nickname=bin
  .cc    Whatever          (eg. vr10\*, s01a\*)             ;nickname=con
  .nd    Texture Archive   (leading) (contains PCX files)   ;nickname=dar
  .rd    Misc Archive      (leading) (eg. init\*)           ;nickname=dar
  .se    Sound Effects?    (trailing)                       ;nickname=efx
  .cg    Whatever, reportedly bytecode functions            ;nickname=gcx
  .ch    Whatever                                           ;nickname=hzm
  .ci    Whatever          (eg. ending\*, s01a\*)           ;nickname=img
  .ck    Whatever, model? aka "pat_xxx" files               ;nickname=kmd
  .cl    Lights, first word = size/10h                      ;nickname=lit
  .sm    Sound Music? Nested DOT1+DOTLESS Archives          ;nickname=mt3
  .co    Whatever "OARa"   (eg. d16e\*, s00a\*, s02c\*)     ;nickname=oar
  .cp    PCX bitmap        (eg. init\*)                     ;nickname=pcc
  .cr    Whatever "sNRJ1F" (eg. roll\*)                     ;nickname=rar
  .cs    Whatever          (eg. d16e\*, s01a\*)             ;nickname=sgt
  .sw    Wave Archive      (trailing)                       ;nickname=wvx
  .cz    Whatever "KMDa"   (eg. s11a, a11c, s14e, s15a)     ;nickname=zmd
  .c,FFh End of Family="c" area                             ;nickname=dar?
Files are starting on 800h-byte boundaries. Files with Family="c" are special, they contain an Offset entries instead of a Size entries, that Offsets are 4-byte aligned (relative to the 800h-byte aligned offset of the first Family="c" entry), the list of Family="c" entries is terminated by an entry with Family="c" and Type=FFh (which contains the end-offset of the last c-Family entry, aka the size of all c-Family entries).
Note: The above 3-letter nicknames are used on some webpages (unknown why, maybe they are derived from MGS filename extensions in the PC version).

FACE.DAT (face animations for video calls):
This contains several large blocks (supposedly one per stage, each block having its own file list). There is no directory to find the begin of the separate blocks, but one can slowly crawl through the file:
  NextBlock = CurrBlock + 4 + Offset(lastfile)+Size(lastfile) + Align800h
The content of each block is:
  000h 4     Number of Files in this block (eg. 19h or 1Ch)
  004h N*0Ch File List for this block
  ...  ..    File Data for this block
  ...  ..    Zeropadding to 800h-byte boundary (followed by next block, if any)
 File List entries:
  000h 2     File Type (0=Main/Eye/Mouth frames, 1=All frames are full size)
  002h 2     File ID (name checksum?)
  004h 4     Filesize in bytes
  008h 4     Offset in bytes, minus 4
Type 0 Files in FACE.DAT:
 This type use a single palette for all frames, and only the first frame is
 full 52x89pix, the other frames contain only the update sections (eg. eyes).
  000h 4     Offset to 200h-byte palette       (usually 20h)    ;\Main
  004h 4     Offset to Main Bitmap (52x89pix) (usually 220h)    ;/
  008h 4     Offset to 4th Bitmap  (usually xxxxh or 0=None)    ;\Eyes
  00Ch 4     Offset to 5th Bitmap  (usually xxxxh or 0=None)    ;/
  010h 4     Zero
  014h 4     Offset to 2nd Bitmap  (usually 143Ch or 0=None)    ;\Mouth
  018h 4     Offset to 3rd Bitmap  (usually xxxxh or 0=None)    ;/
  01Ch 4     Zero
  020h 200h  Palette (256 colors) ;\Main
  220h 1218h Main Bitmap          ;/
  1438h 4    Zero
  143Ch ..   2nd Bitmap (if any)  ;\Mouth
  ...   ..   3rd Bitmap (if any)  ;/
  ...   ..   4th Bitmap (if any)  ;\Eyes
  ...   ..   5th Bitmap (if any)  ;/
Type 1 Files in FACE.DAT:
 This type use separate palettes for each frame, all frames are full 52x89pix.
  000h 4     Number of frames
  004h N*0Ch Frame List
  ...  200h  1st Frame Palette
  ...  1218h 1st Frame Bitmap (52x89pix)
  ...  4     ?
  ...  200h  2nd Frame Palette
  ...  1218h 2nd Frame Bitmap (52x89pix)
  ...  4     ?
  ...  ..    3rd Frame ...
 Frame List entries:
  000h 4     Offset to Palette
  004h 4     Offset to Bitmap (usually at Palette+200h)
  008h 4     Unknown (often 000x000xh)
Bitmap Format (for both Type 0 and Type 1):
  000h 1     Offset X (always 00h in Main Bitmap)
  001h 1     Offset Y (always 00h in Main Bitmap)
  002h 1     Width    (always 34h in Main Bitmap, or less in 2nd-5th bitmap)
  003h 1     Height   (always 59h in Main Bitmap, or less in 2nd-5th bitmap)
  004h ..    Bitmap Pixels at 8bpp (Width*Height bytes)

DEMO.DAT, DEMO.SYM
VOX.DAT, VOX.SYM
The .DAT files contain several huge blocks, found on 800h-boundaries starting with:
  10 08 00 00 0x 00 00 00 ..
The .SYM files (if present) contain Names and .DAT Offsets/800h for those huge blocks in text format:
  "0xNNNNNNNN name",0Ah
VOX.DAT does (among others) contain SPU-ADPCM chunks with 2004h bytes or less, that is, a 1+3 byte chunk header (01h=SPU-ADPCM, 002004h=Size), plus 2000h byte or less SPU-ADPCM data.

RADIO.DAT:
Whatever, contains chunks with text messages, chunks are about as so:
  000h 4   Unknown    (eg. 36h,BFh,5Eh,00h)
  004h 4   Unknown    (eg. 03h,13h,00h,00h)
  008h 1   Unknown    (eg. 80h)
  009h 2   Chunk Size (eg. 0xh,xxh)          ;big-endian
  ..   ..  Chunk Data (Chunk Size-2 bytes) (binary stuff, and text strings)

BRF.DAT:
Contains several "folders" in this format:
  000h 4   Number of files in this folder
  004h ..  File(s)
  ...  ..  01h-padding to 800h-byte boundary
 Files have this format:
  000h ..  Filename ("name.pll",00h)
  ...  ..  Zeropadding to 4-byte boundary (aligned to begin of BRF.DAT)
  ...  4   File data size (usually a multiple of 4)
  ...  ..  File data
  ...  1   Zero (00h)
The above "folders" are then followed by several PCX files:
  000h ..  PCX file (starting with 0A,05,01,01 or 0A,05,01,08)
  ...  ..  01h-padding to 800h-byte boundary
The first part with .pll files does contain some kind of chunk sizes that could be used to find the next entry (but that would be very slow).
The second part with .PCX files doesn't have any chunk sizes at all (though one could decompress the .PCX file to find the end of each file) (also one could guess/find them by looking for 0A,05,01,01/08 on 800h-byte boundaries).

ZMOVIE.STR (movie archive with several STR files with subtitles)
CDROM File Video Streaming STR Variants

STAGE.DIR\*\*.sb - stage binary/header
This is the first file in most folders (except "init*" folders).
The file contains MIPS binary program code. And, there are ascii strings near end of .sb files, which include filenames, alike:
  "name.c",00h + garbage-padding to 4-byte boundary  ;<-- maybe source code?
  "pat_lamp",00h + zero- padding to 4-byte boundary  ;<-- name for File ID !
Those filenames do cover some (not all) of the name checksums in the STAGE.DIR folder.

STAGE.DIR\*\*.cp, STAGE.DIR\*\*.nd\.p, BRF.DAT\* - PCX bitmap files
MGS is using customized/corrupted PCX files as standard texture format (in STAGE.DIR\*\*.cp, STAGE.DIR\*\*.nd\*.p, and BRF.DAT\*).
For details on PCX format (and MGS-specific customizations), see:
CDROM File Video Texture/Bitmap (PCX)
Apart from PCX, there's also custom texture format for animated bitmaps (in FACE.DAT), and a few TIM images (in STAGE.DIR\init*\*.rd\*.r)

STAGE.DIR\*\*.nd - texture archive (with .PCX files)
STAGE.DIR\init*\*.rd - misc archive (with misc files)
These archives contain several chunks in following format:
  000h 2     File ID (checksum on name?)
  002h 1     File Type (one of following chars: "p" for .nd, or "kors" for .rd)
  003h 1     Zero (00h)
  004h 4     Chunk Size (rounded to 4-byte boundary)
  008h ..    Chunk Data
The File Type can be:
  .p    PCX bitmap                      ;-in *\*.nd archives
  .k    Whatever                        ;\
  .o    Whatever "OARa"                 ; in init*\*.rd archives
  .a    Whatever                        ;
  .r    Misc (TIM and other stuff)      ;/
There can be 1-2 texture archives per STAGE.DIR folder (both having File ID=0000h) (probably due to a memory size limit: the game does probably load one archive with max 300Kbytes, relocate its contents to VRAM, then load the next archive, if any).

STAGE.DIR\*\*.sw - wave archive
There can be one or more .sw files per stage folder (eg. two sw's in "vr*\*.sw").
  000h 4     Unknown (800h or C00h)       ;big-endian
  004h 4     Size of File List (N*10h)    ;big-endian
  008h 8     Zerofilled
  010h N*10h File List (xx,xx,xx,00,00,00,00,7F,00,00,00,0F,00,19,0A,00)
  ...  4     Unknown (40000h or 60000h)   ;big-endian
  ...  4     Size of SPU-ADPCM Data area  ;big-endian
  ...  8     Zerofilled
  ...  ..    SPU-ADPCM Data area (indexed from File List)
 File List entries:
  000h 4     Offset+Flags                 ;little-endian!
               bit0-16  Offset (from begin of SPU-ADPCM Data area)
               bit17    Unknown (0 or 1)
               bit18    Unknown (1)
               bit19-31 Unknown (0)
  004h 12    Whatever (always 00,00,00,7F,00,00,00,0F,00,19,0A,00)
The unknown fields might contain volume, ADSR, pitch or the like?

STAGE.DIR\*\*.se - sound effects? maybe short midi-like sequences or so?
  000h 80h*10h List (unused entries are 1x00000000h,3xFFFFFFFFh)
  800h ..      Data (whatever, usually 14h or more bytes per list entry)
 List entries:
  000h 1       Unknown (eg. 01h,10h,20h,A0h,80h,FFh)    ;\
  001h 1       Number of Voices? (1..3)                 ; all zero for
  002h 1       Unknown (1 or 0)                         ; unused list entries
  003h 1       Unknown (2 or 0 or 1)                    ;/
  004h 4       Offset-800h for 1st Voice?               ;-FFFFFFFFh=Unused
  008h 4       Offset-800h for 2nd Voice? (if any)      ;-FFFFFFFFh=Unused
  00Ch 4       Offset-800h for 3rd Voice? (if any)      ;-FFFFFFFFh=Unused
 Data:
  Seems to contain 4-byte entries (last entry being 00,00,FE,FF).

STAGE.DIR\*\*.sm - whatever nested archives - sound music? mide-like?
This does resemble a DOT1 Parent archive with 1-4 DOTLESS Child archives. Except, the offsets in Child archives are counted from begin of Parent archive.
 Data:
  Seems to contain 4-byte entries (last entry being 00,00,FE,FF).

File IDs
File IDs in STAGE.DIR (and maybe elsewhere, too) are computed as so:
  sum=0,
  for i=0 to len(filename)-1
    sum=sum*20h+filename[i]          ;\or so, 16bit overflows might be
    sum=(sum+sum/10000h) AND FFFFh   ;/cropped slightly differently
Examples: "abst"=1706h, "selectvr"=8167h.
Some filenames are empty (name="", ID=0000h).
Some filenames do match up with the STAGE.DIR foldername.
Some filenames do match up with strings in .sb file of current folder.
Other filenames are unknown.


CDROM File Archive DRACULA.DAT (Dracula)

Dracula - The Resurrection - DRACULA.DAT (180Mbyte)
  000h 4     Zero
  004h 4     Number of Entries (503h)
  008h 4     Zero
  00Ch 4     Random
  010h 10h   Zero
  020h N*10h File List
  ...  ..    Zeropadding to 800h-byte boundary
  ...  ..    Fild Data area
File List entries:
  000h 4     Offset/800h
  004h 4     Type (see below for info on different file types)
  008h 4     Filesize in bytes
  00Ch 4     Random (or 0 when Filesize=0)
Most of the .DAT file consists of groups of 3 files (with type 01h/40h, 20h and 400h; of which the files with type 20h and 400h may have Size=0=empty).
  Type=00000001h Cubemap           ;\either one of these
  Type=00000040h Cubemap.empty     ;/
  Type=00000020h Cubemap.overlay?  ;\these have size=0 when unused
  Type=00000400h Cubemap.sounds    ;/
There are some general purpose files with other types at end of .DAT file:
  Type=00000000h Archive with TIMs         (Size=AB74h)  (" RSC3.1V")
  Type=00000004h Unknown                   (Size=16164h) (00000064h)
  Type=00000008h Related to DRACULA1.STR   (Size=1000h)  (" RTS1.1V")
  Type=00001000h Unknown                   (Size=2000h)  ("BXFS1.1V")
  Type=00008000h Unknown                   (Size=71Dh)   ("  CM1.1V")
  Type=00020000h Unknown                   (Size=3B9h)   (" GSM0.1V")
  Type=02000000h Unknown                   (Size=0h)     (empty)
  Type=00000100h Related to DRACULA1.XA    (Size=1000h)  ("RAAX1.1V")
  Type=00000010h Unknown                   (Size=450h)   (" HYP0.1V")
  Type=00100000h Unknown                   (Size=4014h)  (" xFS1.1V") (x=A1h)
  Type=00000080h Unknown                   (Size=258F4h) (00000010h)
  Type=00000200h TIM (gui charset)         (Size=6E9Eh)  (TIM)
  Type=00010000h TIM (gui buttons)         (Size=10220h) (TIM)
  Type=00040000h Unknown                   (Size=2C4h)   (" TES0.1V")
  Type=00002000h TIM (gui book pages)      (Size=1040h)  (TIM)
  Type=00000800h Cubemap ;\as Type 01h,    (Size=4092Ch) (" RIV3.1V")
  Type=00004000h Cubemap ;/but [10h,14h]=0 (Size=4092Ch) (" RIV3.1V", too)
Type 01h - Cubemap:
  000h 8     Name, ASCII, padded with leading spaces (eg. " RIV3.1V")
  008h 4     Something (0, 1 or 2) (unknown, this isn't number of list entries)
  00Ch 4     Zero
  010h 4     Offset to Ext data (ACh)                       ;\ext data
  014h 4     Size of Ext data (eg. 0 or 84h)                ;/
  018h 6*4   Offsets to Side 0-5                            ;\cubemap sides
  030h 6*4   Sizes of Side 0-5 (0, 10220h, or 10820h)       ;/
  048h 44h   Zerofilled
  08Ch 20h   Name, ASCII (eg. "DEBUT0.VR", zeropadded)
  0ACh ..    Ext Data (if any)
  ...  ..    Cubemap TIM sides (if any)
 Note: The cubemap TIMs have 100h or 400h colors (in the latter case: 100h  colors for each quarter of the 8bpp bitmap).
 Note: The TIMs can be arranged as 3D-cubemap with six sides, or as hires
 2D-bitmap (composed of four TIMs, and 2 empty TIMs with size=0).
Type 40h - Empty Cubemap:
  Same as Type 01h, but size is always 0ACh (and all seven Size entries are 0)
Type 400h - Sound VAG's:
  000h 8     Name, ASCII, padded with leading spaces (eg. " XFS0.1V")
  008h 4     Zero
  00Ch 4     Number of Files (N) (max 10h)
  010h N*10h File List (100h bytes, zeropadded when less than 10h files)
  110h ..    File Data (VAG files)
 File List entries:
  000h 4     Unknown (55F0h, 255F0h or 20000h)
  004h 4     File ID (01010000h, increasing, or other when above=2xxxxh)
  008h 4     Offset in bytes                                ;\.VAG files
  00Ch 4     Filesize in bytes                              ;/
Type 20h - Cubemap overlays, polygons, effects or so?:
  000h 8      Name, ASCII, padded with leading dot (eg. ".MNA4.1V")
  008h 4      Zero
  00Ch 4      Random
  010h 4      Unknown 01h
  014h 4      Total Number of 40h-byte blocks (01h..[018h]) (H)
  018h 4      Total Number of 120h-byte blocks (eg. 1Fh,31h) (N)
  01Ch 4      Total Number of 1Ch-byte blocks (eg. 1Eh, 50h, F7h) (M)
  020h 4      Unknown 0 or 1 (in file 4EAh)
  024h 4      Unknown 01h
  028h 6*4    Offsets to Side 0-5 (at end of file and up) (or 0)  ;\cubemap
  040h 6*4    Sizes of Side 0-5   (10220h, or 10820h)     (or 0)  ;/sides
  058h H*40h  40h-byte blocks
  ...  N*120h 120h-byte blocks (related to offsets in 40h-byte blocks)
  ...  M*1Ch  1Ch-byte blocks  (related to offsets in 120h-byte blocks)
  ...  ..     Unknown data     (related to offsets in 1Ch-byte blocks)
  ...  ..     Ext data         (related to Ext offsets in 40h-byte blocks)
  FILE DOES END HERE!
  (below is allocated in above header, but not actually stored in the file)
  (maybe allocated as rendering buffer?)
  ...  -      Cubemap TIM sides
 The 40h-byte blocks are:
  000h 20h    Name (eg. "FLAMMES", zeropadded)
  020h 4      Unknown 01h or 00h
  024h 4      Offset to 120h-byte blocks (usually 98h, or higher)
  028h 4      Unknown 00h
  02Ch 4      Number of 120h-byte blocks (01h..[018h])
  030h 4      Unknown 01h
  034h 4      Ext Offset                ;\usually all zero
  038h 4      Ext Size (3C000h)         ; (except, nonzero in file 4EAh)
  03Ch 4      Ext Random (checksum?)    ;/
 The 120h-byte blocks are:
  000h 18h*4  List with Offsets to 1Ch-byte blocks (usually 4 entries nonzero)
  060h 18h*4  List with Zeroes
  0C0h 18h*4  List with Numbers of 1Ch-byte blocks (usually max 4 entries)
 The 1Ch-byte blocks are:
  000h 4      Unknown 04h
  004h 4      Width   20h or 10h
  008h 4      Height  20h or 10h or 30h
  00Ch 4      Unknown 60h or 10h
  010h 4      Unknown 00h or 30h
  014h 4      Offset to Unknown Data
  018h 4      Size of Unknown Data (Width*Height*1)
Type 00h - TIMs:
  000h 8      Name (" RSC3.1V")
  008h 8      Zerofilled
  010h 4      Number of used entries (1Fh) (max 80h)
  014h 80h*4  Offset List    (offsets to files) (A14h and up)
  214h 80h*4  Zero List      (zerofilled)
  414h 80h*4  Size List      (filesizes)
  614h 80h*4  Width List     (0Ch,18h,34h,2Ch) (in pixels)
  814h 80h*4  Height List    (0Ch,24h,34h,2Ch)
  A14h ..     Data (TIM files, with mouse pointers)


CDROM File Archive Croc 1 (DIR, WAD, etc.)

Croc 1 (MagDemo02: CROC\*) (plus more files in retail version)
CROCFILE.DIR and CROCFILE.1:
 CROCFILE.DIR:
  000h 4     Number of Entries (N)
  004h N*18h File List
  ...  4     Checksum (sum of all of the above bytes)
 CROCFILE.1:
  000h ..    File Data (referenced from .DIR)
 File List entries:
  000h 0Ch   Filename ("FILENAME.EXT", zeropadded if shorter)
  00Ch 4     File Size in bytes (can be odd) (including 8 byte for size/chksum)
  010h 4     File Offset in .1 file (unaligned, can be odd, increasing)
  014h 4     Zero (0)
CROCFILE.DIR\MP*.MAP (and MAP files inside of MAP*.WAD and MP090-100_*.WAD):
  000h 4    Size-8 of whole file (or Size-0 for those in MP*.WAD)
  004h 4    Flags? (usually 0Ch or 14h)
  008h 1    Filename length                (including trailing 00h, if any)
  009h ..   Filename ("P:\CROC\EDITOR\MAPS\..\*.MAP") (+00h in MAP05*.WAD)
  ...  ..   Unknown
  ...  1    Description length
  ...  ..   Description (eg. "Default New Map")
  ...  ..   Unknown
  ...  (4)  Checksum of whole file (sum of all bytes) (not in MP*.WAD)
CROCFILE.DIR\*.WAD:
 MAP*.WAD:
  000h 4    Size-8 of whole file
  004h ..   MAP file(s) (each with size/checksum, same format as MP*.MAP)
  ...  4    Checksum of whole file (sum of all of the above bytes)
 CROC.WAD, CROCSLID.WAD, EXCLUDE.WAD, MP*.WAD, OPTIONS.WAD, SWIMCROC.WAD:
  000h 4    Size-8 of whole file
  004h 4    Offset-8 to SPU-ADPCM data area
  008h ..   Data File area (model.MOD anim.ANI, bytecode.BIN, header.CVG, etc.)
  ...  ..   SPU-ADPCM data area (if any, note in CROCSLID.WAD and OPTIONS.WAD)
  The Data File area contains several "files" but doesn't have any directory
  with filename/offset/size. The only way to find the separate files seems to
  be to detect the type/filesize of each file, and then advance to next file
  (bytecode.BIN files start with a size entry, but files like .MOD or .ANI
  require parsing their fileheader for computing filesize).
  Note: The PC version reportedly has .WAD files bundled with .IDX file (that
  makes it easier to find files and filenames).
  Note: The STRAT.DIR file contains a list of filenames used in .WAD files
  (but lacks info on offset/size, so it isn't really useful).
CROCFILE.DIR\*.BIN:
 Sound.BIN Files (CROCFILE.DIR\AMBI*.BIN, MAP*.BIN, JRHYTHM.BIN, REVERB.BIN):
  000h 4    Size of .SEQ file                   ;\if any (not in REVERB.BIN)
  004h ..   SEQ file (starting with ID "pQES")  ;/
  ...  4    Size of .VH file                    ;\always present
  ...  ..   VH file (starting with ID "pBAV")   ;/
  ...  ..   VB file (sample data, SPU-ADPCM data, up to end of file)
 Music.BIN files (MAGMUS.BIN, MUSIC.BIN):
  000h 4      Size-8 of whole file (118h)
  004h ..     Increasing 32bit values ;sector numbers in PACK*.STR files or so?
  ...  4      Unknown (2EEh or 258h) (aka 750 or 600 decimal)
  ...  ..     Zeropadding
  11Ch 4      Checksum (sum of all of the above bytes)
  Note: MUSIC.BIN has an extra copy (without chksum) in EXCLUDE.WAD\MUSIC.BIN
 Ascii.BIN files (CREDITS*.BIN, MNAME.BIN):
  000h 4      Size-8 of whole file
  004h (2)    Type or so? (02h,01h) (only in CREDITS*.BIN, not in MNAME.BIN)
  ...  ..     Ascii strings (each string is: len,"text string",unknown)
  ...  4      Checksum (sum of all of the above bytes)
 Texture.BIN files (type 4) (STILLGO.BIN, STILLST.BIN, STILLTL.BIN):
  000h 2      Type (4=Texture/uncompressed, with 0Eh-byte list entries)
  002h 1      Zero (maybe Extra6byte as in type 5,6 Texture.BIN files)
  003h 2      Number of List entries (N) (always 4B0h in all three files)
  005h 2      Number of Texture Pages (usually 2)
  007h 2      Zero (maybe Unknown/Animation as in type 5,6 Texture.BIN files)
  009h N*0Eh  Polygon List (?,?,?,?,?,?, x1,y1, x2,y1, x1,y2, x2,y2)
  ...  40000h Texture Page uncompressed data (two pages, 20000h bytes each)
  ...  4      Checksum (sum of all of the above bytes)
 Texture.BIN files (type 5,6) (ENDTEXT*.BIN, FONT.BIN, FRONTEND.BIN,
 OUTRO.BIN, PUBLISH.BIN, STILL*.BIN, TB*.BIN, TK*.BIN, TPAGE213.BIN):
  000h 4      Zero (0)               (in TPAGE213.BIN: Size-8 of whole file)
  004h 2      Type (6=Texture/RLE16) (in TPAGE213.BIN: 5=Texture/uncompressed)
  006h 1      Extra6byte flag/size (0=None, 3=Extra6byte: TB*.BIN, TPAGE*.BIN)
  ...  (6)    Extra6byte data (unknown purpose, only present when [006h]=3)
  ...  2      Number of Polygon List entries (N)
  ...  2      Number of Texture Pages (usually 1) (in TK*_ENM.BIN: usually 2)
  ...  2      Number of Unknown Blocks (0=None, or 1,2,4,8)
  ...  (..)   Unknown Block(s), if any
  ...  2      Number of Animation Blocks (0=None)
  ...  (..)   Animation Block(s), if any
  ...  N*0Ch  Polygon List (?,?,?,?, x1,y1, x2,y1, x1,y2, x2,y2)  ;x,y or y,x?
  ...  (4)    Texture Page compressed size (T1) ;\only when [004h]=Type=6
  ...  (T1)   Texture Page compressed data      ;/
  ...  (4)    Texture Page compressed size (T2) ;\only when [004h]=Type=6
  ...  (T2)   Texture Page compressed data      ;/     and NumPages=2
  ...  20000h Texture Page uncompressed data    ;-only when [004h]=Type=5
  ...  4      Checksum (sum of all of the above bytes)
  Unknown Block(s):
  (Unknown purpose, each Unknown Block has the format shown below)
  000h 2    Unknown (looks like some index value, different for each entry)
  002h 2    Number of Unknown Items (eg. 1 or 2 or 4)
  004h ..   Unknown Items (NumItems*6 bytes) (three halfwords each?)
  Animation Block(s):
  (This is supposedly used to update portions of the Texture Page for
  animated textures, each Animation Block has the format shown below)
  000h 2    Number of Bitmap Frames in this Animation (usually 8)
  002h 2    Bitmap Width (in halfword units)
  004h 2    Bitmap Height
  006h 2    Unknown (1 or 3)                    ;\
  008h 2    Unknown (C10h, CC8h, 1E8h, or xxxh) ; maybe vram X,Y address?
  00Ah 2    Unknown (0)                         ;/
  00Ch ..   Bitmap Frames (Width*2*Height*NumFrames bytes, uncompressed)
  Croc 1 RLE16 compression:
  This is using unsigned little-endian 16bit LEN/DATA pairs, LEN can be:
   0000h..7FFFh --> Load one halfword, fill 1..8000h halfwords
   8000h..FFFFh --> Copy 1..8000h uncompressed halfwords
  BUG: Texture pages should be 20000h bytes (256x256 halfwords), but for
  whatever reason, the size of decompressed data can be 1FFEAh, 1FFF0h,
  1FFFAh, 20000h, or 20002h.
 Bytecode.BIN (inside of .WAD files):
  000h 4    Size of whole file
  004h ..   Whatever bytecode (starting with initial 16bit program counter?)
 Unknown.BIN (last 1-2 file(s) in EXCLUDE.WAD file):
  000h 4     Number of entries (N)
  004h N*18h Whatever
  ...  4     Checksum (sum of above bytes)
  Unknown purpose, retail version has one such file (with 0Ah entries), demo
  version has two such files (with 0Ah and 4Eh entries. The files start with:
  0A,00,00,00,00,00,00,00,00,00,64,00,00,00,EB,FF,...  ;demo+retail
  4E,00,00,00,00,00,64,00,00,00,50,00,00,00,64,00,...  ;demo
CROCFILE.DIR\*.MOD
 Demo version has one .MOD file in CROCFILE.DIR (retail has more such files):
  000h 2     Number of Models (N) (1 or more) (up to ECh exists)     ;\header
  002h 2     Flags (0 or 1)                                          ;/
  004h N*Var SubHeadersWithData  ;see below                          ;-data
  ...  4     Checksum (sum of all of the above bytes)                ;-checksum
  SubHeadersWithData(N*Var):
  004h 4     Radius                                                  ;\
  008h 48h   Bounding Box[9*8] (each 8byte are 4x16bit: X,Y,Z,0)     ; for each
  050h 4     Number of Vertices (V)                                  ; model
  054h V*8   Vectors (4x16bit: X,Y,Z,0)                              ;
  ...  V*8   Normals (4x16bit: X,Y,Z,0)                              ;
  ...  4     Number of Faces (F) (aka Polygons?)                     ;
  ...  F*14h Faces   (8x16bit+4x8bit: X,Y,Z,0,V1,V2,V3,V4, Tex/RGB)  ;
  ...  2     Number of collision info 1? (X)     ;\                  ;
  ...  2     Number of collision info 2? (Y)     ; only if           ;
  ...  X*2Ch Collision info 1?                   ; Flags.bit0=1      ;
  ...  Y*2Ch Collision info 2?                   ;/                  ;/
 There are further .MOD models inside of .WAD files, with slightly
 re-arranged entries (and additional reserved/garbage fields):
  000h 2     Number of Models (N) (1 or more) (up to ECh exists)     ;\
  002h 2     Flags (0 or 1)                                          ; header
  004h 4     Reserved/garbage (usually 224460h) (or 22C9F4h/22DF54h) ;/
  008h (4)   Number of Models WITH Data arrays (M)                   ;\
  00Ch (M*2) Model Numbers WITH Data arrays (increasing, 0..N-1)     ; ext.hdr
  ...  (..)  Padding to 4-byte boundary (garbage, usually=M)         ;/
  ...  N*68h Subheader(s)   ;see below                               ;-part 1
  ...  N*Var DataArray(s)   ;see below                               ;-part 2
  Subheaders(N*68h):
  000h 4     Radius                                                  ;\
  004h 48h   Bounding Box[9*8] (each 8byte are 4x16bit: X,Y,Z,0)     ; for each
  04Ch 4     Number of Vertices (V)                                  ; model
  050h 4     Reserved/garbage (usually 0022xxxxh)                    ;
  054h 4     Reserved/garbage (usually 0022xxxxh)                    ;
  058h 4     Number of Faces (F) (aka Polygons?)                     ;
  05Ch 4     Reserved/garbage (usually 0022xxxxh)                    ;
  060h 2     Number of collision info1? (X)                          ;
  062h 2     Number of collision info2? (Y)                          ;
  064h 4     Reserved/garbage (usually 0022xxxxh) or xxxxxxxxh)      ;/
  DataArrays(N*Var) with sizes V,F,X,Y from corresponding Subheader:
  (if ext.hdr is present, then below exists only for models listed in ext.hdr)
  000h V*8   Vectors (4x16bit: X,Y,Z,0)                              ;\
  ...  V*8   Normals (4x16bit: X,Y,Z,0)                              ; for each
  ...  F*14h Faces   (8x16bit+4x8bit: X,Y,Z,0,V1,V2,V3,V4, Tex/RGB)  ; model
  ...  X*2Ch Collision info 1?                                       ;
  ...  Y*2Ch Collision info 2?                                       ;/
 The ext.hdr mentioned above exists only in some .MOD files (usually in one of
 the last chunks of MP*.WAD). Files with ext.hdr have N>1, Flags=1 (but files
 without ext.hdr can also have those settings). Files with ext.hdr do usually
 have uncommon garbage values at hdr[4], which isn't too helpful for detection.
 The only way to detect models with ext.hdr seems to be to check if the ext.hdr
 contains valid increasing entries in range 0..N-1.
 WAD's that do contain a model with ext.hdr do usually also contain an extra
 100h-byte file, that file contains N bytes for model 0..N-1 (plus zeropadding
 to 100h-byte size), the bytes are supposedly redirecting models without Data
 Arrays to some other data source.
 The 100h-byte files don't have any header or checksum, they contain up to 9Ch
 entries (so there's always some zeropadding to 100h), the existing 100h-byte
 files contain following values in first 4 bytes (as 32bit value):
  04141401h, 0C040017h, 01010101h, 09030503h, 0A0B0A0Bh, 03020102h, 0C060900h,
  00060501h, 04040201h, 01010203h, 01030201h, 05000302h, 0C040317h, or Zero.
  To distinguish from other files: BIN/MAP files start with a 4-byte aligned
  Size value; if Size=0 or (Size AND 3)>0 or Size>RemainingSize then it's
  probably a 100h-byte file. Best also check if last some bytes are zeropadded.
 Exceptions:
  Retail MP090..MP100_*.WAD has model with ext.hdr, but no 100h-byte file
  Demo MP041_00.WAD has model with ext.hdr, with zerofilled 100h-byte file
 Note: Some models have ALL models listed in ext.hdr (which is about same as
 not having any ext.hdr at all; except, they ARE bundled with 100h-byte file).
CROCFILE.DIR\MP*.DEM
 Some (not all) MP*.WAD files are bundled with MP*.DEM files, supposedly
 containing data for demonstration mode. There are two versions:
  demo version:   size 2584h (9604 decimal) (some files with partial checksum)
  retail version: size 0E10h (3600 decimal) (without checksum)
CROCFILE.DIR\CROCWALK.ANI:
 Animation data, there is only one such file in CROCFILE.DIR:
  000h 2       Value (100h)
  002h 2       Number of Triggers (T) (2)
  004h (T*2)   Trigger List (with 2x8bit entries: FrameNo, TriggerID)
  ...  ..      Probably, Padding to 4-byte boundary (when T=odd)
  ...  4       Number of entries 1 (X)
  ...  X*18h   Whatever Array 1
  ...  4       Number of entries 2 (Y) (usually/always 64h)
  ...  X*Y*4   Whatever Array 2
  ...  4       Number of entries 3 (Z) (usually/always 0Ah)
  ...  X*Z*18h Whatever Array 3
 There are further .ANI files inside of .WAD files:
  000h 2       Value (100h or 200h)                         ;Animation Speed?
  002h 2       Number of Triggers (T) (0, 1, 2, 3, 5, or 9)
  004h 4       Garbage/Pointer (usually 224460h) (or zero)
  008h 4       Number of entries 1 (X) (1 or more)          ;Num Frames
  00Ch 4       Garbage/Pointer (usually 22C9F4h) (or 224460h or 22DF54h)
  010h 4       Number of entries 2 (Y) (usually 64h) (or 0) ;Num Vertices (?)
  014h 4       Garbage/Pointer
  018h 4       Number of entries 3 (Z) (usually 0Ah) (or 6 or 9)
  01Ch 4       Garbage/Pointer
  020h (T*2)   Trigger List (with 2x8bit entries: FrameNo, TriggerID)
  ...  ..      Padding to 4-byte boundary (garbage, usually=X)
  ...  X*18h   Whatever Array 1
  ...  X*4     Garbage/Pointers (0021EE74h,0021EE74h,xxx,...)
  ...  X*Y*4   Whatever Array 2   ;Vertex 3x10bit?            ;only if Y>0
  ...  (X*4)   Garbage/Pointers (0021EE74h,0021EE74h,xxx,...) ;only if Y>0
  ...  X*Z*18h Whatever Array 3
CROCFILE.DIR\TCLD.CVG:
 There is only one such file in CROCFILE.DIR:
  000h 4      Size-8 of whole file
  004h 4      Unknown (0)
  008h 4      Unknown (1)
  00Ch ..     SPU-ADPCM data
  ...  4      Checksum (sum of all of the above bytes)
 There are further .CVG files inside of .WAD files, these consist of two
 parts; 0Ch-byte Headers (in the data file area), and raw SPU-ADPCM data
 (in the spu-adpcm data area at end of the .WAD file):
  Header(0Ch):
  000h 4      Size+8 of data part
  004h 4      Unknown (0)
  008h 4      Unknown (0 or 1)
  Data(xxxx0h):
  000h ..     SPU-ADPCM data (starting with sixteen 00h bytes)
STRAT.DIR (in retail version with extra copy in CROCFILE.DIR\STRAT.DIR):
 This file contains a list of filenames for files inside of .WAD files, but
 it does NOT tell where those files are (in which WAD at which offset).
  000h 4     Number of Entries (N)
  004h N*xxh File List (retail=14h bytes, or demo=18h bytes per entry)
  ...  4     Checksum (sum of all of the above bytes)
 List entries are:
  demo:   entrysize=18h  ;Filename(0Ch)+Size(4)+Zeroes(8)
  retail: entrysize=14h  ;Filename(0Ch)+        Zeroes(8)
 The list contains hundreds of filenames, with following extensions:
  *.BIN  byte-code strategies
  *.MOD  models
  *.ANI  animations
  *.CVG  spu-adpcm voice data
 These "filenames" seem to be actually solely used as "memory handle names":
  MemoryHandle(#1) = LoadFile("FILENAME.BIN")  ;<-- names NOT used like this
  MemoryHandle("FILENAME.BIN") = LoadFile(#1)  ;<-- names used like this
PACK*.STR (retail version only):
  Huge files with XA-ADPCM audio data
MAGMUS.STR (demo version only):
  Huge mis-mastered 24Mbyte file (contains several smaller XA-ADPCM blocks,
  accidentally stored in 800h-byte FORM1 data sectors, instead of 914h-byte
  FORM2 audio sectors).
ARGOLOGO.STR, FOXLOGO.STR
  MDEC movies
COPYRIGHT.IMG, WARNING.IMG
  Raw bitmaps (25800h bytes, uncompressed, 320x240x16bpp)
CUTS\*.AN2 (looks like cut-scenes with polygon-streaming):
CDROM File Video Polygon Streaming
Note: MOD/ANI files contain many Reserved/Garbage/Pointer entries which are replaced by pointers after loading (the initial values seem to have no purpose; they are aften set to constants with value 002xxxxxh which could be useful for file type detection, but they vary in different game versions).
See also:
https://github.com/vs49688/CrocUtils/ (for PC version, PSX support in progress)


CDROM File Archive Croc 2 (DIR, WAD, etc.)

Croc 2 (MagDemo22: CROC2\CROCII.DIR\T*.WAD+DEM)
Disney's The Emperor's New Groove (MagDemo39: ENG\KINGDOM.DIR\T*.WAD+DEM)
Disney's Aladdin in Nasira's Rev. (MagDemo46: ALADDIN\ALADDIN.DIR\T*.WAD+DEM)
Alien Resurrection, and Harry Potter 1 and 2 ... slightly different format?
Overall .WAD format:
  000h 4      Total Filesize+/-xx (-4 or +800h or +1800h)
  004h 4+4+.. XSPT Chunk        ;Textures
  ...  4+4+.. XSPS Chunk        ;SPU-ADPCM Sound (if any, not in all .WAD's)
  ...  4+4+.. XSPD Chunk        ;...whatever Data...?
  ...  4+4    DNE Chunk         ;End marker (in Harry Potter: with data!)
XSPT Chunk (Textures):
  000h 4        Chunk Name "XSPT" (aka TPSX backwards)
  004h 4        Chunk Size (excluding 8-byte Name+Size)
  008h 4        Chunk Flags (02h or 06h or 0Eh)  ;02h in Croc 2
  00Ch (20h)    Name (eg. "Default new map", zeropadded)  ;\if Flags bit2=1
  ...  (804h)   Unknown ... SAME as in XSPD chunk !!!     ;/
  ...  4        Number of List 1 entries (N1) (xxh..xxxh) ;\
  ...  4        Number of Texture Pages (1..4)            ; List 1 and NumPages
  ...  N1*0Ch   List 1 Whatever (6B 2F xx 00..)           ;/
  ...  4        Number of List 2 entries (N2) (0..xxh)    ;\
  ...  4        Unknown (2 or 7)                          ; List 2
  ...  N2*04h   List 2 Whatever (halfwords?) (if N2>0)    ;/
  ...  (5*C00h) Whatever, 5*C00h, Palette+Stuff?          ;-if Flags bit3=1
  ...  ..       RLE16 compressed Texture Pages            ;-Texture bitmap
 RLE16 Texture notes:
  Compressed data consists of signed little-endian 16bit LEN+DATA pairs:
   LEN=0000h        --> invalid/unused
   LEN=0001h..7FFFh --> copy LEN halfwords from src
   LEN=8000h..FFFFh --> load ONE halfword as fillvalue, fill -LEN halfwords
  Compressed size is everything up to end of XSPT chunk
  Decompressed size is 20000h*NumTexturePages (=20000h,40000h,60000h or 80000h)
  That is: Width=256 halfwords, height 256*NumTexturePages lines. There seems
  to be only one RLE16 compression block for all Texture Pages, rather than one
  RLE16 block for each Page.
 BUG #1: Decompressed data in Aladding/Emperor does often contain only
  1FFFEh,3FFFEh,5FFFEh,7FFFEh bytes (the decompressed data has correct size
  when appending ONE halfword with random/zero value).
 BUG #2: Compressed data in Croc 2 ends with a RLE16 length value (-LEN), but
  lacks the corresponding RLE16 filldata (the decompressed data is 7FFFEh when
  filling those LEN halfwords with random/zero values).
XSPS Chunk (SPU-ADPCM Sound) (if any, isn't present in all .WAD files):
  000h 4      Chunk Name "XSPS" (aka SPSX backwards)       ;\
  004h 4      Chunk Size (excluding 8-byte Name+Size)      ; header
  008h 4      Chunk Flags (0 or 3 or 7)                    ;/
  00Ch 4      Number of Sounds (N1) (1..xxh)               ;\always present
  010h N1*14h Sound List                                   ;/
  ...  (4)     VAB/VH Size                                 ;\if Flags=3 or 7
  ...  (..)    VAB/VH Header                               ;/   (bit0 or bit1?)
  ...  (4)     Unknown (2 or 4)                            ;-if Flags=3 or 7
  ...  (4)     Whut (N2)                                   ;\if Flags.bit2=1
  ...  (N2*10h) Whut List (4 words: xxh,10h,xxxx00h,xxxx0h);/
  ...  4       Size of all Part 1 Sound Data blocks               ;\always
  ...  ..      SPU-ADPCM Sound Data (referenced from Sound List)  ;/
  ...  (4)     Size of all Part 2 Sound Data blocks (+8)          ;\if Flags=
  ...  (..)    SPU-ADPCM Sound Data (referenced from Sound List?) ;    3 or 7
  ...  (8)     Zero                                               ;/
 Sound List entries (as in FESOUND.WAD):
  000h 4    Sample Rate in Hertz (AC44h=44100Hz, 5622h=22050Hz, 3E80h=16000Hz)
  004h 2    Sample Rate Pitch    (1000h=44100Hz, 0800h=22050Hz, 05CEh=16000Hz)
  006h 2    Unknown (7Fh)
  008h 4    Unknown (1)          (1)               (8)
  00Ch 4    Unknown (42008Fh)    (1FC0001Fh)       (40008Fh)
  010h 4    Filesize             (xxx0h)           (xxx0h)
XSPD Chunk:
  000h 4     Chunk Name "XSPD" (aka DPSX backwards)
  004h 4     Chunk Size (excluding 8-byte Name+Size)
  008h 4     Flags-and/or-other stuff ? (eg. 00000094h or 0A801094h)
  00Ch 804h  Unknown ... SAME as in XSPT chunk !!!
  810h ..    Unknown ...
DNE Chunk (End marker):
  000h 4     Chunk Name " DNE" (aka END backwards)
  004h 4     Chunk Size (0)           (except, in Harry Potter: nonzero)
  ...  ..    Data (usually none such) (except, in Harry Potter: with data!)
Additional DEM files (always 1774h bytes) (if any, not all .WAD's have .DEM's):
  000h 4     Number of entries (N) (always 2EEh, aka 750 decimal)
  004h N*8   Whatever entries... maybe data for demonstration mode?
See also:
http://wiki.xentax.com/index.php/Argonaut_WAD


CDROM File Archive Headerless Archives

Headerless Archives
Some games use files that contain several files badged together. For example,
  PSX Resident Evil 2, COMMON\DATA\*.DIE contains TIM+VAB badged together
  PSX Resident Evil 2, COMMON\DATA\*.ITP contains 1000h-byte aligned TIMs
  Blaster Master, DATA\MENU\*\*.PRT contains three smaller TIMs badged together
  Blaster Master, DATA\MENU\*\*.BG contains three bigger TIMs badged together
  Misadventures of Tron Bonne, KATWA\*.BIN contains headerless archives (with TIMs and audio)
  Headerless BSS files contain several BS files with huge padding inbetween
To some level one could detect & resolve such cases, eg. TIM contains information about the data block size(s), if the file is bigger, then there may be further file(s) appended.
Some corner cases may be: Files with odd size may insert alignment padding before next file. Archives with 800h-byte filesize resolution will have zeropadding (or garbage) if the real size isn't a mutiple of 800h. Regardless of that two cases, archives may use zeropadding to 800h-byte or even 10000h-byte boundaries (as workaround one could skip zeroes until reaching a well-aligned nonzero word or double word (assuming that most files start with nonzero values; though not always, eg. raw ADPCM or raw bitmaps).


CDROM File Compression

Compressed Bitmaps
  .BS used by several games (and also in most .STR videos)
  .GIF used by Lightspan Online Connection CD
  .JPG used by Lightspan Online Connection CD
  .BMP with RLE4 used by Lightspan Online Connection CD (MONOFONT, PROPFONT)
  .BMP with RLE8+Delta also used by Online Connection CD (PROPFONT\ARIA6.BMP)
  .PCX with RLE used by Jampack Vol. 1 (MDK\CD.HED\*.pcx)
  .PCX with RLE used by Hot Wheels Extreme Racing (MagDemo52: US_01293\MISC\*)
  .PCX with RLE used by Metal Gear Solid (slightly corrupted PCX files)

Compressed Audio
  .XA uses XA-ADPCM (and also used in .STR videos)
  .VAG .VB .VAB uses SPU-ADPCM

Compressed Files
CDROM File Compression LZSS (Moto Racer 1 and 2)
CDROM File Compression LZSS (Dino Crisis 1 and 2)
CDROM File Compression LZSS (Serial Experiments Lain)
CDROM File Compression ZOO/LZSS
CDROM File Compression Ulz/ULZ (Namco)
CDROM File Compression SLZ/01Z (chunk-based compressed archive)
CDROM File Compression LZ5 and LZ5-variants
CDROM File Compression PCK (Destruction Derby Raw)
CDROM File Compression GT-ZIP (Gran Turismo 1 and 2)
CDROM File Compression GT20 and PreGT20
CDROM File Compression HornedLZ
CDROM File Compression LZS (Gundam Battle Assault 2)
CDROM File Compression BZZ
CDROM File Compression RESOURCE (Star Wars Rebel Assault 2)
CDROM File Compression TIM-RLE4/RLE8
CDROM File Compression RLE_16
CDROM File Compression PIM/PRS (Legend of Mana)
CDROM File Compression BPE (Byte Pair Encoding)
CDROM File Compression RNC (Rob Northen Compression)
CDROM File Compression Darkworks
CDROM File Compression Blues
CDROM File Compression Z (Running Wild)
CDROM File Compression ZAL (Z-Axis)
CDROM File Compression EA Methods
CDROM File Compression ZIP/GZIP/ZLIB (Inflate/Deflate)
CDROM File Compression LArc/LHarc/LHA (LZS/LZH)
CDROM File Compression UPX
CDROM File Compression LZMA
CDROM File Compression FLAC audio
Some other archvies that aren't used by any PSX games, but, anyways...
CDROM File Compression ARJ
CDROM File Compression ARC
CDROM File Compression RAR
CDROM File Compression ZOO
CDROM File Compression nCompress.Z
CDROM File Compression Octal Oddities (TAR, CPIO, RPM)
CDROM File Compression MacBinary, BinHex, PackIt, StuffIt, Compact Pro

Compressed Archives
Some Archives have "built-in" compression.
CDROM File Archive WAD (Doom)
CDROM File Archive BIGFILE.DAT (Gex - Enter the Gecko)


CDROM File Compression LZSS (Moto Racer 1 and 2)

Moto Racer 1 ("LZSS" with len+2) (MagDemo03: MRDEMO\IMG\*.TIM)
Moto Racer 2 ("LZSS" with len+3) (MagDemo16: MR2DEMO\IMG\*.TIM and .TPK)
  000h 4     ID "LZSS"
  004h 4     Decompressed Size
  008h ..    Compressed Data
This LZSS variant is unusually using 6bit len and 10bit disp. And, there are two versions: Moto Racer 1 uses len+2, and Moto Racer 1 uses len+3. There is no version information in the header, one workaround is to decompress the whole file with len+2, and, if the resulting size is too small, retry with len+3. Observe that the attempt with len+2 may cause page faults (eg. if the sum of len values is smaller than disp; so allocate some extra space at begin of compression buffer, or do error checks),
  @@collect_more:
   flagbits=[src]+100h, src=src+1    ;8bit flags
  @@decompress_lop:
   flagbits=flagbits SHR 1
   if zero then goto @@collect_more
   if carry=1 then
     [dst]=[src], dst=dst+1, src=src+1
   else
     disp=([src]+[src+1]*100h) AND 3FFh, len=([src+1]/4)+2_or_3, src=src+2
     if disp=0 then goto @@decompress_done
     for i=1 to len, [dst]=[dst-disp], dst=dst+1, next i
   endif
   goto @@decompress_lop
  @@decompress_done:
   ret


CDROM File Compression LZSS (Dino Crisis 1 and 2)

Dino Crisis 1 and 2 (PSX\DATA\*.DAT and *.DBS and *.TEX, File type 7,8)
Dino Crisis LZSS Decompression for files with type 7 and 8:
  @@collect_more:
   flagbits=[src]+100h, src=src+1    ;8bit flags
  @@decompress_lop:
   flagbits=flagbits SHR 1
   if zero then goto @@collect_more
   if carry=1 then
     [dst]=[src], dst=dst+1, src=src+1
   else
     disp=[src]+[src+1]*100h AND FFFh, len=[src+1]/10h+2, src=src+2
     if disp=0 then error
     for i=1 to len, [dst]=[dst-disp], dst=dst+1, next i
   endif
   if src<src_end then goto @@decompress_lop
   ret
The compressed file & archive header don't contain any info on the decompressed size (except, for compressed bitmaps, the archive header does contain width/height entries, nethertheless the decompressed file is usually BIGGER then width*height*2 (it can contain padding, plus 8 bytes).


CDROM File Compression LZSS (Serial Experiments Lain)

Serial Experiments Lain is using LZSS compression for TIMs (in SITEA.BIN, SITEN.BIN), and for Transparency Masks (in LAPKS.BIN).

Serial Experiments Lain (7MB SITEA.BIN on Disc 1, 5MB SITEB.BIN on Disc 2)
These are huge 5-7 Mbyte files with hundreds of chunks. Each chunk contains one compressed TIM.
 Each chunk is having this format:
  000h 4     Chunk ID "napk"
  004h 4     Decompressed size
  008h ..    LZSS compressed TIM data
  ...  ..    Zeropadding to 800h-byte boundary
Unknown how the game is accessing chunks (there is no chunk size info, so one would need read the whole file (or at least first 4-byte of each 800h-byte sector) for finding chunks with ID="napk").

Serial Experiments Lain (LAPKS.BIN on Disc 1 and 2)
This a huge 14Mbyte file with 59 chunks. Each chunk contains one or more 24bpp .BS images with black background (the images in each chunk are forming a short animation sequence; width/height may vary because all images are cropped to rectangles containing non-black pixels).
 Each chunk is having this format:
  000h 4     Chunk ID "lapk"
  004h 4     Chunk size (excluding 8-byte chunk header, excluding zeropadding)
  008h 4     Number of Files in this Chunk (N)
  00Ch N*0Ch File List
  ...  ..    File Data (bitmaps in .BS v0 format with uncommon headers)
  ...  ..    Zeropadding to 800h-byte boundary
 File List entries:
  000h 4     Offset in bytes (zerobased, from begin of File Data area)
  004h 2     Bitmap Width/2 + some 3bit value in LSBs?
  006h 2     Bitmap Height
  00Ch 4     Zero
 File Data (bitmaps in .BS v0 format with uncommon headers):
  000h 2     Bitmap Width
  002h 2     Bitmap Height
  004h 2     Quant for Y1,Y2,Y3,Y4
  006h 2     Quant for Cr,Cb
  008h 4     Size of compressed BS Bitstream plus 4 ;Transparency at [008h]+0Ch
  00Ch 2     Size/2 of MDEC data (after huffman decompression, without padding)
  00Eh 2     BS Version (0) (actually MSBs of above Size, but it's always 0)
  010h ..    BS Bitstream with DC and AC values (Huffman compressed MDEC data)
  ...  4     Transparency Mask Decompressed Size (Width*Height*2/8) (=2bpp)
  ...  ..    Transparency Mask LZSS-compressed data
BUG: The chunksize at C3A800h is set to 4C614h but should be 4D164h (the next chunk starts at C88000h).
Unknown how the game is accessing chunks (crawling all chunks would be exceptionally slow due to CDROM seek times, and won't work with the BUGGED chunksize).

Decompression function
This LZSS variant is unusually using 8bit len and 8bit disp.
   dst_end=dst+[src], src=src+4   ;decompressed size
  @@collect_more:
   flagbits=([src] SHL 24)+800000h, src=src+1    ;8bit flags
  @@decompress_lop:
   if dst=dst_end then goto @@decompress_done
   flagbits=flagbits SHL 1    ;32bit shift with carry-out/zeroflag
   if zero then goto @@collect_more
   if carry=0 then
     [dst]=[src], dst=dst+1, src=src+1
   else
     disp=[src]+1, len=[src+1]+3, src=src+2
     for i=1 to len, [dst]=[dst-disp], dst=dst+1, next i
   endif
   goto @@decompress_lop
  @@decompress_done:
   ret


CDROM File Compression ZOO/LZSS

Jarret & LaBonte Stock Car Racing (MagDemo38: WTC\*.ZOO)
  0000h 4     Decompressed Size                          ;\1st sector
  0004h 7FCh  Garbage                                    ;/
  0800h 4     Decompressed Size (same as above)          ;\2nd sector
  0804h 7FCh  LZSS compressed data, part 1               ;/
  1000h 800h  LZSS compressed data, part 2               ;-3rd sector
  1800h 800h  LZSS compressed data, part 3               ;-4th sector
  ...   ..    etc.
Note: The file format & compression method is unrelated to ZOO archives (to distinguish between the formats: ZOO archives have [0014h]=FDC4A7DCh, the ZOO/LZSS files have [0014h]=Garbage).
The decompressed WTC\*.ZOO files can contain large TIMs, or chunk-based archives (where each chunk can contain one or more small TIMs), or other stuff.

Decompression function
  decompress_file:
   if LittleEndian32bit[src+14h]=FDC4A7DCh then goto error ;refuse ZOO archives
   if LittleEndian32bit[src]<>LittleEndian32bit[src+800h] then goto error
   curr=src+800h
   src=curr+4
  @@sector_lop:
   call decompress_sector
   curr=curr+800h
   src=curr
   if src<src_end then goto @@sector_lop
   ret
  ;---
  decompress_sector:
  @@collect_more:
   flagbits=([src] SHL 24)+800000h, src=src+1    ;8bit flags
  @@decompress_lop:
   flagbits=flagbits SHL 1    ;32bit shift with carry-out/zeroflag
   if zero then goto @@collect_more
   if carry=0 then
     [dst]=[src], dst=dst+1, src=src+1
   else
     disp=[src]*100h+[src+1], src=src+2
     if disp=FFFFh then goto @@decompress_done
     len=(disp/800h)+3, disp=(disp AND 7FFh)+1
     for i=1 to len, [dst]=[dst-disp], dst=dst+1, next i
   endif
   goto @@decompress_lop
  @@decompress_done:
   ret


CDROM File Compression Ulz/ULZ (Namco)

Ulz/ULZ uses fairly normal LZSS compression, unusually with variable Len/Disp ratio, three separate data streams (flg/lz/dta), and rather weird end check in version=0.

Ulz Format (Ace Combat 3 Electrosphere, Namco)
Ulz Format (Klonoa, MagDemo08: KLONOA\FILE.IDX\*)
  000h 4    ID ("Ulz",1Ah) (parts lowercase)
  004h 3    Decompressed Size in bytes
  007h 1    Version (0 or 2)
  008h 3    Offset to Uncompressed data   <-- reportedly can be 0 in version=0?
  00Bh 1    Number of Disp bits (DispBits=N, LenBits=16-N) (usually 0Ah..0Dh)
  00Ch 4    Offset to Compressed data
  010h ..   Compression Flags   (32bit entries)
  ...  ..   Uncompressed data   (8bit entries)
  ...  ..   Zeropadding to 4-byte boundary
  ...  ..   Compressed data     (16bit entries)
Most files use version=2 (eg. US:ACE.BPH\0006h\000Fh contains DOT1 with TIMs).
Some files use version=0 (eg. US:ACE.BPH\0048h\*\* contains TIMs).

ULZ Format (Time Crisis, Namco)
  000h 4    ID ("ULZ",1Ah) (all uppercase)
  004h 2    Zero
  006h 1    Version (0 or 2)
  007h 1    Number of Disp bits (DispBits=N, LenBits=16-N) (usually 0Ah..0Dh)
  008h 4    Offset to Uncompressed data
  00Ch 4    Offset to Compressed data
  010h 4    Decompressed Size in bytes
  014h ..   Compression Flags   (32bit entries)
  ...  ..   Uncompressed data   (8bit entries)
  ...  ..   Zeropadding to 4-byte boundary
  ...  ..   Compressed data     (16bit entries)
Most files use version=2 (eg. EUR: AD*\TIM*.FHT\*)
Some files use version=0 (eg. EUR: AD4\TIM0_0.FHT\0018h, 0019h)

Ulz/ULZ Decompression Function
  if [src+00h]="Ulz",1Ah then
    version   = Byte[src+07h]
    disp_bits = Byte[src+0Bh]
    dst_end   = LittleEndian24bit[src+04h] + dst
    src_dta   = LittleEndian24bit[src+08h] + src
    src_lz    = LittleEndian32bit[src+0Ch] + src
    src_flg   = src + 10h
    add_len   = 3
    flg_1st   = 31  ;process flag bit31 first
  if [src+00h]="ULZ",1Ah then
    version   = Byte[src+06h]
    disp_bits = Byte[src+07h]
    src_dta   = LittleEndian32bit[src+08h] + src
    src_lz    = LittleEndian32bit[src+0Ch] + src
    dst_end   = LittleEndian32bit[src+10h] + dst
    src_flg   = src + 14h
    add_len   = 2
    flg_1st   = 0   ;process flag bit0 first
  collected = 80000000h   ;initially empty, plus stop bit
 @@decompress_lop:
  if version=2 AND dst=dst_end then goto @@decompress_done
  flag = collected AND 80000000h
  collected=collected*2
  if collected=0
    collected = LittleEndian32bit[src_flg], src_flg=src_flg+4
    if flg_1st=0 then ReverseBitOrder(collected)  ;or make custom/faster code
    flag = collected AND 80000000h
    if version=0 AND collected=0 then goto @@decompress_done
    if version=0 then collected=collected*2       ;<-- has implied stop bit
    if version=2 then collected=collected*2 + 1   ;<-- shift-in stop bit
  if flag=0     ;compressed
    disp = LittleEndian16bit[src_lz], src_lz=src_lz+2
    len  = (disp SHR disp_bits) + add_len
    disp = (disp AND ((1 shl disp_bits)-1)) + 1
    for i=1 to len, [dst]=[dst-disp], dst=dst+1, next i
  else          ;uncompressed
    [dst]=[src_dta], dst=dst+1, src_dta=src_dta+1
  goto @@decompress_lop
 @@decompress_done:
  ret
Note: Version=2 has 32 flags per 32bit. Version=0 has 31 flags and 1 stop bit per 32bit, plus 32 null bits at end of data (which is all rather wasteful, there's no good reason to use version=0).


CDROM File Compression SLZ/01Z (chunk-based compressed archive)

SLZ/01Z files are Chunk-based archives with one or more compressed chunk(s).
Used by Hot Shots Golf 2 (retail: DATA\F0000.BIN\*, MagDemo31/42: HSG2\MINGOL2.BIN\*)

SLZ/01Z chunk headers
The archive consists of Chunk(s) in following format:
  000h 3     ID (either "01Z" or "SLZ", both are used)
  003h 1     Method (00h=Uncompressed, 01h=LZSS, 02h=LZSS+FILL)
  004h 4     Compressed size (SIZ) (same as decompressed when Method=0)
  008h 4     Decompressed size
  00Ch 4     Distance to next chunk, if any (SIZ+10h+Align4, or 0=None)
  010h SIZ   Compressed data

SLZ/01Z decompression function:
   method=byre[src+3]
   len=word[src+8]
   src=src+10h
   if method=0 then
     for i=1 to len, [dst]=[src], dst=dst+1, src=src+1, next i
     goto @@decompress_done
   dst_end = dst+len
  @@collect_more:
   flagbits=[src]+100h, src=src+1    ;8bit flags
  @@decompress_lop:
   if method=2 AND dst=dst_end then goto @@decompress_done
   flagbits=flagbits SHR 1
   if zero then goto @@collect_more
   if carry=1 then
     [dst]=[src], dst=dst+1, src=src+1
   else
     disp=([src]+[src+1]*100h) AND 0FFFh, len=([src+1]/10h)+3, src=src+2
     if method=1 AND disp=0 then goto @@decompress_done
     if method=2 AND len=12h then     ;special fill mode...
       len=disp/100h+3, val=disp AND FFh               ;len=3..12h
       if len=3 then len=val+13h, val=[src], src=src+1 ;len=13h..112h
       for i=1 to len, [dst]=val, dst=dst+1, next i    ;len=4..112h
     else
       for i=1 to len, [dst]=[dst-disp], dst=dst+1, next i
   endif
   goto @@decompress_lop
  @@decompress_done:
   ret


CDROM File Compression LZ5 and LZ5-variants

Original LArc LZ5 (method "-lz5-")
LZ5 was used by LArc compression tool from 1988/1989, decompression is also supported by LHarc/LHA. LZ5 is basically LZSS compression, but with some oddities:
  LZ5 is often implemented with a ringbuf (instead of actual sliding window)
  LZ5 uses absolute ringbuf indices (instead of relative sliding dest indices)
  LZ5 requires the ringbuf to be initially prefilled with constants
  LZ5 ringbuf is 1000h bytes tall and starts with write index FEEh
LArc was discontinued in 1989, but LZ5-variants have been kept used on PSX and Nintendo DSi; those variants are just using the raw compression, without LArc archive headers.

DSi Dr. Mario (DSiware, Nintendo/Arika, 2008-2009)
 INFO.DAT
  encrypted directory with filename, offset and compressed/uncompressed size
 GAME.DAT
  000h 4   ID "ALZ1"
  004h ... ALZ1 Compressed data (with size as defined in INFO.DAT)
  ...  4   ID "ALZ1"
  ...  ... ALZ1 Compressed data (with size as defined in INFO.DAT)
  ...

PSX Final Fantasy VII (FF7)
ALZ1 compression is used in various folders (ENEMY*, STAGE*, STARTUP, MAGIC, FIELD, MINI, MOVIE, WORLD) with various filename extensions (.LZS .BSX .DAT .MIM .TIZ .PRE .BSZ .TXZ).
  000h 4   Compressed Size       ;=Filesize-4
  004h ..  ALZ1 Compressed data (Filesize-4 bytes)
Detection can be more or less reliably done by checking [000h]=Filesize-4, one could also check the filename extensions, although .DAT doesn't qualify as unique extension.
The file doesn't contain any info on the decompressed size, so one cannot know the decompression buffer size without first decompressing the file.
Note: For whatever reason, the game does also have one GZIP compressed file (BATTLE\TITLE.BIN).

PSX Final Fantasy VIII (FF8)
About same as FF7, but detection is less reliable because there are no filenames or extensions, and the file header is somewhat randomly set to [000h]=(Filesize-4)+0..7, unknown why, maybe it's allocating dummy bytes to last some compression flags.
  000h 4   Compressed Size+0..7  ;=(Filesize-4)+0..7
  004h ..  ALZ1 Compressed data (Filesize-4 bytes)
ALZ1 is used in four Root files (0001h,0002h,0017h,001Ah), and in many Field files, and maybe in further files elsewhere.

PSX Ultimate Fighting Championship (MagDemo38: UFC\CU00.RBB\383h\*)
  000h 8     ID "00zLATAD" (aka DATALz00 backwards)               ;\PreHeader
  008h 4     Total Filesize excluding PreHeader+Padding (SIZ+0Ch) ;/
  00Ch 4     Unknown (always 1000h)                               ;\
  010h 4     Compressed data size                       (SIZ)     ; Header
  014h 4     Decompressed data size                               ;/
  018h SIZ   zLATAD Compressed data                               ;-Data
  ...  ..    Padding to 4-byte boundary                           ;-Padding

Ninja (MagDemo13: NINJA\LOADPICS\*.PAK and NINJA\VRW\FOREST.VRW\*)
  000h 8     ID "VRAM-WAD"
  008h 4     Compressed size (Filesize-Padding-10h)
  00Ch 4     Decompressed size       (18000h, 28000h, 40000h bytes)
  010h ..    VRAMWAD Compressed data (192x256, 320x256, 512x256 halfwords)
  ...  (..)  Padding to 4-byte boundary (if any, in files in .VRW archives)
Observe that Ninja is using the same ID="VRAM-WAD" for .PAK files and .VRW archives (if [008h]=Filesize-Padding-10h then it's a compressed .PAK file, otherwise it's a .VRW archive; whereas, those .VRW archives do themselves contain several .PAK files).

PSX Power Spike (MagDemo43: POWER\GAME.IDX\*.BIZ)
BIZ compression is used in BIZ archives (which are nested in IDX/HUG archive). The compressed & decompressed size is stored in the BIZ archive.
Note: Power Spike 20h-filled initial BIZ ringbuf is required for sky pixels in:
  MagDemo43: POWER\GAME.IDX\PERSOS\PSX\CUSTOM\\TEXTURE\NFIELD.BIZ\LPORJ.PSI

PSX Army Men Air Attack 2 (MagDemo40: AMAA2\*.PCK\*.PAK)
SCRATCH compression is used in PAK archives (which are nested in PCK archive). The compressed & decompressed size is stored in the PAK archive.
Note: The decompressor uses half of the 1Kbyte Scratchpad RAM at 1F800000h as ringbuf (hence the name and unusual small 200h-byte ringbuf size).

Alice in Cyberland (ALICE.PAC\*.FA2)
  000h ..   FA2 Compressed .FA archive
The decompressor is at 80093A3Ch (but the code isn't permanently in memory), and it's by far one of the worst decompression functions in compilerland.

Decompression
   DEFAULT = ALZ1 or BIZ or LZ5
   if DEFAULT then wr=0FEEh, mask=FFFh    ;\
   if VRAMWAD then wr=0FEEh, mask=FFFh    ; initial ringbuf write index
   if zLATAD  then wr=0000h, mask=FFFh    ; and ringbuf mask (size-1)
   if SCRATCH then wr=01BEh, mask=1FFh    ;
   if FA2     then wr=00EFh, mask=0FFh    ;/
   if FA2     then len2=0
   initialize_ringbuf_content (see below)
   numbits=0
  @@decompress_lop:
   if dst>=dst.end then goto @@decompress_done
   if numbits=0
     flagbits=[src], numbits=8, src=src+1    ;8bit flags
   numbits=numbits-1
   if VRAMWAD or FA2 then flagbits SHL 1, else flagbits=flagbits SHR 1
   if carry=1 then
     dta=[src], [dst]=dta, ringbuf[wr AND mask]=dta
     dst=dst+1, wr=wr+1, src=src+1
   else
     if DEFAULT then rd=[src]+([src+1]/10h)*100h), len=([src+1] AND 0Fh)+3
     if zLATAD  then rd=[src]+([src+1] AND 0Fh)*100h), len=([src+1]/10h)+3
     if SCRATCH then rd=[src]+([src+1]/80h)*100h), len=([src+1] AND 7Fh)+3
     if VRAMWAD then rd=[src+1]+([src]/10h)*100h), len=([src] AND 0Fh)+3
     if FA2     then rd=[src], len=len2, len2=0, src=src+1
     if FA2 and len=0 then len=[src]/10h+2, len2=([src] AND 0Fh)+2, src=src+1
     if FA2=0   then src=src+2
     for i=1 to len   ;read ringbuf[rd] (instead of relative [dst-rd])
       dta=ringbuf[rd AND mask], [dst]=dta, ringbuf[wr AND mask]=dta
       dst=dst+1, wr=wr+1, rd=rd+1
     next i
   endif
   goto @@decompress_lop
  @@decompress_done:
   ret

Initial Ringbuf Content
  if ALZ1 or zLATAD then
    ringbuf[000h..FFFh]=(00h)              ;zeroes
  if VRAMWAD then
    ringbuf[000h..FEDh]=(00h)              ;zeroes
    ringbuf[FEEh..FFFh]=(uninitialized)    ;uninitialized, don't use
  if BIZ then
    ringbuf[000h..FEDh]=(20h)              ;ascii space
    ringbuf[FEEh..FFFh]=(uninitialized)    ;uninitialized, don't use
  if SCRATCH then
    ringbuf[000h..1BFh]=(00h)              ;zeroes
    ringbuf[1C0h..1FFh]=(uninitialized)    ;uninitialized, don't use
  if FA2 then
    ringbuf[000h..0FFh]=(00h)              ;zeroes
  if LZ5 then
    ringbuf[000h..CFFh]=(000h..CFFh)/0Dh   ;increasing, repeated 0Dh times each
    ringbuf[D00h..DFFh]=(00h..FFh)         ;increasing
    ringbuf[E00h..EFFh]=(FFh..00h)         ;decreasing
    ringbuf[F00h..F7Fh]=(00h)              ;zeroes
    ringbuf[F80h..FEDh]=(20h)              ;ascii space
    ringbuf[FEEh..FFFh]=(should be 00h)    ;see note, better don't use
Note: The last 12h bytes in LZ5 are 00h in LArc v3.33 (though unknown if that's intended and stable), LHarc source code did accidentally set them to 20h (which is reportedly fixed in later LHA versions).


CDROM File Compression PCK (Destruction Derby Raw)

Destruction Derby Raw (MagDemo35: DDRAW\*.PCK,EXE,DAT)
  000h 3     Decompressed size (24bit, little-endian)
  003h 1     Unused (0)
  004h ...   LZSS compressed data, starting with 30bit+2bit flags
The compression is used in some ISO files, which can be detected as:
  [03h]=00h, [04h]=00h, [08h]="PS-X EXE"                ;DDRAW\*.EXE
  [03h]=00h, [04h] AND FCh=00h, [08h]="BC",04h,40h,0,0  ;DDRAW\LDPICS\*.PCK
The compression is also used in nested PTH+DAT archives (where the whole DAT is compressed), which can be detected by checking if the sum of the PTH filesizes exceeds the DAT filesize.

Self-decompressing GUI code in PSX BIOS for SCPH-7000 and up
The PSX BIOS seems to use the same LZSS format for the self-decompressing GUI code (with GUI/decompression starting at 80030000h).

Decompression function
   dst_end=dst+LittleEndian24bit[src], src=src+4
  @@collect_more:
   flagbits=BigEndian32bit([src]), src=src+4
   dispbits=14-(flagbits AND 03h), flagbits=(flagbits OR 3)-1
   dispmask=(1 SHL dispbits)-1
  @@decompress_lop:
   flagbits=flagbits SHL 1    ;32bit shift with carry-out/zeroflag
   if zero then goto @@collect_more
   if carry=0 then
     [dst]=[src], dst=dst+1, src=src+1
   else
     disp=BigEndian16bit[src], src=src+2
     len=(disp SHR dispbits)+3
     disp=(disp AND dispmask)+1
     for i=1 to len, [dst]=[dst-disp], dst=dst+1, next i
   endif
   id dst<dst_end then goto @@decompress_lop
  @@decompress_done:
   ret


CDROM File Compression GT-ZIP (Gran Turismo 1 and 2)

BS iki Video
IKI is a rather uncommon variant of the .STR video format (used by Gran Turismo 1 and 2, Legend of Legaia, Legend of Dragoon, Omega Boost, Um Jammer Lammy).
IKI videos have a custom .BS header, including some GT-ZIP compressed data:
  000h 2   MDEC Size/4 (after huffman decompression) (rounded to 80h/4 bytes)
  002h 2   File ID (3800h)
  004h 2   Bitmap Width in pixels     ;instead quant
  006h 2   Bitmap Height in pixels    ;instead version
  008h 2   Size of GT-ZIP compressed data (plus 2-byte alignment padding)
  00Ah ..  GT-ZIP compressed DC/Quant values (plus 2-byte alignment padding)
  ...  ..  Huffman compressed AC data blocks (Cr,Cb,Y1,Y2,Y3,Y4, Cr,Cb,Y1,Y2..)
The number of blocks is NumBlocks=(Width+15)/16*(height+15)/16*6. The size of the decompressed GT-ZIP data is NumBlocks*2.

Gran Turismo 1 (MagDemo10: GT\*.DAT) - headerless
Gran Turismo 1 (MagDemo15: GT\*.DAT) - headerless
  000h ..    Compressed Data (without header)
This is used for compressing files inside of GT-ARC archives (or in one case, for compressing the whole GT-ARC archive). The GT-ARC directory contains additional compression info, see GT-ARC description for details.
The file GT\GAMEFONT.DAT is also GT-ZIP compressed, but lacks any ID or info on decompressed size, and there are at least two GAMEFONT.DAT versions (in MagDemo10 va MagDemo15), both versions are 8000h byte when decompressed, and compressed data starts with 00,FF,FF,00,00,00,80,00,00,01,17,07.

Gran Turismo 2 (MagDemo27: GT2\GT2.VOL\arcade\arc_other.tim\*) - with header
  000h 0Ch   ID "@(#)GT-ZIP",0,0
  00Ch 4     Decompressed Size
  010h ..    Compressed Data (unknown compressed size due to below padding)
  ...  ..    Zeropadding to 4-byte boundary (when stored in DOT1 archives)
This is used for compressing some files in one DOT1 archive (most other files in Gran Turismo 2 are using GZIP compression; with corrupted/zeropadded GZIP footers).

Decompression function
   if [src]="@(#)GT-ZIP",0,0 then dst.end=dst+[src+0Ch], src=src+10h
  @@collect_more:
   flagbits=[src]+100h, src=src+1    ;8bit flags
  @@decompress_lop:
   if src>=src.end then goto @@decompress_done  ;(when src.end is known)
   if dst>=dst.end then goto @@decompress_done  ;(when dst.end is known)
   flagbits=flagbits SHR 1
   if zero then goto @@collect_more
   if carry=0 then
     [dst]=[src], dst=dst+1, src=src+1
   else
     len=[src], src=src+1, disp=[src], src=src+1                 ;len, disp
     if disp>=80h then disp=(disp-80h)*100h+[src], src=src+1     ;longer disp
     for i=1 to (len+3), [dst]=[dst-(disp+1)], dst=dst+1, next i
   endif
   goto @@decompress_lop
  @@decompress_done:
   ret

Notes
Depending on the source, only the compressed or decompressed size may be known:
  Source                    Compressed Size           Decompressed Size
  Compressed GAMEFONT.DAT   In ISO Filesystem         Unknown (n/a)
  Compressed GT-ARC         In ISO Filesystem         Unknown (n/a)
  Files in GT-ARC           In GT-ARC                 In GT-ARC
  Files with GT-ZIP header  Unknown (due to padding)  In GT-ZIP
  DC values in IKI videos   Unknown (due to padding)  From Width*Height
Gran Turismo 1 has ID "@(#)GT-ZIP" (and "@(#)G.T-ZIPB" whatever that is) stored in Main RAM (though unknown if/which/any files do have those IDs).
Gran Turismo 2 has ID "@(#)GT-ZIP" in "GT2\GT2.VOL\arcade\arc_other.tim\*", apart from that, it does mainly use GZIP compressed files.


CDROM File Compression GT20 and PreGT20

GT20 Compressed Files
Used by Rollcage (MagDemo19: ROLLCAGE\SPEED.IMG\*)
Used by Rollcage Stage II (MagDemo31: ROLLCAGE\SPEED.IDX\*)
Used by Sydney 2000 (MagDemo37: OLY2000\DEMO.IDX\* and OLY2000\GTO\*.GTO)
Reportedly also Chill (PS1) (*.GTO)
Reportedly also Ducati World: Racing Challenge
Reportedly also Martian Gothic: Unification (PS1) (*.GT20)
  000h 4     ID ("GT20"=Compressed) (or reportedly "NOGT"=Uncompressed)
  004h 4     Size of decompressed data in bytes
  008h 4     Overlap for in-situ decompression (usually 3, or sometimes 7)
  00Ch 4     Size of Leading Zeropadding in bytes (0..7FFh)
  010h ..    Leading Zeropadding (0..7FFh bytes)
  ...  ..    Compressed Data
The Leading Zeropadding can be used to arrange the data to end on a sector boundary (useful when loading the file in units of whole sectors, and wanting to load it to the end of the decompression buffer).
 DecompressGT20:
  src=src+word[src+0Ch]+10h      ;skip header and any leading zeropadding
  collected=00000001h  ;end-bit
 @@lop:
  if GetBit=0
    [dst]=[src], dst=dst+1, src=src+1               ;uncompressed byte
  else
    if GetBit=0
      disp=byte[src]-100h, src=src+1                ;disp=(-100h..-1)
      len=(GetBit*2)+(GetBit*1)+2                   ;len=(2..5)
    else
      tmp=halfword[src], src=src+2
      disp=(tmp/8)-2000h                            ;disp=(-2000h..-1)
      len=(tmp AND 7)+2                             ;len=(2..9)
      if len=2
        tmp=byte[src], src=src+1
        if (tmp AND 80h) then disp=disp-2000h       ;disp=(-4000h..-1)
        len=(len AND 7Fh)+2                         ;len=(2..81h)
        if len=3 then goto decompression_done
        if len=2 then len=halfword[src], src=src+2  ;len=(0..FFFFh)
    for i=1 to len, [dst]=[dst+disp], dst=dst+1, next i
  goto @@lop
 ;---
 GetBit:
  collected=collected SHR 1
  if zero then collected=(word[src] SHR 1)+80000000h, src=src+4
  return carry (from shift right)
Note: Uncompressed files can reportedly contain "NOGT" in the header, however, Rollcage does have compressed files (with GT20 header), and raw uncompressed files (without any NOGT header).
See also: https://zenhax.com/viewtopic.php?t=13175 (specs)
See also: http://wiki.xentax.com/index.php/GT20_Archive (blurp)

Pre-GT20 Compressed Files
Used by Bloody Roar 1 (MagDemo06: BL\*.DAT\*)
Used by Bloody Roar 2 (MagDemo22: ASC,CMN,EFT,LON,SND,ST5,STU\*.DAT\*)
  000h 4    Compression Method (0=None, 2=Compressed, Other=Invalid)
  004h 4    Compressed Size (SIZ) (same as decompressed when method=0)
  008h 4    Decompressed Size
  00Ch SIZ  Compressed Data
  ...  ..   Garbagepadding to 4-byte boundary (in 4-byte aligned DAT files)
This is apparently on older version of what was later called GT20. The PreGT20 decompression works as so:
 DecompressPreGT20:
  src=src+0Ch                    ;skip header
  collected=80h  ;end-bit
 @@lop:
  if GetBit=1
    [dst]=[src], dst=dst+1, src=src+1               ;uncompressed byte
  else
    if GetBit=0
      len=(GetBit*2)+(GetBit*1)+2                   ;len=(2..5)
      disp=byte[src]-100h, src=src+1                ;disp=(-100h..-1)
    else
      tmp=bigendian_halfword[src], src=src+2
      disp=(tmp/8)-2000h                            ;disp=(-2000h..-1)
      len=(tmp AND 7)+2                             ;len=(2..9)
      if len=2
        len=byte[src]+1, src=src+1                  ;len=(1..100h)
        if len=1 then goto decompression_done
    for i=1 to len, [dst]=[dst+disp], dst=dst+1, next i
  goto @@lop
 ;---
 GetBit:
  collected=collected SHL 1    ;8bit shift
  if zero then collected=(byte[src] SHL 1)+01h, src=src+1
  return carry (from 8bit shift left)
Note: Uncompressed files with Method=0 exist in Bloody Roar 2 (CMN\SEL01.DAT).
Bloody Roar 1 (MagDemo06) has decompressor at 8016DD64h (method 0 and 2).
Bloody Roar 2 (MagDemo22) has decompressor at 8015C8C0h (method 0 and 2).


CDROM File Compression HornedLZ

Used by Project Horned Owl (*.BIN\*) (and within self-decompressing EXE)

HornedLZ Detection
The easiest way to detect HornedLZ files is to check first 4 bytes:
  B3 10 00 4F ..    Compressed TIM with TIM Type=00h (4bpp without CLUT)
  DB 10 00 3F ..    Compressed TIM with TIM Type=08h,09h,etc.
Alternately, one could check the Chunktype (in the parent archive):
  Type=05h can be uncompressed .TXT or HornedLZ-compressed .TIM
    (check if 2nd data byte is ASCII or 10h)
  Type=0Fh is a DOT1 archive with HornedLZ-compressed .TIMs
    (parse the DOT1 archive and treat its contents as compressed .TIMs)
  Type=10h contains Deflated TIMs
    (a completely different compression method)

DecompressHornedLZ:
  collected=01h  ;end-bit
 @@lop:
  if GetBit=1
    [dst]=[src], dst=dst+1, src=src+1               ;uncompressed byte
  else
    if GetBit=1
      tmp=[src], src=src+1
      len=tmp/40h+2, disp=tmp or (-40h)       ;len=(2..05h), disp=(-40h..-1)
    else
      tmp=[src]*100h+[src+1], src=src+2
      len=tmp/1000h+2, disp=tmp or (-1000h)   ;len=(2..11h), disp=(-1000h..-1)
      if len=2 then
        len=[src]+2, src=src+1                ;len=(2..101h)
        if len=2 then goto decompression_done
    for i=1 to len, [dst]=[dst+disp], dst=dst+1, next i
  goto @@lop
 ;---
 GetBit:
  collected=collected SHR 1
  if zero then collected=([src] SHR 1)+80h, src=src+1
  return carry (from shift right)
Note: The end code has all bits zero, except, disp is don't care (it's usually FFFh).


CDROM File Compression LZS (Gundam Battle Assault 2)

Gundam Battle Assault 2 (DATA\*.PAC\*, with ID="lzs")
  000h 4     ID ("lzs",00h)
  004h 4     Zerofilled
  008h 4     Fixed (must be 1) (method/version?)
  00Ch 14h   Zerofilled
  020h 2     Fixed (must be 3) (method/version?)
  022h 2     Offset to Compressed Data minus 20h (usually 38h-20h)
  024h 4     Decompressed Size
  028h 2     Flagsize (must be 08h, 10h, or 20h) (usually 20h=32bit)
  02Ah 2     Lensize  (must be 02h..07h)         (usually 05h=5bit)
  02Ch 4     Compressed Size (total filesize, including "lzs" header)
  030h 8     Name? (always "000000",00h,00h)
  038h ..    Compressed data (usually at offset 38h)
decompress_gundam_lzs:
   dst_end = dst+littleendian32bit[src+24h]
   flg_bits = littleendian16bit[src+28h]   ;8,16,32
   len_bits = littleendian16bit[src+2Ah]   ;2..7
   len_mask = (1 shl len_bits)-1           ;03h..7Fh
   src=src+littleendian16bit[src+22h]+20h
   collected_bits=0
  @@collect_more:
   for i=0 to flg_bits/8-1    ;read 8bit/16bit/32bit little-endian
     collected_bits=collected_bits+([src] SHL (i*8)), src=src+1
   num_collected=flg_bits
  @@decompress_lop:
   if dst=dst_end then goto @@decompress_done
   if num_collected=0 then goto @@collect_more
   num_collected=num_collected-1
   flagbits=flagbits SHR 1
   if carry=1 then
     [dst]=[src], dst=dst+1, src=src+1
   else
     temp=bigendian16bit[src], src=src+2
     len=(temp AND len_mask)+3
     disp=(temp SHR len_bits), if disp=0 then goto @@decompress_error
     for i=1 to len, [dst]=[dst-disp], dst=dst+1, next i
   endif
   goto @@decompress_lop
  @@decompress_done:
   ret


CDROM File Compression BZZ

Used in .BZZ archives. Note that there are three slightly different .BZZ archive formats (they are all using the same BZZ compression, only the BZZ archive headers are different).
  Jersey Devil .BZZ (MagDemo10: JD\*.BZZ)
  Bugs Bunny: Lost in Time (MagDemo25: BBLIT\*.BZZ)
  The Grinch (MagDemo40: GRINCH\*.BZZ)
Neither the file header nor the archive directory entries do contain any information about the decompressed size. Best workaround might be to decompress the file twice (without storing the output in 1st pass, to determine the size of the decompression buffer for 2nd pass).

BZZ Decompression
The compression is fairly standard LZSS, except that it supports non-linear length values, and it does support uncommon Len/Disp pairs like 7bitLen/9bitDisp (though usually, it does use standard 4bitLen/12bitDisp).
  decompress_bzz:
   method=byte[src], src=src+1       ;method (00h..1Fh) ;usually/always 0Bh)
   shifter  = ((method/8) and 3)     ;00h..03h                ;usually 1
   len_bits = ((method and 7) xor 7) ;07h..00h                ;usually 4
   len_mask = (1 shl len_bits)-1     ;7Fh..00h                ;usually 0Fh
   threshold=len_mask/2, if threshold>07h then threshold=13h  ;usually 07h
   for i=0 to len_mask
     if i>threshold then len_table[i] = ((i-threshold) shl shifter)+threshold+3
     else len_table[i] = i+3 ;method=18h max=(7Fh-13h)*8+13h+3=376h=886 decimal
   next i                    ;method=0Hh max=(0Fh-07h)*2+07h+3=1Ah=26 decimal
   num_flags=bigendian24bit[src]+1, src=src+3   ;NUM24+1
  @@collect_more:
   if src>=src_end then goto @@decompress_error
   flagbits=[src]+100h, src=src+1    ;8bit flags
  @@decompress_lop:
   flagbits=flagbits SHR 1
   if zero then goto @@collect_more
   if carry=1 then
     if src>=src_end then goto @@decompress_error
     [dst]=[src], dst=dst+1, src=src+1
   else
     if src+1>=src_end then goto @@decompress_error
     temp=bigendian16bit[src], src=src+2
     len=len_table[temp AND len_mask]
     disp=temp SHR len_bits, if disp=0 then goto @@decompress_error
     for i=1 to len, [dst]=[dst-disp], dst=dst+1, next i
   endif
   num_flags=num_flags-1, if num_flags>0 then goto @@decompress_lop
  @@decompress_error:
   ret
Bug: Files can randomly contain NUM24 or NUM24+1 codes (that seems to be due to a compressor bug or different compressor versions; the two variants are unfortunately randomly mixed even within the same game).
And, compressed files are padded to 4-byte boundary (making it impossible to distinguish between "NUM24+1" and "NUM24+padding").
  Case 1) source has NUM24+1 codes
           --> decode all NUM24+1 codes (otherwise output will be too small)
  Case 2) source has NUM24 codes (and enough padding for another code)
           --> decode all NUM24+1 codes (for compatibility with case 1)
           --> output will have some constant garbage byte(s) appended
           --> exception: omit last code if it contains invalid disp=0
  Case 3) source has NUM24 codes (and not enough padding for another code)
           --> decode only NUM24 codes (abort if NUM24+1 exceeds src_end)
           --> output should (probably) have correct size
           --> never exceed src_end which would be highly unstable


CDROM File Compression RESOURCE (Star Wars Rebel Assault 2)

Star Wars Rebel Assault 2 (RESOURCE.*\*)
BallBlazer Champions (*.DAT)
 decompression function:
  base=src, method=[src], dst_end=dst+BigEndian24bit[src+1], src=src+4
 @@decompress_lop:
  if dst>=dst_end then goto @@decompress_done
  if [src] AND 80h then
    if method=01h then
      len=([src]-80h)/8+3, disp=(BigEndian16bit[src] AND 7FFh)+1, src=src+2
    else  ;method=02h
      len=([src]-80h)+4, disp=(BigEndian16bit[src+1])+1, src=src+3
    for i=1 to len, [dst]=[dst-disp], dst=dst+1
  else    ;uncompressed
    len=[src]+1, src=src+1
    for i=1 to len, [dst]=[src], src=src+1, dst=dst+1
  goto @@decompress_lop
 @@decompress_done:
  src=(src+3) AND NOT 3
  if LittleEndian32bit[src]<>crc(base, src-base) then error
  ret
Note: Compression is (normally) used only in Top-level RESOURCE.* and *.DAT archives (not in Nested archives). The Top-level archives do also contain some uncompressed files (which contain data that is compressed on its own: SPU-ADPCM audio, or encrypted BS bitmaps).

Special case for BallBlazer Champions
Normally only Top-level archives contain compression, however, there are also some Nested archives with compression in BallBlazer Champions:
  STD_BBX.DAT\s*t\tp_a\*    ;\double compression, Top-level is ALSO compressed
  BBX_INTR.DAT\data1\pics\* ;/
  BBX_INTR.DAT\Stad\pics\*  ;\
  BBX_INTR.DAT\Stad\wire\*  ; Nested archives with compression
  BBX_INTR.DAT\Subtitl\*    ;
  BBX_INTR.DAT\Subtitl\sub\*;/
The Nested archives don't have any compression flag or decompressed size entries (so there's no good way for detecting compression in nested files).


CDROM File Compression TIM-RLE4/RLE8

Ape Escape (Sony 1999) (MagDemo22: KIDZ\*) has several compressed and uncompressed TIMs in headerless archives, the archives can contain:
  Compressed 4bpp RLE4-TIM with uncompressed CLUT ;\only 4bpp can be compressed
  Compressed 4bpp RLE8-TIM with uncompressed CLUT ;/
  Uncompressed 4bpp TIM with uncompressed CLUT    ;\only this type/combinations
  Uncompressed 8bpp TIM with uncompressed CLUT    ; are allowed if uncompressed
  Uncompressed 16pp TIM without CLUT              ;/
  End code 00000000h (plus more zeropadding)      ;-end of headerless archive
The compression method is indicated by changing a reserved halfword in the TIM header:
  hdr[02h]=Method (0000h=Uncompressed, 0001h=RLE4, 0002h=RLE8)
The rest of the bytes in TIM header and in CLUT section are same as for normal TIMs. The Bitmap section is as follows:
Decompressed size must be computed as Width*Height*2. The Section Size entry contains Section header size, plus compressed size, plus padding to 4-byte boundary.
Method=0001h (RLE4):
 @@decompress_lop:
  color=[src]/10h, len=([src] AND 0Fh)+1, src=src+1
  for i=1 to len, putpixel(color), next i               ;len=1..10h
  if numpixels<Width*Height*4 then goto @@decompress_lop
Method=0002h (RLE8):
 @@decompress_lop:
  color1=[src]/10h, color2=[src] AND 0Fh, src=src+1
  if color1=color2
    len=[src]+2, src=src+1
    for i=1 to len, putpixel(color1), next i            ;len=2..101h
  else
    putpixel(color1), if numpixels<Width*Height*4 then putpixel(color2)
  for i=1 to len, putpixel(color)       ;len=1..10h
  if numpixels<Width*Height*4 then goto @@decompress_lop
The decompression functions in Ape Escape (MagDemo22: KIDZ\*) are found at:
  80078760h ape_escape_load_tim_archive
  8007894Ch ape_escape_decompress_with_4bit_lengths
  800789FCh ape_escape_decompress_with_8bit_lengths
Examples for compressed TIMs are found at:
  RLE8: Ape Escape, MagDemo22: KIDZ\KKIIDDZZ.HED\DAT\file004h\1stTIM
  RLE4: Ape Escape, MagDemo22: KIDZ\KKIIDDZZ.HED\DAT\file135h\1stTIM
  RLE8: Ape Escape, MagDemo22: KIDZ\KKIIDDZZ.HED\DAT\file139h\1stTIM
Being made by Sony, this might be an official (but late) TIM format extension, unknown if there are any other games using that compression.


CDROM File Compression RLE_16

Apocalypse (MagDemo16: APOC\CD.HED\*.RLE)
Spider-Man (MagDemo31,40: SPIDEY\CD.HED\*.RLE)
Spider-Man 2 (MagDemo50: HARNESS\CD.HED\*.RLE)
  000h 8     ID "_RLE_16_"
  008h 4     Decompressed Size (usually 3C008h) (33408h=Apocalypse warning.rle)
  00Ch ..    RLE Compressed Data (usually a .BMR bitmap)
This is using simple RLE compression with 16bit len/data units (suitable for 16bpp VRAM data). The compression ratio ranges from not so bad to very bad.

Decompression
  src=src+0Ch                                       ;skip ID and size
 @@decompress_lop:
  len=halfword[src], src=src+2
  if len=0000h then goto @@decompress_done          ;end-code
  if (len AND 8000h)=0 then
    for i=1 to len, halfword[dst]=halfword[src], dst=dst+2, src=src+2, next i
  else
    fillvalue=halfword[src], src=src+2
    for i=1 to len-8000h, halfword[dst]=fillvalue, dst=dst+2, next i
  goto @@decompress_lop
 @@decompress_done:
  ret

Other RLE16 variants
A similar RLE16 variant is used in Croc 1, and another variant in Croc 2.
CDROM File Archive Croc 1 (DIR, WAD, etc.)
CDROM File Archive Croc 2 (DIR, WAD, etc.)


CDROM File Compression PIM/PRS (Legend of Mana)

Legend of Mana (.PIM/.PRS)
  000h 1   Unknown (always 01h) (maybe File ID or Compression method)
  001h ..  Compressed data  ;for TIM: usually 00,10, F0,00, 00,0x, F0,00, ...
Compression codes are:
  nn,data[nn+1]  ;nn=00..EF len=nn+1   [dst]=data[1]             ;-uncompressed
  F0,xn                     len=n+3    [dst]=0x         ;1x4bit  ;\
  F1,nn,xx                  len=nn+4   [dst]=xx         ;1x8bit  ;
  F2,nn,yx                  len=nn+2   [dst]=0x,0y      ;2x4bit  ; RLE fill
  F3,nn,xx,yy               len=nn+2   [dst]=xx,yy      ;2x8bit  ;
  F4,nn,xx,yy,zz            len=nn+2   [dst]=xx,yy,zz   ;3x8bit  ;/
  F5,nn,xx,data[nn+4]       len=nn+4   [dst]=xx,data[1]          ;\interleaved
  F6,nn,xx,yy,data[nn+3]    len=nn+3   [dst]=xx,yy,data[1]       ; fill combo
  F7,nn,xx,yy,zz,data[nn+2] len=nn+2   [dst]=xx,yy,zz,data[1]    ;/
  F8,nn,xx                  len=nn+4   [dst]=xx    ;xx=xx+1      ;\
  F9,nn,xx                  len=nn+4   [dst]=xx    ;xx=xx-1      ; fill with
  FA,nn,xx,ss               len=nn+5   [dst]=xx    ;xx=xx+ss     ; signed step
  FB,nn,xx,yy,ss ;ss=signed len=nn+3   [dst]=xx,yy ;yyxx=yyxx+ss ;/
  FC,xx,ny                  len=n+4    [dst]=[dst-yxx-1]         ;\
  FD,xx,nn                  len=nn+14h [dst]=[dst-xx-1]          ; LZ compress
  FE,xn                     len=n+3    [dst]=[dst-x*8-8]         ;/
  FF                        len=0      end                       ;-end code
The compression is used for several files in Legend of Mana:
  BIN\*.BIN       ---> packed misc binary
  MAP\*\FDATA.PRS ---> packed resource, whatever
  MAP\*\MAP*.PRS  ---> packed MPD resource, "SKmapDat"
  WM\WMTIM\*.PIM  ---> packed TIM image, 384x384x4bpp, bad compression ratio
  WM\WMAP\*.PAT   ---> packed loaddata
  WM\WMAP\*.PIM   ---> packed TIM image, 320x256x16bit, with UNCOMPRESSED dupe


CDROM File Compression BPE (Byte Pair Encoding)

Byte Pair Encoding (BPE) does replace the most common byte-pairs with bytes that don't occur in the data. That does work best if there are unused bytes (eg. ASCII text, or 8bpp bitmaps with less than 256 colors).

Bust A Groove (MagDemo18: BUSTGR_A\*.BPE)
Bust-A-Groove 2 (MagDemo37: BUSTAGR2\BUST2.BIN\*)
  000h 4     ID "BPE_"
  004h 4     Total Filesize of compressed file including header (big-endian)
  ...  ..    Compression block(s)
 Each compression block contains:
  000h ..    Dictionary info
  ...  2     Size of compressed data (big-endian)
  ...  ..    Compressed data
The decompression function in Bust A Groove (MagDemo18) is at 80023860h, the heap is in 1Kbyte Scratchpad RAM at 1F800208h, so heap size should be max 1F8h bytes (assuming that the remaining Scratchpad isn't used for something else). The fileheader lacks info about the decompressed size.

Legend of Dragoon (MagDemo34: LOD\OVL\*.OV_ and LOD\SECT\*.BIN\*)
  000h 4     Decompressed size (little-endian)
  004h 4     ID "BPE",1Ah
  008h ..    Compression block(s)
  ...  ..    End code (00000000h) (aka last block with Blocksize=0)
 Each compression block contains:
  000h 4     Size of decompressed block (little-endian) (or 0=End code)
  004h ..    Dictionary info
  ...  ..    Compressed data
  ...  ..    Padding to 4-byte boundary
Max nesting appears to be 2Ch, the decompression function allocates a 30h-byte heap on stack, and fetches source data in 32bit units (occupying 4 heap bytes), the decompressor does then remove 1 byte from heap, and adds 2 bytes in case of nested codes.

BPE Decompression for Bust-A-Groove and Legend of Dragoon
  if [src+0]="BPE_" then type=GROOVE                                    ;\
  if [src+4]="BPE",1Ah then type=DRAGOON                                ;
  if type=GROOVE then src_end = src+BigEndian32bit[src+4]               ; hdr
  if type=DRAGOON then dst_end = dst+LittleEndian32bit[src+0]           ;
  src=src+8                                                             ;/
 @@block_lop:
  if type=DRAGOON then                                                  ;\blk
    dst_blk_end = dst+LittleEndian32bit[src]+4, src=src+4               ; len
    if dst=dst_blk_end then goto @@decompress_done                      ;/
  for i=00h to FFh, dict1[i]=i, next i                                  ;\
  i=00h                                                                 ;
 @@dict_lop:                                                            ; dict
  num=[src], src=src+1                                                  ;
  if num>7Fh then i=i+(num-7Fh), num=0, if i=100h then goto @@dict_done ;
  for j=0 to num                                                        ;
    a=[src], src=src+1                                                  ;
    if a<>i then b=[src], src=src+1, dict1[i]=a, dict2[i]=a             ;
    i=i+1                                                               ;
  if i<100h then goto @@dict_lop                                        ;
 @@dict_done:                                                           ;/
  if type=GROOVE then                                                   ;\blk
    src_blk_end = src+BigEndian16bit[src]+2, src=src+2                  ;/len
  i=0                                                                   ;\
 @@data_lop:                                                            ;
  if i=0 then                                                ;\         ; data
    if type=GROOVE and src=src_blk_end then goto @@data_done ; get data ;
    if type=DRAGOON and dst=dst_blk_end then goto @@data_done; from src ;
    x=[src], src=src+1                                       ; or heap  ;
  else                                                       ;          ;
    i=i-1, x=heap[i]                                         ;/         ;
  a=dict1[x]                                    ;-xlat                  ;
  if a=x then                                   ;\                      ;
    [dst]=x, dst=dst+1                          ; output data to        ;
  else                                          ; dst or heap           ;
    b=dict2[x], heap[i]=b, heap[i+1]=a, i=i+2   ;/                      ;
  goto @@data_lop                                                       ;
 @@data_done:                                                           ;/
  if type=GROOVE and src<src_end then goto @@block_lop                  ;\next
  if type=DRAGOON then src=(src+3) AND not 3, goto @@block_lop          ;/blk
 @@decompress_done:
  if type=DRAGOON and dst<>dst_end then error
  ret

Electronic Arts
Electronic Arts games support several compression methods, including a BPE variant. That BPE variant is a bit unusual: It does have only one compression block (with a single dictionary for the whole file), and uses escape codes for rarely used bytes.
CDROM File Compression EA Methods


CDROM File Compression RNC (Rob Northen Compression)

Rob Northen compression
Rob Northen compression (RNC) is a LZ/Huffman compression format used by various games for PC, Amiga, PSX, Mega Drive, Game Boy, SNES and Atari Lynx.
Most RNC compressed files come in a standard 12h-byte header:
  000h 3   Signature ("RNC") (short for Rob Northen Computing compression)
  003h 1   Compression Method (01h or 02h)
  004h 4   Size of Uncompressed Data                             ;big-endian
  008h 4   Size of Compressed Data (SIZ)                         ;big-endian
  00Ch 2   CRC16 on Uncompressed Data (with initial value 0000h) ;big-endian
  00Eh 2   CRC16 on Compressed Data   (with initial value 0000h) ;big-endian
  010h 1   Leeway (difference between compressed and uncompressed data in
                   largest pack chunk, if larger than decompressed data)
  011h 1   Number of pack chunks
  012h SIZ Compressed Data
  ... (..) Zeropadding to 800h-byte boundary-4 ;\as so in PSX Heart of Darkness
  ... (4)  Unknown                             ;/
The compressed data consists of interleaved bit- and byte-streams, the first 2 bits of the bit stream are ignored.

RNC Method 1 - with custom Huffman trees
The bit-stream is read in 16bit units (the 1st bit being in bit0 of 1st byte).
  Each pack chunk contains the following:
  * 3 Huffman trees (one for literal data sizes, one for distance values,
    and one for length values) in the bit stream. These consist of:
      o A 5 bit value for the amount of leaf nodes in the tree
      o 4 bit values for each node representing their bit depth.
  * One 16 bit value in the bitstream for the amount of subchunks in the
    pack chunk.
  * The subchunk data, which contains for each subchunk:
      o A Huffman code value from the first tree in the bit stream for the
        amount of literals in the byte stream.
      o Literals from the byte stream.
      o A Huffman code from the bit stream that represents the distance - 1
        of a distance/length pair.
      o A Huffman code from the bit stream that represents the length - 2
        of a distance/length pair.
Unknown how that works exactly (see source code for details), unknown if method 1 was used on PSX.

RNC Method 2 - with hardcoded Huffman trees
The bit-stream is read in 8bit units (the 1st bit being in bit7).
  0     + Byte(DATA[1])              Copy 1 Byte from Source
  1000  + Dist + Byte(X)             Copy 4 Bytes from Dest-(Dist+X+1)
  10010 + Dist + Byte(X)             Copy 6 Bytes from Dest-(Dist+X+1)
  10011 + Dist + Byte(X)             Copy 7 Bytes from Dest-(Dist+X+1)
  1010  + Dist + Byte(X)             Copy 5 Bytes from Dest-(Dist+X+1)
  10110 + Dist + Byte(X)             Copy 8 Bytes from Dest-(Dist+X+1)
  10111 + nnnn + Byte(DATA[12..72])  Copy nnnn*4+12 Bytes from Source
  110   + Byte(X)                    Copy 2 Bytes from Dest-(X+1)
  1110  + Dist + Byte(X)             Copy 3 bytes from Dest-(Dist+X+1)
  1111  + Byte(0) + 0 + zeropadding  End of last pack chunk
  1111  + Byte(0) + 1                End of non-last pack chunk
  1111  + Byte(L) + Dist + Byte(X)   Copy L+8 Bytes from Dest-(Dist+X+1) ;L>00h
Dist values:
  0      = 0000h            1000   = 0200h
  110    = 0100h            1001   = 0300h
  111000 = 0C00h            101000 = 0800h
  111001 = 0D00h            101001 = 0900h
  11101  = 0600h            10101  = 0400h
  111100 = 0E00h            101100 = 0A00h
  111101 = 0F00h            101101 = 0B00h
  11111  = 0700h            10111  = 0500h
The purpose of the pack chunks isn't quite clear, it might be related to memory restrictions on old CPUs. In PSX Heart of Darkness they are chosen so that the decompressed data is max 3000h bytes per chunk. Unknown if the next chunk may copy data from previous chunk.

Links
  http://aminet.net/package/util/pack/RNC_ProPack - official tool & source code
  https://segaretro.org/Rob_Northen_compression - description (contains bugs)

RNC is used in a number of games by UK developers (notably Bullfrog and Traveller's Tales), including Sonic 3D: Flickies' Island, Blam! Machinehead, Dungeon Keeper 2, Magic Carpet, Syndicate and Syndicate Wars.

RNC in PSX Games
  Method 2: Demolition Racer (MagDemo27: DR\DD.DAT\*.RNC)
  Method 2: Heart of Darkness (IMAGES\US.TIM)
  Method 2: Jonah Lomu Rugby (LOMUDEMO\GFX\*.PAK)
  Method 2: NBA Jam: Tournament Edition (*.RNC, headerless .BIN/.GFX archives)
  Method 2: Test Drive 5 (MagDemo13: TD5.DAT\*.RNC)
  Method 2: Test Drive Off-Road 3 (MagDemo27: TDOR3\TDOR3.DAT\*.rnc)

RNC in Mega Drive games
  3 Ninjas Kick Back
  Addams Family
  Addams Family Values
  The Adventures of Mighty Max
  Ast‚rix and the Great Rescue
  Ast‚rix and the Power of the Gods
  The Incredible Hulk
  The Itchy & Scratchy Game (unreleased)
  Marsupilami
  Mortal Kombat
  Mr. Nutz
  Outlander
  The Pagemaster
  RoboCop 3
  Spirou
  Spot Goes to Hollywood
  Stargate
  Street Racer
  Tinhead
  Tintin in Tibet
  World Championship Soccer II


CDROM File Compression Darkworks

Used by Alone in the Dark The New Nightmare (FAT.BIN\LEVELS\*\chunks)

Decompression
The decompressor is designed to hook the sector loading function: It does decompress incoming sectors during loading, and forwards the decompressed data to the original sector loading function. The decompressed data is temporarily stored in two small Dict buffers (which do also serve as compression dictionary).
 decompress:
  dictsize=1000h, dict0=alloc(dictsize), dict1=alloc(dictsize)
  src=load_next_800h_byte_sector  ;load first sector
  dst=dict0                       ;temp dest in current dict
  dst_base=dst                    ;memorize start of newly decompressed data
 @@decompress_lop:
  if [src]=00h then                                             ;\
    esc=[src+1], src=src+1                                      ;
    forward_to_actual_dest(source=dst_base, len=dst-dst_base)   ; escape
    if esc=0 or esc>4 then esc=2 (or warn_invalid_escape_code)  ;
    if esc=1 then goto @@decompress_done                        ;
    if esc=2 or esc=4 then src=load_next_800h_byte_sector       ;
    if esc=3 or esc=4 then swap(dict0,dict1), dst=dict0         ;
    dst_base=dst                                                ;/
  elseif ([src] AND 03h)=0 then                                 ;\
    len=[src]/4+2, dat=[src+1], src=src+2                       ; fill 8bit
    for i=1 to len, [dst]=dat, dst=dst+1                        ;/
  elseif ([src] AND 03h)=1 then                                 ;\
    len=[src]/4+([src+2] AND 40h)+4                             ;
    ptr=[src+1]+([src+2] AND 3Fh)*100h                          ; LZ compressed
    if ptr+len>dictsize then error (exceeds allocated dictsize) ;
    if ([src+2] AND 80h) then ptr=ptr=dict1 else ptr=ptr=dict0  ;
    src=src+3                                                   ;
    for i=1 to len, [dst]=[ptr], ptr=ptr+1, dst=dst+1           ;/
  elseif ([src] AND 03h)=2 then                                 ;\
    len=[src]/4+3, dat0=[src+1], dat1=[src+2], src=src+3        ; fill 16bit
    for i=1 to len, [dst]=dat0, [dst+1]=dat1, dst=dst+2         ;/
  elseif ([src] AND 03h)=3 then                                 ;\
    len=[src]/4+1, src=src+1                                    ; uncompressed
    for i=1 to len, [dst]=[src], src=src+1, dst=dst+1           ;/
  goto @@decompress_lop
 @@decompress_done:
  dealloc(dict0), dealloc(dict1)
  ret
There are one or more escape codes per sector (one to indicate the of the sector, plus further escape codes to swap the Dict buffers whenever the current Dict is full).
The original decompressor is doing the forwarding in 800h-byte units, so Dict swapping may be only done when dict0 contains a multiple of 800h bytes (aka dictsize bytes).
For whatever reason, there are only 4Kbyte per Dict allocated (although the 14bit LZ indices could have addressed up to 16Kbyte per Dict).


CDROM File Compression Blues

Blue's Clues: Blue's Big Musical (VRAM and FRAM chunks in *.TXD)
Decompression function:
  if LittleEndian32bit[src+08h]<>1 then error  ;compression flag
  dst_end=dst+LittleEndian32bit[src+14h], src=src+18h, num_collected=0
 @@decompress_lop:
  if GetBit=1 then
    [dst]=[src], src=src+1, dst=dst+1           ;code 1 uncompressed byte
  elseif GetBit=1 then
    len=[src], src=src+1                        ;code 01 fill or end code
    if len=00h then goto @@decompress_done
    len=len+1, fillvalue=[dst-1]
    for i=1 to len, [dst]=fillvalue, dst=dst+1
  else
    len=GetBit*2+GetBit
    if len=0 then                               ;code 0000 long LZ range
      len=[src] AND 0Fh, disp=[src]/10h+[src+1]*10h-1000h, src=src+2
    else                                        ;code 00xx short LZ range
      disp=[src]-100h, src=src+1
    len=len+1
    for i=1 to len, [dst]=[dst+disp], dst=dst+1
  goto @@decompress_lop
 @@decompress_done:
  if dst<>dst_end then error
  ret
 ;---
 GetBit:
  if num_collected=0 then collected=[src], src=src+1, num_collected=8
  collected=collected*2
  return (collected/100h) AND 1


CDROM File Compression Z (Running Wild)

Running Wild (MagDemo15: RUNWILD\*.BIN\*.Z and *.z)
  decompress_z:
   src=src+4            ;skip 32bit decompressed size entry
  @@reload_lop:
   load_table1          ;table for first 9bits
   load_table2          ;table for codes longer than 9bits
  @@decompress_lop:
   sym=get_symbol()
   if sym<100h then [dst]=sym, dst=dst+1, goto @@decompress_lop
   if sym=100h then goto @@escape
   len=sym-0FCh                         ;change 101h..140h to 05h..44h
   disp=((get_symbol()-101h)*40h)       ;change 101h..140h to 00h..3Fh*40h
   disp=((get_symbol()-101h) or disp)+1 ;change 101h..140h to 00h..3Fh+above+1
   copy len bytes from dst-disp to dst
   goto @@decompress_lop
  @@escape:
   if GetBits(1)=0 then goto @@reload_lop
   ret
  ;-----
  load_table1:
   t=0
  @@load_lop:
   x=GetBits(10h)
   if x and 8000h then num=1 else num=(1 shl (9-(x/400h)))
   for i=1 to num, table1[t]=x, t=t+1, next i
   if t<200h then goto @@load_lop
   ret
  ;-----
  load_table2:
   num=GetBits(9)*2      ;can be 0=none, max=3FEh
   if num>0 then for i=0 to num-1, table2[i]=GetBits(9), next i
   ret
  ;-----
  get_symbol:
   ;returns a value in range 0..140h:
   ;  00h..FFh   = data 00h..FFh   (or unused for disp codes)
   ;  100h       = escape          (or unused for disp codes)
   ;  101h..140h = length 05h..44h (or 6bit fraction of 12bit disp)
   ;  141h..3FFh = would be possible for short codes, but shouldn't be used
   x=table1[PeekBits(9)]
   if (x and 8000h)=0 then SkipBits(x/400h), return (x and 3FFh)  ;-short code
   SkipBits(9)   ;skip first 9 bits, and process futher bit(s)..  ;\
   x=x-0C000h    ;change C000h..C1FFh and up to 000h..1FFh        ; long code
  @@lop:                                                          ; (with more
   x=table2[x*2+GetBits(1)]             ;branch node0/node1       ; than 9bit)
   if x>=141h then x=x-141h, goto @@lop                           ;
   return x                                                       ;/
The bitstream is fetched in little endian 16bit units (the first bit is in bit7 of second byte). PeekBits returns the next some bits without discarding them, SkipBits does discard them, GetBits does combine PeekBits+SkipBits.
Note: The decompression function in Running Wild (MagDemo15) is at 80029D10h.


CDROM File Compression ZAL (Z-Axis)

Thrasher: Skate and Destroy (MagDemo27: SKATE\ASSETS\*.ZAL) (Z-Axis)
Dave Mirra Freestyle BMX (MagDemo36: BMX\ASSETS\*.ZAL) (Z-Axis)
Dave Mirra Freestyle BMX (MagDemo46: BMX\ASSETS\*.ZAL) (Z-Axis)
ZAL compression is used in ZAL archives. The archive header contains compressed and decompressed size for each file (and a compression flag indicating whether the archive is compressed at all).

ZAL Decompression
  if src_len=0 then goto @@decompress_done      ;empty (without end code)
  lzlen=0, rawlen=0
  if [src]=10h..FFh then                                ;\special handling
    rawlen=[src]-11h, src=src+1                         ; for code=10h..FFh
    if rawlen<=0 then goto @@decompress_error           ;/at begin of source
 @@decompress_lop:
  memcopy(dst-disp,dst,lzlen)   ;copy compressed bytes
  memcopy(src,dst,rawlen)       ;copy uncompressed bytes
  code=[src], src=src+1
  if code=00h..0Fh then
    if rawlen=0   ;when OLD rawlen=0...
      lzlen=0, rawlen=code+3                            ;\
      if rawlen=3 then                                  ;
        while [src]=00h, rawlen=rawlen+FFh, src=src+1   ;
        rawlen=rawlen+[src]+0Fh, src=src+1              ;/
    else          ;when OLD rawlen>0, and depending on OLD lzlen...
      rawlen=code AND 03h
      disp=code/4+[src]*4, src=src+1
      if lzlen=0 then disp=disp+801h, lzlen=3, else then disp=disp+1h, lzlen=2
  if code=10h..1Fh then
    lzlen=(code AND 07h)+2
    if lzlen=2 then
      while [src]=00h, lzlen=lzlen+FFh, src=src+1
      lzlen=lzlen+[src]+07h, src=src+1
    rawlen=[src] AND 03h, disp=[src]/4+[src+1]*40h+(code/8 AND 1)*4000h+4000h
    src=src+2
    if disp=4000h AND code=11h then goto @@decompress_done    ;end code
    if disp=4000h AND code<>11h then goto @@decompress_error
  if code=20h..3Fh then
    lzlen=code-20h+2
    if lzlen=2 then
      while [src]=00h, lzlen=lzlen+FFh, src=src+1
      lzlen=lzlen+[src]+1Fh, src=src+1
    rawlen=[src] AND 03h, disp=[src]/4+[src+1]*40h+1, src=src+2
  if code=40h..FFh then
    rawlen=code AND 03h
    lzlen=(code/20h)+1
    disp=((code/4) AND 07h)+([src]*8)+1, src=src+1
  goto @@decompress_lop
 @@decompress_done:
  ret


CDROM File Compression EA Methods

Electronic Arts Compression Headers
The files start with a 16bit big-endian Method value, with following bits:
  0-7     ID (usually FBh) (or 31h for Method 4A31h with 16bit sizes)
  8       Extended Header (usually 0) (or 1 for headers with extra entries)
  9-14    Used to distinguish different methods
  15      Extended Size (usually 0 for 24bit sizes) (or 1 for 32bit sizes)
The most common Method values are:
  10FBh = LZSS Compression (RefPack)
  90FBh = LZSS Compression (RefPack, with 32bit size) (not on PSX)
  30FBh = Huffman Compression
  32FBh = Huffman Compression with filter
  34FBh = Huffman Compression with dual filter
  46FBh = BPE Byte-Pair Encoding
  4AFBh = RLE Run-Length Encoding
  4A31h = RLE Run-Length Encoding, with 16bit size
  C0FBh = File Archive (not a compression method)
Most or all PSX files have Bit8=0, but anyways, the decompressor does support skipping extra header entries in files with Bit8=1 (with all methods except RLE).
Most or all PSX files have Bit15=0, games for newer consoles can reportedly have Method=90FBh (unknown if anything like B2FBh or CAFBh does also exist).
Most or all PSX files have Bit0-7=FBh (supposedly short for Frank Barchard), the 16bit mode with Bit0-7=31h is supported for Method=4A31h only (the decompressor would also accept invalid methods like 1031h or 3431h, but doesn't actually support 16bit mode for those).

Compression Formats
CDROM File Compression EA Methods (LZSS RefPack)
CDROM File Compression EA Methods (Huffman)
CDROM File Compression EA Methods (BPE)
CDROM File Compression EA Methods (RLE)

Usage in PSX games
The compression can be used to compress whole files:
  PGA Tour 96, 97, 98 (*.* and *.VIV\*) (with method 10FBh)
  Need for Speed 3 Hot Pursuit (*.Q* with method 10FBh, 30FBh, 32FBh)
Or to compress texture bitmaps inside of .PSH file chunks:
  FIFA - Road to World Cup 98 (*.PSH chunk C0h/C1h with method 10FBh)
  Sled Storm (MagDemo24: ART3\LOAD*.PSH chunk C0h/C1h with method 10FBh)
  WCW Mayhem (MagDemo28: WCWDEMO\*.BIG\*.PSH with chunk C0h/C1h with 10FBh)
The decompressor supports further methods (like 34FBh, 46FBh, 4AFBh), but there aren't any files or chunks known to actually use those compression formats.

Note: Some compressed files are slightly larger than uncompressed files (eg. filesizes for PGA Tour 96, 97, 98 COURSES\*\*.VIV\*.mis are compressed=58h, uncompressed=50h).

See also
http://wiki.niotso.org/RefPack - LZ method


CDROM File Compression EA Methods (LZSS RefPack)

RefPack
  000h 2     Method (10FBh, or 11FBh,90FBh,91FBh) (big-endian)
  ...  (3/4) Compressed size   (24bit or 32bit)   (optional)
  ...  3/4   Uncompressed size (24bit or 32bit)   (big-endoan)
  ...  ..    Compressed data
The compression is some kind of LZSS/LZH variant (similar to Z-Axis .ZAL files). The compressed data consists of a big-endian bit-stream (or byte-stream, as all codes are multiples of 8bits). The Compression codes are:
  0ddzzzrrdddddddd                  rawlen=r(2), lzlen=z(3)+3,  disp=d(10)+1
  10zzzzzzrrdddddddddddddd          rawlen=r(2), lzlen=z(6)+4,  disp=d(14+1
  110dzzrrddddddddddddddddzzzzzzzz  rawlen=r(2), lzlen=z(10)+5, disp=d(17)+1
  111rrrrr                          rawlen=r(5)*4+4, lzlen=0
  111111rr                          rawlen=r(2), lzlen=0, endflag=1
refpack_decompress:
  method=BigEndian16bit[src], src=src+2
  if (method AND 100h)>0 then src=src+3+method/8000h ;compressed size, if any
  if (method AND 8000h]=0 then dst_size=BigEndian24bit[src], src=src+3
  if (method AND 8000h)>0 then dst_size=BigEndian32bit[src], src=src+4
  endflag=0
 @@decompress_lop:
  if ([src] AND 80h)=0 then
    rawlen=[src] AND 03h
    lzlen=([src] AND 1Fh)/4+3
    disp=([src] AND 60h)*8+[src+1]+1
    src=src+2
  elseif ([src] AND 40h)=0 then
    rawlen=[src+1]/40h
    lzlen=[src] AND 3Fh+4
    disp=([src+1] AND 3Fh)*100h+[src+2]+1
    src=src+3
  elseif ([src] AND 20h)=0 then
    rawlen=[src] AND 03h
    lzlen=([src] AND 0Ch)*40h+[src+3]+5
    disp=([src] AND 10h)*1000h+[src+1]*100h+[src+2]+1
    src=src+4
  elseif ([src] AND FCh)=FCh then
    rawlen=[src] AND 03h
    lzlen=0
    src=src+1, endflag=1
  else
    rawlen=([src] AND 1Fh)*4+4
    lzlen=0
    src=src+1
  for i=1 to rawlen, [dst]=[src], src=src+1, dst=dst+1, next i
  for i=1 to lzlen, [dst]=[dst-disp], dst=dst+1, next i
  if endflag=0 then goto @@decompress_lop
  if (dst-dst_base)<>dst_size then error
  ret


CDROM File Compression EA Methods (Huffman)

Huffman
  000h 2    Method (30FBh..35FBh) (big-endian)
  ...  (3)  Extra 3 bytes (only present if Method.bit8=1)
  ...  3    Decompressed Size     (big-endian)
  ...  1    Escape code
  ...  ..   Number of codes per width
  ...  ..   Data placement for each code
  ...  ..   Compressed Data

Huffman
  decompress_ea_huffman:
   method=GetBits(16)    ;3xFBh                     ;-get method (30FBh..35FBh)
   if method AND 100h then dummy=GetBits(24)        ;-skip extra (if any)
   dst_size=GetBits(24)                             ;-get uncompressed size
   ESC=GetBits(8)                                   ;-get escape code
   huffwidth=0, huffcode=0, totalnumcodes=0         ;\
   while (huffcode shl (10h-huffwidth))<10000h      ;
     num=GetVarLenCode                              ; get num codes per width
     huffwidth=huffwidth+1                          ;
     numcodes_per_width[width]=num                  ;
     totalnumcodes=totalnumcodes+num                ;
     huffcode=(huffcode*2)+num                      ;/
   for i=0 to FFh, data_defined_flags[i]=00h        ;\
   dat=FFh, index=0                                 ;
   while index<totalnumcodes                        ;
     n=GetVarLenCode+1               ;-             ; get/assign data values
     while n>0  ;search Nth notyet defined entry    ;
       dat=(dat+1) AND FFh  ;wrap in 8bit range!    ;
       if data_defined_flags[dat]=0 then n=n-1      ;
     data_defined_flags[dat]=1                      ;
     data_values[index]=dat, index=index+1          ;/
   huffcode=0000h, index=0                          ;\
   InitEmptyHuffTree(data_tree)                     ;
   for width=1 to huffwidth                         ;
     for i=1 to numcodes_per_width[width]           ; create huffman tree
       dat=data_values[index], index=index+1        ;
       CreateHuffCode(data_tree,dat,huffcode,width) ;
       huffcode=huffcode+(1 shl (10h-width)         ;/
  @@decompress_lop:                                 ;\
   dat=GetHuffCode(data_tree)                       ;
   if dat<>ESC                                      ;
     [dst]=dat, dst=dst+1                           ; decompress
   else                                             ;
     num=GetVarLenCode                              ;
     if num=0 then                                  ;
       if GetBits(1)=1 then goto @@decompress_done  ;
       [dst]=GetBits(8), dst=dst+1                  ;
     else                                           ;
       dat=[dst-1]                                  ;
       for i=0 to num-1, [dst]=dat, dst=dst+1       ;
   goto @@decompress_lop                            ;/
  @@decompress_done:
   if (dst-dst_base)<>dst_size then error                 ;-error check
   dst=dst_base, x=00h, y=00h                             ;\
   if (method AND FEFFh)=32FBh                            ; optional final
     for i=0 to dst_size-1, x=x+[dst+i], [dst+i]=x        ; unfiltering
   if (method AND FEFFh)=34FBh                            ;
     for i=0 to dst_size-1, x=x+[dst+i], y=y+x, [dst+i]=y ;/
   ret
  ;------------------
  GetVarLenCode:
   num=2
   while GetBits(1)=0, num=num+1
   return (GetBits(num)+(1 shl num)-4)
  GetBits(num):
   return "num" bits, fetched from big-endian bitstream
  GetHuffCode(data_tree):
   ...
  InitEmptyHuffTree(data_tree):
   ...
  CreateHuffCode(data_tree,dat,huffcode,width):
   ...
  numcodes_per_width[10h]   ;9bit numcodes per width 0..15 (entry[0]=unused)
  data_values[100h]         ;8bit data values for up to 100h huffman codes
  data_defined_flags[100h]  ;1bit flags for data(00h..FFh)



CDROM File Compression EA Methods (BPE)

Byte-Pair Encoding
  000h 2    Method (46FBh or 47FBh) (big-endian)
  ...  (5)  Extra 5 bytes (only present if Method=47FBh)
  ...  3    Decompressed Size       (big-endian)
  ...  1    Escape code
  ...  1    Number of Dict entries (N)
  ...  N*3  Dict (each 3 bytes: Index,Dat1,Dat2)
  ...  ..   Compressed Data
decompress_bpe:
  method=BigEndian16bit[src], src=src+2
  if method=47FBh then src=src+5
  dst_size=BigEndian24bit[src], src=src+3
  esc=[src], src=src+1
  num=[src], src=src+1
  for i=0 to FFh, dict1[i]=i    ;initially default=self (uncompressed bytes)
  for i=1 to num, j=[src], dict1[j]=[src+1], dict2[j]=[src+2], src=src+3
 @@decompress_lop:
  x=[src], src=src+1
  if x=dict1[x] then
    if x=esc then x=[src], src=src+1, if x=00h then goto @@decompress_done
    [dst]=x, dst=dst+1
  else
    heap[0]=x, i=1
    while i>0
      i=i-1, x=heap[i], a=dict1[x]
      if a=x then [dst]=x, dst=dst+1                    ;\output data to
      else b=dict2[x], heap[i]=b, heap[i+1]=a, i=i+2    ;/dst or heap
  goto @@decompress_lop
 @@decompress_done:
  if (dst-dst_base)<>dst_size then error
  ret


CDROM File Compression EA Methods (RLE)

Run-Length Encoding
  000h 2    Method (4AFBh=24bit or 4A31h=16bit) (big-endian)
  ...  2/3  Decompressed Size (24bit or 16bit)  (big-endian)
  ...  ..   Compressed Data
Compression codes are:
  00h..3Fh  Copy 0..3Fh uncompressed bytes
  40h..7Fh  Load new fillbyte and fill 0..3Fh bytes
  80h..BFh  Use old fillbyte and fill 0..3Fh bytes (initial fillbyte=00h)
  C0h..FFh  Copy 0..3Fh bytes with constant value in upper 4bit
decompress_bpe:
  method=BigEndian16bit[src], src=src+2
  if (method AND 00FFh)=31h then dst_size=BigEndian16bit[src], src=src+2
  if (method AND 00FFh)<>31h then dst_size=BigEndian24bit[src], src=src+3
  fillbyte=00h  ;initially zero
 @@decompress_lop:
  type=[src]/40h, len=[src] AND 3Fh, src=src+1, dst_size=dst_size-len
  if type=0 then                                          ;\uncompressed bytes
    for i=1 to len, [dst]=[src], src=src+1, dst=dst+1     ;/
  elseif type=1 then                                      ;\
    fillbyte=[src], src=src+1                             ; fill with new dat
    for i=1 to len, [dst]=fillbyte, dst=dst+1             ;/
  elseif type=2 then                                      ;\fill with old dat
    for i=1 to len, [dst]=fillbyte, dst=dst+1             ;/
  elseif type=3 then
    x=[src], [dst]=x, src=src+1, dst=dst+1, x=x AND F0h
    for i=2 to len      ;<-- or so?
      if (i AND 1)=0 then [dst]=x+([src]/10h) dst=dst+1
      if (i AND 1)=1 then [dst]=x+([src] AND 0Fh), dst=dst+1, src=src+1
  if dst_size<>0 then goto @@decompress_lop
  ret


CDROM File Compression ZIP/GZIP/ZLIB (Inflate/Deflate)

Inflate/Deflate is a common (de-)compression algorithm, used by ZIP, ZLIB, and GZIP.

Inflate - Core Functions
Inflate - Initialization & Tree Creation
Inflate - Headers and Checksums

PSX Disk Images
In PSX cdrom-images, ZLIB is used by the .CDZ cdrom-image format:
CDROM Disk