158 lines
4.0 KiB
C++
158 lines
4.0 KiB
C++
/*
|
|
|
|
Extract a disk image.
|
|
|
|
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 "disk/disk.h"
|
|
#include "commands.h"
|
|
|
|
#include <libhfs/hfs.h>
|
|
#include <libhfs/data.h>
|
|
|
|
#include <make_unique.hpp>
|
|
#include <path.hpp>
|
|
#include <cstdio>
|
|
|
|
namespace maconv {
|
|
namespace disk {
|
|
|
|
|
|
|
|
// Extract a single fork.
|
|
static void ExtractFork(hfsfile *hfile, const hfsdirent &ent, fs::File &file,
|
|
bool is_res)
|
|
{
|
|
// Allocate buffer.
|
|
int size = is_res ? ent.u.file.rsize : ent.u.file.dsize;
|
|
auto buffer = std::make_unique<uint8_t[]>(size);
|
|
|
|
// Read the data.
|
|
hfs_setfork(hfile, is_res ? 1 : 0);
|
|
if (hfs_read(hfile, buffer.get(), size) != size)
|
|
StopOnError("can't read %s (%d) from HFS disk", file.filename.c_str(), is_res);
|
|
|
|
// Fill file information.
|
|
if (is_res) {
|
|
file.res = buffer.get();
|
|
file.res_size = size;
|
|
} else {
|
|
file.data_size = size;
|
|
file.data = buffer.get();
|
|
}
|
|
|
|
// Save the buffer to the memory pool.
|
|
file.mem_pool.push_back(std::move(buffer));
|
|
}
|
|
|
|
|
|
// Extract a file from a disk.
|
|
static void ExtractFile(Path &localp, hfsvol *vol, const hfsdirent &ent)
|
|
{
|
|
Path::makedirs(localp);
|
|
fs::File file;
|
|
|
|
// Extract file information.
|
|
file.Reset();
|
|
file.type = d_getsl((const unsigned char *)ent.u.file.type);
|
|
file.creator = d_getsl((const unsigned char *)ent.u.file.creator);
|
|
file.flags = ent.fdflags;
|
|
file.filename = ent.name;
|
|
file.creation_date = ent.crdate;
|
|
file.modif_date = ent.mddate;
|
|
|
|
// Extract the two forks.
|
|
hfsfile *hfile = hfs_open(vol, ent.name);
|
|
ExtractFork(hfile, ent, file, false);
|
|
ExtractFork(hfile, ent, file, true);
|
|
hfs_close(hfile);
|
|
|
|
// Save the file.
|
|
auto out = Path::join(localp, GetFilenameFor(ent.name, prefered_conv));
|
|
PackLocalFile(file, out.string(), prefered_conv);
|
|
}
|
|
|
|
|
|
|
|
// Extract a directory from the disk.
|
|
static void ExtractDirectory(Path localp, hfsvol *vol, unsigned long id)
|
|
{
|
|
unsigned long current = hfs_getcwd(vol);
|
|
hfs_setcwd(vol, id);
|
|
|
|
hfsdir *dir = hfs_opendir(vol, ":");
|
|
hfsdirent ent;
|
|
|
|
while (hfs_readdir(dir, &ent) != -1) {
|
|
if (ent.fdflags & HFS_FNDR_ISINVISIBLE)
|
|
continue;
|
|
|
|
if (ent.flags & HFS_ISDIR)
|
|
ExtractDirectory(Path::join(localp, ent.name), vol, ent.cnid);
|
|
else
|
|
ExtractFile(localp, vol, ent);
|
|
}
|
|
|
|
hfs_closedir(dir);
|
|
hfs_setcwd(vol, current);
|
|
}
|
|
|
|
|
|
|
|
// Extract a disk file.
|
|
static void ExtractDiskFrom(const std::string &name, const std::string &out)
|
|
{
|
|
hfsvol *vol = hfs_mount(name.c_str(), 0, HFS_MODE_RDONLY);
|
|
ExtractDirectory(out, vol, HFS_CNID_ROOTDIR);
|
|
hfs_umount(vol);
|
|
}
|
|
|
|
|
|
// Extract a disk file.
|
|
void ExtractDisk(UnPacked &u, const std::string &out_folder)
|
|
{
|
|
// If it's not a "raw" file: extract the file directly.
|
|
if (u.file.is_raw)
|
|
ExtractDiskFrom(u.n1, out_folder);
|
|
|
|
// Else, save the data fork to a temporary file first and then extract.
|
|
char dirname[] = "/tmp/maconv.XXXXXX";
|
|
if (mkdtemp(dirname) == nullptr)
|
|
StopOnError("can't create temporary folder for HFS disk");
|
|
|
|
std::string name = std::string {dirname} + "/disk.dsk";
|
|
PackLocalFile(u.file, name, GetConverter("data"));
|
|
ExtractDiskFrom(name, out_folder);
|
|
|
|
std::remove(name.c_str());
|
|
rmdir(dirname);
|
|
}
|
|
|
|
|
|
|
|
// Is a file a disk file?
|
|
bool IsFileDisk(const std::string &name)
|
|
{
|
|
auto ext = Path(name).extension();
|
|
return ext == "dsk" || ext == "img";
|
|
}
|
|
|
|
|
|
|
|
} // namespace disk
|
|
} // namespace maconv
|