Skip to content

Instantly share code, notes, and snippets.

@ericek111
Created November 20, 2020 02:30
Show Gist options
  • Save ericek111/abe5829f6e52e4b25b3b97a0efd0b22b to your computer and use it in GitHub Desktop.
Save ericek111/abe5829f6e52e4b25b3b97a0efd0b22b to your computer and use it in GitHub Desktop.
CS:GO voice demo parser
#include <unistd.h>
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/time.h>
#include <celt.h>
// gcc -o csgo csgo.c -I./celt-0.11.1/libcelt -L"$HOME/.steam/steam/steamapps/common/Counter-Strike Global Offensive/bin/linux64" -l:vaudio_celt_client.so && ./csgo && play -t raw -r 22050 -e signed -b 16 -c 1 out.raw
// export LD_LIBRARY_PATH="$HOME/.steam/steam/steamapps/common/Counter-Strike Global Offensive/bin/linux64"
// ./csgo
// play -t raw -r 22050 -e signed -b 16 -c 1 out.raw
// sox -t raw -r 22050 -e signed -b 16 -c 1 -L out.raw out.wav
#define BUF_SIZE 1024*1024
int main(int argc, char *argv[]) {
unsigned char buf[BUF_SIZE];
const unsigned int FRAME_SIZE = 512;
const unsigned int SAMPLE_RATE = 22050;
FILE *f = fopen("voicedata.dat", "r");
if (f == NULL) {
return 1;
}
size_t read = fread(buf, 1, BUF_SIZE, f);
fclose(f);
CELTMode *dm = celt_mode_create(SAMPLE_RATE, FRAME_SIZE, NULL);
CELTDecoder *d = celt_decoder_create_custom(dm, 1, NULL);
size_t outsize = (read / 64) * FRAME_SIZE * sizeof(celt_int16);
celt_int16* pcmout = malloc(outsize);
size_t done = 0;
int frames = 0;
for (; done < read; done += 64, frames++) {
int ret = 0;
if ((ret = celt_decode(d, buf + done, 64, pcmout + frames * FRAME_SIZE, FRAME_SIZE)) < 0) {
fprintf(stderr, "unable to decode... > %d (at %d/%d)\n", ret, done, read);
//return 1;
}
//printf("Done %d/%d (to %ld/%ld)\n", done, read, frames * FRAME_SIZE * 2, outsize);
}
FILE* file_p = fopen("out.raw", "w");
size_t written = fwrite(pcmout, outsize, 1, file_p);
fclose(file_p);
free(pcmout);
return 0;
}
@ericek111
Copy link
Author

Actually, maybe it's not that hard. Look at my GODemolyser, even lacking lots of features, it's still the most understandable to me (and also the fastest). You could inject your own dem_packet-s into the file. Protobufs can be, just as easily as they are parsed, constructed from your own data. Here, basically revert this process: https://github.com/ericek111/GODemolyser/blob/master/GODemolyser/src/eu/lixko/godemolyser/parser/dem/PacketParser.java#L15

I think the greatest hurdle here would be implementing write support for the ByteArrayDataStream class, hah. It operates without copying, so inserting data mid-buffer would shift other data and mess up all slices created from it. Some things are also bit-indexed (Protobuf encoding), so the entire affected buffer would have to be bit-shifted on non-aligned writes. But, from what I recall, packets are aligned to bytes, to that shouldn't be an issue for your use case, just thinking aloud. Also, the DataStream class (from which implementations are extended) does not care about the underlying storage.

@michihupf
Copy link

So you can just "insert" the audio into the .dem file by just reversing this one process? How come noone ever thought of that?

@ericek111
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment