11// source https://gist.github.com/Su-s/438be493ae692318c73e30367cbc5c2a
2+ // updated source https://gist.github.com/Matheos96/da8990030dfe3e27b0a48722042d9c0b
23
34using System ;
45using System . IO ;
@@ -16,7 +17,10 @@ public class Tar
1617 /// <param name="outputDir">Output directory to write the files.</param>
1718 public static void ExtractTarGz ( string filename , string outputDir )
1819 {
19- using ( var stream = File . OpenRead ( filename ) ) ExtractTarGz ( stream , outputDir ) ;
20+ using ( var stream = File . OpenRead ( filename ) )
21+ {
22+ ExtractTarGz ( stream , outputDir ) ;
23+ }
2024 }
2125
2226 /// <summary>
@@ -26,9 +30,25 @@ public static void ExtractTarGz(string filename, string outputDir)
2630 /// <param name="outputDir">Output directory to write the files.</param>
2731 public static void ExtractTarGz ( Stream stream , string outputDir )
2832 {
29- using ( var gzip = new GZipStream ( stream , CompressionMode . Decompress ) )
33+ int read ;
34+ const int chunk = 4096 ;
35+ var buffer = new byte [ chunk ] ;
36+
37+ // A GZipStream is not seekable, so copy it first to a MemoryStream
38+ using ( var gzipStream = new GZipStream ( stream , CompressionMode . Decompress ) )
3039 {
31- ExtractTar ( gzip , outputDir ) ;
40+ using ( var memStream = new MemoryStream ( ) )
41+ {
42+ //For .NET 6+
43+ while ( ( read = gzipStream . Read ( buffer , 0 , buffer . Length ) ) > 0 )
44+ {
45+ memStream . Write ( buffer , 0 , read ) ;
46+ }
47+ memStream . Seek ( 0 , SeekOrigin . Begin ) ;
48+
49+ //ExtractTar(gzip, outputDir);
50+ ExtractTar ( memStream , outputDir ) ;
51+ }
3252 }
3353 }
3454
@@ -40,7 +60,9 @@ public static void ExtractTarGz(Stream stream, string outputDir)
4060 public static void ExtractTar ( string filename , string outputDir )
4161 {
4262 using ( var stream = File . OpenRead ( filename ) )
63+ {
4364 ExtractTar ( stream , outputDir ) ;
65+ }
4466 }
4567
4668 /// <summary>
@@ -51,78 +73,115 @@ public static void ExtractTar(string filename, string outputDir)
5173 public static void ExtractTar ( Stream stream , string outputDir )
5274 {
5375 var buffer = new byte [ 100 ] ;
54- // store current position here
55- long pos = 0 ;
76+ var longFileName = string . Empty ;
5677 while ( true )
5778 {
58- pos += stream . Read ( buffer , 0 , 100 ) ;
59- var name = Encoding . ASCII . GetString ( buffer ) . Trim ( '\0 ' ) ;
60- if ( String . IsNullOrWhiteSpace ( name ) ) break ;
61- FakeSeekForward ( stream , 24 ) ;
62- pos += 24 ;
79+ stream . Read ( buffer , 0 , 100 ) ;
80+ string name = string . IsNullOrEmpty ( longFileName ) ? Encoding . ASCII . GetString ( buffer ) . Trim ( '\0 ' ) : longFileName ; //Use longFileName if we have one read
6381
64- pos += stream . Read ( buffer , 0 , 12 ) ;
82+ if ( String . IsNullOrWhiteSpace ( name ) ) break ;
83+ stream . Seek ( 24 , SeekOrigin . Current ) ;
84+ stream . Read ( buffer , 0 , 12 ) ;
6585 var size = Convert . ToInt64 ( Encoding . UTF8 . GetString ( buffer , 0 , 12 ) . Trim ( '\0 ' ) . Trim ( ) , 8 ) ;
66- FakeSeekForward ( stream , 376 ) ;
67- pos += 376 ;
86+ stream . Seek ( 20 , SeekOrigin . Current ) ; //Move head to typeTag byte
87+ var typeTag = stream . ReadByte ( ) ;
88+ stream . Seek ( 355L , SeekOrigin . Current ) ; //Move head to beginning of data (byte 512)
89+
90+ if ( typeTag == 'L' )
91+ {
92+ //If Type Tag is 'L' we have a filename that is longer than the 100 bytes reserved for it in the header.
93+ //We read it here and save it temporarily as it will be the file name of the next block where the actual data is
94+ var buf = new byte [ size ] ;
95+ stream . Read ( buf , 0 , buf . Length ) ;
96+ longFileName = Encoding . ASCII . GetString ( buf ) . Trim ( '\0 ' ) ;
97+ }
98+ else
99+ {
100+ longFileName = string . Empty ; //Reset longFileName if current entry is not indicating one
68101
69- var output = Path . Combine ( outputDir , name ) ;
102+ var output = Path . Combine ( outputDir , name ) ;
70103
71- // only include these folders
72- var include = ( output . IndexOf ( "package/ProjectData~/Assets/" ) > - 1 ) ;
73- include |= ( output . IndexOf ( "package/ProjectData~/ProjectSettings/" ) > - 1 ) ;
74- include |= ( output . IndexOf ( "package/ProjectData~/Packages/" ) > - 1 ) ;
104+ // only include these folders
105+ var include = ( output . IndexOf ( "package/ProjectData~/Assets/" ) > - 1 ) ;
106+ include |= ( output . IndexOf ( "package/ProjectData~/ProjectSettings/" ) > - 1 ) ;
107+ include |= ( output . IndexOf ( "package/ProjectData~/Packages/" ) > - 1 ) ;
75108
76- // rename output path from "package/ProjectData~/Assets/" into "Assets/"
77- output = output . Replace ( "package/ProjectData~/" , "" ) ;
109+ // rename output path from "package/ProjectData~/Assets/" into "Assets/"
110+ output = output . Replace ( "package/ProjectData~/" , "" ) ;
78111
79- if ( include == true && ! Directory . Exists ( Path . GetDirectoryName ( output ) ) ) Directory . CreateDirectory ( Path . GetDirectoryName ( output ) ) ;
112+ if ( include == true && ! Directory . Exists ( Path . GetDirectoryName ( output ) ) ) Directory . CreateDirectory ( Path . GetDirectoryName ( output ) ) ;
80113
81- if ( ! name . Equals ( "./" , StringComparison . InvariantCulture ) )
82- {
83- if ( include == true )
114+ // not folder
115+ //if (name.Equals("./", StringComparison.InvariantCulture) == false)
116+ if ( name . EndsWith ( "/" ) == false ) //Directories are zero size and don't need anything written
84117 {
85- //Console.WriteLine("output=" + output);
86- using ( var str = File . Open ( output , FileMode . OpenOrCreate , FileAccess . Write ) )
118+ if ( include == true )
119+ {
120+ //Console.WriteLine("output=" + output);
121+ using ( var str = File . Open ( output , FileMode . OpenOrCreate , FileAccess . ReadWrite ) )
122+ {
123+ var buf = new byte [ size ] ;
124+ stream . Read ( buf , 0 , buf . Length ) ;
125+ // take only data from this folder
126+ str . Write ( buf , 0 , buf . Length ) ;
127+ }
128+ }
129+ else
87130 {
88131 var buf = new byte [ size ] ;
89- pos += stream . Read ( buf , 0 , buf . Length ) ;
90- // take only data from this folder
91- str . Write ( buf , 0 , buf . Length ) ;
132+ stream . Read ( buf , 0 , buf . Length ) ;
92133 }
93134 }
94- else
95- {
96- //pos += size;
97- var buf = new byte [ size ] ;
98- pos += stream . Read ( buf , 0 , buf . Length ) ;
99- //FakeSeekForward(stream, (int)size);
100- }
101135 }
102136
103- var offset = ( int ) ( 512 - ( pos % 512 ) ) ;
137+ //Move head to next 512 byte block
138+ var pos = stream . Position ;
139+ var offset = 512 - ( pos % 512 ) ;
104140 if ( offset == 512 ) offset = 0 ;
105- FakeSeekForward ( stream , offset ) ;
106- pos += offset ;
107- }
108- }
109141
110- private static void FakeSeekForward ( Stream stream , int offset )
111- {
112- if ( stream . CanSeek )
113142 stream . Seek ( offset , SeekOrigin . Current ) ;
114- else
115- {
116- int bytesRead = 0 ;
117- var buffer = new byte [ offset ] ;
118- while ( bytesRead < offset )
119- {
120- int read = stream . Read ( buffer , bytesRead , offset - bytesRead ) ;
121- if ( read == 0 )
122- throw new EndOfStreamException ( ) ;
123- bytesRead += read ;
124- }
125143 }
126144 }
127- }
128- }
145+ } // class Tar
146+ } // namespace TarLib
147+
148+
149+ /*
150+ This software is available under 2 licenses-- choose whichever you prefer.
151+ ------------------------------------------------------------------------------
152+ ALTERNATIVE A - MIT License
153+ Copyright (c) 2017 Sean Barrett
154+ Permission is hereby granted, free of charge, to any person obtaining a copy of
155+ this software and associated documentation files (the "Software"), to deal in
156+ the Software without restriction, including without limitation the rights to
157+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
158+ of the Software, and to permit persons to whom the Software is furnished to do
159+ so, subject to the following conditions:
160+ The above copyright notice and this permission notice shall be included in all
161+ copies or substantial portions of the Software.
162+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
163+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
164+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
165+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
166+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
167+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
168+ SOFTWARE.
169+ ------------------------------------------------------------------------------
170+ ALTERNATIVE B - Public Domain (www.unlicense.org)
171+ This is free and unencumbered software released into the public domain.
172+ Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
173+ software, either in source code form or as a compiled binary, for any purpose,
174+ commercial or non-commercial, and by any means.
175+ In jurisdictions that recognize copyright laws, the author or authors of this
176+ software dedicate any and all copyright interest in the software to the public
177+ domain.We make this dedication for the benefit of the public at large and to
178+ the detriment of our heirs and successors. We intend this dedication to be an
179+ overt act of relinquishment in perpetuity of all present and future rights to
180+ this software under copyright law.
181+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
182+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
183+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
184+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
185+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
186+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
187+ */
0 commit comments