/* * REIS audio format parser -- kernel side. * * Handles header validation and optional PackBits RLE decompression. * The decompression reuses the same PackBits algorithm as REI and REIV. * * Memory ownership: reis_parse_audio() allocates the sample buffer via * kmalloc(). The caller must call reis_free_audio() to release it. */ #include #include #include #include /* ---- Freestanding helpers ---- */ static void reis_memcpy(void* dst, const void* src, size_t n) { uint8_t* d = (uint8_t*)dst; const uint8_t* s = (const uint8_t*)src; for (size_t i = 0; i < n; ++i) d[i] = s[i]; } static void reis_memset(void* dst, uint8_t val, size_t n) { uint8_t* d = (uint8_t*)dst; for (size_t i = 0; i < n; ++i) d[i] = val; } /* ---- PackBits RLE decoder (shared codec with REI/REIV) ---- */ static int rle_decode_packbits(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len, int pixel_size) { size_t ip = 0; size_t op = 0; if (!in || !out || pixel_size <= 0) return -1; while (ip < in_len && op < out_len) { int8_t n = (int8_t)in[ip++]; if (n >= 0) { /* Literal run: n + 1 units. */ size_t count = (size_t)n + 1u; size_t bytes = count * (size_t)pixel_size; if (ip + bytes > in_len) return -1; if (op + bytes > out_len) return -1; reis_memcpy(out + op, in + ip, bytes); ip += bytes; op += bytes; } else if (n != -128) { /* Replicate run: 1 - n copies of next unit. */ size_t count = (size_t)(1 - n); size_t bytes = count * (size_t)pixel_size; if (ip + (size_t)pixel_size > in_len) return -1; if (op + bytes > out_len) return -1; const uint8_t* px = in + ip; ip += (size_t)pixel_size; for (size_t i = 0; i < count; ++i) { reis_memcpy(out + op, px, (size_t)pixel_size); op += (size_t)pixel_size; } } /* n == -128 is a no-op (padding byte). */ } return (op == out_len) ? 0 : -1; } /* ---- Public API ---- */ int reis_read_header(const uint8_t* data, size_t size, reis_header_t* out) { if (!data || !out) return -1; if (size < sizeof(reis_header_t)) return -1; reis_memcpy(out, data, sizeof(reis_header_t)); return reis_validate_header(out); } int reis_validate_header(const reis_header_t* header) { if (!header) return -1; if (header->magic != REIS_MAGIC) return -1; if (header->version != REIS_VERSION) return -1; if (header->channels != 1 && header->channels != 2) return -1; if (header->bits != 8 && header->bits != 16) return -1; if (header->sample_rate == 0 || header->sample_rate > REIS_MAX_SAMPLE_RATE) return -1; if (header->frame_count == 0 || header->frame_count > REIS_MAX_FRAME_COUNT) return -1; if (header->data_offset < sizeof(reis_header_t)) return -1; if (header->data_size == 0) return -1; if ((header->flags & REIS_COMP_MASK) > REIS_COMP_RLE) return -1; return 0; } int reis_parse_audio(const uint8_t* data, size_t size, reis_audio_t* audio) { if (!data || !audio) return -1; reis_header_t hdr; if (reis_read_header(data, size, &hdr) != 0) return -1; /* Validate that the payload fits within the provided data. */ if ((size_t)hdr.data_offset + (size_t)hdr.data_size > size) return -1; uint32_t pcm_bytes = reis_pcm_size(&hdr); if (pcm_bytes == 0) return -1; audio->samples = (uint8_t*)malloc(pcm_bytes); if (!audio->samples) return -1; audio->samples_size = pcm_bytes; audio->header = hdr; const uint8_t* payload = data + hdr.data_offset; uint8_t comp = (uint8_t)(hdr.flags & REIS_COMP_MASK); if (comp == REIS_COMP_NONE) { if (hdr.data_size < pcm_bytes) { free(audio->samples); audio->samples = NULL; return -1; } reis_memcpy(audio->samples, payload, pcm_bytes); } else if (comp == REIS_COMP_RLE) { int unit = (int)reis_frame_bytes(&hdr); if (rle_decode_packbits(payload, hdr.data_size, audio->samples, pcm_bytes, unit) != 0) { free(audio->samples); audio->samples = NULL; return -1; } } else { free(audio->samples); audio->samples = NULL; return -1; } return 0; } void reis_free_audio(reis_audio_t* audio) { if (!audio) return; if (audio->samples) { free(audio->samples); audio->samples = NULL; } audio->samples_size = 0; reis_memset(&audio->header, 0, sizeof(audio->header)); }