Remedy Archive System
RavuAlHemio (Talk | contribs) (add general structure of metadata) |
RavuAlHemio (Talk | contribs) (document file list entry structure) |
||
| Line 26: | Line 26: | ||
// decrypted structure | // decrypted structure | ||
struct RASMetadata { | struct RASMetadata { | ||
| − | uint32_t | + | uint32_t fileCount; |
uint32_t unknown1; | uint32_t unknown1; | ||
| − | uint32_t | + | uint32_t fileListLength; |
uint32_t unknown3; | uint32_t unknown3; | ||
| − | float32_t version; / | + | float32_t version; // binary32 according to IEEE 754 |
uint32_t unknown5; | uint32_t unknown5; | ||
uint32_t unknown6; | uint32_t unknown6; | ||
uint32_t unknown7; | uint32_t unknown7; | ||
uint32_t compatibility; | uint32_t compatibility; | ||
| + | }; | ||
| + | </pre> | ||
| + | |||
| + | The following version and compatibility values are known: | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! Version | ||
| + | ! Compatibility | ||
| + | ! Game | ||
| + | |- | ||
| + | || 1.2 || 3 || Max Payne | ||
| + | |} | ||
| + | |||
| + | What follows are <code>fileListLength</code> encrypted bytes of file metadata that can be decrypted using <code>encryptionKey</code>. (It is not necessary to remember the last value of <code>key</code> from decrypting <code>RASMetadata</code>.) Each entry has the following structure: | ||
| + | |||
| + | <pre> | ||
| + | // decrypted structure | ||
| + | struct RASFileListEntry { | ||
| + | char name[]; // NUL-terminated | ||
| + | uint32_t unknown0; | ||
| + | uint32_t unknown1; | ||
| + | uint32_t unknown2; | ||
| + | uint32_t unknown3; | ||
| + | uint32_t unknown4; | ||
| + | uint32_t unknown5; | ||
| + | uint32_t unknown6; | ||
| + | uint32_t unknown7; | ||
| + | uint32_t unknown8; | ||
| + | uint32_t unknown9; | ||
}; | }; | ||
</pre> | </pre> | ||
Revision as of 07:05, 22 November 2025
Remedy Archive System is used to store game data for Remedy Entertainment games such as Max Payne and Max Payne 2. The metadata (central directory) following the header is encrypted.
Contents |
Identification
Files begin with signature bytes 52 41 53 00.
Format details
Numbers are in little-endian byte order.
The file's header has the following structure:
struct RASHeader {
uint8_t magic[4]; // "RAS\0"
uint32_t encryptionKey;
};
The next section of the header must be decrypted first:
// decrypted structure
struct RASMetadata {
uint32_t fileCount;
uint32_t unknown1;
uint32_t fileListLength;
uint32_t unknown3;
float32_t version; // binary32 according to IEEE 754
uint32_t unknown5;
uint32_t unknown6;
uint32_t unknown7;
uint32_t compatibility;
};
The following version and compatibility values are known:
| Version | Compatibility | Game |
|---|---|---|
| 1.2 | 3 | Max Payne |
What follows are fileListLength encrypted bytes of file metadata that can be decrypted using encryptionKey. (It is not necessary to remember the last value of key from decrypting RASMetadata.) Each entry has the following structure:
// decrypted structure
struct RASFileListEntry {
char name[]; // NUL-terminated
uint32_t unknown0;
uint32_t unknown1;
uint32_t unknown2;
uint32_t unknown3;
uint32_t unknown4;
uint32_t unknown5;
uint32_t unknown6;
uint32_t unknown7;
uint32_t unknown8;
uint32_t unknown9;
};
Encryption
Depending on the generation of the RAS file format, different encryption schemes are used.
RAS1 (Max Payne)
void decrypt(uint8_t *buf, size_t count, int32_t key) {
size_t i;
uint8_t a;
uint8_t b;
if (key == 0) {
key = 1;
}
for (i = 0; i < count; i++) {
uint8_t a = buf[i];
uint8_t b = ((uint8_t)(i % 5)) & 7;
buf[i] = rotateLeftByte(a, b);
key = key * 171 + (key / 177) * -30269;
buf[i] = (((((uint8_t)i) + 3) * 6) ^ buf[i]) + ((uint8_t)key);
}
}
If your programming language doesn't support the rotateLeftByte operation, it can be emulated using:
uint8_t rotateLeftByte(uint8_t a, uint8_t b) {
return (a << b) | (a >> (8 - b));
}