Fix Stuffit5 conv and some code cleanup.

This commit is contained in:
Guillaume Gonnet 2019-03-10 11:12:10 +01:00
parent ea24dbaeff
commit e54e2f5b5e
12 changed files with 132 additions and 124 deletions

View File

@ -70,7 +70,7 @@ set(MACONV_SRC
"src/main.cc"
)
# Include "src" and "vendors" folder for resolving #include.
# Include "src" and "vendors" folders for resolving #include.
include_directories("src" "vendors")

View File

@ -16,7 +16,7 @@ Data are arranged as follows:
| **Offset** | **Length** | **Contents** |
|:-----------|:-----------|:-------------|
| 000 | Byte | Always zero |
| 000 | Byte | Always zero |
| 001 | Byte | Length of filename (in the range 1-31) |
| 002 | 63 Bytes | Filename (remaning bytes are zero) |
| 065 | Word | File type (4 characters) |
@ -37,7 +37,7 @@ Data are arranged as follows:
| 102 **³** | Word | Signature for indentification purposes (always `mBIN`) |
| 106 **³** | Byte | Script of file name (from the fdScript field of an fxInfo record) |
| 107 **³** | Byte | Extended Finder flags (from the fdXFlags field of an fxInfo record) |
| 108-115 | | Unused (must be zeroed by creators, must be ignored by readers) |
| 108 | 8 Bytes | Unused (must be zeroed by creators, must be ignored by readers) |
| 116 **²** | Word | Length of total files when packed files are unpacked. As of the writing of this document, this field has never been used. |
| 120 **²** | Half | Length of a secondary header. If this is non-zero, skip this many bytes (rounded up to the next multiple of 128). This is for future expansion only, when sending files with MacBinary, this word should be zero. |
| 122 **²** | Byte | Version number of MacBinary (`129` for MacBinary II, `130` for MacBinary III) |
@ -52,13 +52,13 @@ Cyclic redundancy check (CRC) used in header is CRC-16-CCITT, i.e. uses
polynomial number `0x1021` and starts with `0`.
## Data and ressource forks.
## Data and ressource forks
Data fork directly follow the header (at byte 128). The length of these data
Data fork directly follows the header (at byte 128). The length of these data
(which can be zero) must correspond with the length given in the header (at byte
83).
Data are completed with some padding bytes (normally `0x00` but some
Data are completed with some padding bytes (usually `0x00` but some
implementations use `0x7F`) until the total length of file is a multiple of 128.
If the total length is already a multiple of 128 after adding the data fork, no
padding is added.

View File

@ -31,37 +31,37 @@ namespace conv {
// Precalculed CRC 16 table.
static uint16_t kCrc16Table[256] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
};
@ -178,7 +178,7 @@ void WriteMacBinary(fs::File &file, fs::FileWriter &base)
writer.Fill(0x0, 2);
writer.WriteByte(file.flags & 0xFF);
// Write MacBinray III magic strings/numbers.
// Write MacBinary III magic strings/numbers.
writer.WriteString("mBIN");
writer.Fill(0x0, 16);
writer.WriteByte(130);

View File

@ -39,7 +39,7 @@ extern "C" void LogDebug(const char *fmt, ...)
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
vprintf(fmt, args);
va_end(args);
printf("\n");
}

View File

