Skip to content

Commit

Permalink
patterns: Add bcss (BeyondCompare SnapShot) file (#338)
Browse files Browse the repository at this point in the history
* patterns: add bcss (BeyondCompare SnapShot) file

* Add entry to readme

* Change table entries in alphabetical order

* Support both bcss file and uncompressed content

* Remove misleading cases, add warning message

* Add test cases to bcss.hexpat

* ifdef out ImHex-only functionality

---------

Co-authored-by: Nik <[email protected]>
  • Loading branch information
ttimasdf and WerWolv authored Jan 13, 2025
1 parent 1f6c701 commit fdc4a87
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi
| ARIA2 | | [`patterns/aria2.hexpat`](patterns/aria2.hexpat) | ARIA2 Download Manager Control files |
| ARM VTOR | | [`patterns/arm_cm_vtor.hexpat`](patterns/arm_cm_vtor.hexpat) | ARM Cortex M Vector Table Layout |
| Bastion | | [`patterns/bastion/*`](https://gitlab.com/EvelynTSMG/imhex-bastion-pats) | Various [Bastion](https://en.wikipedia.org/wiki/Bastion_(video_game)) files |
| BeyondCompare BCSS | | [`patterns/bcss.hexpat`](patterns/bcss.hexpat) | BeyondCompare Snapshot (BCSS) file |
| Bencode | `application/x-bittorrent` | [`patterns/bencode.hexpat`](patterns/bencode.hexpat) | Bencode encoding, used by Torrent files |
| Prusa BGCODE | | [`patterns/bgcode.hexpat`](patterns/bgcode.hexpat) | PrusaSlicer Binary G-Code files |
| BLEND | | [`patterns/blend.hexpat`](patterns/blend.hexpat) | Blender Project file |
Expand Down
168 changes: 168 additions & 0 deletions patterns/bcss.hexpat
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#pragma author ttimasdf
#pragma description BeyondCompare Snapshot (BCSS) file

#pragma magic [42 43 53 53] @ 0x00

#pragma array_limit 4294967295
#pragma pattern_limit 4294967295


import std.io;
import std.mem;
import std.array;
import std.string;
import type.magic;

#ifdef __IMHEX__
import hex.dec;
import hex.core;
#endif


const u8 max_path_size = 1000;
str current_path[max_path_size];
u8 current_path_level = 0;

enum EntryType : u8 {
DIRECTORY = 0x01,
FILE = 0x02,
SYMLINK = 0x03,
// NULL = 0x00,
DIR_END = 0xFF,
};

struct BCSSEntry {
EntryType type;
match (type) {
(EntryType::DIRECTORY) : {
// FileName name;
std::string::SizedString<u8> name;
if (name.size != 0) {
u8 unknown[12];
on_dir_enter(name.data); // std::string::to_string(name)
} else {
// some buggy edge cases
u8 unknown[6];
std::warning(std::format("invalid empty entry current_lvl={} current_pos=0x{:02x}", current_path_level, $));
}
}
(EntryType::FILE) : {
std::string::SizedString<u8> name;
if (name.size != 0) {
u8 unknown[20];
#ifdef __IMHEX__
hex::core::add_virtual_file(get_vfs_path(name), this); // std::string::to_string(name)
#endif
} else {
// some buggy edge cases
u8 unknown[6];
std::warning(std::format("invalid empty entry current_lvl={} current_pos=0x{:02x}", current_path_level, $));
}
//try {
// u8 unknown[20];
//} catch {
// u8 unknown[0];
//}
}
(EntryType::SYMLINK) : {
std::string::SizedString<u8> name;
u8 unknown[23];
std::string::SizedString<u8> target;
#ifdef __IMHEX__
hex::core::add_virtual_file(get_vfs_path(name + " [s]"), this); // std::string::to_string(name)
#endif
}
(EntryType::DIR_END) : {
on_dir_exit();
}
// (EntryType::NULL) : {
// // some buggy edge cases
// u8 unknown[7];
// std::warning(std::format("invalid empty entry current_lvl={} current_pos=0x{:02x}", current_path_level, $));
// }
(_): {
std::error(std::format("unknown EntryType idx={} current_pos=0x{:02x}", std::core::array_index(), $));
}
}
}[[format_read("fmt_BCSSEntry")]];

fn on_dir_enter(str folder_name) {
// std::print("on_dir_enter folder={} current_lvl={}", folder_name, current_path_level);
if (std::string::length(folder_name) > 0) {
current_path[current_path_level] = folder_name;
current_path_level += 1;
} else {
std::warning(std::format("invalid folder name {} current_lvl={} current_pos=0x{:02x}", folder_name, current_path_level, $));
}
};

fn on_dir_exit() {
if (current_path_level > 0) {
current_path_level -= 1;
} else if (!std::mem::eof()) {
std::warning(std::format("on_dir_exit current_lvl already == 0 current_pos=0x{:02x}", $));
}
// std::print("on_dir_exit current_lvl={}", current_path_level);
};

fn get_vfs_path(str file_name) {
str vfs_path = "";
if (current_path_level > 0) {
vfs_path = current_path[0];
for(u8 i = 1, i < current_path_level, i += 1) {
//hash_hex = hash_hex + std::format("{:02X}",bytes[i]);
vfs_path = vfs_path + "/" + current_path[i];
}
return vfs_path + "/" + file_name;
} else {
return file_name;
}
};


fn fmt_BCSSEntry(BCSSEntry e) {
try {
match (e.type) {
(EntryType::DIRECTORY | EntryType::FILE) : {
return std::format("{}: {}", (e.type == EntryType::DIRECTORY ? "Dir" : "File"), e.name.data);
}
(EntryType::SYMLINK) : {
return std::format("Symlink: {} -> {}", e.name.data, e.target.data);
}
(EntryType::DIR_END) : {
return "Directory End";
}
}
} catch {
return "[FmtErr]";
}
};

struct BCSSFile {
if (std::mem::read_unsigned(0, 4) == 0x53534342) {
type::Magic<"BCSS"> magic;
u8 unknown[14];
std::string::SizedString<u16> root_path;

u8 zlib_content[std::mem::size()-$];

// manually add zlib header which is essential for hex::dec::zlib_decompress
const str zlib_header = "\x78\x9c";
std::mem::Section zlib_compressed = std::mem::create_section("zlib_compressed");
std::mem::copy_value_to_section(zlib_header, zlib_compressed, 0);
std::mem::copy_value_to_section(zlib_content, zlib_compressed, 2);
u8 zlib[std::mem::get_section_size(zlib_compressed)] @ 0x00 in zlib_compressed;

#ifdef __IMHEX__
std::mem::Section zlib_decompressed = std::mem::create_section("zlib_decompressed");
hex::dec::zlib_decompress(zlib, zlib_decompressed, 15);
u8 decompressed_content[std::mem::get_section_size(zlib_decompressed)] @ 0x00 in zlib_decompressed;
hex::core::add_virtual_file("bcss_content", decompressed_content);
std::warning("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n! BCSS file content is compressed !\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\nopen `bcss_content` file from the Virtual Filesystem tab and run this pattern on it.");
#endif
} else {
BCSSEntry entries[while(!std::mem::eof())];
}
};

BCSSFile bcss_file @ 0x00;
Binary file added tests/patterns/test_data/bcss.hexpat/test.bcss
Binary file not shown.
Binary file not shown.

0 comments on commit fdc4a87

Please sign in to comment.