From 604f5412a6a5b9179e680d626468ffbb547465a0 Mon Sep 17 00:00:00 2001 From: Andy Vandijck Date: Sat, 12 Jul 2025 10:52:04 +0200 Subject: [PATCH] Add LZIP support Add LZIP support --- src/core/fex/CMakeLists.txt | 4 + src/core/fex/fex/File_Extractor.h | 2 +- src/core/fex/fex/LZMA_Inflater.cpp | 4 +- src/core/fex/fex/LZ_Extractor.cpp | 103 +++++++++++++++++++++++++ src/core/fex/fex/LZ_Extractor.h | 35 +++++++++ src/core/fex/fex/LZ_Reader.cpp | 117 +++++++++++++++++++++++++++++ src/core/fex/fex/LZ_Reader.h | 60 +++++++++++++++ src/core/fex/fex/blargg_config.h | 2 +- src/core/fex/fex/fex.cpp | 1 + 9 files changed, 325 insertions(+), 3 deletions(-) create mode 100644 src/core/fex/fex/LZ_Extractor.cpp create mode 100644 src/core/fex/fex/LZ_Extractor.h create mode 100644 src/core/fex/fex/LZ_Reader.cpp create mode 100644 src/core/fex/fex/LZ_Reader.h diff --git a/src/core/fex/CMakeLists.txt b/src/core/fex/CMakeLists.txt index 0b954740..d0901f52 100644 --- a/src/core/fex/CMakeLists.txt +++ b/src/core/fex/CMakeLists.txt @@ -65,6 +65,10 @@ target_sources(vbam-fex fex/XZ_Extractor.h fex/XZ_Reader.cpp fex/XZ_Reader.h + fex/LZ_Extractor.cpp + fex/LZ_Extractor.h + fex/LZ_Reader.cpp + fex/LZ_Reader.h fex/Rar_Extractor.cpp fex/Rar_Extractor.h fex/Zip7_Extractor.cpp diff --git a/src/core/fex/fex/File_Extractor.h b/src/core/fex/fex/File_Extractor.h index 23146ffe..7e133977 100644 --- a/src/core/fex/fex/File_Extractor.h +++ b/src/core/fex/fex/File_Extractor.h @@ -213,7 +213,7 @@ struct fex_type_t_ { blargg_err_t (*init)(); // Called by fex_init(). Can be NULL. }; -extern const fex_type_t_ fex_7z_type[1], fex_gz_type[1], fex_bz2_type[1], fex_xz_type[1], fex_rar_type[1], fex_zip_type[1], +extern const fex_type_t_ fex_7z_type[1], fex_gz_type[1], fex_bz2_type[1], fex_xz_type[1], fex_lz_type[1], fex_rar_type[1], fex_zip_type[1], fex_bin_type[1]; inline blargg_err_t File_Extractor::open_v() diff --git a/src/core/fex/fex/LZMA_Inflater.cpp b/src/core/fex/fex/LZMA_Inflater.cpp index c720f1c0..27cf85e4 100644 --- a/src/core/fex/fex/LZMA_Inflater.cpp +++ b/src/core/fex/fex/LZMA_Inflater.cpp @@ -194,6 +194,8 @@ blargg_err_t LZMA_Inflater::set_mode( mode_t mode, int data_offset ) if (mode == mode_raw_deflate) err = lzma_alone_decoder( &zbuf, UINT64_MAX); + else if (mode == mode_unlz) + err = lzma_lzip_decoder( &zbuf, UINT64_MAX, LZMA_CONCATENATED); else err = lzma_stream_decoder( &zbuf, UINT64_MAX, LZMA_CONCATENATED); @@ -300,7 +302,7 @@ blargg_err_t LZMA_Inflater::read( void* out, int* count_io ) if ( zbuf.avail_in ) { - fprintf(stderr, "Available in: %d, file corrupt\n"); + fprintf(stderr, "Available in: %zu, file corrupt\n", zbuf.avail_in); // inflate() should never leave input if there's still space for output check( false ); return blargg_err_file_corrupt; diff --git a/src/core/fex/fex/LZ_Extractor.cpp b/src/core/fex/fex/LZ_Extractor.cpp new file mode 100644 index 00000000..281be8f6 --- /dev/null +++ b/src/core/fex/fex/LZ_Extractor.cpp @@ -0,0 +1,103 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#if FEX_ENABLE_LZMA + +#include "LZ_Extractor.h" +#include + +/* Copyright (C) 2005-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// TODO: could close file once data has been read into memory + +static blargg_err_t init_lz_file() +{ + get_crc_table(); // initialize zlib's CRC-32 tables + return blargg_ok; +} + +static File_Extractor* new_lz() +{ + return BLARGG_NEW LZ_Extractor; +} + +fex_type_t_ const fex_lz_type [1] = {{ + ".lz", + &new_lz, + "lzip file", + &init_lz_file +}}; + +LZ_Extractor::LZ_Extractor() : + File_Extractor( fex_lz_type ) +{ } + +LZ_Extractor::~LZ_Extractor() +{ + close(); +} + +blargg_err_t LZ_Extractor::open_path_v() +{ + // skip opening file + return open_v(); +} + +blargg_err_t LZ_Extractor::stat_v() +{ + RETURN_ERR( open_arc_file( true ) ); + if ( !gr.opened() || gr.tell() != 0 ) + RETURN_ERR( gr.open( &arc() ) ); + + set_info( gr.remain(), 0, gr.crc32() ); + return blargg_ok; +} + +blargg_err_t LZ_Extractor::open_v() +{ + // Remove .gz suffix + size_t len = strlen( arc_path() ); + if ( fex_has_extension( arc_path(), ".lz" ) ) + len -= 3; + + RETURN_ERR( name.resize( len + 1 ) ); + memcpy( name.begin(), arc_path(), name.size() ); + name [name.size() - 1] = '\0'; + + set_name( name.begin() ); + return blargg_ok; +} + +void LZ_Extractor::close_v() +{ + name.clear(); + gr.close(); +} + +blargg_err_t LZ_Extractor::next_v() +{ + return blargg_ok; +} + +blargg_err_t LZ_Extractor::rewind_v() +{ + set_name( name.begin() ); + return blargg_ok; +} + +blargg_err_t LZ_Extractor::extract_v( void* p, int n ) +{ + return gr.read( p, n ); +} + +#endif diff --git a/src/core/fex/fex/LZ_Extractor.h b/src/core/fex/fex/LZ_Extractor.h new file mode 100644 index 00000000..3fb1f60a --- /dev/null +++ b/src/core/fex/fex/LZ_Extractor.h @@ -0,0 +1,35 @@ +// Presents a gzipped file as an "archive" of just that file. +// Also handles non-gzipped files. + +// File_Extractor 1.0.0 +#ifndef LZ_EXTRACTOR_H +#define LZ_EXTRACTOR_H + +#include "File_Extractor.h" +#include "LZ_Reader.h" + +class LZ_Extractor : public File_Extractor +{ + public: + LZ_Extractor(); + virtual ~LZ_Extractor(); + + protected: + virtual blargg_err_t open_path_v(); + virtual blargg_err_t open_v(); + virtual void close_v(); + + virtual blargg_err_t next_v(); + virtual blargg_err_t rewind_v(); + + virtual blargg_err_t stat_v(); + virtual blargg_err_t extract_v(void *, int); + + private: + LZ_Reader gr; + blargg_vector name; + + void set_info_(); +}; + +#endif diff --git a/src/core/fex/fex/LZ_Reader.cpp b/src/core/fex/fex/LZ_Reader.cpp new file mode 100644 index 00000000..be80c2a7 --- /dev/null +++ b/src/core/fex/fex/LZ_Reader.cpp @@ -0,0 +1,117 @@ +// File_Extractor 1.0.0. http://www.slack.net/~ant/ + +#if FEX_ENABLE_LZMA + +#include + +#include "LZ_Reader.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2025 Andy Vandijck. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +LZ_Reader::LZ_Reader() +{ + close(); +} + +LZ_Reader::~LZ_Reader() +{ } + +static blargg_err_t LZ_reader_read( void* file, void* out, int* count ) +{ + return STATIC_CAST(File_Reader*,file)->read_avail( out, count ); +} + +enum { header_size = 6, trailer_size = 20 }; + +size_t LZ_Reader::get_uncompressed_size() +{ + uint8_t *header_ptr = NULL; + uint8_t *trailer_ptr = NULL; + const uint8_t *data = (const uint8_t *)malloc(in->size()); + + if (data == NULL) { + fprintf(stderr, "Error: Couldn't allocate data\n"); + return 0; + } + + in->seek(0); + in->read((void *)data, in->size()); + header_ptr = (uint8_t *)data; + + unsigned dict_size = 1 << (header_ptr[5] & 0x1F); + dict_size -= (dict_size / 16) * ((header_ptr[5] >> 5 ) & 7); + + if(dict_size < (1 << 12) || dict_size > (1 << 29)) + { + fprintf(stderr, "Invalid dictionary size in member header.\n"); + return 0; + } + + trailer_ptr = (uint8_t *)data + (in->size() - trailer_size); + + unsigned long long data_size = 0; + for (int i = 11; i >= 4; --i) + data_size = ( data_size << 8 ) + trailer_ptr[i]; + + in->seek(0); + + return (size_t)data_size; +} + +blargg_err_t LZ_Reader::calc_size() +{ + size_ = (int)get_uncompressed_size(); + fprintf(stderr, "LZ uncompressed size: %d\n", size_); + + crc32_ = 0; + return blargg_ok; +} + +blargg_err_t LZ_Reader::open( File_Reader* new_in ) +{ + close(); + + in = new_in; + RETURN_ERR( in->seek( 0 ) ); + RETURN_ERR( inflater.begin( LZ_reader_read, new_in ) ); + RETURN_ERR( inflater.set_mode( inflater.mode_auto ) ); + RETURN_ERR( calc_size() ); + set_remain( size_ ); + + return blargg_ok; +} + +void LZ_Reader::close() +{ + in = NULL; + inflater.end(); +} + +blargg_err_t LZ_Reader::read_v( void* out, int count ) +{ + assert( in ); + int actual = count; + RETURN_ERR( inflater.read( out, &actual ) ); + + fprintf(stderr, "LZ: Actual read: %d, count: %d\n", actual, count); + + if ( actual != count ) + return blargg_err_file_corrupt; + + return blargg_ok; +} + +#endif diff --git a/src/core/fex/fex/LZ_Reader.h b/src/core/fex/fex/LZ_Reader.h new file mode 100644 index 00000000..d5408d02 --- /dev/null +++ b/src/core/fex/fex/LZ_Reader.h @@ -0,0 +1,60 @@ +// Transparently decompresses gzip files, as well as uncompressed + +// File_Extractor 1.0.0 +#ifndef LZ_READER_H +#define LZ_READER_H + +#include "Data_Reader.h" +#include "LZMA_Inflater.h" + +class LZ_Reader : public Data_Reader +{ + public: + // Keeps pointer to reader until close(). If + blargg_err_t open(File_Reader *); + + // True if file is open + bool opened() const + { + return in != NULL; + } + + // Frees memory + void close(); + + // True if file is compressed + bool deflated() const + { + return inflater.deflated(); + } + + // CRC-32 of data, of 0 if unavailable + unsigned int crc32() const + { + return crc32_; + } + + // Number of bytes read since opening + int tell() const + { + return size_ - remain(); + } + + public: + LZ_Reader(); + virtual ~LZ_Reader(); + size_t get_uncompressed_size(); + + protected: + virtual blargg_err_t read_v(void *, int); + + private: + File_Reader *in; + unsigned crc32_; + int size_; + LZMA_Inflater inflater; + + blargg_err_t calc_size(); +}; + +#endif diff --git a/src/core/fex/fex/blargg_config.h b/src/core/fex/fex/blargg_config.h index bb1464f1..2c3f15a9 100644 --- a/src/core/fex/fex/blargg_config.h +++ b/src/core/fex/fex/blargg_config.h @@ -28,7 +28,7 @@ #endif #if FEX_ENABLE_LZMA -#define FEX_TYPE_LZMA fex_xz_type, +#define FEX_TYPE_LZMA fex_xz_type, fex_lz_type, #else #define FEX_TYPE_LZMA #endif diff --git a/src/core/fex/fex/fex.cpp b/src/core/fex/fex/fex.cpp index 71cdc157..7db63193 100644 --- a/src/core/fex/fex/fex.cpp +++ b/src/core/fex/fex/fex.cpp @@ -35,6 +35,7 @@ BLARGG_EXPORT const fex_type_t* fex_type_list( void ) fex_gz_type, #if FEX_ENABLE_LZMA fex_xz_type, + fex_lz_type, #endif #if FEX_ENABLE_BZ2 fex_bz2_type,