SAR files are simple structures that are encrypted with Blowfish with a 32-bit key and optionally compressed with PRS and a 0x95 XOR.
All sar files start with the magic ASCII string sar
and, at position 0x03, either the byte 0x84
or 0x04
.
SAR files are always encrypted. Unlike Blue Burst, PSO2 does not use special P/S tables for Blowfish, and uses the full number of rounds.
- Initialize Blowfish cipher with 32-bit key
[0x09, 0x07, 0xc1, 0x2b]
(TK is this correct?) - Decrypt the maximum multiple of 8 bytes, starting from 0x04 in the SAR buffer to the end. e.g. if the size of the sar file is 36 bytes, decrypt the last 32 bytes in the file. If it is 37 bytes, decrypt everything except the first 4 bytes and the last byte. The trailing bytes will show up as plaintext in the file if you examine it with a Hex editor. Yes, it's that broken. (If it's compressed, you'll see 0x95 at the end probably)
Now, if the 0x03 byte (flag) is 0x84
, that means the buffer is still compressed. If it is 0x04
, it is not compressed, so skip these steps.
- XOR every byte in the buffer from 0x04 to the end by 0x95.
- PRS decompress the buffer.
Here is a Rust struct example representing the payload.
struct Header {
authorId: u32, // big endian
layers: u8,
sizeHeight: u8, // strange encoding, 0x80 for 96, 0x40 for 32 team flag?
sizeWidth: u8, // strange encoding, 0xC1 for 192, 0x40 for 32 team flag?
soundEffect: u8, // the sound effect used
}
struct Payload {
header: Header,
layers: Vec<Layer>, // Exactly the number of layers specified in the header
name: Vec<u16>, // UTF-16LE characters. Up to 13 characters; no null byte
}
// TK Have not verified the order of this
struct Layer {
topLeft: Position,
bottomLeft: Position,
topRight: Position,
bottomRight: Position,
color: u16, // TK unconfirmed
symbol: u16, // TK unconfirmed
_unknown: u32, // ???
}
// TK This might also be y, x...
struct Position {
x: u8, // 0x80 is the "origin" of the symbol art. So 0x70 is -16 from origin
y: u8, // Same as above
}
A layer is 4 vertices representing a quad of two triangles. These are drawn as a triangle strip with a square symbol texture indexed by the symbol
field. It is colorized using the color
field. The lower the layer number, the higher in the stack; so when drawing, draw from the last layer first and go backwards.