diff --git a/src/jobs.hpp b/src/jobs.hpp index 55b772b..c513742 100644 --- a/src/jobs.hpp +++ b/src/jobs.hpp @@ -1,12 +1,16 @@ #pragma once +#include #include "infra/infra.h" #include "qmc2/qmc2.h" #ifdef _WIN32 #include +#else +#include #endif +#include #include #include #include @@ -14,7 +18,6 @@ #include #include #include -#include #include class KggTask { @@ -34,16 +37,15 @@ class KggTask { 0xA8, 0xAF, 0xA6, 0x8E, 0x0F, 0xFF, 0x99, 0x14}; std::ifstream kgg_stream_in(kgg_path_, std::ios::binary); - char header[0x100]{}; - kgg_stream_in.read(header, sizeof(header)); - if (std::equal(kMagicHeader.cbegin(), kMagicHeader.cend(), header)) { - warning(LSR_STR("invalid kgg header")); + std::array header{}; + kgg_stream_in.read(reinterpret_cast(header.data()), header.size()); + if (!std::equal(kMagicHeader.cbegin(), kMagicHeader.cend(), header.cbegin())) { + warning(LSR_STR("invalid kgg header (not a kgg file)")); return; } - const uint32_t offset_to_audio = *reinterpret_cast(&header[0x10]); - const uint32_t encrypt_mode = *reinterpret_cast(&header[0x14]); - if (encrypt_mode != 5) { - lsr_eprintf("[WARN] unsupported enc_version (expect=0x05, got 0x%02x) (%s)\n", encrypt_mode, + const auto offset_to_audio = Endian::le_read(&header[0x10]); + if (const auto mode = Endian::le_read(&header[0x14]); mode != 5) { + lsr_eprintf("[WARN] unsupported enc_version (expect=0x05, got 0x%02x) (%s)\n", mode, kgg_path_.filename().c_str()); return; } @@ -69,20 +71,22 @@ class KggTask { return; } - std::string magic(4, 0); + std::array magic{}; kgg_stream_in.seekg(offset_to_audio, std::ios::beg); - kgg_stream_in.read(magic.data(), 4); - qmc2->Decrypt(std::span(reinterpret_cast(magic.data()), 4), 0); + kgg_stream_in.read(reinterpret_cast(magic.data()), 4); + qmc2->Decrypt(magic, 0); auto real_ext = DetectRealExt(magic); lsr::string new_name = kgg_path_.stem().native() + lsr::string(suffix) + LSR_STR(".") + real_ext; auto out_path = out_dir_ / new_name; if (exists(out_path)) { - warning(lsr::string(LSR_STR("output file already exists: ")) + out_path.filename().native()); + warning(lsr::string(LSR_STR("output file already exists: ")) + new_name); return; } + kgg_stream_in.seekg(0, std::ios::end); + const auto file_size = static_cast(kgg_stream_in.tellg()); kgg_stream_in.seekg(offset_to_audio, std::ios::beg); std::ofstream ofs_decrypted(out_path, std::ios::binary); if (!ofs_decrypted.is_open()) { @@ -92,29 +96,32 @@ class KggTask { size_t offset{0}; thread_local std::vector temp_buffer(1024 * 1024, 0); - auto buf_signed = std::span(reinterpret_cast(temp_buffer.data()), temp_buffer.size()); - auto buf_unsigned = std::span(temp_buffer); + auto read_page_len = static_cast(temp_buffer.size()); while (!kgg_stream_in.eof()) { - kgg_stream_in.read(buf_signed.data(), buf_signed.size()); - const auto n = static_cast(kgg_stream_in.gcount()); - qmc2->Decrypt(buf_unsigned.subspan(0, n), offset); - ofs_decrypted.write(buf_signed.data(), n); + kgg_stream_in.read(reinterpret_cast(temp_buffer.data()), read_page_len); + const auto n = kgg_stream_in.gcount(); + qmc2->Decrypt(std::span(temp_buffer.begin(), temp_buffer.begin() + n), offset); + ofs_decrypted.write(reinterpret_cast(temp_buffer.data()), n); offset += n; } - info(lsr::string(LSR_STR("** OK ** -> ")) + out_path.filename().native()); + if (offset + offset_to_audio != file_size) { + warning(LSR_STR("OK (size mismatch)")); + } else { + info(lsr::string(LSR_STR("** OK ** -> ")) + out_path.filename().native()); + } } private: std::filesystem::path kgg_path_; std::filesystem::path out_dir_; - static const lsr::character* DetectRealExt(const std::string_view magic) { - if (magic == "fLaC") { + static const lsr::character* DetectRealExt(const std::span magic) { + if (std::equal(magic.begin(), magic.end(), "fLaC")) { return LSR_STR("flac"); } - if (magic == "OggS") { + if (std::equal(magic.begin(), magic.end(), "OggS")) { return LSR_STR("ogg"); } return LSR_STR("mp3"); @@ -169,16 +176,32 @@ class KggTaskQueue { Infra::kgm_ekey_db_t ekey_db_; lsr::string suffix_; void WorkerThreadBody() { -#ifdef _WIN32 - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); -#endif - + ReduceThreadPriority(); std::unique_ptr task{nullptr}; while ((task = Pop())) { task->Execute(ekey_db_, suffix_); } } + static void ReduceThreadPriority() { +#ifdef _WIN32 + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); +#else + pthread_t this_thread = pthread_self(); + sched_param params; + int policy; + + if (pthread_getschedparam(this_thread, &policy, ¶ms) != 0) { + perror("pthread_getschedparam"); + return; + } + params.sched_priority = std::max(params.sched_priority, sched_get_priority_min(policy)); + if (pthread_setschedparam(this_thread, policy, ¶ms) != 0) { + perror("pthread_setschedparam"); + } +#endif + } + std::mutex mutex_{}; std::condition_variable signal_; std::queue> tasks_{};