Skip to content

Commit 94b88a8

Browse files
committed
player: add zip implementation
Requires libarchive. Zlib is an optional requirement for decompressing individually gzipped files within a zip. The zip reader should still work somewhat without zlib, just not in as many scenarios.
1 parent 3adb4f0 commit 94b88a8

4 files changed

Lines changed: 180 additions & 2 deletions

File tree

player/Archive_Reader.cpp

Lines changed: 133 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
#include "Archive_Reader.h"
22

3+
#include <string.h>
4+
35
blargg_err_t const arc_eof = "Archive: End of file";
46

57
#ifdef RARDLL
68

7-
#include <string.h>
8-
99
static const int erar_offset = 10;
1010
static blargg_err_t const erar_handle = "Failed to instantiate RAR handle";
1111
static blargg_err_t const erars[] = {
@@ -85,3 +85,134 @@ Rar_Reader::~Rar_Reader() {
8585
}
8686

8787
#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

player/Archive_Reader.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,23 @@ class Rar_Reader : public Archive_Reader {
5757
};
5858

5959
#endif // RARDLL
60+
61+
62+
#ifdef HAVE_LIBARCHIVE
63+
64+
#include <archive.h>
65+
#include <archive_entry.h>
66+
67+
class Zip_Reader : public Archive_Reader {
68+
archive* zip = nullptr;
69+
archive_entry* head = nullptr;
70+
blargg_vector<char> filename;
71+
public:
72+
static const uint32_t signature = BLARGG_4CHAR( 'P', 'K', 0x3, 0x4 );
73+
blargg_err_t open_zip( const char* path );
74+
blargg_err_t open( const char* path );
75+
blargg_err_t next( void* buf_ptr, arc_entry_t* entry );
76+
~Zip_Reader();
77+
};
78+
79+
#endif // HAVE_LIBARCHIVE

player/CMakeLists.txt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
option(GME_UNRAR "Enable RAR file format (optional, requires UnRAR library)" ON)
2+
option(GME_ARCHIVE "Enable ZIP file format (optional, requires LibArchive library)" ON)
23

34
find_package(SDL2)
45

56
if (GME_UNRAR)
67
find_package(UNRAR QUIET)
78
endif()
89

10+
if(GME_ARCHIVE)
11+
find_package(LibArchive QUIET)
12+
find_package(ZLIB QUIET)
13+
endif()
914

1015
set(player_SRCS Audio_Scope.cpp
1116
Audio_Scope.h
@@ -51,6 +56,25 @@ if(SDL2_FOUND)
5156
message(STATUS "RAR file format excluded")
5257
endif()
5358

59+
if(GME_ARCHIVE)
60+
if(LibArchive_FOUND)
61+
message(STATUS "LibArchive library located, player demo will support the ZIP file format")
62+
target_compile_definitions(gme_player PRIVATE HAVE_LIBARCHIVE)
63+
target_link_libraries(gme_player PRIVATE ${LibArchive_LIBRARIES})
64+
# Is not to be installed though
65+
list(APPEND PC_LIBS -larchive) # for libgme.pc
66+
if(ZLIB_FOUND)
67+
target_compile_definitions(gme_player PRIVATE HAVE_ZLIB_H)
68+
target_link_libraries(gme_player PRIVATE ZLIB::ZLIB)
69+
list(APPEND PC_LIBS -lz)
70+
endif()
71+
else()
72+
message(STATUS "** LibArchive library not found, player demo will not support zip files")
73+
endif()
74+
else()
75+
message(STATUS "Zip extraction not supported")
76+
endif()
77+
5478
else()
5579
message(STATUS "** SDL library not found, disabling player demo build")
5680
endif()

player/Music_Player.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ struct arc_type_t {
5050
static const arc_type_t arcs[] = {
5151
#ifdef RARDLL
5252
{ Rar_Reader::signature, []{ return (Archive_Reader*)BLARGG_NEW Rar_Reader; } },
53+
#endif
54+
#ifdef HAVE_LIBARCHIVE
55+
{ Zip_Reader::signature, []{ return (Archive_Reader*)BLARGG_NEW Zip_Reader; } },
5356
#endif
5457
{ 0, nullptr }
5558
};

0 commit comments

Comments
 (0)