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_factory.cpp
|
||||||
src/qmc2/qmc2_map.cpp
|
src/qmc2/qmc2_map.cpp
|
||||||
src/qmc2/qmc2_rc4.cpp
|
src/qmc2/qmc2_rc4.cpp
|
||||||
|
src/utils/cli.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(kgg-dec
|
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 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 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,
|
constexpr static std::array<uint8_t, 16> kMagicHeader{0x7C, 0xD5, 0x32, 0xEB, 0x86, 0x02, 0x7F, 0x4B,
|
||||||
0xA8, 0xAF, 0xA6, 0x8E, 0x0F, 0xFF, 0x99, 0x14};
|
0xA8, 0xAF, 0xA6, 0x8E, 0x0F, 0xFF, 0x99, 0x14};
|
||||||
|
|
||||||
@ -38,8 +38,8 @@ class KggTask {
|
|||||||
warning(L"invalid kgg header");
|
warning(L"invalid kgg header");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint32_t offset_to_audio = *reinterpret_cast<uint32_t*>(&header[0x10]);
|
const uint32_t offset_to_audio = *reinterpret_cast<uint32_t*>(&header[0x10]);
|
||||||
uint32_t encrypt_mode = *reinterpret_cast<uint32_t*>(&header[0x14]);
|
const uint32_t encrypt_mode = *reinterpret_cast<uint32_t*>(&header[0x14]);
|
||||||
if (encrypt_mode != 5) {
|
if (encrypt_mode != 5) {
|
||||||
warning(std::format(L"unsupported enc_version (expect=0x05, got 0x{:02x})", encrypt_mode));
|
warning(std::format(L"unsupported enc_version (expect=0x05, got 0x{:02x})", encrypt_mode));
|
||||||
return;
|
return;
|
||||||
@ -104,7 +104,7 @@ class KggTask {
|
|||||||
std::filesystem::path kgg_path_;
|
std::filesystem::path kgg_path_;
|
||||||
std::filesystem::path out_dir_;
|
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") {
|
if (magic == "fLaC") {
|
||||||
return L"flac";
|
return L"flac";
|
||||||
}
|
}
|
||||||
|
114
src/main.cpp
114
src/main.cpp
@ -1,22 +1,7 @@
|
|||||||
#include "infra/infra.h"
|
#include "infra/infra.h"
|
||||||
#include "infra/sqlite_error.h"
|
#include "infra/sqlite_error.h"
|
||||||
#include "jobs.hpp"
|
#include "jobs.hpp"
|
||||||
#include "qmc2/qmc2.h"
|
#include "utils/cli.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>
|
|
||||||
|
|
||||||
using Infra::kgm_ekey_db_t;
|
using Infra::kgm_ekey_db_t;
|
||||||
|
|
||||||
@ -48,84 +33,55 @@ void WalkFileOrDir(KggTaskQueue& queue, const std::filesystem::path& input_path,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::vector<std::wstring>, std::unordered_map<std::wstring, std::wstring>> ParseCommandLine() {
|
void print_license() {
|
||||||
int argc;
|
fputs(" This software is free and open source, licensed under the MIT license.\n", stderr);
|
||||||
wchar_t** argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
fputs(" For more details, check out: https://git.unlock-music.dev/um/kgg-dec\n", stderr);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
void print_usage() {
|
||||||
SetConsoleOutputCP(CP_UTF8);
|
|
||||||
setlocale(LC_ALL, ".UTF8");
|
|
||||||
|
|
||||||
fprintf(stderr, "kgg-dec v" KGGDEC_PROJECT_VERSION " by LSR\n");
|
|
||||||
fputs(
|
fputs(
|
||||||
"Usage: kgg-dec "
|
"Usage: kgg-dec "
|
||||||
"[--infra-dll infra.dll] "
|
"[--infra-dll infra.dll] "
|
||||||
"[--scan-all-file-ext 0] "
|
"[--scan-all-file-ext 0] "
|
||||||
"[--db /path/to/KGMusicV3.db] "
|
"[--db /path/to/KGMusicV3.db] "
|
||||||
"[--] [kgg-dir... = '.']\n\n",
|
"[--suffix _kgg-dec] "
|
||||||
|
"[--] "
|
||||||
|
"[FILE]...\n\n\n",
|
||||||
stderr);
|
stderr);
|
||||||
|
}
|
||||||
|
|
||||||
auto [positional_args, named_args] = ParseCommandLine();
|
void print_banner() {
|
||||||
if (positional_args.empty()) {
|
fprintf(stderr, "kgg-dec v" KGGDEC_PROJECT_VERSION " by LSR\n");
|
||||||
positional_args.emplace_back(L".");
|
print_license();
|
||||||
}
|
print_usage();
|
||||||
|
}
|
||||||
|
|
||||||
std::filesystem::path kgm_db_path{};
|
int main() {
|
||||||
std::filesystem::path infra_dll_path{L"infra.dll"};
|
CliParser cli_args;
|
||||||
if (auto it = named_args.find(L"infra-dll"); it != named_args.end()) {
|
print_banner();
|
||||||
infra_dll_path = std::filesystem::path{it->second};
|
|
||||||
}
|
cli_args.parse_from_cli();
|
||||||
infra_dll_path = absolute(infra_dll_path);
|
|
||||||
|
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)) {
|
if (!exists(infra_dll_path)) {
|
||||||
fputs("[ERR ] infra.dll not found\n", stderr);
|
fputs("[ERR ] infra.dll not found\n", stderr);
|
||||||
return 1;
|
cli_arg_error = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) {
|
if (!exists(kgm_db_path)) {
|
||||||
fputs("[ERR ] KGMusicV3.db not found\n", stderr);
|
fputs("[ERR ] KGMusicV3.db not found\n", stderr);
|
||||||
|
cli_arg_error = true;
|
||||||
|
}
|
||||||
|
if (cli_arg_error) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int error{-1};
|
int error{-1};
|
||||||
Infra::KugouDb db{infra_dll_path, kgm_db_path};
|
Infra::KugouDb db{infra_dll_path, kgm_db_path};
|
||||||
@ -159,7 +115,11 @@ int main() {
|
|||||||
queue.AddWorkerThread();
|
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);
|
WalkFileOrDir(queue, positional_arg, scan_all_exts);
|
||||||
}
|
}
|
||||||
queue.Join();
|
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