178 lines
4.9 KiB
Perl

package Parse::Win32Registry::Win95::Value;
use strict;
use warnings;
use base qw(Parse::Win32Registry::Value);
use Carp;
use Encode;
use Parse::Win32Registry::Base qw(:all);
use constant RGDB_VALUE_HEADER_LENGTH => 0xc;
sub new {
my $class = shift;
my $regfile = shift;
my $offset = shift; # offset to RGDB value entry
croak 'Missing registry file' if !defined $regfile;
croak 'Missing offset' if !defined $offset;
my $fh = $regfile->get_filehandle;
# RGDB Value Entry
# 0x00 dword = value type
# 0x04
# 0x08 word = value name length
# 0x0a word = value data length
# 0x0c = value name [for name length bytes]
# + value data [for data length bytes]
# Value type may just be a word, not a dword;
# following word always appears to be zero.
sysseek($fh, $offset, 0);
my $bytes_read = sysread($fh, my $rgdb_value_entry,
RGDB_VALUE_HEADER_LENGTH);
if ($bytes_read != RGDB_VALUE_HEADER_LENGTH) {
warnf('Could not read RGDB value at 0x%x', $offset);
return;
}
my ($type,
$name_length,
$data_length) = unpack('Vx4vv', $rgdb_value_entry);
$bytes_read = sysread($fh, my $name, $name_length);
if ($bytes_read != $name_length) {
warnf('Could not read name for RGDB value at 0x%x', $offset);
return;
}
$name = decode($Parse::Win32Registry::Base::CODEPAGE, $name);
$bytes_read = sysread($fh, my $data, $data_length);
if ($bytes_read != $data_length) {
warnf('Could not read data for RGDB value at 0x%x', $offset);
return;
}
my $self = {};
$self->{_regfile} = $regfile;
$self->{_offset} = $offset;
$self->{_length} = RGDB_VALUE_HEADER_LENGTH + $name_length + $data_length;
$self->{_allocated} = 1;
$self->{_tag} = 'rgdb value';
$self->{_name} = $name;
$self->{_name_length} = $name_length;
$self->{_type} = $type;
$self->{_data} = $data;
$self->{_data_length} = $data_length;
bless $self, $class;
return $self;
}
sub get_data {
my $self = shift;
my $type = $self->get_type;
my $data = $self->{_data};
return if !defined $data; # actually, Win95 value data is always defined
# apply decoding to appropriate data types
if ($type == REG_DWORD) {
if (length($data) == 4) {
$data = unpack('V', $data);
}
else {
# incorrect length for dword data
$data = undef;
}
}
elsif ($type == REG_DWORD_BIG_ENDIAN) {
if (length($data) == 4) {
$data = unpack('N', $data);
}
else {
# incorrect length for dword data
$data = undef;
}
}
elsif ($type == REG_SZ || $type == REG_EXPAND_SZ) {
# Snip off any terminating null.
# Typically, REG_SZ values will not have a terminating null,
# while REG_EXPAND_SZ values will have a terminating null
chop $data if substr($data, -1, 1) eq "\0";
}
elsif ($type == REG_MULTI_SZ) {
# Snip off any terminating nulls
chop $data if substr($data, -1, 1) eq "\0";
chop $data if substr($data, -1, 1) eq "\0";
my @multi_sz = split("\0", $data, -1);
# Make sure there is at least one empty string
@multi_sz = ('') if @multi_sz == 0;
return wantarray ? @multi_sz : join($", @multi_sz);
}
return $data;
}
sub as_regedit_export {
my $self = shift;
my $version = shift || 5;
my $name = $self->get_name;
my $export = $name eq '' ? '@=' : '"' . $name . '"=';
my $type = $self->get_type;
# XXX
# if (!defined $self->{_data}) {
# $name = $name eq '' ? '@' : qq{"$name"};
# return qq{; $name=(invalid data)\n};
# }
if ($type == REG_SZ) {
$export .= '"' . $self->get_data . '"';
$export .= "\n";
}
elsif ($type == REG_BINARY) {
$export .= 'hex:';
$export .= format_octets($self->{_data}, length($export));
}
elsif ($type == REG_DWORD) {
my $data = $self->get_data;
$export .= defined($data)
? sprintf("dword:%08x", $data)
: "dword:";
$export .= "\n";
}
elsif ($type == REG_EXPAND_SZ || $type == REG_MULTI_SZ) {
my $data = $version == 4
? $self->{_data} # raw data
: encode("UCS-2LE", $self->{_data}); # ansi->unicode
$export .= sprintf("hex(%x):", $type);
$export .= format_octets($data, length($export));
}
else {
$export .= sprintf("hex(%x):", $type);
$export .= format_octets($self->{_data}, length($export));
}
return $export;
}
sub parse_info {
my $self = shift;
my $info = sprintf '0x%x rgdb value len=0x%x "%s" type=%d data,len=0x%x',
$self->{_offset},
$self->{_length},
$self->{_name},
$self->{_type},
$self->{_data_length};
return $info;
}
1;