package DVD::Read::DVDCSS;

use 5.032001;
use strict;
use warnings;

require Exporter;

our @ISA = qw(Exporter);

# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.

# This allows declaration	use DVD::Read::DVDCSS ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = ( 'func' => [ qw(
	dvdcss_close
	dvdcss_error
	dvdcss_is_scrambled
	dvdcss_open
	dvdcss_read
	dvdcss_seek
) ],
'const' => [ qw(
	DVDCSS_BLOCK_SIZE
	DVDCSS_NOFLAGS
	DVDCSS_READ_DECRYPT
	DVDCSS_SEEK_KEY
	DVDCSS_SEEK_MPEG
) ] );

push @{$EXPORT_TAGS{all}}, grep {1} @{$EXPORT_TAGS{$_}} foreach keys %EXPORT_TAGS;

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} }, @{ $EXPORT_TAGS{'const'} } );

our @EXPORT = qw(
);

our $VERSION = '0.01';

require XSLoader;
XSLoader::load('DVD::Read::DVDCSS', $VERSION);

# Preloaded methods go here.

use constant {
	DVDCSS_BLOCK_SIZE => 2048,
	DVDCSS_NOFLAGS => 0,
	DVDCSS_READ_DECRYPT => 1,
	DVDCSS_SEEK_KEY => 2,
	DVDCSS_SEEK_MPEG => 1
};

sub DESTROY {
	print "Destroy @_\n";
	close(@_);
}

sub new {
	my($class, $psz_target) = @_;

	my $dvdcss_ = dvdcss_open($psz_target) or return undef if defined($psz_target);
	bless ({
		psz_target => $psz_target,
		dvdcss => $dvdcss_,
		pos => 0,
		is_scrambled => undef,
		buffer => undef,
	}, $class);
}

sub open {
	my($self, $psz_target) = @_;
	dvdcss_close($self->{dvdcss}) if defined $self->{dvdcss};
	undef $self->{dvdcss};
	return undef if not defined $psz_target;
	$self->{dvdcss} = dvdcss_open($psz_target) or return undef;
	@{$self}{qw(psz_target pos is_scrambled)} = ($psz_target, 0, undef);
}

sub close {
	my($self) = @_;
	dvdcss_close($self->{dvdcss}) if defined $self->{dvdcss};
	undef $self->{dvdcss};
}

sub error {
	my($self) = @_;
	return undef if not defined $self->{dvdcss};
	dvdcss_error($self->{dvdcss});
}

sub is_scrambled {
	my($self) = @_;
	return undef if not defined $self->{dvdcss};
	$self->{is_scrambled} = dvdcss_is_scrambled($self->{dvdcss});
}

sub seek {
	my($self, $i_blocks, $i_flags) = @_;
	return undef if not defined $self->{dvdcss};
	$i_blocks = 1 if not defined $i_blocks or $i_blocks < 1;
	$i_flags = 0 if not defined $i_flags;
	my $pos = dvdcss_seek($self->{dvdcss}, $i_blocks, $i_flags);
	return -1 if($pos < 0);
	$self->{pos} = $pos;
}

sub read {
	my($self, $i_blocks, $i_flags) = @_;
	return undef if not defined $self->{dvdcss};
	$i_blocks = 1 if not defined $i_blocks or $i_blocks < 1;
	$i_flags = 0 if not defined $i_flags;
	if(ref($self->{buffer}) ne 'SCALAR') {
		$self->{buffer} = '';
	}
	my $o_blocks =
	dvdcss_read(
	  $self->{dvdcss},
	    $self->{buffer},
	      $i_blocks,
	        $i_flags);
	return undef if($o_blocks < 0);
	$self->{pos} += $o_blocks;
	return $self->{buffer};
}

sub tell {
	my($self) = @_;
	return undef if not defined $self->{dvdcss};
	return $self->{pos};
}

