Skip to content

Commit 7a1d811

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 7a1d811

4 files changed

Lines changed: 178 additions & 0 deletions

File tree

player/Archive_Reader.cpp

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

player/Archive_Reader.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,30 @@ 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+
#ifdef HAVE_ZLIB_H
68+
#include <zlib.h>
69+
#endif // HAVE_ZLIB_H
70+
71+
class Zip_Reader : public Archive_Reader {
72+
archive* zip = nullptr;
73+
archive_entry* head = nullptr;
74+
blargg_vector<char> filename;
75+
public:
76+
static const uint32_t signature = BLARGG_4CHAR( 'P', 'K', 0x3, 0x4 );
77+
#ifdef HAVE_ZLIB_H
78+
static const uint16_t gz_signature = BLARGG_2CHAR( 0x1f, 0x8b );
79+
#endif // HAVE_ZLIB_H
80+
blargg_err_t open_zip( const char* path );
81+
blargg_err_t open( const char* path );
82+
blargg_err_t next( void* buf_ptr, arc_entry_t* entry );
83+
~Zip_Reader();
84+
};
85+
86+
#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)