Created
October 14, 2015 04:44
-
-
Save sigsegv-mvm/5bc501fb1412006c340d to your computer and use it in GitHub Desktop.
Tool for stripping stringtable data out of SourceTV demos
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <err.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <assert.h> | |
#include <stdbool.h> | |
struct demo_header { | |
char header[8]; | |
int32_t demo_proto_version; | |
int32_t net_proto_version; | |
char server_name[260]; | |
char client_name[260]; | |
char map_name[260]; | |
char game_dir[260]; | |
float playback_time; | |
int32_t ticks; | |
int32_t frames; | |
int32_t signon_length; | |
}; | |
enum { | |
DEM_SIGNON = 1, | |
DEM_PACKET = 2, | |
DEM_SYNCTICK = 3, | |
DEM_CONSOLECMD = 4, | |
DEM_USERCMD = 5, | |
DEM_DATATABLES = 6, | |
DEM_STOP = 7, | |
DEM_STRINGTABLES = 8, | |
}; | |
static const char *msgtypenames[] = { | |
"0", | |
"SIGNON", | |
"PACKET", | |
"SYNCTICK", | |
"CONSOLECMD", | |
"USERCMD", | |
"DATATABLES", | |
"STOP", | |
"STRINGTABLES", | |
}; | |
char path_in[1024]; | |
char path_out[1024]; | |
FILE *f_in = NULL; | |
FILE *f_out = NULL; | |
struct demo_header hdr; | |
int main(int argc, char **argv) | |
{ | |
fprintf(stderr, "SourceTV demo stringtable remover (by sigsegv)\n\n"); | |
fprintf(stderr, | |
"WARNING: this is a hacky tool meant solely for use as a temporary workaround;\n" | |
" make sure to keep your original demo file (it won't be overwritten)\n\n"); | |
if (argc != 2) { | |
fprintf(stderr, "usage: %s <demoname>\n" | |
"(reads from demoname.dem, writes to demoname_fixed.dem)\n", | |
argv[0]); | |
return 1; | |
} | |
const char *path_base = argv[1]; | |
snprintf(path_in, sizeof(path_in), "%s.dem", path_base); | |
snprintf(path_out, sizeof(path_out), "%s_fixed.dem", path_base); | |
f_in = fopen(path_in, "r"); | |
if (f_in == NULL) { | |
err(1, "could not open '%s'", path_in); | |
} | |
if (fread(&hdr, 1, sizeof(hdr), f_in) != sizeof(hdr)) { | |
err(1, "could not read header from '%s'", path_in); | |
} | |
bool strtab_found = false; | |
int32_t strtab_off = -1; | |
int32_t strtab_len = 0; | |
int32_t stop_at = hdr.signon_length; | |
if (stop_at == 0) { | |
stop_at = INT32_MAX; | |
} | |
int32_t off = sizeof(hdr); | |
while (!strtab_found && off < stop_at) { | |
int msg_type; | |
if ((msg_type = fgetc(f_in)) == EOF) { | |
if (feof(f_in)) { | |
break; | |
} else { | |
err(1, "could not read msg type from '%s', offset %x", | |
path_in, off); | |
} | |
} | |
++off; | |
warnx("off 0x%x: msg_type %d %s", | |
off, msg_type, msgtypenames[msg_type]); | |
if (msg_type == DEM_STOP) { | |
break; | |
} | |
int32_t msg_tick; | |
if (fread(&msg_tick, 1, sizeof(msg_tick), f_in) != sizeof(msg_tick)) { | |
err(1, "could not read msg tick from '%s', " | |
"msg_type %d, offset 0x%x", | |
path_in, msg_type, off); | |
} | |
off += 4; | |
if (msg_type == DEM_SYNCTICK) { | |
continue; | |
} | |
int skip = 0; | |
if (msg_type == DEM_SIGNON || msg_type == DEM_PACKET) { | |
skip = 0x54; | |
} else if (msg_type == DEM_USERCMD) { | |
skip = 0x4; | |
} | |
if (skip != 0) { | |
if (fseek(f_in, skip, SEEK_CUR) != 0) { | |
err(1, "could not seek 0x%x bytes forward in '%s', " | |
"msg_type %d, offset 0x%x", | |
skip, path_in, msg_type, off); | |
} | |
} | |
off += skip; | |
int32_t msg_len; | |
if (fread(&msg_len, 1, sizeof(msg_len), f_in) != sizeof(msg_len)) { | |
err(1, "could not read msg len from '%s', " | |
"msg_type %d, offset 0x%x", | |
path_in, msg_type, off); | |
} | |
off += 4; | |
if (msg_type == DEM_STRINGTABLES) { | |
strtab_off = off - 9; | |
strtab_len = msg_len + 9; | |
strtab_found = true; | |
warnx("found DEM_STRINGTABLES: offset 0x%x, total len %d", | |
strtab_off, strtab_len); | |
} | |
if (fseek(f_in, msg_len, SEEK_CUR) != 0) { | |
err(1, "could not seek 0x%x bytes forward in '%s', " | |
"msg_type %d, offset 0x%x", | |
msg_len, path_in, msg_type, off); | |
} | |
off += msg_len; | |
} | |
if (strtab_found) { | |
warnx("will now write '%s'", path_out); | |
f_out = fopen(path_out, "w"); | |
if (f_out == NULL) { | |
err(1, "could not open '%s'", path_out); | |
} | |
if (hdr.signon_length != 0) { | |
warnx("adjusting demo header for changed signon length\n" | |
" was: %d\n" | |
" now: %d", | |
hdr.signon_length, | |
hdr.signon_length - strtab_len); | |
hdr.signon_length -= strtab_len; | |
} | |
if (fwrite(&hdr, 1, sizeof(hdr), f_out) != sizeof(hdr)) { | |
err(1, "could not write header to '%s'", path_out); | |
} | |
char buf[4096]; | |
/* data copy: | |
* from: right after the header | |
* to: right before DEM_STRINGTABLES | |
*/ | |
if (fseek(f_in, sizeof(hdr), SEEK_SET) != 0) { | |
err(1, "could not seek back to the header in '%s'", path_in); | |
} | |
int32_t len = strtab_off - sizeof(hdr); | |
int32_t off = sizeof(hdr); | |
while (len > 0) { | |
int32_t this_len = sizeof(buf); | |
if (len < this_len) { | |
this_len = len; | |
} | |
if (fread(buf, 1, this_len, f_in) != this_len) { | |
err(1, "could not read %d bytes from '%s' at offset 0x%x", | |
this_len, path_in, off); | |
} | |
if (fwrite(buf, 1, this_len, f_out) != this_len) { | |
err(1, "could not write %d bytes to '%s' at offset 0x%x", | |
this_len, path_out, off); | |
} | |
len -= this_len; | |
off += this_len; | |
} | |
/* data copy: | |
* from: right after DEM_STRINGTABLES | |
* to: end-of-file | |
*/ | |
if (fseek(f_in, strtab_off + strtab_len, SEEK_SET) != 0) { | |
err(1, "could not seek to post-DEM_STRINGTABLES location in '%s'", | |
path_in); | |
} | |
bool done = false; | |
do { | |
int32_t this_len; | |
if ((this_len = fread(buf, 1, sizeof(buf), f_in)) != sizeof(buf)) { | |
if (feof(f_in)) { | |
done = true; | |
} else { | |
err(1, "could not read %d bytes from '%s' at offset 0x%x", | |
sizeof(buf), path_in, off); | |
} | |
} | |
if (fwrite(buf, 1, this_len, f_out) != this_len) { | |
err(1, "could not write %d bytes to '%s' at offset 0x%x", | |
this_len, path_out, off); | |
} | |
len -= this_len; | |
off += this_len; | |
} while (!done); | |
fclose(f_out); | |
} else { | |
warnx("no DEM_STRINGTABLES msg found, no need to write a new file"); | |
} | |
fclose(f_in); | |
warnx("success"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment