Archive-SCS
view release on metacpan or search on metacpan
lib/Archive/SCS/HashFS.pm view on Meta::CPAN
$dir .= '/' if length $dir;
push @dirs, map { "$dir$_" } $data->dirs;
push @files, map { "$dir$_" } $data->files;
}
}
method list_dirs () {
return @dirs;
}
method list_files () {
return @files;
}
method entry_meta ($hash) {
return $entries{ $hash };
}
method read_entry ($hash) {
$fh or croak sprintf "%s: Not mounted", $path->basename;
my $entry = $entries{ $hash };
seek $fh, $entry->{offset}, SEEK_SET;
my $length = read $fh, my $data, $entry->{zsize};
defined $length or croak
sprintf "%s: %s", $path->basename, $!;
$length == $entry->{zsize} or croak
sprintf "%s: Read %i bytes, expected %i bytes",
$path->basename, $length, $entry->{zsize};
my $crc;
if ($entry->{is_compressed}) {
my $status = $zlib->inflate( \(my $raw = $data), \$data );
$status == Z_OK || $status == Z_STREAM_END
or warnings::warnif io =>
sprintf "%s: Inflation failed: %s (%i)",
$path->basename, $zlib->msg // "", $status;
$crc = $zlib->crc32;
$zlib->inflateReset;
length $data == $entry->{size}
or warnings::warnif io =>
sprintf "%s: Inflated to %i bytes, expected %i bytes",
$path->basename, length $data, $entry->{size};
}
else {
$crc = crc32($data);
}
$crc == $entry->{crc}
or warnings::warnif io =>
sprintf "%s: Found CRC32 %08X, expected %08X",
$path->basename, $crc, $entry->{crc};
# The official SCS extractor doesn't seem to verify the CRC
# Parse directory listing
$entry->{is_dir} or return $data;
my %dir_index;
for my $item (split /\n/, $data) {
if ('*' eq substr $item, 0, 1) {
push $dir_index{dirs}->@*, substr $item, 1;
}
else {
push $dir_index{files}->@*, $item;
}
}
return Archive::SCS::DirIndex->new(%dir_index);
}
method entries () {
keys %entries
}
sub create_file ($pathname, $scs) {
$scs isa Archive::SCS or die;
# This subroutine is designed for internal testing. It may or may not
# produce files that are compatible with SCS. All entry contents are
# loaded into memory.
my (@entries, %entries);
push @entries, map { cityhash64 $_ } $scs->list_dirs, $scs->list_files;
push @entries, map { cityhash64_hex $_ } $scs->list_orphans;
push @entries, cityhash64 '' if eval { $scs->read_entry(''); 1 };
@entries = sort @entries;
$entries{$_} = {
data => $scs->read_entry(cityhash64_as_hex $_),
flags => 0,
} for @entries;
# Serialize directory listings
do {
$entries{$_}->{flags} |= 0x1;
$entries{$_}->{data} = join "\n",
$entries{$_}->{data}->files,
map { "*$_" } $entries{$_}->{data}->dirs;
} for grep {
$entries{$_}->{data} isa Archive::SCS::DirIndex
} keys %entries;
my %opts2 = ( -CRC32 => 1, -WindowBits => 15, -Level => 9, -AppendOutput => 1 );
my $zlib_d = Compress::Raw::Zlib::Deflate->new(%opts2) or die;
my $offset = 0;
for my $hash (@entries) {
# Compress entry contents
$zlib_d->deflate( \($entries{$hash}->{data}), \(my $compressed = '') );
$zlib_d->flush( \$compressed );
$entries{$hash}->{crc} = $zlib_d->crc32;
$entries{$hash}->{size} = $zlib_d->total_in;
( run in 3.084 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )