1010import java .io .ByteArrayInputStream ;
1111import java .io .DataInputStream ;
1212import java .io .IOException ;
13+ import java .util .Arrays ;
1314
1415public final class ScFileUnpacker {
1516 private static final int SC_MAGIC = 0x5343 ;
16- private static final int FIVE_LITTLE_ENDIAN = swapEndian32 (5 );
17- private static final byte [] START_SECTION_BYTES = {'S' , 'T' , 'A' , 'R' , 'T' };
1817
1918 private ScFileUnpacker () {
2019 }
2120
2221 public static ScFileInfo unpack (byte [] compressedData ) throws UnknownFileVersionException , IOException , FileVerificationException {
2322 DataInputStream stream = createDataInputStreamFromBytes (compressedData );
2423
25- checkMagic (stream );
24+ verifyMagic (stream );
2625
2726 byte [] hash = null ;
2827
@@ -32,9 +31,18 @@ public static ScFileInfo unpack(byte[] compressedData) throws UnknownFileVersion
3231 int hashLength = stream .readInt ();
3332 hash = readBytes (hashLength , stream );
3433 }
35- case 5 -> {
36- int metadataRootTableOffset = swapEndian32 (stream .readInt ());
37- skipBytes (stream , metadataRootTableOffset );
34+ // Note: returns compressed data to allow user parse metadata and other compressed chunks
35+ case 5 , 6 -> {
36+ int flagsMaybe ;
37+ if (version == 6 ) {
38+ flagsMaybe = Short .reverseBytes (stream .readShort ()) & 0xFFFF ;
39+ } else {
40+ flagsMaybe = 0 ; // TODO: ?
41+ }
42+
43+ byte [] data = Arrays .copyOfRange (compressedData , compressedData .length - stream .available (), compressedData .length );
44+
45+ return new ScFileInfo (version , flagsMaybe , data );
3846 }
3947 }
4048
@@ -44,25 +52,20 @@ public static ScFileInfo unpack(byte[] compressedData) throws UnknownFileVersion
4452 throw new HashVerificationException ("Decompressed data hash doesn't equal to hash from file" );
4553 }
4654
47- return new ScFileInfo (version , decompressed );
55+ return new ScFileInfo (version , 0 , decompressed );
4856 }
4957
5058 private static byte [] decompress (byte [] compressedData , int version , DataInputStream stream ) throws IOException , UnknownFileVersionException {
5159 byte [] decompressed ;
5260
5361 switch (version ) {
5462 case 1 -> decompressed = Lzma .decompress (stream );
55- case 2 , 3 , 5 -> {
63+ case 2 , 3 -> {
5664 int offset = compressedData .length - stream .available ();
57- int startSectionOffset = indexOf (compressedData , START_SECTION_BYTES );
58- if (startSectionOffset != -1 ) {
59- decompressed = Zstandard .decompress (compressedData , offset , startSectionOffset - offset );
60- } else {
61- decompressed = Zstandard .decompress (compressedData , offset );
62- }
65+ decompressed = Zstandard .decompress (compressedData , offset );
6366 }
6467 default ->
65- throw new UnknownFileVersionException ("Unknown file version: " + version );
68+ throw new UnknownFileVersionException ("Unsupported file version: " + version );
6669 }
6770
6871 return decompressed ;
@@ -78,7 +81,7 @@ private static byte[] readBytes(int hashLength, DataInputStream stream) throws I
7881 return data ;
7982 }
8083
81- private static void checkMagic (DataInputStream stream ) throws IOException , WrongFileMagicException {
84+ private static void verifyMagic (DataInputStream stream ) throws IOException , WrongFileMagicException {
8285 int magic = stream .readShort ();
8386 if (magic != SC_MAGIC ) {
8487 throw new WrongFileMagicException ("Unknown file magic: " + magic );
@@ -90,42 +93,15 @@ private static int parseVersion(DataInputStream stream) throws IOException {
9093 if (version == 4 ) {
9194 version = stream .readInt ();
9295 }
93- if (version == FIVE_LITTLE_ENDIAN ) {
94- version = 5 ;
95- }
96- return version ;
97- }
9896
99- private static DataInputStream createDataInputStreamFromBytes (byte [] compressedData ) {
100- return new DataInputStream (new ByteArrayInputStream (compressedData ));
101- }
102-
103- private static void skipBytes (DataInputStream stream , int bytesToBeSkipped ) throws IOException {
104- long actualSkipped = stream .skip (bytesToBeSkipped );
105- if (actualSkipped != bytesToBeSkipped ) {
106- throw new IllegalStateException ("The number of bytes skipped is not equal to the requested number of bytes: " + actualSkipped + " vs " + bytesToBeSkipped );
97+ if (version <= 4 ) {
98+ return version ;
10799 }
108- }
109100
110- private static int swapEndian32 (int metadataRootTableOffset ) {
111- return (metadataRootTableOffset >> 24 ) & 0xFF | (((metadataRootTableOffset >> 16 ) & 0xFF ) << 8 ) | (((metadataRootTableOffset >> 8 ) & 0xFF ) << 16 ) | ((metadataRootTableOffset & 0xFF ) << 24 );
101+ return Integer .reverseBytes (version );
112102 }
113103
114- private static int indexOf (byte [] array , byte [] bytesToFind ) {
115- for (int i = 0 ; i < array .length ; i ++) {
116- boolean found = true ;
117- for (int j = 0 ; j < bytesToFind .length ; j ++) {
118- if (array [i +j ] != bytesToFind [j ]) {
119- found = false ;
120- break ;
121- }
122- }
123-
124- if (found ) {
125- return i ;
126- }
127- }
128-
129- return -1 ;
104+ private static DataInputStream createDataInputStreamFromBytes (byte [] compressedData ) {
105+ return new DataInputStream (new ByteArrayInputStream (compressedData ));
130106 }
131107}
0 commit comments