2020-01-26 00:39:13 -05:00
|
|
|
package d2mpq
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"compress/zlib"
|
|
|
|
"encoding/binary"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2021-01-08 15:46:11 -05:00
|
|
|
"io"
|
2020-01-26 00:39:13 -05:00
|
|
|
|
|
|
|
"github.com/JoshVarga/blast"
|
2020-02-01 21:06:22 -05:00
|
|
|
|
2020-01-31 23:18:11 -05:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2compression"
|
2020-09-12 16:25:09 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
|
2020-01-26 00:39:13 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// Stream represents a stream of data in an MPQ archive
|
|
|
|
type Stream struct {
|
2021-01-08 15:46:11 -05:00
|
|
|
Data []byte
|
|
|
|
Positions []uint32
|
|
|
|
MPQ *MPQ
|
|
|
|
Block *Block
|
|
|
|
Index uint32
|
|
|
|
Size uint32
|
|
|
|
Position uint32
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// CreateStream creates an MPQ stream
|
2021-01-08 15:46:11 -05:00
|
|
|
func CreateStream(mpq *MPQ, block *Block, fileName string) (*Stream, error) {
|
|
|
|
s := &Stream{
|
|
|
|
MPQ: mpq,
|
|
|
|
Block: block,
|
|
|
|
Index: 0xFFFFFFFF, //nolint:gomnd // MPQ magic
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
if s.Block.HasFlag(FileFixKey) {
|
|
|
|
s.Block.calculateEncryptionSeed(fileName)
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
s.Size = 0x200 << s.MPQ.header.BlockSize //nolint:gomnd // MPQ magic
|
2020-01-26 00:39:13 -05:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
if s.Block.HasFlag(FilePatchFile) {
|
|
|
|
return nil, errors.New("patching is not supported")
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
if (s.Block.HasFlag(FileCompress) || s.Block.HasFlag(FileImplode)) && !s.Block.HasFlag(FileSingleUnit) {
|
|
|
|
if err := s.loadBlockOffsets(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
return s, nil
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Stream) loadBlockOffsets() error {
|
2021-01-08 15:46:11 -05:00
|
|
|
if _, err := v.MPQ.file.Seek(int64(v.Block.FilePosition), io.SeekStart); err != nil {
|
2020-09-23 13:30:54 -04:00
|
|
|
return err
|
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
blockPositionCount := ((v.Block.UncompressedFileSize + v.Size - 1) / v.Size) + 1
|
|
|
|
v.Positions = make([]uint32, blockPositionCount)
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
if err := binary.Read(v.MPQ.file, binary.LittleEndian, &v.Positions); err != nil {
|
2020-09-23 13:30:54 -04:00
|
|
|
return err
|
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
if v.Block.HasFlag(FileEncrypted) {
|
|
|
|
decrypt(v.Positions, v.Block.EncryptionSeed-1)
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
blockPosSize := blockPositionCount << 2 //nolint:gomnd // MPQ magic
|
|
|
|
if v.Positions[0] != blockPosSize {
|
2020-02-01 21:51:49 -05:00
|
|
|
return errors.New("decryption of MPQ failed")
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
if v.Positions[1] > v.Size+blockPosSize {
|
2020-02-01 21:51:49 -05:00
|
|
|
return errors.New("decryption of MPQ failed")
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2020-01-26 00:39:13 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
func (v *Stream) Read(buffer []byte, offset, count uint32) (readTotal uint32, err error) {
|
|
|
|
if v.Block.HasFlag(FileSingleUnit) {
|
2020-01-26 00:39:13 -05:00
|
|
|
return v.readInternalSingleUnit(buffer, offset, count)
|
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
var read uint32
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
toRead := count
|
2020-01-26 00:39:13 -05:00
|
|
|
for toRead > 0 {
|
2021-01-08 15:46:11 -05:00
|
|
|
if read, err = v.readInternal(buffer, offset, toRead); err != nil {
|
2021-01-10 02:44:42 -05:00
|
|
|
return readTotal, err
|
2021-01-08 15:46:11 -05:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2020-01-26 00:39:13 -05:00
|
|
|
if read == 0 {
|
|
|
|
break
|
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2020-01-26 00:39:13 -05:00
|
|
|
readTotal += read
|
|
|
|
offset += read
|
|
|
|
toRead -= read
|
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
return readTotal, nil
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
func (v *Stream) readInternalSingleUnit(buffer []byte, offset, count uint32) (uint32, error) {
|
|
|
|
if len(v.Data) == 0 {
|
|
|
|
if err := v.loadSingleUnit(); err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
return v.copy(buffer, offset, v.Position, count)
|
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
func (v *Stream) readInternal(buffer []byte, offset, count uint32) (uint32, error) {
|
|
|
|
if err := v.bufferData(); err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
localPosition := v.Position % v.Size
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
return v.copy(buffer, offset, localPosition, count)
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
func (v *Stream) copy(buffer []byte, offset, pos, count uint32) (uint32, error) {
|
|
|
|
bytesToCopy := d2math.Min(uint32(len(v.Data))-pos, count)
|
2020-01-26 00:39:13 -05:00
|
|
|
if bytesToCopy <= 0 {
|
2021-01-10 02:44:42 -05:00
|
|
|
return 0, io.EOF
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
copy(buffer[offset:offset+bytesToCopy], v.Data[pos:pos+bytesToCopy])
|
|
|
|
v.Position += bytesToCopy
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
return bytesToCopy, nil
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
func (v *Stream) bufferData() (err error) {
|
|
|
|
blockIndex := v.Position / v.Size
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
if blockIndex == v.Index {
|
|
|
|
return nil
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
expectedLength := d2math.Min(v.Block.UncompressedFileSize-(blockIndex*v.Size), v.Size)
|
|
|
|
if v.Data, err = v.loadBlock(blockIndex, expectedLength); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-01-26 00:39:13 -05:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
v.Index = blockIndex
|
2020-10-22 01:12:06 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Stream) loadSingleUnit() (err error) {
|
|
|
|
if _, err = v.MPQ.file.Seek(int64(v.MPQ.header.HeaderSize), io.SeekStart); err != nil {
|
|
|
|
return err
|
2020-09-23 13:30:54 -04:00
|
|
|
}
|
2020-10-22 01:12:06 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
fileData := make([]byte, v.Size)
|
|
|
|
|
|
|
|
if _, err = v.MPQ.file.Read(fileData); err != nil {
|
|
|
|
return err
|
2020-09-23 13:30:54 -04:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
if v.Size == v.Block.UncompressedFileSize {
|
|
|
|
v.Data = fileData
|
|
|
|
return nil
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
v.Data, err = decompressMulti(fileData, v.Block.UncompressedFileSize)
|
|
|
|
|
|
|
|
return err
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
func (v *Stream) loadBlock(blockIndex, expectedLength uint32) ([]byte, error) {
|
2020-01-26 00:39:13 -05:00
|
|
|
var (
|
|
|
|
offset uint32
|
|
|
|
toRead uint32
|
|
|
|
)
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
if v.Block.HasFlag(FileCompress) || v.Block.HasFlag(FileImplode) {
|
|
|
|
offset = v.Positions[blockIndex]
|
|
|
|
toRead = v.Positions[blockIndex+1] - offset
|
2020-01-26 00:39:13 -05:00
|
|
|
} else {
|
2021-01-08 15:46:11 -05:00
|
|
|
offset = blockIndex * v.Size
|
2020-01-26 00:39:13 -05:00
|
|
|
toRead = expectedLength
|
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
offset += v.Block.FilePosition
|
2020-01-26 00:39:13 -05:00
|
|
|
data := make([]byte, toRead)
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
if _, err := v.MPQ.file.Seek(int64(offset), io.SeekStart); err != nil {
|
|
|
|
return []byte{}, err
|
2020-09-23 13:30:54 -04:00
|
|
|
}
|
2020-10-22 01:12:06 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
if _, err := v.MPQ.file.Read(data); err != nil {
|
|
|
|
return []byte{}, err
|
2020-09-23 13:30:54 -04:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
if v.Block.HasFlag(FileEncrypted) && v.Block.UncompressedFileSize > 3 {
|
|
|
|
if v.Block.EncryptionSeed == 0 {
|
|
|
|
return []byte{}, errors.New("unable to determine encryption key")
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
decryptBytes(data, blockIndex+v.Block.EncryptionSeed)
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
if v.Block.HasFlag(FileCompress) && (toRead != expectedLength) {
|
|
|
|
if !v.Block.HasFlag(FileSingleUnit) {
|
|
|
|
return decompressMulti(data, expectedLength)
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2021-01-08 15:46:11 -05:00
|
|
|
|
|
|
|
return pkDecompress(data)
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
if v.Block.HasFlag(FileImplode) && (toRead != expectedLength) {
|
|
|
|
return pkDecompress(data)
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
return data, nil
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
2021-01-11 20:23:43 -05:00
|
|
|
//nolint:gomnd,funlen,gocyclo // Will fix enum values later, can't help function length
|
2021-01-08 15:46:11 -05:00
|
|
|
func decompressMulti(data []byte /*expectedLength*/, _ uint32) ([]byte, error) {
|
2020-06-28 22:32:34 -04:00
|
|
|
compressionType := data[0]
|
|
|
|
|
|
|
|
switch compressionType {
|
2020-01-26 00:39:13 -05:00
|
|
|
case 1: // Huffman
|
2021-01-08 15:46:11 -05:00
|
|
|
return []byte{}, errors.New("huffman decompression not supported")
|
2020-01-26 00:39:13 -05:00
|
|
|
case 2: // ZLib/Deflate
|
|
|
|
return deflate(data[1:])
|
|
|
|
case 8: // PKLib/Impode
|
|
|
|
return pkDecompress(data[1:])
|
|
|
|
case 0x10: // BZip2
|
2021-01-08 15:46:11 -05:00
|
|
|
return []byte{}, errors.New("bzip2 decompression not supported")
|
2020-01-26 00:39:13 -05:00
|
|
|
case 0x80: // IMA ADPCM Stereo
|
2021-01-11 20:23:43 -05:00
|
|
|
return d2compression.WavDecompress(data[1:], 2)
|
2020-01-26 00:39:13 -05:00
|
|
|
case 0x40: // IMA ADPCM Mono
|
2021-01-11 20:23:43 -05:00
|
|
|
return d2compression.WavDecompress(data[1:], 1)
|
2020-01-26 00:39:13 -05:00
|
|
|
case 0x12:
|
2021-01-08 15:46:11 -05:00
|
|
|
return []byte{}, errors.New("lzma decompression not supported")
|
2020-01-26 00:39:13 -05:00
|
|
|
// Combos
|
|
|
|
case 0x22:
|
2020-06-28 22:32:34 -04:00
|
|
|
// sparse then zlib
|
2021-01-08 15:46:11 -05:00
|
|
|
return []byte{}, errors.New("sparse decompression + deflate decompression not supported")
|
2020-01-26 00:39:13 -05:00
|
|
|
case 0x30:
|
2020-06-28 22:32:34 -04:00
|
|
|
// sparse then bzip2
|
2021-01-08 15:46:11 -05:00
|
|
|
return []byte{}, errors.New("sparse decompression + bzip2 decompression not supported")
|
2020-01-26 00:39:13 -05:00
|
|
|
case 0x41:
|
2021-01-11 20:23:43 -05:00
|
|
|
sinput, err := d2compression.WavDecompress(d2compression.HuffmanDecompress(data[1:]), 1)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-01-26 00:39:13 -05:00
|
|
|
tmp := make([]byte, len(sinput))
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2020-01-26 00:39:13 -05:00
|
|
|
copy(tmp, sinput)
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
return tmp, nil
|
2020-01-26 00:39:13 -05:00
|
|
|
case 0x48:
|
2020-10-26 05:04:50 -04:00
|
|
|
// byte[] result = PKDecompress(sinput, outputLength);
|
|
|
|
// return MpqWavCompression.Decompress(new MemoryStream(result), 1);
|
2021-01-08 15:46:11 -05:00
|
|
|
return []byte{}, errors.New("pk + mpqwav decompression not supported")
|
2020-01-26 00:39:13 -05:00
|
|
|
case 0x81:
|
2021-01-11 20:23:43 -05:00
|
|
|
sinput, err := d2compression.WavDecompress(d2compression.HuffmanDecompress(data[1:]), 2)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-01-26 00:39:13 -05:00
|
|
|
tmp := make([]byte, len(sinput))
|
|
|
|
copy(tmp, sinput)
|
2020-10-26 05:04:50 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
return tmp, nil
|
2020-01-26 00:39:13 -05:00
|
|
|
case 0x88:
|
2020-10-26 05:04:50 -04:00
|
|
|
// byte[] result = PKDecompress(sinput, outputLength);
|
|
|
|
// return MpqWavCompression.Decompress(new MemoryStream(result), 2);
|
2021-01-08 15:46:11 -05:00
|
|
|
return []byte{}, errors.New("pk + wav decompression not supported")
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2021-01-08 15:46:11 -05:00
|
|
|
|
|
|
|
return []byte{}, fmt.Errorf("decompression not supported for unknown compression type %X", compressionType)
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
func deflate(data []byte) ([]byte, error) {
|
2020-01-26 00:39:13 -05:00
|
|
|
b := bytes.NewReader(data)
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
r, err := zlib.NewReader(b)
|
2020-01-26 00:39:13 -05:00
|
|
|
if err != nil {
|
2021-01-08 15:46:11 -05:00
|
|
|
return []byte{}, err
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2020-01-26 00:39:13 -05:00
|
|
|
buffer := new(bytes.Buffer)
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2020-10-22 01:12:06 -04:00
|
|
|
_, err = buffer.ReadFrom(r)
|
2020-01-26 00:39:13 -05:00
|
|
|
if err != nil {
|
2021-01-08 15:46:11 -05:00
|
|
|
return []byte{}, err
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2020-01-26 00:39:13 -05:00
|
|
|
err = r.Close()
|
|
|
|
if err != nil {
|
2021-01-08 15:46:11 -05:00
|
|
|
return []byte{}, err
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
return buffer.Bytes(), nil
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
func pkDecompress(data []byte) ([]byte, error) {
|
2020-01-26 00:39:13 -05:00
|
|
|
b := bytes.NewReader(data)
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
r, err := blast.NewReader(b)
|
2020-01-26 00:39:13 -05:00
|
|
|
if err != nil {
|
2021-01-08 15:46:11 -05:00
|
|
|
return []byte{}, err
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2020-01-26 00:39:13 -05:00
|
|
|
buffer := new(bytes.Buffer)
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
if _, err = buffer.ReadFrom(r); err != nil {
|
|
|
|
return []byte{}, err
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2020-01-26 00:39:13 -05:00
|
|
|
err = r.Close()
|
|
|
|
if err != nil {
|
2021-01-08 15:46:11 -05:00
|
|
|
return []byte{}, err
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|
2020-06-28 22:32:34 -04:00
|
|
|
|
2021-01-08 15:46:11 -05:00
|
|
|
return buffer.Bytes(), nil
|
2020-01-26 00:39:13 -05:00
|
|
|
}
|