|
1 | 1 | #include "Archive_Reader.h" |
2 | 2 |
|
| 3 | +#include <string.h> |
| 4 | + |
3 | 5 | blargg_err_t const arc_eof = "Archive: End of file"; |
4 | 6 |
|
5 | 7 | #ifdef RARDLL |
6 | 8 |
|
7 | | -#include <string.h> |
8 | | - |
9 | 9 | static const int erar_offset = 10; |
10 | 10 | static blargg_err_t const erar_handle = "Failed to instantiate RAR handle"; |
11 | 11 | static blargg_err_t const erars[] = { |
@@ -85,3 +85,134 @@ Rar_Reader::~Rar_Reader() { |
85 | 85 | } |
86 | 86 |
|
87 | 87 | #endif // RARDLL |
| 88 | + |
| 89 | + |
| 90 | +#ifdef HAVE_LIBARCHIVE |
| 91 | + |
| 92 | +const char* zip_err_struct = "Failed to create archive struct"; |
| 93 | + |
| 94 | +#if ARCHIVE_VERSION_NUMBER < 3000000 |
| 95 | +#define archive_read_free archive_read_finish |
| 96 | +#define archive_read_support_filter_all archive_read_support_compression_all |
| 97 | +#endif |
| 98 | + |
| 99 | +#ifdef HAVE_ZLIB_H |
| 100 | + |
| 101 | +#include <zlib.h> |
| 102 | + |
| 103 | +static const uint16_t gz_signature = BLARGG_2CHAR( 0x1f, 0x8b ); |
| 104 | + |
| 105 | +static const int zerr_offset = -6; |
| 106 | +static blargg_err_t const zerrs[] = { |
| 107 | + "GZ: Bad version", // Z_VERSION_ERROR (-6) |
| 108 | + "GZ: Buffer too small", // Z_BUF_ERROR (-5) |
| 109 | + "GZ: Out of memory", // Z_MEM_ERROR (-4) |
| 110 | + "GZ: Bad Data", // Z_DATA_ERROR (-3) |
| 111 | + "GZ: Stream error", // Z_STREAM_ERROR (-2) |
| 112 | +}; |
| 113 | +#endif // HAVE_ZLIB_H |
| 114 | + |
| 115 | +blargg_err_t Zip_Reader::open_zip( const char* path ) { |
| 116 | + if ( !(zip = archive_read_new()) ) |
| 117 | + return zip_err_struct; |
| 118 | + if ( archive_read_support_filter_all( zip ) != ARCHIVE_OK |
| 119 | + || archive_read_support_format_zip( zip ) != ARCHIVE_OK |
| 120 | + || archive_read_open_filename( zip, path, 10240 ) != ARCHIVE_OK ) |
| 121 | + return archive_error_string( zip ); |
| 122 | + return nullptr; |
| 123 | +} |
| 124 | + |
| 125 | +blargg_err_t Zip_Reader::open( const char* path ) |
| 126 | +{ |
| 127 | + blargg_err_t err; |
| 128 | + if ( (err = open_zip( path )) ) |
| 129 | + return err; |
| 130 | + |
| 131 | + // determine space needed for the unpacked size and file count. |
| 132 | + int res; |
| 133 | + while ( (res = archive_read_next_header( zip, &head )) == ARCHIVE_OK ) |
| 134 | + { |
| 135 | +#ifdef HAVE_ZLIB_H |
| 136 | + char h[3]; |
| 137 | + archive_read_data( zip, &h, 3 ); |
| 138 | + if ( BLARGG_2CHAR( h[0], h[1] ) == gz_signature && h[2] == 8 ) |
| 139 | + { |
| 140 | + // gzip puts its uncompressed file size in the footer |
| 141 | + blargg_vector<uint8_t> buf; |
| 142 | + if ( (err = buf.resize( archive_entry_size( head ) - 3 )) ) |
| 143 | + return err; |
| 144 | + archive_read_data( zip, buf.begin(), buf.size() ); |
| 145 | + const uint8_t* b = buf.end() - 4; |
| 146 | + size_ += BLARGG_4CHAR(b[3], b[2], b[1], b[0]); |
| 147 | + } |
| 148 | + else |
| 149 | +#endif // HAVE_ZLIB_H |
| 150 | + { |
| 151 | + size_ += archive_entry_size( head ); |
| 152 | + } |
| 153 | + count_ += 1; |
| 154 | + } |
| 155 | + if ( res != ARCHIVE_EOF || archive_read_free( zip ) != ARCHIVE_OK ) |
| 156 | + return archive_error_string( zip ); |
| 157 | + return (err = open_zip( path )) ? err : nullptr; |
| 158 | +} |
| 159 | + |
| 160 | +blargg_err_t Zip_Reader::next( void* buf_ptr, arc_entry_t* entry ) |
| 161 | +{ |
| 162 | + int res; |
| 163 | + if ( (res = archive_read_next_header( zip, &head )) != ARCHIVE_OK ) |
| 164 | + return (res == ARCHIVE_EOF) ? arc_eof : archive_error_string( zip ); |
| 165 | + |
| 166 | + uint8_t* bp = (uint8_t*)buf_ptr; |
| 167 | + int64_t siz = archive_entry_size( head ); |
| 168 | + ssize_t pos = 0; |
| 169 | +#ifdef HAVE_ZLIB_H |
| 170 | + pos += archive_read_data( zip, bp, 3 ); |
| 171 | + if ( BLARGG_2CHAR( bp[0], bp[1] ) == gz_signature && bp[2] == 8 ) |
| 172 | + { |
| 173 | + // load the gzip file into a separate buffer |
| 174 | + blargg_err_t err; |
| 175 | + blargg_vector<uint8_t> buf; |
| 176 | + if ( (err = buf.resize( siz )) ) |
| 177 | + return err; |
| 178 | + memcpy( &buf[0], bp, pos ); |
| 179 | + archive_read_data( zip, &buf[0] + pos, buf.size() - pos ); |
| 180 | + const uint8_t* b = buf.end() - 4; |
| 181 | + siz = BLARGG_4CHAR(b[3], b[2], b[1], b[0]); |
| 182 | + |
| 183 | + z_stream stream; |
| 184 | + memset( &stream, 0, sizeof stream ); |
| 185 | + stream.next_in = buf.begin(); |
| 186 | + stream.avail_in = buf.size(); |
| 187 | + stream.next_out = bp; |
| 188 | + stream.avail_out = siz; |
| 189 | + |
| 190 | + // 15 window bits, and the +32 tells zlib to to detect if using gzip or zlib |
| 191 | + if ( (res = inflateInit2( &stream, 15 + 32 )) != Z_OK |
| 192 | + || (res = inflate( &stream, Z_FINISH )) != Z_STREAM_END ) |
| 193 | + { |
| 194 | + inflateEnd( &stream ); |
| 195 | + return zerrs[res - zerr_offset]; |
| 196 | + } |
| 197 | + pos = stream.total_out; |
| 198 | + inflateEnd( &stream ); |
| 199 | + } |
| 200 | + else |
| 201 | +#endif // HAVE_ZLIB_H |
| 202 | + { |
| 203 | + pos += archive_read_data( zip, bp + pos, siz - pos ); |
| 204 | + } |
| 205 | + if ( pos != siz ) |
| 206 | + return "ZIP: header size does not match total bytes read"; |
| 207 | + |
| 208 | + entry->name = archive_entry_pathname( head ); |
| 209 | + entry->size = siz; |
| 210 | + return nullptr; |
| 211 | +} |
| 212 | + |
| 213 | +Zip_Reader::~Zip_Reader() |
| 214 | +{ |
| 215 | + archive_read_free( zip ); |
| 216 | +} |
| 217 | + |
| 218 | +#endif // HAVE_LIBARCHIVE |
0 commit comments