//------------------------------------------------ //--- 010 Editor v12.0.1 Binary Template // // File: SCcom.bt // Authors: fourk (github.com/FourCinnamon0), DaniilSV (github.com/Daniil-SV) // Version: 1.2 // Purpose: SupercellSWF compressed assets. // Category: Game // File Mask: *.sc // ID Bytes: 53 43 // History: // 1.2 2023-07-30 DaniilSV: Added Metadata. // 1.1 2023-01-22 DaniilSV: Improved compression type detection. // 1.0 2023-01-05 DaniilSV: Bugfix. Added proper metadata chunk detection. // 0.9 2022-04-12 FourCinnamon0: Added LZMA info // 0.8 2021-11-11 FourCinnamon0: First released version //------------------------------------------------ /* Comment format fuctnions */ string LZMA_props(ubyte& prop) { local string s; local string t; local ubyte lc, lp, pb; if (prop > (4 * 5 + 4) * 9 + 8) { t = "Invalid LZMA properties"; } else { pb = prop / (9 * 5); // Position bits lp = (prop - (pb * 9 * 5)) / 9; // Literal position bits lc = (prop - (pb * 9 * 5)) - lp * 9; // Literal context bits SPrintf(t, "Position bits: %d, Literal position bits: %d, Literal context bits: %d", pb, lp, lc); } return t; }; string ScDataVersion(uint32& index) { switch (index) { case 1: return "Has LZMA or LZHAM compression"; case 3: return "Has ZSTD compression"; case 4: return "Has metadata"; default: return "Unknown"; } }; /* Structs */ typedef struct { LittleEndian(); local uint32 header_ptr = FileSize() - 6; FSeek(header_ptr); char data_size_flags; local uchar hash_data_field_size = 0; local uint32 strings_data_array_ptr = 0; local uchar unknown_bool = 1; if ((data_size_flags & 0xFC) == 0x24) { uchar field_size; local uint32 header_data_ptr = header_ptr - field_size; local char string_data_bits_offset = data_size_flags & 3; local char data_field_size = 1 << (data_size_flags & 3); FSeek(header_data_ptr); if (field_size > 3) { int data_info_offset; } else if (field_size <= 1) { uchar data_info_offset; } else { ushort data_info_offset; } local uint32 data_info_ptr = header_data_ptr - data_info_offset; if (data_field_size > 3u) { local uint32 strings_data_offset = 0xFFFFFFFD << string_data_bits_offset; if (data_field_size >= 8u) { local uint32 string_data_ptr = data_info_ptr + strings_data_offset; FSeek(data_info_ptr - data_field_size); uint32 asset_total_count; data_field_size = 8; field_hash_size = 8; FSeek(string_data_ptr); uint32 strings_array_offset; strings_data_array_ptr = strings_data_offset - strings_array_offset; unknown_bool = 0; FSeek(string_data_ptr + 1 << (data_size_flags & 3)); char strings_field_size; } else { hash_data_field_size = 1 << (data_size_flags & 3); FSeek(data_info_ptr - data_field_size); uint32 asset_total_count; FSeek(data_info_ptr + strings_data_offset + data_field_size); uint32 strings_field_size; FSeek(data_info_ptr + strings_data_offset); uint32 strings_array_offset; strings_data_array_ptr = data_info_ptr + (strings_data_offset - strings_array_offset); unknown_bool = 0; } } else if (data_field_size > 1u) { local uint32 strings_data_offset = data_info_ptr + (0xFFFFFFFD << string_data_bits_offset); hash_data_field_size = 1 << (data_size_flags & 3); FSeek(strings_data_offset); ushort strings_array_offset; strings_data_array_ptr = strings_data_offset - strings_array_offset; FSeek(strings_data_offset + data_field_size); ushort strings_field_size; FSeek(data_info_ptr - data_field_size); ushort asset_total_count; unknown_bool = 1; } else { FSeek(data_info_ptr - data_field_size); uchar asset_total_count; FSeek(data_info_ptr + 0xFFFFFFFE); uchar strings_field_size; FSeek(data_info_ptr + 0xFFFFFFFD); uchar strings_array_offset; strings_data_array_ptr = data_info_ptr + (0xFFFFFFFD - strings_array_offset); data_field_size = 1; hash_data_field_size = 1; } if (!asset_total_count) return; } else { Assert(0, "Invalid file"); } FSeek(strings_data_array_ptr - strings_field_size); if (strings_field_size > 3) { uint32 strings_total_count; } else if (strings_field_size > 1) { uint16 strings_total_count; } else { uchar strings_total_count; } FSeek(data_info_ptr - data_field_size); if (unknown_bool) { if (hash_data_field_size > 1u) { ushort hash_total_count; } else { uchar hash_total_count; } } else { uint32 hash_total_count; } local uint32 asset_index = 0; local uint32 hash_flags_array_ptr = data_info_ptr + (hash_total_count * data_field_size); while (asset_total_count > asset_index) { struct ExportName { local uint32 asset_export_names_ptr = strings_data_array_ptr + asset_index * strings_field_size; if (strings_total_count > asset_index) { FSeek(asset_export_names_ptr); if (strings_field_size > 3) { uint32 asset_export_name_offset; } else if (strings_field_size <= 1) { uchar asset_export_name_offset; } else { ushort asset_export_name_offset; } FSeek(asset_export_names_ptr - asset_export_name_offset); string export_name; } if (hash_total_count > asset_index) { FSeek(hash_flags_array_ptr + asset_index); uchar hash_data_flags; local uchar is_valid_hash = hash_data_flags >> 2 == 0x19; if (!is_valid_hash) is_valid_hash = hash_data_flags >> 2 == 5; if (is_valid_hash) { local int hash_field_size = 1 << (hash_data_flags & 3); local uint32 hash_offset_array_ptr = data_info_ptr + (asset_index * data_field_size); FSeek(hash_offset_array_ptr); if (hash_data_field_size > 3u) { uint32 hash_offset; } else if (hash_data_field_size <= 1u) { uchar hash_offset; } else { uint16 hash_offset; } local uint32 hash_record_ptr = hash_offset_array_ptr - hash_offset; FSeek(hash_record_ptr - hash_field_size); if (hash_field_size > 3u) { uint32 hash_size; } else if (hash_field_size > 1u) { uint16 hash_size; } else { uchar hash_size; } char hash[hash_size] ; } } // This line is just to fix // "Invalid structure. Ending position was in front of starting position." // error FSeek(FileSize()); } exportName ; asset_index++; } } Metadata; BigEndian(); struct FILE { char magic[2] ; Assert(magic == "SC", "File magic is broken"); local uint32 version = 0; uint32 fileVersion ; if (fileVersion == 4) { uint32 dataVersion ; version = dataVersion; } else { version = fileVersion; } uint32 hash_size ; char hash[hash_size] ; struct DATA { local uint32 compressed_data_size = FileSize() - FTell(); if (fileVersion == 4) { local uint32 pos = FTell(); FSeek(FileSize() - 4); uint metadata_size; compressed_data_size -= metadata_size + 9; Metadata data ; FSeek(pos); } LittleEndian(); switch (version) { case 1: local uint compression_header = ReadUInt(); // if LZHAM header if (compression_header == 1514947411) { char lzham_header[4]; uchar dict_size; uint32 uncompressed_size; char compressed_data[compressed_data_size - 10] ; } else { ubyte properties; uint32 dict_size; uint32 uncompressed_size; char compressed_data[compressed_data_size - 9] ; } break; case 3: char compressed_data[compressed_data_size] ; break; default: char compressed_data[compressed_data_size] ; break; } } data; } file;