summaryrefslogtreecommitdiffstats
path: root/talk/sc.c
diff options
context:
space:
mode:
Diffstat (limited to 'talk/sc.c')
-rw-r--r--talk/sc.c229
1 files changed, 229 insertions, 0 deletions
diff --git a/talk/sc.c b/talk/sc.c
new file mode 100644
index 0000000..6be9a2d
--- /dev/null
+++ b/talk/sc.c
@@ -0,0 +1,229 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define MAGIC 0x7d53b605
+
+#define MIN_REF_LEN 5
+#define MAX_REF 127 + MIN_REF_LEN
+
+typedef struct {
+ unsigned size;
+ unsigned char *data;
+ unsigned real_size;
+} file_data_t;
+
+typedef struct __attribute ((packed)) {
+ uint32_t magic;
+ uint32_t size;
+ uint32_t unpacked_size;
+ uint32_t sample_rate;
+} snd_header_t;
+
+file_data_t *read_file(char *name);
+void add_data(file_data_t *d, void *buffer, unsigned size);
+void write_data(file_data_t *d, char *name);
+
+void compr(file_data_t *fd, file_data_t *fd_compr);
+unsigned find_longest(unsigned char *data, unsigned len, unsigned start, unsigned *ofs);
+
+
+int main(int argc, char **argv)
+{
+ file_data_t *fd, fd_compr = { }, fd_samples = { };
+ snd_header_t sh;
+ unsigned sample_rate;
+
+ if(argc != 3) return 1;
+
+ fd = read_file(argv[1]);
+
+ if(fd->size <= 44) return 1;
+
+ if(
+ *((unsigned *) (fd->data + 0)) != 0x46464952 ||
+ *((unsigned *) (fd->data + 8)) != 0x45564157 ||
+ *((short *) (fd->data + 20)) != 1 ||
+ *((short *) (fd->data + 34)) != 8 ||
+ *((short *) (fd->data + 22)) != 1
+ ) {
+ fprintf(stderr, "invalid data, expecting 8bit mono wav (ms pcm) file\n");
+ return 3;
+ }
+ sample_rate = *((unsigned *) (fd->data + 24));
+
+ printf("%s: %u Hz, %u samples\n", argv[1], sample_rate, fd->size - 44);
+
+ add_data(&fd_samples, fd->data + 44, fd->size - 44);
+
+ sh.magic = MAGIC;
+ sh.unpacked_size = fd_samples.size;
+ sh.sample_rate = sample_rate;
+
+ add_data(&fd_compr, &sh, sizeof sh);
+
+ compr(&fd_samples, &fd_compr);
+
+ sh.size = fd_compr.size - sizeof (snd_header_t);
+
+ memcpy(fd_compr.data, &sh, sizeof (snd_header_t));
+
+ write_data(&fd_compr, argv[2]);
+
+ return 0;
+}
+
+
+file_data_t *read_file(char *name)
+{
+ file_data_t *fd;
+ FILE *f;
+
+ fd = calloc(1, sizeof *fd);
+
+ if(!name) return fd;
+
+ f = fopen(name, "r");
+
+ if(!f) { perror(name); return fd; }
+
+ if(fseek(f, 0, SEEK_END)) {
+ perror(name);
+ exit(30);
+ }
+
+ fd->size = fd->real_size = ftell(f);
+
+ if(fseek(f, 0, SEEK_SET)) {
+ perror(name);
+ exit(30);
+ }
+
+ if(fd->size) {
+ fd->data = calloc(1, fd->size);
+ if(!fd->data) {
+ fprintf(stderr, "malloc failed\n");
+ exit(30);
+ }
+ }
+
+ if(fread(fd->data, 1, fd->size, f) != fd->size) {
+ perror(name);
+ exit(30);
+ }
+
+ fclose(f);
+
+ return fd;
+}
+
+
+void add_data(file_data_t *d, void *buffer, unsigned size)
+{
+ if(!size || !d || !buffer) return;
+
+ if(d->size + size > d->real_size) {
+ d->real_size = d->size + size + 0x1000;
+ d->data = realloc(d->data, d->real_size);
+ if(!d->data) d->real_size = 0;
+ }
+
+ if(d->size + size <= d->real_size) {
+ memcpy(d->data + d->size, buffer, size);
+ d->size += size;
+ }
+ else {
+ fprintf(stderr, "Oops, out of memory? Aborted.\n");
+ exit(10);
+ }
+}
+
+
+void write_data(file_data_t *d, char *name)
+{
+ FILE *f;
+
+ f = strcmp(name, "-") ? fopen(name, "w") : stdout;
+
+ if(!f) {
+ perror(name);
+ return;
+ }
+
+ if(fwrite(d->data, d->size, 1, f) != 1) {
+ perror(name);
+ exit(3);
+ }
+
+ fclose(f);
+}
+
+
+void compr(file_data_t *fd, file_data_t *fd_compr)
+{
+ unsigned u, v, l, ofs;
+ unsigned char uc;
+
+ if(!fd->size) return;
+
+ for(u = 0; u < fd->size; u++) {
+ if(fd->data[u] == 0xff) fd->data[u] = 0xfe;
+ }
+
+ // printf("%5u: %02x\n", fd->size, fd->data[0]);
+ add_data(fd_compr, fd->data, 1);
+
+ for(u = 1; u < fd->size; ) {
+ l = find_longest(fd->data, fd->size, u, &ofs);
+ // printf("%u: %u bytes @ %u\n", u, l, ofs);
+ if(l >= MIN_REF_LEN) {
+ // printf("%5u: %u bytes @ %u\n", fd->size, l, ofs);
+ v = (ofs << 7) + l - MIN_REF_LEN;
+ uc = 0xff; add_data(fd_compr, &uc, 1);
+ uc = v; add_data(fd_compr, &uc, 1);
+ uc = v >> 8; add_data(fd_compr, &uc, 1);
+ uc = v >> 16; add_data(fd_compr, &uc, 1);
+ u += l;
+ }
+ else {
+ // printf("%5u: %02x\n", fd->size, fd->data[u]);
+ add_data(fd_compr, fd->data + u, 1);
+ u++;
+ }
+ }
+}
+
+
+unsigned find_longest(unsigned char *data, unsigned len, unsigned start, unsigned *ofs)
+{
+ unsigned l;
+ unsigned char *p, *p1;
+
+ p = data;
+ l = MIN_REF_LEN;
+
+ for(;;) {
+ p1 = memmem(p, data + start + l - 1 - p, data + start, l);
+ if(!p1) break;
+ p = p1;
+ l++;
+
+ if(l > MAX_REF) break;
+ }
+
+ l--;
+
+ if(l < MIN_REF_LEN) {
+ *ofs = 0;
+ return 0;
+ }
+
+ *ofs = p - data;
+ return l;
+}
+
+