// Based on https://github.com/mietek/theunarchiver/wiki/StuffIt5Format and https://github.com/ParksProjets/Maconv/blob/master/docs/stuffit/Stuffit_v5.md #pragma endian big #pragma MIME application/x-stuffit #include #include namespace v5 { bitfield Flags1 { padding : 1; folder : 1; encrypted : 1; padding : 5; }; bitfield Flags2 { padding : 7; resource_fork : 1; padding : 8; }; using MacOSHFSPlusDate = u32 [[format("v5::format_macos_date")]]; fn format_macos_date(MacOSHFSPlusDate date) { return std::time::format(std::time::to_utc(date - 2082844800)); }; struct Header { char magic[0x50]; u32 unknown1; u32 archiveSize; u32 entriesOffset; u32 unknown2; }; struct EntryHeader { u32 magic; u8 version; u8 unknown1; u16 headerSize; u8 unknown2; Flags1 flags; MacOSHFSPlusDate creationDate, modificationDate; u32 prevEntryOffset, nextEntryOffset, parentEntryOffset; u16 nameSize; u16 headerChecksum; u32 dataForkUncompressedLength, dataForkCompressedLength; u16 dataForkChecksum; u16 unknown3; }; enum CompressionMethod : u8 { None = 0x00, // No compression Rle90 = 0x01, // Run length encoding Compress = 0x02, // Compress LZW algorithm, 14 bit max code length, block mode StuffIt3 = 0x03, // Simple Huffman encoding for individual bytes StuffIt5 = 0x05, // LZAH StuffIt8 = 0x08, // Miller-Wegman StuffIt13 = 0x0D, // LZSS and Huffman Stuffit14 = 0x0E, // Unknown StuffItArsenic = 0x0F // BWT and arithmetic coding }; struct Entry { EntryHeader header; if (header.flags.folder) { u16 numFiles; if (header.dataForkUncompressedLength) std::print("Folder entry {} is special!", std::core::array_index()); } else { CompressionMethod compressionMethod; } u8 passwordDataLength; u8 passwordInformation[passwordDataLength]; char fileName[header.nameSize]; u16 commentSize; u16 unknown2; char comment[commentSize]; if (!header.flags.folder) { Flags2 flags; u16 unknown3; char fileType[4]; u32 fileCreator; u16 macOSFinderFlags; u32 unknown4; u32 unknown5; u8 unknown6[6]; if (header.version == 1) u32 unknown7; u8 compressedData[header.dataForkCompressedLength] [[sealed]]; if (header.nextEntryOffset == 0x00) break; else $ = header.nextEntryOffset; } }; struct StuffIt { Header header; Entry entries[while(true)] @ header.entriesOffset; }; } v5::StuffIt stuffIt @ 0x00;