Remedy Archive System
From Just Solve the File Format Problem
(Difference between revisions)
RavuAlHemio (Talk | contribs) m (MP1 crypt: ensure last value fits in a byte) |
RavuAlHemio (Talk | contribs) (add general structure of metadata) |
||
| (2 intermediate revisions by one user not shown) | |||
| Line 11: | Line 11: | ||
== Format details == | == Format details == | ||
| + | |||
| + | Numbers are in little-endian byte order. | ||
The file's header has the following structure: | The file's header has the following structure: | ||
| Line 24: | Line 26: | ||
// decrypted structure | // decrypted structure | ||
struct RASMetadata { | struct RASMetadata { | ||
| − | + | uint32_t unknown0; | |
| + | uint32_t unknown1; | ||
| + | uint32_t centralDirectoryLength; | ||
| + | uint32_t unknown3; | ||
| + | float32_t version; /* binary32 according to IEEE 754 */ | ||
| + | uint32_t unknown5; | ||
| + | uint32_t unknown6; | ||
| + | uint32_t unknown7; | ||
| + | uint32_t compatibility; | ||
}; | }; | ||
</pre> | </pre> | ||
| Line 35: | Line 45: | ||
<pre> | <pre> | ||
| − | + | void decrypt(uint8_t *buf, size_t count, int32_t key) { | |
| − | if key == 0 | + | size_t i; |
| − | key | + | uint8_t a; |
| − | + | uint8_t b; | |
| − | for i | + | |
| − | a | + | if (key == 0) { |
| − | b | + | 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); | |
| − | + | } | |
| − | + | } | |
| − | + | ||
| − | + | ||
| − | + | ||
</pre> | </pre> | ||
| − | If your programming language doesn't support the <code> | + | If your programming language doesn't support the <code>rotateLeftByte</code> operation, it can be emulated using: |
<pre> | <pre> | ||
| − | + | uint8_t rotateLeftByte(uint8_t a, uint8_t b) { | |
| − | return ( | + | return (a << b) | (a >> (8 - b)); |
| + | } | ||
</pre> | </pre> | ||
[[Category:Remedy Entertainment]] | [[Category:Remedy Entertainment]] | ||
Latest revision as of 16:47, 21 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 |
[edit] Identification
Files begin with signature bytes 52 41 53 00.
[edit] 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 unknown0;
uint32_t unknown1;
uint32_t centralDirectoryLength;
uint32_t unknown3;
float32_t version; /* binary32 according to IEEE 754 */
uint32_t unknown5;
uint32_t unknown6;
uint32_t unknown7;
uint32_t compatibility;
};
[edit] Encryption
Depending on the generation of the RAS file format, different encryption schemes are used.
[edit] 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));
}