This is something I wrote for a couple of other forums but might help you out if you want to look into Q3 Game Reversing/Reversing in General, here's the post:

Hmm might come in handy for someone (like me, random and wanting a lil screwaround in times of boredom). For example I'm building a custom commands interface (cos etadmin_mod is gay).

It's simple, stupid and useless as I said above, but hey

main.h
Code:
#include <windows.h>
// Detours
#include "detours.h"

// Put these here, resolve in main.cpp
extern void (*orig_addStr)(const char *text);
extern void (*orig_exec)();
main.cpp/main.c
Code:
#include "main.h"

#pragma unmanaged //dllMain

void (*orig_hooker)(const char *text);
void hook_hooker(const char *text) {
	__asm nop; // Thanks to !K-0t1c! for triggering my memory of functions having to be so long for DetourFunction to work
	// General idea is that you would parse things here
	// This function is what prints text to the console, so everything that is printed goes through here
	orig_hooker(text);
}

void (*orig_addStr)(const char *text);
void (*orig_exec)();

// Can have a wrapper for the above functions here... eg (not in header so define it if using it)
void passToEngine(const char *text) {
	orig_addStr(text);
	orig_exec();
}

int __stdcall DllMain(HANDLE hwnd, DWORD dwReason, LPVOID lpVoid) {
	// If we're attaching to a process
	if(dwReason == DLL_PROCESS_ATTACH) {
		// We could do some CRC32 on ET.exe to determine which version we're on, but if they're not on 2.60b my general thought is '**** them', so, **** them.
		//orig_hooker = (void (__cdecl *)(const char *))DetourFunction((PBYTE)0x433970, (PBYTE)hook_hooker); // 2.60
		orig_hooker = (void (__cdecl *)(const char *))DetourFunction((PBYTE)0x433A90, (PBYTE)&hook_hooker); // 2.60b
		//orig_addStr = (void (__cdecl *)(const char *))0x40A5A0; // 2.60
		orig_addStr = (void (__cdecl *)(const char *))0x40A5A0; // 2.60b
		//orig_exec = (void (__cdecl *)(void))0x40ACD0; // 2.60
		orig_exec = (void (__cdecl *)(void))0x40ACD0; // 2.60b
	}
	return 1;
}
Finding the offsets:
There's a different offset @ 2.60 to 2.60b for the 'orig_hooker' aka 'Conbuf_AppendText' which is 0x433970, the other 2 are the same.

To find the offsets (My method)!
orig_hooker/Conbuf_AppendText - Search for 'Error during initialization', you should come to something like this (Follow the xref if it's rdata):
Code:
push    offset aErrorDuringIni ; "Error during initialization"
call    sub_42FF10
0x42FF10 is Sys_Error, go there, this has two calls to the function right after another (First one with the text, second one with '\n' defined as a word):
Code:
lea     eax, [esp+1038h+Source]
push    eax
call    sub_433970
push    offset word_479BC0
call    sub_433970
There's your offset, 0x433970.

orig_addStr/Cbuf_AddText - The quickest way to find this is probably another way (Namely look for it earlier in Com_Init), but doing my method gets us 2 offsets for the price of one.
To find it, zoom to Com_Init (Where you found "Error during initialization") and search for "set com_crashed 0", it will be after a push to a function and have "exec autoexec.cfg\n" above it too, calling the same function.
Code:
push    offset aExecAutoexec_c
call    sub_40A5A0
push    offset aSetCom_crash_0 ; "set com_crashed 0\n"
call    sub_40A5A0
Guess what that function is? Cbuf_AddText!
Offset: 0x40A5A0

orig_exec/Cbuf_Execute - Oooh look at the line after what we just searched for:
Code:
call    sub_40ACD0
You guessed it Offset: 0x40ACD0