From 10e0c7446bda4a26f7d2ecb1d6c08edee9a70631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=81=E6=A0=91=E4=BA=BA?= Date: Sat, 21 Dec 2024 13:23:44 +0900 Subject: [PATCH] refactor: get linux build --- CMakeLists.txt | 7 +++-- CMakePresets.json | 20 +++++++++++- src/common/endian_helper.h | 1 + src/common/str_helper.h | 38 +++++++++++++++++++++++ src/infra/infra.h | 1 - src/jobs.hpp | 62 ++++++++++++++++++++------------------ src/main.cpp | 12 +++++--- src/utils/cli.cpp | 42 ++++++++++++++++++-------- src/utils/cli.h | 22 ++++++++------ third-party/aes/aes.h | 7 +++-- 10 files changed, 151 insertions(+), 61 deletions(-) create mode 100644 src/common/str_helper.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f562527..636b911 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,10 @@ target_include_directories(kgg-dec src/tc_tea ) -target_link_libraries(kgg-dec PRIVATE shell32 ole32 libaes libmd5) +target_link_libraries(kgg-dec PRIVATE libaes libmd5) +if (WIN32) + target_link_libraries(kgg-dec PRIVATE shell32 ole32) +endif () if (WinSQLite3_Found) target_link_libraries(kgg-dec PRIVATE WinSQLite3) target_include_directories(kgg-dec PRIVATE ${WindowsKitInclude}) @@ -47,4 +50,4 @@ target_compile_definitions(kgg-dec PRIVATE KGGDEC_PROJECT_VERSION="${PROJECT_VER if (USE_WIN_CRYPTO) target_compile_definitions(kgg-dec PRIVATE USE_WIN_CRYPTO=1) -endif () \ No newline at end of file +endif () diff --git a/CMakePresets.json b/CMakePresets.json index 7ab03a5..93b73c5 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -9,7 +9,7 @@ { "name": "default", "hidden": true, - "generator": "Ninja", + "generator": "Ninja Multi-Config", "cacheVariables": { "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" } @@ -29,6 +29,12 @@ "CMAKE_EXE_LINKER_FLAGS": "-static -static-libgcc -static-libstdc++ -lucrt" } }, + { + "name": "ninja", + "inherits": "default", + "description": "Default Ninja Configuration", + "binaryDir": "${sourceDir}/build/default" + }, { "name": "vs", "inherits": "default", @@ -54,6 +60,18 @@ "description": "Build using MinGW (Release)", "configuration": "Release" }, + { + "name": "ninja-debug", + "configurePreset": "ninja", + "description": "Build using Ninja (Debug)", + "configuration": "Debug" + }, + { + "name": "ninja-release", + "configurePreset": "ninja", + "description": "Build using Ninja (Release)", + "configuration": "Release" + }, { "name": "vs-debug", "configurePreset": "vs", diff --git a/src/common/endian_helper.h b/src/common/endian_helper.h index 3630549..5c2c228 100644 --- a/src/common/endian_helper.h +++ b/src/common/endian_helper.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include diff --git a/src/common/str_helper.h b/src/common/str_helper.h new file mode 100644 index 0000000..53f5315 --- /dev/null +++ b/src/common/str_helper.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +namespace lsr { + +#if _WIN32 +typedef wchar_t character; +typedef std::wstring string; +typedef std::wstring_view string_view; +#define LSR_STR(x) L##x + +inline void write_stderr(const string& msg) { + fputws(msg.c_str(), stderr); +} +#else +typedef char character; +typedef std::string string; +typedef std::string_view string_view; +#define LSR_STR(x) x + +inline void write_stderr(const string& msg) { + fputs(msg.c_str(), stderr); +} +#endif + +} // namespace lsr + +#if _WIN32 +#define lsr___fprintf fwprintf +#else +#define lsr___fprintf fprintf +#endif + +#define lsr_eprintf(fmt, ...) lsr___fprintf(stderr, LSR_STR(fmt), __VA_ARGS__) +#define lsr_printf(fmt, ...) lsr___fprintf(stdout, LSR_STR(fmt), __VA_ARGS__) diff --git a/src/infra/infra.h b/src/infra/infra.h index 519aa77..c338db0 100644 --- a/src/infra/infra.h +++ b/src/infra/infra.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include namespace Infra { diff --git a/src/jobs.hpp b/src/jobs.hpp index 01832b7..55b772b 100644 --- a/src/jobs.hpp +++ b/src/jobs.hpp @@ -1,18 +1,20 @@ #pragma once + +#include "infra/infra.h" #include "qmc2/qmc2.h" +#ifdef _WIN32 #include +#endif + +#include #include #include #include -#include #include #include #include -#include #include -#include -#include #include class KggTask { @@ -20,18 +22,14 @@ class KggTask { explicit KggTask(std::filesystem::path kgg_path, std::filesystem::path out_dir) : kgg_path_(std::move(kgg_path)), out_dir_(std::move(out_dir)) {} - static void log(const std::wstring& msg) { fputws(msg.c_str(), stderr); } - void warning(const std::wstring& msg) const { - log(std::format(L"[WARN] {} ({})\n", msg, kgg_path_.filename().wstring())); - } - void error(const std::wstring& msg) const { - log(std::format(L"[ERR ] {} ({})\n", msg, kgg_path_.filename().wstring())); - } - void info(const std::wstring& msg) const { - log(std::format(L"[INFO] {} ({})\n", msg, kgg_path_.filename().wstring())); + void log(const lsr::string& level, const lsr::string& msg) const { + lsr_eprintf("[%s] %s (%s)\n", level.c_str(), msg.c_str(), kgg_path_.filename().c_str()); } + void warning(const lsr::string& msg) const { log(LSR_STR("WARN"), msg); } + void error(const lsr::string& msg) const { log(LSR_STR("ERR "), msg); } + void info(const lsr::string& msg) const { log(LSR_STR("INFO"), msg); } - void Execute(const Infra::kgm_ekey_db_t& ekey_db, const std::wstring_view suffix) const { + void Execute(const Infra::kgm_ekey_db_t& ekey_db, const lsr::string_view suffix) const { constexpr static std::array kMagicHeader{0x7C, 0xD5, 0x32, 0xEB, 0x86, 0x02, 0x7F, 0x4B, 0xA8, 0xAF, 0xA6, 0x8E, 0x0F, 0xFF, 0x99, 0x14}; @@ -39,18 +37,20 @@ class KggTask { char header[0x100]{}; kgg_stream_in.read(header, sizeof(header)); if (std::equal(kMagicHeader.cbegin(), kMagicHeader.cend(), header)) { - warning(L"invalid kgg header"); + warning(LSR_STR("invalid kgg header")); return; } const uint32_t offset_to_audio = *reinterpret_cast(&header[0x10]); const uint32_t encrypt_mode = *reinterpret_cast(&header[0x14]); if (encrypt_mode != 5) { - warning(std::format(L"unsupported enc_version (expect=0x05, got 0x{:02x})", encrypt_mode)); + lsr_eprintf("[WARN] unsupported enc_version (expect=0x05, got 0x%02x) (%s)\n", encrypt_mode, + kgg_path_.filename().c_str()); return; } uint32_t audio_hash_len = *reinterpret_cast(&header[0x44]); if (audio_hash_len != 0x20) { - warning(std::format(L"audio hash length invalid (expect=0x20, got 0x{:02x})", audio_hash_len)); + lsr_eprintf("audio hash length invalid (expect=0x20, got 0x%02x) (%s)\n", audio_hash_len, + kgg_path_.filename().c_str()); return; } std::string audio_hash(&header[0x48], &header[0x48 + audio_hash_len]); @@ -58,13 +58,13 @@ class KggTask { if (auto it = ekey_db.find(audio_hash); it != ekey_db.end()) { ekey = it->second; } else { - warning(L"ekey not found"); + warning(LSR_STR("ekey not found")); return; } auto qmc2 = QMC2::Create(ekey); if (!qmc2) { - error(L"create qmc2 instance failed (ekey decode error?)"); + error(LSR_STR("create qmc2 instance failed (ekey decode error?)")); fprintf(stderr, "%s\n", ekey.c_str()); return; } @@ -74,17 +74,19 @@ class KggTask { kgg_stream_in.read(magic.data(), 4); qmc2->Decrypt(std::span(reinterpret_cast(magic.data()), 4), 0); auto real_ext = DetectRealExt(magic); - auto out_path = out_dir_ / std::format(L"{}{}.{}", kgg_path_.stem().wstring(), suffix, real_ext); + + 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(std::format(L"output file already exists: {}", out_path.filename().wstring())); + warning(lsr::string(LSR_STR("output file already exists: ")) + out_path.filename().native()); return; } kgg_stream_in.seekg(offset_to_audio, std::ios::beg); std::ofstream ofs_decrypted(out_path, std::ios::binary); if (!ofs_decrypted.is_open()) { - error(L"failed to open output file"); + error(LSR_STR("failed to open output file")); return; } @@ -101,27 +103,27 @@ class KggTask { offset += n; } - info(std::format(L"** OK ** -> {}", out_path.filename().wstring())); + info(lsr::string(LSR_STR("** OK ** -> ")) + out_path.filename().native()); } private: std::filesystem::path kgg_path_; std::filesystem::path out_dir_; - static const wchar_t* DetectRealExt(const std::string_view magic) { + static const lsr::character* DetectRealExt(const std::string_view magic) { if (magic == "fLaC") { - return L"flac"; + return LSR_STR("flac"); } if (magic == "OggS") { - return L"ogg"; + return LSR_STR("ogg"); } - return L"mp3"; + return LSR_STR("mp3"); } }; class KggTaskQueue { public: - explicit KggTaskQueue(Infra::kgm_ekey_db_t ekey_db, const std::wstring_view suffix) + explicit KggTaskQueue(Infra::kgm_ekey_db_t ekey_db, const lsr::string_view suffix) : ekey_db_(std::move(ekey_db)), suffix_(suffix) {} void Push(std::unique_ptr task) { @@ -165,9 +167,11 @@ class KggTaskQueue { private: bool thread_end_{false}; Infra::kgm_ekey_db_t ekey_db_; - std::wstring suffix_; + lsr::string suffix_; void WorkerThreadBody() { +#ifdef _WIN32 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); +#endif std::unique_ptr task{nullptr}; while ((task = Pop())) { diff --git a/src/main.cpp b/src/main.cpp index c6d5d34..2725e12 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,11 @@ #include +#include +#include + #include "infra/infra.h" #include "jobs.hpp" +#include "str_helper.h" #include "utils/cli.h" using Infra::kgm_ekey_db_t; @@ -30,7 +34,7 @@ void WalkFileOrDir(KggTaskQueue& queue, const std::filesystem::path& input_path, continue; } - fputws(std::format(L"[WARN] invalid path: {}\n", target_path.wstring()).c_str(), stderr); + lsr_eprintf("[WARN] invalid path: %s\n", target_path.c_str()); } } @@ -56,11 +60,11 @@ void print_banner() { print_usage(); } -int main() { +int main(int argc, char** argv) { CliParser cli_args; print_banner(); - cli_args.parse_from_cli(); + cli_args.parse_from_cli(argc, argv); bool scan_all_exts = cli_args.get_scan_all_file_ext(); @@ -105,7 +109,7 @@ int main() { auto input_files = cli_args.get_input_files(); if (input_files.empty()) { - input_files.emplace_back(L"."); + input_files.emplace_back(LSR_STR(".")); } for (auto& positional_arg : input_files) { WalkFileOrDir(queue, positional_arg, scan_all_exts); diff --git a/src/utils/cli.cpp b/src/utils/cli.cpp index ffdff8e..115ea46 100644 --- a/src/utils/cli.cpp +++ b/src/utils/cli.cpp @@ -1,47 +1,58 @@ #include "cli.h" +#include -#include -#include - +#ifdef _WIN32 // clang-format off +#include +#include #include #include // clang-format on +#endif CliParser::CliParser() { +#ifdef _WIN32 SetConsoleOutputCP(CP_UTF8); setlocale(LC_ALL, ".UTF8"); +#endif } -void CliParser::parse_from_cli() { - int argc; +void CliParser::parse_from_cli(int argc_, char** argv_) { + int argc{argc_}; +#ifdef _WIN32 wchar_t** argv = CommandLineToArgvW(GetCommandLineW(), &argc); +#else + char**& argv = argv_; +#endif - std::vector positional_args{}; - std::unordered_map named_args{}; + std::vector positional_args{}; + std::unordered_map named_args{}; bool positional_only{false}; for (int i = 1; i < argc; i++) { - std::wstring arg{argv[i]}; - if (arg == L"--") { + lsr::string arg{argv[i]}; + if (arg == LSR_STR("--")) { positional_only = true; continue; } - if (!positional_only && arg.starts_with(L"--")) { + if (!positional_only && arg.starts_with(LSR_STR("--"))) { auto pos = arg.find(L'='); - if (pos != std::wstring::npos) { + if (pos != lsr::string::npos) { named_args[arg.substr(2, pos - 2)] = arg.substr(pos + 1); } else if (++i < argc) { named_args[arg.substr(2)] = argv[i]; } else { - named_args[arg.substr(2)] = L""; + named_args[arg.substr(2)] = LSR_STR(""); } } else { positional_args.push_back(arg); } } + +#ifdef _WIN32 LocalFree(argv); +#endif positional_args_ = positional_args; named_args_ = named_args; @@ -49,13 +60,18 @@ void CliParser::parse_from_cli() { std::filesystem::path CliParser::get_db_path() const { std::filesystem::path kugou_db{}; - if (const auto& it = named_args_.find(L"db"); it != named_args_.end()) { + + if (const auto& it = named_args_.find(LSR_STR("db")); it != named_args_.end()) { kugou_db = std::filesystem::path{it->second}; } else { +#ifdef _WIN32 PWSTR pAppDirPath{}; SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &pAppDirPath); kugou_db = std::filesystem::path{pAppDirPath} / L"Kugou8" / L"KGMusicV3.db"; CoTaskMemFree(pAppDirPath); +#else + kugou_db = std::filesystem::path{"KGMusicV3.db"}; +#endif } return absolute(kugou_db); diff --git a/src/utils/cli.h b/src/utils/cli.h index d5b1f68..ee7699e 100644 --- a/src/utils/cli.h +++ b/src/utils/cli.h @@ -1,30 +1,34 @@ #pragma once +#include #include -#include #include #include #include -typedef std::pair, std::unordered_map> parsed_raw_args_t; +typedef std::pair, std::unordered_map> parsed_raw_args_t; class CliParser { public: CliParser(); - void parse_from_cli(); + void parse_from_cli(int argc, char** argv); [[nodiscard]] std::filesystem::path get_infra_dll() const; [[nodiscard]] std::filesystem::path get_db_path() const; - [[nodiscard]] std::wstring get_file_suffix() const { return get_with_default(L"suffix", L"_kgg-dec"); } - [[nodiscard]] bool get_scan_all_file_ext() const { return get_with_default(L"scan-all-file-ext", L"0") == L"1"; }; - [[nodiscard]] std::vector get_input_files() const { return positional_args_; } + [[nodiscard]] lsr::string get_file_suffix() const { + return get_with_default(LSR_STR("suffix"), LSR_STR("_kgg-dec")); + } + [[nodiscard]] bool get_scan_all_file_ext() const { + return get_with_default(LSR_STR("scan-all-file-ext"), LSR_STR("0")) == LSR_STR("1"); + }; + [[nodiscard]] std::vector get_input_files() const { return positional_args_; } private: - std::vector positional_args_{}; - std::unordered_map named_args_{}; + std::vector positional_args_{}; + std::unordered_map named_args_{}; static parsed_raw_args_t parse(); - [[nodiscard]] std::wstring get_with_default(const std::wstring& key, const std::wstring& default_value) const { + [[nodiscard]] lsr::string get_with_default(const lsr::string& key, const lsr::string& default_value) const { if (const auto& it = named_args_.find(key); it != named_args_.end()) { return it->second; } diff --git a/third-party/aes/aes.h b/third-party/aes/aes.h index 932c8b1..4649012 100644 --- a/third-party/aes/aes.h +++ b/third-party/aes/aes.h @@ -1,5 +1,6 @@ #pragma once +#include #include #if USE_WIN_CRYPTO @@ -25,7 +26,7 @@ struct AES_ctx { #endif }; -bool AES_init_ctx_iv(AES_ctx* ctx, const uint8_t* key, const uint8_t* iv); +bool AES_init_ctx_iv(AES_ctx* ctx, const uint8_t* key, const uint8_t* iv); // buffer size MUST be mutile of AES_BLOCKLEN; // Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme @@ -37,7 +38,9 @@ size_t AES_CBC_decrypt_buffer(AES_ctx* ctx, uint8_t* buf, size_t length); #if USE_WIN_CRYPTO bool AES_cleanup(AES_ctx* ctx); #else -inline bool AES_cleanup(AES_ctx* ctx) {return true;} +inline bool AES_cleanup(AES_ctx* ctx) { + return true; +} #endif } // namespace AES