From 76629e1b527856fe55ba8997ea0bb279620ecb0e Mon Sep 17 00:00:00 2001 From: Megamouse Date: Thu, 12 Oct 2023 01:21:35 +0200 Subject: [PATCH] Qt: Allow to use .gz files in Log viewer Log viewer can open .gz files Log viewer can save log as .gz Refactored most instances of zip and unzip code to seperate functions --- 3rdparty/zlib/CMakeLists.txt | 2 + 3rdparty/zlib/zlib.vcxproj | 21 +++- Utilities/JIT.cpp | 90 +++-------------- Utilities/JIT.h | 2 +- rpcs3/Crypto/unself.cpp | 60 ++--------- rpcs3/Crypto/unzip.cpp | 185 ++++++++++++++++++++++++++++++++++ rpcs3/Crypto/unzip.h | 33 ++++++ rpcs3/Emu/CMakeLists.txt | 1 + rpcs3/emucore.vcxproj | 6 +- rpcs3/emucore.vcxproj.filters | 6 ++ rpcs3/rpcs3.vcxproj | 4 +- rpcs3/rpcs3qt/log_viewer.cpp | 57 ++++++++--- 12 files changed, 323 insertions(+), 144 deletions(-) create mode 100644 rpcs3/Crypto/unzip.cpp create mode 100644 rpcs3/Crypto/unzip.h diff --git a/3rdparty/zlib/CMakeLists.txt b/3rdparty/zlib/CMakeLists.txt index a92d05c13e..7f75b85fb9 100644 --- a/3rdparty/zlib/CMakeLists.txt +++ b/3rdparty/zlib/CMakeLists.txt @@ -5,6 +5,7 @@ if (USE_SYSTEM_ZLIB) add_library(3rdparty_zlib INTERFACE) target_link_libraries(3rdparty_zlib INTERFACE ${ZLIB_LIBRARIES}) target_include_directories(3rdparty_zlib INTERFACE ${ZLIB_INCLUDE_DIRS}) + target_compile_definitions(3rdparty_zlib INTERFACE -DZLIB_CONST=1) else() message(STATUS "RPCS3: Using builtin ZLIB") set(SKIP_INSTALL_ALL ON) @@ -13,4 +14,5 @@ else() add_library(3rdparty_zlib INTERFACE) target_link_libraries(3rdparty_zlib INTERFACE zlibstatic) target_include_directories(3rdparty_zlib INTERFACE zlib ${CMAKE_CURRENT_BINARY_DIR}/zlib) + target_compile_definitions(3rdparty_zlib INTERFACE -DZLIB_CONST=1) endif() diff --git a/3rdparty/zlib/zlib.vcxproj b/3rdparty/zlib/zlib.vcxproj index 59c6f6127a..a333694e38 100644 --- a/3rdparty/zlib/zlib.vcxproj +++ b/3rdparty/zlib/zlib.vcxproj @@ -22,6 +22,23 @@ + + + + + + + + + + + + + + + + + {60F89955-91C6-3A36-8000-13C592FEC2DF} @@ -63,7 +80,7 @@ - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + WIN32;ZLIB_CONST;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) $(WarningLevel) ProgramDatabase Disabled @@ -90,7 +107,7 @@ true $(DisableSpecificWarnings);4127;4131;4242;4244 $(TreatWarningAsError) - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + WIN32;ZLIB_CONST;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) true diff --git a/Utilities/JIT.cpp b/Utilities/JIT.cpp index 5d77d16f69..caec5a6b22 100644 --- a/Utilities/JIT.cpp +++ b/Utilities/JIT.cpp @@ -9,8 +9,8 @@ #include "util/asm.hpp" #include "util/v128.hpp" #include "util/simd.hpp" +#include "Crypto/unzip.h" #include -#include #ifdef __linux__ #define CAN_OVERCOMMIT @@ -136,7 +136,7 @@ static atomic_t s_code_pos{0}, s_data_pos{0}; static std::vector s_code_init, s_data_init; template & Ctr, uint Off, utils::protection Prot> -static u8* add_jit_memory(usz size, uint align) +static u8* add_jit_memory(usz size, usz align) { // Select subrange u8* pointer = get_jit_memory() + Off; @@ -251,7 +251,7 @@ uchar* jit_runtime::_alloc(usz size, usz align) noexcept return jit_runtime::alloc(size, align, true); } -u8* jit_runtime::alloc(usz size, uint align, bool exec) noexcept +u8* jit_runtime::alloc(usz size, usz align, bool exec) noexcept { if (exec) { @@ -1170,42 +1170,18 @@ public: //fs::file(name, fs::rewrite).write(obj.getBufferStart(), obj.getBufferSize()); name.append(".gz"); - z_stream zs{}; - uLong zsz = compressBound(::narrow(obj.getBufferSize())) + 256; - auto zbuf = std::make_unique(zsz); -#ifndef _MSC_VER -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#endif - deflateInit2(&zs, 9, Z_DEFLATED, 16 + 15, 9, Z_DEFAULT_STRATEGY); -#ifndef _MSC_VER -#pragma GCC diagnostic pop -#endif - zs.avail_in = static_cast(obj.getBufferSize()); - zs.next_in = reinterpret_cast(const_cast(obj.getBufferStart())); - zs.avail_out = static_cast(zsz); - zs.next_out = zbuf.get(); + const std::vector zbuf = zip(reinterpret_cast(obj.getBufferStart()), obj.getBufferSize()); - switch (deflate(&zs, Z_FINISH)) - { - case Z_OK: - case Z_STREAM_END: - { - deflateEnd(&zs); - break; - } - default: + if (zbuf.empty()) { jit_log.error("LLVM: Failed to compress module: %s", _module->getName().data()); - deflateEnd(&zs); return; } - } - if (!fs::write_file(name, fs::rewrite, zbuf.get(), zsz - zs.avail_out)) + if (!fs::write_file(name, fs::rewrite, zbuf.data(), zbuf.size())) { - jit_log.error("LLVM: Failed to create module file: %s (%s)", name, fs::g_tls_error); - return; + jit_log.error("LLVM: Failed to create module file: %s (%s)", name, fs::g_tls_error); + return; } jit_log.notice("LLVM: Created module: %s", _module->getName().data()); @@ -1215,57 +1191,21 @@ public: { if (fs::file cached{path + ".gz", fs::read}) { - std::vector gz = cached.to_vector(); - std::vector out; - z_stream zs{}; + const std::vector cached_data = cached.to_vector(); - if (gz.empty()) [[unlikely]] + if (cached_data.empty()) [[unlikely]] { return nullptr; } -#ifndef _MSC_VER -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#endif - inflateInit2(&zs, 16 + 15); -#ifndef _MSC_VER -#pragma GCC diagnostic pop -#endif - zs.avail_in = static_cast(gz.size()); - zs.next_in = gz.data(); - out.resize(gz.size() * 6); - zs.avail_out = static_cast(out.size()); - zs.next_out = out.data(); - while (zs.avail_in) + const std::vector out = unzip(cached_data); + + if (out.empty()) { - switch (inflate(&zs, Z_FINISH)) - { - case Z_OK: break; - case Z_STREAM_END: break; - case Z_BUF_ERROR: - { - if (zs.avail_in) - break; - [[fallthrough]]; - } - default: - inflateEnd(&zs); - return nullptr; - } - - if (zs.avail_in) - { - auto cur_size = zs.next_out - out.data(); - out.resize(out.size() + 65536); - zs.avail_out = static_cast(out.size() - cur_size); - zs.next_out = out.data() + cur_size; - } + jit_log.error("LLVM: Failed to unzip module: '%s'", path); + return nullptr; } - out.resize(zs.next_out - out.data()); - inflateEnd(&zs); - auto buf = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(out.size()); std::memcpy(buf->getBufferStart(), out.data(), out.size()); return buf; diff --git a/Utilities/JIT.h b/Utilities/JIT.h index 498d5baf6b..60a9f155af 100644 --- a/Utilities/JIT.h +++ b/Utilities/JIT.h @@ -90,7 +90,7 @@ struct jit_runtime final : jit_runtime_base uchar* _alloc(usz size, usz align) noexcept override; // Allocate memory - static u8* alloc(usz size, uint align, bool exec = true) noexcept; + static u8* alloc(usz size, usz align, bool exec = true) noexcept; // Should be called at least once after global initialization static void initialize(); diff --git a/rpcs3/Crypto/unself.cpp b/rpcs3/Crypto/unself.cpp index a553bca1bd..8e1b8e9bd4 100644 --- a/rpcs3/Crypto/unself.cpp +++ b/rpcs3/Crypto/unself.cpp @@ -5,9 +5,9 @@ #include "Emu/VFS.h" #include "Emu/System.h" #include "Emu/system_utils.hpp" +#include "Crypto/unzip.h" #include -#include inline u8 Read8(const fs::file& f) { @@ -767,72 +767,32 @@ std::vector SCEDecrypter::MakeFile() u32 data_buf_offset = 0; // Write data. - for (unsigned int i = 0; i < meta_hdr.section_count; i++) + for (u32 i = 0; i < meta_hdr.section_count; i++) { + const MetadataSectionHeader& hdr = meta_shdr[i]; + const u8* src = data_buf.get() + data_buf_offset; fs::file out_f = fs::make_stream>(); - bool isValid = true; + bool is_valid = true; // Decompress if necessary. - if (meta_shdr[i].compressed == 2) + if (hdr.compressed == 2) { - const usz BUFSIZE = 32 * 1024; - u8 tempbuf[BUFSIZE]; - z_stream strm; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = ::narrow(meta_shdr[i].data_size); - strm.avail_out = BUFSIZE; - strm.next_in = data_buf.get()+data_buf_offset; - strm.next_out = tempbuf; -#ifndef _MSC_VER -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#endif - int ret = inflateInit(&strm); -#ifndef _MSC_VER -#pragma GCC diagnostic pop -#endif - while (strm.avail_in) - { - ret = inflate(&strm, Z_NO_FLUSH); - if (ret == Z_STREAM_END) - break; - if (ret != Z_OK) - isValid = false; - - if (!strm.avail_out) { - out_f.write(tempbuf, BUFSIZE); - strm.next_out = tempbuf; - strm.avail_out = BUFSIZE; - } - else - break; - } - - int inflate_res = Z_OK; - inflate_res = inflate(&strm, Z_FINISH); - - if (inflate_res != Z_STREAM_END) - isValid = false; - - out_f.write(tempbuf, BUFSIZE - strm.avail_out); - inflateEnd(&strm); + is_valid = unzip(src, hdr.data_size, out_f); } else { // Write the data. - out_f.write(data_buf.get()+data_buf_offset, meta_shdr[i].data_size); + out_f.write(src, hdr.data_size); } // Advance the data buffer offset by data size. - data_buf_offset += ::narrow(meta_shdr[i].data_size); + data_buf_offset += ::narrow(hdr.data_size); if (out_f.pos() != out_f.size()) fmt::throw_exception("MakeELF written bytes (%llu) does not equal buffer size (%llu).", out_f.pos(), out_f.size()); - if (isValid) vec.push_back(std::move(out_f)); + if (is_valid) vec.push_back(std::move(out_f)); } return vec; diff --git a/rpcs3/Crypto/unzip.cpp b/rpcs3/Crypto/unzip.cpp new file mode 100644 index 0000000000..f2d46f161a --- /dev/null +++ b/rpcs3/Crypto/unzip.cpp @@ -0,0 +1,185 @@ +#include "stdafx.h" +#include "unzip.h" + +#include + +std::vector unzip(const void* src, usz size) +{ + if (!src || !size) [[unlikely]] + { + return {}; + } + + std::vector out(size * 6); + + z_stream zs{}; +#ifndef _MSC_VER +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif + int res = inflateInit2(&zs, 16 + 15); +#ifndef _MSC_VER +#pragma GCC diagnostic pop +#endif + zs.avail_in = static_cast(size); + zs.next_in = reinterpret_cast(src); + zs.avail_out = static_cast(out.size()); + zs.next_out = out.data(); + + while (zs.avail_in) + { + switch (inflate(&zs, Z_FINISH)) + { + case Z_OK: + case Z_STREAM_END: + break; + case Z_BUF_ERROR: + if (zs.avail_in) + break; + [[fallthrough]]; + default: + inflateEnd(&zs); + return out; + } + + if (zs.avail_in) + { + const auto cur_size = zs.next_out - out.data(); + out.resize(out.size() + 65536); + zs.avail_out = static_cast(out.size() - cur_size); + zs.next_out = &out[cur_size]; + } + } + + out.resize(zs.next_out - out.data()); + + res = inflateEnd(&zs); + + return out; +} + +bool unzip(const void* src, usz size, fs::file& out) +{ + if (!src || !size || !out) + { + return false; + } + + bool is_valid = true; + + constexpr usz BUFSIZE = 32ULL * 1024ULL; + std::vector tempbuf(BUFSIZE); + z_stream strm{}; + strm.avail_in = ::narrow(size); + strm.avail_out = static_cast(tempbuf.size()); + strm.next_in = reinterpret_cast(src); + strm.next_out = tempbuf.data(); +#ifndef _MSC_VER +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif + int res = inflateInit(&strm); +#ifndef _MSC_VER +#pragma GCC diagnostic pop +#endif + while (strm.avail_in) + { + res = inflate(&strm, Z_NO_FLUSH); + if (res == Z_STREAM_END) + break; + if (res != Z_OK) + is_valid = false; + + if (strm.avail_out) + break; + + if (out.write(tempbuf.data(), tempbuf.size()) != tempbuf.size()) + { + is_valid = false; + } + + strm.next_out = tempbuf.data(); + strm.avail_out = static_cast(tempbuf.size()); + } + + res = inflate(&strm, Z_FINISH); + + if (res != Z_STREAM_END) + is_valid = false; + + if (strm.avail_out < tempbuf.size()) + { + const usz bytes_to_write = tempbuf.size() - strm.avail_out; + + if (out.write(tempbuf.data(), bytes_to_write) != bytes_to_write) + { + is_valid = false; + } + } + + res = inflateEnd(&strm); + + return is_valid; +} + +std::vector zip(const void* src, usz size) +{ + if (!src || !size) + { + return {}; + } + + const uLong zsz = compressBound(::narrow(size)) + 256; + std::vector out(zsz); + + z_stream zs{}; +#ifndef _MSC_VER +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif + int res = deflateInit2(&zs, 9, Z_DEFLATED, 16 + 15, 9, Z_DEFAULT_STRATEGY); +#ifndef _MSC_VER +#pragma GCC diagnostic pop +#endif + zs.avail_in = static_cast(size); + zs.next_in = reinterpret_cast(src); + zs.avail_out = static_cast(out.size()); + zs.next_out = out.data(); + + res = deflate(&zs, Z_FINISH); + + switch (res) + { + case Z_OK: + case Z_STREAM_END: + if (zs.avail_out) + { + out.resize(zsz - zs.avail_out); + } + break; + default: + out.clear(); + break; + } + + deflateEnd(&zs); + + return out; +} + +bool zip(const void* src, usz size, fs::file& out) +{ + if (!src || !size || !out) + { + return false; + } + + const std::vector zipped = zip(src, size); + + if (zipped.empty() || out.write(zipped.data(), zipped.size()) != zipped.size()) + { + return false; + } + + return true; +} diff --git a/rpcs3/Crypto/unzip.h b/rpcs3/Crypto/unzip.h new file mode 100644 index 0000000000..dc7701da3e --- /dev/null +++ b/rpcs3/Crypto/unzip.h @@ -0,0 +1,33 @@ +#pragma once + +std::vector unzip(const void* src, usz size); + +template +std::vector unzip(const T& src) +{ + return unzip(src.data(), src.size()); +} + +bool unzip(const void* src, usz size, fs::file& out); + +template +bool unzip(const std::vector& src, fs::file& out) +{ + return unzip(src.data(), src.size(), out); +} + +std::vector zip(const void* src, usz size); + +template +std::vector zip(const T& src) +{ + return zip(src.data(), src.size()); +} + +bool zip(const void* src, usz size, fs::file& out); + +template +bool zip(const T& src, fs::file& out) +{ + return zip(src.data(), src.size(), out); +} diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 70df0385bf..e29c119c88 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -116,6 +116,7 @@ target_sources(rpcs3_emu PRIVATE ../Crypto/unedat.cpp ../Crypto/unpkg.cpp ../Crypto/unself.cpp + ../Crypto/unzip.cpp ../Crypto/utils.cpp ) diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 5e7215434c..bcadb9051e 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -42,8 +42,8 @@ Use ..\3rdparty\miniupnp\miniupnp\miniupnpc\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\flatbuffers\include;..\3rdparty\libusb\libusb\libusb;..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\zlib\zlib;..\3rdparty\llvm\llvm\include;..\3rdparty\llvm\llvm\llvm\include;..\3rdparty\llvm\llvm_build\include;$(VULKAN_SDK)\Include MaxSpeed - MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL2;%(PreprocessorDefinitions) - MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL2;%(PreprocessorDefinitions) + MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL2;ZLIB_CONST;%(PreprocessorDefinitions) + MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL2;ZLIB_CONST;%(PreprocessorDefinitions) cmd.exe /c "$(SolutionDir)\Utilities\git-version-gen.cmd" @@ -54,6 +54,7 @@ + true @@ -484,6 +485,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index ee1c8bb2ec..087b89c120 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1171,6 +1171,9 @@ Emu\GPU\RSX\Core + + Crypto + @@ -2383,6 +2386,9 @@ Emu\GPU\RSX\Core + + Crypto + diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 0b9baabdcb..1df0ca2339 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -79,7 +79,7 @@ 4577;4467;4281;%(DisableSpecificWarnings) $(IntDir) MaxSpeed - _WINDOWS;UNICODE;WIN32;WIN64;WIN32_LEAN_AND_MEAN;HAVE_VULKAN;MINIUPNP_STATICLIB;HAVE_SDL2;WITH_DISCORD_RPC;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;NDEBUG;QT_WINEXTRAS_LIB;QT_CONCURRENT_LIB;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB;QT_SVG_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;WIN32;WIN64;WIN32_LEAN_AND_MEAN;HAVE_VULKAN;MINIUPNP_STATICLIB;ZLIB_CONST;HAVE_SDL2;WITH_DISCORD_RPC;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;NDEBUG;QT_WINEXTRAS_LIB;QT_CONCURRENT_LIB;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB;QT_SVG_LIB;%(PreprocessorDefinitions) false $(IntDir)vc$(PlatformToolsetVersion).pdb true @@ -132,7 +132,7 @@ 4577;4467;4281;%(DisableSpecificWarnings) $(IntDir) Disabled - _WINDOWS;UNICODE;WIN32;WIN64;WIN32_LEAN_AND_MEAN;HAVE_VULKAN;MINIUPNP_STATICLIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;QT_WINEXTRAS_LIB;QT_CONCURRENT_LIB;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB;QT_SVG_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;WIN32;WIN64;WIN32_LEAN_AND_MEAN;HAVE_VULKAN;MINIUPNP_STATICLIB;ZLIB_CONST;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;QT_WINEXTRAS_LIB;QT_CONCURRENT_LIB;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB;QT_SVG_LIB;%(PreprocessorDefinitions) false true true diff --git a/rpcs3/rpcs3qt/log_viewer.cpp b/rpcs3/rpcs3qt/log_viewer.cpp index d39bfd5367..8073bcee74 100644 --- a/rpcs3/rpcs3qt/log_viewer.cpp +++ b/rpcs3/rpcs3qt/log_viewer.cpp @@ -4,6 +4,7 @@ #include "gui_settings.h" #include "syntax_highlighter.h" #include "config_checker.h" +#include "Crypto/unzip.h" #include #include @@ -145,7 +146,7 @@ void log_viewer::show_context_menu(const QPoint& pos) connect(open, &QAction::triggered, this, [this]() { - const QString file_path = QFileDialog::getOpenFileName(this, tr("Select log file"), m_path_last, tr("Log files (*.log);;All files (*.*)")); + const QString file_path = QFileDialog::getOpenFileName(this, tr("Select log file"), m_path_last, tr("Log files (*.log *.gz);;All files (*.*)")); if (file_path.isEmpty()) return; m_path_last = file_path; @@ -154,14 +155,27 @@ void log_viewer::show_context_menu(const QPoint& pos) connect(save, &QAction::triggered, this, [this]() { - const QString file_path = QFileDialog::getSaveFileName(this, tr("Save to file"), m_path_last, tr("Log files (*.log);;All files (*.*)")); + const QString file_path = QFileDialog::getSaveFileName(this, tr("Save to file"), m_path_last, tr("Log files (*.log *.gz);;All files (*.*)")); if (file_path.isEmpty()) return; - if (QFile log_file(file_path); log_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + if (fs::file log_file; log_file.open(file_path.toStdString(), fs::rewrite)) { - log_file.write(m_log_text->toPlainText().toUtf8()); - log_file.close(); + const QByteArray bytes = m_log_text->toPlainText().toUtf8(); + + if (file_path.endsWith(".gz")) + { + if (!zip(bytes.constData(), bytes.size(), log_file)) + { + gui_log.error("Failed to zip filtered log to file '%s'", file_path); + return; + } + } + else + { + log_file.write(bytes.constData(), bytes.size()); + } + gui_log.success("Exported filtered log to file '%s'", file_path); } else @@ -230,24 +244,43 @@ void log_viewer::show_log() m_log_text->setPlainText(tr("Loading file...")); QApplication::processEvents(); - if (QFile file(m_path_last); - file.exists() && file.open(QIODevice::ReadOnly | QIODevice::Text)) - { - m_gui_settings->SetValue(gui::fd_log_viewer, m_path_last); + bool failed = false; + if (m_path_last.endsWith(".gz")) + { + if (fs::file file{m_path_last.toStdString()}) + { + const std::vector decompressed = unzip(file.to_vector()); + m_full_log = QString::fromUtf8(reinterpret_cast(decompressed.data()), decompressed.size()); + } + else + { + failed = true; + } + } + else if (QFile file(m_path_last); file.exists() && file.open(QIODevice::ReadOnly | QIODevice::Text)) + { // TODO: Due to a bug in Qt 6.5.2 QTextStream::readAll is ridiculously slow to the point where it gets stuck on large files. // In Qt 5.15.2 this was much faster than QFile::readAll. Use QTextStream again once this bug is fixed upstream. //QTextStream stream(&file); //m_full_log = stream.readAll(); m_full_log = file.readAll(); - m_full_log.replace('\0', '0'); - file.close(); } else + { + failed = true; + } + + if (failed) { gui_log.error("log_viewer: Failed to open %s", m_path_last); m_log_text->setPlainText(tr("Failed to open '%0'").arg(m_path_last)); } + else + { + m_gui_settings->SetValue(gui::fd_log_viewer, m_path_last); + m_full_log.replace('\0', '0'); + } filter_log(); } @@ -403,7 +436,7 @@ bool log_viewer::is_valid_file(const QMimeData& md, bool save) const QString suffix = QFileInfo(urls[0].fileName()).suffix().toLower(); - if (suffix == "log") + if (suffix == "log" || suffix == "gz") { if (save) {