@ -320,7 +320,7 @@ constexpr int kLzssEnd = -2;
void Algorithm13Method::InitializeLZSS()
{
int val = *(input.data++);
int code = (val >> 4);
int code = ((val & 0xFF) >> 4);
if (code == 0) {
HuffmanDecoder metacode;

View File

@ -60,10 +60,10 @@ struct Algorithm13Method : CompressionMethod {
utils::BitReaderLE input;
std::unique_ptr<uint8_t[]> window_buffer;
int match_length, match_offset;
int match_length, match_offset;
HuffmanDecoder firstcode, secondcode, offsetcode;
HuffmanDecoder *currcode;
HuffmanDecoder *currcode;
};

View File

@ -38,9 +38,9 @@ void CompressLzw::Initialize(int max_symbols, int reserved_symbols)
nodes = std::make_unique<CompressTreeNode[]>(max_symbols);
for (int i = 0; i < 256; i++) {
nodes[i].chr = i;
nodes[i].parent = -1;
}
nodes[i].chr = i;
nodes[i].parent = -1;
}
ClearTable();
}
@ -93,10 +93,10 @@ void CompressLzw::NextSymbol(int symbol)
// Calculate the number of bytes needed to write the output.
int CompressLzw::CalcOutputLength()
{
int n = 0;
for (int symbol = prev_symbol; symbol >= 0; n++)
symbol = nodes[symbol].parent;
return n;
int n = 0;
for (int symbol = prev_symbol; symbol >= 0; n++)
symbol = nodes[symbol].parent;
return n;
}
@ -106,10 +106,10 @@ void CompressLzw::OutputToBuffer(int len, uint8_t *buffer)
int symbol = prev_symbol;
buffer += len;
while (symbol >= 0) {
*(--buffer) = nodes[symbol].chr;
symbol = nodes[symbol].parent;
}
while (symbol >= 0) {
*(--buffer) = nodes[symbol].chr;
symbol = nodes[symbol].parent;
}
}

View File

