1package steg
2
3import (
4	"encoding/binary"
5	"fmt"
6	"github.com/DimitarPetrov/stegify/bits"
7	"image"
8	"io"
9	"os"
10)
11
12//Decode performs steganography decoding of Reader with previously encoded data by the Encode function and writes to result Writer.
13func Decode(carrier io.Reader, result io.Writer) error {
14	RGBAImage, _, err := getImageAsRGBA(carrier)
15	if err != nil {
16		return fmt.Errorf("error parsing carrier image: %v", err)
17	}
18
19	dx := RGBAImage.Bounds().Dx()
20	dy := RGBAImage.Bounds().Dy()
21
22	dataBytes := make([]byte, 0, 2048)
23	resultBytes := make([]byte, 0, 2048)
24
25	dataCount := extractDataCount(RGBAImage)
26
27	var count int
28
29	for x := 0; x < dx && dataCount > 0; x++ {
30		for y := 0; y < dy && dataCount > 0; y++ {
31			if count >= dataSizeHeaderReservedBytes {
32				c := RGBAImage.RGBAAt(x, y)
33				dataBytes = append(dataBytes, bits.GetLastTwoBits(c.R), bits.GetLastTwoBits(c.G), bits.GetLastTwoBits(c.B))
34				dataCount -= 3
35			} else {
36				count += 4
37			}
38		}
39	}
40
41	if dataCount < 0 {
42		dataBytes = dataBytes[:len(dataBytes)+dataCount] //remove bytes that are not part of data and mistakenly added
43	}
44
45	dataBytes = align(dataBytes) // len(dataBytes) must be aliquot of 4
46
47	for i := 0; i < len(dataBytes); i += 4 {
48		resultBytes = append(resultBytes, bits.ConstructByteOfQuartersAsSlice(dataBytes[i:i+4]))
49	}
50
51	if _, err = result.Write(resultBytes); err != nil {
52		return err
53	}
54
55	return nil
56}
57
58//MultiCarrierDecode performs steganography decoding of Readers with previously encoded data chunks by the MultiCarrierEncode function and writes to result Writer.
59//NOTE: The order of the carriers MUST be the same as the one when encoding.
60func MultiCarrierDecode(carriers []io.Reader, result io.Writer) error {
61	for i := 0; i < len(carriers); i++ {
62		if err := Decode(carriers[i], result); err != nil {
63			return fmt.Errorf("error decoding chunk with index %d: %v", i, err)
64		}
65	}
66	return nil
67}
68
69//DecodeByFileNames performs steganography decoding of data previously encoded by the Encode function.
70//The data is decoded from file carrier and it is saved in separate new file
71func DecodeByFileNames(carrierFileName string, resultName string) (err error) {
72	return MultiCarrierDecodeByFileNames([]string{carrierFileName}, resultName)
73}
74
75//MultiCarrierDecodeByFileNames performs steganography decoding of data previously encoded by the MultiCarrierEncode function.
76//The data is decoded from carrier files and it is saved in separate new file
77//NOTE: The order of the carriers MUST be the same as the one when encoding.
78func MultiCarrierDecodeByFileNames(carrierFileNames []string, resultName string) (err error) {
79	if len(carrierFileNames) == 0 {
80		return fmt.Errorf("missing carriers names")
81	}
82
83	carriers := make([]io.Reader, 0, len(carrierFileNames))
84	for _, name := range carrierFileNames {
85		carrier, err := os.Open(name)
86		if err != nil {
87			return fmt.Errorf("error opening carrier file %s: %v", name, err)
88		}
89		defer func() {
90			closeErr := carrier.Close()
91			if err == nil {
92				err = closeErr
93			}
94		}()
95		carriers = append(carriers, carrier)
96	}
97
98	result, err := os.Create(resultName)
99	if err != nil {
100		return fmt.Errorf("error creating result file: %v", err)
101	}
102	defer func() {
103		closeErr := result.Close()
104		if err == nil {
105			err = closeErr
106		}
107	}()
108
109	err = MultiCarrierDecode(carriers, result)
110	if err != nil {
111		_ = os.Remove(resultName)
112	}
113	return err
114}
115
116func align(dataBytes []byte) []byte {
117	switch len(dataBytes) % 4 {
118	case 1:
119		dataBytes = append(dataBytes, byte(0), byte(0), byte(0))
120	case 2:
121		dataBytes = append(dataBytes, byte(0), byte(0))
122	case 3:
123		dataBytes = append(dataBytes, byte(0))
124	}
125	return dataBytes
126}
127
128func extractDataCount(RGBAImage *image.RGBA) int {
129	dataCountBytes := make([]byte, 0, 16)
130
131	dx := RGBAImage.Bounds().Dx()
132	dy := RGBAImage.Bounds().Dy()
133
134	count := 0
135
136	for x := 0; x < dx && count < dataSizeHeaderReservedBytes; x++ {
137		for y := 0; y < dy && count < dataSizeHeaderReservedBytes; y++ {
138			c := RGBAImage.RGBAAt(x, y)
139			dataCountBytes = append(dataCountBytes, bits.GetLastTwoBits(c.R), bits.GetLastTwoBits(c.G), bits.GetLastTwoBits(c.B))
140			count += 4
141		}
142	}
143
144	dataCountBytes = append(dataCountBytes, byte(0))
145
146	var bs = []byte{bits.ConstructByteOfQuartersAsSlice(dataCountBytes[:4]),
147		bits.ConstructByteOfQuartersAsSlice(dataCountBytes[4:8]),
148		bits.ConstructByteOfQuartersAsSlice(dataCountBytes[8:12]),
149		bits.ConstructByteOfQuartersAsSlice(dataCountBytes[12:])}
150
151	return int(binary.LittleEndian.Uint32(bs))
152}
153