refactor: refactor cli parser, print license on startup
This commit is contained in:
parent
c680eb5a51
commit
ca1162ed28
@ -14,6 +14,7 @@ add_executable(kgg-dec
|
||||
src/qmc2/qmc2_factory.cpp
|
||||
src/qmc2/qmc2_map.cpp
|
||||
src/qmc2/qmc2_rc4.cpp
|
||||
src/utils/cli.cpp
|
||||
)
|
||||
|
||||
target_include_directories(kgg-dec
|
||||
|
@ -27,7 +27,7 @@ class KggTask {
|
||||
void info(const wchar_t* msg) const { fwprintf(stderr, L"[INFO] %s (%s)\n", msg, kgg_path_.filename().c_str()); }
|
||||
void info(const std::wstring& msg) const { info(msg.c_str()); }
|
||||
|
||||
void Execute(const Infra::kgm_ekey_db_t& ekey_db) {
|
||||
void Execute(const Infra::kgm_ekey_db_t& ekey_db) const {
|
||||
constexpr static std::array<uint8_t, 16> kMagicHeader{0x7C, 0xD5, 0x32, 0xEB, 0x86, 0x02, 0x7F, 0x4B,
|
||||
0xA8, 0xAF, 0xA6, 0x8E, 0x0F, 0xFF, 0x99, 0x14};
|
||||
|
||||
@ -38,8 +38,8 @@ class KggTask {
|
||||
warning(L"invalid kgg header");
|
||||
return;
|
||||
}
|
||||
uint32_t offset_to_audio = *reinterpret_cast<uint32_t*>(&header[0x10]);
|
||||
uint32_t encrypt_mode = *reinterpret_cast<uint32_t*>(&header[0x14]);
|
||||
const uint32_t offset_to_audio = *reinterpret_cast<uint32_t*>(&header[0x10]);
|
||||
const uint32_t encrypt_mode = *reinterpret_cast<uint32_t*>(&header[0x14]);
|
||||
if (encrypt_mode != 5) {
|
||||
warning(std::format(L"unsupported enc_version (expect=0x05, got 0x{:02x})", encrypt_mode));
|
||||
return;
|
||||
@ -104,7 +104,7 @@ class KggTask {
|
||||
std::filesystem::path kgg_path_;
|
||||
std::filesystem::path out_dir_;
|
||||
|
||||
static const wchar_t* DetectRealExt(std::string_view magic) {
|
||||
static const wchar_t* DetectRealExt(const std::string_view magic) {
|
||||
if (magic == "fLaC") {
|
||||
return L"flac";
|
||||
}
|
||||
|
124
src/main.cpp
124
src/main.cpp
@ -1,22 +1,7 @@
|
||||
#include "infra/infra.h"
|
||||
#include "infra/sqlite_error.h"
|
||||
#include "jobs.hpp"
|
||||
#include "qmc2/qmc2.h"
|
||||
|
||||
// clang-format off
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <initguid.h>
|
||||
#include <knownfolders.h>
|
||||
// clang-format on
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include "utils/cli.h"
|
||||
|
||||
using Infra::kgm_ekey_db_t;
|
||||
|
||||
@ -48,83 +33,54 @@ void WalkFileOrDir(KggTaskQueue& queue, const std::filesystem::path& input_path,
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::vector<std::wstring>, std::unordered_map<std::wstring, std::wstring>> ParseCommandLine() {
|
||||
int argc;
|
||||
wchar_t** argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||
|
||||
std::vector<std::wstring> positional_args{};
|
||||
std::unordered_map<std::wstring, std::wstring> named_args{};
|
||||
|
||||
bool positional_only{false};
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::wstring arg{argv[i]};
|
||||
if (arg == L"--") {
|
||||
positional_only = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!positional_only && arg.starts_with(L"--")) {
|
||||
auto pos = arg.find(L'=');
|
||||
if (pos != std::wstring::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"";
|
||||
}
|
||||
} else {
|
||||
positional_args.push_back(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(positional_args, named_args);
|
||||
void print_license() {
|
||||
fputs(" This software is free and open source, licensed under the MIT license.\n", stderr);
|
||||
fputs(" For more details, check out: https://git.unlock-music.dev/um/kgg-dec\n", stderr);
|
||||
}
|
||||
|
||||
int main() {
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
setlocale(LC_ALL, ".UTF8");
|
||||
|
||||
fprintf(stderr, "kgg-dec v" KGGDEC_PROJECT_VERSION " by LSR\n");
|
||||
void print_usage() {
|
||||
fputs(
|
||||
"Usage: kgg-dec "
|
||||
"[--infra-dll infra.dll] "
|
||||
"[--scan-all-file-ext 0] "
|
||||
"[--db /path/to/KGMusicV3.db] "
|
||||
"[--] [kgg-dir... = '.']\n\n",
|
||||
"[--suffix _kgg-dec] "
|
||||
"[--] "
|
||||
"[FILE]...\n\n\n",
|
||||
stderr);
|
||||
}
|
||||
|
||||
auto [positional_args, named_args] = ParseCommandLine();
|
||||
if (positional_args.empty()) {
|
||||
positional_args.emplace_back(L".");
|
||||
}
|
||||
void print_banner() {
|
||||
fprintf(stderr, "kgg-dec v" KGGDEC_PROJECT_VERSION " by LSR\n");
|
||||
print_license();
|
||||
print_usage();
|
||||
}
|
||||
|
||||
std::filesystem::path kgm_db_path{};
|
||||
std::filesystem::path infra_dll_path{L"infra.dll"};
|
||||
if (auto it = named_args.find(L"infra-dll"); it != named_args.end()) {
|
||||
infra_dll_path = std::filesystem::path{it->second};
|
||||
}
|
||||
infra_dll_path = absolute(infra_dll_path);
|
||||
if (!exists(infra_dll_path)) {
|
||||
fputs("[ERR ] infra.dll not found\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
int main() {
|
||||
CliParser cli_args;
|
||||
print_banner();
|
||||
|
||||
bool scan_all_exts{false};
|
||||
if (auto it = named_args.find(L"scan-all-file-ext"); it != named_args.end()) {
|
||||
scan_all_exts = it->second == L"1";
|
||||
}
|
||||
cli_args.parse_from_cli();
|
||||
|
||||
if (auto it = named_args.find(L"db"); it != named_args.end()) {
|
||||
kgm_db_path = std::filesystem::path{it->second};
|
||||
} else {
|
||||
PWSTR pAppDirPath{};
|
||||
SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &pAppDirPath);
|
||||
kgm_db_path = std::filesystem::path{pAppDirPath} / L"Kugou8" / L"KGMusicV3.db";
|
||||
CoTaskMemFree(pAppDirPath);
|
||||
}
|
||||
if (!exists(kgm_db_path)) {
|
||||
fputs("[ERR ] KGMusicV3.db not found\n", stderr);
|
||||
return 1;
|
||||
bool scan_all_exts = cli_args.get_scan_all_file_ext();
|
||||
|
||||
auto infra_dll_path = cli_args.get_infra_dll();
|
||||
auto kgm_db_path = cli_args.get_db_path();
|
||||
auto file_suffix = cli_args.get_file_suffix();
|
||||
{
|
||||
bool cli_arg_error{false};
|
||||
if (!exists(infra_dll_path)) {
|
||||
fputs("[ERR ] infra.dll not found\n", stderr);
|
||||
cli_arg_error = true;
|
||||
}
|
||||
|
||||
if (!exists(kgm_db_path)) {
|
||||
fputs("[ERR ] KGMusicV3.db not found\n", stderr);
|
||||
cli_arg_error = true;
|
||||
}
|
||||
if (cli_arg_error) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int error{-1};
|
||||
@ -159,7 +115,11 @@ int main() {
|
||||
queue.AddWorkerThread();
|
||||
}
|
||||
|
||||
for (auto& positional_arg : positional_args) {
|
||||
auto input_files = cli_args.get_input_files();
|
||||
if (input_files.empty()) {
|
||||
input_files.emplace_back(L".");
|
||||
}
|
||||
for (auto& positional_arg : input_files) {
|
||||
WalkFileOrDir(queue, positional_arg, scan_all_exts);
|
||||
}
|
||||
queue.Join();
|
||||
|
67
src/utils/cli.cpp
Normal file
67
src/utils/cli.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
#include "cli.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <clocale>
|
||||
|
||||
// clang-format off
|
||||
#include <shlobj.h>
|
||||
#include <knownfolders.h>
|
||||
// clang-format on
|
||||
|
||||
CliParser::CliParser() {
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
setlocale(LC_ALL, ".UTF8");
|
||||
}
|
||||
|
||||
void CliParser::parse_from_cli() {
|
||||
int argc;
|
||||
wchar_t** argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||
|
||||
std::vector<std::wstring> positional_args{};
|
||||
std::unordered_map<std::wstring, std::wstring> named_args{};
|
||||
|
||||
bool positional_only{false};
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::wstring arg{argv[i]};
|
||||
if (arg == L"--") {
|
||||
positional_only = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!positional_only && arg.starts_with(L"--")) {
|
||||
auto pos = arg.find(L'=');
|
||||
if (pos != std::wstring::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"";
|
||||
}
|
||||
} else {
|
||||
positional_args.push_back(arg);
|
||||
}
|
||||
}
|
||||
LocalFree(argv);
|
||||
|
||||
positional_args_ = positional_args;
|
||||
named_args_ = named_args;
|
||||
}
|
||||
|
||||
std::filesystem::path CliParser::get_infra_dll() const {
|
||||
std::filesystem::path infra_dll_path{get_with_default(L"infra-dll", L"infra.dll")};
|
||||
return absolute(infra_dll_path);
|
||||
}
|
||||
|
||||
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()) {
|
||||
kugou_db = std::filesystem::path{it->second};
|
||||
} else {
|
||||
PWSTR pAppDirPath{};
|
||||
SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &pAppDirPath);
|
||||
kugou_db = std::filesystem::path{pAppDirPath} / L"Kugou8" / L"KGMusicV3.db";
|
||||
CoTaskMemFree(pAppDirPath);
|
||||
}
|
||||
|
||||
return absolute(kugou_db);
|
||||
}
|
33
src/utils/cli.h
Normal file
33
src/utils/cli.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
typedef std::pair<std::vector<std::wstring>, std::unordered_map<std::wstring, std::wstring>> parsed_raw_args_t;
|
||||
|
||||
class CliParser {
|
||||
public:
|
||||
CliParser();
|
||||
void parse_from_cli();
|
||||
|
||||
[[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<std::wstring> get_input_files() const { return positional_args_; }
|
||||
|
||||
private:
|
||||
std::vector<std::wstring> positional_args_{};
|
||||
std::unordered_map<std::wstring, std::wstring> named_args_{};
|
||||
|
||||
static parsed_raw_args_t parse();
|
||||
[[nodiscard]] std::wstring get_with_default(const std::wstring& key, const std::wstring& default_value) const {
|
||||
if (const auto& it = named_args_.find(key); it != named_args_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user