@ -91,7 +91,7 @@ struct CompressMethod : CompressionMethod {
bool block_mode;
int symbol_counter;
int output_len;
int output_len;
utils::BitReaderLE input;
CompressLzw lzw;

View File

@ -26,6 +26,7 @@ this program. If not, see <https://www.gnu.org/licenses/>.
#include <path.hpp>
#include <cstdarg>
#include <algorithm>
namespace maconv {
namespace stuffit {
@ -61,6 +62,7 @@ static void ExtractFork(StuffitEntry &ent, bool is_res, fs::File &file,
return (void)WarnForkError(ent, is_res, "compression method %u not supported", info.method);
// Try extracting the fork.
LogDebug(" Extracting %s fork using algo %d", (is_res ? "ressource" : "data"), info.method);
try {
ptr->Extract(info, data, file.mem_pool);
} catch (ExtractException &e) {
@ -96,6 +98,7 @@ static void ExtractFile(fs::FileReader &reader, StuffitEntry &ent,
// Log information to user.
std::string filename = dest_folder + "/" + GetFilenameFor(ent.name, prefered_conv);
filename.erase(std::remove(filename.begin(), filename.end(), '\r'), filename.end());
LogDebug("Extracting %s ...", filename.c_str());
// Uncompress forks (if not empty).

View File

@ -3,6 +3,9 @@
Extract files from Stuffit (v5) archives.
See docs/stuffit/Stuffit_v5.md for more information on this format.
The code in this file is based on TheUnarchiver.
See README.md and docs/licenses/TheUnarchiver.txt for more information.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
@ -30,6 +33,7 @@ namespace stuffit {
// Stuffit (v5) entity flags.
constexpr uint8_t kFlagDirectory = 0x40;
constexpr uint8_t kFlagCrypted = 0x20;
constexpr uint8_t kFlagHasRessource = 0x1;
@ -85,7 +89,8 @@ static void ReadFileHeader(fs::FileReader &reader, StuffitEntry &ent)
reader.Skip(1);
// Read flags for knowing if entry is a file or a folder.
ent.etype = (reader.ReadByte() & kFlagDirectory) ? StuffitEntryType::Folder
int flags = reader.ReadByte();
ent.etype = (flags & kFlagDirectory) ? StuffitEntryType::Folder
: StuffitEntryType::File;
// Read creation and modification dates.
@ -99,10 +104,10 @@ static void ReadFileHeader(fs::FileReader &reader, StuffitEntry &ent)
// Read name and data lengths.
uint16_t name_length = reader.ReadHalfBE();
reader.Skip(2);
reader.Skip(2); // Skip header CRC.
ent.data.size = reader.ReadWordBE();
ent.data.comp_size = reader.ReadWordBE();
reader.Skip(4);
reader.Skip(4); // Skip data CRC.
// The entry is a folder: read the number of files.
@ -120,7 +125,7 @@ static void ReadFileHeader(fs::FileReader &reader, StuffitEntry &ent)
else {
ent.num_files = 0;
ent.data.method = reader.ReadByte();
reader.Skip(1);
reader.Skip(1); // Skip password length (as archive is not encrypted).
}
@ -149,9 +154,9 @@ static void ReadFileHeader(fs::FileReader &reader, StuffitEntry &ent)
if (has_res) {
ent.res.size = reader.ReadWordBE();
ent.res.comp_size = reader.ReadWordBE();
reader.Skip(4);
reader.Skip(4); // Skip ressource CRC.
ent.res.method = reader.ReadByte();
reader.Skip(1);
reader.Skip(1); // Skip password length (as archive is not encrypted).
} else {
ent.res.size = 0;
ent.res.comp_size = 0;
@ -163,11 +168,11 @@ static void ReadFileHeader(fs::FileReader &reader, StuffitEntry &ent)
// Set data and res offsets.
ent.data.offset = reader.Tell();
ent.res.offset = ent.data.offset + ent.data.comp_size;
ent.res.offset = reader.Tell();
ent.data.offset = ent.res.offset + ent.res.comp_size;
// Seek to the next entry (if it's a file).
reader.Seek(ent.res.offset + ent.res.comp_size);
reader.Seek(ent.data.offset + ent.data.comp_size);
}

View File

@ -35,18 +35,18 @@ namespace stuffit {
// Reverse a 32bits integer.
static uint32_t Reverse32(uint32_t val)
{
val = ((val >> 1) & 0x55555555) | ((val & 0x55555555) << 1);
val = ((val >> 2) & 0x33333333) | ((val & 0x33333333) << 2);
val = ((val >> 4) & 0x0F0F0F0F) | ((val & 0x0F0F0F0F) << 4);
val = ((val >> 8) & 0x00FF00FF) | ((val & 0x00FF00FF) << 8);
return (val >> 16) | (val << 16);
val = ((val >> 1) & 0x55555555) | ((val & 0x55555555) << 1);
val = ((val >> 2) & 0x33333333) | ((val & 0x33333333) << 2);
val = ((val >> 4) & 0x0F0F0F0F) | ((val & 0x0F0F0F0F) << 4);
val = ((val >> 8) & 0x00FF00FF) | ((val & 0x00FF00FF) << 8);
return (val >> 16) | (val << 16);
}
// Reverse a Nbits integer.
static uint32_t ReverseN(uint32_t val, int length)
{
return Reverse32(val) >> (32 - length);
return Reverse32(val) >> (32 - length);
}
@ -145,7 +145,7 @@ void HuffmanDecoder::AddValue(int value, uint32_t code, int length, int repeat_p
if (!IsEmptyNode(last_node))
throw ExtractException("Huffman: prefix already exists");
SetLeafValue(last_node, value);
SetLeafValue(last_node, value);
}
@ -171,28 +171,28 @@ int HuffmanDecoder::NextSymbol(utils::BitReader &input)
if (!table)
throw ExtractException("Huffman: search table not built");
int bits = input.ReadWord(table_size, false);
int length = table[bits].length;
int value = table[bits].value;
int bits = input.ReadWord(table_size, false);
int length = table[bits].length;
int value = table[bits].value;
if (length < 0)
if (length < 0)
throw ExtractException("Huffman: invalid prefix code when getting next symbol [length]");
if (length <= table_size) {
if (length <= table_size) {
input.SkipBits(length);
return value;
}
return value;
}
input.SkipBits(table_size);
int node = value;
int node = value;
for (int bit; !IsLeafNode(node); node = Branch(node, bit)) {
bit = input.ReadBit();
if (IsOpenBranch(node, bit))
for (int bit; !IsLeafNode(node); node = Branch(node, bit)) {
bit = input.ReadBit();
if (IsOpenBranch(node, bit))
throw ExtractException("Huffman: invalid prefix code when getting next symbol [code]");
}
}
return LeafValue(node);
return LeafValue(node);
}
@ -202,27 +202,27 @@ void HuffmanDecoder::MakeTableRecursLE(int node, HuffmanTableEntry *table,
int depth)
{
int curr_table_size = (1 << (table_size - depth));
int curr_stride = (1 << depth);
int curr_stride = (1 << depth);
if (IsLeafNode(node)) {
for (int i = 0; i < curr_table_size; i++) {
table[i * curr_stride].length = depth;
table[i * curr_stride].value = LeafValue(node);
}
}
else if (IsInvalidNode(node)) {
for (int i = 0; i < curr_table_size; i++)
if (IsInvalidNode(node)) {
for (int i = 0; i < curr_table_size; i++)
table[i * curr_stride].length = -1;
}
else {
if (depth == table_size) {
table[0].length = table_size + 1;
table[0].value = node;
} else {
MakeTableRecursLE(LeftBranch(node), table, depth + 1);
MakeTableRecursLE(RightBranch(node), table + curr_stride, depth + 1);
}
}
}
else if (IsLeafNode(node)) {
for (int i = 0; i < curr_table_size; i++) {
table[i * curr_stride].length = depth;
table[i * curr_stride].value = LeafValue(node);
}
}
else {
if (depth == table_size) {
table[0].length = table_size + 1;
table[0].value = node;
} else {
MakeTableRecursLE(LeftBranch(node), table, depth + 1);
MakeTableRecursLE(RightBranch(node), table + curr_stride, depth + 1);
}
}
}
@ -232,25 +232,25 @@ void HuffmanDecoder::MakeTableRecursBE(int node, HuffmanTableEntry *table,
{
int curr_table_size = (1 << (table_size - depth));
if (IsInvalidNode(node)) {
for (int i = 0; i < curr_table_size; i++)
if (IsInvalidNode(node)) {
for (int i = 0; i < curr_table_size; i++)
table[i].length = -1;
}
else if (IsLeafNode(node)) {
for(int i = 0; i < curr_table_size; i++) {
table[i].length = depth;
table[i].value = LeafValue(node);
}
}
else {
if (depth == table_size) {
table[0].length = table_size + 1;
table[0].value = node;
} else {
MakeTableRecursBE(LeftBranch(node), table, depth + 1);
MakeTableRecursBE(RightBranch(node), table + curr_table_size/2, depth + 1);
}
}
}
else if (IsLeafNode(node)) {
for(int i = 0; i < curr_table_size; i++) {
table[i].length = depth;
table[i].value = LeafValue(node);
}
}
else {
if (depth == table_size) {
table[0].length = table_size + 1;
table[0].value = node;
} else {
MakeTableRecursBE(LeftBranch(node), table, depth + 1);
MakeTableRecursBE(RightBranch(node), table + curr_table_size/2, depth + 1);
}
}
}
@ -260,8 +260,8 @@ void HuffmanDecoder::MakeTable(bool is_LE)
constexpr int kMaxTableSize = 10;
if (max_length < min_length) table_size = kMaxTableSize;
else if (max_length >= kMaxTableSize) table_size = kMaxTableSize;
else table_size = max_length;
else if (max_length >= kMaxTableSize) table_size = kMaxTableSize;
else table_size = max_length;
table = std::make_unique<HuffmanTableEntry[]>(1 << table_size);

View File

@ -51,7 +51,7 @@ void BitReaderBE::FillBitCache()
num_bits += (8 * num_bytes);
for (int i = 0; i < num_bytes; i++)
bits = (bits << 8) | *(data++);
bits = (bits << 8) | *(data++);
}
@ -90,7 +90,7 @@ void BitReaderLE::FillBitCache()
int num_bytes = std::min((32 - num_bits) / 8, (int)(end - data));
for (int i = 0; i < num_bytes; i++, num_bits += 8)
bits |= *(data++) << num_bits;
bits |= *(data++) << num_bits;
}