1;
__END__
# Below is stub documentation for your module. You'd better edit it!

=head1 NAME

DVD::Read::DVDCSS - libdvdcss perl binding, you can descramble encrypted DVD-s

=head1 SYNOPSIS

  use DVD::Read::DVDCSS;

=head1 DESCRIPTION

This module provide way to read scrambled sectors from a DVD-VIDEO disc or ISO file using libdvdcss.

=head2 EXPORT

None by default.

=head2 Exportable functions :func

  dvdcss_open()

      dvdcss_t dvdcss_open ( const char *psz_target )

  dvdcss_close()

      int dvdcss_close ( dvdcss_t )

  dvdcss_error()

      const char *dvdcss_error ( const dvdcss_t )

  dvdcss_is_scrambled()

      int dvdcss_is_scrambled ( dvdcss_t )

  dvdcss_read()

      int dvdcss_read ( dvdcss_t,
                               void *p_buffer,
                               int i_blocks,
                               int i_flags )
  dvdcss_seek()

      int dvdcss_seek ( dvdcss_t,
                               int i_blocks,
                               int i_flags )


=head2 Exportable constants :const

  DVDCSS_BLOCK_SIZE = 2048

  DVDCSS_NOFLAGS = 0

  DVDCSS_READ_DECRYPT = 1

  DVDCSS_SEEK_KEY = 2

  DVDCSS_SEEK_MPEG = 1

=head1 SEE ALSO

https://www.videolan.org/developers/libdvdcss.html

If you have a web site set up for your module, mention it here.

=head1 EXAMPLES

B<use DVD::Read::DVDCSS qw(:all);>

#open /dev/cdrom

B<$dvdcss = DVD::Read::DVDCSS-E<gt>new('/dev/cdrom');>

or

B<$dvdcss = DVD::Read::DVDCSS-E<gt>new();>

B<$dvdcss->open('/dev/cdrom');>

or

B<$dvd = dvdcss_open('/dev/cdrom');>

#note: $dvd is not the same as $dvdcss, so you cannot mix OO and non-OO style.

#note2: $dvdcss->{dvdcss} is a $dvd, so you may try dvdcss_is_scrambled($dvdcss->{dvdcss})

#check is dvd scrambled

B<$is_scrambled = $dvdcss-E<gt>is_scrambled();>

or

B<$is_scrambled = dvdcss_is_scrambled($dvd);>

#seek to ISO9660 volume descriptor (sector 16 = 16 x 2048 byte) and get TITLE KEY

B<$pos = $dvdcss-E<gt>seek(16, DVDCSS_SEEK_KEY);>

or

B<$pos = dvdcss_seek($dvd, 16, DVDCSS_SEEK_KEY);>

#note: $pos = -1 if seek failed

#read 2 sector (2 x 2048 byte) without descramble sectors

B<$data = $dvdcss-E<gt>read(2, DVDCSS_NOFLAGS);>

or

B<$ret = dvdcss_read($dvd, $sec_data, 2, DVDCSS_NOFLAGS);>

#note: $data/$sec_data is contain the read data or undef if $dvdcss->read() cannot read,
$ret is the number of read sectors or -1 if dvdcss_read() cannot read;
$sector_data is untouched (not undef!) when read error

#read last error

B<$error_str = $dvdcss-E<gt>error();>

or

B<$error_str = dvdcss_error($dvd);>

#tell current pos (sector)

B<$pos = $dvdcss-E<gt>tell();>

#note OO tracks current sector position itself, but libdvdcss not (not in public), so you are alone if do not use OO

#close

B<$ret = $dvdcss-E<gt>close();>

or

B<$ret = dvdcssE<0x5f>close($dvd);>

=head1 AUTHOR

Gergely Szasz, I<E<lt>szaszg@hu.inter.netE<gt>>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2022 by Gergely Szasz

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.32.1 or,
at your option, any later version of Perl 5 you may have available.


=cut
