# kgm_decoder **Repository Path**: setoutsoft/kgm_decoder ## Basic Information - **Project Name**: kgm_decoder - **Description**: kgm decoder. - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-19 - **Last Updated**: 2026-05-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # KGM Decoder - C++ Version This is a C++ port of the KuGou KGM (酷狗音乐加密格式) decoder algorithm from the original Rust implementation. ## Features - Decodes KuGou encrypted audio files (.kgm format) - Implements the same XOR-based decryption algorithm as the Rust version - **Automatic file type detection** based on magic bytes (MP3, FLAC, WAV, AAC, OGG, M4A, WMA, APE, TTA) - Includes test program that verifies correctness against known output - Flexible key loading: pre-extracted binary (default) or runtime XZ decompression ## Prerequisites - C++17 compatible compiler (GCC, Clang, or MSVC) - CMake 3.10 or higher - Python 3 (for extracting the public key from XZ format) ### Installing Dependencies **Ubuntu/Debian:** ```bash sudo apt-get install build-essential cmake python3 liblzma-dev ``` **macOS:** ```bash brew install cmake python3 xz ``` **Windows:** - Visual Studio 2019 or later with C++ workload - CMake - Python 3 - (Optional) vcpkg for liblzma: `vcpkg install liblzma:x64-windows` ## Setup ### Option 1: Using Embedded Key (Recommended) The C++ version now embeds the XZ compressed key directly in the source code. No additional setup is required! The key data is stored in `src_c/kugou_key_embedded.h` as a C++ array, which is compiled into the binary. This approach: - ✅ No external files needed at runtime - ✅ Single executable deployment - ✅ Faster startup (no file I/O) - ❌ Larger binary size (~95 KB for the embedded XZ data) If you need to regenerate the embedded key (e.g., after updating kugou_key.xz): ```bash cd d:\work\kugou-kgm-decoder python generate_embedded_key.py assets/kugou_key.xz src_c/kugou_key_embedded.h ``` ### Option 2: Runtime XZ File Loading (Alternative) If you prefer to load the XZ file at runtime instead of embedding it: 1. Ensure `assets/kugou_key.xz` exists (90 KB) 2. Modify `kgm_decoder.cpp` to read from file instead of using the embedded array Note: The current implementation uses the embedded approach by default for simplicity and portability. ## Building ### Default Build (Pre-extracted Key) ```bash cd src_c mkdir build cd build cmake .. cmake --build . ``` ### Build with XZ Support ```bash cd src_c mkdir build cd build cmake .. -DUSE_PREEXTRACTED_KEY=OFF cmake --build . ``` ## Running Tests After building, run the test program from the build directory: ```bash cd src_c/build ./Debug/test_decode.exe # Windows ./test_decode # Linux/macOS ``` The test will: 1. Run extension inference tests (magic bytes detection, path utilities) 2. Load the test KGM file from `assets/test_kugou_kgm.dat` 3. Decode it using the KGM algorithm 4. Infer the file type from decoded content 5. Compare the result with the expected output in `assets/test_kugou_kgm_right.dat` 6. Report success or failure Expected output: ``` Testing KGM Decoder... === Testing Extension Inference === Test 1 - ID3 header: PASS (got: mp3) Test 2 - FLAC header: PASS (got: flac) ... === Extension Inference Tests Complete === Decoder created successfully Decoded 4363 bytes Inferred extension for decoded data: mp3 Expected 4363 bytes Decoded 4363 bytes ✓ Test PASSED! Decoded data matches expected output. ``` ## File Type Detection The C++ implementation includes automatic file type detection based on magic bytes signatures: ### Supported Formats | Format | Magic Bytes | Extension | |--------|-------------|-----------| | MP3 (ID3) | `49 44 33` ("ID3") | `.mp3` | | MP3 (MPEG) | `FF E0-FF` | `.mp3` | | FLAC | `66 4C 61 43` ("fLaC") | `.flac` | | WAV | `52 49 46 46` + "WAVE" | `.wav` | | AAC | `FF F0-FF` | `.aac` | | OGG | `4F 67 67 53` ("OggS") | `.ogg` | | M4A/MP4 | `ftyp` or `M4A ` at offset 4 | `.m4a` | | WMA/ASF | `30 26 B2 75 ...` (GUID) | `.wma` | | APE | `4D 41 43 20` ("MAC ") | `.ape` | | TTA | `54 54 41 31` ("TTA1") | `.tta` | ### Usage Example ```cpp #include "kgm_decoder.h" // After decoding, infer the file extension std::vector decodedData = /* ... decode KGM file ... */; // Auto-detect from first bytes std::string ext = FileUtils::inferExtension("song.kgm", decodedData, ""); // Returns: "mp3", "flac", etc. based on content // Or force a specific extension std::string ext = FileUtils::inferExtension("song.kgm", decodedData, "flac"); // Returns: "flac" // Build output path std::string outputPath = FileUtils::buildOutputPath("song.kgm", ext); // Returns: "song.mp3" or "song.flac" etc. ``` ## Algorithm Overview The KGM decoder uses a two-layer XOR encryption scheme: 1. **Own Key Layer**: Uses a 17-byte key extracted from the file header (bytes 0x1c-0x2c) 2. **Public Key Layer**: Uses a large public key (73 MB decompressed / 90 KB compressed) loaded from key file Each byte is decoded using: ```cpp // Own key transformation ownKeyByte = ownKey[globalPos % 17] ^ encryptedByte ownKeyByte ^= (ownKeyByte & 0x0f) << 4 // Public key transformation pubKeyByte = PUB_KEY_MEND[globalPos % 272] ^ pubKey[globalPos / 16] pubKeyByte ^= (pubKeyByte & 0x0f) << 4 // Final decryption decryptedByte = ownKeyByte ^ pubKeyByte ``` The constants are: - `MAGIC_HEADER`: 28-byte signature at the start of KGM files - `PUB_KEY_MEND`: 272-byte constant array used in public key transformation - `PUB_KEY_LEN_MAGNIFICATION`: 16 (compression ratio of the public key) ## API Reference ### KuGouDecoder Class ```cpp // Create decoder from input stream std::unique_ptr decoder = KuGouDecoder::tryCreate(inputStream); // Decode data in chunks std::vector output; while (decoder->decode(output) > 0) { // Process decoded data } ``` ### FileUtils Namespace ```cpp // Infer extension from magic bytes std::string ext = FileUtils::inferExtensionFromMagic(header); // Get file extension from path std::string ext = FileUtils::getFileExtension("/path/to/file.mp3"); // Returns "mp3" // Get filename without extension std::string stem = FileUtils::getFileStem("/path/to/song.kgm"); // Returns "song" // Build output path with new extension std::string outPath = FileUtils::buildOutputPath("song.kgm", "mp3"); // Returns "song.mp3" // Smart extension inference (override -> detect -> fallback -> default) std::string ext = FileUtils::inferExtension(filePath, header, overrideExt); ``` ## File Structure - `kgm_decoder.h` - Header file with KuGouDecoder class and FileUtils declarations - `kgm_decoder.cpp` - Implementation of the decoder algorithm and file utilities - `test_decode.cpp` - Test program that verifies decoding and extension inference - `CMakeLists.txt` - Build configuration with optional liblzma support - `README.md` - This documentation ## Implementation Notes ### Key Differences from Rust Version 1. **Public Key Loading**: - Rust: Embeds XZ at compile time (`include_bytes!`), lazy decompression with `xz2` - C++ (Default): Pre-extracted binary file for simplicity - C++ (Optional): Runtime XZ decompression with liblzma (requires `-DUSE_PREEXTRACTED_KEY=OFF`) 2. **Memory Management**: Uses standard C++ containers and smart pointers 3. **Stream Processing**: Reads data in 4KB chunks for efficient processing 4. **Absolute Indexing**: Uses absolute positions into the full public key array (unlike Rust's slice-based approach) 5. **File Type Detection**: Implements magic bytes detection similar to Rust's `infer` crate ### Important Bug Fix During development, a critical bug was discovered and fixed: - **Issue**: Public key was being indexed with relative offsets, causing incorrect decryption after the first buffer chunk - **Fix**: Changed to use absolute global positions when indexing into the public key array - **Impact**: Without this fix, decryption would fail at position 4112 (after the first 4096-byte buffer) ## Troubleshooting **Test fails with size mismatch:** - Ensure you're running the test from the `src_c/build` directory - Verify that `assets/kugou_key.bin` exists and is 73,155,904 bytes **Cannot open kugou_key.bin:** - Run `python extract_key.py` from the project root first - Check that the path in `kgm_decoder.cpp` matches your directory structure **Want to use XZ decompression instead:** - Install liblzma (via package manager or vcpkg) - Rebuild with: `cmake .. -DUSE_PREEXTRACTED_KEY=OFF` **Compilation errors:** - Ensure you have C++17 support enabled - Check that CMake found your compiler correctly - If using MSVC, make sure there's no conflict with MinGW headers ## License Same as the original project - Anti 996 License Version 1.0