@@ -647,6 +647,180 @@ int32_t libmpq__file_read(mpq_archive_s *mpq_archive, uint32_t file_number, uint
647
647
return LIBMPQ_SUCCESS ;
648
648
}
649
649
650
+ /* opens a file and calculates the filename-based decryption key if needed. */
651
+ int32_t libmpq__block_open_offset_with_filename (mpq_archive_s * mpq_archive , uint32_t file_number , const char * filename ) {
652
+
653
+ /* some common variables. */
654
+ uint32_t i ;
655
+ uint32_t packed_size ;
656
+ int32_t result = 0 ;
657
+
658
+ /* check if given file number is not out of range. */
659
+ CHECK_FILE_NUM (file_number , mpq_archive )
660
+
661
+ if (mpq_archive -> mpq_file [file_number ]) {
662
+
663
+ /* file already opened, so increment counter */
664
+ mpq_archive -> mpq_file [file_number ]-> open_count ++ ;
665
+ return LIBMPQ_SUCCESS ;
666
+ }
667
+
668
+ mpq_block_s * mpq_block = & mpq_archive -> mpq_block [mpq_archive -> mpq_map [file_number ].block_table_indices ];
669
+
670
+ /* check if file is not stored in a single sector. */
671
+ if ((mpq_block -> flags & LIBMPQ_FLAG_SINGLE ) == 0 ) {
672
+
673
+ /* get packed size based on block size and block count. */
674
+ packed_size = sizeof (uint32_t ) * (((mpq_block -> unpacked_size + mpq_archive -> block_size - 1 ) / mpq_archive -> block_size ) + 1 );
675
+ } else {
676
+
677
+ /* file is stored in single sector and we need only two entries for the packed block offset table. */
678
+ packed_size = sizeof (uint32_t ) * 2 ;
679
+ }
680
+
681
+ /* check if data has one extra entry. */
682
+ if ((mpq_block -> flags & LIBMPQ_FLAG_CRC ) != 0 ) {
683
+
684
+ /* add one uint32_t. */
685
+ packed_size += sizeof (uint32_t );
686
+ }
687
+
688
+ /* allocate memory for the file. */
689
+ if ((mpq_archive -> mpq_file [file_number ] = calloc (1 , sizeof (mpq_file_s ))) == NULL ) {
690
+
691
+ /* memory allocation problem. */
692
+ result = LIBMPQ_ERROR_MALLOC ;
693
+ goto error ;
694
+ }
695
+
696
+ /* allocate memory for the packed block offset table. */
697
+ if ((mpq_archive -> mpq_file [file_number ]-> packed_offset = calloc (1 , packed_size )) == NULL ) {
698
+
699
+ /* memory allocation problem. */
700
+ result = LIBMPQ_ERROR_MALLOC ;
701
+ goto error ;
702
+ }
703
+
704
+ /* initialize counter to one opening */
705
+ mpq_archive -> mpq_file [file_number ]-> open_count = 1 ;
706
+
707
+ /* check if we need to load the packed block offset table, we will maintain this table for unpacked files too. */
708
+ if ((mpq_block -> flags & LIBMPQ_FLAG_COMPRESSED ) != 0 &&
709
+ (mpq_block -> flags & LIBMPQ_FLAG_SINGLE ) == 0 ) {
710
+
711
+ /* seek to block position. */
712
+ if (fseeko (mpq_archive -> fp , mpq_block -> offset + (((long long )mpq_archive -> mpq_block_ex [mpq_archive -> mpq_map [file_number ].block_table_indices ].offset_high ) << 32 ) + mpq_archive -> archive_offset , SEEK_SET ) < 0 ) {
713
+
714
+ /* seek in file failed. */
715
+ result = LIBMPQ_ERROR_SEEK ;
716
+ goto error ;
717
+ }
718
+
719
+ /* read block positions from begin of file. */
720
+ if (fread (mpq_archive -> mpq_file [file_number ]-> packed_offset , 1 , packed_size , mpq_archive -> fp ) != packed_size ) {
721
+
722
+ /* something on read from archive failed. */
723
+ result = LIBMPQ_ERROR_READ ;
724
+ goto error ;
725
+ }
726
+
727
+ /* check if the archive is protected some way, sometimes the file appears not to be encrypted, but it is.
728
+ * a special case are files with an additional sector but LIBMPQ_FLAG_CRC not set. we don't want to handle
729
+ * them as encrypted. */
730
+ if (mpq_archive -> mpq_file [file_number ]-> packed_offset [0 ] != packed_size &&
731
+ mpq_archive -> mpq_file [file_number ]-> packed_offset [0 ] != packed_size + 4 ) {
732
+
733
+ /* file is encrypted. */
734
+ mpq_block -> flags |= LIBMPQ_FLAG_ENCRYPTED ;
735
+ }
736
+
737
+ /* check if packed offset block is encrypted, we have to decrypt it. */
738
+ if (mpq_block -> flags & LIBMPQ_FLAG_ENCRYPTED ) {
739
+
740
+ /* get the file seed. */
741
+ uint32_t seed ;
742
+ if (mpq_block -> flags & LIBMPQ_FLAG_ENCRYPTION_KEY_V2 ) {
743
+ result = libmpq__encryption_key_from_filename_v2 (filename , mpq_block -> offset , mpq_block -> unpacked_size , & seed );
744
+ } else {
745
+ result = libmpq__encryption_key_from_filename (filename , & seed );
746
+ }
747
+ if (result < 0 ) {
748
+ result = LIBMPQ_ERROR_DECRYPT ;
749
+ goto error ;
750
+ }
751
+ mpq_archive -> mpq_file [file_number ]-> seed = seed ;
752
+
753
+ /* decrypt block in input buffer. */
754
+ if (libmpq__decrypt_block (mpq_archive -> mpq_file [file_number ]-> packed_offset , packed_size , seed - 1 ) < 0 ) {
755
+
756
+ /* something on decrypt failed. */
757
+ result = LIBMPQ_ERROR_DECRYPT ;
758
+ goto error ;
759
+ }
760
+
761
+ /* check if the block positions are correctly decrypted. */
762
+ if (mpq_archive -> mpq_file [file_number ]-> packed_offset [0 ] != packed_size ) {
763
+
764
+ /* sorry without seed, we cannot extract file. */
765
+ result = LIBMPQ_ERROR_DECRYPT ;
766
+ goto error ;
767
+ }
768
+ }
769
+ } else {
770
+
771
+ /* check if file is not stored in a single sector. */
772
+ if ((mpq_block -> flags & LIBMPQ_FLAG_SINGLE ) == 0 ) {
773
+
774
+ /* loop through all blocks and create packed block offset table based on block size. */
775
+ for (i = 0 ; i < ((mpq_block -> unpacked_size + mpq_archive -> block_size - 1 ) / mpq_archive -> block_size + 1 ); i ++ ) {
776
+
777
+ /* check if we process the last block. */
778
+ if (i == ((mpq_block -> unpacked_size + mpq_archive -> block_size - 1 ) / mpq_archive -> block_size )) {
779
+
780
+ /* store size of last block. */
781
+ mpq_archive -> mpq_file [file_number ]-> packed_offset [i ] = mpq_block -> unpacked_size ;
782
+ } else {
783
+
784
+ /* store default block size. */
785
+ mpq_archive -> mpq_file [file_number ]-> packed_offset [i ] = i * mpq_archive -> block_size ;
786
+ }
787
+ }
788
+ } else {
789
+
790
+ /* store offsets. */
791
+ mpq_archive -> mpq_file [file_number ]-> packed_offset [0 ] = 0 ;
792
+ mpq_archive -> mpq_file [file_number ]-> packed_offset [1 ] = mpq_block -> packed_size ;
793
+ }
794
+
795
+ if (mpq_block -> flags & LIBMPQ_FLAG_ENCRYPTED ) {
796
+ /* get the file seed. */
797
+ uint32_t seed ;
798
+ if (mpq_block -> flags & LIBMPQ_FLAG_ENCRYPTION_KEY_V2 ) {
799
+ result = libmpq__encryption_key_from_filename_v2 (filename , mpq_block -> offset , mpq_block -> unpacked_size , & seed );
800
+ } else {
801
+ result = libmpq__encryption_key_from_filename (filename , & seed );
802
+ }
803
+ if (result < 0 ) {
804
+ result = LIBMPQ_ERROR_DECRYPT ;
805
+ goto error ;
806
+ }
807
+ mpq_archive -> mpq_file [file_number ]-> seed = seed ;
808
+ }
809
+ }
810
+
811
+ /* if no error was found, return zero. */
812
+ return LIBMPQ_SUCCESS ;
813
+
814
+ error :
815
+
816
+ /* free packed block offset table and file pointer. */
817
+ free (mpq_archive -> mpq_file [file_number ]-> packed_offset );
818
+ free (mpq_archive -> mpq_file [file_number ]);
819
+
820
+ /* return error constant. */
821
+ return result ;
822
+ }
823
+
650
824
/* this function open a file in the given archive and caches the block offset information. */
651
825
int32_t libmpq__block_open_offset (mpq_archive_s * mpq_archive , uint32_t file_number ) {
652
826
0 commit comments