Maconv/src/stuffit/stuffit.cc

141 lines
3.8 KiB
C++

/*
Extract Stuffit archives.
See docs/stuffit/Stuffit.md for more information on this format.
Copyright (C) 2019, Guillaume Gonnet
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "stuffit/stuffit.h"
#include "stuffit/methods.h"
#include "formats/formats.h"
#include "commands.h"
#include <path.hpp>
#include <cstdarg>
#include <algorithm>
namespace maconv {
namespace stuffit {
// Warn the user that a fork couldn't be extracted.
static void WarnForkError(StuffitEntry &ent, bool is_res, const char *msg, ...)
{
va_list args;
fprintf(stderr, "\033[1m\033[33mWARNING: ");
fprintf(stderr, is_res ? "ressource fork " : "data fork ");
fprintf(stderr, "of '%s' couldn't be extracted (", ent.name.c_str());
va_start(args, msg);
vfprintf(stderr, msg, args);
va_end(args);
fprintf(stderr, ")\033[0m\n");
}
// Extract a single fork.
static void ExtractFork(StuffitEntry &ent, bool is_res, fs::File &file,
uint8_t *data)
{
const StuffitCompInfo &info = is_res ? ent.res : ent.data;
// Select the compression handler and extract the fork.
auto ptr = GetCompressionMethod(info.method);
if (!ptr)
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) {
return (void)WarnForkError(ent, is_res, e.what());
}
// Fill file information.
if (is_res) {
file.res = ptr->uncompressed;
file.res_size = ptr->total_size;
} else {
file.data_size = ptr->total_size;
file.data = ptr->uncompressed;
}
}
// Extract a file.
static void ExtractFile(fs::FileReader &reader, StuffitEntry &ent,
const std::string &dest_folder)
{
// Copy extracted data to file object.
fs::File file;
file.Reset();
file.type = ent.type;
file.creator = ent.type;
file.flags = ent.type;
file.creation_date = ent.type;
file.modif_date = ent.type;
file.filename = ent.name;
// 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).
if (ent.data.comp_size > 0)
ExtractFork(ent, false, file, reader.data);
if (ent.res.comp_size > 0)
ExtractFork(ent, true, file, reader.data);
// Save the file.
PackLocalFile(file, filename, prefered_conv);
}
// Extract a directory.
static void ExtractDirectory(StuffitEntry &ent, const std::string &dest_folder)
{
std::string dirname = dest_folder + "/" + ent.name;
Path::makedirs(dirname);
// TODO: set mofitication date.
}
// Extract a Stuffit entry.
void ExtractStuffitEntry(fs::FileReader &reader, StuffitEntry &ent,
const std::string &dest_folder)
{
if (ent.etype == StuffitEntryType::File)
ExtractFile(reader, ent, dest_folder);
else if (ent.etype == StuffitEntryType::Folder)
ExtractDirectory(ent, dest_folder);
}
} // namespace stuffit
} // namespace maconv