Skip to content

Commit 53ba5c1

Browse files
authored
BYOND Resource Cache format (#483)
thanks!
1 parent 384a111 commit 53ba5c1

3 files changed

Lines changed: 84 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi
190190
| ReFS | | [`patterns/refs.hexpat`](patterns/fs/refs.hexpat) | Microsoft Resilient File System |
191191
| RGBDS | | [`patterns/rgbds.hexpat`](patterns/rgbds.hexpat) | [RGBDS](https://rgbds.gbdev.io) object file format |
192192
| RPM | | [`patterns/rpm.hexpat`](patterns/rpm.hexpat) | [RPM](http://ftp.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html) package file format |
193+
| RSC | | [`patterns/rsc.hexpat`](patterns/rsc.hexpat) | BYOND Resource Cache file |
193194
| SDB | | [`patterns/sdb.hexpat`](patterns/sdb.hexpat) | [Shim DataBase](https://learn.microsoft.com/en-us/windows/win32/devnotes/application-compatibility-database) file format |
194195
| Shell Link | `application/x-ms-shortcut` | [`patterns/lnk.hexpat`](patterns/lnk.hexpat) | Windows Shell Link file format |
195196
| shp | | [`patterns/shp.hexpat`](patterns/shp.hexpat) | ESRI shape file |

patterns/rsc.hexpat

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#pragma author itsmeow
2+
#pragma description BYOND Resource Cache file
3+
4+
import std.mem;
5+
import std.time;
6+
import std.hash;
7+
8+
// Whether to validate CRC32 for RSCEntries or not.
9+
const bool check_crc = true;
10+
11+
using time_t = std::time::EpochTime [[format("format_unix_time")]];
12+
fn format_unix_time(time_t t) {
13+
return std::time::format(std::time::to_local(t), "%c");
14+
};
15+
16+
// Based on alexkar598/rsc-tools
17+
// Copyright (c) alexkar598
18+
// Permissioned granted for use in pattern
19+
enum Type : u8 {
20+
// Unknown file
21+
Unknown = 0x0,
22+
// Sequencer file (.mid, .midi, .mod, .s3m, .xm, .it, .oxm)
23+
Sequencer = 0x1,
24+
// Audio file (.wav, .ogg, .raw, .wma, .aiff, .mp3)
25+
Audio = 0x2,
26+
// Sprite sheet file (.dmi)
27+
SpriteSheet = 0x3,
28+
// Bitmap file (.bmp)
29+
Bitmap = 0x5,
30+
// Lossless image file (.png)
31+
LosslessImage = 0x6,
32+
// Archive file (.zip)
33+
Archive = 0x9,
34+
// Resource archive file (.rsc)
35+
Resource = 0xa,
36+
// Lossy image file (.jpg, .jpeg)
37+
LossyImage = 0xb,
38+
// Dynamic sprite sheet file (.ddmi)
39+
DynamicSpriteSheet = 0xc,
40+
// Animated image file (.gif)
41+
AnimatedImage = 0xd,
42+
// Font file (.ttf)
43+
Font = 0xe,
44+
};
45+
46+
struct RSCEntry {
47+
u32 byteLength [[comment("Size of the entry, starting after this field")]];
48+
u8 used [[comment("Whether or not this entry is used in the DMB. This is updated on a rebuild to 'skip' now unused entries")]];
49+
if(used) {
50+
u8 type_raw;
51+
Type type = type_raw [[export]];
52+
bool encrypted = (type_raw & 0b10000000) == 0b10000000 [[export, comment("Encrypted entries are unable to have their content decoded at this time")]];
53+
u32 checksum_crc32 [[comment("CRC32 of content array with a poly of 0xaf and a XOR-in/init of 0xffffffff")]];
54+
time_t modified [[comment("Unix/epoch timestamp of modification datetime")]];
55+
time_t added [[comment("Unix/epoch timestamp of creation/addition to RSC datetime")]];
56+
u32 contentLength [[comment("Length of the content array")]];
57+
char filename[] [[comment("Name of the source file")]];
58+
59+
u32 expectedContentLength = byteLength - 17 - sizeof(filename);
60+
// Use the encoded length rather than computed length
61+
// because this is what is used for checksum calculation
62+
u8 content [contentLength];
63+
if (contentLength < expectedContentLength) {
64+
// continue to end of block
65+
// RSC files can contain leftover data, much like a filesystem with unlinked files
66+
padding [expectedContentLength - contentLength];
67+
}
68+
if (expectedContentLength != contentLength) {
69+
u32 computedByteLength = contentLength + 17 + sizeof(filename);
70+
std::warning(std::format("Length mismatch for RSCEntry at 0x{:x}: contentLength={} expectedContentLength={} byteLength={} computedByteLength={}", $, contentLength, expectedContentLength, byteLength, computedByteLength));
71+
}
72+
if (check_crc) {
73+
u32 computed_checksum_crc32 = std::hash::crc32(content, 0xffffffff, 0xaf, 0, false, false) [[export]];
74+
if(computed_checksum_crc32 != checksum_crc32) {
75+
std::warning(std::format("Checksum mismatch at 0x{:x}: stored={} computed={}", $, checksum_crc32, computed_checksum_crc32));
76+
}
77+
}
78+
} else {
79+
u8 content [ byteLength ];
80+
}
81+
};
82+
83+
RSCEntry entries[while(!std::mem::eof())] @ 0x00;
3.09 KB
Binary file not shown.

0 commit comments

Comments
 (0)