/* Copyright (c) 2026 Juhapekka Piiroinen * All Rights Reserved. * AmoosedWeb project: https://amoosed.com */ #include "AmoosedWeb/PreviewC.h" #include #include #include #include #ifdef _WIN32 #include #include #include #include #define AMW_WRITE _write #define AMW_CLOSE _close #define AMW_UNLINK _unlink #else #include #include #define AMW_WRITE write #define AMW_CLOSE close #define AMW_UNLINK unlink #endif static void clear_bytes(void* data, size_t length) { volatile unsigned char* bytes = (volatile unsigned char*)data; while (length--) *bytes++ = 0; } static int hex_digit(unsigned char value) { if (value >= '0' && value <= '9') return value - '0'; if (value >= 'a' && value <= 'f') return value - 'a' + 10; if (value >= 'A' && value <= 'F') return value - 'A' + 10; return -1; } static int read_key(const char* path, unsigned char key[32]) { unsigned char text[68] = {0}; FILE* file = fopen(path, "rb"); size_t length, i; if (!file) return 0; length = fread(text, 1, sizeof(text), file); { int valid_read = !ferror(file) && feof(file); int closed = fclose(file); if (!valid_read || closed) { clear_bytes(text, sizeof(text)); return 0; } } while (length && (text[length - 1] == '\n' || text[length - 1] == '\r')) --length; if (length != 64) { clear_bytes(text, sizeof(text)); return 0; } for (i = 0; i < 32; ++i) { int high = hex_digit(text[2 * i]), low = hex_digit(text[2 * i + 1]); if (high < 0 || low < 0) { clear_bytes(text, sizeof(text)); clear_bytes(key, 32); return 0; } key[i] = (unsigned char)((high << 4) | low); } clear_bytes(text, sizeof(text)); return 1; } static unsigned char* read_wav(const char* path, size_t* length) { FILE* file = fopen(path, "rb"); long size; unsigned char* data; if (!file) return NULL; if (fseek(file, 0, SEEK_END) || (size = ftell(file)) <= 0 || (uint64_t)size > AMW_PREVIEW_AUDIO_WAV_LIMIT || fseek(file, 0, SEEK_SET)) { fclose(file); return NULL; } data = (unsigned char*)malloc((size_t)size); if (!data) { fclose(file); return NULL; } { int valid_read = fread(data, 1, (size_t)size, file) == (size_t)size; int closed = fclose(file); if (!valid_read || closed) { free(data); return NULL; } } *length = (size_t)size; return data; } static int create_output(const char* path, const unsigned char* data, size_t length) { int fd, ok = 1; size_t offset = 0; #ifdef _WIN32 if (_sopen_s(&fd, path, _O_WRONLY | _O_CREAT | _O_EXCL | _O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE)) return 0; #else fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); if (fd < 0) return 0; #endif while (offset < length) { int written = (int)AMW_WRITE(fd, data + offset, (unsigned int)(length - offset)); if (written <= 0) { ok = 0; break; } offset += (size_t)written; } if (AMW_CLOSE(fd)) ok = 0; if (!ok) AMW_UNLINK(path); return ok; } int main(int argc, char** argv) { unsigned char key[32] = {0}; unsigned char *wav = NULL, *recovered = NULL; size_t wav_length = 0; uint64_t required = 0; uint64_t allocated = 0; uint64_t capabilities = 0; char error[192] = {0}; int32_t status; int result = 1; if (argc != 4) { fputs("Usage: native-c-audio CARRIER.wav MEDIA_KEY_HEX_FILE OUTPUT_PATH\n", stderr); return 2; } if (amw_preview_abi_revision() != AMW_PREVIEW_ABI_REVISION || amw_preview_capabilities(&capabilities) != AMW_PREVIEW_OK || !(capabilities & AMW_PREVIEW_CAP_AUDIO_FILE)) { fputs("This library does not support the required preview audio API.\n", stderr); return 1; } if (!read_key(argv[2], key) || !(wav = read_wav(argv[1], &wav_length))) { fputs("Cannot read the media key or bounded WAV input.\n", stderr); goto done; } status = amw_preview_audio_decode_file(wav, wav_length, key, sizeof(key), NULL, 0, &required, error, sizeof(error)); if (status != AMW_PREVIEW_OK || required > 4068) goto decode_failed; allocated = required; recovered = (unsigned char*)malloc((size_t)allocated + 1); if (!recovered) goto done; status = amw_preview_audio_decode_file(wav, wav_length, key, sizeof(key), recovered, required, &required, error, sizeof(error)); if (status != AMW_PREVIEW_OK) goto decode_failed; if (!create_output(argv[3], recovered, (size_t)required)) { fputs("Output exists or cannot be written.\n", stderr); goto done; } result = 0; goto done; decode_failed: fprintf(stderr, "Audio decode failed (%d): %s\n", (int)status, error); done: clear_bytes(key, sizeof(key)); if (recovered) { clear_bytes(recovered, (size_t)allocated); free(recovered); } free(wav); return result; }